-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathArchiveAppender.cs
169 lines (146 loc) · 6.27 KB
/
ArchiveAppender.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Security.Cryptography;
using System.Text;
using System.Threading.Tasks;
namespace BinArchiver
{
/// <summary>
/// Append binary files to an existing archive.
/// The arguments can be set during object existance so that we can use it in a command line argument loop.
/// </summary>
public class ArchiveAppender
{
private BinArchive binArchive = null;
private string archiveFilename = null;
private int appendCall = 0;
/// <summary>
/// Initiates a new instance of the ArchiveAppender type.
/// Does not take any arguments so that you can use the object to gather the right arguments. Not all are required.
/// </summary>
public ArchiveAppender()
{
}
/// <summary>
/// Opens the archive and loads it into memory (don't use to big files for now!)
/// </summary>
/// <returns>True when all went ok.</returns>
public bool Prepare(string archiveFilename)
{
// Ignores the call if it's called for a second time!
if (this.binArchive == null)
{
using (var fileStream = File.OpenRead(archiveFilename))
{
this.binArchive = ProtoBuf.Serializer.Deserialize<BinArchive>(fileStream);
this.archiveFilename = archiveFilename;
}
}
return (this.binArchive != null);
}
/// <summary>
/// Use this to set the file that you want to append, it must exist.
/// </summary>
public string Filename { get; set; }
/// <summary>
/// Use this to set the version that you want to add to the appended file.
/// </summary>
public string Version { get; set; }
/// <summary>
/// Use this property to add/remove the typeValues that are going to be saved with the next file that you Append().
/// </summary>
public List<UInt32> TypeValues { get; set; }
/// <summary>
/// Use this to configure an AES encryption key.
/// </summary>
public byte[] Key { get; set; }
/// <summary>
/// Use this to restrict the whole stuff to a certain value.
/// </summary>
public uint RestrictToValue { get; set; }
/// <summary>
/// Appends the file with the properties that you configured on this object.
/// It will be encrypted if you have set the Key propertie.
/// </summary>
/// <returns>True when all went ok, false when something failed for some reason.</returns>
public bool Append()
{
if (this.binArchive != null)
{
var binFile = new BinFile()
{
version = this.Version,
content = File.ReadAllBytes(this.Filename) // TODO: Will this always fit in memory? For now we just use very simple and small files.
};
// Unfortunate we can't everything into the object initialiser:
binFile.typeValues.AddRange(this.TypeValues);
binFile.size = (uint)binFile.content.Length;
// Calculate the checksum in a proper way for our file data. We now that we should place 20 bytes into our test file!
binFile.check = 3141592653;
binFile.crc = calculateCrc32(binFile.content);
binFile.sha1 = calculateSha1(binFile.content); // Should always be 20 bytes...
binFile.time = Helpers.convertToUnixTimeMilliseconds(File.GetCreationTimeUtc(this.Filename));
// Now add the binFile!
if (this.Key == null)
{
this.binArchive.binFiles.Add(binFile);
}
else
{
// Encrypt it when a key was given!
var binFileCrypter = new BinFileCrypter(this.Key);
var cryptedBinFile = binFileCrypter.convert(binFile, this.RestrictToValue);
this.binArchive.cryptedBinFiles.Add(cryptedBinFile);
}
// Also increase our append call, so that we know that we need to finish it really at the end.
this.appendCall++;
}
else
{
throw new InvalidOperationException("Trying to call Append() without calling Prepare() first!");
}
return true;
}
/// <summary>
/// Rewrite the whole protocol buffers file if needed. If you don't call this then nothing changes effectively.
/// </summary>
/// <returns>True when a new version has been written. False when this was not needed.</returns>
public bool Finish()
{
bool result = false;
if (binArchive != null && this.appendCall > 0)
{
// We can crypt the firmware file, and we cannot crypt it. Supply them both and see if you can get back the same thing twice, would be great.
File.Delete(this.archiveFilename);
using (var fileStream = File.Create(this.archiveFilename))
{
ProtoBuf.Serializer.Serialize(fileStream, binArchive);
}
this.appendCall = 0;
result = true;
}
return result;
}
/// <summary>
/// Calculates the CRC-32 over an byte array.
/// </summary>
/// <param name="data">The byte[] that you want to calculate the CRC-32 on</param>
/// <returns>A 4 bytes large CRC value.</returns>
public static UInt32 calculateCrc32(byte[] data)
{
return Crc32.Compute(data);
}
/// <summary>
/// Calculates the SHA-1 over an byte array.
/// </summary>
/// <param name="data">The byte[] that you want to calculate the checksum for.</param>
/// <returns>A byte[] with the 20 bytes large checksum.</returns>
public static byte[] calculateSha1(byte[] data)
{
var sha = new SHA1CryptoServiceProvider();
return sha.ComputeHash(data);
}
}
}