mirror of
https://github.com/Alex-Rachel/TEngine.git
synced 2025-08-14 16:51:28 +00:00
接入obfuz->2.0
This commit is contained in:
@@ -0,0 +1,14 @@
|
||||
using System;
|
||||
|
||||
namespace UnityFS
|
||||
{
|
||||
[Flags]
|
||||
public enum ArchiveFlags
|
||||
{
|
||||
CompressionTypeMask = 0x3f,
|
||||
BlocksAndDirectoryInfoCombined = 0x40,
|
||||
BlocksInfoAtTheEnd = 0x80,
|
||||
OldWebPluginCompatibility = 0x100,
|
||||
BlockInfoNeedPaddingAtStart = 0x200
|
||||
}
|
||||
}
|
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 099de05eebdffaf4baeed3290dc98aaf
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -0,0 +1,51 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
|
||||
namespace UnityFS
|
||||
{
|
||||
public static class BinaryReaderExtensions
|
||||
{
|
||||
|
||||
public static void AlignStream(this BinaryReader reader, int alignment)
|
||||
{
|
||||
var pos = reader.BaseStream.Position;
|
||||
var mod = pos % alignment;
|
||||
if (mod != 0)
|
||||
{
|
||||
reader.BaseStream.Position += alignment - mod;
|
||||
}
|
||||
}
|
||||
|
||||
public static string ReadAlignedString(this BinaryReader reader)
|
||||
{
|
||||
var length = reader.ReadInt32();
|
||||
if (length > 0 && length <= reader.BaseStream.Length - reader.BaseStream.Position)
|
||||
{
|
||||
var stringData = reader.ReadBytes(length);
|
||||
var result = Encoding.UTF8.GetString(stringData);
|
||||
reader.AlignStream(4);
|
||||
return result;
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
public static string ReadStringToNull(this BinaryReader reader, int maxLength = 32767)
|
||||
{
|
||||
var bytes = new List<byte>();
|
||||
int count = 0;
|
||||
while (reader.BaseStream.Position != reader.BaseStream.Length && count < maxLength)
|
||||
{
|
||||
var b = reader.ReadByte();
|
||||
if (b == 0)
|
||||
{
|
||||
break;
|
||||
}
|
||||
bytes.Add(b);
|
||||
count++;
|
||||
}
|
||||
return Encoding.UTF8.GetString(bytes.ToArray());
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: a84e427dd05adde44b4345b3fd49007a
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -0,0 +1,33 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
|
||||
namespace UnityFS
|
||||
{
|
||||
public static class BinaryWriterExtensions
|
||||
{
|
||||
public static void AlignStream(this BinaryWriter writer, int alignment)
|
||||
{
|
||||
var pos = writer.BaseStream.Position;
|
||||
var mod = pos % alignment;
|
||||
if (mod != 0)
|
||||
{
|
||||
writer.Write(new byte[alignment - mod]);
|
||||
}
|
||||
}
|
||||
|
||||
public static void WriteAlignedString(this BinaryWriter writer, string str)
|
||||
{
|
||||
var bytes = Encoding.UTF8.GetBytes(str);
|
||||
writer.Write(bytes.Length);
|
||||
writer.Write(bytes);
|
||||
writer.AlignStream(4);
|
||||
}
|
||||
|
||||
public static void WriteNullEndString(this BinaryWriter writer, string str)
|
||||
{
|
||||
writer.Write(Encoding.UTF8.GetBytes(str));
|
||||
writer.Write((byte)0);
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 02eda21769c083346a5bd9b7dca49427
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -0,0 +1,24 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace UnityFS
|
||||
{
|
||||
public class BundleSubFile
|
||||
{
|
||||
public string file;
|
||||
public byte[] data;
|
||||
}
|
||||
|
||||
public class BundleFileInfo
|
||||
{
|
||||
public string signature;
|
||||
public uint version;
|
||||
public string unityVersion;
|
||||
public string unityRevision;
|
||||
public ArchiveFlags flags;
|
||||
public List<BundleSubFile> files;
|
||||
}
|
||||
}
|
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 1a72550a949e322419b9c5d6e4fe495d
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -0,0 +1,212 @@
|
||||
using LZ4;
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using UnityEngine;
|
||||
|
||||
namespace UnityFS
|
||||
{
|
||||
|
||||
public class BundleFileReader
|
||||
{
|
||||
|
||||
private Header m_Header;
|
||||
private StorageBlock[] m_BlocksInfo;
|
||||
private Node[] m_DirectoryInfo;
|
||||
|
||||
private StreamFile[] fileList;
|
||||
|
||||
public BundleFileReader()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public void Load(EndianBinaryReader reader)
|
||||
{
|
||||
Debug.Log($"reader. pos:{reader.Position} length:{reader.BaseStream.Length}");
|
||||
m_Header = new Header();
|
||||
m_Header.signature = reader.ReadStringToNull();
|
||||
m_Header.version = reader.ReadUInt32();
|
||||
m_Header.unityVersion = reader.ReadStringToNull();
|
||||
m_Header.unityRevision = reader.ReadStringToNull();
|
||||
System.Diagnostics.Debug.Assert(m_Header.signature == "UnityFS");
|
||||
|
||||
|
||||
m_Header.size = reader.ReadInt64();
|
||||
Debug.Log($"header size:{m_Header.size}");
|
||||
m_Header.compressedBlocksInfoSize = reader.ReadUInt32();
|
||||
m_Header.uncompressedBlocksInfoSize = reader.ReadUInt32();
|
||||
m_Header.flags = (ArchiveFlags)reader.ReadUInt32();
|
||||
if (m_Header.signature != "UnityFS")
|
||||
{
|
||||
reader.ReadByte();
|
||||
}
|
||||
|
||||
ReadMetadata(reader);
|
||||
using (var blocksStream = CreateBlocksStream())
|
||||
{
|
||||
ReadBlocks(reader, blocksStream);
|
||||
ReadFiles(blocksStream);
|
||||
}
|
||||
}
|
||||
|
||||
public BundleFileInfo CreateBundleFileInfo()
|
||||
{
|
||||
return new BundleFileInfo
|
||||
{
|
||||
signature = m_Header.signature,
|
||||
version = m_Header.version,
|
||||
unityVersion = m_Header.unityVersion,
|
||||
unityRevision = m_Header.unityRevision,
|
||||
files = fileList.Select(f => new BundleSubFile { file = f.path, data = f.stream.ReadAllBytes() }).ToList(),
|
||||
};
|
||||
}
|
||||
|
||||
private byte[] ReadBlocksInfoAndDirectoryMetadataUnCompressedBytes(EndianBinaryReader reader)
|
||||
{
|
||||
byte[] metadataUncompressBytes;
|
||||
if (m_Header.version >= 7)
|
||||
{
|
||||
reader.AlignStream(16);
|
||||
}
|
||||
if ((m_Header.flags & ArchiveFlags.BlocksInfoAtTheEnd) != 0)
|
||||
{
|
||||
var position = reader.Position;
|
||||
reader.Position = reader.BaseStream.Length - m_Header.compressedBlocksInfoSize;
|
||||
metadataUncompressBytes = reader.ReadBytes((int)m_Header.compressedBlocksInfoSize);
|
||||
reader.Position = position;
|
||||
}
|
||||
else //0x40 BlocksAndDirectoryInfoCombined
|
||||
{
|
||||
metadataUncompressBytes = reader.ReadBytes((int)m_Header.compressedBlocksInfoSize);
|
||||
}
|
||||
return metadataUncompressBytes;
|
||||
}
|
||||
|
||||
private byte[] DecompressBytes(CompressionType compressionType, byte[] compressedBytes, uint uncompressedSize)
|
||||
{
|
||||
switch (compressionType)
|
||||
{
|
||||
case CompressionType.None:
|
||||
{
|
||||
return compressedBytes;
|
||||
}
|
||||
case CompressionType.Lzma:
|
||||
{
|
||||
var uncompressedStream = new MemoryStream((int)(uncompressedSize));
|
||||
using (var compressedStream = new MemoryStream(compressedBytes))
|
||||
{
|
||||
ComparessHelper.Decompress7Zip(compressedStream, uncompressedStream, m_Header.compressedBlocksInfoSize, m_Header.uncompressedBlocksInfoSize);
|
||||
}
|
||||
return uncompressedStream.ReadAllBytes();
|
||||
}
|
||||
case CompressionType.Lz4:
|
||||
case CompressionType.Lz4HC:
|
||||
{
|
||||
var uncompressedBytes = new byte[uncompressedSize];
|
||||
var numWrite = LZ4Codec.Decode(compressedBytes, 0, compressedBytes.Length, uncompressedBytes, 0, uncompressedBytes.Length, true);
|
||||
if (numWrite != uncompressedSize)
|
||||
{
|
||||
throw new IOException($"Lz4 decompression error, write {numWrite} bytes but expected {uncompressedSize} bytes");
|
||||
}
|
||||
return uncompressedBytes;
|
||||
}
|
||||
default:
|
||||
throw new IOException($"Unsupported compression type {compressionType}");
|
||||
}
|
||||
}
|
||||
|
||||
private void ReadMetadata(EndianBinaryReader reader)
|
||||
{
|
||||
byte[] compressMetadataBytes = ReadBlocksInfoAndDirectoryMetadataUnCompressedBytes(reader);
|
||||
MemoryStream metadataStream = new MemoryStream(DecompressBytes((CompressionType)(m_Header.flags & ArchiveFlags.CompressionTypeMask), compressMetadataBytes, m_Header.uncompressedBlocksInfoSize));
|
||||
using (var blocksInfoReader = new EndianBinaryReader(metadataStream))
|
||||
{
|
||||
var uncompressedDataHash = blocksInfoReader.ReadBytes(16);
|
||||
var blocksInfoCount = blocksInfoReader.ReadInt32();
|
||||
m_BlocksInfo = new StorageBlock[blocksInfoCount];
|
||||
for (int i = 0; i < blocksInfoCount; i++)
|
||||
{
|
||||
m_BlocksInfo[i] = new StorageBlock
|
||||
{
|
||||
uncompressedSize = blocksInfoReader.ReadUInt32(),
|
||||
compressedSize = blocksInfoReader.ReadUInt32(),
|
||||
flags = (StorageBlockFlags)blocksInfoReader.ReadUInt16()
|
||||
};
|
||||
}
|
||||
|
||||
var nodesCount = blocksInfoReader.ReadInt32();
|
||||
m_DirectoryInfo = new Node[nodesCount];
|
||||
for (int i = 0; i < nodesCount; i++)
|
||||
{
|
||||
m_DirectoryInfo[i] = new Node
|
||||
{
|
||||
offset = blocksInfoReader.ReadInt64(),
|
||||
size = blocksInfoReader.ReadInt64(),
|
||||
flags = blocksInfoReader.ReadUInt32(),
|
||||
path = blocksInfoReader.ReadStringToNull(),
|
||||
};
|
||||
}
|
||||
}
|
||||
if (m_Header.flags.HasFlag(ArchiveFlags.BlockInfoNeedPaddingAtStart))
|
||||
{
|
||||
reader.AlignStream(16);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private Stream CreateBlocksStream()
|
||||
{
|
||||
Stream blocksStream;
|
||||
var uncompressedSizeSum = m_BlocksInfo.Sum(x => x.uncompressedSize);
|
||||
if (uncompressedSizeSum >= int.MaxValue)
|
||||
{
|
||||
throw new Exception($"too fig file");
|
||||
}
|
||||
else
|
||||
{
|
||||
blocksStream = new MemoryStream((int)uncompressedSizeSum);
|
||||
}
|
||||
return blocksStream;
|
||||
}
|
||||
|
||||
public void ReadFiles(Stream blocksStream)
|
||||
{
|
||||
fileList = new StreamFile[m_DirectoryInfo.Length];
|
||||
for (int i = 0; i < m_DirectoryInfo.Length; i++)
|
||||
{
|
||||
var node = m_DirectoryInfo[i];
|
||||
var file = new StreamFile();
|
||||
fileList[i] = file;
|
||||
file.path = node.path;
|
||||
file.fileName = Path.GetFileName(node.path);
|
||||
if (node.size >= int.MaxValue)
|
||||
{
|
||||
throw new Exception($"exceed max file size");
|
||||
/*var memoryMappedFile = MemoryMappedFile.CreateNew(null, entryinfo_size);
|
||||
file.stream = memoryMappedFile.CreateViewStream();*/
|
||||
//var extractPath = path + "_unpacked" + Path.DirectorySeparatorChar;
|
||||
//Directory.CreateDirectory(extractPath);
|
||||
//file.stream = new FileStream(extractPath + file.fileName, FileMode.Create, FileAccess.ReadWrite, FileShare.ReadWrite);
|
||||
}
|
||||
file.stream = new MemoryStream((int)node.size);
|
||||
blocksStream.Position = node.offset;
|
||||
blocksStream.CopyTo(file.stream, node.size);
|
||||
file.stream.Position = 0;
|
||||
}
|
||||
}
|
||||
|
||||
private void ReadBlocks(EndianBinaryReader reader, Stream blocksStream)
|
||||
{
|
||||
foreach (var blockInfo in m_BlocksInfo)
|
||||
{
|
||||
var compressedSize = (int)blockInfo.compressedSize;
|
||||
byte[] compressedBlockBytes = reader.ReadBytes(compressedSize);
|
||||
var compressionType = (CompressionType)(blockInfo.flags & StorageBlockFlags.CompressionTypeMask);
|
||||
byte[] uncompressedBlockBytes = DecompressBytes(compressionType, compressedBlockBytes, blockInfo.uncompressedSize);
|
||||
blocksStream.Write(uncompressedBlockBytes, 0, uncompressedBlockBytes.Length);
|
||||
}
|
||||
blocksStream.Position = 0;
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: f9b938458cc610e4d8a910c4499693cf
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -0,0 +1,112 @@
|
||||
using LZ4;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using UnityEngine;
|
||||
|
||||
namespace UnityFS
|
||||
{
|
||||
public class BundleFileWriter
|
||||
{
|
||||
private readonly BundleFileInfo _bundle;
|
||||
|
||||
private readonly List<Node> _files = new List<Node>();
|
||||
private readonly List<StorageBlock> _blocks = new List<StorageBlock>();
|
||||
|
||||
private readonly EndianBinaryWriter _blockDirectoryMetadataStream = new EndianBinaryWriter(new MemoryStream());
|
||||
private byte[] _blockBytes;
|
||||
|
||||
public BundleFileWriter(BundleFileInfo bundle)
|
||||
{
|
||||
_bundle = bundle;
|
||||
}
|
||||
|
||||
public void Write(EndianBinaryWriter output)
|
||||
{
|
||||
InitBlockAndDirectories();
|
||||
|
||||
output.WriteNullEndString(_bundle.signature);
|
||||
output.Write(_bundle.version);
|
||||
output.WriteNullEndString(_bundle.unityVersion);
|
||||
output.WriteNullEndString(_bundle.unityRevision);
|
||||
|
||||
BuildBlockDirectoryMetadata();
|
||||
|
||||
|
||||
long sizePos = output.Position;
|
||||
output.Write(0L);
|
||||
output.Write((uint)_blockDirectoryMetadataStream.Length);
|
||||
output.Write((uint)_blockDirectoryMetadataStream.Length);
|
||||
ArchiveFlags flags = ArchiveFlags.BlocksAndDirectoryInfoCombined | (uint)CompressionType.None;
|
||||
output.Write((uint)flags);
|
||||
|
||||
if (_bundle.version >= 7)
|
||||
{
|
||||
output.AlignStream(16);
|
||||
}
|
||||
byte[] metadataBytes = _blockDirectoryMetadataStream.BaseStream.ReadAllBytes();
|
||||
output.Write(metadataBytes, 0, metadataBytes.Length);
|
||||
|
||||
byte[] dataBytes = _blockBytes;
|
||||
output.Write(dataBytes, 0, dataBytes.Length);
|
||||
|
||||
output.Position = sizePos;
|
||||
output.Write(output.Length);
|
||||
}
|
||||
|
||||
private void InitBlockAndDirectories()
|
||||
{
|
||||
var dataStream = new MemoryStream();
|
||||
foreach(var file in _bundle.files)
|
||||
{
|
||||
byte[] data = file.data;
|
||||
_files.Add(new Node { path = file.file, flags = 0, offset = dataStream.Length, size = data.LongLength });
|
||||
dataStream.Write(data, 0, data.Length);
|
||||
}
|
||||
byte[] dataBytes = dataStream.ToArray();
|
||||
|
||||
var compressedBlockStream = new MemoryStream(dataBytes.Length / 2);
|
||||
int blockByteSize = 128 * 1024;
|
||||
long dataSize = dataBytes.Length;
|
||||
byte[] tempCompressBlock = new byte[blockByteSize * 2];
|
||||
for(long i = 0, blockNum = (dataSize + blockByteSize - 1) / blockByteSize; i < blockNum; i++)
|
||||
{
|
||||
long curBlockSize = Math.Min(dataSize, blockByteSize);
|
||||
dataSize -= curBlockSize;
|
||||
|
||||
int compressedSize = LZ4Codec.Encode(dataBytes, (int)(i * blockByteSize), (int)curBlockSize, tempCompressBlock, 0, tempCompressBlock.Length);
|
||||
compressedBlockStream.Write(tempCompressBlock, 0, compressedSize);
|
||||
_blocks.Add(new StorageBlock { flags = (StorageBlockFlags)(int)CompressionType.Lz4, compressedSize = (uint)compressedSize, uncompressedSize = (uint)curBlockSize });
|
||||
//Debug.Log($"== block[{i}] uncompressedSize:{curBlockSize} compressedSize:{compressedSize} totalblocksize:{compressedBlockStream.Length}");
|
||||
}
|
||||
_blockBytes = compressedBlockStream.ToArray();
|
||||
}
|
||||
|
||||
private void BuildBlockDirectoryMetadata()
|
||||
{
|
||||
var hash = new byte[16];
|
||||
_blockDirectoryMetadataStream.Write(hash, 0, 16);
|
||||
|
||||
_blockDirectoryMetadataStream.Write((uint)_blocks.Count);
|
||||
foreach(var b in _blocks)
|
||||
{
|
||||
_blockDirectoryMetadataStream.Write(b.uncompressedSize);
|
||||
_blockDirectoryMetadataStream.Write(b.compressedSize);
|
||||
_blockDirectoryMetadataStream.Write((ushort)b.flags);
|
||||
}
|
||||
|
||||
_blockDirectoryMetadataStream.Write((uint)_files.Count);
|
||||
foreach(var f in _files)
|
||||
{
|
||||
_blockDirectoryMetadataStream.Write(f.offset);
|
||||
_blockDirectoryMetadataStream.Write(f.size);
|
||||
_blockDirectoryMetadataStream.Write(f.flags);
|
||||
_blockDirectoryMetadataStream.WriteNullEndString(f.path);
|
||||
}
|
||||
//Debug.Log($"block and directory metadata size:{_blockDirectoryMetadataStream.Length}");
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: d6705163b267de54a868f5e84f6c7024
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -0,0 +1,11 @@
|
||||
namespace UnityFS
|
||||
{
|
||||
public enum CompressionType
|
||||
{
|
||||
None,
|
||||
Lzma,
|
||||
Lz4,
|
||||
Lz4HC,
|
||||
Lzham
|
||||
}
|
||||
}
|
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: ddcd6644c83d2a94f9668d6e913bd80e
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -0,0 +1,107 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
|
||||
namespace UnityFS
|
||||
{
|
||||
public class EndianBinaryReader : BinaryReader
|
||||
{
|
||||
private readonly byte[] buffer;
|
||||
|
||||
public EndianType Endian;
|
||||
|
||||
public EndianBinaryReader(Stream stream, EndianType endian = EndianType.BigEndian) : base(stream)
|
||||
{
|
||||
Endian = endian;
|
||||
buffer = new byte[8];
|
||||
}
|
||||
|
||||
public long Position
|
||||
{
|
||||
get => BaseStream.Position;
|
||||
set => BaseStream.Position = value;
|
||||
}
|
||||
|
||||
private unsafe void ReadBufferBigEndian(byte* dst, byte[] src, int size)
|
||||
{
|
||||
System.Diagnostics.Debug.Assert(BitConverter.IsLittleEndian);
|
||||
for (int i = 0; i < size; i++)
|
||||
{
|
||||
dst[i] = src[size - i - 1];
|
||||
}
|
||||
}
|
||||
|
||||
public override short ReadInt16()
|
||||
{
|
||||
return (short)ReadUInt16();
|
||||
}
|
||||
|
||||
public unsafe override ushort ReadUInt16()
|
||||
{
|
||||
if (Endian == EndianType.BigEndian)
|
||||
{
|
||||
Read(buffer, 0, 2);
|
||||
ushort x = 0;
|
||||
ReadBufferBigEndian((byte*)&x, buffer, 2);
|
||||
return x;
|
||||
}
|
||||
return base.ReadUInt16();
|
||||
}
|
||||
|
||||
public override int ReadInt32()
|
||||
{
|
||||
return (int)ReadUInt32();
|
||||
}
|
||||
|
||||
public unsafe override uint ReadUInt32()
|
||||
{
|
||||
if (Endian == EndianType.BigEndian)
|
||||
{
|
||||
Read(buffer, 0, 4);
|
||||
uint x = 0;
|
||||
ReadBufferBigEndian((byte*)&x, buffer, 4);
|
||||
return x;
|
||||
}
|
||||
return base.ReadUInt32();
|
||||
}
|
||||
|
||||
public override long ReadInt64()
|
||||
{
|
||||
return (long)ReadUInt64();
|
||||
}
|
||||
|
||||
public unsafe override ulong ReadUInt64()
|
||||
{
|
||||
if (Endian == EndianType.BigEndian)
|
||||
{
|
||||
Read(buffer, 0, 8);
|
||||
|
||||
ulong x = 0;
|
||||
ReadBufferBigEndian((byte*)&x, buffer, 8);
|
||||
return x;
|
||||
}
|
||||
return base.ReadUInt64();
|
||||
}
|
||||
|
||||
public override float ReadSingle()
|
||||
{
|
||||
if (Endian == EndianType.BigEndian)
|
||||
{
|
||||
Read(buffer, 0, 4);
|
||||
Array.Reverse(buffer, 0, 4);
|
||||
return BitConverter.ToSingle(buffer, 0);
|
||||
}
|
||||
return base.ReadSingle();
|
||||
}
|
||||
|
||||
public override double ReadDouble()
|
||||
{
|
||||
if (Endian == EndianType.BigEndian)
|
||||
{
|
||||
Read(buffer, 0, 8);
|
||||
Array.Reverse(buffer);
|
||||
return BitConverter.ToDouble(buffer, 0);
|
||||
}
|
||||
return base.ReadDouble();
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 9b02c037f0fa1014da65773804248d8d
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -0,0 +1,107 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
|
||||
namespace UnityFS
|
||||
{
|
||||
public class EndianBinaryWriter : BinaryWriter
|
||||
{
|
||||
private readonly byte[] buffer;
|
||||
|
||||
public EndianType Endian;
|
||||
|
||||
public EndianBinaryWriter(Stream stream, EndianType endian = EndianType.BigEndian) : base(stream)
|
||||
{
|
||||
Endian = endian;
|
||||
buffer = new byte[8];
|
||||
}
|
||||
|
||||
public long Position
|
||||
{
|
||||
get => BaseStream.Position;
|
||||
set => BaseStream.Position = value;
|
||||
}
|
||||
|
||||
public long Length => BaseStream.Length;
|
||||
|
||||
public override void Write(short x)
|
||||
{
|
||||
Write((ushort)x);
|
||||
}
|
||||
|
||||
private unsafe void WriteBufferBigEndian(byte[] dst, byte* src, int size)
|
||||
{
|
||||
System.Diagnostics.Debug.Assert(BitConverter.IsLittleEndian);
|
||||
for(int i = 0; i < size; i++)
|
||||
{
|
||||
dst[i] = src[size - i - 1];
|
||||
}
|
||||
}
|
||||
|
||||
public unsafe override void Write(ushort x)
|
||||
{
|
||||
if (Endian == EndianType.BigEndian)
|
||||
{
|
||||
WriteBufferBigEndian(buffer, (byte*)&x, 2);
|
||||
Write(buffer, 0, 2);
|
||||
return;
|
||||
}
|
||||
base.Write(x);
|
||||
}
|
||||
|
||||
public override void Write(int x)
|
||||
{
|
||||
Write((uint)x);
|
||||
}
|
||||
|
||||
public unsafe override void Write(uint x)
|
||||
{
|
||||
if (Endian == EndianType.BigEndian)
|
||||
{
|
||||
WriteBufferBigEndian(buffer, (byte*)&x, 4);
|
||||
Write(buffer, 0, 4);
|
||||
return;
|
||||
}
|
||||
base.Write(x);
|
||||
}
|
||||
|
||||
public override void Write(long x)
|
||||
{
|
||||
Write((ulong)x);
|
||||
}
|
||||
|
||||
public unsafe override void Write(ulong x)
|
||||
{
|
||||
if (Endian == EndianType.BigEndian)
|
||||
{
|
||||
WriteBufferBigEndian(buffer, (byte*)&x, 8);
|
||||
Write(buffer, 0, 8);
|
||||
return;
|
||||
}
|
||||
base.Write(x);
|
||||
}
|
||||
|
||||
public override void Write(float x)
|
||||
{
|
||||
if (Endian == EndianType.BigEndian)
|
||||
{
|
||||
var buf = BitConverter.GetBytes(x);
|
||||
Array.Reverse(buf, 0, 4);
|
||||
Write(buf, 0, 4);
|
||||
return;
|
||||
}
|
||||
base.Write(x);
|
||||
}
|
||||
|
||||
public override void Write(double x)
|
||||
{
|
||||
if (Endian == EndianType.BigEndian)
|
||||
{
|
||||
var buf = BitConverter.GetBytes(x);
|
||||
Array.Reverse(buf, 0, 8);
|
||||
Write(buf, 0, 8);
|
||||
return;
|
||||
}
|
||||
base.Write(x);
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 4107364c7434b2042ad647b28e322513
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -0,0 +1,14 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace UnityFS
|
||||
{
|
||||
public enum EndianType
|
||||
{
|
||||
LittleEndian,
|
||||
BigEndian
|
||||
}
|
||||
}
|
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 2f6cbab4506c18248b410a164be891d2
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -0,0 +1,53 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using UnityEngine;
|
||||
using UnityFS;
|
||||
|
||||
namespace HybridCLR.Editor.UnityBinFileReader
|
||||
{
|
||||
public class Dataunity3dPatcher
|
||||
{
|
||||
|
||||
public void ApplyPatch(string dataunity3dFile, List<string> hotUpdateAssemblies)
|
||||
{
|
||||
var reader = new BundleFileReader();
|
||||
using (var fs = new EndianBinaryReader(new MemoryStream(File.ReadAllBytes(dataunity3dFile))))
|
||||
{
|
||||
reader.Load(fs);
|
||||
}
|
||||
|
||||
var info = reader.CreateBundleFileInfo();
|
||||
//Debug.Log($"name:{info.signature} version:{info.version} files:{info.files.Count}");
|
||||
//foreach (var file in info.files)
|
||||
//{
|
||||
// Debug.Log($"file:{file.file} size:{file.data.Length}");
|
||||
//}
|
||||
|
||||
var globalgamemanagersFile = info.files.Find(f => f.file == "globalgamemanagers");
|
||||
//Debug.LogFormat("gobalgamemanagers origin size:{0}", globalgamemanagersFile.data.Length);
|
||||
|
||||
var ggdBinFile = new UnityBinFile();
|
||||
ggdBinFile.LoadFromStream(new MemoryStream(globalgamemanagersFile.data));
|
||||
ggdBinFile.AddScriptingAssemblies(hotUpdateAssemblies);
|
||||
byte[] patchedGlobalgamedatasBytes = ggdBinFile.CreatePatchedBytes();
|
||||
//Debug.LogFormat("gobalgamemanagers post patche size:{0}", patchedGlobalgamedatasBytes.Length);
|
||||
globalgamemanagersFile.data = patchedGlobalgamedatasBytes;
|
||||
|
||||
var writer = new BundleFileWriter(info);
|
||||
var output = new MemoryStream();
|
||||
writer.Write(new EndianBinaryWriter(output));
|
||||
Debug.Log($"patch file:{dataunity3dFile} size:{output.Length}");
|
||||
|
||||
//string bakFile = dataunity3dFile + ".bak";
|
||||
//if (!File.Exists(bakFile))
|
||||
//{
|
||||
// File.Copy(dataunity3dFile, bakFile);
|
||||
//}
|
||||
File.WriteAllBytes(dataunity3dFile, output.ToArray());
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 653a22d285c79f44a8113c5571b2d26b
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -0,0 +1,14 @@
|
||||
namespace UnityFS
|
||||
{
|
||||
public class Header
|
||||
{
|
||||
public string signature;
|
||||
public uint version;
|
||||
public string unityVersion;
|
||||
public string unityRevision;
|
||||
public long size;
|
||||
public uint compressedBlocksInfoSize;
|
||||
public uint uncompressedBlocksInfoSize;
|
||||
public ArchiveFlags flags;
|
||||
}
|
||||
}
|
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: f121e0520fa65c240884d43fd00b3c2a
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -0,0 +1,10 @@
|
||||
namespace UnityFS
|
||||
{
|
||||
public class Node
|
||||
{
|
||||
public long offset;
|
||||
public long size;
|
||||
public uint flags;
|
||||
public string path;
|
||||
}
|
||||
}
|
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: e3eea8a6a32b6ac4ba609b39715e25e2
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -0,0 +1,50 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using UnityEngine;
|
||||
|
||||
namespace UnityFS
|
||||
{
|
||||
public class ScriptingAssembliesJsonPatcher
|
||||
{
|
||||
[Serializable]
|
||||
private class ScriptingAssemblies
|
||||
{
|
||||
public List<string> names;
|
||||
public List<int> types;
|
||||
}
|
||||
|
||||
private string _file;
|
||||
ScriptingAssemblies _scriptingAssemblies;
|
||||
|
||||
public void Load(string file)
|
||||
{
|
||||
_file = file;
|
||||
string content = File.ReadAllText(file);
|
||||
_scriptingAssemblies = JsonUtility.FromJson<ScriptingAssemblies>(content);
|
||||
}
|
||||
|
||||
public void AddScriptingAssemblies(List<string> assemblies)
|
||||
{
|
||||
foreach (string name in assemblies)
|
||||
{
|
||||
if (!_scriptingAssemblies.names.Contains(name))
|
||||
{
|
||||
_scriptingAssemblies.names.Add(name);
|
||||
_scriptingAssemblies.types.Add(16); // user dll type
|
||||
Debug.Log($"[PatchScriptAssembliesJson] add hotfix assembly:{name} to {_file}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void Save(string jsonFile)
|
||||
{
|
||||
string content = JsonUtility.ToJson(_scriptingAssemblies);
|
||||
|
||||
File.WriteAllText(jsonFile, content);
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 8f2dd29d56a640d4ebd1c2fd374b7638
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -0,0 +1,49 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using SevenZip.Compression.LZMA;
|
||||
|
||||
|
||||
namespace UnityFS
|
||||
{
|
||||
public static class ComparessHelper
|
||||
{
|
||||
public static MemoryStream Decompress7Zip(MemoryStream inStream)
|
||||
{
|
||||
var decoder = new Decoder();
|
||||
|
||||
inStream.Seek(0, SeekOrigin.Begin);
|
||||
var newOutStream = new MemoryStream();
|
||||
|
||||
var properties = new byte[5];
|
||||
if (inStream.Read(properties, 0, 5) != 5)
|
||||
throw new Exception("input .lzma is too short");
|
||||
long outSize = 0;
|
||||
for (var i = 0; i < 8; i++)
|
||||
{
|
||||
var v = inStream.ReadByte();
|
||||
if (v < 0)
|
||||
throw new Exception("Can't Read 1");
|
||||
outSize |= ((long)(byte)v) << (8 * i);
|
||||
}
|
||||
decoder.SetDecoderProperties(properties);
|
||||
|
||||
var compressedSize = inStream.Length - inStream.Position;
|
||||
decoder.Code(inStream, newOutStream, compressedSize, outSize, null);
|
||||
|
||||
newOutStream.Position = 0;
|
||||
return newOutStream;
|
||||
}
|
||||
|
||||
public static void Decompress7Zip(Stream compressedStream, Stream decompressedStream, long compressedSize, long decompressedSize)
|
||||
{
|
||||
var basePosition = compressedStream.Position;
|
||||
var decoder = new Decoder();
|
||||
var properties = new byte[5];
|
||||
if (compressedStream.Read(properties, 0, 5) != 5)
|
||||
throw new Exception("input .lzma is too short");
|
||||
decoder.SetDecoderProperties(properties);
|
||||
decoder.Code(compressedStream, decompressedStream, compressedSize - 5, decompressedSize, null);
|
||||
compressedStream.Position = basePosition + compressedSize;
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 6606a654e10b3ba48b76b566b903b353
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -0,0 +1,9 @@
|
||||
namespace UnityFS
|
||||
{
|
||||
public class StorageBlock
|
||||
{
|
||||
public uint compressedSize;
|
||||
public uint uncompressedSize;
|
||||
public StorageBlockFlags flags;
|
||||
}
|
||||
}
|
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 40dc58bec5631f14c9c17c8a486496d4
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -0,0 +1,11 @@
|
||||
using System;
|
||||
|
||||
namespace UnityFS
|
||||
{
|
||||
[Flags]
|
||||
public enum StorageBlockFlags
|
||||
{
|
||||
CompressionTypeMask = 0x3f,
|
||||
Streamed = 0x40
|
||||
}
|
||||
}
|
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 79b9ed6799d3caf459cf2dfae5765a23
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -0,0 +1,32 @@
|
||||
using System.IO;
|
||||
|
||||
namespace UnityFS
|
||||
{
|
||||
public static class StreamExtensions
|
||||
{
|
||||
private const int BufferSize = 81920;
|
||||
|
||||
public static void CopyTo(this Stream source, Stream destination, long size)
|
||||
{
|
||||
var buffer = new byte[BufferSize];
|
||||
for (var left = size; left > 0; left -= BufferSize)
|
||||
{
|
||||
int toRead = BufferSize < left ? BufferSize : (int)left;
|
||||
int read = source.Read(buffer, 0, toRead);
|
||||
destination.Write(buffer, 0, read);
|
||||
if (read != toRead)
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static byte[] ReadAllBytes(this Stream source)
|
||||
{
|
||||
source.Position = 0;
|
||||
var bytes = new byte[source.Length];
|
||||
source.Read(bytes, 0, bytes.Length);
|
||||
return bytes;
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 2262fbf5672028a48b0c63821d7ff0c0
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -0,0 +1,11 @@
|
||||
using System.IO;
|
||||
|
||||
namespace UnityFS
|
||||
{
|
||||
public class StreamFile
|
||||
{
|
||||
public string path;
|
||||
public string fileName;
|
||||
public Stream stream;
|
||||
}
|
||||
}
|
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: fad7df04825c947489aad0d5d0c191a7
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -0,0 +1,124 @@
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using UnityEngine;
|
||||
using System.Text;
|
||||
using System.Reflection;
|
||||
using System;
|
||||
using System.Linq;
|
||||
|
||||
namespace UnityFS
|
||||
{
|
||||
/// <summary>
|
||||
/// Unity 生成的二进制文件(本代码不支持5.x之前的版本)
|
||||
/// </summary>
|
||||
public unsafe class UnityBinFile
|
||||
{
|
||||
/*
|
||||
* MonoManager: idx: 6;
|
||||
* type: metaData.types[objects[6].typeID]
|
||||
*/
|
||||
public const int kMonoManagerIdx = 6;
|
||||
|
||||
public FileHeader header;
|
||||
public MetaData metaData;
|
||||
public ScriptsData scriptsData;
|
||||
|
||||
private Stream _originStream;
|
||||
|
||||
public void LoadFromStream(Stream source)
|
||||
{
|
||||
_originStream = source;
|
||||
using (var br = new BinaryReader(source, Encoding.UTF8, true))
|
||||
{
|
||||
header.LoadFromStream(br);
|
||||
// 按理说 metaData 应该新开一个buffer来避免加载时的对齐逻辑问题,但由于 sizeof(Header) = 20,已经对齐到4了,所以可以连续读
|
||||
metaData.LoadFromStream(br, header.dataOffset);
|
||||
scriptsData = metaData.GetScriptData(br);
|
||||
}
|
||||
}
|
||||
|
||||
public void Load(string path)
|
||||
{
|
||||
LoadFromStream(new MemoryStream(File.ReadAllBytes(path)));
|
||||
}
|
||||
|
||||
public void AddScriptingAssemblies(List<string> assemblies)
|
||||
{
|
||||
foreach (string name in assemblies)
|
||||
{
|
||||
if (!scriptsData.dllNames.Contains(name))
|
||||
{
|
||||
scriptsData.dllNames.Add(name);
|
||||
scriptsData.dllTypes.Add(16); // user dll type
|
||||
Debug.Log($"[PatchScriptAssembliesJson] add dll:{name} to globalgamemanagers");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public byte[] CreatePatchedBytes()
|
||||
{
|
||||
var fsR = _originStream;
|
||||
fsR.Position = 0;
|
||||
var brR = new BinaryReader(fsR, Encoding.UTF8, true);
|
||||
|
||||
var ms = new MemoryStream((int)(header.fileSize * 1.5f));
|
||||
var bw = new BinaryWriter(ms, Encoding.UTF8, true);
|
||||
|
||||
/*
|
||||
* 开始写入data
|
||||
* dll名称列表存储于 data 区段,修改其数据并不会影响 MetaData 大小,因此 dataOffset 不会改变
|
||||
*/
|
||||
ms.Position = header.dataOffset;
|
||||
|
||||
Dictionary<long, ObjectInfo> newObjInfos = new Dictionary<long, ObjectInfo>();
|
||||
foreach (var kv in metaData.objects)
|
||||
{
|
||||
long objID = kv.Key;
|
||||
ObjectInfo objInfo = kv.Value;
|
||||
|
||||
byte[] buff = new byte[objInfo.size];
|
||||
fsR.Position = objInfo.realPos;
|
||||
brR.Read(buff, 0, buff.Length);
|
||||
|
||||
|
||||
{// unity 的数据偏移貌似会对齐到 8
|
||||
int newPos = (((int)ms.Position + 7) >> 3) << 3;
|
||||
int gapSize = newPos - (int)ms.Position;
|
||||
|
||||
for (int i = 0; i < gapSize; i++)
|
||||
bw.Write((byte)0);
|
||||
|
||||
objInfo.dataPos = (uint)ms.Position - header.dataOffset; // 重定位数据偏移
|
||||
}
|
||||
|
||||
if (objID != kMonoManagerIdx)
|
||||
bw.Write(buff, 0, buff.Length);
|
||||
else
|
||||
objInfo.size = (uint)scriptsData.SaveToStream(bw);
|
||||
|
||||
newObjInfos.Add(objID, objInfo);
|
||||
}
|
||||
|
||||
metaData.objects = newObjInfos;
|
||||
header.fileSize = (uint)ms.Position;
|
||||
|
||||
ms.Position = 0;
|
||||
header.SaveToStream(bw);
|
||||
metaData.SaveToStream(bw);
|
||||
|
||||
brR.Close();
|
||||
|
||||
// 写入新文件
|
||||
ms.Position = 0;
|
||||
return ms.ToArray();
|
||||
}
|
||||
|
||||
public void Save(string newPath)
|
||||
{
|
||||
byte[] patchedBytes = CreatePatchedBytes();
|
||||
File.WriteAllBytes(newPath, patchedBytes);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 7f9902041e9a1ff4c9f2d65d6384530d
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -0,0 +1,397 @@
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using UnityEngine;
|
||||
using static UnityFS.UnityBinUtils;
|
||||
|
||||
namespace UnityFS
|
||||
{
|
||||
public struct FileHeader
|
||||
{
|
||||
public const int kSize = 20;
|
||||
|
||||
public uint dataSize => fileSize - metadataSize;
|
||||
|
||||
public uint metadataSize;
|
||||
public uint fileSize;
|
||||
public uint version;
|
||||
public uint dataOffset;
|
||||
public byte endianess;
|
||||
|
||||
public void LoadFromStream(BinaryReader br)
|
||||
{
|
||||
long startPos = br.BaseStream.Position;
|
||||
metadataSize = br.ReadUInt32();
|
||||
fileSize = br.ReadUInt32();
|
||||
version = br.ReadUInt32();
|
||||
dataOffset = br.ReadUInt32();
|
||||
endianess = br.ReadByte();
|
||||
br.BaseStream.Position = startPos + kSize;
|
||||
|
||||
SwapEndianess();
|
||||
}
|
||||
|
||||
public long SaveToStream(BinaryWriter bw)
|
||||
{
|
||||
SwapEndianess();
|
||||
|
||||
long startPos = bw.BaseStream.Position;
|
||||
bw.Write(metadataSize);
|
||||
bw.Write(fileSize);
|
||||
bw.Write(version);
|
||||
bw.Write(dataOffset);
|
||||
bw.Write(endianess);
|
||||
bw.BaseStream.Position = startPos + kSize;
|
||||
return kSize;
|
||||
}
|
||||
|
||||
void SwapEndianess()
|
||||
{
|
||||
SwapUInt(ref metadataSize);
|
||||
SwapUInt(ref fileSize);
|
||||
SwapUInt(ref version);
|
||||
SwapUInt(ref dataOffset);
|
||||
}
|
||||
}
|
||||
|
||||
public struct MetaData
|
||||
{
|
||||
public long dataStartPos;
|
||||
|
||||
public string version;
|
||||
public uint platform;
|
||||
public bool enableTypeTree;
|
||||
public int typeCount;
|
||||
public ObjectType[] types;
|
||||
public int objectCount;
|
||||
public Dictionary<long, ObjectInfo> objects;
|
||||
public int scriptTypeCount;
|
||||
public ScriptType[] scriptTypes;
|
||||
public int externalsCount;
|
||||
public ExternalInfo[] externals;
|
||||
|
||||
#if UNITY_2019_2_OR_NEWER
|
||||
public int refTypeCount;
|
||||
public ObjectType[] refTypes;
|
||||
#endif
|
||||
public string dummyStr;
|
||||
|
||||
public void LoadFromStream(BinaryReader br, uint dataOffset)
|
||||
{
|
||||
long startPos = br.BaseStream.Position;
|
||||
dataStartPos = startPos;
|
||||
|
||||
version = br.ReadRawString();
|
||||
platform = br.ReadUInt32();
|
||||
enableTypeTree = br.ReadBoolean();
|
||||
typeCount = br.ReadInt32();
|
||||
types = new ObjectType[typeCount];
|
||||
|
||||
for (int i = 0; i < typeCount; i++)
|
||||
{
|
||||
types[i].LoadFromStream(br);
|
||||
}
|
||||
|
||||
objectCount = br.ReadInt32();
|
||||
objects = new Dictionary<long, ObjectInfo>();
|
||||
for(int i = 0; i < objectCount; i++)
|
||||
{
|
||||
long id = br.AlignedReadInt64();
|
||||
ObjectInfo objInfo = new ObjectInfo();
|
||||
objInfo.LoadFromStream(br);
|
||||
objInfo.realPos = objInfo.dataPos + dataOffset;
|
||||
|
||||
objects.Add(id, objInfo);
|
||||
}
|
||||
|
||||
scriptTypeCount = br.ReadInt32();
|
||||
scriptTypes = new ScriptType[scriptTypeCount];
|
||||
for(int i = 0; i < scriptTypeCount; i++)
|
||||
{
|
||||
scriptTypes[i].LoadFromStream(br);
|
||||
}
|
||||
|
||||
externalsCount = br.ReadInt32();
|
||||
externals = new ExternalInfo[externalsCount];
|
||||
for(int i = 0; i < externalsCount; i++)
|
||||
{
|
||||
externals[i].LoadFromStream(br);
|
||||
}
|
||||
|
||||
#if UNITY_2019_2_OR_NEWER
|
||||
refTypeCount = br.ReadInt32();
|
||||
refTypes = new ObjectType[refTypeCount];
|
||||
for(int i = 0; i < refTypeCount; i++)
|
||||
{
|
||||
refTypes[i].LoadFromStream(br);
|
||||
}
|
||||
#endif
|
||||
dummyStr = br.ReadRawString();
|
||||
}
|
||||
|
||||
public long SaveToStream(BinaryWriter bw)
|
||||
{
|
||||
long startPos = bw.BaseStream.Position;
|
||||
bw.WriteRawString(version);
|
||||
bw.Write(platform);
|
||||
bw.Write(enableTypeTree);
|
||||
|
||||
bw.Write(typeCount);
|
||||
foreach(var type in types)
|
||||
type.SaveToStream(bw);
|
||||
|
||||
bw.Write(objectCount);
|
||||
foreach (var kv in objects)
|
||||
{
|
||||
bw.AlignedWriteInt64(kv.Key);
|
||||
kv.Value.SaveToStream(bw);
|
||||
}
|
||||
|
||||
bw.Write(scriptTypeCount);
|
||||
foreach(var st in scriptTypes)
|
||||
st.SaveToStream(bw);
|
||||
|
||||
bw.Write(externalsCount);
|
||||
foreach(var external in externals)
|
||||
external.SaveToStream(bw);
|
||||
|
||||
#if UNITY_2019_2_OR_NEWER
|
||||
bw.Write(refTypeCount);
|
||||
foreach(var refT in refTypes)
|
||||
refT.SaveToStream(bw);
|
||||
#endif
|
||||
|
||||
bw.WriteRawString(dummyStr);
|
||||
|
||||
return bw.BaseStream.Position - startPos;
|
||||
}
|
||||
|
||||
public ScriptsData GetScriptData(BinaryReader br)
|
||||
{
|
||||
ObjectInfo objInfo = objects[UnityBinFile.kMonoManagerIdx];
|
||||
br.BaseStream.Seek(objInfo.realPos, SeekOrigin.Begin);
|
||||
|
||||
ScriptsData data = new ScriptsData();
|
||||
data.LoadFromStream(br);
|
||||
return data;
|
||||
}
|
||||
}
|
||||
|
||||
public struct ObjectType
|
||||
{
|
||||
public int typeID;
|
||||
public bool isStriped;
|
||||
public short scriptTypeIndex;
|
||||
|
||||
public bool needReadScriptHash; // dont save
|
||||
|
||||
public Hash scriptSigHash;
|
||||
public Hash typeHash;
|
||||
|
||||
public void LoadFromStream(BinaryReader br)
|
||||
{
|
||||
typeID = br.ReadInt32();
|
||||
isStriped = br.ReadBoolean();
|
||||
scriptTypeIndex = br.ReadInt16();
|
||||
|
||||
needReadScriptHash = typeID == -1 || typeID == 0x72;
|
||||
if(needReadScriptHash)
|
||||
scriptSigHash.LoadFromStream(br);
|
||||
|
||||
typeHash.LoadFromStream(br);
|
||||
|
||||
// GlobalManagers does not has TypeTrees
|
||||
}
|
||||
|
||||
public long SaveToStream(BinaryWriter bw)
|
||||
{
|
||||
long startPos = bw.BaseStream.Position;
|
||||
bw.Write(typeID);
|
||||
bw.Write(isStriped);
|
||||
bw.Write(scriptTypeIndex);
|
||||
|
||||
if(needReadScriptHash)
|
||||
scriptSigHash.SaveToStream(bw);
|
||||
|
||||
typeHash.SaveToStream(bw);
|
||||
return bw.BaseStream.Position - startPos;
|
||||
}
|
||||
|
||||
public int Size()
|
||||
{
|
||||
int ret = 0;
|
||||
ret += sizeof(int);
|
||||
ret += sizeof(bool);
|
||||
ret += sizeof(short);
|
||||
|
||||
if (needReadScriptHash)
|
||||
ret += Hash.kSize;
|
||||
|
||||
ret += Hash.kSize;
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
public struct ObjectInfo
|
||||
{
|
||||
public const int kSize = 12;
|
||||
|
||||
public uint dataPos;
|
||||
public uint size;
|
||||
public uint typeID;
|
||||
|
||||
public uint realPos; // dataPos + Header.dataOffset; // dont save
|
||||
|
||||
public void LoadFromStream(BinaryReader br)
|
||||
{
|
||||
dataPos = br.ReadUInt32();
|
||||
size = br.ReadUInt32();
|
||||
typeID = br.ReadUInt32();
|
||||
}
|
||||
|
||||
public long SaveToStream(BinaryWriter bw)
|
||||
{
|
||||
bw.Write(dataPos);
|
||||
bw.Write(size);
|
||||
bw.Write(typeID);
|
||||
return kSize;
|
||||
}
|
||||
}
|
||||
|
||||
public struct ScriptType
|
||||
{
|
||||
public int localFileIndex;
|
||||
public long localIdentifierOfBin;
|
||||
|
||||
public void LoadFromStream(BinaryReader br)
|
||||
{
|
||||
localFileIndex = br.ReadInt32();
|
||||
localIdentifierOfBin = br.AlignedReadInt64();
|
||||
}
|
||||
|
||||
public long SaveToStream(BinaryWriter bw)
|
||||
{
|
||||
long startPos = bw.BaseStream.Position;
|
||||
bw.Write(localFileIndex);
|
||||
bw.AlignedWriteInt64(localIdentifierOfBin);
|
||||
return bw.BaseStream.Position - startPos;
|
||||
}
|
||||
}
|
||||
|
||||
public struct ExternalInfo
|
||||
{
|
||||
public string dummy;
|
||||
public Hash guid;
|
||||
public int type;
|
||||
public string name;
|
||||
|
||||
public void LoadFromStream(BinaryReader br)
|
||||
{
|
||||
dummy = br.ReadRawString();
|
||||
guid.LoadFromStream(br);
|
||||
type = br.ReadInt32();
|
||||
name = br.ReadRawString();
|
||||
}
|
||||
|
||||
public long SaveToStream(BinaryWriter bw)
|
||||
{
|
||||
long startPos = bw.BaseStream.Position;
|
||||
bw.WriteRawString(dummy);
|
||||
guid.SaveToStream(bw);
|
||||
bw.Write(type);
|
||||
bw.WriteRawString(name);
|
||||
return bw.BaseStream.Position - startPos;
|
||||
}
|
||||
}
|
||||
|
||||
public struct ScriptsData
|
||||
{
|
||||
public ScriptID[] scriptIDs;
|
||||
public List<string> dllNames;
|
||||
public List<int> dllTypes; // 16 is user type
|
||||
|
||||
public void LoadFromStream(BinaryReader br)
|
||||
{
|
||||
{
|
||||
int count = br.ReadInt32();
|
||||
scriptIDs = new ScriptID[count];
|
||||
for(int i = 0; i < count; i++)
|
||||
scriptIDs[i].LoadFromStream(br);
|
||||
}
|
||||
{
|
||||
int count = br.ReadInt32();
|
||||
dllNames = new List<string>(count);
|
||||
for (var i = 0; i < count; i++)
|
||||
dllNames.Add(br.ReadSizeString());
|
||||
}
|
||||
{
|
||||
int count = br.ReadInt32();
|
||||
dllTypes = new List<int>(count);
|
||||
for(var i = 0; i < count; i++)
|
||||
dllTypes.Add(br.ReadInt32());
|
||||
}
|
||||
}
|
||||
|
||||
public long SaveToStream(BinaryWriter bw)
|
||||
{
|
||||
long startPos = bw.BaseStream.Position;
|
||||
bw.Write(scriptIDs.Length);
|
||||
for(int i = 0; i < scriptIDs.Length; i++)
|
||||
scriptIDs[i].SaveToStream(bw);
|
||||
|
||||
bw.Write(dllNames.Count);
|
||||
for(int i = 0, imax = dllNames.Count; i < imax; i++)
|
||||
bw.WriteSizeString(dllNames[i]);
|
||||
|
||||
bw.Write(dllTypes.Count);
|
||||
for(int i = 0, imax = dllTypes.Count; i < imax; i++)
|
||||
bw.Write(dllTypes[i]);
|
||||
|
||||
return bw.BaseStream.Position - startPos;
|
||||
}
|
||||
}
|
||||
|
||||
public struct ScriptID
|
||||
{
|
||||
public int fileID;
|
||||
public long pathID; // localIdentifier
|
||||
|
||||
public void LoadFromStream(BinaryReader br)
|
||||
{
|
||||
fileID = br.ReadInt32();
|
||||
pathID = br.ReadInt64();
|
||||
}
|
||||
|
||||
public long SaveToStream(BinaryWriter bw)
|
||||
{
|
||||
bw.Write(fileID);
|
||||
bw.Write(pathID);
|
||||
return 4 + 8;
|
||||
}
|
||||
}
|
||||
|
||||
public struct Hash
|
||||
{
|
||||
public const int kSize = 16;
|
||||
|
||||
public int[] data;
|
||||
|
||||
public void LoadFromStream(BinaryReader br)
|
||||
{
|
||||
data = new int[4];
|
||||
for(int i = 0; i < data.Length; i++)
|
||||
{
|
||||
data[i] = br.ReadInt32();
|
||||
}
|
||||
}
|
||||
|
||||
public long SaveToStream(BinaryWriter bw)
|
||||
{
|
||||
for(int i = 0; i < data.Length; i++)
|
||||
{
|
||||
bw.Write(data[i]);
|
||||
}
|
||||
return kSize;
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 10655ce82e730324db6ae297f77df04b
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -0,0 +1,78 @@
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using UnityEngine;
|
||||
using System.Text;
|
||||
|
||||
namespace UnityFS
|
||||
{
|
||||
public static class UnityBinUtils
|
||||
{
|
||||
public static void SwapUInt(ref uint val)
|
||||
{
|
||||
val = (val >> 24) | ((val >> 8) & 0x0000ff00) | ((val << 8) & 0x00ff0000) | (val << 24);
|
||||
}
|
||||
|
||||
public static string ReadRawString(this BinaryReader br)
|
||||
{
|
||||
long startPos = br.BaseStream.Position;
|
||||
while (true)
|
||||
{
|
||||
byte val = br.ReadByte();
|
||||
if(val == 0)
|
||||
break;
|
||||
}
|
||||
int size = (int)(br.BaseStream.Position - startPos);
|
||||
br.BaseStream.Position = startPos;
|
||||
|
||||
byte[] buffer = br.ReadBytes(size);
|
||||
string ret = Encoding.UTF8.GetString(buffer, 0, size - 1);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
public static void WriteRawString(this BinaryWriter bw, string str)
|
||||
{
|
||||
byte[] buffer = Encoding.UTF8.GetBytes(str);
|
||||
bw.Write(buffer, 0, buffer.Length);
|
||||
bw.Write((byte)0);
|
||||
}
|
||||
|
||||
public static string ReadSizeString(this BinaryReader br)
|
||||
{
|
||||
int size = br.ReadInt32();
|
||||
byte[] buff = br.ReadBytes(size);
|
||||
br.BaseStream.AlignOffset4();
|
||||
|
||||
string ret = Encoding.UTF8.GetString(buff);
|
||||
return ret;
|
||||
}
|
||||
|
||||
public static void WriteSizeString(this BinaryWriter bw, string str)
|
||||
{
|
||||
byte[] buff = Encoding.UTF8.GetBytes(str);
|
||||
bw.Write(buff.Length);
|
||||
bw.Write(buff, 0, buff.Length);
|
||||
bw.BaseStream.AlignOffset4();
|
||||
}
|
||||
|
||||
public static void AlignOffset4(this Stream stream)
|
||||
{
|
||||
int offset = (((int)stream.Position + 3) >> 2) << 2;
|
||||
stream.Position = offset;
|
||||
}
|
||||
|
||||
public static long AlignedReadInt64(this BinaryReader br)
|
||||
{
|
||||
br.BaseStream.AlignOffset4();
|
||||
return br.ReadInt64();
|
||||
}
|
||||
|
||||
public static void AlignedWriteInt64(this BinaryWriter bw, long val)
|
||||
{
|
||||
bw.BaseStream.AlignOffset4();
|
||||
bw.Write(val);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 12a24c30a3914be418be10cfebfa9649
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
Reference in New Issue
Block a user