using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using Confuser.Core.Helpers;
using dnlib.DotNet;
using SevenZip;
using SevenZip.Compression.LZMA;
namespace Confuser.Core.Services {
internal class CompressionService : ICompressionService {
static readonly object Decompressor = new object();
readonly ConfuserContext context;
///
/// Initializes a new instance of the class.
///
/// The working context.
public CompressionService(ConfuserContext context) {
this.context = context;
}
///
public MethodDef TryGetRuntimeDecompressor(ModuleDef module, Action init) {
var decompressor = context.Annotations.Get>>(module, Decompressor);
if (decompressor == null)
return null;
foreach (IDnlibDef member in decompressor.Item2)
init(member);
return decompressor.Item1;
}
///
public MethodDef GetRuntimeDecompressor(ModuleDef module, Action init) {
Tuple> decompressor = context.Annotations.GetOrCreate(module, Decompressor, m => {
var rt = context.Registry.GetService();
List members = InjectHelper.Inject(rt.GetRuntimeType("Confuser.Runtime.Lzma"), module.GlobalType, module).ToList();
MethodDef decomp = null;
foreach (IDnlibDef member in members) {
if (member is MethodDef) {
var method = (MethodDef)member;
if (method.Access == MethodAttributes.Public)
method.Access = MethodAttributes.Assembly;
if (!method.IsConstructor)
method.IsSpecialName = false;
if (method.Name == "Decompress")
decomp = method;
}
else if (member is FieldDef) {
var field = (FieldDef)member;
if (field.Access == FieldAttributes.Public)
field.Access = FieldAttributes.Assembly;
if (field.IsLiteral) {
field.DeclaringType.Fields.Remove(field);
}
}
}
members.RemoveWhere(def => def is FieldDef && ((FieldDef)def).IsLiteral);
Debug.Assert(decomp != null);
return Tuple.Create(decomp, members);
});
foreach (IDnlibDef member in decompressor.Item2)
init(member);
return decompressor.Item1;
}
///
public byte[] Compress(byte[] data, Action progressFunc = null) {
CoderPropID[] propIDs = {
CoderPropID.DictionarySize,
CoderPropID.PosStateBits,
CoderPropID.LitContextBits,
CoderPropID.LitPosBits,
CoderPropID.Algorithm,
CoderPropID.NumFastBytes,
CoderPropID.MatchFinder,
CoderPropID.EndMarker
};
object[] properties = {
1 << 23,
2,
3,
0,
2,
128,
"bt4",
false
};
var x = new MemoryStream();
var encoder = new Encoder();
encoder.SetCoderProperties(propIDs, properties);
encoder.WriteCoderProperties(x);
Int64 fileSize;
fileSize = data.Length;
for (int i = 0; i < 8; i++)
x.WriteByte((Byte)(fileSize >> (8 * i)));
ICodeProgress progress = null;
if (progressFunc != null)
progress = new CompressionLogger(progressFunc, data.Length);
encoder.Code(new MemoryStream(data), x, -1, -1, progress);
return x.ToArray();
}
class CompressionLogger : ICodeProgress {
readonly Action progressFunc;
readonly int size;
public CompressionLogger(Action progressFunc, int size) {
this.progressFunc = progressFunc;
this.size = size;
}
public void SetProgress(long inSize, long outSize) {
double precentage = (double)inSize / size;
progressFunc(precentage);
}
}
}
///
/// Provides methods to do compression and inject decompression algorithm.
///
public interface ICompressionService {
///
/// Gets the runtime decompression method in the module, or null if it's not yet injected.
///
/// The module which the decompression method resides in.
/// The initializing method for compression helper definitions.
///
/// The requested decompression method with signature 'static Byte[] (Byte[])',
/// or null if it hasn't been injected yet.
///
MethodDef TryGetRuntimeDecompressor(ModuleDef module, Action init);
///
/// Gets the runtime decompression method in the module and inject if it does not exists.
///
/// The module which the decompression method resides in.
/// The initializing method for injected helper definitions.
/// The requested decompression method with signature 'static Byte[] (Byte[])'.
MethodDef GetRuntimeDecompressor(ModuleDef module, Action init);
///
/// Compresses the specified data.
///
/// The buffer storing the data.
/// The function that receive the progress of compression.
/// The compressed data.
byte[] Compress(byte[] data, Action progressFunc = null);
}
}