automatic downloading of oodle dll if needed

This commit is contained in:
Fabian 2020-09-13 21:32:48 +02:00
parent aeded745cf
commit f2d990e2ff
4 changed files with 197 additions and 6 deletions

View File

@ -136,6 +136,7 @@
<PackageReference Include="DotNetZip" Version="1.13.8" />
<PackageReference Include="Extended.Wpf.Toolkit" Version="4.0.1" />
<PackageReference Include="K4os.Compression.LZ4.Streams" Version="1.1.11" />
<PackageReference Include="LZMA-SDK" Version="19.0.0" />
<PackageReference Include="Newtonsoft.Json" Version="12.0.3" />
<PackageReference Include="NVorbis" Version="0.10.1" />
<PackageReference Include="Ookii.Dialogs.Wpf" Version="1.1.0" />

View File

@ -1,6 +1,7 @@
using System;
using System.IO;
using System.Runtime.InteropServices;
using FModel.Utils;
namespace PakReader.Parsers
{
@ -14,17 +15,13 @@ namespace PakReader.Parsers
public OodleStream(byte[] input, long decompressedLength)
{
Oodle.LoadOodleDll();
_baseStream = new MemoryStream(Decompress(input, decompressedLength), false)
{
Position = 0
};
}
/// <summary>
/// Oodle Library Path
/// </summary>
private const string OodleLibraryPath = "oo2core_5_win64";
protected override void Dispose(bool disposing)
{
try
@ -45,7 +42,7 @@ namespace PakReader.Parsers
/// <summary>
/// Oodle64 Decompression Method
/// </summary>
[DllImport(OodleLibraryPath, CallingConvention = CallingConvention.Cdecl)]
[DllImport(Oodle.OODLE_DLL_NAME, CallingConvention = CallingConvention.Cdecl)]
private static extern long OodleLZ_Decompress(byte[] buffer, long bufferSize, byte[] result, long outputBufferSize, int a, int b, int c, long d, long e, long f, long g, long h, long i, int ThreadModule);
/// <summary>

74
FModel/Utils/Oodle.cs Normal file
View File

@ -0,0 +1,74 @@
using System;
using System.IO;
using System.Net.Http;
using System.Threading.Tasks;
using FModel.Logger;
namespace FModel.Utils
{
public static class Oodle
{
private const string WARFRAME_CDN_HOST = "https://origin.warframe.com";
private const string WARFRAME_INDEX_PATH = "/origin/E926E926/index.txt.lzma";
private const string WARFRAME_INDEX_URL = WARFRAME_CDN_HOST + WARFRAME_INDEX_PATH;
public const string OODLE_DLL_NAME = "oo2core_8_win64.dll";
public static bool LoadOodleDll()
{
if (File.Exists(OODLE_DLL_NAME))
{
return true;
}
return DownloadOodleDll().Result;
}
private static async Task<bool> DownloadOodleDll()
{
using var client = new HttpClient {Timeout = TimeSpan.FromSeconds(2)};
using HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Get,
new Uri(WARFRAME_INDEX_URL));
try
{
using var httpResponseMessage = await client.SendAsync(request, HttpCompletionOption.ResponseHeadersRead).ConfigureAwait(false);
var lzma = await httpResponseMessage.Content.ReadAsByteArrayAsync().ConfigureAwait(false);
var input = new MemoryStream(lzma);
var output = new MemoryStream();
LZMA.Decompress(input, output);
output.Position = 0;
using var reader = new StreamReader(output);
string line, dllUrl = null;
while ((line = reader.ReadLine()) != null)
{
if (line.Contains(OODLE_DLL_NAME))
{
dllUrl = WARFRAME_CDN_HOST + line.Substring(0, line.IndexOf(','));
break;
}
}
if (dllUrl == null)
{
DebugHelper.WriteLine("{0} {1} {2}", "[FModel]", "[Oodle]", "Warframe index did not contain oodle dll");
return default;
}
using var dllRequest = new HttpRequestMessage(HttpMethod.Get, new Uri(dllUrl));
using var dllResponse = await client.SendAsync(dllRequest, HttpCompletionOption.ResponseHeadersRead).ConfigureAwait(false);
var dllLzma = await dllResponse.Content.ReadAsByteArrayAsync().ConfigureAwait(false);
input = new MemoryStream(dllLzma);
output = new MemoryStream();
LZMA.Decompress(input, output);
output.Position = 0;
await File.WriteAllBytesAsync(OODLE_DLL_NAME, output.ToArray()).ConfigureAwait(false);
DebugHelper.WriteLine("{0} {1} {2}", "[FModel]", "[Oodle]", "Successfully downloaded oodle dll");
return true;
}
catch (Exception e)
{
DebugHelper.WriteLine("{0} {1} {2}", "[FModel]", "[Oodle]", $"Uncaught exception while downloading oodle dll: {e.GetType()}: {e.Message}");
/* TaskCanceledException
* HttpRequestException */
}
return default;
}
}
}

View File

@ -0,0 +1,119 @@
using System.IO;
// https://gist.github.com/ststeiger/cb9750664952f775a341#gistcomment-2912797
namespace FModel.Utils
{
using System;
using SevenZip;
using SevenZip.Compression.LZMA;
public enum LzmaSpeed
{
Fastest = 5,
VeryFast = 8,
Fast = 16,
Medium = 32,
Slow = 64,
VerySlow = 128,
}
public enum DictionarySize
{
///<summary>64 KiB</summary>
VerySmall = 1 << 16,
///<summary>1 MiB</summary>
Small = 1 << 20,
///<summary>4 MiB</summary>
Medium = 1 << 22,
///<summary>8 MiB</summary>
Large = 1 << 23,
///<summary>16 MiB</summary>
Larger = 1 << 24,
///<summary>64 MiB</summary>
VeryLarge = 1 << 26,
}
public static class LZMA
{
public static void Compress(Stream input, Stream output, LzmaSpeed speed = LzmaSpeed.Fastest, DictionarySize dictionarySize = DictionarySize.VerySmall, Action<long, long> onProgress = null)
{
int posStateBits = 2; // default: 2
int litContextBits = 3; // 3 for normal files, 0; for 32-bit data
int litPosBits = 0; // 0 for 64-bit data, 2 for 32-bit.
var numFastBytes = (int)speed;
string matchFinder = "BT4"; // default: BT4
bool endMarker = true;
CoderPropID[] propIDs =
{
CoderPropID.DictionarySize,
CoderPropID.PosStateBits, // (0 <= x <= 4).
CoderPropID.LitContextBits, // (0 <= x <= 8).
CoderPropID.LitPosBits, // (0 <= x <= 4).
CoderPropID.NumFastBytes,
CoderPropID.MatchFinder, // "BT2", "BT4".
CoderPropID.EndMarker
};
object[] properties =
{
(int)dictionarySize,
posStateBits,
litContextBits,
litPosBits,
numFastBytes,
matchFinder,
endMarker
};
var lzmaEncoder = new Encoder();
lzmaEncoder.SetCoderProperties(propIDs, properties);
lzmaEncoder.WriteCoderProperties(output);
var fileSize = input.Length;
for (int i = 0; i < 8; i++) output.WriteByte((byte)(fileSize >> (8 * i)));
ICodeProgress prg = null;
if (onProgress != null)
{
prg = new DelegateCodeProgress(onProgress);
}
lzmaEncoder.Code(input, output, -1, -1, prg);
}
public static void Decompress(Stream input, Stream output, Action<long, long>? onProgress = null)
{
var decoder = new Decoder();
byte[] properties = new byte[5];
if (input.Read(properties, 0, 5) != 5)
{
throw new Exception("input .lzma is too short");
}
decoder.SetDecoderProperties(properties);
long fileLength = 0;
for (int i = 0; i < 8; i++)
{
int v = input.ReadByte();
if (v < 0) throw new Exception("Can't Read 1");
fileLength |= ((long)(byte)v) << (8 * i);
}
ICodeProgress prg = null;
if (onProgress != null)
{
prg = new DelegateCodeProgress(onProgress);
}
long compressedSize = input.Length - input.Position;
decoder.Code(input, output, compressedSize, fileLength, prg);
}
private class DelegateCodeProgress : ICodeProgress
{
private readonly Action<long, long> _handler;
public DelegateCodeProgress(Action<long, long> handler) => this._handler = handler;
public void SetProgress(long inSize, long outSize) => _handler(inSize, outSize);
}
}
}