diff options
author | Rob Mensching <rob@firegiant.com> | 2019-10-25 00:23:42 -0700 |
---|---|---|
committer | Rob Mensching <rob@firegiant.com> | 2019-10-25 00:25:53 -0700 |
commit | 6c1ae2593faab59e1a01c96794e0835a6fcd0626 (patch) | |
tree | c15fcb9a1f1c5bec846ba61f1f3dcd9a152bf701 | |
parent | 0078364515ba1f7570f43d47e43bd359918cafd0 (diff) | |
download | wix-6c1ae2593faab59e1a01c96794e0835a6fcd0626.tar.gz wix-6c1ae2593faab59e1a01c96794e0835a6fcd0626.tar.bz2 wix-6c1ae2593faab59e1a01c96794e0835a6fcd0626.zip |
Implement WixOutput with support for multiple streams of data
21 files changed, 399 insertions, 846 deletions
diff --git a/src/WixToolset.Data/ErrorMessages.cs b/src/WixToolset.Data/ErrorMessages.cs index 33cccdd2..3fa23a74 100644 --- a/src/WixToolset.Data/ErrorMessages.cs +++ b/src/WixToolset.Data/ErrorMessages.cs | |||
@@ -198,9 +198,9 @@ namespace WixToolset.Data | |||
198 | return Message(sourceLineNumbers, Ids.ConditionExpected, "The {0} element's inner text cannot be an empty string or completely whitespace. If you don't want a condition, then simply remove the entire {0} element.", elementName); | 198 | return Message(sourceLineNumbers, Ids.ConditionExpected, "The {0} element's inner text cannot be an empty string or completely whitespace. If you don't want a condition, then simply remove the entire {0} element.", elementName); |
199 | } | 199 | } |
200 | 200 | ||
201 | public static Message CorruptFileFormat(string path, FileFormat format) | 201 | public static Message CorruptFileFormat(string path, string format) |
202 | { | 202 | { |
203 | return Message(null, Ids.CorruptFileFormat, "Attempted to load corrupt file from path: {0}. The file with format {1} contained unexpected content. Ensure the correct path was provided and that the file has not been incorrectly modified.", path, format.ToString().ToLowerInvariant()); | 203 | return Message(null, Ids.CorruptFileFormat, "Attempted to load corrupt file from path: {0}. The file with format {1} contained unexpected content. Ensure the correct path was provided and that the file has not been incorrectly modified.", path, format.ToLowerInvariant()); |
204 | } | 204 | } |
205 | 205 | ||
206 | public static Message CreateCabAddFileFailed() | 206 | public static Message CreateCabAddFileFailed() |
@@ -2049,9 +2049,9 @@ namespace WixToolset.Data | |||
2049 | return Message(null, Ids.UnexpectedFileExtension, "The file '{0}' has an unexpected extension. Expected one of the following: '{1}'.", fileName, expectedExtensions); | 2049 | return Message(null, Ids.UnexpectedFileExtension, "The file '{0}' has an unexpected extension. Expected one of the following: '{1}'.", fileName, expectedExtensions); |
2050 | } | 2050 | } |
2051 | 2051 | ||
2052 | public static Message UnexpectedFileFormat(string path, FileFormat expectedFormat, FileFormat actualFormat) | 2052 | public static Message UnexpectedFileFormat(string path, string expectedFormat, string actualFormat) |
2053 | { | 2053 | { |
2054 | return Message(null, Ids.UnexpectedFileFormat, "Unexpected file format loaded from path: {0}. The file was expected to be a {1} but was actually: {2}. Ensure the correct path was provided.", path, expectedFormat.ToString().ToLowerInvariant(), actualFormat.ToString().ToLowerInvariant()); | 2054 | return Message(null, Ids.UnexpectedFileFormat, "Unexpected file format loaded from path: {0}. The file was expected to be a {1} but was actually: {2}. Ensure the correct path was provided.", path, expectedFormat.ToLowerInvariant(), actualFormat.ToLowerInvariant()); |
2055 | } | 2055 | } |
2056 | 2056 | ||
2057 | public static Message UnexpectedGroupChild(string parentType, string parentId, string childType, string childId) | 2057 | public static Message UnexpectedGroupChild(string parentType, string parentId, string childType, string childId) |
diff --git a/src/WixToolset.Data/FileFormat.cs b/src/WixToolset.Data/FileFormat.cs deleted file mode 100644 index 75eab3de..00000000 --- a/src/WixToolset.Data/FileFormat.cs +++ /dev/null | |||
@@ -1,19 +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 | public enum FileFormat | ||
6 | { | ||
7 | Unknown, | ||
8 | |||
9 | Wixobj, | ||
10 | |||
11 | Wixlib, | ||
12 | |||
13 | Wixout, | ||
14 | |||
15 | Wixpdb, | ||
16 | |||
17 | WixIR, | ||
18 | } | ||
19 | } | ||
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 | } | ||
diff --git a/src/WixToolset.Data/Intermediate.cs b/src/WixToolset.Data/Intermediate.cs index 7693b815..1db21d85 100644 --- a/src/WixToolset.Data/Intermediate.cs +++ b/src/WixToolset.Data/Intermediate.cs | |||
@@ -14,8 +14,8 @@ namespace WixToolset.Data | |||
14 | /// </summary> | 14 | /// </summary> |
15 | public sealed class Intermediate | 15 | public sealed class Intermediate |
16 | { | 16 | { |
17 | public const string XmlNamespaceUri = "http://wixtoolset.org/schemas/v4/wixobj"; | ||
18 | private static readonly Version CurrentVersion = new Version("4.0.0.0"); | 17 | private static readonly Version CurrentVersion = new Version("4.0.0.0"); |
18 | private const string WixOutputStreamName = "wix-ir.json"; | ||
19 | 19 | ||
20 | private readonly Dictionary<string, Localization> localizationsByCulture; | 20 | private readonly Dictionary<string, Localization> localizationsByCulture; |
21 | 21 | ||
@@ -25,15 +25,13 @@ namespace WixToolset.Data | |||
25 | public Intermediate() | 25 | public Intermediate() |
26 | { | 26 | { |
27 | this.Id = Convert.ToBase64String(Guid.NewGuid().ToByteArray()).TrimEnd('=').Replace('+', '.').Replace('/', '_'); | 27 | this.Id = Convert.ToBase64String(Guid.NewGuid().ToByteArray()).TrimEnd('=').Replace('+', '.').Replace('/', '_'); |
28 | this.EmbedFilePaths = new List<string>(); | ||
29 | this.localizationsByCulture = new Dictionary<string, Localization>(StringComparer.OrdinalIgnoreCase); | 28 | this.localizationsByCulture = new Dictionary<string, Localization>(StringComparer.OrdinalIgnoreCase); |
30 | this.Sections = new List<IntermediateSection>(); | 29 | this.Sections = new List<IntermediateSection>(); |
31 | } | 30 | } |
32 | 31 | ||
33 | public Intermediate(string id, IEnumerable<IntermediateSection> sections, IDictionary<string, Localization> localizationsByCulture, IEnumerable<string> embedFilePaths) | 32 | public Intermediate(string id, IEnumerable<IntermediateSection> sections, IDictionary<string, Localization> localizationsByCulture) |
34 | { | 33 | { |
35 | this.Id = id; | 34 | this.Id = id; |
36 | this.EmbedFilePaths = (embedFilePaths != null) ? new List<string>(embedFilePaths) : new List<string>(); | ||
37 | this.localizationsByCulture = (localizationsByCulture != null) ? new Dictionary<string, Localization>(localizationsByCulture, StringComparer.OrdinalIgnoreCase) : new Dictionary<string, Localization>(StringComparer.OrdinalIgnoreCase); | 35 | this.localizationsByCulture = (localizationsByCulture != null) ? new Dictionary<string, Localization>(localizationsByCulture, StringComparer.OrdinalIgnoreCase) : new Dictionary<string, Localization>(StringComparer.OrdinalIgnoreCase); |
38 | this.Sections = (sections != null) ? new List<IntermediateSection>(sections) : new List<IntermediateSection>(); | 36 | this.Sections = (sections != null) ? new List<IntermediateSection>(sections) : new List<IntermediateSection>(); |
39 | } | 37 | } |
@@ -44,11 +42,6 @@ namespace WixToolset.Data | |||
44 | public string Id { get; } | 42 | public string Id { get; } |
45 | 43 | ||
46 | /// <summary> | 44 | /// <summary> |
47 | /// Get the embed file paths in this intermediate. | ||
48 | /// </summary> | ||
49 | public IList<string> EmbedFilePaths { get; } | ||
50 | |||
51 | /// <summary> | ||
52 | /// Get the localizations contained in this intermediate. | 45 | /// Get the localizations contained in this intermediate. |
53 | /// </summary> | 46 | /// </summary> |
54 | public IEnumerable<Localization> Localizations => this.localizationsByCulture.Values; | 47 | public IEnumerable<Localization> Localizations => this.localizationsByCulture.Values; |
@@ -66,12 +59,8 @@ namespace WixToolset.Data | |||
66 | /// <returns>Returns the loaded intermediate.</returns> | 59 | /// <returns>Returns the loaded intermediate.</returns> |
67 | public static Intermediate Load(string path, bool suppressVersionCheck = false) | 60 | public static Intermediate Load(string path, bool suppressVersionCheck = false) |
68 | { | 61 | { |
69 | using (var stream = File.OpenRead(path)) | 62 | var creator = new SimpleTupleDefinitionCreator(); |
70 | { | 63 | return Intermediate.Load(path, creator, suppressVersionCheck); |
71 | var uri = new Uri(Path.GetFullPath(path)); | ||
72 | var creator = new SimpleTupleDefinitionCreator(); | ||
73 | return Intermediate.LoadIntermediate(stream, uri, creator, suppressVersionCheck); | ||
74 | } | ||
75 | } | 64 | } |
76 | 65 | ||
77 | /// <summary> | 66 | /// <summary> |
@@ -97,13 +86,9 @@ namespace WixToolset.Data | |||
97 | /// <returns>Returns the loaded intermediate.</returns> | 86 | /// <returns>Returns the loaded intermediate.</returns> |
98 | public static Intermediate Load(Assembly assembly, string resourceName, ITupleDefinitionCreator creator, bool suppressVersionCheck = false) | 87 | public static Intermediate Load(Assembly assembly, string resourceName, ITupleDefinitionCreator creator, bool suppressVersionCheck = false) |
99 | { | 88 | { |
100 | using (var resourceStream = assembly.GetManifestResourceStream(resourceName)) | 89 | using (var wixout = WixOutput.Read(assembly, resourceName)) |
101 | { | 90 | { |
102 | var uriBuilder = new UriBuilder(assembly.CodeBase); | 91 | return Intermediate.LoadIntermediate(wixout, creator, suppressVersionCheck); |
103 | uriBuilder.Scheme = "embeddedresource"; | ||
104 | uriBuilder.Fragment = resourceName; | ||
105 | |||
106 | return Intermediate.LoadIntermediate(resourceStream, uriBuilder.Uri, creator, suppressVersionCheck); | ||
107 | } | 92 | } |
108 | } | 93 | } |
109 | 94 | ||
@@ -116,11 +101,9 @@ namespace WixToolset.Data | |||
116 | /// <returns>Returns the loaded intermediate.</returns> | 101 | /// <returns>Returns the loaded intermediate.</returns> |
117 | public static Intermediate Load(string path, ITupleDefinitionCreator creator, bool suppressVersionCheck = false) | 102 | public static Intermediate Load(string path, ITupleDefinitionCreator creator, bool suppressVersionCheck = false) |
118 | { | 103 | { |
119 | using (var stream = File.OpenRead(path)) | 104 | using (var wixout = WixOutput.Read(path)) |
120 | { | 105 | { |
121 | var uri = new Uri(Path.GetFullPath(path)); | 106 | return Intermediate.LoadIntermediate(wixout, creator, suppressVersionCheck); |
122 | |||
123 | return Intermediate.LoadIntermediate(stream, uri, creator, suppressVersionCheck); | ||
124 | } | 107 | } |
125 | } | 108 | } |
126 | 109 | ||
@@ -133,7 +116,6 @@ namespace WixToolset.Data | |||
133 | public static IEnumerable<Intermediate> Load(IEnumerable<string> intermediateFiles) | 116 | public static IEnumerable<Intermediate> Load(IEnumerable<string> intermediateFiles) |
134 | { | 117 | { |
135 | var creator = new SimpleTupleDefinitionCreator(); | 118 | var creator = new SimpleTupleDefinitionCreator(); |
136 | |||
137 | return Intermediate.Load(intermediateFiles, creator); | 119 | return Intermediate.Load(intermediateFiles, creator); |
138 | } | 120 | } |
139 | 121 | ||
@@ -151,15 +133,14 @@ namespace WixToolset.Data | |||
151 | 133 | ||
152 | foreach (var path in intermediateFiles) | 134 | foreach (var path in intermediateFiles) |
153 | { | 135 | { |
154 | using (var stream = File.OpenRead(path)) | 136 | using (var wixout = WixOutput.Read(path)) |
155 | { | 137 | { |
156 | var uri = new Uri(Path.GetFullPath(path)); | 138 | var data = wixout.GetData(WixOutputStreamName); |
157 | 139 | var json = Intermediate.LoadJson(data, wixout.Uri, suppressVersionCheck); | |
158 | var json = Intermediate.LoadJson(stream, uri, suppressVersionCheck); | ||
159 | 140 | ||
160 | Intermediate.LoadDefinitions(json, creator); | 141 | Intermediate.LoadDefinitions(json, creator); |
161 | 142 | ||
162 | jsons.Enqueue(new JsonWithPath { Json = json, Path = uri }); | 143 | jsons.Enqueue(new JsonWithPath { Json = json, Path = wixout.Uri }); |
163 | } | 144 | } |
164 | } | 145 | } |
165 | 146 | ||
@@ -183,55 +164,21 @@ namespace WixToolset.Data | |||
183 | { | 164 | { |
184 | Directory.CreateDirectory(Path.GetDirectoryName(Path.GetFullPath(path))); | 165 | Directory.CreateDirectory(Path.GetDirectoryName(Path.GetFullPath(path))); |
185 | 166 | ||
186 | using (var stream = File.Create(path)) | 167 | using (var wixout = WixOutput.Create(path)) |
187 | using (var fs = FileStructure.Create(stream, FileFormat.WixIR, this.EmbedFilePaths)) | ||
188 | using (var writer = new StreamWriter(fs.GetDataStream())) | ||
189 | { | 168 | { |
190 | var jsonObject = new JsonObject | 169 | this.Save(wixout); |
191 | { | 170 | } |
192 | { "id", this.Id }, | 171 | } |
193 | { "version", Intermediate.CurrentVersion.ToString() } | ||
194 | }; | ||
195 | |||
196 | var sectionsJson = new JsonArray(this.Sections.Count); | ||
197 | foreach (var section in this.Sections) | ||
198 | { | ||
199 | var sectionJson = section.Serialize(); | ||
200 | sectionsJson.Add(sectionJson); | ||
201 | } | ||
202 | |||
203 | jsonObject.Add("sections", sectionsJson); | ||
204 | |||
205 | var customDefinitions = this.GetCustomDefinitionsInSections(); | ||
206 | |||
207 | if (customDefinitions.Count > 0) | ||
208 | { | ||
209 | var customDefinitionsJson = new JsonArray(customDefinitions.Count); | ||
210 | |||
211 | foreach (var kvp in customDefinitions.OrderBy(d => d.Key)) | ||
212 | { | ||
213 | var customDefinitionJson = kvp.Value.Serialize(); | ||
214 | customDefinitionsJson.Add(customDefinitionJson); | ||
215 | } | ||
216 | |||
217 | jsonObject.Add("definitions", customDefinitionsJson); | ||
218 | } | ||
219 | |||
220 | if (this.Localizations.Any()) | ||
221 | { | ||
222 | var localizationsJson = new JsonArray(); | ||
223 | foreach (var localization in this.Localizations) | ||
224 | { | ||
225 | var localizationJson = localization.Serialize(); | ||
226 | localizationsJson.Add(localizationJson); | ||
227 | } | ||
228 | 172 | ||
229 | jsonObject.Add("localizations", localizationsJson); | 173 | /// <summary> |
230 | } | 174 | /// Saves an intermediate to a path on disk. |
175 | /// </summary> | ||
176 | /// <param name="path">Path to save intermediate file to disk.</param> | ||
177 | public void Save(WixOutput wixout) | ||
178 | { | ||
179 | this.SaveEmbedFiles(wixout); | ||
231 | 180 | ||
232 | var json = SimpleJson.SerializeObject(jsonObject); | 181 | this.SaveIR(wixout); |
233 | writer.Write(json); | ||
234 | } | ||
235 | } | 182 | } |
236 | 183 | ||
237 | /// <summary> | 184 | /// <summary> |
@@ -242,13 +189,14 @@ namespace WixToolset.Data | |||
242 | /// <param name="creator">ITupleDefinitionCreator to use when reconstituting the intermediate.</param> | 189 | /// <param name="creator">ITupleDefinitionCreator to use when reconstituting the intermediate.</param> |
243 | /// <param name="suppressVersionCheck">Suppress checking for wix.dll version mismatches.</param> | 190 | /// <param name="suppressVersionCheck">Suppress checking for wix.dll version mismatches.</param> |
244 | /// <returns>Returns the loaded intermediate.</returns> | 191 | /// <returns>Returns the loaded intermediate.</returns> |
245 | private static Intermediate LoadIntermediate(Stream stream, Uri baseUri, ITupleDefinitionCreator creator, bool suppressVersionCheck = false) | 192 | private static Intermediate LoadIntermediate(WixOutput wixout, ITupleDefinitionCreator creator, bool suppressVersionCheck = false) |
246 | { | 193 | { |
247 | var json = Intermediate.LoadJson(stream, baseUri, suppressVersionCheck); | 194 | var data = wixout.GetData(WixOutputStreamName); |
195 | var json = Intermediate.LoadJson(data, wixout.Uri, suppressVersionCheck); | ||
248 | 196 | ||
249 | Intermediate.LoadDefinitions(json, creator); | 197 | Intermediate.LoadDefinitions(json, creator); |
250 | 198 | ||
251 | return Intermediate.FinalizeLoad(json, baseUri, creator); | 199 | return Intermediate.FinalizeLoad(json, wixout.Uri, creator); |
252 | } | 200 | } |
253 | 201 | ||
254 | /// <summary> | 202 | /// <summary> |
@@ -258,19 +206,9 @@ namespace WixToolset.Data | |||
258 | /// <param name="baseUri">Path name of intermediate file.</param> | 206 | /// <param name="baseUri">Path name of intermediate file.</param> |
259 | /// <param name="suppressVersionCheck">Suppress checking for wix.dll version mismatches.</param> | 207 | /// <param name="suppressVersionCheck">Suppress checking for wix.dll version mismatches.</param> |
260 | /// <returns>Returns the loaded json.</returns> | 208 | /// <returns>Returns the loaded json.</returns> |
261 | private static JsonObject LoadJson(Stream stream, Uri baseUri, bool suppressVersionCheck) | 209 | private static JsonObject LoadJson(string json, Uri baseUri, bool suppressVersionCheck) |
262 | { | 210 | { |
263 | JsonObject jsonObject; | 211 | var jsonObject = SimpleJson.DeserializeObject(json) as JsonObject; |
264 | using (var fs = FileStructure.Read(stream)) | ||
265 | { | ||
266 | if (FileFormat.WixIR != fs.FileFormat) | ||
267 | { | ||
268 | throw new WixUnexpectedFileFormatException(baseUri.LocalPath, FileFormat.WixIR, fs.FileFormat); | ||
269 | } | ||
270 | |||
271 | var json = fs.GetData(); | ||
272 | jsonObject = SimpleJson.DeserializeObject(json) as JsonObject; | ||
273 | } | ||
274 | 212 | ||
275 | if (!suppressVersionCheck) | 213 | if (!suppressVersionCheck) |
276 | { | 214 | { |
@@ -333,153 +271,118 @@ namespace WixToolset.Data | |||
333 | localizations.Add(localization.Culture, localization); | 271 | localizations.Add(localization.Culture, localization); |
334 | } | 272 | } |
335 | 273 | ||
336 | return new Intermediate(id, sections, localizations, null); | 274 | return new Intermediate(id, sections, localizations); |
337 | } | 275 | } |
338 | 276 | ||
339 | #if false | 277 | private void SaveEmbedFiles(WixOutput wixout) |
340 | /// <summary> | ||
341 | /// Loads an intermediate from a path on disk. | ||
342 | /// </summary> | ||
343 | /// <param name="path">Path to intermediate file saved on disk.</param> | ||
344 | /// <param name="tableDefinitions">Collection containing TableDefinitions to use when reconstituting the intermediate.</param> | ||
345 | /// <param name="suppressVersionCheck">Suppress checking for wix.dll version mismatches.</param> | ||
346 | /// <returns>Returns the loaded intermediate.</returns> | ||
347 | public static Intermediate Load(string path, TableDefinitionCollection tableDefinitions, bool suppressVersionCheck) | ||
348 | { | 278 | { |
349 | using (FileStream stream = File.OpenRead(path)) | 279 | var embeddedFields = this.Sections.SelectMany(s => s.Tuples) |
350 | using (FileStructure fs = FileStructure.Read(stream)) | 280 | .SelectMany(t => t.Fields) |
281 | .Where(f => f?.Type == IntermediateFieldType.Path) | ||
282 | .Select(f => f.AsPath()) | ||
283 | .Where(f => f.Embed) | ||
284 | .ToList(); | ||
285 | |||
286 | var savedEmbedFields = new Dictionary<string, IntermediateFieldPathValue>(StringComparer.OrdinalIgnoreCase); | ||
287 | var uniqueEntryNames = new HashSet<string>(StringComparer.OrdinalIgnoreCase); | ||
288 | |||
289 | foreach (var embeddedField in embeddedFields) | ||
351 | { | 290 | { |
352 | if (FileFormat.Wixobj != fs.FileFormat) | 291 | var key = String.Concat(embeddedField.BaseUri?.AbsoluteUri, "?", embeddedField.Path); |
292 | |||
293 | if (savedEmbedFields.TryGetValue(key, out var existing)) | ||
353 | { | 294 | { |
354 | throw new WixUnexpectedFileFormatException(path, FileFormat.Wixobj, fs.FileFormat); | 295 | embeddedField.Path = existing.Path; |
355 | } | 296 | } |
356 | 297 | else | |
357 | Uri uri = new Uri(Path.GetFullPath(path)); | ||
358 | using (XmlReader reader = XmlReader.Create(fs.GetDataStream(), null, uri.AbsoluteUri)) | ||
359 | { | 298 | { |
360 | try | 299 | var entryName = CalculateUniqueEntryName(uniqueEntryNames, embeddedField.Path); |
300 | |||
301 | if (embeddedField.BaseUri == null) | ||
361 | { | 302 | { |
362 | reader.MoveToContent(); | 303 | wixout.ImportDataStream(entryName, embeddedField.Path); |
363 | return Intermediate.Read(reader, tableDefinitions, suppressVersionCheck); | ||
364 | } | 304 | } |
365 | catch (XmlException xe) | 305 | else // open the container specified in baseUri and copy the correct stream out of it. |
366 | { | 306 | { |
367 | throw new WixCorruptFileException(path, fs.FileFormat, xe); | 307 | using (var otherWixout = WixOutput.Read(embeddedField.BaseUri)) |
308 | using (var stream = otherWixout.GetDataStream(embeddedField.Path)) | ||
309 | using (var target = wixout.CreateDataStream(entryName)) | ||
310 | { | ||
311 | stream.CopyTo(target); | ||
312 | } | ||
368 | } | 313 | } |
369 | } | ||
370 | } | ||
371 | } | ||
372 | 314 | ||
373 | /// <summary> | 315 | embeddedField.Path = entryName; |
374 | /// Saves an intermediate to a path on disk. | ||
375 | /// </summary> | ||
376 | /// <param name="path">Path to save intermediate file to disk.</param> | ||
377 | public void Save(string path) | ||
378 | { | ||
379 | Directory.CreateDirectory(Path.GetDirectoryName(Path.GetFullPath(path))); | ||
380 | 316 | ||
381 | using (var stream = File.Create(path)) | 317 | savedEmbedFields.Add(key, embeddedField); |
382 | using (var fs = FileStructure.Create(stream, FileFormat.Wixobj, null)) | 318 | } |
383 | using (var writer = XmlWriter.Create(fs.GetDataStream())) | ||
384 | { | ||
385 | writer.WriteStartDocument(); | ||
386 | this.Write(writer); | ||
387 | writer.WriteEndDocument(); | ||
388 | } | 319 | } |
389 | } | 320 | } |
390 | 321 | ||
391 | /// <summary> | 322 | private void SaveIR(WixOutput wixout) |
392 | /// Parse an intermediate from an XML format. | ||
393 | /// </summary> | ||
394 | /// <param name="reader">XmlReader where the intermediate is persisted.</param> | ||
395 | /// <param name="tableDefinitions">TableDefinitions to use in the intermediate.</param> | ||
396 | /// <param name="suppressVersionCheck">Suppress checking for wix.dll version mismatch.</param> | ||
397 | /// <returns>The parsed Intermediate.</returns> | ||
398 | private static Intermediate Read(XmlReader reader, TableDefinitionCollection tableDefinitions, bool suppressVersionCheck) | ||
399 | { | 323 | { |
400 | if ("wixObject" != reader.LocalName) | 324 | using (var writer = new StreamWriter(wixout.CreateDataStream(WixOutputStreamName))) |
401 | { | 325 | { |
402 | throw new XmlException(); | 326 | var jsonObject = new JsonObject |
403 | } | 327 | { |
404 | 328 | { "id", this.Id }, | |
405 | bool empty = reader.IsEmptyElement; | 329 | { "version", Intermediate.CurrentVersion.ToString() } |
406 | Version objVersion = null; | 330 | }; |
407 | string id = null; | ||
408 | 331 | ||
409 | while (reader.MoveToNextAttribute()) | 332 | var sectionsJson = new JsonArray(this.Sections.Count); |
410 | { | 333 | foreach (var section in this.Sections) |
411 | switch (reader.LocalName) | ||
412 | { | 334 | { |
413 | case "version": | 335 | var sectionJson = section.Serialize(); |
414 | objVersion = new Version(reader.Value); | 336 | sectionsJson.Add(sectionJson); |
415 | break; | ||
416 | case "id": | ||
417 | id = reader.Value; | ||
418 | break; | ||
419 | } | 337 | } |
420 | } | ||
421 | |||
422 | if (!suppressVersionCheck && null != objVersion && !Intermediate.CurrentVersion.Equals(objVersion)) | ||
423 | { | ||
424 | throw new WixException(WixDataErrors.VersionMismatch(SourceLineNumber.CreateFromUri(reader.BaseURI), "object", objVersion.ToString(), Intermediate.CurrentVersion.ToString())); | ||
425 | } | ||
426 | 338 | ||
427 | Intermediate intermediate = new Intermediate(); | 339 | jsonObject.Add("sections", sectionsJson); |
428 | intermediate.id = id; | ||
429 | 340 | ||
430 | if (!empty) | 341 | var customDefinitions = this.GetCustomDefinitionsInSections(); |
431 | { | ||
432 | bool done = false; | ||
433 | 342 | ||
434 | while (!done && reader.Read()) | 343 | if (customDefinitions.Count > 0) |
435 | { | 344 | { |
436 | switch (reader.NodeType) | 345 | var customDefinitionsJson = new JsonArray(customDefinitions.Count); |
346 | |||
347 | foreach (var kvp in customDefinitions.OrderBy(d => d.Key)) | ||
437 | { | 348 | { |
438 | case XmlNodeType.Element: | 349 | var customDefinitionJson = kvp.Value.Serialize(); |
439 | switch (reader.LocalName) | 350 | customDefinitionsJson.Add(customDefinitionJson); |
440 | { | ||
441 | case "section": | ||
442 | intermediate.AddSection(Section.Read(reader, tableDefinitions)); | ||
443 | break; | ||
444 | default: | ||
445 | throw new XmlException(); | ||
446 | } | ||
447 | break; | ||
448 | case XmlNodeType.EndElement: | ||
449 | done = true; | ||
450 | break; | ||
451 | } | 351 | } |
352 | |||
353 | jsonObject.Add("definitions", customDefinitionsJson); | ||
452 | } | 354 | } |
453 | 355 | ||
454 | if (!done) | 356 | if (this.Localizations.Any()) |
455 | { | 357 | { |
456 | throw new XmlException(); | 358 | var localizationsJson = new JsonArray(); |
359 | foreach (var localization in this.Localizations) | ||
360 | { | ||
361 | var localizationJson = localization.Serialize(); | ||
362 | localizationsJson.Add(localizationJson); | ||
363 | } | ||
364 | |||
365 | jsonObject.Add("localizations", localizationsJson); | ||
457 | } | 366 | } |
458 | } | ||
459 | 367 | ||
460 | return intermediate; | 368 | var json = SimpleJson.SerializeObject(jsonObject); |
369 | writer.Write(json); | ||
370 | } | ||
461 | } | 371 | } |
462 | 372 | ||
463 | /// <summary> | 373 | private static string CalculateUniqueEntryName(ISet<string> entryNames, string path) |
464 | /// Persists an intermediate in an XML format. | ||
465 | /// </summary> | ||
466 | /// <param name="writer">XmlWriter where the Intermediate should persist itself as XML.</param> | ||
467 | private void Write(XmlWriter writer) | ||
468 | { | 374 | { |
469 | writer.WriteStartElement("wixObject", XmlNamespaceUri); | 375 | var filename = Path.GetFileName(path); |
470 | 376 | var entryName = "wix-ir/" + filename; | |
471 | writer.WriteAttributeString("version", Intermediate.CurrentVersion.ToString()); | 377 | var i = 0; |
472 | |||
473 | writer.WriteAttributeString("id", this.id); | ||
474 | 378 | ||
475 | foreach (Section section in this.Sections) | 379 | while (!entryNames.Add(entryName)) |
476 | { | 380 | { |
477 | section.Write(writer); | 381 | entryName = $"wix-ir/{filename}-{++i}"; |
478 | } | 382 | } |
479 | 383 | ||
480 | writer.WriteEndElement(); | 384 | return entryName; |
481 | } | 385 | } |
482 | #endif | ||
483 | 386 | ||
484 | private Dictionary<string, IntermediateTupleDefinition> GetCustomDefinitionsInSections() | 387 | private Dictionary<string, IntermediateTupleDefinition> GetCustomDefinitionsInSections() |
485 | { | 388 | { |
diff --git a/src/WixToolset.Data/IntermediateFieldContext.cs b/src/WixToolset.Data/IntermediateFieldContext.cs index a29a63c4..785a959f 100644 --- a/src/WixToolset.Data/IntermediateFieldContext.cs +++ b/src/WixToolset.Data/IntermediateFieldContext.cs | |||
@@ -1,4 +1,4 @@ | |||
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. | 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 | 2 | ||
3 | namespace WixToolset.Data | 3 | namespace WixToolset.Data |
4 | { | 4 | { |
@@ -6,7 +6,7 @@ namespace WixToolset.Data | |||
6 | 6 | ||
7 | public class IntermediateFieldContext : IDisposable | 7 | public class IntermediateFieldContext : IDisposable |
8 | { | 8 | { |
9 | private string previous; | 9 | private readonly string previous; |
10 | private bool disposed; | 10 | private bool disposed; |
11 | 11 | ||
12 | public IntermediateFieldContext(string context) | 12 | public IntermediateFieldContext(string context) |
@@ -18,7 +18,7 @@ namespace WixToolset.Data | |||
18 | 18 | ||
19 | public void Dispose() | 19 | public void Dispose() |
20 | { | 20 | { |
21 | Dispose(true); | 21 | this.Dispose(true); |
22 | GC.SuppressFinalize(this); | 22 | GC.SuppressFinalize(this); |
23 | } | 23 | } |
24 | 24 | ||
diff --git a/src/WixToolset.Data/IntermediateFieldExtensions.cs b/src/WixToolset.Data/IntermediateFieldExtensions.cs index e488866e..06189b9c 100644 --- a/src/WixToolset.Data/IntermediateFieldExtensions.cs +++ b/src/WixToolset.Data/IntermediateFieldExtensions.cs | |||
@@ -386,7 +386,7 @@ namespace WixToolset.Data | |||
386 | 386 | ||
387 | default: | 387 | default: |
388 | throw new ArgumentOutOfRangeException(nameof(value), $"Unknown intermediate field type: {value.GetType()}"); | 388 | throw new ArgumentOutOfRangeException(nameof(value), $"Unknown intermediate field type: {value.GetType()}"); |
389 | }; | 389 | } |
390 | } | 390 | } |
391 | 391 | ||
392 | return AssignFieldValue(field, data); | 392 | return AssignFieldValue(field, data); |
@@ -455,7 +455,7 @@ namespace WixToolset.Data | |||
455 | 455 | ||
456 | default: | 456 | default: |
457 | throw new ArgumentOutOfRangeException(nameof(value), $"Unknown intermediate field type: {value.GetType()}"); | 457 | throw new ArgumentOutOfRangeException(nameof(value), $"Unknown intermediate field type: {value.GetType()}"); |
458 | }; | 458 | } |
459 | } | 459 | } |
460 | 460 | ||
461 | return AssignFieldValue(field, data); | 461 | return AssignFieldValue(field, data); |
diff --git a/src/WixToolset.Data/IntermediateFieldPathValue.cs b/src/WixToolset.Data/IntermediateFieldPathValue.cs index 7d4fcdfe..a8d2735a 100644 --- a/src/WixToolset.Data/IntermediateFieldPathValue.cs +++ b/src/WixToolset.Data/IntermediateFieldPathValue.cs | |||
@@ -1,4 +1,4 @@ | |||
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. | 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 | 2 | ||
3 | namespace WixToolset.Data | 3 | namespace WixToolset.Data |
4 | { | 4 | { |
@@ -7,10 +7,9 @@ namespace WixToolset.Data | |||
7 | public class IntermediateFieldPathValue | 7 | public class IntermediateFieldPathValue |
8 | { | 8 | { |
9 | /// <summary> | 9 | /// <summary> |
10 | /// Gets or sets the index of the embedded file in a library. | 10 | /// Indicates whether to embed the path to the file when the intermediate field is saved. |
11 | /// </summary> | 11 | /// </summary> |
12 | /// <value>The index of the embedded file.</value> | 12 | public bool Embed { get; set; } |
13 | public int? EmbeddedFileIndex { get; set; } | ||
14 | 13 | ||
15 | /// <summary> | 14 | /// <summary> |
16 | /// Gets the base URI of the path field. | 15 | /// Gets the base URI of the path field. |
diff --git a/src/WixToolset.Data/IntermediateFieldValue.cs b/src/WixToolset.Data/IntermediateFieldValue.cs index ca109e7f..9a6b6ef5 100644 --- a/src/WixToolset.Data/IntermediateFieldValue.cs +++ b/src/WixToolset.Data/IntermediateFieldValue.cs | |||
@@ -76,19 +76,19 @@ namespace WixToolset.Data | |||
76 | break; | 76 | break; |
77 | 77 | ||
78 | case JsonObject jsonData: | 78 | case JsonObject jsonData: |
79 | jsonData.TryGetValue("embeddedIndex", out var embeddedIndex); | 79 | jsonData.TryGetValue("embed", out var embed); |
80 | 80 | ||
81 | value = new IntermediateFieldPathValue | 81 | value = new IntermediateFieldPathValue |
82 | { | 82 | { |
83 | BaseUri = (embeddedIndex == null) ? null : baseUri, | 83 | BaseUri = (embed != null) ? baseUri : null, |
84 | EmbeddedFileIndex = (embeddedIndex == null) ? null : (int?)Convert.ToInt32(embeddedIndex), | 84 | Embed = embed != null, |
85 | Path = jsonData.GetValueOrDefault<string>("path"), | 85 | Path = jsonData.GetValueOrDefault<string>("path"), |
86 | }; | 86 | }; |
87 | break; | 87 | break; |
88 | 88 | ||
89 | // Nothing to do for this case, so leave it out. | 89 | // Nothing to do for this case, so leave it out. |
90 | // case string stringData: | 90 | // case string stringData: |
91 | // break; | 91 | // break; |
92 | } | 92 | } |
93 | 93 | ||
94 | var previousValueJson = jsonObject.GetValueOrDefault<JsonObject>("prev"); | 94 | var previousValueJson = jsonObject.GetValueOrDefault<JsonObject>("prev"); |
@@ -117,9 +117,9 @@ namespace WixToolset.Data | |||
117 | 117 | ||
118 | // pathField.BaseUri is set during load, not saved. | 118 | // pathField.BaseUri is set during load, not saved. |
119 | 119 | ||
120 | if (pathField.EmbeddedFileIndex.HasValue) | 120 | if (pathField.Embed) |
121 | { | 121 | { |
122 | jsonData.Add("embeddedIndex", pathField.EmbeddedFileIndex.Value); | 122 | jsonData.Add("embed", "true"); |
123 | } | 123 | } |
124 | 124 | ||
125 | if (!String.IsNullOrEmpty(pathField.Path)) | 125 | if (!String.IsNullOrEmpty(pathField.Path)) |
diff --git a/src/WixToolset.Data/NonClosingStreamWrapper.cs b/src/WixToolset.Data/NonClosingStreamWrapper.cs deleted file mode 100644 index a2d3be6a..00000000 --- a/src/WixToolset.Data/NonClosingStreamWrapper.cs +++ /dev/null | |||
@@ -1,84 +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.IO; | ||
7 | |||
8 | /// <summary> | ||
9 | /// Wrapper around stream to prevent other streams (like BinaryReader/Writer) from prematurely | ||
10 | /// closing a parent stream. | ||
11 | /// </summary> | ||
12 | internal class NonClosingStreamWrapper : Stream | ||
13 | { | ||
14 | private Stream stream; | ||
15 | |||
16 | public NonClosingStreamWrapper(Stream stream) | ||
17 | { | ||
18 | this.stream = stream; | ||
19 | } | ||
20 | |||
21 | public override bool CanRead => this.stream.CanRead; | ||
22 | |||
23 | public override bool CanSeek => this.stream.CanSeek; | ||
24 | |||
25 | public override bool CanTimeout => this.stream.CanTimeout; | ||
26 | |||
27 | public override bool CanWrite => this.stream.CanWrite; | ||
28 | |||
29 | public override long Length => this.stream.Length; | ||
30 | |||
31 | public override long Position | ||
32 | { | ||
33 | get => this.stream.Position; | ||
34 | set => this.stream.Position = value; | ||
35 | } | ||
36 | |||
37 | public override int ReadTimeout | ||
38 | { | ||
39 | get => this.stream.ReadTimeout; | ||
40 | set => this.stream.ReadTimeout = value; | ||
41 | } | ||
42 | |||
43 | public override int WriteTimeout | ||
44 | { | ||
45 | get => this.stream.WriteTimeout; | ||
46 | set => this.stream.WriteTimeout = value; | ||
47 | } | ||
48 | |||
49 | public override IAsyncResult BeginRead(byte[] buffer, int offset, int count, AsyncCallback callback, object state) => this.stream.BeginRead(buffer, offset, count, callback, state); | ||
50 | |||
51 | public override IAsyncResult BeginWrite(byte[] buffer, int offset, int count, AsyncCallback callback, object state) => this.stream.BeginWrite(buffer, offset, count, callback, state); | ||
52 | |||
53 | public override int EndRead(IAsyncResult asyncResult) => this.stream.EndRead(asyncResult); | ||
54 | |||
55 | public override void EndWrite(IAsyncResult asyncResult) => this.stream.EndWrite(asyncResult); | ||
56 | |||
57 | public override void Flush() => this.stream.Flush(); | ||
58 | |||
59 | public override int Read(byte[] buffer, int offset, int count) => this.stream.Read(buffer, offset, count); | ||
60 | |||
61 | public override int ReadByte() => this.stream.ReadByte(); | ||
62 | |||
63 | public override long Seek(long offset, SeekOrigin origin) => this.stream.Seek(offset, origin); | ||
64 | |||
65 | public override void SetLength(long value) => this.stream.SetLength(value); | ||
66 | |||
67 | public override void Write(byte[] buffer, int offset, int count) => this.stream.Write(buffer, offset, count); | ||
68 | |||
69 | public override void WriteByte(byte value) => this.stream.WriteByte(value); | ||
70 | |||
71 | public override void Close() | ||
72 | { | ||
73 | // Do not pass through the call since this is what we are overriding. | ||
74 | } | ||
75 | |||
76 | protected override void Dispose(bool disposing) | ||
77 | { | ||
78 | if (disposing) | ||
79 | { | ||
80 | this.stream.Flush(); | ||
81 | } | ||
82 | } | ||
83 | } | ||
84 | } | ||
diff --git a/src/WixToolset.Data/WindowsInstaller/Field.cs b/src/WixToolset.Data/WindowsInstaller/Field.cs index aa359c64..ac8df9a4 100644 --- a/src/WixToolset.Data/WindowsInstaller/Field.cs +++ b/src/WixToolset.Data/WindowsInstaller/Field.cs | |||
@@ -154,7 +154,7 @@ namespace WixToolset.Data.WindowsInstaller | |||
154 | // be enhanced if that ever changes. | 154 | // be enhanced if that ever changes. |
155 | if (value is int || value.GetType().IsEnum) | 155 | if (value is int || value.GetType().IsEnum) |
156 | { | 156 | { |
157 | int intValue = (int)value; | 157 | var intValue = (int)value; |
158 | 158 | ||
159 | // validate the value against the minimum allowed value | 159 | // validate the value against the minimum allowed value |
160 | if (column.MinValue.HasValue && column.MinValue > intValue) | 160 | if (column.MinValue.HasValue && column.MinValue > intValue) |
@@ -275,7 +275,7 @@ namespace WixToolset.Data.WindowsInstaller | |||
275 | /// <param name="writer">XmlWriter where the Field should persist itself as XML.</param> | 275 | /// <param name="writer">XmlWriter where the Field should persist itself as XML.</param> |
276 | internal virtual void Write(XmlWriter writer) | 276 | internal virtual void Write(XmlWriter writer) |
277 | { | 277 | { |
278 | writer.WriteStartElement("field", Intermediate.XmlNamespaceUri); | 278 | writer.WriteStartElement("field", Output.XmlNamespaceUri); |
279 | 279 | ||
280 | if (this.Modified) | 280 | if (this.Modified) |
281 | { | 281 | { |
diff --git a/src/WixToolset.Data/WindowsInstaller/ObjectField.cs b/src/WixToolset.Data/WindowsInstaller/ObjectField.cs index 016693f5..4e654dde 100644 --- a/src/WixToolset.Data/WindowsInstaller/ObjectField.cs +++ b/src/WixToolset.Data/WindowsInstaller/ObjectField.cs | |||
@@ -130,7 +130,7 @@ namespace WixToolset.Data.WindowsInstaller | |||
130 | /// <param name="writer">XmlWriter where the Field should persist itself as XML.</param> | 130 | /// <param name="writer">XmlWriter where the Field should persist itself as XML.</param> |
131 | internal override void Write(XmlWriter writer) | 131 | internal override void Write(XmlWriter writer) |
132 | { | 132 | { |
133 | writer.WriteStartElement("field", Intermediate.XmlNamespaceUri); | 133 | writer.WriteStartElement("field", Output.XmlNamespaceUri); |
134 | 134 | ||
135 | if (this.EmbeddedFileIndex.HasValue) | 135 | if (this.EmbeddedFileIndex.HasValue) |
136 | { | 136 | { |
diff --git a/src/WixToolset.Data/WindowsInstaller/Output.cs b/src/WixToolset.Data/WindowsInstaller/Output.cs index 7f2990f4..ee46c159 100644 --- a/src/WixToolset.Data/WindowsInstaller/Output.cs +++ b/src/WixToolset.Data/WindowsInstaller/Output.cs | |||
@@ -5,7 +5,6 @@ namespace WixToolset.Data.WindowsInstaller | |||
5 | using System; | 5 | using System; |
6 | using System.Collections.Generic; | 6 | using System.Collections.Generic; |
7 | using System.Globalization; | 7 | using System.Globalization; |
8 | using System.IO; | ||
9 | using System.Linq; | 8 | using System.Linq; |
10 | using System.Xml; | 9 | using System.Xml; |
11 | 10 | ||
@@ -16,6 +15,7 @@ namespace WixToolset.Data.WindowsInstaller | |||
16 | { | 15 | { |
17 | public const string XmlNamespaceUri = "http://wixtoolset.org/schemas/v4/wixout"; | 16 | public const string XmlNamespaceUri = "http://wixtoolset.org/schemas/v4/wixout"; |
18 | private static readonly Version CurrentVersion = new Version("4.0.0.0"); | 17 | private static readonly Version CurrentVersion = new Version("4.0.0.0"); |
18 | private const string WixOutputStreamName = "wix-wi.xml"; | ||
19 | 19 | ||
20 | /// <summary> | 20 | /// <summary> |
21 | /// Creates a new empty output object. | 21 | /// Creates a new empty output object. |
@@ -66,41 +66,29 @@ namespace WixToolset.Data.WindowsInstaller | |||
66 | /// <returns>Output object.</returns> | 66 | /// <returns>Output object.</returns> |
67 | public static Output Load(string path, bool suppressVersionCheck) | 67 | public static Output Load(string path, bool suppressVersionCheck) |
68 | { | 68 | { |
69 | using (FileStream stream = File.OpenRead(path)) | 69 | using (var wixout = WixOutput.Read(path)) |
70 | using (FileStructure fs = FileStructure.Read(stream)) | 70 | using (var stream = wixout.GetDataStream(WixOutputStreamName)) |
71 | using (var reader = XmlReader.Create(stream, null, wixout.Uri.AbsoluteUri)) | ||
71 | { | 72 | { |
72 | if (FileFormat.Wixout != fs.FileFormat) | 73 | try |
73 | { | 74 | { |
74 | throw new WixUnexpectedFileFormatException(path, FileFormat.Wixout, fs.FileFormat); | 75 | reader.MoveToContent(); |
76 | return Output.Read(reader, suppressVersionCheck); | ||
75 | } | 77 | } |
76 | 78 | catch (XmlException xe) | |
77 | Uri uri = new Uri(Path.GetFullPath(path)); | ||
78 | using (XmlReader reader = XmlReader.Create(fs.GetDataStream(), null, uri.AbsoluteUri)) | ||
79 | { | 79 | { |
80 | try | 80 | throw new WixCorruptFileException(path, "wixout", xe); |
81 | { | ||
82 | reader.MoveToContent(); | ||
83 | return Output.Read(reader, suppressVersionCheck); | ||
84 | } | ||
85 | catch (XmlException xe) | ||
86 | { | ||
87 | throw new WixCorruptFileException(path, fs.FileFormat, xe); | ||
88 | } | ||
89 | } | 81 | } |
90 | } | 82 | } |
91 | } | 83 | } |
92 | 84 | ||
93 | /// <summary> | 85 | /// <summary> |
94 | /// Saves an output to a path on disk. | 86 | /// Saves an output to a <c>WixOutput</c> container. |
95 | /// </summary> | 87 | /// </summary> |
96 | /// <param name="path">Path to save output file to on disk.</param> | 88 | /// <param name="wixout">Container to save to.</param> |
97 | public void Save(string path) | 89 | public void Save(WixOutput wixout) |
98 | { | 90 | { |
99 | Directory.CreateDirectory(Path.GetDirectoryName(Path.GetFullPath(path))); | 91 | using (var writer = XmlWriter.Create(wixout.CreateDataStream(WixOutputStreamName))) |
100 | |||
101 | using (FileStream stream = File.Create(path)) | ||
102 | using (FileStructure fs = FileStructure.Create(stream, FileFormat.Wixout, null)) | ||
103 | using (XmlWriter writer = XmlWriter.Create(fs.GetDataStream())) | ||
104 | { | 92 | { |
105 | writer.WriteStartDocument(); | 93 | writer.WriteStartDocument(); |
106 | this.Write(writer); | 94 | this.Write(writer); |
@@ -121,8 +109,8 @@ namespace WixToolset.Data.WindowsInstaller | |||
121 | throw new XmlException(); | 109 | throw new XmlException(); |
122 | } | 110 | } |
123 | 111 | ||
124 | bool empty = reader.IsEmptyElement; | 112 | var empty = reader.IsEmptyElement; |
125 | Output output = new Output(SourceLineNumber.CreateFromUri(reader.BaseURI)); | 113 | var output = new Output(SourceLineNumber.CreateFromUri(reader.BaseURI)); |
126 | Version version = null; | 114 | Version version = null; |
127 | 115 | ||
128 | while (reader.MoveToNextAttribute()) | 116 | while (reader.MoveToNextAttribute()) |
@@ -170,10 +158,10 @@ namespace WixToolset.Data.WindowsInstaller | |||
170 | 158 | ||
171 | // loop through the rest of the xml building up the Output object | 159 | // loop through the rest of the xml building up the Output object |
172 | TableDefinitionCollection tableDefinitions = null; | 160 | TableDefinitionCollection tableDefinitions = null; |
173 | List<Table> tables = new List<Table>(); | 161 | var tables = new List<Table>(); |
174 | if (!empty) | 162 | if (!empty) |
175 | { | 163 | { |
176 | bool done = false; | 164 | var done = false; |
177 | 165 | ||
178 | // loop through all the fields in a row | 166 | // loop through all the fields in a row |
179 | while (!done && reader.Read()) | 167 | while (!done && reader.Read()) |
@@ -224,7 +212,7 @@ namespace WixToolset.Data.WindowsInstaller | |||
224 | /// <returns>The table in this output.</returns> | 212 | /// <returns>The table in this output.</returns> |
225 | public Table EnsureTable(TableDefinition tableDefinition) | 213 | public Table EnsureTable(TableDefinition tableDefinition) |
226 | { | 214 | { |
227 | if (!this.Tables.TryGetTable(tableDefinition.Name, out Table table)) | 215 | if (!this.Tables.TryGetTable(tableDefinition.Name, out var table)) |
228 | { | 216 | { |
229 | table = new Table(tableDefinition); | 217 | table = new Table(tableDefinition); |
230 | this.Tables.Add(table); | 218 | this.Tables.Add(table); |
@@ -251,19 +239,19 @@ namespace WixToolset.Data.WindowsInstaller | |||
251 | writer.WriteAttributeString("version", Output.CurrentVersion.ToString()); | 239 | writer.WriteAttributeString("version", Output.CurrentVersion.ToString()); |
252 | 240 | ||
253 | // Collect all the table definitions and write them. | 241 | // Collect all the table definitions and write them. |
254 | TableDefinitionCollection tableDefinitions = new TableDefinitionCollection(); | 242 | var tableDefinitions = new TableDefinitionCollection(); |
255 | foreach (Table table in this.Tables) | 243 | foreach (var table in this.Tables) |
256 | { | 244 | { |
257 | tableDefinitions.Add(table.Definition); | 245 | tableDefinitions.Add(table.Definition); |
258 | } | 246 | } |
259 | tableDefinitions.Write(writer); | 247 | tableDefinitions.Write(writer); |
260 | 248 | ||
261 | foreach (Table table in this.Tables.OrderBy(t => t.Name)) | 249 | foreach (var table in this.Tables.OrderBy(t => t.Name)) |
262 | { | 250 | { |
263 | table.Write(writer); | 251 | table.Write(writer); |
264 | } | 252 | } |
265 | 253 | ||
266 | foreach (SubStorage subStorage in this.SubStorages) | 254 | foreach (var subStorage in this.SubStorages) |
267 | { | 255 | { |
268 | subStorage.Write(writer); | 256 | subStorage.Write(writer); |
269 | } | 257 | } |
diff --git a/src/WixToolset.Data/WindowsInstaller/Pdb.cs b/src/WixToolset.Data/WindowsInstaller/Pdb.cs deleted file mode 100644 index 574d5593..00000000 --- a/src/WixToolset.Data/WindowsInstaller/Pdb.cs +++ /dev/null | |||
@@ -1,155 +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.WindowsInstaller | ||
4 | { | ||
5 | using System; | ||
6 | using System.IO; | ||
7 | using System.Xml; | ||
8 | |||
9 | /// <summary> | ||
10 | /// Pdb generated by the binder. | ||
11 | /// </summary> | ||
12 | public sealed class Pdb | ||
13 | { | ||
14 | public const string XmlNamespaceUri = "http://wixtoolset.org/schemas/v4/wixpdb"; | ||
15 | private static readonly Version CurrentVersion = new Version("4.0.0.0"); | ||
16 | |||
17 | /// <summary> | ||
18 | /// Gets or sets the output that is a part of this pdb. | ||
19 | /// </summary> | ||
20 | /// <value>Type of the output.</value> | ||
21 | public Output Output { get; set; } | ||
22 | |||
23 | /// <summary> | ||
24 | /// Loads a pdb from a path on disk. | ||
25 | /// </summary> | ||
26 | /// <param name="path">Path to pdb file saved on disk.</param> | ||
27 | /// <param name="suppressVersionCheck">Suppresses wix.dll version mismatch check.</param> | ||
28 | /// <returns>Pdb pdb.</returns> | ||
29 | public static Pdb Load(string path, bool suppressVersionCheck) | ||
30 | { | ||
31 | using (FileStream stream = File.OpenRead(path)) | ||
32 | using (FileStructure fs = FileStructure.Read(stream)) | ||
33 | { | ||
34 | if (FileFormat.Wixpdb != fs.FileFormat) | ||
35 | { | ||
36 | throw new WixUnexpectedFileFormatException(path, FileFormat.Wixpdb, fs.FileFormat); | ||
37 | } | ||
38 | |||
39 | Uri uri = new Uri(Path.GetFullPath(path)); | ||
40 | using (XmlReader reader = XmlReader.Create(fs.GetDataStream(), null, uri.AbsoluteUri)) | ||
41 | { | ||
42 | try | ||
43 | { | ||
44 | reader.MoveToContent(); | ||
45 | return Pdb.Read(reader, suppressVersionCheck); | ||
46 | } | ||
47 | catch (XmlException xe) | ||
48 | { | ||
49 | throw new WixCorruptFileException(path, fs.FileFormat, xe); | ||
50 | } | ||
51 | } | ||
52 | } | ||
53 | } | ||
54 | |||
55 | /// <summary> | ||
56 | /// Saves a pdb to a path on disk. | ||
57 | /// </summary> | ||
58 | /// <param name="path">Path to save pdb file to on disk.</param> | ||
59 | public void Save(string path) | ||
60 | { | ||
61 | Directory.CreateDirectory(Path.GetDirectoryName(Path.GetFullPath(path))); | ||
62 | |||
63 | using (FileStream stream = File.Create(path)) | ||
64 | using (FileStructure fs = FileStructure.Create(stream, FileFormat.Wixpdb, null)) | ||
65 | using (XmlWriter writer = XmlWriter.Create(fs.GetDataStream())) | ||
66 | { | ||
67 | writer.WriteStartDocument(); | ||
68 | this.Write(writer); | ||
69 | writer.WriteEndDocument(); | ||
70 | } | ||
71 | } | ||
72 | |||
73 | /// <summary> | ||
74 | /// Processes an XmlReader and builds up the pdb object. | ||
75 | /// </summary> | ||
76 | /// <param name="reader">Reader to get data from.</param> | ||
77 | /// <param name="suppressVersionCheck">Suppresses wix.dll version mismatch check.</param> | ||
78 | /// <returns>The Pdb represented by the Xml.</returns> | ||
79 | internal static Pdb Read(XmlReader reader, bool suppressVersionCheck) | ||
80 | { | ||
81 | if ("wixPdb" != reader.LocalName) | ||
82 | { | ||
83 | throw new XmlException(); | ||
84 | } | ||
85 | |||
86 | bool empty = reader.IsEmptyElement; | ||
87 | Pdb pdb = new Pdb(); | ||
88 | Version version = null; | ||
89 | |||
90 | while (reader.MoveToNextAttribute()) | ||
91 | { | ||
92 | switch (reader.LocalName) | ||
93 | { | ||
94 | case "version": | ||
95 | version = new Version(reader.Value); | ||
96 | break; | ||
97 | } | ||
98 | } | ||
99 | |||
100 | if (!suppressVersionCheck && null != version && !Pdb.CurrentVersion.Equals(version)) | ||
101 | { | ||
102 | throw new WixException(ErrorMessages.VersionMismatch(SourceLineNumber.CreateFromUri(reader.BaseURI), "wixPdb", version.ToString(), Pdb.CurrentVersion.ToString())); | ||
103 | } | ||
104 | |||
105 | // loop through the rest of the pdb building up the Output object | ||
106 | if (!empty) | ||
107 | { | ||
108 | bool done = false; | ||
109 | |||
110 | // loop through all the fields in a row | ||
111 | while (!done && reader.Read()) | ||
112 | { | ||
113 | switch (reader.NodeType) | ||
114 | { | ||
115 | case XmlNodeType.Element: | ||
116 | switch (reader.LocalName) | ||
117 | { | ||
118 | case "wixOutput": | ||
119 | pdb.Output = Output.Read(reader, suppressVersionCheck); | ||
120 | break; | ||
121 | default: | ||
122 | throw new XmlException(); | ||
123 | } | ||
124 | break; | ||
125 | case XmlNodeType.EndElement: | ||
126 | done = true; | ||
127 | break; | ||
128 | } | ||
129 | } | ||
130 | |||
131 | if (!done) | ||
132 | { | ||
133 | throw new XmlException(); | ||
134 | } | ||
135 | } | ||
136 | |||
137 | return pdb; | ||
138 | } | ||
139 | |||
140 | /// <summary> | ||
141 | /// Persists a pdb in an XML format. | ||
142 | /// </summary> | ||
143 | /// <param name="writer">XmlWriter where the Pdb should persist itself as XML.</param> | ||
144 | internal void Write(XmlWriter writer) | ||
145 | { | ||
146 | writer.WriteStartElement("wixPdb", XmlNamespaceUri); | ||
147 | |||
148 | writer.WriteAttributeString("version", Pdb.CurrentVersion.ToString()); | ||
149 | |||
150 | this.Output.Write(writer); | ||
151 | |||
152 | writer.WriteEndElement(); | ||
153 | } | ||
154 | } | ||
155 | } | ||
diff --git a/src/WixToolset.Data/WindowsInstaller/Row.cs b/src/WixToolset.Data/WindowsInstaller/Row.cs index af1af628..a267f04b 100644 --- a/src/WixToolset.Data/WindowsInstaller/Row.cs +++ b/src/WixToolset.Data/WindowsInstaller/Row.cs | |||
@@ -354,7 +354,7 @@ namespace WixToolset.Data.WindowsInstaller | |||
354 | /// <param name="writer">XmlWriter where the Row should persist itself as XML.</param> | 354 | /// <param name="writer">XmlWriter where the Row should persist itself as XML.</param> |
355 | internal void Write(XmlWriter writer) | 355 | internal void Write(XmlWriter writer) |
356 | { | 356 | { |
357 | writer.WriteStartElement("row", Intermediate.XmlNamespaceUri); | 357 | writer.WriteStartElement("row", Output.XmlNamespaceUri); |
358 | 358 | ||
359 | if (RowOperation.None != this.Operation) | 359 | if (RowOperation.None != this.Operation) |
360 | { | 360 | { |
diff --git a/src/WixToolset.Data/WindowsInstaller/Table.cs b/src/WixToolset.Data/WindowsInstaller/Table.cs index acb4b6fe..c7f2c8f2 100644 --- a/src/WixToolset.Data/WindowsInstaller/Table.cs +++ b/src/WixToolset.Data/WindowsInstaller/Table.cs | |||
@@ -291,7 +291,7 @@ namespace WixToolset.Data.WindowsInstaller | |||
291 | throw new ArgumentNullException("writer"); | 291 | throw new ArgumentNullException("writer"); |
292 | } | 292 | } |
293 | 293 | ||
294 | writer.WriteStartElement("table", Intermediate.XmlNamespaceUri); | 294 | writer.WriteStartElement("table", Output.XmlNamespaceUri); |
295 | writer.WriteAttributeString("name", this.Name); | 295 | writer.WriteAttributeString("name", this.Name); |
296 | 296 | ||
297 | if (TableOperation.None != this.Operation) | 297 | if (TableOperation.None != this.Operation) |
diff --git a/src/WixToolset.Data/WindowsInstaller/TableDefinitionCollection.cs b/src/WixToolset.Data/WindowsInstaller/TableDefinitionCollection.cs index 619a5206..80303913 100644 --- a/src/WixToolset.Data/WindowsInstaller/TableDefinitionCollection.cs +++ b/src/WixToolset.Data/WindowsInstaller/TableDefinitionCollection.cs | |||
@@ -227,7 +227,7 @@ namespace WixToolset.Data.WindowsInstaller | |||
227 | { | 227 | { |
228 | writer.WriteStartElement("tableDefinitions", XmlNamespaceUri); | 228 | writer.WriteStartElement("tableDefinitions", XmlNamespaceUri); |
229 | 229 | ||
230 | foreach (TableDefinition tableDefinition in this.collection.Values.OrderBy(t => t.Name)) | 230 | foreach (var tableDefinition in this.collection.Values.OrderBy(t => t.Name)) |
231 | { | 231 | { |
232 | tableDefinition.Write(writer); | 232 | tableDefinition.Write(writer); |
233 | } | 233 | } |
diff --git a/src/WixToolset.Data/WixCorruptFileException.cs b/src/WixToolset.Data/WixCorruptFileException.cs index f77f0d8a..83c3eb80 100644 --- a/src/WixToolset.Data/WixCorruptFileException.cs +++ b/src/WixToolset.Data/WixCorruptFileException.cs | |||
@@ -9,7 +9,7 @@ namespace WixToolset.Data | |||
9 | /// </summary> | 9 | /// </summary> |
10 | public class WixCorruptFileException : WixException | 10 | public class WixCorruptFileException : WixException |
11 | { | 11 | { |
12 | public WixCorruptFileException(string path, FileFormat format, Exception innerException = null) | 12 | public WixCorruptFileException(string path, string format, Exception innerException = null) |
13 | : base(ErrorMessages.CorruptFileFormat(path, format), innerException) | 13 | : base(ErrorMessages.CorruptFileFormat(path, format), innerException) |
14 | { | 14 | { |
15 | this.Path = path; | 15 | this.Path = path; |
@@ -19,7 +19,7 @@ namespace WixToolset.Data | |||
19 | /// <summary> | 19 | /// <summary> |
20 | /// Gets the actual file format found in the file. | 20 | /// Gets the actual file format found in the file. |
21 | /// </summary> | 21 | /// </summary> |
22 | public FileFormat FileFormat { get; } | 22 | public string FileFormat { get; } |
23 | 23 | ||
24 | /// <summary> | 24 | /// <summary> |
25 | /// Gets the path to the file with unexpected format. | 25 | /// Gets the path to the file with unexpected format. |
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 | |||
3 | namespace 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 | } | ||
diff --git a/src/WixToolset.Data/WixToolset.Data.csproj b/src/WixToolset.Data/WixToolset.Data.csproj index 95f16c66..0a7efe60 100644 --- a/src/WixToolset.Data/WixToolset.Data.csproj +++ b/src/WixToolset.Data/WixToolset.Data.csproj | |||
@@ -12,6 +12,10 @@ | |||
12 | </PropertyGroup> | 12 | </PropertyGroup> |
13 | 13 | ||
14 | <ItemGroup> | 14 | <ItemGroup> |
15 | <PackageReference Include="System.IO.Compression" Version="4.3.0" /> | ||
16 | </ItemGroup> | ||
17 | |||
18 | <ItemGroup> | ||
15 | <PackageReference Include="Microsoft.SourceLink.GitHub" Version="1.0.0-beta2-18618-05" PrivateAssets="All" /> | 19 | <PackageReference Include="Microsoft.SourceLink.GitHub" Version="1.0.0-beta2-18618-05" PrivateAssets="All" /> |
16 | <PackageReference Include="Nerdbank.GitVersioning" Version="2.1.65" PrivateAssets="all" /> | 20 | <PackageReference Include="Nerdbank.GitVersioning" Version="2.1.65" PrivateAssets="all" /> |
17 | </ItemGroup> | 21 | </ItemGroup> |
diff --git a/src/WixToolset.Data/WixUnexpectedFileFormatException.cs b/src/WixToolset.Data/WixUnexpectedFileFormatException.cs index 340fb4d7..4bd6ba4a 100644 --- a/src/WixToolset.Data/WixUnexpectedFileFormatException.cs +++ b/src/WixToolset.Data/WixUnexpectedFileFormatException.cs | |||
@@ -9,7 +9,7 @@ namespace WixToolset.Data | |||
9 | /// </summary> | 9 | /// </summary> |
10 | public class WixUnexpectedFileFormatException : WixException | 10 | public class WixUnexpectedFileFormatException : WixException |
11 | { | 11 | { |
12 | public WixUnexpectedFileFormatException(string path, FileFormat expectedFormat, FileFormat format, Exception innerException = null) | 12 | public WixUnexpectedFileFormatException(string path, string expectedFormat, string format, Exception innerException = null) |
13 | : base(ErrorMessages.UnexpectedFileFormat(path, expectedFormat, format), innerException) | 13 | : base(ErrorMessages.UnexpectedFileFormat(path, expectedFormat, format), innerException) |
14 | { | 14 | { |
15 | this.Path = path; | 15 | this.Path = path; |
@@ -20,12 +20,12 @@ namespace WixToolset.Data | |||
20 | /// <summary> | 20 | /// <summary> |
21 | /// Gets the expected file format. | 21 | /// Gets the expected file format. |
22 | /// </summary> | 22 | /// </summary> |
23 | public FileFormat ExpectedFileFormat { get; } | 23 | public string ExpectedFileFormat { get; } |
24 | 24 | ||
25 | /// <summary> | 25 | /// <summary> |
26 | /// Gets the actual file format found in the file. | 26 | /// Gets the actual file format found in the file. |
27 | /// </summary> | 27 | /// </summary> |
28 | public FileFormat FileFormat { get; } | 28 | public string FileFormat { get; } |
29 | 29 | ||
30 | /// <summary> | 30 | /// <summary> |
31 | /// Gets the path to the file with unexpected format. | 31 | /// Gets the path to the file with unexpected format. |
diff --git a/src/test/WixToolsetTest.Data/SerializeFixture.cs b/src/test/WixToolsetTest.Data/SerializeFixture.cs index 221ca19c..34e50f36 100644 --- a/src/test/WixToolsetTest.Data/SerializeFixture.cs +++ b/src/test/WixToolsetTest.Data/SerializeFixture.cs | |||
@@ -26,7 +26,7 @@ namespace WixToolsetTest.Data | |||
26 | Location = ComponentLocation.Either, | 26 | Location = ComponentLocation.Either, |
27 | }); | 27 | }); |
28 | 28 | ||
29 | var intermediate = new Intermediate("TestIntermediate", new[] { section }, null, null); | 29 | var intermediate = new Intermediate("TestIntermediate", new[] { section }, null); |
30 | 30 | ||
31 | var path = Path.GetTempFileName(); | 31 | var path = Path.GetTempFileName(); |
32 | intermediate.Save(path); | 32 | intermediate.Save(path); |
@@ -64,7 +64,7 @@ namespace WixToolsetTest.Data | |||
64 | 64 | ||
65 | section.Tuples.Add(tuple); | 65 | section.Tuples.Add(tuple); |
66 | 66 | ||
67 | var intermediate = new Intermediate("TestIntermediate", new[] { section }, null, null); | 67 | var intermediate = new Intermediate("TestIntermediate", new[] { section }, null); |
68 | 68 | ||
69 | var path = Path.GetTempFileName(); | 69 | var path = Path.GetTempFileName(); |
70 | try | 70 | try |
@@ -108,7 +108,7 @@ namespace WixToolsetTest.Data | |||
108 | var section = new IntermediateSection("test", SectionType.Product, 65001); | 108 | var section = new IntermediateSection("test", SectionType.Product, 65001); |
109 | section.Tuples.Add(tuple); | 109 | section.Tuples.Add(tuple); |
110 | 110 | ||
111 | var intermediate1 = new Intermediate("TestIntermediate", new[] { section }, null, null); | 111 | var intermediate1 = new Intermediate("TestIntermediate", new[] { section }, null); |
112 | 112 | ||
113 | // Intermediate #2 | 113 | // Intermediate #2 |
114 | var fieldDefs2 = new[] | 114 | var fieldDefs2 = new[] |
@@ -130,7 +130,7 @@ namespace WixToolsetTest.Data | |||
130 | var section2 = new IntermediateSection("test2", SectionType.Fragment, 65001); | 130 | var section2 = new IntermediateSection("test2", SectionType.Fragment, 65001); |
131 | section2.Tuples.Add(tuple2); | 131 | section2.Tuples.Add(tuple2); |
132 | 132 | ||
133 | var intermediate2 = new Intermediate("TestIntermediate2", new[] { section2 }, null, null); | 133 | var intermediate2 = new Intermediate("TestIntermediate2", new[] { section2 }, null); |
134 | 134 | ||
135 | // Save | 135 | // Save |
136 | var path1 = Path.GetTempFileName(); | 136 | var path1 = Path.GetTempFileName(); |
@@ -191,7 +191,7 @@ namespace WixToolsetTest.Data | |||
191 | var section = new IntermediateSection("test", SectionType.Product, 65001); | 191 | var section = new IntermediateSection("test", SectionType.Product, 65001); |
192 | section.Tuples.Add(tuple); | 192 | section.Tuples.Add(tuple); |
193 | 193 | ||
194 | var intermediate1 = new Intermediate("TestIntermediate", new[] { section }, null, null); | 194 | var intermediate1 = new Intermediate("TestIntermediate", new[] { section }, null); |
195 | 195 | ||
196 | // Intermediate #2 | 196 | // Intermediate #2 |
197 | var fieldDefs2 = new[] | 197 | var fieldDefs2 = new[] |
@@ -219,7 +219,7 @@ namespace WixToolsetTest.Data | |||
219 | var section2 = new IntermediateSection("test2", SectionType.Fragment, 65001); | 219 | var section2 = new IntermediateSection("test2", SectionType.Fragment, 65001); |
220 | section2.Tuples.Add(tuple2); | 220 | section2.Tuples.Add(tuple2); |
221 | 221 | ||
222 | var intermediate2 = new Intermediate("TestIntermediate2", new[] { section2 }, null, null); | 222 | var intermediate2 = new Intermediate("TestIntermediate2", new[] { section2 }, null); |
223 | 223 | ||
224 | // Save | 224 | // Save |
225 | var path1 = Path.GetTempFileName(); | 225 | var path1 = Path.GetTempFileName(); |
@@ -290,7 +290,7 @@ namespace WixToolsetTest.Data | |||
290 | Location = ComponentLocation.Either, | 290 | Location = ComponentLocation.Either, |
291 | }); | 291 | }); |
292 | 292 | ||
293 | var intermediate = new Intermediate("TestIntermediate", new[] { section }, localizations.ToDictionary(l => l.Culture), null); | 293 | var intermediate = new Intermediate("TestIntermediate", new[] { section }, localizations.ToDictionary(l => l.Culture)); |
294 | 294 | ||
295 | var path = Path.GetTempFileName(); | 295 | var path = Path.GetTempFileName(); |
296 | try | 296 | try |