接入obfuz->2.0

This commit is contained in:
Alex-Rachel
2025-07-26 08:10:41 +08:00
parent f2c7ff4336
commit cb86d8868e
713 changed files with 57092 additions and 10 deletions

View File

@@ -0,0 +1,14 @@
using System;
namespace UnityFS
{
[Flags]
public enum ArchiveFlags
{
CompressionTypeMask = 0x3f,
BlocksAndDirectoryInfoCombined = 0x40,
BlocksInfoAtTheEnd = 0x80,
OldWebPluginCompatibility = 0x100,
BlockInfoNeedPaddingAtStart = 0x200
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 099de05eebdffaf4baeed3290dc98aaf
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -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());
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: a84e427dd05adde44b4345b3fd49007a
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -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);
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 02eda21769c083346a5bd9b7dca49427
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -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;
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 1a72550a949e322419b9c5d6e4fe495d
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -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;
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: f9b938458cc610e4d8a910c4499693cf
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -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}");
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: d6705163b267de54a868f5e84f6c7024
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,11 @@
namespace UnityFS
{
public enum CompressionType
{
None,
Lzma,
Lz4,
Lz4HC,
Lzham
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: ddcd6644c83d2a94f9668d6e913bd80e
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -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();
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 9b02c037f0fa1014da65773804248d8d
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -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);
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 4107364c7434b2042ad647b28e322513
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -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
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 2f6cbab4506c18248b410a164be891d2
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -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());
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 653a22d285c79f44a8113c5571b2d26b
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -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;
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: f121e0520fa65c240884d43fd00b3c2a
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,10 @@
namespace UnityFS
{
public class Node
{
public long offset;
public long size;
public uint flags;
public string path;
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: e3eea8a6a32b6ac4ba609b39715e25e2
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -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);
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 8f2dd29d56a640d4ebd1c2fd374b7638
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -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;
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 6606a654e10b3ba48b76b566b903b353
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,9 @@
namespace UnityFS
{
public class StorageBlock
{
public uint compressedSize;
public uint uncompressedSize;
public StorageBlockFlags flags;
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 40dc58bec5631f14c9c17c8a486496d4
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,11 @@
using System;
namespace UnityFS
{
[Flags]
public enum StorageBlockFlags
{
CompressionTypeMask = 0x3f,
Streamed = 0x40
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 79b9ed6799d3caf459cf2dfae5765a23
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -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;
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 2262fbf5672028a48b0c63821d7ff0c0
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,11 @@
using System.IO;
namespace UnityFS
{
public class StreamFile
{
public string path;
public string fileName;
public Stream stream;
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: fad7df04825c947489aad0d5d0c191a7
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -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);
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 7f9902041e9a1ff4c9f2d65d6384530d
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -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;
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 10655ce82e730324db6ae297f77df04b
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -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);
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 12a24c30a3914be418be10cfebfa9649
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant: