diff options
Diffstat (limited to '')
-rw-r--r-- | src/WixToolset.Data/FileStructure.cs | 316 |
1 files changed, 0 insertions, 316 deletions
diff --git a/src/WixToolset.Data/FileStructure.cs b/src/WixToolset.Data/FileStructure.cs deleted file mode 100644 index 2667df1e..00000000 --- a/src/WixToolset.Data/FileStructure.cs +++ /dev/null | |||
@@ -1,316 +0,0 @@ | |||
1 | // Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. | ||
2 | |||
3 | namespace WixToolset.Data | ||
4 | { | ||
5 | using System; | ||
6 | using System.Collections.Generic; | ||
7 | using System.Diagnostics; | ||
8 | using System.IO; | ||
9 | using System.Linq; | ||
10 | using System.Text; | ||
11 | |||
12 | /// <summary> | ||
13 | /// Class that understands the standard file structures in the WiX toolset. | ||
14 | /// </summary> | ||
15 | public class FileStructure : IDisposable | ||
16 | { | ||
17 | private long dataStreamOffset; | ||
18 | private long[] embeddedFileSizes; | ||
19 | private Stream stream; | ||
20 | private bool disposed; | ||
21 | |||
22 | private static readonly Dictionary<string, FileFormat> SupportedFileFormats = new Dictionary<string, FileFormat>() | ||
23 | { | ||
24 | { "wir", FileFormat.WixIR }, | ||
25 | { "wixirf", FileFormat.WixIR }, | ||
26 | { "wixipl", FileFormat.WixIR }, | ||
27 | { "wixobj", FileFormat.Wixobj }, | ||
28 | { "wixlib", FileFormat.Wixlib }, | ||
29 | { "wixout", FileFormat.Wixout }, | ||
30 | { "wixpdb", FileFormat.Wixpdb }, | ||
31 | { "wixmst", FileFormat.Wixout }, | ||
32 | { "wixmsp", FileFormat.Wixout }, | ||
33 | }; | ||
34 | |||
35 | /// <summary> | ||
36 | /// Use Create or Read to create a FileStructure. | ||
37 | /// </summary> | ||
38 | private FileStructure() { } | ||
39 | |||
40 | /// <summary> | ||
41 | /// Count of embedded files in the file structure. | ||
42 | /// </summary> | ||
43 | public int EmbeddedFileCount => this.embeddedFileSizes.Length; | ||
44 | |||
45 | /// <summary> | ||
46 | /// File format of the file structure. | ||
47 | /// </summary> | ||
48 | public FileFormat FileFormat { get; private set; } | ||
49 | |||
50 | /// <summary> | ||
51 | /// Creates a new file structure. | ||
52 | /// </summary> | ||
53 | /// <param name="stream">Stream to write the file structure to.</param> | ||
54 | /// <param name="fileFormat">File format for the file structure.</param> | ||
55 | /// <param name="embedFilePaths">Paths to files to embedd in the file structure.</param> | ||
56 | /// <returns>Newly created file structure.</returns> | ||
57 | public static FileStructure Create(Stream stream, FileFormat fileFormat, IEnumerable<string> embedFilePaths) | ||
58 | { | ||
59 | var fs = new FileStructure(); | ||
60 | |||
61 | using (var writer = new BinaryWriter(stream, Encoding.UTF8, true)) | ||
62 | { | ||
63 | fs.WriteType(writer, fileFormat); | ||
64 | |||
65 | fs.WriteEmbeddedFiles(writer, embedFilePaths?.ToArray() ?? Array.Empty<string>()); | ||
66 | |||
67 | // Remember the data stream offset, which is right after the embedded files have been written. | ||
68 | fs.dataStreamOffset = stream.Position; | ||
69 | } | ||
70 | |||
71 | fs.stream = stream; | ||
72 | |||
73 | return fs; | ||
74 | } | ||
75 | |||
76 | /// <summary> | ||
77 | /// Reads a file structure from an open stream. | ||
78 | /// </summary> | ||
79 | /// <param name="stream">Stream to read from.</param> | ||
80 | /// <returns>File structure populated from the stream.</returns> | ||
81 | public static FileStructure Read(Stream stream) | ||
82 | { | ||
83 | var fs = new FileStructure(); | ||
84 | |||
85 | using (var reader = new BinaryReader(stream, Encoding.UTF8, true)) | ||
86 | { | ||
87 | fs.FileFormat = FileStructure.ReadFileFormat(reader); | ||
88 | |||
89 | if (fs.FileFormat != FileFormat.Unknown) | ||
90 | { | ||
91 | fs.embeddedFileSizes = FileStructure.ReadEmbeddedFileSizes(reader); | ||
92 | |||
93 | // Remember the data stream offset, which is right after the embedded files have been written. | ||
94 | fs.dataStreamOffset = stream.Position; | ||
95 | foreach (long size in fs.embeddedFileSizes) | ||
96 | { | ||
97 | fs.dataStreamOffset += size; | ||
98 | } | ||
99 | } | ||
100 | } | ||
101 | |||
102 | fs.stream = stream; | ||
103 | |||
104 | return fs; | ||
105 | } | ||
106 | |||
107 | /// <summary> | ||
108 | /// Guess at the file format based on the file extension. | ||
109 | /// </summary> | ||
110 | /// <param name="extension">File extension to guess the file format for.</param> | ||
111 | /// <returns>Best guess at file format.</returns> | ||
112 | public static FileFormat GuessFileFormatFromExtension(string extension) | ||
113 | { | ||
114 | return FileStructure.SupportedFileFormats.TryGetValue(extension.TrimStart('.').ToLowerInvariant(), out var format) ? format : FileFormat.Unknown; | ||
115 | } | ||
116 | |||
117 | /// <summary> | ||
118 | /// Probes a stream to determine the file format. | ||
119 | /// </summary> | ||
120 | /// <param name="stream">Stream to test.</param> | ||
121 | /// <returns>The file format.</returns> | ||
122 | public static FileFormat TestFileFormat(Stream stream) | ||
123 | { | ||
124 | FileFormat format = FileFormat.Unknown; | ||
125 | |||
126 | long position = stream.Position; | ||
127 | |||
128 | try | ||
129 | { | ||
130 | using (var reader = new BinaryReader(stream, Encoding.UTF8, true)) | ||
131 | { | ||
132 | format = FileStructure.ReadFileFormat(reader); | ||
133 | } | ||
134 | } | ||
135 | finally | ||
136 | { | ||
137 | stream.Seek(position, SeekOrigin.Begin); | ||
138 | } | ||
139 | |||
140 | return format; | ||
141 | } | ||
142 | |||
143 | /// <summary> | ||
144 | /// Extracts an embedded file. | ||
145 | /// </summary> | ||
146 | /// <param name="embeddedIndex">Index to the file to extract.</param> | ||
147 | /// <param name="outputPath">Path to write the extracted file to.</param> | ||
148 | public void ExtractEmbeddedFile(int embeddedIndex, string outputPath) | ||
149 | { | ||
150 | if (this.EmbeddedFileCount <= embeddedIndex) | ||
151 | { | ||
152 | throw new ArgumentOutOfRangeException("embeddedIndex"); | ||
153 | } | ||
154 | |||
155 | long header = 6 + 4 + (this.embeddedFileSizes.Length * 8); // skip the type + the count of embedded files + all the sizes of embedded files. | ||
156 | long position = this.embeddedFileSizes.Take(embeddedIndex).Sum(); // skip to the embedded file we want. | ||
157 | long size = this.embeddedFileSizes[embeddedIndex]; | ||
158 | |||
159 | this.stream.Seek(header + position, SeekOrigin.Begin); | ||
160 | |||
161 | Directory.CreateDirectory(Path.GetDirectoryName(outputPath)); | ||
162 | |||
163 | using (FileStream output = File.OpenWrite(outputPath)) | ||
164 | { | ||
165 | int read; | ||
166 | int total = 0; | ||
167 | byte[] buffer = new byte[64 * 1024]; | ||
168 | while (0 < (read = this.stream.Read(buffer, 0, (int)Math.Min(buffer.Length, size - total)))) | ||
169 | { | ||
170 | output.Write(buffer, 0, read); | ||
171 | total += read; | ||
172 | } | ||
173 | } | ||
174 | } | ||
175 | |||
176 | /// <summary> | ||
177 | /// Gets a non-closing stream to the data of the file. | ||
178 | /// </summary> | ||
179 | /// <returns>Stream to the data of the file.</returns> | ||
180 | public Stream GetDataStream() | ||
181 | { | ||
182 | this.stream.Seek(this.dataStreamOffset, SeekOrigin.Begin); | ||
183 | return new NonClosingStreamWrapper(this.stream); | ||
184 | } | ||
185 | |||
186 | /// <summary> | ||
187 | /// Gets the data of the file as a string. | ||
188 | /// </summary> | ||
189 | /// <returns>String contents data of the file.</returns> | ||
190 | public string GetData() | ||
191 | { | ||
192 | var bytes = new byte[this.stream.Length - this.dataStreamOffset]; | ||
193 | |||
194 | this.stream.Seek(this.dataStreamOffset, SeekOrigin.Begin); | ||
195 | this.stream.Read(bytes, 0, bytes.Length); | ||
196 | |||
197 | return Encoding.UTF8.GetString(bytes); | ||
198 | } | ||
199 | |||
200 | /// <summary> | ||
201 | /// Disposes of the internal state of the file structure. | ||
202 | /// </summary> | ||
203 | public void Dispose() | ||
204 | { | ||
205 | Dispose(true); | ||
206 | GC.SuppressFinalize(this); | ||
207 | } | ||
208 | |||
209 | /// <summary> | ||
210 | /// Disposes of the internsl state of the file structure. | ||
211 | /// </summary> | ||
212 | /// <param name="disposing">True if disposing.</param> | ||
213 | protected virtual void Dispose(bool disposing) | ||
214 | { | ||
215 | if (!this.disposed) | ||
216 | { | ||
217 | if (disposing) | ||
218 | { | ||
219 | if (null != this.stream) | ||
220 | { | ||
221 | // We do not own the stream, so we don't close it. We're just resetting our internal state. | ||
222 | this.embeddedFileSizes = null; | ||
223 | this.dataStreamOffset = 0; | ||
224 | this.stream = null; | ||
225 | } | ||
226 | } | ||
227 | } | ||
228 | |||
229 | this.disposed = true; | ||
230 | } | ||
231 | |||
232 | private static FileFormat ReadFileFormat(BinaryReader reader) | ||
233 | { | ||
234 | FileFormat format = FileFormat.Unknown; | ||
235 | |||
236 | string type = new string(reader.ReadChars(3)); | ||
237 | if (FileStructure.SupportedFileFormats.TryGetValue(type, out format)) | ||
238 | { | ||
239 | return format; | ||
240 | } | ||
241 | |||
242 | type += new string(reader.ReadChars(3)); | ||
243 | FileStructure.SupportedFileFormats.TryGetValue(type, out format); | ||
244 | |||
245 | return format; | ||
246 | } | ||
247 | |||
248 | private static long[] ReadEmbeddedFileSizes(BinaryReader reader) | ||
249 | { | ||
250 | uint count = reader.ReadUInt32(); | ||
251 | |||
252 | long[] embeddedFileSizes = new long[count]; | ||
253 | |||
254 | for (int i = 0; i < embeddedFileSizes.Length; ++i) | ||
255 | { | ||
256 | embeddedFileSizes[i] = (long)reader.ReadUInt64(); | ||
257 | } | ||
258 | |||
259 | return embeddedFileSizes; | ||
260 | } | ||
261 | |||
262 | private BinaryWriter WriteType(BinaryWriter writer, FileFormat fileFormat) | ||
263 | { | ||
264 | string type = null; | ||
265 | foreach (var supported in FileStructure.SupportedFileFormats) | ||
266 | { | ||
267 | if (supported.Value.Equals(fileFormat)) | ||
268 | { | ||
269 | type = supported.Key; | ||
270 | break; | ||
271 | } | ||
272 | } | ||
273 | |||
274 | if (String.IsNullOrEmpty(type)) | ||
275 | { | ||
276 | throw new ArgumentException("Unknown file format type", "fileFormat"); | ||
277 | } | ||
278 | |||
279 | this.FileFormat = fileFormat; | ||
280 | |||
281 | Debug.Assert(3 == type.ToCharArray().Length || 6 == type.ToCharArray().Length); | ||
282 | writer.Write(type.ToCharArray()); | ||
283 | return writer; | ||
284 | } | ||
285 | |||
286 | private BinaryWriter WriteEmbeddedFiles(BinaryWriter writer, string[] embedFilePaths) | ||
287 | { | ||
288 | // First write the count of embedded files as a Uint32; | ||
289 | writer.Write((uint)embedFilePaths.Length); | ||
290 | |||
291 | this.embeddedFileSizes = new long[embedFilePaths.Length]; | ||
292 | |||
293 | // Next write out the size of each file as a Uint64 in order. | ||
294 | FileInfo[] files = new FileInfo[embedFilePaths.Length]; | ||
295 | for (int i = 0; i < embedFilePaths.Length; ++i) | ||
296 | { | ||
297 | files[i] = new FileInfo(embedFilePaths[i]); | ||
298 | |||
299 | this.embeddedFileSizes[i] = files[i].Length; | ||
300 | writer.Write((ulong)this.embeddedFileSizes[i]); | ||
301 | } | ||
302 | |||
303 | // Next write out the content of each file *after* the sizes of | ||
304 | // *all* of the files were written. | ||
305 | foreach (FileInfo file in files) | ||
306 | { | ||
307 | using (FileStream stream = file.OpenRead()) | ||
308 | { | ||
309 | stream.CopyTo(writer.BaseStream); | ||
310 | } | ||
311 | } | ||
312 | |||
313 | return writer; | ||
314 | } | ||
315 | } | ||
316 | } | ||