diff --git a/FModel/FModel.csproj b/FModel/FModel.csproj
index 39b86ba7..a17d4c40 100644
--- a/FModel/FModel.csproj
+++ b/FModel/FModel.csproj
@@ -136,6 +136,7 @@
+
diff --git a/FModel/PakReader/Parsers/OodleStream.cs b/FModel/PakReader/Parsers/OodleStream.cs
index 191dcbce..b7e345bf 100644
--- a/FModel/PakReader/Parsers/OodleStream.cs
+++ b/FModel/PakReader/Parsers/OodleStream.cs
@@ -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
};
}
- ///
- /// Oodle Library Path
- ///
- private const string OodleLibraryPath = "oo2core_5_win64";
-
protected override void Dispose(bool disposing)
{
try
@@ -45,7 +42,7 @@ namespace PakReader.Parsers
///
/// Oodle64 Decompression Method
///
- [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);
///
diff --git a/FModel/Utils/Oodle.cs b/FModel/Utils/Oodle.cs
new file mode 100644
index 00000000..69d5436a
--- /dev/null
+++ b/FModel/Utils/Oodle.cs
@@ -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 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;
+ }
+ }
+}
\ No newline at end of file
diff --git a/FModel/Utils/SevenZipHelper.cs b/FModel/Utils/SevenZipHelper.cs
new file mode 100644
index 00000000..8ad31393
--- /dev/null
+++ b/FModel/Utils/SevenZipHelper.cs
@@ -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
+ {
+ ///64 KiB
+ VerySmall = 1 << 16,
+ ///1 MiB
+ Small = 1 << 20,
+ ///4 MiB
+ Medium = 1 << 22,
+ ///8 MiB
+ Large = 1 << 23,
+ ///16 MiB
+ Larger = 1 << 24,
+ ///64 MiB
+ VeryLarge = 1 << 26,
+ }
+ public static class LZMA
+ {
+ public static void Compress(Stream input, Stream output, LzmaSpeed speed = LzmaSpeed.Fastest, DictionarySize dictionarySize = DictionarySize.VerySmall, Action 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? 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 _handler;
+ public DelegateCodeProgress(Action handler) => this._handler = handler;
+ public void SetProgress(long inSize, long outSize) => _handler(inSize, outSize);
+ }
+ }
+}
\ No newline at end of file