Skip to content

Commit f78778d

Browse files
committed
Use System.IO.Compression DeflateStream for compressing chunks instead of spawning packzip processes.
1 parent 5818df6 commit f78778d

File tree

9 files changed

+115
-125
lines changed

9 files changed

+115
-125
lines changed
-196 KB
Binary file not shown.

VictorBush.Ego.NefsEdit/Properties/AssemblyInfo.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,5 +32,5 @@
3232
// You can specify all the values or you can default the Build and Revision Numbers
3333
// by using the '*' as shown below:
3434
// [assembly: AssemblyVersion("1.0.*")]
35-
[assembly: AssemblyVersion("0.1.1.0")]
36-
[assembly: AssemblyFileVersion("0.1.1.0")]
35+
[assembly: AssemblyVersion("0.1.2.0")]
36+
[assembly: AssemblyFileVersion("0.1.2.0")]

VictorBush.Ego.NefsEdit/VictorBush.Ego.NefsEdit.csproj

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -33,9 +33,6 @@
3333
<WarningLevel>4</WarningLevel>
3434
</PropertyGroup>
3535
<ItemGroup>
36-
<Reference Include="ICSharpCode.SharpZipLib">
37-
<HintPath>.\ICSharpCode.SharpZipLib.dll</HintPath>
38-
</Reference>
3936
<Reference Include="log4net, Version=2.0.8.0, Culture=neutral, PublicKeyToken=669e0ddf0bb1aa2a, processorArchitecture=MSIL">
4037
<HintPath>packages\log4net.2.0.8\lib\net45-full\log4net.dll</HintPath>
4138
<Private>True</Private>

VictorBush.Ego.NefsLib.Tests/Properties/AssemblyInfo.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,5 +32,5 @@
3232
// You can specify all the values or you can default the Build and Revision Numbers
3333
// by using the '*' as shown below:
3434
// [assembly: AssemblyVersion("1.0.*")]
35-
[assembly: AssemblyVersion("0.1.1.0")]
36-
[assembly: AssemblyFileVersion("0.1.1.0")]
35+
[assembly: AssemblyVersion("0.1.2.0")]
36+
[assembly: AssemblyFileVersion("0.1.2.0")]

VictorBush.Ego.NefsLib/NefsItem.cs

