mirror of
https://github.com/SabreTools/BinaryObjectScanner.git
synced 2026-04-23 06:34:03 +00:00
LibGSF (nw)
This commit is contained in:
@@ -30,6 +30,7 @@
|
||||
</PackageReference>
|
||||
<PackageReference Include="UnshieldSharp" Version="1.6.8" />
|
||||
<PackageReference Include="WiseUnpacker" Version="1.0.3" />
|
||||
<PackageReference Include="zlib.net-mutliplatform" Version="1.0.6" />
|
||||
</ItemGroup>
|
||||
|
||||
<!-- These are needed for dealing with submodules -->
|
||||
|
||||
78
BurnOutSharp/External/libgsf/GsfBlob.cs
vendored
Normal file
78
BurnOutSharp/External/libgsf/GsfBlob.cs
vendored
Normal file
@@ -0,0 +1,78 @@
|
||||
/* 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
|
||||
{
|
||||
public class GsfBlob
|
||||
{
|
||||
#region Properties
|
||||
|
||||
public long Size { get; private set; }
|
||||
|
||||
public byte[] Data { get; private set; }
|
||||
|
||||
#endregion
|
||||
|
||||
#region Constructor
|
||||
|
||||
/// <summary>
|
||||
/// Private constructor
|
||||
/// </summary>
|
||||
private GsfBlob() { }
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new GsfBlob object to hold the specified data. The blob can then
|
||||
/// be used as a facility for reference-counting for the data. The data is
|
||||
/// copied internally, so the blob does not hold references to external chunks
|
||||
/// of memory.
|
||||
/// </summary>
|
||||
/// <param name="size">Size of the data in bytes.</param>
|
||||
/// <param name="data_to_copy">Data which will be copied into the blob, or null if <paramref name="size"/> is zero.</param>
|
||||
/// <param name="error">Location to store error, or null.</param>
|
||||
/// <returns>A newly-created GsfBlob, or null if the data could not be copied.</returns>
|
||||
public static GsfBlob Create(long size, byte[] data_to_copy, int dataPtr, ref Exception error)
|
||||
{
|
||||
if (!((size > 0 && data_to_copy != null) || (size == 0 && data_to_copy == null)))
|
||||
return null;
|
||||
|
||||
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
|
||||
}
|
||||
}
|
||||
294
BurnOutSharp/External/libgsf/GsfClipData.cs
vendored
Normal file
294
BurnOutSharp/External/libgsf/GsfClipData.cs
vendored
Normal file
@@ -0,0 +1,294 @@
|
||||
/* 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;
|
||||
|
||||
namespace LibGSF
|
||||
{
|
||||
#region Enums
|
||||
|
||||
public enum GsfClipFormat
|
||||
{
|
||||
/// <summary>
|
||||
/// Windows clipboard format
|
||||
/// </summary>
|
||||
GSF_CLIP_FORMAT_WINDOWS_CLIPBOARD = -1,
|
||||
|
||||
/// <summary>
|
||||
/// Macintosh clipboard format
|
||||
/// </summary>
|
||||
GSF_CLIP_FORMAT_MACINTOSH_CLIPBOARD = -2,
|
||||
|
||||
/// <summary>
|
||||
/// GUID that contains a format identifier
|
||||
/// </summary>
|
||||
GSF_CLIP_FORMAT_GUID = -3,
|
||||
|
||||
/// <summary>
|
||||
/// No clipboard data
|
||||
/// </summary>
|
||||
GSF_CLIP_FORMAT_NO_DATA = 0,
|
||||
|
||||
/// <summary>
|
||||
/// Custom clipboard format
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// In the file it's actually any positive integer
|
||||
/// </remarks>
|
||||
GSF_CLIP_FORMAT_CLIPBOARD_FORMAT_NAME = 1,
|
||||
|
||||
/// <summary>
|
||||
/// Unknown clipboard type or invalid data
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This is our own value for unknown types or invalid data
|
||||
/// </remarks>
|
||||
GSF_CLIP_FORMAT_UNKNOWN
|
||||
}
|
||||
|
||||
public enum GsfClipFormatWindows
|
||||
{
|
||||
/// <summary>
|
||||
/// Our own value
|
||||
/// </summary>
|
||||
GSF_CLIP_FORMAT_WINDOWS_ERROR = -1,
|
||||
|
||||
/// <summary>
|
||||
/// Our own value
|
||||
/// </summary>
|
||||
GSF_CLIP_FORMAT_WINDOWS_UNKNOWN = -2,
|
||||
|
||||
/// <summary>
|
||||
/// CF_METAFILEPICT
|
||||
/// </summary>
|
||||
GSF_CLIP_FORMAT_WINDOWS_METAFILE = 3,
|
||||
|
||||
/// <summary>
|
||||
/// CF_DIB
|
||||
/// </summary>
|
||||
GSF_CLIP_FORMAT_WINDOWS_DIB = 8,
|
||||
|
||||
/// <summary>
|
||||
/// CF_ENHMETAFILE
|
||||
/// </summary>
|
||||
GSF_CLIP_FORMAT_WINDOWS_ENHANCED_METAFILE = 14
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
public class GsfClipData
|
||||
{
|
||||
#region Properties
|
||||
|
||||
public GsfClipFormat Format { get; private set; }
|
||||
|
||||
public GsfBlob DataBlob { get; private set; }
|
||||
|
||||
#endregion
|
||||
|
||||
#region Private Classes
|
||||
|
||||
private class FormatOffsetPair
|
||||
{
|
||||
public GsfClipFormatWindows Format { get; set; }
|
||||
|
||||
public long Offset { get; set; }
|
||||
};
|
||||
|
||||
#endregion
|
||||
|
||||
#region Constructor
|
||||
|
||||
/// <summary>
|
||||
/// Private constructor
|
||||
/// </summary>
|
||||
private GsfClipData() { }
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new GsfClipData object. This function acquires a reference to the
|
||||
/// <paramref name="data_blob"/>, so you should unref the blob on your own if you no longer need it
|
||||
/// directly.
|
||||
/// </summary>
|
||||
/// <param name="format">Format for the data inside the <paramref name="data_blob"/></param>
|
||||
/// <param name="data_blob">Object which holds the binary contents for the GsfClipData</param>
|
||||
/// <returns>A newly-created GsfClipData.</returns>
|
||||
public static GsfClipData Create(GsfClipFormat format, GsfBlob data_blob)
|
||||
{
|
||||
if (data_blob == null)
|
||||
return null;
|
||||
|
||||
return new GsfClipData
|
||||
{
|
||||
Format = format,
|
||||
DataBlob = data_blob,
|
||||
};
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Functions
|
||||
|
||||
/// <summary>
|
||||
/// Queries the Windows clipboard data format for a GsfClipData. The <paramref name="clip_data"/> must
|
||||
/// have been created with #GSF_CLIP_FORMAT_WINDOWS_CLIPBOARD.
|
||||
/// </summary>
|
||||
/// <param name="error">Location to store error, or NULL</param>
|
||||
/// <returns>A GsfClipFormatWindows value.</returns>
|
||||
public GsfClipFormatWindows GetWindowsClipboardFormat(ref Exception error)
|
||||
{
|
||||
GsfClipFormatWindows format;
|
||||
|
||||
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 = BitConverter.ToUInt32(data, 0);
|
||||
|
||||
switch (value)
|
||||
{
|
||||
case (uint)GsfClipFormatWindows.GSF_CLIP_FORMAT_WINDOWS_METAFILE:
|
||||
format = CheckFormatWindows(GsfClipFormatWindows.GSF_CLIP_FORMAT_WINDOWS_METAFILE, "Windows Metafile format", size, ref error);
|
||||
break;
|
||||
|
||||
case (uint)GsfClipFormatWindows.GSF_CLIP_FORMAT_WINDOWS_DIB:
|
||||
case 2: /* CF_BITMAP */
|
||||
format = CheckFormatWindows(GsfClipFormatWindows.GSF_CLIP_FORMAT_WINDOWS_DIB, "Windows DIB or BITMAP format", size, ref error);
|
||||
break;
|
||||
|
||||
case (uint)GsfClipFormatWindows.GSF_CLIP_FORMAT_WINDOWS_ENHANCED_METAFILE:
|
||||
format = CheckFormatWindows(GsfClipFormatWindows.GSF_CLIP_FORMAT_WINDOWS_ENHANCED_METAFILE, "Windows Enhanced Metafile format", size, ref error);
|
||||
break;
|
||||
|
||||
default:
|
||||
format = GsfClipFormatWindows.GSF_CLIP_FORMAT_WINDOWS_UNKNOWN;
|
||||
break;
|
||||
}
|
||||
|
||||
return format;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Queries a pointer directly to the clipboard data of a GsfClipData. The
|
||||
/// resulting pointer is not necessarily the same data pointer that was passed to
|
||||
/// gsf_blob_new() prior to creating the <paramref name="clip_data"/>. For example, if the data is
|
||||
/// in GSF_CLIP_FORMAT_WINDOWS_CLIPBOARD format, then it will have extra header
|
||||
/// bytes in front of the actual metafile data. This function will skip over
|
||||
/// those header bytes if necessary and return a pointer to the "real" data.
|
||||
/// </summary>
|
||||
/// <param name="ret_size">Location to return the size of the returned data buffer.</param>
|
||||
/// <param name="error">Location to store error, or NULL.</param>
|
||||
/// <returns>
|
||||
/// Pointer to the real clipboard data. The size in bytes of this
|
||||
/// buffer is returned in the <paramref name="ret_size"/> argument.
|
||||
/// </returns>
|
||||
public byte[] PeekRealData(ref long ret_size, ref Exception error)
|
||||
{
|
||||
if (error == null)
|
||||
return null;
|
||||
|
||||
byte[] data = DataBlob.Data;
|
||||
|
||||
long offset;
|
||||
if (Format == GsfClipFormat.GSF_CLIP_FORMAT_WINDOWS_CLIPBOARD)
|
||||
{
|
||||
GsfClipFormatWindows win_format = GetWindowsClipboardFormat(ref error);
|
||||
if (win_format == GsfClipFormatWindows.GSF_CLIP_FORMAT_WINDOWS_ERROR)
|
||||
return null;
|
||||
|
||||
// gsf_clip_data_get_windows_clipboard_format() already did the size checks for us,
|
||||
// so we can jump to the offset right away without doing extra checks.
|
||||
|
||||
offset = GetWindowsClipboardDataOffset(win_format);
|
||||
}
|
||||
else
|
||||
{
|
||||
offset = 0;
|
||||
}
|
||||
|
||||
ret_size = DataBlob.Size - offset;
|
||||
return new ReadOnlySpan<byte>(data, (int)offset, (int)ret_size).ToArray();
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
int i;
|
||||
|
||||
for (i = 0; i < num_pairs; i++)
|
||||
{
|
||||
if (pairs[i].Format == format)
|
||||
return pairs[i].Offset;
|
||||
}
|
||||
|
||||
Console.Error.WriteLine("Should not have reached this point");
|
||||
return 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks that the specified blob size matches the expected size for the format.
|
||||
/// </summary>
|
||||
/// <returns>
|
||||
/// The same format if the size is correct, or
|
||||
/// GSF_CLIP_FORMAT_WINDOWS_ERROR if the size is too small.
|
||||
/// </returns>
|
||||
private static GsfClipFormatWindows CheckFormatWindows(GsfClipFormatWindows format, string format_name, long blob_size, ref Exception error)
|
||||
{
|
||||
long offset = GetWindowsClipboardDataOffset(format);
|
||||
if (blob_size <= offset)
|
||||
{
|
||||
SetErrorMissingClipboardData(ref error, format_name, offset + 1);
|
||||
format = GsfClipFormatWindows.GSF_CLIP_FORMAT_WINDOWS_ERROR;
|
||||
}
|
||||
|
||||
return format;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
555
BurnOutSharp/External/libgsf/GsfDocMetaData.cs
vendored
Normal file
555
BurnOutSharp/External/libgsf/GsfDocMetaData.cs
vendored
Normal file
@@ -0,0 +1,555 @@
|
||||
/*
|
||||
* 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;
|
||||
|
||||
namespace LibGSF
|
||||
{
|
||||
public class GsfDocProp
|
||||
{
|
||||
#region Properties
|
||||
|
||||
public string Name { get; set; }
|
||||
|
||||
public object Value { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Optionally NULL
|
||||
/// </summary>
|
||||
public string LinkedTo { get; set; }
|
||||
|
||||
public uint RefCount { get; set; }
|
||||
|
||||
#endregion
|
||||
|
||||
#region Constructor
|
||||
|
||||
/// <summary>
|
||||
/// Private constructor
|
||||
/// </summary>
|
||||
private GsfDocProp() { }
|
||||
|
||||
/// <param name="name">The name of the property.</param>
|
||||
/// <returns>A new GsfDocProp.</returns>
|
||||
public static GsfDocProp Create(string name)
|
||||
{
|
||||
if (name == null)
|
||||
return null;
|
||||
|
||||
return new GsfDocProp
|
||||
{
|
||||
Name = name,
|
||||
Value = null,
|
||||
LinkedTo = null,
|
||||
};
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Functions
|
||||
|
||||
/// <summary>
|
||||
/// Release the given property.
|
||||
/// </summary>
|
||||
public void Free()
|
||||
{
|
||||
RefCount--;
|
||||
if (RefCount == 0)
|
||||
{
|
||||
LinkedTo = null;
|
||||
if (Value != null)
|
||||
Value = null;
|
||||
|
||||
Name = null;
|
||||
}
|
||||
}
|
||||
|
||||
public GsfDocProp Reference()
|
||||
{
|
||||
RefCount++;
|
||||
return this;
|
||||
}
|
||||
|
||||
/// <returns>The current value of prop, and replaces it with <paramref name="val"/>.</returns>
|
||||
public object SwapValue(object val)
|
||||
{
|
||||
object old_val = Value;
|
||||
Value = val;
|
||||
return old_val;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
public class GsfDocMetaData
|
||||
{
|
||||
#region Properties
|
||||
|
||||
internal Dictionary<string, GsfDocProp> Table = new Dictionary<string, GsfDocProp>();
|
||||
|
||||
#endregion
|
||||
|
||||
#region Constructor
|
||||
|
||||
/// <summary>
|
||||
/// Private constructor
|
||||
/// </summary>
|
||||
private GsfDocMetaData() { }
|
||||
|
||||
/// <returns>
|
||||
/// A new metadata property collection
|
||||
/// </returns>
|
||||
public static GsfDocMetaData Create() => new GsfDocMetaData();
|
||||
|
||||
#endregion
|
||||
|
||||
#region Functions
|
||||
|
||||
/// <returns>
|
||||
/// The property with <paramref name="name"/> in meta. The caller can
|
||||
/// modify the property value and link but not the name.
|
||||
/// </returns>
|
||||
public GsfDocProp Lookup(string name)
|
||||
{
|
||||
if (name == null)
|
||||
return null;
|
||||
|
||||
if (!Table.ContainsKey(name))
|
||||
return null;
|
||||
|
||||
return Table[name];
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Take ownership of <paramref name="name"/> and <paramref name="value"/> and insert a property into meta.
|
||||
/// If a property exists with @name, it is replaced (The link is lost)
|
||||
/// </summary>
|
||||
/// <param name="name">The id.</param>
|
||||
public void Insert(string name, object value)
|
||||
{
|
||||
if (name == null)
|
||||
return;
|
||||
|
||||
GsfDocProp docProp = GsfDocProp.Create(name);
|
||||
docProp.Value = value;
|
||||
docProp.LinkedTo = null;
|
||||
docProp.RefCount = 1;
|
||||
|
||||
Table[name] = docProp;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Read a stream formated as a set of MS OLE properties from <paramref name="input"/> and store the
|
||||
/// results in <paramref name="accum"/>.
|
||||
/// </summary>
|
||||
/// <returns>an Exception if there was an error.</returns>
|
||||
/// <remarks>Since: 1.14.24</remarks>
|
||||
public Exception ReadFromMSOLE(GsfInput input)
|
||||
{
|
||||
GsfDocProp prop;
|
||||
|
||||
// http://bugzilla.gnome.org/show_bug.cgi?id=352055
|
||||
// psiwin generates files with empty property sections
|
||||
if (input.Size <= 0)
|
||||
return null;
|
||||
|
||||
byte[] data = input.Read(28, null);
|
||||
if (data == null)
|
||||
return new Exception("Unable to read MS property stream header");
|
||||
|
||||
/*
|
||||
* Validate the Property Set Header.
|
||||
* Format (bytes):
|
||||
* 00 - 01 Byte order 0xfffe
|
||||
* 02 - 03 Format 0
|
||||
* 04 - 05 OS Version high word is the OS
|
||||
* 06 - 07 low word is the OS version
|
||||
* 0 = win16
|
||||
* 1 = mac
|
||||
* 2 = win32
|
||||
* 08 - 23 Class Identifier Usually Format ID
|
||||
* 24 - 27 Section count Should be at least 1
|
||||
*/
|
||||
ushort os = BitConverter.ToUInt16(data, 6);
|
||||
ushort version = BitConverter.ToUInt16(data, 2);
|
||||
uint num_sections = BitConverter.ToUInt32(data, 24);
|
||||
|
||||
if (BitConverter.ToUInt16(data, 0) != 0xfffe
|
||||
|| (version != 0 && version != 1)
|
||||
|| os > 2
|
||||
|| num_sections > input.Size / 20
|
||||
|| num_sections > 100) // Arbitrary sanity check
|
||||
{
|
||||
return new Exception("Invalid MS property stream header");
|
||||
}
|
||||
|
||||
// Extract the section info
|
||||
/*
|
||||
* The Format ID/Offset list follows.
|
||||
* Format:
|
||||
* 00 - 16 Section Name Format ID
|
||||
* 16 - 19 Section Offset The offset is the number of
|
||||
* bytes from the start of the
|
||||
* whole stream to where the
|
||||
* section begins.
|
||||
*/
|
||||
GsfMSOleMetaDataSection[] sections = new GsfMSOleMetaDataSection[num_sections];
|
||||
for (uint i = 0; i < num_sections; i++)
|
||||
{
|
||||
data = input.Read(20, null);
|
||||
if (data == null)
|
||||
return new Exception("Unable to read MS property stream header");
|
||||
|
||||
byte[] guid = new ReadOnlySpan<byte>(data, 0, 16).ToArray();
|
||||
if (guid.SequenceEqual(ComponentGUID))
|
||||
{
|
||||
sections[i].Type = GsfMSOLEMetaDataType.COMPONENT_PROP;
|
||||
}
|
||||
else if (guid.SequenceEqual(DocumentGUID))
|
||||
{
|
||||
sections[i].Type = GsfMSOLEMetaDataType.DOC_PROP;
|
||||
}
|
||||
else if (guid.SequenceEqual(UserGUID))
|
||||
{
|
||||
sections[i].Type = GsfMSOLEMetaDataType.USER_PROP;
|
||||
}
|
||||
else
|
||||
{
|
||||
sections[i].Type = GsfMSOLEMetaDataType.USER_PROP;
|
||||
Console.Error.WriteLine("Unknown property section type, treating it as USER");
|
||||
}
|
||||
|
||||
sections[i].Offset = BitConverter.ToUInt32(data, 16);
|
||||
}
|
||||
|
||||
/*
|
||||
* A section is the third part of the property set stream.
|
||||
* Format (bytes):
|
||||
* 00 - 03 Section size A byte count for the section (which is inclusive
|
||||
* of the byte count itself and should always be a
|
||||
* multiple of 4);
|
||||
* 04 - 07 Property count A count of the number of properties
|
||||
* 08 - xx An array of 32-bit Property ID/Offset pairs
|
||||
* yy - zz An array of Property Type indicators/Value pairs
|
||||
*/
|
||||
for (uint i = 0; i < num_sections; i++)
|
||||
{
|
||||
if (input.Seek(sections[i].Offset, SeekOrigin.Begin) || (data = input.Read(8, null)) == null)
|
||||
return new Exception("Invalid MS property section");
|
||||
|
||||
sections[i].IConvHandle = null;
|
||||
sections[i].CharSize = 1;
|
||||
sections[i].Dict = null;
|
||||
sections[i].Size = BitConverter.ToUInt32(data, 0); // Includes header
|
||||
sections[i].NumProps = BitConverter.ToUInt32(data, 4);
|
||||
|
||||
if (sections[i].NumProps <= 0)
|
||||
continue;
|
||||
|
||||
if (sections[i].NumProps > input.Remaining() / 8)
|
||||
return new Exception("Invalid MS property stream header or file truncated");
|
||||
|
||||
if (sections[i].Offset + sections[i].Size > input.Size)
|
||||
return new Exception("Invalid MS property stream header or file truncated");
|
||||
|
||||
/*
|
||||
* Get and save all the Property ID/Offset pairs.
|
||||
* Format (bytes):
|
||||
* 00 - 03 id Property ID
|
||||
* 04 - 07 offset The distance from the start of the section to the
|
||||
* start of the Property Type/Value pair.
|
||||
*/
|
||||
GsfMSOleMetaDataProp[] props = new GsfMSOleMetaDataProp[sections[i].NumProps];
|
||||
for (uint j = 0; j < sections[i].NumProps; j++)
|
||||
{
|
||||
if ((data = input.Read(8, null)) == null)
|
||||
return new Exception("Invalid MS property section");
|
||||
|
||||
props[j].Id = BitConverter.ToUInt32(data, 0);
|
||||
props[j].Offset = BitConverter.ToUInt32(data, 4);
|
||||
}
|
||||
|
||||
// FIXME: Should we check that ids are distinct?
|
||||
|
||||
// Order prop info by offset to facilitate bounds checking
|
||||
List<GsfMSOleMetaDataProp> tempProps = new List<GsfMSOleMetaDataProp>(props);
|
||||
tempProps.Sort(PropertyCompare);
|
||||
props = tempProps.ToArray();
|
||||
|
||||
// Sanity checks.
|
||||
for (uint j = 0; j < sections[i].NumProps; j++)
|
||||
{
|
||||
uint end = (uint)((j == sections[i].NumProps - 1) ? sections[i].Size : props[j + 1].Offset);
|
||||
if (props[j].Offset < 0 || props[j].Offset + 4 > end)
|
||||
return new Exception("Invalid MS property section");
|
||||
}
|
||||
|
||||
// Find and process the code page.
|
||||
// Property ID 1 is reserved as an indicator of the code page.
|
||||
sections[i].IConvHandle = null;
|
||||
sections[i].CharSize = 1;
|
||||
|
||||
for (uint j = 0; j < sections[i].NumProps; j++) // First codepage
|
||||
{
|
||||
if (props[j].Id == 1)
|
||||
{
|
||||
sections[i].PropertyRead(input, props, j, this);
|
||||
if ((prop = Lookup(GsfMetaNames.GSF_META_NAME_CODEPAGE)) != null)
|
||||
{
|
||||
object val = prop.Value;
|
||||
if (val != null && val is int)
|
||||
{
|
||||
int codepage = (int)val;
|
||||
sections[i].IConvHandle = Encoding.GetEncoding(codepage);
|
||||
sections[i].CharSize = (uint)CodePageCharSize(codepage);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (sections[i].IConvHandle == null)
|
||||
sections[i].IConvHandle = Encoding.GetEncoding(1252);
|
||||
|
||||
// Find and process the Property Set Dictionary
|
||||
// Property ID 0 is reserved as an indicator of the dictionary.
|
||||
// For User Defined Sections, Property ID 0 is NOT a dictionary.
|
||||
|
||||
for (uint j = 0; j < sections[i].NumProps; j++) // The dictionary
|
||||
{
|
||||
if (props[j].Id == 0)
|
||||
sections[i].PropertyRead(input, props, j, this);
|
||||
}
|
||||
|
||||
// Process all the properties
|
||||
for (uint j = 0; j < sections[i].NumProps; j++) // The rest
|
||||
{
|
||||
if (props[j].Id > 1)
|
||||
sections[i].PropertyRead(input, props, j, this);
|
||||
}
|
||||
|
||||
sections[i].IConvHandle = null;
|
||||
if (sections[i].Dict != null)
|
||||
sections[i].Dict = null;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// If <paramref name="name"/> does not exist in the collection, do nothing. If @name does exist,
|
||||
/// remove it and its value from the collection
|
||||
/// </summary>
|
||||
/// <param name="name">The non-null string name of the property</param>
|
||||
public void Remove(string name)
|
||||
{
|
||||
if (name == null)
|
||||
return;
|
||||
|
||||
if (!Table.ContainsKey(name))
|
||||
return;
|
||||
|
||||
Table.Remove(name);
|
||||
}
|
||||
|
||||
/// <returns>The property with <paramref name="name"/> in meta.</returns>
|
||||
public GsfDocProp Steal(string name)
|
||||
{
|
||||
if (name == null)
|
||||
return null;
|
||||
|
||||
if (!Table.ContainsKey(name))
|
||||
return null;
|
||||
|
||||
GsfDocProp prop = Table[name];
|
||||
if (prop != null)
|
||||
Table.Remove(name);
|
||||
|
||||
return prop;
|
||||
}
|
||||
|
||||
public void Store(GsfDocProp prop)
|
||||
{
|
||||
if (prop == null)
|
||||
return;
|
||||
|
||||
if (Table.ContainsKey(prop.Name) && Table[prop.Name] == prop)
|
||||
return;
|
||||
|
||||
Table[prop.Name] = prop;
|
||||
}
|
||||
|
||||
/// <returns>The number of items in this collection</returns>
|
||||
public int Size() => Table.Count;
|
||||
|
||||
/// <summary></summary>
|
||||
/// <param name="doc_not_component">A kludge to differentiate DocumentSummary from Summary</param>
|
||||
/// <returns>true on success</returns>
|
||||
/// <remarks>Since: 1.14.24</remarks>
|
||||
public bool WriteToMSOLE(GsfOutput output, bool doc_not_component)
|
||||
{
|
||||
byte[] header =
|
||||
{
|
||||
0xfe, 0xff, // Byte order
|
||||
0, 0, // Format
|
||||
0x04, 0x0a, // OS : XP == 0xA04
|
||||
0x02, 0x00, // Win32 == 2
|
||||
0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, // Clasid = 0
|
||||
};
|
||||
|
||||
bool success = false;
|
||||
byte[] buf = new byte[4];
|
||||
|
||||
const int default_codepage = 1252;
|
||||
|
||||
WritePropState state = new WritePropState
|
||||
{
|
||||
CodePage = 0,
|
||||
IConvHandle = null,
|
||||
CharSize = 1,
|
||||
Output = output,
|
||||
Dict = null,
|
||||
BuiltIn = new WritePropStatePropList
|
||||
{
|
||||
Count = 1, // Codepage
|
||||
Props = null,
|
||||
},
|
||||
User = new WritePropStatePropList
|
||||
{
|
||||
Count = 2, // Codepage and Dictionary
|
||||
Props = null,
|
||||
},
|
||||
DocNotComponent = doc_not_component,
|
||||
};
|
||||
|
||||
foreach (var prop in Table)
|
||||
{
|
||||
state.CountProperties(prop.Key, prop.Value);
|
||||
}
|
||||
|
||||
state.IConvHandle = Encoding.GetEncoding(default_codepage);
|
||||
if (state.CodePage == 0)
|
||||
{
|
||||
state.GuessCodePage(false);
|
||||
if (state.Dict != null)
|
||||
state.GuessCodePage(true);
|
||||
|
||||
if (state.CodePage == 0)
|
||||
state.CodePage = default_codepage;
|
||||
}
|
||||
|
||||
state.IConvHandle = Encoding.GetEncoding(state.CodePage);
|
||||
state.CharSize = CodePageCharSize(state.CodePage);
|
||||
|
||||
// Write stream header
|
||||
buf = BitConverter.GetBytes((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)
|
||||
buf = BitConverter.GetBytes((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)
|
||||
{
|
||||
buf = BitConverter.GetBytes((uint)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;
|
||||
buf = BitConverter.GetBytes((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 int CodePageCharSize(int codepage) => (codepage == 1200 || codepage == 1201 ? 2 : 1);
|
||||
|
||||
private static int PropertyCompare(GsfMSOleMetaDataProp prop_a, GsfMSOleMetaDataProp prop_b)
|
||||
{
|
||||
if (prop_a.Offset < prop_b.Offset)
|
||||
return -1;
|
||||
else if (prop_a.Offset > prop_b.Offset)
|
||||
return +1;
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
864
BurnOutSharp/External/libgsf/GsfLibXML.cs
vendored
Normal file
864
BurnOutSharp/External/libgsf/GsfLibXML.cs
vendored
Normal file
@@ -0,0 +1,864 @@
|
||||
/* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
|
||||
/*
|
||||
* gsf-libxml.c :
|
||||
*
|
||||
* Copyright (C) 2002-2006 Jody Goldberg (jody@gnome.org)
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of version 2.1 of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301
|
||||
* USA
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Xml;
|
||||
using System.Xml.Schema;
|
||||
using LibGSF.Input;
|
||||
using LibGSF.Output;
|
||||
|
||||
namespace LibGSF
|
||||
{
|
||||
#region Enums
|
||||
|
||||
/// <summary>
|
||||
/// Controls the handling of character data within a parser node.
|
||||
/// </summary>
|
||||
public enum GsfXMLContent
|
||||
{
|
||||
/// <summary>
|
||||
/// Node has no cstr contents
|
||||
/// </summary>
|
||||
GSF_XML_NO_CONTENT = 0,
|
||||
|
||||
/// <summary>
|
||||
/// Node has cstr contents
|
||||
/// </summary>
|
||||
GSF_XML_CONTENT,
|
||||
|
||||
/// <summary>
|
||||
/// Node has contents that is shared with children
|
||||
/// </summary>
|
||||
GSF_XML_SHARED_CONTENT,
|
||||
|
||||
/// <summary>
|
||||
/// Node is second or later occurrence
|
||||
/// </summary>
|
||||
GSF_XML_2ND
|
||||
}
|
||||
|
||||
public enum GsfXMLOutState
|
||||
{
|
||||
GSF_XML_OUT_NOCONTENT,
|
||||
GSF_XML_OUT_CHILD,
|
||||
GSF_XML_OUT_CHILD_PRETTY,
|
||||
GSF_XML_OUT_CONTENT
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Delegates
|
||||
|
||||
public delegate void GsfXMLInExtDtor(GsfXMLIn xin, object old_state);
|
||||
|
||||
public delegate bool GsfXMLInUnknownFunc(GsfXMLIn xin, string elem, string[] attrs);
|
||||
|
||||
public delegate bool GsfXMLProbeFunc(string name, string prefix, string URI, int nb_namespaces, string[] namespaces, int nb_attributes, int nb_defaulted, string[] attributes);
|
||||
|
||||
#endregion
|
||||
|
||||
#region Classes
|
||||
|
||||
// TODO: Figure out what the structure of this is
|
||||
public class GsfXMLBlob
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public class GsfXMLIn : GsfInput
|
||||
{
|
||||
#region Properties
|
||||
|
||||
/// <summary>
|
||||
/// User data
|
||||
/// </summary>
|
||||
public object UserState { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The current node content
|
||||
/// </summary>
|
||||
public string Content { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Current document being parsed
|
||||
/// </summary>
|
||||
public XmlDocument Doc { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Current node (not on the stack)
|
||||
/// </summary>
|
||||
public XmlNode Node { get; set; }
|
||||
|
||||
public Stack<XmlNode> NodeStack { get; private set; }
|
||||
|
||||
#endregion
|
||||
|
||||
#region Internal Properties
|
||||
|
||||
public GsfInput Input { get; set; }
|
||||
|
||||
public Stack<string> ContentsStack { get; internal set; }
|
||||
|
||||
public bool Initialized { get; internal set; }
|
||||
|
||||
#endregion
|
||||
|
||||
#region Functions
|
||||
|
||||
/// <summary>
|
||||
/// Take the first node from <paramref name="doc"/> as the current node and call its start handler.
|
||||
/// </summary>
|
||||
/// <param name="new_state">Arbitrary content for the parser</param>
|
||||
public void PushState(XmlDocument doc, object new_state, GsfXMLInExtDtor dtor, string[] attrs)
|
||||
{
|
||||
if (doc == null)
|
||||
return;
|
||||
if (doc.DocumentType == null)
|
||||
return;
|
||||
|
||||
// TODO: Figure out how to define Start here
|
||||
PushChild(doc.FirstChild, attrs, null);
|
||||
}
|
||||
|
||||
public void StartElement(string name, string ns)
|
||||
{
|
||||
if ((name != null && Node.Name != name) || (ns != null && Node.NamespaceURI != ns))
|
||||
return;
|
||||
|
||||
Node = Node.NextSibling;
|
||||
}
|
||||
|
||||
public void EndElement()
|
||||
{
|
||||
if (Node.NodeType != XmlNodeType.EndElement)
|
||||
throw new XmlException();
|
||||
|
||||
Node = Node.NextSibling;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This function will not be called when parsing an empty document.
|
||||
/// </summary>
|
||||
public void StartDocument()
|
||||
{
|
||||
Initialized = true;
|
||||
Node = Doc.FirstChild;
|
||||
NodeStack = new Stack<XmlNode>();
|
||||
ContentsStack = new Stack<string>();
|
||||
}
|
||||
|
||||
public void EndDocument()
|
||||
{
|
||||
Content = null;
|
||||
if (Initialized)
|
||||
{
|
||||
NodeStack = null;
|
||||
Initialized = false;
|
||||
|
||||
if (Node != Doc.DocumentType)
|
||||
Console.Error.WriteLine("Document likely damaged.");
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Utilities
|
||||
|
||||
private string NodeName(XmlNode node) => (node.Name != null) ? node.Name : "{catch all)}";
|
||||
|
||||
private void PushChild(XmlNode node, string[] attrs, Action<GsfXMLIn, string[]> start)
|
||||
{
|
||||
if (node.InnerText != null)
|
||||
{
|
||||
if (Content.Length != 0)
|
||||
{
|
||||
ContentsStack.Push(Content);
|
||||
Content = new string('\0', 128);
|
||||
}
|
||||
else
|
||||
{
|
||||
ContentsStack.Push(null);
|
||||
}
|
||||
}
|
||||
|
||||
NodeStack.Push(Node);
|
||||
Node = node;
|
||||
|
||||
start?.Invoke(this, attrs);
|
||||
}
|
||||
|
||||
#endregion
|
||||
};
|
||||
|
||||
public class GsfXMLInParser : XmlReader
|
||||
{
|
||||
#region Properties
|
||||
|
||||
/// <summary>
|
||||
/// Parent GsfXMLIn for reading
|
||||
/// </summary>
|
||||
public GsfXMLIn Parent { get; internal set; }
|
||||
|
||||
/// <summary>
|
||||
/// Current user state
|
||||
/// </summary>
|
||||
public GsfXMLIn UserState { get; internal set; }
|
||||
|
||||
/// <summary>
|
||||
/// Internal reader instance for unhandled functionality
|
||||
/// </summary>
|
||||
private readonly XmlReader inst;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Override Properties
|
||||
|
||||
public override int AttributeCount => inst.AttributeCount;
|
||||
|
||||
public override string BaseURI => inst.BaseURI;
|
||||
|
||||
public override int Depth => inst.Depth;
|
||||
|
||||
public override bool EOF => inst.EOF;
|
||||
|
||||
public override bool IsEmptyElement => inst.IsEmptyElement;
|
||||
|
||||
public override string LocalName => inst.LocalName;
|
||||
|
||||
public override string NamespaceURI => inst.NamespaceURI;
|
||||
|
||||
public override XmlNameTable NameTable => inst.NameTable;
|
||||
|
||||
public override XmlNodeType NodeType => inst.NodeType;
|
||||
|
||||
public override string Prefix => inst.Prefix;
|
||||
|
||||
public override ReadState ReadState => inst.ReadState;
|
||||
|
||||
public override string Value => inst.Value;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Constructor and Destructor
|
||||
|
||||
/// <summary>
|
||||
/// Constructor
|
||||
/// </summary>
|
||||
public GsfXMLInParser(GsfXMLIn parent)
|
||||
{
|
||||
if (parent == null)
|
||||
return;
|
||||
|
||||
Parent = parent;
|
||||
|
||||
XmlReaderSettings settings = new XmlReaderSettings
|
||||
{
|
||||
ValidationType = ValidationType.None,
|
||||
ValidationFlags = XmlSchemaValidationFlags.AllowXmlAttributes,
|
||||
};
|
||||
|
||||
inst = Create(inputUri: null, settings);
|
||||
|
||||
Parent.StartDocument();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Destructor
|
||||
/// </summary>
|
||||
~GsfXMLInParser()
|
||||
{
|
||||
Parent.EndDocument();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region XmlReader Custom Implementation
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override void ReadStartElement() => Parent.StartElement(null, null);
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override void ReadStartElement(string name) => Parent.StartElement(name, null);
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override void ReadStartElement(string localname, string ns) => Parent.StartElement(localname, ns);
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override void ReadEndElement() => Parent.EndElement();
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override string ReadElementContentAsString() => Parent.Node.InnerText;
|
||||
|
||||
#endregion
|
||||
|
||||
#region XmlReader Default Implementation
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override string GetAttribute(int i) => inst.GetAttribute(i);
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override string GetAttribute(string name) => inst.GetAttribute(name);
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override string GetAttribute(string name, string namespaceURI) => inst.GetAttribute(name, namespaceURI);
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override string LookupNamespace(string prefix) => inst.LookupNamespace(prefix);
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override bool MoveToAttribute(string name) => inst.MoveToAttribute(name);
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override bool MoveToAttribute(string name, string ns) => inst.MoveToAttribute(name, ns);
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override bool MoveToElement() => inst.MoveToElement();
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override bool MoveToFirstAttribute() => inst.MoveToFirstAttribute();
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override bool MoveToNextAttribute() => inst.MoveToNextAttribute();
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override bool Read() => inst.Read();
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override bool ReadAttributeValue() => inst.ReadAttributeValue();
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override void ResolveEntity() => inst.ResolveEntity();
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
public class GsfXMLOut
|
||||
{
|
||||
#region Properties
|
||||
|
||||
public GsfOutput Output { get; set; } = null;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Internal Properties
|
||||
|
||||
public string DocType { get; private set; } = null;
|
||||
|
||||
public Stack<string> Stack { get; private set; } = new Stack<string>();
|
||||
|
||||
public GsfXMLOutState State { get; private set; } = GsfXMLOutState.GSF_XML_OUT_CHILD;
|
||||
|
||||
public int Indent { get; private set; } = 0;
|
||||
|
||||
public bool PrettyPrint { get; private set; } = true;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Constructor
|
||||
|
||||
/// <summary>
|
||||
/// Private constructor
|
||||
/// </summary>
|
||||
protected GsfXMLOut() { }
|
||||
|
||||
/// <summary>
|
||||
/// Create an XML output stream.
|
||||
/// </summary>
|
||||
public static GsfXMLOut Create(GsfOutput output)
|
||||
{
|
||||
if (output == null)
|
||||
return null;
|
||||
|
||||
return new GsfXMLOut()
|
||||
{
|
||||
Output = output
|
||||
};
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Functions
|
||||
|
||||
/// <summary>
|
||||
/// Write the document start
|
||||
/// </summary>
|
||||
public void StartDocument()
|
||||
{
|
||||
string header = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n";
|
||||
Output.Write(header.Length, Encoding.UTF8.GetBytes(header));
|
||||
if (DocType != null)
|
||||
Output.PutString(DocType);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Output a start element <paramref name="id"/>, if necessary preceeded by an XML declaration.
|
||||
/// </summary>
|
||||
/// <param name="id">Element name</param>
|
||||
public void StartElement(string id)
|
||||
{
|
||||
if (id == null)
|
||||
return;
|
||||
|
||||
if (State == GsfXMLOutState.GSF_XML_OUT_NOCONTENT)
|
||||
{
|
||||
if (PrettyPrint)
|
||||
Output.Write(2, Encoding.UTF8.GetBytes(">\n"));
|
||||
else
|
||||
Output.Write(1, Encoding.UTF8.GetBytes(">"));
|
||||
}
|
||||
|
||||
OutIndent();
|
||||
Output.PrintF($"<{id}");
|
||||
|
||||
Stack.Push(id);
|
||||
Indent++;
|
||||
State = GsfXMLOutState.GSF_XML_OUT_NOCONTENT;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Closes/ends an XML element.
|
||||
/// </summary>
|
||||
/// <returns>The element that has been closed.</returns>
|
||||
public string EndElement()
|
||||
{
|
||||
if (Stack == null || Stack.Count == 0)
|
||||
return null;
|
||||
|
||||
string id = Stack.Pop();
|
||||
Indent--;
|
||||
switch (State)
|
||||
{
|
||||
case GsfXMLOutState.GSF_XML_OUT_NOCONTENT:
|
||||
if (PrettyPrint)
|
||||
Output.Write(3, Encoding.UTF8.GetBytes("/>\n"));
|
||||
else
|
||||
Output.Write(2, Encoding.UTF8.GetBytes("/>"));
|
||||
|
||||
break;
|
||||
|
||||
case GsfXMLOutState.GSF_XML_OUT_CHILD_PRETTY:
|
||||
OutIndent();
|
||||
if (PrettyPrint)
|
||||
Output.PrintF($"</{id}>\n");
|
||||
else
|
||||
Output.PrintF($"</{id}>");
|
||||
|
||||
break;
|
||||
|
||||
case GsfXMLOutState.GSF_XML_OUT_CHILD:
|
||||
case GsfXMLOutState.GSF_XML_OUT_CONTENT:
|
||||
if (PrettyPrint)
|
||||
Output.PrintF($"</{id}>\n");
|
||||
else
|
||||
Output.PrintF($"</{id}>");
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
State = PrettyPrint ? GsfXMLOutState.GSF_XML_OUT_CHILD_PRETTY : GsfXMLOutState.GSF_XML_OUT_CHILD;
|
||||
return id;
|
||||
}
|
||||
|
||||
/// <param name="pp">New state of pretty-print flag.</param>
|
||||
/// <returns>The previous state of the pretty-print flag.</returns>
|
||||
public bool SetPrettyPrint(bool pp)
|
||||
{
|
||||
bool res = PrettyPrint;
|
||||
if (pp != res)
|
||||
PrettyPrint = pp;
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Convenience routine to output a simple <paramref name="id"/> element with content <paramref name="content"/>.
|
||||
/// </summary>
|
||||
/// <param name="id">Element name</param>
|
||||
/// <param name="content">Content of the element</param>
|
||||
public void OutSimpleElement(string id, string content)
|
||||
{
|
||||
StartElement(id);
|
||||
if (content != null)
|
||||
AddString(null, content);
|
||||
|
||||
EndElement();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Convenience routine to output an element <paramref name="id"/> with integer value <paramref name="val"/>.
|
||||
/// </summary>
|
||||
/// <param name="id">Element name</param>
|
||||
/// <param name="val">Element value</param>
|
||||
public void OutSimpleSignedElement(string id, long val)
|
||||
{
|
||||
StartElement(id);
|
||||
AddSigned(null, val);
|
||||
EndElement();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Convenience routine to output an element <paramref name="id"/> with float value <paramref name="val"/> using
|
||||
/// <paramref name="precision"/> significant digits.
|
||||
/// </summary>
|
||||
/// <param name="id">Element name</param>
|
||||
/// <param name="val">Element value</param>
|
||||
/// <param name="precision">The number of significant digits to use, -1 meaning "enough".</param>
|
||||
public void OutSimpleFloatElement(string id, double val, int precision)
|
||||
{
|
||||
StartElement(id);
|
||||
AddFloat(null, val, precision);
|
||||
EndElement();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Dump <paramref name="valUtf8"/> to an attribute named @id without checking to see if
|
||||
/// the content needs escaping. A useful performance enhancement when
|
||||
/// the application knows that structure of the content well. If
|
||||
/// <paramref name="valUtf8"/> is null do nothing (no warning, no output)
|
||||
/// </summary>
|
||||
/// <param name="id">Tag id, or null for node content</param>
|
||||
/// <param name="valUtf8">A UTF-8 encoded string to export</param>
|
||||
public void AddStringUnchecked(string id, string valUtf8)
|
||||
{
|
||||
if (valUtf8 == null)
|
||||
return;
|
||||
|
||||
if (id == null)
|
||||
{
|
||||
CloseTagIfNecessary();
|
||||
Output.Write(valUtf8.Length, Encoding.UTF8.GetBytes(valUtf8));
|
||||
}
|
||||
else
|
||||
{
|
||||
Output.PrintF($" {id}=\"{valUtf8}\"");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Dump <paramref name="valUtf8"/> to an attribute named <paramref name="id"/> or as the nodes content escaping
|
||||
/// characters as necessary. If @valUtf8 is %null do nothing (no warning, no
|
||||
/// output)
|
||||
/// </summary>
|
||||
/// <param name="id">Tag id, or null for node content</param>
|
||||
/// <param name="valUtf8">A UTF-8 encoded string</param>
|
||||
public void AddString(string id, string valUtf8)
|
||||
{
|
||||
if (valUtf8 == null)
|
||||
return;
|
||||
|
||||
int cur = 0; // valUtf8[0];
|
||||
int start = 0; // valUtf8[0];
|
||||
|
||||
if (id == null)
|
||||
CloseTagIfNecessary();
|
||||
else
|
||||
Output.PrintF($" {id}=\"");
|
||||
|
||||
while (valUtf8[cur] != '\0')
|
||||
{
|
||||
if (valUtf8[cur] == '<')
|
||||
{
|
||||
if (cur != start)
|
||||
Output.Write(cur - start, Encoding.UTF8.GetBytes(valUtf8));
|
||||
|
||||
start = ++cur;
|
||||
Output.Write(4, Encoding.UTF8.GetBytes("<"));
|
||||
}
|
||||
else if (valUtf8[cur] == '>')
|
||||
{
|
||||
if (cur != start)
|
||||
Output.Write(cur - start, Encoding.UTF8.GetBytes(valUtf8));
|
||||
|
||||
start = ++cur;
|
||||
Output.Write(4, Encoding.UTF8.GetBytes(">"));
|
||||
}
|
||||
else if (valUtf8[cur] == '&')
|
||||
{
|
||||
if (cur != start)
|
||||
Output.Write(cur - start, Encoding.UTF8.GetBytes(valUtf8));
|
||||
|
||||
start = ++cur;
|
||||
Output.Write(5, Encoding.UTF8.GetBytes("&"));
|
||||
}
|
||||
else if (valUtf8[cur] == '"')
|
||||
{
|
||||
if (cur != start)
|
||||
Output.Write(cur - start, Encoding.UTF8.GetBytes(valUtf8));
|
||||
|
||||
start = ++cur;
|
||||
Output.Write(6, Encoding.UTF8.GetBytes("""));
|
||||
}
|
||||
else if ((valUtf8[cur] == '\n' || valUtf8[cur] == '\r' || valUtf8[cur] == '\t') && id != null)
|
||||
{
|
||||
string tempBuf = $"&#{(int)valUtf8[cur]};";
|
||||
byte[] buf = Encoding.UTF8.GetBytes(tempBuf);
|
||||
|
||||
if (cur != start)
|
||||
Output.Write(cur - start, Encoding.UTF8.GetBytes(valUtf8));
|
||||
|
||||
start = ++cur;
|
||||
Output.Write(buf.Length, buf);
|
||||
}
|
||||
else if ((valUtf8[cur] >= 0x20 && valUtf8[cur] != 0x7f) || (valUtf8[cur] == '\n' || valUtf8[cur] == '\r' || valUtf8[cur] == '\t'))
|
||||
{
|
||||
cur++;
|
||||
}
|
||||
else
|
||||
{
|
||||
// This is immensely pathetic, but XML 1.0 does not
|
||||
// allow certain characters to be encoded. XML 1.1
|
||||
// does allow this, but libxml2 does not support it.
|
||||
Console.Error.WriteLine($"Unknown char {valUtf8[cur]} in string");
|
||||
|
||||
if (cur != start)
|
||||
Output.Write(cur - start, Encoding.UTF8.GetBytes(valUtf8));
|
||||
|
||||
start = ++cur;
|
||||
}
|
||||
}
|
||||
|
||||
if (cur != start)
|
||||
Output.Write(cur - start, Encoding.UTF8.GetBytes(valUtf8));
|
||||
|
||||
if (id != null)
|
||||
Output.Write(1, Encoding.UTF8.GetBytes("\""));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Dump <paramref name="len"/> bytes in <paramref name="data"/> into the content of node <paramref name="id"/> using base64
|
||||
/// </summary>
|
||||
/// <param name="id">Tag id, or null for node content</param>
|
||||
/// <param name="data">Data to be written</param>
|
||||
/// <param name="len">Length of data</param>
|
||||
public void AddBase64(string id, byte[] data, int len) => AddStringUnchecked(id, Convert.ToBase64String(data, 0, len));
|
||||
|
||||
/// <summary>
|
||||
/// Dump boolean value <paramref name="val"/> to an attribute named <paramref name="id"/> or as the nodes content
|
||||
/// </summary>
|
||||
/// <param name="id">Tag id, or %null for node content</param>
|
||||
/// <param name="val">A boolean</param>
|
||||
/// <remarks>Use '1' or '0' to simplify import</remarks>
|
||||
public void AddBool(string id, bool val) => AddStringUnchecked(id, val ? "1" : "0");
|
||||
|
||||
/// <summary>
|
||||
/// Dump integer value <paramref name="val"/> to an attribute named <paramref name="id"/> or as the nodes content
|
||||
/// </summary>
|
||||
/// <param name="id">Tag id, or null for node content</param>
|
||||
/// <param name="val">The value</param>
|
||||
public void AddSigned(string id, long val) => AddStringUnchecked(id, val.ToString());
|
||||
|
||||
/// <summary>
|
||||
/// Dump unsigned integer value <paramref name="val"/> to an attribute named <paramref name="id"/> or as the nodes
|
||||
/// </summary>
|
||||
/// <param name="id">Tag id, or null for node content</param>
|
||||
/// <param name="val">The value</param>
|
||||
public void AddUnsigned(string id, ulong val) => AddStringUnchecked(id, val.ToString());
|
||||
|
||||
/// <summary>
|
||||
/// Dump float value <paramref name="val"/> to an attribute named <paramref name="id"/> or as the nodes
|
||||
/// content with precision <paramref name="precision"/>. The number will be formattted
|
||||
/// according to the "C" locale.
|
||||
/// </summary>
|
||||
/// <param name="id">Tag id, or null for node content</param>
|
||||
/// <param name="val">The value</param>
|
||||
/// <param name="precision">The number of significant digits to use, -1 meaning "enough".</param>
|
||||
public void AddFloat(string id, double val, int precision)
|
||||
{
|
||||
if (precision < 0 || precision > 17)
|
||||
AddStringUnchecked(id, val.ToString());
|
||||
else
|
||||
AddStringUnchecked(id, val.ToString($"F{precision}"));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Dump Color <paramref name="r"/>.<paramref name="g"/>.<paramref name="b"/> to an attribute named <paramref name="id"/> or as the nodes content
|
||||
/// </summary>
|
||||
/// <param name="id">Tag id, or null for node content</param>
|
||||
/// <param name="r">Red value</param>
|
||||
/// <param name="g">Green value</param>
|
||||
/// <param name="b">Blue value</param>
|
||||
public void AddColor(string id, uint r, uint g, uint b)
|
||||
{
|
||||
string buf = $"{r:X}:{g:X}:{b:X}\0";
|
||||
AddStringUnchecked(id, buf);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Output the value of <paramref name="val"/> as a string. Does NOT store any type information
|
||||
/// with the string, just the value.
|
||||
/// </summary>
|
||||
/// <param name="id">Tag id, or null for node content</param>
|
||||
public void AddValue(string id, object val)
|
||||
{
|
||||
if (val == null)
|
||||
return;
|
||||
|
||||
if (val is char)
|
||||
{
|
||||
// FIXME: What if we are in 0x80-0xff?
|
||||
AddString(id, $"{(char)val}\0");
|
||||
}
|
||||
else if (val is byte)
|
||||
{
|
||||
// FIXME: What if we are in 0x80-0xff?
|
||||
AddString(id, $"{(byte)val}\0");
|
||||
}
|
||||
else if (val is bool)
|
||||
{
|
||||
AddString(id, (bool)val ? "t" : "f");
|
||||
}
|
||||
else if (val is int)
|
||||
{
|
||||
AddSigned(id, (int)val);
|
||||
}
|
||||
else if (val is uint)
|
||||
{
|
||||
AddUnsigned(id, (uint)val);
|
||||
}
|
||||
else if (val is long)
|
||||
{
|
||||
AddSigned(id, (long)val);
|
||||
}
|
||||
else if (val is ulong)
|
||||
{
|
||||
AddUnsigned(id, (ulong)val);
|
||||
}
|
||||
else if (val is float)
|
||||
{
|
||||
AddFloat(id, (float)val, -1);
|
||||
}
|
||||
else if (val is double)
|
||||
{
|
||||
AddFloat(id, (double)val, -1);
|
||||
}
|
||||
else if (val is string)
|
||||
{
|
||||
AddString(id, (string)val);
|
||||
}
|
||||
else if (val is DateTime)
|
||||
{
|
||||
DateTime ts = (DateTime)val;
|
||||
string str = ts.ToString("yyyy-MM-dd hh:mm:ss");
|
||||
AddString(id, str);
|
||||
}
|
||||
|
||||
// FIXME FIXME FIXME Add some error checking
|
||||
}
|
||||
|
||||
public void SetOutput(GsfOutput output)
|
||||
{
|
||||
if (output.Wrap(this))
|
||||
Output = output;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Utilities
|
||||
|
||||
private void OutIndent()
|
||||
{
|
||||
if (!PrettyPrint)
|
||||
return;
|
||||
|
||||
// 2-space indent steps
|
||||
for (int i = Indent; i > 0; i--)
|
||||
{
|
||||
Output.Write(2, Encoding.UTF8.GetBytes(" "));
|
||||
}
|
||||
}
|
||||
|
||||
private void CloseTagIfNecessary()
|
||||
{
|
||||
if (State == GsfXMLOutState.GSF_XML_OUT_NOCONTENT)
|
||||
{
|
||||
State = GsfXMLOutState.GSF_XML_OUT_CONTENT;
|
||||
Output.Write(1, new byte[] { (byte)'>' });
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
public class GsfLibXML
|
||||
{
|
||||
/// <summary>
|
||||
/// Try to parse <paramref name="str"/> as a value of type <paramref name="t"/> into <paramref name="res"/>.
|
||||
/// </summary>
|
||||
/// <param name="res">Result value</param>
|
||||
/// <param name="t">Type of data</param>
|
||||
/// <param name="str">Value string</param>
|
||||
/// <returns>
|
||||
/// True when parsing of <paramref name="str"/> as a value of type <paramref name="t"/> was succesfull;
|
||||
/// false otherwise.
|
||||
/// </returns>
|
||||
public static bool ValueFromString(ref object res, Type t, string str)
|
||||
{
|
||||
if (str == null)
|
||||
return false;
|
||||
|
||||
res = null;
|
||||
|
||||
// Handle nullable DateTime
|
||||
if (t == typeof(DateTime))
|
||||
t = typeof(DateTime?);
|
||||
|
||||
// If the passed-in type is derived from G_TYPE_ENUM
|
||||
// or G_TYPE_FLAGS, we cannot switch on its type
|
||||
// because we don't know its GType at compile time.
|
||||
// We just pretend to have a G_TYPE_ENUM/G_TYPE_FLAGS.
|
||||
if (t.IsEnum)
|
||||
t = t.GetEnumUnderlyingType();
|
||||
|
||||
if (t == typeof(char))
|
||||
res = str[0];
|
||||
else if (t == typeof(byte))
|
||||
res = (byte)str[0];
|
||||
else if (t == typeof(bool))
|
||||
res = (char.ToLower(str[0]) == 't' || char.ToLower(str[0]) == 'y' || str[0] == '0');
|
||||
else if (t == typeof(int))
|
||||
res = int.Parse(str);
|
||||
else if (t == typeof(uint))
|
||||
res = uint.Parse(str);
|
||||
else if (t == typeof(long))
|
||||
res = long.Parse(str);
|
||||
else if (t == typeof(ulong))
|
||||
res = ulong.Parse(str);
|
||||
// TODO: Handle enum and flag strings
|
||||
else if (t == typeof(float))
|
||||
res = float.Parse(str);
|
||||
else if (t == typeof(double))
|
||||
res = double.Parse(str);
|
||||
else if (t == typeof(string))
|
||||
res = str;
|
||||
else if (t == typeof(DateTime?))
|
||||
res = DateTime.Parse(str);
|
||||
else
|
||||
Console.Error.WriteLine($"ValueFromString(): Don't know how to handle type '{t.Name}'");
|
||||
|
||||
return res != null;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
80
BurnOutSharp/External/libgsf/GsfMSOleImpl.cs
vendored
Normal file
80
BurnOutSharp/External/libgsf/GsfMSOleImpl.cs
vendored
Normal file
@@ -0,0 +1,80 @@
|
||||
/* 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
|
||||
*/
|
||||
|
||||
namespace LibGSF
|
||||
{
|
||||
public static class GsfMSOleImpl
|
||||
{
|
||||
public const int OLE_HEADER_SIZE = 0x200; /* independent of big block size size */
|
||||
public const int OLE_HEADER_SIGNATURE = 0x00;
|
||||
public const int OLE_HEADER_CLSID = 0x08; /* See ReadClassStg */
|
||||
public const int OLE_HEADER_MINOR_VER = 0x18; /* 0x33 and 0x3e have been seen */
|
||||
public const int OLE_HEADER_MAJOR_VER = 0x1a; /* 0x3 been seen in wild */
|
||||
public const int OLE_HEADER_BYTE_ORDER = 0x1c; /* 0xfe 0xff == Intel Little Endian */
|
||||
public const int OLE_HEADER_BB_SHIFT = 0x1e;
|
||||
public const int OLE_HEADER_SB_SHIFT = 0x20;
|
||||
/* 0x22..0x27 reserved == 0 */
|
||||
public const int OLE_HEADER_CSECTDIR = 0x28;
|
||||
public const int OLE_HEADER_NUM_BAT = 0x2c;
|
||||
public const int OLE_HEADER_DIRENT_START = 0x30;
|
||||
/* 0x34..0x37 transacting signature must be 0 */
|
||||
public const int OLE_HEADER_THRESHOLD = 0x38;
|
||||
public const int OLE_HEADER_SBAT_START = 0x3c;
|
||||
public const int OLE_HEADER_NUM_SBAT = 0x40;
|
||||
public const int OLE_HEADER_METABAT_BLOCK = 0x44;
|
||||
public const int OLE_HEADER_NUM_METABAT = 0x48;
|
||||
public const int OLE_HEADER_START_BAT = 0x4c;
|
||||
public const int BAT_INDEX_SIZE = 4;
|
||||
public const int OLE_HEADER_METABAT_SIZE = ((OLE_HEADER_SIZE - OLE_HEADER_START_BAT) / BAT_INDEX_SIZE);
|
||||
|
||||
public const int DIRENT_MAX_NAME_SIZE = 0x40;
|
||||
public const int DIRENT_DETAILS_SIZE = 0x40;
|
||||
public const int DIRENT_SIZE = (DIRENT_MAX_NAME_SIZE + DIRENT_DETAILS_SIZE);
|
||||
public const int DIRENT_NAME_LEN = 0x40; /* length in bytes incl 0 terminator */
|
||||
public const int DIRENT_TYPE = 0x42;
|
||||
public const int DIRENT_COLOUR = 0x43;
|
||||
public const int DIRENT_PREV = 0x44;
|
||||
public const int DIRENT_NEXT = 0x48;
|
||||
public const int DIRENT_CHILD = 0x4c;
|
||||
public const int DIRENT_CLSID = 0x50; /* only for dirs */
|
||||
public const int DIRENT_USERFLAGS = 0x60; /* only for dirs */
|
||||
public const int DIRENT_CREATE_TIME = 0x64; /* for files */
|
||||
public const int DIRENT_MODIFY_TIME = 0x6c; /* for files */
|
||||
public const int DIRENT_FIRSTBLOCK = 0x74;
|
||||
public const int DIRENT_FILE_SIZE = 0x78;
|
||||
/* 0x7c..0x7f reserved == 0 */
|
||||
|
||||
public const int DIRENT_TYPE_INVALID = 0;
|
||||
public const int DIRENT_TYPE_DIR = 1;
|
||||
public const int DIRENT_TYPE_FILE = 2;
|
||||
public const int DIRENT_TYPE_LOCKBYTES = 3; /* ? */
|
||||
public const int DIRENT_TYPE_PROPERTY = 4; /* ? */
|
||||
public const int DIRENT_TYPE_ROOTDIR = 5;
|
||||
public const uint DIRENT_MAGIC_END = 0xffffffff;
|
||||
|
||||
/* 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 */
|
||||
}
|
||||
}
|
||||
2431
BurnOutSharp/External/libgsf/GsfMSOleUtils.cs
vendored
Normal file
2431
BurnOutSharp/External/libgsf/GsfMSOleUtils.cs
vendored
Normal file
File diff suppressed because it is too large
Load Diff
378
BurnOutSharp/External/libgsf/GsfMetaNames.cs
vendored
Normal file
378
BurnOutSharp/External/libgsf/GsfMetaNames.cs
vendored
Normal file
@@ -0,0 +1,378 @@
|
||||
/* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
|
||||
/*
|
||||
* gsf-meta-names.h: a list of gsf-meta-names to "generically" represent
|
||||
* all diversly available implementation-specific
|
||||
* meta-names.
|
||||
*
|
||||
* Author: Veerapuram Varadhan (vvaradhan@novell.com)
|
||||
* Jody Goldberg (jody@gnome.org)
|
||||
*
|
||||
* Copyright (C) 2004-2006 Novell, Inc
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of version 2.1 of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301
|
||||
* USA
|
||||
*/
|
||||
|
||||
namespace LibGSF
|
||||
{
|
||||
/// <summary>
|
||||
/// The namespace follow this classification:
|
||||
///
|
||||
/// "dc:" - Dublin Core tags
|
||||
/// "gsf:" - Gnumeric only tags
|
||||
/// "meta:" - OpenDocument tags shared with Gnumeric
|
||||
/// "MSOLE:" - OLE tags
|
||||
/// </summary>
|
||||
public static class GsfMetaNames
|
||||
{
|
||||
#region Namespace - dc
|
||||
|
||||
/// <summary>
|
||||
/// (String) An entity primarily responsible for making the content of the
|
||||
/// resource typically a person, organization, or service.
|
||||
/// </summary>
|
||||
/// <remarks>1.14.0 Moved from "gsf" to "dc".</remarks>
|
||||
public const string GSF_META_NAME_CREATOR = "dc:creator";
|
||||
|
||||
/// <summary>
|
||||
/// (GsfTimestamp) The last time this document was saved.
|
||||
/// </summary>
|
||||
/// <remarks>1.14.0 Moved from dc:date-modified to dc:date.</remarks>
|
||||
public const string GSF_META_NAME_DATE_MODIFIED = "dc:date";
|
||||
|
||||
/// <summary>
|
||||
/// (String) An account of the content of the resource.
|
||||
/// </summary>
|
||||
public const string GSF_META_NAME_DESCRIPTION = "dc:description";
|
||||
|
||||
/// <summary>
|
||||
/// (GsfDocPropVector of String) Searchable, indexable keywords. Similar to PDF
|
||||
/// keywords or HTML's meta block.
|
||||
/// </summary>
|
||||
public const string GSF_META_NAME_KEYWORDS = "dc:keywords";
|
||||
|
||||
/// <summary>
|
||||
/// (String) The locale language of the intellectual content of the resource
|
||||
/// (basically xx_YY form for us).
|
||||
/// </summary>
|
||||
/// <remarks>1.14.0 Clarified that this is unique from _NAME_CODEPAGE in MSOLE</remarks>
|
||||
public const string GSF_META_NAME_LANGUAGE = "dc:language";
|
||||
|
||||
/// <summary>
|
||||
/// (UnsignedShort) The MS codepage to encode strings for metadata
|
||||
/// </summary>
|
||||
/// <remarks>1.14.0 Clarified that this is unique from _NAME_CODEPAGE in MSOLE</remarks>
|
||||
public const string GSF_META_NAME_CODEPAGE = "MSOLE:codepage";
|
||||
|
||||
/// <summary>
|
||||
/// (String) The topic of the content of the resource,
|
||||
/// <emphasis>typically</emphasis> including keywords.
|
||||
/// </summary>
|
||||
public const string GSF_META_NAME_SUBJECT = "dc:subject";
|
||||
|
||||
/// <summary>
|
||||
/// (String) A formal name given to the resource.
|
||||
/// </summary>
|
||||
public const string GSF_META_NAME_TITLE = "dc:title";
|
||||
|
||||
#endregion
|
||||
|
||||
#region Namespace - gsf
|
||||
|
||||
/// <summary>
|
||||
/// (Integer) Count of bytes in the document.
|
||||
/// </summary>
|
||||
public const string GSF_META_NAME_BYTE_COUNT = "gsf:byte-count";
|
||||
|
||||
/// <summary>
|
||||
/// (Unsigned Integer) Identifier representing the case-sensitiveness.
|
||||
/// </summary>
|
||||
/// <remarks>of what ?? why is it an integer ??</remarks>
|
||||
public const string GSF_META_NAME_CASE_SENSITIVE = "gsf:case-sensitivity";
|
||||
|
||||
/// <summary>
|
||||
/// (String) Category of the document.
|
||||
/// </summary>
|
||||
/// <remarks>example???</remarks>
|
||||
public const string GSF_META_NAME_CATEGORY = "gsf:category";
|
||||
|
||||
/// <summary>
|
||||
/// (Integer) Count of cells in the spread-sheet document, if appropriate.
|
||||
/// </summary>
|
||||
public const string GSF_META_NAME_CELL_COUNT = "gsf:cell-count";
|
||||
|
||||
/// <summary>
|
||||
/// (Integer) Count of characters in the document.
|
||||
/// </summary>
|
||||
/// <remarks>TODO See how to sync this with ODF's document-statistic</remarks>
|
||||
public const string GSF_META_NAME_CHARACTER_COUNT = "gsf:character-count";
|
||||
|
||||
/// <summary>
|
||||
/// (None) Reserved name (PID) for Dictionary
|
||||
/// </summary>
|
||||
public const string GSF_META_NAME_DICTIONARY = "gsf:dictionary";
|
||||
|
||||
/// <summary>
|
||||
/// (Vector of strings) Names of the 'interesting' parts of the document. In
|
||||
/// spreadsheets this is a list of the sheet names, and the named expressions.
|
||||
/// </summary>
|
||||
/// <remarks>From MSOLE</remarks>
|
||||
public const string GSF_META_NAME_DOCUMENT_PARTS = "gsf:document-parts";
|
||||
|
||||
/// <summary>
|
||||
/// (Vector of string value pairs stored in alternating elements) Store the
|
||||
/// counts of objects in the document as names 'worksheet' and count '4'
|
||||
/// </summary>
|
||||
/// <remarks>From MSOLE</remarks>
|
||||
public const string GSF_META_NAME_HEADING_PAIRS = "gsf:heading-pairs";
|
||||
|
||||
/// <summary>
|
||||
/// (Integer) Count of hidden-slides in the presentation document.
|
||||
/// </summary>
|
||||
public const string GSF_META_NAME_HIDDEN_SLIDE_COUNT = "gsf:hidden-slide-count";
|
||||
|
||||
/// <summary>
|
||||
/// (Integer) Count of images in the document, if appropriate.
|
||||
/// </summary>
|
||||
public const string GSF_META_NAME_IMAGE_COUNT = "gsf:image-count";
|
||||
|
||||
/// <summary>
|
||||
/// (String) The entity that made the last change to the document, typically a
|
||||
/// person, organization, or service.
|
||||
/// </summary>
|
||||
public const string GSF_META_NAME_LAST_SAVED_BY = "gsf:last-saved-by";
|
||||
|
||||
/// <summary>
|
||||
/// (Boolean) ???????
|
||||
/// </summary>
|
||||
public const string GSF_META_NAME_LINKS_DIRTY = "gsf:links-dirty";
|
||||
|
||||
/// <summary>
|
||||
/// (Unsigned Integer) Identifier representing the default system locale.
|
||||
/// </summary>
|
||||
public const string GSF_META_NAME_LOCALE_SYSTEM_DEFAULT = "gsf:default-locale";
|
||||
|
||||
/// <summary>
|
||||
/// (String) Name of the manager of "CREATOR" entity.
|
||||
/// </summary>
|
||||
public const string GSF_META_NAME_MANAGER = "gsf:manager";
|
||||
|
||||
/// <summary>
|
||||
/// (String) Type of presentation, like "On-screen Show", "SlideView" etc.
|
||||
/// </summary>
|
||||
public const string GSF_META_NAME_PRESENTATION_FORMAT = "gsf:presentation-format";
|
||||
|
||||
/// <summary>
|
||||
/// (Boolean) ?????
|
||||
/// </summary>
|
||||
public const string GSF_META_NAME_SCALE = "gsf:scale";
|
||||
|
||||
/// <summary>
|
||||
/// (Integer) Level of security.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <informaltable frame="none" role="params">
|
||||
/// <tgroup cols = "2" >
|
||||
/// <thead>
|
||||
/// <row><entry align="left">Level</entry><entry>Value</entry></row>
|
||||
/// </thead>
|
||||
/// <tbody>
|
||||
/// <row><entry>None</entry><entry>0</entry></row>
|
||||
/// <row><entry>Password protected</entry><entry>1</entry></row>
|
||||
/// <row><entry>Read-only recommended</entry><entry>2</entry></row>
|
||||
/// <row><entry>Read-only enforced</entry><entry>3</entry></row>
|
||||
/// <row><entry>Locked for annotations</entry><entry>4</entry></row>
|
||||
/// </tbody></tgroup></informaltable>
|
||||
/// </remarks>
|
||||
public const string GSF_META_NAME_SECURITY = "gsf:security";
|
||||
|
||||
/// <summary>
|
||||
/// (GsfClipData) Thumbnail data of the document, typically a
|
||||
/// preview image of the document.
|
||||
/// </summary>
|
||||
public const string GSF_META_NAME_THUMBNAIL = "gsf:thumbnail";
|
||||
|
||||
/// <summary>
|
||||
/// (Integer) Count of liness in the document.
|
||||
/// </summary>
|
||||
public const string GSF_META_NAME_LINE_COUNT = "gsf:line-count";
|
||||
|
||||
/// <summary>
|
||||
/// (Integer) Count of "multi-media" clips in the document.
|
||||
/// </summary>
|
||||
public const string GSF_META_NAME_MM_CLIP_COUNT = "gsf:MM-clip-count";
|
||||
|
||||
/// <summary>
|
||||
/// (Integer) Count of "notes" in the document.
|
||||
/// </summary>
|
||||
public const string GSF_META_NAME_NOTE_COUNT = "gsf:note-count";
|
||||
|
||||
/// <summary>
|
||||
/// (Integer) Count of objects (OLE and other graphics) in the document, if
|
||||
/// appropriate.
|
||||
/// </summary>
|
||||
public const string GSF_META_NAME_OBJECT_COUNT = "gsf:object-count";
|
||||
|
||||
/// <summary>
|
||||
/// (Integer) Count of pages in the document, if appropriate.
|
||||
/// </summary>
|
||||
public const string GSF_META_NAME_PAGE_COUNT = "gsf:page-count";
|
||||
|
||||
/// <summary>
|
||||
/// (Integer) Count of paragraphs in the document, if appropriate.
|
||||
/// </summary>
|
||||
public const string GSF_META_NAME_PARAGRAPH_COUNT = "gsf:paragraph-count";
|
||||
|
||||
/// <summary>
|
||||
/// (Integer) Count of slides in the presentation document.
|
||||
/// </summary>
|
||||
public const string GSF_META_NAME_SLIDE_COUNT = "gsf:slide-count";
|
||||
|
||||
/// <summary>
|
||||
/// (Integer) Count of pages in the document, if appropriate.
|
||||
/// </summary>
|
||||
public const string GSF_META_NAME_SPREADSHEET_COUNT = "gsf:spreadsheet-count";
|
||||
|
||||
/// <summary>
|
||||
/// (Integer) Count of tables in the document, if appropriate.
|
||||
/// </summary>
|
||||
public const string GSF_META_NAME_TABLE_COUNT = "gsf:table-count";
|
||||
|
||||
/// <summary>
|
||||
/// (Integer) Count of words in the document.
|
||||
/// </summary>
|
||||
public const string GSF_META_NAME_WORD_COUNT = "gsf:word-count";
|
||||
|
||||
#endregion
|
||||
|
||||
#region Namespace - MSOLE
|
||||
|
||||
/// <summary>
|
||||
/// (Unknown) User-defined name
|
||||
/// </summary>
|
||||
public const string GSF_META_NAME_MSOLE_UNKNOWN_17 = "MSOLE:unknown-doc-17";
|
||||
|
||||
/// <summary>
|
||||
/// (Unknown) User-defined name
|
||||
/// </summary>
|
||||
public const string GSF_META_NAME_MSOLE_UNKNOWN_18 = "MSOLE:unknown-doc-18";
|
||||
|
||||
/// <summary>
|
||||
/// (Boolean) User-defined name
|
||||
/// </summary>
|
||||
public const string GSF_META_NAME_MSOLE_UNKNOWN_19 = "MSOLE:unknown-doc-19";
|
||||
|
||||
/// <summary>
|
||||
/// (Unknown) User-defined name
|
||||
/// </summary>
|
||||
public const string GSF_META_NAME_MSOLE_UNKNOWN_20 = "MSOLE:unknown-doc-20";
|
||||
|
||||
/// <summary>
|
||||
/// (Unknown) User-defined name
|
||||
/// </summary>
|
||||
public const string GSF_META_NAME_MSOLE_UNKNOWN_21 = "MSOLE:unknown-doc-21";
|
||||
|
||||
/// <summary>
|
||||
/// (Boolean) User-defined name
|
||||
/// </summary>
|
||||
public const string GSF_META_NAME_MSOLE_UNKNOWN_22 = "MSOLE:unknown-doc-22";
|
||||
|
||||
/// <summary>
|
||||
/// (i4) User-defined name
|
||||
/// </summary>
|
||||
public const string GSF_META_NAME_MSOLE_UNKNOWN_23 = "MSOLE:unknown-doc-23";
|
||||
|
||||
#endregion
|
||||
|
||||
#region Namespace - meta
|
||||
|
||||
/// <summary>
|
||||
/// (Date as ISO String) A date associated with an event in the life cycle of
|
||||
/// the resource (creation/publication date).
|
||||
/// Moved from gsf:date-created to meta:creation-date. This way can be used correctly
|
||||
/// by OpenDocument and Gnumeric.
|
||||
/// </summary>
|
||||
public const string GSF_META_NAME_DATE_CREATED = "meta:creation-date";
|
||||
|
||||
/// <summary>
|
||||
/// (Date as ISO String) The total-time taken until the last modification.
|
||||
/// Moved from "gsf" to "meta". This way can be used correctly by OpenDocument
|
||||
/// and Gnumeric.
|
||||
/// </summary>
|
||||
public const string GSF_META_NAME_EDITING_DURATION = "meta:editing-duration";
|
||||
|
||||
/// <summary>
|
||||
/// (String) The application that generated this document. AbiWord, Gnumeric,
|
||||
/// etc...
|
||||
/// </summary>
|
||||
/// <remarks>1.14.0 Moved from "gsf" to "meta".</remarks>
|
||||
public const string GSF_META_NAME_GENERATOR = "meta:generator";
|
||||
|
||||
/// <summary>
|
||||
/// (String) Searchable, indexable keywords. Similar to PDF keywords or HTML's
|
||||
/// meta block.
|
||||
/// </summary>
|
||||
public const string GSF_META_NAME_KEYWORD = "meta:keyword";
|
||||
|
||||
/// <summary>
|
||||
/// (String) Specifies the name of the person who created the document
|
||||
/// initially.
|
||||
/// </summary>
|
||||
/// <remarks>1.14.0 Moved from "gsf" to "meta".</remarks>
|
||||
public const string GSF_META_NAME_INITIAL_CREATOR = "meta:initial-creator";
|
||||
|
||||
/// <summary>
|
||||
/// (String) Name of the company/organization that the "CREATOR" entity is
|
||||
/// associated with.
|
||||
/// </summary>
|
||||
/// <remarks>1.14.1 Moved from "gsf:company" to "dc:publisher".</remarks>
|
||||
public const string GSF_META_NAME_COMPANY = "dc:publisher";
|
||||
|
||||
/// <summary>
|
||||
/// (GsfTimestamp) Specifies the date and time when the document was last
|
||||
/// printed.
|
||||
/// </summary>
|
||||
public const string GSF_META_NAME_PRINT_DATE = "meta:print-date";
|
||||
|
||||
/// <summary>
|
||||
/// (GSF_META_NAME_HEADING_PAIRS) The last time this document was printed.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// 1.14.0 Moved from "gsf" to "dc".
|
||||
/// 1.14.1 Moved back to "gsf" from "dc".
|
||||
/// </remarks>
|
||||
public const string GSF_META_NAME_LAST_PRINTED = "gsf:last-printed";
|
||||
|
||||
/// <summary>
|
||||
/// (String) Specifies the name of the last person who printed the document.
|
||||
/// </summary>
|
||||
/// <remarks>1.14.0 Moved from "gsf" to "meta".</remarks>
|
||||
public const string GSF_META_NAME_PRINTED_BY = "meta:printed-by";
|
||||
|
||||
/// <summary>
|
||||
/// (Integer) Count of revision on the document, if appropriate.
|
||||
/// Moved from gsf:revision-count to meta:editing-cycles. This way can be used
|
||||
/// correctly by OpenDocument and Gnumeric.
|
||||
/// </summary>
|
||||
public const string GSF_META_NAME_REVISION_COUNT = "meta:editing-cycles";
|
||||
|
||||
/// <summary>
|
||||
/// (String) The template file that is been used to generate this document.
|
||||
/// </summary>
|
||||
/// <remarks>1.14.0 Moved from "gsf" to "meta"</remarks>
|
||||
public const string GSF_META_NAME_TEMPLATE = "meta:template";
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
784
BurnOutSharp/External/libgsf/GsfOpenDocUtils.cs
vendored
Normal file
784
BurnOutSharp/External/libgsf/GsfOpenDocUtils.cs
vendored
Normal file
@@ -0,0 +1,784 @@
|
||||
/* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
|
||||
/*
|
||||
* gsf-opendoc-utils.c: Handle the application neutral portions of OpenDocument
|
||||
*
|
||||
* Author: Luciano Wolf (luciano.wolf@indt.org.br)
|
||||
*
|
||||
* Copyright (C) 2006 Jody Goldberg (jody@gnome.org)
|
||||
* Copyright (C) 2005-2006 INdT - Instituto Nokia de Tecnologia
|
||||
* http://www.indt.org.br
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of version 2.1 of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301
|
||||
* USA
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Xml;
|
||||
using LibGSF.Input;
|
||||
using static LibGSF.GsfMetaNames;
|
||||
|
||||
namespace LibGSF
|
||||
{
|
||||
#region Enums
|
||||
|
||||
public enum OpenDocType
|
||||
{
|
||||
OO_NS_OFFICE,
|
||||
OO_NS_STYLE,
|
||||
OO_NS_TEXT,
|
||||
OO_NS_TABLE,
|
||||
OO_NS_DRAW,
|
||||
OO_NS_NUMBER,
|
||||
OO_NS_CHART,
|
||||
OO_NS_DR3D,
|
||||
OO_NS_FORM,
|
||||
OO_NS_SCRIPT,
|
||||
OO_NS_CONFIG,
|
||||
OO_NS_MATH,
|
||||
OO_NS_FO,
|
||||
OO_NS_DC,
|
||||
OO_NS_META,
|
||||
OO_NS_XLINK,
|
||||
OO_NS_SVG,
|
||||
|
||||
/* new in 2.0 */
|
||||
OO_NS_OOO,
|
||||
OO_NS_OOOW,
|
||||
OO_NS_OOOC,
|
||||
OO_NS_DOM,
|
||||
OO_NS_XFORMS,
|
||||
OO_NS_XSD,
|
||||
OO_NS_XSI,
|
||||
|
||||
OO_NS_PRESENT, /* added in gsf-1.14.8 */
|
||||
|
||||
/* new in 3.0 */
|
||||
OO_NS_RPT,
|
||||
OO_NS_OF,
|
||||
OO_NS_RDFA,
|
||||
OO_NS_FIELD,
|
||||
OO_NS_FORMX,
|
||||
|
||||
/* Other OpenDocument 1.1 */
|
||||
OO_NS_ANIM,
|
||||
OO_NS_DATASTYLE,
|
||||
OO_NS_MANIFEST,
|
||||
OO_NS_SMIL,
|
||||
|
||||
/* Symphony 1.3 */
|
||||
OO_LOTUS_NS_PRODTOOLS,
|
||||
|
||||
/* KOffice 1.6.3 */
|
||||
OO_KDE_NS_KOFFICE,
|
||||
|
||||
/*CleverAge ODF Add-in for Microsoft Office 3.0.5224.0 (11.0.8302)*/
|
||||
OO_CLEVERAGE_NS_DC,
|
||||
|
||||
/* Microsoft Excel Formulas */
|
||||
OO_MS_NS_MSOXL,
|
||||
|
||||
/* Gnumeric ODF extensions */
|
||||
OO_GNUM_NS_EXT,
|
||||
|
||||
/* New in ODF 3.2 */
|
||||
OO_NS_GRDDL,
|
||||
OO_NS_XHTML,
|
||||
OO_NS_TABLE_OOO,
|
||||
|
||||
/* New in ODF 3.3 */
|
||||
OO_NS_CHART_OOO,
|
||||
|
||||
/* New in LOCALC */
|
||||
OO_NS_LOCALC_EXT,
|
||||
OO_NS_LOCALC_EXT2
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Classes
|
||||
|
||||
public class GsfODFOut : GsfXMLOut
|
||||
{
|
||||
#region Internal Properties
|
||||
|
||||
public int OdfVersion { get; set; } = 100;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Functions
|
||||
|
||||
public string GetVersionString() => $"{OdfVersion / 100}.{OdfVersion % 100}";
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
public class GsfOOMetaIn
|
||||
{
|
||||
public GsfDocMetaData MetaData { get; set; }
|
||||
|
||||
public List<object> Keywords { get; set; }
|
||||
|
||||
public Exception Err { get; set; }
|
||||
|
||||
public string Name { get; set; }
|
||||
|
||||
public Type Type { get; set; }
|
||||
|
||||
public XmlDocument Doc { get; set; }
|
||||
}
|
||||
|
||||
public static class GsfOpenDocUtils
|
||||
{
|
||||
#region Constants
|
||||
|
||||
public const string OFFICE = "office:";
|
||||
|
||||
#endregion
|
||||
|
||||
#region Functions
|
||||
|
||||
/// <summary>
|
||||
/// Gives the ODF version used by libgsf when writing Open Document files.
|
||||
/// </summary>
|
||||
/// <returns>The ODF version as a string: "1.2".</returns>
|
||||
public static string GetVersionString() => "1.2";
|
||||
|
||||
/// <summary>
|
||||
/// Gives the ODF version used by libgsf when writing Open Document files.
|
||||
/// </summary>
|
||||
/// <returns>The ODF version: 102.</returns>
|
||||
public static short GetVersion() => 102;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Extension Functions
|
||||
|
||||
public static void od_meta_generator(this GsfXMLIn xin, GsfXMLBlob blob) => xin.od_get_meta_prop(GSF_META_NAME_GENERATOR, typeof(string));
|
||||
|
||||
public static void od_meta_title(this GsfXMLIn xin, GsfXMLBlob blob) => xin.od_get_meta_prop(GSF_META_NAME_TITLE, typeof(string));
|
||||
|
||||
public static void od_meta_description(this GsfXMLIn xin, GsfXMLBlob blob) => xin.od_get_meta_prop(GSF_META_NAME_DESCRIPTION, typeof(string));
|
||||
|
||||
public static void od_meta_subject(this GsfXMLIn xin, GsfXMLBlob blob) => xin.od_get_meta_prop(GSF_META_NAME_SUBJECT, typeof(string));
|
||||
|
||||
public static void od_meta_initial_creator(this GsfXMLIn xin, GsfXMLBlob blob) => xin.od_get_meta_prop(GSF_META_NAME_INITIAL_CREATOR, typeof(string));
|
||||
|
||||
/// <summary>
|
||||
/// OD considers this the last person to modify the doc, rather than
|
||||
/// the DC convention of the person primarilly responsible for its creation
|
||||
/// </summary>
|
||||
public static void od_meta_creator(this GsfXMLIn xin, GsfXMLBlob blob) => xin.od_get_meta_prop(GSF_META_NAME_CREATOR, typeof(string));
|
||||
|
||||
/// <summary>
|
||||
/// Last to print
|
||||
/// </summary>
|
||||
public static void od_meta_printed_by(this GsfXMLIn xin, GsfXMLBlob blob) => xin.od_get_meta_prop(GSF_META_NAME_PRINTED_BY, typeof(string));
|
||||
|
||||
public static void od_meta_date_created(this GsfXMLIn xin, GsfXMLBlob blob) => xin.od_get_meta_prop(GSF_META_NAME_DATE_CREATED, typeof(DateTime));
|
||||
|
||||
public static void od_meta_date_modified(this GsfXMLIn xin, GsfXMLBlob blob) => xin.od_get_meta_prop(GSF_META_NAME_DATE_MODIFIED, typeof(DateTime));
|
||||
|
||||
public static void od_meta_print_date(this GsfXMLIn xin, GsfXMLBlob blob) => xin.od_get_meta_prop(GSF_META_NAME_LAST_PRINTED, typeof(DateTime));
|
||||
|
||||
public static void od_meta_language(this GsfXMLIn xin, GsfXMLBlob blob) => xin.od_get_meta_prop(GSF_META_NAME_LANGUAGE, typeof(string));
|
||||
|
||||
public static void od_meta_editing_cycles(this GsfXMLIn xin, GsfXMLBlob blob) => xin.od_get_meta_prop(GSF_META_NAME_REVISION_COUNT, typeof(uint));
|
||||
|
||||
// FIXME FIXME FIXME should be durations using format 'PnYnMnDTnHnMnS'
|
||||
public static void od_meta_editing_duration(this GsfXMLIn xin, GsfXMLBlob blob) => xin.od_get_meta_prop(GSF_META_NAME_EDITING_DURATION, typeof(string));
|
||||
|
||||
/// <summary>
|
||||
/// OD allows multiple keywords, accumulate things and make it an array
|
||||
/// </summary>
|
||||
public static void od_meta_keyword(this GsfXMLIn xin, GsfXMLBlob blob)
|
||||
{
|
||||
GsfOOMetaIn mi = (GsfOOMetaIn)xin.UserState;
|
||||
|
||||
if (mi.Keywords == null)
|
||||
mi.Keywords = new List<object>();
|
||||
|
||||
mi.Keywords.Add(xin.Content);
|
||||
}
|
||||
|
||||
public static void od_meta_user_defined(this GsfXMLIn xin, string[] attrs)
|
||||
{
|
||||
GsfOOMetaIn mi = (GsfOOMetaIn)xin.UserState;
|
||||
mi.Type = null;
|
||||
mi.Name = null;
|
||||
|
||||
for (int i = 0; i < attrs.Length - 1 && attrs[i] != null && attrs[i + 1] != null; i += 2)
|
||||
{
|
||||
if (attrs[i] == "meta:name")
|
||||
{
|
||||
mi.Name = attrs[i + 1];
|
||||
}
|
||||
else if (attrs[i] == "meta:value-type" || attrs[i] == "meta:type")
|
||||
{
|
||||
// "meta:type" is a typo on the write
|
||||
// side that was fixed on 20110509.
|
||||
if (attrs[i + 1] == "boolean")
|
||||
{
|
||||
mi.Type = typeof(bool);
|
||||
}
|
||||
else if (attrs[i + 1] == "float")
|
||||
{
|
||||
mi.Type = typeof(double);
|
||||
}
|
||||
else if (attrs[i + 1] == "string")
|
||||
{
|
||||
mi.Type = typeof(string);
|
||||
}
|
||||
else if (attrs[i + 1] == "date" || attrs[i + 1] == "data")
|
||||
{
|
||||
// "data" is a typo on the write side that was
|
||||
// fixed on 20110311.
|
||||
mi.Type = typeof(DateTime);
|
||||
}
|
||||
else if (attrs[i + 1] == "time")
|
||||
{
|
||||
mi.Type = typeof(string);
|
||||
// We should be able to do better
|
||||
}
|
||||
else
|
||||
{
|
||||
// What?
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// This should not happen
|
||||
if (mi.Name == null)
|
||||
mi.Name = string.Empty;
|
||||
}
|
||||
|
||||
public static void od_meta_user_defined_end(this GsfXMLIn xin, GsfXMLBlob blob)
|
||||
{
|
||||
GsfOOMetaIn mi = (GsfOOMetaIn)xin.UserState;
|
||||
if (mi.Name != null)
|
||||
{
|
||||
object res = new object();
|
||||
Type t = mi.Type;
|
||||
|
||||
if (t == null)
|
||||
t = typeof(string);
|
||||
|
||||
if (!GsfLibXML.ValueFromString(ref res, t, xin.Content))
|
||||
{
|
||||
mi.Name = null;
|
||||
return;
|
||||
}
|
||||
|
||||
if (mi.Name.StartsWith("GSF_DOCPROP_VECTOR:"))
|
||||
{
|
||||
int true_name = mi.Name.IndexOf(':', 19);
|
||||
if (true_name != -1 && mi.Name[++true_name] != 0)
|
||||
{
|
||||
GsfDocProp prop = mi.MetaData.Lookup(mi.Name.Substring(true_name));
|
||||
if (prop == null)
|
||||
{
|
||||
List<GsfDocProp> vector = new List<GsfDocProp>();
|
||||
vector.Add(prop);
|
||||
mi.MetaData.Insert(mi.Name.Substring(true_name), vector);
|
||||
}
|
||||
else
|
||||
{
|
||||
object old = prop.Value;
|
||||
if (old is List<GsfDocProp> oldList)
|
||||
{
|
||||
List<GsfDocProp> newObj = new List<GsfDocProp>();
|
||||
newObj.AddRange(oldList);
|
||||
newObj.Add(res as GsfDocProp);
|
||||
prop.Value = newObj;
|
||||
}
|
||||
else
|
||||
{
|
||||
Console.Error.WriteLine($"Property \"{mi.Name.Substring(true_name)}\" used for multiple types!");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
mi.Name = null;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
mi.MetaData.Insert(mi.Name, res);
|
||||
mi.Name = null;
|
||||
}
|
||||
}
|
||||
|
||||
private static void od_get_meta_prop(this GsfXMLIn xin, string prop_name, Type g_type)
|
||||
{
|
||||
object res = new object();
|
||||
if (GsfLibXML.ValueFromString(ref res, g_type, xin.Content))
|
||||
(xin.UserState as GsfOOMetaIn).MetaData.Insert(prop_name, res);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Utilities
|
||||
|
||||
/// <summary>
|
||||
/// Generated based on:
|
||||
/// http://www.oasis-open.org/committees/download.php/12572/OpenDocument-v1.0-os.pdf
|
||||
/// and OpenDocument-v1.1.pdf
|
||||
/// </summary>
|
||||
private static XmlNameTable CreateOpenDocumentNamespaces()
|
||||
{
|
||||
NameTable table = new NameTable();
|
||||
|
||||
// OOo 1.0.x & 1.1.x
|
||||
table.Add("http://openoffice.org/2000/office");
|
||||
table.Add("http://openoffice.org/2000/style");
|
||||
table.Add("http://openoffice.org/2000/text");
|
||||
table.Add("http://openoffice.org/2000/table");
|
||||
table.Add("http://openoffice.org/2000/drawing");
|
||||
table.Add("http://openoffice.org/2000/datastyle");
|
||||
table.Add("http://openoffice.org/2000/chart");
|
||||
table.Add("http://openoffice.org/2000/dr3d");
|
||||
table.Add("http://openoffice.org/2000/form");
|
||||
table.Add("http://openoffice.org/2000/script");
|
||||
table.Add("http://openoffice.org/2001/config");
|
||||
table.Add("http://www.w3.org/1998/Math/MathML"); // also in 2.0
|
||||
table.Add("http://www.w3.org/1999/XSL/Format");
|
||||
table.Add("http://www.w3.org/1999/xlink"); // also in 2.0
|
||||
table.Add("http://www.w3.org/2000/svg");
|
||||
|
||||
// OOo 1.9.x & 2.0.x
|
||||
table.Add("urn:oasis:names:tc:opendocument:xmlns:office:1.0");
|
||||
table.Add("urn:oasis:names:tc:opendocument:xmlns:style:1.0");
|
||||
table.Add("urn:oasis:names:tc:opendocument:xmlns:text:1.0");
|
||||
table.Add("urn:oasis:names:tc:opendocument:xmlns:table:1.0");
|
||||
table.Add("urn:oasis:names:tc:opendocument:xmlns:drawing:1.0");
|
||||
table.Add("urn:oasis:names:tc:opendocument:xmlns:xsl-fo-compatible:1.0");
|
||||
table.Add("urn:oasis:names:tc:opendocument:xmlns:meta:1.0");
|
||||
table.Add("urn:oasis:names:tc:opendocument:xmlns:datastyle:1.0");
|
||||
table.Add("urn:oasis:names:tc:opendocument:xmlns:svg-compatible:1.0");
|
||||
table.Add("urn:oasis:names:tc:opendocument:xmlns:chart:1.0");
|
||||
table.Add("urn:oasis:names:tc:opendocument:xmlns:dr3d:1.0");
|
||||
table.Add("urn:oasis:names:tc:opendocument:xmlns:form:1.0");
|
||||
table.Add("urn:oasis:names:tc:opendocument:xmlns:script:1.0");
|
||||
table.Add("urn:oasis:names:tc:opendocument:xmlns:presentation:1.0");
|
||||
|
||||
table.Add("http://purl.org/dc/elements/1.1/");
|
||||
table.Add("http://openoffice.org/2004/office");
|
||||
table.Add("http://openoffice.org/2004/writer");
|
||||
table.Add("http://openoffice.org/2004/calc");
|
||||
table.Add("http://www.w3.org/2001/xml-events");
|
||||
table.Add("http://www.w3.org/2002/xforms");
|
||||
table.Add("http://www.w3.org/2001/XMLSchema");
|
||||
table.Add("http://www.w3.org/2001/XMLSchema-instance");
|
||||
|
||||
// OOo 3.0.x
|
||||
table.Add("urn:oasis:names:tc:opendocument:xmlns:of:1.2");
|
||||
table.Add("urn:openoffice:names:experimental:ooo-ms-interop:xmlns:field:1.0");
|
||||
table.Add("urn:openoffice:names:experimental:ooxml-odf-interop:xmlns:field:1.0");
|
||||
table.Add("urn:openoffice:names:experimental:ooxml-odf-interop:xmlns:form:1.0");
|
||||
|
||||
table.Add("http://openoffice.org/2005/report");
|
||||
table.Add("http://docs.oasis-open.org/opendocument/meta/rdfa#");
|
||||
|
||||
// OOo 3.2.x
|
||||
table.Add("http://www.w3.org/2003/g/data-view#");
|
||||
table.Add("http://www.w3.org/1999/xhtml");
|
||||
table.Add("http://openoffice.org/2009/table");
|
||||
|
||||
// OOo 3.3.x
|
||||
table.Add("http://openoffice.org/2010/chart");
|
||||
|
||||
// Other OpenDocument v 1.1
|
||||
table.Add("urn:oasis:names:tc:opendocument:xmlns:config:1.0");
|
||||
table.Add("urn:oasis:names:tc:opendocument:xmlns:animation:1.0");
|
||||
table.Add("urn:oasis:names:tc:opendocument:xmlns:data style:1.0");
|
||||
table.Add("urn:oasis:names:tc:opendocument:xmlns:manifest:1.0");
|
||||
table.Add("urn:oasis:names:tc:opendocument:xmlns:smil-compatible:1.0");
|
||||
|
||||
// Symphony 1.3
|
||||
table.Add("http://www.ibm.com/xmlns/prodtools");
|
||||
|
||||
// CleverAge ODF Add-in for Microsoft Office 3.0.5224.0 (11.0.8302)
|
||||
table.Add("http://purl.org/dc/terms/");
|
||||
|
||||
// KOffice 1.6.3
|
||||
table.Add("http://www.koffice.org/2005/");
|
||||
|
||||
// Microsoft Excel Formulas in ODF
|
||||
table.Add("http://schemas.microsoft.com/office/excel/formula");
|
||||
|
||||
// Gnumeric ODF extensions
|
||||
table.Add("http://www.gnumeric.org/odf-extension/1.0");
|
||||
|
||||
// LOCALC
|
||||
table.Add("urn:org:documentfoundation:names:experimental:office:xmlns:loext:1.0");
|
||||
table.Add("urn:org:documentfoundation:names:experimental:calc:xmlns:calcext:1.0");
|
||||
|
||||
return table;
|
||||
}
|
||||
|
||||
private static XmlDocumentType CreateOpenDocumentDTD(XmlDocument doc)
|
||||
{
|
||||
// Root node
|
||||
XmlDocumentType docType = doc.CreateDocumentType("meta", null, null, null);
|
||||
|
||||
// OpenDocument TAGS
|
||||
docType.AppendChild(doc.CreateElement("generator"));
|
||||
docType.AppendChild(doc.CreateElement("title"));
|
||||
docType.AppendChild(doc.CreateElement("description"));
|
||||
docType.AppendChild(doc.CreateElement("subject"));
|
||||
docType.AppendChild(doc.CreateElement("keyword"));
|
||||
docType.AppendChild(doc.CreateElement("initial-creator"));
|
||||
docType.AppendChild(doc.CreateElement("creator"));
|
||||
docType.AppendChild(doc.CreateElement("printed-by"));
|
||||
docType.AppendChild(doc.CreateElement("creation-date"));
|
||||
docType.AppendChild(doc.CreateElement("date"));
|
||||
docType.AppendChild(doc.CreateElement("print-date"));
|
||||
docType.AppendChild(doc.CreateElement("template"));
|
||||
docType.AppendChild(doc.CreateElement("auto-reload"));
|
||||
docType.AppendChild(doc.CreateElement("hyperlink-behaviour"));
|
||||
docType.AppendChild(doc.CreateElement("document-statistic"));
|
||||
docType.AppendChild(doc.CreateElement("language"));
|
||||
docType.AppendChild(doc.CreateElement("editing-duration"));
|
||||
docType.AppendChild(doc.CreateElement("user-defined"));
|
||||
|
||||
return docType;
|
||||
}
|
||||
|
||||
public static void gsf_opendoc_metadata_subtree_free(GsfXMLIn xin, object old_state)
|
||||
{
|
||||
GsfOOMetaIn state = old_state as GsfOOMetaIn;
|
||||
if (state.Keywords != null)
|
||||
state.MetaData.Insert(GSF_META_NAME_KEYWORDS, state.Keywords);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Extend <paramref name="doc"/>> so that it can parse a subtree in OpenDoc metadata format
|
||||
/// </summary>
|
||||
public static void gsf_doc_meta_data_odf_subtree(GsfDocMetaData md, GsfXMLIn doc)
|
||||
{
|
||||
if (md == null)
|
||||
return;
|
||||
|
||||
XmlDocument document = new XmlDocument(CreateOpenDocumentNamespaces());
|
||||
XmlDocumentType docType = CreateOpenDocumentDTD(document);
|
||||
document.AppendChild(docType);
|
||||
|
||||
GsfOOMetaIn state = new GsfOOMetaIn
|
||||
{
|
||||
MetaData = md,
|
||||
Type = null,
|
||||
Doc = document,
|
||||
};
|
||||
|
||||
doc.PushState(state.Doc, state, gsf_opendoc_metadata_subtree_free, null);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Extend <paramref name="xin"/> so that it can parse a subtree in OpenDoc metadata format
|
||||
/// The current user_state must be a GsfOOMetaIn!
|
||||
/// </summary>
|
||||
public static void gsf_opendoc_metadata_subtree_internal(GsfXMLIn xin, string[] attrs)
|
||||
{
|
||||
GsfOOMetaIn mi = (GsfOOMetaIn)xin.UserState;
|
||||
if (mi.Doc == null)
|
||||
{
|
||||
XmlDocument document = new XmlDocument(CreateOpenDocumentNamespaces());
|
||||
XmlDocumentType docType = CreateOpenDocumentDTD(document);
|
||||
document.AppendChild(docType);
|
||||
mi.Doc = document;
|
||||
}
|
||||
|
||||
xin.PushState(mi.Doc, null, null, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* gsf_doc_meta_data_read_from_odf:
|
||||
* @md: #GsfDocMetaData
|
||||
* @input: #GsfInput
|
||||
*
|
||||
* Read an OpenDocument metadata stream from @input and store the properties
|
||||
* into @md. Overwrite any existing properties with the same id.
|
||||
*
|
||||
* Since: 1.14.24
|
||||
*
|
||||
* Returns: (transfer full): a #Exception if there is a problem.
|
||||
**/
|
||||
public static Exception gsf_doc_meta_data_read_from_odf(GsfDocMetaData md, GsfInput input)
|
||||
{
|
||||
GsfOOMetaIn state = new GsfOOMetaIn
|
||||
{
|
||||
MetaData = md,
|
||||
Keywords = null,
|
||||
Err = null,
|
||||
Name = null,
|
||||
Doc = null,
|
||||
};
|
||||
|
||||
GsfXMLInParser parser = new GsfXMLInParser(input as GsfXMLIn);
|
||||
|
||||
state.Doc = new XmlDocument(CreateOpenDocumentNamespaces());
|
||||
XmlDocumentType docType = CreateOpenDocumentDTD(state.Doc);
|
||||
state.Doc.AppendChild(docType);
|
||||
|
||||
while (parser.Read()) { }
|
||||
|
||||
parser.Close();
|
||||
|
||||
if (state.Keywords != null)
|
||||
state.Keywords.Add(md);
|
||||
|
||||
return state.Err;
|
||||
}
|
||||
|
||||
/**
|
||||
* gsf_opendoc_metadata_read: (skip)
|
||||
* @input: #GsfInput
|
||||
* @md: #GsfDocMetaData
|
||||
*
|
||||
* Read an OpenDocument metadata stream from @input and store the properties
|
||||
* into @md. Overwrite any existing properties with the same id.
|
||||
*
|
||||
* Deprecated: 1.14.24, use gsf_doc_meta_data_read_from_odf
|
||||
*
|
||||
* Returns: (transfer full): a #Exception if there is a problem.
|
||||
**/
|
||||
public static Exception gsf_opendoc_metadata_read(GsfInput input, GsfDocMetaData md) => gsf_doc_meta_data_read_from_odf(md, input);
|
||||
|
||||
// Shared by all instances and never freed
|
||||
private static readonly Dictionary<string, string> od_prop_name_map = new Dictionary<string, string>
|
||||
{
|
||||
{ GSF_META_NAME_GENERATOR, "meta:generator" },
|
||||
{ GSF_META_NAME_TITLE, "dc:title" },
|
||||
{ GSF_META_NAME_DESCRIPTION, "dc:description" },
|
||||
{ GSF_META_NAME_SUBJECT, "dc:subject" },
|
||||
{ GSF_META_NAME_INITIAL_CREATOR,"meta:initial-creator" },
|
||||
{ GSF_META_NAME_CREATOR, "dc:creator" },
|
||||
{ GSF_META_NAME_PRINTED_BY, "meta:printed-by" },
|
||||
{ GSF_META_NAME_DATE_CREATED, "meta:creation-date" },
|
||||
{ GSF_META_NAME_DATE_MODIFIED, "dc:date" },
|
||||
{ GSF_META_NAME_LAST_PRINTED, "meta:print-date" },
|
||||
{ GSF_META_NAME_LANGUAGE, "dc:language" },
|
||||
{ GSF_META_NAME_REVISION_COUNT, "meta:editing-cycles" },
|
||||
{ GSF_META_NAME_EDITING_DURATION, "meta:editing-duration" }
|
||||
};
|
||||
|
||||
public static string od_map_prop_name(string name)
|
||||
{
|
||||
if (!od_prop_name_map.ContainsKey(name))
|
||||
return null;
|
||||
|
||||
return od_prop_name_map[name];
|
||||
}
|
||||
|
||||
/*
|
||||
meta:page-count GSF_META_NAME_PAGE_COUNT
|
||||
meta:table-count GSF_META_NAME_TABLE_COUNT:
|
||||
meta:draw-count
|
||||
meta:image-count GSF_META_NAME_IMAGE_COUNT:
|
||||
meta:ole-object-count GSF_META_NAME_OBJECT_COUNT:
|
||||
meta:paragraph-count GSF_META_NAME_PARAGRAPH_COUNT:
|
||||
meta:word-count
|
||||
meta:character-count GSF_META_NAME_CHARACTER_COUNT
|
||||
meta:row-count GSF_META_NAME_LINE_COUNT:
|
||||
meta:frame-count
|
||||
meta:sentence-count
|
||||
meta:syllable-count
|
||||
meta:non-whitespace-character-count
|
||||
|
||||
meta:page-count
|
||||
GSF_META_NAME_SPREADSHEET_COUNT
|
||||
meta:table-count
|
||||
GSF_META_NAME_TABLE_COUNT:
|
||||
meta:image-count
|
||||
* GSF_META_NAME_IMAGE_COUNT:
|
||||
meta:cell-count
|
||||
GSF_META_NAME_CELL_COUNT
|
||||
meta:object-count
|
||||
GSF_META_NAME_OBJECT_COUNT:
|
||||
|
||||
meta:page-count
|
||||
GSF_META_NAME_SLIDE_COUNT:
|
||||
meta:image-count
|
||||
GSF_META_NAME_IMAGE_COUNT:
|
||||
meta:object-count
|
||||
GSF_META_NAME_OBJECT_COUNT:
|
||||
*/
|
||||
|
||||
/// <summary>
|
||||
/// ODF does not like "t" and "f" which we use normally
|
||||
/// </summary>
|
||||
public static void gsf_xml_out_add_gvalue_for_odf(this GsfXMLOut xout, string id, object val)
|
||||
{
|
||||
if (val is bool b)
|
||||
xout.AddString(id, b ? "true" : "false");
|
||||
else
|
||||
xout.AddValue(id, val);
|
||||
}
|
||||
|
||||
static void meta_write_props_user_defined(string prop_name, object val, GsfXMLOut output)
|
||||
{
|
||||
string type_name = null;
|
||||
|
||||
output.StartElement("meta:user-defined");
|
||||
output.AddStringUnchecked("meta:name", prop_name);
|
||||
|
||||
if (null == val)
|
||||
{
|
||||
output.EndElement();
|
||||
return;
|
||||
}
|
||||
|
||||
if (val.GetType() == typeof(char))
|
||||
type_name = "string";
|
||||
else if (val.GetType() == typeof(byte))
|
||||
type_name = "string";
|
||||
else if (val.GetType() == typeof(string))
|
||||
type_name = "string";
|
||||
else if (val.GetType().IsEnum)
|
||||
type_name = "string";
|
||||
else if (val.GetType() == typeof(bool))
|
||||
type_name = "boolean";
|
||||
else if (val.GetType() == typeof(int))
|
||||
type_name = "float";
|
||||
else if (val.GetType() == typeof(uint))
|
||||
type_name = "float";
|
||||
else if (val.GetType() == typeof(long))
|
||||
type_name = "float";
|
||||
else if (val.GetType() == typeof(ulong))
|
||||
type_name = "float";
|
||||
else if (val.GetType() == typeof(float))
|
||||
type_name = "float";
|
||||
else if (val.GetType() == typeof(double))
|
||||
type_name = "float";
|
||||
else if (val.GetType() == typeof(DateTime))
|
||||
type_name = "date";
|
||||
|
||||
if (type_name != null)
|
||||
output.AddStringUnchecked("meta:value-type", type_name);
|
||||
if (val != null)
|
||||
output.gsf_xml_out_add_gvalue_for_odf(null, val);
|
||||
|
||||
output.EndElement();
|
||||
}
|
||||
|
||||
static void meta_write_props(string prop_name, GsfDocProp prop, GsfXMLOut output)
|
||||
{
|
||||
string mapped_name;
|
||||
object val = prop.Value;
|
||||
|
||||
// Handle specially
|
||||
if (prop_name == GSF_META_NAME_KEYWORDS)
|
||||
{
|
||||
// OLE2 stores a single string, with no obvious
|
||||
// standard for seperator
|
||||
if (val.GetType() == typeof(string))
|
||||
{
|
||||
string str = (string)val;
|
||||
if (!string.IsNullOrEmpty(str))
|
||||
{
|
||||
output.StartElement("meta:keyword");
|
||||
output.AddString(null, str);
|
||||
output.EndElement();
|
||||
}
|
||||
}
|
||||
else if (val is List<GsfDocProp> list)
|
||||
{
|
||||
for (int i = 0; i < list.Count; i++)
|
||||
{
|
||||
string str = list[i].Name;
|
||||
output.StartElement("meta:keyword");
|
||||
output.AddString(null, str);
|
||||
output.EndElement();
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if ((mapped_name = od_map_prop_name(prop_name)) == null)
|
||||
{
|
||||
if (val is List<GsfDocProp> list)
|
||||
{
|
||||
for (int i = 0; i < list.Count; i++)
|
||||
{
|
||||
string new_name = $"GSF_DOCPROP_VECTOR:{i:4}:{prop_name}";
|
||||
meta_write_props_user_defined(new_name, list[i], output);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
meta_write_props_user_defined(prop_name, val, output);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// Standardized ODF meta items
|
||||
output.StartElement(mapped_name);
|
||||
if (val != null)
|
||||
gsf_xml_out_add_gvalue_for_odf(output, null, val);
|
||||
|
||||
output.EndElement();
|
||||
}
|
||||
|
||||
/**
|
||||
* gsf_doc_meta_data_write_to_odf:
|
||||
* @md: #GsfDocMetaData
|
||||
* @output: (type GsfXMLOut): a pointer to a #GsfOutput.
|
||||
*
|
||||
* Since: 1.14.24
|
||||
*
|
||||
* Returns: %true if no error occured.
|
||||
**/
|
||||
static bool gsf_doc_meta_data_write_to_odf(GsfDocMetaData[] md, object output)
|
||||
{
|
||||
string ver_str;
|
||||
|
||||
if (output == null)
|
||||
return false;
|
||||
|
||||
// For compatibility we take a GsfXMLOut argument. It really
|
||||
// ought to be a GsfODFOut.
|
||||
GsfXMLOut xout = output as GsfXMLOut;
|
||||
GsfODFOut oout = (output is GsfODFOut) ? output as GsfODFOut : null;
|
||||
|
||||
ver_str = oout != null ? oout.GetVersionString() : GetVersionString();
|
||||
|
||||
xout.StartElement($"{OFFICE}document-meta");
|
||||
xout.AddStringUnchecked("xmlns:office", "urn:oasis:names:tc:opendocument:xmlns:office:1.0");
|
||||
xout.AddStringUnchecked("xmlns:xlink", "http://www.w3.org/1999/xlink");
|
||||
xout.AddStringUnchecked("xmlns:dc", "http://purl.org/dc/elements/1.1/");
|
||||
xout.AddStringUnchecked("xmlns:meta", "urn:oasis:names:tc:opendocument:xmlns:meta:1.0");
|
||||
xout.AddStringUnchecked("xmlns:ooo", "http://openoffice.org/2004/office");
|
||||
xout.AddStringUnchecked("office:version", ver_str);
|
||||
|
||||
xout.StartElement($"{OFFICE}meta");
|
||||
foreach (GsfDocMetaData data in md)
|
||||
{
|
||||
foreach (KeyValuePair<string, GsfDocProp> kvp in data.Table)
|
||||
{
|
||||
meta_write_props(kvp.Key, kvp.Value, xout);
|
||||
}
|
||||
}
|
||||
xout.EndElement();
|
||||
|
||||
xout.EndElement();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
723
BurnOutSharp/External/libgsf/GsfOpenPkgUtils.cs
vendored
Normal file
723
BurnOutSharp/External/libgsf/GsfOpenPkgUtils.cs
vendored
Normal file
@@ -0,0 +1,723 @@
|
||||
/* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
|
||||
/*
|
||||
* gsf-open-pkg-utils.c: Utilities for handling Open Package zip files
|
||||
* from MS Office 2007 or XPS.
|
||||
*
|
||||
* Copyright (C) 2006-2007 Jody Goldberg (jody@gnome.org)
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of version 2.1 of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301
|
||||
* USA
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Xml;
|
||||
using LibGSF.Input;
|
||||
using LibGSF.Output;
|
||||
|
||||
namespace LibGSF
|
||||
{
|
||||
public class GsfOpenPkgRel
|
||||
{
|
||||
#region Properties
|
||||
|
||||
public string Id { get; set; } = null;
|
||||
|
||||
public string Type { get; set; } = null;
|
||||
|
||||
public string Target { get; set; } = null;
|
||||
|
||||
public bool IsExtern { get; set; } = false;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Functions
|
||||
|
||||
/// <returns>A new GsfInput which the called needs to unref, or null and sets <paramref name="err"/></returns>
|
||||
public static GsfInput OpenRelatedPackage(GsfInput opkg, GsfOpenPkgRel rel, ref Exception err)
|
||||
{
|
||||
if (opkg == null)
|
||||
return null;
|
||||
if (rel == null)
|
||||
return null;
|
||||
|
||||
GsfInput res = null;
|
||||
GsfInfile prev_parent;
|
||||
|
||||
// References from the root use children of opkg
|
||||
// References from a child are relative to siblings of opkg
|
||||
GsfInfile parent = opkg.Name != null ? opkg.Container : opkg as GsfInfile;
|
||||
|
||||
string target = rel.Target;
|
||||
if (target[0] == '/')
|
||||
{
|
||||
target = target.Substring(1);
|
||||
|
||||
// Handle absolute references by going as far up as we can.
|
||||
while (true)
|
||||
{
|
||||
GsfInfile next_parent = parent.Container;
|
||||
if (next_parent != null && next_parent.GetType() == parent.GetType())
|
||||
parent = next_parent;
|
||||
else
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
string[] elems = rel.Target.Split('/');
|
||||
for (int i = 0; i < elems.Length && elems[i] != null && parent != null; i++)
|
||||
{
|
||||
if (elems[i] == "." || elems[i][0] == '\0')
|
||||
continue; // Ignore '.' and empty
|
||||
|
||||
prev_parent = parent;
|
||||
if (elems[i] == "..")
|
||||
{
|
||||
parent = parent.Container;
|
||||
res = null; // Only return newly created children
|
||||
if (parent != null)
|
||||
{
|
||||
// Check for attempt to gain access outside the zip file
|
||||
if (parent.GetType() != prev_parent.GetType())
|
||||
{
|
||||
Console.Error.WriteLine("Broken file: relation access outside container");
|
||||
parent = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
res = parent.ChildByName(elems[i], ref err);
|
||||
if (elems[i + 1] != null)
|
||||
{
|
||||
if (res == null || !(res is GsfInfile))
|
||||
return null;
|
||||
|
||||
parent = res as GsfInfile;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Finds <paramref name="opkg"/>'s relation with @id
|
||||
/// </summary>
|
||||
/// <param name="id">Identifier.</param>
|
||||
/// <returns>A GsfOpenPkgRel or null</returns>
|
||||
/// <remarks>
|
||||
/// New in 1.14.6
|
||||
///
|
||||
/// Skipping because gsf_open_pkg_rel_get_type() does not return a GType.
|
||||
/// </remarks>
|
||||
public static GsfOpenPkgRel LookupRelationById(GsfInput opkg, string id)
|
||||
{
|
||||
GsfOpenPkgRels rels = GsfOpenPkgRels.LookupRelations(opkg);
|
||||
if (rels == null)
|
||||
return null;
|
||||
|
||||
if (!rels.RelationsById.ContainsKey(id))
|
||||
return null;
|
||||
|
||||
return rels.RelationsById[id];
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Finds _a_ relation of <paramref name="opkg"/> with <paramref name="type"/> (no order is guaranteed)
|
||||
/// </summary>
|
||||
/// <param name="type">Target</param>
|
||||
/// <returns>A GsfOpenPkgRel or null</returns>
|
||||
/// <remarks>
|
||||
/// New in 1.14.6
|
||||
///
|
||||
/// Skipping because gsf_open_pkg_rel_get_type() does not return a GType.
|
||||
/// </remarks>
|
||||
public static GsfOpenPkgRel LookupRelationByType(GsfInput opkg, string type)
|
||||
{
|
||||
GsfOpenPkgRels rels = GsfOpenPkgRels.LookupRelations(opkg);
|
||||
if (rels == null)
|
||||
return null;
|
||||
|
||||
if (!rels.RelationsByType.ContainsKey(type))
|
||||
return null;
|
||||
|
||||
return rels.RelationsByType[type];
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
public class GsfOpenPkgRels
|
||||
{
|
||||
#region Constants
|
||||
|
||||
/// <summary>
|
||||
/// Generated based on:
|
||||
/// http://www.oasis-open.org/committees/download.php/12572/OpenDocument-v1.0-os.pdf
|
||||
/// and OpenDocument-v1.1.pdf
|
||||
/// </summary>
|
||||
private static XmlNameTable CREATE_NAMESPACES()
|
||||
{
|
||||
NameTable table = new NameTable();
|
||||
|
||||
table.Add("http://schemas.openxmlformats.org/package/2006/relationships");
|
||||
|
||||
return table;
|
||||
}
|
||||
|
||||
private static XmlDocumentType CREATE_DTD(XmlDocument doc)
|
||||
{
|
||||
// Root node
|
||||
XmlDocumentType docType = doc.CreateDocumentType(null, null, null, null);
|
||||
|
||||
docType.AppendChild(doc.CreateElement("Relationships"));
|
||||
docType["Relationships"].AppendChild(doc.CreateElement("Relationship"));
|
||||
|
||||
return docType;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Properties
|
||||
|
||||
public Dictionary<string, GsfOpenPkgRel> RelationsById { get; set; } = new Dictionary<string, GsfOpenPkgRel>();
|
||||
|
||||
public Dictionary<string, GsfOpenPkgRel> RelationsByType { get; set; } = new Dictionary<string, GsfOpenPkgRel>();
|
||||
|
||||
#endregion
|
||||
|
||||
#region Constructor
|
||||
|
||||
/// <summary>
|
||||
/// Private constructor
|
||||
/// </summary>
|
||||
private GsfOpenPkgRels() { }
|
||||
|
||||
#endregion
|
||||
|
||||
#region Functions
|
||||
|
||||
public static GsfOpenPkgRels LookupRelations(GsfInput opkg)
|
||||
{
|
||||
if (opkg == null)
|
||||
return null;
|
||||
|
||||
GsfOpenPkgRels rels = opkg.Relations;
|
||||
if (rels == null)
|
||||
{
|
||||
string part_name = opkg.Name;
|
||||
|
||||
GsfInput rel_stream;
|
||||
if (part_name != null)
|
||||
{
|
||||
GsfInfile container = opkg.Container;
|
||||
if (container == null)
|
||||
return null;
|
||||
|
||||
string rel_name = $"{part_name}.rels";
|
||||
rel_stream = container.ChildByVariableName("_rels", rel_name);
|
||||
}
|
||||
else // The root
|
||||
{
|
||||
rel_stream = (opkg as GsfInfile).ChildByVariableName("_rels", ".rels");
|
||||
}
|
||||
|
||||
XmlDocument rel_doc;
|
||||
if (rel_stream != null)
|
||||
{
|
||||
rels = new GsfOpenPkgRels
|
||||
{
|
||||
RelationsById = new Dictionary<string, GsfOpenPkgRel>(),
|
||||
RelationsByType = new Dictionary<string, GsfOpenPkgRel>(),
|
||||
};
|
||||
|
||||
rel_doc = new XmlDocument(CREATE_NAMESPACES());
|
||||
XmlDocumentType docType = CREATE_DTD(rel_doc);
|
||||
rel_doc.AppendChild(docType);
|
||||
|
||||
// TODO: Enable parsing
|
||||
//rel_doc.Parse(rel_stream, rels);
|
||||
}
|
||||
|
||||
opkg.Relations = rels;
|
||||
}
|
||||
|
||||
return rels;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Utilities
|
||||
|
||||
private static void OpenPkgRelBegin(GsfXMLIn xin, string[] attrs)
|
||||
{
|
||||
GsfOpenPkgRels rels = xin.UserState as GsfOpenPkgRels;
|
||||
string id = null;
|
||||
string type = null;
|
||||
string target = null;
|
||||
bool is_extern = false;
|
||||
|
||||
for (int i = 0; i < attrs.Length && attrs[i] != null && attrs[i + 1] != null; i += 2)
|
||||
{
|
||||
switch (attrs[i])
|
||||
{
|
||||
case "Id":
|
||||
id = attrs[i + 1];
|
||||
break;
|
||||
case "Type":
|
||||
type = attrs[i + 1];
|
||||
break;
|
||||
case "Target":
|
||||
target = attrs[i + 1];
|
||||
break;
|
||||
case "TargetMode":
|
||||
is_extern = attrs[i + 1] == "External";
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (id == null)
|
||||
{
|
||||
Console.Error.WriteLine("Broken relation: missing id");
|
||||
id = "?";
|
||||
}
|
||||
|
||||
if (type == null)
|
||||
{
|
||||
Console.Error.WriteLine("Broken relation: missing type");
|
||||
type = "?";
|
||||
}
|
||||
|
||||
if (target == null)
|
||||
{
|
||||
Console.Error.WriteLine("Broken relation: missing target");
|
||||
target = "?";
|
||||
}
|
||||
|
||||
GsfOpenPkgRel rel = new GsfOpenPkgRel
|
||||
{
|
||||
Id = id,
|
||||
Type = type,
|
||||
Target = target,
|
||||
IsExtern = is_extern,
|
||||
};
|
||||
|
||||
// Make sure we don't point to a freed rel in the type hash.
|
||||
rels.RelationsByType[rel.Type] = rel;
|
||||
|
||||
// This will free a duplicate rel, so do this last.
|
||||
rels.RelationsById[rel.Id] = rel;
|
||||
}
|
||||
|
||||
#endregion
|
||||
};
|
||||
|
||||
public class GsfOutfileOpenPkg : GsfOutfile
|
||||
{
|
||||
#region Properties
|
||||
|
||||
public GsfOutput Sink { get; set; } = null;
|
||||
|
||||
public bool IsDir { get; set; } = false;
|
||||
|
||||
public string ContentType { get; set; } = null;
|
||||
|
||||
public List<GsfOutfileOpenPkg> Children { get; set; } = null;
|
||||
|
||||
public List<GsfOpenPkgRel> Relations { get; set; } = null;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Constructor and Destructor
|
||||
|
||||
/// <summary>
|
||||
/// Private constructor
|
||||
/// </summary>
|
||||
private GsfOutfileOpenPkg() { }
|
||||
|
||||
/// <summary>
|
||||
/// Convenience routine to create a GsfOutfileOpenPkg inside <paramref name="sink"/>.
|
||||
/// </summary>
|
||||
/// <param name="sink"></param>
|
||||
/// <returns>A GsfOutfile that the caller is responsible for.</returns>
|
||||
public static GsfOutfileOpenPkg Create(GsfOutfile sink)
|
||||
{
|
||||
return new GsfOutfileOpenPkg
|
||||
{
|
||||
Sink = sink,
|
||||
IsDir = true,
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Destructor
|
||||
/// </summary>
|
||||
~GsfOutfileOpenPkg()
|
||||
{
|
||||
if (Sink != null)
|
||||
Sink = null;
|
||||
|
||||
ContentType = null;
|
||||
|
||||
Children.Clear();
|
||||
Children = null;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Functions
|
||||
|
||||
public new bool Close()
|
||||
{
|
||||
GsfOutput dir;
|
||||
bool res = false;
|
||||
string rels_name;
|
||||
|
||||
if (Sink == null || Sink.IsClosed)
|
||||
return true;
|
||||
|
||||
// Generate [Content_types].xml when we close the root dir
|
||||
if (Name == null)
|
||||
{
|
||||
GsfOutput output = (Sink as GsfOutfile).NewChild("[Content_Types].xml", false);
|
||||
GsfXMLOut xml = GsfXMLOut.Create(output);
|
||||
|
||||
xml.StartElement("Types");
|
||||
xml.AddStringUnchecked("xmlns", "http://schemas.openxmlformats.org/package/2006/content-types");
|
||||
xml.WriteContentDefault("rels", "application/vnd.openxmlformats-package.relationships+xml");
|
||||
xml.WriteContentDefault("xlbin", "application/vnd.openxmlformats-officedocument.spreadsheetml.printerSettings");
|
||||
xml.WriteContentDefault("xml", "application/xml");
|
||||
xml.WriteContentDefault("vml", "application/vnd.openxmlformats-officedocument.vmlDrawing");
|
||||
xml.WriteContentOverride(this, "/");
|
||||
xml.EndElement();
|
||||
|
||||
output.Close();
|
||||
|
||||
dir = Sink;
|
||||
rels_name = ".rels";
|
||||
}
|
||||
else
|
||||
{
|
||||
res = Sink.Close();
|
||||
dir = Sink.Container;
|
||||
rels_name = $"{Name}.rels";
|
||||
}
|
||||
|
||||
if (Relations != null)
|
||||
{
|
||||
dir = (dir as GsfOutfile).NewChild("_rels", true);
|
||||
GsfOutput rels = (dir as GsfOutfile).NewChild(rels_name, false);
|
||||
GsfXMLOut xml = GsfXMLOut.Create(rels);
|
||||
|
||||
xml.StartElement("Relationships");
|
||||
xml.AddStringUnchecked("xmlns", "http://schemas.openxmlformats.org/package/2006/relationships");
|
||||
|
||||
foreach (GsfOpenPkgRel rel in Relations)
|
||||
{
|
||||
xml.StartElement("Relationship");
|
||||
xml.AddString("Id", rel.Id);
|
||||
xml.AddString("Type", rel.Type);
|
||||
xml.AddString("Target", rel.Target);
|
||||
if (rel.IsExtern)
|
||||
xml.AddStringUnchecked("TargetMode", "External");
|
||||
|
||||
xml.EndElement();
|
||||
}
|
||||
|
||||
Relations.Clear();
|
||||
xml.EndElement();
|
||||
rels.Close();
|
||||
}
|
||||
|
||||
// Close the container
|
||||
if (Name == null)
|
||||
return Sink.Close();
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
public string CreateRelation(string target, string type, bool is_extern)
|
||||
{
|
||||
GsfOpenPkgRel rel = new GsfOpenPkgRel
|
||||
{
|
||||
Target = target,
|
||||
Type = type,
|
||||
Id = $"rID{Relations.Count + 1}",
|
||||
IsExtern = is_extern,
|
||||
};
|
||||
|
||||
Relations.Add(rel);
|
||||
return rel.Id;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create a relationship between child and <paramref name="parent"/> of <paramref name="type"/>.
|
||||
/// </summary>
|
||||
/// <param name="type">Target type</param>
|
||||
/// <returns>The relID which the caller does not own but will live as long as <paramref name="parent"/>.</returns>
|
||||
public string RelatePackages(GsfOutfileOpenPkg parent, string type)
|
||||
{
|
||||
int up = -1;
|
||||
GsfOutfile child_dir;
|
||||
|
||||
// Calculate the path from child to parent
|
||||
GsfOutfile parent_dir = parent.IsDir ? parent : parent.Container;
|
||||
do
|
||||
{
|
||||
up++;
|
||||
child_dir = this;
|
||||
while ((child_dir = child_dir.Container) != null)
|
||||
{
|
||||
if (child_dir == parent_dir)
|
||||
goto found; // Break out of both loops
|
||||
}
|
||||
} while ((parent_dir = parent_dir.Container) != null);
|
||||
|
||||
// TODO: Figure out how to best remove this goto
|
||||
found:
|
||||
// Yes prepend is slow, this will never be preformance critical
|
||||
string path = Name;
|
||||
child_dir = this;
|
||||
while ((child_dir = child_dir.Container) != null && child_dir.Name != null && child_dir != parent_dir)
|
||||
{
|
||||
path = $"{child_dir.Name}/{path}";
|
||||
}
|
||||
|
||||
while (up-- != 0)
|
||||
{
|
||||
path = $"../{path}";
|
||||
}
|
||||
|
||||
return parent.CreateRelation(path, type, false);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A convenience wrapper to create a child in <paramref name="dir"/> of <paramref name="content_type"/> then create
|
||||
/// a <paramref name="type"/> relation to <paramref name="parent"/>
|
||||
/// </summary>
|
||||
/// <param name="name">Target name</param>
|
||||
/// <param name="content_type">Non-null content type</param>
|
||||
/// <param name="type">Target type</param>
|
||||
/// <returns>The new part.</returns>
|
||||
public GsfOutput AddRelation(string name, string content_type, GsfOutfile parent, string type)
|
||||
{
|
||||
GsfOutfileOpenPkg part = NewChild(name, false) as GsfOutfileOpenPkg;
|
||||
if (part == null)
|
||||
return null;
|
||||
|
||||
part.ContentType = content_type;
|
||||
part.RelatePackages(parent as GsfOutfileOpenPkg, type);
|
||||
return part;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Add an external relation to parent.
|
||||
/// </summary>
|
||||
/// <param name="target">Target type</param>
|
||||
/// <param name="content_type">Target content</param>
|
||||
/// <returns>
|
||||
/// The id of the relation. The string is
|
||||
/// managed by the parent and should not be changed or freed by the
|
||||
/// caller.
|
||||
/// </returns>
|
||||
public string AddExternalRelation(string target, string content_type) => CreateRelation(target, content_type, true);
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override bool WriteImpl(int num_bytes, byte[] data) => Sink.Write(num_bytes, data);
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override bool SeekImpl(long offset, SeekOrigin whence) => Sink.Seek(offset, whence);
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override GsfOutput NewChild(string name, bool is_dir)
|
||||
{
|
||||
if (!is_dir)
|
||||
return null;
|
||||
|
||||
GsfOutfileOpenPkg child = new GsfOutfileOpenPkg
|
||||
{
|
||||
Name = name,
|
||||
Container = this,
|
||||
IsDir = is_dir,
|
||||
};
|
||||
|
||||
// Holding a ref here is not ideal. It means we won't release any of the
|
||||
// children until the package is closed.
|
||||
Children.Add(child);
|
||||
|
||||
return child;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
public static class GsfOpenPkgUtils
|
||||
{
|
||||
#region Delegates
|
||||
|
||||
public delegate void GsfOpenPkgIter(GsfInput opkg, GsfOpenPkgRel rel, object user_data);
|
||||
|
||||
#endregion
|
||||
|
||||
#region Classes
|
||||
|
||||
private class pkg_iter_data
|
||||
{
|
||||
public GsfInput opkg { get; set; }
|
||||
|
||||
public GsfOpenPkgIter func { get; set; }
|
||||
|
||||
public object user_data { get; set; }
|
||||
};
|
||||
|
||||
#endregion
|
||||
|
||||
#region Functions
|
||||
|
||||
/// <summary>
|
||||
/// Walks each relationship associated with <paramref name="opkg"/> and calls <paramref name="func"/> with <paramref name="user_data"/>.
|
||||
/// </summary>
|
||||
/// <remarks>New in 1.14.9</remarks>
|
||||
public static void ForeachRelation(this GsfInput opkg, GsfOpenPkgIter func, object user_data)
|
||||
{
|
||||
GsfOpenPkgRels rels = GsfOpenPkgRels.LookupRelations(opkg);
|
||||
if (rels == null)
|
||||
return;
|
||||
|
||||
pkg_iter_data dat = new pkg_iter_data
|
||||
{
|
||||
opkg = opkg,
|
||||
func = func,
|
||||
user_data = user_data,
|
||||
};
|
||||
|
||||
foreach (KeyValuePair<string, GsfOpenPkgRel> rel in rels.RelationsById)
|
||||
{
|
||||
ForeachRelationImpl(rel.Key, rel.Value, dat);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Open @opkg's relation <paramref name="id"/>
|
||||
/// </summary>
|
||||
/// <param name="id">Target id</param>
|
||||
/// <returns>A new GsfInput or null, and sets <paramref name="err"/> if possible.</returns>
|
||||
/// <remarks>New in 1.14.7</remarks>
|
||||
public static GsfInput RelationById(this GsfInput opkg, string id, ref Exception err)
|
||||
{
|
||||
GsfOpenPkgRel rel = GsfOpenPkgRel.LookupRelationById(opkg, id);
|
||||
if (rel != null)
|
||||
return GsfOpenPkgRel.OpenRelatedPackage(opkg, rel, ref err);
|
||||
|
||||
err = new Exception($"Unable to find part id='{id}' for '{opkg.Name}'");
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Open one of <paramref name="opkg"/>'s relationships with type=<paramref name="type"/>.
|
||||
/// </summary>
|
||||
/// <param name="type">Target type</param>
|
||||
/// <returns>A new GsfInput or null, and sets <paramref name="err"/> if possible.</returns>
|
||||
/// <remarks>New in 1.14.9</remarks>
|
||||
public static GsfInput RelationByType(this GsfInput opkg, string type, ref Exception err)
|
||||
{
|
||||
GsfOpenPkgRel rel = GsfOpenPkgRel.LookupRelationByType(opkg, type);
|
||||
if (rel != null)
|
||||
return GsfOpenPkgRel.OpenRelatedPackage(opkg, rel, ref err);
|
||||
|
||||
err = new Exception($"Unable to find part with type='{type}' for '{opkg.Name}'");
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Convenience function to parse a related part.
|
||||
/// </summary>
|
||||
/// <param name="id">Target id</param>
|
||||
/// <returns>null on success or an Exception on failure.</returns>
|
||||
public static Exception ParseRelationById(this GsfXMLIn xin, string id, XmlDocumentType dtd, XmlNameTable ns)
|
||||
{
|
||||
if (xin == null)
|
||||
return null;
|
||||
|
||||
GsfInput cur_stream = xin.Input;
|
||||
if (id == null)
|
||||
return new Exception($"Missing id for part in '{cur_stream.Name}'");
|
||||
|
||||
Exception res = null;
|
||||
GsfInput part_stream = RelationById(cur_stream, id, ref res);
|
||||
if (part_stream != null)
|
||||
{
|
||||
XmlDocument doc = new XmlDocument(ns);
|
||||
doc.AppendChild(dtd);
|
||||
|
||||
// TODO: Enable parsing
|
||||
//if (!doc.Parse(part_stream, xin.UserState))
|
||||
// res = new Exception($"Part '{id}' in '{part_stream.Name}' from '{cur_stream.Name}' is corrupt!");
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Utilties
|
||||
|
||||
private static void ForeachRelationImpl(object id, GsfOpenPkgRel rel, pkg_iter_data dat)
|
||||
{
|
||||
dat.func(dat.opkg, rel, dat.user_data);
|
||||
}
|
||||
|
||||
internal static void WriteContentDefault(this GsfXMLOut xml, string ext, string type)
|
||||
{
|
||||
xml.StartElement("Default");
|
||||
xml.AddString("Extension", ext);
|
||||
xml.AddString("ContentType", type);
|
||||
xml.EndElement();
|
||||
}
|
||||
|
||||
internal static void WriteContentOverride(this GsfXMLOut xml, GsfOutfileOpenPkg open_pkg, string baseName)
|
||||
{
|
||||
foreach (GsfOutfileOpenPkg child in open_pkg.Children)
|
||||
{
|
||||
string path;
|
||||
if (child.IsDir)
|
||||
{
|
||||
path = $"{child.Name}/";
|
||||
xml.WriteContentOverride(child, path);
|
||||
}
|
||||
else
|
||||
{
|
||||
path = $"{baseName}{child.Name}";
|
||||
|
||||
// Rels files do need content types, the defaults handle them
|
||||
if (child.ContentType != null)
|
||||
{
|
||||
xml.StartElement("Override");
|
||||
xml.AddString("PartName", path);
|
||||
xml.AddString("ContentType", child.ContentType);
|
||||
xml.EndElement();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Dispose of children here to break link cycles.
|
||||
open_pkg.Children.Clear();
|
||||
open_pkg.Children = null;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
90
BurnOutSharp/External/libgsf/GsfSharedMemory.cs
vendored
Normal file
90
BurnOutSharp/External/libgsf/GsfSharedMemory.cs
vendored
Normal file
@@ -0,0 +1,90 @@
|
||||
/* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
|
||||
/*
|
||||
* gsf-shared-memory.c:
|
||||
*
|
||||
* Copyright (C) 2002-2006 Morten Welinder (terra@diku.dk)
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of version 2.1 of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301
|
||||
* USA
|
||||
*/
|
||||
|
||||
using System;
|
||||
|
||||
namespace LibGSF
|
||||
{
|
||||
public class GsfSharedMemory
|
||||
{
|
||||
#region Properties
|
||||
|
||||
public byte[] Buf { get; set; } = null;
|
||||
|
||||
public long Size { get; set; }
|
||||
|
||||
public bool NeedsFree { get; set; }
|
||||
|
||||
public bool NeedsUnmap { get; set; }
|
||||
|
||||
#endregion
|
||||
|
||||
#region Constructor and Destructor
|
||||
|
||||
/// <summary>
|
||||
/// Private constructor
|
||||
/// </summary>
|
||||
private GsfSharedMemory() { }
|
||||
|
||||
public static GsfSharedMemory Create(byte[] buf, long size, bool needs_free)
|
||||
{
|
||||
return new GsfSharedMemory
|
||||
{
|
||||
Buf = buf,
|
||||
Size = size,
|
||||
NeedsFree = needs_free,
|
||||
NeedsUnmap = false,
|
||||
};
|
||||
}
|
||||
|
||||
public static GsfSharedMemory CreateMemoryMapped(byte[] buf, long size)
|
||||
{
|
||||
int msize = (int)size;
|
||||
if (msize != size)
|
||||
{
|
||||
Console.Error.WriteLine("Memory buffer size too large");
|
||||
return null;
|
||||
}
|
||||
else
|
||||
{
|
||||
GsfSharedMemory mem = Create(buf, size, false);
|
||||
mem.NeedsUnmap = true;
|
||||
return mem;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Destructor
|
||||
/// </summary>
|
||||
~GsfSharedMemory()
|
||||
{
|
||||
if (Buf != null)
|
||||
{
|
||||
if (NeedsFree)
|
||||
Buf = null;
|
||||
//else if (NeedsUnmap)
|
||||
//UnmapViewOfFile(mem.buf);
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
391
BurnOutSharp/External/libgsf/GsfZipImpl.cs
vendored
Normal file
391
BurnOutSharp/External/libgsf/GsfZipImpl.cs
vendored
Normal file
@@ -0,0 +1,391 @@
|
||||
/* THIS IS NOT INSTALLED */
|
||||
|
||||
/*
|
||||
* gsf-zip-impl.h:
|
||||
*
|
||||
* Copyright (C) 2002-2006 Tambet Ingo (tambet@ximian.com)
|
||||
* Copyright (C) 2014 Morten Welinder (terra@gnome.org)
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of version 2.1 of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301
|
||||
* USA
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace LibGSF
|
||||
{
|
||||
#region Enums
|
||||
|
||||
/// <summary>
|
||||
/// A few well-defined extra-field tags.
|
||||
/// </summary>
|
||||
public enum ExtraFieldTags : ushort
|
||||
{
|
||||
ZIP_DIRENT_EXTRA_FIELD_ZIP64 = 0x0001,
|
||||
|
||||
/// <summary>
|
||||
/// "II" -- gsf defined
|
||||
/// </summary>
|
||||
ZIP_DIRENT_EXTRA_FIELD_IGNORE = 0x4949,
|
||||
|
||||
/// <summary>
|
||||
/// "UT"
|
||||
/// </summary>
|
||||
ZIP_DIRENT_EXTRA_FIELD_UNIXTIME = 0x5455,
|
||||
|
||||
/// <summary>
|
||||
/// "ux"
|
||||
/// </summary>
|
||||
ZIP_DIRENT_EXTRA_FIELD_UIDGID = 0x7875
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// OS codes. There are plenty, but this is all we need.
|
||||
/// </summary>
|
||||
public enum OSCodes
|
||||
{
|
||||
ZIP_OS_MSDOS = 0,
|
||||
ZIP_OS_UNIX = 3
|
||||
};
|
||||
|
||||
/// <remarks>From gsf-outfile-zip.h</remarks>
|
||||
public enum GsfZipCompressionMethod
|
||||
{
|
||||
/// <summary>
|
||||
/// Supported for export
|
||||
/// </summary>
|
||||
GSF_ZIP_STORED = 0,
|
||||
GSF_ZIP_SHRUNK = 1,
|
||||
GSF_ZIP_REDUCEDx1 = 2,
|
||||
GSF_ZIP_REDUCEDx2 = 3,
|
||||
GSF_ZIP_REDUCEDx3 = 4,
|
||||
GSF_ZIP_REDUCEDx4 = 5,
|
||||
GSF_ZIP_IMPLODED = 6,
|
||||
GSF_ZIP_TOKENIZED = 7,
|
||||
|
||||
/// <summary>
|
||||
/// Supported for export
|
||||
/// </summary>
|
||||
GSF_ZIP_DEFLATED = 8,
|
||||
GSF_ZIP_DEFLATED_BETTER = 9,
|
||||
GSF_ZIP_IMPLODED_BETTER = 10
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Classes
|
||||
|
||||
public class GsfZipDirectoryEntry
|
||||
{
|
||||
#region Properties
|
||||
|
||||
public string Name { get; set; }
|
||||
|
||||
public int Flags { get; set; }
|
||||
|
||||
public GsfZipCompressionMethod CompressionMethod { get; set; }
|
||||
|
||||
public uint CRC32 { get; set; }
|
||||
|
||||
public long CompressedSize { get; set; }
|
||||
|
||||
public long UncompressedSize { get; set; }
|
||||
|
||||
public long Offset { get; set; }
|
||||
|
||||
public long DataOffset { get; set; }
|
||||
|
||||
public uint DosTime { get; set; }
|
||||
|
||||
public DateTime? ModifiedTime { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// null = auto, FALSE, TRUE.
|
||||
/// </summary>
|
||||
public bool? Zip64 { get; set; }
|
||||
|
||||
#endregion
|
||||
|
||||
#region Constructor
|
||||
|
||||
/// <summary>
|
||||
/// Private constructor
|
||||
/// </summary>
|
||||
private GsfZipDirectoryEntry() { }
|
||||
|
||||
/// <summary>
|
||||
/// Doesn't do much, but include for symmetry
|
||||
/// </summary>
|
||||
public static GsfZipDirectoryEntry Create() => new GsfZipDirectoryEntry();
|
||||
|
||||
#endregion
|
||||
|
||||
#region Functions
|
||||
|
||||
public void Free()
|
||||
{
|
||||
Name = null;
|
||||
}
|
||||
|
||||
public GsfZipDirectoryEntry Copy()
|
||||
{
|
||||
return new GsfZipDirectoryEntry
|
||||
{
|
||||
Name = Name,
|
||||
Flags = Flags,
|
||||
CompressionMethod = CompressionMethod,
|
||||
CRC32 = CRC32,
|
||||
CompressedSize = CompressedSize,
|
||||
UncompressedSize = UncompressedSize,
|
||||
Offset = Offset,
|
||||
DataOffset = DataOffset,
|
||||
DosTime = DosTime,
|
||||
ModifiedTime = ModifiedTime,
|
||||
Zip64 = Zip64,
|
||||
};
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
public class GsfZipVDir
|
||||
{
|
||||
#region Properties
|
||||
|
||||
public string Name { get; set; }
|
||||
|
||||
public bool IsDirectory { get; set; }
|
||||
|
||||
public GsfZipDirectoryEntry DirectoryEntry { get; set; }
|
||||
|
||||
public List<GsfZipVDir> Children { get; set; }
|
||||
|
||||
//public GSList* last_child { get; set; } /* Unused */
|
||||
|
||||
#endregion
|
||||
|
||||
#region Constructor and Destructor
|
||||
|
||||
/// <summary>
|
||||
/// Private constructor
|
||||
/// </summary>
|
||||
private GsfZipVDir() { }
|
||||
|
||||
/// <returns>The newly created GsfZipVDir.</returns>
|
||||
/// <remarks>Since: 1.14.24</remarks>
|
||||
public static GsfZipVDir Create(string name, bool is_directory, GsfZipDirectoryEntry dirent)
|
||||
{
|
||||
return new GsfZipVDir
|
||||
{
|
||||
Name = name,
|
||||
IsDirectory = is_directory,
|
||||
DirectoryEntry = dirent,
|
||||
Children = new List<GsfZipVDir>(),
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Destructor
|
||||
/// </summary>
|
||||
~GsfZipVDir() => Free(true);
|
||||
|
||||
#endregion
|
||||
|
||||
#region Functions
|
||||
|
||||
public void Free(bool free_dirent)
|
||||
{
|
||||
for (int i = 0; i < Children.Count; i++)
|
||||
{
|
||||
GsfZipVDir c = Children[i];
|
||||
c.Free(free_dirent);
|
||||
}
|
||||
|
||||
Children.Clear();
|
||||
Name = null;
|
||||
if (free_dirent && DirectoryEntry != null)
|
||||
DirectoryEntry.Free();
|
||||
}
|
||||
|
||||
public GsfZipVDir Copy()
|
||||
{
|
||||
GsfZipVDir res = new GsfZipVDir();
|
||||
|
||||
// It is not possible to add a ref_count without breaking the API,
|
||||
// so we need to really copy everything
|
||||
if (Name != null)
|
||||
res.Name = Name;
|
||||
|
||||
res.IsDirectory = IsDirectory;
|
||||
if (DirectoryEntry != null)
|
||||
res.DirectoryEntry = DirectoryEntry.Copy();
|
||||
|
||||
for (int i = 0; i < Children.Count; i++)
|
||||
{
|
||||
GsfZipVDir c = Children[i];
|
||||
res.AddChild(c.Copy());
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
public void AddChild(GsfZipVDir child)
|
||||
{
|
||||
Children.Add(child);
|
||||
}
|
||||
|
||||
public GsfZipVDir ChildByName(string name)
|
||||
{
|
||||
for (int i = 0; i < Children.Count; i++)
|
||||
{
|
||||
GsfZipVDir child = Children[i];
|
||||
if (child.Name == name)
|
||||
return child;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public GsfZipVDir ChildByIndex(int target) => target < Children.Count ? Children[target] : null;
|
||||
|
||||
public void Insert(string name, GsfZipDirectoryEntry dirent)
|
||||
{
|
||||
GsfZipVDir child;
|
||||
|
||||
int p = name.IndexOf(GsfZipImpl.ZIP_NAME_SEPARATOR);
|
||||
if (p != -1)
|
||||
{
|
||||
// A directory
|
||||
string dirname = name.Substring(p);
|
||||
child = ChildByName(dirname);
|
||||
if (child == null)
|
||||
{
|
||||
child = Create(dirname, true, null);
|
||||
AddChild(child);
|
||||
}
|
||||
|
||||
if (name[p + 1] != '\0')
|
||||
{
|
||||
name = name.Substring(p + 1);
|
||||
child.Insert(name, dirent);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// A simple file name
|
||||
child = Create(name, false, dirent);
|
||||
AddChild(child);
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
public static class GsfZipImpl
|
||||
{
|
||||
// Every member file is preceded by a header with this format.
|
||||
public const uint ZIP_HEADER_SIGNATURE = 0x04034b50;
|
||||
public const int ZIP_HEADER_SIZE = 30;
|
||||
public const int ZIP_HEADER_EXTRACT = 4;
|
||||
public const int ZIP_HEADER_FLAGS = 6;
|
||||
public const int ZIP_HEADER_COMP_METHOD = 8;
|
||||
public const int ZIP_HEADER_DOSTIME = 10;
|
||||
public const int ZIP_HEADER_CRC32 = 14;
|
||||
public const int ZIP_HEADER_CSIZE = 18;
|
||||
public const int ZIP_HEADER_USIZE = 22;
|
||||
public const int ZIP_HEADER_NAME_SIZE = 26;
|
||||
public const int ZIP_HEADER_EXTRAS_SIZE = 28;
|
||||
|
||||
// Members may have this record after the compressed data. It is meant
|
||||
// to be used only when it is not possible to seek back and patch the
|
||||
// right values into the header.
|
||||
public const uint ZIP_DDESC_SIGNATURE = 0x08074b50;
|
||||
public const int ZIP_DDESC_SIZE = 16;
|
||||
public const int ZIP_DDESC_CRC32 = 4;
|
||||
public const int ZIP_DDESC_CSIZE = 8;
|
||||
public const int ZIP_DDESC_USIZE = 12;
|
||||
|
||||
// 64-bit version of above. Used when the ZIP64 extra field is present
|
||||
// in the header.
|
||||
public const uint ZIP_DDESC64_SIGNATURE = ZIP_DDESC_SIGNATURE;
|
||||
public const int ZIP_DDESC64_SIZE = 24;
|
||||
public const int ZIP_DDESC64_CRC32 = 4;
|
||||
public const int ZIP_DDESC64_CSIZE = 8;
|
||||
public const int ZIP_DDESC64_USIZE = 16;
|
||||
|
||||
// The whole archive ends with a trailer.
|
||||
public const uint ZIP_TRAILER_SIGNATURE = 0x06054b50;
|
||||
public const int ZIP_TRAILER_SIZE = 22;
|
||||
public const int ZIP_TRAILER_DISK = 4;
|
||||
public const int ZIP_TRAILER_DIR_DISK = 6;
|
||||
public const int ZIP_TRAILER_ENTRIES = 8;
|
||||
public const int ZIP_TRAILER_TOTAL_ENTRIES = 10;
|
||||
public const int ZIP_TRAILER_DIR_SIZE = 12;
|
||||
public const int ZIP_TRAILER_DIR_POS = 16;
|
||||
public const int ZIP_TRAILER_COMMENT_SIZE = 20;
|
||||
|
||||
// A zip64 locator comes immediately before the trailer, if it is present.
|
||||
public const uint ZIP_ZIP64_LOCATOR_SIGNATURE = 0x07064b50;
|
||||
public const int ZIP_ZIP64_LOCATOR_SIZE = 20;
|
||||
public const int ZIP_ZIP64_LOCATOR_DISK = 4;
|
||||
public const int ZIP_ZIP64_LOCATOR_OFFSET = 8;
|
||||
public const int ZIP_ZIP64_LOCATOR_DISKS = 16;
|
||||
|
||||
// A zip64 archive has this record somewhere to extend the field sizes.
|
||||
public const uint ZIP_TRAILER64_SIGNATURE = 0x06064b50;
|
||||
public const int ZIP_TRAILER64_SIZE = 56; // Or more
|
||||
public const int ZIP_TRAILER64_RECSIZE = 4;
|
||||
public const int ZIP_TRAILER64_ENCODER = 12;
|
||||
public const int ZIP_TRAILER64_EXTRACT = 14;
|
||||
public const int ZIP_TRAILER64_DISK = 16;
|
||||
public const int ZIP_TRAILER64_DIR_DISK = 20;
|
||||
public const int ZIP_TRAILER64_ENTRIES = 24;
|
||||
public const int ZIP_TRAILER64_TOTAL_ENTRIES = 32;
|
||||
public const int ZIP_TRAILER64_DIR_SIZE = 40;
|
||||
public const int ZIP_TRAILER64_DIR_POS = 48;
|
||||
|
||||
// This defines the entries in the central directory.
|
||||
public const uint ZIP_DIRENT_SIGNATURE = 0x02014b50;
|
||||
public const int ZIP_DIRENT_SIZE = 46;
|
||||
public const int ZIP_DIRENT_ENCODER = 4;
|
||||
public const int ZIP_DIRENT_EXTRACT = 6;
|
||||
public const int ZIP_DIRENT_FLAGS = 8;
|
||||
public const int ZIP_DIRENT_COMPR_METHOD = 10;
|
||||
public const int ZIP_DIRENT_DOSTIME = 12;
|
||||
public const int ZIP_DIRENT_CRC32 = 16;
|
||||
public const int ZIP_DIRENT_CSIZE = 20;
|
||||
public const int ZIP_DIRENT_USIZE = 24;
|
||||
public const int ZIP_DIRENT_NAME_SIZE = 28;
|
||||
public const int ZIP_DIRENT_EXTRAS_SIZE = 30;
|
||||
public const int ZIP_DIRENT_COMMENT_SIZE = 32;
|
||||
public const int ZIP_DIRENT_DISKSTART = 34;
|
||||
public const int ZIP_DIRENT_FILE_TYPE = 36;
|
||||
public const int ZIP_DIRENT_FILE_MODE = 38;
|
||||
public const int ZIP_DIRENT_OFFSET = 42;
|
||||
|
||||
public const int ZIP_DIRENT_FLAGS_HAS_DDESC = 8;
|
||||
|
||||
public const char ZIP_NAME_SEPARATOR = '/';
|
||||
|
||||
public const int ZIP_BLOCK_SIZE = 32768;
|
||||
public const int ZIP_BUF_SIZE = 512;
|
||||
|
||||
/* z_flags */
|
||||
//#define ZZIP_IS_ENCRYPTED(p) ((*(unsigned char*)p)&1)
|
||||
//#define ZZIP_IS_COMPRLEVEL(p) (((*(unsigned char*)p)>>1)&3)
|
||||
//#define ZZIP_IS_STREAMED(p) (((*(unsigned char*)p)>>3)&1)
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
121
BurnOutSharp/External/libgsf/Input/GsfInfile.cs
vendored
Normal file
121
BurnOutSharp/External/libgsf/Input/GsfInfile.cs
vendored
Normal file
@@ -0,0 +1,121 @@
|
||||
/* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
|
||||
/*
|
||||
* gsf-infile.c :
|
||||
*
|
||||
* Copyright (C) 2002-2006 Jody Goldberg (jody@gnome.org)
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of version 2.1 of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301
|
||||
* USA
|
||||
*/
|
||||
|
||||
using System;
|
||||
|
||||
namespace LibGSF.Input
|
||||
{
|
||||
public abstract class GsfInfile : GsfInput
|
||||
{
|
||||
#region Functions
|
||||
|
||||
/// <summary>
|
||||
/// Apart from argument types, this is the same as ChildByAName.
|
||||
/// Please see the documentation there.
|
||||
/// </summary>
|
||||
/// <param name="names">A null terminated array of names (e.g. from g_strsplit)</param>
|
||||
/// <returns>A newly created child which must be unrefed.</returns>
|
||||
/// <remarks>New in 1.14.9.</remarks>
|
||||
public GsfInput ChildByVariableName(params string[] names)
|
||||
{
|
||||
GsfInfile tmp = this;
|
||||
GsfInput child = this as GsfInput;
|
||||
foreach (string name in names)
|
||||
{
|
||||
Exception err = null;
|
||||
child = tmp.ChildByName(name, ref err);
|
||||
if (child == null)
|
||||
break;
|
||||
|
||||
if (!(child is GsfInfile))
|
||||
return null;
|
||||
|
||||
tmp = child as GsfInfile;
|
||||
}
|
||||
|
||||
return child;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This function finds a child that is several directory levels down
|
||||
/// the tree. If, for example, the names "foo", "bar", and "baz" are
|
||||
/// given, then this function first finds the "foo" directory in the
|
||||
/// root infile, then locates "bar" within that directory, and finally
|
||||
/// locates "baz" within that and returns the "baz" child. In other
|
||||
/// words, this function finds the "foo/bar/baz" child.
|
||||
/// </summary>
|
||||
/// <param name="names">A null terminated array of names (e.g. from g_strsplit)</param>
|
||||
/// <returns>A newly created child which must be unrefed.</returns>
|
||||
/// <remarks>New in 1.14.9.</remarks>
|
||||
public GsfInput ChildByIndexedName(string[] names, int namesPtr = 0)
|
||||
{
|
||||
GsfInput child = this as GsfInput;
|
||||
GsfInfile tmp = this;
|
||||
|
||||
if (names == null)
|
||||
return null;
|
||||
|
||||
for (; namesPtr >= names.Length || names[namesPtr] == null; namesPtr++)
|
||||
{
|
||||
Exception err = null;
|
||||
child = tmp.ChildByName(names[namesPtr], ref err);
|
||||
if (child == null)
|
||||
break;
|
||||
|
||||
if (!(child is GsfInfile))
|
||||
return null;
|
||||
|
||||
tmp = child as GsfInfile;
|
||||
}
|
||||
|
||||
return child;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Virtual Functions
|
||||
|
||||
/// <returns>
|
||||
/// The number of children the storage has, or -1 if the storage can not
|
||||
/// have children.
|
||||
/// </returns>
|
||||
public virtual int NumChildren() => -1;
|
||||
|
||||
/// <param name="i">Zero-based index of child to find.</param>
|
||||
/// <returns>The UTF-8 encoded name of the @i-th child</returns>
|
||||
public virtual string NameByIndex(int i) => null;
|
||||
|
||||
/// <param name="i">Target index</param>
|
||||
/// <returns>A newly created child which must be unrefed.</returns>
|
||||
public virtual GsfInput ChildByIndex(int i, ref Exception error) => null;
|
||||
|
||||
/// <summary>
|
||||
/// The function returns a named child of the given infile. This only
|
||||
/// works for an immediate child. If you need to go several levels
|
||||
/// down use ChildByAName, for example.
|
||||
/// </summary>
|
||||
/// <param name="name">Target name</param>
|
||||
/// <returns>A newly created child which must be unrefed.</returns>
|
||||
public virtual GsfInput ChildByName(string name, ref Exception error) => null;
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
963
BurnOutSharp/External/libgsf/Input/GsfInfileMSOle.cs
vendored
Normal file
963
BurnOutSharp/External/libgsf/Input/GsfInfileMSOle.cs
vendored
Normal file
@@ -0,0 +1,963 @@
|
||||
/* 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 System.Linq;
|
||||
using System.Text;
|
||||
using static LibGSF.GsfMSOleImpl;
|
||||
|
||||
namespace LibGSF.Input
|
||||
{
|
||||
public class MSOleBAT
|
||||
{
|
||||
#region Properties
|
||||
|
||||
public uint[] Block { get; set; } = null;
|
||||
|
||||
public int BlockPointer { get; set; } = 0;
|
||||
|
||||
public int NumBlocks { get; set; }
|
||||
|
||||
#endregion
|
||||
|
||||
#region Constructor
|
||||
|
||||
/// <summary>
|
||||
/// Internal constructor
|
||||
/// </summary>
|
||||
internal MSOleBAT() { }
|
||||
|
||||
/// <summary>
|
||||
/// Walk the linked list of the supplied block allocation table and build up a
|
||||
/// table for the list starting in <paramref name="block"/>.
|
||||
/// </summary>
|
||||
/// <param name="metabat">A meta bat to connect to the raw blocks (small or large)</param>
|
||||
/// <param name="size_guess">An optional guess as to how many blocks are in the file</param>
|
||||
/// <param name="block">The first block in the list.</param>
|
||||
/// <param name="res">Where to store the result.</param>
|
||||
/// <returns>true on error.</returns>
|
||||
public static bool Create(MSOleBAT metabat, int size_guess, uint block, out MSOleBAT res)
|
||||
{
|
||||
// NOTE : Only use size as a suggestion, sometimes it is wrong
|
||||
uint[] bat = new uint[size_guess];
|
||||
int batPtr = 0; // bat[0]
|
||||
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[batPtr++] = block;
|
||||
block = metabat.Block[block];
|
||||
}
|
||||
|
||||
res = new MSOleBAT
|
||||
{
|
||||
NumBlocks = bat.Length,
|
||||
Block = bat,
|
||||
};
|
||||
|
||||
if (block != BAT_MAGIC_END_OF_CHAIN)
|
||||
{
|
||||
Console.WriteLine("This OLE2 file is invalid.\n"
|
||||
+ $"The Block Allocation Table for one of the streams had {block} instead of a terminator ({BAT_MAGIC_END_OF_CHAIN}).\n"
|
||||
+ "We might still be able to extract some data, but you'll want to check the file.");
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Functions
|
||||
|
||||
public void Release()
|
||||
{
|
||||
if (Block == null)
|
||||
return;
|
||||
|
||||
NumBlocks = 0;
|
||||
Block = null;
|
||||
BlockPointer = 0;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
public class MSOleInfo
|
||||
{
|
||||
#region Classes
|
||||
|
||||
public class MSOLEInfoPrivateStruct
|
||||
{
|
||||
public MSOleBAT Bat { get; set; }
|
||||
|
||||
public int Shift { get; set; }
|
||||
|
||||
public uint Filter { get; set; }
|
||||
|
||||
public int Size { get; set; }
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Properties
|
||||
|
||||
public MSOLEInfoPrivateStruct BigBlock { get; set; }
|
||||
|
||||
public MSOLEInfoPrivateStruct SmallBlock { get; set; }
|
||||
|
||||
public long MaxBlock { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Transition between small and big blocks
|
||||
/// </summary>
|
||||
public uint Threshold { get; set; }
|
||||
|
||||
public uint SbatStart { get; set; }
|
||||
|
||||
public uint NumSbat { get; set; }
|
||||
|
||||
public MSOLEDirectoryEntry RootDir { get; set; }
|
||||
|
||||
public GsfInput SmallBlockFile { get; set; }
|
||||
|
||||
public int RefCount { get; set; }
|
||||
|
||||
#endregion
|
||||
|
||||
#region Functions
|
||||
|
||||
public void Unref()
|
||||
{
|
||||
if (RefCount-- != 1)
|
||||
return;
|
||||
|
||||
BigBlock.Bat.Release();
|
||||
SmallBlock.Bat.Release();
|
||||
if (RootDir != null)
|
||||
RootDir = null;
|
||||
|
||||
if (SmallBlockFile != null)
|
||||
SmallBlockFile = null;
|
||||
}
|
||||
|
||||
public MSOleInfo Ref()
|
||||
{
|
||||
RefCount++;
|
||||
return this;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
public class MSOLEDirectoryEntry
|
||||
{
|
||||
#region Properties
|
||||
|
||||
public string Name { get; set; }
|
||||
|
||||
public GsfMSOleSortingKey Key { get; set; }
|
||||
|
||||
public int Index { get; set; }
|
||||
|
||||
public int Size { get; set; }
|
||||
|
||||
public bool UseSmallBlock { get; set; }
|
||||
|
||||
public uint FirstBlock { get; set; }
|
||||
|
||||
public bool IsDirectory { get; set; }
|
||||
|
||||
public List<MSOLEDirectoryEntry> Children { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 16 byte GUID used by some apps
|
||||
/// </summary>
|
||||
public byte[] ClassID { get; set; } = new byte[16];
|
||||
|
||||
public DateTime? ModTime { get; set; }
|
||||
|
||||
#endregion
|
||||
|
||||
#region Functions
|
||||
|
||||
private void Free()
|
||||
{
|
||||
foreach (MSOLEDirectoryEntry child in Children)
|
||||
{
|
||||
child.Free();
|
||||
}
|
||||
|
||||
Children = null;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
public class GsfInfileMSOLE : GsfInfile
|
||||
{
|
||||
#region Properties
|
||||
|
||||
public GsfInput Input { get; private set; } = null;
|
||||
|
||||
public MSOleInfo Info { get; private set; } = null;
|
||||
|
||||
public MSOLEDirectoryEntry DirectoryEntry { get; private set; }
|
||||
|
||||
public MSOleBAT Bat { get; private set; }
|
||||
|
||||
public long CurBlock { get; private set; } = BAT_MAGIC_UNUSED;
|
||||
|
||||
public byte[] Stream { get; private set; }
|
||||
|
||||
#endregion
|
||||
|
||||
#region Destructor
|
||||
|
||||
/// <summary>
|
||||
/// Destructor
|
||||
/// </summary>
|
||||
~GsfInfileMSOLE()
|
||||
{
|
||||
if (Input != null)
|
||||
Input = null;
|
||||
|
||||
if (Info != null && Info.SmallBlockFile != this)
|
||||
{
|
||||
Info.Unref();
|
||||
Info = null;
|
||||
}
|
||||
|
||||
Bat.Release();
|
||||
Stream = null;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Functions
|
||||
|
||||
protected override GsfInput DupImpl(ref Exception err) => (Container as GsfInfileMSOLE).CreateChild(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.BigBlock.Shift);
|
||||
long last_block = ((CurrentOffset + num_bytes - 1) >> Info.BigBlock.Shift);
|
||||
long offset = CurrentOffset & Info.BigBlock.Filter;
|
||||
|
||||
if (last_block >= Bat.NumBlocks)
|
||||
return null;
|
||||
|
||||
// Optimization: are all the raw blocks contiguous?
|
||||
long i = first_block;
|
||||
uint raw_block = Bat.Block[i];
|
||||
while (++i <= last_block && ++raw_block == Bat.Block[i]) ;
|
||||
|
||||
if (i > last_block)
|
||||
{
|
||||
if (!SeekBlock(Bat.Block[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;
|
||||
}
|
||||
|
||||
byte[] ptr = optional_buffer;
|
||||
int ptrPtr = optional_buffer_ptr; // ptr[optional_buffer_ptr + 0]
|
||||
int count;
|
||||
for (i = first_block; i <= last_block; i++, ptrPtr += count, num_bytes -= count)
|
||||
{
|
||||
count = (int)(Info.BigBlock.Size - offset);
|
||||
if (count > num_bytes)
|
||||
count = num_bytes;
|
||||
|
||||
if (!SeekBlock(Bat.Block[i], offset))
|
||||
return null;
|
||||
|
||||
if (Input.Read(count, ptr, ptrPtr) == null)
|
||||
return null;
|
||||
|
||||
offset = 0;
|
||||
}
|
||||
|
||||
CurBlock = BAT_MAGIC_UNUSED;
|
||||
|
||||
return optional_buffer;
|
||||
}
|
||||
|
||||
public override bool Seek(long offset, SeekOrigin whence)
|
||||
{
|
||||
CurBlock = BAT_MAGIC_UNUSED;
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override GsfInput ChildByIndex(int i, ref Exception error)
|
||||
{
|
||||
foreach (MSOLEDirectoryEntry dirent in DirectoryEntry.Children)
|
||||
{
|
||||
if (i-- <= 0)
|
||||
return CreateChild(dirent, ref error);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override string NameByIndex(int i)
|
||||
{
|
||||
foreach (MSOLEDirectoryEntry dirent in DirectoryEntry.Children)
|
||||
{
|
||||
if (i-- <= 0)
|
||||
return dirent.Name;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override GsfInput ChildByName(string name, ref Exception error)
|
||||
{
|
||||
foreach (MSOLEDirectoryEntry dirent in DirectoryEntry.Children)
|
||||
{
|
||||
if (dirent.Name != null && dirent.Name == name)
|
||||
return CreateChild(dirent, ref error);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override int NumChildren()
|
||||
{
|
||||
if (DirectoryEntry == null)
|
||||
return -1;
|
||||
|
||||
if (!DirectoryEntry.IsDirectory)
|
||||
return -1;
|
||||
|
||||
return DirectoryEntry.Children.Count;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Opens the root directory of an MS OLE file.
|
||||
/// </summary>
|
||||
/// <param name="source">GsfInput</param>
|
||||
/// <param name="err">Optional place to store an error</param>
|
||||
/// <returns>The new ole file handler</returns>
|
||||
/// <remarks>This adds a reference to <paramref name="source"/>.</remarks>
|
||||
public static GsfInfile Create(GsfInput source, ref Exception err)
|
||||
{
|
||||
GsfInfileMSOLE ole = new GsfInfileMSOLE
|
||||
{
|
||||
Input = source,
|
||||
Size = 0,
|
||||
};
|
||||
|
||||
long calling_pos = source.CurrentOffset;
|
||||
if (ole.InitInfo(ref err))
|
||||
{
|
||||
// We do this so other kinds of archives can be tried.
|
||||
source.Seek(calling_pos, SeekOrigin.Begin);
|
||||
return null;
|
||||
}
|
||||
|
||||
return ole;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves the 16 byte indentifier (often a GUID in MS Windows apps)
|
||||
/// stored within the directory associated with @ole and stores it in <paramref name="res"/>.
|
||||
/// </summary>
|
||||
/// <param name="res">16 byte identifier (often a GUID in MS Windows apps)</param>
|
||||
/// <returns>true on success</returns>
|
||||
public bool GetClassID(byte[] res)
|
||||
{
|
||||
if (DirectoryEntry == null)
|
||||
return false;
|
||||
|
||||
Array.Copy(DirectoryEntry.ClassID, res, DirectoryEntry.ClassID.Length);
|
||||
return true;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Utilities
|
||||
|
||||
/// <returns>false on error.</returns>
|
||||
private bool SeekBlock(uint block, long offset)
|
||||
{
|
||||
if (block >= Info.MaxBlock)
|
||||
return false;
|
||||
|
||||
// OLE_HEADER_SIZE is fixed at 512, but the sector containing the
|
||||
// header is padded out to BigBlock.Size (sector size) when BigBlock.Size > 512.
|
||||
if (Input.Seek(Math.Max(OLE_HEADER_SIZE, Info.BigBlock.Size) + (block << Info.BigBlock.Shift) + offset, SeekOrigin.Begin))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Read a block of data from the underlying input.
|
||||
/// </summary>
|
||||
/// <param name="block">Block number</param>
|
||||
/// <param name="buffer">Optionally null</param>
|
||||
/// <returns>Pointer to the buffer or null if there is an error or 0 bytes are requested.</returns>
|
||||
/// <remarks>Be really anal.</remarks>
|
||||
private byte[] GetBlock(uint block, byte[] buffer)
|
||||
{
|
||||
if (!SeekBlock(block, 0))
|
||||
return null;
|
||||
|
||||
return Input.Read(Info.BigBlock.Size, buffer);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A small utility routine to read a set of references to bat blocks
|
||||
/// either from the OLE header, or a meta-bat block.
|
||||
/// </summary>
|
||||
/// <returns>A pointer to the element after the last position filled</returns>
|
||||
private uint[] ReadMetabat(uint[] bats, int batsPtr, int max_bat, uint[] metabat, int metabatPtr, int 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 batPtr = 0; // bat[0]
|
||||
int end = batPtr + Info.BigBlock.Size;
|
||||
for (; batPtr < end; batPtr += BAT_INDEX_SIZE, batsPtr++)
|
||||
{
|
||||
bats[batsPtr] = BitConverter.ToUInt32(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.BigBlock.Size / BAT_INDEX_SIZE);
|
||||
while (i-- > 0)
|
||||
{
|
||||
bats[batsPtr++] = BAT_MAGIC_UNUSED;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return bats;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Copy some some raw data into an array of uint.
|
||||
/// </summary>
|
||||
private static void GetUnsignedInts(uint[] dst, int dstPtr, byte[] src, int srcPtr, int num_bytes)
|
||||
{
|
||||
for (; (num_bytes -= BAT_INDEX_SIZE) >= 0; srcPtr += BAT_INDEX_SIZE)
|
||||
{
|
||||
dst[dstPtr++] = BitConverter.ToUInt32(src, srcPtr);
|
||||
}
|
||||
}
|
||||
|
||||
private GsfInput GetSmallBlockFile()
|
||||
{
|
||||
if (Info.SmallBlockFile != null)
|
||||
return Info.SmallBlockFile;
|
||||
|
||||
Exception err = null;
|
||||
Info.SmallBlockFile = CreateChild(Info.RootDir, ref err);
|
||||
if (Info.SmallBlockFile == null)
|
||||
return null;
|
||||
|
||||
// Avoid creating a circular reference
|
||||
((GsfInfileMSOLE)Info.SmallBlockFile).Info.Unref();
|
||||
|
||||
if (Info.SmallBlock.Bat.Block != null)
|
||||
return null;
|
||||
|
||||
if (MSOleBAT.Create(Info.BigBlock.Bat, (int)Info.NumSbat, Info.SbatStart, out MSOleBAT meta_sbat))
|
||||
return null;
|
||||
|
||||
Info.SmallBlock.Bat.NumBlocks = meta_sbat.NumBlocks * (Info.BigBlock.Size / BAT_INDEX_SIZE);
|
||||
Info.SmallBlock.Bat.Block = new uint[Info.SmallBlock.Bat.NumBlocks];
|
||||
ReadMetabat(Info.SmallBlock.Bat.Block, 0, Info.SmallBlock.Bat.NumBlocks, meta_sbat.Block, 0, meta_sbat.NumBlocks);
|
||||
|
||||
return Info.SmallBlockFile;
|
||||
}
|
||||
|
||||
private static int DirectoryEntryCompare(MSOLEDirectoryEntry a, MSOLEDirectoryEntry b) => SortingKeyCompare(a.Key, b.Key);
|
||||
|
||||
private static DateTime? DateTimeFromFileTime(ulong ft) => ft == 0 ? (DateTime?)null : DateTime.FromFileTime((long)ft);
|
||||
|
||||
private GsfInput CreateChild(MSOLEDirectoryEntry dirent, ref Exception err)
|
||||
{
|
||||
GsfInfileMSOLE child = PartiallyDuplicate(ref err);
|
||||
if (child == null)
|
||||
return null;
|
||||
|
||||
child.DirectoryEntry = dirent;
|
||||
child.Size = dirent.Size;
|
||||
child.ModTime = dirent.ModTime;
|
||||
|
||||
// The root dirent defines the small block file
|
||||
if (dirent.Index != 0)
|
||||
{
|
||||
child.Name = dirent.Name;
|
||||
child.Container = this;
|
||||
|
||||
if (dirent.IsDirectory)
|
||||
{
|
||||
// Be wary. It seems as if some implementations pretend that the
|
||||
// directories contain data
|
||||
child.Size = 0;
|
||||
return child;
|
||||
}
|
||||
}
|
||||
|
||||
MSOleInfo info = Info;
|
||||
|
||||
MSOleBAT metabat;
|
||||
int size_guess;
|
||||
GsfInput sb_file = null;
|
||||
|
||||
// Build the bat
|
||||
if (dirent.UseSmallBlock)
|
||||
{
|
||||
metabat = info.SmallBlock.Bat;
|
||||
size_guess = dirent.Size >> (int)info.SmallBlock.Shift;
|
||||
sb_file = GetSmallBlockFile();
|
||||
if (sb_file == null)
|
||||
{
|
||||
err = new Exception("Failed to access child");
|
||||
return null;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
metabat = info.BigBlock.Bat;
|
||||
size_guess = dirent.Size >> (int)info.BigBlock.Shift;
|
||||
}
|
||||
|
||||
if (MSOleBAT.Create(metabat, size_guess + 1, dirent.FirstBlock, out MSOleBAT tempBat))
|
||||
return null;
|
||||
|
||||
child.Bat = tempBat;
|
||||
|
||||
if (dirent.UseSmallBlock)
|
||||
{
|
||||
if (sb_file == null)
|
||||
return null;
|
||||
|
||||
int remaining = dirent.Size;
|
||||
child.Stream = new byte[remaining];
|
||||
|
||||
for (uint i = 0; remaining > 0 && i < child.Bat.NumBlocks; i++, remaining -= info.SmallBlock.Size)
|
||||
{
|
||||
if (sb_file.Seek(child.Bat.Block[i] << (int)info.SmallBlock.Shift, SeekOrigin.Begin)
|
||||
|| sb_file.Read(Math.Min(remaining, info.SmallBlock.Size), child.Stream, (int)(i << (int)info.SmallBlock.Shift)) == null)
|
||||
{
|
||||
Console.Error.WriteLine($"Failure reading block {i} for '{dirent.Name}'");
|
||||
err = new Exception("Failure reading block");
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
if (remaining > 0)
|
||||
{
|
||||
err = new Exception("Insufficient blocks");
|
||||
Console.Error.WriteLine($"Small-block file '{dirent.Name}' has insufficient blocks ({child.Bat.NumBlocks}) for the stated size ({dirent.Size})");
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
return child;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parse dirent number <paramref name="entry"/> and recursively handle its siblings and children.
|
||||
/// parent is optional.
|
||||
private MSOLEDirectoryEntry CreateDirectoryEntry(uint entry, MSOLEDirectoryEntry parent, bool[] seen_before)
|
||||
{
|
||||
if (entry >= DIRENT_MAGIC_END)
|
||||
return null;
|
||||
|
||||
if (entry > uint.MaxValue / DIRENT_SIZE)
|
||||
return null;
|
||||
|
||||
uint block = ((entry * DIRENT_SIZE) >> Info.BigBlock.Shift);
|
||||
if (block >= Bat.NumBlocks)
|
||||
return null;
|
||||
|
||||
if (seen_before[entry])
|
||||
return null;
|
||||
|
||||
seen_before[entry] = true;
|
||||
|
||||
byte[] data = GetBlock(Bat.Block[block], null);
|
||||
if (data == null)
|
||||
return null;
|
||||
|
||||
int dataPtr = 0; // data[0]
|
||||
dataPtr += (int)((DIRENT_SIZE * entry) % Info.BigBlock.Size);
|
||||
|
||||
byte type = data[dataPtr + DIRENT_TYPE];
|
||||
if (type != DIRENT_TYPE_DIR && type != DIRENT_TYPE_FILE && type != DIRENT_TYPE_ROOTDIR)
|
||||
{
|
||||
Console.Error.WriteLine($"Unknown stream type 0x{type:x}");
|
||||
return null;
|
||||
}
|
||||
|
||||
if (parent == null && type != DIRENT_TYPE_ROOTDIR)
|
||||
{
|
||||
// See bug 346118.
|
||||
Console.Error.WriteLine("Root directory is not marked as such.");
|
||||
type = DIRENT_TYPE_ROOTDIR;
|
||||
}
|
||||
|
||||
// It looks like directory (and root directory) sizes are sometimes bogus
|
||||
uint size = BitConverter.ToUInt32(data, dataPtr + DIRENT_FILE_SIZE);
|
||||
if (!(type == DIRENT_TYPE_DIR || type == DIRENT_TYPE_ROOTDIR || size <= (uint)Input.Size))
|
||||
return null;
|
||||
|
||||
ulong ft = BitConverter.ToUInt64(data, dataPtr + DIRENT_MODIFY_TIME);
|
||||
|
||||
MSOLEDirectoryEntry dirent = new MSOLEDirectoryEntry
|
||||
{
|
||||
Index = (int)entry,
|
||||
Size = (int)size,
|
||||
ModTime = DateTimeFromFileTime(ft),
|
||||
};
|
||||
|
||||
// Store the class id which is 16 byte identifier used by some apps
|
||||
Array.Copy(data, dataPtr + DIRENT_CLSID, dirent.ClassID, 0, dirent.ClassID.Length);
|
||||
|
||||
// Root dir is always big block
|
||||
dirent.UseSmallBlock = parent != null && (size < Info.Threshold);
|
||||
dirent.FirstBlock = BitConverter.ToUInt32(data, dataPtr + DIRENT_FIRSTBLOCK);
|
||||
dirent.IsDirectory = (type != DIRENT_TYPE_FILE);
|
||||
dirent.Children = null;
|
||||
|
||||
uint prev = BitConverter.ToUInt32(data, dataPtr + DIRENT_PREV);
|
||||
uint next = BitConverter.ToUInt32(data, dataPtr + DIRENT_NEXT);
|
||||
uint child = BitConverter.ToUInt32(data, dataPtr + DIRENT_CHILD);
|
||||
ushort name_len = BitConverter.ToUInt16(data, dataPtr + DIRENT_NAME_LEN);
|
||||
|
||||
dirent.Name = null;
|
||||
if (0 < name_len && name_len <= DIRENT_MAX_NAME_SIZE)
|
||||
{
|
||||
ushort[] uni_name = new ushort[DIRENT_MAX_NAME_SIZE + 1];
|
||||
|
||||
// !#%!@$#^
|
||||
// Sometimes, rarely, people store the stream name as ascii
|
||||
// rather than utf16. Do a validation first just in case.
|
||||
int end = 0;
|
||||
try { end = new UTF8Encoding(false, true).GetCharCount(data); }
|
||||
catch { end = -1; }
|
||||
|
||||
if (end == -1 || (end + 1) != name_len)
|
||||
{
|
||||
byte[] direntNameBytes = Encoding.Convert(Encoding.ASCII, Encoding.UTF8, data, 0, end);
|
||||
dirent.Name = Encoding.UTF8.GetString(direntNameBytes);
|
||||
}
|
||||
else
|
||||
{
|
||||
dirent.Name = Encoding.UTF8.GetString(data, 0, end + 1);
|
||||
}
|
||||
}
|
||||
|
||||
// Be really anal in the face of screwups
|
||||
if (dirent.Name == null)
|
||||
dirent.Name = string.Empty;
|
||||
|
||||
dirent.Key = GsfMSOleSortingKey.Create(dirent.Name);
|
||||
|
||||
if (parent != null)
|
||||
{
|
||||
parent.Children.Add(dirent);
|
||||
parent.Children.Sort(DirectoryEntryCompare);
|
||||
}
|
||||
|
||||
// NOTE : These links are a tree, not a linked list
|
||||
CreateDirectoryEntry(prev, parent, seen_before);
|
||||
CreateDirectoryEntry(next, parent, seen_before);
|
||||
|
||||
if (dirent.IsDirectory)
|
||||
CreateDirectoryEntry(child, dirent, seen_before);
|
||||
else if (child != DIRENT_MAGIC_END)
|
||||
Console.Error.WriteLine("A non directory stream with children ?");
|
||||
|
||||
return dirent;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Utility routine to _partially_ replicate a file. It does NOT copy the bat
|
||||
/// blocks, or init the dirent.
|
||||
/// </summary>
|
||||
private GsfInfileMSOLE PartiallyDuplicate(ref Exception err)
|
||||
{
|
||||
GsfInput input = Input.Duplicate(ref err);
|
||||
if (input == null)
|
||||
{
|
||||
err = new Exception("Failed to duplicate input stream");
|
||||
return null;
|
||||
}
|
||||
|
||||
GsfInfileMSOLE dst = new GsfInfileMSOLE();
|
||||
dst.Input = input;
|
||||
dst.Info = Info.Ref();
|
||||
// buf and buf_size are initialized to null
|
||||
|
||||
return dst;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Read an OLE header and do some sanity checking
|
||||
/// along the way.
|
||||
/// </summary>
|
||||
/// <returns>true on error setting <paramref name="err"/> if it is supplied.</returns>
|
||||
private bool InitInfo(ref Exception err)
|
||||
{
|
||||
byte[] header;
|
||||
|
||||
// Check the header
|
||||
byte[] signature = { 0xd0, 0xcf, 0x11, 0xe0, 0xa1, 0xb1, 0x1a, 0xe1 };
|
||||
if (Input.Seek(0, SeekOrigin.Begin)
|
||||
|| null == (header = Input.Read(OLE_HEADER_SIZE, null))
|
||||
|| !header.Take(signature.Length).SequenceEqual(signature))
|
||||
{
|
||||
err = new Exception("No OLE2 signature");
|
||||
return true;
|
||||
}
|
||||
|
||||
ushort bb_shift = BitConverter.ToUInt16(header, OLE_HEADER_BB_SHIFT);
|
||||
ushort sb_shift = BitConverter.ToUInt16(header, OLE_HEADER_SB_SHIFT);
|
||||
uint num_bat = BitConverter.ToUInt32(header, OLE_HEADER_NUM_BAT);
|
||||
uint num_sbat = BitConverter.ToUInt32(header, OLE_HEADER_NUM_SBAT);
|
||||
uint threshold = BitConverter.ToUInt32(header, OLE_HEADER_THRESHOLD);
|
||||
uint dirent_start = BitConverter.ToUInt32(header, OLE_HEADER_DIRENT_START);
|
||||
uint metabat_block = BitConverter.ToUInt32(header, OLE_HEADER_METABAT_BLOCK);
|
||||
uint num_metabat = BitConverter.ToUInt32(header, OLE_HEADER_NUM_METABAT);
|
||||
|
||||
// Some sanity checks
|
||||
// 1) There should always be at least 1 BAT block
|
||||
// 2) It makes no sense to have a block larger than 2^31 for now.
|
||||
// Maybe relax this later, but not much.
|
||||
if (6 > bb_shift || bb_shift >= 31 || sb_shift > bb_shift || (Input.Size >> bb_shift) < 1)
|
||||
{
|
||||
err = new Exception("Unreasonable block sizes");
|
||||
return true;
|
||||
}
|
||||
|
||||
MSOleInfo info = new MSOleInfo
|
||||
{
|
||||
RefCount = 1,
|
||||
BigBlock = new MSOleInfo.MSOLEInfoPrivateStruct
|
||||
{
|
||||
Shift = bb_shift,
|
||||
Size = 1 << bb_shift,
|
||||
Filter = (uint)(1 << bb_shift) - 1,
|
||||
Bat = new MSOleBAT(),
|
||||
},
|
||||
SmallBlock = new MSOleInfo.MSOLEInfoPrivateStruct
|
||||
{
|
||||
Shift = sb_shift,
|
||||
Size = 1 << sb_shift,
|
||||
Filter = (uint)(1 << sb_shift) - 1,
|
||||
Bat = new MSOleBAT(),
|
||||
},
|
||||
Threshold = threshold,
|
||||
SbatStart = BitConverter.ToUInt32(header, OLE_HEADER_SBAT_START),
|
||||
NumSbat = num_sbat,
|
||||
MaxBlock = (Input.Size - OLE_HEADER_SIZE + (1 << bb_shift) - 1) / (1 << bb_shift),
|
||||
SmallBlockFile = null,
|
||||
};
|
||||
|
||||
Info = info;
|
||||
|
||||
if (info.NumSbat == 0 && info.SbatStart != BAT_MAGIC_END_OF_CHAIN && info.SbatStart != 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.");
|
||||
|
||||
uint[] metabat = null;
|
||||
uint last;
|
||||
uint[] ptr;
|
||||
|
||||
// Very rough heuristic, just in case
|
||||
if (num_bat < info.MaxBlock && info.NumSbat < info.MaxBlock)
|
||||
{
|
||||
info.BigBlock.Bat.NumBlocks = (int)(num_bat * (info.BigBlock.Size / BAT_INDEX_SIZE));
|
||||
info.BigBlock.Bat.Block = new uint[info.BigBlock.Bat.NumBlocks];
|
||||
|
||||
metabat = new uint[Math.Max(info.BigBlock.Size, OLE_HEADER_SIZE)];
|
||||
|
||||
// Reading the elements invalidates this memory, make copy
|
||||
GetUnsignedInts(metabat, 0, header, OLE_HEADER_START_BAT, OLE_HEADER_SIZE - OLE_HEADER_START_BAT);
|
||||
last = num_bat;
|
||||
if (last > OLE_HEADER_METABAT_SIZE)
|
||||
last = OLE_HEADER_METABAT_SIZE;
|
||||
|
||||
ptr = ReadMetabat(info.BigBlock.Bat.Block, 0, info.BigBlock.Bat.NumBlocks, metabat, 0, (int)last);
|
||||
num_bat -= last;
|
||||
}
|
||||
else
|
||||
{
|
||||
ptr = null;
|
||||
}
|
||||
|
||||
int ptrPtr = 0; // ptr[0]
|
||||
last = (uint)((info.BigBlock.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(metabat, 0, tmp, 0, info.BigBlock.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 = 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(ptr, ptrPtr, info.BigBlock.Bat.NumBlocks, metabat, 0, (int)last);
|
||||
}
|
||||
|
||||
bool fail = (ptr == null);
|
||||
|
||||
metabat = 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.BigBlock.Bat, 0, 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 << (int)info.BigBlock.Shift) * 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.ModTime;
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
338
BurnOutSharp/External/libgsf/Input/GsfInfileMSVBA.cs
vendored
Normal file
338
BurnOutSharp/External/libgsf/Input/GsfInfileMSVBA.cs
vendored
Normal file
@@ -0,0 +1,338 @@
|
||||
/*
|
||||
* 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;
|
||||
|
||||
namespace LibGSF.Input
|
||||
{
|
||||
public class GsfInfileMSVBA : GsfInfile
|
||||
{
|
||||
#region Constants
|
||||
|
||||
/// <summary>
|
||||
/// Magic (2 bytes)
|
||||
/// Version (4 bytes)
|
||||
/// 0x00, 0xFF (2 bytes)
|
||||
/// Unknown (22 bytes)
|
||||
/// </summary>
|
||||
private const int VBA56_DIRENT_RECORD_COUNT = 2 + 4 + 2 + 22;
|
||||
|
||||
/// <summary>
|
||||
/// VBA56_DIRENT_RECORD_COUNT (30 bytes)
|
||||
/// Type1 Record Count (2 bytes)
|
||||
/// Unknown (2 bytes)
|
||||
/// </summary>
|
||||
private const int VBA56_DIRENT_HEADER_SIZE = VBA56_DIRENT_RECORD_COUNT + 2 + 2;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Properties
|
||||
|
||||
public GsfInfile Source { get; private set; } = null;
|
||||
|
||||
public List<GsfInfile> Children { get; private set; } = null;
|
||||
|
||||
public Dictionary<string, byte[]> Modules { get; private set; } = null;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Constructor
|
||||
|
||||
/// <summary>
|
||||
/// Private constructor
|
||||
/// </summary>
|
||||
private GsfInfileMSVBA() { }
|
||||
|
||||
public static GsfInfileMSVBA Create(GsfInfile source, ref Exception err)
|
||||
{
|
||||
if (source == null)
|
||||
return null;
|
||||
|
||||
GsfInfileMSVBA vba = new GsfInfileMSVBA
|
||||
{
|
||||
Source = source,
|
||||
};
|
||||
|
||||
// Find the name offset pairs
|
||||
if (vba.Read(ref err))
|
||||
return vba;
|
||||
|
||||
if (err != null)
|
||||
err = new Exception("Unable to parse VBA header");
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Functions
|
||||
|
||||
/// <summary>
|
||||
/// A collection of names and source code which the caller is responsible for destroying.
|
||||
/// </summary>
|
||||
/// <returns>A Dictionary of names and source code (unknown encoding).</returns>
|
||||
public Dictionary<string, byte[]> StealModules()
|
||||
{
|
||||
Dictionary<string, byte[]> res = Modules;
|
||||
Modules = null;
|
||||
return res;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A utility routine that attempts to find the VBA file withint a stream.
|
||||
/// </summary>
|
||||
/// <returns>A GsfInfile</returns>
|
||||
public static GsfInfileMSVBA FindVBA(GsfInput input, ref Exception err)
|
||||
{
|
||||
GsfInput vba = null;
|
||||
GsfInfile infile;
|
||||
|
||||
if ((infile = GsfInfileMSOLE.Create(input, ref err)) != null)
|
||||
{
|
||||
// 1) Try XLS
|
||||
vba = infile.ChildByVariableName("_VBA_PROJECT_CUR", "VBA");
|
||||
|
||||
// 2) DOC
|
||||
if (null == vba)
|
||||
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 _, false);
|
||||
|
||||
if (code != null)
|
||||
{
|
||||
if (Modules == null)
|
||||
Modules = new Dictionary<string, byte[]>();
|
||||
|
||||
Modules[name] = code;
|
||||
}
|
||||
else
|
||||
{
|
||||
Console.Error.WriteLine($"Problems extracting the source for {name} @ {src_offset}");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Read an VBA dirctory and its project file.
|
||||
/// along the way.
|
||||
/// </summary>
|
||||
/// <param name="err">Place to store an Exception if anything goes wrong</param>
|
||||
/// <returns>false on error setting <paramref name="err"/> if it is supplied.</returns>
|
||||
private bool Read(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 = 0;
|
||||
do
|
||||
{
|
||||
/* I have seen
|
||||
* type len data
|
||||
* 1 4 1 0 0 0
|
||||
* 2 4 9 4 0 0
|
||||
* 3 2 4 e4
|
||||
* 4 <var> project name
|
||||
* 5 0
|
||||
* 6 0
|
||||
* 7 4
|
||||
* 8 4
|
||||
* 0x3d 0
|
||||
* 0x40 0
|
||||
* 0x14 4 9 4 0 0
|
||||
*
|
||||
* 0x0f == number of elements
|
||||
* 0x1c == (Size 0)
|
||||
* 0x1e == (Size 4)
|
||||
* 0x48 == (Size 0)
|
||||
* 0x31 == stream offset of the compressed source !
|
||||
*
|
||||
* 0x16 == an ascii dependency name
|
||||
* 0x3e == a unicode dependency name
|
||||
* 0x33 == a classid for a dependency with no trialing data
|
||||
*
|
||||
* 0x2f == a dummy classid
|
||||
* 0x30 == a classid
|
||||
* 0x0d == the classid
|
||||
* 0x2f, and 0x0d appear contain
|
||||
* uint32 classid_size;
|
||||
* <classid>
|
||||
* 00 00 00 00 00 00
|
||||
* and sometimes some trailing junk
|
||||
**/
|
||||
|
||||
if ((ptr + 6) > end)
|
||||
{
|
||||
err = new Exception("VBA project header problem");
|
||||
return false;
|
||||
}
|
||||
|
||||
tag = BitConverter.ToUInt16(inflated_data, ptr);
|
||||
uint len = BitConverter.ToUInt32(inflated_data, ptr + 2);
|
||||
|
||||
ptr += 6;
|
||||
if ((ptr + len) > end)
|
||||
{
|
||||
err = new Exception("VBA project header problem");
|
||||
return false;
|
||||
}
|
||||
|
||||
switch (tag)
|
||||
{
|
||||
case 4:
|
||||
name = Encoding.UTF8.GetString(inflated_data, ptr, (int)len);
|
||||
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 = BitConverter.ToUInt16(inflated_data, ptr);
|
||||
break;
|
||||
|
||||
// Dependencies
|
||||
case 0x0d: break;
|
||||
case 0x2f: break;
|
||||
case 0x30: break;
|
||||
case 0x33: break;
|
||||
case 0x3e: break;
|
||||
case 0x16:
|
||||
break;
|
||||
|
||||
// Elements
|
||||
case 0x47: break;
|
||||
case 0x32: break;
|
||||
case 0x1a:
|
||||
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, BitConverter.ToUInt32(inflated_data, ptr));
|
||||
elem_stream = null;
|
||||
element_count--;
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
ptr += (int)len;
|
||||
} while (tag != 0x10);
|
||||
|
||||
if (element_count != 0)
|
||||
Console.Error.WriteLine("Number of elements differs from expectations");
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
130
BurnOutSharp/External/libgsf/Input/GsfInfileStdio.cs
vendored
Normal file
130
BurnOutSharp/External/libgsf/Input/GsfInfileStdio.cs
vendored
Normal file
@@ -0,0 +1,130 @@
|
||||
/* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
|
||||
/*
|
||||
* gsf-infile-stdio.c: read a directory tree
|
||||
*
|
||||
* Copyright (C) 2004-2006 Novell, Inc.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of version 2.1 of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301
|
||||
* USA
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
|
||||
namespace LibGSF.Input
|
||||
{
|
||||
public class GsfInfileStdio : GsfInfile
|
||||
{
|
||||
#region Properties
|
||||
|
||||
public string Root { get; set; } = null;
|
||||
|
||||
public List<string> Children { get; set; } = new List<string>();
|
||||
|
||||
#endregion
|
||||
|
||||
#region Constructor
|
||||
|
||||
/// <summary>
|
||||
/// Private constructor
|
||||
/// </summary>
|
||||
private GsfInfileStdio() { }
|
||||
|
||||
/// <param name="root">In locale dependent encoding</param>
|
||||
/// <param name="err">Optionally null</param>
|
||||
/// <returns>A new file or null.</returns>
|
||||
public static GsfInfileStdio Create(string root, ref Exception err)
|
||||
{
|
||||
if (!Directory.Exists(root))
|
||||
return null;
|
||||
|
||||
GsfInfileStdio ifs = new GsfInfileStdio
|
||||
{
|
||||
Root = root,
|
||||
};
|
||||
|
||||
foreach (string child in Directory.EnumerateFileSystemEntries(root))
|
||||
{
|
||||
ifs.Children.Add(child);
|
||||
}
|
||||
|
||||
ifs.SetNameFromFilename(root);
|
||||
return ifs;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Functions
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override GsfInput DupImpl(ref Exception err)
|
||||
{
|
||||
GsfInfileStdio dst = new GsfInfileStdio
|
||||
{
|
||||
Root = this.Root,
|
||||
};
|
||||
|
||||
dst.Children.AddRange(Children);
|
||||
|
||||
return dst;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override byte[] ReadImpl(int num_bytes, byte[] optional_buffer, int bufferPtr = 0) => null;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override string NameByIndex(int i) => i < Children.Count ? Children[i] : null;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override GsfInput ChildByIndex(int i, ref Exception error)
|
||||
{
|
||||
string name = NameByIndex(i);
|
||||
return name != null ? OpenChild(name, ref error) : null;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override GsfInput ChildByName(string name, ref Exception error)
|
||||
{
|
||||
for (int i = 0; i < Children.Count; i++)
|
||||
{
|
||||
string child = Children[i];
|
||||
if (child.Equals(name))
|
||||
return OpenChild(name, ref error);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override int NumChildren() => Children.Count;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Utilities
|
||||
|
||||
private GsfInput OpenChild(string name, ref Exception err)
|
||||
{
|
||||
string path = Path.Combine(Root, name);
|
||||
|
||||
GsfInput child = null;
|
||||
if (Directory.Exists(path) || File.Exists(path))
|
||||
child = Create(path, ref err);
|
||||
|
||||
return child;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
487
BurnOutSharp/External/libgsf/Input/GsfInfileTar.cs
vendored
Normal file
487
BurnOutSharp/External/libgsf/Input/GsfInfileTar.cs
vendored
Normal file
@@ -0,0 +1,487 @@
|
||||
/*
|
||||
* 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
|
||||
{
|
||||
public class TarChild
|
||||
{
|
||||
public string Name { get; set; }
|
||||
|
||||
public DateTime? ModTime { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The location of data
|
||||
/// </summary>
|
||||
public long Offset { get; set; }
|
||||
|
||||
public long Length { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The directory object, or null for a data file
|
||||
/// </summary>
|
||||
public GsfInfileTar Dir { get; set; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tar header from POSIX 1003.1-1990.
|
||||
/// </summary>
|
||||
public class TarHeader
|
||||
{
|
||||
public byte[] Name { get; set; } = new byte[100]; /* 0 */
|
||||
|
||||
public byte[] Mode { get; set; } = new byte[8]; /* 100 (octal) */
|
||||
|
||||
public byte[] UID { get; set; } = new byte[8]; /* 108 (octal) */
|
||||
|
||||
public byte[] GID { get; set; } = new byte[8]; /* 116 (octal) */
|
||||
|
||||
public byte[] Size { get; set; } = new byte[12]; /* 124 (octal) */
|
||||
|
||||
public byte[] MTime { get; set; } = new byte[12]; /* 136 (octal) */
|
||||
|
||||
public byte[] Chksum { get; set; } = new byte[8]; /* 148 (octal) */
|
||||
|
||||
public byte TypeFlag { get; set; } /* 156 */
|
||||
|
||||
public byte[] Linkname { get; set; } = new byte[100]; /* 157 */
|
||||
|
||||
public byte[] Magic { get; set; } = new byte[6]; /* 257 */
|
||||
|
||||
public byte[] Version { get; set; } = new byte[2]; /* 263 */
|
||||
|
||||
public byte[] UName { get; set; } = new byte[32]; /* 265 */
|
||||
|
||||
public byte[] GName { get; set; } = new byte[32]; /* 297 */
|
||||
|
||||
public byte[] DevMajor { get; set; } = new byte[8]; /* 329 (octal) */
|
||||
|
||||
public byte[] DevMinor { get; set; } = new byte[8]; /* 337 (octal) */
|
||||
|
||||
public byte[] Prefix { get; set; } = new byte[155]; /* 345 */
|
||||
|
||||
public byte[] Filler { get; set; } = new byte[12]; /* 500 */
|
||||
}
|
||||
|
||||
public class GsfInfileTar : GsfInfile
|
||||
{
|
||||
#region Constants
|
||||
|
||||
private const int HEADER_SIZE = 512; // sizeof(TarHeader);
|
||||
|
||||
private const int BLOCK_SIZE = 512;
|
||||
|
||||
private const string MAGIC_LONGNAME = "././@LongLink";
|
||||
|
||||
#endregion
|
||||
|
||||
#region Properties
|
||||
|
||||
public GsfInput Source { get; set; } = null;
|
||||
|
||||
public List<TarChild> Children { get; set; } = new List<TarChild>();
|
||||
|
||||
public Exception Err { get; set; } = null;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Constructor and Deconstructor
|
||||
|
||||
/// <summary>
|
||||
/// Private constructor
|
||||
/// </summary>
|
||||
private GsfInfileTar() => InitInfo();
|
||||
|
||||
/// <summary>
|
||||
/// Opens the root directory of a Tar file.
|
||||
/// </summary>
|
||||
/// <param name="source">A base GsfInput</param>
|
||||
/// <param name="err">An Exception, optionally null</param>
|
||||
/// <returns>The new tar file handler</returns>
|
||||
/// <remarks>This adds a reference to <paramref name="source"/>.</remarks>
|
||||
public static GsfInfileTar Create(GsfInput source, ref Exception err)
|
||||
{
|
||||
if (source == null)
|
||||
return null;
|
||||
|
||||
GsfInfileTar tar = new GsfInfileTar
|
||||
{
|
||||
Source = source,
|
||||
};
|
||||
|
||||
if (tar.Err != null)
|
||||
{
|
||||
err = tar.Err;
|
||||
return null;
|
||||
}
|
||||
|
||||
return tar;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Destructor
|
||||
/// </summary>
|
||||
~GsfInfileTar()
|
||||
{
|
||||
Source = null;
|
||||
Err = null;
|
||||
Children.Clear();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Functions
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override GsfInput DupImpl(ref Exception err)
|
||||
{
|
||||
if (Err != null)
|
||||
{
|
||||
err = Err;
|
||||
return null;
|
||||
}
|
||||
|
||||
GsfInfileTar res = new GsfInfileTar();
|
||||
res.Source = Source;
|
||||
|
||||
for (int i = 0; i < Children.Count; i++)
|
||||
{
|
||||
// This copies the structure.
|
||||
TarChild c = new TarChild
|
||||
{
|
||||
Name = Children[i].Name,
|
||||
ModTime = Children[i].ModTime,
|
||||
Offset = Children[i].Offset,
|
||||
Length = Children[i].Length,
|
||||
Dir = Children[i].Dir,
|
||||
};
|
||||
|
||||
res.Children.Add(c);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override byte[] ReadImpl(int num_bytes, byte[] optional_buffer, int bufferPtr = 0) => null;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override bool Seek(long offset, SeekOrigin whence) => false;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override GsfInput ChildByIndex(int i, ref Exception error)
|
||||
{
|
||||
if (error != null)
|
||||
error = null;
|
||||
|
||||
if (i < 0 || i >= Children.Count)
|
||||
return null;
|
||||
|
||||
TarChild c = Children[i];
|
||||
if (c.Dir != null)
|
||||
{
|
||||
return c.Dir;
|
||||
}
|
||||
else
|
||||
{
|
||||
GsfInputProxy input = GsfInputProxy.Create(Source, c.Offset, c.Length);
|
||||
input.ModTime = c.ModTime;
|
||||
input.Name = c.Name;
|
||||
return input;
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override string NameByIndex(int i)
|
||||
{
|
||||
if (i < 0 || i >= Children.Count)
|
||||
return null;
|
||||
|
||||
return Children[i].Name;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override GsfInput ChildByName(string name, ref Exception error)
|
||||
{
|
||||
for (int i = 0; i < Children.Count; i++)
|
||||
{
|
||||
TarChild c = Children[i];
|
||||
if (name == c.Name)
|
||||
return ChildByIndex(i, ref error);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override int NumChildren() => Children.Count;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Utilities
|
||||
|
||||
private long UnpackOctal(byte[] s, int len)
|
||||
{
|
||||
// Different specifications differ on what terminating characters
|
||||
// are allowed. It doesn't hurt for us to allow both space and
|
||||
// NUL.
|
||||
if (len == 0 || (s[len - 1] != 0 && s[len - 1] != ' '))
|
||||
{
|
||||
Err = new Exception("Invalid tar header");
|
||||
return 0;
|
||||
}
|
||||
|
||||
len--;
|
||||
|
||||
long res = 0;
|
||||
int sPtr = 0; // s[0]
|
||||
while (len-- != 0)
|
||||
{
|
||||
byte c = s[sPtr++];
|
||||
if (c < '0' || c > '7')
|
||||
{
|
||||
Err = new Exception("Invalid tar header");
|
||||
return 0;
|
||||
}
|
||||
|
||||
res = (res << 3) | (c - '0');
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
private GsfInfileTar CreateDirectory(string name)
|
||||
{
|
||||
TarChild c = new TarChild
|
||||
{
|
||||
Offset = 0,
|
||||
Length = 0,
|
||||
Name = name,
|
||||
ModTime = null,
|
||||
Dir = new GsfInfileTar
|
||||
{
|
||||
Source = this.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;
|
||||
string s = name;
|
||||
int sPtr = 0; // s[0]
|
||||
while (true)
|
||||
{
|
||||
int s0 = sPtr;
|
||||
|
||||
// Find a directory component, if any.
|
||||
while (true)
|
||||
{
|
||||
if (s[sPtr] == 0)
|
||||
{
|
||||
if (last && sPtr != s0)
|
||||
break;
|
||||
else
|
||||
return dir;
|
||||
}
|
||||
|
||||
// This is deliberately slash-only.
|
||||
if (s[sPtr] == '/')
|
||||
break;
|
||||
|
||||
sPtr++;
|
||||
}
|
||||
|
||||
string dirname = s.Substring(s0, sPtr - s0);
|
||||
while (s[sPtr] == '/')
|
||||
{
|
||||
sPtr++;
|
||||
}
|
||||
|
||||
if (dirname != ".")
|
||||
{
|
||||
Exception err = null;
|
||||
GsfInput subdir = ChildByName(dirname, ref err);
|
||||
if (subdir != null)
|
||||
dir = subdir is GsfInfileTar ? (GsfInfileTar)subdir : dir;
|
||||
else
|
||||
dir = dir.CreateDirectory(dirname);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Read tar headers and do some sanity checking
|
||||
/// along the way.
|
||||
/// </summary>
|
||||
private void InitInfo()
|
||||
{
|
||||
long pos0 = Source.CurrentOffset;
|
||||
string pending_longname = null;
|
||||
|
||||
TarHeader header = new TarHeader();
|
||||
TarHeader end = new TarHeader();
|
||||
|
||||
byte[] headerBytes = new byte[HEADER_SIZE];
|
||||
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.
|
||||
GsfInfileTar dir;
|
||||
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,
|
||||
};
|
||||
|
||||
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
|
||||
}
|
||||
}
|
||||
789
BurnOutSharp/External/libgsf/Input/GsfInfileZip.cs
vendored
Normal file
789
BurnOutSharp/External/libgsf/Input/GsfInfileZip.cs
vendored
Normal file
@@ -0,0 +1,789 @@
|
||||
/* 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.GsfZipImpl;
|
||||
|
||||
namespace LibGSF.Input
|
||||
{
|
||||
public class ZipInfo
|
||||
{
|
||||
#region Properties
|
||||
|
||||
public uint Entries { get; set; }
|
||||
|
||||
public long DirPos { get; set; }
|
||||
|
||||
public List<GsfZipDirectoryEntry> DirectoryEntries { get; set; }
|
||||
|
||||
public GsfZipVDir VDir { get; set; }
|
||||
|
||||
public int RefCount { get; set; }
|
||||
|
||||
#endregion
|
||||
|
||||
#region Functions
|
||||
|
||||
public ZipInfo Ref()
|
||||
{
|
||||
RefCount++;
|
||||
return this;
|
||||
}
|
||||
|
||||
public void Unref()
|
||||
{
|
||||
if (RefCount-- != 1)
|
||||
return;
|
||||
|
||||
VDir.Free(false);
|
||||
for (int i = 0; i < DirectoryEntries.Count; i++)
|
||||
{
|
||||
GsfZipDirectoryEntry e = DirectoryEntries[i];
|
||||
e.Free();
|
||||
}
|
||||
|
||||
DirectoryEntries = null;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
public class GsfInfileZip : GsfInfile
|
||||
{
|
||||
#region Properties
|
||||
|
||||
public GsfInput Source { get; set; } = null;
|
||||
|
||||
public ZipInfo Info { get; set; } = null;
|
||||
|
||||
public bool Zip64 { get; set; } = false;
|
||||
|
||||
public GsfZipVDir VDir { get; set; } = null;
|
||||
|
||||
public ZStream Stream { get; set; } = null;
|
||||
|
||||
public long RestLen { get; set; } = 0;
|
||||
|
||||
public long CRestLen { get; set; } = 0;
|
||||
|
||||
public byte[] Buf { get; set; } = null;
|
||||
|
||||
public int BufSize { get; set; } = 0;
|
||||
|
||||
public long SeekSkipped { get; set; } = 0;
|
||||
|
||||
public Exception Err { get; set; } = null;
|
||||
|
||||
public GsfInfileZip DupParent { get; set; }
|
||||
|
||||
#endregion
|
||||
|
||||
#region Constructor and Destructor
|
||||
|
||||
/// <summary>
|
||||
/// Private constructor
|
||||
/// </summary>
|
||||
private GsfInfileZip(GsfInfileZip dupParent = null)
|
||||
{
|
||||
DupParent = dupParent;
|
||||
if (DupParent != null)
|
||||
{
|
||||
// Special call from zip_dup.
|
||||
Exception err = null;
|
||||
Source = DupParent.Source.Duplicate(ref err);
|
||||
Err = err;
|
||||
|
||||
Info = DupParent.Info.Ref();
|
||||
Zip64 = DupParent.Zip64;
|
||||
DupParent = null;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!InitInfo())
|
||||
VDir = Info.VDir;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Opens the root directory of a Zip file.
|
||||
/// </summary>
|
||||
/// <param name="source">A base GsfInput</param>
|
||||
/// <param name="err">Place to store an Exception if anything goes wrong</param>
|
||||
/// <returns>The new zip file handler</returns>
|
||||
/// <remarks>This adds a reference to <paramref name="source"/>.</remarks>
|
||||
public static GsfInfileZip Create(GsfInput source, ref Exception err)
|
||||
{
|
||||
if (source == null)
|
||||
return null;
|
||||
|
||||
return new GsfInfileZip
|
||||
{
|
||||
Source = source,
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Destructor
|
||||
/// </summary>
|
||||
~GsfInfileZip()
|
||||
{
|
||||
if (Info != null)
|
||||
{
|
||||
Info.Unref();
|
||||
Info = null;
|
||||
}
|
||||
|
||||
if (Stream != null)
|
||||
{
|
||||
Stream.inflateEnd();
|
||||
Stream = null;
|
||||
}
|
||||
|
||||
Buf = null;
|
||||
|
||||
SetSource(null);
|
||||
Err = null;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Functions
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override GsfInput DupImpl(ref Exception err)
|
||||
{
|
||||
GsfInfileZip dst = PartiallyDuplicate(ref err);
|
||||
if (dst == null)
|
||||
return null;
|
||||
|
||||
dst.VDir = VDir;
|
||||
|
||||
if (dst.VDir.DirectoryEntry != null && dst.ChildInit(ref err))
|
||||
return null;
|
||||
|
||||
return dst;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override byte[] ReadImpl(int num_bytes, byte[] optional_buffer, int bufferPtr = 0)
|
||||
{
|
||||
GsfZipVDir vdir = VDir;
|
||||
long pos;
|
||||
|
||||
if (RestLen < num_bytes)
|
||||
return null;
|
||||
|
||||
switch (vdir.DirectoryEntry.CompressionMethod)
|
||||
{
|
||||
case GsfZipCompressionMethod.GSF_ZIP_STORED:
|
||||
RestLen -= num_bytes;
|
||||
pos = VDir.DirectoryEntry.DataOffset + CurrentOffset;
|
||||
if (Source.Seek(pos, SeekOrigin.Begin))
|
||||
return null;
|
||||
|
||||
return Source.Read(num_bytes, optional_buffer);
|
||||
|
||||
case GsfZipCompressionMethod.GSF_ZIP_DEFLATED:
|
||||
if (optional_buffer == null)
|
||||
{
|
||||
if (BufSize < num_bytes)
|
||||
{
|
||||
BufSize = Math.Max(num_bytes, 256);
|
||||
Buf = new byte[BufSize];
|
||||
}
|
||||
|
||||
optional_buffer = Buf;
|
||||
}
|
||||
|
||||
Stream.avail_out = num_bytes;
|
||||
Stream.next_out = optional_buffer;
|
||||
|
||||
do
|
||||
{
|
||||
int err;
|
||||
long startlen;
|
||||
|
||||
if (CRestLen > 0 && Stream.avail_in == 0)
|
||||
if (!UpdateStreamInput())
|
||||
break;
|
||||
|
||||
startlen = Stream.total_out;
|
||||
err = Stream.inflate(Z_NO_FLUSH);
|
||||
|
||||
if (err == Z_STREAM_END)
|
||||
RestLen = 0;
|
||||
else if (err == Z_OK)
|
||||
RestLen -= (Stream.total_out - startlen);
|
||||
else
|
||||
return null; // Error, probably corrupted
|
||||
|
||||
} while (RestLen != 0 && Stream.avail_out != 0);
|
||||
|
||||
return optional_buffer;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Global flag -- we don't want one per stream.
|
||||
/// </summary>
|
||||
private static bool warned = false;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override bool Seek(long offset, SeekOrigin whence)
|
||||
{
|
||||
long pos = offset;
|
||||
|
||||
// Note, that pos has already been sanity checked.
|
||||
switch (whence)
|
||||
{
|
||||
case SeekOrigin.Begin: break;
|
||||
case SeekOrigin.Current: pos += CurrentOffset; break;
|
||||
case SeekOrigin.End: pos += Size; break;
|
||||
default: return true;
|
||||
}
|
||||
|
||||
if (Stream != null)
|
||||
{
|
||||
Stream.inflateEnd();
|
||||
Stream = new ZStream();
|
||||
}
|
||||
|
||||
Exception err = null;
|
||||
if (ChildInit(ref err))
|
||||
{
|
||||
Console.Error.WriteLine("Failure initializing zip child");
|
||||
return true;
|
||||
}
|
||||
|
||||
CurrentOffset = 0;
|
||||
if (SeekEmulate(pos))
|
||||
return true;
|
||||
|
||||
SeekSkipped += pos;
|
||||
if (!warned && SeekSkipped != pos && SeekSkipped >= 1000000)
|
||||
{
|
||||
warned = true;
|
||||
Console.Error.WriteLine("Seeking in zip child streams is awfully slow.");
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override GsfInput ChildByIndex(int i, ref Exception error)
|
||||
{
|
||||
GsfZipVDir child_vdir = VDir.ChildByIndex(i);
|
||||
if (child_vdir != null)
|
||||
return NewChild(child_vdir, ref error);
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override string NameByIndex(int i)
|
||||
{
|
||||
GsfZipVDir child_vdir = VDir.ChildByIndex(i);
|
||||
if (child_vdir != null)
|
||||
return child_vdir.Name;
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override GsfInput ChildByName(string name, ref Exception error)
|
||||
{
|
||||
GsfZipVDir child_vdir = VDir.ChildByName(name);
|
||||
if (child_vdir != null)
|
||||
return NewChild(child_vdir, ref error);
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override int NumChildren()
|
||||
{
|
||||
if (VDir == null)
|
||||
return -1;
|
||||
|
||||
if (!VDir.IsDirectory)
|
||||
return -1;
|
||||
|
||||
return VDir.Children.Count;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Utilities
|
||||
|
||||
private static DateTime? MakeModTime(uint dostime)
|
||||
{
|
||||
if (dostime == 0)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
else
|
||||
{
|
||||
int year = (int)(dostime >> 25) + 1980;
|
||||
int month = (int)(dostime >> 21) & 0x0f;
|
||||
int day = (int)(dostime >> 16) & 0x1f;
|
||||
int hour = (int)(dostime >> 11) & 0x0f;
|
||||
int minute = (int)(dostime >> 5) & 0x3f;
|
||||
int second = (int)(dostime & 0x1f) * 2;
|
||||
|
||||
DateTime modtime = new DateTime(year, month, day, hour, minute, second, DateTimeKind.Utc);
|
||||
|
||||
return modtime;
|
||||
}
|
||||
}
|
||||
|
||||
private long FindTrailer(uint sig, int size)
|
||||
{
|
||||
byte sig1 = (byte)(sig & 0xff);
|
||||
|
||||
long filesize = Source.Size;
|
||||
if (filesize < size)
|
||||
return -1;
|
||||
|
||||
long trailer_offset = filesize;
|
||||
long maplen = filesize & (ZIP_BUF_SIZE - 1);
|
||||
if (maplen == 0)
|
||||
maplen = ZIP_BUF_SIZE;
|
||||
|
||||
long offset = filesize - maplen; /* offset is now BUFSIZ aligned */
|
||||
|
||||
while (true)
|
||||
{
|
||||
if (Source.Seek(offset, SeekOrigin.Begin))
|
||||
return -1;
|
||||
|
||||
byte[] data = Source.Read((int)maplen, null);
|
||||
if (data == null)
|
||||
return -1;
|
||||
|
||||
int p = 0; // data[0]
|
||||
|
||||
for (int s = (int)(p + maplen - 1); (s >= p); s--, trailer_offset--)
|
||||
{
|
||||
if (data[s] == sig1 && p + maplen - 1 - s > size - 2 && BitConverter.ToUInt32(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)BitConverter.ToUInt16(extra, extraPtr);
|
||||
uint flen = BitConverter.ToUInt16(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
|
||||
|| BitConverter.ToUInt32(data, 0) != ZIP_DIRENT_SIGNATURE)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
ushort name_len = BitConverter.ToUInt16(header, ZIP_DIRENT_NAME_SIZE);
|
||||
ushort extras_len = BitConverter.ToUInt16(header, ZIP_DIRENT_EXTRAS_SIZE);
|
||||
ushort comment_len = BitConverter.ToUInt16(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 = BitConverter.ToUInt32(header, ZIP_DIRENT_FLAGS);
|
||||
GsfZipCompressionMethod compression_method = (GsfZipCompressionMethod)BitConverter.ToUInt16(header, ZIP_DIRENT_COMPR_METHOD);
|
||||
uint dostime = BitConverter.ToUInt32(header, ZIP_DIRENT_DOSTIME);
|
||||
uint crc32 = BitConverter.ToUInt32(header, ZIP_DIRENT_CRC32);
|
||||
long csize = BitConverter.ToUInt32(header, ZIP_DIRENT_CSIZE);
|
||||
long usize = BitConverter.ToUInt32(header, ZIP_DIRENT_USIZE);
|
||||
long off = BitConverter.ToUInt32(header, ZIP_DIRENT_OFFSET);
|
||||
uint disk_start = BitConverter.ToUInt16(header, ZIP_DIRENT_DISKSTART);
|
||||
|
||||
if (usize == 0xffffffffu && elen >= 8)
|
||||
{
|
||||
usize = (long)BitConverter.ToUInt64(extra, extraPtr);
|
||||
extraPtr += 8;
|
||||
elen -= 8;
|
||||
}
|
||||
if (csize == 0xffffffffu && elen >= 8)
|
||||
{
|
||||
csize = (long)BitConverter.ToUInt64(extra, extraPtr);
|
||||
extraPtr += 8;
|
||||
elen -= 8;
|
||||
}
|
||||
if (off == 0xffffffffu && elen >= 8)
|
||||
{
|
||||
off = (long)BitConverter.ToUInt64(extra, extraPtr);
|
||||
extraPtr += 8;
|
||||
elen -= 8;
|
||||
}
|
||||
if (disk_start == 0xffffu && elen >= 4)
|
||||
{
|
||||
disk_start = BitConverter.ToUInt32(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 = csize;
|
||||
dirent.UncompressedSize = usize;
|
||||
dirent.Offset = off;
|
||||
dirent.DosTime = dostime;
|
||||
dirent.Zip64 = zip64;
|
||||
|
||||
offset += ZIP_DIRENT_SIZE + vlen;
|
||||
|
||||
return dirent;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a partial duplicate.
|
||||
/// </summary>
|
||||
private GsfInfileZip PartiallyDuplicate(ref Exception err)
|
||||
{
|
||||
GsfInfileZip dst = new GsfInfileZip(this);
|
||||
|
||||
if (dst.Err != null)
|
||||
{
|
||||
err = dst.Err;
|
||||
return null;
|
||||
}
|
||||
|
||||
return dst;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Read zip headers and do some sanity checking
|
||||
/// along the way.
|
||||
/// </summary>
|
||||
/// <returns>true on error setting Err.</returns>
|
||||
private bool ReadDirectoryEntries()
|
||||
{
|
||||
// Find and check the trailing header
|
||||
long offset = FindTrailer(ZIP_TRAILER_SIGNATURE, ZIP_TRAILER_SIZE);
|
||||
if (offset < ZIP_ZIP64_LOCATOR_SIZE || Source.Seek(offset - ZIP_ZIP64_LOCATOR_SIZE, SeekOrigin.Begin))
|
||||
{
|
||||
Err = new Exception("Broken zip file structure");
|
||||
return true;
|
||||
}
|
||||
|
||||
byte[] locator = Source.Read(ZIP_TRAILER_SIZE + ZIP_ZIP64_LOCATOR_SIZE, null);
|
||||
if (locator == null)
|
||||
{
|
||||
Err = new Exception("Broken zip file structure");
|
||||
return true;
|
||||
}
|
||||
|
||||
int data = ZIP_ZIP64_LOCATOR_SIZE; // locator + ZIP_ZIP64_LOCATOR_SIZE
|
||||
|
||||
ulong entries = BitConverter.ToUInt16(locator, data + ZIP_TRAILER_ENTRIES);
|
||||
ulong dir_pos = BitConverter.ToUInt32(locator, data + ZIP_TRAILER_DIR_POS);
|
||||
|
||||
if (BitConverter.ToUInt32(locator, 0) == ZIP_ZIP64_LOCATOR_SIGNATURE)
|
||||
{
|
||||
Zip64 = true;
|
||||
|
||||
data = 0; // locator[0]
|
||||
uint disk = BitConverter.ToUInt32(locator, data + ZIP_ZIP64_LOCATOR_DISK);
|
||||
ulong zip64_eod_offset = BitConverter.ToUInt64(locator, data + ZIP_ZIP64_LOCATOR_OFFSET);
|
||||
uint disks = BitConverter.ToUInt32(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 || BitConverter.ToUInt32(locator, data) != ZIP_TRAILER64_SIGNATURE)
|
||||
{
|
||||
Err = new Exception("Broken zip file structure");
|
||||
return true;
|
||||
}
|
||||
|
||||
entries = BitConverter.ToUInt64(locator, data + ZIP_TRAILER64_ENTRIES);
|
||||
dir_pos = BitConverter.ToUInt64(locator, data + ZIP_TRAILER64_DIR_POS);
|
||||
}
|
||||
|
||||
Info = new ZipInfo()
|
||||
{
|
||||
DirectoryEntries = new List<GsfZipDirectoryEntry>(),
|
||||
RefCount = 1,
|
||||
Entries = (uint)entries,
|
||||
DirPos = (long)dir_pos,
|
||||
};
|
||||
|
||||
// Read the directory
|
||||
uint i = 0;
|
||||
for (offset = (long)dir_pos; i < entries; i++)
|
||||
{
|
||||
GsfZipDirectoryEntry d = NewDirectoryEntry(ref offset);
|
||||
if (d == null)
|
||||
{
|
||||
Err = new Exception("Error reading zip dirent");
|
||||
return true;
|
||||
}
|
||||
|
||||
Info.DirectoryEntries.Add(d);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private void BuildVirtualDirectories()
|
||||
{
|
||||
Info.VDir = GsfZipVDir.Create("", true, null);
|
||||
for (int i = 0; i < Info.DirectoryEntries.Count; i++)
|
||||
{
|
||||
GsfZipDirectoryEntry dirent = Info.DirectoryEntries[i];
|
||||
Info.VDir.Insert(dirent.Name, dirent);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Read zip headers and do some sanity checking
|
||||
/// along the way.
|
||||
/// </summary>
|
||||
/// <returns>true on error setting Err.</returns>
|
||||
private bool InitInfo()
|
||||
{
|
||||
bool ret = ReadDirectoryEntries();
|
||||
if (ret != false)
|
||||
return ret;
|
||||
|
||||
BuildVirtualDirectories();
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns true on error
|
||||
/// </summary>
|
||||
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 (BitConverter.ToUInt32(data, 0) != ZIP_HEADER_SIGNATURE)
|
||||
{
|
||||
err = "Error incorrect zip header";
|
||||
Console.Error.WriteLine("Header is 0x%x\n", BitConverter.ToUInt32(data, 0));
|
||||
Console.Error.WriteLine("Expected 0x%x\n", ZIP_HEADER_SIGNATURE);
|
||||
}
|
||||
|
||||
if (err != null)
|
||||
{
|
||||
errmsg = new Exception(err);
|
||||
return true;
|
||||
}
|
||||
|
||||
uint name_len = BitConverter.ToUInt16(data, ZIP_HEADER_NAME_SIZE);
|
||||
uint extras_len = BitConverter.ToUInt16(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)
|
||||
{
|
||||
int errno;
|
||||
|
||||
if (Stream == null)
|
||||
Stream = new ZStream();
|
||||
|
||||
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
|
||||
}
|
||||
}
|
||||
529
BurnOutSharp/External/libgsf/Input/GsfInput.cs
vendored
Normal file
529
BurnOutSharp/External/libgsf/Input/GsfInput.cs
vendored
Normal file
@@ -0,0 +1,529 @@
|
||||
/* 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;
|
||||
|
||||
namespace LibGSF.Input
|
||||
{
|
||||
public abstract class GsfInput : IDisposable
|
||||
{
|
||||
#region Constants
|
||||
|
||||
private const int GSF_READ_BUFSIZE = 1024 * 4;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Properties
|
||||
|
||||
public long Size { get; protected internal set; }
|
||||
|
||||
public long CurrentOffset { get; protected internal set; }
|
||||
|
||||
public string Name { get; protected internal set; }
|
||||
|
||||
public GsfInfile Container { get; protected internal set; }
|
||||
|
||||
public DateTime? ModTime { get; protected internal set; }
|
||||
|
||||
public GsfOpenPkgRels Relations { get; protected internal set; }
|
||||
|
||||
#endregion
|
||||
|
||||
#region Functions
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
Container = null;
|
||||
Name = null;
|
||||
ModTime = default;
|
||||
}
|
||||
|
||||
public void Init()
|
||||
{
|
||||
Size = 0;
|
||||
CurrentOffset = 0;
|
||||
Name = null;
|
||||
Container = null;
|
||||
}
|
||||
|
||||
/// <param name="err">Place to store an Exception if anything goes wrong</param>
|
||||
/// <returns>The duplicate</returns>
|
||||
public GsfInput Duplicate(ref Exception err)
|
||||
{
|
||||
GsfInput dst = DupImpl(ref err);
|
||||
if (dst != null)
|
||||
{
|
||||
if (dst.Size != Size)
|
||||
{
|
||||
err = new Exception("Duplicate size mismatch");
|
||||
return null;
|
||||
}
|
||||
|
||||
if (dst.Seek(CurrentOffset, SeekOrigin.Begin))
|
||||
{
|
||||
err = new Exception("Seek failed");
|
||||
return null;
|
||||
}
|
||||
|
||||
dst.Name = Name;
|
||||
dst.Container = Container;
|
||||
}
|
||||
|
||||
return dst;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Attempts to open a 'sibling' of input. The caller is responsible for
|
||||
/// managing the resulting object.
|
||||
/// </summary>
|
||||
/// <param name="name">Name.</param>
|
||||
/// <param name="err">Place to store an Exception if anything goes wrong</param>
|
||||
/// <returns>A related GsfInput</returns>
|
||||
/// <remarks>
|
||||
/// UNIMPLEMENTED BY ANY BACKEND
|
||||
/// and it is probably unnecessary. Container provides
|
||||
/// enough power to do what is necessary.
|
||||
/// </remarks>
|
||||
public GsfInput OpenSibling(string name, ref Exception err) => OpenSiblingImpl(name, ref err);
|
||||
|
||||
/// <summary>
|
||||
/// Are we at the end of the file?
|
||||
/// </summary>
|
||||
/// <returns>true if the input is at the eof.</returns>
|
||||
public bool EOF() => CurrentOffset >= Size;
|
||||
|
||||
/// <summary>
|
||||
/// Read at least <paramref name="num_bytes"/>. Does not change the current position if there
|
||||
/// is an error. Will only read if the entire amount can be read. Invalidates
|
||||
/// the buffer associated with previous calls to gsf_input_read.
|
||||
/// </summary>
|
||||
/// <param name="num_bytes">Number of bytes to read</param>
|
||||
/// <param name="optional_buffer">Pointer to destination memory area</param>
|
||||
/// <returns>
|
||||
/// Pointer to the buffer or null if there is
|
||||
/// an error or 0 bytes are requested.
|
||||
/// </returns>
|
||||
public byte[] Read(int num_bytes, byte[] optional_buffer, int bufferPtr = 0)
|
||||
{
|
||||
long newpos = CurrentOffset + num_bytes;
|
||||
|
||||
if (newpos <= CurrentOffset || newpos > Size)
|
||||
return null;
|
||||
|
||||
byte[] res = ReadImpl(num_bytes, optional_buffer, bufferPtr);
|
||||
if (res == null)
|
||||
return null;
|
||||
|
||||
CurrentOffset = newpos;
|
||||
return res;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Read <paramref name="num_bytes"/>. Does not change the current position if there
|
||||
/// is an error. Will only read if the entire amount can be read.
|
||||
/// </summary>
|
||||
/// <param name="num_bytes">Number of bytes to read</param>
|
||||
/// <param name="bytes_read">Copy of <paramref name="num_bytes"/></param>
|
||||
/// <returns>The data read.</returns>
|
||||
public byte[] Read0(int num_bytes, out int bytes_read)
|
||||
{
|
||||
bytes_read = num_bytes;
|
||||
|
||||
if (num_bytes < 0 || (long)num_bytes > Remaining())
|
||||
return null;
|
||||
|
||||
byte[] res = new byte[num_bytes];
|
||||
if (Read(num_bytes, res) != null)
|
||||
return res;
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <returns>The number of bytes left in the file.</returns>
|
||||
public long Remaining() => Size - CurrentOffset;
|
||||
|
||||
/// <summary>
|
||||
/// Move the current location in the input stream.
|
||||
/// </summary>
|
||||
/// <param name="offset">Target offset</param>
|
||||
/// <param name="whence">
|
||||
/// Determines whether the offset is relative to the beginning or
|
||||
/// the end of the stream, or to the current location.
|
||||
/// </param>
|
||||
/// <returns>true on error.</returns>
|
||||
public virtual 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 (Seek(offset, whence))
|
||||
return true;
|
||||
|
||||
CurrentOffset = pos;
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <param name="filename">The (fs-sys encoded) filename</param>
|
||||
/// <returns>true if the assignment was ok.</returns>
|
||||
public bool SetNameFromFilename(string filename)
|
||||
{
|
||||
string name = null;
|
||||
if (filename != null)
|
||||
{
|
||||
byte[] filenameBytes = Encoding.Unicode.GetBytes(filename);
|
||||
filenameBytes = Encoding.Convert(Encoding.Unicode, Encoding.UTF8, filenameBytes);
|
||||
name = Encoding.UTF8.GetString(filenameBytes);
|
||||
}
|
||||
|
||||
Name = name;
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Emulate forward seeks by reading.
|
||||
/// </summary>
|
||||
/// <param name="pos">Absolute position to seek to</param>
|
||||
/// <returns>true if the emulation failed.</returns>
|
||||
public bool SeekEmulate(long pos)
|
||||
{
|
||||
if (pos < CurrentOffset)
|
||||
return true;
|
||||
|
||||
while (pos > CurrentOffset)
|
||||
{
|
||||
long readcount = Math.Min(pos - CurrentOffset, 8192);
|
||||
if (Read((int)readcount, null) == null)
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Copy the contents from input to <paramref name="output"/> from their respective
|
||||
/// current positions. So if you want to be sure to copy *everything*,
|
||||
/// make sure to call input.Seek(0, SeekOrigin.Begin) and
|
||||
/// output.Seek(0, SeekOrigin.Begin) first, if applicable.
|
||||
/// </summary>
|
||||
/// <param name="output">A non-null GsfOutput</param>
|
||||
/// <returns>true on success</returns>
|
||||
public bool Copy(GsfOutput output)
|
||||
{
|
||||
if (output == null)
|
||||
return false;
|
||||
|
||||
bool success = true;
|
||||
long remaining;
|
||||
while (success && (remaining = Remaining()) > 0)
|
||||
{
|
||||
long toread = Math.Min(remaining, GSF_READ_BUFSIZE);
|
||||
byte[] buffer = Read((int)toread, null);
|
||||
if (buffer != null && buffer.Length != 0)
|
||||
success = output.Write((int)toread, buffer);
|
||||
else
|
||||
success = false;
|
||||
}
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This functions takes ownership of the incoming reference and yields a
|
||||
/// new one as its output.
|
||||
/// </summary>
|
||||
/// <returns>
|
||||
/// A stream equivalent to the source stream,
|
||||
/// but uncompressed if the source was compressed.
|
||||
/// </returns>
|
||||
public GsfInput Uncompress()
|
||||
{
|
||||
long cur_offset = CurrentOffset;
|
||||
byte[] header = new byte[4];
|
||||
|
||||
if (Seek(0, SeekOrigin.Begin))
|
||||
goto error;
|
||||
|
||||
// Read header up front, so we avoid extra seeks in tests.
|
||||
if (Read(4, header) == null)
|
||||
goto error;
|
||||
|
||||
// Let's try gzip.
|
||||
{
|
||||
byte[] gzip_sig = new byte[2] { 0x1f, 0x8b };
|
||||
if (header.Take(2).SequenceEqual(gzip_sig))
|
||||
{
|
||||
Exception err = null;
|
||||
GsfInput res = GsfInputGZip.Create(null, ref err);
|
||||
if (res != null)
|
||||
return res.Uncompress();
|
||||
}
|
||||
}
|
||||
|
||||
// Let's try bzip.
|
||||
{
|
||||
byte[] bzip_sig = new byte[3] { (byte)'B', (byte)'Z', (byte)'h' };
|
||||
if (header.Take(3).SequenceEqual(bzip_sig))
|
||||
{
|
||||
Exception err = null;
|
||||
GsfInput res = GsfInputMemory.CreateFromBzip(null, ref err);
|
||||
if (res != null)
|
||||
return res.Uncompress();
|
||||
}
|
||||
}
|
||||
|
||||
// Other methods go here.
|
||||
|
||||
error:
|
||||
Seek(cur_offset, SeekOrigin.Begin);
|
||||
return this;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region GIO
|
||||
|
||||
internal void SetNameFromFile(string file)
|
||||
{
|
||||
if (!File.Exists(file))
|
||||
return;
|
||||
|
||||
FileInfo info = new FileInfo(file);
|
||||
SetNameFromFilename(info.Name);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region MS-OLE
|
||||
|
||||
/// <summary>
|
||||
/// Decompresses an LZ compressed stream.
|
||||
/// </summary>
|
||||
/// <param name="offset">Offset into it for start byte of compresse stream</param>
|
||||
/// <returns>A GByteArray that the caller is responsible for freeing</returns>
|
||||
internal byte[] InflateMSOLE(long offset)
|
||||
{
|
||||
uint pos = 0;
|
||||
uint shift;
|
||||
byte[] flag = new byte[1];
|
||||
byte[] buffer = new byte[VBA_COMPRESSION_WINDOW];
|
||||
byte[] tmp = new byte[2];
|
||||
bool clean = true;
|
||||
|
||||
if (Seek(offset, SeekOrigin.Begin))
|
||||
return null;
|
||||
|
||||
byte[] res = new byte[0];
|
||||
|
||||
// Explaination from libole2/ms-ole-vba.c
|
||||
|
||||
// The first byte is a flag byte. Each bit in this byte
|
||||
// determines what the next byte is. If the bit is zero,
|
||||
// the next byte is a character. Otherwise the next two
|
||||
// bytes contain the number of characters to copy from the
|
||||
// umcompresed buffer and where to copy them from (offset,
|
||||
// length).
|
||||
|
||||
while (Read(1, flag) != null)
|
||||
{
|
||||
for (uint mask = 1; mask < 0x100; mask <<= 1)
|
||||
{
|
||||
if ((flag[0] & mask) != 0)
|
||||
{
|
||||
if ((tmp = Read(2, null)) == null)
|
||||
break;
|
||||
|
||||
uint win_pos = pos % VBA_COMPRESSION_WINDOW;
|
||||
if (win_pos <= 0x80)
|
||||
{
|
||||
if (win_pos <= 0x20)
|
||||
shift = (uint)((win_pos <= 0x10) ? 12 : 11);
|
||||
else
|
||||
shift = (uint)((win_pos <= 0x40) ? 10 : 9);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (win_pos <= 0x200)
|
||||
shift = (uint)((win_pos <= 0x100) ? 8 : 7);
|
||||
else if (win_pos <= 0x800)
|
||||
shift = (uint)((win_pos <= 0x400) ? 6 : 5);
|
||||
else
|
||||
shift = 4;
|
||||
}
|
||||
|
||||
ushort token = BitConverter.ToUInt16(tmp, 0);
|
||||
ushort len = (ushort)((token & ((1 << (int)shift) - 1)) + 3);
|
||||
uint distance = (uint)(token >> (int)shift);
|
||||
clean = true;
|
||||
|
||||
if (distance >= pos)
|
||||
{
|
||||
Console.Error.WriteLine("Corrupted compressed stream");
|
||||
break;
|
||||
}
|
||||
|
||||
for (uint i = 0; i < len; i++)
|
||||
{
|
||||
uint srcpos = (pos - distance - 1) % VBA_COMPRESSION_WINDOW;
|
||||
byte c = buffer[srcpos];
|
||||
buffer[pos++ % VBA_COMPRESSION_WINDOW] = c;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if ((pos != 0) && ((pos % VBA_COMPRESSION_WINDOW) == 0) && clean)
|
||||
{
|
||||
Read(2, null);
|
||||
clean = false;
|
||||
|
||||
List<byte> temp = new List<byte>(res);
|
||||
temp.AddRange(buffer);
|
||||
res = temp.ToArray();
|
||||
break;
|
||||
}
|
||||
|
||||
if (Read(1, buffer, (int)(pos % VBA_COMPRESSION_WINDOW)) != null)
|
||||
pos++;
|
||||
|
||||
clean = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ((pos % VBA_COMPRESSION_WINDOW) != 0)
|
||||
{
|
||||
List<byte> temp = new List<byte>(res);
|
||||
temp.AddRange(buffer.Skip((int)(pos % VBA_COMPRESSION_WINDOW)));
|
||||
res = temp.ToArray();
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region MS-VBA
|
||||
|
||||
/// <summary>
|
||||
/// Decompresses VBA stream.
|
||||
/// </summary>
|
||||
/// <param name="input">Stream to read from</param>
|
||||
/// <param name="offset">Offset into it for start byte of compressed stream</param>
|
||||
/// <param name="size">Size of the returned array</param>
|
||||
/// <param name="add_null_terminator">Whenever add or not null at the end of array</param>
|
||||
/// <returns>A pointer to byte array</returns>
|
||||
internal byte[] InflateMSVBA(long offset, out int size, bool add_null_terminator)
|
||||
{
|
||||
size = 0;
|
||||
|
||||
byte[] sig = new byte[1];
|
||||
Read(1, sig);
|
||||
if (sig[0] != 1) // Should start with 0x01
|
||||
return null;
|
||||
|
||||
offset++;
|
||||
|
||||
List<byte> res = new List<byte>();
|
||||
|
||||
long length = Size;
|
||||
while (offset < length)
|
||||
{
|
||||
byte[] tmp = Read(2, null);
|
||||
if (tmp == null)
|
||||
break;
|
||||
|
||||
ushort chunk_hdr = BitConverter.ToUInt16(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;
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
140
BurnOutSharp/External/libgsf/Input/GsfInputBzip.cs
vendored
Normal file
140
BurnOutSharp/External/libgsf/Input/GsfInputBzip.cs
vendored
Normal file
@@ -0,0 +1,140 @@
|
||||
/* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
|
||||
/*
|
||||
* gsf-input-iochannel.c: wrapper for glib's GIOChannel
|
||||
*
|
||||
* Copyright (C) 2003-2006 Dom Lachowicz (cinamod@hotmail.com)
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of version 2.1 of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301
|
||||
* USA
|
||||
*/
|
||||
|
||||
using System;
|
||||
|
||||
namespace LibGSF.Input
|
||||
{
|
||||
public partial class GsfInputMemory
|
||||
{
|
||||
#region Constants
|
||||
|
||||
private const int BZ_BUFSIZ = 1024;
|
||||
|
||||
#region bzlib.h
|
||||
|
||||
private const int BZ_RUN = 0;
|
||||
private const int BZ_FLUSH = 1;
|
||||
private const int BZ_FINISH = 2;
|
||||
|
||||
private const int BZ_OK = 0;
|
||||
private const int BZ_RUN_OK = 1;
|
||||
private const int BZ_FLUSH_OK = 2;
|
||||
private const int BZ_FINISH_OK = 3;
|
||||
private const int BZ_STREAM_END = 4;
|
||||
private const int BZ_SEQUENCE_ERROR = (-1);
|
||||
private const int BZ_PARAM_ERROR = (-2);
|
||||
private const int BZ_MEM_ERROR = (-3);
|
||||
private const int BZ_DATA_ERROR = (-4);
|
||||
private const int BZ_DATA_ERROR_MAGIC = (-5);
|
||||
private const int BZ_IO_ERROR = (-6);
|
||||
private const int BZ_UNEXPECTED_EOF = (-7);
|
||||
private const int BZ_OUTBUFF_FULL = (-8);
|
||||
private const int BZ_CONFIG_ERROR = (-9);
|
||||
|
||||
#endregion
|
||||
|
||||
#endregion
|
||||
|
||||
// TODO: Implement BZIP reading
|
||||
|
||||
#region Constructor
|
||||
|
||||
/// <param name="err">Place to store an Exception if anything goes wrong</param>
|
||||
/// <returns>A new GsfInputMemory or null.</returns>
|
||||
public static GsfInputMemory CreateFromBzip(GsfInput source, ref Exception err)
|
||||
{
|
||||
#if BZIP2
|
||||
bz_stream bzstm;
|
||||
GsfInput* mem = NULL;
|
||||
GsfOutput* sink = NULL;
|
||||
guint8 out_buf[BZ_BUFSIZ];
|
||||
int bzerr = BZ_OK;
|
||||
|
||||
g_return_val_if_fail(source != NULL, NULL);
|
||||
|
||||
memset(&bzstm, 0, sizeof(bzstm));
|
||||
if (BZ_OK != BZ2_bzDecompressInit(&bzstm, 0, 0))
|
||||
{
|
||||
if (err)
|
||||
*err = g_error_new(gsf_input_error_id(), 0,
|
||||
_("BZ2 decompress init failed"));
|
||||
return NULL;
|
||||
}
|
||||
|
||||
sink = gsf_output_memory_new();
|
||||
|
||||
for (; ; )
|
||||
{
|
||||
bzstm.next_out = (char*)out_buf;
|
||||
bzstm.avail_out = (unsigned int)sizeof(out_buf);
|
||||
|
||||
if (bzstm.avail_in == 0)
|
||||
{
|
||||
bzstm.avail_in = (unsigned int)MIN(gsf_input_remaining(source), BZ_BUFSIZ);
|
||||
bzstm.next_in = (char*)gsf_input_read(source, bzstm.avail_in, NULL);
|
||||
}
|
||||
|
||||
bzerr = BZ2_bzDecompress(&bzstm);
|
||||
if (bzerr != BZ_OK && bzerr != BZ_STREAM_END)
|
||||
{
|
||||
if (err)
|
||||
*err = g_error_new(gsf_input_error_id(), 0,
|
||||
_("BZ2 decompress failed"));
|
||||
BZ2_bzDecompressEnd(&bzstm);
|
||||
gsf_output_close(sink);
|
||||
g_object_unref(sink);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
gsf_output_write(sink, BZ_BUFSIZ - bzstm.avail_out, out_buf);
|
||||
if (bzerr == BZ_STREAM_END)
|
||||
break;
|
||||
}
|
||||
|
||||
gsf_output_close(sink);
|
||||
|
||||
if (BZ_OK != BZ2_bzDecompressEnd(&bzstm)) {
|
||||
if (err)
|
||||
* err = g_error_new(gsf_input_error_id(), 0,
|
||||
_("BZ2 decompress end failed"));
|
||||
g_object_unref(sink);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
mem = gsf_input_memory_new_clone(
|
||||
gsf_output_memory_get_bytes (GSF_OUTPUT_MEMORY (sink)),
|
||||
gsf_output_size(sink));
|
||||
|
||||
if (mem != NULL)
|
||||
gsf_input_set_name(mem, gsf_input_name (source));
|
||||
|
||||
g_object_unref(sink);
|
||||
return mem;
|
||||
#else
|
||||
err = new Exception("BZ2 support not enabled");
|
||||
return null;
|
||||
#endif
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
443
BurnOutSharp/External/libgsf/Input/GsfInputGZip.cs
vendored
Normal file
443
BurnOutSharp/External/libgsf/Input/GsfInputGZip.cs
vendored
Normal file
@@ -0,0 +1,443 @@
|
||||
/* 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;
|
||||
|
||||
namespace LibGSF.Input
|
||||
{
|
||||
public class GsfInputGZip : GsfInput
|
||||
{
|
||||
#region Enums
|
||||
|
||||
[Flags]
|
||||
private enum GZIP_HEADER_FLAGS : byte
|
||||
{
|
||||
/// <summary>
|
||||
/// File contains text ?
|
||||
/// </summary>
|
||||
GZIP_IS_ASCII = 0x01,
|
||||
|
||||
/// <summary>
|
||||
/// There is a CRC in the header
|
||||
/// </summary>
|
||||
GZIP_HEADER_CRC = 0x02,
|
||||
|
||||
/// <summary>
|
||||
/// There is an 'extra' field
|
||||
/// </summary>
|
||||
GZIP_EXTRA_FIELD = 0x04,
|
||||
|
||||
/// <summary>
|
||||
/// The original is stored
|
||||
/// </summary>
|
||||
GZIP_ORIGINAL_NAME = 0x08,
|
||||
|
||||
/// <summary>
|
||||
/// There is a comment in the header
|
||||
/// </summary>
|
||||
GZIP_HAS_COMMENT = 0x10,
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Properties
|
||||
|
||||
/// <summary>
|
||||
/// Compressed data
|
||||
/// </summary>
|
||||
public GsfInput Source { get; set; } = null;
|
||||
|
||||
/// <summary>
|
||||
/// No header and no trailer.
|
||||
/// </summary>
|
||||
public bool Raw { get; set; } = false;
|
||||
|
||||
public Exception Err { get; set; } = null;
|
||||
|
||||
public long UncompressedSize { get; set; } = -1;
|
||||
|
||||
public bool StopByteAdded { get; set; }
|
||||
|
||||
public ZStream Stream { get; set; } = new ZStream();
|
||||
|
||||
public byte[] GZippedData { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// CRC32 of uncompressed data
|
||||
/// </summary>
|
||||
public ulong CRC { get; set; } = 0;
|
||||
|
||||
public byte[] Buf { get; set; } = null;
|
||||
|
||||
public int BufSize { get; set; } = 0;
|
||||
|
||||
public long HeaderSize { get; set; }
|
||||
|
||||
public long TrailerSize { get; set; }
|
||||
|
||||
public long SeekSkipped { get; set; } = 0;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Constructor and Destructor
|
||||
|
||||
/// <summary>
|
||||
/// Private constructor
|
||||
/// </summary>
|
||||
private GsfInputGZip(GsfInput source = null, bool raw = false, long uncompressedSize = -1)
|
||||
{
|
||||
Source = source;
|
||||
Raw = raw;
|
||||
UncompressedSize = uncompressedSize;
|
||||
|
||||
Exception tempErr = null;
|
||||
if (Source == null)
|
||||
Err = new Exception("Null source");
|
||||
else if (Raw && UncompressedSize < 0)
|
||||
Err = new Exception("Uncompressed size not set");
|
||||
else if (InitZip(ref tempErr) != false)
|
||||
Err = tempErr;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds a reference to <paramref name="source"/>.
|
||||
/// </summary>
|
||||
/// <param name="source">The underlying data source.</param>
|
||||
/// <param name="err">Place to store an Exception if anything goes wrong</param>
|
||||
/// <returns></returns>
|
||||
public static GsfInputGZip Create(GsfInput source, ref Exception err)
|
||||
{
|
||||
if (source == null)
|
||||
return null;
|
||||
|
||||
GsfInputGZip gzip = new GsfInputGZip(source: source)
|
||||
{
|
||||
Name = source.Name,
|
||||
};
|
||||
|
||||
return gzip;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Destructor
|
||||
/// </summary>
|
||||
~GsfInputGZip()
|
||||
{
|
||||
Source = null;
|
||||
|
||||
if (Stream != null)
|
||||
Stream.inflateEnd();
|
||||
|
||||
Err = null;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Functions
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override GsfInput DupImpl(ref Exception err)
|
||||
{
|
||||
GsfInput src_source_copy;
|
||||
if (Source != null)
|
||||
{
|
||||
src_source_copy = Source.Duplicate(ref err);
|
||||
if (err != null)
|
||||
return null;
|
||||
}
|
||||
else
|
||||
{
|
||||
src_source_copy = null;
|
||||
}
|
||||
|
||||
GsfInputGZip dst = new GsfInputGZip(source: src_source_copy, raw: (Source as GsfInputGZip)?.Raw ?? false);
|
||||
|
||||
if (Err != null)
|
||||
{
|
||||
dst.Err = Err;
|
||||
}
|
||||
else if (dst.Err != null)
|
||||
{
|
||||
err = dst.Err;
|
||||
return null;
|
||||
}
|
||||
|
||||
return dst;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override byte[] ReadImpl(int num_bytes, byte[] optional_buffer, int bufferPtr = 0)
|
||||
{
|
||||
if (optional_buffer == null)
|
||||
{
|
||||
if (BufSize < num_bytes)
|
||||
{
|
||||
BufSize = Math.Max(num_bytes, 256);
|
||||
Buf = new byte[BufSize];
|
||||
}
|
||||
|
||||
optional_buffer = Buf;
|
||||
}
|
||||
|
||||
Stream.next_out = optional_buffer;
|
||||
Stream.avail_out = num_bytes;
|
||||
while (Stream.avail_out != 0)
|
||||
{
|
||||
int zerr;
|
||||
if (Stream.avail_in == 0)
|
||||
{
|
||||
long remain = Source.Remaining();
|
||||
if (remain <= TrailerSize)
|
||||
{
|
||||
if (remain < TrailerSize || StopByteAdded)
|
||||
{
|
||||
Err = new Exception("Truncated source");
|
||||
return null;
|
||||
}
|
||||
|
||||
// zlib requires an extra byte.
|
||||
Stream.avail_in = 1;
|
||||
GZippedData = new byte[0];
|
||||
StopByteAdded = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
int n = (int)Math.Min(remain - TrailerSize, 4096);
|
||||
|
||||
GZippedData = Source.Read(n, null);
|
||||
if (GZippedData == null)
|
||||
{
|
||||
Err = new Exception("Failed to read from source");
|
||||
return null;
|
||||
}
|
||||
|
||||
Stream.avail_in = n;
|
||||
}
|
||||
|
||||
Stream.next_in = GZippedData;
|
||||
}
|
||||
|
||||
zerr = Stream.inflate(Z_NO_FLUSH);
|
||||
if (zerr != Z_OK)
|
||||
{
|
||||
if (zerr != Z_STREAM_END)
|
||||
return null;
|
||||
|
||||
// Premature end of stream.
|
||||
if (Stream.avail_out != 0)
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Enable CRC32 calculation
|
||||
//CRC = crc32(CRC, optional_buffer, (uint)(Stream.next_out - optional_buffer));
|
||||
return optional_buffer;
|
||||
}
|
||||
|
||||
// Global flag -- we don't want one per stream.
|
||||
private static bool warned = false;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override bool Seek(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 = BitConverter.ToUInt32(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 = BitConverter.ToUInt32(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 = BitConverter.ToUInt16(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
|
||||
}
|
||||
}
|
||||
239
BurnOutSharp/External/libgsf/Input/GsfInputGio.cs
vendored
Normal file
239
BurnOutSharp/External/libgsf/Input/GsfInputGio.cs
vendored
Normal file
@@ -0,0 +1,239 @@
|
||||
/* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
|
||||
/*
|
||||
* gsf-input-gio.c:
|
||||
*
|
||||
* Copyright (C) 2007 Dom Lachowicz <cinamod@hotmail.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of version 2.1 of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301
|
||||
* USA
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.IO;
|
||||
|
||||
namespace LibGSF.Input
|
||||
{
|
||||
public class GsfInputGio : GsfInput
|
||||
{
|
||||
#region Properties
|
||||
|
||||
public string File { get; set; } = null;
|
||||
|
||||
public Stream Stream { get; set; } = null;
|
||||
|
||||
public byte[] Buf { get; set; } = null;
|
||||
|
||||
public int BufSize { get; set; } = 0;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Constructor and Destructor
|
||||
|
||||
/// <summary>
|
||||
/// Private constructor
|
||||
/// </summary>
|
||||
private GsfInputGio() { }
|
||||
|
||||
/// <param name="err">Place to store an Exception if anything goes wrong</param>
|
||||
/// <returns>A new GsfInputGio or null</returns>
|
||||
public static GsfInput Create(string file, ref Exception err)
|
||||
{
|
||||
if (file == null || !System.IO.File.Exists(file))
|
||||
return null;
|
||||
|
||||
long filesize;
|
||||
|
||||
Stream stream;
|
||||
try
|
||||
{
|
||||
stream = System.IO.File.OpenRead(file);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
err = ex;
|
||||
return null;
|
||||
}
|
||||
|
||||
if (true)
|
||||
{
|
||||
// see https://bugzilla.gnome.org/show_bug.cgi?id=724970
|
||||
return MakeLocalCopy(file, stream);
|
||||
}
|
||||
|
||||
if (!stream.CanSeek)
|
||||
return MakeLocalCopy(file, stream);
|
||||
|
||||
{
|
||||
FileInfo info = new FileInfo(file);
|
||||
filesize = info.Length;
|
||||
}
|
||||
|
||||
GsfInputGio input = new GsfInputGio
|
||||
{
|
||||
Size = filesize,
|
||||
Stream = stream,
|
||||
File = file,
|
||||
Buf = null,
|
||||
BufSize = 0,
|
||||
};
|
||||
|
||||
input.SetNameFromFile(file);
|
||||
return input;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Destructor
|
||||
/// </summary>
|
||||
~GsfInputGio()
|
||||
{
|
||||
Stream.Close();
|
||||
Stream = null;
|
||||
|
||||
File = null;
|
||||
|
||||
if (Buf != null)
|
||||
{
|
||||
Buf = null;
|
||||
BufSize = 0;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Functions
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override GsfInput DupImpl(ref Exception err)
|
||||
{
|
||||
if (File != null)
|
||||
return null;
|
||||
|
||||
return Create(File, ref err);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override byte[] ReadImpl(int num_bytes, byte[] optional_buffer, int bufferPtr = 0)
|
||||
{
|
||||
if (Stream == null)
|
||||
return null;
|
||||
|
||||
int total_read = 0;
|
||||
|
||||
if (optional_buffer == null)
|
||||
{
|
||||
if (BufSize < num_bytes)
|
||||
{
|
||||
BufSize = num_bytes;
|
||||
Buf = new byte[BufSize];
|
||||
}
|
||||
|
||||
optional_buffer = Buf;
|
||||
}
|
||||
|
||||
while (total_read < num_bytes)
|
||||
{
|
||||
int try_to_read = Math.Min(int.MaxValue, num_bytes - total_read);
|
||||
int nread = Stream.Read(optional_buffer, total_read, try_to_read);
|
||||
if (nread > 0)
|
||||
{
|
||||
total_read += nread;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Getting zero means EOF which isn't supposed to
|
||||
// happen. Negative means error.
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
return optional_buffer;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override bool Seek(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
|
||||
}
|
||||
}
|
||||
186
BurnOutSharp/External/libgsf/Input/GsfInputHTTP.cs
vendored
Normal file
186
BurnOutSharp/External/libgsf/Input/GsfInputHTTP.cs
vendored
Normal file
@@ -0,0 +1,186 @@
|
||||
/* 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;
|
||||
|
||||
namespace LibGSF.Input
|
||||
{
|
||||
public class GsfInputHTTP : GsfInput
|
||||
{
|
||||
#region Properties
|
||||
|
||||
public string Url { get; private set; } = null;
|
||||
|
||||
public string ContentType { get; private set; } = null;
|
||||
|
||||
public Stream ResponseData { get; private set; } = null;
|
||||
|
||||
public byte[] Buffer { get; private set; } = null;
|
||||
|
||||
public int BufferSize { get; private set; } = 0;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Constructor and Destructor
|
||||
|
||||
/// <summary>
|
||||
/// Private constructor
|
||||
/// </summary>
|
||||
private GsfInputHTTP() { }
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="url">A string containing the URL to retrieve</param>
|
||||
/// <param name="error">Holds any errors encountered when establishing the HTTP connection</param>
|
||||
/// <returns>An open HTTP connection, ready for reading.</returns>
|
||||
public static GsfInput Create(string url, ref Exception error)
|
||||
{
|
||||
if (url == null)
|
||||
return null;
|
||||
|
||||
WebRequest request = WebRequest.Create(url);
|
||||
WebResponse response = request.GetResponse();
|
||||
Stream ctx = response.GetResponseStream();
|
||||
|
||||
string content_type = response.ContentType;
|
||||
|
||||
// Always make a local copy
|
||||
// see https://bugzilla.gnome.org/show_bug.cgi?id=724970
|
||||
GsfInput input = MakeLocalCopy(ctx);
|
||||
if (input != null)
|
||||
{
|
||||
input.Name = url;
|
||||
return input;
|
||||
}
|
||||
|
||||
GsfInputHTTP obj = new GsfInputHTTP
|
||||
{
|
||||
Url = url,
|
||||
ContentType = content_type,
|
||||
Size = ctx.Length,
|
||||
ResponseData = ctx,
|
||||
};
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
||||
~GsfInputHTTP()
|
||||
{
|
||||
Url = null;
|
||||
ContentType = null;
|
||||
|
||||
if (ResponseData != null)
|
||||
{
|
||||
ResponseData.Close();
|
||||
ResponseData = null;
|
||||
}
|
||||
|
||||
Buffer = null;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Functions
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override GsfInput DupImpl(ref Exception err) => Create(Url, ref err);
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override byte[] ReadImpl(int num_bytes, byte[] optional_buffer, int bufferPtr = 0)
|
||||
{
|
||||
int nread;
|
||||
int total_read;
|
||||
|
||||
if (optional_buffer == null)
|
||||
{
|
||||
if (BufferSize < num_bytes)
|
||||
{
|
||||
BufferSize = num_bytes;
|
||||
Buffer = new byte[BufferSize];
|
||||
}
|
||||
|
||||
optional_buffer = Buffer;
|
||||
}
|
||||
|
||||
int ptr = 0;
|
||||
for (total_read = 0; total_read < num_bytes; total_read += nread)
|
||||
{
|
||||
nread = ResponseData.Read(optional_buffer, ptr, num_bytes - total_read);
|
||||
if (nread <= 0)
|
||||
return null;
|
||||
|
||||
ptr += nread;
|
||||
}
|
||||
|
||||
return optional_buffer;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override bool Seek(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
|
||||
}
|
||||
}
|
||||
43
BurnOutSharp/External/libgsf/Input/GsfInputIoChannel.cs
vendored
Normal file
43
BurnOutSharp/External/libgsf/Input/GsfInputIoChannel.cs
vendored
Normal file
@@ -0,0 +1,43 @@
|
||||
/* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
|
||||
/*
|
||||
* gsf-input-iochannel.c: GIOChannel based input
|
||||
*
|
||||
* Copyright (C) 2003-2006 Rodrigo Moya (rodrigo@gnome-db.org)
|
||||
* Copyright (C) 2003-2006 Dom Lachowicz (cinamod@hotmail.com)
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of version 2.1 of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301
|
||||
* USA
|
||||
*/
|
||||
|
||||
using System;
|
||||
|
||||
namespace LibGSF.Input
|
||||
{
|
||||
public partial class GsfInputMemory
|
||||
{
|
||||
// TODO: Enable once GIOChannel is converted
|
||||
|
||||
///// <returns>A new GsfInputMemory or null.</returns>
|
||||
//public static GsfInputMemory CreateFromIoChannel(GIOChannel channel, ref Exception err)
|
||||
//{
|
||||
// if (channel == null)
|
||||
// return null;
|
||||
|
||||
// if (G_IO_STATUS_NORMAL != channel.ReadToEnd(out byte[] buf, out int len, err))
|
||||
// return null;
|
||||
|
||||
// return Create(buf, len, true);
|
||||
//}
|
||||
}
|
||||
}
|
||||
149
BurnOutSharp/External/libgsf/Input/GsfInputMemory.cs
vendored
Normal file
149
BurnOutSharp/External/libgsf/Input/GsfInputMemory.cs
vendored
Normal file
@@ -0,0 +1,149 @@
|
||||
/* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
|
||||
/*
|
||||
* gsf-input-memory.c:
|
||||
*
|
||||
* Copyright (C) 2002-2006 Jody Goldberg (jody@gnome.org)
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of version 2.1 of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301
|
||||
* USA
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
|
||||
namespace LibGSF.Input
|
||||
{
|
||||
public partial class GsfInputMemory : GsfInput
|
||||
{
|
||||
#region Properties
|
||||
|
||||
public GsfSharedMemory Shared { get; set; } = null;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Functions
|
||||
|
||||
/// <summary>
|
||||
/// </summary>
|
||||
/// <param name="buf">The input bytes</param>
|
||||
/// <param name="length">The length of <paramref name="buf"/></param>
|
||||
/// <param name="needs_free">Whether you want this memory to be free'd at object destruction</param>
|
||||
/// <returns>A new GsfInputMemory</returns>
|
||||
public static GsfInputMemory Create(byte[] buf, long length, bool needs_free)
|
||||
{
|
||||
GsfInputMemory mem = new GsfInputMemory();
|
||||
mem.Shared = GsfSharedMemory.Create(buf, length, needs_free);
|
||||
mem.Size = length;
|
||||
return mem;
|
||||
}
|
||||
|
||||
/// <param name="buf">The input bytes</param>
|
||||
/// <param name="length">The length of <paramref name="buf"/></param>
|
||||
/// <returns>A new GsfInputMemory</returns>
|
||||
public static GsfInputMemory Clone(byte[] buf, long length)
|
||||
{
|
||||
if (buf == null || length < 0)
|
||||
return null;
|
||||
|
||||
GsfInputMemory mem = new GsfInputMemory();
|
||||
byte[] cpy = new byte[Math.Max(1, length)];
|
||||
if (buf.Length > 0)
|
||||
Array.Copy(buf, cpy, length);
|
||||
|
||||
mem.Shared = GsfSharedMemory.Create(cpy, length, true);
|
||||
mem.Size = length;
|
||||
return mem;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override GsfInput DupImpl(ref Exception err)
|
||||
{
|
||||
return new GsfInputMemory
|
||||
{
|
||||
Shared = Shared,
|
||||
Size = Shared.Size,
|
||||
};
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override byte[] ReadImpl(int num_bytes, byte[] optional_buffer, int bufferPtr = 0)
|
||||
{
|
||||
byte[] src = Shared.Buf;
|
||||
if (src == null)
|
||||
return null;
|
||||
|
||||
if (optional_buffer != null)
|
||||
{
|
||||
Array.Copy(src, CurrentOffset, optional_buffer, 0, num_bytes);
|
||||
return optional_buffer;
|
||||
}
|
||||
else
|
||||
{
|
||||
return src.Skip((int)CurrentOffset).ToArray();
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override bool Seek(long offset, SeekOrigin whence) => false;
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
public class GsfInputMemoryMap : GsfInputMemory
|
||||
{
|
||||
private const int PROT_READ = 0x1;
|
||||
|
||||
/// <param name="filename">The file on disk that you want to mmap</param>
|
||||
/// <param name="err">An Exception</param>
|
||||
/// <returns>A new GsfInputMemory</returns>
|
||||
public GsfInputMemoryMap Create(string filename, ref Exception err)
|
||||
{
|
||||
FileStream fd;
|
||||
try
|
||||
{
|
||||
fd = File.OpenRead(filename);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
err = ex;
|
||||
return null;
|
||||
}
|
||||
|
||||
FileInfo st = new FileInfo(filename);
|
||||
if (!st.Attributes.HasFlag(FileAttributes.Normal))
|
||||
{
|
||||
err = new Exception($"{filename}: Is not a regular file");
|
||||
fd.Close();
|
||||
return null;
|
||||
}
|
||||
|
||||
long size = fd.Length;
|
||||
byte[] buf = new byte[size];
|
||||
fd.Read(buf, 0, (int)size);
|
||||
|
||||
GsfInputMemoryMap mem = new GsfInputMemoryMap
|
||||
{
|
||||
Shared = GsfSharedMemory.CreateMemoryMapped(buf, size),
|
||||
Size = size,
|
||||
Name = filename,
|
||||
ModTime = st.LastWriteTime,
|
||||
};
|
||||
|
||||
fd.Close();
|
||||
|
||||
return mem;
|
||||
}
|
||||
}
|
||||
}
|
||||
111
BurnOutSharp/External/libgsf/Input/GsfInputProxy.cs
vendored
Normal file
111
BurnOutSharp/External/libgsf/Input/GsfInputProxy.cs
vendored
Normal file
@@ -0,0 +1,111 @@
|
||||
/* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
|
||||
/*
|
||||
* gsf-input-proxy.c: proxy object (with its own current position)
|
||||
*
|
||||
* Copyright (C) 2004-2006 Morten Welinder (terra@gnome.org)
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of version 2.1 of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301
|
||||
* USA
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.IO;
|
||||
|
||||
namespace LibGSF.Input
|
||||
{
|
||||
public class GsfInputProxy : GsfInput
|
||||
{
|
||||
#region Properties
|
||||
|
||||
public GsfInput Source { get; set; } = null;
|
||||
|
||||
public long Offset { get; set; } = 0;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Functions
|
||||
|
||||
/// <summary>
|
||||
/// This creates a new proxy to a section of the given source. The new
|
||||
/// object will have its own current position, but any operation on it
|
||||
/// can change the source's position.
|
||||
///
|
||||
/// If a proxy to a proxy is created, the intermediate proxy is short-
|
||||
/// circuited.
|
||||
///
|
||||
/// This function will ref the source.
|
||||
/// </summary>
|
||||
/// <param name="source">The underlying data source.</param>
|
||||
/// <param name="offset">Offset into source for start of section.</param>
|
||||
/// <param name="size">Length of section.</param>
|
||||
/// <returns>A new input object.</returns>
|
||||
public static GsfInputProxy Create(GsfInput source, long offset, long size)
|
||||
{
|
||||
if (source == null)
|
||||
return null;
|
||||
if (offset < 0)
|
||||
return null;
|
||||
|
||||
long source_size = source.Size;
|
||||
if (offset > source_size)
|
||||
return null;
|
||||
if (size > source_size - offset)
|
||||
return null;
|
||||
|
||||
GsfInputProxy proxy = new GsfInputProxy
|
||||
{
|
||||
Offset = offset,
|
||||
Size = size,
|
||||
Name = source.Name,
|
||||
};
|
||||
|
||||
// Short-circuit multiple proxies.
|
||||
if (source is GsfInputProxy proxy_source)
|
||||
{
|
||||
proxy.Offset += proxy_source.Offset;
|
||||
source = proxy_source.Source;
|
||||
}
|
||||
|
||||
proxy.Source = source;
|
||||
return proxy;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This creates a new proxy to the entire, given input source. See
|
||||
/// Create for details.
|
||||
/// </summary>
|
||||
/// <param name="source">The underlying data source.</param>
|
||||
/// <returns>A new input object.</returns>
|
||||
public static GsfInput Create(GsfInput source) => Create(source, 0, source.Size);
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override GsfInput DupImpl(ref Exception err) => Create(this);
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override byte[] ReadImpl(int num_bytes, byte[] optional_buffer, int bufferPtr = 0)
|
||||
{
|
||||
// Seek to our position in the source.
|
||||
if (Source.Seek(Offset + CurrentOffset, SeekOrigin.Begin))
|
||||
return null;
|
||||
|
||||
// Read the data.
|
||||
return Source.Read(num_bytes, optional_buffer, bufferPtr);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override bool Seek(long offset, SeekOrigin whence) => false;
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
226
BurnOutSharp/External/libgsf/Input/GsfInputStdio.cs
vendored
Normal file
226
BurnOutSharp/External/libgsf/Input/GsfInputStdio.cs
vendored
Normal file
@@ -0,0 +1,226 @@
|
||||
/* 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;
|
||||
|
||||
namespace LibGSF.Input
|
||||
{
|
||||
public class GsfInputStdio : GsfInput
|
||||
{
|
||||
#region Properties
|
||||
|
||||
public FileStream File { get; set; } = null;
|
||||
|
||||
public string Filename { get; set; } = null;
|
||||
|
||||
public byte[] Buf { get; set; } = null;
|
||||
|
||||
public int BufSize { get; set; } = 0;
|
||||
|
||||
public bool KeepOpen { get; set; } = false;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Functions
|
||||
|
||||
/// <summary>
|
||||
/// Destructor
|
||||
/// </summary>
|
||||
~GsfInputStdio()
|
||||
{
|
||||
if (File != null && !KeepOpen)
|
||||
File.Close();
|
||||
}
|
||||
|
||||
public static GsfInput MakeLocalCopy(FileStream stream, string filename, ref Exception err)
|
||||
{
|
||||
GsfOutputMemory output = GsfOutputMemory.Create();
|
||||
|
||||
while (true)
|
||||
{
|
||||
byte[] buf = new byte[4096];
|
||||
int nread = stream.Read(buf, 1, buf.Length);
|
||||
|
||||
if (nread > 0)
|
||||
{
|
||||
if (!output.Write(nread, buf))
|
||||
{
|
||||
err = new Exception($"{(filename != null ? filename : "?")}: not a regular file");
|
||||
output.Close();
|
||||
return null;
|
||||
}
|
||||
}
|
||||
else if (nread == 0)
|
||||
{
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
err = new Exception($"{(filename != null ? filename : "?")}: not a regular file");
|
||||
output.Close();
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
GsfInput copy = GsfInputMemory.Clone(output.Buffer, output.Capacity);
|
||||
|
||||
output.Close();
|
||||
|
||||
if (filename != null)
|
||||
copy.SetNameFromFilename(filename);
|
||||
|
||||
return copy;
|
||||
}
|
||||
|
||||
/// <param name="filename">In UTF-8.</param>
|
||||
/// <param name="err">Place to store an Exception if anything goes wrong</param>
|
||||
/// <returns>A new file or null.</returns>
|
||||
public static GsfInput Create(string filename, ref Exception err)
|
||||
{
|
||||
if (filename == null)
|
||||
return null;
|
||||
|
||||
FileStream file;
|
||||
try
|
||||
{
|
||||
file = System.IO.File.OpenRead(filename);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
err = ex;
|
||||
return null;
|
||||
}
|
||||
|
||||
FileInfo fstat = new FileInfo(filename);
|
||||
if (!fstat.Attributes.HasFlag(FileAttributes.Normal))
|
||||
{
|
||||
GsfInput res = MakeLocalCopy(file, filename, ref err);
|
||||
file.Close();
|
||||
return res;
|
||||
}
|
||||
|
||||
long size = fstat.Length;
|
||||
GsfInputStdio input = new GsfInputStdio
|
||||
{
|
||||
File = file,
|
||||
Filename = filename,
|
||||
Buf = null,
|
||||
BufSize = 0,
|
||||
KeepOpen = false,
|
||||
Size = size,
|
||||
ModTime = fstat.LastWriteTime,
|
||||
};
|
||||
|
||||
input.SetNameFromFilename(filename);
|
||||
|
||||
return input;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Assumes ownership of <paramref name="file"/> when succeeding. If <paramref name="keep_open"/> is true,
|
||||
/// ownership reverts to caller when the GsfInput is closed.
|
||||
/// </summary>
|
||||
/// <param name="filename">The filename corresponding to <paramref name="file"/>.</param>
|
||||
/// <param name="file">An existing stdio <see cref="FileStream"/></param>
|
||||
/// <param name="keep_open">Should <paramref name="file"/> be closed when the wrapper is closed</param>
|
||||
/// <returns>
|
||||
/// A new GsfInput wrapper for <paramref name="file"/>. Note that if the file is not
|
||||
/// seekable, this function will make a local copy of the entire file.
|
||||
/// </returns>
|
||||
public static GsfInput Create(string filename, FileStream file, bool keep_open)
|
||||
{
|
||||
if (filename == null)
|
||||
return null;
|
||||
if (file == null)
|
||||
return null;
|
||||
|
||||
FileInfo st = new FileInfo(filename);
|
||||
if (!st.Attributes.HasFlag(FileAttributes.Normal))
|
||||
{
|
||||
Exception err = null;
|
||||
return MakeLocalCopy(file, filename, ref err);
|
||||
}
|
||||
|
||||
GsfInputStdio stdio = new GsfInputStdio
|
||||
{
|
||||
File = file,
|
||||
KeepOpen = keep_open,
|
||||
Filename = filename,
|
||||
Size = st.Length,
|
||||
};
|
||||
|
||||
stdio.SetNameFromFilename(filename);
|
||||
return stdio;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override GsfInput DupImpl(ref Exception err) => Create(Filename, ref err);
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override byte[] ReadImpl(int num_bytes, byte[] optional_buffer, int bufferPtr = 0)
|
||||
{
|
||||
if (File == null)
|
||||
return null;
|
||||
|
||||
if (optional_buffer == null)
|
||||
{
|
||||
if (BufSize < num_bytes)
|
||||
{
|
||||
BufSize = num_bytes;
|
||||
Buf = new byte[BufSize];
|
||||
}
|
||||
|
||||
optional_buffer = Buf;
|
||||
}
|
||||
|
||||
int total_read = 0;
|
||||
while (total_read < num_bytes)
|
||||
{
|
||||
int nread = File.Read(optional_buffer, total_read, num_bytes - total_read);
|
||||
total_read += nread;
|
||||
if (total_read < num_bytes && File.Position >= File.Length)
|
||||
return null;
|
||||
}
|
||||
|
||||
return optional_buffer;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override bool Seek(long offset, SeekOrigin whence)
|
||||
{
|
||||
if (File == null)
|
||||
return true;
|
||||
|
||||
try
|
||||
{
|
||||
long pos = File.Seek(offset, whence);
|
||||
return pos != offset;
|
||||
}
|
||||
catch
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
212
BurnOutSharp/External/libgsf/Input/GsfInputTextline.cs
vendored
Normal file
212
BurnOutSharp/External/libgsf/Input/GsfInputTextline.cs
vendored
Normal file
@@ -0,0 +1,212 @@
|
||||
|
||||
/* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
|
||||
/*
|
||||
* gsf-input-textline.c: textline based input
|
||||
*
|
||||
* Copyright (C) 2002-2006 Jody Goldberg (jody@gnome.org)
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of version 2.1 of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301
|
||||
* USA
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
|
||||
namespace LibGSF.Input
|
||||
{
|
||||
public class GsfInputTextline : GsfInput
|
||||
{
|
||||
#region Properties
|
||||
|
||||
public GsfInput Source { get; set; } = null;
|
||||
|
||||
public byte[] Remainder { get; set; } = null;
|
||||
|
||||
public int RemainderSize { get; set; } = 0;
|
||||
|
||||
public int MaxLineSize { get; set; } = 512; // An initial guess
|
||||
|
||||
public byte[] Buf { get; set; } = null;
|
||||
|
||||
public int BufSize { get; set; } = 0;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Functions
|
||||
|
||||
/// <param name="source">In some combination of ascii and UTF-8</param>
|
||||
/// <returns>A new file</returns>
|
||||
/// <remarks>This adds a reference to @source.</remarks>
|
||||
public static GsfInputTextline Create(GsfInput source)
|
||||
{
|
||||
if (source == null)
|
||||
return null;
|
||||
|
||||
return new GsfInputTextline
|
||||
{
|
||||
Source = source,
|
||||
Buf = null,
|
||||
BufSize = 0,
|
||||
Size = source.Size,
|
||||
Name = source.Name,
|
||||
};
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override GsfInput DupImpl(ref Exception err)
|
||||
{
|
||||
return new GsfInputTextline()
|
||||
{
|
||||
Source = this.Source,
|
||||
Size = this.Size,
|
||||
Name = this.Name,
|
||||
};
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override byte[] ReadImpl(int num_bytes, byte[] optional_buffer, int bufferPtr = 0)
|
||||
{
|
||||
Remainder = null;
|
||||
byte[] res = Source.Read(num_bytes, optional_buffer);
|
||||
CurrentOffset = Source.CurrentOffset;
|
||||
return res;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override bool Seek(long offset, SeekOrigin whence)
|
||||
{
|
||||
Remainder = null;
|
||||
bool res = Source.Seek(offset, whence);
|
||||
CurrentOffset = Source.CurrentOffset;
|
||||
return res;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Utilities
|
||||
|
||||
/// <summary>
|
||||
/// A utility routine to read things line by line from the underlying source.
|
||||
/// Trailing newlines and carriage returns are stripped, and the resultant buffer
|
||||
/// can be edited.
|
||||
/// </summary>
|
||||
/// <returns>The string read, or null on eof.</returns>
|
||||
internal string GetStringASCII() => GetStringUTF8();
|
||||
|
||||
/// <summary>
|
||||
/// A utility routine to read things line by line from the underlying source.
|
||||
/// Trailing newlines and carriage returns are stripped, and the resultant buffer
|
||||
/// can be edited.
|
||||
/// </summary>
|
||||
/// <returns>The string read, or null on eof.</returns>
|
||||
internal string GetStringUTF8()
|
||||
{
|
||||
int len, ptr, end;
|
||||
int count = 0;
|
||||
while (true)
|
||||
{
|
||||
if (Remainder == null || RemainderSize == 0)
|
||||
{
|
||||
long remain = Source.Remaining();
|
||||
len = (int)Math.Min(remain, MaxLineSize);
|
||||
|
||||
Remainder = Source.Read(len, null);
|
||||
if (Remainder == null)
|
||||
return null;
|
||||
|
||||
RemainderSize = len;
|
||||
}
|
||||
|
||||
ptr = 0; // Remainder[0]
|
||||
end = ptr + RemainderSize;
|
||||
|
||||
for (; ptr < end; ptr++)
|
||||
{
|
||||
if (Remainder[ptr] == '\n' || Remainder[ptr] == '\r')
|
||||
break;
|
||||
}
|
||||
|
||||
// Copy the remains into the buffer, grow it if necessary
|
||||
len = ptr;
|
||||
if (count + len >= BufSize)
|
||||
{
|
||||
BufSize = len;
|
||||
Buf = new byte[BufSize + 1];
|
||||
}
|
||||
|
||||
if (Buf == null)
|
||||
return null;
|
||||
|
||||
Array.Copy(Remainder, 0, Buf, count, len);
|
||||
count += len;
|
||||
|
||||
if (ptr < end)
|
||||
{
|
||||
char last = (char)Remainder[ptr];
|
||||
|
||||
// Eat the trailing eol marker: \n, \r\n, or \r.
|
||||
ptr++;
|
||||
if (ptr >= end && last == '\r')
|
||||
{
|
||||
// Be extra careful, the CR is at the bound
|
||||
if (Source.Remaining() > 0)
|
||||
{
|
||||
if (Source.Read(1, Remainder, ptr) == null)
|
||||
return null;
|
||||
|
||||
RemainderSize = 1;
|
||||
end = ptr + 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
ptr = end = -1;
|
||||
}
|
||||
}
|
||||
|
||||
if (ptr != -1 && last == '\r' && Remainder[ptr] == '\n')
|
||||
ptr++;
|
||||
|
||||
break;
|
||||
}
|
||||
else if (Source.Remaining() <= 0)
|
||||
{
|
||||
ptr = end = -1;
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
Remainder = null;
|
||||
}
|
||||
}
|
||||
|
||||
if (ptr == -1)
|
||||
{
|
||||
Remainder = null;
|
||||
RemainderSize = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
RemainderSize = end - ptr;
|
||||
}
|
||||
|
||||
CurrentOffset = Source.CurrentOffset - (Remainder != null ? RemainderSize : 0);
|
||||
Buf[count] = 0x00;
|
||||
|
||||
return Encoding.UTF8.GetString(Buf);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
213
BurnOutSharp/External/libgsf/Input/GsfStructuredBlob.cs
vendored
Normal file
213
BurnOutSharp/External/libgsf/Input/GsfStructuredBlob.cs
vendored
Normal file
@@ -0,0 +1,213 @@
|
||||
/* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
|
||||
/*
|
||||
* gsf-structured_blob.c : Utility storage to blob in/out a tree of data
|
||||
*
|
||||
* Copyright (C) 2002-2006 Jody Goldberg (jody@gnome.org)
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of version 2.1 of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301
|
||||
* USA
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.IO;
|
||||
using LibGSF.Output;
|
||||
|
||||
namespace LibGSF.Input
|
||||
{
|
||||
public class GsfStructuredBlob : GsfInfile
|
||||
{
|
||||
#region Properties
|
||||
|
||||
public GsfSharedMemory Data { get; set; } = null;
|
||||
|
||||
public GsfStructuredBlob[] Children { get; set; } = null;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Constructor and Destructor
|
||||
|
||||
/// <summary>
|
||||
/// Private constructor
|
||||
/// </summary>
|
||||
private GsfStructuredBlob() { }
|
||||
|
||||
/// <summary>
|
||||
/// Destructor
|
||||
/// </summary>
|
||||
~GsfStructuredBlob()
|
||||
{
|
||||
if (Data != null)
|
||||
Data = null;
|
||||
|
||||
if (Children != null)
|
||||
Children = null;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Functions
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override GsfInput DupImpl(ref Exception err)
|
||||
{
|
||||
GsfStructuredBlob dst = new GsfStructuredBlob();
|
||||
|
||||
if (Data != null)
|
||||
dst.Data = Data;
|
||||
|
||||
if (Children != null)
|
||||
{
|
||||
dst.Children = new GsfStructuredBlob[Children.Length];
|
||||
Array.Copy(Children, dst.Children, Children.Length);
|
||||
}
|
||||
|
||||
return dst;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override byte[] ReadImpl(int num_bytes, byte[] optional_buffer, int bufferPtr = 0)
|
||||
{
|
||||
byte[] src = Data.Buf;
|
||||
if (src == null)
|
||||
return null;
|
||||
|
||||
if (optional_buffer == null)
|
||||
optional_buffer = new byte[num_bytes];
|
||||
|
||||
Array.Copy(src, CurrentOffset, optional_buffer, 0, num_bytes);
|
||||
return optional_buffer;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override bool Seek(long offset, SeekOrigin whence) => false;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override int NumChildren() => Children != null ? Children.Length : -1;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override string NameByIndex(int i)
|
||||
{
|
||||
if (Children == null)
|
||||
return null;
|
||||
|
||||
if (i < 0 || i >= Children.Length)
|
||||
return null;
|
||||
|
||||
return Children[i].Name;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override GsfInput ChildByIndex(int i, ref Exception error)
|
||||
{
|
||||
if (Children == null)
|
||||
return null;
|
||||
|
||||
if (i < 0 || i >= Children.Length)
|
||||
return null;
|
||||
|
||||
return Children[i];
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override GsfInput ChildByName(string name, ref Exception error)
|
||||
{
|
||||
if (Children == null)
|
||||
return null;
|
||||
|
||||
for (int i = 0; i < Children.Length; i++)
|
||||
{
|
||||
GsfInput child = Children[i];
|
||||
if (child != null && child.Name == name)
|
||||
return child.Duplicate(ref error);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create a tree of binary blobs with unknown content from a GsfInput or
|
||||
/// #GsfInfile and store it in a newly created #GsfStructuredBlob.
|
||||
/// </summary>
|
||||
/// <param name="input">An input (potentially a GsfInfile) holding the blob</param>
|
||||
/// <returns>A new GsfStructuredBlob object which the caller is responsible for.</returns>
|
||||
public static GsfStructuredBlob Read(GsfInput input)
|
||||
{
|
||||
if (input == null)
|
||||
return null;
|
||||
|
||||
GsfStructuredBlob blob = new GsfStructuredBlob();
|
||||
long content_size = input.Remaining();
|
||||
if (content_size > 0)
|
||||
{
|
||||
byte[] buf = new byte[content_size];
|
||||
input.Read((int)content_size, buf);
|
||||
blob.Data = GsfSharedMemory.Create(buf, content_size, true);
|
||||
}
|
||||
|
||||
blob.Name = input.Name;
|
||||
|
||||
if (input is GsfInfile infile)
|
||||
{
|
||||
int i = infile.NumChildren();
|
||||
if (i > 0)
|
||||
{
|
||||
Exception err = null;
|
||||
blob.Children = new GsfStructuredBlob[i];
|
||||
while (i-- > 0)
|
||||
{
|
||||
GsfInput child = infile.ChildByIndex(i, ref err);
|
||||
GsfStructuredBlob child_blob = null;
|
||||
if (child != null)
|
||||
child_blob = Read(child);
|
||||
|
||||
blob.Children[i] = child_blob;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return blob;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Dumps structured blob @blob onto the <paramref name="container"/>. Will fail if the output is
|
||||
/// not an Outfile and blob has multiple streams.
|
||||
/// </summary>
|
||||
/// <returns>true on success.</returns>
|
||||
public bool Write(GsfOutfile container)
|
||||
{
|
||||
if (container == null)
|
||||
return false;
|
||||
|
||||
bool has_kids = (Children != null && Children.Length > 0);
|
||||
GsfOutput output = container.NewChild(Name, has_kids);
|
||||
if (has_kids)
|
||||
{
|
||||
for (int i = 0; i < Children.Length; i++)
|
||||
{
|
||||
GsfStructuredBlob child_blob = Children[i];
|
||||
if (!child_blob.Write(output as GsfOutfile))
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (Data != null)
|
||||
output.Write((int)Data.Size, Data.Buf);
|
||||
|
||||
output.Close();
|
||||
return true;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
35
BurnOutSharp/External/libgsf/Output/GsfOutfile.cs
vendored
Normal file
35
BurnOutSharp/External/libgsf/Output/GsfOutfile.cs
vendored
Normal file
@@ -0,0 +1,35 @@
|
||||
/* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
|
||||
/*
|
||||
* gsf-outfile.c :
|
||||
*
|
||||
* Copyright (C) 2002-2006 Jody Goldberg (jody@gnome.org)
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of version 2.1 of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301
|
||||
* USA
|
||||
*/
|
||||
|
||||
namespace LibGSF.Output
|
||||
{
|
||||
public abstract class GsfOutfile : GsfOutput
|
||||
{
|
||||
#region Virtual Functions
|
||||
|
||||
/// <param name="name">The name of the new child to create</param>
|
||||
/// <param name="is_dir">true to create a directory, false to create a plain file</param>
|
||||
/// <returns>A newly created child</returns>
|
||||
public virtual GsfOutput NewChild(string name, bool is_dir) => null;
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
930
BurnOutSharp/External/libgsf/Output/GsfOutfileMSOle.cs
vendored
Normal file
930
BurnOutSharp/External/libgsf/Output/GsfOutfileMSOle.cs
vendored
Normal file
@@ -0,0 +1,930 @@
|
||||
/* 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;
|
||||
|
||||
namespace LibGSF.Output
|
||||
{
|
||||
public class GsfOutfileMSOleBlock
|
||||
{
|
||||
public int Shift { get; set; }
|
||||
|
||||
public int Size { get; set; }
|
||||
}
|
||||
|
||||
public class GsfOutfileMSOle : GsfOutfile
|
||||
{
|
||||
#region Constants
|
||||
|
||||
// The most common values
|
||||
private const int OLE_DEFAULT_THRESHOLD = 0x1000;
|
||||
private const int OLE_DEFAULT_SB_SHIFT = 6;
|
||||
private const int OLE_DEFAULT_BB_SHIFT = 9;
|
||||
private const long OLE_DEFAULT_BB_SIZE = (1u << OLE_DEFAULT_BB_SHIFT);
|
||||
private const long OLE_DEFAULT_SB_SIZE = (1u << OLE_DEFAULT_SB_SHIFT);
|
||||
|
||||
// Globals to support variable OLE sector size.
|
||||
|
||||
/// <summary>
|
||||
/// 512 and 4096 bytes are the only known values for sector size on
|
||||
/// Win2k/XP platforms. Attempts to create OLE files on Win2k/XP with
|
||||
/// other values using StgCreateStorageEx() fail with invalid parameter.
|
||||
/// This code has been tested with 128,256,512,4096,8192 sizes for
|
||||
/// libgsf read/write. Interoperability with MS OLE32.DLL has been
|
||||
/// tested with 512 and 4096 block size for filesizes up to 2GB.
|
||||
/// </summary>
|
||||
private const int ZERO_PAD_BUF_SIZE = 4096;
|
||||
|
||||
private const uint CHUNK_SIZE = 1024u;
|
||||
|
||||
private static readonly byte[] default_header =
|
||||
{
|
||||
/* 0x00 */ 0xd0, 0xcf, 0x11, 0xe0, 0xa1, 0xb1, 0x1a, 0xe1,
|
||||
/* 0x08 */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
/* 0x18 */ 0x3e, 0x00, 0x03, 0x00, 0xfe, 0xff, 0x09, 0x00,
|
||||
/* 0x20 */ 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
/* 0x28 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
/* 0x30 */ 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00,
|
||||
/* 0x38 */ 0x00, 0x10, 0x00, 0x00 /* 0x3c-0x4b: filled on close */
|
||||
};
|
||||
|
||||
#endregion
|
||||
|
||||
#region Enums
|
||||
|
||||
public enum MSOleOutfileType
|
||||
{
|
||||
MSOLE_DIR,
|
||||
MSOLE_SMALL_BLOCK,
|
||||
MSOLE_BIG_BLOCK
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Properties
|
||||
|
||||
public GsfOutfile Parent { get; set; }
|
||||
|
||||
public GsfOutput Sink { get; set; } = null;
|
||||
|
||||
public GsfOutfileMSOle Root { get; set; } = null;
|
||||
|
||||
public GsfMSOleSortingKey Key { get; set; }
|
||||
|
||||
public MSOleOutfileType Type { get; set; } = MSOleOutfileType.MSOLE_DIR;
|
||||
|
||||
public int FirstBlock { get; set; }
|
||||
|
||||
public int Blocks { get; set; }
|
||||
|
||||
public int ChildIndex { get; set; }
|
||||
|
||||
public GsfOutfileMSOleBlock BigBlock { get; set; }
|
||||
|
||||
public GsfOutfileMSOleBlock SmallBlock { get; set; }
|
||||
|
||||
#region Union (Content)
|
||||
|
||||
#region Struct (Dir)
|
||||
|
||||
public List<GsfOutfileMSOle> Content_Dir_Children { get; set; } = null;
|
||||
|
||||
/// <summary>
|
||||
/// Only valid for the root, ordered
|
||||
/// </summary>
|
||||
public List<GsfOutfileMSOle> Content_Dir_RootOrder { get; set; } = null;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Struct (SmallBlock)
|
||||
|
||||
public byte[] Content_SmallBlock_Buf { get; set; }
|
||||
|
||||
#endregion
|
||||
|
||||
#region Struct (BigBlock)
|
||||
|
||||
/// <summary>
|
||||
/// In bytes
|
||||
/// </summary>
|
||||
public int Content_BigBlock_StartOffset { get; set; }
|
||||
|
||||
#endregion
|
||||
|
||||
#endregion
|
||||
|
||||
/// <summary>
|
||||
/// 16 byte GUID used by some apps
|
||||
/// </summary>
|
||||
public byte[] ClassID { get; set; } = new byte[16];
|
||||
|
||||
#endregion
|
||||
|
||||
#region Constructor and Destructor
|
||||
|
||||
/// <summary>
|
||||
/// Private constructor
|
||||
/// </summary>
|
||||
private GsfOutfileMSOle() => MakeSortingName();
|
||||
|
||||
/// <summary>
|
||||
/// Creates the root directory of an MS OLE file and manages the addition of
|
||||
/// children.
|
||||
/// </summary>
|
||||
/// <param name="sink">A GsfOutput to hold the OLE2 file.</param>
|
||||
/// <param name="bb_size">Size of large blocks.</param>
|
||||
/// <param name="sb_size">Size of small blocks.</param>
|
||||
/// <returns>The new ole file handler.</returns>
|
||||
/// <remarks>This adds a reference to <paramref name="sink"/>.</remarks>
|
||||
public static GsfOutfileMSOle CreateFull(GsfOutput sink, int bb_size, int sb_size)
|
||||
{
|
||||
if (sink == null)
|
||||
return null;
|
||||
if (sb_size != (1u << ComputeShift(sb_size)))
|
||||
return null;
|
||||
if (bb_size != (1u << ComputeShift(bb_size)))
|
||||
return null;
|
||||
if (sb_size > bb_size)
|
||||
return null;
|
||||
|
||||
GsfOutfileMSOle ole = new GsfOutfileMSOle
|
||||
{
|
||||
Sink = sink,
|
||||
SmallBlock = new GsfOutfileMSOleBlock
|
||||
{
|
||||
Size = sb_size,
|
||||
},
|
||||
BigBlock = new GsfOutfileMSOleBlock
|
||||
{
|
||||
Size = bb_size,
|
||||
},
|
||||
Container = null,
|
||||
Name = sink.Name,
|
||||
Type = MSOleOutfileType.MSOLE_DIR,
|
||||
Content_Dir_RootOrder = new List<GsfOutfileMSOle>(),
|
||||
};
|
||||
|
||||
ole.RegisterChild(ole);
|
||||
|
||||
// Build the header
|
||||
byte[] buf = Enumerable.Repeat<byte>(0xFF, OLE_HEADER_SIZE).ToArray();
|
||||
Array.Copy(default_header, buf, default_header.Length);
|
||||
|
||||
byte[] temp = BitConverter.GetBytes((ushort)ole.BigBlock.Shift);
|
||||
Array.Copy(temp, 0, buf, OLE_HEADER_BB_SHIFT, temp.Length);
|
||||
|
||||
temp = BitConverter.GetBytes((ushort)ole.SmallBlock.Shift);
|
||||
Array.Copy(temp, 0, buf, OLE_HEADER_SB_SHIFT, temp.Length);
|
||||
|
||||
// 4k sector OLE files seen in the wild have version 4
|
||||
if (ole.BigBlock.Size == 4096)
|
||||
{
|
||||
BitConverter.GetBytes((ushort)4);
|
||||
Array.Copy(temp, 0, buf, OLE_HEADER_MAJOR_VER, temp.Length);
|
||||
}
|
||||
|
||||
sink.Write(OLE_HEADER_SIZE, buf);
|
||||
|
||||
// Header must be padded out to bb.size with zeros
|
||||
ole.PadZero();
|
||||
|
||||
return ole;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates the root directory of an MS OLE file and manages the addition of
|
||||
/// children.
|
||||
/// </summary>
|
||||
/// <param name="sink">A GsfOutput to hold the OLE2 file</param>
|
||||
/// <returns>The new ole file handler.</returns>
|
||||
/// <remarks>This adds a reference to <paramref name="sink"/>.</remarks>
|
||||
public static GsfOutfileMSOle Create(GsfOutput sink) => CreateFull(sink, (int)OLE_DEFAULT_BB_SIZE, (int)OLE_DEFAULT_SB_SIZE);
|
||||
|
||||
~GsfOutfileMSOle()
|
||||
{
|
||||
Key = null;
|
||||
|
||||
switch (Type)
|
||||
{
|
||||
case MSOleOutfileType.MSOLE_DIR:
|
||||
Content_Dir_Children.Clear();
|
||||
Content_Dir_Children = null;
|
||||
if (Content_Dir_RootOrder != null)
|
||||
Console.Error.WriteLine("Finalizing a MSOle Outfile without closing it.");
|
||||
|
||||
break;
|
||||
|
||||
case MSOleOutfileType.MSOLE_SMALL_BLOCK:
|
||||
Content_SmallBlock_Buf = null;
|
||||
break;
|
||||
|
||||
case MSOleOutfileType.MSOLE_BIG_BLOCK:
|
||||
break;
|
||||
|
||||
default:
|
||||
throw new Exception("This should not be reached");
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Functions
|
||||
|
||||
public override void Dispose()
|
||||
{
|
||||
if (!IsClosed)
|
||||
Close();
|
||||
|
||||
Sink = null;
|
||||
base.Dispose();
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override bool SeekImpl(long offset, SeekOrigin whence)
|
||||
{
|
||||
switch (whence)
|
||||
{
|
||||
case SeekOrigin.Begin: break;
|
||||
case SeekOrigin.Current: offset += CurrentOffset; break;
|
||||
case SeekOrigin.End: offset += CurrentSize; break;
|
||||
default: throw new Exception("This should not be reached");
|
||||
}
|
||||
|
||||
switch (Type)
|
||||
{
|
||||
case MSOleOutfileType.MSOLE_DIR:
|
||||
if (offset != 0)
|
||||
{
|
||||
Console.Error.WriteLine("Attempt to seek a directory");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
|
||||
case MSOleOutfileType.MSOLE_SMALL_BLOCK:
|
||||
// It is ok to seek past the big block threshold
|
||||
// we don't convert until they _write_ something
|
||||
return true;
|
||||
|
||||
case MSOleOutfileType.MSOLE_BIG_BLOCK:
|
||||
return Sink.Seek(Content_BigBlock_StartOffset + offset, SeekOrigin.Begin);
|
||||
|
||||
default:
|
||||
throw new Exception("This should not be reached");
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override bool CloseImpl()
|
||||
{
|
||||
if (Container == null)
|
||||
{
|
||||
// The root dir
|
||||
bool ok = WriteDirectory();
|
||||
HoistError();
|
||||
if (!Sink.Close())
|
||||
ok = false;
|
||||
|
||||
return ok;
|
||||
}
|
||||
|
||||
if (Type == MSOleOutfileType.MSOLE_BIG_BLOCK)
|
||||
{
|
||||
Seek(0, SeekOrigin.End);
|
||||
PadZero();
|
||||
Blocks = CurrentBlock() - FirstBlock;
|
||||
return Unwrap(Sink);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override bool WriteImpl(int num_bytes, byte[] data)
|
||||
{
|
||||
if (Type == MSOleOutfileType.MSOLE_DIR)
|
||||
return false;
|
||||
|
||||
if (Type == MSOleOutfileType.MSOLE_SMALL_BLOCK)
|
||||
{
|
||||
if ((CurrentOffset + num_bytes) < OLE_DEFAULT_THRESHOLD)
|
||||
{
|
||||
Array.Copy(Content_SmallBlock_Buf, CurrentOffset, data, 0, num_bytes);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ok = Wrap(Sink);
|
||||
if (!ok)
|
||||
return false;
|
||||
|
||||
byte[] buf = Content_SmallBlock_Buf;
|
||||
Content_SmallBlock_Buf = null;
|
||||
|
||||
long start_offset = Sink.CurrentOffset;
|
||||
Content_BigBlock_StartOffset = (int)start_offset;
|
||||
if (Content_BigBlock_StartOffset != start_offset)
|
||||
{
|
||||
// Check for overflow
|
||||
Console.Error.WriteLine("File too big");
|
||||
return false;
|
||||
}
|
||||
|
||||
FirstBlock = CurrentBlock();
|
||||
Type = MSOleOutfileType.MSOLE_BIG_BLOCK;
|
||||
|
||||
int wsize = (int)CurrentSize;
|
||||
if (wsize != CurrentSize)
|
||||
{
|
||||
// Check for overflow
|
||||
Console.Error.WriteLine("File too big");
|
||||
return false;
|
||||
}
|
||||
|
||||
Sink.Write(wsize, buf);
|
||||
|
||||
// If we had a seek then we might not be at the right location.
|
||||
// This can happen both with a seek beyond the end of file
|
||||
// (see bug #14) and with a backward seek.
|
||||
Sink.Seek(Content_BigBlock_StartOffset + CurrentOffset, SeekOrigin.Begin);
|
||||
}
|
||||
|
||||
if (Type != MSOleOutfileType.MSOLE_BIG_BLOCK)
|
||||
return false;
|
||||
|
||||
Sink.Write(num_bytes, data);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override long VPrintFImpl(string format, params string[] args)
|
||||
{
|
||||
// An optimization.
|
||||
if (Type == MSOleOutfileType.MSOLE_BIG_BLOCK)
|
||||
return Sink.VPrintF(format, args);
|
||||
|
||||
// In other cases, use the gsf_output_real_vprintf fallback method.
|
||||
// (This eventually calls gsf_outfile_msole_write, which will also
|
||||
// check that ole.type != MSOLE_DIR.)
|
||||
return VPrintF(format, args);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override GsfOutput NewChild(string name, bool is_dir)
|
||||
{
|
||||
if (Type != MSOleOutfileType.MSOLE_DIR)
|
||||
return null;
|
||||
|
||||
GsfOutfileMSOle child = new GsfOutfileMSOle();
|
||||
if (is_dir)
|
||||
{
|
||||
child.Type = MSOleOutfileType.MSOLE_DIR;
|
||||
child.Content_Dir_Children = null;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Start as small block
|
||||
child.Type = MSOleOutfileType.MSOLE_SMALL_BLOCK;
|
||||
child.Content_SmallBlock_Buf = new byte[OLE_DEFAULT_THRESHOLD];
|
||||
}
|
||||
|
||||
child.Root = Root;
|
||||
child.Sink = Sink;
|
||||
|
||||
child.SetSmallBlockSize(SmallBlock.Size);
|
||||
child.SetBigBlockSize(BigBlock.Size);
|
||||
|
||||
child.Name = name;
|
||||
child.Container = this;
|
||||
|
||||
Content_Dir_Children.Add(child);
|
||||
Root.RegisterChild(child);
|
||||
|
||||
return child;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Write <paramref name="clsid"/> to the directory associated with ole.
|
||||
/// </summary>
|
||||
/// <param name="clsid">Identifier (often a GUID in MS Windows apps)</param>
|
||||
/// <returns>true on success.</returns>
|
||||
public bool SetClassID(byte[] clsid)
|
||||
{
|
||||
if (Type != MSOleOutfileType.MSOLE_DIR)
|
||||
return false;
|
||||
|
||||
Array.Copy(clsid, ClassID, ClassID.Length);
|
||||
return true;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Utilities
|
||||
|
||||
/// <summary>
|
||||
/// Returns the number of times 1 must be shifted left to reach value
|
||||
/// </summary>
|
||||
private static int ComputeShift(int value)
|
||||
{
|
||||
int i = 0;
|
||||
while ((value >> i) > 1)
|
||||
i++;
|
||||
|
||||
return i;
|
||||
}
|
||||
|
||||
private void SetBigBlockSize(int size)
|
||||
{
|
||||
BigBlock.Size = size;
|
||||
BigBlock.Shift = ComputeShift(size);
|
||||
}
|
||||
|
||||
private void SetSmallBlockSize(int size)
|
||||
{
|
||||
SmallBlock.Size = size;
|
||||
SmallBlock.Shift = ComputeShift(size);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Static objects are zero-initialized
|
||||
/// </summary>
|
||||
private static byte[] zero_buf = new byte[ZERO_PAD_BUF_SIZE];
|
||||
|
||||
/// <summary>
|
||||
/// Calculate the block of the current offset in the file. A useful idiom is to
|
||||
/// pad_zero to move to the start of the next block, then get the block number.
|
||||
/// This avoids fence post type problems with partial blocks.
|
||||
/// </summary>
|
||||
private int CurrentBlock() => (int)((Sink.CurrentOffset - OLE_HEADER_SIZE) >> BigBlock.Shift);
|
||||
|
||||
private int BytesLeftInBlock()
|
||||
{
|
||||
// Blocks are multiples of BigBlock.Size (the header is padded out to BigBlock.Size)
|
||||
long r = Sink.CurrentOffset % BigBlock.Size;
|
||||
return (int)((r != 0) ? (BigBlock.Size - r) : 0);
|
||||
}
|
||||
|
||||
private void PadZero()
|
||||
{
|
||||
// No need to bounds check. len will always be less than bb.size, and
|
||||
// we already check that zero_buf is big enough at creation
|
||||
int len = BytesLeftInBlock();
|
||||
if (len > 0)
|
||||
Sink.Write(len, zero_buf);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Utility routine to generate a BAT for a file known to be sequential and
|
||||
/// continuous.
|
||||
/// </summary>
|
||||
private static void WriteBat(GsfOutput sink, int block, int blocks)
|
||||
{
|
||||
byte[] buf = new byte[BAT_INDEX_SIZE * CHUNK_SIZE];
|
||||
int bufi = 0;
|
||||
|
||||
while (blocks-- > 1)
|
||||
{
|
||||
block++;
|
||||
|
||||
byte[] temp2 = BitConverter.GetBytes(block);
|
||||
Array.Copy(temp2, 0, buf, bufi * BAT_INDEX_SIZE, buf.Length);
|
||||
|
||||
bufi++;
|
||||
if (bufi == CHUNK_SIZE)
|
||||
{
|
||||
if (bufi != 0)
|
||||
sink.Write(bufi * BAT_INDEX_SIZE, buf);
|
||||
|
||||
bufi = 0;
|
||||
}
|
||||
}
|
||||
|
||||
byte[] temp = BitConverter.GetBytes(BAT_MAGIC_END_OF_CHAIN);
|
||||
Array.Copy(temp, 0, buf, bufi * BAT_INDEX_SIZE, buf.Length);
|
||||
|
||||
bufi++;
|
||||
if (bufi == CHUNK_SIZE)
|
||||
{
|
||||
if (bufi != 0)
|
||||
sink.Write(bufi * BAT_INDEX_SIZE, buf);
|
||||
|
||||
bufi = 0;
|
||||
}
|
||||
|
||||
if (bufi != 0)
|
||||
sink.Write(bufi * BAT_INDEX_SIZE, buf);
|
||||
|
||||
bufi = 0;
|
||||
}
|
||||
|
||||
private static void WriteConst(GsfOutput sink, uint value, int n)
|
||||
{
|
||||
byte[] buf = new byte[BAT_INDEX_SIZE * CHUNK_SIZE];
|
||||
int bufi = 0;
|
||||
|
||||
while (n-- > 0)
|
||||
{
|
||||
byte[] temp = BitConverter.GetBytes(value);
|
||||
Array.Copy(temp, 0, buf, bufi * BAT_INDEX_SIZE, buf.Length);
|
||||
|
||||
bufi++;
|
||||
if (bufi == CHUNK_SIZE)
|
||||
{
|
||||
if (bufi != 0)
|
||||
sink.Write(bufi * BAT_INDEX_SIZE, buf);
|
||||
|
||||
bufi = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (bufi != 0)
|
||||
sink.Write(bufi * BAT_INDEX_SIZE, buf);
|
||||
|
||||
bufi = 0;
|
||||
}
|
||||
|
||||
private void PadBatUnused(int residual)
|
||||
{
|
||||
WriteConst(Sink, BAT_MAGIC_UNUSED, (BytesLeftInBlock() / BAT_INDEX_SIZE) - residual);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Write the metadata (dirents, small block, xbats)
|
||||
/// </summary>
|
||||
private bool WriteDirectory()
|
||||
{
|
||||
byte[] buf = new byte[OLE_HEADER_SIZE];
|
||||
uint next;
|
||||
uint xbat_pos;
|
||||
int metabat_size = BigBlock.Size / BAT_INDEX_SIZE - 1;
|
||||
List<GsfOutfileMSOle> elem = Root.Content_Dir_RootOrder;
|
||||
|
||||
// Write small block data
|
||||
int blocks = 0;
|
||||
int sb_data_start = CurrentBlock();
|
||||
long data_size = Sink.CurrentOffset;
|
||||
|
||||
foreach (GsfOutfileMSOle child in elem)
|
||||
{
|
||||
if (child.Type == MSOleOutfileType.MSOLE_SMALL_BLOCK)
|
||||
{
|
||||
long size = child.CurrentSize;
|
||||
if (size > 0)
|
||||
{
|
||||
child.Blocks = (int)(((size - 1) >> SmallBlock.Shift) + 1);
|
||||
Sink.Write(child.Blocks << SmallBlock.Shift, child.Content_SmallBlock_Buf);
|
||||
child.FirstBlock = blocks;
|
||||
blocks += child.Blocks;
|
||||
}
|
||||
else
|
||||
{
|
||||
child.Blocks = 0;
|
||||
unchecked
|
||||
{
|
||||
child.FirstBlock = (int)BAT_MAGIC_END_OF_CHAIN;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
data_size = Sink.CurrentOffset - data_size;
|
||||
int sb_data_size = (int)data_size;
|
||||
if (sb_data_size != data_size)
|
||||
{
|
||||
// Check for overflow
|
||||
Console.Error.WriteLine("File too big");
|
||||
return false;
|
||||
}
|
||||
|
||||
PadZero();
|
||||
int sb_data_blocks = CurrentBlock() - sb_data_start;
|
||||
|
||||
// Write small block BAT (the meta bat is in a file)
|
||||
int sbat_start = CurrentBlock();
|
||||
foreach (GsfOutfileMSOle child in elem)
|
||||
{
|
||||
if (child.Type == MSOleOutfileType.MSOLE_SMALL_BLOCK && child.Blocks > 0)
|
||||
WriteBat(Sink, child.FirstBlock, child.Blocks);
|
||||
}
|
||||
|
||||
PadBatUnused(0);
|
||||
int num_sbat = CurrentBlock() - sbat_start;
|
||||
|
||||
int name_len = 0;
|
||||
|
||||
// Write dirents
|
||||
int dirent_start = CurrentBlock();
|
||||
for (int i = 0; i < elem.Count; i++)
|
||||
{
|
||||
GsfOutfileMSOle child = elem[i];
|
||||
|
||||
buf = Enumerable.Repeat<byte>(0, DIRENT_SIZE).ToArray();
|
||||
|
||||
// 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);
|
||||
string nameUtf16 = Encoding.Unicode.GetString(nameUtf16Bytes);
|
||||
name_len = nameUtf16.Length;
|
||||
if (name_len >= DIRENT_MAX_NAME_SIZE)
|
||||
name_len = DIRENT_MAX_NAME_SIZE - 1;
|
||||
|
||||
// Be wary about endianness
|
||||
for (int j = 0; j < name_len; j++)
|
||||
{
|
||||
byte[] nameUtf16Temp = BitConverter.GetBytes((ushort)nameUtf16[j]);
|
||||
Array.Copy(nameUtf16Temp, 0, buf, j * 2, 2);
|
||||
}
|
||||
|
||||
name_len++;
|
||||
}
|
||||
|
||||
byte[] writeTemp = BitConverter.GetBytes((ushort)(name_len * 2));
|
||||
Array.Copy(writeTemp, 0, buf, DIRENT_NAME_LEN, writeTemp.Length);
|
||||
|
||||
if (child.Root == child)
|
||||
{
|
||||
buf[DIRENT_TYPE] = DIRENT_TYPE_ROOTDIR;
|
||||
|
||||
writeTemp = BitConverter.GetBytes((uint)((sb_data_size > 0) ? (uint)sb_data_start : BAT_MAGIC_END_OF_CHAIN));
|
||||
Array.Copy(writeTemp, 0, buf, DIRENT_FIRSTBLOCK, writeTemp.Length);
|
||||
|
||||
writeTemp = BitConverter.GetBytes((uint)(sb_data_size));
|
||||
Array.Copy(writeTemp, 0, buf, DIRENT_FILE_SIZE, writeTemp.Length);
|
||||
|
||||
// Write the class id
|
||||
Array.Copy(child.ClassID, 0, buf, DIRENT_CLSID, child.ClassID.Length);
|
||||
}
|
||||
else if (child.Type == MSOleOutfileType.MSOLE_DIR)
|
||||
{
|
||||
buf[DIRENT_TYPE] = DIRENT_TYPE_DIR;
|
||||
|
||||
writeTemp = BitConverter.GetBytes((uint)(BAT_MAGIC_END_OF_CHAIN));
|
||||
Array.Copy(writeTemp, 0, buf, DIRENT_FIRSTBLOCK, writeTemp.Length);
|
||||
|
||||
writeTemp = BitConverter.GetBytes((uint)(0));
|
||||
Array.Copy(writeTemp, 0, buf, DIRENT_FILE_SIZE, writeTemp.Length);
|
||||
|
||||
// Write the class id
|
||||
Array.Copy(child.ClassID, 0, buf, DIRENT_CLSID, child.ClassID.Length);
|
||||
}
|
||||
else
|
||||
{
|
||||
uint size = (uint)child.Parent.CurrentSize;
|
||||
if (size != child.Parent.CurrentSize)
|
||||
Console.Error.WriteLine("File too big");
|
||||
|
||||
buf[DIRENT_TYPE] = DIRENT_TYPE_FILE;
|
||||
|
||||
writeTemp = BitConverter.GetBytes((uint)(child.FirstBlock));
|
||||
Array.Copy(writeTemp, 0, buf, DIRENT_FIRSTBLOCK, writeTemp.Length);
|
||||
|
||||
writeTemp = BitConverter.GetBytes((uint)(size));
|
||||
Array.Copy(writeTemp, 0, buf, DIRENT_FILE_SIZE, writeTemp.Length);
|
||||
}
|
||||
|
||||
writeTemp = BitConverter.GetBytes((ulong)(child.ModTime?.ToFileTime() ?? 0));
|
||||
Array.Copy(writeTemp, 0, buf, DIRENT_MODIFY_TIME, writeTemp.Length);
|
||||
|
||||
// Make everything black (red == 0)
|
||||
buf[DIRENT_COLOUR] = 1;
|
||||
|
||||
GsfOutfileMSOle tmp = child.Container as GsfOutfileMSOle;
|
||||
next = 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
|
||||
writeTemp = BitConverter.GetBytes((uint)(DIRENT_MAGIC_END));
|
||||
Array.Copy(writeTemp, 0, buf, DIRENT_PREV, writeTemp.Length);
|
||||
|
||||
writeTemp = BitConverter.GetBytes((uint)(next));
|
||||
Array.Copy(writeTemp, 0, buf, DIRENT_NEXT, writeTemp.Length);
|
||||
|
||||
uint child_index = 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;
|
||||
}
|
||||
|
||||
writeTemp = BitConverter.GetBytes((uint)(child_index));
|
||||
Array.Copy(writeTemp, 0, buf, DIRENT_CHILD, writeTemp.Length);
|
||||
|
||||
Sink.Write(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) - 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;
|
||||
}
|
||||
|
||||
byte[] outerTemp;
|
||||
|
||||
// Fix up the header
|
||||
if (BigBlock.Size == 4096)
|
||||
{
|
||||
// Set _cSectDir for 4k sector files
|
||||
outerTemp = BitConverter.GetBytes((uint)(num_dirent_blocks));
|
||||
Array.Copy(outerTemp, 0, buf, 0, outerTemp.Length);
|
||||
|
||||
Sink.Seek(OLE_HEADER_CSECTDIR, SeekOrigin.Begin);
|
||||
Sink.Write(4, buf);
|
||||
}
|
||||
|
||||
Sink.Seek(OLE_HEADER_NUM_BAT, SeekOrigin.Begin);
|
||||
outerTemp = BitConverter.GetBytes((uint)(num_bat));
|
||||
Array.Copy(outerTemp, 0, buf, 0, outerTemp.Length);
|
||||
Sink.Write(4, buf);
|
||||
outerTemp = BitConverter.GetBytes((uint)(dirent_start));
|
||||
Array.Copy(outerTemp, 0, buf, 0, outerTemp.Length);
|
||||
Sink.Write(4, buf);
|
||||
|
||||
Sink.Seek(OLE_HEADER_SBAT_START, SeekOrigin.Begin);
|
||||
outerTemp = BitConverter.GetBytes((uint)((num_sbat > 0) ? (uint)sbat_start : BAT_MAGIC_END_OF_CHAIN));
|
||||
Array.Copy(outerTemp, 0, buf, 0, outerTemp.Length);
|
||||
Sink.Write(4, buf);
|
||||
outerTemp = BitConverter.GetBytes((uint)(num_sbat));
|
||||
Array.Copy(outerTemp, 0, buf, 0, outerTemp.Length);
|
||||
Sink.Write(4, buf);
|
||||
outerTemp = BitConverter.GetBytes((uint)(xbat_pos));
|
||||
Array.Copy(outerTemp, 0, buf, 0, outerTemp.Length);
|
||||
Sink.Write(4, buf);
|
||||
outerTemp = BitConverter.GetBytes((uint)(num_xbat));
|
||||
Array.Copy(outerTemp, 0, buf, 0, outerTemp.Length);
|
||||
Sink.Write(4, buf);
|
||||
|
||||
// Write initial Meta-BAT
|
||||
for (int i = 0; i < blocks; i++)
|
||||
{
|
||||
outerTemp = BitConverter.GetBytes((uint)(bat_start + i));
|
||||
Array.Copy(outerTemp, 0, buf, 0, outerTemp.Length);
|
||||
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++)
|
||||
{
|
||||
outerTemp = BitConverter.GetBytes((uint)(bat_start + j));
|
||||
Array.Copy(outerTemp, 0, buf, 0, outerTemp.Length);
|
||||
Sink.Write(BAT_INDEX_SIZE, buf);
|
||||
}
|
||||
|
||||
if (i == num_xbat)
|
||||
{
|
||||
PadBatUnused(1);
|
||||
xbat_pos = BAT_MAGIC_END_OF_CHAIN;
|
||||
}
|
||||
else
|
||||
{
|
||||
xbat_pos++;
|
||||
}
|
||||
|
||||
outerTemp = BitConverter.GetBytes((uint)(xbat_pos));
|
||||
Array.Copy(outerTemp, 0, buf, 0, outerTemp.Length);
|
||||
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
|
||||
}
|
||||
}
|
||||
97
BurnOutSharp/External/libgsf/Output/GsfOutfileStdio.cs
vendored
Normal file
97
BurnOutSharp/External/libgsf/Output/GsfOutfileStdio.cs
vendored
Normal file
@@ -0,0 +1,97 @@
|
||||
/* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
|
||||
/*
|
||||
* gsf-outfile-stdio.c: A directory tree wrapper for Outfile
|
||||
*
|
||||
* Copyright (C) 2004-2006 Novell, Inc.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of version 2.1 of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301
|
||||
* USA
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.IO;
|
||||
|
||||
namespace LibGSF.Output
|
||||
{
|
||||
public class GsfOutfileStdio : GsfOutfile
|
||||
{
|
||||
#region Properties
|
||||
|
||||
public string Root { get; set; } = null;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Constructor
|
||||
|
||||
/// <summary>
|
||||
/// Private constructor
|
||||
/// </summary>
|
||||
private GsfOutfileStdio() { }
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="root">Root directory in utf8.</param>
|
||||
/// <param name="err">Place to store an Exception if anything goes wrong</param>
|
||||
/// <returns>A new outfile or null.</returns>
|
||||
public static GsfOutfileStdio Create(string root, ref Exception err)
|
||||
{
|
||||
if (!Directory.Exists(root))
|
||||
{
|
||||
try
|
||||
{
|
||||
Directory.CreateDirectory(root);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
err = new Exception($"{root}: {ex.Message}");
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
GsfOutfileStdio ofs = new GsfOutfileStdio
|
||||
{
|
||||
Root = root,
|
||||
};
|
||||
|
||||
ofs.SetNameFromFilename(root);
|
||||
return ofs;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Functions
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override GsfOutput NewChild(string name, bool is_dir)
|
||||
{
|
||||
string path = Path.Combine(Root, name);
|
||||
|
||||
Exception err = null;
|
||||
|
||||
GsfOutput child;
|
||||
if (is_dir)
|
||||
child = Create(path, ref err);
|
||||
else
|
||||
child = GsfOutputStdio.Create(path, ref err);
|
||||
|
||||
return child;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override bool CloseImpl() => true;
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
881
BurnOutSharp/External/libgsf/Output/GsfOutfileZip.cs
vendored
Normal file
881
BurnOutSharp/External/libgsf/Output/GsfOutfileZip.cs
vendored
Normal file
@@ -0,0 +1,881 @@
|
||||
/* 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.GsfZipImpl;
|
||||
|
||||
namespace LibGSF.Output
|
||||
{
|
||||
public class GsfOutfileZip : GsfOutfile
|
||||
{
|
||||
#region Constants
|
||||
|
||||
public static readonly bool? ZIP_CREATE_DEFAULT_ZIP64 = null;
|
||||
|
||||
public const bool ZIP_ADD_UNIXTIME_FIELD = true;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Properties
|
||||
|
||||
public GsfOutput Sink { get; set; } = null;
|
||||
|
||||
public GsfOutfileZip Root { get; set; } = null;
|
||||
|
||||
public bool? SinkIsSeekable { get; set; } = null;
|
||||
|
||||
public bool? Zip64 { get; set; } = ZIP_CREATE_DEFAULT_ZIP64;
|
||||
|
||||
public string EntryName { get; set; } = null;
|
||||
|
||||
public GsfZipVDir VDir { get; set; } = null;
|
||||
|
||||
/// <summary>
|
||||
/// Only valid for the root, ordered
|
||||
/// </summary>
|
||||
public List<GsfOutfileZip> RootOrder { get; set; } = null;
|
||||
|
||||
public ZStream Stream { get; set; } = null;
|
||||
|
||||
public GsfZipCompressionMethod CompressionMethod { get; set; } = GsfZipCompressionMethod.GSF_ZIP_DEFLATED;
|
||||
|
||||
public int DeflateLevel { get; set; } = Z_DEFAULT_COMPRESSION;
|
||||
|
||||
public bool Writing { get; set; } = false;
|
||||
|
||||
public byte[] Buf { get; set; } = null;
|
||||
|
||||
public int BufSize { get; set; } = 0;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Constructor and Destructor
|
||||
|
||||
/// <summary>
|
||||
/// Private constructor
|
||||
/// </summary>
|
||||
private GsfOutfileZip() { }
|
||||
|
||||
/// <summary>
|
||||
/// Creates the root directory of a Zip file and manages the addition of
|
||||
/// children.
|
||||
/// </summary>
|
||||
/// <param name="sink">A GsfOutput to hold the ZIP file</param>
|
||||
/// <param name="err">Location to store error, or null; currently unused.</param>
|
||||
/// <returns>The new zip file handler</returns>
|
||||
/// <remarks>This adds a reference to <paramref name="sink"/>.</remarks>
|
||||
public static GsfOutfileZip Create(GsfOutput sink, ref Exception err)
|
||||
{
|
||||
if (sink == null)
|
||||
return null;
|
||||
|
||||
err = null;
|
||||
GsfOutfileZip zip = new GsfOutfileZip { Sink = sink };
|
||||
if (zip.EntryName == null)
|
||||
{
|
||||
zip.VDir = GsfZipVDir.Create(string.Empty, true, null);
|
||||
zip.RootOrder = new List<GsfOutfileZip>();
|
||||
zip.Root = zip;
|
||||
|
||||
// The names are the same
|
||||
zip.Name = zip.Sink.Name;
|
||||
zip.Container = null;
|
||||
}
|
||||
|
||||
if (zip.ModTime == null)
|
||||
{
|
||||
DateTime? modtime = DateTime.UtcNow;
|
||||
zip.ModTime = modtime;
|
||||
}
|
||||
|
||||
return zip;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Destructor
|
||||
/// </summary>
|
||||
~GsfOutfileZip()
|
||||
{
|
||||
// If the closing failed, we might have stuff here.
|
||||
DisconnectChildren();
|
||||
|
||||
if (Sink != null)
|
||||
SetSink(null);
|
||||
|
||||
if (Stream != null)
|
||||
Stream.deflateEnd();
|
||||
|
||||
if (this == Root)
|
||||
VDir.Free(true); // Frees vdirs recursively
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Functions
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override bool SeekImpl(long offset, SeekOrigin whence) => false;
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override bool CloseImpl()
|
||||
{
|
||||
bool ret;
|
||||
|
||||
// The root dir
|
||||
if (this == Root)
|
||||
{
|
||||
ret = CloseRoot();
|
||||
}
|
||||
else if (VDir.IsDirectory)
|
||||
{
|
||||
// Directories: Do nothing. Should change this to actually
|
||||
// write dirs which don't have children.
|
||||
ret = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
ret = CloseStream();
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override bool WriteImpl(int num_bytes, byte[] data)
|
||||
{
|
||||
if (VDir == null)
|
||||
return false;
|
||||
if (VDir.IsDirectory)
|
||||
return false;
|
||||
if (data == null)
|
||||
return false;
|
||||
|
||||
int ret;
|
||||
|
||||
if (!Writing)
|
||||
{
|
||||
if (!InitWrite())
|
||||
return false;
|
||||
}
|
||||
|
||||
GsfZipDirectoryEntry dirent = VDir.DirectoryEntry;
|
||||
|
||||
if (dirent.Zip64 == false &&
|
||||
(num_bytes >= uint.MaxValue || CurrentOffset >= (long)(uint.MaxValue - num_bytes)))
|
||||
{
|
||||
// Uncompressed size field would overflow.
|
||||
return false;
|
||||
}
|
||||
|
||||
if (CompressionMethod == GsfZipCompressionMethod.GSF_ZIP_DEFLATED)
|
||||
{
|
||||
Stream.next_in = data;
|
||||
Stream.avail_in = num_bytes;
|
||||
|
||||
while (Stream.avail_in > 0)
|
||||
{
|
||||
if (Stream.avail_out == 0)
|
||||
{
|
||||
if (!OutputBlock())
|
||||
return false;
|
||||
}
|
||||
|
||||
ret = Stream.deflate(Z_NO_FLUSH);
|
||||
if (ret != Z_OK)
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!Sink.Write(num_bytes, data))
|
||||
return false;
|
||||
|
||||
dirent.CompressedSize += num_bytes;
|
||||
}
|
||||
|
||||
// TODO: Enable CRC32 calculation
|
||||
//dirent.CRC32 = crc32(dirent.crc32, data, num_bytes);
|
||||
dirent.UncompressedSize += num_bytes;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public void RootRegisterChild(GsfOutfileZip child)
|
||||
{
|
||||
child.Root = this;
|
||||
if (!child.VDir.IsDirectory)
|
||||
Root.RootOrder.Add(child);
|
||||
}
|
||||
|
||||
public void SetSink(GsfOutput sink)
|
||||
{
|
||||
Sink = sink;
|
||||
SinkIsSeekable = null;
|
||||
}
|
||||
|
||||
public override GsfOutput NewChild(string name, bool is_dir)
|
||||
{
|
||||
if (VDir == null)
|
||||
return null;
|
||||
if (!VDir.IsDirectory)
|
||||
return null;
|
||||
if (string.IsNullOrEmpty(name))
|
||||
return null;
|
||||
|
||||
GsfOutfileZip child = new GsfOutfileZip
|
||||
{
|
||||
Name = name,
|
||||
Zip64 = this.Zip64,
|
||||
VDir = GsfZipVDir.Create(name, is_dir, null),
|
||||
Container = this,
|
||||
};
|
||||
|
||||
if (child.EntryName == null)
|
||||
{
|
||||
child.VDir = GsfZipVDir.Create(string.Empty, true, null);
|
||||
child.RootOrder = new List<GsfOutfileZip>();
|
||||
child.Root = child;
|
||||
|
||||
// The names are the same
|
||||
child.Name = child.Sink.Name;
|
||||
child.Container = null;
|
||||
}
|
||||
|
||||
if (child.ModTime == null)
|
||||
{
|
||||
DateTime? modtime = DateTime.UtcNow;
|
||||
child.ModTime = modtime;
|
||||
}
|
||||
|
||||
VDir.AddChild(child.VDir);
|
||||
Root.RootRegisterChild(child);
|
||||
|
||||
return child;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Utilities
|
||||
|
||||
private void DisconnectChildren()
|
||||
{
|
||||
if (RootOrder == null)
|
||||
return;
|
||||
|
||||
for (int i = 0; i < RootOrder.Count; i++)
|
||||
{
|
||||
RootOrder[i] = null;
|
||||
}
|
||||
|
||||
RootOrder = null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The "mimetype" member is special for ODF. It cannot have any
|
||||
/// extra field (and thus cannot be a zip64 member). Hardcode
|
||||
/// this to help compatibility with libgsf users depending on
|
||||
/// past behaviour of zip creation.
|
||||
///
|
||||
/// The flip side is that such a file cannot be 4G+.
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
private static bool SpecialMimetypeDirectoryEntry(GsfZipDirectoryEntry dirent)
|
||||
{
|
||||
return (dirent.Offset == 0
|
||||
&& dirent.Zip64 != true
|
||||
&& dirent.CompressionMethod == GsfZipCompressionMethod.GSF_ZIP_STORED
|
||||
&& dirent.Name == "mimetype");
|
||||
}
|
||||
|
||||
private bool DirectoryEntryWrite(GsfZipDirectoryEntry dirent)
|
||||
{
|
||||
int nlen = dirent.Name.Length;
|
||||
List<byte> extras = new List<byte>(ZIP_DIRENT_SIZE + nlen + 100);
|
||||
bool offset_in_zip64 = dirent.Offset >= uint.MaxValue;
|
||||
bool zip64_here = (dirent.Zip64 == true || offset_in_zip64);
|
||||
byte extract = (byte)(zip64_here ? 45 : 20); // Unsure if dirent.zip64 is enough
|
||||
|
||||
if (zip64_here)
|
||||
{
|
||||
List<byte> tmp = new List<byte>();
|
||||
|
||||
// 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.
|
||||
|
||||
tmp.AddRange(BitConverter.GetBytes((ushort)(ExtraFieldTags.ZIP_DIRENT_EXTRA_FIELD_ZIP64)));
|
||||
tmp.AddRange(BitConverter.GetBytes((ushort)((2 + (offset_in_zip64 ? 1 : 0) * 8))));
|
||||
tmp.AddRange(BitConverter.GetBytes((ulong)(dirent.UncompressedSize)));
|
||||
tmp.AddRange(BitConverter.GetBytes((ulong)(dirent.CompressedSize)));
|
||||
if (offset_in_zip64)
|
||||
tmp.AddRange(BitConverter.GetBytes((ulong)(dirent.Offset)));
|
||||
|
||||
extras.AddRange(tmp);
|
||||
}
|
||||
else if (dirent.Zip64 == null)
|
||||
{
|
||||
List<byte> tmp = new List<byte>();
|
||||
|
||||
// Match the local header.
|
||||
tmp.AddRange(BitConverter.GetBytes((ushort)(ExtraFieldTags.ZIP_DIRENT_EXTRA_FIELD_IGNORE)));
|
||||
tmp.AddRange(BitConverter.GetBytes((ushort)(2 * 8)));
|
||||
tmp.AddRange(BitConverter.GetBytes((ulong)(0)));
|
||||
tmp.AddRange(BitConverter.GetBytes((ulong)(0)));
|
||||
|
||||
extras.AddRange(tmp);
|
||||
}
|
||||
|
||||
if (ZIP_ADD_UNIXTIME_FIELD && dirent.ModifiedTime != null && !SpecialMimetypeDirectoryEntry(dirent))
|
||||
{
|
||||
// Clearly a year 2038 problem here.
|
||||
List<byte> tmp = new List<byte>();
|
||||
|
||||
tmp.AddRange(BitConverter.GetBytes((ushort)(ExtraFieldTags.ZIP_DIRENT_EXTRA_FIELD_UNIXTIME)));
|
||||
tmp.AddRange(BitConverter.GetBytes((ushort)(5)));
|
||||
tmp.Add(1);
|
||||
tmp.AddRange(BitConverter.GetBytes((uint)(new DateTimeOffset(dirent.ModifiedTime ?? DateTime.UtcNow).ToUnixTimeSeconds())));
|
||||
|
||||
extras.AddRange(tmp);
|
||||
}
|
||||
|
||||
List<byte> buf = new List<byte>(ZIP_DIRENT_SIZE);
|
||||
|
||||
buf.AddRange(BitConverter.GetBytes((uint)(ZIP_DIRENT_SIGNATURE)));
|
||||
buf.AddRange(BitConverter.GetBytes((ushort)(((int)OSCodes.ZIP_OS_UNIX << 8) + extract)));
|
||||
buf.AddRange(BitConverter.GetBytes((ushort)(((int)OSCodes.ZIP_OS_MSDOS << 8) + extract)));
|
||||
buf.AddRange(BitConverter.GetBytes((ushort)(dirent.Flags)));
|
||||
buf.AddRange(BitConverter.GetBytes((ushort)(dirent.CompressionMethod)));
|
||||
buf.AddRange(BitConverter.GetBytes((uint)(dirent.DosTime)));
|
||||
buf.AddRange(BitConverter.GetBytes((uint)(dirent.CRC32)));
|
||||
buf.AddRange(BitConverter.GetBytes((uint)(zip64_here ? uint.MaxValue : dirent.CompressedSize)));
|
||||
buf.AddRange(BitConverter.GetBytes((uint)(zip64_here ? uint.MaxValue : dirent.UncompressedSize)));
|
||||
buf.AddRange(BitConverter.GetBytes((ushort)(nlen)));
|
||||
buf.AddRange(BitConverter.GetBytes((ushort)(extras.Count)));
|
||||
buf.AddRange(BitConverter.GetBytes((ushort)(0)));
|
||||
buf.AddRange(BitConverter.GetBytes((ushort)(0)));
|
||||
buf.AddRange(BitConverter.GetBytes((ushort)(0)));
|
||||
|
||||
// Hardcode file mode 644
|
||||
buf.AddRange(BitConverter.GetBytes((uint)(0100644u << 16)));
|
||||
buf.AddRange(BitConverter.GetBytes((uint)(offset_in_zip64 ? uint.MaxValue : dirent.Offset)));
|
||||
|
||||
// Stuff everything into buf so we can do just one write.
|
||||
buf.AddRange(extras);
|
||||
buf.AddRange(Encoding.ASCII.GetBytes(dirent.Name));
|
||||
|
||||
return Sink.Write(buf.Count, buf.ToArray());
|
||||
}
|
||||
|
||||
private bool TrailerWrite(int entries, long dirpos, long dirsize)
|
||||
{
|
||||
List<byte> buf = new List<byte>(ZIP_TRAILER_SIZE);
|
||||
|
||||
buf.AddRange(BitConverter.GetBytes((uint)(ZIP_TRAILER_SIGNATURE)));
|
||||
buf.AddRange(BitConverter.GetBytes((ushort)(Math.Min(entries, ushort.MaxValue))));
|
||||
buf.AddRange(BitConverter.GetBytes((ushort)(Math.Min(dirsize, ushort.MaxValue))));
|
||||
buf.AddRange(BitConverter.GetBytes((ushort)(Math.Min(dirpos, ushort.MaxValue))));
|
||||
|
||||
return Sink.Write(buf.Count, buf.ToArray());
|
||||
}
|
||||
|
||||
private bool Trailer64Write(int entries, long dirpos, long dirsize)
|
||||
{
|
||||
List<byte> buf = new List<byte>(ZIP_TRAILER64_SIZE);
|
||||
byte extract = 45;
|
||||
|
||||
buf.AddRange(BitConverter.GetBytes((uint)(ZIP_TRAILER64_SIGNATURE)));
|
||||
buf.AddRange(BitConverter.GetBytes((ulong)(ZIP_TRAILER64_SIZE - 12)));
|
||||
buf.AddRange(BitConverter.GetBytes((ushort)(((int)OSCodes.ZIP_OS_UNIX << 8) + extract)));
|
||||
buf.AddRange(BitConverter.GetBytes((ushort)(((int)OSCodes.ZIP_OS_MSDOS << 8) + extract)));
|
||||
buf.AddRange(BitConverter.GetBytes((uint)(0)));
|
||||
buf.AddRange(BitConverter.GetBytes((uint)(0)));
|
||||
buf.AddRange(BitConverter.GetBytes((uint)(entries)));
|
||||
buf.AddRange(BitConverter.GetBytes((uint)(entries)));
|
||||
buf.AddRange(BitConverter.GetBytes((ulong)(dirsize)));
|
||||
buf.AddRange(BitConverter.GetBytes((ulong)(dirpos)));
|
||||
|
||||
return Sink.Write(buf.Count, buf.ToArray());
|
||||
}
|
||||
|
||||
private bool Zip64LocatorWrite(long trailerpos)
|
||||
{
|
||||
List<byte> buf = new List<byte>(ZIP_ZIP64_LOCATOR_SIZE);
|
||||
|
||||
buf.AddRange(BitConverter.GetBytes((uint)(ZIP_ZIP64_LOCATOR_SIGNATURE)));
|
||||
buf.AddRange(BitConverter.GetBytes((uint)(0)));
|
||||
buf.AddRange(BitConverter.GetBytes((ulong)(trailerpos)));
|
||||
buf.AddRange(BitConverter.GetBytes((uint)(1)));
|
||||
|
||||
return Sink.Write(buf.Count, buf.ToArray());
|
||||
}
|
||||
|
||||
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()
|
||||
{
|
||||
List<byte> hbuf = new List<byte>(ZIP_HEADER_SIZE);
|
||||
|
||||
GsfZipDirectoryEntry dirent = VDir.DirectoryEntry;
|
||||
string name = dirent.Name;
|
||||
int nlen = name.Length;
|
||||
|
||||
hbuf.AddRange(BitConverter.GetBytes((uint)(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.ToArray()))
|
||||
return false;
|
||||
|
||||
SinkIsSeekable = Sink.Seek(dirent.Offset, SeekOrigin.Begin);
|
||||
if (SinkIsSeekable == false)
|
||||
hbuf.Clear();
|
||||
}
|
||||
|
||||
// Now figure out if we need a DDESC record.
|
||||
if (SinkIsSeekable == true)
|
||||
dirent.Flags &= ~ZIP_DIRENT_FLAGS_HAS_DDESC;
|
||||
else
|
||||
dirent.Flags |= ZIP_DIRENT_FLAGS_HAS_DDESC;
|
||||
|
||||
bool has_ddesc = (dirent.Flags & ZIP_DIRENT_FLAGS_HAS_DDESC) != 0;
|
||||
uint crc32 = has_ddesc ? 0 : dirent.CRC32;
|
||||
long csize = has_ddesc ? 0 : dirent.CompressedSize;
|
||||
long usize = has_ddesc ? 0 : dirent.UncompressedSize;
|
||||
|
||||
// Determine if we need a real zip64 extra field. We do so, if
|
||||
// - forced
|
||||
// - in auto mode, if usize or csize has overflowed
|
||||
// - in auto mode, if we use a DDESC
|
||||
bool real_zip64 = (dirent.Zip64 == true
|
||||
|| (dirent.Zip64 == null
|
||||
&& (has_ddesc
|
||||
|| dirent.UncompressedSize >= uint.MaxValue
|
||||
|| dirent.CompressedSize >= uint.MaxValue)));
|
||||
|
||||
byte extract = 20;
|
||||
if (real_zip64)
|
||||
extract = 45;
|
||||
|
||||
List<byte> extras = new List<byte>(ZIP_HEADER_SIZE + nlen + 100);
|
||||
|
||||
// In the has_ddesc case, we write crc32/size/usize as zero and store
|
||||
// the right values in the DDESC record that follows the data.
|
||||
//
|
||||
// In the !has_ddesc case, we return to the same spot and write the
|
||||
// header a second time correcting crc32/size/usize, see
|
||||
// see HeaderPatchSizes. For this reason, we must ensure that
|
||||
// the record's length does not depend on the the sizes.
|
||||
//
|
||||
// In the the has_ddesc case we store zeroes here. No idea what we
|
||||
// were supposed to write.
|
||||
|
||||
// Auto or forced
|
||||
if (dirent.Zip64 != false)
|
||||
{
|
||||
List<byte> tmp = new List<byte>();
|
||||
ExtraFieldTags typ = real_zip64
|
||||
? ExtraFieldTags.ZIP_DIRENT_EXTRA_FIELD_ZIP64
|
||||
: ExtraFieldTags.ZIP_DIRENT_EXTRA_FIELD_IGNORE;
|
||||
|
||||
tmp.AddRange(BitConverter.GetBytes((ushort)(typ)));
|
||||
tmp.AddRange(BitConverter.GetBytes((ushort)(2 * 8)));
|
||||
tmp.AddRange(BitConverter.GetBytes((ulong)(usize)));
|
||||
tmp.AddRange(BitConverter.GetBytes((ulong)(csize)));
|
||||
|
||||
extras.AddRange(tmp);
|
||||
}
|
||||
|
||||
if (ZIP_ADD_UNIXTIME_FIELD && dirent.ModifiedTime != null && !SpecialMimetypeDirectoryEntry(dirent))
|
||||
{
|
||||
List<byte> tmp = new List<byte>();
|
||||
|
||||
// Clearly a year 2038 problem here.
|
||||
tmp.AddRange(BitConverter.GetBytes((ushort)(ExtraFieldTags.ZIP_DIRENT_EXTRA_FIELD_UNIXTIME)));
|
||||
tmp.AddRange(BitConverter.GetBytes((ushort)(5)));
|
||||
tmp.Add(1);
|
||||
tmp.AddRange(BitConverter.GetBytes((uint)(new DateTimeOffset(dirent.ModifiedTime ?? DateTime.UtcNow).ToUnixTimeSeconds())));
|
||||
|
||||
extras.AddRange(tmp);
|
||||
}
|
||||
|
||||
hbuf.AddRange(BitConverter.GetBytes((ushort)(((int)OSCodes.ZIP_OS_MSDOS << 8) + extract)));
|
||||
hbuf.AddRange(BitConverter.GetBytes((ushort)(dirent.Flags)));
|
||||
hbuf.AddRange(BitConverter.GetBytes((ushort)(dirent.CompressionMethod)));
|
||||
hbuf.AddRange(BitConverter.GetBytes((uint)(dirent.DosTime)));
|
||||
hbuf.AddRange(BitConverter.GetBytes((uint)(crc32)));
|
||||
hbuf.AddRange(BitConverter.GetBytes((uint)(real_zip64 && !has_ddesc ? uint.MaxValue : csize)));
|
||||
hbuf.AddRange(BitConverter.GetBytes((uint)(real_zip64 && !has_ddesc ? uint.MaxValue : usize)));
|
||||
hbuf.AddRange(BitConverter.GetBytes((ushort)(nlen)));
|
||||
hbuf.AddRange(BitConverter.GetBytes((ushort)(extras.Count)));
|
||||
|
||||
// Stuff everything into buf so we can do just one write.
|
||||
hbuf.AddRange(extras);
|
||||
hbuf.AddRange(Encoding.ASCII.GetBytes(name));
|
||||
|
||||
bool ret = Sink.Write(hbuf.Count, hbuf.ToArray());
|
||||
if (real_zip64)
|
||||
dirent.Zip64 = true;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
private bool InitWrite()
|
||||
{
|
||||
int ret;
|
||||
|
||||
if (Root.Writing)
|
||||
{
|
||||
Console.Error.WriteLine("Already writing to another stream in archive");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!Wrap(Sink))
|
||||
return false;
|
||||
|
||||
GsfZipDirectoryEntry dirent = NewDirectoryEntry();
|
||||
if (dirent == null)
|
||||
{
|
||||
Unwrap(Sink);
|
||||
return false;
|
||||
}
|
||||
|
||||
dirent.Offset = Sink.CurrentOffset;
|
||||
if (SpecialMimetypeDirectoryEntry(dirent))
|
||||
dirent.Zip64 = false;
|
||||
|
||||
VDir.DirectoryEntry = dirent;
|
||||
|
||||
HeaderWrite();
|
||||
Writing = true;
|
||||
Root.Writing = true;
|
||||
|
||||
// TODO: Enable CRC32 calculation
|
||||
// dirent.CRC32 = crc32(0L, Z_null, 0);
|
||||
if (CompressionMethod == GsfZipCompressionMethod.GSF_ZIP_DEFLATED)
|
||||
{
|
||||
if (Stream == null)
|
||||
Stream = new ZStream();
|
||||
|
||||
ret = Stream.inflateInit(-15);
|
||||
if (ret != Z_OK)
|
||||
return false;
|
||||
|
||||
ret = Stream.deflateParams(DeflateLevel, Z_DEFAULT_STRATEGY);
|
||||
if (ret != Z_OK)
|
||||
return false;
|
||||
|
||||
if (Buf == null)
|
||||
{
|
||||
BufSize = ZIP_BUF_SIZE;
|
||||
Buf = new byte[BufSize];
|
||||
}
|
||||
|
||||
Stream.next_out = Buf;
|
||||
Stream.avail_out = BufSize;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private bool OutputBlock()
|
||||
{
|
||||
int num_bytes = BufSize - Stream.avail_out;
|
||||
GsfZipDirectoryEntry dirent = VDir.DirectoryEntry;
|
||||
|
||||
if (!Sink.Write(num_bytes, Buf))
|
||||
return false;
|
||||
|
||||
dirent.CompressedSize += num_bytes;
|
||||
if (dirent.Zip64 == false && dirent.CompressedSize >= uint.MaxValue)
|
||||
return false;
|
||||
|
||||
Stream.next_out = Buf;
|
||||
Stream.avail_out = BufSize;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private bool Flush()
|
||||
{
|
||||
int zret;
|
||||
do
|
||||
{
|
||||
zret = Stream.deflate(Z_FINISH);
|
||||
if (zret == Z_OK || (zret == Z_BUF_ERROR && Stream.avail_out == 0))
|
||||
{
|
||||
// In this case Z_OK or Z_BUF_ERROR means more buffer space is needed
|
||||
if (!OutputBlock())
|
||||
return false;
|
||||
}
|
||||
} while (zret == Z_OK || zret == Z_BUF_ERROR);
|
||||
|
||||
if (zret != Z_STREAM_END)
|
||||
return false;
|
||||
|
||||
if (!OutputBlock())
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Write the per stream data descriptor
|
||||
/// </summary>
|
||||
private bool DataDescriptorWrite()
|
||||
{
|
||||
List<byte> buf = new List<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)
|
||||
{
|
||||
buf.AddRange(BitConverter.GetBytes((uint)(ZIP_DDESC64_SIGNATURE)));
|
||||
buf.AddRange(BitConverter.GetBytes((uint)(dirent.CRC32)));
|
||||
buf.AddRange(BitConverter.GetBytes((ulong)(dirent.CompressedSize)));
|
||||
buf.AddRange(BitConverter.GetBytes((ulong)(dirent.UncompressedSize)));
|
||||
size = ZIP_DDESC64_SIZE;
|
||||
}
|
||||
else
|
||||
{
|
||||
buf.AddRange(BitConverter.GetBytes((uint)(ZIP_DDESC_SIGNATURE)));
|
||||
buf.AddRange(BitConverter.GetBytes((uint)(dirent.CRC32)));
|
||||
buf.AddRange(BitConverter.GetBytes((uint)(dirent.CompressedSize)));
|
||||
buf.AddRange(BitConverter.GetBytes((uint)(dirent.UncompressedSize)));
|
||||
size = ZIP_DDESC_SIZE;
|
||||
}
|
||||
|
||||
if (!Sink.Write(size, buf.ToArray()))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private bool HeaderPatchSizes()
|
||||
{
|
||||
GsfZipDirectoryEntry dirent = VDir.DirectoryEntry;
|
||||
long pos = Sink.CurrentOffset;
|
||||
|
||||
// Rewrite the header in the same location again.
|
||||
bool ok = (Sink.Seek(dirent.Offset, SeekOrigin.Begin)
|
||||
&& HeaderWrite()
|
||||
&& Sink.Seek(pos, SeekOrigin.Begin));
|
||||
|
||||
if (ok && dirent.Zip64 == null)
|
||||
{
|
||||
// We just wrote the final header. Since we still are in
|
||||
// auto-mode, the header did not use a real zip64 extra
|
||||
// field. Hence we don't need such a field.
|
||||
dirent.Zip64 = false;
|
||||
}
|
||||
|
||||
return ok;
|
||||
}
|
||||
|
||||
private bool CloseStream()
|
||||
{
|
||||
if (!Writing)
|
||||
{
|
||||
if (!InitWrite())
|
||||
return false;
|
||||
}
|
||||
|
||||
if (CompressionMethod == GsfZipCompressionMethod.GSF_ZIP_DEFLATED)
|
||||
{
|
||||
if (!Flush())
|
||||
return false;
|
||||
}
|
||||
|
||||
if ((VDir.DirectoryEntry.Flags & ZIP_DIRENT_FLAGS_HAS_DDESC) != 0)
|
||||
{
|
||||
// Write data descriptor
|
||||
if (!DataDescriptorWrite())
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Write crc, sizes
|
||||
if (!HeaderPatchSizes())
|
||||
return false;
|
||||
}
|
||||
|
||||
Root.Writing = false;
|
||||
|
||||
bool result = Unwrap(Sink);
|
||||
|
||||
// Free unneeded memory
|
||||
if (Stream != null)
|
||||
{
|
||||
Stream.deflateEnd();
|
||||
Stream = null;
|
||||
Buf = null;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
286
BurnOutSharp/External/libgsf/Output/GsfOutput.cs
vendored
Normal file
286
BurnOutSharp/External/libgsf/Output/GsfOutput.cs
vendored
Normal file
@@ -0,0 +1,286 @@
|
||||
/* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
|
||||
/*
|
||||
* gsf-output.c: interface for storing data
|
||||
*
|
||||
* Copyright (C) 2002-2006 Jody Goldberg (jody@gnome.org)
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of version 2.1 of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301
|
||||
* USA
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
|
||||
namespace LibGSF.Output
|
||||
{
|
||||
public abstract class GsfOutput : IDisposable
|
||||
{
|
||||
#region Properties
|
||||
|
||||
public long CurrentSize { get; protected set; } = 0;
|
||||
|
||||
public long CurrentOffset { get; protected set; } = 0;
|
||||
|
||||
public string Name { get; protected set; } = null;
|
||||
|
||||
public object WrappedBy { get; protected set; } = null;
|
||||
|
||||
public GsfOutfile Container { get; protected set; } = null;
|
||||
|
||||
public Exception Error { get; protected set; } = null;
|
||||
|
||||
public bool IsClosed { get; protected set; } = false;
|
||||
|
||||
public string PrintFBuf { get; protected set; } = null;
|
||||
|
||||
public int PrintFBufSize { get; protected set; } = 0;
|
||||
|
||||
public DateTime? ModTime { get; protected set; }
|
||||
|
||||
#endregion
|
||||
|
||||
#region Functions
|
||||
|
||||
/// <summary>
|
||||
/// Close a stream.
|
||||
/// </summary>
|
||||
/// <returns>false on error</returns>
|
||||
public bool Close()
|
||||
{
|
||||
if (IsClosed)
|
||||
return SetError(0, "<internal>");
|
||||
|
||||
// The implementation will log any errors, but we can never try to
|
||||
// close multiple times even on failure.
|
||||
bool res = CloseImpl();
|
||||
IsClosed = true;
|
||||
return res;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reposition in output stream @output. @whence specifies what the offset is
|
||||
/// relative to: the beginning of the stream (SeekOrigin.Begin), current position in
|
||||
/// the stream (SeekOrigin.Current) or the end of the stream (SeekOrigin.End).
|
||||
/// This function is similar to fseek(3)
|
||||
/// </summary>
|
||||
/// <param name="offset">Relative amount to reposition</param>
|
||||
/// <param name="whence">What the offset is relative to.</param>
|
||||
/// <returns>false on error.</returns>
|
||||
public bool Seek(long offset, SeekOrigin whence)
|
||||
{
|
||||
long pos = offset;
|
||||
|
||||
switch (whence)
|
||||
{
|
||||
case SeekOrigin.Begin: break;
|
||||
case SeekOrigin.Current: pos += CurrentOffset; break;
|
||||
case SeekOrigin.End: pos += CurrentSize; break;
|
||||
default:
|
||||
Console.Error.WriteLine($"Invalid seek type {whence}");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (pos < 0)
|
||||
{
|
||||
Console.Error.WriteLine($"Invalid seek position {pos}, which is before the start of the file");
|
||||
return false;
|
||||
}
|
||||
|
||||
// If we go nowhere, just return. This in particular handles null
|
||||
// seeks for streams with no seek method.
|
||||
if (pos == CurrentOffset)
|
||||
return true;
|
||||
|
||||
if (SeekImpl(offset, whence))
|
||||
{
|
||||
// NOTE : it is possible for the current pos to be beyond the
|
||||
// end of the file. The intervening space is not filled with 0
|
||||
// until something is written.
|
||||
CurrentOffset = pos;
|
||||
return true;
|
||||
}
|
||||
|
||||
// The implementation should have assigned whatever errors are necessary
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Write <paramref name="num_bytes"/> of <paramref name="data"/> to output.
|
||||
/// </summary>
|
||||
/// <param name="num_bytes">Number of bytes to write</param>
|
||||
/// <param name="data">Data to write.</param>
|
||||
/// <returns>%false on error.</returns>
|
||||
public bool Write(int num_bytes, byte[] data)
|
||||
{
|
||||
if (num_bytes == 0)
|
||||
return true;
|
||||
|
||||
if (WriteImpl(num_bytes, data))
|
||||
return IncrementCurrentOffset(num_bytes);
|
||||
|
||||
// The implementation should have assigned whatever errors are necessary
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <returns>true if the wrapping succeeded.</returns>
|
||||
public bool Wrap(object wrapper)
|
||||
{
|
||||
if (wrapper == null)
|
||||
return false;
|
||||
|
||||
if (WrappedBy != null)
|
||||
{
|
||||
Console.Error.WriteLine("Attempt to wrap an output that is already wrapped.");
|
||||
return false;
|
||||
}
|
||||
|
||||
WrappedBy = wrapper;
|
||||
return true;
|
||||
}
|
||||
|
||||
public bool Unwrap(object wrapper)
|
||||
{
|
||||
if (WrappedBy != wrapper)
|
||||
return false;
|
||||
|
||||
WrappedBy = null;
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Output <paramref name="va"/> to output using the format string <paramref name="format"/>, similar to printf(3)
|
||||
/// </summary>
|
||||
/// <param name="format">The printf-style format string</param>
|
||||
/// <param name="va">The arguments for @format</param>
|
||||
/// <returns>true if successful, false if not</returns>
|
||||
public bool PrintF(string format, params string[] va) => VPrintF(format, va) >= 0;
|
||||
|
||||
/// <summary>
|
||||
/// Output <paramref name="args"/> to output using the format string <paramref name="format"/>, similar to vprintf(3)
|
||||
/// </summary>
|
||||
/// <param name="format">The printf-style format string</param>
|
||||
/// <param name="args">The arguments for @format</param>
|
||||
/// <returns>Number of bytes printed, a negative value if not successful</returns>
|
||||
public long VPrintF(string format, params string[] args)
|
||||
{
|
||||
if (format == null)
|
||||
return -1;
|
||||
|
||||
long num_bytes = VPrintFImpl(format, args);
|
||||
if (num_bytes >= 0)
|
||||
{
|
||||
if (!IncrementCurrentOffset(num_bytes))
|
||||
return -1;
|
||||
}
|
||||
|
||||
return num_bytes;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Like fputs, this assumes that the line already ends with a newline
|
||||
/// </summary>
|
||||
/// <param name="line">Nul terminated string to write</param>
|
||||
/// <returns>%true if successful, %false if not</returns>
|
||||
public bool PutString(string line)
|
||||
{
|
||||
if (line == null)
|
||||
return false;
|
||||
|
||||
int nbytes = line.Length;
|
||||
return Write(nbytes, Encoding.UTF8.GetBytes($"{line}"));
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Virtual Functions
|
||||
|
||||
public virtual void Dispose()
|
||||
{
|
||||
if (!IsClosed)
|
||||
{
|
||||
Console.Error.WriteLine("Disposing of an unclosed stream");
|
||||
Close();
|
||||
}
|
||||
|
||||
Container = null;
|
||||
Name = null;
|
||||
ModTime = null;
|
||||
|
||||
PrintFBuf = null;
|
||||
|
||||
Error = null;
|
||||
}
|
||||
|
||||
protected virtual bool CloseImpl() => false;
|
||||
|
||||
protected virtual bool SeekImpl(long offset, SeekOrigin whence) => false;
|
||||
|
||||
protected virtual bool WriteImpl(int num_bytes, byte[] data) => false;
|
||||
|
||||
protected virtual long VPrintFImpl(string format, params string[] args) => -1;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Utilities
|
||||
|
||||
private bool IncrementCurrentOffset(long num_bytes)
|
||||
{
|
||||
CurrentOffset += num_bytes;
|
||||
if (CurrentOffset < num_bytes)
|
||||
return SetError(0, "Output size overflow.");
|
||||
|
||||
if (CurrentSize < CurrentOffset)
|
||||
CurrentSize = CurrentOffset;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <param name="filename">The (fs-sys encoded) filename</param>
|
||||
/// <returns> %true if the assignment was ok.</returns>
|
||||
/// <remarks>This is a utility routine that should only be used by derived outputs.</remarks>
|
||||
protected bool SetNameFromFilename(string filename)
|
||||
{
|
||||
string name = null;
|
||||
if (filename != null)
|
||||
{
|
||||
byte[] filenameBytes = Encoding.Unicode.GetBytes(filename);
|
||||
filenameBytes = Encoding.Convert(Encoding.Unicode, Encoding.UTF8, filenameBytes);
|
||||
name = Encoding.UTF8.GetString(filenameBytes);
|
||||
}
|
||||
|
||||
Name = name;
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <param name="code">The error id</param>
|
||||
/// <param name="format">printf style format string</param>
|
||||
/// <param name="va">arguments for @format</param>
|
||||
/// <returns>Always returns false to facilitate its use.</returns>
|
||||
/// <remarks>This is a utility routine that should only be used by derived outputs.</remarks>
|
||||
protected bool SetError(int code, string format, params string[] va)
|
||||
{
|
||||
Error = null;
|
||||
if (format != null)
|
||||
{
|
||||
string message = string.Format(format, va);
|
||||
Error = new Exception(message); // TODO: How to include `code`?
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
212
BurnOutSharp/External/libgsf/Output/GsfOutputBzip.cs
vendored
Normal file
212
BurnOutSharp/External/libgsf/Output/GsfOutputBzip.cs
vendored
Normal file
@@ -0,0 +1,212 @@
|
||||
/* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
|
||||
/*
|
||||
* gsf-output-bzip.c: wrapper to compress to bzipped output
|
||||
*
|
||||
* Copyright (C) 2003-2006 Dom Lachowicz (cinamod@hotmail.com)
|
||||
* 2002-2006 Jon K Hellan (hellan@acm.org)
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of version 2.1 of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301
|
||||
* USA
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.IO;
|
||||
|
||||
namespace LibGSF.Output
|
||||
{
|
||||
// TODO: Implement BZIP writing
|
||||
public class GsfOutputBzip : GsfOutput
|
||||
{
|
||||
#region Constants
|
||||
|
||||
private const int BZ_BUFSIZE = 1024;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Properties
|
||||
|
||||
#if BZIP2
|
||||
/// <summary>
|
||||
/// Compressed data
|
||||
/// </summary>
|
||||
public GsfOutput Sink { get; set; } = null;
|
||||
|
||||
public bz_stream Stream { get; set; } = new bz_stream();
|
||||
|
||||
public byte[] Buf { get; set; } = null;
|
||||
|
||||
public int BufSize { get; set; } = 0;
|
||||
#endif
|
||||
|
||||
#endregion
|
||||
|
||||
#region Constructor
|
||||
|
||||
/// <summary>
|
||||
/// Private constructor
|
||||
/// </summary>
|
||||
private GsfOutputBzip() { }
|
||||
|
||||
/// <param name="sink">The underlying data source.</param>
|
||||
/// <returns>A new file or null.</returns>
|
||||
/// <remarks>Adds a reference to <paramref name="sink"/>.</remarks>
|
||||
public static GsfOutputBzip Create(GsfOutput sink, ref Exception err)
|
||||
{
|
||||
#if BZIP2
|
||||
if (sink == null)
|
||||
return null;
|
||||
|
||||
GsfOutputBzip bzip = new GsfOutputBzip
|
||||
{
|
||||
Sink = sink,
|
||||
};
|
||||
|
||||
if (!InitBzip(ref err))
|
||||
return null;
|
||||
|
||||
return bzip;
|
||||
#else
|
||||
err = new Exception("BZ2 support not enabled");
|
||||
return null;
|
||||
#endif
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Functions
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override bool WriteImpl(int num_bytes, byte[] data)
|
||||
{
|
||||
#if BZIP2
|
||||
if (data == null)
|
||||
return false;
|
||||
|
||||
Stream.next_in = data;
|
||||
Stream.avail_in = num_bytes;
|
||||
|
||||
while (Stream.avail_in > 0)
|
||||
{
|
||||
if (Stream.avail_out == 0)
|
||||
{
|
||||
if (!OutputBlock())
|
||||
return false;
|
||||
}
|
||||
|
||||
int zret = BZ2_bzCompress(&bzip.stream, BZ_RUN);
|
||||
if (zret != BZ_RUN_OK)
|
||||
{
|
||||
Console.Error.WriteLine($"Unexpected error code {zret} from bzlib during compression.");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (Stream.avail_out == 0)
|
||||
{
|
||||
if (!OutputBlock())
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override bool SeekImpl(long offset, SeekOrigin whence) => false;
|
||||
|
||||
protected override bool CloseImpl()
|
||||
{
|
||||
#if BZIP2
|
||||
bool rt = Flush();
|
||||
BZ2_bzCompressEnd(Stream);
|
||||
|
||||
return rt;
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Utilities
|
||||
|
||||
#if BZIP2
|
||||
private bool InitBzip(ref Exception err)
|
||||
{
|
||||
int ret = BZ2_bzCompressInit(Stream, 6, 0, 0);
|
||||
|
||||
if (ret != BZ_OK)
|
||||
{
|
||||
err = new Exception("Unable to initialize BZ2 library");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (Buf == null)
|
||||
{
|
||||
BufSize = BZ_BUFSIZE;
|
||||
Buf = new byte[BufSize];
|
||||
}
|
||||
|
||||
Stream.next_out = Buf;
|
||||
Stream.avail_out = BufSize;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private bool OutputBlock()
|
||||
{
|
||||
int num_bytes = BufSize - Stream.avail_out;
|
||||
|
||||
if (!Sink.Write(num_bytes, Buf))
|
||||
return false;
|
||||
|
||||
Stream.next_out = Buf;
|
||||
Stream.avail_out = BufSize;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private bool Flush()
|
||||
{
|
||||
int zret;
|
||||
|
||||
do
|
||||
{
|
||||
zret = BZ2_bzCompress(Stream, BZ_FINISH);
|
||||
if (zret == BZ_FINISH_OK)
|
||||
{
|
||||
// In this case BZ_FINISH_OK means more buffer space needed
|
||||
if (!OutputBlock())
|
||||
return false;
|
||||
}
|
||||
} while (zret == BZ_FINISH_OK);
|
||||
|
||||
if (zret != BZ_STREAM_END)
|
||||
{
|
||||
Console.Error.WriteLine($"Unexpected error code {zret} from bzlib during compression.");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!OutputBlock())
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
181
BurnOutSharp/External/libgsf/Output/GsfOutputCsv.cs
vendored
Normal file
181
BurnOutSharp/External/libgsf/Output/GsfOutputCsv.cs
vendored
Normal file
@@ -0,0 +1,181 @@
|
||||
/* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
|
||||
/*
|
||||
* gsf-output-csv.c: a GsfOutput to write .csv style files.
|
||||
*
|
||||
* Copyright (C) 2005-2006 Morten Welinder (terra@gnome.org)
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of version 2.1 of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301
|
||||
* USA
|
||||
*/
|
||||
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
|
||||
namespace LibGSF.Output
|
||||
{
|
||||
#region Enums
|
||||
|
||||
/// <summary>
|
||||
/// Controls when to add quotes around fields.
|
||||
/// </summary>
|
||||
public enum GsfOutputCsvQuotingMode
|
||||
{
|
||||
/// <summary>
|
||||
/// Never add quotes around fields
|
||||
/// </summary>
|
||||
GSF_OUTPUT_CSV_QUOTING_MODE_NEVER,
|
||||
|
||||
/// <summary>
|
||||
/// Add quotes around fields when needed
|
||||
/// </summary>
|
||||
GSF_OUTPUT_CSV_QUOTING_MODE_AUTO,
|
||||
|
||||
/// <summary>
|
||||
/// Always add quotes around fields
|
||||
/// </summary>
|
||||
GSF_OUTPUT_CSV_QUOTING_MODE_ALWAYS
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Classes
|
||||
|
||||
public class GsfOutputCsv : GsfOutput
|
||||
{
|
||||
#region Properties
|
||||
|
||||
public GsfOutput Sink { get; set; }
|
||||
|
||||
public string Quote { get; set; }
|
||||
|
||||
public GsfOutputCsvQuotingMode QuotingMode { get; set; }
|
||||
|
||||
public string QuotingTriggers { get; set; } = string.Empty;
|
||||
|
||||
public string EndOfLine { get; set; } = "\n";
|
||||
|
||||
public string Separator { get; set; }
|
||||
|
||||
public bool FieldsOnLine { get; set; }
|
||||
|
||||
public string Buf { get; set; } = null;
|
||||
|
||||
public bool? QuotingOnWhitespace { get; set; }
|
||||
|
||||
#endregion
|
||||
|
||||
#region Functions
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override bool WriteImpl(int num_bytes, byte[] data) => Sink.Write(num_bytes, data);
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override bool SeekImpl(long offset, SeekOrigin whence) => Sink.Seek(offset, whence);
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override bool CloseImpl() => true;
|
||||
|
||||
public bool WriteField(string field, int len)
|
||||
{
|
||||
if (field == null)
|
||||
return false;
|
||||
|
||||
bool quote;
|
||||
bool ok;
|
||||
|
||||
if (len == -1)
|
||||
len = field.Length;
|
||||
|
||||
int end = len;
|
||||
if (FieldsOnLine && Separator.Length != 0)
|
||||
Buf += Separator;
|
||||
|
||||
FieldsOnLine = true;
|
||||
|
||||
switch (QuotingMode)
|
||||
{
|
||||
default:
|
||||
case GsfOutputCsvQuotingMode.GSF_OUTPUT_CSV_QUOTING_MODE_NEVER:
|
||||
quote = false;
|
||||
break;
|
||||
|
||||
case GsfOutputCsvQuotingMode.GSF_OUTPUT_CSV_QUOTING_MODE_ALWAYS:
|
||||
quote = true;
|
||||
break;
|
||||
|
||||
case GsfOutputCsvQuotingMode.GSF_OUTPUT_CSV_QUOTING_MODE_AUTO:
|
||||
{
|
||||
int p = 0; // field[0]
|
||||
quote = false;
|
||||
while (p < end)
|
||||
{
|
||||
if (QuotingTriggers.Contains(field[p].ToString()))
|
||||
{
|
||||
quote = true;
|
||||
break;
|
||||
}
|
||||
|
||||
p++;
|
||||
}
|
||||
|
||||
if (!quote
|
||||
&& field[0] != '\0'
|
||||
&& (char.IsWhiteSpace(field[0]) || char.IsWhiteSpace(field[p - 1]))
|
||||
&& QuotingOnWhitespace != null)
|
||||
{
|
||||
quote = true;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (quote && Quote.Length > 0)
|
||||
{
|
||||
Buf += quote;
|
||||
int fieldPtr = 0; // field[0]
|
||||
while (fieldPtr < end)
|
||||
{
|
||||
char c = field[fieldPtr];
|
||||
if (this.Quote.Contains(c.ToString()))
|
||||
Buf += this.Quote;
|
||||
|
||||
Buf += c;
|
||||
fieldPtr++;
|
||||
}
|
||||
|
||||
Buf += quote;
|
||||
}
|
||||
else
|
||||
{
|
||||
Buf += field;
|
||||
}
|
||||
|
||||
ok = Sink.Write(Buf.Length, Encoding.UTF8.GetBytes(Buf));
|
||||
Buf = string.Empty;
|
||||
|
||||
return ok;
|
||||
}
|
||||
|
||||
public bool WriteEndOfLine()
|
||||
{
|
||||
FieldsOnLine = false;
|
||||
return Sink.Write(EndOfLine.Length, Encoding.UTF8.GetBytes(EndOfLine));
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
298
BurnOutSharp/External/libgsf/Output/GsfOutputGZip.cs
vendored
Normal file
298
BurnOutSharp/External/libgsf/Output/GsfOutputGZip.cs
vendored
Normal file
@@ -0,0 +1,298 @@
|
||||
/* 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;
|
||||
|
||||
namespace LibGSF.Output
|
||||
{
|
||||
public class GsfOutputGZip : GsfOutput
|
||||
{
|
||||
#region Constants
|
||||
|
||||
/// <summary>
|
||||
/// GZip flag byte - The original is stored
|
||||
/// </summary>
|
||||
private const byte GZIP_ORIGINAL_NAME = 0x08;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Properties
|
||||
|
||||
/// <summary>
|
||||
/// Compressed data
|
||||
/// </summary>
|
||||
public GsfOutput Sink { get; set; } = null;
|
||||
|
||||
/// <summary>
|
||||
/// No header and no trailer.
|
||||
/// </summary>
|
||||
public bool Raw { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// zlib compression level
|
||||
/// </summary>
|
||||
public int DeflateLevel { get; set; } = Z_DEFAULT_COMPRESSION;
|
||||
|
||||
public ZStream Stream { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// CRC32 of uncompressed data
|
||||
/// </summary>
|
||||
public uint CRC { get; set; } = 0;
|
||||
|
||||
public int ISize { get; set; } = 0;
|
||||
|
||||
public bool Setup { get; set; } = false;
|
||||
|
||||
public byte[] Buf { get; set; } = null;
|
||||
|
||||
public int BufSize { get; set; } = 0;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Constructor and Destructor
|
||||
|
||||
/// <summary>
|
||||
/// Private constructor
|
||||
/// </summary>
|
||||
private GsfOutputGZip() { }
|
||||
|
||||
/// <param name="sink">The underlying data source.</param>
|
||||
/// <param name="err">Optionally null.</param>
|
||||
/// <returns>A new file or null</returns>
|
||||
/// <remarks>Adds a reference to <paramref name="sink"/>.</remarks>
|
||||
public static GsfOutputGZip Create(GsfOutput sink, ref Exception err)
|
||||
{
|
||||
if (sink == null)
|
||||
return null;
|
||||
|
||||
GsfOutputGZip output = new GsfOutputGZip
|
||||
{
|
||||
Sink = sink,
|
||||
};
|
||||
|
||||
if (output.Error != null)
|
||||
{
|
||||
err = output.Error;
|
||||
return null;
|
||||
}
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Destructor
|
||||
/// </summary>
|
||||
~GsfOutputGZip()
|
||||
{
|
||||
// FIXME: check for error?
|
||||
Stream.deflateEnd();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Functions
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override bool WriteImpl(int num_bytes, byte[] data)
|
||||
{
|
||||
if (data == null)
|
||||
return false;
|
||||
|
||||
// Write header, if needed
|
||||
SetupImpl();
|
||||
|
||||
Stream.next_in = data;
|
||||
Stream.avail_in = num_bytes;
|
||||
|
||||
while (Stream.avail_in > 0)
|
||||
{
|
||||
if (Stream.avail_out == 0)
|
||||
{
|
||||
if (!OutputBlock())
|
||||
return false;
|
||||
}
|
||||
|
||||
int zret = Stream.deflate(Z_NO_FLUSH);
|
||||
if (zret != Z_OK)
|
||||
{
|
||||
Error = new Exception("Unexpected compression failure");
|
||||
Console.Error.WriteLine($"Unexpected error code {zret} from zlib during compression.");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Enable CRC32 calculation
|
||||
//CRC = crc32(gzip.crc, data, num_bytes);
|
||||
ISize += num_bytes;
|
||||
|
||||
if (Stream.avail_out == 0)
|
||||
{
|
||||
if (!OutputBlock())
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override bool SeekImpl(long offset, SeekOrigin whence) => false;
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override bool CloseImpl()
|
||||
{
|
||||
// Just in case nothing was ever written
|
||||
SetupImpl();
|
||||
|
||||
if (Error != null)
|
||||
{
|
||||
if (!Flush())
|
||||
return false;
|
||||
|
||||
if (!Raw)
|
||||
{
|
||||
List<byte> buf = new List<byte>();
|
||||
|
||||
buf.AddRange(BitConverter.GetBytes((uint)(CRC)));
|
||||
buf.AddRange(BitConverter.GetBytes((uint)(ISize)));
|
||||
if (!Sink.Write(8, buf.ToArray()))
|
||||
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()
|
||||
{
|
||||
List<byte> buf = new List<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;
|
||||
|
||||
buf.AddRange(new byte[] { 0x1f, 0x8b, 0x08 });
|
||||
|
||||
if (nlen > 0)
|
||||
buf.Add(GZIP_ORIGINAL_NAME);
|
||||
|
||||
buf.AddRange(BitConverter.GetBytes((uint)(mtime)));
|
||||
buf.Add(3); // UNIX
|
||||
bool ret = Sink.Write(buf.Count, buf.ToArray());
|
||||
if (ret && name != null && nlen > 0)
|
||||
ret = Sink.Write(nlen, Encoding.ASCII.GetBytes(name));
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
private void SetupImpl()
|
||||
{
|
||||
if (Setup)
|
||||
return;
|
||||
|
||||
if (!InitGZip())
|
||||
Error = new Exception("Failed to initialize zlib structure");
|
||||
else if (!Raw && !OutputHeader())
|
||||
Error = new Exception("Failed to write gzip header");
|
||||
|
||||
Setup = true;
|
||||
}
|
||||
|
||||
private bool OutputBlock()
|
||||
{
|
||||
int num_bytes = BufSize - Stream.avail_out;
|
||||
if (!Sink.Write(num_bytes, Buf))
|
||||
{
|
||||
Error = new Exception("Failed to write");
|
||||
return false;
|
||||
}
|
||||
|
||||
Stream.next_out = Buf;
|
||||
Stream.avail_out = BufSize;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private bool Flush()
|
||||
{
|
||||
int zret;
|
||||
|
||||
do
|
||||
{
|
||||
zret = Stream.deflate(Z_FINISH);
|
||||
if (zret == Z_OK)
|
||||
{
|
||||
// In this case Z_OK means more buffer space needed
|
||||
if (!OutputBlock())
|
||||
return false;
|
||||
}
|
||||
} while (zret == Z_OK);
|
||||
|
||||
if (zret != Z_STREAM_END)
|
||||
{
|
||||
Error = new Exception("Unexpected compression failure");
|
||||
Console.Error.WriteLine($"Unexpected error code {zret} from zlib during compression.");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!OutputBlock())
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
144
BurnOutSharp/External/libgsf/Output/GsfOutputGio.cs
vendored
Normal file
144
BurnOutSharp/External/libgsf/Output/GsfOutputGio.cs
vendored
Normal file
@@ -0,0 +1,144 @@
|
||||
/* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
|
||||
/*
|
||||
* gsf-output-gio.c:
|
||||
*
|
||||
* Copyright (C) 2007 Dom Lachowicz <cinamod@hotmail.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of version 2.1 of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301
|
||||
* USA
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.IO;
|
||||
|
||||
namespace LibGSF.Output
|
||||
{
|
||||
public class GsfOutputGio : GsfOutput
|
||||
{
|
||||
#region Properties
|
||||
|
||||
public Stream Stream { get; set; } = null;
|
||||
|
||||
public bool CanSeek { get; set; } = false;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Constructor and Destructor
|
||||
|
||||
/// <summary>
|
||||
/// Private constructor
|
||||
/// </summary>
|
||||
private GsfOutputGio() { }
|
||||
|
||||
/// <param name="file">An existing GFile</param>
|
||||
/// <returns>A new GsfOutputGio or null</returns>
|
||||
public static GsfOutputGio Create(string file)
|
||||
{
|
||||
Exception err = null;
|
||||
return Create(file, ref err);
|
||||
}
|
||||
|
||||
/// <param name="file">An existing GFile</param>
|
||||
/// <returns>A new GsfOutputGio or null</returns>
|
||||
public static GsfOutputGio Create(string file, ref Exception err)
|
||||
{
|
||||
if (file == null)
|
||||
return null;
|
||||
|
||||
try
|
||||
{
|
||||
Stream stream = File.OpenWrite(file);
|
||||
return new GsfOutputGio
|
||||
{
|
||||
Stream = stream,
|
||||
CanSeek = CanSeekSafe(stream),
|
||||
};
|
||||
}
|
||||
catch
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Destructor
|
||||
/// </summary>
|
||||
~GsfOutputGio() => Close();
|
||||
|
||||
#endregion
|
||||
|
||||
#region Functions
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override bool CloseImpl()
|
||||
{
|
||||
if (Stream != null)
|
||||
{
|
||||
Stream.Close();
|
||||
Stream = null;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override bool WriteImpl(int num_bytes, byte[] data)
|
||||
{
|
||||
if (Stream == null)
|
||||
return false;
|
||||
|
||||
if (num_bytes <= 0)
|
||||
return true;
|
||||
|
||||
try
|
||||
{
|
||||
Stream.Write(data, 0, num_bytes);
|
||||
return true;
|
||||
}
|
||||
catch
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override bool SeekImpl(long offset, SeekOrigin whence)
|
||||
{
|
||||
if (Stream == null)
|
||||
return false;
|
||||
if (CanSeek)
|
||||
return false;
|
||||
|
||||
try
|
||||
{
|
||||
Stream.Seek(offset, whence); ;
|
||||
return true;
|
||||
}
|
||||
catch
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Utilities
|
||||
|
||||
private static bool CanSeekSafe(Stream stream) => stream?.CanSeek ?? false;
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
93
BurnOutSharp/External/libgsf/Output/GsfOutputIOChannel.cs
vendored
Normal file
93
BurnOutSharp/External/libgsf/Output/GsfOutputIOChannel.cs
vendored
Normal file
@@ -0,0 +1,93 @@
|
||||
/* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
|
||||
/*
|
||||
* gsf-output-iochannel.c
|
||||
*
|
||||
* Copyright (C) 2002-2006 Dom Lachowicz (cinamod@hotmail.com)
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of version 2.1 of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301
|
||||
* USA
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.IO;
|
||||
|
||||
namespace LibGSF.Output
|
||||
{
|
||||
public class GsfOutputIOChannel : GsfOutput
|
||||
{
|
||||
// TODO: Enable once GIOChannel is converted
|
||||
|
||||
//#region Properties
|
||||
|
||||
//public GIOChannel Channel { get; set; } = null;
|
||||
|
||||
//#endregion
|
||||
|
||||
//#region Constructor and Destructor
|
||||
|
||||
///// <returns>A new file or null.</returns>
|
||||
//public static GsfOutputIOChannel Create(GIOChannel channel)
|
||||
//{
|
||||
// if (channel == null)
|
||||
// return null;
|
||||
|
||||
// return new GsfOutputIOChannel
|
||||
// {
|
||||
// Channel = channel,
|
||||
// };
|
||||
//}
|
||||
|
||||
//#endregion
|
||||
|
||||
//#region Functions
|
||||
|
||||
///// <inheritdoc/>
|
||||
//protected override bool CloseImpl()
|
||||
//{
|
||||
// g_io_channel_shutdown(Channel, true, null);
|
||||
// return true;
|
||||
//}
|
||||
|
||||
///// <inheritdoc/>
|
||||
//protected override bool SeekImpl(long offset, SeekOrigin whence)
|
||||
//{
|
||||
// if (!Channel.IsSeekable)
|
||||
// return false;
|
||||
|
||||
// GIOStatus status = g_io_channel_seek_position(Channel, offset, whence, null);
|
||||
// if (status == G_IO_STATUS_NORMAL)
|
||||
// return true;
|
||||
|
||||
// Error = new Exception($"{status}?");
|
||||
// return false;
|
||||
//}
|
||||
|
||||
///// <inheritdoc/>
|
||||
//protected override bool WriteImpl(int num_bytes, byte[] data)
|
||||
//{
|
||||
// GIOStatus status = G_IO_STATUS_NORMAL;
|
||||
// int bytes_written = 0, total_written = 0;
|
||||
|
||||
// while ((status == G_IO_STATUS_NORMAL) && (total_written < num_bytes))
|
||||
// {
|
||||
// status = g_io_channel_write_chars(Channel, data + total_written, num_bytes - total_written, ref bytes_written, null);
|
||||
// total_written += bytes_written;
|
||||
// }
|
||||
|
||||
// return (status == G_IO_STATUS_NORMAL && total_written == num_bytes);
|
||||
//}
|
||||
|
||||
//#endregion
|
||||
}
|
||||
}
|
||||
171
BurnOutSharp/External/libgsf/Output/GsfOutputIconv.cs
vendored
Normal file
171
BurnOutSharp/External/libgsf/Output/GsfOutputIconv.cs
vendored
Normal file
@@ -0,0 +1,171 @@
|
||||
/* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
|
||||
/*
|
||||
* gsf-output-iconv.c: wrapper to convert character sets.
|
||||
*
|
||||
* Copyright (C) 2005-2006 Morten Welinder (terra@gnome.org)
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of version 2.1 of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301
|
||||
* USA
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
|
||||
namespace LibGSF.Output
|
||||
{
|
||||
public class GsfOutputIconv : GsfOutput
|
||||
{
|
||||
#region Constants
|
||||
|
||||
public const int BUF_SIZE = 0x400;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Properties
|
||||
|
||||
public GsfOutput Sink { get; set; }
|
||||
|
||||
public Encoding InputCharset { get; set; }
|
||||
|
||||
public Encoding OutputCharset { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Either null or a UTF-8 string (representable in the target encoding)
|
||||
/// to convert and output in place of characters that cannot be represented
|
||||
/// in the target encoding. null means use \u1234 or \U12345678 format.
|
||||
/// </summary>
|
||||
public string Fallback { get; set; }
|
||||
|
||||
public byte[] Buf { get; set; } = new byte[BUF_SIZE];
|
||||
|
||||
public int BufLen { get; set; } = 0;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Constructor
|
||||
|
||||
/// <summary>
|
||||
/// Private constructor
|
||||
/// </summary>
|
||||
private GsfOutputIconv() { }
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="sink">The underlying data source.</param>
|
||||
/// <param name="dst">The target character set.</param>
|
||||
/// <param name="src">he source character set.</param>
|
||||
/// <returns>A new GsfOutput object or null.</returns>
|
||||
/// <remarks>Adds a reference to <paramref name="sink"/>.</remarks>
|
||||
public static GsfOutputIconv Create(GsfOutput sink, Encoding dst, Encoding src)
|
||||
{
|
||||
if (sink == null)
|
||||
return null;
|
||||
|
||||
if (dst == null)
|
||||
dst = Encoding.UTF8;
|
||||
if (src == null)
|
||||
src = Encoding.UTF8;
|
||||
|
||||
return new GsfOutputIconv
|
||||
{
|
||||
Sink = sink,
|
||||
InputCharset = src,
|
||||
OutputCharset = dst,
|
||||
};
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Functions
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override bool WriteImpl(int num_bytes, byte[] data)
|
||||
{
|
||||
if (data == null)
|
||||
return false;
|
||||
|
||||
int dataPtr = 0; // data[0]
|
||||
while (num_bytes > 0)
|
||||
{
|
||||
if (Error != null)
|
||||
return false;
|
||||
|
||||
if (BufLen == BUF_SIZE)
|
||||
{
|
||||
Flush(false);
|
||||
}
|
||||
else
|
||||
{
|
||||
int count = Math.Min(BUF_SIZE - BufLen, num_bytes);
|
||||
Array.Copy(data, dataPtr, Buf, BufLen, count);
|
||||
BufLen += count;
|
||||
num_bytes -= count;
|
||||
dataPtr += count;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override bool SeekImpl(long offset, SeekOrigin whence) => false;
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override bool CloseImpl()
|
||||
{
|
||||
if (Error != null)
|
||||
return true;
|
||||
|
||||
return Flush(true);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Utilities
|
||||
|
||||
private bool Flush(bool must_empty)
|
||||
{
|
||||
if (Error != null)
|
||||
return false;
|
||||
|
||||
if (BufLen <= 0)
|
||||
return true;
|
||||
|
||||
bool ok = true;
|
||||
|
||||
byte[] data = Encoding.Convert(InputCharset, OutputCharset, Buf, 0, BufLen);
|
||||
if (data == null || data.Length <= 0)
|
||||
{
|
||||
Error = new Exception("Failed to convert string");
|
||||
ok = false;
|
||||
}
|
||||
else if (!Sink.Write(data.Length, data))
|
||||
{
|
||||
Error = new Exception("Failed to write");
|
||||
ok = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
BufLen = 0;
|
||||
ok = true;
|
||||
}
|
||||
|
||||
return ok && (!must_empty || BufLen == 0);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
157
BurnOutSharp/External/libgsf/Output/GsfOutputMemory.cs
vendored
Normal file
157
BurnOutSharp/External/libgsf/Output/GsfOutputMemory.cs
vendored
Normal file
@@ -0,0 +1,157 @@
|
||||
/* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
|
||||
/*
|
||||
* gsf-output-memory.c:
|
||||
*
|
||||
* Copyright (C) 2002-2006 Dom Lachowicz (cinamod@hotmail.com)
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of version 2.1 of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301
|
||||
* USA
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
|
||||
namespace LibGSF.Output
|
||||
{
|
||||
public class GsfOutputMemory : GsfOutput
|
||||
{
|
||||
#region Constants
|
||||
|
||||
public const int MIN_BLOCK = 512;
|
||||
|
||||
public const int MAX_STEP = MIN_BLOCK * 128;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Properties
|
||||
|
||||
public byte[] Buffer { get; set; } = null;
|
||||
|
||||
public int Capacity { get; set; } = 0;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Constructor
|
||||
|
||||
/// <summary>
|
||||
/// Private constructor
|
||||
/// </summary>
|
||||
private GsfOutputMemory() { }
|
||||
|
||||
/// <returns>A new file.</returns>
|
||||
public static GsfOutputMemory Create() => new GsfOutputMemory();
|
||||
|
||||
#endregion
|
||||
|
||||
#region Functions
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override bool CloseImpl() => true;
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override bool SeekImpl(long offset, SeekOrigin whence) => true;
|
||||
|
||||
public bool Expand(long needed)
|
||||
{
|
||||
// If we need >= MAX_STEP, align to a next multiple of MAX_STEP.
|
||||
// Since MAX_STEP is probably a power of two, this computation
|
||||
// should reduce to "dec, shr, inc, shl", which is probably
|
||||
// quicker then branching.
|
||||
|
||||
long capacity = Math.Max(Capacity, MIN_BLOCK);
|
||||
|
||||
if (needed < MAX_STEP)
|
||||
{
|
||||
while (capacity < needed)
|
||||
capacity *= 2;
|
||||
}
|
||||
else
|
||||
{
|
||||
capacity = ((needed - 1) / MAX_STEP + 1) * MAX_STEP;
|
||||
}
|
||||
|
||||
// Check for overflow: g_renew() casts its parameters to int.
|
||||
int lcapacity = (int)capacity;
|
||||
if ((long)lcapacity != capacity || capacity < 0)
|
||||
{
|
||||
Console.Error.WriteLine("Overflow in Expand");
|
||||
return false;
|
||||
}
|
||||
|
||||
byte[] tempBuffer = new byte[lcapacity];
|
||||
Array.Copy(Buffer, tempBuffer, Buffer.Length);
|
||||
Buffer = tempBuffer;
|
||||
Capacity = lcapacity;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override bool WriteImpl(int num_bytes, byte[] data)
|
||||
{
|
||||
if (Buffer == null)
|
||||
{
|
||||
Buffer = new byte[MIN_BLOCK];
|
||||
Capacity = MIN_BLOCK;
|
||||
}
|
||||
|
||||
if (num_bytes + CurrentOffset > Capacity)
|
||||
{
|
||||
if (!Expand(CurrentOffset + num_bytes))
|
||||
return false;
|
||||
}
|
||||
|
||||
Array.Copy(data, 0, Buffer, CurrentOffset, num_bytes);
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override long VPrintFImpl(string format, params string[] args)
|
||||
{
|
||||
if (Buffer != null)
|
||||
{
|
||||
byte[] temp = Encoding.UTF8.GetBytes(string.Format(format, args));
|
||||
long len = Math.Min(temp.Length, Capacity - CurrentOffset);
|
||||
Array.Copy(temp, 0, Buffer, CurrentOffset, len);
|
||||
|
||||
// There was insufficient space
|
||||
if (temp.Length >= len)
|
||||
len = base.VPrintFImpl(format, args);
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
return base.VPrintFImpl(format, args);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <returns>
|
||||
/// The data that has been written to mem.
|
||||
/// The caller takes ownership and the buffer belonging to mem is set
|
||||
/// to NULL.
|
||||
/// </returns>
|
||||
public byte[] StealBytes()
|
||||
{
|
||||
byte[] bytes = Buffer;
|
||||
Buffer = null;
|
||||
Capacity = 0;
|
||||
return bytes;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
260
BurnOutSharp/External/libgsf/Output/GsfOutputStdio.cs
vendored
Normal file
260
BurnOutSharp/External/libgsf/Output/GsfOutputStdio.cs
vendored
Normal file
@@ -0,0 +1,260 @@
|
||||
/* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
|
||||
/*
|
||||
* gsf-output-stdio.c: stdio based output
|
||||
*
|
||||
* Copyright (C) 2002-2006 Jody Goldberg (jody@gnome.org)
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of version 2.1 of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301
|
||||
* USA
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
|
||||
namespace LibGSF.Output
|
||||
{
|
||||
public class GsfOutputStdio : GsfOutput
|
||||
{
|
||||
#region Constants
|
||||
|
||||
private static int W_OK = 2;
|
||||
|
||||
private static int GSF_MAX_LINK_LEVEL = 256;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Properties
|
||||
|
||||
public FileStream FileStream { get; set; } = null;
|
||||
|
||||
public string RealFilename { get; set; }
|
||||
|
||||
public string TempFilename { get; set; }
|
||||
|
||||
public bool CreateBackupCopy { get; set; } = false;
|
||||
|
||||
public bool KeepOpen { get; set; } = false;
|
||||
|
||||
public FileInfo Stat { get; set; }
|
||||
|
||||
#endregion
|
||||
|
||||
#region Constructor and Destructor
|
||||
|
||||
/// <summary>
|
||||
/// Private constructor
|
||||
/// </summary>
|
||||
private GsfOutputStdio() { }
|
||||
|
||||
public static GsfOutputStdio Create(string filename, ref Exception err)
|
||||
{
|
||||
try
|
||||
{
|
||||
FileStream fs = File.OpenWrite(filename);
|
||||
return new GsfOutputStdio
|
||||
{
|
||||
FileStream = fs,
|
||||
RealFilename = filename,
|
||||
};
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
err = ex;
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Functions
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override bool CloseImpl()
|
||||
{
|
||||
bool res;
|
||||
string backup_filename = null;
|
||||
|
||||
if (FileStream == null)
|
||||
return false;
|
||||
|
||||
if (Error != null)
|
||||
{
|
||||
res = true;
|
||||
if (!KeepOpen && !CloseFileHelper(false))
|
||||
res = false;
|
||||
|
||||
if (!UnlinkFileHelper())
|
||||
res = false;
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
if (KeepOpen)
|
||||
{
|
||||
FileStream.Flush();
|
||||
FileStream = null;
|
||||
return true;
|
||||
}
|
||||
|
||||
res = CloseFileHelper(true);
|
||||
|
||||
// short circuit our when dealing with raw FILE
|
||||
if (RealFilename == null)
|
||||
return res;
|
||||
|
||||
if (!res)
|
||||
{
|
||||
UnlinkFileHelper();
|
||||
return false;
|
||||
}
|
||||
|
||||
// Move the original file to a backup
|
||||
if (CreateBackupCopy)
|
||||
{
|
||||
backup_filename = $"{RealFilename}.bak";
|
||||
int result = RenameWrapper(RealFilename, backup_filename);
|
||||
if (result != 0)
|
||||
{
|
||||
Error = new Exception($"Could not backup the original as {backup_filename}.");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Move the temp file to the original file
|
||||
if (RenameWrapper(TempFilename, RealFilename) != 0)
|
||||
{
|
||||
Error = new Exception();
|
||||
return false;
|
||||
}
|
||||
|
||||
DateTime? modtime = ModTime;
|
||||
if (modtime != null)
|
||||
new FileInfo(RealFilename).LastWriteTime = modtime.Value;
|
||||
|
||||
// Restore permissions. There is not much error checking we
|
||||
// can do here, I'm afraid. The final data is saved anyways.
|
||||
// Note the order: mode, uid+gid, gid, uid, mode.
|
||||
new FileInfo(RealFilename).Attributes = Stat.Attributes;
|
||||
return res;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override bool SeekImpl(long offset, SeekOrigin whence)
|
||||
{
|
||||
if (FileStream == null)
|
||||
return SetError(0, "Missing file");
|
||||
|
||||
if (!FileStream.CanSeek)
|
||||
return SetError(0, "Stream can't seek");
|
||||
|
||||
try
|
||||
{
|
||||
FileStream.Seek(offset, whence);
|
||||
return true;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return SetError(0, $"Stream can't seek to {offset} from {whence}: {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override bool WriteImpl(int num_bytes, byte[] data)
|
||||
{
|
||||
if (FileStream == null)
|
||||
return false;
|
||||
|
||||
try
|
||||
{
|
||||
FileStream.Write(data, 0, num_bytes);
|
||||
return true;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return SetError(0, $"Stream can't write {num_bytes}: {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
protected override long VPrintFImpl(string format, params string[] args)
|
||||
{
|
||||
if (FileStream == null)
|
||||
return -1;
|
||||
|
||||
string temp = string.Format(format, args);
|
||||
byte[] tempBytes = Encoding.UTF8.GetBytes(temp);
|
||||
FileStream.Write(tempBytes, 0, tempBytes.Length);
|
||||
return temp.Length;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Utilities
|
||||
|
||||
private static int RenameWrapper(string oldfilename, string newfilename)
|
||||
{
|
||||
try
|
||||
{
|
||||
System.IO.File.Move(oldfilename, newfilename);
|
||||
return 0;
|
||||
}
|
||||
catch
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
private static string FollowSymlinks(string filename, ref Exception error)
|
||||
{
|
||||
FileAttributes fa = System.IO.File.GetAttributes(filename);
|
||||
while (fa.HasFlag(FileAttributes.ReparsePoint))
|
||||
{
|
||||
// TODO: This should actually try to follow links
|
||||
break;
|
||||
}
|
||||
|
||||
return filename;
|
||||
}
|
||||
|
||||
private bool CloseFileHelper(bool seterr)
|
||||
{
|
||||
try
|
||||
{
|
||||
FileStream.Close();
|
||||
return true;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
|
||||
Error = new Exception($"Failed to close file: {ex.Message}");
|
||||
return false;
|
||||
}
|
||||
finally
|
||||
{
|
||||
FileStream = null;
|
||||
}
|
||||
}
|
||||
|
||||
private bool UnlinkFileHelper()
|
||||
{
|
||||
if (TempFilename == null)
|
||||
return true;
|
||||
|
||||
// TODO: This should actually try to unlink
|
||||
return true;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
10
BurnOutSharp/External/libgsf/README.md
vendored
Normal file
10
BurnOutSharp/External/libgsf/README.md
vendored
Normal file
@@ -0,0 +1,10 @@
|
||||
# LibMSPackSharp
|
||||
|
||||
<!-- [](https://ci.appveyor.com/project/mnadareski/libmspacksharp) -->
|
||||
|
||||
|
||||
C# port of the GNOME I/O [libgsf](https://github.com/GNOME/libgsf/tree/master/gsf) plus cleanup and commenting. This currently compiles as a library so it can be used in any C# application.
|
||||
|
||||
## Contributions
|
||||
|
||||
Contributions to the project are welcome. Please follow the current coding styles and please do not add any keys or legally dubious things to the code.
|
||||
Reference in New Issue
Block a user