using System; using System.Collections; using System.Collections.Concurrent; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Runtime.Versioning; using System.Text; using System.Text.RegularExpressions; using AssetsTools.NET.Extra; using AssetsTools.NET.Extra.Decompressors.LZ4; using LZ4ps; using SevenZip; using SevenZip.Compression.LZ; using SevenZip.Compression.LZMA; using SevenZip.Compression.RangeCoder; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: AssemblyTitle("AssetsTools.NET")] [assembly: AssemblyDescription("A remake and port of SeriousCache's AssetTools")] [assembly: AssemblyConfiguration("")] [assembly: AssemblyCompany("nesrak1")] [assembly: AssemblyProduct("AssetsTools.NET")] [assembly: AssemblyCopyright("Written by nes")] [assembly: AssemblyTrademark("")] [assembly: ComVisible(false)] [assembly: Guid("e09d5ac2-1a2e-4ec1-94ad-3f5e22f17658")] [assembly: AssemblyFileVersion("3.0.0.0")] [assembly: TargetFramework(".NETStandard,Version=v2.0", FrameworkDisplayName = ".NET Standard 2.0")] [assembly: AssemblyVersion("3.0.0.0")] namespace SevenZip { internal class CRC { public static readonly uint[] Table; private uint _value = uint.MaxValue; static CRC() { Table = new uint[256]; for (uint num = 0u; num < 256; num++) { uint num2 = num; for (int i = 0; i < 8; i++) { num2 = (((num2 & 1) == 0) ? (num2 >> 1) : ((num2 >> 1) ^ 0xEDB88320u)); } Table[num] = num2; } } public void Init() { _value = uint.MaxValue; } public void UpdateByte(byte b) { _value = Table[(byte)_value ^ b] ^ (_value >> 8); } public void Update(byte[] data, uint offset, uint size) { for (uint num = 0u; num < size; num++) { _value = Table[(byte)_value ^ data[offset + num]] ^ (_value >> 8); } } public uint GetDigest() { return _value ^ 0xFFFFFFFFu; } private static uint CalculateDigest(byte[] data, uint offset, uint size) { CRC cRC = new CRC(); cRC.Update(data, offset, size); return cRC.GetDigest(); } private static bool VerifyDigest(uint digest, byte[] data, uint offset, uint size) { return CalculateDigest(data, offset, size) == digest; } } internal class DataErrorException : ApplicationException { public DataErrorException() : base("Data Error") { } } internal class InvalidParamException : ApplicationException { public InvalidParamException() : base("Invalid Parameter") { } } public interface ICodeProgress { void SetProgress(long inSize, long outSize); } public interface ICoder { void Code(Stream inStream, Stream outStream, long inSize, long outSize, ICodeProgress progress); } public enum CoderPropID { DefaultProp, DictionarySize, UsedMemorySize, Order, BlockSize, PosStateBits, LitContextBits, LitPosBits, NumFastBytes, MatchFinder, MatchFinderCycles, NumPasses, Algorithm, NumThreads, EndMarker } public interface ISetCoderProperties { void SetCoderProperties(CoderPropID[] propIDs, object[] properties); } public interface IWriteCoderProperties { void WriteCoderProperties(Stream outStream); } public interface ISetDecoderProperties { void SetDecoderProperties(byte[] properties); } } namespace SevenZip.Compression.RangeCoder { internal class Encoder { public const uint kTopValue = 16777216u; private Stream Stream; public ulong Low; public uint Range; private uint _cacheSize; private byte _cache; private long StartPosition; public void SetStream(Stream stream) { Stream = stream; } public void ReleaseStream() { Stream = null; } public void Init() { StartPosition = Stream.Position; Low = 0uL; Range = uint.MaxValue; _cacheSize = 1u; _cache = 0; } public void FlushData() { for (int i = 0; i < 5; i++) { ShiftLow(); } } public void FlushStream() { Stream.Flush(); } public void CloseStream() { Stream.Close(); } public void Encode(uint start, uint size, uint total) { Low += start * (Range /= total); Range *= size; while (Range < 16777216) { Range <<= 8; ShiftLow(); } } public void ShiftLow() { if ((uint)Low < 4278190080u || (int)(Low >> 32) == 1) { byte b = _cache; do { Stream.WriteByte((byte)(b + (Low >> 32))); b = byte.MaxValue; } while (--_cacheSize != 0); _cache = (byte)((uint)Low >> 24); } _cacheSize++; Low = (uint)((int)Low << 8); } public void EncodeDirectBits(uint v, int numTotalBits) { for (int num = numTotalBits - 1; num >= 0; num--) { Range >>= 1; if (((v >> num) & 1) == 1) { Low += Range; } if (Range < 16777216) { Range <<= 8; ShiftLow(); } } } public void EncodeBit(uint size0, int numTotalBits, uint symbol) { uint num = (Range >> numTotalBits) * size0; if (symbol == 0) { Range = num; } else { Low += num; Range -= num; } while (Range < 16777216) { Range <<= 8; ShiftLow(); } } public long GetProcessedSizeAdd() { return _cacheSize + Stream.Position - StartPosition + 4; } } internal class Decoder { public const uint kTopValue = 16777216u; public uint Range; public uint Code; public Stream Stream; public void Init(Stream stream) { Stream = stream; Code = 0u; Range = uint.MaxValue; for (int i = 0; i < 5; i++) { Code = (Code << 8) | (byte)Stream.ReadByte(); } } public void ReleaseStream() { Stream = null; } public void CloseStream() { Stream.Close(); } public void Normalize() { while (Range < 16777216) { Code = (Code << 8) | (byte)Stream.ReadByte(); Range <<= 8; } } public void Normalize2() { if (Range < 16777216) { Code = (Code << 8) | (byte)Stream.ReadByte(); Range <<= 8; } } public uint GetThreshold(uint total) { return Code / (Range /= total); } public void Decode(uint start, uint size, uint total) { Code -= start * Range; Range *= size; Normalize(); } public uint DecodeDirectBits(int numTotalBits) { uint num = Range; uint num2 = Code; uint num3 = 0u; for (int num4 = numTotalBits; num4 > 0; num4--) { num >>= 1; uint num5 = num2 - num >> 31; num2 -= num & (num5 - 1); num3 = (num3 << 1) | (1 - num5); if (num < 16777216) { num2 = (num2 << 8) | (byte)Stream.ReadByte(); num <<= 8; } } Range = num; Code = num2; return num3; } public uint DecodeBit(uint size0, int numTotalBits) { uint num = (Range >> numTotalBits) * size0; uint result; if (Code < num) { result = 0u; Range = num; } else { result = 1u; Code -= num; Range -= num; } Normalize(); return result; } } internal struct BitEncoder { public const int kNumBitModelTotalBits = 11; public const uint kBitModelTotal = 2048u; private const int kNumMoveBits = 5; private const int kNumMoveReducingBits = 2; public const int kNumBitPriceShiftBits = 6; private uint Prob; private static uint[] ProbPrices; public void Init() { Prob = 1024u; } public void UpdateModel(uint symbol) { if (symbol == 0) { Prob += 2048 - Prob >> 5; } else { Prob -= Prob >> 5; } } public void Encode(Encoder encoder, uint symbol) { uint num = (encoder.Range >> 11) * Prob; if (symbol == 0) { encoder.Range = num; Prob += 2048 - Prob >> 5; } else { encoder.Low += num; encoder.Range -= num; Prob -= Prob >> 5; } if (encoder.Range < 16777216) { encoder.Range <<= 8; encoder.ShiftLow(); } } static BitEncoder() { ProbPrices = new uint[512]; for (int num = 8; num >= 0; num--) { int num2 = 1 << 9 - num - 1; uint num3 = (uint)(1 << 9 - num); for (uint num4 = (uint)num2; num4 < num3; num4++) { ProbPrices[num4] = (uint)(num << 6) + (num3 - num4 << 6 >> 9 - num - 1); } } } public uint GetPrice(uint symbol) { return ProbPrices[(((Prob - symbol) ^ (int)(0 - symbol)) & 0x7FF) >> 2]; } public uint GetPrice0() { return ProbPrices[Prob >> 2]; } public uint GetPrice1() { return ProbPrices[2048 - Prob >> 2]; } } internal struct BitDecoder { public const int kNumBitModelTotalBits = 11; public const uint kBitModelTotal = 2048u; private const int kNumMoveBits = 5; private uint Prob; public void UpdateModel(int numMoveBits, uint symbol) { if (symbol == 0) { Prob += 2048 - Prob >> numMoveBits; } else { Prob -= Prob >> numMoveBits; } } public void Init() { Prob = 1024u; } public uint Decode(Decoder rangeDecoder) { uint num = (rangeDecoder.Range >> 11) * Prob; if (rangeDecoder.Code < num) { rangeDecoder.Range = num; Prob += 2048 - Prob >> 5; if (rangeDecoder.Range < 16777216) { rangeDecoder.Code = (rangeDecoder.Code << 8) | (byte)rangeDecoder.Stream.ReadByte(); rangeDecoder.Range <<= 8; } return 0u; } rangeDecoder.Range -= num; rangeDecoder.Code -= num; Prob -= Prob >> 5; if (rangeDecoder.Range < 16777216) { rangeDecoder.Code = (rangeDecoder.Code << 8) | (byte)rangeDecoder.Stream.ReadByte(); rangeDecoder.Range <<= 8; } return 1u; } } internal struct BitTreeEncoder { private BitEncoder[] Models; private int NumBitLevels; public BitTreeEncoder(int numBitLevels) { NumBitLevels = numBitLevels; Models = new BitEncoder[1 << numBitLevels]; } public void Init() { for (uint num = 1u; num < 1 << NumBitLevels; num++) { Models[num].Init(); } } public void Encode(Encoder rangeEncoder, uint symbol) { uint num = 1u; int num2 = NumBitLevels; while (num2 > 0) { num2--; uint num3 = (symbol >> num2) & 1u; Models[num].Encode(rangeEncoder, num3); num = (num << 1) | num3; } } public void ReverseEncode(Encoder rangeEncoder, uint symbol) { uint num = 1u; for (uint num2 = 0u; num2 < NumBitLevels; num2++) { uint num3 = symbol & 1u; Models[num].Encode(rangeEncoder, num3); num = (num << 1) | num3; symbol >>= 1; } } public uint GetPrice(uint symbol) { uint num = 0u; uint num2 = 1u; int num3 = NumBitLevels; while (num3 > 0) { num3--; uint num4 = (symbol >> num3) & 1u; num += Models[num2].GetPrice(num4); num2 = (num2 << 1) + num4; } return num; } public uint ReverseGetPrice(uint symbol) { uint num = 0u; uint num2 = 1u; for (int num3 = NumBitLevels; num3 > 0; num3--) { uint num4 = symbol & 1u; symbol >>= 1; num += Models[num2].GetPrice(num4); num2 = (num2 << 1) | num4; } return num; } public static uint ReverseGetPrice(BitEncoder[] Models, uint startIndex, int NumBitLevels, uint symbol) { uint num = 0u; uint num2 = 1u; for (int num3 = NumBitLevels; num3 > 0; num3--) { uint num4 = symbol & 1u; symbol >>= 1; num += Models[startIndex + num2].GetPrice(num4); num2 = (num2 << 1) | num4; } return num; } public static void ReverseEncode(BitEncoder[] Models, uint startIndex, Encoder rangeEncoder, int NumBitLevels, uint symbol) { uint num = 1u; for (int i = 0; i < NumBitLevels; i++) { uint num2 = symbol & 1u; Models[startIndex + num].Encode(rangeEncoder, num2); num = (num << 1) | num2; symbol >>= 1; } } } internal struct BitTreeDecoder { private BitDecoder[] Models; private int NumBitLevels; public BitTreeDecoder(int numBitLevels) { NumBitLevels = numBitLevels; Models = new BitDecoder[1 << numBitLevels]; } public void Init() { for (uint num = 1u; num < 1 << NumBitLevels; num++) { Models[num].Init(); } } public uint Decode(Decoder rangeDecoder) { uint num = 1u; for (int num2 = NumBitLevels; num2 > 0; num2--) { num = (num << 1) + Models[num].Decode(rangeDecoder); } return num - (uint)(1 << NumBitLevels); } public uint ReverseDecode(Decoder rangeDecoder) { uint num = 1u; uint num2 = 0u; for (int i = 0; i < NumBitLevels; i++) { uint num3 = Models[num].Decode(rangeDecoder); num <<= 1; num += num3; num2 |= num3 << i; } return num2; } public static uint ReverseDecode(BitDecoder[] Models, uint startIndex, Decoder rangeDecoder, int NumBitLevels) { uint num = 1u; uint num2 = 0u; for (int i = 0; i < NumBitLevels; i++) { uint num3 = Models[startIndex + num].Decode(rangeDecoder); num <<= 1; num += num3; num2 |= num3 << i; } return num2; } } } namespace SevenZip.Compression.LZ { internal interface IInWindowStream { void SetStream(Stream inStream); void Init(); void ReleaseStream(); byte GetIndexByte(int index); uint GetMatchLen(int index, uint distance, uint limit); uint GetNumAvailableBytes(); } internal interface IMatchFinder : IInWindowStream { void Create(uint historySize, uint keepAddBufferBefore, uint matchMaxLen, uint keepAddBufferAfter); uint GetMatches(uint[] distances); void Skip(uint num); } public class BinTree : InWindow, IMatchFinder, IInWindowStream { private uint _cyclicBufferPos; private uint _cyclicBufferSize; private uint _matchMaxLen; private uint[] _son; private uint[] _hash; private uint _cutValue = 255u; private uint _hashMask; private uint _hashSizeSum; private bool HASH_ARRAY = true; private const uint kHash2Size = 1024u; private const uint kHash3Size = 65536u; private const uint kBT2HashSize = 65536u; private const uint kStartMaxLen = 1u; private const uint kHash3Offset = 1024u; private const uint kEmptyHashValue = 0u; private const uint kMaxValForNormalize = 2147483647u; private uint kNumHashDirectBytes; private uint kMinMatchCheck = 4u; private uint kFixHashSize = 66560u; public void SetType(int numHashBytes) { HASH_ARRAY = numHashBytes > 2; if (HASH_ARRAY) { kNumHashDirectBytes = 0u; kMinMatchCheck = 4u; kFixHashSize = 66560u; } else { kNumHashDirectBytes = 2u; kMinMatchCheck = 3u; kFixHashSize = 0u; } } public new void SetStream(Stream stream) { base.SetStream(stream); } public new void ReleaseStream() { base.ReleaseStream(); } public new void Init() { base.Init(); for (uint num = 0u; num < _hashSizeSum; num++) { _hash[num] = 0u; } _cyclicBufferPos = 0u; ReduceOffsets(-1); } public new void MovePos() { if (++_cyclicBufferPos >= _cyclicBufferSize) { _cyclicBufferPos = 0u; } base.MovePos(); if (_pos == int.MaxValue) { Normalize(); } } public new byte GetIndexByte(int index) { return base.GetIndexByte(index); } public new uint GetMatchLen(int index, uint distance, uint limit) { return base.GetMatchLen(index, distance, limit); } public new uint GetNumAvailableBytes() { return base.GetNumAvailableBytes(); } public void Create(uint historySize, uint keepAddBufferBefore, uint matchMaxLen, uint keepAddBufferAfter) { if (historySize > 2147483391) { throw new Exception(); } _cutValue = 16 + (matchMaxLen >> 1); uint keepSizeReserv = (historySize + keepAddBufferBefore + matchMaxLen + keepAddBufferAfter) / 2 + 256; Create(historySize + keepAddBufferBefore, matchMaxLen + keepAddBufferAfter, keepSizeReserv); _matchMaxLen = matchMaxLen; uint num = historySize + 1; if (_cyclicBufferSize != num) { _son = new uint[(_cyclicBufferSize = num) * 2]; } uint num2 = 65536u; if (HASH_ARRAY) { num2 = historySize - 1; num2 |= num2 >> 1; num2 |= num2 >> 2; num2 |= num2 >> 4; num2 |= num2 >> 8; num2 >>= 1; num2 |= 0xFFFFu; if (num2 > 16777216) { num2 >>= 1; } _hashMask = num2; num2++; num2 += kFixHashSize; } if (num2 != _hashSizeSum) { _hash = new uint[_hashSizeSum = num2]; } } public uint GetMatches(uint[] distances) { uint num; if (_pos + _matchMaxLen <= _streamPos) { num = _matchMaxLen; } else { num = _streamPos - _pos; if (num < kMinMatchCheck) { MovePos(); return 0u; } } uint num2 = 0u; uint num3 = ((_pos > _cyclicBufferSize) ? (_pos - _cyclicBufferSize) : 0u); uint num4 = _bufferOffset + _pos; uint num5 = 1u; uint num6 = 0u; uint num7 = 0u; uint num10; if (HASH_ARRAY) { uint num8 = CRC.Table[_bufferBase[num4]] ^ _bufferBase[num4 + 1]; num6 = num8 & 0x3FFu; int num9 = (int)num8 ^ (_bufferBase[num4 + 2] << 8); num7 = (uint)num9 & 0xFFFFu; num10 = ((uint)num9 ^ (CRC.Table[_bufferBase[num4 + 3]] << 5)) & _hashMask; } else { num10 = (uint)(_bufferBase[num4] ^ (_bufferBase[num4 + 1] << 8)); } uint num11 = _hash[kFixHashSize + num10]; if (HASH_ARRAY) { uint num12 = _hash[num6]; uint num13 = _hash[1024 + num7]; _hash[num6] = _pos; _hash[1024 + num7] = _pos; if (num12 > num3 && _bufferBase[_bufferOffset + num12] == _bufferBase[num4]) { num5 = (distances[num2++] = 2u); distances[num2++] = _pos - num12 - 1; } if (num13 > num3 && _bufferBase[_bufferOffset + num13] == _bufferBase[num4]) { if (num13 == num12) { num2 -= 2; } num5 = (distances[num2++] = 3u); distances[num2++] = _pos - num13 - 1; num12 = num13; } if (num2 != 0 && num12 == num11) { num2 -= 2; num5 = 1u; } } _hash[kFixHashSize + num10] = _pos; uint num14 = (_cyclicBufferPos << 1) + 1; uint num15 = _cyclicBufferPos << 1; uint val; uint val2 = (val = kNumHashDirectBytes); if (kNumHashDirectBytes != 0 && num11 > num3 && _bufferBase[_bufferOffset + num11 + kNumHashDirectBytes] != _bufferBase[num4 + kNumHashDirectBytes]) { num5 = (distances[num2++] = kNumHashDirectBytes); distances[num2++] = _pos - num11 - 1; } uint cutValue = _cutValue; while (true) { if (num11 <= num3 || cutValue-- == 0) { _son[num14] = (_son[num15] = 0u); break; } uint num16 = _pos - num11; uint num17 = ((num16 <= _cyclicBufferPos) ? (_cyclicBufferPos - num16) : (_cyclicBufferPos - num16 + _cyclicBufferSize)) << 1; uint num18 = _bufferOffset + num11; uint num19 = Math.Min(val2, val); if (_bufferBase[num18 + num19] == _bufferBase[num4 + num19]) { while (++num19 != num && _bufferBase[num18 + num19] == _bufferBase[num4 + num19]) { } if (num5 < num19) { num5 = (distances[num2++] = num19); distances[num2++] = num16 - 1; if (num19 == num) { _son[num15] = _son[num17]; _son[num14] = _son[num17 + 1]; break; } } } if (_bufferBase[num18 + num19] < _bufferBase[num4 + num19]) { _son[num15] = num11; num15 = num17 + 1; num11 = _son[num15]; val = num19; } else { _son[num14] = num11; num14 = num17; num11 = _son[num14]; val2 = num19; } } MovePos(); return num2; } public void Skip(uint num) { do { uint num2; if (_pos + _matchMaxLen <= _streamPos) { num2 = _matchMaxLen; } else { num2 = _streamPos - _pos; if (num2 < kMinMatchCheck) { MovePos(); continue; } } uint num3 = ((_pos > _cyclicBufferSize) ? (_pos - _cyclicBufferSize) : 0u); uint num4 = _bufferOffset + _pos; uint num9; if (HASH_ARRAY) { uint num5 = CRC.Table[_bufferBase[num4]] ^ _bufferBase[num4 + 1]; uint num6 = num5 & 0x3FFu; _hash[num6] = _pos; int num7 = (int)num5 ^ (_bufferBase[num4 + 2] << 8); uint num8 = (uint)num7 & 0xFFFFu; _hash[1024 + num8] = _pos; num9 = ((uint)num7 ^ (CRC.Table[_bufferBase[num4 + 3]] << 5)) & _hashMask; } else { num9 = (uint)(_bufferBase[num4] ^ (_bufferBase[num4 + 1] << 8)); } uint num10 = _hash[kFixHashSize + num9]; _hash[kFixHashSize + num9] = _pos; uint num11 = (_cyclicBufferPos << 1) + 1; uint num12 = _cyclicBufferPos << 1; uint val; uint val2 = (val = kNumHashDirectBytes); uint cutValue = _cutValue; while (true) { if (num10 <= num3 || cutValue-- == 0) { _son[num11] = (_son[num12] = 0u); break; } uint num13 = _pos - num10; uint num14 = ((num13 <= _cyclicBufferPos) ? (_cyclicBufferPos - num13) : (_cyclicBufferPos - num13 + _cyclicBufferSize)) << 1; uint num15 = _bufferOffset + num10; uint num16 = Math.Min(val2, val); if (_bufferBase[num15 + num16] == _bufferBase[num4 + num16]) { while (++num16 != num2 && _bufferBase[num15 + num16] == _bufferBase[num4 + num16]) { } if (num16 == num2) { _son[num12] = _son[num14]; _son[num11] = _son[num14 + 1]; break; } } if (_bufferBase[num15 + num16] < _bufferBase[num4 + num16]) { _son[num12] = num10; num12 = num14 + 1; num10 = _son[num12]; val = num16; } else { _son[num11] = num10; num11 = num14; num10 = _son[num11]; val2 = num16; } } MovePos(); } while (--num != 0); } private void NormalizeLinks(uint[] items, uint numItems, uint subValue) { for (uint num = 0u; num < numItems; num++) { uint num2 = items[num]; num2 = ((num2 > subValue) ? (num2 - subValue) : 0u); items[num] = num2; } } private void Normalize() { uint subValue = _pos - _cyclicBufferSize; NormalizeLinks(_son, _cyclicBufferSize * 2, subValue); NormalizeLinks(_hash, _hashSizeSum, subValue); ReduceOffsets((int)subValue); } public void SetCutValue(uint cutValue) { _cutValue = cutValue; } } public class InWindow { public byte[] _bufferBase; private Stream _stream; private uint _posLimit; private bool _streamEndWasReached; private uint _pointerToLastSafePosition; public uint _bufferOffset; public uint _blockSize; public uint _pos; private uint _keepSizeBefore; private uint _keepSizeAfter; public uint _streamPos; public void MoveBlock() { uint num = _bufferOffset + _pos - _keepSizeBefore; if (num != 0) { num--; } uint num2 = _bufferOffset + _streamPos - num; for (uint num3 = 0u; num3 < num2; num3++) { _bufferBase[num3] = _bufferBase[num + num3]; } _bufferOffset -= num; } public virtual void ReadBlock() { if (_streamEndWasReached) { return; } while (true) { int num = (int)(0 - _bufferOffset + _blockSize - _streamPos); if (num == 0) { return; } int num2 = _stream.Read(_bufferBase, (int)(_bufferOffset + _streamPos), num); if (num2 == 0) { break; } _streamPos += (uint)num2; if (_streamPos >= _pos + _keepSizeAfter) { _posLimit = _streamPos - _keepSizeAfter; } } _posLimit = _streamPos; if (_bufferOffset + _posLimit > _pointerToLastSafePosition) { _posLimit = _pointerToLastSafePosition - _bufferOffset; } _streamEndWasReached = true; } private void Free() { _bufferBase = null; } public void Create(uint keepSizeBefore, uint keepSizeAfter, uint keepSizeReserv) { _keepSizeBefore = keepSizeBefore; _keepSizeAfter = keepSizeAfter; uint num = keepSizeBefore + keepSizeAfter + keepSizeReserv; if (_bufferBase == null || _blockSize != num) { Free(); _blockSize = num; _bufferBase = new byte[_blockSize]; } _pointerToLastSafePosition = _blockSize - keepSizeAfter; } public void SetStream(Stream stream) { _stream = stream; } public void ReleaseStream() { _stream = null; } public void Init() { _bufferOffset = 0u; _pos = 0u; _streamPos = 0u; _streamEndWasReached = false; ReadBlock(); } public void MovePos() { _pos++; if (_pos > _posLimit) { if (_bufferOffset + _pos > _pointerToLastSafePosition) { MoveBlock(); } ReadBlock(); } } public byte GetIndexByte(int index) { return _bufferBase[_bufferOffset + _pos + index]; } public uint GetMatchLen(int index, uint distance, uint limit) { if (_streamEndWasReached && _pos + index + limit > _streamPos) { limit = _streamPos - (uint)(int)(_pos + index); } distance++; uint num = _bufferOffset + _pos + (uint)index; uint num2; for (num2 = 0u; num2 < limit && _bufferBase[num + num2] == _bufferBase[num + num2 - distance]; num2++) { } return num2; } public uint GetNumAvailableBytes() { return _streamPos - _pos; } public void ReduceOffsets(int subValue) { _bufferOffset += (uint)subValue; _posLimit -= (uint)subValue; _pos -= (uint)subValue; _streamPos -= (uint)subValue; } } public class OutWindow { private byte[] _buffer; private uint _pos; private uint _windowSize; private uint _streamPos; private Stream _stream; public uint TrainSize; public void Create(uint windowSize) { if (_windowSize != windowSize) { _buffer = new byte[windowSize]; } _windowSize = windowSize; _pos = 0u; _streamPos = 0u; } public void Init(Stream stream, bool solid) { ReleaseStream(); _stream = stream; if (!solid) { _streamPos = 0u; _pos = 0u; TrainSize = 0u; } } public bool Train(Stream stream) { long length = stream.Length; uint num = (TrainSize = (uint)((length < _windowSize) ? length : _windowSize)); stream.Position = length - num; _streamPos = (_pos = 0u); while (num != 0) { uint num2 = _windowSize - _pos; if (num < num2) { num2 = num; } int num3 = stream.Read(_buffer, (int)_pos, (int)num2); if (num3 == 0) { return false; } num -= (uint)num3; _pos += (uint)num3; _streamPos += (uint)num3; if (_pos == _windowSize) { _streamPos = (_pos = 0u); } } return true; } public void ReleaseStream() { Flush(); _stream = null; } public void Flush() { uint num = _pos - _streamPos; if (num != 0) { _stream.Write(_buffer, (int)_streamPos, (int)num); if (_pos >= _windowSize) { _pos = 0u; } _streamPos = _pos; } } public void CopyBlock(uint distance, uint len) { uint num = _pos - distance - 1; if (num >= _windowSize) { num += _windowSize; } while (len != 0) { if (num >= _windowSize) { num = 0u; } _buffer[_pos++] = _buffer[num++]; if (_pos >= _windowSize) { Flush(); } len--; } } public void PutByte(byte b) { _buffer[_pos++] = b; if (_pos >= _windowSize) { Flush(); } } public byte GetByte(uint distance) { uint num = _pos - distance - 1; if (num >= _windowSize) { num += _windowSize; } return _buffer[num]; } } } namespace SevenZip.Compression.LZMA { internal abstract class Base { public struct State { public uint Index; public void Init() { Index = 0u; } public void UpdateChar() { if (Index < 4) { Index = 0u; } else if (Index < 10) { Index -= 3u; } else { Index -= 6u; } } public void UpdateMatch() { Index = ((Index < 7) ? 7u : 10u); } public void UpdateRep() { Index = ((Index < 7) ? 8u : 11u); } public void UpdateShortRep() { Index = ((Index < 7) ? 9u : 11u); } public bool IsCharState() { return Index < 7; } } public const uint kNumRepDistances = 4u; public const uint kNumStates = 12u; public const int kNumPosSlotBits = 6; public const int kDicLogSizeMin = 0; public const int kNumLenToPosStatesBits = 2; public const uint kNumLenToPosStates = 4u; public const uint kMatchMinLen = 2u; public const int kNumAlignBits = 4; public const uint kAlignTableSize = 16u; public const uint kAlignMask = 15u; public const uint kStartPosModelIndex = 4u; public const uint kEndPosModelIndex = 14u; public const uint kNumPosModels = 10u; public const uint kNumFullDistances = 128u; public const uint kNumLitPosStatesBitsEncodingMax = 4u; public const uint kNumLitContextBitsMax = 8u; public const int kNumPosStatesBitsMax = 4; public const uint kNumPosStatesMax = 16u; public const int kNumPosStatesBitsEncodingMax = 4; public const uint kNumPosStatesEncodingMax = 16u; public const int kNumLowLenBits = 3; public const int kNumMidLenBits = 3; public const int kNumHighLenBits = 8; public const uint kNumLowLenSymbols = 8u; public const uint kNumMidLenSymbols = 8u; public const uint kNumLenSymbols = 272u; public const uint kMatchMaxLen = 273u; public static uint GetLenToPosState(uint len) { len -= 2; if (len < 4) { return len; } return 3u; } } public class Decoder : ICoder, ISetDecoderProperties { private class LenDecoder { private BitDecoder m_Choice; private BitDecoder m_Choice2; private BitTreeDecoder[] m_LowCoder = new BitTreeDecoder[16]; private BitTreeDecoder[] m_MidCoder = new BitTreeDecoder[16]; private BitTreeDecoder m_HighCoder = new BitTreeDecoder(8); private uint m_NumPosStates; public void Create(uint numPosStates) { for (uint num = m_NumPosStates; num < numPosStates; num++) { m_LowCoder[num] = new BitTreeDecoder(3); m_MidCoder[num] = new BitTreeDecoder(3); } m_NumPosStates = numPosStates; } public void Init() { m_Choice.Init(); for (uint num = 0u; num < m_NumPosStates; num++) { m_LowCoder[num].Init(); m_MidCoder[num].Init(); } m_Choice2.Init(); m_HighCoder.Init(); } public uint Decode(SevenZip.Compression.RangeCoder.Decoder rangeDecoder, uint posState) { if (m_Choice.Decode(rangeDecoder) == 0) { return m_LowCoder[posState].Decode(rangeDecoder); } uint num = 8u; if (m_Choice2.Decode(rangeDecoder) == 0) { return num + m_MidCoder[posState].Decode(rangeDecoder); } num += 8; return num + m_HighCoder.Decode(rangeDecoder); } } private class LiteralDecoder { private struct Decoder2 { private BitDecoder[] m_Decoders; public void Create() { m_Decoders = new BitDecoder[768]; } public void Init() { for (int i = 0; i < 768; i++) { m_Decoders[i].Init(); } } public byte DecodeNormal(SevenZip.Compression.RangeCoder.Decoder rangeDecoder) { uint num = 1u; do { num = (num << 1) | m_Decoders[num].Decode(rangeDecoder); } while (num < 256); return (byte)num; } public byte DecodeWithMatchByte(SevenZip.Compression.RangeCoder.Decoder rangeDecoder, byte matchByte) { uint num = 1u; do { uint num2 = (uint)(matchByte >> 7) & 1u; matchByte <<= 1; uint num3 = m_Decoders[(1 + num2 << 8) + num].Decode(rangeDecoder); num = (num << 1) | num3; if (num2 != num3) { while (num < 256) { num = (num << 1) | m_Decoders[num].Decode(rangeDecoder); } break; } } while (num < 256); return (byte)num; } } private Decoder2[] m_Coders; private int m_NumPrevBits; private int m_NumPosBits; private uint m_PosMask; public void Create(int numPosBits, int numPrevBits) { if (m_Coders == null || m_NumPrevBits != numPrevBits || m_NumPosBits != numPosBits) { m_NumPosBits = numPosBits; m_PosMask = (uint)((1 << numPosBits) - 1); m_NumPrevBits = numPrevBits; uint num = (uint)(1 << m_NumPrevBits + m_NumPosBits); m_Coders = new Decoder2[num]; for (uint num2 = 0u; num2 < num; num2++) { m_Coders[num2].Create(); } } } public void Init() { uint num = (uint)(1 << m_NumPrevBits + m_NumPosBits); for (uint num2 = 0u; num2 < num; num2++) { m_Coders[num2].Init(); } } private uint GetState(uint pos, byte prevByte) { return ((pos & m_PosMask) << m_NumPrevBits) + (uint)(prevByte >> 8 - m_NumPrevBits); } public byte DecodeNormal(SevenZip.Compression.RangeCoder.Decoder rangeDecoder, uint pos, byte prevByte) { return m_Coders[GetState(pos, prevByte)].DecodeNormal(rangeDecoder); } public byte DecodeWithMatchByte(SevenZip.Compression.RangeCoder.Decoder rangeDecoder, uint pos, byte prevByte, byte matchByte) { return m_Coders[GetState(pos, prevByte)].DecodeWithMatchByte(rangeDecoder, matchByte); } } private OutWindow m_OutWindow = new OutWindow(); private SevenZip.Compression.RangeCoder.Decoder m_RangeDecoder = new SevenZip.Compression.RangeCoder.Decoder(); private BitDecoder[] m_IsMatchDecoders = new BitDecoder[192]; private BitDecoder[] m_IsRepDecoders = new BitDecoder[12]; private BitDecoder[] m_IsRepG0Decoders = new BitDecoder[12]; private BitDecoder[] m_IsRepG1Decoders = new BitDecoder[12]; private BitDecoder[] m_IsRepG2Decoders = new BitDecoder[12]; private BitDecoder[] m_IsRep0LongDecoders = new BitDecoder[192]; private BitTreeDecoder[] m_PosSlotDecoder = new BitTreeDecoder[4]; private BitDecoder[] m_PosDecoders = new BitDecoder[114]; private BitTreeDecoder m_PosAlignDecoder = new BitTreeDecoder(4); private LenDecoder m_LenDecoder = new LenDecoder(); private LenDecoder m_RepLenDecoder = new LenDecoder(); private LiteralDecoder m_LiteralDecoder = new LiteralDecoder(); private uint m_DictionarySize; private uint m_DictionarySizeCheck; private uint m_PosStateMask; private bool _solid; public Decoder() { m_DictionarySize = uint.MaxValue; for (int i = 0; (long)i < 4L; i++) { m_PosSlotDecoder[i] = new BitTreeDecoder(6); } } private void SetDictionarySize(uint dictionarySize) { if (m_DictionarySize != dictionarySize) { m_DictionarySize = dictionarySize; m_DictionarySizeCheck = Math.Max(m_DictionarySize, 1u); uint windowSize = Math.Max(m_DictionarySizeCheck, 4096u); m_OutWindow.Create(windowSize); } } private void SetLiteralProperties(int lp, int lc) { if (lp > 8) { throw new InvalidParamException(); } if (lc > 8) { throw new InvalidParamException(); } m_LiteralDecoder.Create(lp, lc); } private void SetPosBitsProperties(int pb) { if (pb > 4) { throw new InvalidParamException(); } uint num = (uint)(1 << pb); m_LenDecoder.Create(num); m_RepLenDecoder.Create(num); m_PosStateMask = num - 1; } private void Init(Stream inStream, Stream outStream) { m_RangeDecoder.Init(inStream); m_OutWindow.Init(outStream, _solid); for (uint num = 0u; num < 12; num++) { for (uint num2 = 0u; num2 <= m_PosStateMask; num2++) { uint num3 = (num << 4) + num2; m_IsMatchDecoders[num3].Init(); m_IsRep0LongDecoders[num3].Init(); } m_IsRepDecoders[num].Init(); m_IsRepG0Decoders[num].Init(); m_IsRepG1Decoders[num].Init(); m_IsRepG2Decoders[num].Init(); } m_LiteralDecoder.Init(); for (uint num = 0u; num < 4; num++) { m_PosSlotDecoder[num].Init(); } for (uint num = 0u; num < 114; num++) { m_PosDecoders[num].Init(); } m_LenDecoder.Init(); m_RepLenDecoder.Init(); m_PosAlignDecoder.Init(); } public void Code(Stream inStream, Stream outStream, long inSize, long outSize, ICodeProgress progress) { Init(inStream, outStream); Base.State state = default(Base.State); state.Init(); uint num = 0u; uint num2 = 0u; uint num3 = 0u; uint num4 = 0u; ulong num5 = 0uL; if (num5 < (ulong)outSize) { if (m_IsMatchDecoders[state.Index << 4].Decode(m_RangeDecoder) != 0) { throw new DataErrorException(); } state.UpdateChar(); byte b = m_LiteralDecoder.DecodeNormal(m_RangeDecoder, 0u, 0); m_OutWindow.PutByte(b); num5++; } while (num5 < (ulong)outSize) { uint num6 = (uint)(int)num5 & m_PosStateMask; if (m_IsMatchDecoders[(state.Index << 4) + num6].Decode(m_RangeDecoder) == 0) { byte @byte = m_OutWindow.GetByte(0u); byte b2 = (state.IsCharState() ? m_LiteralDecoder.DecodeNormal(m_RangeDecoder, (uint)num5, @byte) : m_LiteralDecoder.DecodeWithMatchByte(m_RangeDecoder, (uint)num5, @byte, m_OutWindow.GetByte(num))); m_OutWindow.PutByte(b2); state.UpdateChar(); num5++; continue; } uint num8; if (m_IsRepDecoders[state.Index].Decode(m_RangeDecoder) == 1) { if (m_IsRepG0Decoders[state.Index].Decode(m_RangeDecoder) == 0) { if (m_IsRep0LongDecoders[(state.Index << 4) + num6].Decode(m_RangeDecoder) == 0) { state.UpdateShortRep(); m_OutWindow.PutByte(m_OutWindow.GetByte(num)); num5++; continue; } } else { uint num7; if (m_IsRepG1Decoders[state.Index].Decode(m_RangeDecoder) == 0) { num7 = num2; } else { if (m_IsRepG2Decoders[state.Index].Decode(m_RangeDecoder) == 0) { num7 = num3; } else { num7 = num4; num4 = num3; } num3 = num2; } num2 = num; num = num7; } num8 = m_RepLenDecoder.Decode(m_RangeDecoder, num6) + 2; state.UpdateRep(); } else { num4 = num3; num3 = num2; num2 = num; num8 = 2 + m_LenDecoder.Decode(m_RangeDecoder, num6); state.UpdateMatch(); uint num9 = m_PosSlotDecoder[Base.GetLenToPosState(num8)].Decode(m_RangeDecoder); if (num9 >= 4) { int num10 = (int)((num9 >> 1) - 1); num = (2 | (num9 & 1)) << num10; if (num9 < 14) { num += BitTreeDecoder.ReverseDecode(m_PosDecoders, num - num9 - 1, m_RangeDecoder, num10); } else { num += m_RangeDecoder.DecodeDirectBits(num10 - 4) << 4; num += m_PosAlignDecoder.ReverseDecode(m_RangeDecoder); } } else { num = num9; } } if (num >= m_OutWindow.TrainSize + num5 || num >= m_DictionarySizeCheck) { if (num == uint.MaxValue) { break; } throw new DataErrorException(); } m_OutWindow.CopyBlock(num, num8); num5 += num8; } m_OutWindow.Flush(); m_OutWindow.ReleaseStream(); m_RangeDecoder.ReleaseStream(); } public void SetDecoderProperties(byte[] properties) { if (properties.Length < 5) { throw new InvalidParamException(); } int lc = properties[0] % 9; int num = properties[0] / 9; int lp = num % 5; int num2 = num / 5; if (num2 > 4) { throw new InvalidParamException(); } uint num3 = 0u; for (int i = 0; i < 4; i++) { num3 += (uint)(properties[1 + i] << i * 8); } SetDictionarySize(num3); SetLiteralProperties(lp, lc); SetPosBitsProperties(num2); } public bool Train(Stream stream) { _solid = true; return m_OutWindow.Train(stream); } } public class Encoder : ICoder, ISetCoderProperties, IWriteCoderProperties { private enum EMatchFinderType { BT2, BT4 } private class LiteralEncoder { public struct Encoder2 { private BitEncoder[] m_Encoders; public void Create() { m_Encoders = new BitEncoder[768]; } public void Init() { for (int i = 0; i < 768; i++) { m_Encoders[i].Init(); } } public void Encode(SevenZip.Compression.RangeCoder.Encoder rangeEncoder, byte symbol) { uint num = 1u; for (int num2 = 7; num2 >= 0; num2--) { uint num3 = (uint)(symbol >> num2) & 1u; m_Encoders[num].Encode(rangeEncoder, num3); num = (num << 1) | num3; } } public void EncodeMatched(SevenZip.Compression.RangeCoder.Encoder rangeEncoder, byte matchByte, byte symbol) { uint num = 1u; bool flag = true; for (int num2 = 7; num2 >= 0; num2--) { uint num3 = (uint)(symbol >> num2) & 1u; uint num4 = num; if (flag) { uint num5 = (uint)(matchByte >> num2) & 1u; num4 += 1 + num5 << 8; flag = num5 == num3; } m_Encoders[num4].Encode(rangeEncoder, num3); num = (num << 1) | num3; } } public uint GetPrice(bool matchMode, byte matchByte, byte symbol) { uint num = 0u; uint num2 = 1u; int num3 = 7; if (matchMode) { while (num3 >= 0) { uint num4 = (uint)(matchByte >> num3) & 1u; uint num5 = (uint)(symbol >> num3) & 1u; num += m_Encoders[(1 + num4 << 8) + num2].GetPrice(num5); num2 = (num2 << 1) | num5; if (num4 != num5) { num3--; break; } num3--; } } while (num3 >= 0) { uint num6 = (uint)(symbol >> num3) & 1u; num += m_Encoders[num2].GetPrice(num6); num2 = (num2 << 1) | num6; num3--; } return num; } } private Encoder2[] m_Coders; private int m_NumPrevBits; private int m_NumPosBits; private uint m_PosMask; public void Create(int numPosBits, int numPrevBits) { if (m_Coders == null || m_NumPrevBits != numPrevBits || m_NumPosBits != numPosBits) { m_NumPosBits = numPosBits; m_PosMask = (uint)((1 << numPosBits) - 1); m_NumPrevBits = numPrevBits; uint num = (uint)(1 << m_NumPrevBits + m_NumPosBits); m_Coders = new Encoder2[num]; for (uint num2 = 0u; num2 < num; num2++) { m_Coders[num2].Create(); } } } public void Init() { uint num = (uint)(1 << m_NumPrevBits + m_NumPosBits); for (uint num2 = 0u; num2 < num; num2++) { m_Coders[num2].Init(); } } public Encoder2 GetSubCoder(uint pos, byte prevByte) { return m_Coders[(int)((pos & m_PosMask) << m_NumPrevBits) + (prevByte >> 8 - m_NumPrevBits)]; } } private class LenEncoder { private BitEncoder _choice; private BitEncoder _choice2; private BitTreeEncoder[] _lowCoder = new BitTreeEncoder[16]; private BitTreeEncoder[] _midCoder = new BitTreeEncoder[16]; private BitTreeEncoder _highCoder = new BitTreeEncoder(8); public LenEncoder() { for (uint num = 0u; num < 16; num++) { _lowCoder[num] = new BitTreeEncoder(3); _midCoder[num] = new BitTreeEncoder(3); } } public void Init(uint numPosStates) { _choice.Init(); _choice2.Init(); for (uint num = 0u; num < numPosStates; num++) { _lowCoder[num].Init(); _midCoder[num].Init(); } _highCoder.Init(); } public void Encode(SevenZip.Compression.RangeCoder.Encoder rangeEncoder, uint symbol, uint posState) { if (symbol < 8) { _choice.Encode(rangeEncoder, 0u); _lowCoder[posState].Encode(rangeEncoder, symbol); return; } symbol -= 8; _choice.Encode(rangeEncoder, 1u); if (symbol < 8) { _choice2.Encode(rangeEncoder, 0u); _midCoder[posState].Encode(rangeEncoder, symbol); } else { _choice2.Encode(rangeEncoder, 1u); _highCoder.Encode(rangeEncoder, symbol - 8); } } public void SetPrices(uint posState, uint numSymbols, uint[] prices, uint st) { uint price = _choice.GetPrice0(); uint price2 = _choice.GetPrice1(); uint num = price2 + _choice2.GetPrice0(); uint num2 = price2 + _choice2.GetPrice1(); uint num3 = 0u; for (num3 = 0u; num3 < 8; num3++) { if (num3 >= numSymbols) { return; } prices[st + num3] = price + _lowCoder[posState].GetPrice(num3); } for (; num3 < 16; num3++) { if (num3 >= numSymbols) { return; } prices[st + num3] = num + _midCoder[posState].GetPrice(num3 - 8); } for (; num3 < numSymbols; num3++) { prices[st + num3] = num2 + _highCoder.GetPrice(num3 - 8 - 8); } } } private class LenPriceTableEncoder : LenEncoder { private uint[] _prices = new uint[4352]; private uint _tableSize; private uint[] _counters = new uint[16]; public void SetTableSize(uint tableSize) { _tableSize = tableSize; } public uint GetPrice(uint symbol, uint posState) { return _prices[posState * 272 + symbol]; } private void UpdateTable(uint posState) { SetPrices(posState, _tableSize, _prices, posState * 272); _counters[posState] = _tableSize; } public void UpdateTables(uint numPosStates) { for (uint num = 0u; num < numPosStates; num++) { UpdateTable(num); } } public new void Encode(SevenZip.Compression.RangeCoder.Encoder rangeEncoder, uint symbol, uint posState) { base.Encode(rangeEncoder, symbol, posState); if (--_counters[posState] == 0) { UpdateTable(posState); } } } private class Optimal { public Base.State State; public bool Prev1IsChar; public bool Prev2; public uint PosPrev2; public uint BackPrev2; public uint Price; public uint PosPrev; public uint BackPrev; public uint Backs0; public uint Backs1; public uint Backs2; public uint Backs3; public void MakeAsChar() { BackPrev = uint.MaxValue; Prev1IsChar = false; } public void MakeAsShortRep() { BackPrev = 0u; Prev1IsChar = false; } public bool IsShortRep() { return BackPrev == 0; } } private const uint kIfinityPrice = 268435455u; private static byte[] g_FastPos; private Base.State _state; private byte _previousByte; private uint[] _repDistances = new uint[4]; private const int kDefaultDictionaryLogSize = 22; private const uint kNumFastBytesDefault = 32u; private const uint kNumLenSpecSymbols = 16u; private const uint kNumOpts = 4096u; private Optimal[] _optimum = new Optimal[4096]; private IMatchFinder _matchFinder; private SevenZip.Compression.RangeCoder.Encoder _rangeEncoder = new SevenZip.Compression.RangeCoder.Encoder(); private BitEncoder[] _isMatch = new BitEncoder[192]; private BitEncoder[] _isRep = new BitEncoder[12]; private BitEncoder[] _isRepG0 = new BitEncoder[12]; private BitEncoder[] _isRepG1 = new BitEncoder[12]; private BitEncoder[] _isRepG2 = new BitEncoder[12]; private BitEncoder[] _isRep0Long = new BitEncoder[192]; private BitTreeEncoder[] _posSlotEncoder = new BitTreeEncoder[4]; private BitEncoder[] _posEncoders = new BitEncoder[114]; private BitTreeEncoder _posAlignEncoder = new BitTreeEncoder(4); private LenPriceTableEncoder _lenEncoder = new LenPriceTableEncoder(); private LenPriceTableEncoder _repMatchLenEncoder = new LenPriceTableEncoder(); private LiteralEncoder _literalEncoder = new LiteralEncoder(); private uint[] _matchDistances = new uint[548]; private uint _numFastBytes = 32u; private uint _longestMatchLength; private uint _numDistancePairs; private uint _additionalOffset; private uint _optimumEndIndex; private uint _optimumCurrentIndex; private bool _longestMatchWasFound; private uint[] _posSlotPrices = new uint[256]; private uint[] _distancesPrices = new uint[512]; private uint[] _alignPrices = new uint[16]; private uint _alignPriceCount; private uint _distTableSize = 44u; private int _posStateBits = 2; private uint _posStateMask = 3u; private int _numLiteralPosStateBits; private int _numLiteralContextBits = 3; private uint _dictionarySize = 4194304u; private uint _dictionarySizePrev = uint.MaxValue; private uint _numFastBytesPrev = uint.MaxValue; private long nowPos64; private bool _finished; private Stream _inStream; private EMatchFinderType _matchFinderType = EMatchFinderType.BT4; private bool _writeEndMark; private bool _needReleaseMFStream; private uint[] reps = new uint[4]; private uint[] repLens = new uint[4]; private const int kPropSize = 5; private byte[] properties = new byte[5]; private uint[] tempPrices = new uint[128]; private uint _matchPriceCount; private static string[] kMatchFinderIDs; private uint _trainSize; static Encoder() { g_FastPos = new byte[2048]; kMatchFinderIDs = new string[2] { "BT2", "BT4" }; int num = 2; g_FastPos[0] = 0; g_FastPos[1] = 1; for (byte b = 2; b < 22; b++) { uint num2 = (uint)(1 << (b >> 1) - 1); uint num3 = 0u; while (num3 < num2) { g_FastPos[num] = b; num3++; num++; } } } private static uint GetPosSlot(uint pos) { if (pos < 2048) { return g_FastPos[pos]; } if (pos < 2097152) { return (uint)(g_FastPos[pos >> 10] + 20); } return (uint)(g_FastPos[pos >> 20] + 40); } private static uint GetPosSlot2(uint pos) { if (pos < 131072) { return (uint)(g_FastPos[pos >> 6] + 12); } if (pos < 134217728) { return (uint)(g_FastPos[pos >> 16] + 32); } return (uint)(g_FastPos[pos >> 26] + 52); } private void BaseInit() { _state.Init(); _previousByte = 0; for (uint num = 0u; num < 4; num++) { _repDistances[num] = 0u; } } private void Create() { if (_matchFinder == null) { BinTree binTree = new BinTree(); int type = 4; if (_matchFinderType == EMatchFinderType.BT2) { type = 2; } binTree.SetType(type); _matchFinder = binTree; } _literalEncoder.Create(_numLiteralPosStateBits, _numLiteralContextBits); if (_dictionarySize != _dictionarySizePrev || _numFastBytesPrev != _numFastBytes) { _matchFinder.Create(_dictionarySize, 4096u, _numFastBytes, 274u); _dictionarySizePrev = _dictionarySize; _numFastBytesPrev = _numFastBytes; } } public Encoder() { for (int i = 0; (long)i < 4096L; i++) { _optimum[i] = new Optimal(); } for (int j = 0; (long)j < 4L; j++) { _posSlotEncoder[j] = new BitTreeEncoder(6); } } private void SetWriteEndMarkerMode(bool writeEndMarker) { _writeEndMark = writeEndMarker; } private void Init() { BaseInit(); _rangeEncoder.Init(); for (uint num = 0u; num < 12; num++) { for (uint num2 = 0u; num2 <= _posStateMask; num2++) { uint num3 = (num << 4) + num2; _isMatch[num3].Init(); _isRep0Long[num3].Init(); } _isRep[num].Init(); _isRepG0[num].Init(); _isRepG1[num].Init(); _isRepG2[num].Init(); } _literalEncoder.Init(); for (uint num = 0u; num < 4; num++) { _posSlotEncoder[num].Init(); } for (uint num = 0u; num < 114; num++) { _posEncoders[num].Init(); } _lenEncoder.Init((uint)(1 << _posStateBits)); _repMatchLenEncoder.Init((uint)(1 << _posStateBits)); _posAlignEncoder.Init(); _longestMatchWasFound = false; _optimumEndIndex = 0u; _optimumCurrentIndex = 0u; _additionalOffset = 0u; } private void ReadMatchDistances(out uint lenRes, out uint numDistancePairs) { lenRes = 0u; numDistancePairs = _matchFinder.GetMatches(_matchDistances); if (numDistancePairs != 0) { lenRes = _matchDistances[numDistancePairs - 2]; if (lenRes == _numFastBytes) { lenRes += _matchFinder.GetMatchLen((int)(lenRes - 1), _matchDistances[numDistancePairs - 1], 273 - lenRes); } } _additionalOffset++; } private void MovePos(uint num) { if (num != 0) { _matchFinder.Skip(num); _additionalOffset += num; } } private uint GetRepLen1Price(Base.State state, uint posState) { return _isRepG0[state.Index].GetPrice0() + _isRep0Long[(state.Index << 4) + posState].GetPrice0(); } private uint GetPureRepPrice(uint repIndex, Base.State state, uint posState) { uint price; if (repIndex == 0) { price = _isRepG0[state.Index].GetPrice0(); return price + _isRep0Long[(state.Index << 4) + posState].GetPrice1(); } price = _isRepG0[state.Index].GetPrice1(); if (repIndex == 1) { return price + _isRepG1[state.Index].GetPrice0(); } price += _isRepG1[state.Index].GetPrice1(); return price + _isRepG2[state.Index].GetPrice(repIndex - 2); } private uint GetRepPrice(uint repIndex, uint len, Base.State state, uint posState) { return _repMatchLenEncoder.GetPrice(len - 2, posState) + GetPureRepPrice(repIndex, state, posState); } private uint GetPosLenPrice(uint pos, uint len, uint posState) { uint lenToPosState = Base.GetLenToPosState(len); uint num = ((pos >= 128) ? (_posSlotPrices[(lenToPosState << 6) + GetPosSlot2(pos)] + _alignPrices[pos & 0xF]) : _distancesPrices[lenToPosState * 128 + pos]); return num + _lenEncoder.GetPrice(len - 2, posState); } private uint Backward(out uint backRes, uint cur) { _optimumEndIndex = cur; uint posPrev = _optimum[cur].PosPrev; uint backPrev = _optimum[cur].BackPrev; do { if (_optimum[cur].Prev1IsChar) { _optimum[posPrev].MakeAsChar(); _optimum[posPrev].PosPrev = posPrev - 1; if (_optimum[cur].Prev2) { _optimum[posPrev - 1].Prev1IsChar = false; _optimum[posPrev - 1].PosPrev = _optimum[cur].PosPrev2; _optimum[posPrev - 1].BackPrev = _optimum[cur].BackPrev2; } } uint num = posPrev; uint backPrev2 = backPrev; backPrev = _optimum[num].BackPrev; posPrev = _optimum[num].PosPrev; _optimum[num].BackPrev = backPrev2; _optimum[num].PosPrev = cur; cur = num; } while (cur != 0); backRes = _optimum[0].BackPrev; _optimumCurrentIndex = _optimum[0].PosPrev; return _optimumCurrentIndex; } private uint GetOptimum(uint position, out uint backRes) { if (_optimumEndIndex != _optimumCurrentIndex) { uint result = _optimum[_optimumCurrentIndex].PosPrev - _optimumCurrentIndex; backRes = _optimum[_optimumCurrentIndex].BackPrev; _optimumCurrentIndex = _optimum[_optimumCurrentIndex].PosPrev; return result; } _optimumCurrentIndex = (_optimumEndIndex = 0u); uint lenRes; uint numDistancePairs; if (!_longestMatchWasFound) { ReadMatchDistances(out lenRes, out numDistancePairs); } else { lenRes = _longestMatchLength; numDistancePairs = _numDistancePairs; _longestMatchWasFound = false; } uint num = _matchFinder.GetNumAvailableBytes() + 1; if (num < 2) { backRes = uint.MaxValue; return 1u; } if (num > 273) { num = 273u; } uint num2 = 0u; for (uint num3 = 0u; num3 < 4; num3++) { reps[num3] = _repDistances[num3]; repLens[num3] = _matchFinder.GetMatchLen(-1, reps[num3], 273u); if (repLens[num3] > repLens[num2]) { num2 = num3; } } if (repLens[num2] >= _numFastBytes) { backRes = num2; uint num4 = repLens[num2]; MovePos(num4 - 1); return num4; } if (lenRes >= _numFastBytes) { backRes = _matchDistances[numDistancePairs - 1] + 4; MovePos(lenRes - 1); return lenRes; } byte indexByte = _matchFinder.GetIndexByte(-1); byte indexByte2 = _matchFinder.GetIndexByte((int)(0 - _repDistances[0] - 1 - 1)); if (lenRes < 2 && indexByte != indexByte2 && repLens[num2] < 2) { backRes = uint.MaxValue; return 1u; } _optimum[0].State = _state; uint num5 = position & _posStateMask; _optimum[1].Price = _isMatch[(_state.Index << 4) + num5].GetPrice0() + _literalEncoder.GetSubCoder(position, _previousByte).GetPrice(!_state.IsCharState(), indexByte2, indexByte); _optimum[1].MakeAsChar(); uint price = _isMatch[(_state.Index << 4) + num5].GetPrice1(); uint num6 = price + _isRep[_state.Index].GetPrice1(); if (indexByte2 == indexByte) { uint num7 = num6 + GetRepLen1Price(_state, num5); if (num7 < _optimum[1].Price) { _optimum[1].Price = num7; _optimum[1].MakeAsShortRep(); } } uint num8 = ((lenRes >= repLens[num2]) ? lenRes : repLens[num2]); if (num8 < 2) { backRes = _optimum[1].BackPrev; return 1u; } _optimum[1].PosPrev = 0u; _optimum[0].Backs0 = reps[0]; _optimum[0].Backs1 = reps[1]; _optimum[0].Backs2 = reps[2]; _optimum[0].Backs3 = reps[3]; uint num9 = num8; do { _optimum[num9--].Price = 268435455u; } while (num9 >= 2); for (uint num3 = 0u; num3 < 4; num3++) { uint num10 = repLens[num3]; if (num10 < 2) { continue; } uint num11 = num6 + GetPureRepPrice(num3, _state, num5); do { uint num12 = num11 + _repMatchLenEncoder.GetPrice(num10 - 2, num5); Optimal optimal = _optimum[num10]; if (num12 < optimal.Price) { optimal.Price = num12; optimal.PosPrev = 0u; optimal.BackPrev = num3; optimal.Prev1IsChar = false; } } while (--num10 >= 2); } uint num13 = price + _isRep[_state.Index].GetPrice0(); num9 = ((repLens[0] >= 2) ? (repLens[0] + 1) : 2u); if (num9 <= lenRes) { uint num14; for (num14 = 0u; num9 > _matchDistances[num14]; num14 += 2) { } while (true) { uint num15 = _matchDistances[num14 + 1]; uint num16 = num13 + GetPosLenPrice(num15, num9, num5); Optimal optimal2 = _optimum[num9]; if (num16 < optimal2.Price) { optimal2.Price = num16; optimal2.PosPrev = 0u; optimal2.BackPrev = num15 + 4; optimal2.Prev1IsChar = false; } if (num9 == _matchDistances[num14]) { num14 += 2; if (num14 == numDistancePairs) { break; } } num9++; } } uint num17 = 0u; uint lenRes2; while (true) { num17++; if (num17 == num8) { return Backward(out backRes, num17); } ReadMatchDistances(out lenRes2, out numDistancePairs); if (lenRes2 >= _numFastBytes) { break; } position++; uint num18 = _optimum[num17].PosPrev; Base.State state; if (_optimum[num17].Prev1IsChar) { num18--; if (_optimum[num17].Prev2) { state = _optimum[_optimum[num17].PosPrev2].State; if (_optimum[num17].BackPrev2 < 4) { state.UpdateRep(); } else { state.UpdateMatch(); } } else { state = _optimum[num18].State; } state.UpdateChar(); } else { state = _optimum[num18].State; } if (num18 == num17 - 1) { if (_optimum[num17].IsShortRep()) { state.UpdateShortRep(); } else { state.UpdateChar(); } } else { uint num19; if (_optimum[num17].Prev1IsChar && _optimum[num17].Prev2) { num18 = _optimum[num17].PosPrev2; num19 = _optimum[num17].BackPrev2; state.UpdateRep(); } else { num19 = _optimum[num17].BackPrev; if (num19 < 4) { state.UpdateRep(); } else { state.UpdateMatch(); } } Optimal optimal3 = _optimum[num18]; switch (num19) { case 0u: reps[0] = optimal3.Backs0; reps[1] = optimal3.Backs1; reps[2] = optimal3.Backs2; reps[3] = optimal3.Backs3; break; case 1u: reps[0] = optimal3.Backs1; reps[1] = optimal3.Backs0; reps[2] = optimal3.Backs2; reps[3] = optimal3.Backs3; break; case 2u: reps[0] = optimal3.Backs2; reps[1] = optimal3.Backs0; reps[2] = optimal3.Backs1; reps[3] = optimal3.Backs3; break; case 3u: reps[0] = optimal3.Backs3; reps[1] = optimal3.Backs0; reps[2] = optimal3.Backs1; reps[3] = optimal3.Backs2; break; default: reps[0] = num19 - 4; reps[1] = optimal3.Backs0; reps[2] = optimal3.Backs1; reps[3] = optimal3.Backs2; break; } } _optimum[num17].State = state; _optimum[num17].Backs0 = reps[0]; _optimum[num17].Backs1 = reps[1]; _optimum[num17].Backs2 = reps[2]; _optimum[num17].Backs3 = reps[3]; uint price2 = _optimum[num17].Price; indexByte = _matchFinder.GetIndexByte(-1); indexByte2 = _matchFinder.GetIndexByte((int)(0 - reps[0] - 1 - 1)); num5 = position & _posStateMask; uint num20 = price2 + _isMatch[(state.Index << 4) + num5].GetPrice0() + _literalEncoder.GetSubCoder(position, _matchFinder.GetIndexByte(-2)).GetPrice(!state.IsCharState(), indexByte2, indexByte); Optimal optimal4 = _optimum[num17 + 1]; bool flag = false; if (num20 < optimal4.Price) { optimal4.Price = num20; optimal4.PosPrev = num17; optimal4.MakeAsChar(); flag = true; } price = price2 + _isMatch[(state.Index << 4) + num5].GetPrice1(); num6 = price + _isRep[state.Index].GetPrice1(); if (indexByte2 == indexByte && (optimal4.PosPrev >= num17 || optimal4.BackPrev != 0)) { uint num21 = num6 + GetRepLen1Price(state, num5); if (num21 <= optimal4.Price) { optimal4.Price = num21; optimal4.PosPrev = num17; optimal4.MakeAsShortRep(); flag = true; } } uint val = _matchFinder.GetNumAvailableBytes() + 1; val = Math.Min(4095 - num17, val); num = val; if (num < 2) { continue; } if (num > _numFastBytes) { num = _numFastBytes; } if (!flag && indexByte2 != indexByte) { uint limit = Math.Min(val - 1, _numFastBytes); uint matchLen = _matchFinder.GetMatchLen(0, reps[0], limit); if (matchLen >= 2) { Base.State state2 = state; state2.UpdateChar(); uint num22 = (position + 1) & _posStateMask; uint num23 = num20 + _isMatch[(state2.Index << 4) + num22].GetPrice1() + _isRep[state2.Index].GetPrice1(); uint num24 = num17 + 1 + matchLen; while (num8 < num24) { _optimum[++num8].Price = 268435455u; } uint num25 = num23 + GetRepPrice(0u, matchLen, state2, num22); Optimal optimal5 = _optimum[num24]; if (num25 < optimal5.Price) { optimal5.Price = num25; optimal5.PosPrev = num17 + 1; optimal5.BackPrev = 0u; optimal5.Prev1IsChar = true; optimal5.Prev2 = false; } } } uint num26 = 2u; for (uint num27 = 0u; num27 < 4; num27++) { uint num28 = _matchFinder.GetMatchLen(-1, reps[num27], num); if (num28 < 2) { continue; } uint num29 = num28; while (true) { if (num8 < num17 + num28) { _optimum[++num8].Price = 268435455u; continue; } uint num30 = num6 + GetRepPrice(num27, num28, state, num5); Optimal optimal6 = _optimum[num17 + num28]; if (num30 < optimal6.Price) { optimal6.Price = num30; optimal6.PosPrev = num17; optimal6.BackPrev = num27; optimal6.Prev1IsChar = false; } if (--num28 < 2) { break; } } num28 = num29; if (num27 == 0) { num26 = num28 + 1; } if (num28 >= val) { continue; } uint limit2 = Math.Min(val - 1 - num28, _numFastBytes); uint matchLen2 = _matchFinder.GetMatchLen((int)num28, reps[num27], limit2); if (matchLen2 >= 2) { Base.State state3 = state; state3.UpdateRep(); uint num31 = (position + num28) & _posStateMask; uint num32 = num6 + GetRepPrice(num27, num28, state, num5) + _isMatch[(state3.Index << 4) + num31].GetPrice0() + _literalEncoder.GetSubCoder(position + num28, _matchFinder.GetIndexByte((int)(num28 - 1 - 1))).GetPrice(matchMode: true, _matchFinder.GetIndexByte((int)(num28 - 1 - (reps[num27] + 1))), _matchFinder.GetIndexByte((int)(num28 - 1))); state3.UpdateChar(); num31 = (position + num28 + 1) & _posStateMask; uint num33 = num32 + _isMatch[(state3.Index << 4) + num31].GetPrice1() + _isRep[state3.Index].GetPrice1(); uint num34 = num28 + 1 + matchLen2; while (num8 < num17 + num34) { _optimum[++num8].Price = 268435455u; } uint num35 = num33 + GetRepPrice(0u, matchLen2, state3, num31); Optimal optimal7 = _optimum[num17 + num34]; if (num35 < optimal7.Price) { optimal7.Price = num35; optimal7.PosPrev = num17 + num28 + 1; optimal7.BackPrev = 0u; optimal7.Prev1IsChar = true; optimal7.Prev2 = true; optimal7.PosPrev2 = num17; optimal7.BackPrev2 = num27; } } } if (lenRes2 > num) { lenRes2 = num; for (numDistancePairs = 0u; lenRes2 > _matchDistances[numDistancePairs]; numDistancePairs += 2) { } _matchDistances[numDistancePairs] = lenRes2; numDistancePairs += 2; } if (lenRes2 < num26) { continue; } num13 = price + _isRep[state.Index].GetPrice0(); while (num8 < num17 + lenRes2) { _optimum[++num8].Price = 268435455u; } uint num36; for (num36 = 0u; num26 > _matchDistances[num36]; num36 += 2) { } uint num37 = num26; while (true) { uint num38 = _matchDistances[num36 + 1]; uint num39 = num13 + GetPosLenPrice(num38, num37, num5); Optimal optimal8 = _optimum[num17 + num37]; if (num39 < optimal8.Price) { optimal8.Price = num39; optimal8.PosPrev = num17; optimal8.BackPrev = num38 + 4; optimal8.Prev1IsChar = false; } if (num37 == _matchDistances[num36]) { if (num37 < val) { uint limit3 = Math.Min(val - 1 - num37, _numFastBytes); uint matchLen3 = _matchFinder.GetMatchLen((int)num37, num38, limit3); if (matchLen3 >= 2) { Base.State state4 = state; state4.UpdateMatch(); uint num40 = (position + num37) & _posStateMask; uint num41 = num39 + _isMatch[(state4.Index << 4) + num40].GetPrice0() + _literalEncoder.GetSubCoder(position + num37, _matchFinder.GetIndexByte((int)(num37 - 1 - 1))).GetPrice(matchMode: true, _matchFinder.GetIndexByte((int)(num37 - (num38 + 1) - 1)), _matchFinder.GetIndexByte((int)(num37 - 1))); state4.UpdateChar(); num40 = (position + num37 + 1) & _posStateMask; uint num42 = num41 + _isMatch[(state4.Index << 4) + num40].GetPrice1() + _isRep[state4.Index].GetPrice1(); uint num43 = num37 + 1 + matchLen3; while (num8 < num17 + num43) { _optimum[++num8].Price = 268435455u; } num39 = num42 + GetRepPrice(0u, matchLen3, state4, num40); optimal8 = _optimum[num17 + num43]; if (num39 < optimal8.Price) { optimal8.Price = num39; optimal8.PosPrev = num17 + num37 + 1; optimal8.BackPrev = 0u; optimal8.Prev1IsChar = true; optimal8.Prev2 = true; optimal8.PosPrev2 = num17; optimal8.BackPrev2 = num38 + 4; } } } num36 += 2; if (num36 == numDistancePairs) { break; } } num37++; } } _numDistancePairs = numDistancePairs; _longestMatchLength = lenRes2; _longestMatchWasFound = true; return Backward(out backRes, num17); } private bool ChangePair(uint smallDist, uint bigDist) { if (smallDist < 33554432) { return bigDist >= smallDist << 7; } return false; } private void WriteEndMarker(uint posState) { if (_writeEndMark) { _isMatch[(_state.Index << 4) + posState].Encode(_rangeEncoder, 1u); _isRep[_state.Index].Encode(_rangeEncoder, 0u); _state.UpdateMatch(); uint num = 2u; _lenEncoder.Encode(_rangeEncoder, num - 2, posState); uint symbol = 63u; uint lenToPosState = Base.GetLenToPosState(num); _posSlotEncoder[lenToPosState].Encode(_rangeEncoder, symbol); int num2 = 30; uint num3 = (uint)((1 << num2) - 1); _rangeEncoder.EncodeDirectBits(num3 >> 4, num2 - 4); _posAlignEncoder.ReverseEncode(_rangeEncoder, num3 & 0xFu); } } private void Flush(uint nowPos) { ReleaseMFStream(); WriteEndMarker(nowPos & _posStateMask); _rangeEncoder.FlushData(); _rangeEncoder.FlushStream(); } public void CodeOneBlock(out long inSize, out long outSize, out bool finished) { inSize = 0L; outSize = 0L; finished = true; if (_inStream != null) { _matchFinder.SetStream(_inStream); _matchFinder.Init(); _needReleaseMFStream = true; _inStream = null; if (_trainSize != 0) { _matchFinder.Skip(_trainSize); } } if (_finished) { return; } _finished = true; long num = nowPos64; if (nowPos64 == 0L) { if (_matchFinder.GetNumAvailableBytes() == 0) { Flush((uint)nowPos64); return; } ReadMatchDistances(out var _, out var _); uint num2 = (uint)(int)nowPos64 & _posStateMask; _isMatch[(_state.Index << 4) + num2].Encode(_rangeEncoder, 0u); _state.UpdateChar(); byte indexByte = _matchFinder.GetIndexByte((int)(0 - _additionalOffset)); _literalEncoder.GetSubCoder((uint)nowPos64, _previousByte).Encode(_rangeEncoder, indexByte); _previousByte = indexByte; _additionalOffset--; nowPos64++; } if (_matchFinder.GetNumAvailableBytes() == 0) { Flush((uint)nowPos64); return; } while (true) { uint backRes; uint optimum = GetOptimum((uint)nowPos64, out backRes); uint num3 = (uint)(int)nowPos64 & _posStateMask; uint num4 = (_state.Index << 4) + num3; if (optimum == 1 && backRes == uint.MaxValue) { _isMatch[num4].Encode(_rangeEncoder, 0u); byte indexByte2 = _matchFinder.GetIndexByte((int)(0 - _additionalOffset)); LiteralEncoder.Encoder2 subCoder = _literalEncoder.GetSubCoder((uint)nowPos64, _previousByte); if (!_state.IsCharState()) { byte indexByte3 = _matchFinder.GetIndexByte((int)(0 - _repDistances[0] - 1 - _additionalOffset)); subCoder.EncodeMatched(_rangeEncoder, indexByte3, indexByte2); } else { subCoder.Encode(_rangeEncoder, indexByte2); } _previousByte = indexByte2; _state.UpdateChar(); } else { _isMatch[num4].Encode(_rangeEncoder, 1u); if (backRes < 4) { _isRep[_state.Index].Encode(_rangeEncoder, 1u); if (backRes == 0) { _isRepG0[_state.Index].Encode(_rangeEncoder, 0u); if (optimum == 1) { _isRep0Long[num4].Encode(_rangeEncoder, 0u); } else { _isRep0Long[num4].Encode(_rangeEncoder, 1u); } } else { _isRepG0[_state.Index].Encode(_rangeEncoder, 1u); if (backRes == 1) { _isRepG1[_state.Index].Encode(_rangeEncoder, 0u); } else { _isRepG1[_state.Index].Encode(_rangeEncoder, 1u); _isRepG2[_state.Index].Encode(_rangeEncoder, backRes - 2); } } if (optimum == 1) { _state.UpdateShortRep(); } else { _repMatchLenEncoder.Encode(_rangeEncoder, optimum - 2, num3); _state.UpdateRep(); } uint num5 = _repDistances[backRes]; if (backRes != 0) { for (uint num6 = backRes; num6 >= 1; num6--) { _repDistances[num6] = _repDistances[num6 - 1]; } _repDistances[0] = num5; } } else { _isRep[_state.Index].Encode(_rangeEncoder, 0u); _state.UpdateMatch(); _lenEncoder.Encode(_rangeEncoder, optimum - 2, num3); backRes -= 4; uint posSlot = GetPosSlot(backRes); uint lenToPosState = Base.GetLenToPosState(optimum); _posSlotEncoder[lenToPosState].Encode(_rangeEncoder, posSlot); if (posSlot >= 4) { int num7 = (int)((posSlot >> 1) - 1); uint num8 = (2 | (posSlot & 1)) << num7; uint num9 = backRes - num8; if (posSlot < 14) { BitTreeEncoder.ReverseEncode(_posEncoders, num8 - posSlot - 1, _rangeEncoder, num7, num9); } else { _rangeEncoder.EncodeDirectBits(num9 >> 4, num7 - 4); _posAlignEncoder.ReverseEncode(_rangeEncoder, num9 & 0xFu); _alignPriceCount++; } } uint num10 = backRes; for (uint num11 = 3u; num11 >= 1; num11--) { _repDistances[num11] = _repDistances[num11 - 1]; } _repDistances[0] = num10; _matchPriceCount++; } _previousByte = _matchFinder.GetIndexByte((int)(optimum - 1 - _additionalOffset)); } _additionalOffset -= optimum; nowPos64 += optimum; if (_additionalOffset == 0) { if (_matchPriceCount >= 128) { FillDistancesPrices(); } if (_alignPriceCount >= 16) { FillAlignPrices(); } inSize = nowPos64; outSize = _rangeEncoder.GetProcessedSizeAdd(); if (_matchFinder.GetNumAvailableBytes() == 0) { Flush((uint)nowPos64); return; } if (nowPos64 - num >= 4096) { break; } } } _finished = false; finished = false; } private void ReleaseMFStream() { if (_matchFinder != null && _needReleaseMFStream) { _matchFinder.ReleaseStream(); _needReleaseMFStream = false; } } private void SetOutStream(Stream outStream) { _rangeEncoder.SetStream(outStream); } private void ReleaseOutStream() { _rangeEncoder.ReleaseStream(); } private void ReleaseStreams() { ReleaseMFStream(); ReleaseOutStream(); } private void SetStreams(Stream inStream, Stream outStream, long inSize, long outSize) { _inStream = inStream; _finished = false; Create(); SetOutStream(outStream); Init(); FillDistancesPrices(); FillAlignPrices(); _lenEncoder.SetTableSize(_numFastBytes + 1 - 2); _lenEncoder.UpdateTables((uint)(1 << _posStateBits)); _repMatchLenEncoder.SetTableSize(_numFastBytes + 1 - 2); _repMatchLenEncoder.UpdateTables((uint)(1 << _posStateBits)); nowPos64 = 0L; } public void Code(Stream inStream, Stream outStream, long inSize, long outSize, ICodeProgress progress) { _needReleaseMFStream = false; try { SetStreams(inStream, outStream, inSize, outSize); while (true) { CodeOneBlock(out var inSize2, out var outSize2, out var finished); if (finished) { break; } progress?.SetProgress(inSize2, outSize2); } } finally { ReleaseStreams(); } } public void WriteCoderProperties(Stream outStream) { properties[0] = (byte)((_posStateBits * 5 + _numLiteralPosStateBits) * 9 + _numLiteralContextBits); for (int i = 0; i < 4; i++) { properties[1 + i] = (byte)((_dictionarySize >> 8 * i) & 0xFFu); } outStream.Write(properties, 0, 5); } private void FillDistancesPrices() { for (uint num = 4u; num < 128; num++) { uint posSlot = GetPosSlot(num); int num2 = (int)((posSlot >> 1) - 1); uint num3 = (2 | (posSlot & 1)) << num2; tempPrices[num] = BitTreeEncoder.ReverseGetPrice(_posEncoders, num3 - posSlot - 1, num2, num - num3); } for (uint num4 = 0u; num4 < 4; num4++) { BitTreeEncoder bitTreeEncoder = _posSlotEncoder[num4]; uint num5 = num4 << 6; for (uint num6 = 0u; num6 < _distTableSize; num6++) { _posSlotPrices[num5 + num6] = bitTreeEncoder.GetPrice(num6); } for (uint num6 = 14u; num6 < _distTableSize; num6++) { _posSlotPrices[num5 + num6] += (num6 >> 1) - 1 - 4 << 6; } uint num7 = num4 * 128; uint num8; for (num8 = 0u; num8 < 4; num8++) { _distancesPrices[num7 + num8] = _posSlotPrices[num5 + num8]; } for (; num8 < 128; num8++) { _distancesPrices[num7 + num8] = _posSlotPrices[num5 + GetPosSlot(num8)] + tempPrices[num8]; } } _matchPriceCount = 0u; } private void FillAlignPrices() { for (uint num = 0u; num < 16; num++) { _alignPrices[num] = _posAlignEncoder.ReverseGetPrice(num); } _alignPriceCount = 0u; } private static int FindMatchFinder(string s) { for (int i = 0; i < kMatchFinderIDs.Length; i++) { if (s == kMatchFinderIDs[i]) { return i; } } return -1; } public void SetCoderProperties(CoderPropID[] propIDs, object[] properties) { for (uint num = 0u; num < properties.Length; num++) { object obj = properties[num]; switch (propIDs[num]) { case CoderPropID.NumFastBytes: if (!(obj is int num2)) { throw new InvalidParamException(); } if (num2 < 5 || (long)num2 > 273L) { throw new InvalidParamException(); } _numFastBytes = (uint)num2; break; case CoderPropID.MatchFinder: { if (!(obj is string)) { throw new InvalidParamException(); } EMatchFinderType matchFinderType = _matchFinderType; int num6 = FindMatchFinder(((string)obj).ToUpper()); if (num6 < 0) { throw new InvalidParamException(); } _matchFinderType = (EMatchFinderType)num6; if (_matchFinder != null && matchFinderType != _matchFinderType) { _dictionarySizePrev = uint.MaxValue; _matchFinder = null; } break; } case CoderPropID.DictionarySize: { if (!(obj is int num7)) { throw new InvalidParamException(); } if ((long)num7 < 1L || (long)num7 > 1073741824L) { throw new InvalidParamException(); } _dictionarySize = (uint)num7; int i; for (i = 0; (long)i < 30L && num7 > (uint)(1 << i); i++) { } _distTableSize = (uint)(i * 2); break; } case CoderPropID.PosStateBits: if (!(obj is int num3)) { throw new InvalidParamException(); } if (num3 < 0 || (long)num3 > 4L) { throw new InvalidParamException(); } _posStateBits = num3; _posStateMask = (uint)((1 << _posStateBits) - 1); break; case CoderPropID.LitPosBits: if (!(obj is int num5)) { throw new InvalidParamException(); } if (num5 < 0 || (long)num5 > 4L) { throw new InvalidParamException(); } _numLiteralPosStateBits = num5; break; case CoderPropID.LitContextBits: if (!(obj is int num4)) { throw new InvalidParamException(); } if (num4 < 0 || (long)num4 > 8L) { throw new InvalidParamException(); } _numLiteralContextBits = num4; break; case CoderPropID.EndMarker: if (!(obj is bool)) { throw new InvalidParamException(); } SetWriteEndMarkerMode((bool)obj); break; default: throw new InvalidParamException(); case CoderPropID.Algorithm: break; } } } public void SetTrainSize(uint trainSize) { _trainSize = trainSize; } } public static class SevenZipHelper { private static CoderPropID[] propIDs = new CoderPropID[8] { CoderPropID.DictionarySize, CoderPropID.PosStateBits, CoderPropID.LitContextBits, CoderPropID.LitPosBits, CoderPropID.Algorithm, CoderPropID.NumFastBytes, CoderPropID.MatchFinder, CoderPropID.EndMarker }; private static object[] properties = new object[8] { 2097152, 2, 3, 0, 2, 32, "bt4", false }; public static byte[] Compress(byte[] inputBytes, ICodeProgress progress = null) { MemoryStream inStream = new MemoryStream(inputBytes); MemoryStream memoryStream = new MemoryStream(); Compress(inStream, memoryStream, progress); return memoryStream.ToArray(); } public static void Compress(Stream inStream, Stream outStream, ICodeProgress progress = null) { Encoder encoder = new Encoder(); encoder.SetCoderProperties(propIDs, properties); encoder.WriteCoderProperties(outStream); encoder.Code(inStream, outStream, -1L, -1L, progress); } public static byte[] Decompress(byte[] inputBytes) { MemoryStream memoryStream = new MemoryStream(inputBytes); Decoder decoder = new Decoder(); memoryStream.Seek(0L, SeekOrigin.Begin); MemoryStream memoryStream2 = new MemoryStream(); byte[] array = new byte[5]; if (memoryStream.Read(array, 0, 5) != 5) { throw new Exception("input .lzma is too short"); } long num = 0L; for (int i = 0; i < 8; i++) { int num2 = memoryStream.ReadByte(); if (num2 < 0) { throw new Exception("Can't Read 1"); } num |= (long)((ulong)(byte)num2 << 8 * i); } decoder.SetDecoderProperties(array); long inSize = memoryStream.Length - memoryStream.Position; decoder.Code(memoryStream, memoryStream2, inSize, num, null); return memoryStream2.ToArray(); } public static MemoryStream StreamDecompress(MemoryStream newInStream) { Decoder decoder = new Decoder(); newInStream.Seek(0L, SeekOrigin.Begin); MemoryStream memoryStream = new MemoryStream(); byte[] array = new byte[5]; if (newInStream.Read(array, 0, 5) != 5) { throw new Exception("input .lzma is too short"); } long num = 0L; for (int i = 0; i < 8; i++) { int num2 = newInStream.ReadByte(); if (num2 < 0) { throw new Exception("Can't Read 1"); } num |= (long)((ulong)(byte)num2 << 8 * i); } decoder.SetDecoderProperties(array); long inSize = newInStream.Length - newInStream.Position; decoder.Code(newInStream, memoryStream, inSize, num, null); memoryStream.Position = 0L; return memoryStream; } public static MemoryStream StreamDecompress(MemoryStream newInStream, long outSize) { Decoder decoder = new Decoder(); newInStream.Seek(0L, SeekOrigin.Begin); MemoryStream memoryStream = new MemoryStream(); byte[] array = new byte[5]; if (newInStream.Read(array, 0, 5) != 5) { throw new Exception("input .lzma is too short"); } decoder.SetDecoderProperties(array); long inSize = newInStream.Length - newInStream.Position; decoder.Code(newInStream, memoryStream, inSize, outSize, null); memoryStream.Position = 0L; return memoryStream; } public static void StreamDecompress(Stream compressedStream, Stream decompressedStream, long compressedSize, long decompressedSize) { long position = compressedStream.Position; Decoder decoder = new Decoder(); byte[] array = new byte[5]; if (compressedStream.Read(array, 0, 5) != 5) { throw new Exception("input .lzma is too short"); } decoder.SetDecoderProperties(array); decoder.Code(compressedStream, decompressedStream, compressedSize - 5, decompressedSize, null); compressedStream.Position = position + compressedSize; } } } namespace SevenZip.Buffer { public class InBuffer { private byte[] m_Buffer; private uint m_Pos; private uint m_Limit; private uint m_BufferSize; private Stream m_Stream; private bool m_StreamWasExhausted; private ulong m_ProcessedSize; public InBuffer(uint bufferSize) { m_Buffer = new byte[bufferSize]; m_BufferSize = bufferSize; } public void Init(Stream stream) { m_Stream = stream; m_ProcessedSize = 0uL; m_Limit = 0u; m_Pos = 0u; m_StreamWasExhausted = false; } public bool ReadBlock() { if (m_StreamWasExhausted) { return false; } m_ProcessedSize += m_Pos; int num = m_Stream.Read(m_Buffer, 0, (int)m_BufferSize); m_Pos = 0u; m_Limit = (uint)num; m_StreamWasExhausted = num == 0; return !m_StreamWasExhausted; } public void ReleaseStream() { m_Stream = null; } public bool ReadByte(byte b) { if (m_Pos >= m_Limit && !ReadBlock()) { return false; } b = m_Buffer[m_Pos++]; return true; } public byte ReadByte() { if (m_Pos >= m_Limit && !ReadBlock()) { return byte.MaxValue; } return m_Buffer[m_Pos++]; } public ulong GetProcessedSize() { return m_ProcessedSize + m_Pos; } } public class OutBuffer { private byte[] m_Buffer; private uint m_Pos; private uint m_BufferSize; private Stream m_Stream; private ulong m_ProcessedSize; public OutBuffer(uint bufferSize) { m_Buffer = new byte[bufferSize]; m_BufferSize = bufferSize; } public void SetStream(Stream stream) { m_Stream = stream; } public void FlushStream() { m_Stream.Flush(); } public void CloseStream() { m_Stream.Close(); } public void ReleaseStream() { m_Stream = null; } public void Init() { m_ProcessedSize = 0uL; m_Pos = 0u; } public void WriteByte(byte b) { m_Buffer[m_Pos++] = b; if (m_Pos >= m_BufferSize) { FlushData(); } } public void FlushData() { if (m_Pos != 0) { m_Stream.Write(m_Buffer, 0, (int)m_Pos); m_Pos = 0u; } } public ulong GetProcessedSize() { return m_ProcessedSize + m_Pos; } } } namespace SevenZip.CommandLineParser { public enum SwitchType { Simple, PostMinus, LimitedPostString, UnLimitedPostString, PostChar } public class SwitchForm { public string IDString; public SwitchType Type; public bool Multi; public int MinLen; public int MaxLen; public string PostCharSet; public SwitchForm(string idString, SwitchType type, bool multi, int minLen, int maxLen, string postCharSet) { IDString = idString; Type = type; Multi = multi; MinLen = minLen; MaxLen = maxLen; PostCharSet = postCharSet; } public SwitchForm(string idString, SwitchType type, bool multi, int minLen) : this(idString, type, multi, minLen, 0, "") { } public SwitchForm(string idString, SwitchType type, bool multi) : this(idString, type, multi, 0) { } } public class SwitchResult { public bool ThereIs; public bool WithMinus; public ArrayList PostStrings = new ArrayList(); public int PostCharIndex; public SwitchResult() { ThereIs = false; } } public class Parser { public ArrayList NonSwitchStrings = new ArrayList(); private SwitchResult[] _switches; private const char kSwitchID1 = '-'; private const char kSwitchID2 = '/'; private const char kSwitchMinus = '-'; private const string kStopSwitchParsing = "--"; public SwitchResult this[int index] => _switches[index]; public Parser(int numSwitches) { _switches = new SwitchResult[numSwitches]; for (int i = 0; i < numSwitches; i++) { _switches[i] = new SwitchResult(); } } private bool ParseString(string srcString, SwitchForm[] switchForms) { int length = srcString.Length; if (length == 0) { return false; } int num = 0; if (!IsItSwitchChar(srcString[num])) { return false; } while (num < length) { if (IsItSwitchChar(srcString[num])) { num++; } int num2 = 0; int num3 = -1; for (int i = 0; i < _switches.Length; i++) { int length2 = switchForms[i].IDString.Length; if (length2 > num3 && num + length2 <= length && string.Compare(switchForms[i].IDString, 0, srcString, num, length2, ignoreCase: true) == 0) { num2 = i; num3 = length2; } } if (num3 == -1) { throw new Exception("maxLen == kNoLen"); } SwitchResult switchResult = _switches[num2]; SwitchForm switchForm = switchForms[num2]; if (!switchForm.Multi && switchResult.ThereIs) { throw new Exception("switch must be single"); } switchResult.ThereIs = true; num += num3; int num4 = length - num; SwitchType type = switchForm.Type; switch (type) { case SwitchType.PostMinus: if (num4 == 0) { switchResult.WithMinus = false; break; } switchResult.WithMinus = srcString[num] == '-'; if (switchResult.WithMinus) { num++; } break; case SwitchType.PostChar: { if (num4 < switchForm.MinLen) { throw new Exception("switch is not full"); } string postCharSet = switchForm.PostCharSet; if (num4 == 0) { switchResult.PostCharIndex = -1; break; } int num6 = postCharSet.IndexOf(srcString[num]); if (num6 < 0) { switchResult.PostCharIndex = -1; break; } switchResult.PostCharIndex = num6; num++; break; } case SwitchType.LimitedPostString: case SwitchType.UnLimitedPostString: { int minLen = switchForm.MinLen; if (num4 < minLen) { throw new Exception("switch is not full"); } if (type == SwitchType.UnLimitedPostString) { switchResult.PostStrings.Add(srcString.Substring(num)); return true; } string text = srcString.Substring(num, minLen); num += minLen; int num5 = minLen; while (num5 < switchForm.MaxLen && num < length) { char c = srcString[num]; if (IsItSwitchChar(c)) { break; } text += c; num5++; num++; } switchResult.PostStrings.Add(text); break; } } } return true; } public void ParseStrings(SwitchForm[] switchForms, string[] commandStrings) { int num = commandStrings.Length; bool flag = false; for (int i = 0; i < num; i++) { string text = commandStrings[i]; if (flag) { NonSwitchStrings.Add(text); } else if (text == "--") { flag = true; } else if (!ParseString(text, switchForms)) { NonSwitchStrings.Add(text); } } } public static int ParseCommand(CommandForm[] commandForms, string commandString, out string postString) { for (int i = 0; i < commandForms.Length; i++) { string iDString = commandForms[i].IDString; if (commandForms[i].PostStringMode) { if (commandString.IndexOf(iDString) == 0) { postString = commandString.Substring(iDString.Length); return i; } } else if (commandString == iDString) { postString = ""; return i; } } postString = ""; return -1; } private static bool ParseSubCharsCommand(int numForms, CommandSubCharsSet[] forms, string commandString, ArrayList indices) { indices.Clear(); int num = 0; for (int i = 0; i < numForms; i++) { CommandSubCharsSet commandSubCharsSet = forms[i]; int num2 = -1; int length = commandSubCharsSet.Chars.Length; for (int j = 0; j < length; j++) { char value = commandSubCharsSet.Chars[j]; int num3 = commandString.IndexOf(value); if (num3 >= 0) { if (num2 >= 0) { return false; } if (commandString.IndexOf(value, num3 + 1) >= 0) { return false; } num2 = j; num++; } } if (num2 == -1 && !commandSubCharsSet.EmptyAllowed) { return false; } indices.Add(num2); } return num == commandString.Length; } private static bool IsItSwitchChar(char c) { if (c != '-') { return c == '/'; } return true; } } public class CommandForm { public string IDString = ""; public bool PostStringMode; public CommandForm(string idString, bool postStringMode) { IDString = idString; PostStringMode = postStringMode; } } internal class CommandSubCharsSet { public string Chars = ""; public bool EmptyAllowed; } } namespace LZ4ps { public static class LZ4Codec { private class LZ4HC_Data_Structure { public byte[] src; public int src_base; public int src_end; public int src_LASTLITERALS; public byte[] dst; public int dst_base; public int dst_len; public int dst_end; public int[] hashTable; public ushort[] chainTable; public int nextToUpdate; } private const int MEMORY_USAGE = 14; private const int NOTCOMPRESSIBLE_DETECTIONLEVEL = 6; private const int BLOCK_COPY_LIMIT = 16; private const int MINMATCH = 4; private const int SKIPSTRENGTH = 6; private const int COPYLENGTH = 8; private const int LASTLITERALS = 5; private const int MFLIMIT = 12; private const int MINLENGTH = 13; private const int MAXD_LOG = 16; private const int MAXD = 65536; private const int MAXD_MASK = 65535; private const int MAX_DISTANCE = 65535; private const int ML_BITS = 4; private const int ML_MASK = 15; private const int RUN_BITS = 4; private const int RUN_MASK = 15; private const int STEPSIZE_64 = 8; private const int STEPSIZE_32 = 4; private const int LZ4_64KLIMIT = 65547; private const int HASH_LOG = 12; private const int HASH_TABLESIZE = 4096; private const int HASH_ADJUST = 20; private const int HASH64K_LOG = 13; private const int HASH64K_TABLESIZE = 8192; private const int HASH64K_ADJUST = 19; private const int HASHHC_LOG = 15; private const int HASHHC_TABLESIZE = 32768; private const int HASHHC_ADJUST = 17; private static readonly int[] DECODER_TABLE_32 = new int[8] { 0, 3, 2, 3, 0, 0, 0, 0 }; private static readonly int[] DECODER_TABLE_64 = new int[8] { 0, 0, 0, -1, 0, 1, 2, 3 }; private static readonly int[] DEBRUIJN_TABLE_32 = new int[32] { 0, 0, 3, 0, 3, 1, 3, 0, 3, 2, 2, 1, 3, 2, 0, 1, 3, 3, 1, 2, 2, 2, 2, 0, 3, 1, 2, 0, 1, 0, 1, 1 }; private static readonly int[] DEBRUIJN_TABLE_64 = new int[64] { 0, 0, 0, 0, 0, 1, 1, 2, 0, 3, 1, 3, 1, 4, 2, 7, 0, 2, 3, 6, 1, 5, 3, 5, 1, 3, 4, 4, 2, 5, 6, 7, 7, 0, 1, 2, 3, 3, 4, 6, 2, 6, 5, 5, 3, 4, 5, 6, 7, 1, 2, 4, 6, 4, 4, 5, 7, 2, 6, 5, 7, 6, 7, 7 }; private const int MAX_NB_ATTEMPTS = 256; private const int OPTIMAL_ML = 18; public static int MaximumOutputLength(int inputLength) { return inputLength + inputLength / 255 + 16; } internal static void CheckArguments(byte[] input, int inputOffset, ref int inputLength, byte[] output, int outputOffset, ref int outputLength) { if (inputLength < 0) { inputLength = input.Length - inputOffset; } if (inputLength == 0) { outputLength = 0; return; } if (input == null) { throw new ArgumentNullException("input"); } if (inputOffset < 0 || inputOffset + inputLength > input.Length) { throw new ArgumentException("inputOffset and inputLength are invalid for given input"); } if (outputLength < 0) { outputLength = output.Length - outputOffset; } if (output == null) { throw new ArgumentNullException("output"); } if (outputOffset >= 0 && outputOffset + outputLength <= output.Length) { return; } throw new ArgumentException("outputOffset and outputLength are invalid for given output"); } [Conditional("DEBUG")] private static void Assert(bool condition, string errorMessage) { if (!condition) { throw new ArgumentException(errorMessage); } } internal static void Poke2(byte[] buffer, int offset, ushort value) { buffer[offset] = (byte)value; buffer[offset + 1] = (byte)(value >> 8); } internal static ushort Peek2(byte[] buffer, int offset) { return (ushort)(buffer[offset] | (buffer[offset + 1] << 8)); } internal static uint Peek4(byte[] buffer, int offset) { return (uint)(buffer[offset] | (buffer[offset + 1] << 8) | (buffer[offset + 2] << 16) | (buffer[offset + 3] << 24)); } private static uint Xor4(byte[] buffer, int offset1, int offset2) { int num = buffer[offset1] | (buffer[offset1 + 1] << 8) | (buffer[offset1 + 2] << 16) | (buffer[offset1 + 3] << 24); uint num2 = (uint)(buffer[offset2] | (buffer[offset2 + 1] << 8) | (buffer[offset2 + 2] << 16) | (buffer[offset2 + 3] << 24)); return (uint)num ^ num2; } private static ulong Xor8(byte[] buffer, int offset1, int offset2) { ulong num = buffer[offset1] | ((ulong)buffer[offset1 + 1] << 8) | ((ulong)buffer[offset1 + 2] << 16) | ((ulong)buffer[offset1 + 3] << 24) | ((ulong)buffer[offset1 + 4] << 32) | ((ulong)buffer[offset1 + 5] << 40) | ((ulong)buffer[offset1 + 6] << 48) | ((ulong)buffer[offset1 + 7] << 56); ulong num2 = buffer[offset2] | ((ulong)buffer[offset2 + 1] << 8) | ((ulong)buffer[offset2 + 2] << 16) | ((ulong)buffer[offset2 + 3] << 24) | ((ulong)buffer[offset2 + 4] << 32) | ((ulong)buffer[offset2 + 5] << 40) | ((ulong)buffer[offset2 + 6] << 48) | ((ulong)buffer[offset2 + 7] << 56); return num ^ num2; } private static bool Equal2(byte[] buffer, int offset1, int offset2) { if (buffer[offset1] != buffer[offset2]) { return false; } return buffer[offset1 + 1] == buffer[offset2 + 1]; } private static bool Equal4(byte[] buffer, int offset1, int offset2) { if (buffer[offset1] != buffer[offset2]) { return false; } if (buffer[offset1 + 1] != buffer[offset2 + 1]) { return false; } if (buffer[offset1 + 2] != buffer[offset2 + 2]) { return false; } return buffer[offset1 + 3] == buffer[offset2 + 3]; } private static void Copy4(byte[] buf, int src, int dst) { buf[dst + 3] = buf[src + 3]; buf[dst + 2] = buf[src + 2]; buf[dst + 1] = buf[src + 1]; buf[dst] = buf[src]; } private static void Copy8(byte[] buf, int src, int dst) { buf[dst + 7] = buf[src + 7]; buf[dst + 6] = buf[src + 6]; buf[dst + 5] = buf[src + 5]; buf[dst + 4] = buf[src + 4]; buf[dst + 3] = buf[src + 3]; buf[dst + 2] = buf[src + 2]; buf[dst + 1] = buf[src + 1]; buf[dst] = buf[src]; } private static void BlockCopy(byte[] src, int src_0, byte[] dst, int dst_0, int len) { if (len >= 16) { Buffer.BlockCopy(src, src_0, dst, dst_0, len); return; } while (len >= 8) { dst[dst_0] = src[src_0]; dst[dst_0 + 1] = src[src_0 + 1]; dst[dst_0 + 2] = src[src_0 + 2]; dst[dst_0 + 3] = src[src_0 + 3]; dst[dst_0 + 4] = src[src_0 + 4]; dst[dst_0 + 5] = src[src_0 + 5]; dst[dst_0 + 6] = src[src_0 + 6]; dst[dst_0 + 7] = src[src_0 + 7]; len -= 8; src_0 += 8; dst_0 += 8; } while (len >= 4) { dst[dst_0] = src[src_0]; dst[dst_0 + 1] = src[src_0 + 1]; dst[dst_0 + 2] = src[src_0 + 2]; dst[dst_0 + 3] = src[src_0 + 3]; len -= 4; src_0 += 4; dst_0 += 4; } while (len-- > 0) { dst[dst_0++] = src[src_0++]; } } private static int WildCopy(byte[] src, int src_0, byte[] dst, int dst_0, int dst_end) { int num = dst_end - dst_0; if (num >= 16) { Buffer.BlockCopy(src, src_0, dst, dst_0, num); } else { while (num >= 4) { dst[dst_0] = src[src_0]; dst[dst_0 + 1] = src[src_0 + 1]; dst[dst_0 + 2] = src[src_0 + 2]; dst[dst_0 + 3] = src[src_0 + 3]; num -= 4; src_0 += 4; dst_0 += 4; } while (num-- > 0) { dst[dst_0++] = src[src_0++]; } } return num; } private static int SecureCopy(byte[] buffer, int src, int dst, int dst_end) { int num = dst - src; int num2 = dst_end - dst; int num3 = num2; if (num >= 16) { if (num >= num2) { Buffer.BlockCopy(buffer, src, buffer, dst, num2); return num2; } do { Buffer.BlockCopy(buffer, src, buffer, dst, num); src += num; dst += num; num3 -= num; } while (num3 >= num); } while (num3 >= 4) { buffer[dst] = buffer[src]; buffer[dst + 1] = buffer[src + 1]; buffer[dst + 2] = buffer[src + 2]; buffer[dst + 3] = buffer[src + 3]; dst += 4; src += 4; num3 -= 4; } while (num3-- > 0) { buffer[dst++] = buffer[src++]; } return num2; } public static int Encode32(byte[] input, int inputOffset, int inputLength, byte[] output, int outputOffset, int outputLength) { CheckArguments(input, inputOffset, ref inputLength, output, outputOffset, ref outputLength); if (outputLength == 0) { return 0; } if (inputLength < 65547) { return LZ4_compress64kCtx_safe32(new ushort[8192], input, output, inputOffset, outputOffset, inputLength, outputLength); } return LZ4_compressCtx_safe32(new int[4096], input, output, inputOffset, outputOffset, inputLength, outputLength); } public static byte[] Encode32(byte[] input, int inputOffset, int inputLength) { if (inputLength < 0) { inputLength = input.Length - inputOffset; } if (input == null) { throw new ArgumentNullException("input"); } if (inputOffset < 0 || inputOffset + inputLength > input.Length) { throw new ArgumentException("inputOffset and inputLength are invalid for given input"); } byte[] array = new byte[MaximumOutputLength(inputLength)]; int num = Encode32(input, inputOffset, inputLength, array, 0, array.Length); if (num != array.Length) { if (num < 0) { throw new InvalidOperationException("Compression has been corrupted"); } byte[] array2 = new byte[num]; Buffer.BlockCopy(array, 0, array2, 0, num); return array2; } return array; } public static int Encode64(byte[] input, int inputOffset, int inputLength, byte[] output, int outputOffset, int outputLength) { CheckArguments(input, inputOffset, ref inputLength, output, outputOffset, ref outputLength); if (outputLength == 0) { return 0; } if (inputLength < 65547) { return LZ4_compress64kCtx_safe64(new ushort[8192], input, output, inputOffset, outputOffset, inputLength, outputLength); } return LZ4_compressCtx_safe64(new int[4096], input, output, inputOffset, outputOffset, inputLength, outputLength); } public static byte[] Encode64(byte[] input, int inputOffset, int inputLength) { if (inputLength < 0) { inputLength = input.Length - inputOffset; } if (input == null) { throw new ArgumentNullException("input"); } if (inputOffset < 0 || inputOffset + inputLength > input.Length) { throw new ArgumentException("inputOffset and inputLength are invalid for given input"); } byte[] array = new byte[MaximumOutputLength(inputLength)]; int num = Encode64(input, inputOffset, inputLength, array, 0, array.Length); if (num != array.Length) { if (num < 0) { throw new InvalidOperationException("Compression has been corrupted"); } byte[] array2 = new byte[num]; Buffer.BlockCopy(array, 0, array2, 0, num); return array2; } return array; } public static int Decode32(byte[] input, int inputOffset, int inputLength, byte[] output, int outputOffset, int outputLength, bool knownOutputLength) { CheckArguments(input, inputOffset, ref inputLength, output, outputOffset, ref outputLength); if (outputLength == 0) { return 0; } if (knownOutputLength) { if (LZ4_uncompress_safe32(input, output, inputOffset, outputOffset, outputLength) != inputLength) { throw new ArgumentException("LZ4 block is corrupted, or invalid length has been given."); } return outputLength; } int num = LZ4_uncompress_unknownOutputSize_safe32(input, output, inputOffset, outputOffset, inputLength, outputLength); if (num < 0) { throw new ArgumentException("LZ4 block is corrupted, or invalid length has been given."); } return num; } public static byte[] Decode32(byte[] input, int inputOffset, int inputLength, int outputLength) { if (inputLength < 0) { inputLength = input.Length - inputOffset; } if (input == null) { throw new ArgumentNullException("input"); } if (inputOffset < 0 || inputOffset + inputLength > input.Length) { throw new ArgumentException("inputOffset and inputLength are invalid for given input"); } byte[] array = new byte[outputLength]; if (Decode32(input, inputOffset, inputLength, array, 0, outputLength, knownOutputLength: true) != outputLength) { throw new ArgumentException("outputLength is not valid"); } return array; } public static int Decode64(byte[] input, int inputOffset, int inputLength, byte[] output, int outputOffset, int outputLength, bool knownOutputLength) { CheckArguments(input, inputOffset, ref inputLength, output, outputOffset, ref outputLength); if (outputLength == 0) { return 0; } if (knownOutputLength) { if (LZ4_uncompress_safe64(input, output, inputOffset, outputOffset, outputLength) != inputLength) { throw new ArgumentException("LZ4 block is corrupted, or invalid length has been given."); } return outputLength; } int num = LZ4_uncompress_unknownOutputSize_safe64(input, output, inputOffset, outputOffset, inputLength, outputLength); if (num < 0) { throw new ArgumentException("LZ4 block is corrupted, or invalid length has been given."); } return num; } public static byte[] Decode64(byte[] input, int inputOffset, int inputLength, int outputLength) { if (inputLength < 0) { inputLength = input.Length - inputOffset; } if (input == null) { throw new ArgumentNullException("input"); } if (inputOffset < 0 || inputOffset + inputLength > input.Length) { throw new ArgumentException("inputOffset and inputLength are invalid for given input"); } byte[] array = new byte[outputLength]; if (Decode64(input, inputOffset, inputLength, array, 0, outputLength, knownOutputLength: true) != outputLength) { throw new ArgumentException("outputLength is not valid"); } return array; } private static LZ4HC_Data_Structure LZ4HC_Create(byte[] src, int src_0, int src_len, byte[] dst, int dst_0, int dst_len) { LZ4HC_Data_Structure lZ4HC_Data_Structure = new LZ4HC_Data_Structure { src = src, src_base = src_0, src_end = src_0 + src_len, src_LASTLITERALS = src_0 + src_len - 5, dst = dst, dst_base = dst_0, dst_len = dst_len, dst_end = dst_0 + dst_len, hashTable = new int[32768], chainTable = new ushort[65536], nextToUpdate = src_0 + 1 }; ushort[] chainTable = lZ4HC_Data_Structure.chainTable; for (int num = chainTable.Length - 1; num >= 0; num--) { chainTable[num] = ushort.MaxValue; } return lZ4HC_Data_Structure; } private static int LZ4_compressHC_32(byte[] input, int inputOffset, int inputLength, byte[] output, int outputOffset, int outputLength) { return LZ4_compressHCCtx_32(LZ4HC_Create(input, inputOffset, inputLength, output, outputOffset, outputLength)); } public static int Encode32HC(byte[] input, int inputOffset, int inputLength, byte[] output, int outputOffset, int outputLength) { if (inputLength == 0) { return 0; } CheckArguments(input, inputOffset, ref inputLength, output, outputOffset, ref outputLength); int num = LZ4_compressHC_32(input, inputOffset, inputLength, output, outputOffset, outputLength); if (num > 0) { return num; } return -1; } public static byte[] Encode32HC(byte[] input, int inputOffset, int inputLength) { if (inputLength == 0) { return new byte[0]; } int num = MaximumOutputLength(inputLength); byte[] array = new byte[num]; int num2 = Encode32HC(input, inputOffset, inputLength, array, 0, num); if (num2 < 0) { throw new ArgumentException("Provided data seems to be corrupted."); } if (num2 != num) { byte[] array2 = new byte[num2]; Buffer.BlockCopy(array, 0, array2, 0, num2); array = array2; } return array; } private static int LZ4_compressHC_64(byte[] input, int inputOffset, int inputLength, byte[] output, int outputOffset, int outputLength) { return LZ4_compressHCCtx_64(LZ4HC_Create(input, inputOffset, inputLength, output, outputOffset, outputLength)); } public static int Encode64HC(byte[] input, int inputOffset, int inputLength, byte[] output, int outputOffset, int outputLength) { if (inputLength == 0) { return 0; } CheckArguments(input, inputOffset, ref inputLength, output, outputOffset, ref outputLength); int num = LZ4_compressHC_64(input, inputOffset, inputLength, output, outputOffset, outputLength); if (num > 0) { return num; } return -1; } public static byte[] Encode64HC(byte[] input, int inputOffset, int inputLength) { if (inputLength == 0) { return new byte[0]; } int num = MaximumOutputLength(inputLength); byte[] array = new byte[num]; int num2 = Encode64HC(input, inputOffset, inputLength, array, 0, num); if (num2 < 0) { throw new ArgumentException("Provided data seems to be corrupted."); } if (num2 != num) { byte[] array2 = new byte[num2]; Buffer.BlockCopy(array, 0, array2, 0, num2); array = array2; } return array; } private static int LZ4_compressCtx_safe32(int[] hash_table, byte[] src, byte[] dst, int src_0, int dst_0, int src_len, int dst_maxlen) { int[] dEBRUIJN_TABLE_ = DEBRUIJN_TABLE_32; int num = src_0; int num2 = num; int num3 = num + src_len; int num4 = num3 - 12; int num5 = dst_0; int num6 = num5 + dst_maxlen; int num7 = num3 - 5; int num8 = num7 - 1; int num9 = num7 - 3; int num10 = num6 - 6; int num11 = num6 - 8; if (src_len >= 13) { hash_table[(int)Peek4(src, num) * -1640531535 >>> 20] = num - src_0; num++; uint num12 = (uint)((int)Peek4(src, num) * -1640531535) >> 20; while (true) { int num13 = 67; int num14 = num; int num17; while (true) { uint num15 = num12; int num16 = num13++ >> 6; num = num14; num14 = num + num16; if (num14 > num4) { break; } num12 = (uint)((int)Peek4(src, num14) * -1640531535) >> 20; num17 = src_0 + hash_table[num15]; hash_table[num15] = num - src_0; if (num17 < num - 65535 || !Equal4(src, num17, num)) { continue; } goto IL_00e3; } break; IL_0340: num2 = num++; num12 = (uint)((int)Peek4(src, num) * -1640531535) >> 20; continue; IL_00e3: while (num > num2 && num17 > src_0 && src[num - 1] == src[num17 - 1]) { num--; num17--; } int num18 = num - num2; int num19 = num5++; if (num5 + num18 + (num18 >> 8) > num11) { return 0; } if (num18 >= 15) { int num20 = num18 - 15; dst[num19] = 240; if (num20 > 254) { do { dst[num5++] = byte.MaxValue; num20 -= 255; } while (num20 > 254); dst[num5++] = (byte)num20; BlockCopy(src, num2, dst, num5, num18); num5 += num18; goto IL_01ad; } dst[num5++] = (byte)num20; } else { dst[num19] = (byte)(num18 << 4); } if (num18 > 0) { int num21 = num5 + num18; WildCopy(src, num2, dst, num5, num21); num5 = num21; } goto IL_01ad; IL_01ad: while (true) { Poke2(dst, num5, (ushort)(num - num17)); num5 += 2; num += 4; num17 += 4; num2 = num; while (true) { if (num < num9) { int num22 = (int)Xor4(src, num17, num); if (num22 == 0) { num += 4; num17 += 4; continue; } num += dEBRUIJN_TABLE_[(num22 & -num22) * 125613361 >>> 27]; break; } if (num < num8 && Equal2(src, num17, num)) { num += 2; num17 += 2; } if (num < num7 && src[num17] == src[num]) { num++; } break; } num18 = num - num2; if (num5 + (num18 >> 8) > num10) { return 0; } if (num18 >= 15) { dst[num19] += 15; for (num18 -= 15; num18 > 509; num18 -= 510) { dst[num5++] = byte.MaxValue; dst[num5++] = byte.MaxValue; } if (num18 > 254) { num18 -= 255; dst[num5++] = byte.MaxValue; } dst[num5++] = (byte)num18; } else { dst[num19] += (byte)num18; } if (num > num4) { break; } hash_table[(int)Peek4(src, num - 2) * -1640531535 >>> 20] = num - 2 - src_0; uint num15 = (uint)((int)Peek4(src, num) * -1640531535) >> 20; num17 = src_0 + hash_table[num15]; hash_table[num15] = num - src_0; if (num17 > num - 65536 && Equal4(src, num17, num)) { num19 = num5++; dst[num19] = 0; continue; } goto IL_0340; } num2 = num; break; } } int num23 = num3 - num2; if (num5 + num23 + 1 + (num23 + 255 - 15) / 255 > num6) { return 0; } if (num23 >= 15) { dst[num5++] = 240; for (num23 -= 15; num23 > 254; num23 -= 255) { dst[num5++] = byte.MaxValue; } dst[num5++] = (byte)num23; } else { dst[num5++] = (byte)(num23 << 4); } BlockCopy(src, num2, dst, num5, num3 - num2); num5 += num3 - num2; return num5 - dst_0; } private static int LZ4_compress64kCtx_safe32(ushort[] hash_table, byte[] src, byte[] dst, int src_0, int dst_0, int src_len, int dst_maxlen) { int[] dEBRUIJN_TABLE_ = DEBRUIJN_TABLE_32; int num = src_0; int num2 = num; int num3 = num; int num4 = num + src_len; int num5 = num4 - 12; int num6 = dst_0; int num7 = num6 + dst_maxlen; int num8 = num4 - 5; int num9 = num8 - 1; int num10 = num8 - 3; int num11 = num7 - 6; int num12 = num7 - 8; if (src_len >= 13) { num++; uint num13 = (uint)((int)Peek4(src, num) * -1640531535) >> 19; while (true) { int num14 = 67; int num15 = num; int num18; while (true) { uint num16 = num13; int num17 = num14++ >> 6; num = num15; num15 = num + num17; if (num15 > num5) { break; } num13 = (uint)((int)Peek4(src, num15) * -1640531535) >> 19; num18 = num3 + hash_table[num16]; hash_table[num16] = (ushort)(num - num3); if (!Equal4(src, num18, num)) { continue; } goto IL_00c6; } break; IL_0313: num2 = num++; num13 = (uint)((int)Peek4(src, num) * -1640531535) >> 19; continue; IL_00c6: while (num > num2 && num18 > src_0 && src[num - 1] == src[num18 - 1]) { num--; num18--; } int num19 = num - num2; int num20 = num6++; if (num6 + num19 + (num19 >> 8) > num12) { return 0; } if (num19 >= 15) { int num21 = num19 - 15; dst[num20] = 240; if (num21 > 254) { do { dst[num6++] = byte.MaxValue; num21 -= 255; } while (num21 > 254); dst[num6++] = (byte)num21; BlockCopy(src, num2, dst, num6, num19); num6 += num19; goto IL_018c; } dst[num6++] = (byte)num21; } else { dst[num20] = (byte)(num19 << 4); } if (num19 > 0) { int num22 = num6 + num19; WildCopy(src, num2, dst, num6, num22); num6 = num22; } goto IL_018c; IL_018c: while (true) { Poke2(dst, num6, (ushort)(num - num18)); num6 += 2; num += 4; num18 += 4; num2 = num; while (true) { if (num < num10) { int num23 = (int)Xor4(src, num18, num); if (num23 == 0) { num += 4; num18 += 4; continue; } num += dEBRUIJN_TABLE_[(num23 & -num23) * 125613361 >>> 27]; break; } if (num < num9 && Equal2(src, num18, num)) { num += 2; num18 += 2; } if (num < num8 && src[num18] == src[num]) { num++; } break; } int num21 = num - num2; if (num6 + (num21 >> 8) > num11) { return 0; } if (num21 >= 15) { dst[num20] += 15; for (num21 -= 15; num21 > 509; num21 -= 510) { dst[num6++] = byte.MaxValue; dst[num6++] = byte.MaxValue; } if (num21 > 254) { num21 -= 255; dst[num6++] = byte.MaxValue; } dst[num6++] = (byte)num21; } else { dst[num20] += (byte)num21; } if (num > num5) { break; } hash_table[(int)Peek4(src, num - 2) * -1640531535 >>> 19] = (ushort)(num - 2 - num3); uint num16 = (uint)((int)Peek4(src, num) * -1640531535) >> 19; num18 = num3 + hash_table[num16]; hash_table[num16] = (ushort)(num - num3); if (Equal4(src, num18, num)) { num20 = num6++; dst[num20] = 0; continue; } goto IL_0313; } num2 = num; break; } } int num24 = num4 - num2; if (num6 + num24 + 1 + (num24 - 15 + 255) / 255 > num7) { return 0; } if (num24 >= 15) { dst[num6++] = 240; for (num24 -= 15; num24 > 254; num24 -= 255) { dst[num6++] = byte.MaxValue; } dst[num6++] = (byte)num24; } else { dst[num6++] = (byte)(num24 << 4); } BlockCopy(src, num2, dst, num6, num4 - num2); num6 += num4 - num2; return num6 - dst_0; } private static int LZ4_uncompress_safe32(byte[] src, byte[] dst, int src_0, int dst_0, int dst_len) { int[] dECODER_TABLE_ = DECODER_TABLE_32; int num = src_0; int num2 = dst_0; int num3 = num2 + dst_len; int num4 = num3 - 5; int num5 = num3 - 8; int num6 = num3 - 8; while (true) { byte b = src[num++]; int num7; if ((num7 = b >> 4) == 15) { int num8; while ((num8 = src[num++]) == 255) { num7 += 255; } num7 += num8; } int num9 = num2 + num7; if (num9 > num5) { if (num9 != num3) { break; } BlockCopy(src, num, dst, num2, num7); num += num7; return num - src_0; } if (num2 < num9) { int num10 = WildCopy(src, num, dst, num2, num9); num += num10; num2 += num10; } num -= num2 - num9; num2 = num9; int num11 = num9 - Peek2(src, num); num += 2; if (num11 < dst_0) { break; } if ((num7 = b & 0xF) == 15) { while (src[num] == byte.MaxValue) { num++; num7 += 255; } num7 += src[num++]; } if (num2 - num11 < 4) { dst[num2] = dst[num11]; dst[num2 + 1] = dst[num11 + 1]; dst[num2 + 2] = dst[num11 + 2]; dst[num2 + 3] = dst[num11 + 3]; num2 += 4; num11 += 4; num11 -= dECODER_TABLE_[num2 - num11]; Copy4(dst, num11, num2); num2 = num2; num11 = num11; } else { Copy4(dst, num11, num2); num2 += 4; num11 += 4; } num9 = num2 + num7; if (num9 > num6) { if (num9 > num4) { break; } if (num2 < num5) { int num10 = SecureCopy(dst, num11, num2, num5); num11 += num10; num2 += num10; } while (num2 < num9) { dst[num2++] = dst[num11++]; } num2 = num9; } else { if (num2 < num9) { SecureCopy(dst, num11, num2, num9); } num2 = num9; } } return -(num - src_0); } private static int LZ4_uncompress_unknownOutputSize_safe32(byte[] src, byte[] dst, int src_0, int dst_0, int src_len, int dst_maxlen) { int[] dECODER_TABLE_ = DECODER_TABLE_32; int num = src_0; int num2 = num + src_len; int num3 = dst_0; int num4 = num3 + dst_maxlen; int num5 = num2 - 8; int num6 = num2 - 6; int num7 = num4 - 8; int num8 = num4 - 8; int num9 = num4 - 5; int num10 = num4 - 12; if (num != num2) { while (true) { byte b = src[num++]; int num11; if ((num11 = b >> 4) == 15) { int num12 = 255; while (num < num2 && num12 == 255) { num11 += (num12 = src[num++]); } } int num13 = num3 + num11; if (num13 > num10 || num + num11 > num5) { if (num13 > num4 || num + num11 != num2) { break; } BlockCopy(src, num, dst, num3, num11); num3 += num11; return num3 - dst_0; } if (num3 < num13) { int num14 = WildCopy(src, num, dst, num3, num13); num += num14; num3 += num14; } num -= num3 - num13; num3 = num13; int num15 = num13 - Peek2(src, num); num += 2; if (num15 < dst_0) { break; } if ((num11 = b & 0xF) == 15) { while (num < num6) { int num16 = src[num++]; num11 += num16; if (num16 != 255) { break; } } } if (num3 - num15 < 4) { dst[num3] = dst[num15]; dst[num3 + 1] = dst[num15 + 1]; dst[num3 + 2] = dst[num15 + 2]; dst[num3 + 3] = dst[num15 + 3]; num3 += 4; num15 += 4; num15 -= dECODER_TABLE_[num3 - num15]; Copy4(dst, num15, num3); num3 = num3; num15 = num15; } else { Copy4(dst, num15, num3); num3 += 4; num15 += 4; } num13 = num3 + num11; if (num13 > num8) { if (num13 > num9) { break; } if (num3 < num7) { int num14 = SecureCopy(dst, num15, num3, num7); num15 += num14; num3 += num14; } while (num3 < num13) { dst[num3++] = dst[num15++]; } num3 = num13; } else { if (num3 < num13) { SecureCopy(dst, num15, num3, num13); } num3 = num13; } } } return -(num - src_0); } private static void LZ4HC_Insert_32(LZ4HC_Data_Structure ctx, int src_p) { ushort[] chainTable = ctx.chainTable; int[] hashTable = ctx.hashTable; int i = ctx.nextToUpdate; byte[] src = ctx.src; int src_base = ctx.src_base; for (; i < src_p; i++) { int num = i; int num2 = num - (hashTable[(int)Peek4(src, num) * -1640531535 >>> 17] + src_base); if (num2 > 65535) { num2 = 65535; } chainTable[num & 0xFFFF] = (ushort)num2; hashTable[(int)Peek4(src, num) * -1640531535 >>> 17] = num - src_base; } ctx.nextToUpdate = i; } private static int LZ4HC_CommonLength_32(LZ4HC_Data_Structure ctx, int p1, int p2) { int[] dEBRUIJN_TABLE_ = DEBRUIJN_TABLE_32; byte[] src = ctx.src; int src_LASTLITERALS = ctx.src_LASTLITERALS; int num = p1; while (num < src_LASTLITERALS - 3) { int num2 = (int)Xor4(src, p2, num); if (num2 == 0) { num += 4; p2 += 4; continue; } num += dEBRUIJN_TABLE_[(num2 & -num2) * 125613361 >>> 27]; return num - p1; } if (num < src_LASTLITERALS - 1 && Equal2(src, p2, num)) { num += 2; p2 += 2; } if (num < src_LASTLITERALS && src[p2] == src[num]) { num++; } return num - p1; } private static int LZ4HC_InsertAndFindBestMatch_32(LZ4HC_Data_Structure ctx, int src_p, ref int src_match) { ushort[] chainTable = ctx.chainTable; int[] hashTable = ctx.hashTable; byte[] src = ctx.src; int src_base = ctx.src_base; int num = 256; int num2 = 0; int num3 = 0; ushort num4 = 0; LZ4HC_Insert_32(ctx, src_p); int num5 = hashTable[(int)Peek4(src, src_p) * -1640531535 >>> 17] + src_base; if (num5 >= src_p - 4) { if (Equal4(src, num5, src_p)) { num4 = (ushort)(src_p - num5); num2 = (num3 = LZ4HC_CommonLength_32(ctx, src_p + 4, num5 + 4) + 4); src_match = num5; } num5 -= chainTable[num5 & 0xFFFF]; } while (num5 >= src_p - 65535 && num != 0) { num--; if (src[num5 + num3] == src[src_p + num3] && Equal4(src, num5, src_p)) { int num6 = LZ4HC_CommonLength_32(ctx, src_p + 4, num5 + 4) + 4; if (num6 > num3) { num3 = num6; src_match = num5; } } num5 -= chainTable[num5 & 0xFFFF]; } if (num2 != 0) { int i = src_p; int num7; for (num7 = src_p + num2 - 3; i < num7 - num4; i++) { chainTable[i & 0xFFFF] = num4; } do { chainTable[i & 0xFFFF] = num4; hashTable[(int)Peek4(src, i) * -1640531535 >>> 17] = i - src_base; i++; } while (i < num7); ctx.nextToUpdate = num7; } return num3; } private static int LZ4HC_InsertAndGetWiderMatch_32(LZ4HC_Data_Structure ctx, int src_p, int startLimit, int longest, ref int matchpos, ref int startpos) { ushort[] chainTable = ctx.chainTable; int[] hashTable = ctx.hashTable; byte[] src = ctx.src; int src_base = ctx.src_base; int src_LASTLITERALS = ctx.src_LASTLITERALS; int[] dEBRUIJN_TABLE_ = DEBRUIJN_TABLE_32; int num = 256; int num2 = src_p - startLimit; LZ4HC_Insert_32(ctx, src_p); int num3 = hashTable[(int)Peek4(src, src_p) * -1640531535 >>> 17] + src_base; while (num3 >= src_p - 65535 && num != 0) { num--; if (src[startLimit + longest] == src[num3 - num2 + longest] && Equal4(src, num3, src_p)) { int num4 = num3 + 4; int num5 = src_p + 4; int num6 = src_p; while (true) { if (num5 < src_LASTLITERALS - 3) { int num7 = (int)Xor4(src, num4, num5); if (num7 == 0) { num5 += 4; num4 += 4; continue; } num5 += dEBRUIJN_TABLE_[(num7 & -num7) * 125613361 >>> 27]; break; } if (num5 < src_LASTLITERALS - 1 && Equal2(src, num4, num5)) { num5 += 2; num4 += 2; } if (num5 < src_LASTLITERALS && src[num4] == src[num5]) { num5++; } break; } num4 = num3; while (num6 > startLimit && num4 > src_base && src[num6 - 1] == src[num4 - 1]) { num6--; num4--; } if (num5 - num6 > longest) { longest = num5 - num6; matchpos = num4; startpos = num6; } } num3 -= chainTable[num3 & 0xFFFF]; } return longest; } private static int LZ4_encodeSequence_32(LZ4HC_Data_Structure ctx, ref int src_p, ref int dst_p, ref int src_anchor, int matchLength, int src_ref, int dst_end) { byte[] src = ctx.src; byte[] dst = ctx.dst; int num = src_p - src_anchor; int num2 = dst_p++; if (dst_p + num + 8 + (num >> 8) > dst_end) { return 1; } int num3; if (num >= 15) { dst[num2] = 240; for (num3 = num - 15; num3 > 254; num3 -= 255) { dst[dst_p++] = byte.MaxValue; } dst[dst_p++] = (byte)num3; } else { dst[num2] = (byte)(num << 4); } if (num > 0) { int num4 = dst_p + num; src_anchor += WildCopy(src, src_anchor, dst, dst_p, num4); dst_p = num4; } Poke2(dst, dst_p, (ushort)(src_p - src_ref)); dst_p += 2; num3 = matchLength - 4; if (dst_p + 6 + (num3 >> 8) > dst_end) { return 1; } if (num3 >= 15) { dst[num2] += 15; for (num3 -= 15; num3 > 509; num3 -= 510) { dst[dst_p++] = byte.MaxValue; dst[dst_p++] = byte.MaxValue; } if (num3 > 254) { num3 -= 255; dst[dst_p++] = byte.MaxValue; } dst[dst_p++] = (byte)num3; } else { dst[num2] += (byte)num3; } src_p += matchLength; src_anchor = src_p; return 0; } private static int LZ4_compressHCCtx_32(LZ4HC_Data_Structure ctx) { byte[] src = ctx.src; byte[] dst = ctx.dst; int src_base = ctx.src_base; int src_end = ctx.src_end; int dst_base = ctx.dst_base; int dst_len = ctx.dst_len; int dst_end = ctx.dst_end; int num = src_base; int src_anchor = num; int num2 = src_end - 12; int dst_p = dst_base; int src_match = 0; int startpos = 0; int matchpos = 0; int startpos2 = 0; int matchpos2 = 0; num++; while (num < num2) { int num3 = LZ4HC_InsertAndFindBestMatch_32(ctx, num, ref src_match); if (num3 == 0) { num++; continue; } int num4 = num; int num5 = src_match; int num6 = num3; while (true) { int num7 = ((num + num3 < num2) ? LZ4HC_InsertAndGetWiderMatch_32(ctx, num + num3 - 2, num + 1, num3, ref matchpos, ref startpos) : num3); if (num7 == num3) { if (LZ4_encodeSequence_32(ctx, ref num, ref dst_p, ref src_anchor, num3, src_match, dst_end) == 0) { break; } return 0; } if (num4 < num && startpos < num + num6) { num = num4; src_match = num5; num3 = num6; } if (startpos - num < 3) { num3 = num7; num = startpos; src_match = matchpos; continue; } int num10; while (true) { if (startpos - num < 18) { int num8 = num3; if (num8 > 18) { num8 = 18; } if (num + num8 > startpos + num7 - 4) { num8 = startpos - num + num7 - 4; } int num9 = num8 - (startpos - num); if (num9 > 0) { startpos += num9; matchpos += num9; num7 -= num9; } } num10 = ((startpos + num7 < num2) ? LZ4HC_InsertAndGetWiderMatch_32(ctx, startpos + num7 - 3, startpos, num7, ref matchpos2, ref startpos2) : num7); if (num10 == num7) { break; } if (startpos2 < num + num3 + 3) { if (startpos2 < num + num3) { startpos = startpos2; matchpos = matchpos2; num7 = num10; continue; } goto IL_01d1; } if (startpos < num + num3) { if (startpos - num < 15) { if (num3 > 18) { num3 = 18; } if (num + num3 > startpos + num7 - 4) { num3 = startpos - num + num7 - 4; } int num11 = num3 - (startpos - num); if (num11 > 0) { startpos += num11; matchpos += num11; num7 -= num11; } } else { num3 = startpos - num; } } if (LZ4_encodeSequence_32(ctx, ref num, ref dst_p, ref src_anchor, num3, src_match, dst_end) != 0) { return 0; } num = startpos; src_match = matchpos; num3 = num7; startpos = startpos2; matchpos = matchpos2; num7 = num10; } if (startpos < num + num3) { num3 = startpos - num; } if (LZ4_encodeSequence_32(ctx, ref num, ref dst_p, ref src_anchor, num3, src_match, dst_end) != 0) { return 0; } num = startpos; if (LZ4_encodeSequence_32(ctx, ref num, ref dst_p, ref src_anchor, num7, matchpos, dst_end) == 0) { break; } return 0; IL_01d1: if (startpos < num + num3) { int num12 = num + num3 - startpos; startpos += num12; matchpos += num12; num7 -= num12; if (num7 < 4) { startpos = startpos2; matchpos = matchpos2; num7 = num10; } } if (LZ4_encodeSequence_32(ctx, ref num, ref dst_p, ref src_anchor, num3, src_match, dst_end) != 0) { return 0; } num = startpos2; src_match = matchpos2; num3 = num10; num4 = startpos; num5 = matchpos; num6 = num7; } } int num13 = src_end - src_anchor; if (dst_p - dst_base + num13 + 1 + (num13 + 255 - 15) / 255 > (uint)dst_len) { return 0; } if (num13 >= 15) { dst[dst_p++] = 240; for (num13 -= 15; num13 > 254; num13 -= 255) { dst[dst_p++] = byte.MaxValue; } dst[dst_p++] = (byte)num13; } else { dst[dst_p++] = (byte)(num13 << 4); } BlockCopy(src, src_anchor, dst, dst_p, src_end - src_anchor); dst_p += src_end - src_anchor; return dst_p - dst_base; } private static int LZ4_compressCtx_safe64(int[] hash_table, byte[] src, byte[] dst, int src_0, int dst_0, int src_len, int dst_maxlen) { int[] dEBRUIJN_TABLE_ = DEBRUIJN_TABLE_64; int num = src_0; int num2 = num; int num3 = num + src_len; int num4 = num3 - 12; int num5 = dst_0; int num6 = num5 + dst_maxlen; int num7 = num3 - 5; int num8 = num7 - 1; int num9 = num7 - 3; int num10 = num7 - 7; int num11 = num6 - 6; int num12 = num6 - 8; if (src_len >= 13) { hash_table[(int)Peek4(src, num) * -1640531535 >>> 20] = num - src_0; num++; uint num13 = (uint)((int)Peek4(src, num) * -1640531535) >> 20; while (true) { int num14 = 67; int num15 = num; int num18; while (true) { uint num16 = num13; int num17 = num14++ >> 6; num = num15; num15 = num + num17; if (num15 > num4) { break; } num13 = (uint)((int)Peek4(src, num15) * -1640531535) >> 20; num18 = src_0 + hash_table[num16]; hash_table[num16] = num - src_0; if (num18 < num - 65535 || !Equal4(src, num18, num)) { continue; } goto IL_00e9; } break; IL_0365: num2 = num++; num13 = (uint)((int)Peek4(src, num) * -1640531535) >> 20; continue; IL_00e9: while (num > num2 && num18 > src_0 && src[num - 1] == src[num18 - 1]) { num--; num18--; } int num19 = num - num2; int num20 = num5++; if (num5 + num19 + (num19 >> 8) > num12) { return 0; } if (num19 >= 15) { int num21 = num19 - 15; dst[num20] = 240; if (num21 > 254) { do { dst[num5++] = byte.MaxValue; num21 -= 255; } while (num21 > 254); dst[num5++] = (byte)num21; BlockCopy(src, num2, dst, num5, num19); num5 += num19; goto IL_01b3; } dst[num5++] = (byte)num21; } else { dst[num20] = (byte)(num19 << 4); } if (num19 > 0) { int num22 = num5 + num19; WildCopy(src, num2, dst, num5, num22); num5 = num22; } goto IL_01b3; IL_01b3: while (true) { Poke2(dst, num5, (ushort)(num - num18)); num5 += 2; num += 4; num18 += 4; num2 = num; while (true) { if (num < num10) { long num23 = (long)Xor8(src, num18, num); if (num23 == 0L) { num += 8; num18 += 8; continue; } num += dEBRUIJN_TABLE_[(num23 & -num23) * 151050438428048703L >>> 58]; break; } if (num < num9 && Equal4(src, num18, num)) { num += 4; num18 += 4; } if (num < num8 && Equal2(src, num18, num)) { num += 2; num18 += 2; } if (num < num7 && src[num18] == src[num]) { num++; } break; } num19 = num - num2; if (num5 + (num19 >> 8) > num11) { return 0; } if (num19 >= 15) { dst[num20] += 15; for (num19 -= 15; num19 > 509; num19 -= 510) { dst[num5++] = byte.MaxValue; dst[num5++] = byte.MaxValue; } if (num19 > 254) { num19 -= 255; dst[num5++] = byte.MaxValue; } dst[num5++] = (byte)num19; } else { dst[num20] += (byte)num19; } if (num > num4) { break; } hash_table[(int)Peek4(src, num - 2) * -1640531535 >>> 20] = num - 2 - src_0; uint num16 = (uint)((int)Peek4(src, num) * -1640531535) >> 20; num18 = src_0 + hash_table[num16]; hash_table[num16] = num - src_0; if (num18 > num - 65536 && Equal4(src, num18, num)) { num20 = num5++; dst[num20] = 0; continue; } goto IL_0365; } num2 = num; break; } } int num24 = num3 - num2; if (num5 + num24 + 1 + (num24 + 255 - 15) / 255 > num6) { return 0; } if (num24 >= 15) { dst[num5++] = 240; for (num24 -= 15; num24 > 254; num24 -= 255) { dst[num5++] = byte.MaxValue; } dst[num5++] = (byte)num24; } else { dst[num5++] = (byte)(num24 << 4); } BlockCopy(src, num2, dst, num5, num3 - num2); num5 += num3 - num2; return num5 - dst_0; } private static int LZ4_compress64kCtx_safe64(ushort[] hash_table, byte[] src, byte[] dst, int src_0, int dst_0, int src_len, int dst_maxlen) { int[] dEBRUIJN_TABLE_ = DEBRUIJN_TABLE_64; int num = src_0; int num2 = num; int num3 = num; int num4 = num + src_len; int num5 = num4 - 12; int num6 = dst_0; int num7 = num6 + dst_maxlen; int num8 = num4 - 5; int num9 = num8 - 1; int num10 = num8 - 3; int num11 = num8 - 7; int num12 = num7 - 6; int num13 = num7 - 8; if (src_len >= 13) { num++; uint num14 = (uint)((int)Peek4(src, num) * -1640531535) >> 19; while (true) { int num15 = 67; int num16 = num; int num19; while (true) { uint num17 = num14; int num18 = num15++ >> 6; num = num16; num16 = num + num18; if (num16 > num5) { break; } num14 = (uint)((int)Peek4(src, num16) * -1640531535) >> 19; num19 = num3 + hash_table[num17]; hash_table[num17] = (ushort)(num - num3); if (!Equal4(src, num19, num)) { continue; } goto IL_00cc; } break; IL_0338: num2 = num++; num14 = (uint)((int)Peek4(src, num) * -1640531535) >> 19; continue; IL_00cc: while (num > num2 && num19 > src_0 && src[num - 1] == src[num19 - 1]) { num--; num19--; } int num20 = num - num2; int num21 = num6++; if (num6 + num20 + (num20 >> 8) > num13) { return 0; } if (num20 >= 15) { int num22 = num20 - 15; dst[num21] = 240; if (num22 > 254) { do { dst[num6++] = byte.MaxValue; num22 -= 255; } while (num22 > 254); dst[num6++] = (byte)num22; BlockCopy(src, num2, dst, num6, num20); num6 += num20; goto IL_0192; } dst[num6++] = (byte)num22; } else { dst[num21] = (byte)(num20 << 4); } if (num20 > 0) { int num23 = num6 + num20; WildCopy(src, num2, dst, num6, num23); num6 = num23; } goto IL_0192; IL_0192: while (true) { Poke2(dst, num6, (ushort)(num - num19)); num6 += 2; num += 4; num19 += 4; num2 = num; while (true) { if (num < num11) { long num24 = (long)Xor8(src, num19, num); if (num24 == 0L) { num += 8; num19 += 8; continue; } num += dEBRUIJN_TABLE_[(num24 & -num24) * 151050438428048703L >>> 58]; break; } if (num < num10 && Equal4(src, num19, num)) { num += 4; num19 += 4; } if (num < num9 && Equal2(src, num19, num)) { num += 2; num19 += 2; } if (num < num8 && src[num19] == src[num]) { num++; } break; } int num22 = num - num2; if (num6 + (num22 >> 8) > num12) { return 0; } if (num22 >= 15) { dst[num21] += 15; for (num22 -= 15; num22 > 509; num22 -= 510) { dst[num6++] = byte.MaxValue; dst[num6++] = byte.MaxValue; } if (num22 > 254) { num22 -= 255; dst[num6++] = byte.MaxValue; } dst[num6++] = (byte)num22; } else { dst[num21] += (byte)num22; } if (num > num5) { break; } hash_table[(int)Peek4(src, num - 2) * -1640531535 >>> 19] = (ushort)(num - 2 - num3); uint num17 = (uint)((int)Peek4(src, num) * -1640531535) >> 19; num19 = num3 + hash_table[num17]; hash_table[num17] = (ushort)(num - num3); if (Equal4(src, num19, num)) { num21 = num6++; dst[num21] = 0; continue; } goto IL_0338; } num2 = num; break; } } int num25 = num4 - num2; if (num6 + num25 + 1 + (num25 - 15 + 255) / 255 > num7) { return 0; } if (num25 >= 15) { dst[num6++] = 240; for (num25 -= 15; num25 > 254; num25 -= 255) { dst[num6++] = byte.MaxValue; } dst[num6++] = (byte)num25; } else { dst[num6++] = (byte)(num25 << 4); } BlockCopy(src, num2, dst, num6, num4 - num2); num6 += num4 - num2; return num6 - dst_0; } private static int LZ4_uncompress_safe64(byte[] src, byte[] dst, int src_0, int dst_0, int dst_len) { int[] dECODER_TABLE_ = DECODER_TABLE_32; int[] dECODER_TABLE_2 = DECODER_TABLE_64; int num = src_0; int num2 = dst_0; int num3 = num2 + dst_len; int num4 = num3 - 5; int num5 = num3 - 8; int num6 = num3 - 8 - 4; while (true) { uint num7 = src[num++]; int num8; if ((num8 = (byte)(num7 >> 4)) == 15) { int num9; while ((num9 = src[num++]) == 255) { num8 += 255; } num8 += num9; } int num10 = num2 + num8; if (num10 > num5) { if (num10 != num3) { break; } BlockCopy(src, num, dst, num2, num8); num += num8; return num - src_0; } if (num2 < num10) { int num11 = WildCopy(src, num, dst, num2, num10); num += num11; num2 += num11; } num -= num2 - num10; num2 = num10; int num12 = num10 - Peek2(src, num); num += 2; if (num12 < dst_0) { break; } if ((num8 = (byte)(num7 & 0xF)) == 15) { while (src[num] == byte.MaxValue) { num++; num8 += 255; } num8 += src[num++]; } if (num2 - num12 < 8) { int num13 = dECODER_TABLE_2[num2 - num12]; dst[num2] = dst[num12]; dst[num2 + 1] = dst[num12 + 1]; dst[num2 + 2] = dst[num12 + 2]; dst[num2 + 3] = dst[num12 + 3]; num2 += 4; num12 += 4; num12 -= dECODER_TABLE_[num2 - num12]; Copy4(dst, num12, num2); num2 += 4; num12 -= num13; } else { Copy8(dst, num12, num2); num2 += 8; num12 += 8; } num10 = num2 + num8 - 4; if (num10 > num6) { if (num10 > num4) { break; } if (num2 < num5) { int num11 = SecureCopy(dst, num12, num2, num5); num12 += num11; num2 += num11; } while (num2 < num10) { dst[num2++] = dst[num12++]; } num2 = num10; } else { if (num2 < num10) { SecureCopy(dst, num12, num2, num10); } num2 = num10; } } return -(num - src_0); } private static int LZ4_uncompress_unknownOutputSize_safe64(byte[] src, byte[] dst, int src_0, int dst_0, int src_len, int dst_maxlen) { int[] dECODER_TABLE_ = DECODER_TABLE_32; int[] dECODER_TABLE_2 = DECODER_TABLE_64; int num = src_0; int num2 = num + src_len; int num3 = dst_0; int num4 = num3 + dst_maxlen; int num5 = num2 - 8; int num6 = num2 - 6; int num7 = num4 - 8; int num8 = num4 - 12; int num9 = num4 - 5; int num10 = num4 - 12; if (num != num2) { while (true) { byte b = src[num++]; int num11; if ((num11 = b >> 4) == 15) { int num12 = 255; while (num < num2 && num12 == 255) { num11 += (num12 = src[num++]); } } int num13 = num3 + num11; if (num13 > num10 || num + num11 > num5) { if (num13 > num4 || num + num11 != num2) { break; } BlockCopy(src, num, dst, num3, num11); num3 += num11; return num3 - dst_0; } if (num3 < num13) { int num14 = WildCopy(src, num, dst, num3, num13); num += num14; num3 += num14; } num -= num3 - num13; num3 = num13; int num15 = num13 - Peek2(src, num); num += 2; if (num15 < dst_0) { break; } if ((num11 = b & 0xF) == 15) { while (num < num6) { int num16 = src[num++]; num11 += num16; if (num16 != 255) { break; } } } if (num3 - num15 < 8) { int num17 = dECODER_TABLE_2[num3 - num15]; dst[num3] = dst[num15]; dst[num3 + 1] = dst[num15 + 1]; dst[num3 + 2] = dst[num15 + 2]; dst[num3 + 3] = dst[num15 + 3]; num3 += 4; num15 += 4; num15 -= dECODER_TABLE_[num3 - num15]; Copy4(dst, num15, num3); num3 += 4; num15 -= num17; } else { Copy8(dst, num15, num3); num3 += 8; num15 += 8; } num13 = num3 + num11 - 4; if (num13 > num8) { if (num13 > num9) { break; } if (num3 < num7) { int num14 = SecureCopy(dst, num15, num3, num7); num15 += num14; num3 += num14; } while (num3 < num13) { dst[num3++] = dst[num15++]; } num3 = num13; } else { if (num3 < num13) { SecureCopy(dst, num15, num3, num13); } num3 = num13; } } } return -(num - src_0); } private static void LZ4HC_Insert_64(LZ4HC_Data_Structure ctx, int src_p) { ushort[] chainTable = ctx.chainTable; int[] hashTable = ctx.hashTable; byte[] src = ctx.src; int src_base = ctx.src_base; int i; for (i = ctx.nextToUpdate; i < src_p; i++) { int num = i; int num2 = num - (hashTable[(int)Peek4(src, num) * -1640531535 >>> 17] + src_base); if (num2 > 65535) { num2 = 65535; } chainTable[num & 0xFFFF] = (ushort)num2; hashTable[(int)Peek4(src, num) * -1640531535 >>> 17] = num - src_base; } ctx.nextToUpdate = i; } private static int LZ4HC_CommonLength_64(LZ4HC_Data_Structure ctx, int p1, int p2) { int[] dEBRUIJN_TABLE_ = DEBRUIJN_TABLE_64; byte[] src = ctx.src; int src_LASTLITERALS = ctx.src_LASTLITERALS; int num = p1; while (num < src_LASTLITERALS - 7) { long num2 = (long)Xor8(src, p2, num); if (num2 == 0L) { num += 8; p2 += 8; continue; } num += dEBRUIJN_TABLE_[(num2 & -num2) * 151050438428048703L >>> 58]; return num - p1; } if (num < src_LASTLITERALS - 3 && Equal4(src, p2, num)) { num += 4; p2 += 4; } if (num < src_LASTLITERALS - 1 && Equal2(src, p2, num)) { num += 2; p2 += 2; } if (num < src_LASTLITERALS && src[p2] == src[num]) { num++; } return num - p1; } private static int LZ4HC_InsertAndFindBestMatch_64(LZ4HC_Data_Structure ctx, int src_p, ref int matchpos) { ushort[] chainTable = ctx.chainTable; int[] hashTable = ctx.hashTable; byte[] src = ctx.src; int src_base = ctx.src_base; int num = 256; int num2 = 0; int num3 = 0; ushort num4 = 0; LZ4HC_Insert_64(ctx, src_p); int num5 = hashTable[(int)Peek4(src, src_p) * -1640531535 >>> 17] + src_base; if (num5 >= src_p - 4) { if (Equal4(src, num5, src_p)) { num4 = (ushort)(src_p - num5); num2 = (num3 = LZ4HC_CommonLength_64(ctx, src_p + 4, num5 + 4) + 4); matchpos = num5; } num5 -= chainTable[num5 & 0xFFFF]; } while (num5 >= src_p - 65535 && num != 0) { num--; if (src[num5 + num3] == src[src_p + num3] && Equal4(src, num5, src_p)) { int num6 = LZ4HC_CommonLength_64(ctx, src_p + 4, num5 + 4) + 4; if (num6 > num3) { num3 = num6; matchpos = num5; } } num5 -= chainTable[num5 & 0xFFFF]; } if (num2 != 0) { int i = src_p; int num7; for (num7 = src_p + num2 - 3; i < num7 - num4; i++) { chainTable[i & 0xFFFF] = num4; } do { chainTable[i & 0xFFFF] = num4; hashTable[(int)Peek4(src, i) * -1640531535 >>> 17] = i - src_base; i++; } while (i < num7); ctx.nextToUpdate = num7; } return num3; } private static int LZ4HC_InsertAndGetWiderMatch_64(LZ4HC_Data_Structure ctx, int src_p, int startLimit, int longest, ref int matchpos, ref int startpos) { int[] dEBRUIJN_TABLE_ = DEBRUIJN_TABLE_64; ushort[] chainTable = ctx.chainTable; int[] hashTable = ctx.hashTable; byte[] src = ctx.src; int src_base = ctx.src_base; int src_LASTLITERALS = ctx.src_LASTLITERALS; int num = 256; int num2 = src_p - startLimit; LZ4HC_Insert_64(ctx, src_p); int num3 = hashTable[(int)Peek4(src, src_p) * -1640531535 >>> 17] + src_base; while (num3 >= src_p - 65535 && num != 0) { num--; if (src[startLimit + longest] == src[num3 - num2 + longest] && Equal4(src, num3, src_p)) { int num4 = num3 + 4; int num5 = src_p + 4; int num6 = src_p; while (true) { if (num5 < src_LASTLITERALS - 7) { long num7 = (long)Xor8(src, num4, num5); if (num7 == 0L) { num5 += 8; num4 += 8; continue; } num5 += dEBRUIJN_TABLE_[(num7 & -num7) * 151050438428048703L >>> 58]; break; } if (num5 < src_LASTLITERALS - 3 && Equal4(src, num4, num5)) { num5 += 4; num4 += 4; } if (num5 < src_LASTLITERALS - 1 && Equal2(src, num4, num5)) { num5 += 2; num4 += 2; } if (num5 < src_LASTLITERALS && src[num4] == src[num5]) { num5++; } break; } num4 = num3; while (num6 > startLimit && num4 > src_base && src[num6 - 1] == src[num4 - 1]) { num6--; num4--; } if (num5 - num6 > longest) { longest = num5 - num6; matchpos = num4; startpos = num6; } } num3 -= chainTable[num3 & 0xFFFF]; } return longest; } private static int LZ4_encodeSequence_64(LZ4HC_Data_Structure ctx, ref int src_p, ref int dst_p, ref int src_anchor, int matchLength, int src_ref) { byte[] src = ctx.src; byte[] dst = ctx.dst; int dst_end = ctx.dst_end; int num = src_p - src_anchor; int num2 = dst_p++; if (dst_p + num + 8 + (num >> 8) > dst_end) { return 1; } int num3; if (num >= 15) { dst[num2] = 240; for (num3 = num - 15; num3 > 254; num3 -= 255) { dst[dst_p++] = byte.MaxValue; } dst[dst_p++] = (byte)num3; } else { dst[num2] = (byte)(num << 4); } if (num > 0) { int num4 = dst_p + num; src_anchor += WildCopy(src, src_anchor, dst, dst_p, num4); dst_p = num4; } Poke2(dst, dst_p, (ushort)(src_p - src_ref)); dst_p += 2; num3 = matchLength - 4; if (dst_p + 6 + (num3 >> 8) > dst_end) { return 1; } if (num3 >= 15) { dst[num2] += 15; for (num3 -= 15; num3 > 509; num3 -= 510) { dst[dst_p++] = byte.MaxValue; dst[dst_p++] = byte.MaxValue; } if (num3 > 254) { num3 -= 255; dst[dst_p++] = byte.MaxValue; } dst[dst_p++] = (byte)num3; } else { dst[num2] += (byte)num3; } src_p += matchLength; src_anchor = src_p; return 0; } private static int LZ4_compressHCCtx_64(LZ4HC_Data_Structure ctx) { byte[] src = ctx.src; int src_base = ctx.src_base; int src_end = ctx.src_end; int dst_base = ctx.dst_base; int src_anchor = src_base; int num = src_end - 12; byte[] dst = ctx.dst; int dst_len = ctx.dst_len; int dst_p = ctx.dst_base; int matchpos = 0; int startpos = 0; int matchpos2 = 0; int startpos2 = 0; int matchpos3 = 0; src_base++; while (src_base < num) { int num2 = LZ4HC_InsertAndFindBestMatch_64(ctx, src_base, ref matchpos); if (num2 == 0) { src_base++; continue; } int num3 = src_base; int num4 = matchpos; int num5 = num2; while (true) { int num6 = ((src_base + num2 < num) ? LZ4HC_InsertAndGetWiderMatch_64(ctx, src_base + num2 - 2, src_base + 1, num2, ref matchpos2, ref startpos) : num2); if (num6 == num2) { if (LZ4_encodeSequence_64(ctx, ref src_base, ref dst_p, ref src_anchor, num2, matchpos) == 0) { break; } return 0; } if (num3 < src_base && startpos < src_base + num5) { src_base = num3; matchpos = num4; num2 = num5; } if (startpos - src_base < 3) { num2 = num6; src_base = startpos; matchpos = matchpos2; continue; } int num9; while (true) { if (startpos - src_base < 18) { int num7 = num2; if (num7 > 18) { num7 = 18; } if (src_base + num7 > startpos + num6 - 4) { num7 = startpos - src_base + num6 - 4; } int num8 = num7 - (startpos - src_base); if (num8 > 0) { startpos += num8; matchpos2 += num8; num6 -= num8; } } num9 = ((startpos + num6 < num) ? LZ4HC_InsertAndGetWiderMatch_64(ctx, startpos + num6 - 3, startpos, num6, ref matchpos3, ref startpos2) : num6); if (num9 == num6) { break; } if (startpos2 < src_base + num2 + 3) { if (startpos2 < src_base + num2) { startpos = startpos2; matchpos2 = matchpos3; num6 = num9; continue; } goto IL_01b0; } if (startpos < src_base + num2) { if (startpos - src_base < 15) { if (num2 > 18) { num2 = 18; } if (src_base + num2 > startpos + num6 - 4) { num2 = startpos - src_base + num6 - 4; } int num10 = num2 - (startpos - src_base); if (num10 > 0) { startpos += num10; matchpos2 += num10; num6 -= num10; } } else { num2 = startpos - src_base; } } if (LZ4_encodeSequence_64(ctx, ref src_base, ref dst_p, ref src_anchor, num2, matchpos) != 0) { return 0; } src_base = startpos; matchpos = matchpos2; num2 = num6; startpos = startpos2; matchpos2 = matchpos3; num6 = num9; } if (startpos < src_base + num2) { num2 = startpos - src_base; } if (LZ4_encodeSequence_64(ctx, ref src_base, ref dst_p, ref src_anchor, num2, matchpos) != 0) { return 0; } src_base = startpos; if (LZ4_encodeSequence_64(ctx, ref src_base, ref dst_p, ref src_anchor, num6, matchpos2) == 0) { break; } return 0; IL_01b0: if (startpos < src_base + num2) { int num11 = src_base + num2 - startpos; startpos += num11; matchpos2 += num11; num6 -= num11; if (num6 < 4) { startpos = startpos2; matchpos2 = matchpos3; num6 = num9; } } if (LZ4_encodeSequence_64(ctx, ref src_base, ref dst_p, ref src_anchor, num2, matchpos) != 0) { return 0; } src_base = startpos2; matchpos = matchpos3; num2 = num9; num3 = startpos; num4 = matchpos2; num5 = num6; } } int num12 = src_end - src_anchor; if (dst_p - dst_base + num12 + 1 + (num12 + 255 - 15) / 255 > (uint)dst_len) { return 0; } if (num12 >= 15) { dst[dst_p++] = 240; for (num12 -= 15; num12 > 254; num12 -= 255) { dst[dst_p++] = byte.MaxValue; } dst[dst_p++] = (byte)num12; } else { dst[dst_p++] = (byte)(num12 << 4); } BlockCopy(src, src_anchor, dst, dst_p, src_end - src_anchor); dst_p += src_end - src_anchor; return dst_p - dst_base; } } } namespace AssetsTools.NET { public class AssetBundleBlockAndDirInfo { public Hash128 Hash { get; set; } public AssetBundleBlockInfo[] BlockInfos { get; set; } public List DirectoryInfos { get; set; } public void Read(AssetsFileReader reader) { Hash = new Hash128(reader.ReadBytes(16)); int num = reader.ReadInt32(); BlockInfos = new AssetBundleBlockInfo[num]; for (int i = 0; i < num; i++) { BlockInfos[i] = new AssetBundleBlockInfo(); BlockInfos[i].DecompressedSize = reader.ReadUInt32(); BlockInfos[i].CompressedSize = reader.ReadUInt32(); BlockInfos[i].Flags = reader.ReadUInt16(); } int num2 = reader.ReadInt32(); DirectoryInfos = new List(num2); for (int j = 0; j < num2; j++) { AssetBundleDirectoryInfo item = new AssetBundleDirectoryInfo { Offset = reader.ReadInt64(), DecompressedSize = reader.ReadInt64(), Flags = reader.ReadUInt32(), Name = reader.ReadNullTerminated() }; DirectoryInfos.Add(item); } } public void Write(AssetsFileWriter writer) { if (Hash.data == null) { writer.Write(0uL); writer.Write(0uL); } else { writer.Write(Hash.data); } int num = BlockInfos.Length; writer.Write(num); for (int i = 0; i < num; i++) { writer.Write(BlockInfos[i].DecompressedSize); writer.Write(BlockInfos[i].CompressedSize); writer.Write(BlockInfos[i].Flags); } int count = DirectoryInfos.Count; writer.Write(count); for (int j = 0; j < count; j++) { writer.Write(DirectoryInfos[j].Offset); writer.Write(DirectoryInfos[j].DecompressedSize); writer.Write(DirectoryInfos[j].Flags); writer.WriteNullTerminated(DirectoryInfos[j].Name); } } } public class AssetBundleBlockInfo { public uint DecompressedSize { get; set; } public uint CompressedSize { get; set; } public ushort Flags { get; set; } public byte GetCompressionType() { return (byte)(Flags & 0x3Fu); } } public class AssetBundleDirectoryInfo { public long Offset; public long DecompressedSize; public uint Flags; public string Name; public IContentReplacer Replacer { get; set; } public ContentReplacerType ReplacerType { get { if (Replacer == null) { return ContentReplacerType.None; } return Replacer.GetReplacerType(); } } public bool IsReplacerPreviewable { get { if (Replacer != null) { return Replacer.HasPreview(); } return false; } } public bool IsSerialized => (Flags & 4) != 0; public void SetNewData(byte[] newBytes) { Replacer = new ContentReplacerFromBuffer(newBytes); } public void SetNewData(AssetsFile assetsFile) { Replacer = new ContentReplacerFromAssets(assetsFile); } public void SetRemoved() { Replacer = new ContentRemover(); } public static AssetBundleDirectoryInfo Create(string name, bool isSerialized) { return new AssetBundleDirectoryInfo { Offset = -1L, DecompressedSize = 0L, Flags = (isSerialized ? 4u : 0u), Name = name }; } } public class AssetBundleFile { public AssetsFileReader Reader; public AssetBundleHeader Header { get; set; } public AssetBundleBlockAndDirInfo BlockAndDirInfo { get; set; } public AssetsFileReader DataReader { get; set; } public bool DataIsCompressed { get; set; } public void Close() { Reader.Close(); DataReader.Close(); } public void Read(AssetsFileReader reader) { Reader = reader; Reader.Position = 0L; Reader.BigEndian = true; reader.ReadNullTerminated(); uint num = reader.ReadUInt32(); if (num >= 6 || num <= 8) { Reader.Position = 0L; Header = new AssetBundleHeader(); Header.Read(reader); if (Header.Version >= 7) { reader.Align16(); } if (Header.Signature == "UnityFS") { UnpackInfoOnly(); } else { new NotImplementedException("Non UnityFS bundles are not supported yet."); } } else { new NotImplementedException($"Version {num} bundles are not supported yet."); } } public void Write(AssetsFileWriter writer, long filePos = 0L) { if (Header == null) { throw new Exception("Header must be loaded! (Did you forget to call bundle.Read?)"); } if (Header.Signature != "UnityFS") { throw new NotImplementedException("Non UnityFS bundles are not supported yet."); } if (DataIsCompressed) { throw new Exception("Bundles must be decompressed before writing."); } long position = filePos; if (filePos == -1) { position = writer.Position; } else { writer.Position = filePos; } List directoryInfos = BlockAndDirInfo.DirectoryInfos; Header.Write(writer); if (Header.Version >= 7) { writer.Align16(); } long num = 0L; int num2 = 1; foreach (AssetBundleDirectoryInfo item in directoryInfos) { num = ((item.Replacer == null) ? (num + item.DecompressedSize) : (num + item.Replacer.GetSize())); while (num >= uint.MaxValue) { num -= uint.MaxValue; num2++; } } AssetBundleBlockAndDirInfo assetBundleBlockAndDirInfo = new AssetBundleBlockAndDirInfo { Hash = default(Hash128), BlockInfos = new AssetBundleBlockInfo[num2] }; for (int i = 0; i < num2; i++) { assetBundleBlockAndDirInfo.BlockInfos[i] = new AssetBundleBlockInfo { CompressedSize = 0u, DecompressedSize = 0u, Flags = 64 }; } List list = new List(); int count = directoryInfos.Count; for (int j = 0; j < count; j++) { AssetBundleDirectoryInfo assetBundleDirectoryInfo = directoryInfos[j]; if (assetBundleDirectoryInfo.ReplacerType != ContentReplacerType.Remove) { list.Add(new AssetBundleDirectoryInfo { Offset = assetBundleDirectoryInfo.Offset, DecompressedSize = assetBundleDirectoryInfo.DecompressedSize, Flags = assetBundleDirectoryInfo.Flags, Name = assetBundleDirectoryInfo.Name, Replacer = assetBundleDirectoryInfo.Replacer }); } } long position2 = writer.Position; assetBundleBlockAndDirInfo.DirectoryInfos = list; assetBundleBlockAndDirInfo.Write(writer); if ((Header.FileStreamHeader.Flags & AssetBundleFSHeaderFlags.BlockInfoNeedPaddingAtStart) != 0) { writer.Align16(); } long position3 = writer.Position; for (int k = 0; k < list.Count; k++) { AssetBundleDirectoryInfo assetBundleDirectoryInfo2 = list[k]; long position4 = writer.Position; long offset = position4 - position3; if (assetBundleDirectoryInfo2.ReplacerType == ContentReplacerType.AddOrModify) { assetBundleDirectoryInfo2.Replacer.Write(writer, finalWrite: true); } else { DataReader.Position = assetBundleDirectoryInfo2.Offset; DataReader.BaseStream.CopyToCompat(writer.BaseStream, assetBundleDirectoryInfo2.DecompressedSize); } assetBundleDirectoryInfo2.Offset = offset; assetBundleDirectoryInfo2.DecompressedSize = writer.Position - position4; } long position5 = writer.Position; long num3 = position5 - position3; for (int l = 0; l < assetBundleBlockAndDirInfo.BlockInfos.Length; l++) { AssetBundleBlockInfo obj = assetBundleBlockAndDirInfo.BlockInfos[l]; uint num6 = (obj.CompressedSize = (obj.DecompressedSize = (uint)Math.Min(num3, 4294967295L))); num3 -= num6; } assetBundleBlockAndDirInfo.DirectoryInfos = list; writer.Position = position2; assetBundleBlockAndDirInfo.Write(writer); uint num7 = (uint)(position3 - position2); writer.Position = position; AssetBundleHeader assetBundleHeader = new AssetBundleHeader(); assetBundleHeader.Signature = Header.Signature; assetBundleHeader.Version = Header.Version; assetBundleHeader.GenerationVersion = Header.GenerationVersion; assetBundleHeader.EngineVersion = Header.EngineVersion; assetBundleHeader.FileStreamHeader = new AssetBundleFSHeader { TotalFileSize = position5, CompressedSize = num7, DecompressedSize = num7, Flags = (Header.FileStreamHeader.Flags & (AssetBundleFSHeaderFlags)(-129) & (AssetBundleFSHeaderFlags)(-64)) }; assetBundleHeader.Write(writer); } public void Unpack(AssetsFileWriter writer) { if (Header == null) { new Exception("Header must be loaded! (Did you forget to call bundle.Read?)"); } if (Header.Signature != "UnityFS") { new NotImplementedException("Non UnityFS bundles are not supported yet."); } AssetBundleFSHeader fileStreamHeader = Header.FileStreamHeader; AssetsFileReader dataReader = DataReader; AssetBundleBlockInfo[] blockInfos = BlockAndDirInfo.BlockInfos; List directoryInfos = BlockAndDirInfo.DirectoryInfos; AssetBundleHeader assetBundleHeader = new AssetBundleHeader { Signature = Header.Signature, Version = Header.Version, GenerationVersion = Header.GenerationVersion, EngineVersion = Header.EngineVersion, FileStreamHeader = new AssetBundleFSHeader { TotalFileSize = 0L, CompressedSize = fileStreamHeader.DecompressedSize, DecompressedSize = fileStreamHeader.DecompressedSize, Flags = (AssetBundleFSHeaderFlags.HasDirectoryInfo | (((fileStreamHeader.Flags & AssetBundleFSHeaderFlags.BlockInfoNeedPaddingAtStart) != 0) ? AssetBundleFSHeaderFlags.BlockInfoNeedPaddingAtStart : AssetBundleFSHeaderFlags.None)) } }; long num = assetBundleHeader.GetFileDataOffset(); for (int i = 0; i < blockInfos.Length; i++) { num += blockInfos[i].DecompressedSize; } assetBundleHeader.FileStreamHeader.TotalFileSize = num; AssetBundleBlockAndDirInfo assetBundleBlockAndDirInfo = new AssetBundleBlockAndDirInfo { Hash = default(Hash128), BlockInfos = new AssetBundleBlockInfo[blockInfos.Length], DirectoryInfos = new List(directoryInfos.Count) }; for (int j = 0; j < blockInfos.Length; j++) { assetBundleBlockAndDirInfo.BlockInfos[j] = new AssetBundleBlockInfo { CompressedSize = blockInfos[j].DecompressedSize, DecompressedSize = blockInfos[j].DecompressedSize, Flags = (ushort)(blockInfos[j].Flags & 0xFFFFFFC0u) }; } for (int k = 0; k < directoryInfos.Count; k++) { assetBundleBlockAndDirInfo.DirectoryInfos.Add(new AssetBundleDirectoryInfo { Offset = directoryInfos[k].Offset, DecompressedSize = directoryInfos[k].DecompressedSize, Flags = directoryInfos[k].Flags, Name = directoryInfos[k].Name }); } assetBundleHeader.Write(writer); if (assetBundleHeader.Version >= 7) { writer.Align16(); } assetBundleBlockAndDirInfo.Write(writer); if ((assetBundleHeader.FileStreamHeader.Flags & AssetBundleFSHeaderFlags.BlockInfoNeedPaddingAtStart) != 0) { writer.Align16(); } dataReader.Position = 0L; if (DataIsCompressed) { for (int l = 0; l < assetBundleBlockAndDirInfo.BlockInfos.Length; l++) { AssetBundleBlockInfo assetBundleBlockInfo = blockInfos[l]; switch (assetBundleBlockInfo.GetCompressionType()) { case 0: dataReader.BaseStream.CopyToCompat(writer.BaseStream, assetBundleBlockInfo.CompressedSize); break; case 1: SevenZipHelper.StreamDecompress(dataReader.BaseStream, writer.BaseStream, assetBundleBlockInfo.CompressedSize, assetBundleBlockInfo.DecompressedSize); break; case 2: case 3: { using (MemoryStream memoryStream = new MemoryStream()) { dataReader.BaseStream.CopyToCompat(memoryStream, assetBundleBlockInfo.CompressedSize); memoryStream.Position = 0L; using Lz4DecoderStream input = new Lz4DecoderStream(memoryStream); input.CopyToCompat(writer.BaseStream, assetBundleBlockInfo.DecompressedSize); } break; } } } } else { for (int m = 0; m < assetBundleBlockAndDirInfo.BlockInfos.Length; m++) { AssetBundleBlockInfo assetBundleBlockInfo2 = blockInfos[m]; dataReader.BaseStream.CopyToCompat(writer.BaseStream, assetBundleBlockInfo2.DecompressedSize); } } } public void Pack(AssetsFileWriter writer, AssetBundleCompressionType compType, bool blockDirAtEnd = true, IAssetBundleCompressProgress progress = null) { if (Header == null) { throw new Exception("Header must be loaded! (Did you forget to call bundle.Read?)"); } if (Header.Signature != "UnityFS") { throw new NotImplementedException("Non UnityFS bundles are not supported yet."); } if (DataIsCompressed) { throw new Exception("Bundles must be decompressed before writing."); } Reader.Position = 0L; writer.Position = 0L; AssetBundleFSHeader assetBundleFSHeader = new AssetBundleFSHeader { TotalFileSize = 0L, CompressedSize = 0u, DecompressedSize = 0u, Flags = ((AssetBundleFSHeaderFlags)67 | (blockDirAtEnd ? AssetBundleFSHeaderFlags.BlockAndDirAtEnd : AssetBundleFSHeaderFlags.None)) }; AssetBundleHeader assetBundleHeader = new AssetBundleHeader { Signature = Header.Signature, Version = Header.Version, GenerationVersion = Header.GenerationVersion, EngineVersion = Header.EngineVersion, FileStreamHeader = assetBundleFSHeader }; AssetBundleBlockAndDirInfo assetBundleBlockAndDirInfo = new AssetBundleBlockAndDirInfo { Hash = default(Hash128), BlockInfos = null, DirectoryInfos = BlockAndDirInfo.DirectoryInfos }; long position = writer.Position; assetBundleHeader.Write(writer); if (assetBundleHeader.Version >= 7) { writer.Align16(); } int num = (int)(writer.Position - position); long num2 = 0L; List list = new List(); List list2 = new List(); Stream baseStream = DataReader.BaseStream; baseStream.Position = 0L; int num3 = (int)baseStream.Length; switch (compType) { case AssetBundleCompressionType.LZMA: { Stream stream2 = ((!blockDirAtEnd) ? GetTempFileStream() : writer.BaseStream); AssetBundleLZMAProgress progress2 = new AssetBundleLZMAProgress(progress, baseStream.Length); long position2 = stream2.Position; SevenZipHelper.Compress(baseStream, stream2, progress2); uint compressedSize = (uint)(stream2.Position - position2); AssetBundleBlockInfo assetBundleBlockInfo4 = new AssetBundleBlockInfo { CompressedSize = compressedSize, DecompressedSize = (uint)num3, Flags = 65 }; num2 += assetBundleBlockInfo4.CompressedSize; list.Add(assetBundleBlockInfo4); if (!blockDirAtEnd) { list2.Add(stream2); } progress?.SetProgress(1f); break; } case AssetBundleCompressionType.LZ4: case AssetBundleCompressionType.LZ4Fast: { BinaryReader binaryReader = new BinaryReader(baseStream); Stream stream = ((!blockDirAtEnd) ? GetTempFileStream() : writer.BaseStream); byte[] array = binaryReader.ReadBytes(131072); while (array.Length != 0) { byte[] array2 = ((compType == AssetBundleCompressionType.LZ4Fast) ? LZ4Codec.Encode32(array, 0, array.Length) : LZ4Codec.Encode32HC(array, 0, array.Length)); progress?.SetProgress((float)binaryReader.BaseStream.Position / (float)binaryReader.BaseStream.Length); if (array2.Length > array.Length) { stream.Write(array, 0, array.Length); AssetBundleBlockInfo assetBundleBlockInfo2 = new AssetBundleBlockInfo { CompressedSize = (uint)array.Length, DecompressedSize = (uint)array.Length, Flags = 0 }; num2 += assetBundleBlockInfo2.CompressedSize; list.Add(assetBundleBlockInfo2); } else { stream.Write(array2, 0, array2.Length); AssetBundleBlockInfo assetBundleBlockInfo3 = new AssetBundleBlockInfo { CompressedSize = (uint)array2.Length, DecompressedSize = (uint)array.Length, Flags = 3 }; num2 += assetBundleBlockInfo3.CompressedSize; list.Add(assetBundleBlockInfo3); } array = binaryReader.ReadBytes(131072); } if (!blockDirAtEnd) { list2.Add(stream); } progress?.SetProgress(1f); break; } case AssetBundleCompressionType.None: { AssetBundleBlockInfo assetBundleBlockInfo = new AssetBundleBlockInfo { CompressedSize = (uint)num3, DecompressedSize = (uint)num3, Flags = 0 }; num2 += assetBundleBlockInfo.CompressedSize; list.Add(assetBundleBlockInfo); if (blockDirAtEnd) { baseStream.CopyToCompat(writer.BaseStream, -1L); } else { list2.Add(baseStream); } break; } } assetBundleBlockAndDirInfo.BlockInfos = list.ToArray(); byte[] array3; using (MemoryStream memoryStream = new MemoryStream()) { AssetsFileWriter assetsFileWriter = new AssetsFileWriter(memoryStream); assetsFileWriter.BigEndian = writer.BigEndian; assetBundleBlockAndDirInfo.Write(assetsFileWriter); array3 = memoryStream.ToArray(); } byte[] array4 = ((compType == AssetBundleCompressionType.LZ4Fast) ? LZ4Codec.Encode32(array3, 0, array3.Length) : LZ4Codec.Encode32HC(array3, 0, array3.Length)); long totalFileSize = num + array4.Length + num2; assetBundleFSHeader.TotalFileSize = totalFileSize; assetBundleFSHeader.DecompressedSize = (uint)array3.Length; assetBundleFSHeader.CompressedSize = (uint)array4.Length; if (!blockDirAtEnd) { writer.Write(array4); foreach (Stream item in list2) { item.Position = 0L; item.CopyToCompat(writer.BaseStream, -1L); item.Close(); } } else { writer.Write(array4); } writer.Position = 0L; assetBundleHeader.Write(writer); if (assetBundleHeader.Version >= 7) { writer.Align16(); } } private void UnpackInfoOnly() { if (Header == null) { throw new Exception("Header must be loaded! (Did you forget to call bundle.Read?)"); } Reader.Position = Header.GetBundleInfoOffset(); if (Header.GetCompressionType() == 0) { BlockAndDirInfo = new AssetBundleBlockAndDirInfo(); BlockAndDirInfo.Read(Reader); } else { int compressedSize = (int)Header.FileStreamHeader.CompressedSize; int decompressedSize = (int)Header.FileStreamHeader.DecompressedSize; MemoryStream memoryStream; switch (Header.GetCompressionType()) { case 1: { using (MemoryStream compressedStream = new MemoryStream(Reader.ReadBytes(compressedSize))) { memoryStream = new MemoryStream(); SevenZipHelper.StreamDecompress(compressedStream, memoryStream, compressedSize, decompressedSize); } break; } case 2: case 3: { byte[] buffer = new byte[Header.FileStreamHeader.DecompressedSize]; using (MemoryStream input = new MemoryStream(Reader.ReadBytes(compressedSize))) { Lz4DecoderStream lz4DecoderStream = new Lz4DecoderStream(input); lz4DecoderStream.Read(buffer, 0, (int)Header.FileStreamHeader.DecompressedSize); lz4DecoderStream.Dispose(); } memoryStream = new MemoryStream(buffer); break; } default: memoryStream = null; break; } AssetsFileReader assetsFileReader; using (assetsFileReader = new AssetsFileReader(memoryStream)) { assetsFileReader.Position = 0L; assetsFileReader.BigEndian = Reader.BigEndian; BlockAndDirInfo = new AssetBundleBlockAndDirInfo(); BlockAndDirInfo.Read(assetsFileReader); } } switch (GetCompressionType()) { case AssetBundleCompressionType.None: { SegmentStream stream3 = new SegmentStream(Reader.BaseStream, Header.GetFileDataOffset()); DataReader = new AssetsFileReader(stream3); DataIsCompressed = false; break; } case AssetBundleCompressionType.LZMA: { SegmentStream stream2 = new SegmentStream(Reader.BaseStream, Header.GetFileDataOffset()); DataReader = new AssetsFileReader(stream2); DataIsCompressed = true; break; } case AssetBundleCompressionType.LZ4: { LZ4BlockStream stream = new LZ4BlockStream(Reader.BaseStream, Header.GetFileDataOffset(), BlockAndDirInfo.BlockInfos); DataReader = new AssetsFileReader(stream); DataIsCompressed = false; break; } } } public AssetBundleCompressionType GetCompressionType() { AssetBundleBlockInfo[] blockInfos = BlockAndDirInfo.BlockInfos; for (int i = 0; i < blockInfos.Length; i++) { switch (blockInfos[i].GetCompressionType()) { case 2: case 3: return AssetBundleCompressionType.LZ4; case 1: return AssetBundleCompressionType.LZMA; } } return AssetBundleCompressionType.None; } public bool IsAssetsFile(int index) { GetFileRange(index, out var offset, out var length); return AssetsFile.IsAssetsFile(DataReader, offset, length); } public int GetFileIndex(string name) { if (Header == null) { throw new Exception("Header must be loaded! (Did you forget to call bundle.Read?)"); } for (int i = 0; i < BlockAndDirInfo.DirectoryInfos.Count; i++) { if (BlockAndDirInfo.DirectoryInfos[i].Name == name) { return i; } } return -1; } public string GetFileName(int index) { if (Header == null) { throw new Exception("Header must be loaded! (Did you forget to call bundle.Read?)"); } if (index < 0 || index >= BlockAndDirInfo.DirectoryInfos.Count) { return null; } return BlockAndDirInfo.DirectoryInfos[index].Name; } public void GetFileRange(int index, out long offset, out long length) { if (Header == null) { throw new Exception("Header must be loaded! (Did you forget to call bundle.Read?)"); } if (index < 0 || index >= BlockAndDirInfo.DirectoryInfos.Count) { offset = -1L; length = 0L; } else { AssetBundleDirectoryInfo assetBundleDirectoryInfo = BlockAndDirInfo.DirectoryInfos[index]; offset = assetBundleDirectoryInfo.Offset; length = assetBundleDirectoryInfo.DecompressedSize; } } public List GetAllFileNames() { if (Header == null) { throw new Exception("Header must be loaded! (Did you forget to call bundle.Read?)"); } List list = new List(); foreach (AssetBundleDirectoryInfo directoryInfo in BlockAndDirInfo.DirectoryInfos) { list.Add(directoryInfo.Name); } return list; } private FileStream GetTempFileStream() { return new FileStream(Path.GetTempFileName(), FileMode.OpenOrCreate, FileAccess.ReadWrite, FileShare.Read, 4096, FileOptions.DeleteOnClose); } } public enum AssetBundleCompressionType { None, LZMA, LZ4, LZ4Fast } public class AssetBundleFSHeader { public long TotalFileSize { get; set; } public uint CompressedSize { get; set; } public uint DecompressedSize { get; set; } public AssetBundleFSHeaderFlags Flags { get; set; } public void Read(AssetsFileReader reader) { TotalFileSize = reader.ReadInt64(); CompressedSize = reader.ReadUInt32(); DecompressedSize = reader.ReadUInt32(); Flags = (AssetBundleFSHeaderFlags)reader.ReadUInt32(); } public void Write(AssetsFileWriter writer) { writer.Write(TotalFileSize); writer.Write(CompressedSize); writer.Write(DecompressedSize); writer.Write((uint)Flags); } } public enum AssetBundleFSHeaderFlags { None = 0, LZMACompressed = 1, LZ4Compressed = 2, LZ4HCCompressed = 3, CompressionMask = 63, HasDirectoryInfo = 64, BlockAndDirAtEnd = 128, OldWebPluginCompatibility = 256, BlockInfoNeedPaddingAtStart = 512 } public class AssetBundleHeader { public string Signature { get; set; } public uint Version { get; set; } public string GenerationVersion { get; set; } public string EngineVersion { get; set; } public AssetBundleFSHeader FileStreamHeader { get; set; } public void Read(AssetsFileReader reader) { reader.BigEndian = true; Signature = reader.ReadNullTerminated(); Version = reader.ReadUInt32(); GenerationVersion = reader.ReadNullTerminated(); EngineVersion = reader.ReadNullTerminated(); if (Signature == "UnityFS") { FileStreamHeader = new AssetBundleFSHeader(); FileStreamHeader.Read(reader); return; } throw new NotSupportedException(Signature + " signature not supported!"); } public void Write(AssetsFileWriter writer) { writer.BigEndian = true; writer.WriteNullTerminated(Signature); writer.Write(Version); writer.WriteNullTerminated(GenerationVersion); writer.WriteNullTerminated(EngineVersion); if (Signature == "UnityFS") { FileStreamHeader.Write(writer); return; } throw new NotSupportedException(Signature + " signature not supported!"); } public long GetBundleInfoOffset() { if (Signature != "UnityFS") { throw new NotSupportedException(Signature + " signature not supported!"); } AssetBundleFSHeaderFlags flags = FileStreamHeader.Flags; long totalFileSize = FileStreamHeader.TotalFileSize; long num = FileStreamHeader.CompressedSize; if ((flags & AssetBundleFSHeaderFlags.BlockAndDirAtEnd) != 0) { if (totalFileSize == 0L) { return -1L; } return totalFileSize - num; } long num2 = GenerationVersion.Length + EngineVersion.Length + 26; if (Version >= 7) { if ((flags & AssetBundleFSHeaderFlags.OldWebPluginCompatibility) != 0) { return (num2 + 10 + 15) & -16; } return (num2 + Signature.Length + 1 + 15) & -16; } if ((flags & AssetBundleFSHeaderFlags.OldWebPluginCompatibility) != 0) { return num2 + 10; } return num2 + Signature.Length + 1; } public long GetFileDataOffset() { if (Signature != "UnityFS") { throw new NotSupportedException(Signature + " signature not supported!"); } AssetBundleFSHeaderFlags flags = FileStreamHeader.Flags; long num = FileStreamHeader.CompressedSize; long num2 = GenerationVersion.Length + EngineVersion.Length + 26; num2 = (((flags & AssetBundleFSHeaderFlags.OldWebPluginCompatibility) == 0) ? (num2 + (Signature.Length + 1)) : (num2 + 10)); if (Version >= 7) { num2 = (num2 + 15) & -16; } if ((flags & AssetBundleFSHeaderFlags.BlockAndDirAtEnd) == 0) { num2 += num; } if ((flags & AssetBundleFSHeaderFlags.BlockInfoNeedPaddingAtStart) != 0) { num2 = (num2 + 15) & -16; } return num2; } public byte GetCompressionType() { if (Signature != "UnityFS") { throw new NotSupportedException(Signature + " signature not supported!"); } return (byte)(FileStreamHeader.Flags & AssetBundleFSHeaderFlags.CompressionMask); } } internal class AssetBundleLZMAProgress : ICodeProgress { private IAssetBundleCompressProgress progress; private long currentSize; private long length; public AssetBundleLZMAProgress(IAssetBundleCompressProgress progress, long length) { this.progress = progress; currentSize = 0L; this.length = length; } public void SetProgress(long inSize, long outSize) { if (progress != null) { progress.SetProgress((float)inSize / (float)length); } } } public interface IAssetBundleCompressProgress { void SetProgress(float progress); } public class AssetFileInfo { public long PathId { get; set; } public long ByteOffset { get; set; } public uint ByteSize { get; set; } public int TypeIdOrIndex { get; set; } public ushort OldTypeId { get; set; } public ushort ScriptTypeIndex { get; set; } public byte Stripped { get; set; } public int TypeId { get; set; } public IContentReplacer Replacer { get; set; } public ContentReplacerType ReplacerType { get { if (Replacer == null) { return ContentReplacerType.None; } return Replacer.GetReplacerType(); } } public bool IsReplacerPreviewable { get { if (Replacer != null) { return Replacer.HasPreview(); } return false; } } public void Read(AssetsFileReader reader, uint version) { reader.Align(); if (version >= 14) { PathId = reader.ReadInt64(); } else { PathId = reader.ReadUInt32(); } if (version >= 22) { ByteOffset = reader.ReadInt64(); } else { ByteOffset = reader.ReadUInt32(); } ByteSize = reader.ReadUInt32(); TypeIdOrIndex = reader.ReadInt32(); if (version <= 15) { OldTypeId = reader.ReadUInt16(); } if (version <= 16) { ScriptTypeIndex = reader.ReadUInt16(); } if (15 <= version && version <= 16) { Stripped = reader.ReadByte(); } Replacer = null; } public void Write(AssetsFileWriter writer, uint version) { writer.Align(); if (version >= 14) { writer.Write(PathId); } else { writer.Write((uint)PathId); } if (version >= 22) { writer.Write(ByteOffset); } else { writer.Write((uint)ByteOffset); } writer.Write(ByteSize); writer.Write(TypeIdOrIndex); if (version <= 15) { writer.Write(OldTypeId); } if (version <= 16) { writer.Write(ScriptTypeIndex); } if (15 <= version && version <= 16) { writer.Write(Stripped); } } public int GetTypeId(AssetsFile assetsFile) { return GetTypeId(assetsFile.Metadata.TypeTreeTypes, assetsFile.Header.Version); } public int GetTypeId(AssetsFileMetadata metadata, uint version) { return GetTypeId(metadata.TypeTreeTypes, version); } public int GetTypeId(List typeTreeTypes, uint version) { if (version < 16) { return TypeIdOrIndex; } if (TypeIdOrIndex >= typeTreeTypes.Count) { throw new IndexOutOfRangeException("TypeIndex is larger than type tree count!"); } return typeTreeTypes[TypeIdOrIndex].TypeId; } public ushort GetScriptIndex(AssetsFile assetsFile) { return GetScriptIndex(assetsFile.Metadata.TypeTreeTypes, assetsFile.Header.Version); } public ushort GetScriptIndex(AssetsFileMetadata metadata, uint version) { return GetScriptIndex(metadata.TypeTreeTypes, version); } public ushort GetScriptIndex(List typeTreeTypes, uint version) { if (version < 16) { return ScriptTypeIndex; } if (TypeIdOrIndex >= typeTreeTypes.Count) { throw new IndexOutOfRangeException("TypeIndex is larger than type tree count!"); } return typeTreeTypes[TypeIdOrIndex].ScriptTypeIndex; } public long GetAbsoluteByteOffset(AssetsFile assetsFile) { return assetsFile.Header.DataOffset + ByteOffset; } public long GetAbsoluteByteOffset(AssetsFileHeader header) { return header.DataOffset + ByteOffset; } public long GetAbsoluteByteOffset(long dataOffset) { return dataOffset + ByteOffset; } public void SetNewData(byte[] newBytes) { Replacer = new ContentReplacerFromBuffer(newBytes); } public void SetNewData(AssetTypeValueField baseField) { Replacer = new ContentReplacerFromBuffer(baseField.WriteToByteArray()); } public void SetRemoved() { Replacer = new ContentRemover(); } public static AssetFileInfo Create(AssetsFile assetsFile, long pathId, int typeId, ClassDatabaseFile classDatabase = null, bool preferEditor = false) { return Create(assetsFile, pathId, typeId, ushort.MaxValue, classDatabase, preferEditor); } public static AssetFileInfo Create(AssetsFile assetsFile, long pathId, int typeId, ushort scriptIndex, ClassDatabaseFile classDatabase = null, bool preferEditor = false) { uint version = assetsFile.Header.Version; int typeIdOrIndex; ushort oldTypeId; if (version >= 16) { int num = assetsFile.Metadata.FindTypeTreeTypeIndexByID(typeId, scriptIndex); if (num == -1) { if (classDatabase == null) { return null; } TypeTreeType typeTreeType; if (assetsFile.Metadata.TypeTreeEnabled) { typeTreeType = ClassDatabaseToTypeTree.Convert(classDatabase, typeId, preferEditor); if (typeTreeType == null) { return null; } } else { typeTreeType = new TypeTreeType { TypeId = typeId, IsStrippedType = false, ScriptTypeIndex = scriptIndex, ScriptIdHash = Hash128.NewBlankHash(), TypeHash = Hash128.NewBlankHash(), Nodes = new List(), StringBufferBytes = new byte[0], TypeDependencies = new int[0], IsRefType = false, TypeReference = null }; } typeIdOrIndex = assetsFile.Metadata.TypeTreeTypes.Count; assetsFile.Metadata.TypeTreeTypes.Add(typeTreeType); } else { typeIdOrIndex = num; } oldTypeId = 0; } else { typeIdOrIndex = typeId; oldTypeId = (ushort)((typeId >= 0) ? ((ushort)typeId) : 114); } ushort scriptTypeIndex = (ushort)((version < 17) ? scriptIndex : 0); return new AssetFileInfo { PathId = pathId, ByteOffset = -1L, ByteSize = 0u, TypeIdOrIndex = typeIdOrIndex, OldTypeId = oldTypeId, ScriptTypeIndex = scriptTypeIndex, Stripped = 0, TypeId = typeId, Replacer = null }; } public static long GetSize(uint version) { long num = 0L; num = ((version < 14) ? (num + 4) : (num + 8)); num = ((version < 22) ? (num + 4) : (num + 8)); num += 4; num += 4; if (version <= 15) { num += 2; } if (version <= 16) { num += 2; } if (15 <= version && version <= 16) { num++; } return (num + 3) & -4; } } public class AssetPPtr { public string FilePath { get; set; } public int FileId { get; set; } public long PathId { get; set; } public AssetPPtr() { FilePath = string.Empty; FileId = 0; PathId = 0L; } public AssetPPtr(int fileId, long pathId) { FilePath = string.Empty; FileId = fileId; PathId = pathId; } public AssetPPtr(string fileName, long pathId) { FilePath = fileName; FileId = 0; PathId = pathId; } public AssetPPtr(string fileName, int fileId, long pathId) { FilePath = fileName; FileId = fileId; PathId = pathId; } public bool HasFilePath() { if (FilePath != string.Empty) { return FilePath != null; } return false; } public bool IsNull() { if (HasFilePath()) { return PathId == 0; } if (FileId == 0) { return PathId == 0; } return false; } public void SetFilePathFromFile(AssetsFile file) { int num = FileId - 1; if (FileId > 0 && file.Metadata.Externals.Count < num) { FilePath = file.Metadata.Externals[num].PathName; } } public void SetFilePathFromFile(AssetsManager am, AssetsFileInstance fileInst) { if (FileId == 0) { FilePath = fileInst.path; return; } int depIdx = FileId - 1; AssetsFileInstance dependency = fileInst.GetDependency(am, depIdx); if (dependency != null) { FilePath = dependency.path; } } public static AssetPPtr FromField(AssetTypeValueField field) { return new AssetPPtr(field["m_FileID"].AsInt, field["m_PathID"].AsLong); } public override bool Equals(object obj) { if (!(obj is AssetPPtr)) { return false; } AssetPPtr assetPPtr = (AssetPPtr)obj; if (assetPPtr.HasFilePath() && HasFilePath()) { if (assetPPtr.PathId == PathId) { return assetPPtr.FilePath == FilePath; } return false; } if (!assetPPtr.HasFilePath() && !HasFilePath()) { if (assetPPtr.PathId == PathId) { return assetPPtr.FileId == FileId; } return false; } return false; } public override int GetHashCode() { return (17 * 23 + (HasFilePath() ? FilePath.GetHashCode() : FileId.GetHashCode())) * 23 + PathId.GetHashCode(); } } public class AssetsFile { public AssetsFileHeader Header { get; set; } public AssetsFileMetadata Metadata { get; set; } public AssetsFileReader Reader { get; set; } public IList AssetInfos => Metadata.AssetInfos; public void Close() { Reader.Close(); } public void Read(AssetsFileReader reader) { Reader = reader; Header = new AssetsFileHeader(); Header.Read(reader); Metadata = new AssetsFileMetadata(); Metadata.Read(reader, Header); } public void Read(Stream stream) { Read(new AssetsFileReader(stream)); } public void Write(AssetsFileWriter writer, long filePos = 0L) { long num = filePos; if (filePos == -1) { num = writer.Position; } else { writer.Position = filePos; } Header.Write(writer); List list = new List(); int count = Metadata.AssetInfos.Count; for (int j = 0; j < count; j++) { AssetFileInfo assetFileInfo = Metadata.AssetInfos[j]; ContentReplacerType replacerType = assetFileInfo.ReplacerType; if (replacerType != ContentReplacerType.Remove) { if (replacerType == ContentReplacerType.AddOrModify && assetFileInfo.Replacer == null) { throw new Exception("Replacer must be non-null when status is Modified!"); } list.Add(new AssetFileInfo { PathId = assetFileInfo.PathId, ByteOffset = assetFileInfo.ByteOffset, ByteSize = assetFileInfo.ByteSize, TypeIdOrIndex = assetFileInfo.TypeIdOrIndex, OldTypeId = assetFileInfo.OldTypeId, ScriptTypeIndex = assetFileInfo.ScriptTypeIndex, Stripped = assetFileInfo.Stripped, Replacer = assetFileInfo.Replacer }); } } list.Sort((AssetFileInfo i1, AssetFileInfo i2) => i1.PathId.CompareTo(i2.PathId)); AssetsFileMetadata assetsFileMetadata = new AssetsFileMetadata { UnityVersion = Metadata.UnityVersion, TargetPlatform = Metadata.TargetPlatform, TypeTreeEnabled = Metadata.TypeTreeEnabled, TypeTreeTypes = Metadata.TypeTreeTypes, AssetInfos = list, ScriptTypes = Metadata.ScriptTypes, Externals = Metadata.Externals, RefTypes = Metadata.RefTypes, UserInformation = Metadata.UserInformation }; long position = writer.Position; assetsFileMetadata.Write(writer, Header.Version); int num2 = (int)(writer.Position - position); if (writer.Position < 4096) { while (writer.Position < 4096) { writer.Write((byte)0); } } else if (writer.Position % 16 == 0L) { writer.Position += 16L; } else { writer.Align16(); } long position2 = writer.Position; for (int k = 0; k < list.Count; k++) { AssetFileInfo assetFileInfo2 = list[k]; long position3 = writer.Position; long byteOffset = position3 - position2; if (assetFileInfo2.ReplacerType == ContentReplacerType.AddOrModify) { assetFileInfo2.Replacer.Write(writer, finalWrite: true); } else { Reader.Position = assetFileInfo2.GetAbsoluteByteOffset(this); Reader.BaseStream.CopyToCompat(writer.BaseStream, assetFileInfo2.ByteSize); } assetFileInfo2.ByteOffset = byteOffset; assetFileInfo2.ByteSize = (uint)(writer.Position - position3); if (k != list.Count - 1) { writer.Align8(); } } long num3 = writer.Position - num; AssetsFileHeader obj = new AssetsFileHeader { MetadataSize = num2, FileSize = num3, Version = Header.Version, DataOffset = position2, Endianness = Header.Endianness }; writer.Position = num; obj.Write(writer); writer.Position = position; assetsFileMetadata.Write(writer, Header.Version); writer.Position = num + num3; } [Obsolete("Use the more consistent and safer info.GetScriptIndex() instead.")] public ushort GetScriptIndex(AssetFileInfo info) { if (Header.Version < 16) { return info.ScriptTypeIndex; } return Metadata.TypeTreeTypes[info.TypeIdOrIndex].ScriptTypeIndex; } public static bool IsAssetsFile(string filePath) { using AssetsFileReader assetsFileReader = new AssetsFileReader(filePath); return IsAssetsFile(assetsFileReader, 0L, assetsFileReader.BaseStream.Length); } public static bool IsAssetsFile(AssetsFileReader reader, long offset, long length) { reader.BigEndian = true; if (length < 48) { return false; } reader.Position = offset; if (reader.ReadStringLength(5) == "Unity") { return false; } reader.Position = offset + 8; int num = reader.ReadInt32(); if (num > 99) { return false; } reader.Position = offset + 20; if (num >= 22) { reader.Position += 28L; } string text = ""; char c; while (reader.Position < reader.BaseStream.Length && (c = (char)reader.ReadByte()) != 0) { text += c; if (text.Length > 255) { return false; } } string text2 = Regex.Replace(text, "[a-zA-Z0-9\\.\\n\\-]", ""); string text3 = Regex.Replace(text, "[^a-zA-Z0-9\\.\\n\\-]", ""); if (text2 == "") { return text3.Length > 0; } return false; } public AssetFileInfo GetAssetInfo(long pathId) { return Metadata.GetAssetInfo(pathId); } public void GenerateQuickLookup() { Metadata.GenerateQuickLookup(); } public List GetAssetsOfType(int typeId) { return Metadata.GetAssetsOfType(typeId); } public List GetAssetsOfType(AssetClassID typeId) { return Metadata.GetAssetsOfType(typeId); } public List GetAssetsOfType(int typeId, ushort scriptIndex) { return Metadata.GetAssetsOfType(typeId, scriptIndex); } public List GetAssetsOfType(AssetClassID typeId, ushort scriptIndex) { return Metadata.GetAssetsOfType(typeId, scriptIndex); } } public class AssetsFileExternal { public string VirtualAssetPathName { get; set; } public GUID128 Guid { get; set; } public AssetsFileExternalType Type { get; set; } public string PathName { get; set; } public string OriginalPathName { get; set; } public void Read(AssetsFileReader reader) { VirtualAssetPathName = reader.ReadNullTerminated(); Guid = new GUID128(reader); Type = (AssetsFileExternalType)reader.ReadInt32(); PathName = reader.ReadNullTerminated(); OriginalPathName = PathName; if (PathName == "resources/unity_builtin_extra") { PathName = "Resources/unity_builtin_extra"; } else if (PathName == "library/unity default resources" || PathName == "Library/unity default resources") { PathName = "Resources/unity default resources"; } else if (PathName == "library/unity editor resources" || PathName == "Library/unity editor resources") { PathName = "Resources/unity editor resources"; } } public void Write(AssetsFileWriter writer) { writer.WriteNullTerminated(VirtualAssetPathName); Guid.Write(writer); writer.Write((int)Type); string text = PathName; if ((PathName == "Resources/unity_builtin_extra" || PathName == "Resources/unity default resources" || PathName == "Resources/unity editor resources") && OriginalPathName != string.Empty) { text = OriginalPathName; } writer.WriteNullTerminated(text); } public long GetSize() { long num = 0L; num += VirtualAssetPathName.Length + 1; num += 16; num += 4; if ((PathName == "Resources/unity_builtin_extra" || PathName == "Resources/unity default resources" || PathName == "Resources/unity editor resources") && OriginalPathName != string.Empty) { return num + (OriginalPathName.Length + 1); } return num + (PathName.Length + 1); } } public enum AssetsFileExternalType { Normal, Cached, Serialized, Meta } public class AssetsFileHeader { public long MetadataSize { get; set; } public long FileSize { get; set; } public uint Version { get; set; } public long DataOffset { get; set; } public bool Endianness { get; set; } public void Read(AssetsFileReader reader) { reader.BigEndian = true; MetadataSize = reader.ReadUInt32(); FileSize = reader.ReadUInt32(); Version = reader.ReadUInt32(); DataOffset = reader.ReadUInt32(); Endianness = reader.ReadBoolean(); reader.Position += 3L; if (Version >= 22) { MetadataSize = reader.ReadUInt32(); FileSize = reader.ReadInt64(); DataOffset = reader.ReadInt64(); reader.Position += 8L; } reader.BigEndian = Endianness; } public void Write(AssetsFileWriter writer) { writer.BigEndian = true; if (Version >= 22) { writer.Write(0); writer.Write(0); writer.Write(Version); writer.Write(0); } else { writer.Write((uint)MetadataSize); writer.Write((uint)FileSize); writer.Write(Version); writer.Write((uint)DataOffset); } writer.Write(Endianness); writer.Write(new byte[3]); if (Version >= 22) { writer.Write((uint)MetadataSize); writer.Write(FileSize); writer.Write(DataOffset); writer.Write(new byte[8]); } writer.BigEndian = Endianness; } public long GetSize() { long num = 20L; if (Version >= 22) { num += 28; } return num; } } public class AssetsFileMetadata { private Dictionary _quickLookup; public string UnityVersion { get; set; } public uint TargetPlatform { get; set; } public bool TypeTreeEnabled { get; set; } public List TypeTreeTypes { get; set; } public IList AssetInfos { get; set; } public List ScriptTypes { get; set; } public List Externals { get; set; } public List RefTypes { get; set; } public string UserInformation { get; set; } public void Read(AssetsFileReader reader, AssetsFileHeader header) { Read(reader, header.Version); } public void Read(AssetsFileReader reader, uint version) { _quickLookup = null; UnityVersion = reader.ReadNullTerminated(); TargetPlatform = reader.ReadUInt32(); if (version >= 13) { TypeTreeEnabled = reader.ReadBoolean(); } int num = reader.ReadInt32(); TypeTreeTypes = new List(num); for (int i = 0; i < num; i++) { TypeTreeType typeTreeType = new TypeTreeType(); typeTreeType.Read(reader, version, TypeTreeEnabled, isRefType: false); TypeTreeTypes.Add(typeTreeType); } int num2 = reader.ReadInt32(); reader.Align(); AssetInfos = new List(num2); for (int j = 0; j < num2; j++) { AssetFileInfo assetFileInfo = new AssetFileInfo(); assetFileInfo.Read(reader, version); assetFileInfo.TypeId = assetFileInfo.GetTypeId(this, version); AssetInfos.Add(assetFileInfo); } int num3 = reader.ReadInt32(); ScriptTypes = new List(num3); for (int k = 0; k < num3; k++) { int fileId = reader.ReadInt32(); reader.Align(); long pathId = reader.ReadInt64(); AssetPPtr item = new AssetPPtr(fileId, pathId); ScriptTypes.Add(item); } int num4 = reader.ReadInt32(); Externals = new List(num4); for (int l = 0; l < num4; l++) { AssetsFileExternal assetsFileExternal = new AssetsFileExternal(); assetsFileExternal.Read(reader); Externals.Add(assetsFileExternal); } if (version >= 20) { int num5 = reader.ReadInt32(); RefTypes = new List(num5); for (int m = 0; m < num5; m++) { TypeTreeType typeTreeType2 = new TypeTreeType(); typeTreeType2.Read(reader, version, TypeTreeEnabled, isRefType: true); RefTypes.Add(typeTreeType2); } } if (version >= 5) { UserInformation = reader.ReadNullTerminated(); } } public void Write(AssetsFileWriter writer, uint version) { writer.WriteNullTerminated(UnityVersion); writer.Write(TargetPlatform); if (version >= 13) { writer.Write(TypeTreeEnabled); } writer.Write(TypeTreeTypes.Count); for (int i = 0; i < TypeTreeTypes.Count; i++) { TypeTreeTypes[i].Write(writer, version, TypeTreeEnabled); } writer.Write(AssetInfos.Count); writer.Align(); for (int j = 0; j < AssetInfos.Count; j++) { AssetInfos[j].Write(writer, version); } writer.Write(ScriptTypes.Count); for (int k = 0; k < ScriptTypes.Count; k++) { writer.Write(ScriptTypes[k].FileId); writer.Align(); writer.Write(ScriptTypes[k].PathId); } writer.Write(Externals.Count); for (int l = 0; l < Externals.Count; l++) { Externals[l].Write(writer); } if (version >= 20) { writer.Write(RefTypes.Count); for (int m = 0; m < RefTypes.Count; m++) { RefTypes[m].Write(writer, version, TypeTreeEnabled); } } if (version >= 5) { writer.WriteNullTerminated(UserInformation); } } public AssetFileInfo GetAssetInfo(long pathId) { if (_quickLookup != null) { if (_quickLookup.ContainsKey(pathId)) { return _quickLookup[pathId]; } } else { for (int i = 0; i < AssetInfos.Count; i++) { AssetFileInfo assetFileInfo = AssetInfos[i]; if (assetFileInfo.PathId == pathId) { return assetFileInfo; } } } return null; } public void AddAssetInfo(AssetFileInfo info) { if (_quickLookup != null) { _quickLookup[info.PathId] = info; } AssetInfos.Add(info); } public bool RemoveAssetInfo(AssetFileInfo info) { if (_quickLookup != null) { _quickLookup.Remove(info.PathId); } return AssetInfos.Remove(info); } public void GenerateQuickLookup() { _quickLookup = new Dictionary(); for (int i = 0; i < AssetInfos.Count; i++) { AssetFileInfo assetFileInfo = AssetInfos[i]; _quickLookup[assetFileInfo.PathId] = assetFileInfo; } } public List GetAssetsOfType(int typeId) { List list = new List(); foreach (AssetFileInfo assetInfo in AssetInfos) { if (assetInfo.TypeId == typeId) { list.Add(assetInfo); } } return list; } public List GetAssetsOfType(int typeId, ushort scriptIndex) { List list = new List(); foreach (AssetFileInfo assetInfo in AssetInfos) { if (scriptIndex != ushort.MaxValue) { if (assetInfo.TypeId < 0) { if (assetInfo.ScriptTypeIndex != scriptIndex || (typeId != 114 && (typeId >= 0 || assetInfo.TypeId != typeId))) { continue; } } else if (assetInfo.TypeId != 114 || assetInfo.TypeIdOrIndex == assetInfo.TypeId || assetInfo.GetScriptIndex(this, 16u) != scriptIndex || typeId != 114) { continue; } } else if (assetInfo.TypeId != typeId) { continue; } list.Add(assetInfo); } return list; } public List GetAssetsOfType(AssetClassID typeId) { return GetAssetsOfType((int)typeId); } public List GetAssetsOfType(AssetClassID typeId, ushort scriptIndex) { return GetAssetsOfType((int)typeId, scriptIndex); } public TypeTreeType FindTypeTreeTypeByID(int id) { foreach (TypeTreeType typeTreeType in TypeTreeTypes) { if (typeTreeType.TypeId == id) { return typeTreeType; } } return null; } public TypeTreeType FindTypeTreeTypeByID(int id, ushort scriptIndex) { foreach (TypeTreeType typeTreeType in TypeTreeTypes) { if (typeTreeType.TypeId == id) { if (typeTreeType.ScriptTypeIndex == scriptIndex) { return typeTreeType; } if (id < 0 && typeTreeType.ScriptTypeIndex == ushort.MaxValue) { return typeTreeType; } } } return null; } public int FindTypeTreeTypeIndexByID(int id, ushort scriptIndex) { int count = TypeTreeTypes.Count; for (int i = 0; i < count; i++) { TypeTreeType typeTreeType = TypeTreeTypes[i]; if (typeTreeType.TypeId == id) { if (typeTreeType.ScriptTypeIndex == scriptIndex) { return i; } if (id < 0 && typeTreeType.ScriptTypeIndex == ushort.MaxValue) { return i; } } } return -1; } public TypeTreeType FindTypeTreeTypeByScriptIndex(ushort scriptIndex) { foreach (TypeTreeType typeTreeType in TypeTreeTypes) { if (typeTreeType.ScriptTypeIndex == scriptIndex) { return typeTreeType; } } return null; } public TypeTreeType FindTypeTreeTypeByName(string name) { foreach (TypeTreeType typeTreeType in TypeTreeTypes) { if (typeTreeType.Nodes.Count != 0 && typeTreeType.Nodes[0].GetTypeString(typeTreeType.StringBufferBytes) == name) { return typeTreeType; } } return null; } public TypeTreeType FindRefTypeByIndex(ushort scriptIndex) { foreach (TypeTreeType refType in RefTypes) { if (refType.ScriptTypeIndex == scriptIndex) { return refType; } } return null; } public long GetSize(uint version) { long num = 0L; num += UnityVersion.Length + 1; num += 4; if (version >= 13) { num++; } num += 4; for (int i = 0; i < TypeTreeTypes.Count; i++) { num += TypeTreeTypes[i].GetSize(version, TypeTreeEnabled); } num += 4; num = (num + 3) & -4; num += AssetFileInfo.GetSize(version) * AssetInfos.Count; num += 4; num = (num + 3) & -4; num += 12 * ScriptTypes.Count; num += 4; for (int j = 0; j < Externals.Count; j++) { num += Externals[j].GetSize(); } if (version >= 20) { num += 4; for (int k = 0; k < RefTypes.Count; k++) { num += RefTypes[k].GetSize(version, TypeTreeEnabled); } } if (version >= 5) { num += UserInformation.Length + 1; } return num; } } public class AssetTypeReference { public static readonly AssetTypeReference TERMINUS = new AssetTypeReference("Terminus", "UnityEngine.DMAT", "FAKE_ASM"); public string ClassName { get; set; } public string Namespace { get; set; } public string AsmName { get; set; } public AssetTypeReference() { } public AssetTypeReference(string className, string nameSpace, string asmName) { ClassName = className; Namespace = nameSpace; AsmName = asmName; } public void ReadMetadata(AssetsFileReader reader) { ClassName = reader.ReadNullTerminated(); Namespace = reader.ReadNullTerminated(); AsmName = reader.ReadNullTerminated(); } public void ReadAsset(AssetsFileReader reader) { ClassName = reader.ReadCountStringInt32(); reader.Align(); Namespace = reader.ReadCountStringInt32(); reader.Align(); AsmName = reader.ReadCountStringInt32(); reader.Align(); } public void WriteMetadata(AssetsFileWriter writer) { writer.WriteNullTerminated(ClassName); writer.WriteNullTerminated(Namespace); writer.WriteNullTerminated(AsmName); } public void WriteAsset(AssetsFileWriter writer) { writer.WriteCountStringInt32(ClassName); writer.Align(); writer.WriteCountStringInt32(Namespace); writer.Align(); writer.WriteCountStringInt32(AsmName); writer.Align(); } public override bool Equals(object obj) { if (!(obj is AssetTypeReference assetTypeReference)) { return false; } if (ClassName == assetTypeReference.ClassName && Namespace == assetTypeReference.Namespace) { return AsmName == assetTypeReference.AsmName; } return false; } public override int GetHashCode() { return ((17 * 23 + ClassName.GetHashCode()) * 23 + Namespace.GetHashCode()) * 23 + AsmName.GetHashCode(); } } public struct GUID128 { private const string HexToLiteral = "0123456789abcdef"; public uint data0; public uint data1; public uint data2; public uint data3; public bool IsEmpty { get { if (data0 == 0 && data1 == 0 && data2 == 0) { return data3 == 0; } return false; } } public uint this[int i] { get { return i switch { 0 => data0, 1 => data1, 2 => data2, 3 => data3, _ => throw new IndexOutOfRangeException(), }; } set { switch (i) { case 0: data0 = value; break; case 1: data1 = value; break; case 2: data2 = value; break; case 3: data3 = value; break; default: throw new IndexOutOfRangeException(); } } } public GUID128(AssetsFileReader reader) { this = default(GUID128); Read(reader); } public void Read(AssetsFileReader reader) { data0 = reader.ReadUInt32(); data1 = reader.ReadUInt32(); data2 = reader.ReadUInt32(); data3 = reader.ReadUInt32(); } public void Write(AssetsFileWriter writer) { writer.Write(data0); writer.Write(data1); writer.Write(data2); writer.Write(data3); } public override string ToString() { StringBuilder stringBuilder = new StringBuilder(32); for (int num = 3; num >= 0; num--) { for (int num2 = 7; num2 >= 0; num2--) { uint num3 = this[num]; num3 >>= num2 * 4; num3 &= 0xFu; stringBuilder.Insert(0, "0123456789abcdef"[(int)num3]); } } return stringBuilder.ToString(); } public static bool TryParse(string str, out GUID128 guid) { guid = default(GUID128); if (str.Length != 32) { return false; } for (int i = 0; i < 4; i++) { uint num = 0u; for (int num2 = 7; num2 >= 0; num2--) { uint num3 = LiteralToHex(str[i * 8 + num2]); if (num3 == uint.MaxValue) { return false; } num |= num3 << num2 * 4; } guid[i] = num; } return true; } private static uint LiteralToHex(char c) { return c switch { '0' => 0u, '1' => 1u, '2' => 2u, '3' => 3u, '4' => 4u, '5' => 5u, '6' => 6u, '7' => 7u, '8' => 8u, '9' => 9u, 'a' => 10u, 'b' => 11u, 'c' => 12u, 'd' => 13u, 'e' => 14u, 'f' => 15u, _ => uint.MaxValue, }; } } public struct Hash128 { public byte[] data; public Hash128(byte[] data) { this.data = data; } public Hash128(AssetsFileReader reader) { data = reader.ReadBytes(16); } public bool IsZero() { if (data == null) { return true; } for (int i = 0; i < data.Length; i++) { if (data[i] != 0) { return false; } } return true; } public override string ToString() { StringBuilder stringBuilder = new StringBuilder(data.Length * 2); byte[] array = data; foreach (byte b in array) { stringBuilder.AppendFormat("{0:x2}", b); } return stringBuilder.ToString(); } public static Hash128 NewBlankHash() { Hash128 result = default(Hash128); result.data = new byte[16]; return result; } } public class TypeTreeNode { public ushort Version { get; set; } public byte Level { get; set; } public TypeTreeNodeFlags TypeFlags { get; set; } public uint TypeStrOffset { get; set; } public uint NameStrOffset { get; set; } public int ByteSize { get; set; } public uint Index { get; set; } public uint MetaFlags { get; set; } public ulong RefTypeHash { get; set; } public void Read(AssetsFileReader reader, uint version) { Version = reader.ReadUInt16(); Level = reader.ReadByte(); TypeFlags = (TypeTreeNodeFlags)reader.ReadByte(); TypeStrOffset = reader.ReadUInt32(); NameStrOffset = reader.ReadUInt32(); ByteSize = reader.ReadInt32(); Index = reader.ReadUInt32(); MetaFlags = reader.ReadUInt32(); if (version >= 18) { RefTypeHash = reader.ReadUInt64(); } } public void Write(AssetsFileWriter writer, uint version) { writer.Write(Version); writer.Write(Level); writer.Write((byte)TypeFlags); writer.Write(TypeStrOffset); writer.Write(NameStrOffset); writer.Write(ByteSize); writer.Write(Index); writer.Write(MetaFlags); if (version >= 18) { writer.Write(RefTypeHash); } } public string GetTypeString(byte[] stringTable, byte[] commonStringTable = null) { return ReadStringTableString(stringTable, commonStringTable ?? TypeTreeType.COMMON_STRING_TABLE, TypeStrOffset); } public string GetNameString(byte[] stringTable, byte[] commonStringTable = null) { return ReadStringTableString(stringTable, commonStringTable ?? TypeTreeType.COMMON_STRING_TABLE, NameStrOffset); } public static long GetSize(uint version) { long num = 24L; if (version >= 18) { num += 8; } return num; } private string ReadStringTableString(byte[] stringTable, byte[] commonStringTable, uint offset) { if ((offset & 0x80000000u) != 0) { offset &= 0x7FFFFFFFu; stringTable = commonStringTable; } int num = Array.IndexOf(stringTable, (byte)0, (int)offset); return Encoding.UTF8.GetString(stringTable, (int)offset, (int)(num - offset)); } } [Flags] public enum TypeTreeNodeFlags { None = 0, Array = 1, Ref = 2, Registry = 4, ArrayOfRefs = 8 } public class TypeTreeType { public static readonly byte[] COMMON_STRING_TABLE = Encoding.UTF8.GetBytes("AABB\0AnimationClip\0AnimationCurve\0AnimationState\0Array\0Base\0BitField\0bitset\0bool\0char\0ColorRGBA\0Component\0data\0deque\0double\0dynamic_array\0FastPropertyName\0first\0float\0Font\0GameObject\0Generic Mono\0GradientNEW\0GUID\0GUIStyle\0int\0list\0long long\0map\0Matrix4x4f\0MdFour\0MonoBehaviour\0MonoScript\0m_ByteSize\0m_Curve\0m_EditorClassIdentifier\0m_EditorHideFlags\0m_Enabled\0m_ExtensionPtr\0m_GameObject\0m_Index\0m_IsArray\0m_IsStatic\0m_MetaFlag\0m_Name\0m_ObjectHideFlags\0m_PrefabInternal\0m_PrefabParentObject\0m_Script\0m_StaticEditorFlags\0m_Type\0m_Version\0Object\0pair\0PPtr\0PPtr\0PPtr\0PPtr\0PPtr\0PPtr\0PPtr\0PPtr\0PPtr\0PPtr\0PPtr\0PPtr\0Prefab\0Quaternionf\0Rectf\0RectInt\0RectOffset\0second\0set\0short\0size\0SInt16\0SInt32\0SInt64\0SInt8\0staticvector\0string\0TextAsset\0TextMesh\0Texture\0Texture2D\0Transform\0TypelessData\0UInt16\0UInt32\0UInt64\0UInt8\0unsigned int\0unsigned long long\0unsigned short\0vector\0Vector2f\0Vector3f\0Vector4f\0m_ScriptingClassIdentifier\0Gradient\0Type*\0int2_storage\0int3_storage\0BoundsInt\0m_CorrespondingSourceObject\0m_PrefabInstance\0m_PrefabAsset\0FileSize\0Hash128\0RenderingLayerMask\0"); public int TypeId { get; set; } public bool IsStrippedType { get; set; } public ushort ScriptTypeIndex { get; set; } public Hash128 ScriptIdHash { get; set; } public Hash128 TypeHash { get; set; } public List Nodes { get; set; } public byte[] StringBufferBytes { get; set; } public bool IsRefType { get; set; } public int[] TypeDependencies { get; set; } public AssetTypeReference TypeReference { get; set; } public string StringBuffer { get { if (StringBufferBytes == null) { return null; } return Encoding.UTF8.GetString(StringBufferBytes); } set { StringBufferBytes = Encoding.UTF8.GetBytes(value); } } public void Read(AssetsFileReader reader, uint version, bool hasTypeTree, bool isRefType) { TypeId = reader.ReadInt32(); if (version >= 16) { IsStrippedType = reader.ReadBoolean(); } if (version >= 17) { ScriptTypeIndex = reader.ReadUInt16(); } else { ScriptTypeIndex = ushort.MaxValue; } if ((version < 17 && TypeId < 0) || (version >= 17 && TypeId == 114) || (isRefType && ScriptTypeIndex != ushort.MaxValue)) { ScriptIdHash = new Hash128(reader); } TypeHash = new Hash128(reader); IsRefType = isRefType; if (!hasTypeTree) { return; } int num = reader.ReadInt32(); int count = reader.ReadInt32(); Nodes = new List(num); for (int i = 0; i < num; i++) { TypeTreeNode typeTreeNode = new TypeTreeNode(); typeTreeNode.Read(reader, version); Nodes.Add(typeTreeNode); } StringBufferBytes = reader.ReadBytes(count); if (version < 21) { return; } if (!isRefType) { int num2 = reader.ReadInt32(); TypeDependencies = new int[num2]; for (int j = 0; j < num2; j++) { TypeDependencies[j] = reader.ReadInt32(); } } else { TypeReference = new AssetTypeReference(); TypeReference.ReadMetadata(reader); } } public void Write(AssetsFileWriter writer, uint version, bool hasTypeTree) { writer.Write(TypeId); if (version >= 16) { writer.Write(IsStrippedType); } if (version >= 17) { writer.Write(ScriptTypeIndex); } if ((version < 17 && TypeId < 0) || (version >= 17 && TypeId == 114) || (IsRefType && ScriptTypeIndex != ushort.MaxValue)) { writer.Write(ScriptIdHash.data); } writer.Write(TypeHash.data); if (!hasTypeTree) { return; } writer.Write(Nodes.Count); writer.Write(StringBufferBytes.Length); for (int i = 0; i < Nodes.Count; i++) { Nodes[i].Write(writer, version); } writer.Write(StringBufferBytes); if (version < 21) { return; } if (!IsRefType) { writer.Write(TypeDependencies.Length); for (int j = 0; j < TypeDependencies.Length; j++) { writer.Write(TypeDependencies[j]); } } else { TypeReference.WriteMetadata(writer); } } public long GetSize(uint version, bool hasTypeTree) { long num = 0L; num += 4; if (version >= 16) { num++; } if (version >= 17) { num += 2; } if ((version < 17 && TypeId < 0) || (version >= 17 && TypeId == 114) || (IsRefType && ScriptTypeIndex != ushort.MaxValue)) { num += 16; } num += 16; if (hasTypeTree) { num += 4; num += 4; num += TypeTreeNode.GetSize(version) * Nodes.Count; num += StringBufferBytes.Length; if (version >= 21) { if (!IsRefType) { num += 4; num += TypeDependencies.Length * 4; } else { num += TypeReference.ClassName.Length + 1; num += TypeReference.Namespace.Length + 1; num += TypeReference.AsmName.Length + 1; } } } return num; } } public struct AssetTypeArrayInfo { public int size; public AssetTypeArrayInfo(int size) { this.size = size; } } public class AssetTypeReferencedObject { public long rid; public AssetTypeReference type; public AssetTypeValueField data; } public class AssetTypeTemplateField { public string Name { get; set; } public string Type { get; set; } public AssetValueType ValueType { get; set; } public bool IsArray { get; set; } public bool IsAligned { get; set; } public bool HasValue { get; set; } public ushort Version { get; set; } public List Children { get; set; } public AssetTypeTemplateField this[string name] { get { if (name.Contains(".")) { string[] array = name.Split(new char[1] { '.' }); AssetTypeTemplateField assetTypeTemplateField = this; string[] array2 = array; foreach (string text in array2) { bool flag = false; foreach (AssetTypeTemplateField child in assetTypeTemplateField.Children) { if (child.Name == text) { flag = true; assetTypeTemplateField = child; break; } } if (!flag) { return null; } } return assetTypeTemplateField; } foreach (AssetTypeTemplateField child2 in Children) { if (child2.Name == name) { return child2; } } return null; } } public AssetTypeTemplateField this[int index] => Children[index]; public void FromTypeTree(TypeTreeType typeTreeType) { int fieldIndex = 0; FromTypeTree(typeTreeType, ref fieldIndex); } private void FromTypeTree(TypeTreeType typeTreeType, ref int fieldIndex) { TypeTreeNode typeTreeNode = typeTreeType.Nodes[fieldIndex]; Name = typeTreeNode.GetNameString(typeTreeType.StringBufferBytes); Type = typeTreeNode.GetTypeString(typeTreeType.StringBufferBytes); ValueType = AssetTypeValueField.GetValueTypeByTypeName(Type); IsArray = Net35Polyfill.HasFlag(typeTreeNode.TypeFlags, TypeTreeNodeFlags.Array); IsAligned = (typeTreeNode.MetaFlags & 0x4000) != 0; HasValue = ValueType != AssetValueType.None; Version = typeTreeNode.Version; Children = new List(); fieldIndex++; while (fieldIndex < typeTreeType.Nodes.Count) { if (typeTreeType.Nodes[fieldIndex].Level <= typeTreeNode.Level) { fieldIndex--; break; } AssetTypeTemplateField assetTypeTemplateField = new AssetTypeTemplateField(); assetTypeTemplateField.FromTypeTree(typeTreeType, ref fieldIndex); Children.Add(assetTypeTemplateField); fieldIndex++; } if (ValueType == AssetValueType.String && !Children[0].IsArray && Children[0].ValueType != 0) { Type = "_string"; ValueType = AssetValueType.None; } if (IsArray) { ValueType = ((Children[1].ValueType == AssetValueType.UInt8) ? AssetValueType.ByteArray : AssetValueType.Array); } Children.TrimExcess(); } public void FromClassDatabase(ClassDatabaseFile cldbFile, ClassDatabaseType cldbType, bool preferEditor = false) { if (cldbType.EditorRootNode == null && cldbType.ReleaseRootNode == null) { throw new Exception("No root nodes were found!"); } ClassDatabaseTypeNode preferredNode = cldbType.GetPreferredNode(preferEditor); FromClassDatabase(cldbFile.StringTable, preferredNode); } private void FromClassDatabase(ClassDatabaseStringTable strTable, ClassDatabaseTypeNode node) { Name = strTable.GetString(node.FieldName); Type = strTable.GetString(node.TypeName); if (Type == "SInt32") { Type = "int"; } else if (Type == "UInt32") { Type = "unsigned int"; } ValueType = AssetTypeValueField.GetValueTypeByTypeName(Type); IsArray = node.TypeFlags == 1; IsAligned = (node.MetaFlag & 0x4000) != 0; HasValue = ValueType != AssetValueType.None; Version = node.Version; Children = new List(node.Children.Count); foreach (ClassDatabaseTypeNode child in node.Children) { AssetTypeTemplateField assetTypeTemplateField = new AssetTypeTemplateField(); assetTypeTemplateField.FromClassDatabase(strTable, child); Children.Add(assetTypeTemplateField); } if (ValueType == AssetValueType.String && !Children[0].IsArray && Children[0].ValueType != 0) { Type = "_string"; ValueType = AssetValueType.None; } if (IsArray) { ValueType = ((Children[1].ValueType == AssetValueType.UInt8) ? AssetValueType.ByteArray : AssetValueType.Array); } } public AssetTypeValueField MakeValue(AssetsFileReader reader, RefTypeManager refMan = null) { AssetTypeValueField valueField = new AssetTypeValueField { TemplateField = this }; return ReadType(reader, valueField, refMan); } public AssetTypeValueField MakeValue(AssetsFileReader reader, long position, RefTypeManager refMan = null) { reader.Position = position; return MakeValue(reader, refMan); } public AssetTypeValueField ReadType(AssetsFileReader reader, AssetTypeValueField valueField, RefTypeManager refMan) { if (valueField.TemplateField.IsArray) { int count = valueField.TemplateField.Children.Count; if (count != 2) { throw new Exception($"Expected array to have two children, found {count} instead!"); } AssetValueType valueType = valueField.TemplateField.Children[0].ValueType; if (valueType != AssetValueType.Int32 && valueType != AssetValueType.UInt32) { throw new Exception($"Expected int array size type, found {valueType} instead!"); } if (valueField.TemplateField.ValueType == AssetValueType.ByteArray) { valueField.Children = new List(0); int count2 = reader.ReadInt32(); byte[] value = reader.ReadBytes(count2); if (valueField.TemplateField.IsAligned) { reader.Align(); } valueField.Value = new AssetTypeValue(AssetValueType.ByteArray, value); } else { int num = reader.ReadInt32(); valueField.Children = new List(num); for (int i = 0; i < num; i++) { AssetTypeValueField assetTypeValueField = new AssetTypeValueField(); assetTypeValueField.TemplateField = valueField.TemplateField.Children[1]; valueField.Children.Add(ReadType(reader, assetTypeValueField, refMan)); } valueField.Children.TrimExcess(); if (valueField.TemplateField.IsAligned) { reader.Align(); } AssetTypeArrayInfo assetTypeArrayInfo = default(AssetTypeArrayInfo); assetTypeArrayInfo.size = num; AssetTypeArrayInfo assetTypeArrayInfo2 = assetTypeArrayInfo; valueField.Value = new AssetTypeValue(AssetValueType.Array, assetTypeArrayInfo2); } } else { AssetValueType valueType2 = valueField.TemplateField.ValueType; if (valueType2 == AssetValueType.None) { int count3 = valueField.TemplateField.Children.Count; valueField.Children = new List(count3); for (int j = 0; j < count3; j++) { AssetTypeValueField assetTypeValueField2 = new AssetTypeValueField(); assetTypeValueField2.TemplateField = valueField.TemplateField.Children[j]; valueField.Children.Add(ReadType(reader, assetTypeValueField2, refMan)); } valueField.Children.TrimExcess(); valueField.Value = null; if (valueField.TemplateField.IsAligned) { reader.Align(); } } else { ReadPrimitiveType(reader, valueField, valueType2, refMan); } } return valueField; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void ReadPrimitiveType(AssetsFileReader reader, AssetTypeValueField valueField, AssetValueType type, RefTypeManager refMan) { switch (type) { case AssetValueType.String: { valueField.Children = new List(0); int count = reader.ReadInt32(); valueField.Value = new AssetTypeValue(reader.ReadBytes(count), asString: true); reader.Align(); return; } case AssetValueType.ManagedReferencesRegistry: ReadManagedReferencesRegistryType(reader, valueField, refMan); return; } if (valueField.TemplateField.Children.Count == 0) { valueField.Children = new List(0); switch (type) { case AssetValueType.Int8: valueField.Value = new AssetTypeValue(reader.ReadSByte()); break; case AssetValueType.UInt8: valueField.Value = new AssetTypeValue(reader.ReadByte()); break; case AssetValueType.Bool: valueField.Value = new AssetTypeValue(reader.ReadBoolean()); break; case AssetValueType.Int16: valueField.Value = new AssetTypeValue(reader.ReadInt16()); break; case AssetValueType.UInt16: valueField.Value = new AssetTypeValue(reader.ReadUInt16()); break; case AssetValueType.Int32: valueField.Value = new AssetTypeValue(reader.ReadInt32()); break; case AssetValueType.UInt32: valueField.Value = new AssetTypeValue(reader.ReadUInt32()); break; case AssetValueType.Int64: valueField.Value = new AssetTypeValue(reader.ReadInt64()); break; case AssetValueType.UInt64: valueField.Value = new AssetTypeValue(reader.ReadUInt64()); break; case AssetValueType.Float: valueField.Value = new AssetTypeValue(reader.ReadSingle()); break; case AssetValueType.Double: valueField.Value = new AssetTypeValue(reader.ReadDouble()); break; } if (valueField.TemplateField.IsAligned) { reader.Align(); } } else if (type != 0) { throw new Exception("Cannot read value of field with children!"); } } public void ReadManagedReferencesRegistryType(AssetsFileReader reader, AssetTypeValueField valueField, RefTypeManager refMan) { if (refMan == null) { throw new Exception("refMan must be non-null to deserialize objects with ref types."); } valueField.Children = new List(0); ManagedReferencesRegistry managedReferencesRegistry = new ManagedReferencesRegistry(); valueField.Value = new AssetTypeValue(managedReferencesRegistry); int count = valueField.TemplateField.Children.Count; if (count != 2) { throw new Exception($"Expected ManagedReferencesRegistry to have two children, found {count} instead!"); } managedReferencesRegistry.version = reader.ReadInt32(); managedReferencesRegistry.references = new List(); if (managedReferencesRegistry.version == 1) { while (true) { AssetTypeReferencedObject assetTypeReferencedObject = MakeReferencedObject(reader, managedReferencesRegistry.version, managedReferencesRegistry.references.Count, refMan); if (!assetTypeReferencedObject.type.Equals(AssetTypeReference.TERMINUS)) { managedReferencesRegistry.references.Add(assetTypeReferencedObject); continue; } break; } } else { int num = reader.ReadInt32(); for (int i = 0; i < num; i++) { AssetTypeReferencedObject item = MakeReferencedObject(reader, managedReferencesRegistry.version, -1, refMan); managedReferencesRegistry.references.Add(item); } } } public AssetTypeTemplateField Clone() { return new AssetTypeTemplateField { Name = Name, Type = Type, ValueType = ValueType, IsArray = IsArray, IsAligned = IsAligned, HasValue = HasValue, Children = Children.Select((AssetTypeTemplateField c) => c.Clone()).ToList() }; } private AssetTypeReferencedObject MakeReferencedObject(AssetsFileReader reader, int registryVersion, int referenceIndex, RefTypeManager refMan) { AssetTypeReferencedObject assetTypeReferencedObject = new AssetTypeReferencedObject(); if (registryVersion == 1) { assetTypeReferencedObject.rid = referenceIndex; } else { assetTypeReferencedObject.rid = reader.ReadInt64(); } AssetTypeReference assetTypeReference = new AssetTypeReference(); assetTypeReference.ReadAsset(reader); assetTypeReferencedObject.type = assetTypeReference; AssetTypeTemplateField templateField = refMan.GetTemplateField(assetTypeReference); if (templateField != null) { AssetTypeValueField valueField = new AssetTypeValueField { TemplateField = templateField }; assetTypeReferencedObject.data = ReadType(reader, valueField, refMan); } else { assetTypeReferencedObject.data = AssetTypeValueField.DUMMY_FIELD; } return assetTypeReferencedObject; } public override string ToString() { return Type + " " + Name; } } public class AssetTypeValue { public AssetValueType ValueType { get; set; } private object Value { get; set; } public bool AsBool { get { object value = Value; if (value is bool) { return (bool)value; } if (Value is byte b) { return b == 1; } return false; } set { Value = value; } } public sbyte AsSByte { get { object value = Value; if (value is sbyte) { return (sbyte)value; } return (sbyte)Convert.ChangeType(Value, typeof(sbyte)); } set { Value = value; } } public byte AsByte { get { object value = Value; if (value is byte) { return (byte)value; } return (byte)Convert.ChangeType(Value, typeof(byte)); } set { Value = value; } } public short AsShort { get { object value = Value; if (value is short) { return (short)value; } return (short)Convert.ChangeType(Value, typeof(short)); } set { Value = value; } } public ushort AsUShort { get { object value = Value; if (value is ushort) { return (ushort)value; } return (ushort)Convert.ChangeType(Value, typeof(ushort)); } set { Value = value; } } public int AsInt { get { object value = Value; if (value is int) { return (int)value; } return (int)Convert.ChangeType(Value, typeof(int)); } set { Value = value; } } public uint AsUInt { get { object value = Value; if (value is uint) { return (uint)value; } return (uint)Convert.ChangeType(Value, typeof(uint)); } set { Value = value; } } public long AsLong { get { object value = Value; if (value is long) { return (long)value; } return (long)Convert.ChangeType(Value, typeof(long)); } set { Value = value; } } public ulong AsULong { get { object value = Value; if (value is ulong) { return (ulong)value; } return (ulong)Convert.ChangeType(Value, typeof(ulong)); } set { Value = value; } } public float AsFloat { get { object value = Value; if (value is float) { return (float)value; } return (float)Convert.ChangeType(Value, typeof(float)); } set { Value = value; } } public double AsDouble { get { object value = Value; if (value is double) { return (double)value; } return (double)Convert.ChangeType(Value, typeof(double)); } set { Value = value; } } public string AsString { get { if (ValueType == AssetValueType.String) { return Encoding.UTF8.GetString((byte[])Value); } if (ValueType == AssetValueType.Bool) { if (!(bool)Value) { return "false"; } return "true"; } if (ValueType == AssetValueType.ByteArray) { return SimpleHexDump((byte[])Value); } return Value.ToString(); } set { Value = Encoding.UTF8.GetBytes(value); } } public AssetTypeArrayInfo AsArray { get { return (AssetTypeArrayInfo)Value; } set { Value = value; } } public byte[] AsByteArray { get { return (byte[])Value; } set { Value = value; } } public ManagedReferencesRegistry AsManagedReferencesRegistry { get { return (ManagedReferencesRegistry)Value; } set { Value = value; } } public object AsObject { get { return Value; } set { if (value is string s) { Value = Encoding.UTF8.GetBytes(s); } else { Value = value; } } } public AssetTypeValue(bool value) { ValueType = AssetValueType.Bool; Value = value; } public AssetTypeValue(sbyte value) { ValueType = AssetValueType.Int8; Value = value; } public AssetTypeValue(byte value) { ValueType = AssetValueType.UInt8; Value = value; } public AssetTypeValue(short value) { ValueType = AssetValueType.Int16; Value = value; } public AssetTypeValue(ushort value) { ValueType = AssetValueType.UInt16; Value = value; } public AssetTypeValue(int value) { ValueType = AssetValueType.Int32; Value = value; } public AssetTypeValue(uint value) { ValueType = AssetValueType.UInt32; Value = value; } public AssetTypeValue(long value) { ValueType = AssetValueType.Int64; Value = value; } public AssetTypeValue(ulong value) { ValueType = AssetValueType.UInt64; Value = value; } public AssetTypeValue(float value) { ValueType = AssetValueType.Float; Value = value; } public AssetTypeValue(double value) { ValueType = AssetValueType.Double; Value = value; } public AssetTypeValue(string value) { ValueType = AssetValueType.String; Value = Encoding.UTF8.GetBytes(value); } public AssetTypeValue(byte[] value, bool asString) { ValueType = (asString ? AssetValueType.String : AssetValueType.ByteArray); Value = value; } public AssetTypeValue(ManagedReferencesRegistry value) { ValueType = AssetValueType.ManagedReferencesRegistry; Value = value; } public AssetTypeValue(AssetValueType valueType, object value = null) { ValueType = valueType; if (value is string s) { Value = Encoding.UTF8.GetBytes(s); } else { Value = value; } } public AssetTypeValue Clone() { object value = null; object value2 = Value; if (!(value2 is bool flag)) { if (!(value2 is sbyte b)) { if (!(value2 is byte b2)) { if (!(value2 is short num)) { if (!(value2 is ushort num2)) { if (!(value2 is int num3)) { if (!(value2 is uint num4)) { if (!(value2 is long num5)) { if (!(value2 is ulong num6)) { if (!(value2 is float num7)) { if (!(value2 is double num8)) { if (!(value2 is byte[] array)) { if (!(value2 is AssetTypeArrayInfo assetTypeArrayInfo)) { if (value2 is ManagedReferencesRegistry managedReferencesRegistry) { List list = new List(); for (int i = 0; i < managedReferencesRegistry.references.Count; i++) { AssetTypeReferencedObject assetTypeReferencedObject = managedReferencesRegistry.references[i]; list[i] = new AssetTypeReferencedObject { rid = assetTypeReferencedObject.rid, type = new AssetTypeReference { ClassName = assetTypeReferencedObject.type.ClassName, Namespace = assetTypeReferencedObject.type.Namespace, AsmName = assetTypeReferencedObject.type.AsmName }, data = assetTypeReferencedObject.data?.Clone() }; } value = new ManagedReferencesRegistry { version = managedReferencesRegistry.version, references = list }; } } else { value = assetTypeArrayInfo; } } else { value = (byte[])array.Clone(); } } else { value = num8; } } else { value = num7; } } else { value = num6; } } else { value = num5; } } else { value = num4; } } else { value = num3; } } else { value = num2; } } else { value = num; } } else { value = b2; } } else { value = b; } } else { value = flag; } return new AssetTypeValue(ValueType, value); } public override string ToString() { return AsString; } private string SimpleHexDump(byte[] byteArray) { StringBuilder stringBuilder = new StringBuilder(); if (byteArray.Length == 0) { return string.Empty; } int i; for (i = 0; i < byteArray.Length - 1; i++) { stringBuilder.Append(byteArray[i].ToString("x2")); stringBuilder.Append(" "); } stringBuilder.Append(byteArray[i].ToString("x2")); return stringBuilder.ToString(); } } public class AssetTypeValueField : IEnumerable, IEnumerable { public static readonly AssetTypeValueField DUMMY_FIELD = new AssetTypeValueField { TemplateField = new AssetTypeTemplateField { Name = "DUMMY", HasValue = false, IsAligned = false, IsArray = false, Type = "DUMMY", ValueType = AssetValueType.None, Children = new List(0) }, Value = null, IsDummy = true, Children = new List(0) }; public AssetTypeTemplateField TemplateField { get; set; } public AssetTypeValue Value { get; set; } public List Children { get; set; } public bool IsDummy { get; set; } public AssetTypeValueField this[string name] { get { if (IsDummy) { throw new DummyFieldAccessException("Cannot access fields of a dummy field!"); } if (name.Contains(".")) { string[] array = name.Split(new char[1] { '.' }); AssetTypeValueField assetTypeValueField = this; string[] array2 = array; foreach (string text in array2) { bool flag = false; foreach (AssetTypeValueField child in assetTypeValueField.Children) { if (child.TemplateField.Name == text) { flag = true; assetTypeValueField = child; break; } } if (!flag) { return DUMMY_FIELD; } } return assetTypeValueField; } foreach (AssetTypeValueField child2 in Children) { if (child2.TemplateField.Name == name) { return child2; } } return DUMMY_FIELD; } } public AssetTypeValueField this[int index] { get { if (IsDummy) { throw new DummyFieldAccessException("Cannot access fields of a dummy field!"); } return Children[index]; } } public bool AsBool { get { return Value.AsBool; } set { Value.AsBool = value; } } public sbyte AsSByte { get { return Value.AsSByte; } set { Value.AsSByte = value; } } public byte AsByte { get { return Value.AsByte; } set { Value.AsByte = value; } } public short AsShort { get { return Value.AsShort; } set { Value.AsShort = value; } } public ushort AsUShort { get { return Value.AsUShort; } set { Value.AsUShort = value; } } public int AsInt { get { return Value.AsInt; } set { Value.AsInt = value; } } public uint AsUInt { get { return Value.AsUInt; } set { Value.AsUInt = value; } } public long AsLong { get { return Value.AsLong; } set { Value.AsLong = value; } } public ulong AsULong { get { return Value.AsULong; } set { Value.AsULong = value; } } public float AsFloat { get { return Value.AsFloat; } set { Value.AsFloat = value; } } public double AsDouble { get { return Value.AsDouble; } set { Value.AsDouble = value; } } public string AsString { get { return Value.AsString; } set { Value.AsString = value; } } public object AsObject { get { return Value.AsObject; } set { Value.AsObject = value; } } public AssetTypeArrayInfo AsArray { get { return Value.AsArray; } set { Value.AsArray = value; } } public byte[] AsByteArray { get { return Value.AsByteArray; } set { Value.AsByteArray = value; } } public ManagedReferencesRegistry AsManagedReferencesRegistry { get { return Value.AsManagedReferencesRegistry; } set { Value.AsManagedReferencesRegistry = value; } } public string TypeName => TemplateField.Type; public string FieldName => TemplateField.Name; public void Read(AssetTypeValue value, AssetTypeTemplateField templateField, List children) { Value = value; TemplateField = templateField; Children = children; IsDummy = false; } public AssetTypeValueField Get(string name) { return this[name]; } public AssetTypeValueField Get(int index) { return this[index]; } public static AssetValueType GetValueTypeByTypeName(string type) { switch (type) { case "string": return AssetValueType.String; case "SInt8": case "char": return AssetValueType.Int8; case "UInt8": case "unsigned char": return AssetValueType.UInt8; case "SInt16": case "short": return AssetValueType.Int16; case "UInt16": case "unsigned short": return AssetValueType.UInt16; case "SInt32": case "Type*": case "int": return AssetValueType.Int32; case "UInt32": case "unsigned int": return AssetValueType.UInt32; case "SInt64": case "long": return AssetValueType.Int64; case "UInt64": case "unsigned long long": case "FileSize": return AssetValueType.UInt64; case "float": return AssetValueType.Float; case "double": return AssetValueType.Double; case "bool": return AssetValueType.Bool; case "Array": return AssetValueType.Array; case "TypelessData": return AssetValueType.ByteArray; case "ManagedReferencesRegistry": return AssetValueType.ManagedReferencesRegistry; default: return AssetValueType.None; } } public void Write(AssetsFileWriter writer) { if (TemplateField.IsArray) { if (TemplateField.ValueType == AssetValueType.ByteArray) { byte[] asByteArray = AsByteArray; writer.Write(asByteArray.Length); writer.Write(asByteArray); if (TemplateField.IsAligned) { writer.Align(); } return; } int count = Children.Count; writer.Write(count); for (int i = 0; i < count; i++) { this[i].Write(writer); } if (TemplateField.IsAligned) { writer.Align(); } } else if (Children.Count == 0) { switch (TemplateField.ValueType) { case AssetValueType.Int8: writer.Write(AsSByte); if (TemplateField.IsAligned) { writer.Align(); } break; case AssetValueType.UInt8: writer.Write(AsByte); if (TemplateField.IsAligned) { writer.Align(); } break; case AssetValueType.Bool: writer.Write(AsBool); if (TemplateField.IsAligned) { writer.Align(); } break; case AssetValueType.Int16: writer.Write(AsShort); if (TemplateField.IsAligned) { writer.Align(); } break; case AssetValueType.UInt16: writer.Write(AsUShort); if (TemplateField.IsAligned) { writer.Align(); } break; case AssetValueType.Int32: writer.Write(AsInt); break; case AssetValueType.UInt32: writer.Write(AsUInt); break; case AssetValueType.Int64: writer.Write(AsLong); break; case AssetValueType.UInt64: writer.Write(AsULong); break; case AssetValueType.Float: writer.Write(AsFloat); break; case AssetValueType.Double: writer.Write(AsDouble); break; case AssetValueType.String: writer.Write(AsByteArray.Length); writer.Write(AsByteArray); writer.Align(); break; case AssetValueType.ManagedReferencesRegistry: { writer.Write(AsManagedReferencesRegistry.version); int count2 = AsManagedReferencesRegistry.references.Count; if (AsManagedReferencesRegistry.version >= 2) { writer.Write(count2); } for (int j = 0; j < count2; j++) { AssetTypeReferencedObject assetTypeReferencedObject = AsManagedReferencesRegistry.references[j]; if (AsManagedReferencesRegistry.version >= 2) { writer.Write(assetTypeReferencedObject.rid); } assetTypeReferencedObject.type.WriteAsset(writer); assetTypeReferencedObject.data.Write(writer); } if (AsManagedReferencesRegistry.version == 1) { AssetTypeReference.TERMINUS.WriteAsset(writer); } break; } case AssetValueType.Array: case AssetValueType.ByteArray: break; } } else { for (int k = 0; k < Children.Count; k++) { this[k].Write(writer); } if (TemplateField.IsAligned) { writer.Align(); } } } public byte[] WriteToByteArray(bool bigEndian = false) { using MemoryStream memoryStream = new MemoryStream(); using AssetsFileWriter assetsFileWriter = new AssetsFileWriter(memoryStream); assetsFileWriter.BigEndian = bigEndian; Write(assetsFileWriter); return memoryStream.ToArray(); } public AssetTypeValueField Clone() { List list = new List(Children.Count); for (int i = 0; i < Children.Count; i++) { AssetTypeValueField assetTypeValueField = Children[i]; list.Add(assetTypeValueField.Clone()); } return new AssetTypeValueField { TemplateField = TemplateField, Value = Value?.Clone(), Children = list, IsDummy = IsDummy }; } public IEnumerator GetEnumerator() { return Children.GetEnumerator(); } IEnumerator IEnumerable.GetEnumerator() { return Children.GetEnumerator(); } public override string ToString() { if (TemplateField != null) { return TemplateField.ToString(); } return null; } } public class AssetTypeValueIterator { [CompilerGenerated] private sealed class d__20 : IEnumerator, IEnumerator, IDisposable { private int <>1__state; private AssetTypeTemplateField <>2__current; public AssetTypeTemplateField templateField; public int times; private int 5__2; AssetTypeTemplateField IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } object IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } [DebuggerHidden] public d__20(int <>1__state) { this.<>1__state = <>1__state; } [DebuggerHidden] void IDisposable.Dispose() { <>1__state = -2; } private bool MoveNext() { switch (<>1__state) { default: return false; case 0: <>1__state = -1; 5__2 = 0; break; case 1: <>1__state = -1; 5__2++; break; } if (5__2 < times) { <>2__current = templateField; <>1__state = 1; return true; } return false; } bool IEnumerator.MoveNext() { //ILSpy generated this explicit interface implementation from .override directive in MoveNext return this.MoveNext(); } [DebuggerHidden] void IEnumerator.Reset() { throw new NotSupportedException(); } } private AssetTypeTemplateField baseTempField; private AssetsFileReader reader; private RefTypeManager refMan; private readonly Stack> tempFieldStack; private AssetTypeValueField valueFieldCache; private readonly long basePosition; public AssetTypeTemplateField TempField; public List TempFieldStack { get { List list = new List(tempFieldStack.Count); foreach (IEnumerator item in tempFieldStack) { list.Add(item.Current); } return list; } } public int Depth { get { bool flag = TempField.ValueType switch { AssetValueType.None => true, AssetValueType.Bool => false, AssetValueType.Int8 => false, AssetValueType.UInt8 => false, AssetValueType.Int16 => false, AssetValueType.UInt16 => false, AssetValueType.Int32 => false, AssetValueType.UInt32 => false, AssetValueType.Int64 => false, AssetValueType.UInt64 => false, AssetValueType.Float => false, AssetValueType.Double => false, AssetValueType.String => false, AssetValueType.Array => true, AssetValueType.ByteArray => false, AssetValueType.ManagedReferencesRegistry => false, _ => throw new Exception("Invalid value type"), }; return tempFieldStack.Count - (flag ? 1 : 0); } } public int ReadPosition => (int)(reader.Position - basePosition); public AssetTypeValueField ReadValueField() { if (valueFieldCache == null) { valueFieldCache = new AssetTypeValueField { TemplateField = TempField }; if (TempField.IsArray && TempField.ValueType != AssetValueType.ByteArray) { reader.Position -= 4L; } TempField.ReadType(reader, valueFieldCache, refMan); if (TempField.Children.Count > 0 && TempField.ValueType != AssetValueType.String) { IEnumerator enumerator = tempFieldStack.Peek(); do { if (enumerator.Current != null && enumerator.Current.IsArray && enumerator.Current.ValueType != AssetValueType.ByteArray && enumerator.Current.IsAligned) { reader.Align(); } } while (enumerator.MoveNext()); } } return valueFieldCache; } public AssetTypeValueIterator(AssetTypeTemplateField templateField, AssetsFileReader reader, RefTypeManager refMan = null) { baseTempField = templateField; this.reader = reader; this.refMan = refMan; basePosition = reader.Position; tempFieldStack = new Stack>(); Reset(); } public AssetTypeValueIterator(AssetTypeTemplateField templateField, AssetsFileReader reader, long position, RefTypeManager refMan = null) : this(templateField, ReaderWithSetPos(reader, position), refMan) { } private static AssetsFileReader ReaderWithSetPos(AssetsFileReader reader, long position) { reader.Position = position; return reader; } public void Reset() { tempFieldStack.Clear(); tempFieldStack.Push(baseTempField.Children.GetEnumerator()); TempField = baseTempField; valueFieldCache = null; } public void Reset(AssetTypeTemplateField templateField, AssetsFileReader reader, RefTypeManager refMan = null) { baseTempField = templateField; this.reader = reader; this.refMan = refMan; tempFieldStack.Clear(); tempFieldStack.Push(baseTempField.Children.GetEnumerator()); TempField = baseTempField; valueFieldCache = null; } public bool ReadNext() { if (tempFieldStack.Count == 0) { return false; } if (valueFieldCache == null) { if (TempField.IsArray) { if (TempField.ValueType == AssetValueType.ByteArray) { int num = reader.ReadInt32(); reader.Position += num; if (TempField.IsAligned) { reader.Align(); } } } else if (TempField.ValueType != 0) { bool flag = TempField.IsAligned; switch (TempField.ValueType) { case AssetValueType.Bool: case AssetValueType.Int8: case AssetValueType.UInt8: reader.Position++; break; case AssetValueType.Int16: case AssetValueType.UInt16: reader.Position += 2L; break; case AssetValueType.Int32: case AssetValueType.UInt32: case AssetValueType.Float: reader.Position += 4L; break; case AssetValueType.Int64: case AssetValueType.UInt64: case AssetValueType.Double: reader.Position += 8L; break; case AssetValueType.String: { int num2 = reader.ReadInt32(); reader.Position += num2; flag = TempField.Children.Count <= 0 || TempField.Children[0].IsAligned; break; } case AssetValueType.ManagedReferencesRegistry: { AssetTypeValueField valueField = new AssetTypeValueField(); TempField.ReadManagedReferencesRegistryType(reader, valueField, refMan); break; } } if (flag) { reader.Align(); } } } IEnumerator enumerator = tempFieldStack.Peek(); while (true) { if (enumerator.Current != null && (!enumerator.Current.IsArray || enumerator.Current.ValueType != AssetValueType.ByteArray) && enumerator.Current.IsAligned) { reader.Align(); } if (enumerator.MoveNext()) { break; } tempFieldStack.Pop(); if (tempFieldStack.Count == 0) { return false; } enumerator = tempFieldStack.Peek(); } AssetTypeTemplateField current = enumerator.Current; if (current.IsArray && current.ValueType != AssetValueType.ByteArray) { int times = reader.ReadInt32(); tempFieldStack.Push(GetArrayEnumerator(current.Children[1], times)); } else if (current.ValueType == AssetValueType.None && current.Children.Count > 0) { tempFieldStack.Push(current.Children.GetEnumerator()); } TempField = current; valueFieldCache = null; return true; } [IteratorStateMachine(typeof(d__20))] private IEnumerator GetArrayEnumerator(AssetTypeTemplateField templateField, int times) { //yield-return decompiler failed: Unexpected instruction in Iterator.Dispose() return new d__20(0) { templateField = templateField, times = times }; } } public class DummyFieldAccessException : Exception { public DummyFieldAccessException(string message) : base(message) { } } public enum AssetValueType { None, Bool, Int8, UInt8, Int16, UInt16, Int32, UInt32, Int64, UInt64, Float, Double, String, Array, ByteArray, ManagedReferencesRegistry } public class ManagedReferencesRegistry { public int version; public List references; } public class RefTypeManager { private Dictionary typeTreeLookup; private Dictionary monoTemplateLookup; private IMonoBehaviourTemplateGenerator monoTemplateGenerator; private UnityVersion unityVersion; private bool isSharedMonoLookup; public RefTypeManager() { typeTreeLookup = new Dictionary(); } public void Clear() { typeTreeLookup.Clear(); if (!isSharedMonoLookup) { monoTemplateLookup.Clear(); } } public void FromTypeTree(AssetsFileMetadata metadata) { if (!metadata.TypeTreeEnabled || metadata.RefTypes == null) { return; } foreach (TypeTreeType refType in metadata.RefTypes) { if (refType.IsRefType) { AssetTypeTemplateField assetTypeTemplateField = new AssetTypeTemplateField(); assetTypeTemplateField.FromTypeTree(refType); RemoveRedundantRegistry(assetTypeTemplateField); typeTreeLookup[refType.TypeReference] = assetTypeTemplateField; } } } public void WithMonoTemplateGenerator(AssetsFileMetadata metadata, IMonoBehaviourTemplateGenerator monoTemplateGenerator, IDictionary monoTemplateFieldCache = null) { this.monoTemplateGenerator = monoTemplateGenerator; unityVersion = new UnityVersion(metadata.UnityVersion); monoTemplateLookup = ((monoTemplateFieldCache != null) ? new Dictionary(monoTemplateFieldCache) : new Dictionary()); isSharedMonoLookup = monoTemplateLookup != null; } public AssetTypeTemplateField GetTemplateField(AssetTypeReference type) { if (type == null || (string.IsNullOrEmpty(type.ClassName) && string.IsNullOrEmpty(type.Namespace) && string.IsNullOrEmpty(type.AsmName)) || type.Equals(AssetTypeReference.TERMINUS)) { return null; } if (typeTreeLookup.TryGetValue(type, out var value)) { return value; } if (monoTemplateGenerator != null) { if (monoTemplateLookup.TryGetValue(type, out value)) { return value; } value = new AssetTypeTemplateField { Name = "Base", Type = type.ClassName, ValueType = AssetValueType.None, IsArray = false, IsAligned = false, HasValue = false, Children = new List(0) }; value = monoTemplateGenerator.GetTemplateField(value, type.AsmName, type.Namespace, type.ClassName, unityVersion); if (value != null) { RemoveRedundantRegistry(value); monoTemplateLookup[type] = value; return value; } } return null; } private void RemoveRedundantRegistry(AssetTypeTemplateField templateField) { if (templateField.Children.Count != 0) { int index = templateField.Children.Count - 1; if (templateField[index].ValueType == AssetValueType.ManagedReferencesRegistry) { templateField.Children.RemoveAt(index); } } } } public class ClassDatabaseFile { public ClassDatabaseFileHeader Header { get; set; } public List Classes { get; set; } public ClassDatabaseStringTable StringTable { get; set; } public List CommonStringBufferIndices { get; set; } public void Read(AssetsFileReader reader) { if (Header == null) { ClassDatabaseFileHeader classDatabaseFileHeader2 = (Header = new ClassDatabaseFileHeader()); } Header.Read(reader); AssetsFileReader decompressedReader = GetDecompressedReader(reader); int num = decompressedReader.ReadInt32(); Classes = new List(num); for (int i = 0; i < num; i++) { ClassDatabaseType classDatabaseType = new ClassDatabaseType(); classDatabaseType.Read(decompressedReader); Classes.Add(classDatabaseType); } if (StringTable == null) { ClassDatabaseStringTable classDatabaseStringTable2 = (StringTable = new ClassDatabaseStringTable()); } StringTable.Read(decompressedReader); if (CommonStringBufferIndices == null) { List list2 = (CommonStringBufferIndices = new List()); } int num2 = decompressedReader.ReadInt32(); for (int j = 0; j < num2; j++) { CommonStringBufferIndices.Add(decompressedReader.ReadUInt16()); } } public void Write(AssetsFileWriter writer, ClassFileCompressionType compressionType) { Header.CompressionType = compressionType; MemoryStream memoryStream = new MemoryStream(); AssetsFileWriter assetsFileWriter = new AssetsFileWriter(memoryStream); assetsFileWriter.Write(Classes.Count); for (int i = 0; i < Classes.Count; i++) { Classes[i].Write(assetsFileWriter); } StringTable.Write(assetsFileWriter); assetsFileWriter.Write(CommonStringBufferIndices.Count); for (int j = 0; j < CommonStringBufferIndices.Count; j++) { assetsFileWriter.Write(CommonStringBufferIndices[j]); } using MemoryStream memoryStream2 = GetCompressedStream(memoryStream); Header.CompressedSize = (int)memoryStream2.Length; Header.DecompressedSize = (int)memoryStream.Length; Header.Write(writer); memoryStream2.CopyToCompat(writer.BaseStream, -1L); } private AssetsFileReader GetDecompressedReader(AssetsFileReader reader) { AssetsFileReader result = reader; if (Header.CompressionType != 0) { MemoryStream stream; if (Header.CompressionType == ClassFileCompressionType.Lz4) { byte[] buffer = new byte[Header.DecompressedSize]; using (MemoryStream input = new MemoryStream(reader.ReadBytes(Header.CompressedSize))) { Lz4DecoderStream lz4DecoderStream = new Lz4DecoderStream(input); lz4DecoderStream.Read(buffer, 0, Header.DecompressedSize); lz4DecoderStream.Dispose(); } stream = new MemoryStream(buffer); } else { if (Header.CompressionType != ClassFileCompressionType.Lzma) { throw new Exception($"Class database is using invalid compression type {Header.CompressionType}!"); } using MemoryStream newInStream = new MemoryStream(reader.ReadBytes(Header.CompressedSize)); stream = SevenZipHelper.StreamDecompress(newInStream); } result = new AssetsFileReader(stream); } return result; } private MemoryStream GetCompressedStream(MemoryStream inStream) { if (Header.CompressionType != 0) { if (Header.CompressionType == ClassFileCompressionType.Lz4) { return new MemoryStream(LZ4Codec.Encode32HC(inStream.ToArray(), 0, (int)inStream.Length)); } if (Header.CompressionType == ClassFileCompressionType.Lzma) { MemoryStream memoryStream = new MemoryStream(); SevenZipHelper.Compress(inStream, memoryStream); memoryStream.Position = 0L; return memoryStream; } throw new Exception($"Class database is using invalid compression type {Header.CompressionType}!"); } inStream.Position = 0L; return inStream; } public ClassDatabaseType FindAssetClassByID(int id) { if (id < 0) { id = 114; } foreach (ClassDatabaseType @class in Classes) { if (@class.ClassId == id) { return @class; } } return null; } public ClassDatabaseType FindAssetClassByName(string name) { foreach (ClassDatabaseType @class in Classes) { if (GetString(@class.Name) == name) { return @class; } } return null; } public string GetString(ushort index) { return StringTable.GetString(index); } } public class ClassDatabaseFileHeader { public string Magic { get; set; } public byte FileVersion { get; set; } public UnityVersion Version { get; set; } public ClassFileCompressionType CompressionType { get; set; } public int CompressedSize { get; set; } public int DecompressedSize { get; set; } public void Read(AssetsFileReader reader) { Magic = reader.ReadStringLength(4); if (Magic != "CLDB") { if (Magic == "cldb") { throw new NotSupportedException("Old cldb style class databases are no longer supported."); } throw new NotSupportedException("CLDB magic not found. Is this really a class database file?"); } FileVersion = reader.ReadByte(); if (FileVersion > 1) { throw new Exception($"Unsupported or invalid file version {FileVersion}."); } Version = UnityVersion.FromUInt64(reader.ReadUInt64()); CompressionType = (ClassFileCompressionType)reader.ReadByte(); CompressedSize = reader.ReadInt32(); DecompressedSize = reader.ReadInt32(); } public void Write(AssetsFileWriter writer) { writer.Write(Encoding.ASCII.GetBytes(Magic)); writer.Write(FileVersion); writer.Write(Version.ToUInt64()); writer.Write((byte)CompressionType); writer.Write(CompressedSize); writer.Write(DecompressedSize); } } public class ClassDatabaseStringTable { public List Strings { get; set; } public void Read(AssetsFileReader reader) { int num = reader.ReadInt32(); Strings = new List(num); for (int i = 0; i < num; i++) { Strings.Add(reader.ReadString()); } } public void Write(AssetsFileWriter writer) { writer.Write(Strings.Count); for (int i = 0; i < Strings.Count; i++) { writer.Write(Strings[i]); } } public ushort AddString(string str) { int num = Strings.IndexOf(str); if (num == -1) { num = Strings.Count; Strings.Add(str); } return (ushort)num; } public string GetString(ushort index) { return Strings[index]; } } public class ClassDatabaseType { public int ClassId { get; set; } public ushort Name { get; set; } public ushort BaseName { get; set; } public ClassFileTypeFlags Flags { get; set; } public ClassDatabaseTypeNode EditorRootNode { get; set; } public ClassDatabaseTypeNode ReleaseRootNode { get; set; } public void Read(AssetsFileReader reader) { ClassId = reader.ReadInt32(); Name = reader.ReadUInt16(); BaseName = reader.ReadUInt16(); Flags = (ClassFileTypeFlags)reader.ReadByte(); EditorRootNode = null; if (Net35Polyfill.HasFlag(Flags, ClassFileTypeFlags.HasEditorRootNode)) { EditorRootNode = new ClassDatabaseTypeNode(); EditorRootNode.Read(reader); } ReleaseRootNode = null; if (Net35Polyfill.HasFlag(Flags, ClassFileTypeFlags.HasReleaseRootNode)) { ReleaseRootNode = new ClassDatabaseTypeNode(); ReleaseRootNode.Read(reader); } } public void Write(AssetsFileWriter writer) { writer.Write(ClassId); writer.Write(Name); writer.Write(BaseName); writer.Write((byte)Flags); if (Net35Polyfill.HasFlag(Flags, ClassFileTypeFlags.HasEditorRootNode) && EditorRootNode != null) { EditorRootNode.Write(writer); } if (Net35Polyfill.HasFlag(Flags, ClassFileTypeFlags.HasReleaseRootNode) && ReleaseRootNode != null) { ReleaseRootNode.Write(writer); } } public ClassDatabaseTypeNode GetPreferredNode(bool preferEditor = false) { if (EditorRootNode != null && ReleaseRootNode != null) { if (!preferEditor) { return ReleaseRootNode; } return EditorRootNode; } if (EditorRootNode != null) { return EditorRootNode; } if (ReleaseRootNode != null) { return ReleaseRootNode; } return null; } } public class ClassDatabaseTypeNode { public ushort TypeName { get; set; } public ushort FieldName { get; set; } public int ByteSize { get; set; } public ushort Version { get; set; } public byte TypeFlags { get; set; } public uint MetaFlag { get; set; } public List Children { get; set; } public void Read(AssetsFileReader reader) { TypeName = reader.ReadUInt16(); FieldName = reader.ReadUInt16(); ByteSize = reader.ReadInt32(); Version = reader.ReadUInt16(); TypeFlags = reader.ReadByte(); MetaFlag = reader.ReadUInt32(); int num = reader.ReadUInt16(); Children = new List(num); for (int i = 0; i < num; i++) { ClassDatabaseTypeNode classDatabaseTypeNode = new ClassDatabaseTypeNode(); classDatabaseTypeNode.Read(reader); Children.Add(classDatabaseTypeNode); } } public void Write(AssetsFileWriter writer) { writer.Write(TypeName); writer.Write(FieldName); writer.Write(ByteSize); writer.Write(Version); writer.Write(TypeFlags); writer.Write(MetaFlag); writer.Write((ushort)Children.Count); for (int i = 0; i < Children.Count; i++) { Children[i].Write(writer); } } } public enum ClassFileCompressionType { Uncompressed, Lz4, Lzma } [Flags] public enum ClassFileTypeFlags : byte { None = 0, IsAbstract = 1, IsSealed = 2, IsEditorOnly = 4, IsReleaseOnly = 8, IsStripped = 0x10, Reserved = 0x20, HasEditorRootNode = 0x40, HasReleaseRootNode = 0x80 } public class ClassPackageFile { public ClassPackageHeader Header { get; set; } public ClassPackageTypeTree TpkTypeTree { get; set; } public void Read(AssetsFileReader reader) { Header = new ClassPackageHeader(); Header.Read(reader); AssetsFileReader assetsFileReader; if (Header.CompressionType == ClassFileCompressionType.Lz4) { byte[] buffer = new byte[Header.DecompressedSize]; using (MemoryStream input = new MemoryStream(reader.ReadBytes((int)Header.CompressedSize))) { Lz4DecoderStream lz4DecoderStream = new Lz4DecoderStream(input); lz4DecoderStream.Read(buffer, 0, (int)Header.DecompressedSize); lz4DecoderStream.Dispose(); } assetsFileReader = new AssetsFileReader(new MemoryStream(buffer)); assetsFileReader.Position = 0L; } else if (Header.CompressionType == ClassFileCompressionType.Lzma) { using MemoryStream newInStream = new MemoryStream(reader.ReadBytes((int)Header.CompressedSize)); assetsFileReader = new AssetsFileReader(SevenZipHelper.StreamDecompress(newInStream, (int)Header.DecompressedSize)); assetsFileReader.Position = 0L; } else { assetsFileReader = reader; } TpkTypeTree = new ClassPackageTypeTree(); TpkTypeTree.Read(assetsFileReader); } public void Read(string path) { Read(new AssetsFileReader(File.OpenRead(path))); } public void Write(AssetsFileWriter writer, ClassFileCompressionType compressionType) { Header.CompressionType = compressionType; MemoryStream memoryStream = new MemoryStream(); AssetsFileWriter writer2 = new AssetsFileWriter(memoryStream); TpkTypeTree.Write(writer2); if (Header.CompressionType == ClassFileCompressionType.Lz4) { byte[] array = LZ4Codec.Encode32HC(memoryStream.ToArray(), 0, (int)memoryStream.Length); Header.CompressedSize = (uint)array.Length; Header.DecompressedSize = (uint)memoryStream.Length; Header.Write(writer); writer.Write(array); } else if (Header.CompressionType == ClassFileCompressionType.Lzma) { MemoryStream memoryStream2 = new MemoryStream(); SevenZipHelper.Compress(memoryStream, memoryStream2); Header.CompressedSize = (uint)memoryStream2.Length; Header.DecompressedSize = (uint)memoryStream.Length; Header.Write(writer); memoryStream2.CopyToCompat(writer.BaseStream, -1L); } else { Header.CompressedSize = (uint)memoryStream.Length; Header.DecompressedSize = (uint)memoryStream.Length; Header.Write(writer); memoryStream.CopyToCompat(writer.BaseStream, -1L); } } public void Write(string path, ClassFileCompressionType compressionType) { Write(new AssetsFileWriter(File.OpenWrite(path)), compressionType); } public ClassDatabaseFile GetClassDatabase(string version) { return GetClassDatabase(new UnityVersion(version)); } public ClassDatabaseFile GetClassDatabase(UnityVersion version) { if (Header == null) { throw new Exception("Header not loaded! (Did you forget to call package.Read?)"); } ClassDatabaseFile classDatabaseFile = new ClassDatabaseFile(); classDatabaseFile.Header = new ClassDatabaseFileHeader { Magic = "CLDB", FileVersion = 1, Version = version, CompressionType = ClassFileCompressionType.Uncompressed, CompressedSize = 0, DecompressedSize = 0 }; classDatabaseFile.Classes = new List(); foreach (ClassPackageClassInfo item in TpkTypeTree.ClassInformation) { ClassPackageType typeForVersion = item.GetTypeForVersion(version); if (typeForVersion != null) { ClassDatabaseType classDatabaseType = new ClassDatabaseType { ClassId = item.ClassId, Name = typeForVersion.Name, BaseName = typeForVersion.BaseName, Flags = typeForVersion.Flags }; if (typeForVersion.EditorRootNode != ushort.MaxValue) { classDatabaseType.EditorRootNode = ConvertNodes(typeForVersion.EditorRootNode); } if (typeForVersion.ReleaseRootNode != ushort.MaxValue) { classDatabaseType.ReleaseRootNode = ConvertNodes(typeForVersion.ReleaseRootNode); } classDatabaseFile.Classes.Add(classDatabaseType); } } classDatabaseFile.StringTable = TpkTypeTree.StringTable; byte commonStringLengthForVersion = TpkTypeTree.CommonString.GetCommonStringLengthForVersion(version); classDatabaseFile.CommonStringBufferIndices = new List(commonStringLengthForVersion); for (int i = 0; i < commonStringLengthForVersion; i++) { classDatabaseFile.CommonStringBufferIndices.Add(TpkTypeTree.CommonString.StringBufferIndices[i]); } return classDatabaseFile; } private ClassDatabaseTypeNode ConvertNodes(ushort tpkNodeIdx) { ClassPackageTypeNode classPackageTypeNode = TpkTypeTree.Nodes[tpkNodeIdx]; ClassDatabaseTypeNode classDatabaseTypeNode = new ClassDatabaseTypeNode { TypeName = classPackageTypeNode.TypeName, FieldName = classPackageTypeNode.FieldName, ByteSize = classPackageTypeNode.ByteSize, Version = classPackageTypeNode.Version, TypeFlags = classPackageTypeNode.TypeFlags, MetaFlag = classPackageTypeNode.MetaFlag }; int num = classPackageTypeNode.SubNodes.Length; classDatabaseTypeNode.Children = new List(num); for (int i = 0; i < num; i++) { classDatabaseTypeNode.Children.Add(ConvertNodes(classPackageTypeNode.SubNodes[i])); } return classDatabaseTypeNode; } } public class ClassPackageClassInfo { public int ClassId { get; set; } public List> Classes { get; set; } public void Read(AssetsFileReader reader) { ClassId = reader.ReadInt32(); int num = reader.ReadInt32(); Classes = new List>(num); for (int i = 0; i < num; i++) { UnityVersion key = UnityVersion.FromUInt64(reader.ReadUInt64()); bool num2 = reader.ReadBoolean(); ClassPackageType classPackageType = null; if (num2) { classPackageType = new ClassPackageType(); classPackageType.Read(reader, ClassId); } Classes.Add(new KeyValuePair(key, classPackageType)); } } public void Write(AssetsFileWriter writer) { writer.Write(ClassId); writer.Write(Classes.Count); for (int i = 0; i < Classes.Count; i++) { writer.Write(Classes[i].Key.ToUInt64()); if (Classes[i].Value != null) { writer.Write((byte)1); Classes[i].Value.Write(writer); } else { writer.Write((byte)0); } } } public ClassPackageType GetTypeForVersion(UnityVersion version) { if (Classes.Count == 0) { return null; } if (Classes[0].Key.ToUInt64() > version.ToUInt64()) { return null; } ClassPackageType value = Classes[0].Value; for (int i = 0; i < Classes.Count; i++) { if (Classes[i].Key.ToUInt64() == version.ToUInt64()) { return Classes[i].Value; } if (Classes[i].Key.ToUInt64() > version.ToUInt64()) { return value; } value = Classes[i].Value; } return value; } } public class ClassPackageCommonString { public List> VersionInformation { get; set; } public List StringBufferIndices { get; set; } public void Read(AssetsFileReader reader) { int num = reader.ReadInt32(); VersionInformation = new List>(num); for (int i = 0; i < num; i++) { UnityVersion key = UnityVersion.FromUInt64(reader.ReadUInt64()); byte value = reader.ReadByte(); VersionInformation.Add(new KeyValuePair(key, value)); } int num2 = reader.ReadInt32(); StringBufferIndices = new List(num2); for (int j = 0; j < num2; j++) { StringBufferIndices.Add(reader.ReadUInt16()); } } public void Write(AssetsFileWriter writer) { writer.Write(VersionInformation.Count); foreach (KeyValuePair item in VersionInformation) { writer.Write(item.Key.ToUInt64()); writer.Write(item.Value); } writer.Write(StringBufferIndices.Count); for (int i = 0; i < StringBufferIndices.Count; i++) { writer.Write(StringBufferIndices[i]); } } public byte GetCommonStringLengthForVersion(UnityVersion version) { if (VersionInformation.Count == 0) { return 0; } byte value = VersionInformation[0].Value; for (int i = 0; i < VersionInformation.Count; i++) { if (VersionInformation[i].Key.ToUInt64() >= version.ToUInt64()) { return value; } value = VersionInformation[i].Value; } return value; } } public class ClassPackageHeader { public string Magic { get; set; } public byte FileVersion { get; set; } public ClassFileCompressionType CompressionType { get; set; } public byte DataType { get; set; } public uint CompressedSize { get; set; } public uint DecompressedSize { get; set; } public void Read(AssetsFileReader reader) { reader.BigEndian = false; Magic = reader.ReadStringLength(4); if (Magic != "TPK*") { if (Magic == "CLPK") { throw new NotSupportedException("Old CLPK style class packages are no longer supported."); } throw new NotSupportedException("TPK* magic not found. Is this really a tpk file?"); } FileVersion = reader.ReadByte(); if (FileVersion > 1) { throw new Exception($"Unsupported or invalid file version {FileVersion}."); } CompressionType = (ClassFileCompressionType)reader.ReadByte(); DataType = reader.ReadByte(); reader.ReadByte(); reader.ReadUInt32(); CompressedSize = reader.ReadUInt32(); DecompressedSize = reader.ReadUInt32(); } public void Write(AssetsFileWriter writer) { writer.BigEndian = false; writer.Write(Encoding.ASCII.GetBytes(Magic)); writer.Write(FileVersion); writer.Write((byte)CompressionType); writer.Write(DataType); writer.Write((byte)0); writer.Write(0); writer.Write(CompressedSize); writer.Write(DecompressedSize); } } public class ClassPackageType { public int ClassId { get; set; } public ushort Name { get; set; } public ushort BaseName { get; set; } public ClassFileTypeFlags Flags { get; set; } public ushort EditorRootNode { get; set; } public ushort ReleaseRootNode { get; set; } public void Read(AssetsFileReader reader, int classId) { ClassId = classId; Name = reader.ReadUInt16(); BaseName = reader.ReadUInt16(); Flags = (ClassFileTypeFlags)reader.ReadByte(); EditorRootNode = ushort.MaxValue; if (Net35Polyfill.HasFlag(Flags, ClassFileTypeFlags.HasEditorRootNode)) { EditorRootNode = reader.ReadUInt16(); } ReleaseRootNode = ushort.MaxValue; if (Net35Polyfill.HasFlag(Flags, ClassFileTypeFlags.HasReleaseRootNode)) { ReleaseRootNode = reader.ReadUInt16(); } } public void Write(AssetsFileWriter writer) { writer.Write(Name); writer.Write(BaseName); writer.Write((byte)Flags); if (Net35Polyfill.HasFlag(Flags, ClassFileTypeFlags.HasEditorRootNode)) { writer.Write(EditorRootNode); } if (Net35Polyfill.HasFlag(Flags, ClassFileTypeFlags.HasReleaseRootNode)) { writer.Write(ReleaseRootNode); } } } public class ClassPackageTypeNode { public ushort TypeName { get; set; } public ushort FieldName { get; set; } public int ByteSize { get; set; } public ushort Version { get; set; } public byte TypeFlags { get; set; } public uint MetaFlag { get; set; } public ushort[] SubNodes { get; set; } public void Read(AssetsFileReader reader) { TypeName = reader.ReadUInt16(); FieldName = reader.ReadUInt16(); ByteSize = reader.ReadInt32(); Version = reader.ReadUInt16(); TypeFlags = reader.ReadByte(); MetaFlag = reader.ReadUInt32(); ushort num = reader.ReadUInt16(); SubNodes = new ushort[num]; for (int i = 0; i < num; i++) { SubNodes[i] = reader.ReadUInt16(); } } public void Write(AssetsFileWriter writer) { writer.Write(TypeName); writer.Write(FieldName); writer.Write(ByteSize); writer.Write(Version); writer.Write(TypeFlags); writer.Write(MetaFlag); writer.Write(SubNodes.Length); for (int i = 0; i < SubNodes.Length; i++) { writer.Write(SubNodes[i]); } } } public class ClassPackageTypeTree { public DateTime CreationTime { get; set; } public List Versions { get; set; } public List ClassInformation { get; set; } public ClassPackageCommonString CommonString { get; set; } public List Nodes { get; set; } public ClassDatabaseStringTable StringTable { get; set; } public void Read(AssetsFileReader reader) { CreationTime = DateTime.FromBinary(reader.ReadInt64()); int num = reader.ReadInt32(); Versions = new List(num); for (int i = 0; i < num; i++) { Versions.Add(UnityVersion.FromUInt64(reader.ReadUInt64())); } int num2 = reader.ReadInt32(); ClassInformation = new List(); for (int j = 0; j < num2; j++) { ClassPackageClassInfo classPackageClassInfo = new ClassPackageClassInfo(); classPackageClassInfo.Read(reader); ClassInformation.Add(classPackageClassInfo); } CommonString = new ClassPackageCommonString(); CommonString.Read(reader); int num3 = reader.ReadInt32(); Nodes = new List(num3); for (int k = 0; k < num3; k++) { ClassPackageTypeNode classPackageTypeNode = new ClassPackageTypeNode(); classPackageTypeNode.Read(reader); Nodes.Add(classPackageTypeNode); } StringTable = new ClassDatabaseStringTable(); StringTable.Read(reader); } public void Write(AssetsFileWriter writer) { writer.Write(CreationTime.ToBinary()); writer.Write(Versions.Count); for (int i = 0; i < Versions.Count; i++) { writer.Write(Versions[i].ToUInt64()); } writer.Write(ClassInformation.Count); for (int j = 0; j < ClassInformation.Count; j++) { ClassInformation[j].Write(writer); } CommonString.Write(writer); writer.Write(Nodes.Count); for (int k = 0; k < Nodes.Count; k++) { Nodes[k].Write(writer); } StringTable.Write(writer); } } internal sealed class ConcurrentDictionary : IDictionary, ICollection>, IEnumerable>, IEnumerable { private readonly System.Collections.Concurrent.ConcurrentDictionary _dict = new System.Collections.Concurrent.ConcurrentDictionary(); public ICollection Keys => _dict.Keys; public ICollection Values => _dict.Values; public int Count => _dict.Count; public bool IsReadOnly => false; public TValue this[TKey key] { get { return _dict[key]; } set { _dict[key] = value; } } public bool ContainsKey(TKey key) { return _dict.ContainsKey(key); } public IEnumerator> GetEnumerator() { return _dict.GetEnumerator(); } IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); } public void Add(TKey key, TValue value) { ((IDictionary)_dict).Add(key, value); } public bool Remove(TKey key) { TValue value; return _dict.TryRemove(key, out value); } public bool TryRemove(TKey key, out TValue value) { return _dict.TryRemove(key, out value); } public bool TryGetValue(TKey key, out TValue value) { return _dict.TryGetValue(key, out value); } public void Add(KeyValuePair item) { Add(item.Key, item.Value); } public void Clear() { _dict.Clear(); } public bool Contains(KeyValuePair item) { return ((ICollection>)_dict).Contains(item); } public void CopyTo(KeyValuePair[] array, int arrayIndex) { ((ICollection>)_dict).CopyTo(array, arrayIndex); } public bool Remove(KeyValuePair item) { return ((ICollection>)_dict).Remove(item); } } public class AssetsFileReader : BinaryReader { public bool BigEndian { get; set; } public long Position { get { return BaseStream.Position; } set { BaseStream.Position = value; } } public AssetsFileReader(string filePath) : base(File.OpenRead(filePath)) { } public AssetsFileReader(Stream stream) : base(stream) { } public override short ReadInt16() { if (!BigEndian) { return base.ReadInt16(); } return (short)ReverseShort((ushort)base.ReadInt16()); } public override ushort ReadUInt16() { if (!BigEndian) { return base.ReadUInt16(); } return ReverseShort(base.ReadUInt16()); } public int ReadInt24() { if (!BigEndian) { return BitConverter.ToInt32(ReadBytes(3).Concat(new byte[1]).ToArray(), 0); } return (int)ReverseInt((uint)BitConverter.ToInt32(ReadBytes(3).Concat(new byte[1]).ToArray(), 0)); } public uint ReadUInt24() { if (!BigEndian) { return BitConverter.ToUInt32(ReadBytes(3).Concat(new byte[1]).ToArray(), 0); } return ReverseInt(BitConverter.ToUInt32(new byte[1].Concat(ReadBytes(3)).ToArray(), 0)); } public override int ReadInt32() { if (!BigEndian) { return base.ReadInt32(); } return (int)ReverseInt((uint)base.ReadInt32()); } public override uint ReadUInt32() { if (!BigEndian) { return base.ReadUInt32(); } return ReverseInt(base.ReadUInt32()); } public override long ReadInt64() { if (!BigEndian) { return base.ReadInt64(); } return (long)ReverseLong((ulong)base.ReadInt64()); } public override ulong ReadUInt64() { if (!BigEndian) { return base.ReadUInt64(); } return ReverseLong(base.ReadUInt64()); } public ushort ReverseShort(ushort value) { return (ushort)(((value & 0xFF00) >> 8) | ((value & 0xFF) << 8)); } public uint ReverseInt(uint value) { value = (value >> 16) | (value << 16); return ((value & 0xFF00FF00u) >> 8) | ((value & 0xFF00FF) << 8); } public ulong ReverseLong(ulong value) { value = (value >> 32) | (value << 32); value = ((value & 0xFFFF0000FFFF0000uL) >> 16) | ((value & 0xFFFF0000FFFFL) << 16); return ((value & 0xFF00FF00FF00FF00uL) >> 8) | ((value & 0xFF00FF00FF00FFL) << 8); } public void Align() { long num = 4 - BaseStream.Position % 4; if (num != 4) { BaseStream.Position += num; } } public void Align8() { long num = 8 - BaseStream.Position % 8; if (num != 8) { BaseStream.Position += num; } } public void Align16() { long num = 16 - BaseStream.Position % 16; if (num != 16) { BaseStream.Position += num; } } public string ReadStringLength(int len) { return Encoding.UTF8.GetString(ReadBytes(len)); } public string ReadNullTerminated() { MemoryStream memoryStream = new MemoryStream(); byte value; while ((value = ReadByte()) != 0) { memoryStream.WriteByte(value); } return Encoding.UTF8.GetString(memoryStream.ToArray()); } public static string ReadNullTerminatedArray(byte[] bytes, uint pos) { StringBuilder stringBuilder = new StringBuilder(); char value; while ((value = (char)bytes[pos]) != 0) { stringBuilder.Append(value); pos++; } return stringBuilder.ToString(); } public string ReadCountString() { byte len = ReadByte(); return ReadStringLength(len); } public string ReadCountStringInt16() { ushort len = ReadUInt16(); return ReadStringLength(len); } public string ReadCountStringInt32() { int len = ReadInt32(); return ReadStringLength(len); } } public class AssetsFileWriter : BinaryWriter { public bool BigEndian { get; set; } public long Position { get { return BaseStream.Position; } set { BaseStream.Position = value; } } public AssetsFileWriter(string filePath) : base(File.Open(filePath, FileMode.Create, FileAccess.Write)) { } public AssetsFileWriter(FileStream fileStream) : base(fileStream) { } public AssetsFileWriter(MemoryStream memoryStream) : base(memoryStream) { } public AssetsFileWriter(Stream stream) : base(stream) { } public override void Write(short val) { if (BigEndian) { base.Write((short)ReverseShort((ushort)val)); } else { base.Write(val); } } public override void Write(ushort val) { if (BigEndian) { base.Write(ReverseShort(val)); } else { base.Write(val); } } public override void Write(int val) { if (BigEndian) { base.Write((int)ReverseInt((uint)val)); } else { base.Write(val); } } public override void Write(uint val) { if (BigEndian) { base.Write(ReverseInt(val)); } else { base.Write(val); } } public override void Write(long val) { if (BigEndian) { base.Write((long)ReverseLong((ulong)val)); } else { base.Write(val); } } public override void Write(ulong val) { if (BigEndian) { base.Write(ReverseLong(val)); } else { base.Write(val); } } public void WriteRawString(string val) { base.Write(Encoding.UTF8.GetBytes(val)); } public void WriteUInt24(uint val) { if (BigEndian) { base.Write(BitConverter.GetBytes(ReverseInt(val)), 1, 3); } else { base.Write(BitConverter.GetBytes(val), 0, 3); } } public void WriteInt24(int val) { if (BigEndian) { base.Write(BitConverter.GetBytes((int)ReverseInt((uint)val)), 1, 3); } else { base.Write(BitConverter.GetBytes(val), 0, 3); } } public ushort ReverseShort(ushort value) { return (ushort)(((value & 0xFF00) >> 8) | ((value & 0xFF) << 8)); } public uint ReverseInt(uint value) { value = (value >> 16) | (value << 16); return ((value & 0xFF00FF00u) >> 8) | ((value & 0xFF00FF) << 8); } public ulong ReverseLong(ulong value) { value = (value >> 32) | (value << 32); value = ((value & 0xFFFF0000FFFF0000uL) >> 16) | ((value & 0xFFFF0000FFFFL) << 16); return ((value & 0xFF00FF00FF00FF00uL) >> 8) | ((value & 0xFF00FF00FF00FFL) << 8); } public void Align() { while (BaseStream.Position % 4 != 0L) { Write((byte)0); } } public void Align8() { while (BaseStream.Position % 8 != 0L) { Write((byte)0); } } public void Align16() { while (BaseStream.Position % 16 != 0L) { Write((byte)0); } } public void WriteNullTerminated(string text) { WriteRawString(text); Write((byte)0); } public void WriteCountString(string text) { if (Encoding.UTF8.GetByteCount(text) > 255) { new Exception("String is longer than 255! Use the Int32 variant instead!"); } Write((byte)Encoding.UTF8.GetByteCount(text)); WriteRawString(text); } public void WriteCountStringInt16(string text) { if (Encoding.UTF8.GetByteCount(text) > 65535) { new Exception("String is longer than 65535! Use the Int32 variant instead!"); } Write((ushort)Encoding.UTF8.GetByteCount(text)); WriteRawString(text); } public void WriteCountStringInt32(string text) { Write(Encoding.UTF8.GetByteCount(text)); WriteRawString(text); } } public class LZ4BlockStream : Stream { private readonly long length; private readonly long blockSize; private readonly AssetBundleBlockInfo[] blockInfos; private readonly long[] blockPoses; private readonly Dictionary decompressedBlockMap; private readonly Queue decompressedBlockQueue; public int maxBlockMapSize; public const int DEFAULT_MAX_BLOCK_MAP_SIZE = 23; public Stream BaseStream { get; } public long BaseOffset { get; } public override long Position { get; set; } public override long Length => length; public override bool CanRead => BaseStream.CanRead; public override bool CanSeek => BaseStream.CanSeek; public override bool CanWrite => BaseStream.CanWrite; public LZ4BlockStream(Stream baseStream, long baseOffset, AssetBundleBlockInfo[] blockInfos, int maxBlockMapSize = 23) { if (baseOffset < 0 || baseOffset > baseStream.Length) { throw new ArgumentOutOfRangeException("baseOffset"); } if (length >= 0 && baseOffset + length > baseStream.Length) { throw new ArgumentOutOfRangeException("length"); } decompressedBlockMap = new Dictionary(); decompressedBlockQueue = new Queue(); if (blockInfos.Length == 0) { length = 0L; blockSize = 131072L; BaseStream = new MemoryStream(); BaseOffset = baseOffset; this.blockInfos = new AssetBundleBlockInfo[0]; blockPoses = new long[0]; return; } long num = blockInfos[0].CompressedSize; blockPoses = new long[blockInfos.Length]; blockPoses[0] = 0L; blockSize = GetLz4BlockSize(blockInfos); length = blockInfos[0].DecompressedSize; for (int i = 1; i < blockInfos.Length; i++) { if (blockInfos[i].DecompressedSize != blockSize && i != blockInfos.Length - 1) { throw new NotImplementedException("Cannot handle bundles with multiple block sizes yet."); } length += blockInfos[i].DecompressedSize; blockPoses[i] = num; num += blockInfos[i].CompressedSize; } if (blockSize > int.MaxValue) { throw new NotImplementedException("Block size too large!"); } this.blockInfos = blockInfos; this.maxBlockMapSize = maxBlockMapSize; BaseStream = baseStream; BaseOffset = baseOffset; } public override void Flush() { BaseStream.Flush(); } public override int Read(byte[] buffer, int offset, int count) { int num = 0; while (num < count) { int num2 = (int)(Position / blockSize); MemoryStream memoryStream2; if (!decompressedBlockMap.ContainsKey(num2)) { if (decompressedBlockMap.Count >= maxBlockMapSize) { int key = decompressedBlockQueue.Dequeue(); decompressedBlockMap[key].Close(); decompressedBlockMap.Remove(key); } BaseStream.Position = BaseOffset + blockPoses[num2]; MemoryStream memoryStream = new MemoryStream(); BaseStream.CopyToCompat(memoryStream, blockInfos[num2].CompressedSize, (int)blockSize); memoryStream.Position = 0L; switch (blockInfos[num2].GetCompressionType()) { case 0: memoryStream2 = memoryStream; break; case 2: case 3: { byte[] array = new byte[blockInfos[num2].DecompressedSize]; using (Lz4DecoderStream lz4DecoderStream = new Lz4DecoderStream(memoryStream)) { lz4DecoderStream.Read(array, 0, array.Length); } memoryStream2 = new MemoryStream(array); break; } default: throw new Exception("Invalid block compression type in supposed LZ4 only stream!"); } decompressedBlockMap[num2] = memoryStream2; decompressedBlockQueue.Enqueue(num2); } else { memoryStream2 = decompressedBlockMap[num2]; } memoryStream2.Position = Position % blockSize; int num3 = memoryStream2.Read(buffer, offset + num, (int)Math.Min(memoryStream2.Length, count - num)); if (num3 == 0) { break; } num += num3; Position += num3; } return num; } public override long Seek(long offset, SeekOrigin origin) { long num = origin switch { SeekOrigin.Begin => offset, SeekOrigin.Current => Position + offset, SeekOrigin.End => Position + Length + offset, _ => throw new ArgumentException(), }; if (num < 0 || num > Length) { throw new ArgumentOutOfRangeException("offset"); } Position = num; return Position; } public override void SetLength(long value) { throw new NotSupportedException(); } public override void Write(byte[] buffer, int offset, int count) { throw new NotSupportedException("LZ4BlockStream cannot be written to, only read from."); } private long GetLz4BlockSize(AssetBundleBlockInfo[] blockInfos) { for (int i = 0; i < blockInfos.Length; i++) { if (blockInfos[i].GetCompressionType() == 2 || blockInfos[i].GetCompressionType() == 3) { return blockInfos[i].DecompressedSize; } } if (blockInfos[0].GetCompressionType() == 0) { return blockInfos[0].DecompressedSize; } throw new Exception("No LZ4 blocks were found in block infos. Can't find block size."); } } public class SegmentStream : Stream { private readonly long length; public Stream BaseStream { get; } public long BaseOffset { get; } public override long Position { get; set; } public override long Length { get { if (length < 0) { return BaseStream.Length - BaseOffset; } return length; } } public override bool CanRead => BaseStream.CanRead; public override bool CanSeek => BaseStream.CanSeek; public override bool CanWrite => BaseStream.CanWrite; public SegmentStream(Stream baseStream, long baseOffset) : this(baseStream, baseOffset, -1L) { } public SegmentStream(Stream baseStream, long baseOffset, long length) { if (baseOffset < 0 || baseOffset > baseStream.Length) { throw new ArgumentOutOfRangeException("baseOffset"); } if (length >= 0 && baseOffset + length > baseStream.Length) { throw new ArgumentOutOfRangeException("length"); } BaseStream = baseStream; BaseOffset = baseOffset; this.length = length; } public override void Flush() { BaseStream.Flush(); } public override int Read(byte[] buffer, int offset, int count) { BaseStream.Position = BaseOffset + Position; int count2 = (int)((length >= 0) ? Math.Min(count, length - Position) : count); count = BaseStream.Read(buffer, offset, count2); Position += count; return count; } public override long Seek(long offset, SeekOrigin origin) { long num = origin switch { SeekOrigin.Begin => offset, SeekOrigin.Current => Position + offset, SeekOrigin.End => Position + Length + offset, _ => throw new ArgumentException(), }; if (num < 0 || num > Length) { throw new ArgumentOutOfRangeException("offset"); } Position = num; return Position; } public override void SetLength(long value) { throw new NotSupportedException(); } public override void Write(byte[] buffer, int offset, int count) { if (length >= 0 && count > Length - Position) { throw new ArgumentOutOfRangeException("count"); } BaseStream.Position = BaseOffset + Position; BaseStream.Write(buffer, offset, count); Position += count; } } public class ContentRemover : IContentReplacer { public void Write(AssetsFileWriter writer, bool finalWrite) { } public bool HasPreview() { return false; } public Stream GetPreviewStream() { return null; } public ContentReplacerType GetReplacerType() { return ContentReplacerType.Remove; } public long GetSize() { return 0L; } } public class ContentReplacerFromAssets : IContentReplacer { public AssetsFile file; public ContentReplacerFromAssets(AssetsFile file) { this.file = file; } public ContentReplacerFromAssets(AssetsFileInstance inst) { file = inst.file; } public void Write(AssetsFileWriter writer, bool finalWrite) { AssetsFileWriter writer2 = new AssetsFileWriter(new SegmentStream(writer.BaseStream, writer.Position)); file.Write(writer2, -1L); writer.Position = writer.BaseStream.Length; } public bool HasPreview() { return false; } public Stream GetPreviewStream() { return null; } public ContentReplacerType GetReplacerType() { return ContentReplacerType.AddOrModify; } public long GetSize() { long num = 0L; num += file.Header.GetSize(); num += file.Metadata.GetSize(file.Header.Version); num = ((num >= 4096) ? (num + 16) : 4096); foreach (AssetFileInfo assetInfo in file.Metadata.AssetInfos) { if (assetInfo.Replacer != null) { if (assetInfo.Replacer.HasPreview()) { num += assetInfo.Replacer.GetPreviewStream().Length; } else { using AssetsFileWriter assetsFileWriter = new AssetsFileWriter(new MemoryStream()); assetInfo.Replacer.Write(assetsFileWriter, finalWrite: false); num += assetsFileWriter.BaseStream.Length; } } else { num += assetInfo.ByteSize; } num = (num + 7) & -8; } return 0L; } } public class ContentReplacerFromBuffer : IContentReplacer { private readonly byte[] buffer; private MemoryStream previewStream; public ContentReplacerFromBuffer(byte[] buffer) { this.buffer = buffer; } public void Write(AssetsFileWriter writer, bool finalWrite) { writer.Write(buffer); } public bool HasPreview() { return true; } public Stream GetPreviewStream() { if (previewStream == null) { previewStream = new MemoryStream(buffer); } previewStream.Position = 0L; return previewStream; } public ContentReplacerType GetReplacerType() { return ContentReplacerType.AddOrModify; } public long GetSize() { return buffer.Length; } } public class ContentReplacerFromStream : IContentReplacer { private readonly Stream stream; private readonly long offset; private readonly long length; private readonly bool closeOnWrite; public ContentReplacerFromStream(Stream stream, long offset = 0L, int length = -1, bool closeOnWrite = false) { this.stream = stream; this.offset = offset; this.closeOnWrite = closeOnWrite; if (!stream.CanSeek) { throw new NotSupportedException("Stream needs to be seekable."); } if (length == -1) { long num = ((offset == -1) ? stream.Position : offset); this.length = stream.Length - num; } else { this.length = length; } } public void Write(AssetsFileWriter writer, bool finalWrite) { if (offset != -1) { stream.Position = offset; } stream.CopyToCompat(writer.BaseStream, length); if (closeOnWrite && finalWrite) { stream.Close(); } } public bool HasPreview() { return true; } public Stream GetPreviewStream() { if (offset == 0L && length == -1) { return stream; } return new SegmentStream(stream, offset, length); } public ContentReplacerType GetReplacerType() { return ContentReplacerType.AddOrModify; } public long GetSize() { return length; } } public enum ContentReplacerType { None, AddOrModify, Remove } public interface IContentReplacer { void Write(AssetsFileWriter writer, bool finalWrite); bool HasPreview(); Stream GetPreviewStream(); ContentReplacerType GetReplacerType(); long GetSize(); } } namespace AssetsTools.NET.Extra { public enum AssetClassID { Object = 0, GameObject = 1, Component = 2, LevelGameManager = 3, Transform = 4, TimeManager = 5, GlobalGameManager = 6, Behaviour = 8, GameManager = 9, AudioManager = 11, ParticleAnimator = 12, InputManager = 13, EllipsoidParticleEmitter = 15, Pipeline = 17, EditorExtension = 18, Physics2DSettings = 19, Camera = 20, Material = 21, MeshRenderer = 23, Renderer = 25, ParticleRenderer = 26, Texture = 27, Texture2D = 28, OcclusionCullingSettings = 29, GraphicsSettings = 30, MeshFilter = 33, OcclusionPortal = 41, Mesh = 43, Skybox = 45, QualitySettings = 47, Shader = 48, TextAsset = 49, Rigidbody2D = 50, Physics2DManager = 51, NotificationManager = 52, Collider2D = 53, Rigidbody = 54, PhysicsManager = 55, Collider = 56, Joint = 57, CircleCollider2D = 58, HingeJoint = 59, PolygonCollider2D = 60, BoxCollider2D = 61, PhysicsMaterial2D = 62, MeshCollider = 64, BoxCollider = 65, CompositeCollider2D = 66, EdgeCollider2D = 68, PolygonColliderBase2D = 69, CapsuleCollider2D = 70, AnimationManager = 71, ComputeShader = 72, AnimationClip = 74, ConstantForce = 75, WorldParticleCollider = 76, TagManager = 78, AudioListener = 81, AudioSource = 82, AudioClip = 83, RenderTexture = 84, CustomRenderTexture = 86, MeshParticleEmitter = 87, ParticleEmitter = 88, Cubemap = 89, Avatar = 90, AnimatorController = 91, GUILayer = 92, RuntimeAnimatorController = 93, ScriptMapper = 94, Animator = 95, TrailRenderer = 96, DelayedCallManager = 98, TextMesh = 102, RenderSettings = 104, Light = 108, CGProgram = 109, BaseAnimationTrack = 110, Animation = 111, MonoBehaviour = 114, MonoScript = 115, MonoManager = 116, Texture3D = 117, NewAnimationTrack = 118, Projector = 119, LineRenderer = 120, Flare = 121, Halo = 122, LensFlare = 123, FlareLayer = 124, HaloLayer = 125, NavMeshProjectSettings = 126, HaloManager = 127, Font = 128, PlayerSettings = 129, NamedObject = 130, GUITexture = 131, GUIText = 132, GUIElement = 133, PhysicMaterial = 134, SphereCollider = 135, CapsuleCollider = 136, SkinnedMeshRenderer = 137, FixedJoint = 138, RaycastCollider = 140, BuildSettings = 141, AssetBundle = 142, CharacterController = 143, CharacterJoint = 144, SpringJoint = 145, WheelCollider = 146, ResourceManager = 147, NetworkView = 148, NetworkManager = 149, PreloadData = 150, MovieTexture = 152, ConfigurableJoint = 153, TerrainCollider = 154, MasterServerInterface = 155, TerrainData = 156, LightmapSettings = 157, WebCamTexture = 158, EditorSettings = 159, InteractiveCloth = 160, ClothRenderer = 161, EditorUserSettings = 162, SkinnedCloth = 163, AudioReverbFilter = 164, AudioHighPassFilter = 165, AudioChorusFilter = 166, AudioReverbZone = 167, AudioEchoFilter = 168, AudioLowPassFilter = 169, AudioDistortionFilter = 170, SparseTexture = 171, AudioBehaviour = 180, AudioFilter = 181, WindZone = 182, Cloth = 183, SubstanceArchive = 184, ProceduralMaterial = 185, ProceduralTexture = 186, Texture2DArray = 187, CubemapArray = 188, OffMeshLink = 191, OcclusionArea = 192, Tree = 193, NavMeshObsolete = 194, NavMeshAgent = 195, NavMeshSettings = 196, LightProbesLegacy = 197, ParticleSystem = 198, ParticleSystemRenderer = 199, ShaderVariantCollection = 200, LODGroup = 205, BlendTree = 206, Motion = 207, NavMeshObstacle = 208, SortingGroup = 210, SpriteRenderer = 212, Sprite = 213, CachedSpriteAtlas = 214, ReflectionProbe = 215, ReflectionProbes = 216, Terrain = 218, LightProbeGroup = 220, AnimatorOverrideController = 221, CanvasRenderer = 222, Canvas = 223, RectTransform = 224, CanvasGroup = 225, BillboardAsset = 226, BillboardRenderer = 227, SpeedTreeWindAsset = 228, AnchoredJoint2D = 229, Joint2D = 230, SpringJoint2D = 231, DistanceJoint2D = 232, HingeJoint2D = 233, SliderJoint2D = 234, WheelJoint2D = 235, ClusterInputManager = 236, BaseVideoTexture = 237, NavMeshData = 238, AudioMixer = 240, AudioMixerController = 241, AudioMixerGroupController = 243, AudioMixerEffectController = 244, AudioMixerSnapshotController = 245, PhysicsUpdateBehaviour2D = 246, ConstantForce2D = 247, Effector2D = 248, AreaEffector2D = 249, PointEffector2D = 250, PlatformEffector2D = 251, SurfaceEffector2D = 252, BuoyancyEffector2D = 253, RelativeJoint2D = 254, FixedJoint2D = 255, FrictionJoint2D = 256, TargetJoint2D = 257, LightProbes = 258, LightProbeProxyVolume = 259, SampleClip = 271, AudioMixerSnapshot = 272, AudioMixerGroup = 273, NScreenBridge = 280, AssetBundleManifest = 290, UnityAdsManager = 292, RuntimeInitializeOnLoadManager = 300, CloudWebServicesManager = 301, CloudServiceHandlerBehaviour = 302, UnityAnalyticsManager = 303, CrashReportManager = 304, PerformanceReportingManager = 305, UnityConnectSettings = 310, AvatarMask = 319, PlayableDirector = 320, VideoPlayer = 328, VideoClip = 329, ParticleSystemForceField = 330, SpriteMask = 331, WorldAnchor = 362, OcclusionCullingData = 363, SmallestEditorClassID = 1000, PrefabInstance = 1001, EditorExtensionImpl = 1002, AssetImporter = 1003, AssetDatabaseV1 = 1004, Mesh3DSImporter = 1005, TextureImporter = 1006, ShaderImporter = 1007, ComputeShaderImporter = 1008, AudioImporter = 1020, HierarchyState = 1026, GUIDSerializer = 1027, AssetMetaData = 1028, DefaultAsset = 1029, DefaultImporter = 1030, TextScriptImporter = 1031, SceneAsset = 1032, NativeFormatImporter = 1034, MonoImporter = 1035, AssetServerCache = 1037, LibraryAssetImporter = 1038, ModelImporter = 1040, FBXImporter = 1041, TrueTypeFontImporter = 1042, MovieImporter = 1044, EditorBuildSettings = 1045, DDSImporter = 1046, InspectorExpandedState = 1048, AnnotationManager = 1049, PluginImporter = 1050, EditorUserBuildSettings = 1051, PVRImporter = 1052, ASTCImporter = 1053, KTXImporter = 1054, IHVImageFormatImporter = 1055, AnimatorStateTransition = 1101, AnimatorState = 1102, HumanTemplate = 1105, AnimatorStateMachine = 1107, PreviewAnimationClip = 1108, AnimatorTransition = 1109, SpeedTreeImporter = 1110, AnimatorTransitionBase = 1111, SubstanceImporter = 1112, LightmapParameters = 1113, LightingDataAsset = 1120, GISRaster = 1121, GISRasterImporter = 1122, CadImporter = 1123, SketchUpImporter = 1124, BuildReport = 1125, PackedAssets = 1126, VideoClipImporter = 1127, ActivationLogComponent = 2000, @int = 100000, @bool = 100001, @float = 100002, MonoObject = 100003, Collision = 100004, Vector3f = 100005, RootMotionData = 100006, Collision2D = 100007, AudioMixerLiveUpdateFloat = 100008, AudioMixerLiveUpdateBool = 100009, Polygon2D = 100010, @void = 100011, TilemapCollider2D = 19719996, AssetImporterLog = 41386430, VFXRenderer = 73398921, SerializableManagedRefTestClass = 76251197, Grid = 156049354, ScenesUsingAssets = 156483287, ArticulationBody = 171741748, Preset = 181963792, EmptyObject = 277625683, IConstraint = 285090594, TestObjectWithSpecialLayoutOne = 293259124, AssemblyDefinitionReferenceImporter = 294290339, SiblingDerived = 334799969, TestObjectWithSerializedMapStringNonAlignedStruct = 342846651, SubDerived = 367388927, AssetImportInProgressProxy = 369655926, PluginBuildInfo = 382020655, EditorProjectAccess = 426301858, PrefabImporter = 468431735, TestObjectWithSerializedArray = 478637458, TestObjectWithSerializedAnimationCurve = 478637459, TilemapRenderer = 483693784, ScriptableCamera = 488575907, SpriteAtlasAsset = 612988286, SpriteAtlasDatabase = 638013454, AudioBuildInfo = 641289076, CachedSpriteAtlasRuntimeData = 644342135, RendererFake = 646504946, AssemblyDefinitionReferenceAsset = 662584278, BuiltAssetBundleInfoSet = 668709126, SpriteAtlas = 687078895, RayTracingShaderImporter = 747330370, PreviewImporter = 815301076, RayTracingShader = 825902497, LightingSettings = 850595691, PlatformModuleSetup = 877146078, VersionControlSettings = 890905787, AimConstraint = 895512359, VFXManager = 937362698, VisualEffectSubgraph = 994735392, RuleSetFileAsset = 954905827, VisualEffectSubgraphOperator = 994735403, VisualEffectSubgraphBlock = 994735404, Prefab = 1001480554, LocalizationImporter = 1027052791, Derived = 1091556383, PropertyModificationsTargetTestObject = 1111377672, ReferencesArtifactGenerator = 1114811875, AssemblyDefinitionAsset = 1152215463, SceneVisibilityState = 1154873562, LookAtConstraint = 1183024399, SpriteAtlasImporter = 1210832254, MultiArtifactTestImporter = 1223240404, GameObjectRecorder = 1268269756, LightingDataAssetParent = 1325145578, PresetManager = 1386491679, TestObjectWithSpecialLayoutTwo = 1392443030, StreamingManager = 1403656975, LowerResBlitTexture = 1480428607, StreamingController = 1542919678, RenderPassAttachment = 1571458007, TestObjectVectorPairStringBool = 1628831178, GridLayout = 1742807556, AssemblyDefinitionImporter = 1766753193, ParentConstraint = 1773428102, FakeComponent = 1803986026, RuleSetFileImporter = 1777034230, PositionConstraint = 1818360608, RotationConstraint = 1818360609, ScaleConstraint = 1818360610, Tilemap = 1839735485, PackageManifest = 1896753125, PackageManifestImporter = 1896753126, TerrainLayer = 1953259897, SpriteShapeRenderer = 1971053207, NativeObjectType = 1977754360, TestObjectWithSerializedMapStringBool = 1981279845, SerializableManagedHost = 1995898324, VisualEffectAsset = 2058629509, VisualEffectImporter = 2058629510, VisualEffectResource = 2058629511, VisualEffectObject = 2059678085, VisualEffect = 2083052967, LocalizationAsset = 2083778819, ScriptedImporter = 2089858483, TilemapEditorUserSettings = 2126867596 } public static class AssetHelper { public static Dictionary GetAssetsFileScriptInfos(AssetsManager am, AssetsFileInstance inst) { Dictionary dictionary = new Dictionary(); List scriptTypes = inst.file.Metadata.ScriptTypes; for (int i = 0; i < scriptTypes.Count; i++) { AssetTypeReference assetsFileScriptInfo = GetAssetsFileScriptInfo(am, inst, i); if (assetsFileScriptInfo != null) { dictionary[i] = assetsFileScriptInfo; } } return dictionary; } public static AssetTypeReference GetAssetsFileScriptInfo(AssetsManager am, AssetsFileInstance inst, int index) { AssetPPtr assetPPtr = inst.file.Metadata.ScriptTypes[index]; AssetTypeValueField baseField; try { baseField = am.GetExtAsset(inst, assetPPtr.FileId, assetPPtr.PathId).baseField; if (baseField == null) { return null; } } catch { return null; } AssetTypeValueField assetTypeValueField = baseField["m_AssemblyName"]; AssetTypeValueField assetTypeValueField2 = baseField["m_Namespace"]; AssetTypeValueField assetTypeValueField3 = baseField["m_ClassName"]; if (assetTypeValueField.IsDummy || assetTypeValueField2.IsDummy || assetTypeValueField3.IsDummy) { return null; } string asString = assetTypeValueField.AsString; string asString2 = assetTypeValueField2.AsString; return new AssetTypeReference(assetTypeValueField3.AsString, asString2, asString); } public static string GetAssetNameFast(AssetsFile file, ClassDatabaseFile cldb, AssetFileInfo info) { ClassDatabaseType classDatabaseType = cldb.FindAssetClassByID(info.TypeId); AssetsFileReader reader = file.Reader; if (file.Metadata.TypeTreeEnabled) { ushort scriptIndex = info.GetScriptIndex(file); TypeTreeType typeTreeType = file.Metadata.FindTypeTreeTypeByID(info.TypeId, scriptIndex); string typeString = typeTreeType.Nodes[0].GetTypeString(typeTreeType.StringBufferBytes); if (typeTreeType.Nodes.Count == 0) { return cldb.GetString(classDatabaseType.Name); } if (typeTreeType.Nodes.Count > 1 && typeTreeType.Nodes[1].GetNameString(typeTreeType.StringBufferBytes) == "m_Name") { reader.Position = info.GetAbsoluteByteOffset(file); return reader.ReadCountStringInt32(); } if (typeString == "GameObject") { reader.Position = info.GetAbsoluteByteOffset(file); int num = reader.ReadInt32(); int num2 = ((file.Header.Version > 16) ? 12 : 16); reader.Position += num * num2; reader.Position += 4L; return reader.ReadCountStringInt32(); } if (typeString == "MonoBehaviour") { reader.Position = info.GetAbsoluteByteOffset(file); reader.Position += 28L; string text = reader.ReadCountStringInt32(); if (text != "") { return text; } } return typeString; } string @string = cldb.GetString(classDatabaseType.Name); if (classDatabaseType.ReleaseRootNode.Children.Count == 0) { return @string; } if (classDatabaseType.ReleaseRootNode.Children.Count > 1 && cldb.GetString(classDatabaseType.ReleaseRootNode.Children[0].FieldName) == "m_Name") { reader.Position = info.GetAbsoluteByteOffset(file); return reader.ReadCountStringInt32(); } if (@string == "GameObject") { reader.Position = info.GetAbsoluteByteOffset(file); int num3 = reader.ReadInt32(); int num4 = ((file.Header.Version > 16) ? 12 : 16); reader.Position += num3 * num4; reader.Position += 4L; return reader.ReadCountStringInt32(); } if (@string == "MonoBehaviour") { reader.Position = info.GetAbsoluteByteOffset(file); reader.Position += 28L; string text2 = reader.ReadCountStringInt32(); if (text2 != "") { return text2; } } return @string; } public static long GuidToPathId(string guid, int assetType, long internalId) { using MemoryStream memoryStream = new MemoryStream(); AssetsFileWriter assetsFileWriter = new AssetsFileWriter(memoryStream); assetsFileWriter.WriteRawString(guid); assetsFileWriter.Write(assetType); assetsFileWriter.Write(internalId); return BitConverter.ToInt64(new MD4().GetByteHashFromBytes(memoryStream.ToArray()), 0); } } [Obsolete("use AssetPPtr")] public class AssetID { public string fileName; public long pathID; public AssetID(string fileName, long pathID) { this.fileName = fileName; this.pathID = pathID; } public override bool Equals(object obj) { if (!(obj is AssetID)) { return false; } AssetID assetID = (AssetID)obj; if (assetID.fileName == fileName) { return assetID.pathID == pathID; } return false; } public override int GetHashCode() { return (17 * 23 + fileName.GetHashCode()) * 23 + pathID.GetHashCode(); } } [Flags] public enum AssetReadFlags { None = 0, PreferEditor = 1, SkipMonoBehaviourFields = 2, ForceFromCldb = 4 } public class AssetsFileInstance { public string path; public string name; public AssetsFile file; public BundleFileInstance parentBundle; internal ConcurrentDictionary dependencyCache; public Stream AssetsStream => file.Reader.BaseStream; public AssetsFileReader LockReader { get { if (parentBundle == null) { return file.Reader; } return parentBundle.file.DataReader; } } public AssetsFileInstance(AssetsFile file, string filePath) { path = Path.GetFullPath(filePath); name = Path.GetFileName(path); this.file = file; dependencyCache = new ConcurrentDictionary(); } public AssetsFileInstance(Stream stream, string filePath) { path = Path.GetFullPath(filePath); name = Path.GetFileName(path); file = new AssetsFile(); file.Read(new AssetsFileReader(stream)); dependencyCache = new ConcurrentDictionary(); } public AssetsFileInstance(FileStream stream) { path = stream.Name; name = Path.GetFileName(path); file = new AssetsFile(); file.Read(new AssetsFileReader(stream)); dependencyCache = new ConcurrentDictionary(); } public AssetsFileInstance GetDependency(AssetsManager am, int depIdx) { if (!dependencyCache.ContainsKey(depIdx) || dependencyCache[depIdx] == null) { if (depIdx >= file.Metadata.Externals.Count) { return null; } string depPath = file.Metadata.Externals[depIdx].PathName; if (depPath == string.Empty) { return null; } if (depPath.StartsWith("archive:/")) { depPath = depPath.Substring(depPath.IndexOf('/', "archive:/".Length) + 1); } if (!am.FileLookup.TryGetValue(AssetsManager.GetFileLookupKey(depPath), out var value)) { string directoryName = Path.GetDirectoryName(path); string text = Path.Combine(directoryName, depPath); string text2 = Path.Combine(directoryName, Path.GetFileName(depPath)); if (File.Exists(text)) { dependencyCache[depIdx] = am.LoadAssetsFile(text, loadDeps: true); } else if (File.Exists(text2)) { dependencyCache[depIdx] = am.LoadAssetsFile(text2, loadDeps: true); } else { if (parentBundle == null) { return null; } if (parentBundle.file.BlockAndDirInfo.DirectoryInfos.Any((AssetBundleDirectoryInfo di) => di.Name == depPath)) { dependencyCache[depIdx] = am.LoadAssetsFileFromBundle(parentBundle, depPath, loadDeps: true); } else { string text3 = Path.Combine(Path.GetDirectoryName(directoryName), Path.GetFileName(depPath)); if (!File.Exists(text3)) { return null; } dependencyCache[depIdx] = am.LoadAssetsFile(text3, loadDeps: true); } } } else { dependencyCache[depIdx] = value; } } return dependencyCache[depIdx]; } } public class AssetsManager { private IMonoBehaviourTemplateGenerator _monoTempGenerator; private readonly ConcurrentDictionary templateFieldCache = new ConcurrentDictionary(); private readonly ConcurrentDictionary monoTemplateFieldCache = new ConcurrentDictionary(); private readonly ConcurrentDictionary> monoTypeTreeTemplateFieldCache = new ConcurrentDictionary>(); private readonly ConcurrentDictionary> monoCldbTemplateFieldCache = new ConcurrentDictionary>(); private readonly ConcurrentDictionary refTypeManagerCache = new ConcurrentDictionary(); public bool UseTemplateFieldCache { get; set; } public bool UseMonoTemplateFieldCache { get; set; } public bool UseRefTypeManagerCache { get; set; } public bool UseQuickLookup { get; set; } public ClassDatabaseFile ClassDatabase { get; private set; } public ClassPackageFile ClassPackage { get; private set; } public List Files { get; private set; } = new List(); public Dictionary FileLookup { get; private set; } = new Dictionary(); public List Bundles { get; private set; } = new List(); public Dictionary BundleLookup { get; private set; } = new Dictionary(); public IMonoBehaviourTemplateGenerator MonoTempGenerator { get { return _monoTempGenerator; } set { _monoTempGenerator = value; foreach (KeyValuePair item in refTypeManagerCache) { AssetsFileInstance key = item.Key; item.Value.WithMonoTemplateGenerator(key.file.Metadata, _monoTempGenerator, UseMonoTemplateFieldCache ? monoTemplateFieldCache : null); } } } public static string GetFileLookupKey(string path) { return Path.GetFileName(path).ToLower(); } private void LoadAssetsFileDependencies(AssetsFileInstance fileInst, string path, BundleFileInstance bunInst) { if (bunInst == null) { LoadDependencies(fileInst); } else { LoadBundleDependencies(fileInst, bunInst, Path.GetDirectoryName(path)); } } private AssetsFileInstance LoadAssetsFileCacheless(AssetsFile file, string path, bool loadDeps, BundleFileInstance bunInst = null) { AssetsFileInstance assetsFileInstance = new AssetsFileInstance(file, path); assetsFileInstance.parentBundle = bunInst; return LoadAssetsFileCacheless(assetsFileInstance, path, loadDeps, bunInst); } private AssetsFileInstance LoadAssetsFileCacheless(Stream stream, string path, bool loadDeps, BundleFileInstance bunInst = null) { AssetsFileInstance assetsFileInstance = new AssetsFileInstance(stream, path); assetsFileInstance.parentBundle = bunInst; return LoadAssetsFileCacheless(assetsFileInstance, path, loadDeps, bunInst); } private AssetsFileInstance LoadAssetsFileCacheless(AssetsFileInstance fileInst, string path, bool loadDeps, BundleFileInstance bunInst = null) { string fileLookupKey = GetFileLookupKey(path); lock (FileLookup) { lock (Files) { FileLookup[fileLookupKey] = fileInst; Files.Add(fileInst); } } if (loadDeps) { LoadAssetsFileDependencies(fileInst, path, bunInst); } if (UseQuickLookup) { fileInst.file.GenerateQuickLookup(); } return fileInst; } public AssetsFileInstance LoadAssetsFile(Stream stream, string path, bool loadDeps = false, BundleFileInstance bunInst = null) { string fileLookupKey = GetFileLookupKey(path); if (FileLookup.TryGetValue(fileLookupKey, out var value)) { if (loadDeps) { LoadAssetsFileDependencies(value, path, bunInst); } return value; } if (stream != null) { return LoadAssetsFileCacheless(stream, path, loadDeps, bunInst); } return null; } public AssetsFileInstance LoadAssetsFile(FileStream stream, bool loadDeps = false) { return LoadAssetsFile(stream, stream.Name, loadDeps); } public AssetsFileInstance LoadAssetsFile(string path, bool loadDeps = false) { string fileLookupKey = GetFileLookupKey(path); if (FileLookup.TryGetValue(fileLookupKey, out var value)) { if (loadDeps) { LoadAssetsFileDependencies(value, path, null); } return value; } FileStream fileStream = File.OpenRead(path); return LoadAssetsFileCacheless(fileStream, fileStream.Name, loadDeps); } public AssetsFileInstance AddAssetsFile(AssetsFile file, string path, bool loadDeps = false) { string fileLookupKey = GetFileLookupKey(path); if (FileLookup.TryGetValue(fileLookupKey, out var value)) { return value; } return LoadAssetsFileCacheless(file, path, loadDeps); } public bool UnloadAssetsFile(string path) { string fileLookupKey = GetFileLookupKey(path); if (FileLookup.TryGetValue(fileLookupKey, out var value)) { monoTypeTreeTemplateFieldCache.TryRemove(value, out var _); monoCldbTemplateFieldCache.TryRemove(value, out var _); refTypeManagerCache.TryRemove(value, out var _); lock (FileLookup) { lock (Files) { Files.Remove(value); FileLookup.Remove(fileLookupKey); } } value.file.Close(); return true; } return false; } public bool UnloadAssetsFile(AssetsFileInstance fileInst) { fileInst.file.Close(); if (Files.Contains(fileInst)) { monoTypeTreeTemplateFieldCache.TryRemove(fileInst, out var _); monoCldbTemplateFieldCache.TryRemove(fileInst, out var _); refTypeManagerCache.TryRemove(fileInst, out var _); string fileLookupKey = GetFileLookupKey(fileInst.path); lock (FileLookup) { lock (Files) { FileLookup.Remove(fileLookupKey); Files.Remove(fileInst); } } return true; } return false; } public bool UnloadAllAssetsFiles(bool clearCache = false) { if (clearCache) { templateFieldCache.Clear(); monoTemplateFieldCache.Clear(); } monoTypeTreeTemplateFieldCache.Clear(); monoCldbTemplateFieldCache.Clear(); refTypeManagerCache.Clear(); if (Files.Count != 0) { foreach (AssetsFileInstance file in Files) { file.file.Close(); } lock (FileLookup) { lock (Files) { Files.Clear(); FileLookup.Clear(); } } return true; } return false; } public static string GetBundleLookupKey(string path) { return Path.GetFullPath(path); } public BundleFileInstance LoadBundleFile(Stream stream, string path, bool unpackIfPacked = true) { string bundleLookupKey = GetBundleLookupKey(path); if (BundleLookup.TryGetValue(bundleLookupKey, out var value)) { return value; } value = new BundleFileInstance(stream, path, unpackIfPacked); lock (BundleLookup) { lock (Bundles) { BundleLookup[bundleLookupKey] = value; Bundles.Add(value); return value; } } } public BundleFileInstance LoadBundleFile(FileStream stream, bool unpackIfPacked = true) { return LoadBundleFile(stream, Path.GetFullPath(stream.Name), unpackIfPacked); } public BundleFileInstance LoadBundleFile(string path, bool unpackIfPacked = true) { return LoadBundleFile(File.OpenRead(path), unpackIfPacked); } public bool UnloadBundleFile(string path) { string bundleLookupKey = GetBundleLookupKey(path); if (BundleLookup.TryGetValue(bundleLookupKey, out var value)) { value.file.Close(); foreach (AssetsFileInstance loadedAssetsFile in value.loadedAssetsFiles) { loadedAssetsFile.file.Close(); } lock (BundleLookup) { lock (Bundles) { Bundles.Remove(value); BundleLookup.Remove(bundleLookupKey); } } return true; } return false; } public bool UnloadBundleFile(BundleFileInstance bunInst) { bunInst.file.Close(); foreach (AssetsFileInstance loadedAssetsFile in bunInst.loadedAssetsFiles) { UnloadAssetsFile(loadedAssetsFile); } bunInst.loadedAssetsFiles.Clear(); if (Bundles.Contains(bunInst)) { string bundleLookupKey = GetBundleLookupKey(bunInst.path); lock (BundleLookup) { lock (Bundles) { Bundles.Remove(bunInst); BundleLookup.Remove(bundleLookupKey); } } return true; } return false; } public bool UnloadAllBundleFiles() { if (Bundles.Count != 0) { foreach (BundleFileInstance bundle in Bundles) { bundle.file.Close(); foreach (AssetsFileInstance loadedAssetsFile in bundle.loadedAssetsFiles) { UnloadAssetsFile(loadedAssetsFile); } bundle.loadedAssetsFiles.Clear(); } lock (BundleLookup) { lock (Bundles) { Bundles.Clear(); BundleLookup.Clear(); } } return true; } return false; } private bool IsAssetsFilePreviewSafe(BundleFileInstance bunInst, int index) { AssetBundleDirectoryInfo dirInfo = BundleHelper.GetDirInfo(bunInst.file, index); if (dirInfo.IsReplacerPreviewable) { Stream previewStream = dirInfo.Replacer.GetPreviewStream(); lock (previewStream) { return AssetsFile.IsAssetsFile(new AssetsFileReader(previewStream), 0L, previewStream.Length); } } return bunInst.file.IsAssetsFile(index); } public AssetsFileInstance LoadAssetsFileFromBundle(BundleFileInstance bunInst, int index, bool loadDeps = false) { string path = Path.Combine(bunInst.path, bunInst.file.GetFileName(index)); string fileLookupKey = GetFileLookupKey(path); if (!FileLookup.TryGetValue(fileLookupKey, out var value)) { if (IsAssetsFilePreviewSafe(bunInst, index)) { bunInst.file.GetFileRange(index, out var offset, out var length); AssetsFileInstance assetsFileInstance; lock (bunInst.file.DataReader) { SegmentStream stream = new SegmentStream(bunInst.DataStream, offset, length); assetsFileInstance = LoadAssetsFile(stream, path, loadDeps, bunInst); } bunInst.loadedAssetsFiles.Add(assetsFileInstance); return assetsFileInstance; } return null; } return value; } public AssetsFileInstance LoadAssetsFileFromBundle(BundleFileInstance bunInst, string name, bool loadDeps = false) { int fileIndex = bunInst.file.GetFileIndex(name); if (fileIndex < 0) { return null; } return LoadAssetsFileFromBundle(bunInst, fileIndex, loadDeps); } public ClassDatabaseFile LoadClassDatabase(Stream stream) { ClassDatabase = new ClassDatabaseFile(); ClassDatabase.Read(new AssetsFileReader(stream)); return ClassDatabase; } public ClassDatabaseFile LoadClassDatabase(string path) { return LoadClassDatabase(File.OpenRead(path)); } public ClassDatabaseFile LoadClassDatabaseFromPackage(UnityVersion version) { return ClassDatabase = ClassPackage.GetClassDatabase(version); } public ClassDatabaseFile LoadClassDatabaseFromPackage(string version) { return ClassDatabase = ClassPackage.GetClassDatabase(version); } public ClassPackageFile LoadClassPackage(Stream stream) { ClassPackage = new ClassPackageFile(); ClassPackage.Read(new AssetsFileReader(stream)); return ClassPackage; } public ClassPackageFile LoadClassPackage(string path) { return LoadClassPackage(File.OpenRead(path)); } public void UnloadClassDatabase() { ClassDatabase = null; } public void UnloadClassPackage() { ClassPackage = null; } public void UnloadAll(bool unloadClassData = false) { UnloadAllAssetsFiles(clearCache: true); UnloadAllBundleFiles(); MonoTempGenerator?.Dispose(); if (unloadClassData) { ClassPackage = null; ClassDatabase = null; } } private bool DependencyNotLoaded(string depPath) { lock (Files) { return !Files.Any((AssetsFileInstance f) => Path.GetFileName(f.path).ToLower() == Path.GetFileName(depPath).ToLower()); } } public void LoadDependencies(AssetsFileInstance ofFile) { string directoryName = Path.GetDirectoryName(ofFile.path); for (int i = 0; i < ofFile.file.Metadata.Externals.Count; i++) { string pathName = ofFile.file.Metadata.Externals[i].PathName; if (!(pathName == string.Empty) && DependencyNotLoaded(pathName)) { string path = Path.Combine(directoryName, pathName); string path2 = Path.Combine(directoryName, Path.GetFileName(pathName)); if (File.Exists(path)) { LoadAssetsFile(path, loadDeps: true); } else if (File.Exists(path2)) { LoadAssetsFile(path2, loadDeps: true); } } } } public void LoadBundleDependencies(AssetsFileInstance ofFile, BundleFileInstance ofBundle, string path) { for (int i = 0; i < ofFile.file.Metadata.Externals.Count; i++) { string pathName = ofFile.file.Metadata.Externals[i].PathName; if (DependencyNotLoaded(pathName)) { string bunPath = Path.GetFileName(pathName); int num = ofBundle.file.BlockAndDirInfo.DirectoryInfos.FindIndex((AssetBundleDirectoryInfo d) => Path.GetFileName(d.Name) == bunPath); string path2 = Path.Combine(path, ".."); string path3 = Path.Combine(path2, pathName); string path4 = Path.Combine(path2, Path.GetFileName(pathName)); string path5 = Path.Combine(path, pathName); string path6 = Path.Combine(path, Path.GetFileName(pathName)); if (num != -1) { LoadAssetsFileFromBundle(ofBundle, num, loadDeps: true); } else if (File.Exists(path5)) { LoadAssetsFile(path5, loadDeps: true); } else if (File.Exists(path6)) { LoadAssetsFile(path6, loadDeps: true); } else if (File.Exists(path3)) { LoadAssetsFile(path3, loadDeps: true); } else if (File.Exists(path4)) { LoadAssetsFile(path4, loadDeps: true); } } } } public RefTypeManager GetRefTypeManager(AssetsFileInstance inst) { if (UseRefTypeManagerCache && refTypeManagerCache.TryGetValue(inst, out var value)) { return value; } value = new RefTypeManager(); value.FromTypeTree(inst.file.Metadata); if (MonoTempGenerator != null) { value.WithMonoTemplateGenerator(inst.file.Metadata, MonoTempGenerator, UseMonoTemplateFieldCache ? monoTemplateFieldCache : null); } if (UseRefTypeManagerCache) { refTypeManagerCache[inst] = value; } return value; } public AssetTypeTemplateField GetTemplateBaseField(AssetsFileInstance inst, AssetFileInfo info, AssetReadFlags readFlags = AssetReadFlags.None) { ushort scriptIndex = info.GetScriptIndex(inst.file); if (info.ReplacerType != ContentReplacerType.AddOrModify) { long absoluteByteOffset = info.GetAbsoluteByteOffset(inst.file); return GetTemplateBaseField(inst, inst.file.Reader, absoluteByteOffset, info.TypeId, scriptIndex, readFlags); } if (info.Replacer.HasPreview()) { AssetsFileReader reader = new AssetsFileReader(info.Replacer.GetPreviewStream()); return GetTemplateBaseField(inst, reader, 0L, info.TypeId, scriptIndex, readFlags); } return GetTemplateBaseField(inst, null, 0L, info.TypeId, scriptIndex, readFlags); } public AssetTypeTemplateField GetTemplateBaseField(AssetsFileInstance inst, AssetsFileReader reader, long absByteStart, int typeId, ushort scriptIndex, AssetReadFlags readFlags) { AssetTypeTemplateField value = null; bool flag = typeId == 114 || typeId < 0; if (UseTemplateFieldCache && !flag && templateFieldCache.TryGetValue(typeId, out value)) { return value; } AssetsFile file = inst.file; bool typeTreeEnabled = file.Metadata.TypeTreeEnabled; bool flag2 = Net35Polyfill.HasFlag(readFlags, AssetReadFlags.ForceFromCldb); if (typeTreeEnabled && (!flag2 || ClassDatabase == null)) { if (UseMonoTemplateFieldCache && flag && monoTypeTreeTemplateFieldCache.TryGetValue(inst, out var value2) && value2.TryGetValue(scriptIndex, out value)) { return value; } TypeTreeType typeTreeType = file.Metadata.FindTypeTreeTypeByID(typeId, scriptIndex); if (typeTreeType != null && typeTreeType.Nodes.Count > 0) { value = new AssetTypeTemplateField(); value.FromTypeTree(typeTreeType); if (UseTemplateFieldCache && !flag) { templateFieldCache[typeId] = value; } else if (UseMonoTemplateFieldCache && flag) { if (!monoTypeTreeTemplateFieldCache.TryGetValue(inst, out var value3)) { value3 = (monoTypeTreeTemplateFieldCache[inst] = new ConcurrentDictionary()); } value3[scriptIndex] = value; } return value; } } if (UseTemplateFieldCache && UseMonoTemplateFieldCache && flag && templateFieldCache.TryGetValue(114, out value)) { value = value.Clone(); } if (value == null) { if (ClassDatabase == null) { return null; } int num = (flag ? 114 : typeId); ClassDatabaseType classDatabaseType = ClassDatabase.FindAssetClassByID(num); if (classDatabaseType == null) { return null; } bool preferEditor = Net35Polyfill.HasFlag(readFlags, AssetReadFlags.PreferEditor); value = new AssetTypeTemplateField(); value.FromClassDatabase(ClassDatabase, classDatabaseType, preferEditor); if (UseTemplateFieldCache) { if (num == 114) { templateFieldCache[num] = value.Clone(); } else { templateFieldCache[num] = value; } } } bool flag3 = Net35Polyfill.HasFlag(readFlags, AssetReadFlags.SkipMonoBehaviourFields); if (flag && MonoTempGenerator != null && !flag3 && reader != null) { AssetPPtr assetPPtr = AssetPPtr.FromField(value.MakeValue(reader, absByteStart)["m_Script"]); if (!assetPPtr.IsNull()) { AssetsFileInstance assetsFileInstance = ((assetPPtr.FileId != 0) ? inst.GetDependency(this, assetPPtr.FileId - 1) : inst); if (assetsFileInstance == null) { return value; } ConcurrentDictionary value4 = null; if (UseMonoTemplateFieldCache) { AssetTypeTemplateField value5; if (!monoCldbTemplateFieldCache.TryGetValue(assetsFileInstance, out value4)) { value4 = (monoCldbTemplateFieldCache[assetsFileInstance] = new ConcurrentDictionary()); } else if (value4.TryGetValue(assetPPtr.PathId, out value5)) { return value5; } } AssetFileInfo assetInfo = assetsFileInstance.file.GetAssetInfo(assetPPtr.PathId); long absoluteByteOffset = assetInfo.GetAbsoluteByteOffset(assetsFileInstance.file); int typeId2 = assetInfo.TypeId; ushort scriptIndex2 = assetInfo.GetScriptIndex(assetsFileInstance.file); string assemblyName; string nameSpace; string className; bool monoScriptInfo = GetMonoScriptInfo(assetsFileInstance, absoluteByteOffset, typeId2, scriptIndex2, out assemblyName, out nameSpace, out className, readFlags); if (assemblyName.EndsWith(".dll")) { assemblyName = assemblyName.Substring(0, assemblyName.Length - 4); } if (monoScriptInfo) { AssetTypeReference key = new AssetTypeReference(className, nameSpace, assemblyName); if (UseMonoTemplateFieldCache && monoTemplateFieldCache.TryGetValue(key, out var value6)) { value4[assetPPtr.PathId] = value6; return value6; } AssetTypeTemplateField templateField = MonoTempGenerator.GetTemplateField(value, assemblyName, nameSpace, className, new UnityVersion(file.Metadata.UnityVersion)); if (templateField != null) { value = templateField; if (UseMonoTemplateFieldCache) { ConcurrentDictionary concurrentDictionary3 = value4; long pathId = assetPPtr.PathId; AssetTypeTemplateField value7 = (monoTemplateFieldCache[key] = value); concurrentDictionary3[pathId] = value7; return value; } } } } } return value; } private bool GetMonoScriptInfo(AssetsFileInstance inst, long absFilePos, int typeId, ushort scriptIndex, out string assemblyName, out string nameSpace, out string className, AssetReadFlags readFlags) { assemblyName = null; nameSpace = null; className = null; AssetTypeTemplateField templateBaseField = GetTemplateBaseField(inst, null, absFilePos, typeId, scriptIndex, readFlags); if (templateBaseField == null) { return false; } AssetTypeValueField assetTypeValueField; lock (inst.LockReader) { inst.file.Reader.Position = absFilePos; assetTypeValueField = templateBaseField.MakeValue(inst.file.Reader); } assemblyName = assetTypeValueField["m_AssemblyName"].AsString; nameSpace = assetTypeValueField["m_Namespace"].AsString; className = assetTypeValueField["m_ClassName"].AsString; return true; } public AssetTypeTemplateField CreateTemplateBaseField(AssetsFileInstance inst, int id, ushort scriptIndex = ushort.MaxValue) { AssetsFile file = inst.file; AssetTypeTemplateField assetTypeTemplateField = new AssetTypeTemplateField(); if (file.Metadata.TypeTreeEnabled) { TypeTreeType typeTreeType = file.Metadata.FindTypeTreeTypeByID(id, scriptIndex); assetTypeTemplateField.FromTypeTree(typeTreeType); } else if ((id != 114 && id >= 0) || scriptIndex == ushort.MaxValue) { ClassDatabaseType cldbType = ClassDatabase.FindAssetClassByID(id); assetTypeTemplateField.FromClassDatabase(ClassDatabase, cldbType); } else { if (MonoTempGenerator == null) { throw new Exception("MonoTempGenerator must be non-null to create a MonoBehaviour!"); } AssetTypeReference assetsFileScriptInfo = AssetHelper.GetAssetsFileScriptInfo(this, inst, scriptIndex); AssetTypeTemplateField templateBaseField = GetTemplateBaseField(inst, file.Reader, -1L, 114, scriptIndex, AssetReadFlags.SkipMonoBehaviourFields); UnityVersion unityVersion = new UnityVersion(file.Metadata.UnityVersion); assetTypeTemplateField = MonoTempGenerator.GetTemplateField(templateBaseField, assetsFileScriptInfo.AsmName, assetsFileScriptInfo.Namespace, assetsFileScriptInfo.ClassName, unityVersion); } return assetTypeTemplateField; } public AssetTypeValueField CreateValueBaseField(AssetsFileInstance inst, int id, ushort scriptIndex = ushort.MaxValue) { return ValueBuilder.DefaultValueFieldFromTemplate(CreateTemplateBaseField(inst, id, scriptIndex)); } public AssetTypeValueField GetBaseField(AssetsFileInstance inst, AssetFileInfo info, AssetReadFlags readFlags = AssetReadFlags.None) { AssetTypeTemplateField templateBaseField = GetTemplateBaseField(inst, info, readFlags); RefTypeManager refTypeManager = GetRefTypeManager(inst); if (info.IsReplacerPreviewable) { Stream previewStream = info.Replacer.GetPreviewStream(); lock (previewStream) { return templateBaseField.MakeValue(new AssetsFileReader(previewStream), 0L, refTypeManager); } } using MemoryStream memoryStream = new MemoryStream((int)info.ByteSize); lock (inst.LockReader) { AssetsFileReader reader = inst.file.Reader; reader.Position = info.GetAbsoluteByteOffset(inst.file); reader.BaseStream.CopyToCompat(memoryStream, info.ByteSize); } memoryStream.Position = 0L; return templateBaseField.MakeValue(new AssetsFileReader(memoryStream), 0L, refTypeManager); } public AssetTypeValueField GetBaseField(AssetsFileInstance inst, long pathId, AssetReadFlags readFlags = AssetReadFlags.None) { AssetFileInfo assetInfo = inst.file.GetAssetInfo(pathId); return GetBaseField(inst, assetInfo, readFlags); } public AssetExternal GetExtAsset(AssetsFileInstance relativeTo, int fileId, long pathId, bool onlyGetInfo = false, AssetReadFlags readFlags = AssetReadFlags.None) { AssetExternal assetExternal = default(AssetExternal); assetExternal.info = null; assetExternal.baseField = null; assetExternal.file = null; AssetExternal result = assetExternal; if (fileId == 0 && pathId == 0L) { return result; } if (fileId != 0) { AssetsFileInstance dependency = relativeTo.GetDependency(this, fileId - 1); if (dependency == null) { return result; } result.file = dependency; result.info = dependency.file.GetAssetInfo(pathId); if (result.info == null) { return result; } if (!onlyGetInfo) { result.baseField = GetBaseField(dependency, result.info, readFlags); } else { result.baseField = null; } return result; } result.file = relativeTo; result.info = relativeTo.file.GetAssetInfo(pathId); if (result.info == null) { return result; } if (!onlyGetInfo) { result.baseField = GetBaseField(relativeTo, result.info, readFlags); } else { result.baseField = null; } return result; } public AssetExternal GetExtAsset(AssetsFileInstance relativeTo, AssetTypeValueField pptrField, bool onlyGetInfo = false, AssetReadFlags readFlags = AssetReadFlags.None) { int asInt = pptrField["m_FileID"].AsInt; long asLong = pptrField["m_PathID"].AsLong; return GetExtAsset(relativeTo, asInt, asLong, onlyGetInfo, readFlags); } } public struct AssetExternal { public AssetFileInfo info; public AssetTypeValueField baseField; public AssetsFileInstance file; } public class BundleFileInstance { public string path; public string name; public AssetBundleFile file; public AssetBundleCompressionType originalCompression; public List loadedAssetsFiles; public Stream BundleStream => file.Reader.BaseStream; public Stream DataStream => file.DataReader.BaseStream; public BundleFileInstance(Stream stream, string filePath, bool unpackIfPacked = true) { path = Path.GetFullPath(filePath); name = Path.GetFileName(path); file = new AssetBundleFile(); file.Read(new AssetsFileReader(stream)); originalCompression = file.GetCompressionType(); if (file.Header != null && file.DataIsCompressed && unpackIfPacked) { file = BundleHelper.UnpackBundle(file); } loadedAssetsFiles = new List(); } public BundleFileInstance(FileStream stream, bool unpackIfPacked = true) : this(stream, stream.Name, unpackIfPacked) { } } public interface IMonoBehaviourTemplateGenerator { AssetTypeTemplateField GetTemplateField(AssetTypeTemplateField baseField, string assemblyName, string nameSpace, string className, UnityVersion unityVersion); void Dispose(); } public class BundleCreator { public static void CreateBlankAssets(MemoryStream ms, string engineVersion, uint formatVersion, uint typeTreeVersion, bool hasTypeTree = false) { AssetsFileWriter assetsFileWriter = new AssetsFileWriter(ms); AssetsFileHeader assetsFileHeader = new AssetsFileHeader { MetadataSize = 0L, FileSize = -1L, Version = formatVersion, DataOffset = -1L, Endianness = false }; AssetsFileMetadata obj = new AssetsFileMetadata { UnityVersion = engineVersion, TargetPlatform = typeTreeVersion, TypeTreeEnabled = hasTypeTree, TypeTreeTypes = new List(), AssetInfos = new List(), ScriptTypes = new List(), Externals = new List(), RefTypes = new List() }; assetsFileHeader.Write(assetsFileWriter); obj.Write(assetsFileWriter, formatVersion); assetsFileWriter.Write(0u); assetsFileWriter.Align(); assetsFileWriter.Write(0u); assetsFileWriter.Write(0u); if (assetsFileHeader.Version >= 20) { assetsFileWriter.Write(0); } uint num = (uint)(assetsFileWriter.Position - 19); if (assetsFileHeader.Version >= 22) { num -= 28; } if (assetsFileWriter.Position < 4096) { while (assetsFileWriter.Position < 4096) { assetsFileWriter.Write((byte)0); } } else if (assetsFileWriter.Position % 16 == 0L) { assetsFileWriter.Position += 16L; } else { assetsFileWriter.Align16(); } long position2 = (assetsFileHeader.DataOffset = (assetsFileHeader.FileSize = assetsFileWriter.Position)); assetsFileHeader.MetadataSize = num; assetsFileWriter.Position = 0L; assetsFileHeader.Write(assetsFileWriter); assetsFileWriter.Position = position2; } } public static class BundleHelper { public static byte[] LoadAssetDataFromBundle(AssetBundleFile bundle, int index) { bundle.GetFileRange(index, out var offset, out var length); AssetsFileReader dataReader = bundle.DataReader; dataReader.Position = offset; return dataReader.ReadBytes((int)length); } public static byte[] LoadAssetDataFromBundle(AssetBundleFile bundle, string name) { int fileIndex = bundle.GetFileIndex(name); if (fileIndex < 0) { return null; } return LoadAssetDataFromBundle(bundle, fileIndex); } public static AssetsFile LoadAssetFromBundle(AssetBundleFile bundle, int index) { bundle.GetFileRange(index, out var offset, out var length); AssetsFileReader reader = new AssetsFileReader(new SegmentStream(bundle.DataReader.BaseStream, offset, length)); AssetsFile assetsFile = new AssetsFile(); assetsFile.Read(reader); return assetsFile; } public static AssetsFile LoadAssetFromBundle(AssetBundleFile bundle, string name) { int fileIndex = bundle.GetFileIndex(name); if (fileIndex < 0) { return null; } return LoadAssetFromBundle(bundle, fileIndex); } public static List LoadAllAssetsDataFromBundle(AssetBundleFile bundle) { List list = new List(); int count = bundle.BlockAndDirInfo.DirectoryInfos.Count; for (int i = 0; i < count; i++) { if (bundle.IsAssetsFile(i)) { list.Add(LoadAssetDataFromBundle(bundle, i)); } } return list; } public static List LoadAllAssetsFromBundle(AssetBundleFile bundle) { List list = new List(); int count = bundle.BlockAndDirInfo.DirectoryInfos.Count; for (int i = 0; i < count; i++) { if (bundle.IsAssetsFile(i)) { list.Add(LoadAssetFromBundle(bundle, i)); } } return list; } public static AssetBundleFile UnpackBundle(AssetBundleFile file, bool freeOriginalStream = true) { MemoryStream memoryStream = new MemoryStream(); file.Unpack(new AssetsFileWriter(memoryStream)); memoryStream.Position = 0L; AssetBundleFile assetBundleFile = new AssetBundleFile(); assetBundleFile.Read(new AssetsFileReader(memoryStream)); if (freeOriginalStream) { file.Reader.Close(); file.DataReader.Close(); } return assetBundleFile; } public static AssetBundleFile UnpackBundleToStream(AssetBundleFile file, Stream stream, bool freeOriginalStream = true) { file.Unpack(new AssetsFileWriter(stream)); stream.Position = 0L; AssetBundleFile assetBundleFile = new AssetBundleFile(); assetBundleFile.Read(new AssetsFileReader(stream)); if (freeOriginalStream) { file.Reader.Close(); file.DataReader.Close(); } return assetBundleFile; } public static AssetBundleDirectoryInfo GetDirInfo(AssetBundleFile bundle, int index) { return bundle.BlockAndDirInfo.DirectoryInfos[index]; } public static AssetBundleDirectoryInfo GetDirInfo(AssetBundleFile bundle, string name) { List directoryInfos = bundle.BlockAndDirInfo.DirectoryInfos; for (int i = 0; i < directoryInfos.Count; i++) { AssetBundleDirectoryInfo assetBundleDirectoryInfo = directoryInfos[i]; if (assetBundleDirectoryInfo.Name == name) { return assetBundleDirectoryInfo; } } return null; } } public static class CommonMonoTemplateHelper { private static readonly string[] blacklistedAssemblies = new string[14] { "mscorlib", "mscorlib.dll", "netstandard", "netstandard.dll", "System.Core", "System.Core.dll", "System", "System.dll", "System.Private.CoreLib", "System.Private.CoreLib.dll", "System.Collections", "System.Collections.dll", "System.Collections.NonGeneric", "System.Collections.NonGeneric.dll" }; private static readonly string[] specialUnityTypes = new string[18] { "UnityEngine.Color", "UnityEngine.Color32", "UnityEngine.Gradient", "UnityEngine.Vector2", "UnityEngine.Vector3", "UnityEngine.Vector4", "UnityEngine.LayerMask", "UnityEngine.Quaternion", "UnityEngine.Bounds", "UnityEngine.Rect", "UnityEngine.RectOffset", "UnityEngine.Matrix4x4", "UnityEngine.AnimationCurve", "UnityEngine.GUIStyle", "UnityEngine.Vector2Int", "UnityEngine.Vector3Int", "UnityEngine.PropertyName", "UnityEngine.BoundsInt" }; private static readonly string[] primitiveTypes = new string[12] { "System.Boolean", "System.SByte", "System.Byte", "System.Char", "System.Int16", "System.UInt16", "System.Int32", "System.UInt32", "System.Int64", "System.UInt64", "System.Double", "System.Single" }; private static readonly Dictionary baseToPrimitive = new Dictionary { ["System.Boolean"] = "UInt8", ["System.SByte"] = "SInt8", ["System.Byte"] = "UInt8", ["System.Char"] = "UInt16", ["System.Int16"] = "SInt16", ["System.UInt16"] = "UInt16", ["System.Int32"] = "int", ["System.UInt32"] = "unsigned int", ["System.Int64"] = "SInt64", ["System.UInt64"] = "UInt64", ["System.Double"] = "double", ["System.Single"] = "float", ["System.String"] = "string" }; public static string ConvertBaseToPrimitive(string name) { if (baseToPrimitive.TryGetValue(name, out var value)) { return value; } return name; } public static bool IsSpecialUnityType(string fullName) { return specialUnityTypes.Contains(fullName); } public static bool IsAssemblyBlacklisted(string assembly, UnityVersion unityVersion) { return blacklistedAssemblies.Contains(assembly); } public static bool IsPrimitiveType(string fullName) { return primitiveTypes.Contains(fullName); } public static bool TypeAligns(AssetValueType valueType) { if (!valueType.Equals(AssetValueType.Bool) && !valueType.Equals(AssetValueType.Int8) && !valueType.Equals(AssetValueType.UInt8) && !valueType.Equals(AssetValueType.Int16)) { return valueType.Equals(AssetValueType.UInt16); } return true; } public static int GetSerializationLimit(UnityVersion unityVersion) { if (unityVersion.major > 2020 || (unityVersion.major == 2020 && (unityVersion.minor > 1 || (unityVersion.minor == 1 && unityVersion.patch >= 4))) || (unityVersion.major == 2019 && unityVersion.minor == 4 && unityVersion.patch >= 9)) { return 10; } return 7; } public static AssetTypeTemplateField Bool(string name, bool align = false) { return CreateTemplateField(name, "bool", AssetValueType.Bool, isArray: false, align); } public static AssetTypeTemplateField SByte(string name, bool align = false) { return CreateTemplateField(name, "SInt8", AssetValueType.Int8, isArray: false, align); } public static AssetTypeTemplateField Byte(string name, bool align = false) { return CreateTemplateField(name, "UInt8", AssetValueType.UInt8, isArray: false, align); } public static AssetTypeTemplateField CChar(string name, bool align = false) { return CreateTemplateField(name, "char", AssetValueType.UInt8, isArray: false, align); } public static AssetTypeTemplateField Char(string name, bool align = false) { return CreateTemplateField(name, "UInt16", AssetValueType.UInt16, isArray: false, align); } public static AssetTypeTemplateField Short(string name, bool align = false) { return CreateTemplateField(name, "SInt16", AssetValueType.Int16, isArray: false, align); } public static AssetTypeTemplateField UShort(string name, bool align = false) { return CreateTemplateField(name, "UInt16", AssetValueType.UInt16, isArray: false, align); } public static AssetTypeTemplateField Int(string name) { return CreateTemplateField(name, "int", AssetValueType.Int32); } public static AssetTypeTemplateField UInt(string name) { return CreateTemplateField(name, "unsigned int", AssetValueType.UInt32); } public static AssetTypeTemplateField Long(string name) { return CreateTemplateField(name, "SInt64", AssetValueType.Int64); } public static AssetTypeTemplateField ULong(string name) { return CreateTemplateField(name, "UInt64", AssetValueType.UInt64); } public static AssetTypeTemplateField Float(string name) { return CreateTemplateField(name, "float", AssetValueType.Float); } public static AssetTypeTemplateField Double(string name) { return CreateTemplateField(name, "double", AssetValueType.Double); } public static AssetTypeTemplateField String(string name) { return CreateTemplateField(name, "string", AssetValueType.String, String()); } public static List String() { return Array(CChar("data")); } public static AssetTypeTemplateField Vector(AssetTypeTemplateField field) { return CreateTemplateField(field.Name, "vector", Array(field)); } public static AssetTypeTemplateField VectorWithType(AssetTypeTemplateField field) { return CreateTemplateField(field.Name, field.Type, Array(field)); } public static List Array(AssetTypeTemplateField field) { AssetTypeTemplateField item = new AssetTypeTemplateField { Name = "Array", Type = "Array", ValueType = ((field.ValueType == AssetValueType.UInt8) ? AssetValueType.ByteArray : AssetValueType.Array), IsArray = true, IsAligned = true, HasValue = true, Children = new List { Int("size"), CreateTemplateField("data", field.Type, field.ValueType, field.Children) } }; return new List { item }; } public static AssetTypeTemplateField ManagedReference(string name, UnityVersion unityVersion) { return CreateTemplateField(name, "managedReference", ManagedReference(unityVersion)); } public static List ManagedReference(UnityVersion unityVersion) { if (unityVersion.major > 2021 || (unityVersion.major == 2021 && unityVersion.minor >= 2)) { return new List { Long("rid") }; } return new List { Int("id") }; } public static AssetTypeTemplateField ManagedReferencesRegistry(string name, UnityVersion unityVersion) { return CreateTemplateField(name, "ManagedReferencesRegistry", AssetValueType.ManagedReferencesRegistry, ManagedReferencesRegistry(unityVersion)); } public static List ManagedReferencesRegistry(UnityVersion unityVersion) { if (unityVersion.major > 2021 || (unityVersion.major == 2021 && unityVersion.minor >= 2)) { return new List { Int("version"), Vector(ReferencedObject("RefIds", unityVersion)) }; } return new List { Int("version"), ReferencedObject("00000000", unityVersion) }; } public static AssetTypeTemplateField ReferencedObject(string name, UnityVersion unityVersion) { return CreateTemplateField(name, "ReferencedObject", ReferencedObject(unityVersion)); } public static List ReferencedObject(UnityVersion unityVersion) { if (unityVersion.major > 2021 || (unityVersion.major == 2021 && unityVersion.minor >= 2)) { return new List { Long("rid"), ReferencedManagedType("type"), CreateTemplateField("data", "ReferencedObjectData", AssetValueType.None) }; } return new List { ReferencedManagedType("type"), CreateTemplateField("data", "ReferencedObjectData", AssetValueType.None) }; } public static AssetTypeTemplateField ReferencedManagedType(string name) { return CreateTemplateField(name, "ReferencedManagedType", ReferencedManagedType()); } public static List ReferencedManagedType() { return new List { String("class"), String("ns"), String("asm") }; } public static AssetTypeTemplateField Gradient(string name, UnityVersion unityVersion) { return CreateTemplateField(name, "Gradient", Gradient(unityVersion)); } public static List Gradient(UnityVersion unityVersion) { if (unityVersion.major > 5 || (unityVersion.major == 5 && unityVersion.minor >= 6)) { List list = new List { RGBAf("key0"), RGBAf("key1"), RGBAf("key2"), RGBAf("key3"), RGBAf("key4"), RGBAf("key5"), RGBAf("key6"), RGBAf("key7"), UShort("ctime0"), UShort("ctime1"), UShort("ctime2"), UShort("ctime3"), UShort("ctime4"), UShort("ctime5"), UShort("ctime6"), UShort("ctime7"), UShort("atime0"), UShort("atime1"), UShort("atime2"), UShort("atime3"), UShort("atime4"), UShort("atime5"), UShort("atime6"), UShort("atime7") }; if (unityVersion.major > 2022 || (unityVersion.major == 2022 && unityVersion.minor >= 2)) { list.Add(Byte("m_Mode")); list.Add(SByte("m_ColorSpace")); } else { list.Add(Int("m_Mode")); } list.Add(Byte("m_NumColorKeys")); list.Add(Byte("m_NumAlphaKeys", align: true)); return list; } return new List { RGBAi("key0"), RGBAi("key1"), RGBAi("key2"), RGBAi("key3"), RGBAi("key4"), RGBAi("key5"), RGBAi("key6"), RGBAi("key7") }; } public static AssetTypeTemplateField AnimationCurve(string name, UnityVersion unityVersion) { return CreateTemplateField(name, "AnimationCurve", AnimationCurve(unityVersion)); } public static List AnimationCurve(UnityVersion unityVersion) { List list = new List { Vector(Keyframe("m_Curve", unityVersion)), Int("m_PreInfinity"), Int("m_PostInfinity") }; if (unityVersion.major > 5 || (unityVersion.major == 5 && unityVersion.minor >= 3)) { list.Add(Int("m_RotationOrder")); } return list; } public static AssetTypeTemplateField GUIStyle(string name, UnityVersion unityVersion) { return CreateTemplateField(name, "GUIStyle", GUIStyle(unityVersion)); } public static List GUIStyle(UnityVersion unityVersion) { List list = new List { String("m_Name"), GUIStyleState("m_Normal", unityVersion), GUIStyleState("m_Hover", unityVersion), GUIStyleState("m_Active", unityVersion), GUIStyleState("m_Focused", unityVersion), GUIStyleState("m_OnNormal", unityVersion), GUIStyleState("m_OnHover", unityVersion), GUIStyleState("m_OnActive", unityVersion), GUIStyleState("m_OnFocused", unityVersion), RectOffset("m_Border") }; if (unityVersion.major >= 4) { list.Add(RectOffset("m_Margin")); list.Add(RectOffset("m_Padding")); } else { list.Add(RectOffset("m_Padding")); list.Add(RectOffset("m_Margin")); } list.Add(RectOffset("m_Overflow")); list.Add(PPtr("m_Font", "Font", unityVersion)); if (unityVersion.major >= 4) { list.Add(Int("m_FontSize")); list.Add(Int("m_FontStyle")); list.Add(Int("m_Alignment")); list.Add(Bool("m_WordWrap")); list.Add(Bool("m_RichText", align: true)); } else { list.Add(Int("m_ImagePosition")); list.Add(Int("m_Alignment")); list.Add(Bool("m_WordWrap", align: true)); } list.Add(Int("m_TextClipping")); if (unityVersion.major >= 4) { list.Add(Int("m_ImagePosition")); } list.Add(Vector2f("m_ContentOffset")); if (unityVersion.major < 4) { list.Add(Vector2f("m_ClipOffset")); } list.Add(Float("m_FixedWidth")); list.Add(Float("m_FixedHeight")); if (unityVersion.major >= 4) { list.Add(Bool("m_StretchWidth")); } else { list.Add(Int("m_FontSize")); list.Add(Int("m_FontStyle")); list.Add(Bool("m_StretchWidth", align: true)); } list.Add(Bool("m_StretchHeight", align: true)); return list; } public static AssetTypeTemplateField Keyframe(string name, UnityVersion unityVersion) { return CreateTemplateField(name, "Keyframe", Keyframe(unityVersion)); } public static List Keyframe(UnityVersion unityVersion) { List list = new List { Float("time"), Float("value"), Float("inSlope"), Float("outSlope") }; if (unityVersion.major >= 2018) { list.Add(Int("weightedMode")); list.Add(Float("inWeight")); list.Add(Float("outWeight")); } return list; } public static AssetTypeTemplateField GUIStyleState(string name, UnityVersion unityVersion) { return CreateTemplateField(name, "GUIStyleState", GUIStyleState(unityVersion)); } public static List GUIStyleState(UnityVersion unityVersion) { return new List { PPtr("m_Background", "Texture2D", unityVersion), RGBAf("m_TextColor") }; } public static AssetTypeTemplateField SphericalHarmonicsL2(string name, UnityVersion unityVersion) { return CreateTemplateField(name, "SphericalHarmonicsL2", SphericalHarmonicsL2(unityVersion)); } public static List SphericalHarmonicsL2(UnityVersion unityVersion) { List list = new List(); if (unityVersion.major >= 5) { list.Add(Float("sh[ 0]")); list.Add(Float("sh[ 1]")); list.Add(Float("sh[ 2]")); list.Add(Float("sh[ 3]")); list.Add(Float("sh[ 4]")); list.Add(Float("sh[ 5]")); list.Add(Float("sh[ 6]")); list.Add(Float("sh[ 7]")); list.Add(Float("sh[ 8]")); list.Add(Float("sh[ 9]")); } else { list.Add(Float("sh[0]")); list.Add(Float("sh[1]")); list.Add(Float("sh[2]")); list.Add(Float("sh[3]")); list.Add(Float("sh[4]")); list.Add(Float("sh[5]")); list.Add(Float("sh[6]")); list.Add(Float("sh[7]")); list.Add(Float("sh[8]")); list.Add(Float("sh[9]")); } list.Add(Float("sh[10]")); list.Add(Float("sh[11]")); list.Add(Float("sh[12]")); list.Add(Float("sh[13]")); list.Add(Float("sh[14]")); list.Add(Float("sh[15]")); list.Add(Float("sh[16]")); list.Add(Float("sh[17]")); list.Add(Float("sh[18]")); list.Add(Float("sh[19]")); list.Add(Float("sh[20]")); list.Add(Float("sh[21]")); list.Add(Float("sh[22]")); list.Add(Float("sh[23]")); list.Add(Float("sh[24]")); list.Add(Float("sh[25]")); list.Add(Float("sh[26]")); return list; } public static AssetTypeTemplateField RGBAf(string name) { return CreateTemplateField(name, "ColorRGBA", RGBAf()); } public static List RGBAf() { return new List { Float("r"), Float("g"), Float("b"), Float("a") }; } public static AssetTypeTemplateField RGBAi(string name) { return CreateTemplateField(name, "ColorRGBA", RGBAi()); } public static List RGBAi() { return new List { UInt("rgba") }; } public static AssetTypeTemplateField AABB(string name) { return CreateTemplateField(name, "AABB", AABB()); } public static List AABB() { return new List { Vector3f("m_Center"), Vector3f("m_Extent") }; } public static AssetTypeTemplateField BoundsInt(string name) { return CreateTemplateField(name, "BoundsInt", BoundsInt()); } public static List BoundsInt() { return new List { Vector3Int("m_Position"), Vector3Int("m_Size") }; } public static AssetTypeTemplateField BitField(string name) { return CreateTemplateField(name, "BitField", BitField()); } public static List BitField() { return new List { UInt("m_Bits") }; } public static AssetTypeTemplateField Rectf(string name) { return CreateTemplateField(name, "Rectf", Rectf()); } public static List Rectf() { return new List { Float("x"), Float("y"), Float("width"), Float("height") }; } public static AssetTypeTemplateField RectOffset(string name) { return CreateTemplateField(name, "RectOffset", RectOffset()); } public static List RectOffset() { return new List { Int("m_Left"), Int("m_Right"), Int("m_Top"), Int("m_Bottom") }; } public static AssetTypeTemplateField Vector2Int(string name) { return CreateTemplateField(name, "int2_storage", Vector2Int()); } public static List Vector2Int() { return new List { Int("x"), Int("y") }; } public static AssetTypeTemplateField Vector3Int(string name) { return CreateTemplateField(name, "int3_storage", Vector3Int()); } public static List Vector3Int() { return new List { Int("x"), Int("y"), Int("z") }; } public static AssetTypeTemplateField Vector2f(string name) { return CreateTemplateField(name, "Vector2f", Vector2f()); } public static List Vector2f() { return new List { Float("x"), Float("y") }; } public static AssetTypeTemplateField Vector3f(string name) { return CreateTemplateField(name, "Vector3f", Vector3f()); } public static List Vector3f() { return new List { Float("x"), Float("y"), Float("z") }; } public static AssetTypeTemplateField PPtr(string name, string typeName, UnityVersion unityVersion) { return CreateTemplateField(name, "PPtr<" + typeName + ">", PPtr(unityVersion)); } public static List PPtr(UnityVersion unityVersion) { if (unityVersion.major >= 5) { return new List { Int("m_FileID"), Long("m_PathID") }; } return new List { Int("m_FileID"), Int("m_PathID") }; } public static AssetTypeTemplateField PropertyName(string name, UnityVersion unityVersion) { return CreateTemplateField(name, "string", PropertyName(unityVersion)); } public static List PropertyName(UnityVersion unityVersion) { if (unityVersion.major > 2020 || (unityVersion.major == 2020 && (unityVersion.minor > 2 || (unityVersion.minor == 2 && (unityVersion.type != "a" || unityVersion.typeNum >= 16)))) || (unityVersion.major == 2020 && (unityVersion.minor > 1 || (unityVersion.minor == 1 && unityVersion.type != "a" && (unityVersion.type != "b" || unityVersion.typeNum >= 15)))) || (unityVersion.major == 2019 && (unityVersion.minor > 4 || (unityVersion.minor == 4 && (unityVersion.type != "a" || unityVersion.patch >= 3)))) || (unityVersion.major == 2018 && (unityVersion.minor > 4 || (unityVersion.minor == 4 && (unityVersion.type != "a" || unityVersion.patch >= 25))))) { return new List { String("id") }; } return new List { Int("id") }; } public static AssetTypeTemplateField CreateTemplateField(string name, string type, List children) { return CreateTemplateField(name, type, AssetValueType.None, isArray: false, align: false, children); } public static AssetTypeTemplateField CreateTemplateField(string name, string type, AssetValueType valueType) { return CreateTemplateField(name, type, valueType, isArray: false, align: false, new List(0)); } public static AssetTypeTemplateField CreateTemplateField(string name, string type, AssetValueType valueType, bool isArray, bool align) { return CreateTemplateField(name, type, valueType, isArray, align, new List(0)); } public static AssetTypeTemplateField CreateTemplateField(string name, string type, AssetValueType valueType, List children) { return CreateTemplateField(name, type, valueType, isArray: false, align: false, children); } public static AssetTypeTemplateField CreateTemplateField(string name, string type, AssetValueType valueType, bool isArray, bool align, List children) { return new AssetTypeTemplateField { Name = name, Type = type, ValueType = valueType, IsArray = isArray, IsAligned = align, HasValue = (valueType != AssetValueType.None), Children = (children ?? new List(0)) }; } } public static class Net35Polyfill { public static void CopyToCompat(this Stream input, Stream output, long bytes = -1L, int bufferSize = 81920) { byte[] array = new byte[bufferSize]; if (bytes == -1) { bytes = long.MaxValue; } int num; while (bytes > 0 && (num = input.Read(array, 0, (int)Math.Min(array.Length, bytes))) > 0) { output.Write(array, 0, num); bytes -= num; } } public static bool HasFlag(Enum variable, Enum value) { if (variable == null) { return false; } if (value == null) { throw new ArgumentNullException("value"); } if (!Enum.IsDefined(variable.GetType(), value)) { throw new ArgumentException($"Enumeration type mismatch. The flag is of type '{value.GetType()}', was expecting '{variable.GetType()}'."); } ulong num = Convert.ToUInt64(value); return (Convert.ToUInt64(variable) & num) == num; } } public class ClassDatabaseToTypeTree { private ClassDatabaseFile cldbFile; private uint stringTablePos; private Dictionary stringTableLookup; private Dictionary commonStringTableLookup; private List typeTreeNodes; public static TypeTreeType Convert(ClassDatabaseFile classes, string name, bool preferEditor = false) { ClassDatabaseType classDatabaseType = classes.FindAssetClassByName(name); if (classDatabaseType == null) { return null; } return Convert(classes, classDatabaseType, preferEditor); } public static TypeTreeType Convert(ClassDatabaseFile classes, int id, bool preferEditor = false) { ClassDatabaseType classDatabaseType = classes.FindAssetClassByID(id); if (classDatabaseType == null) { return null; } return Convert(classes, classDatabaseType, preferEditor); } public static TypeTreeType Convert(ClassDatabaseFile classes, ClassDatabaseType type, bool preferEditor = false) { return new ClassDatabaseToTypeTree().ConvertInternal(classes, type, preferEditor); } private TypeTreeType ConvertInternal(ClassDatabaseFile classes, ClassDatabaseType type, bool preferEditor = false) { TypeTreeType typeTreeType = new TypeTreeType { TypeId = type.ClassId, ScriptTypeIndex = ushort.MaxValue, IsStrippedType = false, ScriptIdHash = Hash128.NewBlankHash(), TypeHash = Hash128.NewBlankHash(), TypeDependencies = new int[0] }; cldbFile = classes; stringTablePos = 0u; stringTableLookup = new Dictionary(); commonStringTableLookup = new Dictionary(); typeTreeNodes = new List(); InitializeDefaultStringTableIndices(); ClassDatabaseTypeNode preferredNode = type.GetPreferredNode(preferEditor); ConvertFields(preferredNode, 0); StringBuilder stringBuilder = new StringBuilder(); foreach (KeyValuePair item in stringTableLookup.OrderBy((KeyValuePair n) => n.Value).ToList()) { stringBuilder.Append(item.Key + "\0"); } typeTreeType.StringBuffer = stringBuilder.ToString(); typeTreeType.Nodes = typeTreeNodes; typeTreeType.TypeHash = ComputeHash(typeTreeType); return typeTreeType; } private void InitializeDefaultStringTableIndices() { int num = 0; foreach (ushort commonStringBufferIndex in cldbFile.CommonStringBufferIndices) { string @string = cldbFile.StringTable.GetString(commonStringBufferIndex); if (@string != string.Empty) { commonStringTableLookup.Add(@string, (uint)num); num += @string.Length + 1; } } } private void ConvertFields(ClassDatabaseTypeNode node, int depth) { string @string = cldbFile.GetString(node.FieldName); string string2 = cldbFile.GetString(node.TypeName); uint nameStrOffset; if (stringTableLookup.ContainsKey(@string)) { nameStrOffset = stringTableLookup[@string]; } else if (commonStringTableLookup.ContainsKey(@string)) { nameStrOffset = commonStringTableLookup[@string] + 2147483648u; } else { nameStrOffset = stringTablePos; stringTableLookup.Add(@string, stringTablePos); stringTablePos += (uint)(@string.Length + 1); } uint typeStrOffset; if (stringTableLookup.ContainsKey(string2)) { typeStrOffset = stringTableLookup[string2]; } else if (commonStringTableLookup.ContainsKey(string2)) { typeStrOffset = commonStringTableLookup[string2] + 2147483648u; } else { typeStrOffset = stringTablePos; stringTableLookup.Add(string2, stringTablePos); stringTablePos += (uint)(string2.Length + 1); } typeTreeNodes.Add(new TypeTreeNode { Level = (byte)depth, MetaFlags = node.MetaFlag, Index = (uint)typeTreeNodes.Count, TypeFlags = (TypeTreeNodeFlags)node.TypeFlags, NameStrOffset = nameStrOffset, ByteSize = node.ByteSize, TypeStrOffset = typeStrOffset, Version = node.Version }); foreach (ClassDatabaseTypeNode child in node.Children) { ConvertFields(child, depth + 1); } } private static Hash128 ComputeHash(TypeTreeType typeTree) { MD4 mD = new MD4(); Update(typeTree, mD); return new Hash128(mD.Digest()); } private static void Update(TypeTreeType typeTree, MD4 md4) { List.Enumerator enumerator = typeTree.Nodes.GetEnumerator(); while (enumerator.MoveNext()) { TypeTreeNode current = enumerator.Current; md4.Update(Encoding.UTF8.GetBytes(current.GetTypeString(typeTree.StringBufferBytes))); md4.Update(Encoding.UTF8.GetBytes(current.GetNameString(typeTree.StringBufferBytes))); md4.Update(BitConverter.GetBytes(current.ByteSize)); md4.Update(BitConverter.GetBytes(System.Convert.ToInt32(current.TypeFlags))); md4.Update(BitConverter.GetBytes(System.Convert.ToInt32(current.Version))); md4.Update(BitConverter.GetBytes(System.Convert.ToInt32(current.MetaFlags & 0x4000u))); } } } public class MD4 { private const int BLOCK_LENGTH = 64; private readonly uint[] X = new uint[16]; private readonly uint[] context = new uint[4]; private byte[] buffer = new byte[64]; private long count; public MD4() { EngineReset(); } private MD4(MD4 md) : this() { context = (uint[])md.context.Clone(); buffer = (byte[])md.buffer.Clone(); count = md.count; } public object Clone() { return new MD4(this); } public void Update(byte[] bytes) { EngineUpdate(bytes, 0, bytes.Length); } public byte[] Digest() { return EngineDigest(); } private void EngineReset() { context[0] = 1732584193u; context[1] = 4023233417u; context[2] = 2562383102u; context[3] = 271733878u; count = 0L; for (int i = 0; i < 64; i++) { buffer[i] = 0; } } private void EngineUpdate(byte b) { int num = (int)(count % 64); count++; buffer[num] = b; if (num == 63) { Transform(ref buffer, 0); } } private void EngineUpdate(byte[] input, int offset, int len) { if (offset < 0 || len < 0 || (long)offset + (long)len > input.Length) { throw new ArgumentOutOfRangeException(); } int num = (int)(count % 64); count += len; int num2 = 64 - num; int i = 0; if (len >= num2) { Array.Copy(input, offset + i, buffer, num, num2); Transform(ref buffer, 0); for (i = num2; i + 64 - 1 < len; i += 64) { Transform(ref input, offset + i); } num = 0; } if (i < len) { Array.Copy(input, offset + i, buffer, num, len - i); } } private byte[] EngineDigest() { int num = (int)(count % 64); int num2 = ((num < 56) ? (56 - num) : (120 - num)); byte[] array = new byte[num2 + 8]; array[0] = 128; for (int i = 0; i < 8; i++) { array[num2 + i] = (byte)(count * 8 >> 8 * i); } EngineUpdate(array, 0, array.Length); byte[] array2 = new byte[16]; for (int j = 0; j < 4; j++) { for (int k = 0; k < 4; k++) { array2[j * 4 + k] = (byte)(context[j] >> 8 * k); } } EngineReset(); return array2; } public byte[] GetByteHashFromString(string s) { byte[] bytes = Encoding.UTF8.GetBytes(s); MD4 mD = new MD4(); mD.EngineUpdate(bytes, 0, bytes.Length); return mD.EngineDigest(); } public byte[] GetByteHashFromBytes(byte[] b) { MD4 mD = new MD4(); mD.EngineUpdate(b, 0, b.Length); return mD.EngineDigest(); } public string GetHexHashFromBytes(byte[] b) { byte[] byteHashFromBytes = GetByteHashFromBytes(b); return BytesToHex(byteHashFromBytes, byteHashFromBytes.Length); } public byte[] GetByteHashFromByte(byte b) { MD4 mD = new MD4(); mD.EngineUpdate(b); return mD.EngineDigest(); } public string GetHexHashFromByte(byte b) { byte[] byteHashFromByte = GetByteHashFromByte(b); return BytesToHex(byteHashFromByte, byteHashFromByte.Length); } public string GetHexHashFromString(string s) { byte[] byteHashFromString = GetByteHashFromString(s); return BytesToHex(byteHashFromString, byteHashFromString.Length); } private static string BytesToHex(byte[] a, int len) { string text = BitConverter.ToString(a); StringBuilder stringBuilder = new StringBuilder((len - 2) / 2); for (int i = 0; i < text.Length; i++) { if (text[i] != '-') { stringBuilder.Append(text[i]); } } return stringBuilder.ToString(); } private void Transform(ref byte[] block, int offset) { for (int i = 0; i < 16; i++) { X[i] = (block[offset++] & 0xFFu) | (uint)((block[offset++] & 0xFF) << 8) | (uint)((block[offset++] & 0xFF) << 16) | (uint)((block[offset++] & 0xFF) << 24); } uint a = context[0]; uint num = context[1]; uint num2 = context[2]; uint num3 = context[3]; a = FF(a, num, num2, num3, X[0], 3); num3 = FF(num3, a, num, num2, X[1], 7); num2 = FF(num2, num3, a, num, X[2], 11); num = FF(num, num2, num3, a, X[3], 19); a = FF(a, num, num2, num3, X[4], 3); num3 = FF(num3, a, num, num2, X[5], 7); num2 = FF(num2, num3, a, num, X[6], 11); num = FF(num, num2, num3, a, X[7], 19); a = FF(a, num, num2, num3, X[8], 3); num3 = FF(num3, a, num, num2, X[9], 7); num2 = FF(num2, num3, a, num, X[10], 11); num = FF(num, num2, num3, a, X[11], 19); a = FF(a, num, num2, num3, X[12], 3); num3 = FF(num3, a, num, num2, X[13], 7); num2 = FF(num2, num3, a, num, X[14], 11); num = FF(num, num2, num3, a, X[15], 19); a = GG(a, num, num2, num3, X[0], 3); num3 = GG(num3, a, num, num2, X[4], 5); num2 = GG(num2, num3, a, num, X[8], 9); num = GG(num, num2, num3, a, X[12], 13); a = GG(a, num, num2, num3, X[1], 3); num3 = GG(num3, a, num, num2, X[5], 5); num2 = GG(num2, num3, a, num, X[9], 9); num = GG(num, num2, num3, a, X[13], 13); a = GG(a, num, num2, num3, X[2], 3); num3 = GG(num3, a, num, num2, X[6], 5); num2 = GG(num2, num3, a, num, X[10], 9); num = GG(num, num2, num3, a, X[14], 13); a = GG(a, num, num2, num3, X[3], 3); num3 = GG(num3, a, num, num2, X[7], 5); num2 = GG(num2, num3, a, num, X[11], 9); num = GG(num, num2, num3, a, X[15], 13); a = HH(a, num, num2, num3, X[0], 3); num3 = HH(num3, a, num, num2, X[8], 9); num2 = HH(num2, num3, a, num, X[4], 11); num = HH(num, num2, num3, a, X[12], 15); a = HH(a, num, num2, num3, X[2], 3); num3 = HH(num3, a, num, num2, X[10], 9); num2 = HH(num2, num3, a, num, X[6], 11); num = HH(num, num2, num3, a, X[14], 15); a = HH(a, num, num2, num3, X[1], 3); num3 = HH(num3, a, num, num2, X[9], 9); num2 = HH(num2, num3, a, num, X[5], 11); num = HH(num, num2, num3, a, X[13], 15); a = HH(a, num, num2, num3, X[3], 3); num3 = HH(num3, a, num, num2, X[11], 9); num2 = HH(num2, num3, a, num, X[7], 11); num = HH(num, num2, num3, a, X[15], 15); context[0] += a; context[1] += num; context[2] += num2; context[3] += num3; } private uint FF(uint a, uint b, uint c, uint d, uint x, int s) { uint num = a + ((b & c) | (~b & d)) + x; return (num << s) | (num >> 32 - s); } private uint GG(uint a, uint b, uint c, uint d, uint x, int s) { uint num = a + ((b & (c | d)) | (c & d)) + x + 1518500249; return (num << s) | (num >> 32 - s); } private uint HH(uint a, uint b, uint c, uint d, uint x, int s) { uint num = a + (b ^ c ^ d) + x + 1859775393; return (num << s) | (num >> 32 - s); } } public class TemplateFieldToTypeTree { private uint stringTablePos; private Dictionary stringTableLookup; private Dictionary commonStringTableLookup; private List typeTreeNodes; public static TypeTreeType Convert(AssetTypeTemplateField baseField, int typeId, ushort scriptIndex = ushort.MaxValue, string[] commonStrings = null) { return new TemplateFieldToTypeTree().ConvertInternal(baseField, typeId, scriptIndex, commonStrings); } public TypeTreeType ConvertInternal(AssetTypeTemplateField baseField, int typeId, ushort scriptIndex = ushort.MaxValue, string[] commonStrings = null) { TypeTreeType typeTreeType = new TypeTreeType { TypeId = typeId, ScriptTypeIndex = scriptIndex, IsStrippedType = false, ScriptIdHash = Hash128.NewBlankHash(), TypeHash = Hash128.NewBlankHash(), TypeDependencies = new int[0] }; stringTablePos = 0u; stringTableLookup = new Dictionary(); commonStringTableLookup = new Dictionary(); typeTreeNodes = new List(); InitializeDefaultStringTableIndices(commonStrings); ConvertFields(baseField, 0); StringBuilder stringBuilder = new StringBuilder(); foreach (KeyValuePair item in stringTableLookup.OrderBy((KeyValuePair n) => n.Value).ToList()) { stringBuilder.Append(item.Key + "\0"); } typeTreeType.StringBuffer = stringBuilder.ToString(); typeTreeType.Nodes = typeTreeNodes; typeTreeType.TypeHash = ComputeHash(typeTreeType); return typeTreeType; } private void InitializeDefaultStringTableIndices(string[] commonStrings) { int num = 0; if (commonStrings == null || commonStrings.Length == 0) { return; } foreach (string text in commonStrings) { if (text != string.Empty) { commonStringTableLookup.Add(text, (uint)num); num += text.Length + 1; } } } private void ConvertFields(AssetTypeTemplateField templateField, int depth) { string name = templateField.Name; string type = templateField.Type; uint nameStrOffset; if (stringTableLookup.ContainsKey(name)) { nameStrOffset = stringTableLookup[name]; } else if (commonStringTableLookup.ContainsKey(name)) { nameStrOffset = commonStringTableLookup[name] + 2147483648u; } else { nameStrOffset = stringTablePos; stringTableLookup.Add(name, stringTablePos); stringTablePos += (uint)(name.Length + 1); } uint typeStrOffset; if (stringTableLookup.ContainsKey(type)) { typeStrOffset = stringTableLookup[type]; } else if (commonStringTableLookup.ContainsKey(type)) { typeStrOffset = commonStringTableLookup[type] + 2147483648u; } else { typeStrOffset = stringTablePos; stringTableLookup.Add(type, stringTablePos); stringTablePos += (uint)(type.Length + 1); } typeTreeNodes.Add(new TypeTreeNode { Level = (byte)depth, MetaFlags = GetMetaFlags(templateField), Index = (uint)typeTreeNodes.Count, TypeFlags = GetTypeFlags(templateField), NameStrOffset = nameStrOffset, ByteSize = GetByteSize(templateField), TypeStrOffset = typeStrOffset, Version = templateField.Version }); foreach (AssetTypeTemplateField child in templateField.Children) { ConvertFields(child, depth + 1); } } private uint GetMetaFlags(AssetTypeTemplateField templateField) { if (!templateField.IsAligned) { return 0u; } return 16384u; } private TypeTreeNodeFlags GetTypeFlags(AssetTypeTemplateField templateField) { TypeTreeNodeFlags typeTreeNodeFlags = TypeTreeNodeFlags.None; if (templateField.IsArray) { typeTreeNodeFlags |= TypeTreeNodeFlags.Array; } return typeTreeNodeFlags; } private int GetByteSize(AssetTypeTemplateField templateField) { return templateField.ValueType switch { AssetValueType.Bool => 1, AssetValueType.Int8 => 1, AssetValueType.UInt8 => 1, AssetValueType.Int16 => 2, AssetValueType.UInt16 => 2, AssetValueType.Int32 => 4, AssetValueType.UInt32 => 4, AssetValueType.Int64 => 8, AssetValueType.UInt64 => 8, AssetValueType.Float => 4, AssetValueType.Double => 8, _ => -1, }; } private static Hash128 ComputeHash(TypeTreeType typeTree) { MD4 mD = new MD4(); Update(typeTree, mD); return new Hash128(mD.Digest()); } private static void Update(TypeTreeType typeTree, MD4 md4) { List.Enumerator enumerator = typeTree.Nodes.GetEnumerator(); while (enumerator.MoveNext()) { TypeTreeNode current = enumerator.Current; md4.Update(Encoding.UTF8.GetBytes(current.GetTypeString(typeTree.StringBufferBytes))); md4.Update(Encoding.UTF8.GetBytes(current.GetNameString(typeTree.StringBufferBytes))); md4.Update(BitConverter.GetBytes(current.ByteSize)); md4.Update(BitConverter.GetBytes(System.Convert.ToInt32(current.TypeFlags))); md4.Update(BitConverter.GetBytes(System.Convert.ToInt32(current.Version))); md4.Update(BitConverter.GetBytes(System.Convert.ToInt32(current.MetaFlags & 0x4000u))); } } } public class UnityVersion { public int major; public int minor; public int patch; public string type; public int typeNum; public UnityVersion() { } public UnityVersion(string version) { major = 0; minor = 0; patch = 0; type = ""; typeNum = 0; string[] array = version.Split(new char[1] { '.' }); if (array.Length >= 1) { major = int.Parse(array[0]); } if (array.Length >= 2) { minor = int.Parse(array[1]); } if (array.Length <= 2) { return; } int num = array[2].IndexOfAny(new char[7] { 'f', 'p', 'a', 'b', 'c', 't', 'x' }); if (num != -1) { type = array[2][num].ToString(); patch = int.Parse(array[2].Substring(0, num)); string text = array[2].Substring(num + 1); if (!int.TryParse(text, out typeNum)) { string text2 = ""; for (int i = 0; i < text.Length && text[i] >= '0' && text[i] <= '9'; i++) { text2 += text[i]; } if (text2.Length > 0) { typeNum = int.Parse(text2); } } } else { patch = int.Parse(array[2]); type = ""; typeNum = 0; } } public override string ToString() { if (type == string.Empty) { return $"{major}.{minor}.{patch}"; } return $"{major}.{minor}.{patch}{type}{typeNum}"; } public ulong ToUInt64() { byte b = type switch { "a" => 0, "b" => 1, "c" => 2, "f" => 3, "p" => 4, "x" => 5, _ => byte.MaxValue, }; return (ulong)(((long)major << 48) | ((long)minor << 32) | ((long)patch << 16)) | ((ulong)b << 8) | (uint)typeNum; } public static UnityVersion FromUInt64(ulong data) { UnityVersion unityVersion = new UnityVersion(); unityVersion.major = (int)((data >> 48) & 0xFFFF); unityVersion.minor = (int)((data >> 32) & 0xFFFF); unityVersion.patch = (int)((data >> 16) & 0xFFFF); UnityVersion unityVersion2 = unityVersion; ulong num = (data >> 8) & 0xFF; if (num > 5) { goto IL_00a2; } switch (num) { case 0uL: break; case 1uL: goto IL_007a; case 2uL: goto IL_0082; case 3uL: goto IL_008a; case 4uL: goto IL_0092; case 5uL: goto IL_009a; default: goto IL_00a2; } string text = "a"; goto IL_00a8; IL_00a8: unityVersion2.type = text; unityVersion.typeNum = (int)(data & 0xFF); return unityVersion; IL_00a2: text = "?"; goto IL_00a8; IL_009a: text = "x"; goto IL_00a8; IL_0092: text = "p"; goto IL_00a8; IL_008a: text = "f"; goto IL_00a8; IL_0082: text = "c"; goto IL_00a8; IL_007a: text = "b"; goto IL_00a8; } } public class ValueBuilder { public static AssetTypeValueField DefaultValueFieldFromArrayTemplate(AssetTypeValueField arrayField) { return DefaultValueFieldFromArrayTemplate(arrayField.TemplateField); } public static AssetTypeValueField DefaultValueFieldFromArrayTemplate(AssetTypeTemplateField arrayField) { if (!arrayField.IsArray) { return null; } return DefaultValueFieldFromTemplate(arrayField.Children[1]); } public static AssetTypeValueField DefaultValueFieldFromTemplate(AssetTypeTemplateField templateField) { List children = templateField.Children; List list; if (templateField.IsArray || templateField.ValueType == AssetValueType.String) { list = new List(0); } else { list = new List(children.Count); for (int i = 0; i < children.Count; i++) { list.Add(DefaultValueFieldFromTemplate(children[i])); } } AssetTypeValue value = DefaultValueFromTemplate(templateField); return new AssetTypeValueField { Children = list, TemplateField = templateField, Value = value }; } public static AssetTypeValue DefaultValueFromTemplate(AssetTypeTemplateField templateField) { object obj; switch (templateField.ValueType) { case AssetValueType.Int8: obj = (sbyte)0; break; case AssetValueType.UInt8: obj = (byte)0; break; case AssetValueType.Bool: obj = false; break; case AssetValueType.Int16: obj = (short)0; break; case AssetValueType.UInt16: obj = (ushort)0; break; case AssetValueType.Int32: obj = 0; break; case AssetValueType.UInt32: obj = 0u; break; case AssetValueType.Int64: obj = 0L; break; case AssetValueType.UInt64: obj = 0uL; break; case AssetValueType.Float: obj = 0f; break; case AssetValueType.Double: obj = 0.0; break; case AssetValueType.String: case AssetValueType.ByteArray: obj = new byte[0]; break; case AssetValueType.Array: obj = default(AssetTypeArrayInfo); break; case AssetValueType.ManagedReferencesRegistry: obj = new ManagedReferencesRegistry(); break; default: obj = null; break; } if (obj == null && templateField.IsArray) { obj = default(AssetTypeArrayInfo); return new AssetTypeValue(AssetValueType.Array, obj); } return new AssetTypeValue(templateField.ValueType, obj); } } } namespace AssetsTools.NET.Extra.Decompressors.LZ4 { public class Lz4DecoderStream : Stream { private enum DecodePhase { ReadToken, ReadExLiteralLength, CopyLiteral, ReadOffset, ReadExMatchLength, CopyMatch } private long inputLength; private Stream input; private const int DecBufLen = 65536; private const int DecBufMask = 65535; private const int InBufLen = 128; private byte[] decodeBuffer = new byte[65664]; private int decodeBufferPos; private int inBufPos; private int inBufEnd; private DecodePhase phase; private int litLen; private int matLen; private int matDst; public override bool CanRead => true; public override bool CanSeek => false; public override bool CanWrite => false; public override long Length { get { throw new NotSupportedException(); } } public override long Position { get { throw new NotSupportedException(); } set { throw new NotSupportedException(); } } public Lz4DecoderStream(Stream input, long inputLength = long.MaxValue) { Reset(input, inputLength); } public void Reset(Stream input, long inputLength = long.MaxValue) { this.inputLength = inputLength; this.input = input; phase = DecodePhase.ReadToken; decodeBufferPos = 0; litLen = 0; matLen = 0; matDst = 0; inBufPos = 65536; inBufEnd = 65536; } public override void Close() { input = null; } public override int Read(byte[] buffer, int offset, int count) { if (buffer == null) { throw new ArgumentNullException("buffer"); } if (offset < 0 || count < 0 || buffer.Length - count < offset) { throw new ArgumentOutOfRangeException(); } if (input == null) { throw new InvalidOperationException(); } int num = count; byte[] array = decodeBuffer; int num7; switch (phase) { default: { int num2; if (inBufPos < inBufEnd) { num2 = array[inBufPos++]; } else { num2 = ReadByteCore(); if (num2 == -1) { break; } } litLen = num2 >> 4; matLen = (num2 & 0xF) + 4; int num3 = litLen; if (num3 != 0) { if (num3 == 15) { phase = DecodePhase.ReadExLiteralLength; goto case DecodePhase.ReadExLiteralLength; } phase = DecodePhase.CopyLiteral; goto case DecodePhase.CopyLiteral; } phase = DecodePhase.ReadOffset; goto case DecodePhase.ReadOffset; } case DecodePhase.ReadExLiteralLength: while (true) { int num14; if (inBufPos < inBufEnd) { num14 = array[inBufPos++]; } else { num14 = ReadByteCore(); if (num14 == -1) { break; } } litLen += num14; if (num14 == 255) { continue; } goto IL_012e; } break; case DecodePhase.CopyLiteral: do { int num4 = ((litLen < num) ? litLen : num); if (num4 == 0) { break; } if (inBufPos + num4 <= inBufEnd) { int num5 = offset; int num6 = num4; while (num6-- != 0) { buffer[num5++] = array[inBufPos++]; } num7 = num4; } else { num7 = ReadCore(buffer, offset, num4); if (num7 == 0) { goto end_IL_0045; } } offset += num7; num -= num7; litLen -= num7; } while (litLen != 0); if (num == 0) { break; } phase = DecodePhase.ReadOffset; goto case DecodePhase.ReadOffset; case DecodePhase.ReadOffset: if (inBufPos + 1 < inBufEnd) { matDst = (array[inBufPos + 1] << 8) | array[inBufPos]; inBufPos += 2; } else { matDst = ReadOffsetCore(); if (matDst == -1) { break; } } if (matLen == 19) { phase = DecodePhase.ReadExMatchLength; goto case DecodePhase.ReadExMatchLength; } phase = DecodePhase.CopyMatch; goto case DecodePhase.CopyMatch; case DecodePhase.ReadExMatchLength: while (true) { int num13; if (inBufPos < inBufEnd) { num13 = array[inBufPos++]; } else { num13 = ReadByteCore(); if (num13 == -1) { break; } } matLen += num13; if (num13 == 255) { continue; } goto IL_0293; } break; case DecodePhase.CopyMatch: { int num8 = ((matLen < num) ? matLen : num); if (num8 != 0) { num7 = count - num; int num9 = matDst - num7; if (num9 > 0) { int num10 = decodeBufferPos - num9; if (num10 < 0) { num10 += 65536; } int num11 = ((num9 < num8) ? num9 : num8); while (num11-- != 0) { buffer[offset++] = array[num10++ & 0xFFFF]; } } else { num9 = 0; } int num12 = offset - matDst; for (int i = num9; i < num8; i++) { buffer[offset++] = buffer[num12++]; } num -= num8; matLen -= num8; } if (num == 0) { break; } phase = DecodePhase.ReadToken; goto default; } IL_0293: phase = DecodePhase.CopyMatch; goto case DecodePhase.CopyMatch; IL_012e: phase = DecodePhase.CopyLiteral; goto case DecodePhase.CopyLiteral; end_IL_0045: break; } num7 = count - num; int num15 = ((num7 < 65536) ? num7 : 65536); int srcOffset = offset - num15; if (num15 == 65536) { Buffer.BlockCopy(buffer, srcOffset, array, 0, 65536); decodeBufferPos = 0; } else { int num16 = decodeBufferPos; while (num15-- != 0) { array[num16++ & 0xFFFF] = buffer[srcOffset++]; } decodeBufferPos = num16 & 0xFFFF; } return num7; } private int ReadByteCore() { byte[] array = decodeBuffer; if (inBufPos == inBufEnd) { int num = input.Read(array, 65536, (int)((128 < inputLength) ? 128 : inputLength)); if (num == 0) { return -1; } inputLength -= num; inBufPos = 65536; inBufEnd = 65536 + num; } return array[inBufPos++]; } private int ReadOffsetCore() { byte[] array = decodeBuffer; if (inBufPos == inBufEnd) { int num = input.Read(array, 65536, (int)((128 < inputLength) ? 128 : inputLength)); if (num == 0) { return -1; } inputLength -= num; inBufPos = 65536; inBufEnd = 65536 + num; } if (inBufEnd - inBufPos == 1) { array[65536] = array[inBufPos]; int num2 = input.Read(array, 65537, (int)((127 < inputLength) ? 127 : inputLength)); if (num2 == 0) { inBufPos = 65536; inBufEnd = 65537; return -1; } inputLength -= num2; inBufPos = 65536; inBufEnd = 65536 + num2 + 1; } int result = (array[inBufPos + 1] << 8) | array[inBufPos]; inBufPos += 2; return result; } private int ReadCore(byte[] buffer, int offset, int count) { int num = count; byte[] array = decodeBuffer; int num2 = inBufEnd - inBufPos; int num3 = ((num < num2) ? num : num2); if (num3 != 0) { int num4 = inBufPos; int num5 = num3; while (num5-- != 0) { buffer[offset++] = array[num4++]; } inBufPos = num4; num -= num3; } if (num != 0) { int num6; if (num >= 128) { num6 = input.Read(buffer, offset, (int)((num < inputLength) ? num : inputLength)); num -= num6; } else { num6 = input.Read(array, 65536, (int)((128 < inputLength) ? 128 : inputLength)); inBufPos = 65536; inBufEnd = 65536 + num6; num3 = ((num < num6) ? num : num6); int num7 = inBufPos; int num8 = num3; while (num8-- != 0) { buffer[offset++] = array[num7++]; } inBufPos = num7; num -= num3; } inputLength -= num6; } return count - num; } public override void Flush() { } public override long Seek(long offset, SeekOrigin origin) { throw new NotSupportedException(); } public override void SetLength(long value) { throw new NotSupportedException(); } public override void Write(byte[] buffer, int offset, int count) { throw new NotSupportedException(); } } }