Lines changed: 48 additions & 116 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
using System;
33
using System.Collections.Generic;
44
using System.IO;
5+
using System.IO.Compression;
56
using System.Linq;
67
using System.Security.Cryptography;
78
using System.Text;
@@ -370,9 +371,8 @@ public void Extract(string outputFilePath, NefsProgressInfo p)
370371
public void Inject(string inputFilePath, NefsProgressInfo p)
371372
{
372373
float taskWeightPrep = 0.05f;
373-
float taskWeightSplit = 0.15f;
374-
float taskWeightCompress = 0.6f;
375-
float taskWeightJoin = 0.15f;
374+
float taskWeightCompress = 0.85f;
375+
float taskWeightUpdateMetadata = 0.05f;
376376
float taskWeightCleanup = 0.05f;
377377

378378
p.BeginTask(taskWeightPrep, "Preparing file injection...");
@@ -390,156 +390,94 @@ public void Inject(string inputFilePath, NefsProgressInfo p)
390390
/* Temp working directory */
391391
var workDir = Path.Combine(FilePathHelper.TempDirectory, _archive.FilePathHash, FilePathInArchiveHash);
392392

393-
/* Dir for uncompressed chunks */
394-
var u_chunks_dir = Path.Combine(workDir, "u_chunks");
395-
396-
/* Dir for compressed chunks */
397-
var c_chunks_dir = Path.Combine(workDir, "c_chunks");
398-
399393
/* Delete the working directory if exists and recreate it */
400394
if (Directory.Exists(workDir))
401395
{
402396
Directory.Delete(workDir, true);
403397
}
404398

405399
Directory.CreateDirectory(workDir);
406-
Directory.CreateDirectory(u_chunks_dir);
407-
Directory.CreateDirectory(c_chunks_dir);
408400

409401
p.EndTask();
410402

411403
/*
412-
* SPLIT INPUT FILE INTO CHUNKS
404+
* SPLIT INPUT FILE INTO CHUNKS AND COMPRESS THEM
413405
*/
414-
p.BeginTask(taskWeightSplit, "Splitting input file into chunks...");
406+
p.BeginTask(taskWeightCompress, "Compressing file...");
415407

408+
int compressedSizeDiff = 0;
409+
int currentChunk = 0;
410+
var destFilePath = Path.Combine(workDir, "inject.dat");
416411
int numChunks = 0;
417-
int fileNum = 0;
412+
int numChunksDiff = 0;
413+
int oldNumChunks = ChunkSizes.Count();
418414

419415
/* Open the input file */
420416
using (var inputFile = new FileStream(inputFilePath, FileMode.Open))
421417
{
418+
inputFile.Seek(0, SeekOrigin.Begin);
419+
422420
/* Determine how many chunks to split file into */
423421
numChunks = (int)Math.Ceiling(inputFile.Length / (double)CHUNK_SIZE);
424422
_extractedSize = (UInt32)inputFile.Length;
425423

426-
/* Read in each chunk and write to the uncompressed dir */
427-
var temp = new byte[CHUNK_SIZE];
424+
/* Clear out chunk sizes list so we can rebuild it */
425+
ChunkSizes.Clear();
426+
428427
int lastBytesRead = 0;
429428
int totalBytesRead = 0;
429+
int lastChunkSize = 0;
430+
int totalChunkSize = 0;
430431

431-
do
432+
using (var outputFile = new FileStream(destFilePath, FileMode.Create))
432433
{
433-
p.BeginTask(1.0f / (float)numChunks, String.Format("Creating chunk {0}/{1}...", fileNum + 1, numChunks));
434+
do
435+
{
436+
p.BeginTask(1.0f / (float)numChunks, String.Format("Compressing chunk {0}/{1}...", currentChunk + 1, numChunks));
434437

435-
/* Read the chunk from input file */
436-
inputFile.Seek(totalBytesRead, SeekOrigin.Begin);
437-
lastBytesRead = inputFile.Read(temp, 0, CHUNK_SIZE);
438+
/* Compress this chunk and write it to the output file */
439+
lastBytesRead = DeflateHelper.DeflateToFile(inputFile, CHUNK_SIZE, outputFile, out lastChunkSize);
438440

439-
/* Write chunk to its own output file. It is important
440-
* that chunks are named alphabetically so that later
441-
* they are read in order. */
442-
var uChunkFilePath = Path.Combine(u_chunks_dir, "u_" + fileNum.ToString("00000000") + ".dat");
441+
totalBytesRead += lastBytesRead;
442+
totalChunkSize += lastChunkSize;
443+
currentChunk++;
443444

444-
var outBytes = new byte[lastBytesRead];
445-
Array.Copy(temp, outBytes, lastBytesRead);
446-
File.WriteAllBytes(uChunkFilePath, outBytes);
445+
/* Record the total compressed size after this chunk */
446+
ChunkSizes.Add((uint)totalChunkSize);
447447

448-
fileNum++;
449-
totalBytesRead += lastBytesRead;
448+
p.EndTask();
450449

451-
p.EndTask();
450+
} while (lastBytesRead == CHUNK_SIZE);
452451

453-
} while (lastBytesRead == CHUNK_SIZE);
454-
}
452+
/* Get difference in number of chunks compared to item we replaced */
453+
numChunksDiff = numChunks - oldNumChunks;
455454

456-
/* Quick sanity check */
457-
if (fileNum != numChunks)
458-
{
459-
throw new Exception("Did not create the expected number of uncompressed chunks.");
460-
}
455+
/* Get the difference in compressed size */
456+
compressedSizeDiff = (int)outputFile.Length - (int)_compressedSize;
461457

462-
p.EndTask();
458+
/* Update new compressed size */
459+
_compressedSize = (uint)outputFile.Length;
463460

464-
/*
465-
* COMPRESS EACH CHUNK WITH PACKZIP
466-
*/
467-
p.BeginTask(taskWeightCompress, "Compressing chunks...");
461+
/* Quick sanity check */
462+
if (_compressedSize != totalChunkSize)
463+
{
464+
log.Error("Compressed file size different than what was expected.");
465+
}
466+
}
467+
}
468468

