mirror of
https://github.com/chibicitiberiu/drumkit.git
synced 2024-02-24 10:53:32 +00:00
Latest build (version 2.2)
This commit is contained in:
58
Tarball/IOHelper.cs
Normal file
58
Tarball/IOHelper.cs
Normal file
@ -0,0 +1,58 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Windows.Storage;
|
||||
|
||||
namespace Tarball
|
||||
{
|
||||
static class IOHelper
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets a folder using relative path.
|
||||
/// </summary>
|
||||
public static async Task<StorageFolder> GetFolderRelativeAsync(StorageFolder root, string path)
|
||||
{
|
||||
// Split the path
|
||||
var splitpath = path.Split(new char[] { '\\', '/' }, StringSplitOptions.RemoveEmptyEntries);
|
||||
var currentdir = root;
|
||||
|
||||
// Browse to the last folder in the path
|
||||
for (int i = 0; i < splitpath.Length; i++)
|
||||
currentdir = await currentdir.GetFolderAsync(splitpath[i]);
|
||||
|
||||
// Return file
|
||||
return currentdir;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a folder using relative path.
|
||||
/// </summary>
|
||||
public static async Task<StorageFolder> CreateFolderRelativeAsync(StorageFolder root, string path)
|
||||
{
|
||||
// Split the path
|
||||
var splitpath = path.Split(new char[] { '\\', '/' }, StringSplitOptions.RemoveEmptyEntries);
|
||||
var currentdir = root;
|
||||
|
||||
// Browse to the last folder in the path
|
||||
for (int i = 0; i < splitpath.Length - 1; i++)
|
||||
currentdir = await currentdir.GetFolderAsync(splitpath[i]);
|
||||
|
||||
// Create folder
|
||||
return await currentdir.CreateFolderAsync(splitpath.Last(), CreationCollisionOption.ReplaceExisting);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a file using a relative path.
|
||||
/// </summary>
|
||||
public static async Task<StorageFile> CreateFileRelativeAsync(StorageFolder root, string path)
|
||||
{
|
||||
var currentdir = await GetFolderRelativeAsync(root, System.IO.Path.GetDirectoryName(path));
|
||||
|
||||
// Create file
|
||||
return await currentdir.CreateFileAsync(System.IO.Path.GetFileName(path), CreationCollisionOption.ReplaceExisting);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
29
Tarball/Properties/AssemblyInfo.cs
Normal file
29
Tarball/Properties/AssemblyInfo.cs
Normal file
@ -0,0 +1,29 @@
|
||||
using System.Reflection;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
// General Information about an assembly is controlled through the following
|
||||
// set of attributes. Change these attribute values to modify the information
|
||||
// associated with an assembly.
|
||||
[assembly: AssemblyTitle("Tarball")]
|
||||
[assembly: AssemblyDescription("")]
|
||||
[assembly: AssemblyConfiguration("")]
|
||||
[assembly: AssemblyCompany("")]
|
||||
[assembly: AssemblyProduct("Tarball")]
|
||||
[assembly: AssemblyCopyright("Copyright © 2013")]
|
||||
[assembly: AssemblyTrademark("")]
|
||||
[assembly: AssemblyCulture("")]
|
||||
|
||||
// Version information for an assembly consists of the following four values:
|
||||
//
|
||||
// Major Version
|
||||
// Minor Version
|
||||
// Build Number
|
||||
// Revision
|
||||
//
|
||||
// You can specify all the values or you can default the Build and Revision Numbers
|
||||
// by using the '*' as shown below:
|
||||
// [assembly: AssemblyVersion("1.0.*")]
|
||||
[assembly: AssemblyVersion("1.0.0.0")]
|
||||
[assembly: AssemblyFileVersion("1.0.0.0")]
|
||||
[assembly: ComVisible(false)]
|
122
Tarball/Tarball.csproj
Normal file
122
Tarball/Tarball.csproj
Normal file
@ -0,0 +1,122 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
|
||||
<PropertyGroup>
|
||||
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
||||
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
|
||||
<ProductVersion>8.0.30703</ProductVersion>
|
||||
<SchemaVersion>2.0</SchemaVersion>
|
||||
<ProjectGuid>{5CB567D7-572E-4BAE-802F-7E3F62CDDF64}</ProjectGuid>
|
||||
<OutputType>Library</OutputType>
|
||||
<AppDesignerFolder>Properties</AppDesignerFolder>
|
||||
<RootNamespace>Tarball</RootNamespace>
|
||||
<AssemblyName>Tarball</AssemblyName>
|
||||
<DefaultLanguage>en-US</DefaultLanguage>
|
||||
<FileAlignment>512</FileAlignment>
|
||||
<ProjectTypeGuids>{BC8A1FFA-BEE3-4634-8014-F334798102B3};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
|
||||
<DebugSymbols>true</DebugSymbols>
|
||||
<DebugType>full</DebugType>
|
||||
<Optimize>false</Optimize>
|
||||
<OutputPath>bin\Debug\</OutputPath>
|
||||
<DefineConstants>DEBUG;TRACE;NETFX_CORE</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
|
||||
<DebugType>pdbonly</DebugType>
|
||||
<Optimize>true</Optimize>
|
||||
<OutputPath>bin\Release\</OutputPath>
|
||||
<DefineConstants>TRACE;NETFX_CORE</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|ARM'">
|
||||
<DebugSymbols>true</DebugSymbols>
|
||||
<OutputPath>bin\ARM\Debug\</OutputPath>
|
||||
<DefineConstants>DEBUG;TRACE;NETFX_CORE</DefineConstants>
|
||||
<NoWarn>;2008</NoWarn>
|
||||
<DebugType>full</DebugType>
|
||||
<PlatformTarget>ARM</PlatformTarget>
|
||||
<UseVSHostingProcess>false</UseVSHostingProcess>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<Prefer32Bit>true</Prefer32Bit>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|ARM'">
|
||||
<OutputPath>bin\ARM\Release\</OutputPath>
|
||||
<DefineConstants>TRACE;NETFX_CORE</DefineConstants>
|
||||
<Optimize>true</Optimize>
|
||||
<NoWarn>;2008</NoWarn>
|
||||
<DebugType>pdbonly</DebugType>
|
||||
<PlatformTarget>ARM</PlatformTarget>
|
||||
<UseVSHostingProcess>false</UseVSHostingProcess>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<Prefer32Bit>true</Prefer32Bit>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x64'">
|
||||
<DebugSymbols>true</DebugSymbols>
|
||||
<OutputPath>bin\x64\Debug\</OutputPath>
|
||||
<DefineConstants>DEBUG;TRACE;NETFX_CORE</DefineConstants>
|
||||
<NoWarn>;2008</NoWarn>
|
||||
<DebugType>full</DebugType>
|
||||
<PlatformTarget>x64</PlatformTarget>
|
||||
<UseVSHostingProcess>false</UseVSHostingProcess>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<Prefer32Bit>true</Prefer32Bit>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x64'">
|
||||
<OutputPath>bin\x64\Release\</OutputPath>
|
||||
<DefineConstants>TRACE;NETFX_CORE</DefineConstants>
|
||||
<Optimize>true</Optimize>
|
||||
<NoWarn>;2008</NoWarn>
|
||||
<DebugType>pdbonly</DebugType>
|
||||
<PlatformTarget>x64</PlatformTarget>
|
||||
<UseVSHostingProcess>false</UseVSHostingProcess>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<Prefer32Bit>true</Prefer32Bit>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x86'">
|
||||
<DebugSymbols>true</DebugSymbols>
|
||||
<OutputPath>bin\x86\Debug\</OutputPath>
|
||||
<DefineConstants>DEBUG;TRACE;NETFX_CORE</DefineConstants>
|
||||
<NoWarn>;2008</NoWarn>
|
||||
<DebugType>full</DebugType>
|
||||
<PlatformTarget>x86</PlatformTarget>
|
||||
<UseVSHostingProcess>false</UseVSHostingProcess>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<Prefer32Bit>true</Prefer32Bit>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x86'">
|
||||
<OutputPath>bin\x86\Release\</OutputPath>
|
||||
<DefineConstants>TRACE;NETFX_CORE</DefineConstants>
|
||||
<Optimize>true</Optimize>
|
||||
<NoWarn>;2008</NoWarn>
|
||||
<DebugType>pdbonly</DebugType>
|
||||
<PlatformTarget>x86</PlatformTarget>
|
||||
<UseVSHostingProcess>false</UseVSHostingProcess>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<Prefer32Bit>true</Prefer32Bit>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<!-- A reference to the entire .Net Framework and Windows SDK are automatically included -->
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="IOHelper.cs" />
|
||||
<Compile Include="TarballHeader.cs" />
|
||||
<Compile Include="TarballReader.cs" />
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
<Compile Include="TarballWriter.cs" />
|
||||
</ItemGroup>
|
||||
<PropertyGroup Condition=" '$(VisualStudioVersion)' == '' or '$(VisualStudioVersion)' < '11.0' ">
|
||||
<VisualStudioVersion>11.0</VisualStudioVersion>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(MSBuildExtensionsPath)\Microsoft\WindowsXaml\v$(VisualStudioVersion)\Microsoft.Windows.UI.Xaml.CSharp.targets" />
|
||||
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
|
||||
Other similar extension points exist, see Microsoft.Common.targets.
|
||||
<Target Name="BeforeBuild">
|
||||
</Target>
|
||||
<Target Name="AfterBuild">
|
||||
</Target>
|
||||
-->
|
||||
</Project>
|
23
Tarball/TarballHeader.cs
Normal file
23
Tarball/TarballHeader.cs
Normal file
@ -0,0 +1,23 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Tarball
|
||||
{
|
||||
/// <summary>
|
||||
/// Tarball header structure
|
||||
/// </summary>
|
||||
struct TarballHeader
|
||||
{
|
||||
public string FileName;
|
||||
public uint FileMode;
|
||||
public uint OwnerId, GroupId;
|
||||
public long Size;
|
||||
public DateTime LastModified;
|
||||
public uint Checksum;
|
||||
public byte LinkIndicator;
|
||||
public string LinkedFile;
|
||||
}
|
||||
}
|
318
Tarball/TarballReader.cs
Normal file
318
Tarball/TarballReader.cs
Normal file
@ -0,0 +1,318 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using System.IO;
|
||||
using Windows.Storage;
|
||||
|
||||
namespace Tarball
|
||||
{
|
||||
public class TarballReader
|
||||
{
|
||||
#region Private attributes
|
||||
private Stream stream;
|
||||
private TarballHeader header;
|
||||
#endregion
|
||||
|
||||
#region Constructor
|
||||
/// <summary>
|
||||
/// Creates a new instance of a tarball archive reader.
|
||||
/// </summary>
|
||||
public TarballReader()
|
||||
{
|
||||
stream = null;
|
||||
header = new TarballHeader();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Public functions (unpack)
|
||||
/// <summary>
|
||||
/// Unpacks a tarball in a temporary folder.
|
||||
/// </summary>
|
||||
/// <param name="file">An URI to the tarball file.</param>
|
||||
/// <returns>Storage folder pointing to where the files were unpacked.</returns>
|
||||
public async Task<StorageFolder> Unpack(Uri file)
|
||||
{
|
||||
var stfile = await StorageFile.GetFileFromApplicationUriAsync(file);
|
||||
return await this.Unpack(stfile);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Unpacks a tarball in a specified folder.
|
||||
/// </summary>
|
||||
/// <param name="file">An URI to the tarball file.</param>
|
||||
/// <param name="destination">A folder where files will be unpacked.</param>
|
||||
/// <returns>Storage folder pointing to where the files were unpacked.</returns>
|
||||
public async Task<StorageFolder> Unpack(Uri file, StorageFolder destination)
|
||||
{
|
||||
var stfile = await StorageFile.GetFileFromApplicationUriAsync(file);
|
||||
return await this.Unpack(stfile, destination);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Unpacks a tarball in a temporary folder.
|
||||
/// </summary>
|
||||
/// <param name="file">A path to the tarball file.</param>
|
||||
/// <returns>Storage folder pointing to where the files were unpacked.</returns>
|
||||
public async Task<StorageFolder> Unpack(string file)
|
||||
{
|
||||
var stfile = await StorageFile.GetFileFromPathAsync(file);
|
||||
return await this.Unpack(stfile);
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Unpacks a tarball in a specified folder.
|
||||
/// </summary>
|
||||
/// <param name="file">A path to the tarball file.</param>
|
||||
/// <param name="destination">A folder where files will be unpacked.</param>
|
||||
/// <returns>Storage folder pointing to where the files were unpacked.</returns>
|
||||
public async Task<StorageFolder> Unpack(string file, StorageFolder destination)
|
||||
{
|
||||
var stfile = await StorageFile.GetFileFromPathAsync(file);
|
||||
return await this.Unpack(stfile, destination);
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Unpacks a tarball in a temporary folder.
|
||||
/// </summary>
|
||||
/// <param name="file">The tarball file.</param>
|
||||
/// <returns>Storage folder pointing to where the files were unpacked.</returns>
|
||||
public async Task<StorageFolder> Unpack(StorageFile file)
|
||||
{
|
||||
// Prepare temp folder
|
||||
var dest = await this.CreateTempFolder();
|
||||
|
||||
// Unpack
|
||||
await this.Initialize(file);
|
||||
await this.UnpackFiles(dest);
|
||||
this.Dispose();
|
||||
|
||||
// Results
|
||||
return dest;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Unpacks a tarball in a specified folder.
|
||||
/// </summary>
|
||||
/// <param name="file">The tarball file.</param>
|
||||
/// <param name="destination">A folder where files will be unpacked.</param>
|
||||
/// <returns>Storage folder pointing to where the files were unpacked.</returns>
|
||||
public async Task<StorageFolder> Unpack(StorageFile file, StorageFolder destination)
|
||||
{
|
||||
// Unpack
|
||||
await this.Initialize(file);
|
||||
await this.UnpackFiles(destination);
|
||||
this.Dispose();
|
||||
|
||||
// Results
|
||||
return destination;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Initialize, dispose
|
||||
/// <summary>
|
||||
/// Performs initialization actions before unpacking (such as opening the stream).
|
||||
/// </summary>
|
||||
private async Task Initialize(StorageFile file)
|
||||
{
|
||||
var str = await file.OpenReadAsync();
|
||||
this.stream = str.AsStream();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Performs cleanups after unpacking finished.
|
||||
/// </summary>
|
||||
private void Dispose()
|
||||
{
|
||||
// Clean up
|
||||
this.stream.Dispose();
|
||||
this.stream = null;
|
||||
|
||||
this.header = new TarballHeader();
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Headers
|
||||
/// <summary>
|
||||
/// Calculates the checksum from a header.
|
||||
/// </summary>
|
||||
/// <param name="buffer">The header bytes</param>
|
||||
private uint CalculateChecksum(byte[] buffer)
|
||||
{
|
||||
uint result = 0;
|
||||
|
||||
// Calculate sum of all bytes, with the exception of bytes 148-155
|
||||
// (checksum field). These are all assumed to be 0x20.
|
||||
for (int i = 0; i < buffer.Length; i++)
|
||||
if (i >= 148 && i < 156)
|
||||
result += 0x20;
|
||||
else result += Convert.ToUInt32(buffer[i]);
|
||||
|
||||
// Done
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts binary data to a TarballHeader.
|
||||
/// </summary>
|
||||
private TarballHeader ParseHeaderFields(byte[] buffer)
|
||||
{
|
||||
TarballHeader header = new TarballHeader();
|
||||
string temp;
|
||||
|
||||
// File name
|
||||
temp = UTF8Encoding.UTF8.GetString(buffer, 0, 100).Trim('\0', ' ');
|
||||
header.FileName = temp;
|
||||
|
||||
// File mode
|
||||
temp = UTF8Encoding.UTF8.GetString(buffer, 100, 8).Trim('\0', ' ');
|
||||
header.FileMode = (string.IsNullOrEmpty(temp)) ? 0 : Convert.ToUInt32(temp, 8);
|
||||
|
||||
// Owner id
|
||||
temp = UTF8Encoding.UTF8.GetString(buffer, 108, 8).Trim('\0', ' ');
|
||||
header.OwnerId = (string.IsNullOrEmpty(temp)) ? 0 : Convert.ToUInt32(temp, 8);
|
||||
|
||||
// Group id
|
||||
temp = UTF8Encoding.UTF8.GetString(buffer, 116, 8).Trim('\0', ' ');
|
||||
header.GroupId = (string.IsNullOrEmpty(temp)) ? 0 : Convert.ToUInt32(temp, 8);
|
||||
|
||||
// Size
|
||||
temp = UTF8Encoding.UTF8.GetString(buffer, 124, 12).Trim('\0', ' ');
|
||||
header.Size = (string.IsNullOrEmpty(temp)) ? 0 : Convert.ToInt64(temp, 8);
|
||||
|
||||
// Last modified date
|
||||
temp = UTF8Encoding.UTF8.GetString(buffer, 136, 12).Trim('\0', ' ');
|
||||
long seconds = (string.IsNullOrEmpty(temp)) ? 0 : Convert.ToInt64(temp, 8);
|
||||
header.LastModified = new DateTime(1970, 1, 1, 0, 0, 0, 0).AddSeconds(seconds).ToLocalTime();
|
||||
|
||||
// Checksum
|
||||
temp = UTF8Encoding.UTF8.GetString(buffer, 148, 8).Trim('\0', ' ');
|
||||
header.Checksum = (string.IsNullOrEmpty(temp)) ? 0 : Convert.ToUInt32(temp, 8);
|
||||
|
||||
// Link indicator
|
||||
header.LinkIndicator = buffer[156];
|
||||
|
||||
// Linked file
|
||||
temp = UTF8Encoding.UTF8.GetString(buffer, 157, 100).Trim('\0', ' ');
|
||||
header.LinkedFile = temp;
|
||||
|
||||
// Done
|
||||
return header;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reads a file header.
|
||||
/// </summary>
|
||||
/// <returns>True if another header was read, false otherwise.</returns>
|
||||
private async Task<bool> ReadNextFileHeader()
|
||||
{
|
||||
byte[] buffer = new byte[512];
|
||||
|
||||
// Check current position
|
||||
if (stream.Position >= stream.Length)
|
||||
return false;
|
||||
|
||||
// Read header
|
||||
await stream.ReadAsync(buffer, 0, 512);
|
||||
|
||||
// Parse header fields
|
||||
try
|
||||
{
|
||||
this.header = this.ParseHeaderFields(buffer);
|
||||
}
|
||||
|
||||
catch (Exception)
|
||||
{
|
||||
throw new IOException("Invalid tarball header!");
|
||||
}
|
||||
|
||||
// Verify checksum
|
||||
uint checksum = this.CalculateChecksum(buffer);
|
||||
|
||||
if (checksum == 256) // If 256 (only the checksum bytes different than 0), then
|
||||
return false; // we most likely hit an invalid entry, probably marking the
|
||||
// end of the file
|
||||
if (checksum != header.Checksum)
|
||||
throw new IOException("Invalid tarball checksum!");
|
||||
|
||||
// Done
|
||||
return true;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region File system helpers
|
||||
/// <summary>
|
||||
/// Creates a temporary folder.
|
||||
/// </summary>
|
||||
private async Task<StorageFolder> CreateTempFolder()
|
||||
{
|
||||
// Generate file name
|
||||
string name = "tar" + DateTime.Now.Ticks.ToString();
|
||||
|
||||
// Create file
|
||||
var temp = ApplicationData.Current.TemporaryFolder;
|
||||
return await temp.CreateFolderAsync(name, CreationCollisionOption.GenerateUniqueName);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Unpack
|
||||
/// <summary>
|
||||
/// Unpacks a file using the information from the header.
|
||||
/// The function assumes the header was previously read.
|
||||
/// </summary>
|
||||
/// <param name="destination">The destination file.</param>
|
||||
private async Task UnpackNextFile(StorageFile destination)
|
||||
{
|
||||
// Open destination file
|
||||
var str = await destination.OpenAsync(FileAccessMode.ReadWrite);
|
||||
var iostr = str.AsStream();
|
||||
|
||||
// Write data
|
||||
var buffer = new byte[512];
|
||||
int read = 0, total = 0;
|
||||
|
||||
while (total < this.header.Size)
|
||||
{
|
||||
read = await this.stream.ReadAsync(buffer, 0, 512);
|
||||
await iostr.WriteAsync(buffer, 0, Math.Min(read, Convert.ToInt32(this.header.Size) - total));
|
||||
total += read;
|
||||
}
|
||||
|
||||
// Cleanup
|
||||
await iostr.FlushAsync();
|
||||
iostr.Dispose();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Unpacks the files from the loaded tarball.
|
||||
/// </summary>
|
||||
/// <param name="destination">Destination folder.</param>
|
||||
private async Task UnpackFiles(StorageFolder destination)
|
||||
{
|
||||
if (this.stream == null)
|
||||
throw new ArgumentNullException("No file opened!");
|
||||
|
||||
while (await this.ReadNextFileHeader())
|
||||
{
|
||||
// Directory?
|
||||
if (this.header.FileName.EndsWith("/"))
|
||||
await IOHelper.CreateFolderRelativeAsync(destination, this.header.FileName);
|
||||
|
||||
// Create file
|
||||
else
|
||||
{
|
||||
var file = await IOHelper.CreateFileRelativeAsync(destination, this.header.FileName);
|
||||
await this.UnpackNextFile(file);
|
||||
}
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
}
|
345
Tarball/TarballWriter.cs
Normal file
345
Tarball/TarballWriter.cs
Normal file
@ -0,0 +1,345 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using System.IO;
|
||||
using Windows.Storage;
|
||||
|
||||
namespace Tarball
|
||||
{
|
||||
public class TarballWriter
|
||||
{
|
||||
#region Private members
|
||||
private List<IStorageItem> items;
|
||||
private System.IO.Stream stream;
|
||||
#endregion
|
||||
|
||||
#region Constructor
|
||||
/// <summary>
|
||||
/// Creates a new instance of tarball writer
|
||||
/// </summary>
|
||||
public TarballWriter()
|
||||
{
|
||||
this.items = new List<IStorageItem>();
|
||||
this.stream = null;
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Public methods
|
||||
/// <summary>
|
||||
/// Adds an item to the list to be packed.
|
||||
/// </summary>
|
||||
public void AddItem(IStorageItem item)
|
||||
{
|
||||
this.items.Add(item);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds a list of items to the list to be packed
|
||||
/// </summary>
|
||||
public void AddItems(IEnumerable<IStorageItem> items)
|
||||
{
|
||||
this.items.AddRange(items);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Packs the added items into the destination file.
|
||||
/// </summary>
|
||||
/// <param name="destination_file_path">The path of the tarball which will be created.</param>
|
||||
public async Task Pack(string destination_file_path)
|
||||
{
|
||||
// Initialize
|
||||
await this.InitializeDestFilename(destination_file_path);
|
||||
|
||||
// Write file
|
||||
await this.Write();
|
||||
|
||||
// Cleanup
|
||||
await this.Dispose();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Packs the added items into the destination file.
|
||||
/// </summary>
|
||||
/// <param name="destination">The destination StorageFile where the tarball will be written.</param>
|
||||
public async Task Pack(StorageFile destination)
|
||||
{
|
||||
// Initialize
|
||||
await this.InitializeDestStoragefile(destination);
|
||||
|
||||
// Write file
|
||||
await this.Write();
|
||||
|
||||
// Cleanup
|
||||
await this.Dispose();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Packs the added items into the destination stream.
|
||||
/// </summary>
|
||||
/// <param name="destination">The destination stream where the tarball will be written.</param>
|
||||
public async Task Pack(System.IO.Stream destination)
|
||||
{
|
||||
// Initialize
|
||||
this.stream = destination;
|
||||
|
||||
// Write file
|
||||
await this.Write();
|
||||
|
||||
// Cleanup
|
||||
await this.Dispose();
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Initialization
|
||||
/// <summary>
|
||||
/// Prepares for writing: creates and opens the destination file from path.
|
||||
/// </summary>
|
||||
private async Task InitializeDestFilename(string destfile)
|
||||
{
|
||||
// Get file and folder name from path
|
||||
string folder_path = Path.GetDirectoryName(destfile);
|
||||
string file_name = Path.GetFileName(destfile);
|
||||
|
||||
// Create file
|
||||
StorageFolder folder = await StorageFolder.GetFolderFromPathAsync(folder_path);
|
||||
StorageFile file = await folder.CreateFileAsync(file_name, CreationCollisionOption.GenerateUniqueName);
|
||||
|
||||
// Continue initialization
|
||||
await this.InitializeDestStoragefile(file);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Prepares for writing: opens the destination file.
|
||||
/// </summary>
|
||||
private async Task InitializeDestStoragefile(StorageFile file)
|
||||
{
|
||||
// Open destination file
|
||||
var stream = await file.OpenAsync(FileAccessMode.ReadWrite);
|
||||
var iostream = stream.AsStream();
|
||||
|
||||
// Set up stream
|
||||
this.stream = iostream;
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Cleanup
|
||||
/// <summary>
|
||||
/// Disposes resources, flushes and closes the stream.
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
private async Task Dispose()
|
||||
{
|
||||
// Close the file
|
||||
await this.stream.FlushAsync();
|
||||
this.stream.Dispose();
|
||||
|
||||
// Finish this
|
||||
this.stream = null;
|
||||
}
|
||||
|
||||
|
||||
#endregion
|
||||
|
||||
#region Write
|
||||
/// <summary>
|
||||
/// Writes all the added items, and the final null headers.
|
||||
/// </summary>
|
||||
private async Task Write()
|
||||
{
|
||||
// Write every item in the list
|
||||
foreach (var i in this.items)
|
||||
await WriteItemsRecursively(i, "");
|
||||
|
||||
// Write 2 empty entries
|
||||
await WriteItemsRecursively(null, "");
|
||||
await WriteItemsRecursively(null, "");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Writes a storage item, and if it is a folder writes those files recursively.
|
||||
/// </summary>
|
||||
/// <param name="root">The current (root) item</param>
|
||||
/// <param name="path">The path to the current (root) item</param>
|
||||
private async Task WriteItemsRecursively(IStorageItem root, string path)
|
||||
{
|
||||
// Write this item
|
||||
await this.WriteItem(root, path);
|
||||
|
||||
// Directory?
|
||||
if (root != null && root.IsOfType(StorageItemTypes.Folder))
|
||||
{
|
||||
// Read and write children
|
||||
StorageFolder folder = root as StorageFolder;
|
||||
var items = await folder.GetItemsAsync();
|
||||
|
||||
foreach (var i in items)
|
||||
await this.WriteItemsRecursively(i, path + folder.Name + "/");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Writes an item (header and bytes) to the file.
|
||||
/// </summary>
|
||||
/// <param name="item">The item to be written.</param>
|
||||
/// <param name="path">Path of the file.</param>
|
||||
private async Task WriteItem(IStorageItem item, string path)
|
||||
{
|
||||
// Initial stream
|
||||
Stream iostr = null;
|
||||
|
||||
// If it is a file, open the stream and get correct size.
|
||||
if (item != null && item.IsOfType(StorageItemTypes.File))
|
||||
{
|
||||
var file = item as StorageFile;
|
||||
var stream = await file.OpenReadAsync();
|
||||
iostr = stream.AsStream();
|
||||
|
||||
await WriteItemHeader(item, path, iostr.Length);
|
||||
}
|
||||
|
||||
// Not a file, just write the header with size 0.
|
||||
else await WriteItemHeader(item, path, 0);
|
||||
|
||||
// If possible, write bytes
|
||||
if (iostr != null)
|
||||
await WriteItemBytes(iostr);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Writes the header information for an item.
|
||||
/// </summary>
|
||||
/// <param name="item">The storage item.</param>
|
||||
/// <param name="path">The path to the storage item.</param>
|
||||
/// <param name="size">The size of the storage item (0 for folders).</param>
|
||||
private async Task WriteItemHeader(IStorageItem item, string path, long size)
|
||||
{
|
||||
// Create header
|
||||
byte[] header = new byte[512];
|
||||
|
||||
// Special null item => empty header
|
||||
if (item == null) {
|
||||
await this.stream.WriteAsync(header, 0, 512);
|
||||
return;
|
||||
}
|
||||
|
||||
// Is it a directory?
|
||||
bool isDir = item.IsOfType(StorageItemTypes.Folder);
|
||||
|
||||
// File name
|
||||
StringToBytes(this.HeaderCalculatePath(item.Name, path, isDir), header, 0, 100);
|
||||
|
||||
// File mode
|
||||
if (isDir) StringToBytes("40777", header, 100, 8);
|
||||
else StringToBytes("777", header, 100, 8);
|
||||
|
||||
// Owner and group ids
|
||||
StringToBytes("0", header, 108, 8);
|
||||
StringToBytes("0", header, 116, 8);
|
||||
|
||||
// Write size
|
||||
StringToBytes(Convert.ToString(size, 8), header, 124, 12);
|
||||
|
||||
// Write last modification date
|
||||
StringToBytes(this.HeaderDatetimeToUnix(item), header, 136, 12);
|
||||
|
||||
// Write temporary checksum
|
||||
for (int i = 148; i < 156; i++)
|
||||
header[i] = 0x20;
|
||||
|
||||
// Write link indicator
|
||||
StringToBytes("", header, 156, 101);
|
||||
|
||||
// Checksum
|
||||
this.HeaderChecksum(header);
|
||||
|
||||
// And (finally) WRITE
|
||||
await this.stream.WriteAsync(header, 0, 512);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Writes the data of the storage item. The file must be opened, and stream passed as parameter.
|
||||
/// </summary>
|
||||
private async Task WriteItemBytes(Stream stream)
|
||||
{
|
||||
// Create buffer
|
||||
byte[] buffer = new byte[512];
|
||||
|
||||
// Read & write bytes
|
||||
while (stream.Position < stream.Length)
|
||||
{
|
||||
int read = await stream.ReadAsync(buffer, 0, 512);
|
||||
|
||||
for (; read < 512; read++)
|
||||
buffer[read] = 0;
|
||||
|
||||
await this.stream.WriteAsync(buffer, 0, 512);
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Misc
|
||||
/// <summary>
|
||||
/// Converts a string to null terminated UTF8 byte array. Padded with spaces.
|
||||
/// </summary>
|
||||
private void StringToBytes(string source, byte[] array, int index, int size)
|
||||
{
|
||||
byte[] bytes = UTF8Encoding.UTF8.GetBytes(source);
|
||||
|
||||
for (int i = 0; i < size; i++)
|
||||
if (i < bytes.Length)
|
||||
array[index + i] = bytes[i];
|
||||
else array[index + i] = 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Obtains the path of the file from given information.
|
||||
/// </summary>
|
||||
/// <param name="name">The name of the item.</param>
|
||||
/// <param name="path">The path to the item.</param>
|
||||
/// <param name="isDir">True if the item is a directory.</param>
|
||||
/// <returns>A strign with combined path.</returns>
|
||||
private string HeaderCalculatePath(string name, string path, bool isDir)
|
||||
{
|
||||
string temp1 = path + name;
|
||||
string temp2 = temp1.Replace('\\', '/');
|
||||
|
||||
if (isDir && !temp2.EndsWith("/"))
|
||||
return temp2 + "/";
|
||||
|
||||
return temp2;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts the created date time to unix time, and returns the string representation in octal.
|
||||
/// </summary>
|
||||
/// <param name="item"></param>
|
||||
/// <returns></returns>
|
||||
private string HeaderDatetimeToUnix(IStorageItem item)
|
||||
{
|
||||
DateTime created = item.DateCreated.UtcDateTime;
|
||||
TimeSpan unix_span = created - new DateTime(1970, 1, 1, 0, 0, 0, 0);
|
||||
double seconds_d = Math.Truncate(unix_span.TotalSeconds);
|
||||
long seconds_l = Convert.ToInt64(seconds_d);
|
||||
|
||||
return Convert.ToString(seconds_l, 8);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Calculates the header checksum, and writes it in the byte array.
|
||||
/// </summary>
|
||||
private void HeaderChecksum(byte[] bytes)
|
||||
{
|
||||
// Calculate checksum
|
||||
long checksum = 0;
|
||||
foreach (var i in bytes)
|
||||
checksum += Convert.ToInt64(i);
|
||||
|
||||
// Write checksum
|
||||
StringToBytes(Convert.ToString(checksum, 8), bytes, 148, 8);
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
}
|
BIN
Tarball/bin/Debug/Tarball.dll
Normal file
BIN
Tarball/bin/Debug/Tarball.dll
Normal file
Binary file not shown.
BIN
Tarball/bin/Debug/Tarball.pdb
Normal file
BIN
Tarball/bin/Debug/Tarball.pdb
Normal file
Binary file not shown.
BIN
Tarball/bin/Debug/Tarball.pri
Normal file
BIN
Tarball/bin/Debug/Tarball.pri
Normal file
Binary file not shown.
BIN
Tarball/bin/Release/Tarball.dll
Normal file
BIN
Tarball/bin/Release/Tarball.dll
Normal file
Binary file not shown.
BIN
Tarball/bin/Release/Tarball.pdb
Normal file
BIN
Tarball/bin/Release/Tarball.pdb
Normal file
Binary file not shown.
BIN
Tarball/bin/Release/Tarball.pri
Normal file
BIN
Tarball/bin/Release/Tarball.pri
Normal file
Binary file not shown.
BIN
Tarball/obj/Debug/DesignTimeResolveAssemblyReferencesInput.cache
Normal file
BIN
Tarball/obj/Debug/DesignTimeResolveAssemblyReferencesInput.cache
Normal file
Binary file not shown.
0
Tarball/obj/Debug/LanguageQualifiers.txt
Normal file
0
Tarball/obj/Debug/LanguageQualifiers.txt
Normal file
10
Tarball/obj/Debug/Tarball.csproj.FileListAbsolute.txt
Normal file
10
Tarball/obj/Debug/Tarball.csproj.FileListAbsolute.txt
Normal file
@ -0,0 +1,10 @@
|
||||
F:\Dev\Windows8\DrumKit\Tarball\bin\Debug\Tarball.dll
|
||||
F:\Dev\Windows8\DrumKit\Tarball\bin\Debug\Tarball.pdb
|
||||
F:\Dev\Windows8\DrumKit\Tarball\bin\Debug\Tarball.pri
|
||||
F:\Dev\Windows8\DrumKit\Tarball\obj\Debug\Tarball.dll
|
||||
F:\Dev\Windows8\DrumKit\Tarball\obj\Debug\Tarball.pdb
|
||||
F:\Dev\Windows8\DrumKit\Tarball\obj\Debug\priconfig.xml
|
||||
F:\Dev\Windows8\DrumKit\Tarball\obj\Debug\layout.resfiles
|
||||
F:\Dev\Windows8\DrumKit\Tarball\obj\Debug\resources.resfiles
|
||||
F:\Dev\Windows8\DrumKit\Tarball\obj\Debug\pri.resfiles
|
||||
F:\Dev\Windows8\DrumKit\Tarball\obj\Debug\LanguageQualifiers.txt
|
BIN
Tarball/obj/Debug/Tarball.dll
Normal file
BIN
Tarball/obj/Debug/Tarball.dll
Normal file
Binary file not shown.
BIN
Tarball/obj/Debug/Tarball.pdb
Normal file
BIN
Tarball/obj/Debug/Tarball.pdb
Normal file
Binary file not shown.
0
Tarball/obj/Debug/layout.resfiles
Normal file
0
Tarball/obj/Debug/layout.resfiles
Normal file
0
Tarball/obj/Debug/pri.resfiles
Normal file
0
Tarball/obj/Debug/pri.resfiles
Normal file
24
Tarball/obj/Debug/priconfig.xml
Normal file
24
Tarball/obj/Debug/priconfig.xml
Normal file
@ -0,0 +1,24 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<index root="\" startIndexAt="obj\Debug\layout.resfiles">
|
||||
<default>
|
||||
<qualifier name="language" value="en-US" />
|
||||
</default>
|
||||
<indexer-config type="RESFILES" qualifierDelimiter="." />
|
||||
</index>
|
||||
<index root="\" startIndexAt="obj\Debug\resources.resfiles">
|
||||
<default>
|
||||
<qualifier name="language" value="en-US" />
|
||||
</default>
|
||||
<indexer-config type="RESW" convertDotsToSlashes="true" initialPath="Tarball" />
|
||||
<indexer-config type="RESJSON" initialPath="Tarball" />
|
||||
<indexer-config type="RESFILES" qualifierDelimiter="." />
|
||||
</index>
|
||||
<index root="\" startIndexAt="obj\Debug\pri.resfiles">
|
||||
<default>
|
||||
<qualifier name="language" value="en-US" />
|
||||
</default>
|
||||
<indexer-config type="PRI" />
|
||||
<indexer-config type="RESFILES" qualifierDelimiter="." />
|
||||
</index>
|
||||
</resources>
|
0
Tarball/obj/Debug/resources.resfiles
Normal file
0
Tarball/obj/Debug/resources.resfiles
Normal file
Binary file not shown.
0
Tarball/obj/Release/LanguageQualifiers.txt
Normal file
0
Tarball/obj/Release/LanguageQualifiers.txt
Normal file
10
Tarball/obj/Release/Tarball.csproj.FileListAbsolute.txt
Normal file
10
Tarball/obj/Release/Tarball.csproj.FileListAbsolute.txt
Normal file
@ -0,0 +1,10 @@
|
||||
F:\Dev\Windows8\DrumKit\Tarball\bin\Release\Tarball.dll
|
||||
F:\Dev\Windows8\DrumKit\Tarball\bin\Release\Tarball.pdb
|
||||
F:\Dev\Windows8\DrumKit\Tarball\bin\Release\Tarball.pri
|
||||
F:\Dev\Windows8\DrumKit\Tarball\obj\Release\Tarball.dll
|
||||
F:\Dev\Windows8\DrumKit\Tarball\obj\Release\Tarball.pdb
|
||||
F:\Dev\Windows8\DrumKit\Tarball\obj\Release\priconfig.xml
|
||||
F:\Dev\Windows8\DrumKit\Tarball\obj\Release\layout.resfiles
|
||||
F:\Dev\Windows8\DrumKit\Tarball\obj\Release\resources.resfiles
|
||||
F:\Dev\Windows8\DrumKit\Tarball\obj\Release\pri.resfiles
|
||||
F:\Dev\Windows8\DrumKit\Tarball\obj\Release\LanguageQualifiers.txt
|
BIN
Tarball/obj/Release/Tarball.dll
Normal file
BIN
Tarball/obj/Release/Tarball.dll
Normal file
Binary file not shown.
BIN
Tarball/obj/Release/Tarball.pdb
Normal file
BIN
Tarball/obj/Release/Tarball.pdb
Normal file
Binary file not shown.
0
Tarball/obj/Release/layout.resfiles
Normal file
0
Tarball/obj/Release/layout.resfiles
Normal file
0
Tarball/obj/Release/pri.resfiles
Normal file
0
Tarball/obj/Release/pri.resfiles
Normal file
24
Tarball/obj/Release/priconfig.xml
Normal file
24
Tarball/obj/Release/priconfig.xml
Normal file
@ -0,0 +1,24 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<index root="\" startIndexAt="obj\Release\layout.resfiles">
|
||||
<default>
|
||||
<qualifier name="language" value="en-US" />
|
||||
</default>
|
||||
<indexer-config type="RESFILES" qualifierDelimiter="." />
|
||||
</index>
|
||||
<index root="\" startIndexAt="obj\Release\resources.resfiles">
|
||||
<default>
|
||||
<qualifier name="language" value="en-US" />
|
||||
</default>
|
||||
<indexer-config type="RESW" convertDotsToSlashes="true" initialPath="Tarball" />
|
||||
<indexer-config type="RESJSON" initialPath="Tarball" />
|
||||
<indexer-config type="RESFILES" qualifierDelimiter="." />
|
||||
</index>
|
||||
<index root="\" startIndexAt="obj\Release\pri.resfiles">
|
||||
<default>
|
||||
<qualifier name="language" value="en-US" />
|
||||
</default>
|
||||
<indexer-config type="PRI" />
|
||||
<indexer-config type="RESFILES" qualifierDelimiter="." />
|
||||
</index>
|
||||
</resources>
|
0
Tarball/obj/Release/resources.resfiles
Normal file
0
Tarball/obj/Release/resources.resfiles
Normal file
Reference in New Issue
Block a user