Skip to content

Commit 389c81e

Browse files
committed
fixed bugs, added arguments, and library merging with ilmerge
1 parent 203f8b4 commit 389c81e

File tree

10 files changed

+224
-56
lines changed

10 files changed

+224
-56
lines changed

bytepress/Compresison/ICompressor.cs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
using System;
22
using System.Collections.Generic;
3-
using System.Linq;
43
using System.Text;
54
using System.Threading.Tasks;
65

bytepress/Compresison/LZMA/Helper.cs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
using System;
22
using System.Collections.Generic;
33
using System.IO;
4-
using System.Linq;
54
using System.Text;
65
using System.Threading.Tasks;
76

bytepress/Engine/Merger.cs

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Diagnostics;
4+
using System.IO;
5+
using System.Windows.Forms;
6+
7+
namespace bytepress.Engine
8+
{
9+
class Merger
10+
{
11+
private List<string> _libraries;
12+
public Merger(List<string> libraries)
13+
{
14+
_libraries = libraries;
15+
}
16+
17+
public bool Merge(string tempAssembly, string outputLocation)
18+
{
19+
ProcessStartInfo startInfo = new ProcessStartInfo();
20+
startInfo.CreateNoWindow = false;
21+
startInfo.UseShellExecute = false;
22+
startInfo.FileName = Application.StartupPath + "\\ILMerge.exe";
23+
startInfo.WindowStyle = ProcessWindowStyle.Normal;
24+
startInfo.Arguments = Quote(tempAssembly) + " ";
25+
foreach(string lib in _libraries)
26+
startInfo.Arguments += Quote(lib) + " ";
27+
28+
//TODO: See if we can get 4.5 to work, and check if the assembly location exists before using.
29+
string refAsms = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ProgramFilesX86),
30+
"Reference Assemblies\\Microsoft\\Framework\\.NETFramework\\v4.5");
31+
startInfo.Arguments += $"/out:{Quote(outputLocation)} /targetplatform:\"v4,{refAsms}\"";
32+
33+
using (Process proc = Process.Start(startInfo))
34+
proc?.WaitForExit();
35+
36+
37+
File.Delete(tempAssembly);
38+
File.Delete(outputLocation.Replace(".exe", ".pdb"));
39+
40+
return File.Exists(outputLocation);
41+
}
42+
43+
private string Quote(string str)
44+
{
45+
return "\"" + str + "\"";
46+
}
47+
}
48+
}

bytepress/Engine/Presser.cs

Lines changed: 117 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
11
using System;
22
using System.Collections.Generic;
33
using System.IO;
4+
using System.Linq;
45
using System.Windows.Forms;
56
using bytepress.Compresison;
67
using bytepress.Compresison.LZMA;
78
using bytepress.Extensions;
9+
using bytepress.Properties;
810

