diff --git a/BurnOutSharp/External/README.MD b/BurnOutSharp/External/README.MD index eaff1d1a..bd6aed86 100644 --- a/BurnOutSharp/External/README.MD +++ b/BurnOutSharp/External/README.MD @@ -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 | \ No newline at end of file diff --git a/BurnOutSharp/External/SevenZip/ComHandler.cs b/BurnOutSharp/External/SevenZip/ComHandler.cs deleted file mode 100644 index 8904fe1f..00000000 --- a/BurnOutSharp/External/SevenZip/ComHandler.cs +++ /dev/null @@ -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 _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 copyCoder = copyCoderSpec; - -// CLocalProgress* lps = new CLocalProgress; -// CMyComPtr 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 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 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 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 Items { get; set; } - - public List 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 - } -} \ No newline at end of file diff --git a/BurnOutSharp/External/libgsf/GsfBlob.cs b/BurnOutSharp/External/libgsf/GsfBlob.cs deleted file mode 100644 index 852f0b6c..00000000 --- a/BurnOutSharp/External/libgsf/GsfBlob.cs +++ /dev/null @@ -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 - - /// - /// Private constructor - /// - private GsfBlob() { } - - /// - /// 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. - /// - /// Size of the data in bytes. - /// Data which will be copied into the blob, or null if is zero. - /// Location to store error, or null. - /// A newly-created GsfBlob, or null if the data could not be copied. - 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 - } -} diff --git a/BurnOutSharp/External/libgsf/GsfClipData.cs b/BurnOutSharp/External/libgsf/GsfClipData.cs deleted file mode 100644 index c557c448..00000000 --- a/BurnOutSharp/External/libgsf/GsfClipData.cs +++ /dev/null @@ -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 - { - /// - /// Windows clipboard format - /// - GSF_CLIP_FORMAT_WINDOWS_CLIPBOARD = -1, - - /// - /// Macintosh clipboard format - /// - GSF_CLIP_FORMAT_MACINTOSH_CLIPBOARD = -2, - - /// - /// GUID that contains a format identifier - /// - GSF_CLIP_FORMAT_GUID = -3, - - /// - /// No clipboard data - /// - GSF_CLIP_FORMAT_NO_DATA = 0, - - /// - /// Custom clipboard format - /// - /// - /// In the file it's actually any positive integer - /// - GSF_CLIP_FORMAT_CLIPBOARD_FORMAT_NAME = 1, - - /// - /// Unknown clipboard type or invalid data - /// - /// - /// This is our own value for unknown types or invalid data - /// - GSF_CLIP_FORMAT_UNKNOWN - } - - // TODO: Can this be made internal? - public enum GsfClipFormatWindows - { - /// - /// Our own value - /// - GSF_CLIP_FORMAT_WINDOWS_ERROR = -1, - - /// - /// Our own value - /// - GSF_CLIP_FORMAT_WINDOWS_UNKNOWN = -2, - - /// - /// CF_METAFILEPICT - /// - GSF_CLIP_FORMAT_WINDOWS_METAFILE = 3, - - /// - /// CF_DIB - /// - GSF_CLIP_FORMAT_WINDOWS_DIB = 8, - - /// - /// CF_ENHMETAFILE - /// - 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 - - /// - /// Private constructor - /// - private GsfClipData() { } - - /// - /// Creates a new GsfClipData object. This function acquires a reference to the - /// , so you should unref the blob on your own if you no longer need it - /// directly. - /// - /// Format for the data inside the - /// Object which holds the binary contents for the GsfClipData - /// A newly-created GsfClipData. - 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 - - /// - /// Queries the Windows clipboard data format for a GsfClipData. The must - /// have been created with #GSF_CLIP_FORMAT_WINDOWS_CLIPBOARD. - /// - /// Location to store error, or NULL - /// A GsfClipFormatWindows value. - 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; - } - } - - /// - /// 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 . 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. - /// - /// Location to return the size of the returned data buffer. - /// Location to store error, or NULL. - /// - /// Pointer to the real clipboard data. The size in bytes of this - /// buffer is returned in the argument. - /// - 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(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; - } - - /// - /// Checks that the specified blob size matches the expected size for the format. - /// - /// - /// The same format if the size is correct, or - /// GSF_CLIP_FORMAT_WINDOWS_ERROR if the size is too small. - /// - 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 - } -} diff --git a/BurnOutSharp/External/libgsf/GsfDocMetaData.cs b/BurnOutSharp/External/libgsf/GsfDocMetaData.cs deleted file mode 100644 index 834b951c..00000000 --- a/BurnOutSharp/External/libgsf/GsfDocMetaData.cs +++ /dev/null @@ -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; } - - /// - /// Optionally null - /// - public string LinkedTo { get; set; } - - public uint RefCount { get; set; } - - #endregion - - #region Constructor - - /// - /// Private constructor - /// - private GsfDocProp() { } - - /// The name of the property. - /// A new GsfDocProp. - public static GsfDocProp Create(string name) - { - if (name == null) - return null; - - return new GsfDocProp - { - Name = name, - Value = null, - LinkedTo = null, - }; - } - - #endregion - - #region Functions - - /// - /// Release the given property. - /// - public void Free() - { - RefCount--; - if (RefCount == 0) - { - LinkedTo = null; - if (Value != null) - Value = null; - - Name = null; - } - } - - public GsfDocProp Reference() - { - RefCount++; - return this; - } - - /// The current value of prop, and replaces it with . - public object SwapValue(object val) - { - object old_val = Value; - Value = val; - return old_val; - } - - /// - /// A debugging utility to dump prop as text via Console - /// - /// New in 1.14.2 - public void Dump() - { - if (Value is List 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 Table = new Dictionary(); - - #endregion - - #region Constructor - - /// - /// Private constructor - /// - private GsfDocMetaData() { } - - /// A new metadata property collection - public static GsfDocMetaData Create() => new GsfDocMetaData(); - - #endregion - - #region Functions - - /// - /// The property with in meta. The caller can - /// modify the property value and link but not the name. - /// - public GsfDocProp Lookup(string name) - { - if (name == null) - return null; - - if (!Table.ContainsKey(name)) - return null; - - return Table[name]; - } - - /// - /// Take ownership of and and insert a property into meta. - /// If a property exists with @name, it is replaced (The link is lost) - /// - /// The id. - 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; - } - - /// - /// If does not exist in the collection, do nothing. If @name does exist, - /// remove it and its value from the collection - /// - /// The non-null string name of the property - public void Remove(string name) - { - if (name == null) - return; - - if (!Table.ContainsKey(name)) - return; - - Table.Remove(name); - } - - /// The property with in meta. - 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; - } - - /// - /// Iterate through each (key, value) pair in this collection - /// - /// The function called once for each element in the collection - /// Any supplied user data - public void ForEach(Action func, object user_data) - { - if (Table.Count == 0) - return; - - // Sort the pairs by property name in order to generate consistent files - List> 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); - } - } - - /// The number of items in this collection - public int Size() => Table.Count; - - /// - /// A debugging utility to dump the content of meta via Console - /// - public void Dump() - { - ForEach(PrintProperty, null); - } - - #endregion - - #region MS-OLE - - /// - /// Read a stream formated as a set of MS OLE properties from and store the - /// results in . - /// - /// an Exception if there was an error. - /// Since: 1.14.24 - 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(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 tempProps = new List(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; - } - - /// - /// A kludge to differentiate DocumentSummary from Summary - /// True on success - /// Since: 1.14.24 - 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 - } -} diff --git a/BurnOutSharp/External/libgsf/GsfLibXML.cs b/BurnOutSharp/External/libgsf/GsfLibXML.cs deleted file mode 100644 index 4390b699..00000000 --- a/BurnOutSharp/External/libgsf/GsfLibXML.cs +++ /dev/null @@ -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 - - /// - /// Controls the handling of character data within a parser node. - /// - public enum GsfXMLContent - { - /// - /// Node has no cstr contents - /// - GSF_XML_NO_CONTENT = 0, - - /// - /// Node has cstr contents - /// - GSF_XML_CONTENT, - - /// - /// Node has contents that is shared with children - /// - GSF_XML_SHARED_CONTENT, - - /// - /// Node is second or later occurrence - /// - 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 - - /// - /// User data - /// - public object UserState { get; set; } - - /// - /// The current node content - /// - public string Content { get; set; } - - /// - /// Current document being parsed - /// - public XmlDocument Doc { get; set; } - - /// - /// Current node (not on the stack) - /// - public XmlNode Node { get; set; } - - public Stack NodeStack { get; private set; } - - #endregion - - #region Internal Properties - - public GsfInput Input { get; set; } - - public Stack ContentsStack { get; internal set; } - - public bool Initialized { get; internal set; } - - #endregion - - #region Functions - - /// - /// Take the first node from as the current node and call its start handler. - /// - /// Arbitrary content for the parser - 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; - } - - /// - /// This function will not be called when parsing an empty document. - /// - public void StartDocument() - { - Initialized = true; - Node = Doc.FirstChild; - NodeStack = new Stack(); - ContentsStack = new Stack(); - } - - 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 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 - - /// - /// Parent GsfXMLIn for reading - /// - public GsfXMLIn Parent { get; internal set; } - - /// - /// Current user state - /// - public GsfXMLIn UserState { get; internal set; } - - /// - /// Internal reader instance for unhandled functionality - /// - 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 - - /// - /// Constructor - /// - 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(); - } - - /// - /// Destructor - /// - ~GsfXMLInParser() - { - Parent.EndDocument(); - } - - #endregion - - #region XmlReader Custom Implementation - - /// - public override void ReadStartElement() => Parent.StartElement(null, null); - - /// - public override void ReadStartElement(string name) => Parent.StartElement(name, null); - - /// - public override void ReadStartElement(string localname, string ns) => Parent.StartElement(localname, ns); - - /// - public override void ReadEndElement() => Parent.EndElement(); - - /// - public override string ReadElementContentAsString() => Parent.Node.InnerText; - - #endregion - - #region XmlReader Default Implementation - - /// - public override string GetAttribute(int i) => inst.GetAttribute(i); - - /// - public override string GetAttribute(string name) => inst.GetAttribute(name); - - /// - public override string GetAttribute(string name, string namespaceURI) => inst.GetAttribute(name, namespaceURI); - - /// - public override string LookupNamespace(string prefix) => inst.LookupNamespace(prefix); - - /// - public override bool MoveToAttribute(string name) => inst.MoveToAttribute(name); - - /// - public override bool MoveToAttribute(string name, string ns) => inst.MoveToAttribute(name, ns); - - /// - public override bool MoveToElement() => inst.MoveToElement(); - - /// - public override bool MoveToFirstAttribute() => inst.MoveToFirstAttribute(); - - /// - public override bool MoveToNextAttribute() => inst.MoveToNextAttribute(); - - /// - public override bool Read() => inst.Read(); - - /// - public override bool ReadAttributeValue() => inst.ReadAttributeValue(); - - /// - 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 Stack { get; private set; } = new Stack(); - - 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 - - /// - /// Private constructor - /// - protected GsfXMLOut() { } - - /// - /// Create an XML output stream. - /// - public static GsfXMLOut Create(GsfOutput output) - { - if (output == null) - return null; - - return new GsfXMLOut() - { - Output = output - }; - } - - #endregion - - #region Functions - - /// - /// Write the document start - /// - public void StartDocument() - { - string header = "\n"; - Output.Write(header.Length, Encoding.UTF8.GetBytes(header)); - if (DocType != null) - Output.PutString(DocType); - } - - /// - /// Output a start element , if necessary preceeded by an XML declaration. - /// - /// Element name - 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; - } - - /// - /// Closes/ends an XML element. - /// - /// The element that has been closed. - 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($"\n"); - else - Output.PrintF($""); - - break; - - case GsfXMLOutState.GSF_XML_OUT_CHILD: - case GsfXMLOutState.GSF_XML_OUT_CONTENT: - if (PrettyPrint) - Output.PrintF($"\n"); - else - Output.PrintF($""); - - break; - } - - State = PrettyPrint ? GsfXMLOutState.GSF_XML_OUT_CHILD_PRETTY : GsfXMLOutState.GSF_XML_OUT_CHILD; - return id; - } - - /// New state of pretty-print flag. - /// The previous state of the pretty-print flag. - public bool SetPrettyPrint(bool pp) - { - bool res = PrettyPrint; - if (pp != res) - PrettyPrint = pp; - - return res; - } - - /// - /// Convenience routine to output a simple element with content . - /// - /// Element name - /// Content of the element - public void OutSimpleElement(string id, string content) - { - StartElement(id); - if (content != null) - AddString(null, content); - - EndElement(); - } - - /// - /// Convenience routine to output an element with integer value . - /// - /// Element name - /// Element value - public void OutSimpleSignedElement(string id, long val) - { - StartElement(id); - AddSigned(null, val); - EndElement(); - } - - /// - /// Convenience routine to output an element with float value using - /// significant digits. - /// - /// Element name - /// Element value - /// The number of significant digits to use, -1 meaning "enough". - public void OutSimpleFloatElement(string id, double val, int precision) - { - StartElement(id); - AddFloat(null, val, precision); - EndElement(); - } - - /// - /// Dump 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 - /// is null do nothing (no warning, no output) - /// - /// Tag id, or null for node content - /// A UTF-8 encoded string to export - 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}\""); - } - } - - /// - /// Dump to an attribute named or as the nodes content escaping - /// characters as necessary. If @valUtf8 is %null do nothing (no warning, no - /// output) - /// - /// Tag id, or null for node content - /// A UTF-8 encoded string - 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("\"")); - } - - /// - /// Dump bytes in into the content of node using base64 - /// - /// Tag id, or null for node content - /// Data to be written - /// Length of data - public void AddBase64(string id, byte[] data, int len) => AddStringUnchecked(id, Convert.ToBase64String(data, 0, len)); - - /// - /// Dump boolean value to an attribute named or as the nodes content - /// - /// Tag id, or %null for node content - /// A boolean - /// Use '1' or '0' to simplify import - public void AddBool(string id, bool val) => AddStringUnchecked(id, val ? "1" : "0"); - - /// - /// Dump integer value to an attribute named or as the nodes content - /// - /// Tag id, or null for node content - /// The value - public void AddSigned(string id, long val) => AddStringUnchecked(id, val.ToString()); - - /// - /// Dump unsigned integer value to an attribute named or as the nodes - /// - /// Tag id, or null for node content - /// The value - public void AddUnsigned(string id, ulong val) => AddStringUnchecked(id, val.ToString()); - - /// - /// Dump float value to an attribute named or as the nodes - /// content with precision . The number will be formattted - /// according to the "C" locale. - /// - /// Tag id, or null for node content - /// The value - /// The number of significant digits to use, -1 meaning "enough". - 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}")); - } - - /// - /// Dump Color .. to an attribute named or as the nodes content - /// - /// Tag id, or null for node content - /// Red value - /// Green value - /// Blue value - public void AddColor(string id, uint r, uint g, uint b) - { - string buf = $"{r:X}:{g:X}:{b:X}\0"; - AddStringUnchecked(id, buf); - } - - /// - /// Output the value of as a string. Does NOT store any type information - /// with the string, just the value. - /// - /// Tag id, or null for node content - 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 - { - /// - /// Try to parse as a value of type into . - /// - /// Result value - /// Type of data - /// Value string - /// - /// True when parsing of as a value of type was succesfull; - /// false otherwise. - /// - 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 -} diff --git a/BurnOutSharp/External/libgsf/GsfMSOleImpl.cs b/BurnOutSharp/External/libgsf/GsfMSOleImpl.cs deleted file mode 100644 index 5453f6fd..00000000 --- a/BurnOutSharp/External/libgsf/GsfMSOleImpl.cs +++ /dev/null @@ -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 - - /// - /// MS-OLE Header (.msi) - /// - internal class MSOleHeader - { - #region Constants - - /// - /// Independent of big block size size - /// - public const int OLE_HEADER_SIZE = 0x200; - - /// - /// OLE Signature as a byte array - /// - public static readonly byte[] SIGNATURE_BYTES = { 0xd0, 0xcf, 0x11, 0xe0, 0xa1, 0xb1, 0x1a, 0xe1 }; - - /// - /// OLE Signature as an unsigned Int64 - /// - 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 - - /// - /// MS-OLE header signature - /// - /// 0x00 - public byte[] SIGNATURE { get; set; } - - /// - /// See ReadClassStg - /// - /// 0x08 - public byte[] CLSID { get; set; } - - /// - /// 0x33 and 0x3E have been seen - /// - /// 0x18 - public ushort MINOR_VER { get; set; } - - /// - /// 0x03 been seen in wild - /// - /// 0x1A - public ushort MAJOR_VER { get; set; } - - /// - /// 0xFE 0xFF == Intel Little Endian - /// - /// 0x1C - public ushort BYTE_ORDER { get; set; } - - /// - /// Number of bits to shift to get a big block size - /// - /// 0x1E - public ushort BB_SHIFT { get; set; } - - /// - /// Number of bits to shift to get a small block size - /// - /// 0x20 - public ushort SB_SHIFT { get; set; } - - /// - /// 0x22..0x27 reserved == 0 - /// - /// 0x22 - public byte[] RESERVED { get; set; } - - /// 0x28 - public uint CSECTDIR { get; set; } - - /// - /// Reported number of big block BATs in the file - /// - /// 0x2C - public uint NUM_BAT { get; set; } - - /// - /// Directory entry start offset - /// - /// 0x30 - public uint DIRENT_START { get; set; } - - /// - /// 0x34..0x37 transacting signature must be 0 - /// - /// 0x34 - public uint TRANSACTING_SIGNATURE { get; set; } - - /// - /// File size transition between small and big blocks - /// - /// 0x38 - public uint THRESHOLD { get; set; } - - /// - /// Offset where small block BATs start - /// - /// 0x3C - public uint SBAT_START { get; set; } - - /// - /// Reported number of small block BATs in the file - /// - /// 0x40 - public uint NUM_SBAT { get; set; } - - /// - /// Block ID of the first MetaBAT block - /// - /// 0x44 - public uint METABAT_BLOCK { get; set; } - - /// - /// Number of MetaBAT blocks in the file - /// - /// 0x48 - public uint NUM_METABAT { get; set; } - - /// - /// Block ID of the first BAT block - /// - /// 0x4C - public uint START_BAT { get; set; } - - #endregion - - #region Derived Properties - - /// - /// Indicate if the contents are Intel Little-Endian - /// - public bool LITTLE_ENDIAN => BYTE_ORDER == 0xFFFE; - - /// - /// Size of a big block, in bytes - /// - public int BB_SIZE => 1 << BB_SHIFT; - - public int BB_FILTER => BB_SIZE << 1; - - /// - /// Size of a small block, in bytes - /// - public int SB_SIZE => 1 << SB_SHIFT; - - public int SB_FILTER => SB_SIZE << 1; - - #endregion - - #region Constructor - - /// - /// Private constructor - /// - private MSOleHeader() { } - - /// - /// Create a new default MSOleHeader - /// - 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; - } - - /// - /// Create a new MSOleHeader from data - /// - 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; - } - - /// - /// Write to data from an existing MSOleHeader - /// - 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 - } - - /// - /// MS-OLE Directory Entry (.msi) - /// - internal class MSOleDirectoryEntry - { - #region Constants - - /// - /// Maximum length of the entry name including the null terminator, in bytes - /// - public const int DIRENT_MAX_NAME_SIZE = 0x40; - - /// - /// Size of the non-name parts of directory entry header, in bytes - /// - public const int DIRENT_DETAILS_SIZE = 0x40; - - /// - /// Total size of the directory entry header - /// - public const int DIRENT_SIZE = (DIRENT_MAX_NAME_SIZE + DIRENT_DETAILS_SIZE); - - /// - /// Magic number indicating the termination of a node - /// - 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 - - /// - /// Name of the entry as a byte array - /// - /// 0x00 - public byte[] NAME { get; set; } - - /// - /// Length of the entry name including the null terminator, in bytes - /// - /// 0x40 - public ushort NAME_LEN { get; set; } - - /// - /// Indicates the type of directory entry - /// - /// 0x42 - public DIRENT_TYPE TYPE_FLAG { get; set; } - - /// 0x43 - public byte COLOR { get; set; } - - /// - /// Index of the previous item in the linked list, DIRENT_MAGIC_END if none - /// - /// 0x44 - public uint PREV { get; set; } - - /// - /// Index of the next item in the linked list, DIRENT_MAGIC_END if none - /// - /// 0x48 - public uint NEXT { get; set; } - - /// - /// Index of the first child directory entry, DIRENT_MAGIC_END if none - /// - /// 0x4C - public uint CHILD { get; set; } - - /// - /// 16 byte GUID used by some apps; Only for dirs - /// - /// 0x50 - public byte[] CLSID { get; set; } - - /// - /// Only for dirs - /// - /// 0x60 - public uint USERFLAGS { get; set; } - - /// - /// Entry creation time; For files - /// - /// 0x64 - public ulong CREATE_TIME { get; set; } - - /// - /// Entry modification time; For files - /// - /// 0x6C - public ulong MODIFY_TIME { get; set; } - - /// - /// Index of the first block that contains this directory entry - /// - /// 0x74 - public uint FIRSTBLOCK { get; set; } - - /// - /// Size of the file contained within this directory entry - /// - /// 0x78 - public uint FILE_SIZE { get; set; } - - /// - /// 0x7c..0x7f reserved == 0 - /// - /// 0x7C - public uint RESERVED { get; set; } - - #endregion - - #region Derived Properties - - /// - /// Directory Entry name as a UTF-8 encoded string - /// - 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'); - } - } - } - - /// - /// Last modified time as a nullable DateTime - /// - public DateTime? MODIFY_DATETIME => MODIFY_TIME == 0 ? (DateTime?)null : DateTime.FromFileTime((long)MODIFY_TIME); - - /// - /// Determine if this entry is a directory - /// - public bool IS_DIRECTORY => TYPE_FLAG != DIRENT_TYPE.DIRENT_TYPE_FILE; - - #endregion - - #region Constructor - - /// - /// Private constructor - /// - private MSOleDirectoryEntry() { } - - /// - /// Create a new default MSOleDirectoryEntry - /// - public static MSOleDirectoryEntry CreateDefault() - { - // TODO: Figure out if there are any sane defaults - return new MSOleDirectoryEntry(); - } - - /// - /// Create a new MSOleDirectoryEntry from data - /// - 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; - } - - /// - /// Write to data from an existing MSOleHeader - /// - 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 -} diff --git a/BurnOutSharp/External/libgsf/GsfMSOleUtils.cs b/BurnOutSharp/External/libgsf/GsfMSOleUtils.cs deleted file mode 100644 index a75f8892..00000000 --- a/BurnOutSharp/External/libgsf/GsfMSOleUtils.cs +++ /dev/null @@ -1,2433 +0,0 @@ -/* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ -/* - * gsf-MSOLE-utils.c: - * - * Copyright (C) 2002-2006 Jody Goldberg (jody@gnome.org) - * Copyright (C) 2002-2006 Dom Lachowicz (cinamod@hotmail.com) - * excel_iconv* family of functions (C) 2001 by Vlad Harchev - * - * 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 LibGSF.Input; -using LibGSF.Output; -using static LibGSF.GsfMetaNames; -using static LibGSF.GsfUtils; - -namespace LibGSF -{ - #region Enums - - public enum GsfMSOLEMetaDataType - { - /// - /// In either summary or docsummary - /// - COMMON_PROP, - - /// - /// SummaryInformation properties - /// - COMPONENT_PROP, - - /// - /// DocumentSummaryInformation properties - /// - DOC_PROP, - - USER_PROP - } - - [Flags] - public enum GsfMSOLEVariantType - { - VT_EMPTY = 0, - VT_NULL = 1, - VT_I2 = 2, - VT_I4 = 3, - VT_R4 = 4, - VT_R8 = 5, - VT_CY = 6, - VT_DATE = 7, - VT_BSTR = 8, - VT_DISPATCH = 9, - VT_ERROR = 10, - VT_BOOL = 11, - VT_VARIANT = 12, - VT_UNKNOWN = 13, - VT_DECIMAL = 14, - - VT_I1 = 16, - VT_UI1 = 17, - VT_UI2 = 18, - VT_UI4 = 19, - VT_I8 = 20, - VT_UI8 = 21, - VT_INT = 22, - VT_UINT = 23, - VT_VOID = 24, - VT_HRESULT = 25, - VT_PTR = 26, - VT_SAFEARRAY = 27, - VT_CARRAY = 28, - VT_USERDEFINED = 29, - VT_LPSTR = 30, - VT_LPWSTR = 31, - - VT_FILETIME = 64, - VT_BLOB = 65, - VT_STREAM = 66, - VT_STORAGE = 67, - VT_STREAMED_OBJECT = 68, - VT_STORED_OBJECT = 69, - VT_BLOB_OBJECT = 70, - VT_CF = 71, - VT_CLSID = 72, - VT_VECTOR = 0x1000 - } - - #endregion - - #region Classes - - public static class GsfMSOleUtils - { - #region Constants - - /// - /// The Format Identifier for Summary Information - /// F29F85E0-4FF9-1068-AB91-08002B27B3D9 - /// - public static readonly byte[] ComponentGUID = - { - 0xe0, 0x85, 0x9f, 0xf2, 0xf9, 0x4f, 0x68, 0x10, - 0xab, 0x91, 0x08, 0x00, 0x2b, 0x27, 0xb3, 0xd9 - }; - - /// - /// The Format Identifier for Document Summary Information - /// D5CDD502-2E9C-101B-9397-08002B2CF9AE - /// - public static readonly byte[] DocumentGUID = - { - 0x02, 0xd5, 0xcd, 0xd5, 0x9c, 0x2e, 0x1b, 0x10, - 0x93, 0x97, 0x08, 0x00, 0x2b, 0x2c, 0xf9, 0xae - }; - - /// - /// The Format Identifier for User-Defined Properties - /// D5CDD505-2E9C-101B-9397-08002B2CF9AE - /// - public static readonly byte[] UserGUID = - { - 0x05, 0xd5, 0xcd, 0xd5, 0x9c, 0x2e, 0x1b, 0x10, - 0x93, 0x97, 0x08, 0x00, 0x2b, 0x2c, 0xf9, 0xae - }; - - public static readonly GsfMSOleMetaDataPropMap[] BuiltInProperties = - { - new GsfMSOleMetaDataPropMap - { - MsName = "Dictionary", - Section = GsfMSOLEMetaDataType.COMMON_PROP, - GsfName = GSF_META_NAME_DICTIONARY, - Id = 0, - PreferredType = 0, // Magic - }, - new GsfMSOleMetaDataPropMap - { - MsName = "CodePage", - Section = GsfMSOLEMetaDataType.COMMON_PROP, - GsfName = GSF_META_NAME_CODEPAGE, - Id = 1, - PreferredType = GsfMSOLEVariantType.VT_I2 - }, - new GsfMSOleMetaDataPropMap - { - MsName = "LOCALE_SYSTEM_DEFAULT", - Section = GsfMSOLEMetaDataType.COMMON_PROP, - GsfName = GSF_META_NAME_LOCALE_SYSTEM_DEFAULT, - Id = 0x80000000, - PreferredType = GsfMSOLEVariantType.VT_UI4 - }, - new GsfMSOleMetaDataPropMap - { - MsName = "CASE_SENSITIVE", - Section = GsfMSOLEMetaDataType.COMMON_PROP, - GsfName = GSF_META_NAME_CASE_SENSITIVE, - Id = 0x80000003, - PreferredType = GsfMSOLEVariantType.VT_UI4 - }, - new GsfMSOleMetaDataPropMap - { - MsName = "Category", - Section = GsfMSOLEMetaDataType.DOC_PROP, - GsfName = GSF_META_NAME_CATEGORY, - Id = 2, - PreferredType = GsfMSOLEVariantType.VT_LPSTR - }, - new GsfMSOleMetaDataPropMap - { - MsName = "PresentationFormat", - Section = GsfMSOLEMetaDataType.DOC_PROP, - GsfName = GSF_META_NAME_PRESENTATION_FORMAT, - Id = 3, - PreferredType = GsfMSOLEVariantType.VT_LPSTR - }, - new GsfMSOleMetaDataPropMap - { - MsName = "NumBytes", - Section = GsfMSOLEMetaDataType.DOC_PROP, - GsfName = GSF_META_NAME_BYTE_COUNT, - Id = 4, - PreferredType = GsfMSOLEVariantType.VT_I4 - }, - new GsfMSOleMetaDataPropMap - { - MsName = "NumLines", - Section = GsfMSOLEMetaDataType.DOC_PROP, - GsfName = GSF_META_NAME_LINE_COUNT, - Id = 5, - PreferredType = GsfMSOLEVariantType.VT_I4 - }, - new GsfMSOleMetaDataPropMap - { - MsName = "NumParagraphs", - Section = GsfMSOLEMetaDataType.DOC_PROP, - GsfName = GSF_META_NAME_PARAGRAPH_COUNT, - Id = 6, - PreferredType = GsfMSOLEVariantType.VT_I4 - }, - new GsfMSOleMetaDataPropMap - { - MsName = "NumSlides", - Section = GsfMSOLEMetaDataType.DOC_PROP, - GsfName = GSF_META_NAME_SLIDE_COUNT, - Id = 7, - PreferredType = GsfMSOLEVariantType.VT_I4 - }, - new GsfMSOleMetaDataPropMap - { - MsName = "NumNotes", - Section = GsfMSOLEMetaDataType.DOC_PROP, - GsfName = GSF_META_NAME_NOTE_COUNT, - Id = 8, - PreferredType = GsfMSOLEVariantType.VT_I4 - }, - new GsfMSOleMetaDataPropMap - { - MsName = "NumHiddenSlides", - Section = GsfMSOLEMetaDataType.DOC_PROP, - GsfName = GSF_META_NAME_HIDDEN_SLIDE_COUNT, - Id = 9, - PreferredType = GsfMSOLEVariantType.VT_I4 - }, - new GsfMSOleMetaDataPropMap - { - MsName = "NumMMClips", - Section = GsfMSOLEMetaDataType.DOC_PROP, - GsfName = GSF_META_NAME_MM_CLIP_COUNT, - Id = 10, - PreferredType = GsfMSOLEVariantType.VT_I4 - }, - new GsfMSOleMetaDataPropMap - { - MsName = "Scale", - Section = GsfMSOLEMetaDataType.DOC_PROP, - GsfName = GSF_META_NAME_SCALE, - Id = 11, - PreferredType = GsfMSOLEVariantType.VT_BOOL - }, - new GsfMSOleMetaDataPropMap - { - MsName = "HeadingPairs", - Section = GsfMSOLEMetaDataType.DOC_PROP, - GsfName = GSF_META_NAME_HEADING_PAIRS, - Id = 12, - PreferredType = GsfMSOLEVariantType.VT_VECTOR | GsfMSOLEVariantType.VT_VARIANT - }, - new GsfMSOleMetaDataPropMap - { - MsName = "DocumentParts", - Section = GsfMSOLEMetaDataType.DOC_PROP, - GsfName = GSF_META_NAME_DOCUMENT_PARTS, - Id = 13, - PreferredType = GsfMSOLEVariantType.VT_VECTOR | GsfMSOLEVariantType.VT_LPSTR - }, - new GsfMSOleMetaDataPropMap - { - MsName = "Manager", - Section = GsfMSOLEMetaDataType.DOC_PROP, - GsfName = GSF_META_NAME_MANAGER, - Id = 14, - PreferredType = GsfMSOLEVariantType.VT_LPSTR - }, - new GsfMSOleMetaDataPropMap - { - MsName = "Company", - Section = GsfMSOLEMetaDataType.DOC_PROP, - GsfName = GSF_META_NAME_COMPANY, - Id = 15, - PreferredType = GsfMSOLEVariantType.VT_LPSTR - }, - new GsfMSOleMetaDataPropMap - { - MsName = "LinksDirty", - Section = GsfMSOLEMetaDataType.DOC_PROP, - GsfName = GSF_META_NAME_LINKS_DIRTY, - Id = 16, - PreferredType = GsfMSOLEVariantType.VT_BOOL - }, - - // Possible match: { 0x0011, 0x0003, "PIDDSI_CCHWITHSPACES", "Number of characters with white-space" }, - new GsfMSOleMetaDataPropMap - { - MsName = "DocSumInfo_17", - Section = GsfMSOLEMetaDataType.DOC_PROP, - GsfName = GSF_META_NAME_MSOLE_UNKNOWN_17, - Id = 17, - PreferredType = GsfMSOLEVariantType.VT_UNKNOWN - }, - new GsfMSOleMetaDataPropMap - { - MsName = "DocSumInfo_18", - Section = GsfMSOLEMetaDataType.DOC_PROP, - GsfName = GSF_META_NAME_MSOLE_UNKNOWN_18, - Id = 18, - PreferredType = GsfMSOLEVariantType.VT_UNKNOWN - }, - - // Possible match: { 0x0013, 0x000b, "PIDDSI_SHAREDDOC", "Shared document" }, - new GsfMSOleMetaDataPropMap - { - MsName = "DocSumInfo_19", - Section = GsfMSOLEMetaDataType.DOC_PROP, - GsfName = GSF_META_NAME_MSOLE_UNKNOWN_19, - Id = 19, - PreferredType = GsfMSOLEVariantType.VT_BOOL - }, - - // Possible match: + PIDDSI_LINKBASE = 0x0014 - new GsfMSOleMetaDataPropMap - { - MsName = "DocSumInfo_20", - Section = GsfMSOLEMetaDataType.DOC_PROP, - GsfName = GSF_META_NAME_MSOLE_UNKNOWN_20, - Id = 20, - PreferredType = GsfMSOLEVariantType.VT_UNKNOWN - }, - - // Possible match: + PIDDSI_HLINKS= 0x0015, - new GsfMSOleMetaDataPropMap - { - MsName = "DocSumInfo_21", - Section = GsfMSOLEMetaDataType.DOC_PROP, - GsfName = GSF_META_NAME_MSOLE_UNKNOWN_21, - Id = 21, - PreferredType = GsfMSOLEVariantType.VT_UNKNOWN - }, - - // Possible match: { 0x0016, 0x000b, "PIDDSI_HYPERLINKSCHANGED", "Hyper links changed" }, - new GsfMSOleMetaDataPropMap - { - MsName = "DocSumInfo_22", - Section = GsfMSOLEMetaDataType.DOC_PROP, - GsfName = GSF_META_NAME_MSOLE_UNKNOWN_22, - Id = 22, - PreferredType = GsfMSOLEVariantType.VT_BOOL - }, - - // Possible match: { 0x0017, 0x0003, "PIDDSI_VERSION", "Creating application version" }, - new GsfMSOleMetaDataPropMap - { - MsName = "DocSumInfo_23", - Section = GsfMSOLEMetaDataType.DOC_PROP, - GsfName = GSF_META_NAME_MSOLE_UNKNOWN_23, - Id = 23, - PreferredType = GsfMSOLEVariantType.VT_I4 - }, - new GsfMSOleMetaDataPropMap - { - MsName = "Title", - Section = GsfMSOLEMetaDataType.COMPONENT_PROP, - GsfName = GSF_META_NAME_TITLE, - Id = 2, - PreferredType = GsfMSOLEVariantType.VT_LPSTR - }, - new GsfMSOleMetaDataPropMap - { - MsName = "Subject", - Section = GsfMSOLEMetaDataType.COMPONENT_PROP, - GsfName = GSF_META_NAME_SUBJECT, - Id = 3, - PreferredType = GsfMSOLEVariantType.VT_LPSTR - }, - new GsfMSOleMetaDataPropMap - { - MsName = "Author", - Section = GsfMSOLEMetaDataType.COMPONENT_PROP, - GsfName = GSF_META_NAME_CREATOR, - Id = 4, - PreferredType = GsfMSOLEVariantType.VT_LPSTR - }, - new GsfMSOleMetaDataPropMap - { - MsName = "Keywords", - Section = GsfMSOLEMetaDataType.COMPONENT_PROP, - GsfName = GSF_META_NAME_KEYWORDS, - Id = 5, - PreferredType = GsfMSOLEVariantType.VT_LPSTR - }, - new GsfMSOleMetaDataPropMap - { - MsName = "Comments", - Section = GsfMSOLEMetaDataType.COMPONENT_PROP, - GsfName = GSF_META_NAME_DESCRIPTION, - Id = 6, - PreferredType = GsfMSOLEVariantType.VT_LPSTR - }, - new GsfMSOleMetaDataPropMap - { - MsName = "Template", - Section = GsfMSOLEMetaDataType.COMPONENT_PROP, - GsfName = GSF_META_NAME_TEMPLATE, - Id = 7, - PreferredType = GsfMSOLEVariantType.VT_LPSTR - }, - new GsfMSOleMetaDataPropMap - { - MsName = "LastSavedBy", - Section = GsfMSOLEMetaDataType.COMPONENT_PROP, - GsfName = GSF_META_NAME_LAST_SAVED_BY, - Id = 8, - PreferredType = GsfMSOLEVariantType.VT_LPSTR - }, - new GsfMSOleMetaDataPropMap - { - MsName = "RevisionNumber", - Section = GsfMSOLEMetaDataType.COMPONENT_PROP, - GsfName = GSF_META_NAME_REVISION_COUNT, - Id = 9, - PreferredType = GsfMSOLEVariantType.VT_LPSTR - }, - new GsfMSOleMetaDataPropMap - { - MsName = "TotalEditingTime", - Section = GsfMSOLEMetaDataType.COMPONENT_PROP, - GsfName = GSF_META_NAME_EDITING_DURATION, - Id = 10, - PreferredType = GsfMSOLEVariantType.VT_FILETIME - }, - new GsfMSOleMetaDataPropMap - { - MsName = "LastPrinted", - Section = GsfMSOLEMetaDataType.COMPONENT_PROP, - GsfName = GSF_META_NAME_LAST_PRINTED, - Id = 11, - PreferredType = GsfMSOLEVariantType.VT_FILETIME - }, - new GsfMSOleMetaDataPropMap - { - MsName = "CreateTime", - Section = GsfMSOLEMetaDataType.COMPONENT_PROP, - GsfName = GSF_META_NAME_DATE_CREATED, - Id = 12, - PreferredType = GsfMSOLEVariantType.VT_FILETIME - }, - new GsfMSOleMetaDataPropMap - { - MsName = "LastSavedTime", - Section = GsfMSOLEMetaDataType.COMPONENT_PROP, - GsfName = GSF_META_NAME_DATE_MODIFIED, - Id = 13, - PreferredType = GsfMSOLEVariantType.VT_FILETIME - }, - new GsfMSOleMetaDataPropMap - { - MsName = "NumPages", - Section = GsfMSOLEMetaDataType.COMPONENT_PROP, - GsfName = GSF_META_NAME_PAGE_COUNT, - Id = 14, - PreferredType = GsfMSOLEVariantType.VT_I4 - }, - new GsfMSOleMetaDataPropMap - { - MsName = "NumWords", - Section = GsfMSOLEMetaDataType.COMPONENT_PROP, - GsfName = GSF_META_NAME_WORD_COUNT, - Id = 15, - PreferredType = GsfMSOLEVariantType.VT_I4 - }, - new GsfMSOleMetaDataPropMap - { - MsName = "NumCharacters", - Section = GsfMSOLEMetaDataType.COMPONENT_PROP, - GsfName = GSF_META_NAME_CHARACTER_COUNT, - Id = 16, - PreferredType = GsfMSOLEVariantType.VT_I4 - }, - new GsfMSOleMetaDataPropMap - { - MsName = "Thumbnail", - Section = GsfMSOLEMetaDataType.COMPONENT_PROP, - GsfName = GSF_META_NAME_THUMBNAIL, - Id = 17, - PreferredType = GsfMSOLEVariantType.VT_CF - }, - new GsfMSOleMetaDataPropMap - { - MsName = "AppName", - Section = GsfMSOLEMetaDataType.COMPONENT_PROP, - GsfName = GSF_META_NAME_GENERATOR, - Id = 18, - PreferredType = GsfMSOLEVariantType.VT_LPSTR - }, - new GsfMSOleMetaDataPropMap - { - MsName = "Security", - Section = GsfMSOLEMetaDataType.COMPONENT_PROP, - GsfName = GSF_META_NAME_SECURITY, - Id = 19, - PreferredType = GsfMSOLEVariantType.VT_I4 - } - }; - - public static readonly LanguageId[] MSOLELanguageIds = - { - new LanguageId { Tag = "-none-", Id = 0x0000 }, /* none (language neutral) */ - new LanguageId { Tag = "-none-", Id = 0x0400 }, /* none */ - new LanguageId { Tag = "af_ZA", Id = 0x0436 }, /* Afrikaans */ - new LanguageId { Tag = "am", Id = 0x045e }, /* Amharic */ - new LanguageId { Tag = "sq_AL", Id = 0x041c }, /* Albanian */ - new LanguageId { Tag = "ar_SA", Id = 0x0401 }, /* Arabic (Saudi) */ - new LanguageId { Tag = "ar_IQ", Id = 0x0801 }, /* Arabic (Iraq) */ - new LanguageId { Tag = "ar_EG", Id = 0x0c01 }, /* Arabic (Egypt) */ - new LanguageId { Tag = "ar_LY", Id = 0x1001 }, /* Arabic (Libya) */ - new LanguageId { Tag = "ar_DZ", Id = 0x1401 }, /* Arabic (Algeria) */ - new LanguageId { Tag = "ar_MA", Id = 0x1801 }, /* Arabic (Morocco) */ - new LanguageId { Tag = "ar_TN", Id = 0x1c01 }, /* Arabic (Tunisia) */ - new LanguageId { Tag = "ar_OM", Id = 0x2001 }, /* Arabic (Oman) */ - new LanguageId { Tag = "ar_YE", Id = 0x2401 }, /* Arabic (Yemen) */ - new LanguageId { Tag = "ar_SY", Id = 0x2801 }, /* Arabic (Syria) */ - new LanguageId { Tag = "ar_JO", Id = 0x2c01 }, /* Arabic (Jordan) */ - new LanguageId { Tag = "ar_LB", Id = 0x3001 }, /* Arabic (Lebanon) */ - new LanguageId { Tag = "ar_KW", Id = 0x3401 }, /* Arabic (Kuwait) */ - new LanguageId { Tag = "ar_AE", Id = 0x3801 }, /* Arabic (United Arab Emirates) */ - new LanguageId { Tag = "ar_BH", Id = 0x3c01 }, /* Arabic (Bahrain) */ - new LanguageId { Tag = "ar_QA", Id = 0x4001 }, /* Arabic (Qatar) */ - new LanguageId { Tag = "as", Id = 0x044d }, /* Assamese */ - new LanguageId { Tag = "az", Id = 0x042c }, /* Azerbaijani */ - new LanguageId { Tag = "hy_AM", Id = 0x042b }, /* Armenian */ - new LanguageId { Tag = "az", Id = 0x044c }, /* Azeri (Latin) az_ */ - new LanguageId { Tag = "az", Id = 0x082c }, /* Azeri (Cyrillic) az_ */ - new LanguageId { Tag = "eu_ES", Id = 0x042d }, /* Basque */ - new LanguageId { Tag = "be_BY", Id = 0x0423 }, /* Belarussian */ - new LanguageId { Tag = "bn", Id = 0x0445 }, /* Bengali bn_ */ - new LanguageId { Tag = "bg_BG", Id = 0x0402 }, /* Bulgarian */ - new LanguageId { Tag = "ca_ES", Id = 0x0403 }, /* Catalan */ - new LanguageId { Tag = "zh_TW", Id = 0x0404 }, /* Chinese (Taiwan) */ - new LanguageId { Tag = "zh_CN", Id = 0x0804 }, /* Chinese (PRC) */ - new LanguageId { Tag = "zh_HK", Id = 0x0c04 }, /* Chinese (Hong Kong) */ - new LanguageId { Tag = "zh_SG", Id = 0x1004 }, /* Chinese (Singapore) */ - new LanguageId { Tag = "ch_MO", Id = 0x1404 }, /* Chinese (Macau SAR) */ - new LanguageId { Tag = "hr_HR", Id = 0x041a }, /* Croatian */ - new LanguageId { Tag = "cs_CZ", Id = 0x0405 }, /* Czech */ - new LanguageId { Tag = "da_DK", Id = 0x0406 }, /* Danish */ - new LanguageId { Tag = "div", Id = 0x465 }, /* Divehi div_*/ - new LanguageId { Tag = "nl_NL", Id = 0x0413 }, /* Dutch (Netherlands) */ - new LanguageId { Tag = "nl_BE", Id = 0x0813 }, /* Dutch (Belgium) */ - new LanguageId { Tag = "en_US", Id = 0x0409 }, /* English (USA) */ - new LanguageId { Tag = "en_GB", Id = 0x0809 }, /* English (UK) */ - new LanguageId { Tag = "en_AU", Id = 0x0c09 }, /* English (Australia) */ - new LanguageId { Tag = "en_CA", Id = 0x1009 }, /* English (Canada) */ - new LanguageId { Tag = "en_NZ", Id = 0x1409 }, /* English (New Zealand) */ - new LanguageId { Tag = "en_IE", Id = 0x1809 }, /* English (Ireland) */ - new LanguageId { Tag = "en_ZA", Id = 0x1c09 }, /* English (South Africa) */ - new LanguageId { Tag = "en_JM", Id = 0x2009 }, /* English (Jamaica) */ - new LanguageId { Tag = "en", Id = 0x2409 }, /* English (Caribbean) */ - new LanguageId { Tag = "en_BZ", Id = 0x2809 }, /* English (Belize) */ - new LanguageId { Tag = "en_TT", Id = 0x2c09 }, /* English (Trinidad) */ - new LanguageId { Tag = "en_ZW", Id = 0x3009 }, /* English (Zimbabwe) */ - new LanguageId { Tag = "en_PH", Id = 0x3409 }, /* English (Phillipines) */ - new LanguageId { Tag = "et_EE", Id = 0x0425 }, /* Estonian */ - new LanguageId { Tag = "fo", Id = 0x0438 }, /* Faeroese fo_ */ - new LanguageId { Tag = "fa_IR", Id = 0x0429 }, /* Farsi */ - new LanguageId { Tag = "fi_FI", Id = 0x040b }, /* Finnish */ - new LanguageId { Tag = "fr_FR", Id = 0x040c }, /* French (France) */ - new LanguageId { Tag = "fr_BE", Id = 0x080c }, /* French (Belgium) */ - new LanguageId { Tag = "fr_CA", Id = 0x0c0c }, /* French (Canada) */ - new LanguageId { Tag = "fr_CH", Id = 0x100c }, /* French (Switzerland) */ - new LanguageId { Tag = "fr_LU", Id = 0x140c }, /* French (Luxembourg) */ - new LanguageId { Tag = "fr_MC", Id = 0x180c }, /* French (Monaco) */ - new LanguageId { Tag = "gl", Id = 0x0456 }, /* Galician gl_ */ - new LanguageId { Tag = "ga_IE", Id = 0x083c }, /* Irish Gaelic */ - new LanguageId { Tag = "gd_GB", Id = 0x100c }, /* Scottish Gaelic */ - new LanguageId { Tag = "ka_GE", Id = 0x0437 }, /* Georgian */ - new LanguageId { Tag = "de_DE", Id = 0x0407 }, /* German (Germany) */ - new LanguageId { Tag = "de_CH", Id = 0x0807 }, /* German (Switzerland) */ - new LanguageId { Tag = "de_AT", Id = 0x0c07 }, /* German (Austria) */ - new LanguageId { Tag = "de_LU", Id = 0x1007 }, /* German (Luxembourg) */ - new LanguageId { Tag = "de_LI", Id = 0x1407 }, /* German (Liechtenstein) */ - new LanguageId { Tag = "el_GR", Id = 0x0408 }, /* Greek */ - new LanguageId { Tag = "gu", Id = 0x0447 }, /* Gujarati gu_ */ - new LanguageId { Tag = "ha", Id = 0x0468 }, /* Hausa */ - new LanguageId { Tag = "he_IL", Id = 0x040d }, /* Hebrew */ - new LanguageId { Tag = "hi_IN", Id = 0x0439 }, /* Hindi */ - new LanguageId { Tag = "hu_HU", Id = 0x040e }, /* Hungarian */ - new LanguageId { Tag = "is_IS", Id = 0x040f }, /* Icelandic */ - new LanguageId { Tag = "id_ID", Id = 0x0421 }, /* Indonesian */ - new LanguageId { Tag = "iu", Id = 0x045d }, /* Inkutitut */ - new LanguageId { Tag = "it_IT", Id = 0x0410 }, /* Italian (Italy) */ - new LanguageId { Tag = "it_CH", Id = 0x0810 }, /* Italian (Switzerland) */ - new LanguageId { Tag = "ja_JP", Id = 0x0411}, /* Japanese */ - new LanguageId { Tag = "kn", Id = 0x044b }, /* Kannada kn_ */ - new LanguageId { Tag = "ks", Id = 0x0860 }, /* Kashmiri (India) ks_ */ - new LanguageId { Tag = "kk", Id = 0x043f }, /* Kazakh kk_ */ - new LanguageId { Tag = "kok", Id = 0x0457 }, /* Konkani kok_ */ - new LanguageId { Tag = "ko_KR", Id = 0x0412 }, /* Korean */ - new LanguageId { Tag = "ko", Id = 0x0812 }, /* Korean (Johab) ko_ */ - new LanguageId { Tag = "kir", Id = 0x0440 }, /* Kyrgyz */ - new LanguageId { Tag = "la", Id = 0x0476 }, /* Latin */ - new LanguageId { Tag = "lo", Id = 0x0454 }, /* Laothian */ - new LanguageId { Tag = "lv_LV", Id = 0x0426 }, /* Latvian */ - new LanguageId { Tag = "lt_LT", Id = 0x0427 }, /* Lithuanian */ - new LanguageId { Tag = "lt_LT", Id = 0x0827 }, /* Lithuanian (Classic) */ - new LanguageId { Tag = "mk", Id = 0x042f }, /* FYRO Macedonian */ - new LanguageId { Tag = "my_MY", Id = 0x043e }, /* Malaysian */ - new LanguageId { Tag = "my_BN", Id = 0x083e }, /* Malay Brunei Darussalam */ - new LanguageId { Tag = "ml", Id = 0x044c }, /* Malayalam ml_ */ - new LanguageId { Tag = "mr", Id = 0x044e }, /* Marathi mr_ */ - new LanguageId { Tag = "mt", Id = 0x043a }, /* Maltese */ - new LanguageId { Tag = "mo", Id = 0x0450 }, /* Mongolian */ - new LanguageId { Tag = "ne_NP", Id = 0x0461 }, /* Napali (Nepal) */ - new LanguageId { Tag = "ne_IN", Id = 0x0861 }, /* Nepali (India) */ - new LanguageId { Tag = "nb_NO", Id = 0x0414 }, /* Norwegian (Bokmaal) */ - new LanguageId { Tag = "nn_NO", Id = 0x0814 }, /* Norwegian (Nynorsk) */ - new LanguageId { Tag = "or", Id = 0x0448 }, /* Oriya or_ */ - new LanguageId { Tag = "om", Id = 0x0472 }, /* Oromo (Afan, Galla) */ - new LanguageId { Tag = "pl_PL", Id = 0x0415 }, /* Polish */ - new LanguageId { Tag = "pt_BR", Id = 0x0416 }, /* Portuguese (Brazil) */ - new LanguageId { Tag = "pt_PT", Id = 0x0816 }, /* Portuguese (Portugal) */ - new LanguageId { Tag = "pa", Id = 0x0446 }, /* Punjabi pa_ */ - new LanguageId { Tag = "ps", Id = 0x0463 }, /* Pashto (Pushto) */ - new LanguageId { Tag = "rm", Id = 0x0417 }, /* Rhaeto_Romanic rm_ */ - new LanguageId { Tag = "ro_RO", Id = 0x0418 }, /* Romanian */ - new LanguageId { Tag = "ro_MD", Id = 0x0818 }, /* Romanian (Moldova) */ - new LanguageId { Tag = "ru_RU", Id = 0x0419 }, /* Russian */ - new LanguageId { Tag = "ru_MD", Id = 0x0819 }, /* Russian (Moldova) */ - new LanguageId { Tag = "se", Id = 0x043b }, /* Sami (Lappish) se_ */ - new LanguageId { Tag = "sa", Id = 0x044f }, /* Sanskrit sa_ */ - new LanguageId { Tag = "sr", Id = 0x0c1a }, /* Serbian (Cyrillic) sr_ */ - new LanguageId { Tag = "sr", Id = 0x081a }, /* Serbian (Latin) sr_ */ - new LanguageId { Tag = "sd", Id = 0x0459 }, /* Sindhi sd_ */ - new LanguageId { Tag = "sk_SK", Id = 0x041b }, /* Slovak */ - new LanguageId { Tag = "sl_SI", Id = 0x0424 }, /* Slovenian */ - new LanguageId { Tag = "wen", Id = 0x042e }, /* Sorbian wen_ */ - new LanguageId { Tag = "so", Id = 0x0477 }, /* Somali */ - new LanguageId { Tag = "es_ES", Id = 0x040a }, /* Spanish (Spain, Traditional) */ - new LanguageId { Tag = "es_MX", Id = 0x080a }, /* Spanish (Mexico) */ - new LanguageId { Tag = "es_ES", Id = 0x0c0a }, /* Spanish (Modern) */ - new LanguageId { Tag = "es_GT", Id = 0x100a }, /* Spanish (Guatemala) */ - new LanguageId { Tag = "es_CR", Id = 0x140a }, /* Spanish (Costa Rica) */ - new LanguageId { Tag = "es_PA", Id = 0x180a }, /* Spanish (Panama) */ - new LanguageId { Tag = "es_DO", Id = 0x1c0a }, /* Spanish (Dominican Republic) */ - new LanguageId { Tag = "es_VE", Id = 0x200a }, /* Spanish (Venezuela) */ - new LanguageId { Tag = "es_CO", Id = 0x240a }, /* Spanish (Colombia) */ - new LanguageId { Tag = "es_PE", Id = 0x280a }, /* Spanish (Peru) */ - new LanguageId { Tag = "es_AR", Id = 0x2c0a }, /* Spanish (Argentina) */ - new LanguageId { Tag = "es_EC", Id = 0x300a }, /* Spanish (Ecuador) */ - new LanguageId { Tag = "es_CL", Id = 0x340a }, /* Spanish (Chile) */ - new LanguageId { Tag = "es_UY", Id = 0x380a }, /* Spanish (Uruguay) */ - new LanguageId { Tag = "es_PY", Id = 0x3c0a }, /* Spanish (Paraguay) */ - new LanguageId { Tag = "es_BO", Id = 0x400a }, /* Spanish (Bolivia) */ - new LanguageId { Tag = "es_SV", Id = 0x440a }, /* Spanish (El Salvador) */ - new LanguageId { Tag = "es_HN", Id = 0x480a }, /* Spanish (Honduras) */ - new LanguageId { Tag = "es_NI", Id = 0x4c0a }, /* Spanish (Nicaragua) */ - new LanguageId { Tag = "es_PR", Id = 0x500a }, /* Spanish (Puerto Rico) */ - new LanguageId { Tag = "sx", Id = 0x0430 }, /* Sutu */ - new LanguageId { Tag = "sw", Id = 0x0441 }, /* Swahili (Kiswahili/Kenya) */ - new LanguageId { Tag = "sv_SE", Id = 0x041d }, /* Swedish */ - new LanguageId { Tag = "sv_FI", Id = 0x081d }, /* Swedish (Finland) */ - new LanguageId { Tag = "ta", Id = 0x0449 }, /* Tamil ta_ */ - new LanguageId { Tag = "tt", Id = 0x0444 }, /* Tatar (Tatarstan) tt_ */ - new LanguageId { Tag = "te", Id = 0x044a }, /* Telugu te_ */ - new LanguageId { Tag = "th_TH", Id = 0x041e }, /* Thai */ - new LanguageId { Tag = "ts", Id = 0x0431 }, /* Tsonga ts_ */ - new LanguageId { Tag = "tn", Id = 0x0432 }, /* Tswana tn_ */ - new LanguageId { Tag = "tr_TR", Id = 0x041f }, /* Turkish */ - new LanguageId { Tag = "tl", Id = 0x0464 }, /* Tagalog */ - new LanguageId { Tag = "tg", Id = 0x0428 }, /* Tajik */ - new LanguageId { Tag = "bo", Id = 0x0451 }, /* Tibetan */ - new LanguageId { Tag = "ti", Id = 0x0473 }, /* Tigrinya */ - new LanguageId { Tag = "uk_UA", Id = 0x0422 }, /* Ukrainian */ - new LanguageId { Tag = "ur_PK", Id = 0x0420 }, /* Urdu (Pakistan) */ - new LanguageId { Tag = "ur_IN", Id = 0x0820 }, /* Urdu (India) */ - new LanguageId { Tag = "uz", Id = 0x0443 }, /* Uzbek (Latin) uz_ */ - new LanguageId { Tag = "uz", Id = 0x0843 }, /* Uzbek (Cyrillic) uz_ */ - new LanguageId { Tag = "ven", Id = 0x0433 }, /* Venda ven_ */ - new LanguageId { Tag = "vi_VN", Id = 0x042a }, /* Vietnamese */ - new LanguageId { Tag = "cy_GB", Id = 0x0452 }, /* Welsh */ - new LanguageId { Tag = "xh", Id = 0x0434 }, /* Xhosa xh */ - new LanguageId { Tag = "yi", Id = 0x043d }, /* Yiddish yi_ */ - new LanguageId { Tag = "yo", Id = 0x046a }, /* Yoruba */ - new LanguageId { Tag = "zu", Id = 0x0435 }, /* Zulu zu_ */ - new LanguageId { Tag = "en_US", Id = 0x0800 } /* Default */ - }; - - public const int VBA_COMPRESSION_WINDOW = 4096; - - #endregion - - #region Utilities - - private static Dictionary NameToPropHash = null; - - public static GsfMSOleMetaDataPropMap GsfNameToProp(string name) - { - if (NameToPropHash == null) - { - NameToPropHash = new Dictionary(); - foreach (GsfMSOleMetaDataPropMap prop in GsfMSOleUtils.BuiltInProperties) - { - NameToPropHash[prop.GsfName] = prop; - } - } - - if (NameToPropHash.ContainsKey(name)) - return NameToPropHash[name]; - - return null; - } - - #endregion - } - - public class GsfMSOleMetaDataPropMap - { - public string MsName { get; set; } - - public GsfMSOLEMetaDataType Section { get; set; } - - public string GsfName { get; set; } - - public uint Id { get; set; } - - public GsfMSOLEVariantType PreferredType { get; set; } - } - - public class GsfMSOleMetaDataProp - { - public uint Id { get; set; } - - public long Offset { get; set; } - } - - public class GsfMSOleMetaDataSection - { - #region Properties - - public GsfMSOLEMetaDataType Type { get; set; } - - public long Offset { get; set; } - - public uint Size { get; set; } - - public uint NumProps { get; set; } - - public Encoding IConvHandle { get; set; } - - public uint CharSize { get; set; } - - public Dictionary Dict { get; set; } - - #endregion - - #region Functions - - public object PropertyParse(GsfMSOLEVariantType type, byte[] data, ref int dataPtr, int data_end) - { - // Not valid in a prop set - if (((int)type & ~0x1fff) != 0) - return null; - - object res = null; - string str = null; - uint len; - Exception error; - int bytes_needed = 0; - - type &= (GsfMSOLEVariantType)0xfff; - - bool is_vector = (type & GsfMSOLEVariantType.VT_VECTOR) != 0; - if (is_vector) - { - // A vector is basically an array. If the type associated with - // it is a variant, then each element can have a different - // variant type. Otherwise, each element has the same variant - // type associated with the vector. - - if (!NEED_RECS(4, 1, dataPtr, data_end, ref bytes_needed)) - return null; - - uint n = GSF_LE_GET_GUINT32(data, dataPtr); - dataPtr += bytes_needed; - - int size1 = PropMinSize(type); - if (!NEED_RECS(n, (uint)size1, dataPtr, data_end, ref bytes_needed)) - return null; - - List vector = new List(); - - for (uint i = 0; i < n; i++) - { - int data0 = dataPtr; - object v = PropertyParse(type, data, ref dataPtr, data_end); - if (v != null) - vector.Add(v as GsfDocProp); - - if (dataPtr == data0) - break; - } - - return vector; - } - - res = new object(); - switch (type) - { - // A property with a type indicator of VT_EMPTY has no data - // associated with it; that is, the size of the value is zero. - case GsfMSOLEVariantType.VT_EMPTY: - // value::unset == empty - break; - - // This is like a pointer to null - case GsfMSOLEVariantType.VT_NULL: - // value::unset == null too :-) do we need to distinguish ? - break; - - // 2-byte signed integer - case GsfMSOLEVariantType.VT_I2: - if (!NEED_RECS(2, 1, dataPtr, data_end, ref bytes_needed)) - return null; - - res = GSF_LE_GET_GINT16(data, dataPtr); - dataPtr += bytes_needed; - break; - - // 4-byte signed integer - case GsfMSOLEVariantType.VT_I4: - if (!NEED_RECS(4, 1, dataPtr, data_end, ref bytes_needed)) - return null; - - res = GSF_LE_GET_GINT32(data, dataPtr); - dataPtr += bytes_needed; - break; - - // 32-bit IEEE floating-point value - case GsfMSOLEVariantType.VT_R4: - if (!NEED_RECS(4, 1, dataPtr, data_end, ref bytes_needed)) - return null; - - res = GSF_LE_GET_FLOAT(data, dataPtr); - dataPtr += bytes_needed; - break; - - // 64-bit IEEE floating-point value - case GsfMSOLEVariantType.VT_R8: - if (!NEED_RECS(8, 1, dataPtr, data_end, ref bytes_needed)) - return null; - - res = GSF_LE_GET_DOUBLE(data, dataPtr); - dataPtr += bytes_needed; - break; - - // 8-byte two's complement integer (scaled by 10,000) - case GsfMSOLEVariantType.VT_CY: - if (!NEED_RECS(8, 1, dataPtr, data_end, ref bytes_needed)) - return null; - - // CHEAT : just store as an int64 for now - res = GSF_LE_GET_GINT64(data, dataPtr); - break; - - // 64-bit floating-point number representing the number of days - // (not seconds) since December 31, 1899. - case GsfMSOLEVariantType.VT_DATE: - if (!NEED_RECS(8, 1, dataPtr, data_end, ref bytes_needed)) - return null; - - dataPtr += bytes_needed; - break; - - // Pointer to null-terminated Unicode string; the string is pre- - // ceeded by a DWORD representing the byte count of the number - // of bytes in the string (including the terminating null). - case GsfMSOLEVariantType.VT_BSTR: - if (!NEED_RECS(4, 1, dataPtr, data_end, ref bytes_needed)) - return null; - - dataPtr += bytes_needed; - break; - - case GsfMSOLEVariantType.VT_DISPATCH: - break; - - // A boolean (WORD) value containg 0 (false) or -1 (true). - case GsfMSOLEVariantType.VT_BOOL: - if (!NEED_RECS(1, 1, dataPtr, data_end, ref bytes_needed)) - return null; - - res = (data[dataPtr] != 0x00); - dataPtr += bytes_needed; - break; - - // A type indicator (a DWORD) followed by the corresponding - // value. VT_VARIANT is only used in conjunction with - // VT_VECTOR. - case GsfMSOLEVariantType.VT_VARIANT: - if (!NEED_RECS(4, 1, dataPtr, data_end, ref bytes_needed)) - return null; - - res = null; - type = (GsfMSOLEVariantType)GSF_LE_GET_GUINT32(data, dataPtr); - dataPtr += bytes_needed; - return PropertyParse(type, data, ref dataPtr, data_end); - - // 1-byte unsigned integer - case GsfMSOLEVariantType.VT_UI1: - if (!NEED_RECS(1, 1, dataPtr, data_end, ref bytes_needed)) - return null; - - res = GSF_LE_GET_GUINT8(data, dataPtr); - dataPtr += bytes_needed; - break; - - // 1-byte signed integer - case GsfMSOLEVariantType.VT_I1: - if (!NEED_RECS(1, 1, dataPtr, data_end, ref bytes_needed)) - return null; - - res = GSF_LE_GET_GINT8(data, dataPtr); - dataPtr += bytes_needed; - break; - - // 2-byte unsigned integer - case GsfMSOLEVariantType.VT_UI2: - if (!NEED_RECS(2, 1, dataPtr, data_end, ref bytes_needed)) - return null; - - res = GSF_LE_GET_GUINT16(data, dataPtr); - dataPtr += bytes_needed; - break; - - // 4-type unsigned integer - case GsfMSOLEVariantType.VT_UI4: - if (!NEED_RECS(4, 1, dataPtr, data_end, ref bytes_needed)) - return null; - - res = GSF_LE_GET_GUINT32(data, dataPtr); - dataPtr += bytes_needed; - break; - - // 8-byte signed integer - case GsfMSOLEVariantType.VT_I8: - if (!NEED_RECS(8, 1, dataPtr, data_end, ref bytes_needed)) - return null; - - res = GSF_LE_GET_GINT64(data, dataPtr); - dataPtr += bytes_needed; - break; - - // 8-byte unsigned integer - case GsfMSOLEVariantType.VT_UI8: - if (!NEED_RECS(8, 1, dataPtr, data_end, ref bytes_needed)) - return null; - - res = GSF_LE_GET_GUINT64(data, dataPtr); - dataPtr += bytes_needed; - break; - - // This is the representation of many strings. It is stored in - // the same representation as VT_BSTR. Note that the serialized - // representation of VP_LPSTR has a preceding byte count, - // whereas the in-memory representation does not. - case GsfMSOLEVariantType.VT_LPSTR: - { - if (!NEED_RECS(4, 1, dataPtr, data_end, ref bytes_needed)) - return null; - - len = GSF_LE_GET_GUINT32(data, dataPtr); - dataPtr += bytes_needed; - - if (len >= 0x10000) - return null; - - uint need = len; - if (CharSize > 1 && (need & 3) != 0) - need = (uint)((need & ~3) + 4); - - if (!NEED_RECS(need, 1, dataPtr, data_end, ref bytes_needed)) - return null; - - error = null; - - try - { - byte[] lpstrTemp = Encoding.Convert(IConvHandle, Encoding.UTF8, data, dataPtr, (int)(len > CharSize ? len - CharSize : 0)); - str = Encoding.UTF8.GetString(lpstrTemp); - } - catch (Exception ex) - { - error = ex; - } - - res = string.Empty; - if (str != null) - { - res = str; - } - else if (error != null) - { - Console.Error.WriteLine($"Error: {error.Message}"); - error = null; - } - else - { - Console.Error.WriteLine("unknown error converting string property, using blank"); - } - - dataPtr += bytes_needed; - break; - } - - // A counted and null-terminated Unicode string; a DWORD character - // count (where the count includes the terminating null) followed - // by that many Unicode (16-bit) characters. Note that the count - // is character count, not byte count. - case GsfMSOLEVariantType.VT_LPWSTR: - if (!NEED_RECS(4, 1, dataPtr, data_end, ref bytes_needed)) - return null; - - len = GSF_LE_GET_GUINT32(data, dataPtr); - dataPtr += bytes_needed; - - if (!NEED_RECS(len, 2, dataPtr, data_end, ref bytes_needed)) - return null; - - if (len >= 0x10000) - return null; - - error = null; - - try - { - byte[] lpwstrTemp = Encoding.Convert(Encoding.Unicode, Encoding.UTF8, data, dataPtr, (int)len * 2); - str = Encoding.UTF8.GetString(lpwstrTemp); - } - catch (Exception ex) - { - error = ex; - } - - res = string.Empty; - if (str != null) - { - res = str; - } - else if (error != null) - { - Console.Error.WriteLine($"Error: {error.Message}"); - error = null; - } - else - { - Console.Error.WriteLine("unknown error converting string property, using blank"); - } - - dataPtr += bytes_needed; - break; - - // 64-bit FILETIME structure, as defined by Win32. - case GsfMSOLEVariantType.VT_FILETIME: - { - if (!NEED_RECS(8, 1, dataPtr, data_end, ref bytes_needed)) - return null; - - // ft * 100ns since Jan 1 1601 - ulong ft = GSF_LE_GET_GUINT64(data, dataPtr); - res = DateTime.FromFileTime((long)ft); - dataPtr += bytes_needed; - break; - } - - // A DWORD count of bytes, followed by that many bytes of data. - // The byte count does not include the four bytes for the length - // of the count itself: An empty blob would have a count of - // zero, followed by zero bytes. Thus the serialized represen- - // tation of a VT_BLOB is similar to that of a VT_BSTR but does - // not guarantee a null byte at the end of the data. - case GsfMSOLEVariantType.VT_BLOB: - if (!NEED_RECS(4, 1, dataPtr, data_end, ref bytes_needed)) - return null; - - dataPtr += bytes_needed; - res = null; - break; - - // Indicates the value is stored in a stream that is sibling - // to the CONTENTS stream. Following this type indicator is - // data in the format of a serialized VT_LPSTR, which names - // the stream containing the data. - case GsfMSOLEVariantType.VT_STREAM: - res = null; - break; - - // Indicates the value is stored in an IStorage that is - // sibling to the CONTENTS stream. Following this type - // indicator is data in the format of a serialized VT_LPSTR, - // which names the IStorage containing the data. - case GsfMSOLEVariantType.VT_STORAGE: - res = null; - break; - - // Same as VT_STREAM, but indicates that the stream contains a - // serialized object, which is a class ID followed by initiali- - // zation data for the class. - case GsfMSOLEVariantType.VT_STREAMED_OBJECT: - res = null; - break; - - // Same as VT_STORAGE, but indicates that the designated - // IStorage contains a loadable object. - case GsfMSOLEVariantType.VT_STORED_OBJECT: - res = null; - break; - - // Contains a serialized object in the same representation as - // would appear in a VT_STREAMED_OBJECT. That is, following - // the VT_BLOB_OBJECT tag is a DWORD byte count of the - // remaining data (where the byte count does not include the - // size of itself) which is in the format of a class ID - // followed by initialization data for that class - case GsfMSOLEVariantType.VT_BLOB_OBJECT: - res = null; - break; - - case GsfMSOLEVariantType.VT_CF: - error = null; - if (!ParseVariantCF(res, data, ref dataPtr, ref data_end, ref error)) - { - // Suck, we can't propagate the error upwards - if (error != null) - { - Console.Error.WriteLine($"Error: {error.Message}"); - error = null; - } - else - { - Console.Error.WriteLine("Unknown error parsing vt_cf"); - } - - res = null; - } - - break; - - // A class ID (or other GUID) - case GsfMSOLEVariantType.VT_CLSID: - if (!NEED_RECS(16, 1, dataPtr, data_end, ref bytes_needed)) - return null; - - dataPtr += bytes_needed; - res = null; - break; - - // A DWORD containing a status code. - case GsfMSOLEVariantType.VT_ERROR: - - case GsfMSOLEVariantType.VT_UNKNOWN: - case GsfMSOLEVariantType.VT_DECIMAL: - case GsfMSOLEVariantType.VT_INT: - case GsfMSOLEVariantType.VT_UINT: - case GsfMSOLEVariantType.VT_VOID: - case GsfMSOLEVariantType.VT_HRESULT: - case GsfMSOLEVariantType.VT_PTR: - case GsfMSOLEVariantType.VT_SAFEARRAY: - case GsfMSOLEVariantType.VT_CARRAY: - case GsfMSOLEVariantType.VT_USERDEFINED: - Console.Error.WriteLine($"Type {VariantName(type)} (0x{type:x}) is not permitted in property sets"); - res = null; - break; - - default: - res = null; - break; - } - - return res; - } - - public bool PropertyRead(GsfInput input, GsfMSOleMetaDataProp[] props, uint i, GsfDocMetaData accum) - { - if (i >= NumProps) - return false; - - long size = ((i + 1) >= NumProps) ? Size : props[i + 1].Offset; - if (size < props[i].Offset + 4) - return false; - - string name; - object val; - - size -= props[i].Offset; // Includes the type id - - // From now on, size is actually a size. - byte[] data; - if (input.Seek(Offset + props[i].Offset, SeekOrigin.Begin) || (data = input.Read((int)size, null)) == null) - { - Console.Error.WriteLine("Failed to read prop #%d", i); - return false; - } - - int dataPtr = 0; // data[0] - GsfMSOLEVariantType type = (GsfMSOLEVariantType)GSF_LE_GET_GUINT32(data, dataPtr); - dataPtr += 4; - - // Dictionary is magic - if (props[i].Id == 0) - { - uint len, id; - int gslen; - int start = dataPtr; // data[dataPtr] - int end = start + ((int)size - 4); - - if (Dict != null) - return false; - - Dict = new Dictionary(); - - uint n = (uint)type; - for (uint j = 0; j < n; j++) - { - if (end - dataPtr < 8) - return false; - - id = GSF_LE_GET_GUINT32(data, dataPtr); - len = GSF_LE_GET_GUINT32(data, dataPtr + 4); - - if (len >= 0x10000) - return false; - if (len > end - dataPtr + 8) - return false; - - gslen = 0; - - try - { - byte[] convTemp = Encoding.Convert(IConvHandle, Encoding.UTF8, data, dataPtr + 8, (int)(len * CharSize)); - name = Encoding.UTF8.GetString(convTemp); - } - catch - { - name = null; - } - - len = (uint)gslen; - dataPtr += (int)(8 + len); - - Dict[id] = name; - - // MS documentation blows goats ! - // The docs claim there are padding bytes in the dictionary. - // Their examples show padding bytes. - // In reality non-unicode strings do not seem to - // have padding. - if (CharSize != 1 && ((dataPtr - start) % 4) != 0) - dataPtr += 4 - ((dataPtr - start) % 4); - } - } - else - { - bool linked = false; - - name = PropIdToGsf(props[i].Id, ref linked); - val = PropertyParse(type, data, ref dataPtr, (int)(dataPtr + size - 4)); - - if (name != null && val != null) - { - if (linked) - { - GsfDocProp prop = accum.Lookup(name); - if (prop == null) - Console.Error.WriteLine($"Linking property '{(name != null ? name : "")}' before it's value is specified"); - else if (!(val is string)) - Console.Error.WriteLine($"Linking property '{(name != null ? name : "")}' before it's value is specified"); - else - prop.LinkedTo = (val as string); - } - else - { - accum.Insert(name, val); - val = null; - name = null; - } - } - } - - return true; - } - - #endregion - - #region Utilities - - private bool NEED_RECS(uint _n, uint _size1, int dataPtr, int data_end, ref int bytes_needed) - { - bytes_needed = (int)_n; - if (_size1 > 0 && (data_end - dataPtr) / _size1 < bytes_needed) - { - Console.Error.WriteLine("Invalid MS property or file truncated"); - return false; - } - - bytes_needed *= (int)_size1; - return true; - } - - /// - /// Can return errors from gsf_blob_new() and GSF_ERROR_INVALID_DATA - /// - private bool ParseVariantCF(object res, byte[] data, ref int dataPtr, ref int data_end, ref Exception error) - { - /* clipboard size uint sizeof (clipboard format tag) + sizeof (clipboard data) - * clipboard format tag int32 see below - * clipboard data byte[] see below - * - * Clipboard format tag: - * -1 - Windows clipboard format - * -2 - Macintosh clipboard format - * -3 - GUID that contains a format identifier (FMTID) - * >0 - custom clipboard format name plus data (see msdn site below) - * 0 - No data - * - * References: - * http://msdn.microsoft.com/library/default.asp?url=/library/en-us/stg/stg/propvariant.asp - * http://jakarta.apache.org/poi/hpsf/thumbnails.html - * http://linux.com.hk/docs/poi/org/apache/poi/hpsf/Thumbnail.html - * http://sparks.discreet.com/knowledgebase/public/solutions/ExtractThumbnailImg.htm - */ - - // Clipboard size field - - if (data_end < dataPtr + 4) - { - SetErrorMissingData(ref error, "VT_CF", 4, data_end - dataPtr); - return false; - } - - uint clip_size = GSF_LE_GET_GUINT32(data, dataPtr); - - if (clip_size < 4) - { - // Must emcompass int32 format plus data size - error = new Exception($"Corrupt data in the VT_CF property; clipboard data length must be at least 4 bytes, but the data says it only has {clip_size} bytes available."); - return false; - } - - dataPtr += 4; - - // Check clipboard format plus data size - - if (data_end < dataPtr + clip_size) - { - SetErrorMissingData(ref error, "VT_CF", (int)clip_size, data_end - dataPtr); - return false; - } - - GsfClipFormat clip_format = (GsfClipFormat)GSF_LE_GET_GINT32(data, dataPtr); - dataPtr += 4; - - switch (clip_format) - { - case GsfClipFormat.GSF_CLIP_FORMAT_WINDOWS_CLIPBOARD: - case GsfClipFormat.GSF_CLIP_FORMAT_MACINTOSH_CLIPBOARD: - case GsfClipFormat.GSF_CLIP_FORMAT_GUID: - case GsfClipFormat.GSF_CLIP_FORMAT_NO_DATA: - // Everything is ok - break; - - default: - if (clip_format > 0) - clip_format = GsfClipFormat.GSF_CLIP_FORMAT_CLIPBOARD_FORMAT_NAME; - else - clip_format = GsfClipFormat.GSF_CLIP_FORMAT_UNKNOWN; - - break; - } - - int clip_data_size = (int)clip_size - 4; - - GsfBlob blob = GsfBlob.Create(clip_data_size, data, dataPtr, ref error); - - dataPtr += clip_data_size; - - if (blob == null) - return false; - - GsfClipData clip_data = GsfClipData.Create(clip_format, blob); - res = clip_data; - - return true; - } - - private string PropIdToGsf(uint id, ref bool linked) - { - linked = false; - if (Dict != null) - { - if ((id & 0x1000000) != 0) - { - linked = true; - unchecked { id &= (uint)~0x1000000; } - } - - if (Dict.TryGetValue(id, out string res) && res != null) - return res; - } - - GsfMSOleMetaDataPropMap[] map = GsfMSOleUtils.BuiltInProperties; - int i = GsfMSOleUtils.BuiltInProperties.Length; - while (i-- > 0) - { - if (map[i].Id == id && (map[i].Section == GsfMSOLEMetaDataType.COMMON_PROP || map[i].Section == Type)) - return map[i].GsfName; - } - - return null; - } - - /// - /// Return a number no bigger than the number of bytes used for a property - /// value of a given type. The returned number might be too small, but - /// we try to return as big a value as possible. - /// - private int PropMinSize(GsfMSOLEVariantType type) - { - switch (type) - { - case GsfMSOLEVariantType.VT_EMPTY: - case GsfMSOLEVariantType.VT_NULL: - return 0; - - case GsfMSOLEVariantType.VT_BOOL: - case GsfMSOLEVariantType.VT_I1: - case GsfMSOLEVariantType.VT_UI1: - return 1; - - case GsfMSOLEVariantType.VT_I2: - case GsfMSOLEVariantType.VT_UI2: - return 2; - - case GsfMSOLEVariantType.VT_I4: - case GsfMSOLEVariantType.VT_R4: - case GsfMSOLEVariantType.VT_ERROR: - case GsfMSOLEVariantType.VT_VARIANT: - case GsfMSOLEVariantType.VT_UI4: - case GsfMSOLEVariantType.VT_LPSTR: - case GsfMSOLEVariantType.VT_LPWSTR: - case GsfMSOLEVariantType.VT_BLOB: - case GsfMSOLEVariantType.VT_BLOB_OBJECT: - case GsfMSOLEVariantType.VT_CF: - case GsfMSOLEVariantType.VT_VECTOR: - return 4; - - case GsfMSOLEVariantType.VT_BSTR: - return 5; - - case GsfMSOLEVariantType.VT_R8: - case GsfMSOLEVariantType.VT_CY: - case GsfMSOLEVariantType.VT_DATE: - case GsfMSOLEVariantType.VT_I8: - case GsfMSOLEVariantType.VT_UI8: - case GsfMSOLEVariantType.VT_FILETIME: - return 8; - - case GsfMSOLEVariantType.VT_CLSID: - return 16; - - case GsfMSOLEVariantType.VT_DISPATCH: - case GsfMSOLEVariantType.VT_UNKNOWN: - case GsfMSOLEVariantType.VT_DECIMAL: - case GsfMSOLEVariantType.VT_INT: - case GsfMSOLEVariantType.VT_UINT: - case GsfMSOLEVariantType.VT_VOID: - case GsfMSOLEVariantType.VT_HRESULT: - case GsfMSOLEVariantType.VT_PTR: - case GsfMSOLEVariantType.VT_SAFEARRAY: - case GsfMSOLEVariantType.VT_CARRAY: - case GsfMSOLEVariantType.VT_USERDEFINED: - case GsfMSOLEVariantType.VT_STREAM: - case GsfMSOLEVariantType.VT_STORAGE: - case GsfMSOLEVariantType.VT_STREAMED_OBJECT: - case GsfMSOLEVariantType.VT_STORED_OBJECT: - default: - return 0; - } - } - - private void SetErrorMissingData(ref Exception error, string property_name, int size_needed, int size_gotten) - { - error = new Exception($"Missing data when reading the {property_name} property; got {size_needed} bytes, but {size_gotten} bytes at least are needed."); - } - - private string VariantName(GsfMSOLEVariantType type) - { - string[] names = - { - "VT_EMPTY", "VT_null", "VT_I2", "VT_I4", "VT_R4", - "VT_R8", "VT_CY", "VT_DATE", "VT_BSTR", "VT_DISPATCH", - "VT_ERROR", "VT_BOOL", "VT_VARIANT", "VT_UNKNOWN", "VT_DECIMAL", - null, "VT_I1", "VT_UI1", "VT_UI2", "VT_UI4", - "VT_I8", "VT_UI8", "VT_INT", "VT_UINT", "VT_VOID", - "VT_HRESULT", "VT_PTR", "VT_SAFEARRAY", "VT_CARRAY", "VT_USERDEFINED", - "VT_LPSTR", "VT_LPWSTR", - }; - - string[] names2 = - { - "VT_FILETIME", - "VT_BLOB", "VT_STREAM", "VT_STORAGE", "VT_STREAMED_OBJECT", - "VT_STORED_OBJECT", "VT_BLOB_OBJECT", "VT_CF", "VT_CLSID" - }; - - type &= ~GsfMSOLEVariantType.VT_VECTOR; - if (type <= GsfMSOLEVariantType.VT_LPWSTR) - return names[(int)type]; - - if (type < GsfMSOLEVariantType.VT_FILETIME) - return "_UNKNOWN_"; - if (type > GsfMSOLEVariantType.VT_CLSID) - return "_UNKNOWN_"; - - return names2[type - GsfMSOLEVariantType.VT_FILETIME]; - } - - #endregion - } - - public class WritePropState - { - #region Properties - - public GsfOutput Output { get; set; } - - public bool DocNotComponent { get; set; } - - public Dictionary Dict { get; set; } - - public WritePropStatePropList BuiltIn { get; set; } - - public WritePropStatePropList User { get; set; } - - public int CodePage { get; set; } - - public Encoding IConvHandle { get; set; } - - public int CharSize { get; set; } - - #endregion - - #region Functions - - public void GuessCodePage(bool user) - { - List ptr = user ? User.Props : BuiltIn.Props; - uint count = user ? User.Count : BuiltIn.Count; - uint i = 0; - - if (i < count) - { - // CodePage - i++; - } - - if (user && i < count) - { - // Dictionary - i++; - } - - foreach (GsfDocProp prop in ptr) - { - string name = prop.Name; - GuessCodePageString(name); - GuessCodePageProperty(name, prop.Value); - } - } - - public void CountProperties(string name, GsfDocProp prop) - { - GsfMSOleMetaDataPropMap map = GsfMSOleUtils.GsfNameToProp(name); - - // Allocate predefined ids or add it to the dictionary - if (map != null) - { - // Dictionary is handled elsewhere - if (map.Id == 0) - return; - - if (map.Section == (DocNotComponent ? GsfMSOLEMetaDataType.COMPONENT_PROP : GsfMSOLEMetaDataType.DOC_PROP)) - return; - - // CodePage - if (map.Id == 1) - { - object val = prop.Value; - if (val != null && val is int intVal) - CodePage = intVal; - - return; - } - - BuiltIn.Count += (uint)(prop.LinkedTo != null ? 2 : 1); - BuiltIn.Props.Add(prop); - } - - // Keep user props in the document - else if (DocNotComponent) - { - if (Dict == null) - Dict = new Dictionary(); - - Dict[name] = User.Count; - User.Count += (uint)(prop.LinkedTo != null ? 2 : 1); - User.Props.Add(prop); - } - } - - public bool WriteSection(bool user) - { - if (user && Dict == null) - return true; - - List ptr = user ? User.Props : BuiltIn.Props; - uint count = user ? User.Count : BuiltIn.Count; - long baseOffset = Output.CurrentOffset; - - // Skip past the size+count and id/offset pairs - byte[] buf = new byte[8]; - GSF_LE_SET_GUINT32(buf, 0, 0); - for (int j = 0; j < 1 + 1 + 2 * count; j++) - { - Output.Write(4, buf); - } - - object scratch = string.Empty; - - GsfMSOleMetaDataProp[] offsets = new GsfMSOleMetaDataProp[count]; - - // 0) CodePage - uint i = 0; - if (i < count) - { - offsets[0].Id = 1; - offsets[0].Offset = Output.CurrentOffset; - GSF_LE_SET_GUINT32(buf, 0, (uint)GsfMSOLEVariantType.VT_I2); - GSF_LE_SET_GUINT32(buf, 4, (uint)CodePage); - Output.Write(8, buf); - i++; - } - - // 1) Dictionary - if (user && i < count) - { - offsets[1].Id = 0; - offsets[1].Offset = Output.CurrentOffset; - GSF_LE_SET_GUINT32(buf, 4, (uint)Dict.Count); - Output.Write(4, buf); - foreach (var kvp in Dict) - { - WriteDictionaryEntry(kvp.Key, kvp.Value); - } - - i++; - } - - // 2) Props - foreach (GsfDocProp prop in ptr) - { - offsets[i].Offset = Output.CurrentOffset; - string name = prop.Name; - if (user) - { - Dict.TryGetValue(name, out uint tmp); - offsets[i].Id = tmp; - if (offsets[i].Id < 2) - { - Console.Error.WriteLine($"Invalid ID (%d) for custom name '%s'", offsets[i].Id, name); - continue; - } - } - else - { - GsfMSOleMetaDataPropMap map = GsfMSOleUtils.GsfNameToProp(name); - if (map == null) - { - Console.Error.WriteLine("Missing map for built-in property '%s'", name); - continue; - } - - offsets[i].Id = map.Id; - } - - WriteProperty(name, prop.Value, false); - if (prop.LinkedTo != null) - { - i++; - offsets[i].Id = offsets[i - 1].Id | 0x1000000; - offsets[i].Offset = Output.CurrentOffset; - scratch = prop.LinkedTo; - WriteProperty(null, scratch, false); - } - } - - bool warned = false; - while (i < count) - { - if (!warned) - { - warned = true; - Console.Error.WriteLine("Something strange in MetadataWriteSection"); - } - - offsets[i].Id = 0; - offsets[i].Offset = offsets[i - 1].Offset; - i++; - } - - long len = Output.CurrentOffset - baseOffset; - Output.Seek(baseOffset, SeekOrigin.Begin); - GSF_LE_SET_GUINT32(buf, 0, (uint)len); - GSF_LE_SET_GUINT32(buf, 0, count); - Output.Write(8, buf); - - for (i = 0; i < count; i++) - { - GSF_LE_SET_GUINT32(buf, 0, offsets[i].Id); - GSF_LE_SET_GUINT32(buf, 0, (uint)(offsets[i].Offset - baseOffset)); - Output.Write(8, buf); - } - - return Output.Seek(0, SeekOrigin.End); - } - - #endregion - - #region Utilities - - private void GuessCodePageProperty(string name, object value) - { - GsfMSOleMetaDataPropMap map = (name != null) ? GsfMSOleUtils.GsfNameToProp(name) : null; - GsfMSOLEVariantType type = ValueToMSOLEVariant(value, map); - - if (type.HasFlag(GsfMSOLEVariantType.VT_VECTOR)) - { - GsfDocProp[] vector = value as GsfDocProp[]; - int n = vector.Length; - for (int i = 0; i < n; i++) - { - GuessCodePageProperty(null, vector[i]); - } - - return; - } - - switch (type) - { - case GsfMSOLEVariantType.VT_LPSTR: - GuessCodePageString((string)value); - return; - - default: - // Don't care. - return; - } - } - - private void GuessCodePageString(string str) - { - if (CodePage != 0) - return; - - if (str == null) - return; - - // Don't bother with ASCII strings - bool is_ascii = true; - for (int p = 0; p < str.Length && is_ascii; p++) - { - is_ascii = (str[p] & 0x80) == 0; - } - - if (is_ascii) - return; - - try - { - byte[] cstrTemp = IConvHandle.GetBytes(str); - cstrTemp = Encoding.Convert(IConvHandle, Encoding.UTF8, cstrTemp); - string cstr = Encoding.UTF8.GetString(cstrTemp); - } - catch - { - // Conversion failed. Switch to UTF-8 - CodePage = -535; - } - } - - private GsfMSOLEVariantType ValueToMSOLEVariant(object value, GsfMSOleMetaDataPropMap map) - { - if (value == null) - return GsfMSOLEVariantType.VT_EMPTY; - - if (value is bool) - return GsfMSOLEVariantType.VT_BOOL; - else if (value is char) - return GsfMSOLEVariantType.VT_UI1; - else if (value is float) - return GsfMSOLEVariantType.VT_R4; - else if (value is double) - return GsfMSOLEVariantType.VT_R8; - else if (value is string) - return GsfMSOLEVariantType.VT_LPSTR; - else if (value is int) - return (map?.PreferredType == GsfMSOLEVariantType.VT_I2 ? GsfMSOLEVariantType.VT_I2 : GsfMSOLEVariantType.VT_I4); - else if (value is uint) - return (map?.PreferredType == GsfMSOLEVariantType.VT_UI2 ? GsfMSOLEVariantType.VT_UI2 : GsfMSOLEVariantType.VT_UI4); - else if (value is DateTime) - return GsfMSOLEVariantType.VT_FILETIME; - - if (value is List vector) - { - GsfMSOLEVariantType type, tmp; - - if (vector == null) - return GsfMSOLEVariantType.VT_UNKNOWN; - - if (map != null) - { - type = map.PreferredType & (~GsfMSOLEVariantType.VT_VECTOR); - if (type == GsfMSOLEVariantType.VT_VARIANT) - return GsfMSOLEVariantType.VT_VECTOR | GsfMSOLEVariantType.VT_VARIANT; - } - else - { - type = GsfMSOLEVariantType.VT_UNKNOWN; - } - - int n = vector.Count; - for (int i = 0; i < n; i++) - { - tmp = ValueToMSOLEVariant(vector[i], null); - if (type == GsfMSOLEVariantType.VT_UNKNOWN) - type = tmp; - else if (type != tmp) - return GsfMSOLEVariantType.VT_VECTOR | GsfMSOLEVariantType.VT_VARIANT; - } - - return GsfMSOLEVariantType.VT_VECTOR | type; - } - - return GsfMSOLEVariantType.VT_UNKNOWN; - } - - private void WriteDictionaryEntry(string name, uint id) - { - byte[] buf = new byte[4]; - - // TODO: `id` is a pointer not a value - GSF_LE_SET_GUINT32(buf, 0, id); - Output.Write(4, buf); - WriteString(name); - } - - /// True on success - private bool WriteProperty(string name, object value, bool suppress_type) - { - if (value == null) - return false; - - GsfMSOleMetaDataPropMap map = (name != null) ? GsfMSOleUtils.GsfNameToProp(name) : null; - byte[] buf = new byte[8]; - - GsfMSOLEVariantType type = ValueToMSOLEVariant(value, map); - if (!suppress_type) - { - GSF_LE_SET_GUINT32(buf, 0, (uint)type); - Output.Write(4, buf); - } - - if (type.HasFlag(GsfMSOLEVariantType.VT_VECTOR)) - { - GsfDocProp[] vector = value as GsfDocProp[]; - int n = vector.Length; - bool res; - - GSF_LE_SET_GINT32(buf, 0, n); - res = Output.Write(4, buf); - for (int i = 0; i < n; i++) - { - bool suppress = type != (GsfMSOLEVariantType.VT_VECTOR | GsfMSOLEVariantType.VT_VARIANT); - res &= WriteProperty(null, vector[i], suppress); - } - - return res; - } - - switch (type) - { - case GsfMSOLEVariantType.VT_BOOL: - if ((bool)value) - GSF_LE_SET_GUINT32(buf, 0, 0xffffffff); - else - GSF_LE_SET_GINT32(buf, 0, 0); - - return Output.Write(4, buf); - - case GsfMSOLEVariantType.VT_UI1: - GSF_LE_SET_GUINT32(buf, 0, (uint)value); - return Output.Write(4, buf); - - case GsfMSOLEVariantType.VT_I2: - GSF_LE_SET_GINT16(buf, 0, (short)value); - GSF_LE_SET_GUINT16(buf, 2, 0); - return Output.Write(4, buf); - - case GsfMSOLEVariantType.VT_I4: - GSF_LE_SET_GINT32(buf, 0, (int)value); - return Output.Write(4, buf); - - case GsfMSOLEVariantType.VT_UI2: - case GsfMSOLEVariantType.VT_UI4: - GSF_LE_SET_GUINT32(buf, 0, (uint)value); - return Output.Write(4, buf); - - case GsfMSOLEVariantType.VT_R4: - GSF_LE_SET_FLOAT(buf, 0, (float)value); - return Output.Write(4, buf); - - case GsfMSOLEVariantType.VT_R8: - GSF_LE_SET_DOUBLE(buf, 0, (double)value); - return Output.Write(8, buf); - - case GsfMSOLEVariantType.VT_LPSTR: - return WriteString((string)value); - - case GsfMSOLEVariantType.VT_FILETIME: - { - DateTime ts = (DateTime)value; - GSF_LE_SET_GUINT64(buf, 0, (ulong)ts.ToFileTime()); - return Output.Write(8, buf); - } - - default: - break; - } - - Console.Error.WriteLine($"Ignoring property '{(name != null ? name : "")}', how do we export a property of type '{value.GetType()}'"); - return false; - } - - private bool WriteString(string txt) - { - byte[] buf = new byte[4]; - int bytes_written; - bool res; - - if (txt == null) - txt = ""; - - int len = txt.Length; - - byte[] ctxt; - try - { - ctxt = Encoding.Unicode.GetBytes(txt); - ctxt = Encoding.Convert(IConvHandle, Encoding.UTF8, ctxt); - bytes_written = ctxt.Length; - } - catch - { - ctxt = null; - bytes_written = 0; - } - - if (ctxt == null) - { - // See bug #703952 - Console.Error.WriteLine("Failed to write metadata string"); - bytes_written = 0; - } - - // *Bytes*, not characters, including the termination, but not the padding. - GSF_LE_SET_GUINT32(buf, 0, (uint)(bytes_written + CharSize)); - res = Output.Write(4, buf); - - res = res && Output.Write(bytes_written, ctxt); - - GSF_LE_SET_GUINT32(buf, 0, 0); - res = res && Output.Write(CharSize, buf); - - if (CharSize > 1) - { - uint padding = (uint)(4 - (bytes_written + CharSize) % 4); - if (padding < 4) - res = res && Output.Write((int)padding, buf); - } - - return res; - } - - #endregion - } - - public class WritePropStatePropList - { - /// - /// Includes 2nd prop for links - /// - public uint Count { get; set; } - - public List Props { get; set; } - } - - public class LanguageId - { - #region Properties - - public string Tag { get; set; } - - public uint Id { get; set; } - - #endregion - - #region Functions - - /// Language id, i.e., locale name. - /// - /// The LID (Language Identifier) for the input language. - /// If lang is %null, return 0x0400 ("-none-"), and not 0x0000 ("no proofing") - /// - public static uint LanguageIdForLanguage(string lang) - { - if (lang == null) - return 0x0400; // Return -none- - - // Allow lang to match as a prefix (eg fr == fr_FR@euro) - int len = lang.Length; - for (int i = 0; i < GsfMSOleUtils.MSOLELanguageIds.Length; i++) - { - if (lang.Equals(GsfMSOleUtils.MSOLELanguageIds[i].Tag.Substring(0, Math.Min(len, GsfMSOleUtils.MSOLELanguageIds[i].Tag.Length)))) - return GsfMSOleUtils.MSOLELanguageIds[i].Id; - } - - return 0x0400; // Return -none- - } - - /// Numerical language id - /// - /// The xx_YY style string (can be just xx or - /// xxx) for the given LID. If the LID is not found, is set to 0x0400, - /// or is set to 0x0000, will return "-none-" - /// - public static string LanguageForLanguageId(uint lid) - { - for (int i = 0; i < GsfMSOleUtils.MSOLELanguageIds.Length; i++) - { - if (GsfMSOleUtils.MSOLELanguageIds[i].Id == lid) - return GsfMSOleUtils.MSOLELanguageIds[i].Tag; - } - - return "-none-"; // Default - } - - /// Convert the the codepage into an applicable LID - /// Character code page. - public static uint CodePageToLanguageId(int codepage) - { - switch (codepage) - { - case 77: /* MAC_CHARSET */ - return 0xFFF; /* This number is a hack */ - case 128: /* SHIFTJIS_CHARSET */ - return 0x411; /* Japanese */ - case 129: /* HANGEUL_CHARSET */ - return 0x412; /* Korean */ - case 130: /* JOHAB_CHARSET */ - return 0x812; /* Korean (Johab) */ - case 134: /* GB2312_CHARSET - Chinese Simplified */ - return 0x804; /* China PRC - And others!! */ - case 136: /* CHINESEBIG5_CHARSET - Chinese Traditional */ - return 0x404; /* Taiwan - And others!! */ - case 161: /* GREEK_CHARSET */ - return 0x408; /* Greek */ - case 162: /* TURKISH_CHARSET */ - return 0x41f; /* Turkish */ - case 163: /* VIETNAMESE_CHARSET */ - return 0x42a; /* Vietnamese */ - case 177: /* HEBREW_CHARSET */ - return 0x40d; /* Hebrew */ - case 178: /* ARABIC_CHARSET */ - return 0x01; /* Arabic */ - case 186: /* BALTIC_CHARSET */ - return 0x425; /* Estonian - And others!! */ - case 204: /* RUSSIAN_CHARSET */ - return 0x419; /* Russian - And others!! */ - case 222: /* THAI_CHARSET */ - return 0x41e; /* Thai */ - case 238: /* EASTEUROPE_CHARSET */ - return 0x405; /* Czech - And many others!! */ - } - - /* default */ - return 0x0; - } - - /// Numerical language id - /// Our best guess at the codepage for the given language id - public static int LanguageIdToCodePage(uint lid) - { - if (lid == 0x0FFF) /* Macintosh Hack */ - return 0x0FFF; - - switch (lid & 0xff) - { - case 0x01: /* Arabic */ - return 1256; - case 0x02: /* Bulgarian */ - return 1251; - case 0x03: /* Catalan */ - return 1252; - case 0x04: /* Chinese */ - switch (lid) - { - case 0x1004: /* Chinese (Singapore) */ - case 0x0404: /* Chinese (Taiwan) */ - case 0x1404: /* Chinese (Macau SAR) */ - case 0x0c04: /* Chinese (Hong Kong SAR, PRC) */ - return 950; - - case 0x0804: /* Chinese (PRC) */ - return 936; - default: - break; - } - break; - case 0x05: /* Czech */ - return 1250; - case 0x06: /* Danish */ - return 1252; - case 0x07: /* German */ - return 1252; - case 0x08: /* Greek */ - return 1253; - case 0x09: /* English */ - return 1252; - case 0x0a: /* Spanish */ - return 1252; - case 0x0b: /* Finnish */ - return 1252; - case 0x0c: /* French */ - return 1252; - case 0x0d: /* Hebrew */ - return 1255; - case 0x0e: /* Hungarian */ - return 1250; - case 0x0f: /* Icelandic */ - return 1252; - case 0x10: /* Italian */ - return 1252; - case 0x11: /* Japanese */ - return 932; - case 0x12: /* Korean */ - switch (lid) - { - case 0x0812: /* Korean (Johab) */ - return 1361; - case 0x0412: /* Korean */ - return 949; - default: - break; - } - break; - case 0x13: /* Dutch */ - return 1252; - case 0x14: /* Norwegian */ - return 1252; - case 0x15: /* Polish */ - return 1250; - case 0x16: /* Portuguese */ - return 1252; - case 0x17: /* Rhaeto-Romanic */ - return 1252; - case 0x18: /* Romanian */ - return 1250; - case 0x19: /* Russian */ - return 1251; - case 0x1a: /* Serbian, Croatian, (Bosnian?) */ - switch (lid) - { - case 0x041a: /* Croatian */ - return 1252; - case 0x0c1a: /* Serbian (Cyrillic) */ - return 1251; - case 0x081a: /* Serbian (Latin) */ - return 1252; - default: - break; - } - break; - case 0x1b: /* Slovak */ - return 1250; - case 0x1c: /* Albanian */ - return 1251; - case 0x1d: /* Swedish */ - return 1252; - case 0x1e: /* Thai */ - return 874; - case 0x1f: /* Turkish */ - return 1254; - case 0x20: /* Urdu. This is Unicode only. */ - return 0; - case 0x21: /* Bahasa Indonesian */ - return 1252; - case 0x22: /* Ukrainian */ - return 1251; - case 0x23: /* Byelorussian / Belarusian */ - return 1251; - case 0x24: /* Slovenian */ - return 1250; - case 0x25: /* Estonian */ - return 1257; - case 0x26: /* Latvian */ - return 1257; - case 0x27: /* Lithuanian */ - return 1257; - case 0x29: /* Farsi / Persian. This is Unicode only. */ - return 0; - case 0x2a: /* Vietnamese */ - return 1258; - case 0x2b: /* Windows 2000: Armenian. This is Unicode only. */ - return 0; - case 0x2c: /* Azeri */ - switch (lid) - { - case 0x082c: /* Azeri (Cyrillic) */ - return 1251; - default: - break; - } - break; - case 0x2d: /* Basque */ - return 1252; - case 0x2f: /* Macedonian */ - return 1251; - case 0x36: /* Afrikaans */ - return 1252; - case 0x37: /* Windows 2000: Georgian. This is Unicode only. */ - return 0; - case 0x38: /* Faeroese */ - return 1252; - case 0x39: /* Windows 2000: Hindi. This is Unicode only. */ - return 0; - case 0x3E: /* Malaysian / Malay */ - return 1252; - case 0x41: /* Swahili */ - return 1252; - case 0x43: /* Uzbek */ - switch (lid) - { - case 0x0843: /* Uzbek (Cyrillic) */ - return 1251; - default: - break; - } - break; - case 0x45: /* Windows 2000: Bengali. This is Unicode only. */ - case 0x46: /* Windows 2000: Punjabi. This is Unicode only. */ - case 0x47: /* Windows 2000: Gujarati. This is Unicode only. */ - case 0x48: /* Windows 2000: Oriya. This is Unicode only. */ - case 0x49: /* Windows 2000: Tamil. This is Unicode only. */ - case 0x4a: /* Windows 2000: Telugu. This is Unicode only. */ - case 0x4b: /* Windows 2000: Kannada. This is Unicode only. */ - case 0x4c: /* Windows 2000: Malayalam. This is Unicode only. */ - case 0x4d: /* Windows 2000: Assamese. This is Unicode only. */ - case 0x4e: /* Windows 2000: Marathi. This is Unicode only. */ - case 0x4f: /* Windows 2000: Sanskrit. This is Unicode only. */ - case 0x55: /* Myanmar / Burmese. This is Unicode only. */ - case 0x57: /* Windows 2000: Konkani. This is Unicode only. */ - case 0x61: /* Windows 2000: Nepali (India). This is Unicode only. */ - return 0; - -#if TESTING - /****************************************************************** - * Below this line is untested, unproven, and are just guesses. * - * Insert above and use at your own risk * - ******************************************************************/ - - case 0x042c: /* Azeri (Latin) */ - case 0x0443: /* Uzbek (Latin) */ - case 0x30: /* Sutu */ - return 1252; /* UNKNOWN, believed to be CP1252 */ - - case 0x3f: /* Kazakh */ - return 1251; /* JUST UNKNOWN, probably CP1251 */ - - case 0x44: /* Tatar */ - case 0x58: /* Manipuri */ - case 0x59: /* Sindhi */ - case 0x60: /* Kashmiri (India) */ - return 0; /* UNKNOWN, believed to be Unicode only */ -#endif - }; - - // This is just a guess, but it will be a frequent guess - return 1252; - } - - /// Numerical language id - /// The Iconv codepage string for the given LID. - public static string LanguageIdToCodePageString(uint lid) - { - if (lid == 0x0FFF) /* Macintosh Hack */ - return "MACINTOSH"; - - int cp = LanguageIdToCodePage(lid); - return $"CP{cp}"; - } - - public static List GetCodePageStringList(int codepage) - { - List cp_list = new List(); - - switch (codepage) - { - case 1200: - cp_list.Add("UTF-16LE"); - break; - - case 1201: - cp_list.Add("UTF-16BE"); - break; - - case 0x8000: - case 10000: - cp_list.Add("MACROMAN"); - cp_list.Add("MACINTOSH"); - break; - - case -535: - case 65001: - cp_list.Add("UTF-8"); - break; - - case 0x8001: - // According to OOo docs 8001 is a synonym CP1252 - codepage = 1252; - cp_list.Add($"CP{codepage}"); - break; - - default: - cp_list.Add($"CP{codepage}"); - break; - } - - return cp_list; - } - - #endregion - } - - public class GsfMSOleSortingKey - { - #region Properties - - public string Name { get; set; } - - public int Length { get; set; } - - #endregion - - #region Functions - - public static GsfMSOleSortingKey Create(string name) - { - GsfMSOleSortingKey res = new GsfMSOleSortingKey(); - - if (name == null) - name = ""; - - int name_len = name.Length; - - char[] resNameTemp = new char[name_len + 1]; - res.Length = 0; - - // This code is a bit like g_UTF-8_to_utf16. - - for (int p = 0; p < name.Length; p++) - { - char wc = name[p]; - if ((wc & 0x80000000) != 0) - break; // Something invalid or incomplete - - if (wc < 0x10000) - { - wc = char.ToUpper(wc); - - // Let's hope no uppercase char is above 0xffff! - resNameTemp[res.Length++] = wc; - } - else - { - resNameTemp[res.Length++] = (char)((wc - 0x10000) / 0x400 + 0xd800); - resNameTemp[res.Length++] = (char)((wc - 0x10000) % 0x400 + 0xdc00); - } - } - - resNameTemp[res.Length] = '\0'; - res.Name = new string(resNameTemp); - - return res; - } - - public GsfMSOleSortingKey Copy() - { - return new GsfMSOleSortingKey - { - Name = this.Name, - Length = this.Length, - }; - } - - #endregion - } - - #endregion -} diff --git a/BurnOutSharp/External/libgsf/GsfMetaNames.cs b/BurnOutSharp/External/libgsf/GsfMetaNames.cs deleted file mode 100644 index c17d0c7b..00000000 --- a/BurnOutSharp/External/libgsf/GsfMetaNames.cs +++ /dev/null @@ -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 -{ - /// - /// The namespace follow this classification: - /// - /// "dc:" - Dublin Core tags - /// "gsf:" - Gnumeric only tags - /// "meta:" - OpenDocument tags shared with Gnumeric - /// "MSOLE:" - OLE tags - /// - public static class GsfMetaNames - { - #region Namespace - dc - - /// - /// (String) An entity primarily responsible for making the content of the - /// resource typically a person, organization, or service. - /// - /// 1.14.0 Moved from "gsf" to "dc". - public const string GSF_META_NAME_CREATOR = "dc:creator"; - - /// - /// (GsfTimestamp) The last time this document was saved. - /// - /// 1.14.0 Moved from dc:date-modified to dc:date. - public const string GSF_META_NAME_DATE_MODIFIED = "dc:date"; - - /// - /// (String) An account of the content of the resource. - /// - public const string GSF_META_NAME_DESCRIPTION = "dc:description"; - - /// - /// (GsfDocPropVector of String) Searchable, indexable keywords. Similar to PDF - /// keywords or HTML's meta block. - /// - public const string GSF_META_NAME_KEYWORDS = "dc:keywords"; - - /// - /// (String) The locale language of the intellectual content of the resource - /// (basically xx_YY form for us). - /// - /// 1.14.0 Clarified that this is unique from _NAME_CODEPAGE in MSOLE - public const string GSF_META_NAME_LANGUAGE = "dc:language"; - - /// - /// (UnsignedShort) The MS codepage to encode strings for metadata - /// - /// 1.14.0 Clarified that this is unique from _NAME_CODEPAGE in MSOLE - public const string GSF_META_NAME_CODEPAGE = "MSOLE:codepage"; - - /// - /// (String) The topic of the content of the resource, - /// typically including keywords. - /// - public const string GSF_META_NAME_SUBJECT = "dc:subject"; - - /// - /// (String) A formal name given to the resource. - /// - public const string GSF_META_NAME_TITLE = "dc:title"; - - #endregion - - #region Namespace - gsf - - /// - /// (Integer) Count of bytes in the document. - /// - public const string GSF_META_NAME_BYTE_COUNT = "gsf:byte-count"; - - /// - /// (Unsigned Integer) Identifier representing the case-sensitiveness. - /// - /// of what ?? why is it an integer ?? - public const string GSF_META_NAME_CASE_SENSITIVE = "gsf:case-sensitivity"; - - /// - /// (String) Category of the document. - /// - /// example??? - public const string GSF_META_NAME_CATEGORY = "gsf:category"; - - /// - /// (Integer) Count of cells in the spread-sheet document, if appropriate. - /// - public const string GSF_META_NAME_CELL_COUNT = "gsf:cell-count"; - - /// - /// (Integer) Count of characters in the document. - /// - /// TODO See how to sync this with ODF's document-statistic - public const string GSF_META_NAME_CHARACTER_COUNT = "gsf:character-count"; - - /// - /// (None) Reserved name (PID) for Dictionary - /// - public const string GSF_META_NAME_DICTIONARY = "gsf:dictionary"; - - /// - /// (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. - /// - /// From MSOLE - public const string GSF_META_NAME_DOCUMENT_PARTS = "gsf:document-parts"; - - /// - /// (Vector of string value pairs stored in alternating elements) Store the - /// counts of objects in the document as names 'worksheet' and count '4' - /// - /// From MSOLE - public const string GSF_META_NAME_HEADING_PAIRS = "gsf:heading-pairs"; - - /// - /// (Integer) Count of hidden-slides in the presentation document. - /// - public const string GSF_META_NAME_HIDDEN_SLIDE_COUNT = "gsf:hidden-slide-count"; - - /// - /// (Integer) Count of images in the document, if appropriate. - /// - public const string GSF_META_NAME_IMAGE_COUNT = "gsf:image-count"; - - /// - /// (String) The entity that made the last change to the document, typically a - /// person, organization, or service. - /// - public const string GSF_META_NAME_LAST_SAVED_BY = "gsf:last-saved-by"; - - /// - /// (Boolean) ??????? - /// - public const string GSF_META_NAME_LINKS_DIRTY = "gsf:links-dirty"; - - /// - /// (Unsigned Integer) Identifier representing the default system locale. - /// - public const string GSF_META_NAME_LOCALE_SYSTEM_DEFAULT = "gsf:default-locale"; - - /// - /// (String) Name of the manager of "CREATOR" entity. - /// - public const string GSF_META_NAME_MANAGER = "gsf:manager"; - - /// - /// (String) Type of presentation, like "On-screen Show", "SlideView" etc. - /// - public const string GSF_META_NAME_PRESENTATION_FORMAT = "gsf:presentation-format"; - - /// - /// (Boolean) ????? - /// - public const string GSF_META_NAME_SCALE = "gsf:scale"; - - /// - /// (Integer) Level of security. - /// - /// - /// - /// - /// - /// LevelValue - /// - /// - /// None0 - /// Password protected1 - /// Read-only recommended2 - /// Read-only enforced3 - /// Locked for annotations4 - /// - /// - public const string GSF_META_NAME_SECURITY = "gsf:security"; - - /// - /// (GsfClipData) Thumbnail data of the document, typically a - /// preview image of the document. - /// - public const string GSF_META_NAME_THUMBNAIL = "gsf:thumbnail"; - - /// - /// (Integer) Count of liness in the document. - /// - public const string GSF_META_NAME_LINE_COUNT = "gsf:line-count"; - - /// - /// (Integer) Count of "multi-media" clips in the document. - /// - public const string GSF_META_NAME_MM_CLIP_COUNT = "gsf:MM-clip-count"; - - /// - /// (Integer) Count of "notes" in the document. - /// - public const string GSF_META_NAME_NOTE_COUNT = "gsf:note-count"; - - /// - /// (Integer) Count of objects (OLE and other graphics) in the document, if - /// appropriate. - /// - public const string GSF_META_NAME_OBJECT_COUNT = "gsf:object-count"; - - /// - /// (Integer) Count of pages in the document, if appropriate. - /// - public const string GSF_META_NAME_PAGE_COUNT = "gsf:page-count"; - - /// - /// (Integer) Count of paragraphs in the document, if appropriate. - /// - public const string GSF_META_NAME_PARAGRAPH_COUNT = "gsf:paragraph-count"; - - /// - /// (Integer) Count of slides in the presentation document. - /// - public const string GSF_META_NAME_SLIDE_COUNT = "gsf:slide-count"; - - /// - /// (Integer) Count of pages in the document, if appropriate. - /// - public const string GSF_META_NAME_SPREADSHEET_COUNT = "gsf:spreadsheet-count"; - - /// - /// (Integer) Count of tables in the document, if appropriate. - /// - public const string GSF_META_NAME_TABLE_COUNT = "gsf:table-count"; - - /// - /// (Integer) Count of words in the document. - /// - public const string GSF_META_NAME_WORD_COUNT = "gsf:word-count"; - - #endregion - - #region Namespace - MSOLE - - /// - /// (Unknown) User-defined name - /// - public const string GSF_META_NAME_MSOLE_UNKNOWN_17 = "MSOLE:unknown-doc-17"; - - /// - /// (Unknown) User-defined name - /// - public const string GSF_META_NAME_MSOLE_UNKNOWN_18 = "MSOLE:unknown-doc-18"; - - /// - /// (Boolean) User-defined name - /// - public const string GSF_META_NAME_MSOLE_UNKNOWN_19 = "MSOLE:unknown-doc-19"; - - /// - /// (Unknown) User-defined name - /// - public const string GSF_META_NAME_MSOLE_UNKNOWN_20 = "MSOLE:unknown-doc-20"; - - /// - /// (Unknown) User-defined name - /// - public const string GSF_META_NAME_MSOLE_UNKNOWN_21 = "MSOLE:unknown-doc-21"; - - /// - /// (Boolean) User-defined name - /// - public const string GSF_META_NAME_MSOLE_UNKNOWN_22 = "MSOLE:unknown-doc-22"; - - /// - /// (i4) User-defined name - /// - public const string GSF_META_NAME_MSOLE_UNKNOWN_23 = "MSOLE:unknown-doc-23"; - - #endregion - - #region Namespace - meta - - /// - /// (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. - /// - public const string GSF_META_NAME_DATE_CREATED = "meta:creation-date"; - - /// - /// (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. - /// - public const string GSF_META_NAME_EDITING_DURATION = "meta:editing-duration"; - - /// - /// (String) The application that generated this document. AbiWord, Gnumeric, - /// etc... - /// - /// 1.14.0 Moved from "gsf" to "meta". - public const string GSF_META_NAME_GENERATOR = "meta:generator"; - - /// - /// (String) Searchable, indexable keywords. Similar to PDF keywords or HTML's - /// meta block. - /// - public const string GSF_META_NAME_KEYWORD = "meta:keyword"; - - /// - /// (String) Specifies the name of the person who created the document - /// initially. - /// - /// 1.14.0 Moved from "gsf" to "meta". - public const string GSF_META_NAME_INITIAL_CREATOR = "meta:initial-creator"; - - /// - /// (String) Name of the company/organization that the "CREATOR" entity is - /// associated with. - /// - /// 1.14.1 Moved from "gsf:company" to "dc:publisher". - public const string GSF_META_NAME_COMPANY = "dc:publisher"; - - /// - /// (GsfTimestamp) Specifies the date and time when the document was last - /// printed. - /// - public const string GSF_META_NAME_PRINT_DATE = "meta:print-date"; - - /// - /// (GSF_META_NAME_HEADING_PAIRS) The last time this document was printed. - /// - /// - /// 1.14.0 Moved from "gsf" to "dc". - /// 1.14.1 Moved back to "gsf" from "dc". - /// - public const string GSF_META_NAME_LAST_PRINTED = "gsf:last-printed"; - - /// - /// (String) Specifies the name of the last person who printed the document. - /// - /// 1.14.0 Moved from "gsf" to "meta". - public const string GSF_META_NAME_PRINTED_BY = "meta:printed-by"; - - /// - /// (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. - /// - public const string GSF_META_NAME_REVISION_COUNT = "meta:editing-cycles"; - - /// - /// (String) The template file that is been used to generate this document. - /// - /// 1.14.0 Moved from "gsf" to "meta" - public const string GSF_META_NAME_TEMPLATE = "meta:template"; - - #endregion - } -} diff --git a/BurnOutSharp/External/libgsf/GsfOpenDocUtils.cs b/BurnOutSharp/External/libgsf/GsfOpenDocUtils.cs deleted file mode 100644 index 4706faab..00000000 --- a/BurnOutSharp/External/libgsf/GsfOpenDocUtils.cs +++ /dev/null @@ -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 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 - - /// - /// Gives the ODF version used by libgsf when writing Open Document files. - /// - /// The ODF version as a string: "1.2". - public static string GetVersionString() => "1.2"; - - /// - /// Gives the ODF version used by libgsf when writing Open Document files. - /// - /// The ODF version: 102. - 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)); - - /// - /// OD considers this the last person to modify the doc, rather than - /// the DC convention of the person primarilly responsible for its creation - /// - public static void od_meta_creator(this GsfXMLIn xin, GsfXMLBlob blob) => xin.od_get_meta_prop(GSF_META_NAME_CREATOR, typeof(string)); - - /// - /// Last to print - /// - 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)); - - /// - /// OD allows multiple keywords, accumulate things and make it an array - /// - public static void od_meta_keyword(this GsfXMLIn xin, GsfXMLBlob blob) - { - GsfOOMetaIn mi = (GsfOOMetaIn)xin.UserState; - - if (mi.Keywords == null) - mi.Keywords = new List(); - - 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 vector = new List(); - vector.Add(prop); - mi.MetaData.Insert(mi.Name.Substring(true_name), vector); - } - else - { - object old = prop.Value; - if (old is List oldList) - { - List newObj = new List(); - 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 - - /// - /// Generated based on: - /// http://www.oasis-open.org/committees/download.php/12572/OpenDocument-v1.0-os.pdf - /// and OpenDocument-v1.1.pdf - /// - 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); - } - - /// - /// Extend > so that it can parse a subtree in OpenDoc metadata format - /// - 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); - } - - /// - /// Extend so that it can parse a subtree in OpenDoc metadata format - /// The current user_state must be a GsfOOMetaIn! - /// - 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 od_prop_name_map = new Dictionary - { - { 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: - */ - - /// - /// ODF does not like "t" and "f" which we use normally - /// - 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 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 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 kvp in data.Table) - { - meta_write_props(kvp.Key, kvp.Value, xout); - } - } - xout.EndElement(); - - xout.EndElement(); - - return true; - } - - #endregion - } - - #endregion -} diff --git a/BurnOutSharp/External/libgsf/GsfOpenPkgUtils.cs b/BurnOutSharp/External/libgsf/GsfOpenPkgUtils.cs deleted file mode 100644 index 1ac96dad..00000000 --- a/BurnOutSharp/External/libgsf/GsfOpenPkgUtils.cs +++ /dev/null @@ -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 - - /// A new GsfInput which the called needs to unref, or null and sets - 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; - } - - /// - /// Finds 's relation with @id - /// - /// Identifier. - /// A GsfOpenPkgRel or null - /// - /// New in 1.14.6 - /// - /// Skipping because gsf_open_pkg_rel_get_type() does not return a GType. - /// - 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]; - } - - /// - /// Finds _a_ relation of with (no order is guaranteed) - /// - /// Target - /// A GsfOpenPkgRel or null - /// - /// New in 1.14.6 - /// - /// Skipping because gsf_open_pkg_rel_get_type() does not return a GType. - /// - 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 - - /// - /// Generated based on: - /// http://www.oasis-open.org/committees/download.php/12572/OpenDocument-v1.0-os.pdf - /// and OpenDocument-v1.1.pdf - /// - 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 RelationsById { get; set; } = new Dictionary(); - - public Dictionary RelationsByType { get; set; } = new Dictionary(); - - #endregion - - #region Constructor - - /// - /// Private constructor - /// - 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(), - RelationsByType = new Dictionary(), - }; - - 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 Children { get; set; } = null; - - public List Relations { get; set; } = null; - - #endregion - - #region Constructor and Destructor - - /// - /// Private constructor - /// - private GsfOutfileOpenPkg() { } - - /// - /// Convenience routine to create a GsfOutfileOpenPkg inside . - /// - /// - /// A GsfOutfile that the caller is responsible for. - public static GsfOutfileOpenPkg Create(GsfOutfile sink) - { - return new GsfOutfileOpenPkg - { - Sink = sink, - IsDir = true, - }; - } - - /// - /// Destructor - /// - ~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; - } - - /// - /// Create a relationship between child and of . - /// - /// Target type - /// The relID which the caller does not own but will live as long as . - 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); - } - - /// - /// A convenience wrapper to create a child in of then create - /// a relation to - /// - /// Target name - /// Non-null content type - /// Target type - /// The new part. - 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; - } - - /// - /// Add an external relation to parent. - /// - /// Target type - /// Target content - /// - /// The id of the relation. The string is - /// managed by the parent and should not be changed or freed by the - /// caller. - /// - public string AddExternalRelation(string target, string content_type) => CreateRelation(target, content_type, true); - - /// - protected override bool WriteImpl(int num_bytes, byte[] data) => Sink.Write(num_bytes, data); - - /// - protected override bool SeekImpl(long offset, SeekOrigin whence) => Sink.Seek(offset, whence); - - /// - 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 - - /// - /// Walks each relationship associated with and calls with . - /// - /// New in 1.14.9 - 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 rel in rels.RelationsById) - { - ForeachRelationImpl(rel.Key, rel.Value, dat); - } - } - - /// - /// Open @opkg's relation - /// - /// Target id - /// A new GsfInput or null, and sets if possible. - /// New in 1.14.7 - 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; - } - - /// - /// Open one of 's relationships with type=. - /// - /// Target type - /// A new GsfInput or null, and sets if possible. - /// New in 1.14.9 - 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; - } - - /// - /// Convenience function to parse a related part. - /// - /// Target id - /// null on success or an Exception on failure. - 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 - } -} diff --git a/BurnOutSharp/External/libgsf/GsfSharedMemory.cs b/BurnOutSharp/External/libgsf/GsfSharedMemory.cs deleted file mode 100644 index c40089d2..00000000 --- a/BurnOutSharp/External/libgsf/GsfSharedMemory.cs +++ /dev/null @@ -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 - - /// - /// Private constructor - /// - 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; - } - } - - /// - /// Destructor - /// - ~GsfSharedMemory() - { - if (Buf != null) - { - if (NeedsFree) - Buf = null; - //else if (NeedsUnmap) - //UnmapViewOfFile(mem.buf); - } - } - - #endregion - } -} diff --git a/BurnOutSharp/External/libgsf/GsfUtils.cs b/BurnOutSharp/External/libgsf/GsfUtils.cs deleted file mode 100644 index 22378405..00000000 --- a/BurnOutSharp/External/libgsf/GsfUtils.cs +++ /dev/null @@ -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 -{ - /// - /// Do this the ugly way so that we don't have to worry about alignment - /// - internal static class GsfUtils - { - #region GET - - /// - /// Interpret binary data as an unsigned 8-bit integer in little endian order. - /// - /// Storage - /// Pointer to storage - /// Interpreted data - public static byte GSF_LE_GET_GUINT8(byte[] data, int p) - => data[p]; - - /// - /// Interpret binary data as an unsigned 16-bit integer in little endian order. - /// - /// Storage - /// Pointer to storage - /// Interpreted data - public static ushort GSF_LE_GET_GUINT16(byte[] data, int p) - => (ushort)((data[p] << 0) - | (data[p + 1] << 8)); - - /// - /// Interpret binary data as an unsigned 32-bit integer in little endian order. - /// - /// Storage - /// Pointer to storage - /// Interpreted data - 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)); - - /// - /// Interpret binary data as an unsigned 64-bit integer in little endian order. - /// - /// Storage - /// Pointer to storage - /// Interpreted data - 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)); - - /// - /// Interpret binary data as a signed 8-bit integer in little endian order. - /// - /// Storage - /// Pointer to storage - /// Interpreted data - public static sbyte GSF_LE_GET_GINT8(byte[] data, int p) => (sbyte)GSF_LE_GET_GINT8(data, p); - - /// - /// Interpret binary data as a signed 16-bit integer in little endian order. - /// - /// Storage - /// Pointer to storage - /// Interpreted data - public static short GSF_LE_GET_GINT16(byte[] data, int p) => (short)GSF_LE_GET_GUINT16(data, p); - - /// - /// Interpret binary data as a signed 32-bit integer in little endian order. - /// - /// Storage - /// Pointer to storage - /// Interpreted data - public static int GSF_LE_GET_GINT32(byte[] data, int p) => (int)GSF_LE_GET_GUINT32(data, p); - - /// - /// Interpret binary data as a signed 64-bit integer in little endian order. - /// - /// Storage - /// Pointer to storage - /// Interpreted data - public static long GSF_LE_GET_GINT64(byte[] data, int p) => (long)GSF_LE_GET_GUINT64(data, p); - - /// - /// Interpret binary data as a float in little endian order. - /// - /// Storage - /// Pointer to storage - /// Interpreted data - public static float GSF_LE_GET_FLOAT(byte[] data, int p) - { - byte[] temp = new ReadOnlySpan(data, p, 4).ToArray(); - if (!BitConverter.IsLittleEndian) - Array.Reverse(temp); - - return BitConverter.ToSingle(temp, 0); - } - - /// - /// Interpret binary data as a double in little endian order. - /// - /// Storage - /// Pointer to storage - /// Interpreted data - public static double GSF_LE_GET_DOUBLE(byte[] data, int p) - { - byte[] temp = new ReadOnlySpan(data, p, 8).ToArray(); - if (!BitConverter.IsLittleEndian) - Array.Reverse(temp); - - return BitConverter.ToDouble(temp, 0); - } - - #endregion - - #region SET - - /// - /// Store in little endian order in memory pointed to by . - /// - /// Storage - /// Pointer to storage - /// 8-bit unsigned integer - public static void GSF_LE_SET_GUINT8(byte[] data, int p, byte dat) - { - data[p] = (byte)(dat & 0xff); - } - - /// - /// Store in little endian order in memory pointed to by . - /// - /// Storage - /// Pointer to storage - /// 16-bit unsigned integer - 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); - } - - /// - /// Store in little endian order in memory pointed to by . - /// - /// Storage - /// Pointer to storage - /// 32-bit unsigned integer - 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); - } - - /// - /// Store in little endian order in memory pointed to by . - /// - /// Storage - /// Pointer to storage - /// 64-bit unsigned integer - 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); - } - - /// - /// Store in little endian order in memory pointed to by . - /// - /// Storage - /// Pointer to storage - /// 8-bit signed integer - public static void GSF_LE_SET_GINT8(byte[] data, int p, sbyte dat) => GSF_LE_SET_GUINT8(data, p, (byte)dat); - - /// - /// Store in little endian order in memory pointed to by . - /// - /// Storage - /// Pointer to storage - /// 16-bit signed integer - public static void GSF_LE_SET_GINT16(byte[] data, int p, short dat) => GSF_LE_SET_GUINT16(data, p, (ushort)dat); - - /// - /// Store in little endian order in memory pointed to by . - /// - /// Storage - /// Pointer to storage - /// 32-bit signed integer - public static void GSF_LE_SET_GINT32(byte[] data, int p, int dat) => GSF_LE_SET_GUINT32(data, p, (uint)dat); - - /// - /// Store in little endian order in memory pointed to by . - /// - /// Storage - /// Pointer to storage - /// 64-bit signed integer - public static void GSF_LE_SET_GINT64(byte[] data, int p, long dat) => GSF_LE_SET_GUINT64(data, p, (ulong)dat); - - /// - /// Store in little endian order in memory pointed to by . - /// - /// Storage - /// Pointer to storage - /// Float to be stored - 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); - } - - /// - /// Store in little endian order in memory pointed to by . - /// - /// Storage - /// Pointer to storage - /// Double to be stored - 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 - - } -} diff --git a/BurnOutSharp/External/libgsf/GsfZipImpl.cs b/BurnOutSharp/External/libgsf/GsfZipImpl.cs deleted file mode 100644 index 879f30bf..00000000 --- a/BurnOutSharp/External/libgsf/GsfZipImpl.cs +++ /dev/null @@ -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 - - /// - /// A few well-defined extra-field tags. - /// - public enum ExtraFieldTags : ushort - { - ZIP_DIRENT_EXTRA_FIELD_ZIP64 = 0x0001, - - /// - /// "II" -- gsf defined - /// - ZIP_DIRENT_EXTRA_FIELD_IGNORE = 0x4949, - - /// - /// "UT" - /// - ZIP_DIRENT_EXTRA_FIELD_UNIXTIME = 0x5455, - - /// - /// "ux" - /// - ZIP_DIRENT_EXTRA_FIELD_UIDGID = 0x7875 - }; - - /// - /// OS codes. There are plenty, but this is all we need. - /// - public enum OSCodes - { - ZIP_OS_MSDOS = 0, - ZIP_OS_UNIX = 3 - }; - - /// From gsf-outfile-zip.h - public enum GsfZipCompressionMethod - { - /// - /// Supported for export - /// - 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, - - /// - /// Supported for export - /// - 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; } - - /// - /// null = auto, FALSE, TRUE. - /// - public bool? Zip64 { get; set; } - - #endregion - - #region Constructor - - /// - /// Private constructor - /// - private GsfZipDirectoryEntry() { } - - /// - /// Doesn't do much, but include for symmetry - /// - 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 Children { get; set; } - - //public GSList* last_child { get; set; } /* Unused */ - - #endregion - - #region Constructor and Destructor - - /// - /// Private constructor - /// - private GsfZipVDir() { } - - /// The newly created GsfZipVDir. - /// Since: 1.14.24 - public static GsfZipVDir Create(string name, bool is_directory, GsfZipDirectoryEntry dirent) - { - return new GsfZipVDir - { - Name = name, - IsDirectory = is_directory, - DirectoryEntry = dirent, - Children = new List(), - }; - } - - /// - /// Destructor - /// - ~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 -} diff --git a/BurnOutSharp/External/libgsf/Input/GsfInfile.cs b/BurnOutSharp/External/libgsf/Input/GsfInfile.cs deleted file mode 100644 index c6c39e01..00000000 --- a/BurnOutSharp/External/libgsf/Input/GsfInfile.cs +++ /dev/null @@ -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 - - /// - /// Apart from argument types, this is the same as ChildByAName. - /// Please see the documentation there. - /// - /// A null terminated array of names (e.g. from g_strsplit) - /// A newly created child which must be unrefed. - /// New in 1.14.9. - 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; - } - - /// - /// 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. - /// - /// A null terminated array of names (e.g. from g_strsplit) - /// A newly created child which must be unrefed. - /// New in 1.14.9. - 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 - - /// - /// The number of children the storage has, or -1 if the storage can not - /// have children. - /// - public virtual int NumChildren() => -1; - - /// Zero-based index of child to find. - /// The UTF-8 encoded name of the @i-th child - public virtual string NameByIndex(int i) => null; - - /// Target index - /// A newly created child which must be unrefed. - public virtual GsfInput ChildByIndex(int i, ref Exception error) => null; - - /// - /// 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. - /// - /// Target name - /// A newly created child which must be unrefed. - public virtual GsfInput ChildByName(string name, ref Exception error) => null; - - #endregion - } -} diff --git a/BurnOutSharp/External/libgsf/Input/GsfInfileMSOle.cs b/BurnOutSharp/External/libgsf/Input/GsfInfileMSOle.cs deleted file mode 100644 index 14686cdc..00000000 --- a/BurnOutSharp/External/libgsf/Input/GsfInfileMSOle.cs +++ /dev/null @@ -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 - - /// - /// Number of blocks in the BAT as an unsigned Int32 - /// - public uint NumBlocks => (uint)(Blocks?.Length ?? 0); - - #endregion - - #region Constructor - - /// - /// Internal constructor - /// - internal MSOleBAT() { } - - /// - /// Walk the linked list of the supplied block allocation table and build up a - /// table for the list starting in . - /// - /// A meta bat to connect to the raw blocks (small or large) - /// The first block in the list. - /// Where to store the result. - /// True on error. - public static bool Create(in MSOleBAT metabat, uint block, out MSOleBAT res) - { - List bat = new List(); - 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 - - /// - /// Internal representation of the MS-OLE directory entry header - /// - public MSOleDirectoryEntry Header { get; set; } - - public GsfMSOleSortingKey Key { get; set; } - - public uint Index { get; set; } - - public bool UseSmallBlock { get; set; } - - public List Children { get; set; } = new List(); - - #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 - - /// - /// Internal representation of the MS-OLE header - /// - public MSOleHeader Header { get; set; } - - public MSOleBAT BigBlockBat { get; set; } - - public MSOleBAT SmallBlockBat { get; set; } - - /// - /// MetaBAT for all BATs in the file - /// - public uint[] MetaBAT { get; set; } - - /// - /// Maximum number of blocks derived from total input length and block size - /// - 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; - - /// Actually `{ byte[] Buf, long BufSize }` - internal byte[] Stream { get; private set; } - - #endregion - - #region Constructor and Destructor - - /// - /// Private constructor - /// - private GsfInfileMSOle() { } - - /// - /// Opens the root directory of an MS OLE file. - /// - /// GsfInput - /// Optional place to store an error - /// The new ole file handler - /// This adds a reference to . - 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; - } - - /// - /// Destructor - /// - ~GsfInfileMSOle() - { - Input = null; - - if (Info != null && Info.SmallBlockFile != this) - { - Info.Unref(); - Info = null; - } - - Stream = null; - } - - #endregion - - #region Functions - - /// - protected override GsfInput DupImpl(ref Exception err) => (Container as GsfInfileMSOle)?.NewChild(DirectoryEntry, ref err); - - /// - 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; - } - - /// - protected override bool SeekImpl(long offset, SeekOrigin whence) - { - CurBlock = BAT_MAGIC_UNUSED; - return false; - } - - /// - 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; - } - - /// - public override string NameByIndex(int i) - { - foreach (MSOleDirent dirent in DirectoryEntry.Children) - { - if (i-- <= 0) - return dirent.Header.NAME_STRING; - } - - return null; - } - - /// - 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; - } - - /// - public override int NumChildren() - { - if (DirectoryEntry == null) - return -1; - - if (!DirectoryEntry.Header.IS_DIRECTORY) - return -1; - - return DirectoryEntry.Children.Count; - } - - /// - /// Retrieves the 16 byte indentifier (often a GUID in MS Windows apps) - /// stored within the directory associated with @ole and stores it in . - /// - /// 16 byte identifier (often a GUID in MS Windows apps) - /// True on success - 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 - - /// false on error. - 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; - } - - /// - /// Read a block of data from the underlying input. - /// - /// Block number - /// Optionally null - /// Pointer to the buffer or null if there is an error or 0 bytes are requested. - /// Be really anal. - private byte[] GetBlock(uint block, byte[] buffer) - { - if (!SeekBlock(block, 0)) - return null; - - return Input.Read(Info.Header.BB_SIZE, buffer); - } - - /// - /// A small utility routine to read a set of references to bat blocks - /// either from the OLE header, or a meta-bat block. - /// - /// A pointer to the element after the last position filled - 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; - } - - /// - /// Copy some some raw data into an array of uint. - /// - 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); - - /// - /// Parse dirent number 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(), - }; - - 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; - } - - /// - /// Utility routine to _partially_ replicate a file. It does NOT copy the bat - /// blocks, or init the dirent. - /// - 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; - } - - /// - /// Read an OLE header and do some sanity checking - /// along the way. - /// - /// True on error setting if it is supplied. - 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 - } -} diff --git a/BurnOutSharp/External/libgsf/Input/GsfInfileMSVBA.cs b/BurnOutSharp/External/libgsf/Input/GsfInfileMSVBA.cs deleted file mode 100644 index ad6a6642..00000000 --- a/BurnOutSharp/External/libgsf/Input/GsfInfileMSVBA.cs +++ /dev/null @@ -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 - - /// - /// Magic (2 bytes) - /// Version (4 bytes) - /// 0x00, 0xFF (2 bytes) - /// Unknown (22 bytes) - /// - private const int VBA56_DIRENT_RECORD_COUNT = 2 + 4 + 2 + 22; - - /// - /// VBA56_DIRENT_RECORD_COUNT (30 bytes) - /// Type1 Record Count (2 bytes) - /// Unknown (2 bytes) - /// - 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 Children { get; private set; } = null; - - public Dictionary Modules { get; private set; } = null; - - #endregion - - #region Constructor and Destructor - - /// - /// Private constructor - /// - 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; - } - - /// - /// Destructor - /// - ~GsfInfileMSVBA() - { - if (Modules != null) - { - Modules.Clear(); - Modules = null; - } - - if (Source != null) - Source = null; - } - - #endregion - - #region Functions - - /// - /// A collection of names and source code which the caller is responsible for destroying. - /// - /// A Dictionary of names and source code (unknown encoding). - public Dictionary StealModules() - { - Dictionary res = Modules; - Modules = null; - return res; - } - - /// - /// A utility routine that attempts to find the VBA file withint a stream. - /// - /// A GsfInfile - 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(); - - Modules[name] = code; - } - else - { - Console.Error.WriteLine($"Problems extracting the source for {name} @ {src_offset}"); - } - } - - /// - /// Read an VBA dirctory and its project file. - /// along the way. - /// - /// Place to store an Exception if anything goes wrong - /// false on error setting if it is supplied. - 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 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; - * - * 00 00 00 00 00 00 - * and sometimes some trailing junk - **/ - - if ((ptr + 6) > end) - { - //#ifdef OLD_VBA_DUMP - //puts(""); - //#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(""); - //#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(""); - //printf("", 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 - } -} diff --git a/BurnOutSharp/External/libgsf/Input/GsfInfileStdio.cs b/BurnOutSharp/External/libgsf/Input/GsfInfileStdio.cs deleted file mode 100644 index 048e1cda..00000000 --- a/BurnOutSharp/External/libgsf/Input/GsfInfileStdio.cs +++ /dev/null @@ -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 Children { get; set; } = new List(); - - #endregion - - #region Constructor and Destructor - - /// - /// Private constructor - /// - private GsfInfileStdio() { } - - /// In locale dependent encoding - /// Optionally null - /// A new file or null. - 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; - } - - /// - /// Destructor - /// - ~GsfInfileStdio() - { - Root = null; - Children.Clear(); - Children = null; - } - - #endregion - - #region Functions - - /// - protected override GsfInput DupImpl(ref Exception err) - { - GsfInfileStdio dst = new GsfInfileStdio - { - Root = Root, - }; - - dst.Children.AddRange(Children); - - return dst; - } - - /// - protected override byte[] ReadImpl(int num_bytes, byte[] optional_buffer, int bufferPtr = 0) => null; - - /// - public override string NameByIndex(int i) => i < Children.Count ? Children[i] : null; - - /// - public override GsfInput ChildByIndex(int i, ref Exception error) - { - string name = NameByIndex(i); - return name != null ? OpenChild(name, ref error) : null; - } - - /// - 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; - } - - /// - 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 - } -} diff --git a/BurnOutSharp/External/libgsf/Input/GsfInfileTar.cs b/BurnOutSharp/External/libgsf/Input/GsfInfileTar.cs deleted file mode 100644 index c17e5dd2..00000000 --- a/BurnOutSharp/External/libgsf/Input/GsfInfileTar.cs +++ /dev/null @@ -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; } - - /// - /// The location of data - /// - public long Offset { get; set; } - - public long Length { get; set; } - - /// - /// The directory object, or null for a data file - /// - public GsfInfileTar Dir { get; set; } - } - - /// - /// Tar header from POSIX 1003.1-1990. - /// - // 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 Children { get; set; } = new List(); - - public Exception Err { get; set; } = null; - - #endregion - - #region Constructor and Deconstructor - - /// - /// Private constructor - /// - private GsfInfileTar() { } - - /// - /// Opens the root directory of a Tar file. - /// - /// A base GsfInput - /// An Exception, optionally null - /// The new tar file handler - /// This adds a reference to . - 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; - } - - /// - /// Destructor - /// - ~GsfInfileTar() - { - Source = null; - Err = null; - Children.Clear(); - } - - #endregion - - #region Functions - - /// - 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; - } - - /// - protected override byte[] ReadImpl(int num_bytes, byte[] optional_buffer, int bufferPtr = 0) => null; - - /// - protected override bool SeekImpl(long offset, SeekOrigin whence) => false; - - /// - 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; - } - } - - /// - public override string NameByIndex(int i) - { - if (i < 0 || i >= Children.Count) - return null; - - return Children[i].Name; - } - - /// - 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; - } - - /// - 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); - } - } - } - - /// - /// Read tar headers and do some sanity checking - /// along the way. - /// - 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 - } -} diff --git a/BurnOutSharp/External/libgsf/Input/GsfInfileZip.cs b/BurnOutSharp/External/libgsf/Input/GsfInfileZip.cs deleted file mode 100644 index babcc24c..00000000 --- a/BurnOutSharp/External/libgsf/Input/GsfInfileZip.cs +++ /dev/null @@ -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 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 - - /// - /// Private constructor - /// - 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; - } - } - - /// - /// Opens the root directory of a Zip file. - /// - /// A base GsfInput - /// Place to store an Exception if anything goes wrong - /// The new zip file handler - /// This adds a reference to . - 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; - } - - /// - /// Destructor - /// - ~GsfInfileZip() - { - if (Info != null) - { - Info.Unref(); - Info = null; - } - - if (Stream != null) - { - Stream.inflateEnd(); - Stream = null; - } - - Buf = null; - - SetSource(null); - Err = null; - } - - #endregion - - #region Functions - - /// - 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; - } - - /// - 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; - } - - /// - /// Global flag -- we don't want one per stream. - /// - private static bool warned = false; - - /// - 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; - } - - /// - 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; - } - - /// - public override string NameByIndex(int i) - { - GsfZipVDir child_vdir = VDir.ChildByIndex(i); - if (child_vdir != null) - return child_vdir.Name; - - return null; - } - - /// - 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; - } - - /// - 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; - } - - /// - /// Returns a partial duplicate. - /// - private GsfInfileZip PartiallyDuplicate(ref Exception err) - { - GsfInfileZip dst = new GsfInfileZip(this); - - if (dst.Err != null) - { - err = dst.Err; - return null; - } - - return dst; - } - - /// - /// Read zip headers and do some sanity checking - /// along the way. - /// - /// True on error setting Err. - 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(), - 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); - } - } - - /// - /// Read zip headers and do some sanity checking - /// along the way. - /// - /// True on error setting Err. - private bool InitInfo() - { - bool ret = ReadDirectoryEntries(); - if (ret != false) - return ret; - - BuildVirtualDirectories(); - - return false; - } - - /// Returns true on error - 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 - } -} diff --git a/BurnOutSharp/External/libgsf/Input/GsfInput.cs b/BurnOutSharp/External/libgsf/Input/GsfInput.cs deleted file mode 100644 index 33ae65b7..00000000 --- a/BurnOutSharp/External/libgsf/Input/GsfInput.cs +++ /dev/null @@ -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; - } - - /// Place to store an Exception if anything goes wrong - /// The duplicate - 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; - } - - /// - /// Attempts to open a 'sibling' of input. The caller is responsible for - /// managing the resulting object. - /// - /// Name. - /// Place to store an Exception if anything goes wrong - /// A related GsfInput - /// - /// UNIMPLEMENTED BY ANY BACKEND - /// and it is probably unnecessary. Container provides - /// enough power to do what is necessary. - /// - public GsfInput OpenSibling(string name, ref Exception err) => OpenSiblingImpl(name, ref err); - - /// - /// Are we at the end of the file? - /// - /// True if the input is at the eof. - public bool EOF() => CurrentOffset >= Size; - - /// - /// Read at least . 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. - /// - /// Number of bytes to read - /// Pointer to destination memory area - /// - /// Pointer to the buffer or null if there is - /// an error or 0 bytes are requested. - /// - 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; - } - - /// - /// Read . Does not change the current position if there - /// is an error. Will only read if the entire amount can be read. - /// - /// Number of bytes to read - /// Copy of - /// The data read. - 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; - } - - /// The number of bytes left in the file. - public long Remaining() => Size - CurrentOffset; - - /// - /// Move the current location in the input stream. - /// - /// Target offset - /// - /// Determines whether the offset is relative to the beginning or - /// the end of the stream, or to the current location. - /// - /// True on error. - 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; - } - - /// The (fs-sys encoded) filename - /// True if the assignment was ok. - 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; - } - - /// - /// Emulate forward seeks by reading. - /// - /// Absolute position to seek to - /// True if the emulation failed. - 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; - } - - /// - /// Copy the contents from input to 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. - /// - /// A non-null GsfOutput - /// True on success - 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; - } - - /// - /// This functions takes ownership of the incoming reference and yields a - /// new one as its output. - /// - /// - /// A stream equivalent to the source stream, - /// but uncompressed if the source was compressed. - /// - 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 - - /// - /// Decompresses an LZ compressed stream. - /// - /// Offset into it for start byte of compresse stream - /// A GByteArray that the caller is responsible for freeing - 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 temp = new List(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 temp = new List(res); - temp.AddRange(buffer.Skip((int)(pos % VBA_COMPRESSION_WINDOW))); - res = temp.ToArray(); - } - - return res; - } - - #endregion - - #region MS-VBA - - /// - /// Decompresses VBA stream. - /// - /// Offset into it for start byte of compressed stream - /// Size of the returned array - /// Whenever add or not null at the end of array - /// A pointer to byte array - 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 res = new List(); - - 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 - } -} diff --git a/BurnOutSharp/External/libgsf/Input/GsfInputBzip.cs b/BurnOutSharp/External/libgsf/Input/GsfInputBzip.cs deleted file mode 100644 index 0665e516..00000000 --- a/BurnOutSharp/External/libgsf/Input/GsfInputBzip.cs +++ /dev/null @@ -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 - - /// Place to store an Exception if anything goes wrong - /// A new GsfInputMemory or null. - 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 - } -} diff --git a/BurnOutSharp/External/libgsf/Input/GsfInputGZip.cs b/BurnOutSharp/External/libgsf/Input/GsfInputGZip.cs deleted file mode 100644 index 49bb6626..00000000 --- a/BurnOutSharp/External/libgsf/Input/GsfInputGZip.cs +++ /dev/null @@ -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 - { - /// - /// File contains text ? - /// - GZIP_IS_ASCII = 0x01, - - /// - /// There is a CRC in the header - /// - GZIP_HEADER_CRC = 0x02, - - /// - /// There is an 'extra' field - /// - GZIP_EXTRA_FIELD = 0x04, - - /// - /// The original is stored - /// - GZIP_ORIGINAL_NAME = 0x08, - - /// - /// There is a comment in the header - /// - GZIP_HAS_COMMENT = 0x10, - } - - #endregion - - #region Properties - - /// - /// Compressed data - /// - public GsfInput Source { get; set; } = null; - - /// - /// No header and no trailer. - /// - 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; } - - /// - /// CRC32 of uncompressed data - /// - 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 - - /// - /// Private constructor - /// - 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; - } - - /// - /// Adds a reference to . - /// - /// The underlying data source. - /// Place to store an Exception if anything goes wrong - /// - 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; - } - - /// - /// Destructor - /// - ~GsfInputGZip() - { - Source = null; - - if (Stream != null) - Stream.inflateEnd(); - - Err = null; - } - - #endregion - - #region Functions - - /// - 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; - } - - /// - 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; - - /// - 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 - } -} diff --git a/BurnOutSharp/External/libgsf/Input/GsfInputGio.cs b/BurnOutSharp/External/libgsf/Input/GsfInputGio.cs deleted file mode 100644 index 7662045d..00000000 --- a/BurnOutSharp/External/libgsf/Input/GsfInputGio.cs +++ /dev/null @@ -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 - * - * 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 - - /// - /// Private constructor - /// - private GsfInputGio() { } - - /// Place to store an Exception if anything goes wrong - /// A new GsfInputGio or null - 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; - } - - /// - /// Destructor - /// - ~GsfInputGio() - { - Stream.Close(); - Stream = null; - - File = null; - - if (Buf != null) - { - Buf = null; - BufSize = 0; - } - } - - #endregion - - #region Functions - - /// - protected override GsfInput DupImpl(ref Exception err) - { - if (File != null) - return null; - - return Create(File, ref err); - } - - /// - 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; - } - - /// - 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 - } -} diff --git a/BurnOutSharp/External/libgsf/Input/GsfInputHTTP.cs b/BurnOutSharp/External/libgsf/Input/GsfInputHTTP.cs deleted file mode 100644 index 6255a27c..00000000 --- a/BurnOutSharp/External/libgsf/Input/GsfInputHTTP.cs +++ /dev/null @@ -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 - - /// - /// Private constructor - /// - private GsfInputHTTP() { } - - /// - /// - /// - /// A string containing the URL to retrieve - /// Holds any errors encountered when establishing the HTTP connection - /// An open HTTP connection, ready for reading. - 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 - - /// - protected override GsfInput DupImpl(ref Exception err) => Create(Url, ref err); - - /// - 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; - } - - /// - 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 - } -} diff --git a/BurnOutSharp/External/libgsf/Input/GsfInputIoChannel.cs b/BurnOutSharp/External/libgsf/Input/GsfInputIoChannel.cs deleted file mode 100644 index 79cf2c9e..00000000 --- a/BurnOutSharp/External/libgsf/Input/GsfInputIoChannel.cs +++ /dev/null @@ -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 - - ///// A new GsfInputMemory or null. - //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); - //} - } -} diff --git a/BurnOutSharp/External/libgsf/Input/GsfInputMemory.cs b/BurnOutSharp/External/libgsf/Input/GsfInputMemory.cs deleted file mode 100644 index 5416695b..00000000 --- a/BurnOutSharp/External/libgsf/Input/GsfInputMemory.cs +++ /dev/null @@ -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 - - /// - /// - /// The input bytes - /// The length of - /// Whether you want this memory to be free'd at object destruction - /// A new GsfInputMemory - 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; - } - - /// The input bytes - /// The length of - /// A new GsfInputMemory - 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; - } - - /// - protected override GsfInput DupImpl(ref Exception err) - { - return new GsfInputMemory - { - Shared = Shared, - Size = Shared.Size, - }; - } - - /// - 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(src, (int)CurrentOffset, num_bytes).ToArray(); - } - } - - /// - protected override bool SeekImpl(long offset, SeekOrigin whence) => false; - - #endregion - } - - public class GsfInputMemoryMap : GsfInputMemory - { - private const int PROT_READ = 0x1; - - /// The file on disk that you want to mmap - /// An Exception - /// A new GsfInputMemory - 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; - } - } -} diff --git a/BurnOutSharp/External/libgsf/Input/GsfInputProxy.cs b/BurnOutSharp/External/libgsf/Input/GsfInputProxy.cs deleted file mode 100644 index d6514a85..00000000 --- a/BurnOutSharp/External/libgsf/Input/GsfInputProxy.cs +++ /dev/null @@ -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 - - /// - /// 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. - /// - /// The underlying data source. - /// Offset into source for start of section. - /// Length of section. - /// A new input object. - 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; - } - - /// - /// This creates a new proxy to the entire, given input source. See - /// Create for details. - /// - /// The underlying data source. - /// A new input object. - public static GsfInput Create(GsfInput source) => Create(source, 0, source.Size); - - /// - protected override GsfInput DupImpl(ref Exception err) => Create(this); - - /// - 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); - } - - /// - protected override bool SeekImpl(long offset, SeekOrigin whence) => false; - - #endregion - } -} diff --git a/BurnOutSharp/External/libgsf/Input/GsfInputStdio.cs b/BurnOutSharp/External/libgsf/Input/GsfInputStdio.cs deleted file mode 100644 index 0a5bc586..00000000 --- a/BurnOutSharp/External/libgsf/Input/GsfInputStdio.cs +++ /dev/null @@ -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 - - /// - /// Destructor - /// - ~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; - } - - /// In UTF-8. - /// Place to store an Exception if anything goes wrong - /// A new file or null. - 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; - } - - /// - /// Assumes ownership of when succeeding. If is true, - /// ownership reverts to caller when the GsfInput is closed. - /// - /// The filename corresponding to . - /// An existing stdio - /// Should be closed when the wrapper is closed - /// - /// A new GsfInput wrapper for . Note that if the file is not - /// seekable, this function will make a local copy of the entire file. - /// - 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; - } - - /// - protected override GsfInput DupImpl(ref Exception err) => Create(Filename, ref err); - - /// - 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; - } - - /// - 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 - } -} diff --git a/BurnOutSharp/External/libgsf/Input/GsfInputTextline.cs b/BurnOutSharp/External/libgsf/Input/GsfInputTextline.cs deleted file mode 100644 index ce1f01e5..00000000 --- a/BurnOutSharp/External/libgsf/Input/GsfInputTextline.cs +++ /dev/null @@ -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 - - /// In some combination of ascii and UTF-8 - /// A new file - /// This adds a reference to @source. - 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, - }; - } - - /// - protected override GsfInput DupImpl(ref Exception err) - { - return new GsfInputTextline() - { - Source = this.Source, - Size = this.Size, - Name = this.Name, - }; - } - - /// - 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; - } - - /// - protected override bool SeekImpl(long offset, SeekOrigin whence) - { - Remainder = null; - bool res = Source.Seek(offset, whence); - CurrentOffset = Source.CurrentOffset; - return res; - } - - #endregion - - #region Utilities - - /// - /// 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. - /// - /// The string read, or null on eof. - internal string GetStringASCII() => GetStringUTF8(); - - /// - /// 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. - /// - /// The string read, or null on eof. - 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 - } -} diff --git a/BurnOutSharp/External/libgsf/Input/GsfStructuredBlob.cs b/BurnOutSharp/External/libgsf/Input/GsfStructuredBlob.cs deleted file mode 100644 index 3193fb9c..00000000 --- a/BurnOutSharp/External/libgsf/Input/GsfStructuredBlob.cs +++ /dev/null @@ -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 - - /// - /// Private constructor - /// - private GsfStructuredBlob() { } - - /// - /// Destructor - /// - ~GsfStructuredBlob() - { - if (Data != null) - Data = null; - - if (Children != null) - Children = null; - } - - #endregion - - #region Functions - - /// - 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; - } - - /// - 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; - } - - /// - protected override bool SeekImpl(long offset, SeekOrigin whence) => false; - - /// - public override int NumChildren() => Children != null ? Children.Length : -1; - - /// - public override string NameByIndex(int i) - { - if (Children == null) - return null; - - if (i < 0 || i >= Children.Length) - return null; - - return Children[i].Name; - } - - /// - 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]; - } - - /// - 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; - } - - /// - /// Create a tree of binary blobs with unknown content from a GsfInput or - /// #GsfInfile and store it in a newly created #GsfStructuredBlob. - /// - /// An input (potentially a GsfInfile) holding the blob - /// A new GsfStructuredBlob object which the caller is responsible for. - 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; - } - - /// - /// Dumps structured blob @blob onto the . Will fail if the output is - /// not an Outfile and blob has multiple streams. - /// - /// True on success. - 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 - } -} diff --git a/BurnOutSharp/External/libgsf/Output/GsfOutfile.cs b/BurnOutSharp/External/libgsf/Output/GsfOutfile.cs deleted file mode 100644 index a163a416..00000000 --- a/BurnOutSharp/External/libgsf/Output/GsfOutfile.cs +++ /dev/null @@ -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 - - /// The name of the new child to create - /// true to create a directory, false to create a plain file - /// A newly created child - public virtual GsfOutput NewChild(string name, bool is_dir) => null; - - #endregion - } -} diff --git a/BurnOutSharp/External/libgsf/Output/GsfOutfileMSOle.cs b/BurnOutSharp/External/libgsf/Output/GsfOutfileMSOle.cs deleted file mode 100644 index efaab52e..00000000 --- a/BurnOutSharp/External/libgsf/Output/GsfOutfileMSOle.cs +++ /dev/null @@ -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. - - /// - /// 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. - /// - 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 Content_Dir_Children { get; set; } = null; - - /// - /// Only valid for the root, ordered - /// - public List Content_Dir_RootOrder { get; set; } = null; - - #endregion - - #region Struct (SmallBlock) - - public byte[] Content_SmallBlock_Buf { get; set; } - - #endregion - - #region Struct (BigBlock) - - /// - /// In bytes - /// - public int Content_BigBlock_StartOffset { get; set; } - - #endregion - - #endregion - - /// - /// 16 byte GUID used by some apps - /// - public byte[] ClassID { get; set; } = new byte[16]; - - #endregion - - #region Constructor and Destructor - - /// - /// Private constructor - /// - private GsfOutfileMSOle() => MakeSortingName(); - - /// - /// Creates the root directory of an MS OLE file and manages the addition of - /// children. - /// - /// A GsfOutput to hold the OLE2 file. - /// Size of large blocks. - /// Size of small blocks. - /// The new ole file handler. - /// This adds a reference to . - 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(), - }; - - ole.RegisterChild(ole); - - // Build the header - byte[] buf = Enumerable.Repeat(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; - } - - /// - /// Creates the root directory of an MS OLE file and manages the addition of - /// children. - /// - /// A GsfOutput to hold the OLE2 file - /// The new ole file handler. - /// This adds a reference to . - 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(); - } - - /// - 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; - } - - /// - 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; - } - - /// - 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; - } - - /// - 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); - } - - /// - 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; - } - - /// - /// Write to the directory associated with ole. - /// - /// Identifier (often a GUID in MS Windows apps) - /// True on success. - public bool SetClassID(byte[] clsid) - { - if (Type != MSOleOutfileType.MSOLE_DIR) - return false; - - Array.Copy(clsid, ClassID, ClassID.Length); - return true; - } - - #endregion - - #region Utilities - - /// - /// Returns the number of times 1 must be shifted left to reach value - /// - 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); - } - - /// - /// Static objects are zero-initialized - /// - private static byte[] zero_buf = new byte[ZERO_PAD_BUF_SIZE]; - - /// - /// 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. - /// - 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); - } - - /// - /// Utility routine to generate a BAT for a file known to be sequential and - /// continuous. - /// - 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); - } - - /// - /// Write the metadata (dirents, small block, xbats) - /// - 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 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(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 - } -} diff --git a/BurnOutSharp/External/libgsf/Output/GsfOutfileStdio.cs b/BurnOutSharp/External/libgsf/Output/GsfOutfileStdio.cs deleted file mode 100644 index 5214434e..00000000 --- a/BurnOutSharp/External/libgsf/Output/GsfOutfileStdio.cs +++ /dev/null @@ -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 - - /// - /// Private constructor - /// - private GsfOutfileStdio() { } - - /// - /// - /// - /// Root directory in utf8. - /// Place to store an Exception if anything goes wrong - /// A new outfile or null. - 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 - - /// - 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; - } - - /// - protected override bool CloseImpl() => true; - - #endregion - } -} diff --git a/BurnOutSharp/External/libgsf/Output/GsfOutfileZip.cs b/BurnOutSharp/External/libgsf/Output/GsfOutfileZip.cs deleted file mode 100644 index 90e1ecb7..00000000 --- a/BurnOutSharp/External/libgsf/Output/GsfOutfileZip.cs +++ /dev/null @@ -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; - - /// - /// Only valid for the root, ordered - /// - public List 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 - - /// - /// Private constructor - /// - private GsfOutfileZip() { } - - /// - /// Creates the root directory of a Zip file and manages the addition of - /// children. - /// - /// A GsfOutput to hold the ZIP file - /// Location to store error, or null; currently unused. - /// The new zip file handler - /// This adds a reference to . - 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(); - 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; - } - - /// - /// Destructor - /// - ~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 - - /// - protected override bool SeekImpl(long offset, SeekOrigin whence) => false; - - /// - 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; - } - - /// - 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(); - 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; - } - - /// - /// 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+. - /// - /// - 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 extras = new List(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(tmp, 0, 4).ToArray()); - GSF_LE_SET_GUINT64(tmp, 0, (ulong)dirent.UncompressedSize); - extras.AddRange(new ReadOnlySpan(tmp, 0, 8).ToArray()); - GSF_LE_SET_GUINT64(tmp, 0, (ulong)dirent.CompressedSize); - extras.AddRange(new ReadOnlySpan(tmp, 0, 8).ToArray()); - if (offset_in_zip64) - { - GSF_LE_SET_GUINT64(tmp, 0, (ulong)dirent.Offset); - extras.AddRange(new ReadOnlySpan(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(tmp, 0, 4).ToArray()); - GSF_LE_SET_GUINT64(tmp, 0, 0); - extras.AddRange(new ReadOnlySpan(tmp, 0, 8).ToArray()); - GSF_LE_SET_GUINT64(tmp, 0, 0); - extras.AddRange(new ReadOnlySpan(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(tmp, 0, 4).ToArray()); - tmp[0] = 1; - extras.AddRange(new ReadOnlySpan(tmp, 0, 1).ToArray()); - GSF_LE_SET_GUINT32(tmp, 0, (uint)new DateTimeOffset(dirent.ModifiedTime ?? DateTime.UtcNow).ToUnixTimeSeconds()); - extras.AddRange(new ReadOnlySpan(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 header = new List(); - 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 extras = new List(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(tmp, 0, 4).ToArray()); - GSF_LE_SET_GUINT64(tmp, 0, (ulong)usize); - extras.AddRange(new ReadOnlySpan(tmp, 0, 8).ToArray()); - GSF_LE_SET_GUINT64(tmp, 0, (ulong)csize); - extras.AddRange(new ReadOnlySpan(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(tmp, 0, 4).ToArray()); - tmp[0] = 1; - extras.AddRange(new ReadOnlySpan(tmp, 0, 1).ToArray()); - GSF_LE_SET_GUINT32(tmp, 0, (uint)new DateTimeOffset(dirent.ModifiedTime ?? DateTime.UtcNow).ToUnixTimeSeconds()); - extras.AddRange(new ReadOnlySpan(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 header = new List(); - 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; - } - - /// - /// Write the per stream data descriptor - /// - 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 - } -} diff --git a/BurnOutSharp/External/libgsf/Output/GsfOutput.cs b/BurnOutSharp/External/libgsf/Output/GsfOutput.cs deleted file mode 100644 index fa98c601..00000000 --- a/BurnOutSharp/External/libgsf/Output/GsfOutput.cs +++ /dev/null @@ -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 - - /// - /// Close a stream. - /// - /// false on error - public bool Close() - { - if (IsClosed) - return SetError(0, ""); - - // 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; - } - - /// - /// 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) - /// - /// Relative amount to reposition - /// What the offset is relative to. - /// false on error. - 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; - } - - /// - /// Write of to output. - /// - /// Number of bytes to write - /// Data to write. - /// %false on error. - 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; - } - - /// True if the wrapping succeeded. - 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; - } - - /// - /// Output to output using the format string , similar to printf(3) - /// - /// The printf-style format string - /// The arguments for @format - /// True if successful, false if not - public bool PrintF(string format, params string[] va) => VPrintF(format, va) >= 0; - - /// - /// Output to output using the format string , similar to vprintf(3) - /// - /// The printf-style format string - /// The arguments for @format - /// Number of bytes printed, a negative value if not successful - 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; - } - - /// - /// Like fputs, this assumes that the line already ends with a newline - /// - /// Nul terminated string to write - /// %true if successful, %false if not - 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; - } - - /// The (fs-sys encoded) filename - /// %true if the assignment was ok. - /// This is a utility routine that should only be used by derived outputs. - 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; - } - - /// The error id - /// printf style format string - /// arguments for @format - /// Always returns false to facilitate its use. - /// This is a utility routine that should only be used by derived outputs. - 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 - } -} diff --git a/BurnOutSharp/External/libgsf/Output/GsfOutputBzip.cs b/BurnOutSharp/External/libgsf/Output/GsfOutputBzip.cs deleted file mode 100644 index a62192ed..00000000 --- a/BurnOutSharp/External/libgsf/Output/GsfOutputBzip.cs +++ /dev/null @@ -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 - /// - /// Compressed data - /// - 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 - - /// - /// Private constructor - /// - private GsfOutputBzip() { } - - /// The underlying data source. - /// A new file or null. - /// Adds a reference to . - 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 - - /// - 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 - } - - /// - 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 - } -} diff --git a/BurnOutSharp/External/libgsf/Output/GsfOutputCsv.cs b/BurnOutSharp/External/libgsf/Output/GsfOutputCsv.cs deleted file mode 100644 index 2add96c0..00000000 --- a/BurnOutSharp/External/libgsf/Output/GsfOutputCsv.cs +++ /dev/null @@ -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 - - /// - /// Controls when to add quotes around fields. - /// - public enum GsfOutputCsvQuotingMode - { - /// - /// Never add quotes around fields - /// - GSF_OUTPUT_CSV_QUOTING_MODE_NEVER, - - /// - /// Add quotes around fields when needed - /// - GSF_OUTPUT_CSV_QUOTING_MODE_AUTO, - - /// - /// Always add quotes around fields - /// - 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 - - /// - protected override bool WriteImpl(int num_bytes, byte[] data) => Sink.Write(num_bytes, data); - - /// - protected override bool SeekImpl(long offset, SeekOrigin whence) => Sink.Seek(offset, whence); - - /// - 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 -} diff --git a/BurnOutSharp/External/libgsf/Output/GsfOutputGZip.cs b/BurnOutSharp/External/libgsf/Output/GsfOutputGZip.cs deleted file mode 100644 index 8ecd556d..00000000 --- a/BurnOutSharp/External/libgsf/Output/GsfOutputGZip.cs +++ /dev/null @@ -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 - - /// - /// GZip flag byte - The original is stored - /// - private const byte GZIP_ORIGINAL_NAME = 0x08; - - #endregion - - #region Properties - - /// - /// Compressed data - /// - public GsfOutput Sink { get; set; } = null; - - /// - /// No header and no trailer. - /// - public bool Raw { get; set; } - - /// - /// zlib compression level - /// - public int DeflateLevel { get; set; } = Z_DEFAULT_COMPRESSION; - - public ZStream Stream { get; set; } - - /// - /// CRC32 of uncompressed data - /// - 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 - - /// - /// Private constructor - /// - private GsfOutputGZip() { } - - /// The underlying data source. - /// Optionally null. - /// A new file or null - /// Adds a reference to . - 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; - } - - /// - /// Destructor - /// - ~GsfOutputGZip() - { - // FIXME: check for error? - Stream.deflateEnd(); - } - - #endregion - - #region Functions - - /// - 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; - } - - /// - protected override bool SeekImpl(long offset, SeekOrigin whence) => false; - - /// - 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 - } -} diff --git a/BurnOutSharp/External/libgsf/Output/GsfOutputGio.cs b/BurnOutSharp/External/libgsf/Output/GsfOutputGio.cs deleted file mode 100644 index 1ae2b1b7..00000000 --- a/BurnOutSharp/External/libgsf/Output/GsfOutputGio.cs +++ /dev/null @@ -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 - * - * 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 - - /// - /// Private constructor - /// - private GsfOutputGio() { } - - /// An existing GFile - /// A new GsfOutputGio or null - public static GsfOutputGio Create(string file) - { - Exception err = null; - return Create(file, ref err); - } - - /// An existing GFile - /// A new GsfOutputGio or null - 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; - } - } - - /// - /// Destructor - /// - ~GsfOutputGio() => Close(); - - #endregion - - #region Functions - - /// - protected override bool CloseImpl() - { - if (Stream != null) - { - Stream.Close(); - Stream = null; - - return true; - } - - return false; - } - - /// - 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; - } - } - - /// - 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 - } -} diff --git a/BurnOutSharp/External/libgsf/Output/GsfOutputIOChannel.cs b/BurnOutSharp/External/libgsf/Output/GsfOutputIOChannel.cs deleted file mode 100644 index 9e103d0f..00000000 --- a/BurnOutSharp/External/libgsf/Output/GsfOutputIOChannel.cs +++ /dev/null @@ -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 - - ///// A new file or null. - //public static GsfOutputIOChannel Create(GIOChannel channel) - //{ - // if (channel == null) - // return null; - - // return new GsfOutputIOChannel - // { - // Channel = channel, - // }; - //} - - //#endregion - - //#region Functions - - ///// - //protected override bool CloseImpl() - //{ - // g_io_channel_shutdown(Channel, true, null); - // return true; - //} - - ///// - //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; - //} - - ///// - //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 - } -} diff --git a/BurnOutSharp/External/libgsf/Output/GsfOutputIconv.cs b/BurnOutSharp/External/libgsf/Output/GsfOutputIconv.cs deleted file mode 100644 index 74a2cc88..00000000 --- a/BurnOutSharp/External/libgsf/Output/GsfOutputIconv.cs +++ /dev/null @@ -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; } - - /// - /// 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. - /// - public string Fallback { get; set; } - - public byte[] Buf { get; set; } = new byte[BUF_SIZE]; - - public int BufLen { get; set; } = 0; - - #endregion - - #region Constructor - - /// - /// Private constructor - /// - private GsfOutputIconv() { } - - /// - /// - /// - /// The underlying data source. - /// The target character set. - /// he source character set. - /// A new GsfOutput object or null. - /// Adds a reference to . - 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 - - /// - 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; - } - - /// - protected override bool SeekImpl(long offset, SeekOrigin whence) => false; - - /// - 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 - } -} diff --git a/BurnOutSharp/External/libgsf/Output/GsfOutputMemory.cs b/BurnOutSharp/External/libgsf/Output/GsfOutputMemory.cs deleted file mode 100644 index 0063ec47..00000000 --- a/BurnOutSharp/External/libgsf/Output/GsfOutputMemory.cs +++ /dev/null @@ -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 - - /// - /// Private constructor - /// - private GsfOutputMemory() { } - - /// A new file. - public static GsfOutputMemory Create() => new GsfOutputMemory(); - - #endregion - - #region Functions - - /// - protected override bool CloseImpl() => true; - - /// - 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; - } - - /// - 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; - } - - /// - 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); - } - - /// - /// - /// - /// - /// The data that has been written to mem. - /// The caller takes ownership and the buffer belonging to mem is set - /// to NULL. - /// - public byte[] StealBytes() - { - byte[] bytes = Buffer; - Buffer = null; - Capacity = 0; - return bytes; - } - - #endregion - } -} diff --git a/BurnOutSharp/External/libgsf/Output/GsfOutputStdio.cs b/BurnOutSharp/External/libgsf/Output/GsfOutputStdio.cs deleted file mode 100644 index 42034711..00000000 --- a/BurnOutSharp/External/libgsf/Output/GsfOutputStdio.cs +++ /dev/null @@ -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 - - /// - /// Private constructor - /// - 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 - - /// - 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; - } - - /// - 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}"); - } - } - - /// - 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 - } -} diff --git a/BurnOutSharp/External/libgsf/README.md b/BurnOutSharp/External/libgsf/README.md deleted file mode 100644 index 121f6ac7..00000000 --- a/BurnOutSharp/External/libgsf/README.md +++ /dev/null @@ -1,10 +0,0 @@ -# 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. \ No newline at end of file diff --git a/BurnOutSharp/External/libmsi/Internal/LibmsiIStream.cs b/BurnOutSharp/External/libmsi/Internal/LibmsiIStream.cs deleted file mode 100644 index 3fb25e9e..00000000 --- a/BurnOutSharp/External/libmsi/Internal/LibmsiIStream.cs +++ /dev/null @@ -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 - - /// - /// Private constructor - /// - 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 - } -} \ No newline at end of file diff --git a/BurnOutSharp/External/libmsi/Internal/LibmsiSQLInput.cs b/BurnOutSharp/External/libmsi/Internal/LibmsiSQLInput.cs deleted file mode 100644 index f032f5b3..00000000 --- a/BurnOutSharp/External/libmsi/Internal/LibmsiSQLInput.cs +++ /dev/null @@ -1,2011 +0,0 @@ -/* A Bison parser, made by GNU Bison 3.8.2. */ - -/* Bison implementation for Yacc-like parsers in C - - Copyright (C) 1984, 1989-1990, 2000-2015, 2018-2021 Free Software Foundation, - Inc. - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - 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 General Public License - along with this program. If not, see . */ - -/* As a special exception, you may create a larger work that contains - part or all of the Bison parser skeleton and distribute that work - under terms of your choice, so long as that work isn't itself a - parser generator using the skeleton or a modified version thereof - as a parser skeleton. Alternatively, if you modify or redistribute - the parser skeleton itself, you may (at your option) remove this - special exception, which will cause the skeleton and the resulting - Bison output files to be licensed under the GNU General Public - License without this special exception. - - This special exception was added by the Free Software Foundation in - version 2.2 of Bison. */ - -/* C LALR(1) parser skeleton written by Richard Stallman, by - simplifying the original so-called "semantic" parser. */ - -/* DO NOT RELY ON FEATURES THAT ARE NOT DOCUMENTED in the manual, - especially those whose name start with YY_ or yy_. They are - private implementation details that can be changed or removed. */ - -/* All symbols defined below should begin with yy or YY, to avoid - infringing on user name space. This should be done even for local - variables, as they might otherwise be expanded by user macros. - There are some unavoidable exceptions within include files to - define necessary library symbols; they are noted "INFRINGES ON - USER NAME SPACE" below. */ - -/* - * 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.Collections.Generic; -using LibMSI.Views; -using static LibMSI.Internal.MsiPriv; - -namespace LibMSI.Internal -{ - #region Generated Enums - - /// - /// Token kinds. - /// - internal enum sql_tokentype - { - SQL_EMPTY = -2, - SQL_EOF = 0, /* "end of file" */ - SQL_error = 256, /* error */ - SQL_UNDEF = 257, /* "invalid token" */ - TK_ALTER = 258, /* TK_ALTER */ - TK_AND = 259, /* TK_AND */ - TK_BY = 260, /* TK_BY */ - TK_CHAR = 261, /* TK_CHAR */ - TK_COMMA = 262, /* TK_COMMA */ - TK_CREATE = 263, /* TK_CREATE */ - TK_DELETE = 264, /* TK_DELETE */ - TK_DROP = 265, /* TK_DROP */ - TK_DISTINCT = 266, /* TK_DISTINCT */ - TK_DOT = 267, /* TK_DOT */ - TK_EQ = 268, /* TK_EQ */ - TK_FREE = 269, /* TK_FREE */ - TK_FROM = 270, /* TK_FROM */ - TK_GE = 271, /* TK_GE */ - TK_GT = 272, /* TK_GT */ - TK_HOLD = 273, /* TK_HOLD */ - TK_ADD = 274, /* TK_ADD */ - TK_ID = 275, /* TK_ID */ - TK_ILLEGAL = 276, /* TK_ILLEGAL */ - TK_INSERT = 277, /* TK_INSERT */ - TK_INT = 278, /* TK_INT */ - TK_INTEGER = 279, /* TK_INTEGER */ - TK_INTO = 280, /* TK_INTO */ - TK_IS = 281, /* TK_IS */ - TK_KEY = 282, /* TK_KEY */ - TK_LE = 283, /* TK_LE */ - TK_LONG = 284, /* TK_LONG */ - TK_LONGCHAR = 285, /* TK_LONGCHAR */ - TK_LP = 286, /* TK_LP */ - TK_LT = 287, /* TK_LT */ - TK_LOCALIZABLE = 288, /* TK_LOCALIZABLE */ - TK_MINUS = 289, /* TK_MINUS */ - TK_NE = 290, /* TK_NE */ - TK_NOT = 291, /* TK_NOT */ - TK_NULL = 292, /* TK_NULL */ - TK_OBJECT = 293, /* TK_OBJECT */ - TK_OR = 294, /* TK_OR */ - TK_ORDER = 295, /* TK_ORDER */ - TK_PRIMARY = 296, /* TK_PRIMARY */ - TK_RP = 297, /* TK_RP */ - TK_SELECT = 298, /* TK_SELECT */ - TK_SET = 299, /* TK_SET */ - TK_SHORT = 300, /* TK_SHORT */ - TK_SPACE = 301, /* TK_SPACE */ - TK_STAR = 302, /* TK_STAR */ - TK_STRING = 303, /* TK_STRING */ - TK_TABLE = 304, /* TK_TABLE */ - TK_TEMPORARY = 305, /* TK_TEMPORARY */ - TK_UPDATE = 306, /* TK_UPDATE */ - TK_VALUES = 307, /* TK_VALUES */ - TK_WHERE = 308, /* TK_WHERE */ - TK_WILDCARD = 309, /* TK_WILDCARD */ - END_OF_FILE = 310, /* END_OF_FILE */ - ILLEGAL = 311, /* ILLEGAL */ - SPACE = 312, /* SPACE */ - UNCLOSED_STRING = 313, /* UNCLOSED_STRING */ - COMMENT = 314, /* COMMENT */ - FUNCTION = 315, /* FUNCTION */ - COLUMN = 316, /* COLUMN */ - TK_LIKE = 318, /* TK_LIKE */ - TK_NEGATION = 319 /* TK_NEGATION */ - }; - - /// - /// Symbol kind. - /// - internal enum yysymbol_kind_t - { - YYSYMBOL_YYEMPTY = -2, - YYSYMBOL_YYEOF = 0, /* "end of file" */ - YYSYMBOL_YYerror = 1, /* error */ - YYSYMBOL_YYUNDEF = 2, /* "invalid token" */ - YYSYMBOL_TK_ALTER = 3, /* TK_ALTER */ - YYSYMBOL_TK_AND = 4, /* TK_AND */ - YYSYMBOL_TK_BY = 5, /* TK_BY */ - YYSYMBOL_TK_CHAR = 6, /* TK_CHAR */ - YYSYMBOL_TK_COMMA = 7, /* TK_COMMA */ - YYSYMBOL_TK_CREATE = 8, /* TK_CREATE */ - YYSYMBOL_TK_DELETE = 9, /* TK_DELETE */ - YYSYMBOL_TK_DROP = 10, /* TK_DROP */ - YYSYMBOL_TK_DISTINCT = 11, /* TK_DISTINCT */ - YYSYMBOL_TK_DOT = 12, /* TK_DOT */ - YYSYMBOL_TK_EQ = 13, /* TK_EQ */ - YYSYMBOL_TK_FREE = 14, /* TK_FREE */ - YYSYMBOL_TK_FROM = 15, /* TK_FROM */ - YYSYMBOL_TK_GE = 16, /* TK_GE */ - YYSYMBOL_TK_GT = 17, /* TK_GT */ - YYSYMBOL_TK_HOLD = 18, /* TK_HOLD */ - YYSYMBOL_TK_ADD = 19, /* TK_ADD */ - YYSYMBOL_TK_ID = 20, /* TK_ID */ - YYSYMBOL_TK_ILLEGAL = 21, /* TK_ILLEGAL */ - YYSYMBOL_TK_INSERT = 22, /* TK_INSERT */ - YYSYMBOL_TK_INT = 23, /* TK_INT */ - YYSYMBOL_TK_INTEGER = 24, /* TK_INTEGER */ - YYSYMBOL_TK_INTO = 25, /* TK_INTO */ - YYSYMBOL_TK_IS = 26, /* TK_IS */ - YYSYMBOL_TK_KEY = 27, /* TK_KEY */ - YYSYMBOL_TK_LE = 28, /* TK_LE */ - YYSYMBOL_TK_LONG = 29, /* TK_LONG */ - YYSYMBOL_TK_LONGCHAR = 30, /* TK_LONGCHAR */ - YYSYMBOL_TK_LP = 31, /* TK_LP */ - YYSYMBOL_TK_LT = 32, /* TK_LT */ - YYSYMBOL_TK_LOCALIZABLE = 33, /* TK_LOCALIZABLE */ - YYSYMBOL_TK_MINUS = 34, /* TK_MINUS */ - YYSYMBOL_TK_NE = 35, /* TK_NE */ - YYSYMBOL_TK_NOT = 36, /* TK_NOT */ - YYSYMBOL_TK_NULL = 37, /* TK_NULL */ - YYSYMBOL_TK_OBJECT = 38, /* TK_OBJECT */ - YYSYMBOL_TK_OR = 39, /* TK_OR */ - YYSYMBOL_TK_ORDER = 40, /* TK_ORDER */ - YYSYMBOL_TK_PRIMARY = 41, /* TK_PRIMARY */ - YYSYMBOL_TK_RP = 42, /* TK_RP */ - YYSYMBOL_TK_SELECT = 43, /* TK_SELECT */ - YYSYMBOL_TK_SET = 44, /* TK_SET */ - YYSYMBOL_TK_SHORT = 45, /* TK_SHORT */ - YYSYMBOL_TK_SPACE = 46, /* TK_SPACE */ - YYSYMBOL_TK_STAR = 47, /* TK_STAR */ - YYSYMBOL_TK_STRING = 48, /* TK_STRING */ - YYSYMBOL_TK_TABLE = 49, /* TK_TABLE */ - YYSYMBOL_TK_TEMPORARY = 50, /* TK_TEMPORARY */ - YYSYMBOL_TK_UPDATE = 51, /* TK_UPDATE */ - YYSYMBOL_TK_VALUES = 52, /* TK_VALUES */ - YYSYMBOL_TK_WHERE = 53, /* TK_WHERE */ - YYSYMBOL_TK_WILDCARD = 54, /* TK_WILDCARD */ - YYSYMBOL_END_OF_FILE = 55, /* END_OF_FILE */ - YYSYMBOL_ILLEGAL = 56, /* ILLEGAL */ - YYSYMBOL_SPACE = 57, /* SPACE */ - YYSYMBOL_UNCLOSED_STRING = 58, /* UNCLOSED_STRING */ - YYSYMBOL_COMMENT = 59, /* COMMENT */ - YYSYMBOL_FUNCTION = 60, /* FUNCTION */ - YYSYMBOL_COLUMN = 61, /* COLUMN */ - YYSYMBOL_62_AGG_FUNCTION_ = 62, /* AGG_FUNCTION. */ - YYSYMBOL_TK_LIKE = 63, /* TK_LIKE */ - YYSYMBOL_TK_NEGATION = 64, /* TK_NEGATION */ - YYSYMBOL_YYACCEPT = 65, /* $accept */ - YYSYMBOL_query = 66, /* query */ - YYSYMBOL_onequery = 67, /* onequery */ - YYSYMBOL_oneinsert = 68, /* oneinsert */ - YYSYMBOL_onecreate = 69, /* onecreate */ - YYSYMBOL_oneupdate = 70, /* oneupdate */ - YYSYMBOL_onedelete = 71, /* onedelete */ - YYSYMBOL_onealter = 72, /* onealter */ - YYSYMBOL_alterop = 73, /* alterop */ - YYSYMBOL_onedrop = 74, /* onedrop */ - YYSYMBOL_table_def = 75, /* table_def */ - YYSYMBOL_column_def = 76, /* column_def */ - YYSYMBOL_column_and_type = 77, /* column_and_type */ - YYSYMBOL_column_type = 78, /* column_type */ - YYSYMBOL_data_type_l = 79, /* data_type_l */ - YYSYMBOL_data_type = 80, /* data_type */ - YYSYMBOL_data_count = 81, /* data_count */ - YYSYMBOL_oneselect = 82, /* oneselect */ - YYSYMBOL_selectfrom = 83, /* selectfrom */ - YYSYMBOL_selcollist = 84, /* selcollist */ - YYSYMBOL_collist = 85, /* collist */ - YYSYMBOL_from = 86, /* from */ - YYSYMBOL_unorderdfrom = 87, /* unorderdfrom */ - YYSYMBOL_tablelist = 88, /* tablelist */ - YYSYMBOL_expr = 89, /* expr */ - YYSYMBOL_val = 90, /* val */ - YYSYMBOL_constlist = 91, /* constlist */ - YYSYMBOL_update_assign_list = 92, /* update_assign_list */ - YYSYMBOL_column_assignment = 93, /* column_assignment */ - YYSYMBOL_const_val = 94, /* const_val */ - YYSYMBOL_column_val = 95, /* column_val */ - YYSYMBOL_column = 96, /* column */ - YYSYMBOL_selcolumn = 97, /* selcolumn */ - YYSYMBOL_table = 98, /* table */ - YYSYMBOL_id = 99, /* id */ - YYSYMBOL_string = 100, /* string */ - YYSYMBOL_number = 101 /* number */ - }; - - #endregion - - #region Classes - - internal class sql_str - { - public string Data { get; set; } - - public int Len { get; set; } - } - - internal class complex_expr - { - public int Op { get; set; } - - public expr Left { get; set; } - - public expr Right { get; set; } - } - - internal class ext_column - { - // TODO: Make into struct of { column, table } - public Tuple Unparsed - { - get => o as Tuple; - set => o = value; - } - - // TODO: Make into struct of { column, table } - public Tuple Parsed - { - get => o as Tuple; - set => o = value; - } - - private object o = new object(); - } - - internal class expr - { - public int Type { get; set; } - - #region Union (u) - - public complex_expr Expr - { - get => u as complex_expr; - set => u = value; - } - - public int IVal - { - get - { - if (u is int) - return (int)u; - - return default; - } - set => u = value; - } - - public uint UVal - { - get - { - if (u is uint) - return (uint)u; - - return default; - } - set => u = value; - } - - public string SVal - { - get => u as string; - set => u = value; - } - - public ext_column Column - { - get => u as ext_column; - set => u = value; - } - - private object u = new object(); - - #endregion - } - - #endregion - - #region Generated Classes - - /// - /// Value type. - /// - /// Union - internal class SQL_STYPE - { - public sql_str Str { get; set; } - - public string String { get; set; } - - public column_info ColumnList { get; set; } - - public LibmsiView Query { get; set; } - - public expr Expr { get; set; } - - public ushort ColumnType { get; set; } - - public int Integer { get; set; } - } - - #endregion - - internal class LibmsiSQLInput - { - #region Constants - - public const int OP_EQ = 1; - public const int OP_AND = 2; - public const int OP_OR = 3; - public const int OP_GT = 4; - public const int OP_LT = 5; - public const int OP_LE = 6; - public const int OP_GE = 7; - public const int OP_NE = 8; - public const int OP_ISNULL = 9; - public const int OP_NOTNULL = 10; - - public const int EXPR_COMPLEX = 1; - public const int EXPR_COLUMN = 2; - public const int EXPR_COL_NUMBER = 3; - public const int EXPR_IVAL = 4; - public const int EXPR_SVAL = 5; - public const int EXPR_UVAL = 6; - public const int EXPR_STRCMP = 7; - public const int EXPR_WILDCARD = 9; - public const int EXPR_COL_NUMBER_STRING = 10; - public const int EXPR_COL_NUMBER32 = 11; - public const int EXPR_UNARY = 12; - - #endregion - - #region Generated Constants - - /// - /// Identify Bison output, and Bison version. - /// - private const int YYBISON = 30802; - - /// - /// Bison version string. - /// - private const string YYBISON_VERSION = "3.8.2"; - - /// - /// Skeleton name. - /// - private const string YYSKELETON_NAME = "yacc.c"; - - /// - /// Pure parsers. - /// - private const int YYPURE = 1; - - /// - /// Push parsers. - /// - /// Pull parsers. - /// - private const int YYPULL = 1; - - /// - /// State number of the termination state. - /// - private const int YYFINAL = 36; - - /// - /// Last index in YYTABLE. - /// - private const int YYLAST = 156; - - /// - /// Number of terminals. - /// - private const int YYNTOKENS = 65; - - /// - /// Number of nonterminals. - /// - private const int YYNNTS = 37; - - /// - /// Number of rules. - /// - private const int YYNRULES = 87; - - /// - /// Number of states. - /// - private const int YYNSTATES = 154; - - /// - /// Last valid token kind. - /// - private const int YYMAXUTOK = 319; - - /// - /// Symbol number corresponding to TOKEN-NUM as returned by Lexer. - /// - private static readonly byte[] yytranslate = - { - 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 1, 2, 3, 4, - 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, - 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, - 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, - 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, - 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, - 55, 56, 57, 58, 59, 60, 61, 62, 63, 64 - }; - - private const int YYPACK_NINF = -80; - - private const int YYTABLE_NINF = -85; - - /// - /// Index in YYTABLE of the portion describing STATE-NUM. - /// - private static readonly short[] yypact = - { - 36, -44, -39, -1, -26, 9, 50, 37, 56, -80, - -80, -80, -80, -80, -80, -80, -80, 37, 37, 37, - -80, 25, 37, 37, -18, -80, -80, -80, -80, -1, - 78, 47, 76, -80, 57, -80, -80, 105, 72, 51, - 55, 100, -80, 81, -80, -80, -18, 37, 37, -80, - -80, 37, -80, 37, 62, 37, -12, -12, -80, -80, - 63, 102, 108, 101, 76, 97, 45, 83, 2, -80, - 62, 3, 94, -80, -80, 126, -80, -80, 127, 93, - 62, 37, 52, 37, -80, 106, -80, -80, -80, -80, - -80, -80, 31, 103, 118, 37, 111, 11, 62, 62, - 60, 60, 60, -11, 60, 60, 60, -12, 88, 3, - -80, -80, 117, -80, -80, -80, -80, -80, 117, -80, - -80, 107, -80, -80, -12, -80, -80, 138, -80, -80, - -80, -80, -80, 109, -80, -80, -80, -80, -80, 112, - -80, 110, -80, -80, -80, -80, 52, -80, 113, 140, - 95, 52, -80, -80 - }; - - /// - /// Default reduction number in state STATE-NUM. - /// Performed when YYTABLE does not specify something else to do. Zero - /// means the default is an error. - /// - private static readonly byte[] yydefact = - { - 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, - 5, 4, 6, 7, 8, 9, 3, 0, 0, 0, - 16, 51, 0, 0, 0, 85, 45, 86, 40, 0, - 43, 0, 82, 83, 0, 84, 1, 0, 0, 52, - 54, 0, 22, 0, 41, 42, 0, 0, 0, 21, - 20, 0, 17, 0, 0, 0, 0, 0, 44, 81, - 15, 71, 0, 0, 80, 18, 0, 0, 0, 25, - 0, 53, 0, 78, 55, 54, 48, 50, 46, 0, - 0, 0, 0, 0, 19, 32, 36, 37, 34, 38, - 35, 26, 27, 30, 12, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 14, - 72, 87, 0, 76, 77, 73, 74, 79, 0, 28, - 29, 0, 13, 24, 0, 56, 57, 58, 59, 68, - 67, 63, 60, 0, 65, 62, 61, 64, 47, 0, - 75, 0, 39, 31, 23, 66, 0, 33, 0, 69, - 10, 0, 11, 70 - }; - - private static readonly sbyte[] yypgoto = - { - -80, -80, -80, -80, -80, -80, -80, -80, -80, -80, - -80, -80, -47, -80, -80, -80, -80, -80, 124, 104, - -53, 120, -80, 96, 19, 26, 5, 73, -80, -79, - -7, -29, -80, 14, -6, -80, -16 - }; - - private static readonly byte[] yydefgoto = - { - 0, 8, 9, 10, 11, 12, 13, 14, 52, 15, - 67, 68, 65, 91, 92, 93, 141, 16, 28, 29, - 77, 20, 21, 39, 71, 128, 148, 60, 61, 129, - 130, 73, 30, 63, 64, 33, 116 - }; - - /// - /// What to do in state STATE-NUM. If - /// positive, shift that token. If negative, reduce the rule whose - /// number is the opposite. If YYTABLE_NINF, syntax error. - /// - private static readonly short[] yytable = - { - 32, 35, 25, 115, 79, 17, 69, 98, 25, 95, - 18, 35, 35, 35, 19, 98, 35, 35, 32, 62, - 31, 34, 66, 22, 66, 133, 134, 78, 78, 26, - 27, 37, 38, 40, 23, 76, 42, 43, 31, 1, - 32, 59, 99, 96, 2, 3, 4, 72, 123, 35, - 99, 85, 62, 125, 138, -49, 36, 25, 5, 47, - 31, 24, 55, 72, 119, 41, 66, 149, 86, 75, - 25, 144, 149, 72, 87, 88, 111, 117, 78, 6, - 25, 120, 25, 89, 111, 46, 112, 7, -84, 97, - 90, 72, 72, 70, 112, 78, 140, 26, 27, 109, - 113, 48, 142, 53, 54, 56, 114, 100, 113, 81, - 101, 102, 57, 83, 114, 84, 80, 126, 127, 49, - 103, 82, 104, 50, 51, 94, 105, 131, 132, 106, - 135, 136, 137, 55, 107, 108, 122, 118, 124, 121, - 139, 111, 98, 146, 143, 152, 145, 151, 44, 45, - 58, 74, 147, 0, 110, 150, 153 - }; - - private static readonly short[] yycheck = - { - 6, 7, 20, 82, 57, 49, 53, 4, 20, 7, - 49, 17, 18, 19, 15, 4, 22, 23, 24, 48, - 6, 7, 51, 49, 53, 36, 37, 56, 57, 47, - 48, 17, 18, 19, 25, 47, 22, 23, 24, 3, - 46, 47, 39, 41, 8, 9, 10, 54, 95, 55, - 39, 6, 81, 42, 107, 0, 0, 20, 22, 12, - 46, 11, 7, 70, 33, 40, 95, 146, 23, 55, - 20, 124, 151, 80, 29, 30, 24, 83, 107, 43, - 20, 50, 20, 38, 24, 7, 34, 51, 12, 70, - 45, 98, 99, 31, 34, 124, 112, 47, 48, 80, - 48, 44, 118, 31, 53, 5, 54, 13, 48, 7, - 16, 17, 31, 12, 54, 18, 53, 98, 99, 14, - 26, 13, 28, 18, 19, 42, 32, 101, 102, 35, - 104, 105, 106, 7, 7, 42, 18, 31, 27, 36, - 52, 24, 4, 31, 37, 50, 37, 7, 24, 29, - 46, 55, 42, -1, 81, 42, 151 - }; - - /// - /// The symbol kind of the accessing symbol of state STATE-NUM. - /// - private static readonly byte[] yystos = - { - 0, 3, 8, 9, 10, 22, 43, 51, 66, 67, - 68, 69, 70, 71, 72, 74, 82, 49, 49, 15, - 86, 87, 49, 25, 11, 20, 47, 48, 83, 84, - 97, 98, 99, 100, 98, 99, 0, 98, 98, 88, - 98, 40, 98, 98, 83, 86, 7, 12, 44, 14, - 18, 19, 73, 31, 53, 7, 5, 31, 84, 99, - 92, 93, 96, 98, 99, 77, 96, 75, 76, 77, - 31, 89, 95, 96, 88, 98, 47, 85, 96, 85, - 53, 7, 13, 12, 18, 6, 23, 29, 30, 38, - 45, 78, 79, 80, 42, 7, 41, 89, 4, 39, - 13, 16, 17, 26, 28, 32, 35, 7, 42, 89, - 92, 24, 34, 48, 54, 94, 101, 99, 31, 33, - 50, 36, 18, 77, 27, 42, 89, 89, 90, 94, - 95, 90, 90, 36, 37, 90, 90, 90, 85, 52, - 101, 81, 101, 37, 85, 37, 31, 42, 91, 94, - 42, 7, 50, 91 - }; - - /// - /// Symbol kind of the left-hand side of rule RULE-NUM. - /// - private static readonly byte[] yyr1 = - { - 0, 65, 66, 67, 67, 67, 67, 67, 67, 67, - 68, 68, 69, 69, 70, 70, 71, 72, 72, 72, - 73, 73, 74, 75, 76, 76, 77, 78, 78, 78, - 79, 79, 80, 80, 80, 80, 80, 80, 80, 81, - 82, 82, 83, 84, 84, 84, 85, 85, 85, 86, - 86, 86, 87, 87, 88, 88, 89, 89, 89, 89, - 89, 89, 89, 89, 89, 89, 89, 90, 90, 91, - 91, 92, 92, 93, 94, 94, 94, 94, 95, 96, - 96, 97, 97, 97, 98, 99, 100, 101 - }; - - /// - /// Number of symbols on the right-hand side of rule RULE-NUM. - /// - private static readonly byte[] yyr2 = - { - 0, 2, 1, 1, 1, 1, 1, 1, 1, 1, - 10, 11, 6, 7, 6, 4, 2, 4, 5, 6, - 1, 1, 3, 4, 3, 1, 2, 1, 2, 2, - 1, 3, 1, 4, 1, 1, 1, 1, 1, 1, - 2, 3, 2, 1, 3, 1, 1, 3, 1, 2, - 4, 1, 2, 4, 1, 3, 3, 3, 3, 3, - 3, 3, 3, 3, 3, 3, 4, 1, 1, 1, - 3, 1, 3, 3, 1, 2, 1, 1, 1, 3, - 1, 3, 1, 1, 1, 1, 1, 1 - }; - - private const int YYENOMEM = -2; - - /// - /// Initial size of the parser's stacks. - /// - private const int YYINITDEPTH = 200; - - /// - /// Maximum size the stacks can grow to (effective only - /// if the built-in stack extension method is used). - /// - /// - /// Do not make this value too large; the results are undefined if - /// YYSTACK_ALLOC_MAXIMUM < YYSTACK_BYTES (YYMAXDEPTH) - /// evaluated with infinite-precision integer arithmetic. - /// - private const int YYMAXDEPTH = 10000; - - #endregion - - #region Properties - - public LibmsiDatabase Database { get; set; } - - public string Command { get; set; } - - public int N { get; set; } - - public int Len { get; set; } - - public LibmsiResult R { get; set; } - - /// - /// View structure for the resulting query. This value - /// tracks the view currently being created so we can free - /// this view on syntax error. - /// - public LibmsiView View { get; set; } - - public LinkedList Mem { get; set; } = new LinkedList(); - - #endregion - - #region Functions - - public static LibmsiResult ParseSQL(LibmsiDatabase db, string command, out LibmsiView phview, LinkedList mem) - { - phview = null; - LibmsiSQLInput sql = new LibmsiSQLInput - { - Database = db, - Command = command, - N = 0, - Len = 0, - R = LibmsiResult.LIBMSI_RESULT_BAD_QUERY_SYNTAX, - View = phview, - Mem = mem, - }; - - LibmsiResult r = sql.Parse(); - if (r != LibmsiResult.LIBMSI_RESULT_SUCCESS) - { - if (sql.View != null) - { - sql.View.Delete(); - sql.View = null; - } - - return sql.R; - } - - return LibmsiResult.LIBMSI_RESULT_SUCCESS; - } - - #endregion - - #region Generated Functions - - /// - /// Symbol number corresponding to TOKEN-NUM - /// as returned by yylex, with out-of-bounds checking. - /// - private yysymbol_kind_t YYTRANSLATE(sql_tokentype yyx) - => (0 <= yyx && (int)yyx <= YYMAXUTOK) ? (yysymbol_kind_t)yytranslate[(int)yyx] : yysymbol_kind_t.YYSYMBOL_YYUNDEF; - - /// - /// yyparse. - /// - public LibmsiResult Parse() - { - // Lookahead token kind. - sql_tokentype yychar; - - // The semantic value of the lookahead symbol. - SQL_STYPE yylval = new SQL_STYPE(); - - // Number of syntax errors so far. - int sql_nerrs = 0; - - int yystate = 0; - - // Number of tokens to shift before error messages enabled. - int yyerrstatus = 0; - - // Refer to the stacks through separate pointers, to allow yyoverflow - // to reallocate them elsewhere. - - // Their size. - long yystacksize = YYINITDEPTH; - - // The state stack: array, bottom, top. - byte[] yyssa = new byte[YYINITDEPTH]; - int yyss = 0; // yyssa[0] - int yyssp = yyss; // yyssa[0] - - // The semantic value stack: array, bottom, top. - SQL_STYPE[] yyvsa = new SQL_STYPE[YYINITDEPTH]; - int yyvs = 0; // yyvsa[0] - int yyvsp = 0; // yyvsa[0] - - int yyn; - - // The return value of yyparse. - LibmsiResult yyresult; - - // Lookahead symbol kind. - yysymbol_kind_t yytoken = yysymbol_kind_t.YYSYMBOL_YYEMPTY; - - // The variables used to return semantic value and location from the action routines. - SQL_STYPE yyval = new SQL_STYPE(); - - // The number of symbols on the RHS of the reduced rule. - // Keep to zero when no symbol should be popped. - int yylen = 0; - - // YYDPRINTF ((stderr, "Starting parse\n")); - - yychar = sql_tokentype.SQL_EMPTY; // Cause a token to be read. - - goto yysetstate; - - #region yynewstate -- push a new state, which is found in yystate. - - yynewstate: - // In all cases, when you get here, the value and location stacks - // have just been pushed. So pushing a state here evens the stacks. - yyssp++; - - #endregion - - #region yysetstate -- set current state (the top of the stack) to yystate. - - yysetstate: - //YYDPRINTF ((stderr, "Entering state %d\n", yystate)); - //YY_ASSERT (0 <= yystate && yystate < YYNSTATES); - yyssa[yyssp] = (byte)yystate; - - if (yyss + yystacksize - 1 <= yyssp) - { - // Get the current used size of the three stacks, in elements. - int yysize = yyssp - yyss + 1; - - // Extend the stack our own way. - if (YYMAXDEPTH <= yystacksize) - goto yyexhaustedlab; - - yystacksize *= 2; - if (YYMAXDEPTH < yystacksize) - yystacksize = YYMAXDEPTH; - - yyssp = yyss + yysize - 1; - yyvsp = yyvs + yysize - 1; - - // YYDPRINTF ((stderr, "Stack size increased to %ld\n", YY_CAST (long, yystacksize))); - if (yyss + yystacksize - 1 <= yyssp) - goto yyabortlab; - } - - if (yystate == YYFINAL) - goto yyacceptlab; - - goto yybackup; - - #endregion - - #region yybackup. - - yybackup: - // Do appropriate processing given the current state. Read a - // lookahead token if we need one and don't already have one. - - // First try to decide what to do without reference to lookahead token. - yyn = yypact[yystate]; - if (yyn == YYPACK_NINF) - goto yydefault; - - // Not known => get a lookahead token if don't already have one. - - // YYCHAR is either empty, or end-of-input, or a valid lookahead. - if (yychar == sql_tokentype.SQL_EMPTY) - { - //YYDPRINTF ((stderr, "Reading a token\n")); - yychar = Lexer(ref yylval); - } - - if (yychar <= sql_tokentype.SQL_EOF) - { - yychar = sql_tokentype.SQL_EOF; - yytoken = yysymbol_kind_t.YYSYMBOL_YYEOF; - //YYDPRINTF ((stderr, "Now at end of input.\n")); - } - else if (yychar == sql_tokentype.SQL_error) - { - // The scanner already issued an error message, process directly - // to error recovery. But do not keep the error token as - // lookahead, it is too special and may lead us to an endless - // loop in error recovery. - yychar = sql_tokentype.SQL_UNDEF; - yytoken = yysymbol_kind_t.YYSYMBOL_YYerror; - goto yyerrlab1; - } - else - { - yytoken = YYTRANSLATE(yychar); - //YY_SYMBOL_PRINT("Next token is", yytoken, &yylval, &yylloc); - } - - // If the proper action on seeing token YYTOKEN is to reduce or to - // detect an error, take that action. - yyn += (int)yytoken; - if (yyn < 0 || YYLAST < yyn || yycheck[yyn] != (short)yytoken) - goto yydefault; - - yyn = yytable[yyn]; - if (yyn <= 0) - { - yyn = -yyn; - goto yyreduce; - } - - // Count tokens shifted since error; after three, turn off error status. - if (yyerrstatus != 0) - yyerrstatus--; - - // Shift the lookahead token. - yystate = yyn; - yyvsa[++yyvsp] = yylval; - - // Discard the shifted token. - yychar = sql_tokentype.SQL_EMPTY; - goto yynewstate; - - #endregion - - #region yydefault -- do the default action for the current state. - - yydefault: - yyn = yydefact[yystate]; - if (yyn == 0) - goto yyerrlab; - - goto yyreduce; - - #endregion - - #region yyreduce -- do a reduction. - - yyreduce: - // yyn is the number of a rule to reduce with. - yylen = yyr2[yyn]; - - /* If YYLEN is nonzero, implement the default value of the action: - '$$ = $1'. - - Otherwise, the following line sets YYVAL to garbage. - This behavior is undocumented and Bison - users should not rely upon it. Assigning to YYVAL - unconditionally makes the parser a bit smaller, and it avoids a - GCC warning that YYVAL may be used uninitialized. */ - - yyval = yyvsa[yyvsp + 1 - yylen]; - switch (yyn) - { - case 2: /* query: onequery */ - { - View = (yyvsa[yyvsp].Query); - break; - } - - case 10: /* oneinsert: TK_INSERT TK_INTO table TK_LP collist TK_RP TK_VALUES TK_LP constlist TK_RP */ - { - LibmsiInsertView.Create(Database, out LibmsiView insert, (yyvsa[yyvsp + -7].String), (yyvsa[yyvsp + -5].ColumnList), (yyvsa[yyvsp + -1].ColumnList), false); - if (insert == null) - goto yyabortlab; - - View = insert; - yyval.Query = insert; - break; - } - - case 11: /* oneinsert: TK_INSERT TK_INTO table TK_LP collist TK_RP TK_VALUES TK_LP constlist TK_RP TK_TEMPORARY */ - { - LibmsiInsertView.Create(Database, out LibmsiView insert, (yyvsa[yyvsp + -8].String), (yyvsa[yyvsp + -6].ColumnList), (yyvsa[yyvsp + -2].ColumnList), true); - if (insert == null) - goto yyabortlab; - - View = insert; - yyval.Query = insert; - break; - } - - case 12: /* onecreate: TK_CREATE TK_TABLE table TK_LP table_def TK_RP */ - { - if ((yyvsa[yyvsp + -1].ColumnList) == null) - goto yyabortlab; - - LibmsiResult r = LibmsiCreateView.Create(Database, out LibmsiView create, (yyvsa[yyvsp + -3].String), (yyvsa[yyvsp + -1].ColumnList), false); - if (create == null) - { - R = r; - goto yyabortlab; - } - - View = create; - yyval.Query = create; - break; - } - - case 13: /* onecreate: TK_CREATE TK_TABLE table TK_LP table_def TK_RP TK_HOLD */ - { - if ((yyvsa[yyvsp + -2].ColumnList) == null) - goto yyabortlab; - - LibmsiCreateView.Create(Database, out LibmsiView create, (yyvsa[yyvsp + -4].String), (yyvsa[yyvsp + -2].ColumnList), true); - if (create == null) - goto yyabortlab; - - View = create; - yyval.Query = create; - break; - } - - case 14: /* oneupdate: TK_UPDATE table TK_SET update_assign_list TK_WHERE expr */ - { - LibmsiUpdateView.Create(Database, out LibmsiView update, (yyvsa[yyvsp + -4].String), (yyvsa[yyvsp + -2].ColumnList), (yyvsa[yyvsp + 0].Expr)); - if (update == null) - goto yyabortlab; - - View = update; - yyval.Query = update; - break; - } - - case 15: /* oneupdate: TK_UPDATE table TK_SET update_assign_list */ - { - LibmsiUpdateView.Create(Database, out LibmsiView update, (yyvsa[yyvsp + -2].String), (yyvsa[yyvsp + 0].ColumnList), null); - if (update == null) - goto yyabortlab; - - View = update; - yyval.Query = update; - break; - } - - case 16: /* onedelete: TK_DELETE from */ - { - LibmsiDeleteView.Create(Database, out LibmsiView delete, (yyvsa[yyvsp + 0].Query)); - if (delete == null) - goto yyabortlab; - - View = delete; - yyval.Query = delete; - break; - } - - case 17: /* onealter: TK_ALTER TK_TABLE table alterop */ - { - LibmsiAlterView.Create(Database, out LibmsiView alter, (yyvsa[yyvsp + -1].String), null, (yyvsa[yyvsp + 0].Integer)); - if (alter == null) - goto yyabortlab; - - View = alter; - yyval.Query = alter; - break; - } - - case 18: /* onealter: TK_ALTER TK_TABLE table TK_ADD column_and_type */ - { - LibmsiAlterView.Create(Database, out LibmsiView alter, (yyvsa[yyvsp + -2].String), (yyvsa[yyvsp + 0].ColumnList), 0); - if (alter == null) - goto yyabortlab; - - View = alter; - yyval.Query = alter; - break; - } - - case 19: /* onealter: TK_ALTER TK_TABLE table TK_ADD column_and_type TK_HOLD */ - { - LibmsiAlterView.Create(Database, out LibmsiView alter, (yyvsa[yyvsp + -3].String), (yyvsa[yyvsp + -1].ColumnList), 1); - if (alter == null) - goto yyabortlab; - - View = alter; - yyval.Query = alter; - break; - } - - case 20: /* alterop: TK_HOLD */ - { - (yyval.Integer) = 1; - break; - } - - case 21: /* alterop: TK_FREE */ - { - (yyval.Integer) = -1; - break; - } - - case 22: /* onedrop: TK_DROP TK_TABLE table */ - { - LibmsiResult r = LibmsiDropView.Create(Database, out LibmsiView drop, (yyvsa[yyvsp + 0].String)); - if (r != LibmsiResult.LIBMSI_RESULT_SUCCESS || (yyval.Query == null)) - goto yyabortlab; - - View = drop; - yyval.Query = drop; - break; - } - - case 23: /* table_def: column_def TK_PRIMARY TK_KEY collist */ - { - column_info column_list = yyvsa[yyvsp + -3].ColumnList; - if (MarkPrimaryKeys(ref column_list, (yyvsa[yyvsp + 0].ColumnList))) - { - yyvsa[yyvsp + -3].ColumnList = column_list; - (yyval.ColumnList) = (yyvsa[yyvsp + -3].ColumnList); - } - else - { - yyvsa[yyvsp + -3].ColumnList = column_list; - (yyval.ColumnList) = null; - } - - break; - } - - case 24: /* column_def: column_def TK_COMMA column_and_type */ - { - column_info ci; - for (ci = (yyvsa[yyvsp + -2].ColumnList); ci.Next != null; ci = ci.Next); - - ci.Next = (yyvsa[yyvsp + 0].ColumnList); - (yyval.ColumnList) = (yyvsa[yyvsp + -2].ColumnList); - break; - } - - case 25: /* column_def: column_and_type */ - { - (yyval.ColumnList) = (yyvsa[yyvsp + 0].ColumnList); - break; - } - - case 26: /* column_and_type: column column_type */ - { - (yyval.ColumnList) = (yyvsa[yyvsp + -1].ColumnList); - (yyval.ColumnList).Type = ((yyvsa[yyvsp + 0].ColumnType) | MSITYPE_VALID); - (yyval.ColumnList).Temporary = ((yyvsa[yyvsp + 0].ColumnType) & MSITYPE_TEMPORARY) != 0 ? true : false; - break; - } - - case 27: /* column_type: data_type_l */ - { - (yyval.ColumnType) = (yyvsa[yyvsp + 0].ColumnType); - break; - } - - case 28: /* column_type: data_type_l TK_LOCALIZABLE */ - { - (yyval.ColumnType) = (ushort)((yyvsa[yyvsp + -1].ColumnType) | MSITYPE_LOCALIZABLE); - break; - } - - case 29: /* column_type: data_type_l TK_TEMPORARY */ - { - (yyval.ColumnType) = (ushort)((yyvsa[yyvsp + -1].ColumnType) | MSITYPE_TEMPORARY); - break; - } - - case 30: /* data_type_l: data_type */ - { - (yyval.ColumnType) |= MSITYPE_NULLABLE; - break; - } - - case 31: /* data_type_l: data_type TK_NOT TK_NULL */ - { - (yyval.ColumnType) = (yyvsa[yyvsp + -2].ColumnType); - break; - } - - case 32: /* data_type: TK_CHAR */ - { - (yyval.ColumnType) = MSITYPE_STRING | 1; - break; - } - - case 33: /* data_type: TK_CHAR TK_LP data_count TK_RP */ - { - (yyval.ColumnType) = (ushort)(MSITYPE_STRING | 0x400 | (yyvsa[yyvsp + -1].ColumnType)); - break; - } - - case 34: /* data_type: TK_LONGCHAR */ - { - (yyval.ColumnType) = MSITYPE_STRING | 0x400; - break; - } - - case 35: /* data_type: TK_SHORT */ - { - (yyval.ColumnType) = 2 | 0x400; - break; - } - - case 36: /* data_type: TK_INT */ - { - (yyval.ColumnType) = 2 | 0x400; - break; - } - - case 37: /* data_type: TK_LONG */ - { - (yyval.ColumnType) = 4; - break; - } - - case 38: /* data_type: TK_OBJECT */ - { - (yyval.ColumnType) = MSITYPE_STRING | MSITYPE_VALID; - break; - } - - case 39: /* data_count: number */ - { - if (((yyvsa[yyvsp + 0].Integer) > 255) || ((yyvsa[yyvsp + 0].Integer) < 0)) - goto yyabortlab; - - (yyval.ColumnType) = (ushort)(yyvsa[yyvsp + 0].Integer); - break; - } - - case 40: /* oneselect: TK_SELECT selectfrom */ - { - (yyval.Query) = (yyvsa[yyvsp + 0].Query); - break; - } - - case 41: /* oneselect: TK_SELECT TK_DISTINCT selectfrom */ - { - LibmsiResult r = LibmsiDistinctView.Create(Database, out LibmsiView distinct, (yyvsa[yyvsp + 0].Query)); - if (r != LibmsiResult.LIBMSI_RESULT_SUCCESS) - goto yyabortlab; - - View = distinct; - yyval.Query = distinct; - break; - } - - case 42: /* selectfrom: selcollist from */ - { - if ((yyvsa[yyvsp + -1].ColumnList) != null) - { - LibmsiResult r = LibmsiSelectView.Create(Database, out LibmsiView select, (yyvsa[yyvsp + 0].Query), (yyvsa[yyvsp + -1].ColumnList)); - if (r != LibmsiResult .LIBMSI_RESULT_SUCCESS) - goto yyabortlab; - - View = select; - yyval.Query = select; - } - else - { - (yyval.Query) = (yyvsa[yyvsp + 0].Query); - } - - break; - } - - case 44: /* selcollist: selcolumn TK_COMMA selcollist */ - { - (yyvsa[yyvsp + -2].ColumnList).Next = (yyvsa[yyvsp + 0].ColumnList); - break; - } - - case 45: /* selcollist: TK_STAR */ - { - (yyval.ColumnList) = null; - break; - } - - case 47: /* collist: column TK_COMMA collist */ - { - (yyvsa[yyvsp + -2].ColumnList).Next = (yyvsa[yyvsp + 0].ColumnList); - break; - } - - case 48: /* collist: TK_STAR */ - { - (yyval.ColumnList) = null; - break; - } - - case 49: /* from: TK_FROM table */ - { - LibmsiResult r = LibmsiTableView.Create(Database, (yyvsa[yyvsp + 0].String), out LibmsiView table); - if (r != LibmsiResult.LIBMSI_RESULT_SUCCESS || (yyval.Query == null)) - goto yyabortlab; - - View = table; - yyval.Query = table; - break; - } - - case 50: /* from: unorderdfrom TK_ORDER TK_BY collist */ - { - if ((yyvsa[yyvsp + 0].ColumnList) != null) - { - LibmsiResult r = (yyvsa[yyvsp + -3].Query).Sort(yyvsa[yyvsp + 0].ColumnList); - if (r != LibmsiResult.LIBMSI_RESULT_SUCCESS) - goto yyabortlab; - } - - (yyval.Query) = (yyvsa[yyvsp + -3].Query); - break; - } - - case 52: /* unorderdfrom: TK_FROM tablelist */ - { - LibmsiResult r = LibmsiWhereView.Create(Database, out LibmsiView where, (yyvsa[yyvsp + 0].String), null); - if (r != LibmsiResult.LIBMSI_RESULT_SUCCESS) - goto yyabortlab; - - View = where; - yyval.Query = where; - break; - } - - case 53: /* unorderdfrom: TK_FROM tablelist TK_WHERE expr */ - { - LibmsiResult r = LibmsiWhereView.Create(Database, out LibmsiView where, (yyvsa[yyvsp + -2].String), (yyvsa[yyvsp + 0].Expr)); - if (r != LibmsiResult.LIBMSI_RESULT_SUCCESS) - goto yyabortlab; - - View = where; - yyval.Query = where; - break; - } - - case 54: /* tablelist: table */ - { - (yyval.String) = (yyvsa[yyvsp + 0].String); - break; - } - - case 55: /* tablelist: table TK_COMMA tablelist */ - { - (yyval.String) = AddTable((yyvsa[yyvsp + 0].String), (yyvsa[yyvsp + -2].String)); - if (yyval.String == null) - goto yyabortlab; - - break; - } - - case 56: /* expr: TK_LP expr TK_RP */ - { - (yyval.Expr) = (yyvsa[yyvsp + -1].Expr); - if (yyval.Expr == null) - goto yyabortlab; - - break; - } - - case 57: /* expr: expr TK_AND expr */ - { - (yyval.Expr) = BuildExprComplex((yyvsa[yyvsp + -2].Expr), OP_AND, (yyvsa[yyvsp + 0].Expr)); - if (yyval.Expr == null) - goto yyabortlab; - - break; - } - - case 58: /* expr: expr TK_OR expr */ - { - (yyval.Expr) = BuildExprComplex((yyvsa[yyvsp + -2].Expr), OP_OR, (yyvsa[yyvsp + 0].Expr)); - if (yyval.Expr == null) - goto yyabortlab; - - break; - } - - case 59: /* expr: column_val TK_EQ val */ - { - (yyval.Expr) = BuildExprComplex((yyvsa[yyvsp + -2].Expr), OP_EQ, (yyvsa[yyvsp + 0].Expr)); - if (yyval.Expr == null) - goto yyabortlab; - - break; - } - - case 60: /* expr: column_val TK_GT val */ - { - (yyval.Expr) = BuildExprComplex((yyvsa[yyvsp + -2].Expr), OP_GT, (yyvsa[yyvsp + 0].Expr)); - if (yyval.Expr == null) - goto yyabortlab; - - break; - } - - case 61: /* expr: column_val TK_LT val */ - { - (yyval.Expr) = BuildExprComplex((yyvsa[yyvsp + -2].Expr), OP_LT, (yyvsa[yyvsp + 0].Expr)); - if (yyval.Expr == null) - goto yyabortlab; - - break; - } - - case 62: /* expr: column_val TK_LE val */ - { - (yyval.Expr) = BuildExprComplex((yyvsa[yyvsp + -2].Expr), OP_LE, (yyvsa[yyvsp + 0].Expr)); - if (yyval.Expr == null) - goto yyabortlab; - - break; - } - - case 63: /* expr: column_val TK_GE val */ - { - (yyval.Expr) = BuildExprComplex((yyvsa[yyvsp + -2].Expr), OP_GE, (yyvsa[yyvsp + 0].Expr)); - if (yyval.Expr == null) - goto yyabortlab; - - break; - } - - case 64: /* expr: column_val TK_NE val */ - { - (yyval.Expr) = BuildExprComplex((yyvsa[yyvsp + -2].Expr), OP_NE, (yyvsa[yyvsp + 0].Expr)); - if (yyval.Expr == null) - goto yyabortlab; - - break; - } - - case 65: /* expr: column_val TK_IS TK_NULL */ - { - (yyval.Expr) = BuildExprUnary((yyvsa[yyvsp + -2].Expr), OP_ISNULL); - if (yyval.Expr == null) - goto yyabortlab; - - break; - } - - case 66: /* expr: column_val TK_IS TK_NOT TK_NULL */ - { - (yyval.Expr) = BuildExprUnary((yyvsa[yyvsp + -3].Expr), OP_NOTNULL); - if (yyval.Expr == null) - goto yyabortlab; - - break; - } - - case 69: /* constlist: const_val */ - { - (yyval.ColumnList) = AllocColumn(null, null); - if (yyval.ColumnList == null) - goto yyabortlab; - - (yyval.ColumnList).Val = (yyvsa[yyvsp + 0].Expr); - break; - } - - case 70: /* constlist: const_val TK_COMMA constlist */ - { - (yyval.ColumnList) = AllocColumn(null, null); - if (yyval.ColumnList == null) - goto yyabortlab; - - (yyval.ColumnList).Val = (yyvsa[yyvsp + -2].Expr); - (yyval.ColumnList).Next = (yyvsa[yyvsp + 0].ColumnList); - break; - } - - case 72: /* update_assign_list: column_assignment TK_COMMA update_assign_list */ - { - (yyval.ColumnList) = (yyvsa[yyvsp + -2].ColumnList); - (yyval.ColumnList).Next = (yyvsa[yyvsp + 0].ColumnList); - break; - } - - case 73: /* column_assignment: column TK_EQ const_val */ - { - (yyval.ColumnList) = (yyvsa[yyvsp + -2].ColumnList); - (yyval.ColumnList).Val = (yyvsa[yyvsp + 0].Expr); - break; - } - - case 74: /* const_val: number */ - { - (yyval.Expr) = BuildExprIVal(yyvsa[yyvsp + 0].Integer); - if (yyval.Expr == null) - goto yyabortlab; - - break; - } - - case 75: /* const_val: TK_MINUS number */ - { - (yyval.Expr) = BuildExprIVal(-(yyvsa[yyvsp + 0].Integer)); - if (yyval.Expr == null) - goto yyabortlab; - - break; - } - - case 76: /* const_val: TK_STRING */ - { - (yyval.Expr) = BuildExprSVal((yyvsa[yyvsp + 0].Str)); - if (yyval.Expr == null) - goto yyabortlab; - - break; - } - - case 77: /* const_val: TK_WILDCARD */ - { - (yyval.Expr) = BuildExprWildcard(); - if (yyval.Expr == null) - goto yyabortlab; - - break; - } - - case 78: /* column_val: column */ - { - (yyval.Expr) = BuildExprColumn((yyvsa[yyvsp + 0].ColumnList)); - if (yyval.Expr == null) - goto yyabortlab; - - break; - } - - case 79: /* column: table TK_DOT id */ - { - (yyval.ColumnList) = AllocColumn((yyvsa[yyvsp + -2].String), (yyvsa[yyvsp + 0].String)); - if (yyval.ColumnList == null) - goto yyabortlab; - - break; - } - - case 80: /* column: id */ - { - (yyval.ColumnList) = AllocColumn(null, (yyvsa[yyvsp + 0].String)); - if (yyval.ColumnList == null) - goto yyabortlab; - - break; - } - - case 81: /* selcolumn: table TK_DOT id */ - { - (yyval.ColumnList) = AllocColumn((yyvsa[yyvsp + -2].String), (yyvsa[yyvsp + 0].String)); - if (yyval.ColumnList == null) - goto yyabortlab; - - break; - } - - case 82: /* selcolumn: id */ - { - (yyval.ColumnList) = AllocColumn(null, (yyvsa[yyvsp + 0].String)); - if (yyval.ColumnList == null) - goto yyabortlab; - - break; - } - - case 83: /* selcolumn: string */ - { - (yyval.ColumnList) = AllocColumn(null, (yyvsa[yyvsp + 0].String)); - if (yyval.ColumnList == null) - goto yyabortlab; - - break; - } - - case 84: /* table: id */ - { - (yyval.String) = (yyvsa[yyvsp + 0].String); - break; - } - - case 85: /* id: TK_ID */ - { - if (UnescapeString((yyvsa[yyvsp + 0].Str), out string str) != LibmsiResult.LIBMSI_RESULT_SUCCESS || str == null) - goto yyabortlab; - - yyval.String = str; - break; - } - - case 86: /* string: TK_STRING */ - { - if (UnescapeString((yyvsa[yyvsp + 0].Str),out string str) != LibmsiResult.LIBMSI_RESULT_SUCCESS || str == null) - goto yyabortlab; - - yyval.String = str; - break; - } - - case 87: /* number: TK_INTEGER */ - { - (yyval.Integer) = StringToInt(); - break; - } - - default: - break; - } - - /* User semantic actions sometimes alter yychar, and that requires - that yytoken be updated with the new translation. We take the - approach of translating immediately before every use of yytoken. - One alternative is translating here after every semantic action, - but that translation would be missed if the semantic action invokes - goto yyabortlab, goto yyacceptlab, or goto yyerrorlab immediately after altering yychar or - if it invokes YYBACKUP. In the case of goto yyabortlab or goto yyacceptlab, an - incorrect destructor might then be invoked immediately. In the - case of goto yyerrorlab or YYBACKUP, subsequent parser actions might lead - to an incorrect destructor call or verbose syntax error message - before the lookahead is translated. */ - - yyvsp -= yylen; - yyssp -= yylen; - yylen = 0; - - yyvsa[++yyvsp] = yyval; - - // Now 'shift' the result of the reduction. Determine what state - // that goes to, based on the state we popped back to and the rule - // number reduced by. - { - int yylhs = yyr1[yyn] - YYNTOKENS; - int yyi = yypgoto[yylhs] + yyssa[yyssp]; - yystate = (0 <= yyi && yyi <= YYLAST && yycheck[yyi] == yyssa[yyssp] ? yytable[yyi] : yydefgoto[yylhs]); - } - - goto yynewstate; - - #endregion - - #region yyerrlab -- here on detecting error. - - yyerrlab: - // Make sure we have latest lookahead translation. See comments at - // user semantic actions for why this is necessary. - yytoken = yychar == sql_tokentype.SQL_EMPTY ? yysymbol_kind_t.YYSYMBOL_YYEMPTY : YYTRANSLATE (yychar); - - // If not already recovering from an error, report this error. - if (yyerrstatus == 0) - { - ++sql_nerrs; - Console.Error.WriteLine("Syntax error"); - } - - if (yyerrstatus == 3) - { - // If just tried and failed to reuse lookahead token after an error, discard it. - if (yychar <= sql_tokentype.SQL_EOF) - { - // Return failure if at end of input. - if (yychar ==sql_tokentype. SQL_EOF) - goto yyabortlab; - } - else - { - yychar = sql_tokentype.SQL_EMPTY; - } - } - - // Else will try to reuse lookahead token after shifting the error token. - goto yyerrlab1; - - #endregion - - #region yyerrorlab -- error raised explicitly by goto yyerrorlab. - - yyerrorlab: - - ++sql_nerrs; - - // Do not reclaim the symbols of the rule whose action triggered this goto yyerrorlab. - yyvsp -= yylen; - yyssp -= yylen; - yylen = 0; - yystate = yyssa[yyssp]; - goto yyerrlab1; - - #endregion - - #region yyerrlab1 -- common code for both syntax error and goto yyerrorlab. - - yyerrlab1: - yyerrstatus = 3; // Each real token shifted decrements this. - - // Pop stack until we find a state that shifts the error token. - for (;;) - { - yyn = yypact[yystate]; - if (yyn != YYPACK_NINF) - { - yyn += (int)yysymbol_kind_t.YYSYMBOL_YYerror; - if (0 <= yyn && yyn <= YYLAST && yycheck[yyn] == (short)yysymbol_kind_t.YYSYMBOL_YYerror) - { - yyn = yytable[yyn]; - if (0 < yyn) - break; - } - } - - // Pop the current state because it cannot handle the error token. - if (yyssp == yyss) - goto yyabortlab; - - - yyvsp -= 1; - yyssp -= 1; - yystate = yyssa[yyssp]; - } - - yyvsa[++yyvsp] = yylval; - - // Shift the error token. - yystate = yyn; - goto yynewstate; - - #endregion - - #region yyacceptlab -- goto yyacceptlab comes here. - - yyacceptlab: - yyresult = LibmsiResult.LIBMSI_RESULT_SUCCESS; - goto yyreturnlab; - - #endregion - - #region yyabortlab -- goto yyabortlab comes here. - - yyabortlab: - yyresult = LibmsiResult.LIBMSI_RESULT_ACCESS_DENIED; - goto yyreturnlab; - - #endregion - - #region yyexhaustedlab -- goto yyexhaustedlab (memory exhaustion) comes here. - - yyexhaustedlab: - Console.Error.WriteLine("Memory exhausted"); - yyresult = LibmsiResult.LIBMSI_RESULT_INVALID_HANDLE; - goto yyreturnlab; - - #endregion - - #region yyreturnlab -- parsing is finished, clean up and return. - - yyreturnlab: - if (yychar != sql_tokentype.SQL_EMPTY) - { - // Make sure we have latest lookahead translation. See comments at - // user semantic actions for why this is necessary. - yytoken = YYTRANSLATE (yychar); - } - - // Do not reclaim the symbols of the rule whose action triggered - // this goto yyabortlab or goto yyacceptlab. - yyvsp -= yylen; - yyssp -= yylen; - - while (yyssp != yyss) - { - yyvsp -= 1; - yyssp -= 1; - } - - #endregion - - return yyresult; - } - - #endregion - - #region Utilities - - private string AddTable(string list, string table) - { - string str = $"{list} \0{table}"; - Mem.AddLast(str); - return str; - } - - private column_info AllocColumn(string table, string column) - { - column_info col = new column_info - { - Table = table, - Column = column, - Val = null, - Type = 0, - Next = null, - }; - - Mem.AddLast(col); - return col; - } - - private sql_tokentype Lexer(ref SQL_STYPE SQL_lval) - { - sql_tokentype token; - do - { - N += Len; - if (N >= Command.Length || Command[N] == '\0') - return 0; // End of input - - Len = Tokenize.GetToken(Command, N, out token, out int skip); - if (Len == 0) - break; - - SQL_lval.Str = new sql_str - { - Data = Command.Substring(N, Len), - Len = Len, - }; - - N += skip; - } - while (token == sql_tokentype.TK_SPACE); - - return token; - } - - private LibmsiResult UnescapeString(sql_str strdata, out string str) - { - str = null; - int p = 0; // strdata.Data[0] - int len = strdata.Len; - - // Match quotes - if (((strdata.Data[0]=='`') && (strdata.Data[len - 1]!='`')) || ((strdata.Data[0]=='\'') && (strdata.Data[len - 1]!='\''))) - return LibmsiResult.LIBMSI_RESULT_FUNCTION_FAILED; - - // If there's quotes, remove them - if (((strdata.Data[0]=='`') && (strdata.Data[len - 1]=='`')) || ((strdata.Data[0]=='\'') && (strdata.Data[len - 1]=='\''))) - { - p++; - len -= 2; - } - - str = strdata.Data.Substring(p, len); - Mem.AddLast(str); - return LibmsiResult.LIBMSI_RESULT_SUCCESS; - } - - private int StringToInt() - { - int r = 0; - for (int i = 0; i < Len; i++) - { - if ('0' > Command[N + i] || '9' < Command[N + i]) - { - Console.Error.WriteLine("Should only be numbers here!"); - break; - } - - r = (Command[N + i] - '0') + r * 10; - } - - return r; - } - - private expr BuildExprWildcard() - { - expr e = new expr { Type = EXPR_WILDCARD }; - Mem.AddLast(e); - return e; - } - - private expr BuildExprComplex(expr l, int op, expr r) - { - expr e = new expr - { - Type = EXPR_COMPLEX, - Expr = new complex_expr - { - Left = l, - Op = op, - Right = r, - } - }; - Mem.AddLast(e); - return e; - } - - private expr BuildExprUnary(expr l, int op) - { - expr e = new expr - { - Type = EXPR_UNARY, - Expr = new complex_expr - { - Left = l, - Op = op, - Right = null, - } - }; - Mem.AddLast(e); - return e; - } - - private expr BuildExprColumn(column_info column) - { - expr e = new expr - { - Type = EXPR_COLUMN, - Column = new ext_column - { - Unparsed = new Tuple(column.Column, column.Table), - } - }; - Mem.AddLast(e); - return e; - } - - private expr BuildExprIVal(int val) - { - expr e = new expr - { - Type = EXPR_IVAL, - IVal = val, - }; - Mem.AddLast(e); - return e; - } - - private expr BuildExprSVal(sql_str str) - { - if (UnescapeString(str, out string unescaped) != LibmsiResult.LIBMSI_RESULT_SUCCESS) - return null; - - expr e = new expr - { - Type = EXPR_SVAL, - SVal = unescaped, - }; - Mem.AddLast(e); - return e; - } - - private void SwapColumns(ref column_info cols, column_info A, int idx) - { - int i = 0; - - column_info ptr = cols, preA = null, preB = null, B = null; - while (ptr != null) - { - if (i++ == idx) - B = ptr; - else if (B != null) - preB = ptr; - - if (ptr.Next == A) - preA = ptr; - - ptr = ptr.Next; - } - - if (preB != null) - preB.Next = A; - if (preA != null) - preA.Next = B; - - ptr = A.Next; - A.Next = B.Next; - B.Next = ptr; - - if (idx == 0) - cols = A; - } - - private bool MarkPrimaryKeys(ref column_info cols, column_info keys) - { - column_info k; - bool found = true; - int count; - - for (k = keys, count = 0; k != null && found; k = k.Next, count++) - { - column_info c; - int idx; - - found = false; - for (c = cols, idx = 0; c != null && !found; c = c.Next, idx++) - { - if (k.Column != c.Column) - continue; - - c.Type |= MSITYPE_KEY; - found = true; - if (idx != count) - SwapColumns(ref cols, c, count); - } - } - - return found; - } - - #endregion - } -} \ No newline at end of file diff --git a/BurnOutSharp/External/libmsi/Internal/LibmsiTable.cs b/BurnOutSharp/External/libmsi/Internal/LibmsiTable.cs deleted file mode 100644 index 1a3b13a5..00000000 --- a/BurnOutSharp/External/libmsi/Internal/LibmsiTable.cs +++ /dev/null @@ -1,1593 +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 LibGSF.Output; -using LibMSI.Views; -using static LibMSI.LibmsiTypes; -using static LibMSI.Internal.MsiPriv; -using static LibMSI.Internal.StringTable; - -namespace LibMSI.Internal -{ - internal class LibmsiColumnInfo - { - public string TableName { get; set; } - - public int Number { get; set; } - - public string ColName { get; set; } - - public int Type { get; set; } - - public int Offset { get; set; } - - public int RefCount { get; set; } - - public bool Temporary { get; set; } - - public LibmsiColumnHashEntry[] HashTable { get; set; } - } - - internal class TRANSFORMDATA - { - public string Name { get; set; } - } - - internal class LibmsiTable - { - #region Constants - - public const int LibmsiTable_HASH_TABLE_SIZE = 37; - - public const string szDot = "."; - - // Information for default tables - public const string szTables = "_Tables"; - public const string szTable = "Table"; - public const string szColumns = "_Columns"; - public const string szNumber = "Number"; - public const string szType = "Type"; - - public static readonly LibmsiColumnInfo[] _Columns_cols = new LibmsiColumnInfo[] - { - new LibmsiColumnInfo - { - TableName = szColumns, - Number = 1, - ColName = szTable, - Type = MSITYPE_VALID | MSITYPE_STRING | MSITYPE_KEY | 64, - Offset = 0, - RefCount = 0, - Temporary = false, - HashTable = null - }, - new LibmsiColumnInfo - { - TableName = szColumns, - Number = 2, - ColName = szNumber, - Type = MSITYPE_VALID | MSITYPE_KEY | 2, - Offset = 2, - RefCount = 0, - Temporary = false, - HashTable = null - }, - new LibmsiColumnInfo - { - TableName = szColumns, - Number = 3, - ColName = szName, - Type = MSITYPE_VALID | MSITYPE_STRING | 64, - Offset = 4, - RefCount = 0, - Temporary = false, - HashTable = null - }, - new LibmsiColumnInfo - { - TableName = szColumns, - Number = 4, - ColName = szType, - Type = MSITYPE_VALID | 2, - Offset = 6, - RefCount = 0, - Temporary = false, - HashTable = null - }, - }; - - public static readonly LibmsiColumnInfo[] _Tables_cols = new LibmsiColumnInfo[] - { - new LibmsiColumnInfo - { - TableName = szTables, - Number = 1, - ColName = szName, - Type = MSITYPE_VALID | MSITYPE_STRING | MSITYPE_KEY | 64, - Offset = 0, - RefCount = 0, - Temporary = false, - HashTable = null - }, - }; - - private const int MAX_STREAM_NAME = 0x1f; - - #endregion - - #region Properties - - public byte[][] Data { get; set; } - - public bool[] DataPersistent { get; set; } - - public int RowCount { get; set; } - - public LibmsiColumnInfo[] ColInfo { get; set; } - - public int ColCount { get; set; } - - public LibmsiCondition Persistent { get; set; } - - public int RefCount { get; set; } - - public string Name { get; set; } = string.Empty; - - #endregion - - #region Functions - - public static int BytesPerColumn(LibmsiColumnInfo col, int bytes_per_strref) - { - if (MSITYPE_IS_BINARY(col.Type)) - return 2; - - if ((col.Type & MSITYPE_STRING) != 0) - return bytes_per_strref; - - if ((col.Type & 0xff) <= 2) - return 2; - - if ((col.Type & 0xff) != 4) - Console.Error.WriteLine("Invalid column size!"); - - return 4; - } - - public static string EncodeStreamName(bool bTable, string input) - { - int count = MAX_STREAM_NAME; - int next; - - if (!bTable) - count = input.Length + 2; - - byte[] output = new byte[count * 3]; - int p = 0; // output[0] - - if (bTable) - { - // UTF-8 encoding of 0x4840. - output[p++] = 0xe4; - output[p++] = 0xa1; - output[p++] = 0x80; - count--; - } - - int inputPtr = 0; // input[0] - while (count-- != 0) - { - int ch = inputPtr < input.Length ? input[inputPtr++] : 0; - if (ch == 0) - { - output[p] = (byte)ch; - return Encoding.UTF8.GetString(output); - } - - if ((ch < 0x80) && (Utf2Mime(ch) >= 0)) - { - ch = Utf2Mime(ch); - - if (inputPtr < input.Length) - { - next = input[inputPtr]; - if (next != 0 && (next < 0x80)) - next = Utf2Mime(next); - else - next = -1; - } - else - { - next = -1; - } - - if (next == -1) - { - // UTF-8 encoding of 0x4800..0x483f. - output[p++] = 0xe4; - output[p++] = 0xa0; - output[p++] = (byte)(0x80 | ch); - } - else - { - // UTF-8 encoding of 0x3800..0x47ff. - output[p++] = (byte)(0xe3 + (next >> 5)); - output[p++] = (byte)(0xa0 ^ next); - output[p++] = (byte)(0x80 | ch); - inputPtr++; - } - } - else - { - output[p++] = (byte)ch; - } - } - - Console.Error.WriteLine($"Failed to encode stream name ({input})"); - return null; - } - - public static string DecodeStreamName(string input) - { - if (input == null) - return null; - - int count = 0; - byte[] inputBytes = Encoding.UTF8.GetBytes(input); - int p = 0; // inputBytes[0] - - byte[] output = new byte[inputBytes.Length + 1]; - int q = 0; // output[0] - while (p < inputBytes.Length && inputBytes[p] != 0) - { - int ch = inputBytes[p]; - if ((ch == 0xe3 && inputBytes[p + 1] >= 0xa0) || (ch == 0xe4 && inputBytes[p + 1] < 0xa0)) - { - // UTF-8 encoding of 0x3800..0x47ff. - output[q++] = (byte)Mime2Utf(inputBytes[p + 2] & 0x7f); - output[q++] = (byte)Mime2Utf(inputBytes[p + 1] ^ 0xa0); - p += 3; - count += 2; - continue; - } - - if (ch == 0xe4 && inputBytes[p + 1] == 0xa0) - { - // UTF-8 encoding of 0x4800..0x483f. - output[q++] = (byte)Mime2Utf(inputBytes[p + 2] & 0x7f); - p += 3; - count++; - continue; - } - - output[q++] = inputBytes[p++]; - if (ch >= 0xc1) - output[q++] = inputBytes[p++]; - if (ch >= 0xe0) - output[q++] = inputBytes[p++]; - if (ch >= 0xf0) - output[q++] = inputBytes[p++]; - - count++; - } - - output[q] = 0; - return Encoding.ASCII.GetString(output); - } - - public static void EnumStreamNames(GsfInfile stg) - { - int n = stg.NumChildren(); - for (int i = 0; i < n; i++) - { - string stname = stg.NameByIndex(i); - if (stname == null) - continue; - - string name = DecodeStreamName(stname); - Console.WriteLine($"Stream {n} . {stname} {name}"); - } - } - - public static LibmsiResult ReadStreamData(GsfInfile stg, string stname, out byte[] pdata, out int psz) - { - pdata = null; psz = 0; - if (stg == null) - return LibmsiResult.LIBMSI_RESULT_FUNCTION_FAILED; - - string encname = EncodeStreamName(true, stname).TrimEnd('\0'); - - Exception err = null; - GsfInput stm = stg.ChildByName(encname, ref err); - if (stm == null) - return LibmsiResult.LIBMSI_RESULT_FUNCTION_FAILED; - - if ((stm.Size >> 32) != 0) - { - Console.Error.WriteLine("Too big!"); - return LibmsiResult.LIBMSI_RESULT_FUNCTION_FAILED; - } - - int sz = (int)stm.Size; - byte[] data; - if (sz == 0) - { - data = null; - } - else - { - data = new byte[sz]; - if (stm.Read(sz, data) == null) - { - Console.Error.WriteLine("Read stream failed"); - return LibmsiResult.LIBMSI_RESULT_FUNCTION_FAILED; - } - } - - pdata = data; - psz = sz; - return LibmsiResult.LIBMSI_RESULT_SUCCESS; - } - - // TODO: Move to LibmsiDatabase - public static LibmsiResult WriteStreamData(LibmsiDatabase db, string stname, byte[] data, int sz) - { - if (db.Outfile == null) - return LibmsiResult.LIBMSI_RESULT_FUNCTION_FAILED; - - string encname = EncodeStreamName(true, stname).TrimEnd('\0'); - GsfOutput stm = db.Outfile.NewChild(encname, false); - - if (stm == null) - { - Console.Error.WriteLine("Open stream failed"); - return LibmsiResult.LIBMSI_RESULT_FUNCTION_FAILED; - } - - if (!stm.Write(sz, data)) - { - Console.Error.WriteLine("Failed to Write"); - stm.Close(); - return LibmsiResult.LIBMSI_RESULT_FUNCTION_FAILED; - } - - stm.Close(); - return LibmsiResult.LIBMSI_RESULT_SUCCESS; - } - - // TODO: Move to LibmsiDatabase - public static int GetRowSize(LibmsiColumnInfo[] cols, int count, int bytes_per_strref) - { - if (count == 0) - return 0; - - if (bytes_per_strref != LONG_STR_BYTES) - { - int size = 0; - for (int i = 0; i < count; i++) - { - size += BytesPerColumn(cols[i], bytes_per_strref); - } - - return size; - } - - LibmsiColumnInfo last_col = cols[count - 1]; - return last_col.Offset + BytesPerColumn(last_col, bytes_per_strref); - } - - // TODO: Move to LibmsiDatabase - public static void FreeCachedTables(LibmsiDatabase db) - { - db.Tables.Clear(); - } - - // TODO: Move to LibmsiDatabase - public static LibmsiResult OpenTable(LibmsiDatabase db, string name, bool encoded) - { - string decname = null; - byte[] name8 = Encoding.UTF8.GetBytes(name); - - if (encoded) - { - //assert(name8[0] == 0xe4 && name8[1] == 0xa1 && name8[2] == 0x80); - decname = DecodeStreamName(name.Substring(1)); - } - - LibmsiTable table = new LibmsiTable - { - Persistent = LibmsiCondition.LIBMSI_CONDITION_TRUE, - Name = name, - }; - - if (name == szTables || name == szColumns) - table.Persistent = LibmsiCondition.LIBMSI_CONDITION_NONE; - - db.Tables.AddLast(table); - return LibmsiResult.LIBMSI_RESULT_SUCCESS; - } - - // TODO: Move to LibmsiDatabase - public static LibmsiResult GetTable(LibmsiDatabase db, string name, out LibmsiTable table_ret) - { - table_ret = null; - LibmsiResult r; - - // First, see if the table is cached - LibmsiTable table = FindCachedTable(db, name); - if (table == null) - { - // Nonexistent tables should be interpreted as empty tables - r = OpenTable(db, name, false); - if (r != LibmsiResult.LIBMSI_RESULT_SUCCESS) - return r; - - table = FindCachedTable(db, name); - } - - if (table.ColInfo != null) - { - table_ret = table; - return LibmsiResult.LIBMSI_RESULT_SUCCESS; - } - - r = TableGetColumnInfo(db, name, out LibmsiColumnInfo[] col_info, out int col_count); - if (r != LibmsiResult.LIBMSI_RESULT_SUCCESS) - return r; - - table.ColInfo = col_info; - table.ColCount = col_count; - - r = ReadTableFromStorage(db, table, db.Infile); - if (r != LibmsiResult.LIBMSI_RESULT_SUCCESS) - return r; - - table_ret = table; - return LibmsiResult.LIBMSI_RESULT_SUCCESS; - } - - public static int ReadTableInt(byte[][] data, int row, int col, int bytes) - { - int ret = 0; - for (int i = 0; i < bytes; i++) - { - ret += data[row][col + i] << i * 8; - } - - return ret; - } - - // TODO: Move to LibmsiDatabase - public static LibmsiResult CreateTable(LibmsiDatabase db, string name, column_info col_info, LibmsiCondition persistent) - { - StringPersistence string_persistence = (persistent != LibmsiCondition.LIBMSI_CONDITION_FALSE) ? StringPersistence.StringPersistent : StringPersistence.StringNonPersistent; - int nField; - LibmsiRecord rec = null; - - // Only add tables that don't exist already - if (TableViewExists(db, name)) - { - Console.Error.WriteLine($"Table {name} exists"); - return LibmsiResult.LIBMSI_RESULT_BAD_QUERY_SYNTAX; - } - - LibmsiTable table = new LibmsiTable - { - RefCount = 1, - RowCount = 0, - Data = null, - DataPersistent = null, - ColInfo = null, - ColCount = 0, - Persistent = persistent, - Name = name, - }; - - column_info col = col_info; - for (; col != null; col = col.Next) - { - table.ColCount++; - } - - table.ColInfo = new LibmsiColumnInfo[table.ColCount]; - - int i = 0; col = col_info; - for (; col != null; i++, col = col.Next) - { - int table_id = db.Strings.AddString(col.Table, -1, 1, string_persistence); - int col_id = db.Strings.AddString(col.Column, -1, 1, string_persistence); - - table.ColInfo[i] = new LibmsiColumnInfo - { - TableName = db.Strings.LookupId(table_id), - Number = i + 1, - ColName = db.Strings.LookupId(col_id), - Type = col.Type, - Offset = 0, - RefCount = 0, - HashTable = null, - Temporary = col.Temporary, - }; - } - - CalcColumnOffsets(table.ColInfo, table.ColCount); - - LibmsiResult r = LibmsiTableView.Create(db, szTables, out LibmsiView tv); - if (r != LibmsiResult.LIBMSI_RESULT_SUCCESS) - return r; - - r = tv.Execute(null); - if (r != LibmsiResult.LIBMSI_RESULT_SUCCESS) - goto err; - - rec = LibmsiRecord.Create(1); - if (rec == null) - goto err; - - r = rec.SetString(1, name) ? LibmsiResult.LIBMSI_RESULT_SUCCESS : LibmsiResult.LIBMSI_RESULT_FUNCTION_FAILED; - if (r != LibmsiResult.LIBMSI_RESULT_SUCCESS) - goto err; - - r = tv.InsertRow(rec, -1, persistent == LibmsiCondition.LIBMSI_CONDITION_FALSE); - if (r != LibmsiResult.LIBMSI_RESULT_SUCCESS) - goto err; - - tv.Delete(); - tv = null; - - rec = null; - - if (persistent != LibmsiCondition.LIBMSI_CONDITION_FALSE) - { - // Add each column to the _Columns table - r = LibmsiTableView.Create(db, szColumns, out tv); - if (r != LibmsiResult.LIBMSI_RESULT_SUCCESS) - return r; - - r = tv.Execute(null); - if (r != LibmsiResult.LIBMSI_RESULT_SUCCESS) - goto err; - - rec = LibmsiRecord.Create(4); - if (rec == null) - goto err; - - if (!rec.SetString(1, name)) - goto err; - - // Need to set the table, column number, col name and type - // for each column we enter in the table - nField = 1; - for (col = col_info; col != null; col = col.Next) - { - if (!rec.SetInt(2, nField) - || !rec.SetString(3, col.Column) - || !rec.SetInt(4, col.Type)) - { - goto err; - } - - r = tv.InsertRow(rec, -1, false); - if (r != LibmsiResult.LIBMSI_RESULT_SUCCESS) - goto err; - - nField++; - } - - if (col == null) - r = LibmsiResult.LIBMSI_RESULT_SUCCESS; - } - - err: - // FIXME: remove values from the string table on error - if (tv != null) - tv.Delete(); - - if (r == LibmsiResult.LIBMSI_RESULT_SUCCESS) - db.Tables.AddLast(table); - - return r; - } - - // TODO: Move to LibmsiDatabase - public static void UpdateTableColumns(LibmsiDatabase db, string name) - { - LibmsiTable table = FindCachedTable(db, name); - int old_count = table.ColCount; - table.ColInfo = null; - - TableGetColumnInfo(db, name, out LibmsiColumnInfo[] col_info, out int col_count); - table.ColInfo = col_info; - table.ColCount = col_count; - - if (table.ColCount == 0) - return; - - int size = GetRowSize(table.ColInfo, table.ColCount, LONG_STR_BYTES); - int offset = table.ColInfo[table.ColCount - 1].Offset; - - for (int n = 0; n < table.RowCount; n++) - { - byte[] tempData = table.Data[n]; - Array.Resize(ref tempData, size); - table.Data[n] = tempData; - - if (old_count < table.ColCount) - { - for (int i = offset; i < size; i++) - { - table.Data[n][i] = 0; - } - } - } - } - - /// - /// Try to find the table name in the _Tables table - /// - // TODO: Move to LibmsiDatabase - public static bool TableViewExists(LibmsiDatabase db, string name) - { - if (name == szTables || name == szColumns || name == szStreams || name == szStorages) - return true; - - LibmsiResult r = db.Strings.IdFromStringUTF8(name, out int table_id); - if (r != LibmsiResult.LIBMSI_RESULT_SUCCESS) - return false; - - r = GetTable(db, szTables, out LibmsiTable table); - if (r != LibmsiResult.LIBMSI_RESULT_SUCCESS) - { - Console.Error.WriteLine($"Table {szTables} not available"); - return false; - } - - for (int i = 0; i < table.RowCount; i++) - { - if (ReadTableInt(table.Data, i, 0, LONG_STR_BYTES) == table_id) - return true; - } - - return false; - } - - // TODO: Move to LibmsiDatabase - public static LibmsiResult AddStream(LibmsiDatabase db, string name, GsfInput data) - { - LibmsiRecord rec = LibmsiRecord.Create(2); - if (rec == null) - return LibmsiResult.LIBMSI_RESULT_OUTOFMEMORY; - - if (!rec.SetString(1, name)) - return LibmsiResult.LIBMSI_RESULT_SUCCESS; - - LibmsiResult r = rec.SetGsfInput(2, data); - if (r != LibmsiResult.LIBMSI_RESULT_SUCCESS) - return r; - - string insert = "INSERT INTO `_Streams`(`Name`, `Data`) VALUES (?, ?)"; - Exception err = null; - LibmsiQuery query = LibmsiQuery.Create(db, insert, ref err); - return query.Execute(rec); - } - - // TODO: Move to LibmsiDatabase - public static LibmsiResult DatabseCommitTables(LibmsiDatabase db, int bytes_per_strref) - { - LibmsiResult r = LibmsiResult.LIBMSI_RESULT_SUCCESS; - - // Ensure the Tables stream is written. - GetTable(db, szTables, out LibmsiTable t); - - foreach (LibmsiTable table in db.Tables) - { - r = GetTable(db, table.Name, out t); - if (r != LibmsiResult.LIBMSI_RESULT_SUCCESS) - { - Console.Error.WriteLine($"Failed to load table {table.Name} (r={r})"); - return r; - } - - r = SaveTable(db, table, bytes_per_strref); - if (r != LibmsiResult.LIBMSI_RESULT_SUCCESS) - { - Console.Error.WriteLine($"Failed to save table {table.Name} (r={r})"); - return r; - } - } - - db.Tables.Clear(); - - return r; - } - - // TODO: Move to LibmsiDatabase - public static LibmsiCondition IsTablePersistent(LibmsiDatabase db, string table) - { - if (table == null) - return LibmsiCondition.LIBMSI_CONDITION_ERROR; - - LibmsiResult r = GetTable(db, table, out LibmsiTable t); - if (r != LibmsiResult.LIBMSI_RESULT_SUCCESS) - return LibmsiCondition.LIBMSI_CONDITION_NONE; - - return t.Persistent; - } - - // TODO: Move to LibmsiTableView - public static LibmsiResult FindRow(LibmsiTableView tv, LibmsiRecord rec, out int row, out int column) - { - row = 0; column = 0; - int[] data = RecordToRow(tv, rec); - if (data == null) - return LibmsiResult.LIBMSI_RESULT_FUNCTION_FAILED; - - LibmsiResult r = LibmsiResult.LIBMSI_RESULT_FUNCTION_FAILED; - for (int i = 0; i < tv.Table.RowCount; i++) - { - r = RowMatches(tv, i, data, out column); - if (r == LibmsiResult.LIBMSI_RESULT_SUCCESS) - { - row = i; - break; - } - } - - return r; - } - - /// - /// Enumerate the table transforms in a transform storage and apply each one. - /// - // TODO: Move to LibmsiDatabase - public static LibmsiResult ApplyTransform(LibmsiDatabase db, GsfInfile stg) - { - TRANSFORMDATA tables = null, columns = null; - LibmsiResult ret = LibmsiResult.LIBMSI_RESULT_FUNCTION_FAILED; - - StringTable strings = LoadStringTable(stg, out int bytes_per_strref); - if (strings == null) - goto end; - - int n = stg.NumChildren(); - - LinkedList transforms = new LinkedList(); - - for (int i = 0; i < n; i++) - { - string encname = stg.NameByIndex(i); - byte[] encnameBytes = Encoding.UTF8.GetBytes(encname); - if (encnameBytes[0] != 0xe4 || encnameBytes[1] != 0xa1 || encnameBytes[2] != 0x80) - continue; - - string name = DecodeStreamName(encname); - if (name.Substring(3) == szStringPool || name.Substring(3) == szStringData) - continue; - - TRANSFORMDATA transform = new TRANSFORMDATA { Name = $"{name}\0" }; - transforms.AddFirst(transform); - - if (transform.Name == szTables) - tables = transform; - else if (transform.Name == szColumns) - columns = transform; - - // Load the table - LibmsiResult r = LibmsiTableView.Create(db, transform.Name, out LibmsiView view); - if (r != LibmsiResult.LIBMSI_RESULT_SUCCESS) - continue; - - LibmsiTableView tv = (view as LibmsiTableView); - if (tv == null) - continue; - - r = tv.Execute(null); - if (r != LibmsiResult.LIBMSI_RESULT_SUCCESS) - { - tv.Delete(); - continue; - } - - tv.Delete(); - } - - // Apply _Tables and _Columns transforms first so that - // the table metadata is correct, and empty tables exist. - ret = LoadTransform(db, stg, strings, tables, bytes_per_strref); - if (ret != LibmsiResult.LIBMSI_RESULT_SUCCESS && ret != LibmsiResult.LIBMSI_RESULT_INVALID_TABLE) - goto end; - - ret = LoadTransform(db, stg, strings, columns, bytes_per_strref); - if (ret != LibmsiResult.LIBMSI_RESULT_SUCCESS && ret != LibmsiResult.LIBMSI_RESULT_INVALID_TABLE) - goto end; - - ret = LibmsiResult.LIBMSI_RESULT_SUCCESS; - - foreach (TRANSFORMDATA transform in transforms) - { - if (transform.Name == szColumns && transform.Name == szTables && ret == LibmsiResult.LIBMSI_RESULT_SUCCESS) - ret = LoadTransform(db, stg, strings, transform, bytes_per_strref); - } - - transforms.Clear(); - - if (ret == LibmsiResult.LIBMSI_RESULT_SUCCESS) - db.AppendStorageToDb(stg); - - end: - if (strings != null) - strings.Destroy(); - - return ret; - } - - #endregion - - #region Utilities - - private static int Utf2Mime(int x) - { - if ((x >= '0') && (x <= '9')) - return x - '0'; - if ((x >= 'A') && (x <= 'Z')) - return x - 'A' + 10; - if ((x >= 'a') && (x <= 'z')) - return x - 'a' + 10 + 26; - if (x == '.') - return 10 + 26 + 26; - if (x == '_') - return 10 + 26 + 26 + 1; - return -1; - } - - private static int Mime2Utf(int x) - { - if (x < 10) - return x + '0'; - if (x < (10 + 26)) - return x - 10 + 'A'; - if (x < (10 + 26 + 26)) - return x - 10 - 26 + 'a'; - if (x == (10 + 26 + 26)) - return '.'; - return '_'; - } - - /// - /// Add this table to the list of cached tables in the database - /// - // TODO: Move to LibmsiDatabase - private static LibmsiResult ReadTableFromStorage(LibmsiDatabase db, LibmsiTable t, GsfInfile stg) - { - int row_size = GetRowSize(t.ColInfo, t.ColCount, db.BytesPerStrref); - int row_size_mem = GetRowSize(t.ColInfo, t.ColCount, LONG_STR_BYTES); - - // If we can't read the table, just assume that it's empty - ReadStreamData(stg, t.Name, out byte[] rawdata, out int rawsize); - if (rawdata == null) - return LibmsiResult.LIBMSI_RESULT_SUCCESS; - - if ((rawsize % row_size) != 0) - { - Console.Error.WriteLine($"Table size is invalid {rawsize}/{row_size}"); - return LibmsiResult.LIBMSI_RESULT_FUNCTION_FAILED; - } - - t.RowCount = rawsize / row_size; - t.Data = new byte[t.RowCount][]; - t.DataPersistent = new bool[t.RowCount]; - - // Transpose all the data - for (int i = 0; i < t.RowCount; i++) - { - int ofs = 0, ofs_mem = 0; - - t.Data[i] = new byte[row_size_mem]; - t.DataPersistent[i] = true; - - for (int j = 0; j < t.ColCount; j++) - { - int m = BytesPerColumn(t.ColInfo[j], LONG_STR_BYTES); - int n = BytesPerColumn(t.ColInfo[j], db.BytesPerStrref); - - if (n != 2 && n != 3 && n != 4) - { - Console.Error.WriteLine("oops - unknown column width %d\n", n); - return LibmsiResult.LIBMSI_RESULT_FUNCTION_FAILED; - } - - if ((t.ColInfo[j].Type & MSITYPE_STRING) != 0 && n < m) - { - for (int k = 0; k < m; k++) - { - if (k < n) - t.Data[i][ofs_mem + k] = rawdata[ofs * t.RowCount + i * n + k]; - else - t.Data[i][ofs_mem + k] = 0; - } - } - else - { - for (int k = 0; k < n; k++) - { - t.Data[i][ofs_mem + k] = rawdata[ofs * t.RowCount + i * n + k]; - } - } - - ofs_mem += m; - ofs += n; - } - } - - return LibmsiResult.LIBMSI_RESULT_SUCCESS; - } - - // TODO: Move this to the correct section - // TODO: Move to LibmsiDatabase - public static LibmsiTable FindCachedTable(LibmsiDatabase db, string name) - { - foreach (LibmsiTable t in db.Tables) - { - if (name == t.Name) - return t; - } - - return null; - } - - private static void CalcColumnOffsets(LibmsiColumnInfo[] colinfo, int count) - { - for (int i = 0; colinfo[i] != null && i < count; i++) - { - //assert(i + 1 == colinfo[i].number); - if (i != 0) - colinfo[i].Offset = colinfo[i - 1].Offset + BytesPerColumn(colinfo[i - 1], LONG_STR_BYTES); - else - colinfo[i].Offset = 0; - } - } - - // TODO: Move to LibmsiDatabase - private static LibmsiResult GetDefaultTableColumns(string name, LibmsiColumnInfo[] colinfo, ref int sz) - { - LibmsiColumnInfo[] p; - int n; - - if (name == szTables) - { - p = _Tables_cols; - n = 1; - } - else if (name == szColumns) - { - p = _Columns_cols; - n = 4; - } - else - { - sz = 0; - return LibmsiResult.LIBMSI_RESULT_FUNCTION_FAILED; - } - - for (int i = 0; i < n; i++) - { - if (colinfo != null && i < sz) - colinfo[i] = p[i]; - - if (colinfo != null && i >= sz) - break; - } - - CalcColumnOffsets(colinfo, n); - sz = n; - return LibmsiResult.LIBMSI_RESULT_SUCCESS; - } - - // TODO: Move to LibmsiDatabase - private static LibmsiResult TableGetColumnInfo(LibmsiDatabase db, string name, out LibmsiColumnInfo[] pcols, out int pcount) - { - pcols = null; pcount = 0; - - // Get the number of columns in this table - int column_count = 0; - LibmsiResult r = GetTableColumns(db, name, null, ref column_count); - if (r != LibmsiResult.LIBMSI_RESULT_SUCCESS) - return r; - - pcount = column_count; - - // if there's no columns, there's no table - if (column_count == 0) - return LibmsiResult.LIBMSI_RESULT_INVALID_PARAMETER; - - LibmsiColumnInfo[] columns = new LibmsiColumnInfo[column_count]; - r = GetTableColumns(db, name, columns, ref column_count); - if (r != LibmsiResult.LIBMSI_RESULT_SUCCESS) - return LibmsiResult.LIBMSI_RESULT_FUNCTION_FAILED; - - pcols = columns; - return r; - } - - // TODO: Move to LibmsiDatabase - private static LibmsiResult GetTableColumns(LibmsiDatabase db, string szTableName, LibmsiColumnInfo[] colinfo, ref int sz) - { - int maxcount = sz; - int n = 0; - - // First check if there is a default table with that name - LibmsiResult r = GetDefaultTableColumns(szTableName, colinfo, ref sz); - if (r == LibmsiResult.LIBMSI_RESULT_SUCCESS && sz != 0) - return r; - - r = GetTable(db, szColumns, out LibmsiTable table); - if (r != LibmsiResult.LIBMSI_RESULT_SUCCESS) - { - Console.Error.WriteLine("Couldn't load _Columns table"); - return LibmsiResult.LIBMSI_RESULT_FUNCTION_FAILED; - } - - // Convert table and column names to IDs from the string table - r = db.Strings.IdFromStringUTF8(szTableName, out int table_id); - if (r != LibmsiResult.LIBMSI_RESULT_SUCCESS) - { - Console.Error.WriteLine($"Couldn't find id for {szTableName}"); - return r; - } - - // Note: _Columns table doesn't have non-persistent data - - // If maxcount is non-zero, assume it's exactly right for this table - if (colinfo != null) - Array.Clear(colinfo, 0, maxcount); - - int count = table.RowCount; - for (int i = 0; i < count; i++) - { - if (ReadTableInt(table.Data, i, 0, LONG_STR_BYTES) != table_id) - continue; - - if (colinfo != null) - { - int id = ReadTableInt(table.Data, i, table.ColInfo[2].Offset, LONG_STR_BYTES); - int col = ReadTableInt(table.Data, i, table.ColInfo[1].Offset, sizeof(ushort)) - (1 << 15); - - // Check the column number is in range - if (col < 1 || col > maxcount) - { - Console.Error.WriteLine("column %d out of range (maxcount: %d)\n", col, maxcount); - continue; - } - - // Check if this column was already set - if (colinfo[col - 1].Number != 0) - { - Console.Error.WriteLine($"Duplicate column {col}"); - continue; - } - - colinfo[col - 1].TableName = db.Strings.LookupId(table_id); - colinfo[col - 1].Number = col; - colinfo[col - 1].ColName = db.Strings.LookupId(id); - colinfo[col - 1].Type = ReadTableInt(table.Data, i, table.ColInfo[3].Offset, sizeof(ushort)) - (1 << 15); - colinfo[col - 1].Offset = 0; - colinfo[col - 1].RefCount = 0; - colinfo[col - 1].HashTable = null; - } - - n++; - } - - if (colinfo != null && n != maxcount) - { - Console.Error.WriteLine($"Missing column in table {szTableName}"); - return LibmsiResult.LIBMSI_RESULT_FUNCTION_FAILED; - } - - CalcColumnOffsets(colinfo, n); - sz = n; - return LibmsiResult.LIBMSI_RESULT_SUCCESS; - } - - // TODO: Move to LibmsiDatabase - private static LibmsiResult SaveTable(LibmsiDatabase db, LibmsiTable t, int bytes_per_strref) - { - // Nothing to do for non-persistent tables - if (t.Persistent == LibmsiCondition.LIBMSI_CONDITION_FALSE) - return LibmsiResult.LIBMSI_RESULT_SUCCESS; - - // All tables are copied to the new file when committing, so - // we can just skip them if they are empty. However, always - // save the Tables stream. - if (t.RowCount == 0 && t.Name != szTables) - return LibmsiResult.LIBMSI_RESULT_SUCCESS; - - int row_size = GetRowSize(t.ColInfo, t.ColCount, bytes_per_strref); - int row_count = t.RowCount; - for (int i = 0; i < t.RowCount; i++) - { - if (!t.DataPersistent[i]) - { - row_count = 1; // Yes, this is bizarre - break; - } - } - - int rawsize = row_count * row_size; - byte[] rawdata = new byte[rawsize]; - - rawsize = 0; - for (int i = 0; i < t.RowCount; i++) - { - int ofs = 0, ofs_mem = 0; - - if (!t.DataPersistent[i]) - break; - - for (int j = 0; j < t.ColCount; j++) - { - int m = BytesPerColumn(t.ColInfo[j], LONG_STR_BYTES); - int n = BytesPerColumn(t.ColInfo[j], bytes_per_strref); - - if (n != 2 && n != 3 && n != 4) - { - Console.Error.WriteLine($"Oops - unknown column width {n}"); - return LibmsiResult.LIBMSI_RESULT_FUNCTION_FAILED; - } - - if ((t.ColInfo[j].Type & MSITYPE_STRING) != 0 && n < m) - { - int id = ReadTableInt(t.Data, i, ofs_mem, LONG_STR_BYTES); - if (id > 1 << bytes_per_strref * 8) - { - Console.Error.WriteLine($"String id {id} out of range"); - return LibmsiResult.LIBMSI_RESULT_FUNCTION_FAILED; - } - } - - for (int k = 0; k < n; k++) - { - rawdata[ofs * row_count + i * n + k] = t.Data[i][ofs_mem + k]; - } - - ofs_mem += m; - ofs += n; - } - - rawsize += row_size; - } - - return WriteStreamData(db, t.Name, rawdata, rawsize); - } - - private static int ReadRawInt(byte[] data, int col, int bytes) - { - int ret = 0; - for (int i = 0; i < bytes; i++) - { - ret += (data[col + i] << i * 8); - } - - return ret; - } - - // TODO: Move to LibmsiTableView - private static LibmsiResult RecordEncodedStreamName(LibmsiTableView tv, LibmsiRecord rec, out string pstname) - { - int len = tv.Name.Length + 1; - string stname = tv.Name + '\0'; - - LibmsiResult r; - for (int i = 0; i < tv.NumCols; i++) - { - if ((tv.Columns[i].Type & MSITYPE_KEY) != 0) - { - string sval = rec.DupRecordField(i + 1); - if (sval == null) - { - r = LibmsiResult.LIBMSI_RESULT_OUTOFMEMORY; - goto err; - } - - len += szDot.Length + sval.Length; - stname += szDot + sval; - } - else - { - continue; - } - } - - pstname = EncodeStreamName(false, stname).TrimEnd('\0'); - return LibmsiResult.LIBMSI_RESULT_SUCCESS; - - err: - pstname = null; - return r; - } - - // TODO: Move to LibmsiTableView - private static LibmsiRecord GetTransformRecord(LibmsiTableView tv, StringTable st, GsfInfile stg, byte[] rawdata, int bytes_per_strref) - { - int val, ofs = 0; - LibmsiColumnInfo[] columns = tv.Columns; - - ushort mask = (ushort)(rawdata[0] | (rawdata[1] << 8)); - rawdata = rawdata.Skip(2).ToArray(); - - LibmsiRecord rec = LibmsiRecord.Create(tv.NumCols); - if (rec == null) - return rec; - - for (int i = 0; i < tv.NumCols; i++) - { - if ((mask & 1) != 0 && (i >= (mask >> 8))) - break; - - // All keys must be present - if ((~mask & 1) != 0 && (~columns[i].Type & MSITYPE_KEY) != 0 && ((1 << i) & ~mask) != 0) - continue; - - if (MSITYPE_IS_BINARY(tv.Columns[i].Type)) - { - GsfInput stm = null; - - ofs += BytesPerColumn(columns[i], bytes_per_strref); - - LibmsiResult r = RecordEncodedStreamName(tv, rec, out string encname); - if (r != LibmsiResult.LIBMSI_RESULT_SUCCESS) - return null; - - Exception err = null; - stm = stg.ChildByName(encname, ref err); - if (r != LibmsiResult.LIBMSI_RESULT_SUCCESS) - return null; - - rec.LoadStream(i + 1, stm); - } - else if ((columns[i].Type & MSITYPE_STRING) != 0) - { - val = ReadRawInt(rawdata, ofs, bytes_per_strref); - string sval = st.LookupId(val); - rec.SetString(i + 1, sval); - ofs += bytes_per_strref; - } - else - { - int n = BytesPerColumn(columns[i], bytes_per_strref); - switch (n) - { - case 2: - val = ReadRawInt(rawdata, ofs, n); - if (val != 0) - rec.SetInt(i + 1, val - 0x8000); - - break; - case 4: - val = ReadRawInt(rawdata, ofs, n); - if (val != 0) - rec.SetInt(i + 1, (int)(val ^ 0x80000000)); - - break; - default: - Console.Error.WriteLine($"Oops - unknown column width {n}"); - break; - } - - ofs += n; - } - } - - return rec; - } - - // TODO: Move to LibmsiRecord - private static void DumpRecord(LibmsiRecord rec) - { - int n = rec.GetFieldCount(); - for (int i = 1; i <= n; i++) - { - string sval; - if (rec.IsNull(i)) - { - Console.WriteLine("row . []"); - } - else if ((sval = rec.GetStringRaw(i)) != null) - { - Console.WriteLine($"row . [{sval}]"); - } - else - { - Console.WriteLine($"row . [0x{rec.GetInt(i):8x}]"); - } - } - } - - // TODO: Move to StringTable - private static void DumpTable(StringTable st, ushort[] rawdata, int rawsize) - { - for (int i = 0; i < (rawsize / 2); i++) - { - string sval = st.LookupId(rawdata[i]); - Console.WriteLine($" {rawdata[i]:4x} {sval}"); - } - } - - // TODO: Move to LibmsiTableView - private static int[] RecordToRow(LibmsiTableView tv, LibmsiRecord rec) - { - string str; - LibmsiResult r; - - int[] data = new int[tv.NumCols]; - for (int i = 0; i < tv.NumCols; i++) - { - data[i] = 0; - - if ((~tv.Columns[i].Type & MSITYPE_KEY) != 0) - continue; - - // Turn the transform column value into a row value - if ((tv.Columns[i].Type & MSITYPE_STRING) != 0 && !MSITYPE_IS_BINARY(tv.Columns[i].Type)) - { - str = rec.GetStringRaw(i + 1); - if (str != null) - { - r = tv.Database.Strings.IdFromStringUTF8(str, out data[i]); - - /* if there's no matching string in the string table, - these keys can't match any record, so fail now. */ - if (r != LibmsiResult.LIBMSI_RESULT_SUCCESS) - return null; - } - else - { - data[i] = 0; - } - } - else - { - data[i] = rec.GetInt(i + 1); - - if (data[i] == LIBMSI_NULL_INT) - data[i] = 0; - else if ((tv.Columns[i].Type & 0xff) == 2) - data[i] += 0x8000; - else - unchecked { data[i] += (int)0x80000000; } - } - } - return data; - } - - // TODO: Move to LibmsiTableView - private static LibmsiResult RowMatches(LibmsiTableView tv, int row, int[] data, out int column) - { - column = 0; - - LibmsiResult ret = LibmsiResult.LIBMSI_RESULT_FUNCTION_FAILED; - for (int i = 0; i < tv.NumCols; i++) - { - if ((~tv.Columns[i].Type & MSITYPE_KEY) != 0) - continue; - - // Turn the transform column value into a row value - LibmsiResult r = tv.FetchInt(row, i + 1, out int x); - if (r != LibmsiResult.LIBMSI_RESULT_SUCCESS) - { - Console.Error.WriteLine("FetchInt shouldn't fail here"); - break; - } - - // If this key matches, move to the next column - if (x != data[i]) - { - ret = LibmsiResult.LIBMSI_RESULT_FUNCTION_FAILED; - break; - } - - column = i; - ret = LibmsiResult.LIBMSI_RESULT_SUCCESS; - } - - return ret; - } - - // TODO: Move to LibmsiDatabase - private static LibmsiResult LoadTransform(LibmsiDatabase db, GsfInfile stg, StringTable st, TRANSFORMDATA transform, int bytes_per_strref) - { - int i, colcol = 0; - - if (transform == null) - return LibmsiResult.LIBMSI_RESULT_SUCCESS; - - string name = transform.Name; - string coltable = "\0"; - - // Read the transform data - ReadStreamData(stg, name, out byte[] rawdata, out int rawsize); - if (rawdata == null) - return LibmsiResult.LIBMSI_RESULT_INVALID_TABLE; - - // Create a table view - LibmsiResult r = LibmsiTableView.Create(db, name, out LibmsiView view); - if (r != LibmsiResult.LIBMSI_RESULT_SUCCESS) - { - if (view != null) - view.Delete(); - - return LibmsiResult.LIBMSI_RESULT_SUCCESS; - } - - LibmsiTableView tv = (view as LibmsiTableView); - if (tv == null) - { - if (view != null) - view.Delete(); - - return LibmsiResult.LIBMSI_RESULT_SUCCESS; - } - - r = tv.Execute(null); - if (r != LibmsiResult.LIBMSI_RESULT_SUCCESS) - { - if (tv != null) - tv.Delete(); - - return LibmsiResult.LIBMSI_RESULT_SUCCESS; - } - - // Interpret the data - for (int n = 0; n < rawsize;) - { - int sz, num_cols; - - int mask = rawdata[n] | (rawdata[n + 1] << 8); - if ((mask & 1) != 0) - { - // If the low bit is set, columns are continuous and - // the number of columns is specified in the high byte - - sz = 2; - num_cols = mask >> 8; - for (i = 0; i < num_cols; i++) - { - if ((tv.Columns[i].Type & MSITYPE_STRING) != 0 && !MSITYPE_IS_BINARY(tv.Columns[i].Type)) - sz += bytes_per_strref; - else - sz += BytesPerColumn(tv.Columns[i], bytes_per_strref); - } - } - else - { - // If the low bit is not set, mask is a bitmask. - // Excepting for key fields, which are always present, - // each bit indicates that a field is present in the transform record. - // - // mask == 0 is a special case ... only the keys will be present - // and it means that this row should be deleted. - - sz = 2; - num_cols = tv.NumCols; - for (i = 0; i < num_cols; i++) - { - if ((tv.Columns[i].Type & MSITYPE_KEY) != 0 || ((1 << i) & mask) != 0) - { - if ((tv.Columns[i].Type & MSITYPE_STRING) != 0 && !MSITYPE_IS_BINARY(tv.Columns[i].Type)) - sz += bytes_per_strref; - else - sz += BytesPerColumn(tv.Columns[i], bytes_per_strref); - } - } - } - - // Check we didn't run of the end of the table - if (n + sz > rawsize) - { - Console.Error.WriteLine("Borked."); - DumpTable(st, rawdata.Cast().ToArray(), rawsize); - break; - } - - LibmsiRecord rec = GetTransformRecord(tv, st, stg, rawdata.Skip(n).ToArray(), bytes_per_strref); - if (rec != null) - { - string table = string.Empty; - int number = 0; - unchecked { number = (int)LIBMSI_NULL_INT; } - int row = 0; - - if (name == szColumns) - { - int tablesz = 32; - rec.GetString(1, out table, ref tablesz); - number = rec.GetInt(2); - - // Native msi seems writes nul into the Number (2nd) column of - // the _Columns table, only when the columns are from a new table - if (number == LIBMSI_NULL_INT) - { - // Reset the column number on a new table - if (coltable != table) - { - colcol = 0; - coltable = table; - } - - // Fix nul column numbers - rec.SetInt(2, ++colcol); - } - } - - bool TRACE_ON = false; // TODO: Make configurable - if (TRACE_ON) - DumpRecord(rec); - - r = FindRow(tv, rec, out row, out _); - if (r == LibmsiResult.LIBMSI_RESULT_SUCCESS) - { - if (mask == 0) - { - r = tv.DeleteRow(row); - if (r != LibmsiResult.LIBMSI_RESULT_SUCCESS) - Console.Error.WriteLine($"Failed to delete row {r}"); - } - else if ((mask & 1) != 0) - { - r = tv.SetRow(row, rec, (1 << tv.NumCols) - 1); - if (r != LibmsiResult.LIBMSI_RESULT_SUCCESS) - Console.Error.WriteLine($"Failed to modify row {r}"); - } - else - { - r = tv.SetRow(row, rec, mask); - if (r != LibmsiResult.LIBMSI_RESULT_SUCCESS) - Console.Error.WriteLine($"Failed to modify row {r}"); - } - } - else - { - r = tv.InsertRow(rec, -1, false); - if (r != LibmsiResult.LIBMSI_RESULT_SUCCESS) - Console.Error.WriteLine($"Failed to insert row {r}"); - } - - if (number != LIBMSI_NULL_INT && name == szColumns) - UpdateTableColumns(db, table); - } - - n += sz; - } - - // No need to free the table, it's associated with the database - if (tv != null) - tv.Delete(); - - return LibmsiResult.LIBMSI_RESULT_SUCCESS; - } - - #endregion - } -} \ No newline at end of file diff --git a/BurnOutSharp/External/libmsi/Internal/MsiPriv.cs b/BurnOutSharp/External/libmsi/Internal/MsiPriv.cs deleted file mode 100644 index bf6b309a..00000000 --- a/BurnOutSharp/External/libmsi/Internal/MsiPriv.cs +++ /dev/null @@ -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 -} \ No newline at end of file diff --git a/BurnOutSharp/External/libmsi/Internal/StringTable.cs b/BurnOutSharp/External/libmsi/Internal/StringTable.cs deleted file mode 100644 index adf8da31..00000000 --- a/BurnOutSharp/External/libmsi/Internal/StringTable.cs +++ /dev/null @@ -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 - - /// - /// The number of strings - /// - public int MaxCount { get; set; } - - public int FreeSlot { get; set; } - - public int CodePage { get; set; } - - public int SortCount { get; set; } - - /// - /// An array of strings - /// - public msistring[] Strings { get; set; } - - /// - /// Index - /// - 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; - } - - /// - /// Find the string identified by an id - return null if there's none - /// - 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; - } - - /// String to find in the string table - /// Id of the string, if found - 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(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; - } - - /// Pointer to the string table - /// Id of the string to retrieve - /// Destination of the UTF8 string - /// - /// Number of bytes available in the buffer on input - /// Number of bytes used on output - /// - /// Returned string is not NUL-terminated. - 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 - } -} \ No newline at end of file diff --git a/BurnOutSharp/External/libmsi/Internal/Tokenize.cs b/BurnOutSharp/External/libmsi/Internal/Tokenize.cs deleted file mode 100644 index 6375c362..00000000 --- a/BurnOutSharp/External/libmsi/Internal/Tokenize.cs +++ /dev/null @@ -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 -{ - /// - /// The token value for this keyword - /// - internal class Keyword - { - /// - /// All the keywords of the SQL language are stored as in a hash - /// table composed of instances of the following structure. - /// - public string Name { get; set; } - - /// - /// The token value for this keyword - /// - public sql_tokentype TokenType { get; set; } - } - - internal class Tokenize - { - #region Constants - - public const int MAX_TOKEN_LEN = 11; - - /// - /// These are the keywords - /// They MUST be in alphabetical order - /// - 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; - - /// - /// 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. - /// - 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 - - /// - /// Comparison function for binary search. - /// - public static int CompareKeyword(string m1, Keyword m2) => string.Compare(m1, m2.Name, StringComparison.OrdinalIgnoreCase); - - /// - /// 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. - /// - 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; - } - - /// - /// Return the length of the token that begins at [0]. Return - /// -1 if the token is (or might be) incomplete. Store the token - /// type in *tokenType before returning. - /// - 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 - } -} \ No newline at end of file diff --git a/BurnOutSharp/External/libmsi/LibmsiDatabase.cs b/BurnOutSharp/External/libmsi/LibmsiDatabase.cs deleted file mode 100644 index 363a6427..00000000 --- a/BurnOutSharp/External/libmsi/LibmsiDatabase.cs +++ /dev/null @@ -1,2108 +0,0 @@ -/* - * Implementation of the Microsoft Installer (msi.dll) - * - * Copyright 2002,2003,2004,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.IO; -using System.Linq; -using System.Text; -using LibGSF.Input; -using LibGSF.Output; -using LibMSI.Internal; -using LibMSI.Views; -using static LibMSI.LibmsiQuery; -using static LibMSI.LibmsiRecord; -using static LibMSI.LibmsiSummaryInfo; -using static LibMSI.Internal.LibmsiTable; -using static LibMSI.Internal.MsiPriv; -using static LibMSI.Internal.StringTable; - -namespace LibMSI -{ - internal class LibmsiTransform - { - public GsfInfile Stg { get; set; } - } - - internal class LibmsiStorage - { - public string Name { get; set; } - - public GsfInfile Stg { get; set; } - } - - internal class LibmsiStream - { - public string Name { get; set; } - - public GsfInput Stm { get; set; } - } - - /// - /// .MSI file format - /// - /// An .msi file is a structured storage file. - /// It contains a number of streams. - /// A stream for each table in the database. - /// Two streams for the string table in the database. - /// Any binary data in a table is a reference to a stream. - /// - public class LibmsiDatabase - { - #region Constants - - internal static readonly byte[] clsid_msi_transform = { 0x82, 0x10, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46 }; - - internal static readonly byte[] clsid_msi_database = { 0x84, 0x10, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46 }; - - internal static readonly byte[] clsid_msi_patch = { 0x86, 0x10, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46 }; - - #endregion - - #region Classes - - private class ExportRow - { - public Stream FD { get; set; } - - public string TableDir { get; set; } - - public Exception Error { get; set; } - } - - private class MERGETABLE - { - public LinkedList Rows { get; set; } - - public string Name { get; set; } - - public int NumConflicts { get; set; } - - public string[] Columns { get; set; } - - public int NumColumns { get; set; } - - public string[] Types { get; set; } - - public int NumTypes { get; set; } - - public string[] Labels { get; set; } - - public int NumLabels { get; set; } - } - - private class MERGEROW - { - public LibmsiRecord Data { get; set; } - } - - private class MERGEDATA - { - public LibmsiDatabase Database { get; set; } - - public LibmsiDatabase Merge { get; set; } - - public MERGETABLE CurTable { get; set; } - - public LibmsiQuery CurView { get; set; } - - public LinkedList TableData { get; set; } - } - - private class MsiPrimaryKeyRecordInfo - { - public int N { get; set; } - - public LibmsiRecord Rec { get; set; } - } - - #endregion - - #region Properties - - internal GsfInfile Infile { get; set; } - - internal GsfOutfile Outfile { get; set; } - - internal StringTable Strings { get; set; } - - internal int BytesPerStrref { get; set; } - - internal string Path { get; set; } - - internal string Outpath { get; set; } - - internal bool RenameOutpath { get; set; } - - internal LibmsiDbFlags Flags { get; set; } - - internal int MediaTransformOffset { get; set; } - - internal int MediaTransformDiskId { get; set; } - - internal LinkedList Tables { get; set; } = new LinkedList(); - - internal LinkedList Transforms { get; set; } = new LinkedList(); - - internal LinkedList Streams { get; set; } = new LinkedList(); - - internal LinkedList Storages { get; set; } = new LinkedList(); - - #endregion - - #region Constructor and Destructor - - /// - /// Private constructor - /// - private LibmsiDatabase() { } - - /// - /// Create a MSI database or open from . - /// - /// An MST transform file path - /// LibmsiDbFlags opening flags - /// Path to output MSI file - /// Exception to set on error, or null - /// A new LibmsiDatabase on success, null if fail. - public static LibmsiDatabase Create(string path, LibmsiDbFlags flags, string persist, ref Exception error) - { - if (path == null) - return null; - if (error != null) - return null; - - LibmsiDatabase self = new LibmsiDatabase - { - Path = path, - Outpath = persist, - Flags = flags, - }; - - if (!self.Init(ref error)) - return null; - - return self; - } - - /// - /// Destructor - /// - ~LibmsiDatabase() - { - Close(false); - FreeCachedTables(this); - Transforms.Clear(); - } - - #endregion - - #region Functions - - /// True if the database is read-only - public bool IsReadOnly() => Flags.HasFlag(LibmsiDbFlags.LIBMSI_DB_FLAGS_READONLY); - - /// An exisiting table name - /// Exception to set on error, or null - /// A LibmsiRecord containing the names of all the primary key columns. - public LibmsiRecord GetPrimaryKeys(string table, ref Exception error) - { - if (table == null) - return null; - if (error != null) - return null; - - LibmsiResult r = GetPrimaryKeys(table, out LibmsiRecord rec); - if (r != LibmsiResult.LIBMSI_RESULT_SUCCESS) - error = new Exception($"LIBMSI_RESULT_ERROR: {r}"); - - return rec; - } - - /// - /// FIXME - /// - /// An MST transform file path - /// Exception to set on error, or null - /// True on success - public bool ApplyTransform(string file, ref Exception error) - { - if (file == null) - return false; - if (error != null) - return false; - - LibmsiResult r = ApplyTransform(file); - if (r != LibmsiResult.LIBMSI_RESULT_SUCCESS) - error = new Exception($"LIBMSI_RESULT_ERROR: {r}"); - - return r == LibmsiResult.LIBMSI_RESULT_SUCCESS; - } - - /// - /// Writes a file containing the table data as tab separated ASCII. - /// - /// A table name - /// A file descriptor - /// Exception to set on error, or null - /// True on success - /// - /// The format is as follows: - /// - /// row1 : colname1 colname2 .... colnameN - /// row2 : coltype1 coltype2 .... coltypeN - /// row3 : tablename key1 key2 ... keyM - /// - /// Followed by the data, starting at row 1 with one row per line - /// - /// row4 : data data data ... data - /// - public bool Export(string table, Stream fd, ref Exception error) - { - if (table == null) - return false; - if (fd == null || !fd.CanWrite) - return false; - if (error != null) - return false; - - LibmsiResult r = ExportImpl(table, fd, ref error); - if (r != LibmsiResult.LIBMSI_RESULT_SUCCESS) - error = new Exception($"LIBMSI_RESULT_ERROR: {r}"); - - return r == LibmsiResult.LIBMSI_RESULT_SUCCESS; - } - - /// Adapted from WixToolset - public bool ExportAll(string path) - { - if (string.IsNullOrEmpty(path)) - return false; - - if (!Directory.Exists(path)) - Directory.CreateDirectory(path); - - // _MsiDigitalSignatureEx - - //Exception err = null; - //string digitalSignaturePath = System.IO.Path.Combine(path, "_MsiDigitalSignatureEx.idt"); - //using (Stream digitalSignatureStream = File.OpenWrite(digitalSignaturePath)) - //{ - // Export("_MsiDigitalSignatureEx", digitalSignatureStream, ref err); - //} - - // SummaryInfo - - Exception err = null; - string summaryInfoPath = System.IO.Path.Combine(path, "_SummaryInformation.idt"); - using (Stream summaryInfoStream = File.OpenWrite(summaryInfoPath)) - { - Export("_SummaryInformation", summaryInfoStream, ref err); - } - - // Tables - - err = null; - LibmsiQuery tableQuery = LibmsiQuery.Create(this, $"SELECT `Name` FROM `_Tables`", ref err); - tableQuery.Execute(null, ref err); - - LibmsiRecord tableRecord = tableQuery.Fetch(ref err); - while (tableRecord != null) - { - err = null; - string table = tableRecord.GetString(1); - string tablePath = System.IO.Path.Combine(path, $"{table}.idt"); - using (Stream tableStream = File.OpenWrite(tablePath)) - { - Export(table, tableStream, ref err); - } - - tableRecord = tableQuery.Fetch(ref err); - } - - // Streams - - if (!Directory.Exists(System.IO.Path.Combine(path, "_Streams"))) - Directory.CreateDirectory(System.IO.Path.Combine(path, "_Streams")); - - err = null; - LibmsiQuery streamsQuery = LibmsiQuery.Create(this, $"SELECT `Name`, `Data` FROM `_Streams`", ref err); - streamsQuery.Execute(null, ref err); - - LibmsiRecord streamRecord = streamsQuery.Fetch(ref err); - while (streamRecord != null) - { - err = null; - string stream = streamRecord.GetString(1); - if (stream.EndsWith("SummaryInformation", StringComparison.Ordinal)) - continue; - - int i = stream.IndexOf('.'); - if (i >= 0) - { - if (File.Exists(System.IO.Path.Combine(path, stream.Substring(0, i), stream.Substring(i + 1) + ".ibd"))) - continue; - } - - using (Stream streamRecordStream = streamRecord.GetStream(2)) - using (Stream streamRecordOutput = File.OpenWrite(System.IO.Path.Combine(path, "_Streams", stream))) - { - streamRecordStream.CopyTo(streamRecordOutput); - } - - streamRecord = tableQuery.Fetch(ref err); - } - - return true; - } - - /// - /// Import a table to the database from file . - /// - /// Path to a table file - /// Exception to set on error, or null - /// True on success - public bool Import(string path, ref Exception error) - { - if (path == null) - return false; - if (error != null) - return false; - - LibmsiResult r = Import(path); - if (r != LibmsiResult.LIBMSI_RESULT_SUCCESS) - error = new Exception($"LIBMSI_RESULT_ERROR: {r}"); - - return r == LibmsiResult.LIBMSI_RESULT_SUCCESS; - } - - /// An exisiting table name - /// Exception to set on error, or null - /// True if the is persistent, false if it's temporary - public bool IsTablePersistent(string table, ref Exception error) - { - if (table == null) - return false; - if (error != null) - return false; - - LibmsiCondition r = LibmsiTable.IsTablePersistent(this, table); - if (r == LibmsiCondition.LIBMSI_CONDITION_NONE) - error = new InvalidDataException("The table is unknown"); - else if (r == LibmsiCondition.LIBMSI_CONDITION_ERROR) - error = new Exception("Error"); - - return r == LibmsiCondition.LIBMSI_CONDITION_TRUE; - } - - /// A LibmsiDatabase to merge - /// An optionnal table name - /// Exception to set on error, or null - /// True on success - public bool Merge(LibmsiDatabase merge, string tablename, ref Exception error) - { - if (merge == null) - return false; - if (tablename != null && string.IsNullOrEmpty(tablename)) - return false; - if (error != null) - return false; - - LinkedList tabledata = new LinkedList(); - bool conflicts; - - LibmsiResult r = GatherMergeData(merge, tabledata.First()); - if (r != LibmsiResult.LIBMSI_RESULT_SUCCESS) - goto done; - - conflicts = false; - - foreach (MERGETABLE table in tabledata) - { - if (table.NumConflicts != 0) - { - conflicts = true; - r = UpdateMergeErrors(tablename, table.Name, table.NumConflicts); - if (r != LibmsiResult.LIBMSI_RESULT_SUCCESS) - break; - } - else - { - r = MergeTable(table); - if (r != LibmsiResult.LIBMSI_RESULT_SUCCESS) - break; - } - } - - foreach (MERGETABLE table in tabledata) - { - FreeMergeTable(table); - } - - tabledata.Clear(); - - if (conflicts) - r = LibmsiResult.LIBMSI_RESULT_FUNCTION_FAILED; - - done: - if (r != LibmsiResult.LIBMSI_RESULT_SUCCESS) - error = new Exception($"LIBMSI_RESULT_ERROR: {r}"); - - return r == LibmsiResult.LIBMSI_RESULT_SUCCESS; - } - - /// Exception to set on error, or null - /// True on success - public bool Commit(ref Exception error) - { - if (error != null) - return false; - - LibmsiResult r = LibmsiResult.LIBMSI_RESULT_SUCCESS; - if (Flags.HasFlag(LibmsiDbFlags.LIBMSI_DB_FLAGS_READONLY)) - return r == LibmsiResult.LIBMSI_RESULT_SUCCESS; - - // FIXME: lock the database - - r = Strings.SaveStringTable(this, out int bytes_per_strref); - if (r != LibmsiResult.LIBMSI_RESULT_SUCCESS) - { - error = new Exception($"Failed to save string table r={r}"); - return r == LibmsiResult.LIBMSI_RESULT_SUCCESS; - } - - r = EnumDbStorages(CommitStorage, this); - if (r != LibmsiResult.LIBMSI_RESULT_SUCCESS) - { - error = new Exception($"Failed to save storages r={r}"); - return r == LibmsiResult.LIBMSI_RESULT_SUCCESS; - } - - r = EnumDbStreams(CommitStream, this); - if (r != LibmsiResult.LIBMSI_RESULT_SUCCESS) - { - error = new Exception($"Failed to save streams r={r}"); - return r == LibmsiResult.LIBMSI_RESULT_SUCCESS; - } - - r = DatabseCommitTables(this, bytes_per_strref); - if (r != LibmsiResult.LIBMSI_RESULT_SUCCESS) - { - error = new Exception($"Failed to save tables r={r}"); - return r == LibmsiResult.LIBMSI_RESULT_SUCCESS; - } - - BytesPerStrref = bytes_per_strref; - - // FIXME: unlock the database - - Close(true); - Flags &= ~LibmsiDbFlags.LIBMSI_DB_FLAGS_CREATE; - Flags |= LibmsiDbFlags.LIBMSI_DB_FLAGS_TRANSACT; - Open(); - StartTransaction(); - - return r == LibmsiResult.LIBMSI_RESULT_SUCCESS; - } - - #endregion - - #region Internal Functions - - internal LibmsiResult OpenStorage(string stname) - { - LibmsiResult r = LibmsiResult.LIBMSI_RESULT_NOT_ENOUGH_MEMORY; - foreach (LibmsiStorage stg in Storages) - { - if (stname == stg.Name) - return r; - } - - LibmsiStorage storage = new LibmsiStorage { Name = stname }; - - Exception err = null; - GsfInput input = Infile.ChildByName(stname, ref err); - if (input == null || !(input is GsfInfile)) - return r; - - storage.Stg = input as GsfInfile; - if (storage.Stg == null) - return r; - - Storages.AddFirst(storage); - return LibmsiResult.LIBMSI_RESULT_SUCCESS; - } - - internal LibmsiResult CreateStorage(string stname, GsfInput stm) - { - if (Flags.HasFlag(LibmsiDbFlags.LIBMSI_DB_FLAGS_READONLY)) - return LibmsiResult.LIBMSI_RESULT_ACCESS_DENIED; - - bool found = false; - LibmsiStorage storage = null; - foreach (LibmsiStorage stg in Storages) - { - if (stname == stg.Name) - { - found = true; - storage = stg; - break; - } - } - - if (!found) - storage = new LibmsiStorage { Name = stname }; - - Exception err = null; - GsfInfile origstg = GsfInfileMSOle.Create(stm, ref err); - if (origstg == null) - return LibmsiResult.LIBMSI_RESULT_FUNCTION_FAILED; - - if (!found) - Storages.AddFirst(storage); - - storage.Stg = origstg; - - return LibmsiResult.LIBMSI_RESULT_SUCCESS; - } - - internal void DestroyStorage(string stname) - { - foreach (LibmsiStorage storage in Storages) - { - if (stname == storage.Name) - { - Storages.Remove(storage); - break; - } - } - } - - internal LibmsiResult WriteRawStreamData(string stname, byte[] data, int sz, out GsfInput outstm) - { - LibmsiResult ret = LibmsiResult.LIBMSI_RESULT_FUNCTION_FAILED; - - outstm = null; - if (Flags.HasFlag(LibmsiDbFlags.LIBMSI_DB_FLAGS_READONLY)) - return LibmsiResult.LIBMSI_RESULT_FUNCTION_FAILED; - - foreach (LibmsiStream stream in Streams) - { - if (stname == stream.Name) - { - DestroyStream(stname); - break; - } - } - - byte[] mem = new byte[sz == 0 ? 1 : sz]; - if (data != null || sz != 0) - Array.Copy(data, mem, sz); - - GsfInput stm = GsfInputMemory.Create(mem, sz, true); - ret = MsiAllocStream(stname, stm); - outstm = stm; - return ret; - } - - internal LibmsiResult CreateStream(string stname, GsfInput stm) - { - LibmsiResult r = LibmsiResult.LIBMSI_RESULT_FUNCTION_FAILED; - bool found = false; - - if (Flags.HasFlag(LibmsiDbFlags.LIBMSI_DB_FLAGS_READONLY)) - return LibmsiResult.LIBMSI_RESULT_ACCESS_DENIED; - - string encname = EncodeStreamName(false, stname).TrimEnd('\0'); - foreach (LibmsiStream stream in Streams) - { - if (encname == stream.Name) - { - stream.Stm = stm; - found = true; - break; - } - } - - if (found) - r = LibmsiResult.LIBMSI_RESULT_SUCCESS; - else - r = MsiAllocStream(encname, stm); - - return r; - } - - internal LibmsiResult EnumDbStreams(Func fn, object opaque) - { - foreach (LibmsiStream stream in Streams) - { - GsfInput stm = stream.Stm; - LibmsiResult r = fn(stream.Name, stm, opaque); - if (r != LibmsiResult.LIBMSI_RESULT_SUCCESS) - return r; - } - - return LibmsiResult.LIBMSI_RESULT_SUCCESS; - } - - internal LibmsiResult EnumDbStorages(Func fn, object opaque) - { - foreach (LibmsiStorage storage in Storages) - { - GsfInfile stg = storage.Stg; - LibmsiResult r = fn(storage.Name, stg, opaque); - if (r != LibmsiResult.LIBMSI_RESULT_SUCCESS) - return r; - } - - return LibmsiResult.LIBMSI_RESULT_SUCCESS; - } - - internal LibmsiResult GetRawStream(string stname, out GsfInput stm) - { - if (CloneInfileStream(stname, out stm) == LibmsiResult.LIBMSI_RESULT_SUCCESS) - return LibmsiResult.LIBMSI_RESULT_SUCCESS; - - LibmsiResult ret = LibmsiResult.LIBMSI_RESULT_FUNCTION_FAILED; - foreach (LibmsiTransform transform in Transforms) - { - Exception err = null; - stm = transform.Stg.ChildByName(stname, ref err); - if (stm != null) - { - ret = LibmsiResult.LIBMSI_RESULT_SUCCESS; - break; - } - } - - return ret; - } - - internal void DestroyStream(string stname) - { - foreach (LibmsiStream stream in Streams) - { - if (stname == stream.Name) - { - Streams.Remove(stream); - break; - } - } - } - - internal void AppendStorageToDb(GsfInfile stg) - { - Transforms.AddLast(new LibmsiTransform { Stg = stg }); - } - - internal LibmsiResult Close(bool committed) - { - if (Strings != null) - { - Strings.Destroy(); - Strings = null; - } - - if (Infile != null) - Infile = null; - - if (Outfile != null) - { - Outfile.Close(); - Outfile = null; - } - - Streams.Clear(); - Storages.Clear(); - - if (Outpath != null) - { - if (!committed) - { - //unlink(Outpath); - } - else if (RenameOutpath) - { - //unlink(Path); - File.Move(Outpath, Path); - } - else - { - Path = Outpath; - } - } - - Outpath = null; - return LibmsiResult.LIBMSI_RESULT_SUCCESS; - } - - internal LibmsiResult StartTransaction() - { - if (Flags.HasFlag(LibmsiDbFlags.LIBMSI_DB_FLAGS_READONLY)) - return LibmsiResult.LIBMSI_RESULT_SUCCESS; - - RenameOutpath = false; - if (Outpath == null) - { - string path = Path; - if (Flags.HasFlag(LibmsiDbFlags.LIBMSI_DB_FLAGS_TRANSACT)) - { - path += ".tmp"; - RenameOutpath = true; - } - - Outpath = path; - } - - Exception err = null; - GsfOutput output = GsfOutputStdio.Create(Outpath, ref err); - if (output == null) - { - Console.Error.WriteLine($"Open file failed for {Outpath}"); - return LibmsiResult.LIBMSI_RESULT_OPEN_FAILED; - } - - GsfOutfileMSOle stg = GsfOutfileMSOle.Create(output); - if (stg == null) - { - Console.Error.WriteLine($"Open failed for {Outpath}"); - return LibmsiResult.LIBMSI_RESULT_OPEN_FAILED; - } - - if (!stg.SetClassID(Flags.HasFlag(LibmsiDbFlags.LIBMSI_DB_FLAGS_PATCH) ? clsid_msi_patch : clsid_msi_database)) - { - Console.Error.WriteLine("Set guid failed"); - Outfile = null; - return LibmsiResult.LIBMSI_RESULT_FUNCTION_FAILED; - } - - Outfile = stg; - return LibmsiResult.LIBMSI_RESULT_SUCCESS; - } - - internal LibmsiResult Open() - { - byte[] uuid = new byte[16]; - LibmsiResult ret = LibmsiResult.LIBMSI_RESULT_OPEN_FAILED; - - Exception err = null; - GsfInput input = GsfInputStdio.Create(Path, ref err); - if (input == null) - { - Console.Error.WriteLine($"Open file failed for {Path}"); - return LibmsiResult.LIBMSI_RESULT_OPEN_FAILED; - } - - GsfInfile stg = GsfInfileMSOle.Create(input, ref err); - if (stg == null) - { - Console.Error.WriteLine($"Open file failed for {Path}"); - return LibmsiResult.LIBMSI_RESULT_OPEN_FAILED; - } - - if (!(stg as GsfInfileMSOle).GetClassID(uuid)) - { - Console.Error.WriteLine("FIXME: Failed to stat storage"); - goto end; - } - - if (!uuid.SequenceEqual(clsid_msi_database) && !uuid.SequenceEqual(clsid_msi_patch) && !uuid.SequenceEqual(clsid_msi_transform)) - { - Console.Error.WriteLine($"Storage GUID is not a MSI database GUID {BitConverter.ToString(uuid).Replace("-", string.Empty)}"); - goto end; - } - - if (Flags.HasFlag(LibmsiDbFlags.LIBMSI_DB_FLAGS_PATCH) && !uuid.SequenceEqual(clsid_msi_patch)) - { - Console.Error.WriteLine($"Storage GUID is not the MSI patch GUID {BitConverter.ToString(uuid).Replace("-", string.Empty)}"); - goto end; - } - - Infile = stg; - CacheInfileStructure(); - - Strings = LoadStringTable(Infile, out int bytes_per_strref); - if (Strings == null) - goto end; - - BytesPerStrref = bytes_per_strref; - ret = LibmsiResult.LIBMSI_RESULT_SUCCESS; - - end: - if (ret != LibmsiResult.LIBMSI_RESULT_SUCCESS) - Infile = null; - - return ret; - } - - internal LibmsiResult ApplyTransform(string szTransformFile) - { - LibmsiResult ret = LibmsiResult.LIBMSI_RESULT_FUNCTION_FAILED; - byte[] uuid = new byte[16]; - - Exception err = null; - GsfInput input = GsfInputStdio.Create(szTransformFile, ref err); - if (input == null) - { - Console.Error.WriteLine($"Open file failed for transform {szTransformFile}"); - return LibmsiResult.LIBMSI_RESULT_OPEN_FAILED; - } - - GsfInfile stg = GsfInfileMSOle.Create(input, ref err); - if (!(stg as GsfInfileMSOle).GetClassID(uuid)) - { - Console.Error.WriteLine("FIXME: Failed to stat storage"); - goto end; - } - - if (!uuid.SequenceEqual(clsid_msi_transform)) - goto end; - - bool TRACE_ON = false; // TODO: Make configurable - if (TRACE_ON && stg != null) - EnumStreamNames(stg); - - ret = LibmsiTable.ApplyTransform(this, stg); - - end: - return ret; - } - - internal LibmsiResult GetPrimaryKeys(string table, out LibmsiRecord prec) - { - prec = null; - if (!TableViewExists(this, table)) - return LibmsiResult.LIBMSI_RESULT_INVALID_TABLE; - - string sql = $"select * from `_Columns` where `Table` = '{table}'"; - LibmsiResult r = QueryOpen(this, out LibmsiQuery query, sql); - if (r != LibmsiResult.LIBMSI_RESULT_SUCCESS) - return r; - - // Count the number of primary key records - MsiPrimaryKeyRecordInfo info = new MsiPrimaryKeyRecordInfo - { - N = 0, - Rec = null, - }; - - int count = 0; - r = query.IterateRecords(ref count, PrimaryKeyIterator, info); - if (r == LibmsiResult.LIBMSI_RESULT_SUCCESS) - { - // Allocate a record and fill in the names of the tables - info.Rec = LibmsiRecord.Create(info.N); - info.N = 0; - - count = 0; - r = query.IterateRecords(ref count, PrimaryKeyIterator, info); - if (r == LibmsiResult.LIBMSI_RESULT_SUCCESS) - prec = info.Rec; - } - - return r; - } - - #endregion - - #region Utilities - - private LibmsiResult FindInfileStream(string name, out GsfInput stm) - { - foreach (LibmsiStream stream in Streams) - { - if (name == stream.Name) - { - stm = stream.Stm; - return LibmsiResult.LIBMSI_RESULT_SUCCESS; - } - } - - stm = null; - return LibmsiResult.LIBMSI_RESULT_FUNCTION_FAILED; - } - - private LibmsiResult MsiAllocStream(string name, GsfInput stm) - { - LibmsiStream stream = new LibmsiStream - { - Name = name, - Stm = stm, - }; - - Streams.AddFirst(stream); - return LibmsiResult.LIBMSI_RESULT_SUCCESS; - } - - private LibmsiResult CloneInfileStream(string name, out GsfInput stm) - { - stm = null; - if (FindInfileStream(name, out GsfInput stream) == LibmsiResult.LIBMSI_RESULT_SUCCESS) - { - Exception err = null; - stream = stream.Duplicate(ref err); - if (stream == null) - { - Console.Error.WriteLine("Failed to clone stream"); - return LibmsiResult.LIBMSI_RESULT_FUNCTION_FAILED; - } - - stream.Seek(0, SeekOrigin.Begin); - stm = stream; - return LibmsiResult.LIBMSI_RESULT_SUCCESS; - } - - return LibmsiResult.LIBMSI_RESULT_FUNCTION_FAILED; - } - - private string ReadTextArchive(string path, out int len) - { - try - { - string data = File.ReadAllText(path); - len = data.Length; - return data; - } - catch - { - len = 0; - return null; - } - } - - private void ParseLine(ref string line, ref int linePtr, out string[] entries, out int num_entries, ref int len) - { - int ptr = linePtr; - int save; - int i, count = 1, chars_left = len; - - entries = null; - - // Stay on this line - while (chars_left != 0 && line[ptr] != '\n') - { - // Entries are separated by tabs - if (line[ptr] == '\t') - count++; - - ptr++; - chars_left--; - } - - - entries = new string[count]; - - // Store pointers into the data - chars_left = len; - for (i = 0, ptr = linePtr; i < count; i++) - { - while (chars_left != 0 && line[ptr] == '\r') - { - ptr++; - chars_left--; - } - - save = ptr; - - while (chars_left != 0 && line[ptr] != '\t' && line[ptr] != '\n' && line[ptr] != '\r') - { - // Convert embedded nulls to \n - if (line[ptr] == '\0') - { - char[] temp = line.ToCharArray(); - temp[ptr] = '\n'; - line = new string(temp); - } - - if (ptr > linePtr && line[ptr] == '\x19' && line[ptr - 1] == '\x11') - { - char[] temp = line.ToCharArray(); - temp[ptr] = '\n'; - temp[ptr - 1] = '\r'; - line = new string(temp); - } - - ptr++; - chars_left--; - } - - // null-separate the data - if (line[ptr] == '\n' || line[ptr] == '\r') - { - while (chars_left != 0 && (line[ptr] == '\n' || line[ptr] == '\r')) - { - char[] temp = line.ToCharArray(); - temp[ptr++] = '\0'; - chars_left--; - line = new string(temp); - } - } - else if (line[ptr] != 0) - { - char[] temp = line.ToCharArray(); - temp[ptr++] = '\0'; - chars_left--; - line = new string(temp); - } - - entries[i] = new string(line.Skip(save).TakeWhile(c => c != '\0').ToArray()); - } - - // Move to the next line if there's more, else EOF - linePtr = ptr; - len = chars_left; - num_entries = count; - } - - private string BuildCreateSQLPrelude(string table) => $"CREATE TABLE `{table}` ("; - - private string BuildCreateSQLColumns(string[] columns_data, string[] types, int num_columns) - { - int sql_size = 1; - long len; - - string columns = string.Empty; - for (int i = 0; i < num_columns; i++) - { - string comma; - if (i == num_columns - 1) - comma = "\0"; - else - comma = ","; - - comma += '\0'; - - int ptr = 1; // types[i][1]; - len = long.Parse(new string(types[i].Skip(1).SkipWhile(c => char.IsWhiteSpace(c)).TakeWhile(c => c == '+' || c == '-' || char.IsDigit(c)).ToArray())); - - string extra = "\0"; - string type = null; - string size = "\0"; - - switch (types[i][0]) - { - case 'l': - extra = " NOT null"; - extra += " LOCALIZABLE"; - type = "CHAR"; - size = $"({ptr})"; - break; - - case 'L': - extra += " LOCALIZABLE"; - type = "CHAR"; - size = $"({ptr})"; - break; - - case 's': - extra = " NOT null"; - type = "CHAR"; - size = $"({ptr})"; - break; - - case 'S': - type = "CHAR"; - size = $"({ptr})"; - break; - - case 'i': - extra = " NOT null"; - if (len <= 2) - { - type = "INT"; - } - else if (len == 4) - { - type = "LONG"; - } - else - { - Console.Error.WriteLine($"Invalid int width {len}"); - return null; - } - - break; - - case 'I': - if (len <= 2) - { - type = "INT"; - } - else if (len == 4) - { - type = "LONG"; - } - else - { - Console.Error.WriteLine($"Invalid int width {len}"); - return null; - } - - break; - - case 'v': - extra = " NOT null"; - type = "OBJECT"; - break; - - case 'V': - type = "OBJECT"; - break; - - default: - Console.Error.WriteLine($"Unknown type: {types[i][0]}"); - return null; - } - - string expanded = $"`{columns_data[i]}` {type}{size}{extra}{comma} "; - - sql_size += expanded.Length; - columns += expanded; - } - - return columns; - } - - private string BuildCreateSQLPostlude(string[] primary_keys, int num_keys) - { - string keys = string.Join(", ", primary_keys.Select(s => $"`{s}`")); - return $"PRIMARY KEY {keys})"; - } - - private LibmsiResult AddTableToDb(string[] columns, string[] types, string[] labels, int num_labels, int num_columns) - { - string prelude = BuildCreateSQLPrelude(labels[0]); - string columns_sql = BuildCreateSQLColumns(columns, types, num_columns); - string postlude = BuildCreateSQLPostlude(labels.Skip(1).ToArray(), num_labels - 1); // Skip over table name - - if (prelude == null || columns_sql == null || postlude == null) - return LibmsiResult.LIBMSI_RESULT_OUTOFMEMORY; - - int size = prelude.Length + columns_sql.Length + postlude.Length + 1; - string create_sql = prelude + columns_sql + postlude; - - Exception error = null; // FIXME: move error handling to caller - LibmsiQuery view = LibmsiQuery.Create(this, create_sql, ref error); - if (view == null) - { - if (error != null) - Console.Error.WriteLine(error.Message); - - error = null; - return LibmsiResult.LIBMSI_RESULT_OUTOFMEMORY; - } - - LibmsiResult r = view.Execute(null); - view.Close(ref error); - if (error != null) - Console.Error.WriteLine(error.Message); - - error = null; - return r; - } - - private LibmsiResult ConstructRecord(int num_columns, string[] types, string[] data, string name, out LibmsiRecord rec) - { - rec = LibmsiRecord.Create(num_columns); - for (int i = 0; i < num_columns; i++) - { - switch (types[i][0]) - { - case 'L': - case 'l': - case 'S': - case 's': - rec.SetString(i + 1, data[i]); - break; - case 'I': - case 'i': - if (data[i] != null) - rec.SetInt(i + 1, int.Parse(new string(data[i].Skip(1).SkipWhile(c => char.IsWhiteSpace(c)).TakeWhile(c => c == '+' || c == '-' || char.IsDigit(c)).ToArray()))); - - break; - case 'V': - case 'v': - if (data[i] != null) - { - string file = System.IO.Path.Combine(name, data[i]); - LibmsiResult r = rec.LoadStreamFromFile(i + 1, file); - if (r != LibmsiResult.LIBMSI_RESULT_SUCCESS) - return LibmsiResult.LIBMSI_RESULT_FUNCTION_FAILED; - } - - break; - - default: - Console.Error.WriteLine($"Unhandled column type: {types[i][0]}"); - return LibmsiResult.LIBMSI_RESULT_FUNCTION_FAILED; - } - } - - return LibmsiResult.LIBMSI_RESULT_SUCCESS; - } - - private LibmsiResult AddRecordsToTable(string[] columns, string[] types, string[] labels, string[][] records, int num_columns, int num_records) - { - LibmsiResult r = LibmsiTableView.Create(this, labels[0], out LibmsiView view); - if (r != LibmsiResult.LIBMSI_RESULT_SUCCESS) - return r; - - r = view.GetDimensions(out int num_rows, out int num_cols); - if (r != LibmsiResult.LIBMSI_RESULT_SUCCESS) - return r; - - while (num_rows > 0) - { - r = view.DeleteRow(--num_rows); - if (r != LibmsiResult.LIBMSI_RESULT_SUCCESS) - return r; - } - - for (int i = 0; i < num_records; i++) - { - r = ConstructRecord(num_columns, types, records[i], labels[0], out LibmsiRecord rec); - if (r != LibmsiResult.LIBMSI_RESULT_SUCCESS) - return r; - - r = view.InsertRow(rec, -1, false); - if (r != LibmsiResult.LIBMSI_RESULT_SUCCESS) - { - return r; - } - } - - return LibmsiResult.LIBMSI_RESULT_SUCCESS; - } - - private LibmsiResult Import(string path) - { - LibmsiResult r = LibmsiResult.LIBMSI_RESULT_OUTOFMEMORY; - int num_records = 0; - - string suminfo = "_SummaryInformation"; - string forcecodepage = "_ForceCodepage"; - - string data = ReadTextArchive(path, out int len); - if (data == null) - return LibmsiResult.LIBMSI_RESULT_OUTOFMEMORY; - - int ptr = 0; // data[0] - ParseLine(ref data, ref ptr, out string[] columns, out int num_columns, ref len); - ParseLine(ref data, ref ptr, out string[] types, out int num_types, ref len); - ParseLine(ref data, ref ptr, out string[] labels, out int num_labels, ref len); - - if (num_columns == 1 && columns[0][0] == '\0' && num_labels == 1 && labels[0][0] == '\0' && num_types == 2 && types[1] == forcecodepage) - return Strings.SetCodePage(int.Parse(new string(types[0].Skip(1).SkipWhile(c => char.IsWhiteSpace(c)).TakeWhile(c => c == '+' || c == '-' || char.IsDigit(c)).ToArray()))); - - if (num_columns != num_types) - return LibmsiResult.LIBMSI_RESULT_FUNCTION_FAILED; - - List records = new List(); - - // Read in the table records - while (len != 0) - { - - ParseLine(ref data, ref ptr, out string[] record, out _, ref len); - records.Add(record); - } - - if (labels[0] == suminfo) - { - r = AddSummaryInfo(this, records.ToArray(), num_records, num_columns); - if (r != LibmsiResult.LIBMSI_RESULT_SUCCESS) - return LibmsiResult.LIBMSI_RESULT_FUNCTION_FAILED; - } - else - { - if (!TableViewExists(this, labels[0])) - { - r = AddTableToDb(columns, types, labels, num_labels, num_columns); - if (r != LibmsiResult.LIBMSI_RESULT_SUCCESS) - return LibmsiResult.LIBMSI_RESULT_FUNCTION_FAILED; - } - - r = AddRecordsToTable(columns, types, labels, records.ToArray(), num_columns, num_records); - } - - return r; - } - - private static bool ExportStream(GsfInput gsfin, string table_dir, out string str, ref Exception error) - { - str = null; - if (table_dir == null) - return false; - - try - { - Directory.CreateDirectory(table_dir); - } - catch (Exception err) - { - error = err; - return false; - } - - str = gsfin.Name; - string file = System.IO.Path.Combine(table_dir, str); - - try - { - Stream output = File.OpenWrite(file); - LibmsiIStream input = LibmsiIStream.Create(gsfin); - - // TODO: Figure out how to do a "splice" on the streams - // int spliced = g_output_stream_splice (output, input, 0, null, null); - // return spliced != -1; - return true; - } - catch (Exception err) - { - error = err; - return false; - } - } - - private static LibmsiResult ExportRecord(Stream fd, LibmsiRecord row, int start, string table_dir, ref Exception error) - { - int count = row.GetFieldCount(); - for (int i = start; i <= count; i++) - { - string str; - row.GetGsfInput(i, out GsfInput input); - if (input != null) - { - if (!ExportStream(input, table_dir, out str, ref error)) - return LibmsiResult.LIBMSI_RESULT_FUNCTION_FAILED; - - input = null; - } - else - { - str = row.GetString(i); - if (str == null) - return LibmsiResult.LIBMSI_RESULT_FUNCTION_FAILED; - } - - try - { - // TODO full_write - fd.Write(Encoding.ASCII.GetBytes(str), 0, str.Length); - - string sep = (i < count) ? "\t" : "\r\n"; - fd.Write(Encoding.ASCII.GetBytes(sep), 0, sep.Length); - } - catch - { - return LibmsiResult.LIBMSI_RESULT_FUNCTION_FAILED; - } - } - - return LibmsiResult.LIBMSI_RESULT_SUCCESS; - } - - // TODO: Move to LibmsiRecord - private static LibmsiResult ExportRowImpl(LibmsiRecord row, object arg) - { - ExportRow export = arg as ExportRow; - Exception err = export.Error; - LibmsiResult r = ExportRecord(export.FD, row, 1, export.TableDir, ref err); - export.Error = err; - return r; - } - - private static LibmsiResult ExportForceCodePage(Stream fd, int codepage) - { - string data = $"\r\n\r\n{codepage}\t_ForceCodepage\r\n\0"; - int sz = data.Length; - try - { - fd.Write(Encoding.ASCII.GetBytes(data), 0, sz); - return LibmsiResult.LIBMSI_RESULT_SUCCESS; - } - catch - { - return LibmsiResult.LIBMSI_RESULT_FUNCTION_FAILED; - } - } - - private LibmsiResult ExportSummaryInfo(Stream fd, ref Exception error) - { - LibmsiSummaryInfo si = LibmsiSummaryInfo.Create(this, 0, ref error); - if (si == null) - return LibmsiResult.LIBMSI_RESULT_FUNCTION_FAILED; - - string header = "PropertyId\tValue\r\ni2\tl255\r\n_SummaryInformation\tPropertyId\r\n"; - int sz = header.Length; - - try - { - fd.Write(Encoding.ASCII.GetBytes(header), 0, sz); - } - catch - { - return LibmsiResult.LIBMSI_RESULT_FUNCTION_FAILED; - } - - for (int i = 0; i < MSI_MAX_PROPS; i++) - { - if (si.Properties[i] != null && si.Properties[i].VariantType != LibmsiOLEVariantType.OLEVT_EMPTY) - { - string val = si.SummaryInfoAsString(i); - if (val == null) - return LibmsiResult.LIBMSI_RESULT_FUNCTION_FAILED; - - string str = $"{i}\t{val}\r\n"; - sz = str.Length; - try - { - fd.Write(Encoding.ASCII.GetBytes(str), 0, sz); - } - catch - { - return LibmsiResult.LIBMSI_RESULT_FUNCTION_FAILED; - } - } - } - - return LibmsiResult.LIBMSI_RESULT_SUCCESS; - } - - private LibmsiResult ExportImpl(string table, Stream fd, ref Exception error) - { - if (table == "_ForceCodepage") - { - int codepage = Strings.GetCodePage(); - return ExportForceCodePage(fd, codepage); - } - else if (table == "_SummaryInformation") - { - return ExportSummaryInfo(fd, ref error); - } - - string query = $"SELECT * FROM `{table}`"; - LibmsiResult r = QueryOpen(this, out LibmsiQuery view, query); - if (r == LibmsiResult.LIBMSI_RESULT_SUCCESS) - { - // Write out row 1, the column names - r = view.GetColumnInfo(LibmsiColInfo.LIBMSI_COL_INFO_NAMES, out LibmsiRecord rec); - if (r == LibmsiResult.LIBMSI_RESULT_SUCCESS) - ExportRecord(fd, rec, 1, null, ref error); - - // Write out row 2, the column types - r = view.GetColumnInfo(LibmsiColInfo.LIBMSI_COL_INFO_TYPES, out rec); - if (r == LibmsiResult.LIBMSI_RESULT_SUCCESS) - ExportRecord(fd, rec, 1, null, ref error); - - // Write out row 3, the table name + keys - r = GetPrimaryKeys(table, out rec); - if (r == LibmsiResult.LIBMSI_RESULT_SUCCESS) - { - rec.SetString(0, table); - ExportRecord(fd, rec, 0, null, ref error); - } - - // Write out row 4 onwards, the data - ExportRow export = new ExportRow - { - FD = fd, - TableDir = table, - Error = error, - }; - - int count = 0; - r = view.IterateRecords(ref count, ExportRowImpl, export); - } - - return r; - } - - private static bool MergeTypeMatch(string type1, string type2) - { - if (((type1[0] == 'l') || (type1[0] == 's')) && - ((type2[0] == 'l') || (type2[0] == 's'))) - return true; - - if (((type1[0] == 'L') || (type1[0] == 'S')) && - ((type2[0] == 'L') || (type2[0] == 'S'))) - return true; - - return type1 == type2; - } - - // TODO: Move to LibmsiQuery - private static LibmsiResult MergeVerifyColnames(LibmsiQuery dbview, LibmsiQuery mergeview) - { - LibmsiResult r = dbview.GetColumnInfo(LibmsiColInfo.LIBMSI_COL_INFO_NAMES, out LibmsiRecord dbrec); - if (r != LibmsiResult.LIBMSI_RESULT_SUCCESS) - return r; - - r = mergeview.GetColumnInfo(LibmsiColInfo.LIBMSI_COL_INFO_NAMES, out LibmsiRecord mergerec); - if (r != LibmsiResult.LIBMSI_RESULT_SUCCESS) - return r; - - int count = dbrec.GetFieldCount(); - for (int i = 1; i <= count; i++) - { - if (mergerec.GetStringRaw(i) == null) - break; - - if (dbrec.GetStringRaw(i) != mergerec.GetStringRaw(i)) - return LibmsiResult.LIBMSI_RESULT_DATATYPE_MISMATCH; - } - - r = dbview.GetColumnInfo(LibmsiColInfo.LIBMSI_COL_INFO_TYPES, out dbrec); - if (r != LibmsiResult.LIBMSI_RESULT_SUCCESS) - return r; - - r = mergeview.GetColumnInfo(LibmsiColInfo.LIBMSI_COL_INFO_TYPES, out mergerec); - if (r != LibmsiResult.LIBMSI_RESULT_SUCCESS) - return r; - - count = dbrec.GetFieldCount(); - for (int i = 1; i <= count; i++) - { - if (mergerec.GetStringRaw(i) == null) - break; - - if (!MergeTypeMatch(dbrec.GetStringRaw(i), mergerec.GetStringRaw(i))) - { - r = LibmsiResult.LIBMSI_RESULT_DATATYPE_MISMATCH; - break; - } - } - - return r; - } - - private LibmsiResult MergeVerifyPrimaryKeys(LibmsiDatabase mergedb, string table) - { - LibmsiResult r = GetPrimaryKeys(table, out LibmsiRecord dbrec); - if (r != LibmsiResult.LIBMSI_RESULT_SUCCESS) - return r; - - r = mergedb.GetPrimaryKeys(table, out LibmsiRecord mergerec); - if (r != LibmsiResult.LIBMSI_RESULT_SUCCESS) - return r; - - int count = dbrec.GetFieldCount(); - if (count != mergerec.GetFieldCount()) - return LibmsiResult.LIBMSI_RESULT_DATATYPE_MISMATCH; - - for (int i = 1; i <= count; i++) - { - if (dbrec.GetStringRaw(i) != mergerec.GetStringRaw(i)) - return LibmsiResult.LIBMSI_RESULT_DATATYPE_MISMATCH; - } - - return r; - } - - // TODO: Move to LibmsiRecord - private static string GetKeyValue(LibmsiQuery view, string key, LibmsiRecord rec) - { - LibmsiResult r = view.GetColumnInfo(LibmsiColInfo.LIBMSI_COL_INFO_NAMES, out LibmsiRecord colnames); - if (r != LibmsiResult.LIBMSI_RESULT_SUCCESS) - return null; - - int i = 0; - bool cmp; - do - { - string temp = colnames.DupRecordField(++i); - cmp = (key == temp); - } while (!cmp); - - // Check record field is a string - string str; - - // Quote string record fields - if (rec.GetStringRaw(i) != null) - str = $"{rec.GetStringRaw(i)}"; - else - str = rec.GetString(i); - - return str; - } - - private string CreateDiffRowQuery(LibmsiQuery view, string table, LibmsiRecord rec) - { - LibmsiResult r = GetPrimaryKeys(table, out LibmsiRecord keys); - if (r != LibmsiResult.LIBMSI_RESULT_SUCCESS) - return null; - - string query = $"SELECT * FROM `{table}` WHERE "; - int count = keys.GetFieldCount(); - for (int i = 1; i <= count; i++) - { - string key = keys.GetStringRaw(i); - string val = GetKeyValue(view, key, rec); - - if (i == count) - query += $"`{key}` = {val}"; - else - query += $"`{key}` = {val} AND "; - } - - return query; - } - - // TODO: Move to LibmsiRecord - private static LibmsiResult MergeDiffRow(LibmsiRecord rec, object param) - { - MERGEDATA data = param as MERGEDATA; - MERGETABLE table = data.CurTable; - MERGEROW mergerow; - LibmsiResult r = LibmsiResult.LIBMSI_RESULT_SUCCESS; - Exception err = null; - - if (TableViewExists(data.Database, table.Name)) - { - string query = data.Merge.CreateDiffRowQuery(data.CurView, table.Name, rec); - if (query == null) - return LibmsiResult.LIBMSI_RESULT_OUTOFMEMORY; - - LibmsiQuery dbview = LibmsiQuery.Create(data.Database, query, ref err); - if (err != null) - { - Console.Error.WriteLine(err.Message); - return r; - } - - r = dbview.Execute(null); - if (r != LibmsiResult.LIBMSI_RESULT_SUCCESS) - return r; - - r = dbview.Fetch(out LibmsiRecord row); - if (r == LibmsiResult.LIBMSI_RESULT_SUCCESS && !RecordCompare(rec, row)) - { - table.NumConflicts++; - return r; - } - else if (r != LibmsiResult.NO_MORE_ITEMS) - { - return r; - } - - r = LibmsiResult.LIBMSI_RESULT_SUCCESS; - } - - mergerow = new MERGEROW(); - mergerow.Data = rec.Clone(); - if (mergerow.Data == null) - { - r = LibmsiResult.LIBMSI_RESULT_OUTOFMEMORY; - return r; - } - - table.Rows.AddFirst(mergerow); - return r; - } - - private LibmsiResult GetTableLabels(string table, out string[] labels, out int numlabels) - { - labels = null; numlabels = 0; - LibmsiResult r = GetPrimaryKeys(table, out LibmsiRecord prec); - if (r != LibmsiResult.LIBMSI_RESULT_SUCCESS) - return r; - - int count = prec.GetFieldCount(); - numlabels = count + 1; - labels = new string[numlabels]; - - labels[0] = table; - for (int i = 1; i <= count; i++) - { - labels[i] = prec.GetStringRaw(i); - } - - return r; - } - - // TODO: Move to LibmsiRecord - private static LibmsiResult GetQueryColumns(LibmsiQuery query, out string[] columns, out int numcolumns) - { - columns = null; numcolumns = 0; - LibmsiResult r = query.GetColumnInfo(LibmsiColInfo.LIBMSI_COL_INFO_NAMES, out LibmsiRecord prec); - if (r != LibmsiResult.LIBMSI_RESULT_SUCCESS) - return r; - - int count = prec.GetFieldCount(); - columns = new string[count]; - - for (int i = 1; i <= count; i++) - { - columns[i - 1] = prec.GetStringRaw(i); - } - - numcolumns = count; - return r; - } - - // TODO: Move to LibmsiRecord - private static LibmsiResult GetQueryTypes(LibmsiQuery query, out string[] types, out int numtypes) - { - types = null; numtypes = 0; - LibmsiResult r = query.GetColumnInfo(LibmsiColInfo.LIBMSI_COL_INFO_TYPES, out LibmsiRecord prec); - if (r != LibmsiResult.LIBMSI_RESULT_SUCCESS) - return r; - - int count = prec.GetFieldCount(); - types = new string[count]; - numtypes = count; - for (int i = 1; i <= count; i++) - { - types[i - 1] = prec.GetStringRaw(i); - } - - return r; - } - - // TODO: Move to MERGETABLE - private static void FreeMergeTable(MERGETABLE table) - { - table.Labels = null; - table.Columns = null; - table.Types = null; - table.Name = null; - table.Rows.Clear(); - } - - private LibmsiResult GetMergeTable(string name, out MERGETABLE ptable) - { - MERGETABLE table = new MERGETABLE(); - LibmsiResult r = GetTableLabels(name, out string[] labels, out int num_labels); - if (r != LibmsiResult.LIBMSI_RESULT_SUCCESS) - goto err; - - table.Labels = labels; - table.NumLabels = num_labels; - - string query = $"SELECT * FROM `{name}`"; - r = QueryOpen(this, out LibmsiQuery mergeview, query); - if (r != LibmsiResult.LIBMSI_RESULT_SUCCESS) - goto err; - - r = GetQueryColumns(mergeview, out string[] columns, out int num_columns); - if (r != LibmsiResult.LIBMSI_RESULT_SUCCESS) - goto err; - - table.Columns = columns; - table.NumColumns = num_columns; - - r = GetQueryTypes(mergeview, out string[] types, out int num_types); - if (r != LibmsiResult.LIBMSI_RESULT_SUCCESS) - goto err; - - table.Types = types; - table.NumTypes = num_types; - - table.Rows = new LinkedList(); - - table.Name = name; - table.NumConflicts = 0; - - ptable = table; - return LibmsiResult.LIBMSI_RESULT_SUCCESS; - - err: - FreeMergeTable(table); - ptable = null; - return r; - } - - // TODO: Move to LibmsiRecord - private static LibmsiResult MergeDiffTables(LibmsiRecord rec, object param) - { - MERGEDATA data = param as MERGEDATA; - string name = rec.GetStringRaw(1); - - string query = $"SELECT * FROM `{name}`"; - LibmsiResult r = QueryOpen(data.Merge, out LibmsiQuery mergeview, query); - if (r != LibmsiResult.LIBMSI_RESULT_SUCCESS) - return r; - - if (TableViewExists(data.Database, name)) - { - r = QueryOpen(data.Database, out LibmsiQuery dbview, query); - if (r != LibmsiResult.LIBMSI_RESULT_SUCCESS) - return r; - - r = MergeVerifyColnames(dbview, mergeview); - if (r != LibmsiResult.LIBMSI_RESULT_SUCCESS) - return r; - - r = data.Database.MergeVerifyPrimaryKeys(data.Merge, name); - if (r != LibmsiResult.LIBMSI_RESULT_SUCCESS) - return r; - } - - r = data.Merge.GetMergeTable(name, out MERGETABLE table); - if (r != LibmsiResult.LIBMSI_RESULT_SUCCESS) - return r; - - data.CurTable = table; - data.CurView = mergeview; - - int count = 0; - r = mergeview.IterateRecords(ref count, MergeDiffRow, data); - if (r != LibmsiResult.LIBMSI_RESULT_SUCCESS) - { - FreeMergeTable(table); - return r; - } - - data.TableData.AddFirst(table); - return r; - } - - private LibmsiResult GatherMergeData(LibmsiDatabase merge, MERGETABLE tabledata) - { - string query = "SELECT * FROM `_Tables`"; - LibmsiResult r = DatabaseOpenQuery(merge, query, out LibmsiQuery view); - if (r != LibmsiResult.LIBMSI_RESULT_SUCCESS) - return r; - - MERGEDATA data = new MERGEDATA - { - Database = this, - Merge = merge, - TableData = new LinkedList() - }; - - data.TableData.AddFirst(tabledata); - - int count = 0; - r = view.IterateRecords(ref count, MergeDiffTables, data); - return r; - } - - private LibmsiResult MergeTable(MERGETABLE table) - { - LibmsiResult r; - if (!TableViewExists(this, table.Name)) - { - r = AddTableToDb(table.Columns, table.Types, table.Labels, table.NumLabels, table.NumColumns); - if (r != LibmsiResult.LIBMSI_RESULT_SUCCESS) - return LibmsiResult.LIBMSI_RESULT_FUNCTION_FAILED; - } - - foreach (MERGEROW row in table.Rows) - { - r = LibmsiTableView.Create(this, table.Name, out LibmsiView tv); - if (r != LibmsiResult.LIBMSI_RESULT_SUCCESS) - return r; - - r = tv.InsertRow(row.Data, -1, false); - tv.Delete(); - - if (r != LibmsiResult.LIBMSI_RESULT_SUCCESS) - return r; - } - - return LibmsiResult.LIBMSI_RESULT_SUCCESS; - } - - private LibmsiResult UpdateMergeErrors(string error, string table, int numconflicts) - { - LibmsiResult r; - if (!TableViewExists(this, error)) - { - string create = $"CREATE TABLE `{error}` (`Table` CHAR(255) NOT NULL, `NumRowMergeConflicts` SHORT NOT NULL PRIMARY KEY `Table`)"; - r = QueryOpen(this, out LibmsiQuery createView, create); - if (r != LibmsiResult.LIBMSI_RESULT_SUCCESS) - return r; - - r = createView.Execute(null); - if (r != LibmsiResult.LIBMSI_RESULT_SUCCESS) - return r; - } - - string insert = $"INSERT INTO `{error}` (`Table`, `NumRowMergeConflicts`) VALUES ('{table}', {numconflicts})"; - r = QueryOpen(this, out LibmsiQuery view, insert); - if (r != LibmsiResult.LIBMSI_RESULT_SUCCESS) - return r; - - r = view.Execute(null); - return r; - } - - private void CacheInfileStructure() - { - LibmsiResult r; - - int n = Infile.NumChildren(); - - // TODO: error handling - for (int i = 0; i < n; i++) - { - Exception err = null; - GsfInput input = Infile.ChildByIndex(i, ref err); - string name = input?.Name; - byte[] name8 = Encoding.UTF8.GetBytes(name ?? string.Empty); - - if (name == null) - { - Console.Error.WriteLine("Name was null"); - continue; - } - - // Table streams are not in the _Streams table - if (!(input is GsfInfile) || (input as GsfInfile).NumChildren() == -1) - { - // UTF-8 encoding of 0x4840. - if (name8[0] == 0xe4 && name8[1] == 0xa1 && name8[2] == 0x80) - { - string decname = DecodeStreamName(name.Substring(1)).TrimEnd('\0'); - if (decname.StartsWith(szStringPool) || decname.StartsWith(szStringData)) - continue; - - r = OpenTable(this, decname, false); - if (r != LibmsiResult.LIBMSI_RESULT_SUCCESS) - Console.Error.WriteLine($"Error opening table {decname}: {r}"); - } - else - { - r = MsiAllocStream(name, input); - } - } - else - { - OpenStorage(name); - } - } - } - - private static bool GsfInfileCopy(GsfInfile inf, GsfOutfile outf) - { - int n = inf.NumChildren(); - for (int i = 0; i < n; i++) - { - Exception err = null; - string name = inf.NameByIndex(i); - GsfInput child = inf.ChildByName(name, ref err); - GsfInfile childf = (child is GsfInfile) ? (child as GsfInfile) : null; - bool is_dir = childf != null && childf.NumChildren() > 0; - GsfOutput dest = outf.NewChild(name, is_dir); - - bool ok; - if (is_dir) - ok = GsfInfileCopy(childf, dest as GsfOutfile); - else - ok = child.Copy(dest); - - if (!ok) - return false; - } - - return true; - } - - // TODO: Can we avoid the use of `opaque`? - private static LibmsiResult CommitStorage(string name, GsfInfile stg, object opaque) - { - LibmsiDatabase db = opaque as LibmsiDatabase; - GsfOutfile outstg = (db.Outfile.NewChild(name, true) as GsfOutfile); - if (outstg == null) - return LibmsiResult.LIBMSI_RESULT_FUNCTION_FAILED; - - if (!GsfInfileCopy(stg, outstg)) - { - outstg.Close(); - return LibmsiResult.LIBMSI_RESULT_FUNCTION_FAILED; - } - - outstg.Close(); - return LibmsiResult.LIBMSI_RESULT_SUCCESS; - } - - // TODO: Can we avoid the use of `opaque`? - private static LibmsiResult CommitStream(string name, GsfInput stm, object opaque) - { - LibmsiDatabase db = opaque as LibmsiDatabase; - string decname = DecodeStreamName(name); - GsfOutput outstm = db.Outfile.NewChild(name, false); - if (outstm == null) - return LibmsiResult.LIBMSI_RESULT_FUNCTION_FAILED; - - stm.Seek(0, SeekOrigin.Begin); - outstm.Seek(0, SeekOrigin.Begin); - if (!stm.Copy(outstm)) - { - outstm.Close(); - return LibmsiResult.LIBMSI_RESULT_FUNCTION_FAILED; - } - - outstm.Close(); - return LibmsiResult.LIBMSI_RESULT_SUCCESS; - } - - // TODO: Can we avoid the use of `param`? - private static LibmsiResult PrimaryKeyIterator(LibmsiRecord rec, object param) - { - MsiPrimaryKeyRecordInfo info = param as MsiPrimaryKeyRecordInfo; - int type = rec.GetInt(4); - if ((type & MSITYPE_KEY) != 0) - { - info.N++; - if (info.Rec != null) - { - if (info.N == 1) - { - string table = rec.GetStringRaw(1); - info.Rec.SetString(0, table); - } - - string name = rec.GetStringRaw(3); - info.Rec.SetString(info.N, name); - } - } - - return LibmsiResult.LIBMSI_RESULT_SUCCESS; - } - - private bool Init(ref Exception err) - { - if (Flags.HasFlag(LibmsiDbFlags.LIBMSI_DB_FLAGS_CREATE)) - { - Strings = InitStringTable(out int bytes_per_strref); - BytesPerStrref = bytes_per_strref; - } - else - { - if (Open() != LibmsiResult.LIBMSI_RESULT_SUCCESS) - return false; - } - - MediaTransformOffset = MSI_INITIAL_MEDIA_TRANSFORM_OFFSET; - MediaTransformDiskId = MSI_INITIAL_MEDIA_TRANSFORM_DISKID; - - bool TRACE_ON = false; // TODO: Make configurable - if (TRACE_ON && Infile != null) - EnumStreamNames(Infile); - - LibmsiResult ret = StartTransaction(); - return ret == LibmsiResult.LIBMSI_RESULT_SUCCESS; - } - - #endregion - } -} \ No newline at end of file diff --git a/BurnOutSharp/External/libmsi/LibmsiQuery.cs b/BurnOutSharp/External/libmsi/LibmsiQuery.cs deleted file mode 100644 index b74d3e83..00000000 --- a/BurnOutSharp/External/libmsi/LibmsiQuery.cs +++ /dev/null @@ -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 Mem { get; set; } = new LinkedList(); - - #endregion - - #region Constructor and Destructor - - /// - /// Private constructor - /// - private LibmsiQuery() { } - - /// - /// Create a SQL query for . - /// - /// A LibmsiDatabase - /// A SQL query - /// Exception to set on error, or null - /// A new LibmsiQuery on success, null on failure - 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; - } - - /// - /// Destructor - /// - ~LibmsiQuery() - { - if (View != null) - try { View.Delete(); } catch { } - - Mem.Clear(); - } - - #endregion - - #region Functions - - /// - /// Return the next query result. null is returned when there - /// is no more results. - /// - /// Return location for the error - /// A newly allocated LibmsiRecord or null when no results or failure. - 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; - } - - /// - /// Execute the query with the arguments from . - /// - /// A LibmsiRecord containing query arguments, or null if no arguments needed - /// Return location for the error - /// True on success - 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; - } - - /// - /// Release the current result set. - /// - /// Return location for the error - /// True on success - 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; - } - } - - /// - /// Call this to get more information on the last query error. - /// - /// Location to store the allocated column name - /// Return location for the error - 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; - } - } - - /// - /// Get column informations, returned as record string fields. - /// - /// A LibmsiColInfo specifying the type of information to return - /// Return location for the error - /// A LibmsiRecord containing informations, or null on error - 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; - } - - /// - /// Return a single record from a query - /// - // 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 - } -} \ No newline at end of file diff --git a/BurnOutSharp/External/libmsi/LibmsiRecord.cs b/BurnOutSharp/External/libmsi/LibmsiRecord.cs deleted file mode 100644 index 41116f03..00000000 --- a/BurnOutSharp/External/libmsi/LibmsiRecord.cs +++ /dev/null @@ -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 - - /// - /// as passed to libmsi_record_new - /// - internal int Count { get; set; } - - /// - /// nb. array size is count+1 - /// - internal LibmsiField[] Fields { get; set; } - - #endregion - - #region Constructor and Destructor - - /// - /// Private constructor - /// - 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 - - /// - /// Clear record fields. - /// - /// True on success. - public bool Clear() - { - for (int i = 0; i <= Count; i++) - { - Fields[i].Type = LIBMSI_FIELD_TYPE_NULL; - Fields[i].iVal = 0; - } - - return true; - } - - /// The number of record fields. - public int GetFieldCount() => Count; - - /// A field identifier - /// True if the field is null (or > record field count) - public bool IsNull(int field) => (field > Count) || (Fields[field].Type == LIBMSI_FIELD_TYPE_NULL); - - /// - /// Set the to the integer value . - /// - /// A field identifier - /// Value to set field to - /// True on success. - 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; - } - - /// - /// Get the integer value of . 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. - /// - /// A field identifier - /// The integer value, or LIBMSI_NULL_INT if the field is not an integer. - 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; } - } - - /// - /// Set the value to string. - /// - /// A field identifier - /// A string or null - /// True on success. - 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; - } - - /// - /// Get a string representation of . - /// - /// A field identifier - /// A string, or null on error. - 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; - } - - /// - /// Load the file content as a stream in . - /// - /// A field identifier - /// A filename or null - /// True on success. - public bool LoadStream(int field, string szFilename) - { - LibmsiResult ret = LoadStreamFromFile(field, szFilename); - return ret == LibmsiResult.LIBMSI_RESULT_SUCCESS; - } - - /// - /// Set the stream content from stream. - /// - /// A field identifier - /// A GInputStream - /// The number of bytes to read from - /// Exception to set on error, or null - /// True on success. - 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; - } - - /// - /// Get the stream associated with the given record . - /// - /// A field identifier - /// True on success. - 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; - } - - /// - /// Read the data in a file into a memory-backed GsfInput - /// - 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 - } -} \ No newline at end of file diff --git a/BurnOutSharp/External/libmsi/LibmsiSummaryInfo.cs b/BurnOutSharp/External/libmsi/LibmsiSummaryInfo.cs deleted file mode 100644 index b7d010a6..00000000 --- a/BurnOutSharp/External/libmsi/LibmsiSummaryInfo.cs +++ /dev/null @@ -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; } - - /// - /// Number of changes allowed - /// - internal int UpdateCount { get; set; } - - internal LibmsiOLEVariant[] Properties { get; set; } = new LibmsiOLEVariant[MSI_MAX_PROPS]; - - #endregion - - #region Constructor and Destructor - - /// - /// Private constructor - /// - private LibmsiSummaryInfo() { } - - // TODO: Investigate why Create isn't populating the properties - - /// - /// 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. - /// - /// An optional associated LibmsiDatabase - /// Number of changes allowed - /// Exception to set on error, or null - /// A LibmsiSummaryInfo or null on failure - 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; - - } - - /// - /// Destructor - /// - ~LibmsiSummaryInfo() - { - for (int i = 0; i < MSI_MAX_PROPS; i++) - { - FreeProp(Properties[i]); - } - } - - #endregion - - #region Functions - - /// A LibmsiProperty to get - /// Exception to set on error, or null - /// The property type associated for .= 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; - } - } - - /// A LibmsiProperty to get - /// Exception to set on error, or null - /// The property value or null on failure.A LibmsiProperty to get - /// Exception to set on error, or null - /// The property value or -1 on failure.A LibmsiProperty to get - /// Exception to set on error, or null - /// The property value or 0 on failure. - /// Set string property . - /// - /// A LibmsiProperty to set - /// A string value - /// Exception to set on error, or null - /// True on success - 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; - } - - /// - /// Set integer property . - /// - /// A LibmsiProperty to set - /// A value - /// Exception to set on error, or null - /// True on success - 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; - } - - /// - /// Set integer property . - /// - /// A LibmsiProperty to set - /// A value - /// Exception to set on error, or null - /// True on success - 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; - } - - /// - /// Save summary informations to the associated database. - /// - /// Exception to set on error, or null - /// True on success - 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; - } - - /// - /// Save summary informations to the associated database. - /// - /// A LibmsiDatabase to save to - /// Exception to set on error, or null - /// True on success - 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; - } - - /// A new list with the list of set properties - public List GetProperties() - { - List props = new List(); - 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(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 - } -} \ No newline at end of file diff --git a/BurnOutSharp/External/libmsi/LibmsiTypes.cs b/BurnOutSharp/External/libmsi/LibmsiTypes.cs deleted file mode 100644 index 48401cc2..00000000 --- a/BurnOutSharp/External/libmsi/LibmsiTypes.cs +++ /dev/null @@ -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 -{ - /// Also known as LibmsiResultError - 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; - } -} \ No newline at end of file diff --git a/BurnOutSharp/External/libmsi/Views/LibmsiAlterView.cs b/BurnOutSharp/External/libmsi/Views/LibmsiAlterView.cs deleted file mode 100644 index 81e1754e..00000000 --- a/BurnOutSharp/External/libmsi/Views/LibmsiAlterView.cs +++ /dev/null @@ -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 - - /// - /// Private constructor - /// - 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 - - /// - public override LibmsiResult FetchInt(int row, int col, out int val) - { - val = default; - return LibmsiResult.LIBMSI_RESULT_FUNCTION_FAILED; - } - - /// - public override LibmsiResult FetchStream(int row, int col, out GsfInput stm) - { - stm = null; - return LibmsiResult.LIBMSI_RESULT_FUNCTION_FAILED; - } - - /// - public override LibmsiResult GetRow(int row, out LibmsiRecord rec) => Table.GetRow(row, out rec); - - /// - 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; - } - - /// - public override LibmsiResult Close() => LibmsiResult.LIBMSI_RESULT_SUCCESS; - - /// - public override LibmsiResult GetDimensions(out int rows, out int cols) - { - rows = 0; cols = 0; - return LibmsiResult.LIBMSI_RESULT_FUNCTION_FAILED; - } - - /// - 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; - } - - /// - public override LibmsiResult Delete() - { - if (Table != null) - Table.Delete(); - - return LibmsiResult.LIBMSI_RESULT_SUCCESS; - } - - /// - 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 - } -} \ No newline at end of file diff --git a/BurnOutSharp/External/libmsi/Views/LibmsiCreateView.cs b/BurnOutSharp/External/libmsi/Views/LibmsiCreateView.cs deleted file mode 100644 index 08a8a7fa..00000000 --- a/BurnOutSharp/External/libmsi/Views/LibmsiCreateView.cs +++ /dev/null @@ -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 - - /// - /// Private constructor - /// - 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 - - /// - public override LibmsiResult FetchInt(int row, int col, out int val) - { - val = 0; - return LibmsiResult.LIBMSI_RESULT_FUNCTION_FAILED; - } - - /// - 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); - } - - /// - public override LibmsiResult Close() => LibmsiResult.LIBMSI_RESULT_SUCCESS; - - /// - public override LibmsiResult GetDimensions(out int rows, out int cols) - { - rows = 0; cols = 0; - return LibmsiResult.LIBMSI_RESULT_FUNCTION_FAILED; - } - - /// - 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; - } - - /// - 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 - } -} \ No newline at end of file diff --git a/BurnOutSharp/External/libmsi/Views/LibmsiDeleteView.cs b/BurnOutSharp/External/libmsi/Views/LibmsiDeleteView.cs deleted file mode 100644 index 5b052890..00000000 --- a/BurnOutSharp/External/libmsi/Views/LibmsiDeleteView.cs +++ /dev/null @@ -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 -{ - /// - /// Code to delete rows from a table. - /// - /// - /// 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 - - /// - /// Private constructor - /// - 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 - - /// - public override LibmsiResult FetchInt(int row, int col, out int val) - { - val = 0; - return LibmsiResult.LIBMSI_RESULT_FUNCTION_FAILED; - } - - /// - public override LibmsiResult FetchStream(int row, int col, out GsfInput stm) - { - stm = null; - return LibmsiResult.LIBMSI_RESULT_FUNCTION_FAILED; - } - - /// - 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; - } - - /// - public override LibmsiResult Close() - { - if (Table == null) - return LibmsiResult.LIBMSI_RESULT_FUNCTION_FAILED; - - return Table.Close(); - } - - /// - 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); - } - - /// - 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); - } - - /// - public override LibmsiResult Delete() - { - if (Table != null) - Table.Delete(); - - return LibmsiResult.LIBMSI_RESULT_SUCCESS; - } - - /// - public override LibmsiResult FindMatchingRows(int col, int val, out int row, ref LibmsiColumnHashEntry handle) - { - row = 0; - return LibmsiResult.LIBMSI_RESULT_FUNCTION_FAILED; - } - - #endregion - } -} \ No newline at end of file diff --git a/BurnOutSharp/External/libmsi/Views/LibmsiDistinctView.cs b/BurnOutSharp/External/libmsi/Views/LibmsiDistinctView.cs deleted file mode 100644 index 8f08c1e3..00000000 --- a/BurnOutSharp/External/libmsi/Views/LibmsiDistinctView.cs +++ /dev/null @@ -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 - - /// - /// Private constructor - /// - 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 - - /// - 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); - } - - /// - 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; - } - - /// - public override LibmsiResult Close() - { - if (Table == null) - return LibmsiResult.LIBMSI_RESULT_FUNCTION_FAILED; - - Translation = null; - RowCount = 0; - - return Table.Close(); - } - - /// - 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); - } - - /// - 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); - } - - /// - public override LibmsiResult Delete() - { - if (Table != null) - Table.Delete(); - - Translation = null; - Database = null; - - return LibmsiResult.LIBMSI_RESULT_SUCCESS; - } - - /// - 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 - } -} \ No newline at end of file diff --git a/BurnOutSharp/External/libmsi/Views/LibmsiDropView.cs b/BurnOutSharp/External/libmsi/Views/LibmsiDropView.cs deleted file mode 100644 index b2b7809a..00000000 --- a/BurnOutSharp/External/libmsi/Views/LibmsiDropView.cs +++ /dev/null @@ -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 - - /// - /// Private constructor - /// - 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(); - } - - /// - public override LibmsiResult Close() => LibmsiResult.LIBMSI_RESULT_SUCCESS; - - /// - public override LibmsiResult GetDimensions(out int rows, out int cols) - { - rows = 0; cols = 0; - return LibmsiResult.LIBMSI_RESULT_FUNCTION_FAILED; - } - - /// - public override LibmsiResult Delete() - { - if (Table != null) - Table.Delete(); - - return LibmsiResult.LIBMSI_RESULT_SUCCESS; - } - - #endregion - } -} \ No newline at end of file diff --git a/BurnOutSharp/External/libmsi/Views/LibmsiInsertView.cs b/BurnOutSharp/External/libmsi/Views/LibmsiInsertView.cs deleted file mode 100644 index 063ab758..00000000 --- a/BurnOutSharp/External/libmsi/Views/LibmsiInsertView.cs +++ /dev/null @@ -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 - - /// - /// Private constructor - /// - 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 - - /// - public override LibmsiResult FetchInt(int row, int col, out int val) - { - val = 0; - return LibmsiResult.LIBMSI_RESULT_FUNCTION_FAILED; - } - - /// - 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); - } - - /// - public override LibmsiResult Close() - { - if (View == null) - return LibmsiResult.LIBMSI_RESULT_FUNCTION_FAILED; - - return View.Close(); - } - - /// - 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); - } - - /// - 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); - } - - /// - public override LibmsiResult Delete() - { - if (View != null) - View.Delete(); - - return LibmsiResult.LIBMSI_RESULT_SUCCESS; - } - - /// - 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 - - /// - /// Merge a value_list and a record to create a second record. - /// Replace wildcard entries in the valuelist with values from the record - /// - 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 - - /// - /// Checks to see if the column order specified in the INSERT query - /// matches the column order of the table - /// - 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; - } - - /// - /// 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 - /// - 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 - } -} \ No newline at end of file diff --git a/BurnOutSharp/External/libmsi/Views/LibmsiSelectView.cs b/BurnOutSharp/External/libmsi/Views/LibmsiSelectView.cs deleted file mode 100644 index a026a288..00000000 --- a/BurnOutSharp/External/libmsi/Views/LibmsiSelectView.cs +++ /dev/null @@ -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 -{ - /// - /// Below is the query interface to a table - /// - 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 - - /// - /// Private constructor - /// - 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 - - /// - 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); - } - - /// - 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); - } - - /// - 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); - } - - /// - 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; - } - - /// - 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 ); - } - - /// - public override LibmsiResult Execute(LibmsiRecord record) - { - if (Table == null) - return LibmsiResult.LIBMSI_RESULT_FUNCTION_FAILED; - - return Table.Execute(record); - } - - /// - public override LibmsiResult Close() - { - if (Table == null) - return LibmsiResult.LIBMSI_RESULT_FUNCTION_FAILED; - - return Table.Close(); - } - - /// - 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 _); - } - - /// - 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); - } - - /// - public override LibmsiResult Delete() - { - if (Table != null) - Table.Delete(); - - Table = null; - - return LibmsiResult.LIBMSI_RESULT_SUCCESS; - } - - /// - 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 - } -} \ No newline at end of file diff --git a/BurnOutSharp/External/libmsi/Views/LibmsiStorageView.cs b/BurnOutSharp/External/libmsi/Views/LibmsiStorageView.cs deleted file mode 100644 index 45d52f91..00000000 --- a/BurnOutSharp/External/libmsi/Views/LibmsiStorageView.cs +++ /dev/null @@ -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 - - /// - /// Private constructor - /// - 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) - }; - } - - /// - 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; - } - - /// - 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; - } - - /// - public override LibmsiResult GetRow(int row, out LibmsiRecord rec) - { - rec = null; - return LibmsiResult.LIBMSI_RESULT_CALL_NOT_IMPLEMENTED; - } - - /// - 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; - } - - /// - 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); - } - - /// - 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; - } - - /// - public override LibmsiResult Execute(LibmsiRecord record) => LibmsiResult.LIBMSI_RESULT_SUCCESS; - - /// - public override LibmsiResult Close() => LibmsiResult.LIBMSI_RESULT_SUCCESS; - - /// - public override LibmsiResult GetDimensions(out int rows, out int cols) - { - cols = NUM_STORAGES_COLS; - rows = NumRows; - return LibmsiResult.LIBMSI_RESULT_SUCCESS; - } - - /// - 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; - } - - /// - public override LibmsiResult Delete() - { - Storages = null; - return 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_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 - } -} \ No newline at end of file diff --git a/BurnOutSharp/External/libmsi/Views/LibmsiStreamsView.cs b/BurnOutSharp/External/libmsi/Views/LibmsiStreamsView.cs deleted file mode 100644 index 900b1aac..00000000 --- a/BurnOutSharp/External/libmsi/Views/LibmsiStreamsView.cs +++ /dev/null @@ -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 - - /// - /// Private constructor - /// - 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 - - /// - 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; - } - - /// - 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; - } - - /// - public override LibmsiResult GetRow(int row, out LibmsiRecord rec) => MsiViewGetRow(Database, this, row, out rec); - - /// - 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; - } - - /// - 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); - } - - /// - 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; - } - - /// - public override LibmsiResult Execute(LibmsiRecord record) => LibmsiResult.LIBMSI_RESULT_SUCCESS; - - /// - public override LibmsiResult Close() => LibmsiResult.LIBMSI_RESULT_SUCCESS; - - /// - public override LibmsiResult GetDimensions(out int rows, out int cols) - { - cols = NUM_STREAMS_COLS; - rows = NumRows; - return LibmsiResult.LIBMSI_RESULT_SUCCESS; - } - - /// - 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; - } - - /// - 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 - } -} \ No newline at end of file diff --git a/BurnOutSharp/External/libmsi/Views/LibmsiTableView.cs b/BurnOutSharp/External/libmsi/Views/LibmsiTableView.cs deleted file mode 100644 index b1883d2a..00000000 --- a/BurnOutSharp/External/libmsi/Views/LibmsiTableView.cs +++ /dev/null @@ -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 - - /// - /// Private constructor - /// - 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 - - /// - 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; - } - - /// - 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; - } - - /// - 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); - } - - /// - 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; - } - - /// - public override LibmsiResult Execute(LibmsiRecord record) => LibmsiResult.LIBMSI_RESULT_SUCCESS; - - /// - public override LibmsiResult Close() => LibmsiResult.LIBMSI_RESULT_SUCCESS; - - /// - 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; - } - - /// - 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; - } - - /// - 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(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); - } - - /// - 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; - } - - /// - public override LibmsiResult Delete() - { - Table = null; - Columns = null; - return LibmsiResult.LIBMSI_RESULT_SUCCESS; - } - - /// - 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; - } - - /// - 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; - } - - /// - 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; - } - - /// - 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; - } - - /// - 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; - } - - /// - 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 - } -} \ No newline at end of file diff --git a/BurnOutSharp/External/libmsi/Views/LibmsiUpdateView.cs b/BurnOutSharp/External/libmsi/Views/LibmsiUpdateView.cs deleted file mode 100644 index f09d95bb..00000000 --- a/BurnOutSharp/External/libmsi/Views/LibmsiUpdateView.cs +++ /dev/null @@ -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 -{ - /// - /// Below is the query interface to a table - /// - 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 - - /// - /// Private constructor - /// - 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 - - /// - public override LibmsiResult FetchInt(int row, int col, out int val) - { - val = 0; - return LibmsiResult.LIBMSI_RESULT_FUNCTION_FAILED; - } - - /// - 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; - } - - /// - public override LibmsiResult Close() - { - if (View == null) - return LibmsiResult.LIBMSI_RESULT_FUNCTION_FAILED; - - return View.Close(); - } - - /// - 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); - } - - /// - 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); - } - - /// - public override LibmsiResult Delete() - { - if (View != null) - View.Delete(); - - return LibmsiResult.LIBMSI_RESULT_SUCCESS; - } - - /// - public override LibmsiResult FindMatchingRows(int col, int val, out int row, ref LibmsiColumnHashEntry handle) - { - row = 0; - return LibmsiResult.LIBMSI_RESULT_FUNCTION_FAILED; - } - - #endregion - } -} \ No newline at end of file diff --git a/BurnOutSharp/External/libmsi/Views/LibmsiView.cs b/BurnOutSharp/External/libmsi/Views/LibmsiView.cs deleted file mode 100644 index 8f0e248c..00000000 --- a/BurnOutSharp/External/libmsi/Views/LibmsiView.cs +++ /dev/null @@ -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 - - /// - /// Reads one integer from {row,col} in the table - /// - /// - /// 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. - /// - public virtual LibmsiResult FetchInt(int row, int col, out int val) => throw new NotImplementedException(); - - /// - /// Gets a stream from {row,col} in the table - /// - /// - /// This function is similar to FetchInt, except fetches a - /// stream instead of an integer. - /// - public virtual LibmsiResult FetchStream(int row, int col, out GsfInput stm) => throw new NotImplementedException(); - - /// - /// Gets values from a row - /// - public virtual LibmsiResult GetRow(int row, out LibmsiRecord rec) => throw new NotImplementedException(); - - /// - /// Sets values in a row as specified by mask - /// - /// Similar semantics to fetch_int - public virtual LibmsiResult SetRow(int row, LibmsiRecord rec, int mask) => throw new NotImplementedException(); - - /// - /// Inserts a new row into the database from the records contents - /// - public virtual LibmsiResult InsertRow(LibmsiRecord record, int row, bool temporary) => throw new NotImplementedException(); - - /// - /// Deletes a row from the database - /// - public virtual LibmsiResult DeleteRow(int row) => throw new NotImplementedException(); - - /// - /// Loads the underlying data into memory so it can be read - /// - public virtual LibmsiResult Execute(LibmsiRecord record) => throw new NotImplementedException(); - - /// - /// Clears the data read by execute from memory - /// - public virtual LibmsiResult Close() => throw new NotImplementedException(); - - /// - /// Returns the number of rows or columns in a table. - /// - /// - /// The number of rows can only be queried after the execute method - /// is called. The number of columns can be queried at any time. - /// - public virtual LibmsiResult GetDimensions(out int rows, out int cols) => throw new NotImplementedException(); - - /// - /// Returns the name and type of a specific column - /// - /// The column information can be queried at any time. - public virtual LibmsiResult GetColumnInfo(int n, out string name, out int type, out bool temporary, out string table_name) => throw new NotImplementedException(); - - /// - /// Destroys the structure completely - /// - public virtual LibmsiResult Delete() => throw new NotImplementedException(); - - /// - /// Iterates through rows that match a value - /// - /// - /// 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. - /// - public virtual LibmsiResult FindMatchingRows(int col, int val, out int row, ref LibmsiColumnHashEntry handle) => throw new NotImplementedException(); - - /// - /// Increases the reference count of the table - /// - public virtual int AddRef() => throw new NotImplementedException(); - - /// - /// Decreases the reference count of the table - /// - public virtual int Release() => throw new NotImplementedException(); - - /// - /// Adds a column to the table - /// - public virtual LibmsiResult AddColumn(string table, int number, string column, int type, bool hold) => throw new NotImplementedException(); - - /// - /// Removes the column represented by table name and column number from the table - /// - public virtual LibmsiResult RemoveColumn(string table, int number) => throw new NotImplementedException(); - - /// - /// Orders the table by columns - /// - public virtual LibmsiResult Sort(column_info columns) => throw new NotImplementedException(); - - /// - /// Drops the table from the database - /// - public virtual LibmsiResult Drop() => throw new NotImplementedException(); - - #endregion - } -} \ No newline at end of file diff --git a/BurnOutSharp/External/libmsi/Views/LibmsiWhereView.cs b/BurnOutSharp/External/libmsi/Views/LibmsiWhereView.cs deleted file mode 100644 index 2b7afe3d..00000000 --- a/BurnOutSharp/External/libmsi/Views/LibmsiWhereView.cs +++ /dev/null @@ -1,1105 +0,0 @@ -/* - * Implementation of the Microsoft Installer (msi.dll) - * - * Copyright 2002 Mike McCormack for CodeWeavers - * Copyright 2011 Bernhard Loos - * - * 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.Internal.LibmsiSQLInput; -using static LibMSI.Internal.MsiPriv; - -namespace LibMSI.Views -{ - internal class LibmsiRowEntry - { - /// - /// Used during sorting - /// - public LibmsiWhereView WhereView { get; set; } - - public int[] Values { get; set; } = new int[1]; - } - - internal class JOINTABLE - { - public JOINTABLE Next { get; set; } - - public LibmsiView View { get; set; } - - public int ColCount { get; set; } - - public int RowCount { get; set; } - - public int TableIndex { get; set; } - } - - internal class LibmsiOrderInfo - { - public int ColCount { get; set; } - - public LibmsiResult Error { get; set; } - - public ext_column[] Columns { get; set; } = new ext_column[1]; - } - - internal class LibmsiWhereView : LibmsiView - { - #region Constants - - private const int INITIAL_REORDER_SIZE = 16; - - private const int INVALID_ROW_INDEX = -1; - - /// - /// Comparison to a constant value - /// - private const int CONST_EXPR = 1; - - /// - /// Comparison to a table involved with a CONST_EXPR comaprison - /// - private const int JOIN_TO_CONST_EXPR = 0x10000; - - #endregion - - #region Properties - - public LibmsiDatabase Database { get; set; } - - public JOINTABLE Tables { get; set; } - - public int RowCount { get; set; } - - public int ColCount { get; set; } - - public int TableCount { get; set; } - - public LibmsiRowEntry[] Reorder { get; set; } - - /// - /// Number of entries available in reorder - /// - public int ReorderSize { get; set; } - - public expr Condition { get; set; } - - public int RecIndex { get; set; } - - public LibmsiOrderInfo OrderInfo { get; set; } - - #endregion - - #region Constructor - - /// - /// Private constructor - /// - private LibmsiWhereView() { } - - public static LibmsiResult Create(LibmsiDatabase db, out LibmsiView view, string tables, expr cond) - { - view = null; - - LibmsiWhereView wv = new LibmsiWhereView - { - Database = db, - Condition = cond, - }; - - LibmsiResult r; - int tablesPtr = 0; // tables[0] - while (tablesPtr < tables.Length && tables[tablesPtr] != '\0') - { - int ptr = tables.IndexOf(' ', tablesPtr); - if (ptr != -1) - { - char[] temp = tables.ToCharArray(); - temp[ptr] = '\0'; - tables = new string(temp); - } - - JOINTABLE table = new JOINTABLE(); - r = LibmsiTableView.Create(db, tables, out LibmsiView table_view); - if (r != LibmsiResult.LIBMSI_RESULT_SUCCESS) - { - Console.Error.WriteLine($"Can't create table: {tables}"); - wv.Delete(); - return LibmsiResult.LIBMSI_RESULT_BAD_QUERY_SYNTAX; - } - - table.View = table_view; - r = table.View.GetDimensions(out _, out int col_count); - if (r != LibmsiResult.LIBMSI_RESULT_SUCCESS) - { - Console.Error.WriteLine("Can't get table dimensions"); - wv.Delete(); - return r; - } - - table.ColCount = col_count; - wv.ColCount += table.ColCount; - table.TableIndex = wv.TableCount++; - - table.Next = wv.Tables; - wv.Tables = table; - - if (ptr == -1) - break; - - tablesPtr = ptr + 1; - } - - bool valid = false; - if (cond != null) - { - r = wv.VerifyCondition(cond, ref valid); - if (r != LibmsiResult.LIBMSI_RESULT_SUCCESS) - { - wv.Delete(); - return r; - } - - if (!valid) - { - wv.Delete(); - return LibmsiResult.LIBMSI_RESULT_FUNCTION_FAILED;; - } - } - - view = wv; - return LibmsiResult.LIBMSI_RESULT_SUCCESS; - } - - #endregion - - #region Functions - - /// - public override LibmsiResult FetchInt(int row, int col, out int val) - { - val = 0; - if (Tables == null) - return LibmsiResult.LIBMSI_RESULT_FUNCTION_FAILED; - - LibmsiResult r = FindRow(row, out int[] rows); - if (r != LibmsiResult.LIBMSI_RESULT_SUCCESS) - return r; - - JOINTABLE table = FindTable(col, out col); - if (table == null) - return LibmsiResult.LIBMSI_RESULT_FUNCTION_FAILED; - - return table.View.FetchInt(rows[table.TableIndex], col, out val); - } - - /// - public override LibmsiResult FetchStream(int row, int col, out GsfInput stm) - { - stm = null; - if (Tables == null) - return LibmsiResult.LIBMSI_RESULT_FUNCTION_FAILED; - - LibmsiResult r = FindRow(row, out int[] rows); - if (r != LibmsiResult.LIBMSI_RESULT_SUCCESS) - return r; - - JOINTABLE table = FindTable(col, out col); - if (table == null) - return LibmsiResult.LIBMSI_RESULT_FUNCTION_FAILED; - - return table.View.FetchStream(rows[table.TableIndex], col, out stm); - } - - /// - public override LibmsiResult GetRow(int row, out LibmsiRecord rec) - { - rec = null; - if (Tables == null) - return LibmsiResult.LIBMSI_RESULT_FUNCTION_FAILED; - - return MsiViewGetRow(Database, this, row, out rec); - } - - /// - public override LibmsiResult SetRow(int row, LibmsiRecord rec, int mask) - { - if (Tables == null) - return LibmsiResult.LIBMSI_RESULT_FUNCTION_FAILED; - - LibmsiResult r = FindRow(row, out int[] rows); - if (r != LibmsiResult.LIBMSI_RESULT_SUCCESS) - return r; - - if (mask >= 1 << ColCount) - return LibmsiResult.LIBMSI_RESULT_INVALID_PARAMETER; - - JOINTABLE table = Tables; - int mask_copy = mask; - do - { - for (int i = 0; i < table.ColCount; i++) - { - if ((mask_copy & (1 << i)) == 0) - continue; - - r = table.View.GetColumnInfo(i + 1, out _, out int type, out _, out _); - if (r != LibmsiResult.LIBMSI_RESULT_SUCCESS) - return r; - - if ((type & MSITYPE_KEY) != 0) - return LibmsiResult.LIBMSI_RESULT_FUNCTION_FAILED; - } - - mask_copy >>= table.ColCount; - } - while (mask_copy != 0 && (table = table.Next) != null); - - table = Tables; - - int offset = 0; - do - { - int col_count = table.ColCount; - int reduced_mask = (mask >> offset) & ((1 << col_count) - 1); - - if (reduced_mask == 0) - { - offset += col_count; - continue; - } - - LibmsiRecord reduced = LibmsiRecord.Create(col_count); - if (reduced == null) - return LibmsiResult.LIBMSI_RESULT_FUNCTION_FAILED; - - for (int i = 1; i <= col_count; i++) - { - r = rec.CopyField(i + offset, reduced, i); - if (r != LibmsiResult.LIBMSI_RESULT_SUCCESS) - break; - } - - offset += col_count; - - if (r == LibmsiResult.LIBMSI_RESULT_SUCCESS) - r = table.View.SetRow(rows[table.TableIndex], reduced, reduced_mask); - - } - while ((table = table.Next) != null); - - return r; - } - - /// - public override LibmsiResult DeleteRow(int row) - { - if (Tables == null) - return LibmsiResult.LIBMSI_RESULT_FUNCTION_FAILED; - - LibmsiResult r = FindRow(row, out int[] rows); - if (r != LibmsiResult.LIBMSI_RESULT_SUCCESS) - return r; - - if (TableCount > 1) - return LibmsiResult.LIBMSI_RESULT_CALL_NOT_IMPLEMENTED; - - return Tables.View.DeleteRow(rows[0]); - } - - /// - public override LibmsiResult Execute(LibmsiRecord record) - { - JOINTABLE table = Tables; - if (table == null) - return LibmsiResult.LIBMSI_RESULT_FUNCTION_FAILED; - - LibmsiResult r = InitReorder(); - if (r != LibmsiResult.LIBMSI_RESULT_SUCCESS) - return r; - - do - { - table.View.Execute(null); - r = table.View.GetDimensions(out int row_count, out _); - if (r != LibmsiResult.LIBMSI_RESULT_SUCCESS) - { - Console.Error.WriteLine("Failed to get table dimensions"); - return r; - } - - table.RowCount = row_count; - - // Each table must have at least one row - if (table.RowCount == 0) - return LibmsiResult.LIBMSI_RESULT_SUCCESS; - } - while ((table = table.Next) != null); - - JOINTABLE[] ordered_tables = OrderTables(); - - int[] rows = new int[TableCount]; - for (int i = 0; i < TableCount; i++) - { - rows[i] = INVALID_ROW_INDEX; - } - - r = CheckCondition(record, ordered_tables, rows); - if (OrderInfo != null) - OrderInfo.Error = LibmsiResult.LIBMSI_RESULT_SUCCESS; - - LibmsiRowEntry[] temp = Reorder; - Array.Sort(temp, CompareEntry); - Reorder = temp; - - if (OrderInfo != null) - r = OrderInfo.Error; - - return r; - } - - /// - public override LibmsiResult Close() - { - JOINTABLE table = Tables; - if (table == null) - return LibmsiResult.LIBMSI_RESULT_FUNCTION_FAILED; - - do - { - table.View.Close(); - } - while ((table = table.Next) != null); - - return LibmsiResult.LIBMSI_RESULT_SUCCESS; - } - - /// - public override LibmsiResult GetDimensions(out int rows, out int cols) - { - rows = 0; cols = 0; - if (Tables == null) - return LibmsiResult.LIBMSI_RESULT_FUNCTION_FAILED; - - if (Reorder == null) - return LibmsiResult.LIBMSI_RESULT_FUNCTION_FAILED; - - rows = RowCount; - cols = ColCount; - - return LibmsiResult.LIBMSI_RESULT_SUCCESS; - } - - /// - 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 (Tables == null) - return LibmsiResult.LIBMSI_RESULT_FUNCTION_FAILED; - - JOINTABLE table = FindTable(n, out n); - if (table == null) - return LibmsiResult.LIBMSI_RESULT_FUNCTION_FAILED; - - return table.View.GetColumnInfo(n, out name, out type, out temporary, out table_name); - } - - /// - public override LibmsiResult Delete() - { - JOINTABLE table = Tables; - while (table != null) - { - table.View.Delete(); - table.View = null; - JOINTABLE next = table.Next; - table = next; - } - - Tables = null; - TableCount = 0; - - FreeReorder(); - - OrderInfo = null; - - return LibmsiResult.LIBMSI_RESULT_SUCCESS; - } - - /// - public override LibmsiResult FindMatchingRows(int col, int val, out int row, ref LibmsiColumnHashEntry handle) - { - row = 0; - if (Tables == null) - return LibmsiResult.LIBMSI_RESULT_FUNCTION_FAILED; - - if (col == 0 || col > ColCount) - return LibmsiResult.LIBMSI_RESULT_INVALID_PARAMETER; - - for (int i = handle.Row; i < RowCount; i++) - { - if (FetchInt(i, col, out int row_value) != LibmsiResult.LIBMSI_RESULT_SUCCESS) - continue; - - if (row_value == val) - { - row = i; - handle = handle.Next; - return LibmsiResult.LIBMSI_RESULT_SUCCESS; - } - } - - return LibmsiResult.NO_MORE_ITEMS; - } - - /// - public override LibmsiResult Sort(column_info columns) - { - JOINTABLE table = Tables; - if (table == null) - return LibmsiResult.LIBMSI_RESULT_FUNCTION_FAILED; - - int count = 0; - column_info column = columns; - while (column != null) - { - count++; - column = column.Next; - } - - if (count == 0) - return LibmsiResult.LIBMSI_RESULT_SUCCESS; - - LibmsiOrderInfo orderinfo = new LibmsiOrderInfo - { - ColCount = count, - }; - - column = columns; - - for (int i = 0; i < count; i++) - { - orderinfo.Columns[i].Unparsed = new Tuple(column.Column, column.Table); - LibmsiResult r = ParseColumn(orderinfo.Columns[i], out _); - if (r != LibmsiResult.LIBMSI_RESULT_SUCCESS) - return r; - } - - OrderInfo = orderinfo; - - return LibmsiResult.LIBMSI_RESULT_SUCCESS; - } - - #endregion - - #region Utilities - - private void FreeReorder() - { - if (Reorder == null) - return; - - Reorder = null; - ReorderSize = 0; - RowCount = 0; - } - - private LibmsiResult InitReorder() - { - LibmsiRowEntry[] nre = new LibmsiRowEntry[INITIAL_REORDER_SIZE]; - FreeReorder(); - - Reorder = nre; - ReorderSize = INITIAL_REORDER_SIZE; - - return LibmsiResult.LIBMSI_RESULT_SUCCESS; - } - - private LibmsiResult FindRow(int row, out int[] values) - { - values = null; - if (row >= RowCount) - return LibmsiResult.NO_MORE_ITEMS; - - values = Reorder[row].Values; - return LibmsiResult.LIBMSI_RESULT_SUCCESS; - } - - private LibmsiResult AddRow(int[] vals) - { - if (ReorderSize <= RowCount) - { - int newsize = ReorderSize * 2; - LibmsiRowEntry[] new_reorder = new LibmsiRowEntry[newsize]; - Array.Copy(Reorder, new_reorder, ReorderSize); - Reorder = new_reorder; - ReorderSize = newsize; - } - - LibmsiRowEntry nre = new LibmsiRowEntry - { - Values = new int[TableCount], - }; - - Reorder[RowCount++] = nre; - Array.Copy(vals, nre.Values, TableCount); - nre.WhereView = this; - - return LibmsiResult.LIBMSI_RESULT_SUCCESS; - } - - private JOINTABLE FindTable(int col, out int table_col) - { - table_col = 0; - JOINTABLE table = Tables; - if (col == 0 || col > ColCount) - return null; - - while (col > table.ColCount) - { - col -= table.ColCount; - table = table.Next; - if (table == null) - return null; - } - - table_col = col; - return table; - } - - private LibmsiResult ParseColumn(ext_column column, out int column_type) - { - column_type = 0; - JOINTABLE table = Tables; - LibmsiResult r; - - do - { - string table_name; - if (column.Unparsed.Item2 != null) - { - r = table.View.GetColumnInfo(1, out _, out _, out _, out table_name); - if (r != LibmsiResult.LIBMSI_RESULT_SUCCESS) - return r; - - if (table_name != column.Unparsed.Item2) - continue; - } - - for (int i = 1; i <= table.ColCount; i++) - { - r = table.View.GetColumnInfo(i, out string col_name, out column_type, out _, out _); - if (r != LibmsiResult.LIBMSI_RESULT_SUCCESS) - return r; - - if (col_name != column.Unparsed.Item1) - continue; - - column.Parsed = new Tuple(i, table); - return LibmsiResult.LIBMSI_RESULT_SUCCESS; - } - } - while ((table = table.Next) != null); - - Console.Error.WriteLine($"Couldn't find column {column.Unparsed.Item2}.{column.Unparsed.Item1}"); - return LibmsiResult.LIBMSI_RESULT_BAD_QUERY_SYNTAX; - } - - /// - /// Reorders the tablelist in a way to evaluate the condition as fast as possible - /// - private JOINTABLE[] OrderTables() - { - JOINTABLE table; - JOINTABLE[] tables = new JOINTABLE[TableCount + 1]; - if (Condition != null) - { - table = null; - ReorderCheck(Condition, tables, false, ref table); - table = null; - ReorderCheck(Condition, tables, true, ref table); - } - - table = Tables; - while (table != null) - { - AddToArray(tables, 0, table); - table = table.Next; - } - return tables; - } - - private LibmsiResult ExprEvalBinary(int [] rows, complex_expr expr, out int val, LibmsiRecord record) - { - val = 0; - LibmsiResult rl = Evaluate(rows, expr.Left, out int lval, record); - if (rl != LibmsiResult.LIBMSI_RESULT_SUCCESS && rl != LibmsiResult.LIBMSI_RESULT_CONTINUE) - return rl; - LibmsiResult rr = Evaluate(rows, expr.Right, out int rval, record); - if (rr != LibmsiResult.LIBMSI_RESULT_SUCCESS && rr != LibmsiResult.LIBMSI_RESULT_CONTINUE) - return rr; - - if (rl == LibmsiResult.LIBMSI_RESULT_CONTINUE || rr == LibmsiResult.LIBMSI_RESULT_CONTINUE) - { - if (rl == rr) - { - val = 1; - return LibmsiResult.LIBMSI_RESULT_CONTINUE; - } - - if (expr.Op == OP_AND) - { - if ((rl == LibmsiResult.LIBMSI_RESULT_CONTINUE && rval == 0) || (rr == LibmsiResult.LIBMSI_RESULT_CONTINUE && lval == 0)) - { - val = 0; - return LibmsiResult.LIBMSI_RESULT_SUCCESS; - } - } - else if (expr.Op == OP_OR) - { - if ((rl == LibmsiResult.LIBMSI_RESULT_CONTINUE && rval != 0) || (rr == LibmsiResult.LIBMSI_RESULT_CONTINUE && lval != 0)) - { - val = 1; - return LibmsiResult.LIBMSI_RESULT_SUCCESS; - } - } - - val = 1; - return LibmsiResult.LIBMSI_RESULT_CONTINUE; - } - - switch (expr.Op) - { - case OP_EQ: - val = (lval == rval) ? 1 : 0; - break; - case OP_AND: - val = (lval != 0 && rval != 0) ? 1 : 0;; - break; - case OP_OR: - val = (lval != 0 || rval != 0) ? 1 : 0;; - break; - case OP_GT: - val = (lval > rval) ? 1 : 0;; - break; - case OP_LT: - val = (lval < rval) ? 1 : 0;; - break; - case OP_LE: - val = (lval <= rval) ? 1 : 0;; - break; - case OP_GE: - val = (lval >= rval) ? 1 : 0;; - break; - case OP_NE: - val = (lval != rval) ? 1 : 0;; - break; - default: - Console.Error.WriteLine($"Unknown operator {expr.Op}"); - return LibmsiResult.LIBMSI_RESULT_FUNCTION_FAILED; - } - - return LibmsiResult.LIBMSI_RESULT_SUCCESS; - } - - private static LibmsiResult ExprFetchValue(ext_column expr, int[] rows, out int val) - { - JOINTABLE table = expr.Parsed.Item2; - if (rows[table.TableIndex] == INVALID_ROW_INDEX) - { - val = 1; - return LibmsiResult.LIBMSI_RESULT_CONTINUE; - } - - return table.View.FetchInt(rows[table.TableIndex], expr.Parsed.Item1, out val); - } - - private LibmsiResult ExprEvalUnary(int[] rows, complex_expr expr, out int val, LibmsiRecord record) - { - val = 0; - LibmsiResult r = ExprFetchValue(expr.Left.Column, rows, out int lval); - if (r != LibmsiResult.LIBMSI_RESULT_SUCCESS) - return r; - - switch (expr.Op) - { - case OP_ISNULL: - val = (lval == 0) ? 1 : 0;; - break; - case OP_NOTNULL: - val = (lval != 0) ? 1 : 0;; - break; - default: - Console.Error.WriteLine($"Unknown operator {expr.Op}"); - return LibmsiResult.LIBMSI_RESULT_FUNCTION_FAILED; - } - - return LibmsiResult.LIBMSI_RESULT_SUCCESS; - } - - private LibmsiResult ExprEvalString(int[] rows, expr expr, LibmsiRecord record, out string str) - { - LibmsiResult r = LibmsiResult.LIBMSI_RESULT_SUCCESS; - switch (expr.Type) - { - case EXPR_COL_NUMBER_STRING: - r = ExprFetchValue(expr.Column, rows, out int val); - if (r == LibmsiResult.LIBMSI_RESULT_SUCCESS) - str = Database.Strings.LookupId(val); - else - str = null; - - break; - - case EXPR_SVAL: - str = expr.SVal; - break; - - case EXPR_WILDCARD: - str = record.GetStringRaw(++RecIndex); - break; - - default: - Console.Error.WriteLine("Invalid expression type"); - r = LibmsiResult.LIBMSI_RESULT_FUNCTION_FAILED; - str = null; - break; - } - - return r; - } - - private LibmsiResult ExprEvlStrCmp(int[] rows, complex_expr expr, out int val, LibmsiRecord record) - { - val = 1; - LibmsiResult r = ExprEvalString(rows, expr.Left, record, out string l_str); - if (r == LibmsiResult.LIBMSI_RESULT_CONTINUE) - return r; - - r = ExprEvalString(rows, expr.Right, record, out string r_str); - if (r == LibmsiResult.LIBMSI_RESULT_CONTINUE) - return r; - - int sr; - if (l_str == r_str || (string.IsNullOrEmpty(l_str) && string.IsNullOrEmpty(r_str))) - sr = 0; - else if (l_str != null && r_str == null) - sr = 1; - else if (r_str != null && l_str == null) - sr = -1; - else - sr = l_str.CompareTo(r_str); - - val = (expr.Op == OP_EQ && (sr == 0)) || (expr.Op == OP_NE && (sr != 0)) ? 1 : 0;; - - return LibmsiResult.LIBMSI_RESULT_SUCCESS; - } - - private LibmsiResult Evaluate(int[] rows, expr cond, out int val, LibmsiRecord record) - { - val = 0; - if (cond == null) - { - val = 1; - return LibmsiResult.LIBMSI_RESULT_SUCCESS; - } - - LibmsiResult r; - int tval; - switch (cond.Type) - { - case EXPR_COL_NUMBER: - r = ExprFetchValue(cond.Column, rows, out tval); - if (r != LibmsiResult.LIBMSI_RESULT_SUCCESS) - return r; - - val = tval - 0x8000; - return LibmsiResult.LIBMSI_RESULT_SUCCESS; - - case EXPR_COL_NUMBER32: - r = ExprFetchValue(cond.Column, rows, out tval); - if (r != LibmsiResult.LIBMSI_RESULT_SUCCESS) - return r; - - unchecked { val = tval - (int)0x80000000; } - return r; - - case EXPR_UVAL: - val = (int)cond.UVal; - return LibmsiResult.LIBMSI_RESULT_SUCCESS; - - case EXPR_COMPLEX: - return ExprEvalBinary(rows, cond.Expr, out val, record); - - case EXPR_UNARY: - return ExprEvalUnary(rows, cond.Expr, out val, record); - - case EXPR_STRCMP: - return ExprEvlStrCmp(rows, cond.Expr, out val, record); - - case EXPR_WILDCARD: - val = record.GetInt(++RecIndex); - return LibmsiResult.LIBMSI_RESULT_SUCCESS; - - default: - Console.Error.WriteLine("Invalid expression type"); - break; - } - - return LibmsiResult.LIBMSI_RESULT_SUCCESS; - } - - private LibmsiResult CheckCondition(LibmsiRecord record, JOINTABLE[] tables, int[] table_rows) - { - LibmsiResult r = LibmsiResult.LIBMSI_RESULT_FUNCTION_FAILED; - - int tablesPtr = 0; // tables[0] - for (table_rows[tables[tablesPtr].TableIndex] = 0; - table_rows[tables[tablesPtr].TableIndex] < tables[tablesPtr].RowCount; - table_rows[tables[tablesPtr].TableIndex]++) - { - RecIndex = 0; - r = Evaluate(table_rows, Condition, out int val, record ); - if (r != LibmsiResult.LIBMSI_RESULT_SUCCESS && r != LibmsiResult.LIBMSI_RESULT_CONTINUE) - break; - - if (val != 0) - { - if (tablesPtr < tables.Length - 1 && tables[tablesPtr + 1] != null) - { - r = CheckCondition(record, tables.Skip(tablesPtr + 1).ToArray(), table_rows); - if (r != LibmsiResult.LIBMSI_RESULT_SUCCESS) - break; - } - else - { - if (r != LibmsiResult.LIBMSI_RESULT_SUCCESS) - break; - - AddRow(table_rows); - } - } - } - - table_rows[tables[tablesPtr].TableIndex] = INVALID_ROW_INDEX; - return r; - } - - private static int CompareEntry(object left, object right) - { - LibmsiRowEntry le = (LibmsiRowEntry)left; - LibmsiRowEntry re = (LibmsiRowEntry)right; - LibmsiWhereView wv = le.WhereView; - LibmsiOrderInfo order = wv.OrderInfo; - - // TODO: Re-enable this assert - // assert(le.wv == re.wv); - - if (order != null) - { - for (int i = 0; i < order.ColCount; i++) - { - ext_column column = order.Columns[i]; - LibmsiResult r = column.Parsed.Item2.View.FetchInt(le.Values[column.Parsed.Item2.TableIndex], column.Parsed.Item1, out int l_val); - if (r != LibmsiResult.LIBMSI_RESULT_SUCCESS) - { - order.Error = r; - return 0; - } - - r = column.Parsed.Item2.View.FetchInt(re.Values[column.Parsed.Item2.TableIndex], column.Parsed.Item1, out int r_val); - if (r != LibmsiResult.LIBMSI_RESULT_SUCCESS) - { - order.Error = r; - return 0; - } - - if (l_val != r_val) - return l_val < r_val ? -1 : 1; - } - } - - for (int j = 0; j < wv.TableCount; j++) - { - if (le.Values[j] != re.Values[j]) - return le.Values[j] < re.Values[j] ? -1 : 1; - } - - return 0; - } - - private static void AddToArray(JOINTABLE[] array, int arrayPtr, JOINTABLE elem) - { - while (arrayPtr < array.Length && array[arrayPtr] != null && array[arrayPtr] != elem) - { - arrayPtr++; - } - - if (array[arrayPtr] == null) - array[arrayPtr] = elem; - } - - private static bool InArray(JOINTABLE[] array, int arrayPtr, JOINTABLE elem) - { - while (arrayPtr < array.Length && array[arrayPtr] != null && array[arrayPtr] != elem) - { - arrayPtr++; - } - - return array[arrayPtr] != null; - } - - private static int ReorderCheck(expr expr, JOINTABLE[] ordered_tables, bool process_joins, ref JOINTABLE lastused) - { - int res = 0; - switch (expr.Type) - { - case EXPR_WILDCARD: - case EXPR_SVAL: - case EXPR_UVAL: - return 0; - - case EXPR_COL_NUMBER: - case EXPR_COL_NUMBER32: - case EXPR_COL_NUMBER_STRING: - if (InArray(ordered_tables, 0, expr.Column.Parsed.Item2)) - return JOIN_TO_CONST_EXPR; - - lastused = expr.Column.Parsed.Item2; - return CONST_EXPR; - - case EXPR_STRCMP: - case EXPR_COMPLEX: - res = ReorderCheck(expr.Expr.Right, ordered_tables, process_joins, ref lastused); - res += ReorderCheck(expr.Expr.Left, ordered_tables, process_joins, ref lastused); - if (res == 0) - return 0; - if (res == CONST_EXPR) - AddToArray(ordered_tables, 0, lastused); - if (process_joins && res == JOIN_TO_CONST_EXPR + CONST_EXPR) - AddToArray(ordered_tables, 0, lastused); - - return res; - - case EXPR_UNARY: - res += ReorderCheck(expr.Expr.Left, ordered_tables, process_joins, ref lastused); - if (res == 0) - return 0; - if (res == CONST_EXPR) - AddToArray(ordered_tables, 0, lastused); - if (process_joins && res == JOIN_TO_CONST_EXPR + CONST_EXPR) - AddToArray(ordered_tables, 0, lastused); - return res; - - default: - Console.Error.WriteLine($"Unknown expr type: {expr.Type}"); - return 0x1000000; - } - } - - private LibmsiResult VerifyCondition(expr cond, ref bool valid) - { - LibmsiResult r; - - switch (cond.Type) - { - case EXPR_COLUMN: - { - valid = false; - - r = ParseColumn(cond.Column, out int type); - if (r != LibmsiResult.LIBMSI_RESULT_SUCCESS) - break; - - if ((type & MSITYPE_STRING) != 0) - cond.Type = EXPR_COL_NUMBER_STRING; - else if ((type & 0xff) == 4) - cond.Type = EXPR_COL_NUMBER32; - else - cond.Type = EXPR_COL_NUMBER; - - valid = true; - break; - } - case EXPR_COMPLEX: - r = VerifyCondition(cond.Expr.Left, ref valid); - if (r != LibmsiResult.LIBMSI_RESULT_SUCCESS) - return r; - if (!valid) - return LibmsiResult.LIBMSI_RESULT_SUCCESS; - r = VerifyCondition(cond.Expr.Right, ref valid); - if (r != LibmsiResult.LIBMSI_RESULT_SUCCESS) - return r; - - // Check the type of the comparison - if ((cond.Expr.Left.Type == EXPR_SVAL) || (cond.Expr.Left.Type == EXPR_COL_NUMBER_STRING) - || (cond.Expr.Right.Type == EXPR_SVAL) || (cond.Expr.Right.Type == EXPR_COL_NUMBER_STRING)) - { - switch (cond.Expr.Op) - { - case OP_EQ: - case OP_NE: - break; - default: - valid = false; - return LibmsiResult.LIBMSI_RESULT_INVALID_PARAMETER; - } - - // FIXME: check we're comparing a string to a column - cond.Type = EXPR_STRCMP; - } - - break; - case EXPR_UNARY: - if (cond.Expr.Left.Type != EXPR_COLUMN) - { - valid = false; - return LibmsiResult.LIBMSI_RESULT_INVALID_PARAMETER; - } - - r = VerifyCondition(cond.Expr.Left, ref valid); - if (r != LibmsiResult.LIBMSI_RESULT_SUCCESS) - return r; - - break; - case EXPR_IVAL: - valid = true; - cond.Type = EXPR_UVAL; - cond.UVal = (uint)cond.IVal; - break; - case EXPR_WILDCARD: - valid = true; - break; - case EXPR_SVAL: - valid = true; - break; - default: - Console.Error.WriteLine("Invalid expression type"); - valid = false; - break; - } - - return LibmsiResult.LIBMSI_RESULT_SUCCESS; - } - - #endregion - } -} \ No newline at end of file