aboutsummaryrefslogtreecommitdiff
path: root/src/WixToolset.Data/WixOutput.cs
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--src/WixToolset.Data/WixOutput.cs233
1 files changed, 233 insertions, 0 deletions
diff --git a/src/WixToolset.Data/WixOutput.cs b/src/WixToolset.Data/WixOutput.cs
new file mode 100644
index 00000000..f27f7c77
--- /dev/null
+++ b/src/WixToolset.Data/WixOutput.cs
@@ -0,0 +1,233 @@
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.IO;
7 using System.IO.Compression;
8 using System.Reflection;
9 using System.Text;
10
11 /// <summary>
12 /// Class that understands the standard file structure of the WiX toolset.
13 /// </summary>
14 public class WixOutput : IDisposable
15 {
16 private readonly ZipArchive archive;
17 private bool disposed;
18
19 private WixOutput(Uri uri, ZipArchive archive)
20 {
21 this.Uri = uri;
22 this.archive = archive;
23 }
24
25 public Uri Uri { get; }
26
27 /// <summary>
28 /// Creates a new file structure in memory.
29 /// </summary>
30 /// <returns>Newly created <c>WixOutput</c>.</returns>
31 public static WixOutput Create()
32 {
33 var uri = new Uri("memorystream:");
34
35 var stream = new MemoryStream();
36
37 return WixOutput.Create(uri, stream);
38 }
39
40 /// <summary>
41 /// Creates a new file structure on disk.
42 /// </summary>
43 /// <param name="path">Path to write file structure to.</param>
44 /// <returns>Newly created <c>WixOutput</c>.</returns>
45 public static WixOutput Create(string path)
46 {
47 var uri = new Uri(Path.GetFullPath(path));
48
49 var stream = File.Create(path);
50
51 return WixOutput.Create(uri, stream);
52 }
53
54 /// <summary>
55 /// Creates a new file structure.
56 /// </summary>
57 /// <param name="stream">Stream to write the file structure to.</param>
58 /// <param name="embedFilePaths">Paths to files to embedd in the file structure.</param>
59 /// <returns>Newly created <c>WixOutput</c>.</returns>
60 public static WixOutput Create(Uri uri, Stream stream)
61 {
62 var archive = new ZipArchive(stream, ZipArchiveMode.Update);
63
64 return new WixOutput(uri, archive);
65 }
66
67 /// <summary>
68 /// Loads a wixout from a path on disk.
69 /// </summary>
70 /// <param name="path">Path to wixout file saved on disk.</param>
71 /// <returns>Loaded created <c>WixOutput</c>.</returns>
72 public static WixOutput Read(string path)
73 {
74 var uri = new Uri(Path.GetFullPath(path));
75
76 var stream = File.OpenRead(path);
77
78 return Read(uri, stream);
79 }
80
81 /// <summary>
82 /// Loads a wixout from a path on disk or embedded resource in assembly.
83 /// </summary>
84 /// <param name="baseUri">Uri with local path to wixout file saved on disk or embedded resource in assembly.</param>
85 /// <returns>Loaded created <c>WixOutput</c>.</returns>
86 public static WixOutput Read(Uri baseUri)
87 {
88 // If the embedded files are stored in an assembly resource stream (usually
89 // a .wixlib embedded in a WixExtension).
90 if ("embeddedresource" == baseUri.Scheme)
91 {
92 var assemblyPath = Path.GetFullPath(baseUri.LocalPath);
93 var resourceName = baseUri.Fragment.TrimStart('#');
94
95 var assembly = Assembly.LoadFile(assemblyPath);
96 return WixOutput.Read(assembly, resourceName);
97 }
98 else // normal file (usually a binary .wixlib on disk).
99 {
100 var stream = File.OpenRead(baseUri.LocalPath);
101 return WixOutput.Read(baseUri, stream);
102 }
103 }
104
105 /// <summary>
106 /// Loads a wixout from a assembly resource stream.
107 /// </summary>
108 /// <param name="path">Path to wixout file saved on disk.</param>
109 public static WixOutput Read(Assembly assembly, string resourceName)
110 {
111 var resourceStream = assembly.GetManifestResourceStream(resourceName);
112
113 var uriBuilder = new UriBuilder(assembly.CodeBase)
114 {
115 Scheme = "embeddedresource",
116 Fragment = resourceName
117 };
118
119 return Read(uriBuilder.Uri, resourceStream);
120 }
121
122 /// <summary>
123 /// Reads a file structure from an open stream.
124 /// </summary>
125 /// <param name="stream">Stream to read from.</param>
126 /// <returns>Loaded created <c>WixOutput</c>.</returns>
127 public static WixOutput Read(Uri uri, Stream stream, bool leaveOpen = false)
128 {
129 try
130 {
131 var archive = new ZipArchive(stream, ZipArchiveMode.Read, leaveOpen);
132
133 return new WixOutput(uri, archive);
134 }
135 catch (InvalidDataException)
136 {
137 throw new WixException(ErrorMessages.CorruptFileFormat(uri.AbsoluteUri, "wixout"));
138 }
139 }
140
141 /// <summary>
142 /// Extracts an embedded file.
143 /// </summary>
144 /// <param name="embeddedId">Id to the file to extract.</param>
145 /// <param name="outputPath">Path to write the extracted file to.</param>
146 public void ExtractEmbeddedFile(string embeddedId, string outputPath)
147 {
148 var entry = this.archive.GetEntry(embeddedId);
149
150 if (entry == null)
151 {
152 throw new ArgumentOutOfRangeException(nameof(embeddedId));
153 }
154
155 var folder = Path.GetDirectoryName(outputPath);
156
157 Directory.CreateDirectory(folder);
158
159 entry.ExtractToFile(outputPath);
160 }
161
162 /// <summary>
163 /// Creates a data stream in the wixout.
164 /// </summary>
165 /// <returns>Stream to the data of the file.</returns>
166 public Stream CreateDataStream(string name)
167 {
168 var entry = this.archive.CreateEntry(name);
169
170 return entry.Open();
171 }
172
173 public void ImportDataStream(string name, string path)
174 {
175 this.archive.CreateEntryFromFile(path, name, System.IO.Compression.CompressionLevel.Optimal);
176 }
177
178 /// <summary>
179 /// Gets a non-closing stream to the data of the file.
180 /// </summary>
181 /// <returns>Stream to the data of the file.</returns>
182 public Stream GetDataStream(string name)
183 {
184 var entry = this.archive.GetEntry(name);
185
186 return entry.Open();
187 }
188
189 /// <summary>
190 /// Gets the data of the file as a string.
191 /// </summary>
192 /// <returns>String contents data of the file.</returns>
193 public string GetData(string name)
194 {
195 var entry = this.archive.GetEntry(name);
196
197 var bytes = new byte[entry.Length];
198
199 using (var stream = entry.Open())
200 {
201 stream.Read(bytes, 0, bytes.Length);
202 }
203
204 return Encoding.UTF8.GetString(bytes);
205 }
206
207 /// <summary>
208 /// Disposes of the internal state of the file structure.
209 /// </summary>
210 public void Dispose()
211 {
212 this.Dispose(true);
213 GC.SuppressFinalize(this);
214 }
215
216 /// <summary>
217 /// Disposes of the internsl state of the file structure.
218 /// </summary>
219 /// <param name="disposing">True if disposing.</param>
220 protected virtual void Dispose(bool disposing)
221 {
222 if (!this.disposed)
223 {
224 if (disposing)
225 {
226 this.archive?.Dispose();
227 }
228 }
229
230 this.disposed = true;
231 }
232 }
233}