469-
for (int i = 0; i < fileNum; i++)
469+
/* Quick sanity check */
470+
if (currentChunk != numChunks)
470471
{
471-
p.BeginTask(1.0f / (float)fileNum, String.Format("Compressing chunk {0}/{1}...", i + 1, fileNum));
472-
473-
var inf = Path.Combine(u_chunks_dir, "u_" + i.ToString("00000000") + ".dat");
474-
var outf = Path.Combine(c_chunks_dir, "c_" + i.ToString("00000000") + ".dat");
475-
var args = String.Format("-o 0x0 -w -15 \"{0}\" \"{1}\"", inf, outf);
476-
477-
/* Spawn a packzip.exe process to do the compression */
478-
ProcessHelper.Run(FilePathHelper.PackzipPath, args);
479-
480-
p.EndTask();
472+
log.Error("Did not create the expected number of chunks.");
481473
}
482474

483475
p.EndTask();
484476

485477
/*
486-
* COMBINE COMPRESSED CHUNKS AND UPDATE CHUNK SIZE LIST
478+
* UPDATE METADATA FOR ITEMS AFTER THIS ONE
487479
*/
488-
p.BeginTask(taskWeightJoin, "Combining compressed chunks...");
489-
490-
int compressedSizeDiff = 0;
491-
int numChunksDiff = 0;
492-
int oldNumChunks = ChunkSizes.Count();
493-
var destFilePath = Path.Combine(workDir, "inject.dat");
494-
495-
using (var destFile = new FileStream(destFilePath, FileMode.Create))
496-
{
497-
var lastChunkSize = 0;
498-
ChunkSizes.Clear();
499-
500-
/* Get the list of compressed chunks - files should be in order alphabetically */
501-
string[] chunkFiles = Directory.GetFiles(c_chunks_dir);
502-
503-
/* Quick sanity check */
504-
if (chunkFiles.Length != numChunks)
505-
{
506-
throw new Exception("Did not find expected number of compressed chunks.");
507-
}
508-
509-
/* Read in each compressed chunk and add it to the output file */
510-
for (int i = 0; i < numChunks; i++)
511-
{
512-
p.BeginTask(1.0f / (float)numChunks, String.Format("Combining chunk {0}/{1}...", i + 1, numChunks));
513-
514-
/* Open the chunk file */
515-
using (var chunk = new FileStream(chunkFiles[i], FileMode.Open))
516-
{
517-
/* Get the chunk size */
518-
int chunkSize = (int)chunk.Length;
519-
520-
/* Read in the chunk */
521-
var chunkBytes = new byte[chunkSize];
522-
chunk.Seek(0, SeekOrigin.Begin);
523-
chunk.Read(chunkBytes, 0, chunkSize);
524-
525-
/* Write chunk to output injection file */
526-
destFile.Write(chunkBytes, 0, chunkSize);
527-
528-
/* Update our list of chunk sizes (remember they are cumulative) */
529-
lastChunkSize += chunkSize;
530-
ChunkSizes.Add((uint)lastChunkSize);
531-
}
532-
533-
p.EndTask();
534-
}
535-
536-
/* Get different in number of chunks compared to item we replaced */
537-
numChunksDiff = numChunks - oldNumChunks;
538-
539-
/* Get the difference in compressed size */
540-
compressedSizeDiff = (int)destFile.Length - (int)_compressedSize;
541-
_compressedSize = (uint)destFile.Length;
542-
}
480+
p.BeginTask(taskWeightUpdateMetadata, "Updating archive metadata...");
543481

544482
/* Update data offsets for each item AFTER this one */
545483
for (int i = (int)Id + 1; i < Archive.Items.Count; i++)
@@ -601,18 +539,12 @@ public void Inject(string inputFilePath, NefsProgressInfo p)
601539
*/
602540
p.BeginTask(taskWeightCleanup, "Cleaning up...");
603541

604-
Directory.Delete(u_chunks_dir, true);
605-
Directory.Delete(c_chunks_dir, true);
606-
607542
/* Whenever the archive is saved, this data will be injected */
608543
_pendingInjection = true;
609544
_fileToInject = destFilePath;
610545
_archive.Modified = true;
611546

