637 lines
25 KiB
C#
637 lines
25 KiB
C#
using System;
|
|
using System.IO;
|
|
using System.Runtime.InteropServices;
|
|
using System.Collections;
|
|
using System.Collections.Generic;
|
|
using UnityEngine;
|
|
using UnityEngine.Networking;
|
|
|
|
|
|
public class brotli{
|
|
|
|
#if !UNITY_WEBPLAYER || UNITY_EDITOR
|
|
|
|
|
|
#if UNITY_5_4_OR_NEWER
|
|
#if (UNITY_ANDROID || UNITY_STANDALONE_LINUX) && !UNITY_EDITOR || UNITY_EDITOR_LINUX
|
|
private const string libname = "brotli";
|
|
#elif UNITY_EDITOR || UNITY_STANDALONE_WIN || UNITY_STANDALONE_OSX
|
|
private const string libname = "libbrotli";
|
|
#endif
|
|
#else
|
|
#if (UNITY_ANDROID || UNITY_STANDALONE_LINUX) && !UNITY_EDITOR
|
|
private const string libname = "brotli";
|
|
#endif
|
|
#if UNITY_EDITOR || UNITY_STANDALONE_WIN || UNITY_STANDALONE_OSX
|
|
private const string libname = "libbrotli";
|
|
#endif
|
|
#endif
|
|
|
|
#if UNITY_EDITOR || UNITY_STANDALONE_WIN || UNITY_STANDALONE_OSX || UNITY_ANDROID || UNITY_STANDALONE_LINUX
|
|
#if (UNITY_STANDALONE_OSX || UNITY_STANDALONE_LINUX || UNITY_ANDROID || UNITY_EDITOR_OSX || UNITY_EDITOR_LINUX)&& !UNITY_EDITOR_WIN
|
|
[DllImport(libname, EntryPoint = "setPermissions")]
|
|
internal static extern int setPermissions(string filePath, string _user, string _group, string _other);
|
|
#endif
|
|
|
|
[DllImport(libname, EntryPoint = "brCompress"
|
|
#if (UNITY_STANDALONE_WIN && ENABLE_IL2CPP) || UNITY_ANDROID
|
|
, CallingConvention = CallingConvention.Cdecl
|
|
#endif
|
|
)]
|
|
internal static extern int brCompress(string inFile, string outFile, IntPtr proc, int quality, int lgwin, int lgblock, int mode);
|
|
|
|
[DllImport(libname, EntryPoint = "brDecompresss"
|
|
#if (UNITY_STANDALONE_WIN && ENABLE_IL2CPP) || UNITY_ANDROID
|
|
, CallingConvention = CallingConvention.Cdecl
|
|
#endif
|
|
)]
|
|
internal static extern int brDecompresss(string inFile, string outFile, IntPtr proc, IntPtr FileBuffer, int fileBufferLength);
|
|
|
|
[DllImport(libname, EntryPoint = "brReleaseBuffer"
|
|
#if (UNITY_STANDALONE_WIN && ENABLE_IL2CPP) || UNITY_ANDROID
|
|
, CallingConvention = CallingConvention.Cdecl
|
|
#endif
|
|
)]
|
|
public static extern void brReleaseBuffer(IntPtr buffer);
|
|
|
|
[DllImport(libname, EntryPoint = "brCreate_Buffer"
|
|
#if (UNITY_STANDALONE_WIN && ENABLE_IL2CPP) || UNITY_ANDROID
|
|
, CallingConvention = CallingConvention.Cdecl
|
|
#endif
|
|
)]
|
|
public static extern IntPtr brCreate_Buffer(int size);
|
|
|
|
[DllImport(libname, EntryPoint = "brAddTo_Buffer"
|
|
#if (UNITY_STANDALONE_WIN && ENABLE_IL2CPP) || UNITY_ANDROID
|
|
, CallingConvention = CallingConvention.Cdecl
|
|
#endif
|
|
)]
|
|
private static extern void brAddTo_Buffer(IntPtr destination, int offset, IntPtr buffer, int len);
|
|
|
|
[DllImport(libname, EntryPoint = "brCompressBuffer"
|
|
#if (UNITY_STANDALONE_WIN && ENABLE_IL2CPP) || UNITY_ANDROID
|
|
, CallingConvention = CallingConvention.Cdecl
|
|
#endif
|
|
)]
|
|
internal static extern IntPtr brCompressBuffer( int bufferLength, IntPtr buffer, IntPtr encodedSize, IntPtr proc, int quality, int lgwin, int lgblock, int mode);
|
|
|
|
//this will work on small files with one meta block
|
|
[DllImport(libname, EntryPoint = "brGetDecodedSize"
|
|
#if (UNITY_STANDALONE_WIN && ENABLE_IL2CPP) || UNITY_ANDROID
|
|
, CallingConvention = CallingConvention.Cdecl
|
|
#endif
|
|
)]
|
|
internal static extern int brGetDecodedSize(int bufferLength, IntPtr buffer);
|
|
|
|
[DllImport(libname, EntryPoint = "brDecompressBuffer"
|
|
#if (UNITY_STANDALONE_WIN && ENABLE_IL2CPP) || UNITY_ANDROID
|
|
, CallingConvention = CallingConvention.Cdecl
|
|
#endif
|
|
)]
|
|
internal static extern int brDecompressBuffer(int bufferLength, IntPtr buffer, int outLength, IntPtr outbuffer);
|
|
#endif
|
|
|
|
#if (UNITY_IOS || UNITY_TVOS || UNITY_IPHONE || UNITY_WEBGL) && !UNITY_EDITOR
|
|
|
|
#if !UNITY_TVOS && !UNITY_WEBGL
|
|
[DllImport("__Internal")]
|
|
internal static extern int setPermissions(string filePath, string _user, string _group, string _other);
|
|
[DllImport("__Internal")]
|
|
internal static extern int brCompress( string inFile, string outFile, IntPtr proc, int quality, int lgwin, int lgblock, int mode);
|
|
[DllImport("__Internal")]
|
|
internal static extern int brDecompresss(string inFile, string outFile, IntPtr proc, IntPtr FileBuffer, int fileBufferLength);
|
|
|
|
#endif
|
|
#if !UNITY_WEBGL
|
|
[DllImport("__Internal")]
|
|
public static extern IntPtr brCreate_Buffer(int size);
|
|
[DllImport("__Internal")]
|
|
internal static extern void brAddTo_Buffer(IntPtr destination, int offset, IntPtr buffer, int len);
|
|
[DllImport("__Internal")]
|
|
internal static extern IntPtr brCompressBuffer( int bufferLength, IntPtr buffer, IntPtr encodedSize, IntPtr proc, int quality, int lgwin, int lgblock, int mode);
|
|
#endif
|
|
|
|
[DllImport("__Internal")]
|
|
public static extern void brReleaseBuffer(IntPtr buffer);
|
|
//this will work on small files with one meta block
|
|
[DllImport("__Internal")]
|
|
internal static extern int brGetDecodedSize(int bufferLength, IntPtr buffer);
|
|
[DllImport("__Internal")]
|
|
internal static extern int brDecompressBuffer(int bufferLength, IntPtr buffer, int outLength, IntPtr outbuffer);
|
|
|
|
#endif
|
|
|
|
#if (!UNITY_TVOS && !UNITY_WEBGL) || UNITY_EDITOR
|
|
// set permissions of a file in user, group, other.
|
|
// Each string should contain any or all chars of "rwx".
|
|
// returns 0 on success
|
|
public static int setFilePermissions(string filePath, string _user, string _group, string _other){
|
|
#if (UNITY_STANDALONE_OSX || UNITY_STANDALONE_LINUX || UNITY_ANDROID || UNITY_EDITOR_OSX || UNITY_EDITOR_LINUX || UNITY_IOS || UNITY_IPHONE) && !UNITY_EDITOR_WIN
|
|
if(!File.Exists(filePath)) return -1;
|
|
return setPermissions(filePath, _user, _group, _other);
|
|
#else
|
|
return -1;
|
|
#endif
|
|
}
|
|
|
|
|
|
//Helper functions
|
|
internal static GCHandle gcA(object o) {
|
|
return GCHandle.Alloc(o, GCHandleType.Pinned);
|
|
}
|
|
|
|
private static bool checkObject(object fileBuffer, string filePath, ref GCHandle fbuf, ref IntPtr fileBufferPointer, ref int fileBufferLength) {
|
|
if(fileBuffer is byte[]) { byte[] tempBuf = (byte[])fileBuffer; fbuf = gcA(tempBuf); fileBufferPointer = fbuf.AddrOfPinnedObject(); fileBufferLength = tempBuf.Length; return true; }
|
|
if(fileBuffer is IntPtr) { fileBufferPointer = (IntPtr)fileBuffer; fileBufferLength = Convert.ToInt32(filePath); }
|
|
return false;
|
|
}
|
|
|
|
// Compress a file to brotli format.
|
|
//
|
|
// Full paths to the files should be provided.
|
|
// inFile: The input file
|
|
// outFile: The output file
|
|
// proc: A single item ulong array to provide progress of compression
|
|
//
|
|
// quality: (0 - 11) quality of compression (0 = faster/bigger - 11 = slower/smaller).
|
|
//
|
|
// Base 2 logarithm of the sliding window size. Range is 10 to 24.
|
|
// lgwin : (10 - 24) memory used for compression (higher numbers use more ram)
|
|
//
|
|
// Base 2 logarithm of the maximum input block size. Range is 16 to 24.
|
|
// If set to 0, the value will be set based on the quality.
|
|
// lgblock: 0 for auto or 16-24
|
|
// mode : (0 - 2) 0 = default, 1 = utf8 text, 2 = woff 2.0 font
|
|
//
|
|
// error codes: 1 : OK
|
|
// -1 : compression failed
|
|
// -2 : not enough memory
|
|
// -3 : could not close in file
|
|
// -4 : could not close out file
|
|
// -5 : no input file found
|
|
//
|
|
public static int compressFile(string inFile, string outFile, ulong[] proc, int quality = 9, int lgwin = 19, int lgblock = 0, int mode = 0) {
|
|
if(!File.Exists(inFile)) return -5;
|
|
if (quality < 0) quality = 1; if (quality > 11) quality = 11;
|
|
if (lgwin < 10) lgwin = 10; if(lgwin > 24) lgwin = 24;
|
|
if(proc == null) proc = new ulong[1];
|
|
GCHandle cbuf = GCHandle.Alloc(proc, GCHandleType.Pinned);
|
|
int res = brCompress( @inFile, @outFile,cbuf.AddrOfPinnedObject(), quality, lgwin, lgblock, mode);
|
|
cbuf.Free();
|
|
return res;
|
|
}
|
|
|
|
// Decompress a brotli file.
|
|
//
|
|
// Full paths to the files should be provided.
|
|
// inFile: The input file
|
|
// outFile: The output file
|
|
// proc: A single item ulong array to provide progress of decompression
|
|
// FileBuffer: A buffer that holds a brotli file. When assigned the function will read from this buffer and will ignore the filePath. (Linux, iOS, Android, MacOSX)
|
|
// returns: 1 on success.
|
|
//
|
|
// error codes: 1 : OK
|
|
// -1 : failed to write output
|
|
// -2 : corrupt input
|
|
// -3 : could not close in file
|
|
// -4 : could not close out file
|
|
// -5 : no input file found
|
|
//
|
|
public static int decompressFile(string inFile, string outFile, ulong[] proc, object fileBuffer = null) {
|
|
if(fileBuffer == null && !File.Exists(inFile)) return -5;
|
|
if(proc == null) proc = new ulong[1];
|
|
GCHandle cbuf = GCHandle.Alloc(proc, GCHandleType.Pinned);
|
|
int res;
|
|
|
|
#if (UNITY_IPHONE || UNITY_IOS || UNITY_STANDALONE_OSX || UNITY_ANDROID || UNITY_STANDALONE_LINUX || UNITY_EDITOR) && !UNITY_EDITOR_WIN
|
|
if(fileBuffer != null) {
|
|
int fileBufferLength = 0;
|
|
IntPtr fileBufferPointer = IntPtr.Zero;
|
|
GCHandle fbuf = gcA(null);
|
|
bool managed = checkObject(fileBuffer, inFile, ref fbuf, ref fileBufferPointer, ref fileBufferLength);
|
|
|
|
if (!managed && fileBufferLength == 0) { Debug.Log("Please provide a valid native buffer size as a string in filePath"); return -5; }
|
|
|
|
res = brDecompresss(null, @outFile, cbuf.AddrOfPinnedObject(), fileBufferPointer, fileBufferLength);
|
|
|
|
fbuf.Free();
|
|
cbuf.Free();
|
|
return res;
|
|
}
|
|
#endif
|
|
|
|
res = brDecompresss(@inFile, @outFile, cbuf.AddrOfPinnedObject(), IntPtr.Zero, 0);
|
|
cbuf.Free();
|
|
return res;
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
// Get the uncompressed size of a brotli buffer.
|
|
// This will work only on small buffers with one metablock. Otherwise use the includeSize/hasFooter flags with the buffer functions.
|
|
//
|
|
// inBuffer: the input buffer that stores a brotli compressed buffer.
|
|
public static int getDecodedSize(byte[] inBuffer)
|
|
{
|
|
GCHandle cbuf = GCHandle.Alloc(inBuffer, GCHandleType.Pinned);
|
|
int res = brGetDecodedSize(inBuffer.Length, cbuf.AddrOfPinnedObject());
|
|
cbuf.Free();
|
|
return res;
|
|
}
|
|
|
|
#if !UNITY_WEBGL || UNITY_EDITOR
|
|
// Compress a byte buffer in brotli format.
|
|
//
|
|
// inBuffer: the uncompressed buffer.
|
|
// outBuffer: a referenced buffer that will store the compressed data. (it should be large enough to store it.)
|
|
// proc: A single item referenced ulong array to provide progress of compression
|
|
//
|
|
// includeSize: include the uncompressed size of the buffer in the resulted compressed one because brotli does not support it for larger then 1 metablock.
|
|
//
|
|
// quality: (0 - 11) quality of compression (0 = faster/bigger - 11 = slower/smaller).
|
|
//
|
|
// Base 2 logarithm of the sliding window size. Range is 10 to 24.
|
|
// lgwin : (10 - 24) memory used for compression (higher numbers use more ram)
|
|
//
|
|
// Base 2 logarithm of the maximum input block size. Range is 16 to 24.
|
|
// If set to 0, the value will be set based on the quality.
|
|
// lgblock: 0 for auto or 16-24
|
|
// mode : (0 - 2) 0 = default, 1 = utf8 text, 2 = woff 2.0 font
|
|
// returns true on success
|
|
//
|
|
public static bool compressBuffer(byte[] inBuffer, ref byte[] outBuffer, ulong[] proc, bool includeSize = false, int quality = 9, int lgwin = 19, int lgblock = 0, int mode = 0) {
|
|
if (quality < 0) quality = 1; if (quality > 11) quality = 11;
|
|
if (lgwin < 10) lgwin = 10; if(lgwin > 24) lgwin = 24;
|
|
|
|
GCHandle cbuf = GCHandle.Alloc(inBuffer, GCHandleType.Pinned);
|
|
IntPtr ptr;
|
|
|
|
int size = 0;
|
|
byte[] bsiz = null;
|
|
int[] esiz = new int[1];//the compressed size
|
|
GCHandle ebuf = GCHandle.Alloc(esiz, GCHandleType.Pinned);
|
|
|
|
//if the uncompressed size of the buffer should be included. This is a hack since brotli lib does not support this on larger buffers.
|
|
if (includeSize) {
|
|
bsiz = new byte[4];
|
|
size = 4;
|
|
bsiz = BitConverter.GetBytes(inBuffer.Length);
|
|
if (!BitConverter.IsLittleEndian) Array.Reverse(bsiz);
|
|
}
|
|
|
|
if(proc == null) proc = new ulong[1];
|
|
GCHandle pbuf = GCHandle.Alloc(proc, GCHandleType.Pinned);
|
|
|
|
ptr = brCompressBuffer(inBuffer.Length, cbuf.AddrOfPinnedObject(), ebuf.AddrOfPinnedObject(), pbuf.AddrOfPinnedObject(), quality, lgwin, lgblock, mode);
|
|
|
|
cbuf.Free(); ebuf.Free(); pbuf.Free();
|
|
|
|
if (ptr == IntPtr.Zero) { brReleaseBuffer(ptr); esiz = null; bsiz = null; return false; }
|
|
|
|
System.Array.Resize(ref outBuffer, esiz[0] + size);
|
|
|
|
//add the uncompressed size to the buffer
|
|
if (includeSize) { for (int i = 0; i < 4; i++) outBuffer[i + esiz[0]] = bsiz[i]; }
|
|
|
|
Marshal.Copy( ptr, outBuffer, 0, esiz[0] );
|
|
|
|
brReleaseBuffer(ptr);
|
|
esiz = null;
|
|
bsiz = null;
|
|
|
|
return true;
|
|
}
|
|
|
|
// same as above only this function returns a new created buffer with the compressed data.
|
|
//
|
|
public static byte[] compressBuffer(byte[] inBuffer, int[] proc, bool includeSize = false, int quality = 9, int lgwin = 19, int lgblock = 0, int mode = 0) {
|
|
if (quality < 0) quality = 1; if (quality > 11) quality = 11;
|
|
if (lgwin < 10) lgwin = 10; if(lgwin > 24) lgwin = 24;
|
|
|
|
GCHandle cbuf = GCHandle.Alloc(inBuffer, GCHandleType.Pinned);
|
|
IntPtr ptr;
|
|
|
|
int size = 0;
|
|
byte[] bsiz = null;
|
|
int[] esiz = new int[1];//the compressed size
|
|
GCHandle ebuf = GCHandle.Alloc(esiz, GCHandleType.Pinned);
|
|
|
|
// if the uncompressed size of the buffer should be included. This is a hack since brotli lib does not support this on larger buffers.
|
|
if (includeSize) {
|
|
bsiz = new byte[4];
|
|
size = 4;
|
|
bsiz = BitConverter.GetBytes(inBuffer.Length);
|
|
if (!BitConverter.IsLittleEndian) Array.Reverse(bsiz);
|
|
}
|
|
|
|
if(proc == null) proc = new int[1];
|
|
GCHandle pbuf = GCHandle.Alloc(proc, GCHandleType.Pinned);
|
|
|
|
ptr = brCompressBuffer(inBuffer.Length, cbuf.AddrOfPinnedObject(), ebuf.AddrOfPinnedObject(), pbuf.AddrOfPinnedObject(), quality, lgwin, lgblock, mode);
|
|
|
|
cbuf.Free(); ebuf.Free(); pbuf.Free();
|
|
|
|
if (ptr == IntPtr.Zero) { brReleaseBuffer(ptr); esiz = null; bsiz = null; return null; }
|
|
|
|
byte[] outBuffer = new byte[esiz[0] + size];
|
|
|
|
//add the uncompressed size to the buffer
|
|
if (includeSize) { for (int i = 0; i < 4; i++) outBuffer[i + esiz[0]] = bsiz[i]; }
|
|
|
|
Marshal.Copy( ptr, outBuffer, 0, esiz[0] );
|
|
|
|
brReleaseBuffer(ptr);
|
|
esiz = null;
|
|
bsiz = null;
|
|
|
|
return outBuffer;
|
|
}
|
|
|
|
// Same as above, only this time the compressed buffer is written in a fixed size buffer.
|
|
// The compressed size in bytes is returned. If includeSize is true, then 4 extra bytes are written at the end of the buffer.
|
|
// The fixed size buffer should be larger then the compressed size returned.
|
|
//
|
|
public static int compressBuffer(byte[] inBuffer, byte[] outBuffer, int[] proc, bool includeSize = false, int quality = 9, int lgwin = 19, int lgblock = 0, int mode = 0) {
|
|
if (quality < 0) quality = 1; if (quality > 11) quality = 11;
|
|
if (lgwin < 10) lgwin = 10; if(lgwin > 24) lgwin = 24;
|
|
|
|
GCHandle cbuf = GCHandle.Alloc(inBuffer, GCHandleType.Pinned);
|
|
IntPtr ptr;
|
|
|
|
int size = 0;
|
|
byte[] bsiz = null;
|
|
int res = 0;
|
|
int[] esiz = new int[1];//the compressed size
|
|
GCHandle ebuf = GCHandle.Alloc(esiz, GCHandleType.Pinned);
|
|
|
|
// if the uncompressed size of the buffer should be included. This is a hack since brotli lib does not support this on larger buffers.
|
|
if (includeSize) {
|
|
bsiz = new byte[4];
|
|
size = 4;
|
|
bsiz = BitConverter.GetBytes(inBuffer.Length);
|
|
if (!BitConverter.IsLittleEndian) Array.Reverse(bsiz);
|
|
}
|
|
|
|
if(proc == null) proc = new int[1];
|
|
GCHandle pbuf = GCHandle.Alloc(proc, GCHandleType.Pinned);
|
|
|
|
ptr = brCompressBuffer(inBuffer.Length, cbuf.AddrOfPinnedObject(), ebuf.AddrOfPinnedObject(), pbuf.AddrOfPinnedObject(), quality, lgwin, lgblock, mode);
|
|
|
|
cbuf.Free(); ebuf.Free(); pbuf.Free();
|
|
res = esiz[0];
|
|
|
|
if (ptr == IntPtr.Zero || outBuffer.Length < (esiz[0] + size)) { brReleaseBuffer(ptr); esiz = null; bsiz = null; return 0; }
|
|
|
|
Marshal.Copy( ptr, outBuffer, 0, esiz[0] );
|
|
|
|
if (includeSize) { for (int i = 0; i < 4; i++) outBuffer[i + esiz[0]] = bsiz[i]; }
|
|
|
|
brReleaseBuffer(ptr);
|
|
esiz = null;
|
|
bsiz = null;
|
|
|
|
return res + size;
|
|
}
|
|
|
|
#endif
|
|
|
|
// Decompress a brotli compressed buffer to a referenced buffer.
|
|
//
|
|
// inBuffer: the brotli compressed buffer
|
|
// outBuffer: a referenced buffer that will be resized to store the uncompressed data.
|
|
// useFooter: if the input Buffer has the uncompressed size info.
|
|
// unCompressedSize: if unCompressedSize is > 0 then this is the uncompressed size that will be used. Useful when decompressing brotli buffers created from servers.
|
|
//
|
|
// returns true on success
|
|
//
|
|
public static bool decompressBuffer(byte[] inBuffer, ref byte[] outBuffer, bool useFooter = false, int unCompressedSize = 0) {
|
|
GCHandle cbuf = GCHandle.Alloc(inBuffer, GCHandleType.Pinned);
|
|
int uncompressedSize = 0, res2 = inBuffer.Length;
|
|
|
|
if (unCompressedSize == 0) {
|
|
if (useFooter) {
|
|
res2 -= 4;
|
|
uncompressedSize = (int)BitConverter.ToInt32(inBuffer, res2);
|
|
} else {
|
|
//use the brotli native method to get the uncompressed size (this will work on buffers with one metablock)
|
|
uncompressedSize = getDecodedSize(inBuffer);
|
|
}
|
|
} else {
|
|
uncompressedSize = unCompressedSize;
|
|
}
|
|
|
|
System.Array.Resize(ref outBuffer, uncompressedSize);
|
|
|
|
GCHandle obuf = GCHandle.Alloc(outBuffer, GCHandleType.Pinned);
|
|
|
|
int res = brDecompressBuffer(inBuffer.Length, cbuf.AddrOfPinnedObject(), uncompressedSize, obuf.AddrOfPinnedObject());
|
|
|
|
cbuf.Free();
|
|
obuf.Free();
|
|
|
|
if(res == 1) return true; else return false;
|
|
}
|
|
|
|
// same as above only this time the uncompressed data is returned in a new created buffer
|
|
//
|
|
public static byte[] decompressBuffer(byte[] inBuffer, bool useFooter = false, int unCompressedSize = 0) {
|
|
GCHandle cbuf = GCHandle.Alloc(inBuffer, GCHandleType.Pinned);
|
|
int uncompressedSize = 0, res2 = inBuffer.Length;
|
|
|
|
if (unCompressedSize == 0) {
|
|
if (useFooter) {
|
|
res2 -= 4;
|
|
uncompressedSize = (int)BitConverter.ToInt32(inBuffer, res2);
|
|
} else {
|
|
//use the brotli native method to get the uncompressed size (this will work on buffers with one metablock)
|
|
uncompressedSize = getDecodedSize(inBuffer);
|
|
}
|
|
} else {
|
|
uncompressedSize = unCompressedSize;
|
|
}
|
|
|
|
byte[] outBuffer = new byte[uncompressedSize];
|
|
|
|
GCHandle obuf = GCHandle.Alloc(outBuffer, GCHandleType.Pinned);
|
|
|
|
int res = brDecompressBuffer(inBuffer.Length, cbuf.AddrOfPinnedObject(), uncompressedSize, obuf.AddrOfPinnedObject());
|
|
|
|
cbuf.Free();
|
|
obuf.Free();
|
|
|
|
if(res == 1) return outBuffer; else return null;
|
|
}
|
|
|
|
|
|
// same as above only the decompressed data will be stored in a fixed size outBuffer.
|
|
// make sure the fixed buffer is big enough to store the data.
|
|
//
|
|
// returns: uncompressed size in bytes.
|
|
//
|
|
public static int decompressBuffer(byte[] inBuffer, byte[] outBuffer, bool useFooter = false, int unCompressedSize = 0) {
|
|
GCHandle cbuf = GCHandle.Alloc(inBuffer, GCHandleType.Pinned);
|
|
int uncompressedSize = 0, res2 = inBuffer.Length;
|
|
|
|
if (unCompressedSize == 0) {
|
|
if (useFooter) {
|
|
res2 -= 4;
|
|
uncompressedSize = (int)BitConverter.ToInt32(inBuffer, res2);
|
|
} else {
|
|
//use the brotli native method to get the uncompressed size (this will work on buffers with one metablock)
|
|
uncompressedSize = getDecodedSize(inBuffer);
|
|
}
|
|
} else {
|
|
uncompressedSize = unCompressedSize;
|
|
}
|
|
|
|
GCHandle obuf = GCHandle.Alloc(outBuffer, GCHandleType.Pinned);
|
|
|
|
int res = brDecompressBuffer(inBuffer.Length, cbuf.AddrOfPinnedObject(), uncompressedSize, obuf.AddrOfPinnedObject());
|
|
|
|
cbuf.Free();
|
|
obuf.Free();
|
|
|
|
if(res == 1) return uncompressedSize; else return 0;
|
|
}
|
|
|
|
#if !UNITY_WEBGL && !UNITY_TVOS
|
|
// A reusable native memory pointer for downloading files.
|
|
public static IntPtr nativeBuffer = IntPtr.Zero;
|
|
public static bool nativeBufferIsBeingUsed = false;
|
|
public static int nativeOffset = 0;
|
|
|
|
// A Coroutine to dowload a file to a native/unmaged memory buffer.
|
|
// You can call it for an IntPtr.
|
|
//
|
|
//
|
|
// This function can only be called for one file at a time. Don't use it to call multiple files at once.
|
|
//
|
|
// This is useful to avoid memory spikes when downloading large files and intend to decompress from memory.
|
|
// With the old method, a copy of the downloaded file to memory would be produced by pinning the buffer to memory.
|
|
// Now with this method, it is downloaded to memory and can be manipulated with no memory spikes.
|
|
//
|
|
// In any case, if you don't need the created in-Memory file, you should use the brotli.brReleaseBuffer function to free the memory!
|
|
//
|
|
// Parameters:
|
|
//
|
|
// url: The url of the file you want to download to a native memory buffer.
|
|
// downloadDone: Informs a bool that the download of the file to memory is done.
|
|
// pointer: An IntPtr for a native memory buffer
|
|
// fileSize: The size of the downloaded file will be returned here.
|
|
public static IEnumerator downloadBrFileNative(string url, Action<bool> downloadDone, Action<IntPtr> pointer = null, Action<int> fileSize = null) {
|
|
// Get the file lenght first, so we create a correct size native memory buffer.
|
|
UnityWebRequest wr = UnityWebRequest.Head(url);
|
|
|
|
nativeBufferIsBeingUsed = true;
|
|
|
|
yield return wr.SendWebRequest();
|
|
string size = wr.GetResponseHeader("Content-Length");
|
|
|
|
nativeBufferIsBeingUsed = false;
|
|
|
|
#if UNITY_2020_1_OR_NEWER
|
|
if (wr.result == UnityWebRequest.Result.ConnectionError || wr.result == UnityWebRequest.Result.ProtocolError) {
|
|
#else
|
|
if (wr.isNetworkError || wr.isHttpError) {
|
|
#endif
|
|
Debug.LogError("Error While Getting Length: " + wr.error);
|
|
} else {
|
|
if (!nativeBufferIsBeingUsed) {
|
|
|
|
//get the size of the zip
|
|
int zipSize = Convert.ToInt32(size);
|
|
|
|
// If the zip size is larger then 0
|
|
if (zipSize > 0) {
|
|
|
|
nativeBuffer = brCreate_Buffer(zipSize);
|
|
nativeBufferIsBeingUsed = true;
|
|
|
|
// buffer for the download
|
|
byte[] bytes = new byte[2048];
|
|
nativeOffset = 0;
|
|
|
|
using (UnityWebRequest wwwSK = UnityWebRequest.Get(url)) {
|
|
|
|
// Here we call our custom webrequest function to download our archive to a native memory buffer.
|
|
wwwSK.downloadHandler = new CustomWebRequest5(bytes);
|
|
|
|
yield return wwwSK.SendWebRequest();
|
|
|
|
if (wwwSK.error != null) {
|
|
Debug.Log(wwwSK.error);
|
|
} else {
|
|
downloadDone(true);
|
|
|
|
if(pointer != null) { pointer(nativeBuffer); fileSize(zipSize); }
|
|
|
|
//reset intermediate buffer params.
|
|
nativeBufferIsBeingUsed = false;
|
|
nativeOffset = 0;
|
|
nativeBuffer = IntPtr.Zero;
|
|
|
|
//Debug.Log("Custom download done");
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
} else { Debug.LogError("Native buffer is being used, or not yet freed!"); }
|
|
}
|
|
}
|
|
|
|
|
|
// A custom WebRequest Override to download data to a native-unmanaged memory buffer.
|
|
public class CustomWebRequest5 : DownloadHandlerScript {
|
|
|
|
public CustomWebRequest5()
|
|
: base()
|
|
{
|
|
}
|
|
|
|
public CustomWebRequest5(byte[] buffer)
|
|
: base(buffer)
|
|
{
|
|
}
|
|
|
|
protected override byte[] GetData() { return null; }
|
|
|
|
|
|
protected override bool ReceiveData(byte[] bytesFromServer, int dataLength) {
|
|
if (bytesFromServer == null || bytesFromServer.Length < 1) {
|
|
Debug.Log("CustomWebRequest: Received a null/empty buffer");
|
|
return false;
|
|
}
|
|
|
|
var pbuf = gcA(bytesFromServer);
|
|
|
|
//Process byteFromServer
|
|
brAddTo_Buffer(nativeBuffer, nativeOffset, pbuf.AddrOfPinnedObject(), dataLength );
|
|
nativeOffset += dataLength;
|
|
pbuf.Free();
|
|
|
|
return true;
|
|
}
|
|
|
|
// Use the below functions only when needed. You get the same functionality from the main coroutine.
|
|
/*
|
|
// If all data has been received from the server
|
|
protected override void CompleteContent()
|
|
{
|
|
//Debug.Log(Download Complete.");
|
|
}
|
|
|
|
// If a Content-Length header is received from the server.
|
|
protected override void ReceiveContentLength(int fileLength)
|
|
{
|
|
//Debug.Log("ReceiveContentLength: " + fileLength);
|
|
}
|
|
*/
|
|
}
|
|
#endif
|
|
#endif
|
|
}
|
|
|