aboutsummaryrefslogtreecommitdiff
path: root/src/WixToolset.Data/FileStructure.cs
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--src/WixToolset.Data/FileStructure.cs316
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
3namespace 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}