Skip to content

Commit a189726

Browse files
committed
Prototype appx generation code
With optional use of external makeappx (current issue with deflate algo is blocking this from being lifted)
1 parent d22cb12 commit a189726

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

44 files changed

+2218
-2
lines changed

MobilePackageGen.sln

Lines changed: 40 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11

22
Microsoft Visual Studio Solution File, Format Version 12.00
3-
# Visual Studio Version 17
4-
VisualStudioVersion = 17.6.33626.354
3+
# Visual Studio Version 18
4+
VisualStudioVersion = 18.3.11218.70 d18.3
55
MinimumVisualStudioVersion = 10.0.40219.1
66
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CbsToReg", "src\Applications\CbsToReg\CbsToReg.csproj", "{8C0A8B8C-F58A-4F08-88CA-47521AE6CA21}"
77
EndProject
@@ -39,6 +39,10 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WPBuildInfo", "src\Applicat
3939
EndProject
4040
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "QualcommEDLProgramStream", "thirdparty\EDLProgram2VHDX\QualcommEDLProgramStream\QualcommEDLProgramStream.csproj", "{F3B32230-C960-DE70-DD4A-55B953758C98}"
4141
EndProject
42+
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DotnetPackaging.Msix", "thirdparty\DotnetPackaging.Msix\DotnetPackaging.Msix.csproj", "{0353F2BA-12FA-AA17-BE2C-3D961E06AB6E}"
43+
EndProject
44+
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DeflateBlockCompressor", "thirdparty\DeflateBlockCompressor\DeflateBlockCompressor.csproj", "{E2F66420-4219-4841-4225-629C51CF175F}"
45+
EndProject
4246
Global
4347
GlobalSection(SolutionConfigurationPlatforms) = preSolution
4448
Debug|ARM32 = Debug|ARM32
@@ -323,6 +327,38 @@ Global
323327
{F3B32230-C960-DE70-DD4A-55B953758C98}.Release|x64.Build.0 = Release|Any CPU
324328
{F3B32230-C960-DE70-DD4A-55B953758C98}.Release|x86.ActiveCfg = Release|Any CPU
325329
{F3B32230-C960-DE70-DD4A-55B953758C98}.Release|x86.Build.0 = Release|Any CPU
330+
{0353F2BA-12FA-AA17-BE2C-3D961E06AB6E}.Debug|ARM32.ActiveCfg = Debug|Any CPU
331+
{0353F2BA-12FA-AA17-BE2C-3D961E06AB6E}.Debug|ARM32.Build.0 = Debug|Any CPU
332+
{0353F2BA-12FA-AA17-BE2C-3D961E06AB6E}.Debug|ARM64.ActiveCfg = Debug|Any CPU
333+
{0353F2BA-12FA-AA17-BE2C-3D961E06AB6E}.Debug|ARM64.Build.0 = Debug|Any CPU
334+
{0353F2BA-12FA-AA17-BE2C-3D961E06AB6E}.Debug|x64.ActiveCfg = Debug|Any CPU
335+
{0353F2BA-12FA-AA17-BE2C-3D961E06AB6E}.Debug|x64.Build.0 = Debug|Any CPU
336+
{0353F2BA-12FA-AA17-BE2C-3D961E06AB6E}.Debug|x86.ActiveCfg = Debug|Any CPU
337+
{0353F2BA-12FA-AA17-BE2C-3D961E06AB6E}.Debug|x86.Build.0 = Debug|Any CPU
338+
{0353F2BA-12FA-AA17-BE2C-3D961E06AB6E}.Release|ARM32.ActiveCfg = Release|Any CPU
339+
{0353F2BA-12FA-AA17-BE2C-3D961E06AB6E}.Release|ARM32.Build.0 = Release|Any CPU
340+
{0353F2BA-12FA-AA17-BE2C-3D961E06AB6E}.Release|ARM64.ActiveCfg = Release|Any CPU
341+
{0353F2BA-12FA-AA17-BE2C-3D961E06AB6E}.Release|ARM64.Build.0 = Release|Any CPU
342+
{0353F2BA-12FA-AA17-BE2C-3D961E06AB6E}.Release|x64.ActiveCfg = Release|Any CPU
343+
{0353F2BA-12FA-AA17-BE2C-3D961E06AB6E}.Release|x64.Build.0 = Release|Any CPU
344+
{0353F2BA-12FA-AA17-BE2C-3D961E06AB6E}.Release|x86.ActiveCfg = Release|Any CPU
345+
{0353F2BA-12FA-AA17-BE2C-3D961E06AB6E}.Release|x86.Build.0 = Release|Any CPU
346+
{E2F66420-4219-4841-4225-629C51CF175F}.Debug|ARM32.ActiveCfg = Debug|Any CPU
347+
{E2F66420-4219-4841-4225-629C51CF175F}.Debug|ARM32.Build.0 = Debug|Any CPU
348+
{E2F66420-4219-4841-4225-629C51CF175F}.Debug|ARM64.ActiveCfg = Debug|Any CPU
349+
{E2F66420-4219-4841-4225-629C51CF175F}.Debug|ARM64.Build.0 = Debug|Any CPU
350+
{E2F66420-4219-4841-4225-629C51CF175F}.Debug|x64.ActiveCfg = Debug|Any CPU
351+
{E2F66420-4219-4841-4225-629C51CF175F}.Debug|x64.Build.0 = Debug|Any CPU
352+
{E2F66420-4219-4841-4225-629C51CF175F}.Debug|x86.ActiveCfg = Debug|Any CPU
353+
{E2F66420-4219-4841-4225-629C51CF175F}.Debug|x86.Build.0 = Debug|Any CPU
354+
{E2F66420-4219-4841-4225-629C51CF175F}.Release|ARM32.ActiveCfg = Release|Any CPU
355+
{E2F66420-4219-4841-4225-629C51CF175F}.Release|ARM32.Build.0 = Release|Any CPU
356+
{E2F66420-4219-4841-4225-629C51CF175F}.Release|ARM64.ActiveCfg = Release|Any CPU
357+
{E2F66420-4219-4841-4225-629C51CF175F}.Release|ARM64.Build.0 = Release|Any CPU
358+
{E2F66420-4219-4841-4225-629C51CF175F}.Release|x64.ActiveCfg = Release|Any CPU
359+
{E2F66420-4219-4841-4225-629C51CF175F}.Release|x64.Build.0 = Release|Any CPU
360+
{E2F66420-4219-4841-4225-629C51CF175F}.Release|x86.ActiveCfg = Release|Any CPU
361+
{E2F66420-4219-4841-4225-629C51CF175F}.Release|x86.Build.0 = Release|Any CPU
326362
EndGlobalSection
327363
GlobalSection(SolutionProperties) = preSolution
328364
HideSolutionNode = FALSE
@@ -338,6 +374,8 @@ Global
338374
{213206D7-B37F-4619-8E92-56BBEFFE18FF} = {79E4A3B7-A504-488B-B5C7-8FDC8DA8816C}
339375
{C6D40ACD-339F-462F-98E6-8C25EDDDA251} = {79E4A3B7-A504-488B-B5C7-8FDC8DA8816C}
340376
{F3B32230-C960-DE70-DD4A-55B953758C98} = {79E4A3B7-A504-488B-B5C7-8FDC8DA8816C}
377+
{0353F2BA-12FA-AA17-BE2C-3D961E06AB6E} = {79E4A3B7-A504-488B-B5C7-8FDC8DA8816C}
378+
{E2F66420-4219-4841-4225-629C51CF175F} = {79E4A3B7-A504-488B-B5C7-8FDC8DA8816C}
341379
EndGlobalSection
342380
GlobalSection(ExtensibilityGlobals) = postSolution
343381
SolutionGuid = {0645CA94-4715-4CE3-AB18-258CED23724F}