911
namespace bytepress.Engine
1012
{
@@ -15,8 +17,9 @@ public class Presser
1517
private byte[] _fileBytes;
1618
private byte[] _fileBytesCompressed;
1719
private AssemblyCloner _cloner;
18-
private static List<ICompressor> _compressors;
19-
20+
private ICompressor _compressor;
21+
private List<ICompressor> _compressors;
22+
private List<string> _libraries;
2023
public UpdateHandler UpdateStatus = delegate { };
2124
public delegate void UpdateHandler(string status, StatusType type);
2225

@@ -31,7 +34,7 @@ public enum StatusType
3134
public Presser(string file)
3235
{
3336
_file = file;
34-
_source = Properties.Resources.source;
37+
_source = Resources.source;
3538
_cloner = new AssemblyCloner(_file);
3639
_compressors = new List<ICompressor>();
3740
LoadCompressors();
@@ -42,13 +45,65 @@ public Presser(string file, byte[] data)
4245
{
4346
_file = file;
4447
_fileBytes = data;
45-
_source = Properties.Resources.source;
48+
_source = Resources.source;
4649
_cloner = new AssemblyCloner(_file);
4750
_compressors = new List<ICompressor>();
4851
LoadCompressors();
4952
CleanWorkspace();
5053
}
5154

55+
/// <summary>
56+
/// Set which compression algorithm to use.
57+
/// </summary>
58+
public void SetCompressor(string algorithm)
59+
{
60+
algorithm = algorithm.ToLower().Trim();
61+
switch (algorithm)
62+
{
63+
case "gzip":
64+
_compressor = _compressors[0];
65+
break;
66+
case "quicklz":
67+
_compressor = _compressors[1];
68+
break;
69+
case "lzma":
70+
_compressor = _compressors[2];
71+
break;
72+
default:
73+
UpdateStatus($"Invalid compression algorithm '{algorithm}'. Defaulting to automatic calculation...", StatusType.Warning);
74+
break;
75+
}
76+
77+
}
78+
79+
/// <summary>
80+
/// Verifies and sets the libraries to be merged with the main assembly.
81+
/// </summary>
82+
/// <param name="libraries"></param>
83+
public void MergeLibraries(List<string> libraries)
84+
{
85+
foreach (string lib in libraries)
86+
{
87+
byte[] temp = File.ReadAllBytes(lib);
88+
if (!IsManagedAssembly(temp))
89+
throw new Exception("Libraries to merge must be valid .NET assemblies.");
90+
Array.Clear(temp, 0, temp.Length);
91+
}
92+
_libraries = libraries;
93+
}
94+
95+
/// <summary>
96+
/// Checks the .NET header conventions to determine if assembly is managed (.NET).
97+
/// </summary>
98+
private bool IsManagedAssembly(byte[] payloadBuffer)
99+
{
100+
int e_lfanew = BitConverter.ToInt32(payloadBuffer, 0x3c);
101+
int magicNumber = BitConverter.ToInt16(payloadBuffer, e_lfanew + 0x18);
102+
int isManagedOffset = magicNumber == 0x10B ? 0xE8 : 0xF8;
103+
int isManaged = BitConverter.ToInt32(payloadBuffer, e_lfanew + isManagedOffset);
104+
return isManaged != 0;
105+
}
106+
52107
/// <summary>
53108
/// Loads the available compression algorithms.
54109
/// </summary>
@@ -67,22 +122,35 @@ public void Process()
67122
if(_fileBytes == null)
68123
_fileBytes = File.ReadAllBytes(_file);
69124

125+
UpdateStatus("Verifying file is .NET assembly...", Presser.StatusType.Normal);
126+
if (!IsManagedAssembly(_fileBytes))
127+
throw new Exception("Only .NET executable files are supported");
128+
70129
FileInfo f = new FileInfo(_file);
71130

72-
UpdateStatus($"Calculating the best compression algorithm...", StatusType.Normal);
73-
PickCompressor();
131+
if(_compressor == null)
132+
UpdateStatus("Calculating the best compression algorithm...", StatusType.Normal);
74133

75-
UpdateStatus($"Writing compressed payload to %temp%...", StatusType.Normal);
134+
Compress();
135+
136+
UpdateStatus("Writing compressed payload to %temp%...", StatusType.Normal);
76137
File.WriteAllBytes(Path.GetTempPath() + "\\data", _fileBytesCompressed);
77138

78139
UpdateStatus("Copying assembly information and icon...", StatusType.Normal);
79140
CopyAssembly();
80141

81142
UpdateStatus("Compiling...", StatusType.Normal);
143+
144+
string outLocation = String.Empty;
145+
if (_libraries != null && _libraries.Count > 0)
146+
outLocation = Path.GetTempPath() + f.Name.Replace(".exe", "_bytepressed.exe");
147+
148+
else
149+
outLocation = f.DirectoryName + "\\" + f.Name.Replace(".exe", "_bytepressed.exe");
150+
82151
Compiler comp = new Compiler
83152
{
84-
// add option to save as same name
85-
CompileLocation = f.DirectoryName + "\\" + f.Name.Replace(".exe", "_bytepressed.exe"),
153+
CompileLocation = outLocation,
86154
ResourceFiles = new[] { Path.GetTempPath() + "\\data", Application.StartupPath + "\\bytepress.lib.dll" },
87155
SourceCodes = new[] { _source },
88156
References = new[] {
@@ -98,10 +166,18 @@ public void Process()
98166
CleanWorkspace();
99167
throw new Exception(comp.CompileError);
100168
}
101-
169+
if (_libraries != null && _libraries.Count > 0)
170+
{
171+
UpdateStatus("Merging additional libraries...", StatusType.Normal);
172+
Merger m = new Merger(_libraries);
173+
if(!m.Merge(outLocation, f.DirectoryName + "\\" + f.Name.Replace(".exe", "_bytepressed.exe")))
174+
throw new Exception("Failed to merge libraries");
175+
}
176+
177+
102178
Console.WriteLine();
103179

104-
byte[] compiled = File.ReadAllBytes(comp.CompileLocation);
180+
byte[] compiled = File.ReadAllBytes(f.DirectoryName + "\\" + f.Name.Replace(".exe", "_bytepressed.exe"));
105181
double compressionRatio = 100 - (double)compiled.Length / _fileBytes.Length * 100.0;
106182
UpdateStatus("Compression Results", StatusType.Normal);
107183
UpdateStatus("--------------------------------------------", StatusType.Normal);
@@ -122,38 +198,50 @@ public void Process()
122198
/// <summary>
123199
/// Tests and chooses the best compression algorithm.
124200
/// </summary>
125-
private void PickCompressor()
201+
private void Compress()
126202
{
127-
ICompressor best = null;
128-
long bestLength = long.MaxValue;
129-
foreach (ICompressor c in _compressors)
203+
if (_compressor == null)
130204
{
131-
UpdateStatus("--------------------------------------------", StatusType.Normal);
132-
UpdateStatus($"Testing algorithm: {c.Name}...", StatusType.Normal);
133-
byte[] tmp = c.Compress(_fileBytes);
134-
double cR = 100 - (double)tmp.Length / _fileBytes.Length * 100.0;
135-
UpdateStatus($"{c.Name} Compression Ratio: {cR:F}%", StatusType.Normal);
136-
if (tmp.Length < bestLength)
205+
long bestLength = Int64.MaxValue;
206+
foreach (ICompressor c in _compressors)
137207
{
138-
best = c;
139-
bestLength = tmp.Length;
140-
_fileBytesCompressed = tmp;
208+
UpdateStatus("--------------------------------------------", StatusType.Normal);
209+
UpdateStatus($"Testing algorithm: {c.Name}...", StatusType.Normal);
210+
byte[] tmp = c.Compress(_fileBytes);
211+
double cR = 100 - (double)tmp.Length / _fileBytes.Length * 100.0;
212+
UpdateStatus($"{c.Name} Compression Ratio: {cR:F}%", StatusType.Normal);
213+
if (tmp.Length < bestLength)
214+
{
215+
_compressor = c;
216+
bestLength = tmp.Length;
217+
_fileBytesCompressed = tmp;
218+
}
141219
}
142220
}
221+
else
222+
{
223+
UpdateStatus($"Using algorithm: {_compressor.Name}...", StatusType.Normal);
224+
_fileBytesCompressed = _compressor.Compress(_fileBytes);
225+
double cR = 100 - (double)_fileBytesCompressed.Length / _fileBytes.Length * 100.0;
226+
UpdateStatus($"{_compressor.Name} Compression Ratio: {cR:F}%", StatusType.Normal);
227+
_source = _source.Replace("*type*", _compressors.IndexOf(_compressor).ToString());
228+
return;
229+
}
143230

144-
UpdateStatus("--------------------------------------------", StatusType.Normal);
145231

146-
if (best.GetType() == typeof(GZIP))
232+
UpdateStatus("--------------------------------------------", StatusType.Normal);
233+
234+
if (_compressor.GetType() == typeof(GZIP))
147235
{
148236
_source = _source.Replace("*type*", "0");
149237
UpdateStatus("Chosen algorithm: GZIP", StatusType.Normal);
150238
}
151-
if (best.GetType() == typeof(QuickLZ))
239+
if (_compressor.GetType() == typeof(QuickLZ))
152240
{
153241
_source = _source.Replace("*type*", "1");
154242
UpdateStatus("Chosen algorithm: QuickLZ", StatusType.Normal);
155243
}
156-
if (best.GetType() == typeof(LZMAez))
244+
if (_compressor.GetType() == typeof(LZMAez))
157245
{
158246
_source = _source.Replace("*type*", "2");
159247
UpdateStatus("Chosen algorithm: LZMA", StatusType.Normal);
@@ -187,7 +275,7 @@ private void CleanWorkspace()
187275
string[] tempFiles =
188276
{
189277
"data",
190-
"icon.ico"
278+
"icon.ico",
191279
};
192280
foreach (string file in tempFiles)
193281
{

bytepress/Program.cs

Lines changed: 44 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
using System;
2+
using System.Collections.Generic;
23
using System.IO;
34
using System.Windows.Forms;
45
using bytepress.Engine;
6+
using NDesk.Options;
57

68
namespace bytepress
79
{
@@ -12,11 +14,36 @@ class Program
1214
/// </summary>
1315
static void Main(string[] args)
1416
{
17+
string algo = string.Empty;
18+
bool showHelp = false;
19+
List<string> libs = new List<string>();
20+
var argz = new OptionSet() {
21+
{
22+
"a|algorithm=", "the compression algorithm to use. (gzip, quicklz, lzma)",
23+
v => algo = v
24+
},
25+
{
26+
"l|lib=", "libraries to merge to main assembly",
27+
v => libs.Add(v)
28+
},
29+
{
30+
"h|help", "show this message",
31+
v => showHelp = v != null
32+
},
33+
};
34+
1535
try
1636
{
17-
if (args.Length == 0) return;
1837
Watermark();
1938

39+
List<string> argsList = argz.Parse(args);
40+
41+
if (showHelp || argsList.Count == 0)
42+
{
43+
ShowHelp(argz);
44+
return;
45+
}
46+
2047
string inFile = args[0];
2148

2249
if (!File.Exists(inFile))
@@ -28,14 +55,21 @@ static void Main(string[] args)
2855
if (!f.Name.EndsWith(".exe", StringComparison.InvariantCultureIgnoreCase))
2956
throw new Exception("Only executable files are supported");
3057

31-
UpdateStatus("Verifying file is .NET assembly...", Presser.StatusType.Normal);
32-
if (!IsManagedAssembly(original))
33-
throw new Exception("Only .NET executable files are supported");
34-
3558
Presser p = new Presser(inFile, original);
3659
p.UpdateStatus += UpdateStatus;
37-
p.Process();
60+
61+
if(!string.IsNullOrEmpty(algo))
62+
p.SetCompressor(algo);
63+
64+
if(libs.Count > 0)
65+
p.MergeLibraries(libs);
3866

67+
p.Process();
68+
}
69+
catch (OptionException e)
70+
{
71+
UpdateStatus(e.ToString(), Presser.StatusType.Error);
72+
UpdateStatus("Try bytepress --help for more information.", Presser.StatusType.Error);
3973
Console.Read();
4074
}
4175
catch (Exception e)
@@ -48,15 +82,12 @@ static void Main(string[] args)
4882
}
4983

5084
/// <summary>
51-
/// Checks the .NET header conventions to determine if assembly is managed (.NET).
85+
/// Displays help information to the user
5286
/// </summary>
53-
private static bool IsManagedAssembly(byte[] payloadBuffer)
87+
private static void ShowHelp(OptionSet p)
5488
{
55-
int e_lfanew = BitConverter.ToInt32(payloadBuffer, 0x3c);
56-
int magicNumber = BitConverter.ToInt16(payloadBuffer, e_lfanew + 0x18);
57-
int isManagedOffset = magicNumber == 0x10B ? 0xE8 : 0xF8;
58-
int isManaged = BitConverter.ToInt32(payloadBuffer, e_lfanew + isManagedOffset);
59-
return isManaged != 0;
89+
Console.WriteLine("Usage: bytepress [file to compress] [option] [value]");
90+
p.WriteOptionDescriptions(Console.Out);
6091
}
6192

6293
/// <summary>

0 commit comments

Comments
 (0)