612547
p.EndTask();
613-
614-
// TODO : HANDLE CASES WHEN # OF CHUNKS CHANGED!!!!!!!!!!!!!!!
615-
616548
}
617549

618550
/// <summary>

VictorBush.Ego.NefsLib/Properties/AssemblyInfo.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,8 +32,8 @@
3232
// You can specify all the values or you can default the Build and Revision Numbers
3333
// by using the '*' as shown below:
3434
// [assembly: AssemblyVersion("1.0.*")]
35-
[assembly: AssemblyVersion("0.1.1.0")]
36-
[assembly: AssemblyFileVersion("0.1.1.0")]
35+
[assembly: AssemblyVersion("0.1.2.0")]
36+
[assembly: AssemblyFileVersion("0.1.2.0")]
3737

3838
// Allow internals to be visible to the unit tests.
3939
[assembly: InternalsVisibleTo("VictorBush.Ego.NefsLib.Tests")]
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.IO;
4+
using System.IO.Compression;
5+
using System.Linq;
6+
using System.Text;
7+
using System.Threading.Tasks;
8+
9+
namespace VictorBush.Ego.NefsLib.Utility
10+
{
11+
class DeflateHelper
12+
{
13+
/// <summary>
14+
/// Takes data from an input file stream, compresses it, and writes it to the specified
15+
/// output file. File streams should already seek to the proper location before calling
16+
/// this function.
17+
/// </summary>
18+
/// <param name="infs">The input file stream to read from.</param>
19+
/// <param name="numBytes">Number of bytes to read in.</param>
20+
/// <param name="outfs">The output file stream to write compressed chunk to.</param>
21+
/// <param name="chunkSize">The compressed size of the chunk that was written.</param>
22+
/// <returns>Number of bytes actually read from input file.</returns>
23+
public static int DeflateToFile(FileStream infs, int numBytes, FileStream outfs, out int chunkSize)
24+
{
25+
/* Read in the input data to compress */
26+
var inData = new byte[numBytes];
27+
var bytesRead = infs.Read(inData, 0, numBytes);
28+
chunkSize = 0;
29+
30+
/* Deflate stream doesn't write properly directly to a FileStream when
31+
* doing this chunk business. So have to do this multi-stream setup. */
32+
33+
using (var inStream = new MemoryStream())
34+
using (var outStream = new MemoryStream())
35+
using (var deflateStream = new DeflateStream(outStream, CompressionMode.Compress))
36+
{
37+
/* Read input chunk into memory stream */
38+
inStream.Write(inData, 0, bytesRead);
39+
inStream.Seek(0, SeekOrigin.Begin);
40+
41+
/* Compress the chunk with deflate stream */
42+
inStream.CopyTo(deflateStream, bytesRead);
43+
44+
/* Close deflate stream to finalize compression - breaking
45+
* the "using()" convention, but this is needed */
46+
deflateStream.Close();
47+
48+
/* Write the compressed chunk to the output file */
49+
var compressedData = outStream.ToArray();
50+
chunkSize = (int)compressedData.Length;
51+
outfs.Write(compressedData, 0, compressedData.Length);
52+
}
53+
54+
return bytesRead;
55+
}
56+
}
57+
}

VictorBush.Ego.NefsLib/VictorBush.Ego.NefsLib.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,7 @@
6767
<Compile Include="NefsProgress.cs" />
6868
<Compile Include="NefsProgressInfo.cs" />
6969
<Compile Include="Properties\AssemblyInfo.cs" />
70+
<Compile Include="Utility\DeflateHelper.cs" />
7071
<Compile Include="Utility\FilePathHelper.cs" />
7172
<Compile Include="Utility\FormatHelper.cs" />
7273
<Compile Include="Utility\LogHelper.cs" />

readme.txt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,9 @@ NeFS Edit
22

33
*** Use at your own risk. Backup your files. ***
44

5+
Version 0.1.2
6+
- Using .NET compression library instead of spawning packzip processes.
7+
58
Version 0.1.1
69
- Fixed some issues related to replacing files in archive with larger files.
710

0 commit comments

Comments
 (0)