Skip to content

Commit

Permalink
ready for release
Browse files Browse the repository at this point in the history
  • Loading branch information
jcaillon committed Oct 17, 2018
1 parent 02fd99b commit 54a10a4
Show file tree
Hide file tree
Showing 10 changed files with 206 additions and 57 deletions.
17 changes: 13 additions & 4 deletions CabinetManager/CabEventType.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,17 +6,26 @@
public enum CabEventType : byte {

/// <summary>
/// Published when the archive process progresses.
/// Published when the archive process progresses, as the cabinet is written on the disc.
/// </summary>
/// <remarks>
/// This event is published for each chunk of data written on the disc when the cabinet is actually saved.
/// The <see cref="ICabProgressionEventArgs.RelativePathInCab"/> will indicate which file is currently processed.
/// </remarks>
GlobalProgression,

/// <summary>
/// Published when a file has been completed.
/// Published when a file has been processed, this can be used to determine which files are actually processed.
/// </summary>
FileCompleted,
/// <remarks>
/// This event does NOT mean the file is actually stored in the cabinet file on the disc.
/// This is only to inform that the file has been processed and will be saved in the cabinet file.
/// Use the <see cref="GlobalProgression"/> to follow the actual writing on disc.
/// </remarks>
FileProcessed,

/// <summary>
/// Published when a cabinet has been completed.
/// Published when a cabinet has been completed and is saved on disc.
/// </summary>
CabinetCompleted
}
Expand Down
1 change: 1 addition & 0 deletions CabinetManager/ICabProgressionEventArgs.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
/// Sent through the <see cref="ICabManager.OnProgress"/> event.
/// </summary>
public interface ICabProgressionEventArgs {

/// <summary>
/// The type of event.
/// </summary>
Expand Down
28 changes: 2 additions & 26 deletions CabinetManager/core/CfCabinet.cs
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,6 @@ public CfCabinet(string cabPath, CancellationToken? cancelToken) {
}

public void Dispose() {
NextCabinet?.Dispose();
_reader?.Dispose();
}

Expand Down Expand Up @@ -235,16 +234,11 @@ private uint HeaderLength {
/// </summary>
public bool Exists => File.Exists(CabPath);

/// <summary>
/// The next cabinet instance
/// </summary>
internal CfCabinet NextCabinet { get; private set; }

/// <summary>
/// Returns the complete list of files in this cabinet (and contiguous cabinet if they exist
/// </summary>
/// <returns></returns>
public IEnumerable<CfFile> GetFiles() => Folders.SelectMany(f => f.Files).Concat(NextCabinet?.GetFiles() ?? Enumerable.Empty<CfFile>());
public IEnumerable<CfFile> GetFiles() => Folders.SelectMany(f => f.Files);

/// <summary>
/// Add a new external file to this cabinet file.
Expand Down Expand Up @@ -352,11 +346,7 @@ public bool DeleteFile(string relativePathInCab) {
int nbFileDeleted = 0;
// remove existing files with the same name
foreach (var folder in Folders) {
if ((nbFileDeleted += folder.Files.RemoveAll(f => f.RelativePathInCab.Equals(relativePathInCab, StringComparison.OrdinalIgnoreCase))) > 0) {
// case of a file split into several cabinets
nbFileDeleted += NextCabinet?.Folders.FirstOrDefault()?.Files.RemoveAll(f => f.RelativePathInCab.Equals(relativePathInCab, StringComparison.OrdinalIgnoreCase)) ?? 0;
break;
}
nbFileDeleted += folder.Files.RemoveAll(f => f.RelativePathInCab.Equals(relativePathInCab, StringComparison.OrdinalIgnoreCase));
}
return nbFileDeleted > 0;
}
Expand Down Expand Up @@ -394,7 +384,6 @@ public void Save(CfFolderTypeCompress compressionType) {

private void OpenCab() {
Folders.Clear();
NextCabinet = null;
_dataHeadersRead = false;
if (Exists) {
_reader = new BinaryReader(File.OpenRead(CabPath));
Expand Down Expand Up @@ -639,19 +628,6 @@ private void ReadDataHeaders(BinaryReader reader) {
_dataHeadersRead = true;
}

/// <summary>
/// Returns the compressed data of the first data block of the cabinet.
/// </summary>
/// <remarks>This is used to read a data block that continues on a next cabinet file.</remarks>
/// <param name="uncompressedDataLength"></param>
/// <returns></returns>
internal byte[] GetFirstDataBlockCompressedData(out ushort uncompressedDataLength) {
if (Folders.Count == 0) {
throw new CfCabException($"Could not get the first data block compressed data because there are no folders in the cabinet {CabPath}.");
}
return Folders[0].GetFirstDataBlockCompressedData(_reader, out uncompressedDataLength);
}

/// <summary>
/// Returns a text representation of this cabinet file
/// </summary>
Expand Down
17 changes: 4 additions & 13 deletions CabinetManager/core/CfData.cs
Original file line number Diff line number Diff line change
Expand Up @@ -91,23 +91,14 @@ public CfData(CfFolder parent) {
public byte[] ReadUncompressedData(BinaryReader reader) {
reader.BaseStream.Position = CompressedDataOffset;

byte[] completeCompressedData;
ushort completeUncompressedDataLength;

if (UncompressedDataLength == 0) {
// the compressed data in this data block is only partial and should be continued in the next cabinet file
var nextCompressedData = _parent.GetNextCabinetFirstDataBlockCompressedData(out completeUncompressedDataLength);

completeCompressedData = new byte[CompressedDataLength + nextCompressedData.Length];
Array.Copy(nextCompressedData, 0, completeCompressedData, CompressedDataLength, nextCompressedData.Length);

var thisBlockCompressedData = ReadCompressedData(reader);
Array.Copy(thisBlockCompressedData, completeCompressedData, thisBlockCompressedData.Length);
} else {
completeUncompressedDataLength = UncompressedDataLength;
completeCompressedData = ReadCompressedData(reader);
throw new NotImplementedException("An uncompressed length of 0 indicates a partial block data which continues on the next cabinet file but this library does not implement multi-files cabinet yet.");
}

var completeUncompressedDataLength = UncompressedDataLength;
var completeCompressedData = ReadCompressedData(reader);

var uncompressedData = _parent.UncompressData(completeCompressedData);
if (completeUncompressedDataLength != 0 && completeUncompressedDataLength != uncompressedData.Length) {
throw new CfDataCorruptedException($"Corrupted data block, the expected uncompressed data length is {completeUncompressedDataLength} but the actual is {uncompressedData.Length}.");
Expand Down
11 changes: 0 additions & 11 deletions CabinetManager/core/CfFolder.cs
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading;
using CabinetManager.core.Exceptions;
Expand Down Expand Up @@ -364,16 +363,6 @@ public byte[] UncompressData(byte[] compressedData) {
return _dataDecompressor.DecompressData(compressedData);
}

/// <summary>
/// Returns the compressed data of the first data block of the next cabinet.
/// </summary>
/// <remarks>This is used to read a data block that continues on a next cabinet file.</remarks>
/// <param name="uncompressedDataLength"></param>
/// <returns></returns>
internal byte[] GetNextCabinetFirstDataBlockCompressedData(out ushort uncompressedDataLength) {
return _parent.NextCabinet.GetFirstDataBlockCompressedData(out uncompressedDataLength);
}

/// <summary>
/// Returns the compressed data of the first data block of the folder.
/// </summary>
Expand Down
2 changes: 1 addition & 1 deletion CabinetManager/internal/CabProgressionEventArgs.cs
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ internal class CabProgressionEventArgs : EventArgs, ICabProgressionEventArgs {
internal static CabProgressionEventArgs NewCompletedFile(string cabPath, string relativePathInCab) {
return new CabProgressionEventArgs {
CabPath = cabPath,
EventType = CabEventType.FileCompleted,
EventType = CabEventType.FileProcessed,
RelativePathInCab = relativePathInCab
};
}
Expand Down
2 changes: 1 addition & 1 deletion CabinetManagerTest/Tests/ArchiveTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,7 @@ protected void DeleteFilesInArchive(ICabManager cabManager, List<FileInCab> list
}

private void ArchiverOnOnProgress(object sender, ICabProgressionEventArgs e) {
if (e.EventType == CabEventType.FileCompleted) {
if (e.EventType == CabEventType.FileProcessed) {
_nbFileFinished++;
} else if (e.EventType == CabEventType.CabinetCompleted) {
_nbArchiveFinished++;
Expand Down
2 changes: 1 addition & 1 deletion CabinetManagerTest/Tests/FileInCab.cs
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
using CabinetManager;

namespace CabinetManagerTest.Tests {
public class FileInCab : IFileInCab, IFileInCabToDelete, IFileInCabToExtract, IFileToAddInCab {
public class FileInCab : IFileInCabToDelete, IFileInCabToExtract, IFileToAddInCab {
public string CabPath { get; set; }
public string RelativePathInCab { get; set; }
public ulong SizeInBytes { get; set; }
Expand Down
84 changes: 84 additions & 0 deletions CabinetManagerTest/UsageExample.cs
Original file line number Diff line number Diff line change
Expand Up @@ -20,13 +20,97 @@

#endregion

using System;
using System.Collections.Generic;
using System.Linq;
using CabinetManager;

namespace CabinetManagerTest {

public class UsageExample {

public static int Main_(string[] args) {

var cabManager = CabManager.New();

cabManager.SetCompressionLevel(CabCompressionLevel.None);
cabManager.SetCancellationToken(null);
cabManager.OnProgress += CabManagerOnProgress;

// Add files to a new or existing cabinet
var nbProcessed = cabManager.PackFileSet(new List<IFileToAddInCab> {
CabFile.NewToPack(@"archive.cab", @"folder\file.txt", @"my_source_file.txt")
});

Console.WriteLine($" -> {nbProcessed} files were added to a cabinet.");

// List all the files in a cabinet
var filesInCab = cabManager.ListFiles(@"archive.cab").ToList();

Console.WriteLine("Listing files:");
foreach (var fileInCab in filesInCab) {
Console.WriteLine($"{fileInCab.RelativePathInCab}: {fileInCab.LastWriteTime}, {fileInCab.SizeInBytes}");
}

// Extract files to external paths
nbProcessed = cabManager.ExtractFileSet(new List<IFileInCabToExtract> {
CabFile.NewToExtract(@"archive.cab", @"folder\file.txt", @"extraction_path.txt")
});

Console.WriteLine($" -> {nbProcessed} files were extracted from a cabinet.");

// Delete files in a cabinet
nbProcessed = cabManager.DeleteFileSet(filesInCab.Select(f => CabFile.NewToDelete(f.CabPath, f.RelativePathInCab)));

Console.WriteLine($" -> {nbProcessed} files were deleted from a cabinet.");

return 0;
}

private static void CabManagerOnProgress(object sender, ICabProgressionEventArgs e) {
switch (e.EventType) {
case CabEventType.GlobalProgression:
Console.WriteLine($"Global progression : {e.PercentageDone}%, current file is {e.RelativePathInCab}");
break;
case CabEventType.FileProcessed:
Console.WriteLine($"New file processed : {e.RelativePathInCab}");
break;
case CabEventType.CabinetCompleted:
Console.WriteLine($"New cabinet completed : {e.CabPath}");
break;
}
}

private class CabFile : IFileInCabToDelete, IFileInCabToExtract, IFileToAddInCab {

public string CabPath { get; private set; }
public string RelativePathInCab { get; private set; }
public string ExtractionPath { get; private set; }
public string SourcePath { get; private set; }

public static CabFile NewToPack(string cabPath, string relativePathInCab, string sourcePath) {
return new CabFile {
CabPath = cabPath,
RelativePathInCab = relativePathInCab,
SourcePath = sourcePath
};
}

public static CabFile NewToExtract(string cabPath, string relativePathInCab, string extractionPath) {
return new CabFile {
CabPath = cabPath,
RelativePathInCab = relativePathInCab,
ExtractionPath = extractionPath
};
}

public static CabFile NewToDelete(string cabPath, string relativePathInCab) {
return new CabFile {
CabPath = cabPath,
RelativePathInCab = relativePathInCab
};
}

}
}
}
99 changes: 99 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,105 @@ The limitations are listed below:
- Does not handle compressed data, you can only store/retrieve uncompressed data
- Does not compute nor verify checksums

### Usage example

```csharp
using System;
using System.Collections.Generic;
using System.Linq;
using CabinetManager;

namespace CabinetManagerTest {

public class Program {

public static int Main(string[] args) {

var cabManager = CabManager.New();

cabManager.SetCompressionLevel(CabCompressionLevel.None);
cabManager.SetCancellationToken(null);
cabManager.OnProgress += CabManagerOnProgress;

// Add files to a new or existing cabinet
var nbProcessed = cabManager.PackFileSet(new List<IFileToAddInCab> {
CabFile.NewToPack(@"archive.cab", @"folder\file.txt", @"my_source_file.txt")
});

Console.WriteLine($" -> {nbProcessed} files were added to a cabinet.");

// List all the files in a cabinet
var filesInCab = cabManager.ListFiles(@"archive.cab").ToList();

Console.WriteLine("Listing files:");
foreach (var fileInCab in filesInCab) {
Console.WriteLine($"{fileInCab.RelativePathInCab}: {fileInCab.LastWriteTime}, {fileInCab.SizeInBytes}");
}

// Extract files to external paths
nbProcessed = cabManager.ExtractFileSet(new List<IFileInCabToExtract> {
CabFile.NewToExtract(@"archive.cab", @"folder\file.txt", @"extraction_path.txt")
});

Console.WriteLine($" -> {nbProcessed} files were extracted from a cabinet.");

// Delete files in a cabinet
nbProcessed = cabManager.DeleteFileSet(filesInCab.Select(f => CabFile.NewToDelete(f.CabPath, f.RelativePathInCab)));

Console.WriteLine($" -> {nbProcessed} files were deleted from a cabinet.");

return 0;
}

private static void CabManagerOnProgress(object sender, ICabProgressionEventArgs e) {
switch (e.EventType) {
case CabEventType.GlobalProgression:
Console.WriteLine($"Global progression : {e.PercentageDone}%, current file is {e.RelativePathInCab}");
break;
case CabEventType.FileProcessed:
Console.WriteLine($"New file processed : {e.RelativePathInCab}");
break;
case CabEventType.CabinetCompleted:
Console.WriteLine($"New cabinet completed : {e.CabPath}");
break;
}
}

private class CabFile : IFileInCabToDelete, IFileInCabToExtract, IFileToAddInCab {

public string CabPath { get; private set; }
public string RelativePathInCab { get; private set; }
public string ExtractionPath { get; private set; }
public string SourcePath { get; private set; }

public static CabFile NewToPack(string cabPath, string relativePathInCab, string sourcePath) {
return new CabFile {
CabPath = cabPath,
RelativePathInCab = relativePathInCab,
SourcePath = sourcePath
};
}

public static CabFile NewToExtract(string cabPath, string relativePathInCab, string extractionPath) {
return new CabFile {
CabPath = cabPath,
RelativePathInCab = relativePathInCab,
ExtractionPath = extractionPath
};
}

public static CabFile NewToDelete(string cabPath, string relativePathInCab) {
return new CabFile {
CabPath = cabPath,
RelativePathInCab = relativePathInCab
};
}

}
}
}
```

## Thanks

This project was developped using an opensource license of the [rider by jetbrains](https://www.jetbrains.com/).
Expand Down

0 comments on commit 54a10a4

Please sign in to comment.