src/MobilePackageGen.Common/AppBuilder.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -237,6 +237,8 @@ public static void ExtractAppPackages(IFileSystem fileSystem, string destination
237237
}
238238
}
239239

240+
RemakeAppx.Program.MakeAppx(destination, $"{destination}.appx", false, false).Wait();
241+
240242
// --------------------------
241243

242244
if (i != packagesCount - 1)

src/MobilePackageGen.Common/MobilePackageGen.Common.csproj

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,14 @@
99
</PropertyGroup>
1010

1111
<ItemGroup>
12+
<ProjectReference Include="..\..\thirdparty\DotnetPackaging.Msix\DotnetPackaging.Msix.csproj" />
1213
<ProjectReference Include="..\LibSxS\LibSxS.csproj" />
1314
<ProjectReference Include="..\Microsoft.Deployment.Compression.Cab\Microsoft.Deployment.Compression.Cab.csproj" />
1415
<ProjectReference Include="..\MobilePackageGen.Adapters\MobilePackageGen.Adapters.csproj" />
1516
</ItemGroup>
1617

18+
<ItemGroup>
19+
<Folder Include="RemakeAppx\" />
20+
</ItemGroup>
21+
1722
</Project>
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
using System.IO.Abstractions;
2+
using Zafiro.DivineBytes;
3+
using IDirectory = Zafiro.DivineBytes.IDirectory;
4+
5+
namespace RemakeAppx.Helpers;
6+
7+
internal class IODir(IDirectoryInfo directoryInfo) : IDirectory
8+
{
9+
public string Name => directoryInfo.Name;
10+
public IEnumerable<INamed> Children => directoryInfo
11+
.GetFiles()
12+
.Select(info => new IOFile(info))
13+
.Concat<INamed>(directoryInfo
14+
.GetDirectories()
15+
.Select(info => new IODir(info)));
16+
}
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
using System.IO.Abstractions;
2+
using CSharpFunctionalExtensions;
3+
using Zafiro.DivineBytes;
4+
5+
namespace RemakeAppx.Helpers;
6+
7+
internal class IOFile : INamedByteSource
8+
{
9+
private readonly IFileInfo fileInfo;
10+
11+
public IOFile(IFileInfo info)
12+
{
13+
fileInfo = info;
14+
Source = ByteSource.FromStreamFactory(info.OpenRead, async () => info.Length);
15+
}
16+
17+
public IByteSource Source { get; }
18+
19+
public string Name => fileInfo.Name;
20+
public IDisposable Subscribe(IObserver<byte[]> observer)
21+
{
22+
return Source.Subscribe(observer);
23+
}
24+
25+
public IObservable<byte[]> Bytes => Source.Bytes;
26+
27+
public Task<Maybe<long>> GetLength()
28+
{
29+
return Source.GetLength();
30+
}
31+
}
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
using System.IO.Abstractions;
2+
using System.Reactive.Linq;
3+
using CSharpFunctionalExtensions;
4+
using DotnetPackaging.Msix;
5+
using Zafiro.DivineBytes;
6+
using Serilog;
7+
using RemakeAppx.Helpers;
8+
using File = System.IO.File;
9+
10+
namespace RemakeAppx
11+
{
12+
public class Program
13+
{
14+
public static async Task MakeAppx(string inputFolder, string outputFile, bool bundleMode, bool unsignedMode)
15+
{
16+
/*Console.WriteLine($"Making {outputFile} out of {inputFolder}");
17+
Console.WriteLine($"Bundle Mode: {bundleMode}");
18+
Console.WriteLine($"Unsigned Mode: {unsignedMode}");*/
19+
20+
FileSystem fs = new();
21+
IDirectoryInfo directoryInfo = fs.DirectoryInfo.New(inputFolder);
22+
IODir ioDir = new(directoryInfo);
23+
24+
await Msix.FromDirectory(ioDir, Maybe<ILogger>.None, bundleMode, unsignedMode, inputFolder)
25+
.Map(async source =>
26+
{
27+
await using var fileStream = File.Open(outputFile, FileMode.Create);
28+
return await source.DumpTo(fileStream);
29+
});
30+
}
31+
}
32+
}
Lines changed: 169 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,169 @@
1+
using System;
2+
using System.Buffers;
3+
using System.Reactive.Linq;
4+
using ZLibDotNet;
5+
6+
namespace BlockCompressor;
7+
8+
public static class Compressed
9+
{
10+
/// <summary>
11+
/// Default block size (64KB)
12+
/// </summary>
13+
public const int DefaultBlockSize = 64 * 1024; // 64KB
14+
15+
/// <summary>
16+
/// Compresses an observable stream of byte arrays into blocks using zlib's deflate algorithm
17+
/// configured in the style of makemsix/makeappx
18+
/// </summary>
19+
public static IObservable<DeflateBlock> Blocks(
20+
IObservable<byte[]> input,
21+
int compressionLevel = ZLib.Z_BEST_COMPRESSION,
22+
int uncompressedBlockSize = DefaultBlockSize)
23+
{
24+
return Observable.Create<DeflateBlock>(observer =>
25+
{
26+
// Use ArrayPool to reduce GC pressure
27+
byte[] rentedBuffer = ArrayPool<byte>.Shared.Rent(uncompressedBlockSize * 2);
28+
int bytesInBuffer = 0;
29+
30+
var wrapper = new ZStreamWrapper();
31+
32+
var subscription = input.Subscribe(
33+
onNext: incomingBytes =>
34+
{
35+
try
36+
{
37+
// Check if we need a larger buffer
38+
if (bytesInBuffer + incomingBytes.Length > rentedBuffer.Length)
39+
{
40+
// Get a new buffer of appropriate size
41+
int newSize = Math.Max(rentedBuffer.Length * 2, bytesInBuffer + incomingBytes.Length);
42+
byte[] newBuffer = ArrayPool<byte>.Shared.Rent(newSize);
43+
44+
// Copy existing data to new buffer
45+
Buffer.BlockCopy(rentedBuffer, 0, newBuffer, 0, bytesInBuffer);
46+
47+
// Return old buffer to pool
48+
ArrayPool<byte>.Shared.Return(rentedBuffer);
49+
50+
// Use new buffer
51+
rentedBuffer = newBuffer;
52+
}
53+
54+
// Copy incoming bytes to our buffer
55+
Buffer.BlockCopy(incomingBytes, 0, rentedBuffer, bytesInBuffer, incomingBytes.Length);
56+
bytesInBuffer += incomingBytes.Length;
57+
58+
// Process complete blocks
59+
ProcessCompleteBlocks();
60+
}
61+
catch (Exception ex)
62+
{
63+
observer.OnError(ex);
64+
}
65+
},
66+
onError: ex =>
67+
{
68+
try
69+
{
70+
ArrayPool<byte>.Shared.Return(rentedBuffer);
71+
}
72+
finally
73+
{
74+
observer.OnError(ex);
75+
}
76+
},
77+
onCompleted: () =>
78+
{
79+
try
80+
{
81+
// Process any remaining bytes if we have some
82+
if (bytesInBuffer > 0)
83+
{
84+
ProcessBlock(rentedBuffer.AsSpan(0, bytesInBuffer).ToArray());
85+
}
86+
87+
// Process the final zlib block
88+
byte[] finalData = wrapper.DeflateFinish();
89+
90+
if (finalData.Length > 0)
91+
{
92+
var finalBlock = new DeflateBlock
93+
{
94+
CompressedData = finalData,
95+
OriginalData = [],
96+
};
97+
98+
observer.OnNext(finalBlock);
99+
}
100+
101+
observer.OnCompleted();
102+
}
103+
catch (Exception ex)
104+
{
105+
observer.OnError(ex);
106+
}
107+
finally
108+
{
109+
// Return the buffer to the pool
110+
ArrayPool<byte>.Shared.Return(rentedBuffer);
111+
}
112+
});
113+
114+
// Local function to process all complete blocks
115+
void ProcessCompleteBlocks()
116+
{
117+
// Process as many complete blocks as we can
118+
while (bytesInBuffer >= uncompressedBlockSize)
119+
{
120+
// Create a block from the buffer
121+
byte[] blockData = new byte[uncompressedBlockSize];
122+
Buffer.BlockCopy(rentedBuffer, 0, blockData, 0, uncompressedBlockSize);
123+
124+
// Process it
125+
ProcessBlock(blockData);
126+
127+
// Move remaining data to the start of the buffer
128+
if (bytesInBuffer > uncompressedBlockSize)
129+
{
130+
Buffer.BlockCopy(
131+
rentedBuffer,
132+
uncompressedBlockSize,
133+
rentedBuffer,
134+
0,
135+
bytesInBuffer - uncompressedBlockSize);
136+
}
137+
138+
bytesInBuffer -= uncompressedBlockSize;
139+
}
140+
}
141+
142+
// Local function to process a single block
143+
void ProcessBlock(byte[] blockData)
144+
{
145+
try
146+
{
147+
// Compress the block using our wrapper
148+
byte[] compressedData = wrapper.Deflate(
149+
blockData,
150+
compressionLevel);
151+
152+
// Create and emit the compressed block
153+
var block = new DeflateBlock
154+
{
155+
CompressedData = compressedData,
156+
OriginalData = blockData,
157+
};
158+
observer.OnNext(block);
159+
}
160+
catch (Exception ex)
161+
{
162+
observer.OnError(ex);
163+
}
164+
}
165+
166+
return subscription;
167+
});
168+
}
169+
}
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
namespace BlockCompressor;
2+
3+
public class DeflateBlock
4+
{
5+
public required byte[] CompressedData { get; init; }
6+
public required byte[] OriginalData { get; init; }
7+
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
<Project Sdk="Microsoft.NET.Sdk">
2+
3+
<PropertyGroup>
4+
<TargetFramework>net8.0</TargetFramework>
5+
<ImplicitUsings>enable</ImplicitUsings>
6+
<Nullable>enable</Nullable>
7+
<RootNamespace>BlockCompressor</RootNamespace>
8+
</PropertyGroup>
9+
10+
<ItemGroup>
11+
<PackageReference Include="System.Reactive" Version="6.0.1" />
12+
<PackageReference Include="ZLibDotNet" Version="0.1.1" />
13+
</ItemGroup>
14+
15+
</Project>
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
MIT License
2+
3+
Copyright (c) [year] [fullname]
4+
5+
Permission is hereby granted, free of charge, to any person obtaining a copy
6+
of this software and associated documentation files (the "Software"), to deal
7+
in the Software without restriction, including without limitation the rights
8+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9+
copies of the Software, and to permit persons to whom the Software is
10+
furnished to do so, subject to the following conditions:
11+
12+
The above copyright notice and this permission notice shall be included in all
13+
copies or substantial portions of the Software.
14+
15+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21+
SOFTWARE.

0 commit comments

Comments
 (0)