Skip to content

Implement bundle03 #66

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: assetstoolsv2
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 5 additions & 2 deletions AssetTools.NET/Standard/AssetTypeClass/AssetTypeInstance.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ public class AssetTypeInstance
{
public int baseFieldCount;
public AssetTypeValueField[] baseFields;
public byte[] memoryToClear;

public AssetTypeInstance(AssetTypeTemplateField[] baseFields, AssetsFileReader reader, long filePos)
{
reader.bigEndian = false;
Expand All @@ -18,8 +18,11 @@ public AssetTypeInstance(AssetTypeTemplateField[] baseFields, AssetsFileReader r
this.baseFields[i] = atvf;
}
}

public AssetTypeInstance(AssetTypeTemplateField baseField, AssetsFileReader reader, long filePos)
: this(new[] { baseField }, reader, filePos) { }
: this(new[] { baseField }, reader, filePos)
{
}

public static AssetTypeValueField GetDummyAssetTypeField()
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,5 +5,19 @@ public class AssetsBundleEntry
public uint offset;
public uint length;
public string name;

public void Read(AssetsFileReader reader)
{
name = reader.ReadNullTerminated();
offset = reader.ReadUInt32();
length = reader.ReadUInt32();
}

public void Write(AssetsFileWriter writer)
{
writer.WriteNullTerminated(name);
writer.Write(offset);
writer.Write(length);
}
}
}
178 changes: 135 additions & 43 deletions AssetTools.NET/Standard/AssetsBundleFileFormat/AssetsBundleFile.cs
Original file line number Diff line number Diff line change
Expand Up @@ -30,17 +30,33 @@ public bool Read(AssetsFileReader reader, bool allowCompressed = false)
this.reader = reader;
reader.ReadNullTerminated();
uint version = reader.ReadUInt32();
if (version == 6 || version == 7)
switch (version)
{
reader.Position = 0;
bundleHeader6 = new AssetBundleHeader06();
bundleHeader6.Read(reader);
if (bundleHeader6.fileVersion >= 7)
{
reader.Align16();
}
if (bundleHeader6.signature == "UnityFS")
{
case 3:
reader.Position = 0;
bundleHeader3 = new AssetBundleHeader03();
bundleHeader3.Read(reader);
if (bundleHeader3.signature != "UnityRaw")
throw new NotImplementedException("Non UnityRaw bundles are not supported yet.");

if (bundleHeader3.blockList.Any(b => b.compressed != b.uncompressed))
throw new NotImplementedException("Compressed UnityRaw bundles are not supported yet.");

assetsLists3 = new AssetsList();
assetsLists3.Read(reader);
return true;

case 6:
case 7:
reader.Position = 0;
bundleHeader6 = new AssetBundleHeader06();
bundleHeader6.Read(reader);
if (bundleHeader6.fileVersion >= 7)
reader.Align16();

if (bundleHeader6.signature != "UnityFS")
throw new NotImplementedException("Non UnityFS bundles are not supported yet.");

bundleInf6 = new AssetBundleBlockAndDirectoryList06();
if ((bundleHeader6.flags & 0x3F) != 0)
{
Expand All @@ -59,23 +75,103 @@ public bool Read(AssetsFileReader reader, bool allowCompressed = false)
bundleInf6.Read(bundleHeader6.GetBundleInfoOffset(), reader);
return true;
}
}
else

default:
throw new Exception("AssetsBundleFile.Read : Unknown file version!");
}
}

public bool Write(AssetsFileWriter writer, List<BundleReplacer> replacers, ClassDatabaseFile typeMeta = null)
{
if (bundleHeader3 != null)
return Write03(writer, replacers);

if (bundleHeader6 != null)
return Write06(writer, replacers);

return false;
}

private bool Write03(AssetsFileWriter writer, List<BundleReplacer> replacers)
{
AssetBundleHeader03 newBundleHeader3 = bundleHeader3.Clone();
newBundleHeader3.blockList = new[] { new AssetsBundleOffsetPair() };
newBundleHeader3.Write(writer);

newBundleHeader3.bundleDataOffs = (uint)writer.Position;

Dictionary<string, BundleReplacer> addingReplacers =
replacers.Where(r => r.GetReplacementType() == BundleReplacementType.AddOrModify)
.ToDictionary(r => r.GetOriginalEntryName());

List<AssetsBundleEntry> newEntries = new List<AssetsBundleEntry>();
Dictionary<AssetsBundleEntry, AssetsBundleEntry> newEntryToOldEntry = new Dictionary<AssetsBundleEntry, AssetsBundleEntry>();
Dictionary<AssetsBundleEntry, BundleReplacer> newEntryToReplacer = new Dictionary<AssetsBundleEntry, BundleReplacer>();
foreach (AssetsBundleEntry entry in assetsLists3.entries)
{
addingReplacers.Remove(entry.name);

BundleReplacer replacer = replacers.FirstOrDefault(r => r.GetOriginalEntryName() == entry.name);
if (replacer == null ||
replacer.GetReplacementType() == BundleReplacementType.AddOrModify ||
replacer.GetReplacementType() == BundleReplacementType.Rename)
{
new NotImplementedException("Non UnityFS bundles are not supported yet.");
AssetsBundleEntry newEntry = new AssetsBundleEntry { name = replacer?.GetEntryName() ?? entry.name };
newEntries.Add(newEntry);
newEntryToOldEntry.Add(newEntry, entry);
if (replacer != null && replacer.GetReplacementType() == BundleReplacementType.AddOrModify)
newEntryToReplacer.Add(newEntry, replacer);
}
}
else if (version == 3)

foreach (BundleReplacer replacer in addingReplacers.Values)
{
new NotImplementedException("Version 3 bundles are not supported yet.");
AssetsBundleEntry newEntry = new AssetsBundleEntry { name = replacer.GetEntryName() };
newEntries.Add(newEntry);
newEntryToReplacer.Add(newEntry, replacer);
}
else

bundleHeader3.numberOfAssetsToDownload = (uint)newEntries.Count;

AssetsList newAssetsList = new AssetsList { entries = newEntries.ToArray() };
newAssetsList.Write(writer);
writer.Align16();

bundleHeader3.assetsListSize = (uint)writer.Position - bundleHeader3.bundleDataOffs;

foreach (AssetsBundleEntry newEntry in newEntries)
{
new Exception("AssetsBundleFile.Read : Unknown file version!");
long newEntryPosition = writer.Position;
newEntry.offset = (uint)(writer.Position - bundleHeader3.bundleDataOffs);

if (newEntryToReplacer.TryGetValue(newEntry, out BundleReplacer replacer))
{
replacer.Write(writer);
}
else
{
AssetsBundleEntry oldEntry = newEntryToOldEntry[newEntry];
reader.Position = bundleHeader3.bundleDataOffs + oldEntry.offset;
reader.BaseStream.CopyToCompat(writer.BaseStream, oldEntry.length);
}

newEntry.length = (uint)(writer.Position - newEntryPosition);
}
return false;

newBundleHeader3.minimumStreamedBytes = (uint)writer.Position;
newBundleHeader3.blockList[0].compressed = (uint)writer.Position - newBundleHeader3.bundleDataOffs;
newBundleHeader3.blockList[0].uncompressed = (uint)writer.Position - newBundleHeader3.bundleDataOffs;
newBundleHeader3.fileSize2 = (uint)writer.Position;

writer.Position = 0;
newBundleHeader3.Write(writer);
newAssetsList.Write(writer);

writer.Position = newBundleHeader3.fileSize2;
return true;
}
public bool Write(AssetsFileWriter writer, List<BundleReplacer> replacers, ClassDatabaseFile typeMeta = null)

private bool Write06(AssetsFileWriter writer, List<BundleReplacer> replacers)
{
bundleHeader6.Write(writer);

Expand All @@ -84,15 +180,15 @@ public bool Write(AssetsFileWriter writer, List<BundleReplacer> replacers, Class
writer.Align16();
}

AssetBundleBlockAndDirectoryList06 newBundleInf6 = new AssetBundleBlockAndDirectoryList06()
AssetBundleBlockAndDirectoryList06 newBundleInf6 = new AssetBundleBlockAndDirectoryList06
{
checksumLow = 0,
checksumHigh = 0
};
//I could map the assets to their blocks but I don't
//have any more-than-1-block files to test on
//this should work just fine as far as I know
newBundleInf6.blockInf = new AssetBundleBlockInfo06[]
newBundleInf6.blockInf = new[]
{
new AssetBundleBlockInfo06
{
Expand All @@ -108,19 +204,14 @@ public bool Write(AssetsFileWriter writer, List<BundleReplacer> replacers, Class
List<AssetBundleDirectoryInfo06> originalDirInfos = new List<AssetBundleDirectoryInfo06>();
List<AssetBundleDirectoryInfo06> dirInfos = new List<AssetBundleDirectoryInfo06>();
List<BundleReplacer> currentReplacers = replacers.ToList();
//this is kind of useless at the moment but leaving it here
//because if the AssetsFile size can be precalculated in the
//future, we can use this to skip rewriting sizes
long currentOffset = 0;

//write all original files, modify sizes if needed and skip those to be removed
for (int i = 0; i < bundleInf6.directoryCount; i++)
{
AssetBundleDirectoryInfo06 info = bundleInf6.dirInf[i];
originalDirInfos.Add(info);
AssetBundleDirectoryInfo06 newInfo = new AssetBundleDirectoryInfo06()
AssetBundleDirectoryInfo06 newInfo = new AssetBundleDirectoryInfo06
{
offset = currentOffset,
decompressedSize = info.decompressedSize,
flags = info.flags,
name = info.name
Expand All @@ -131,19 +222,16 @@ public bool Write(AssetsFileWriter writer, List<BundleReplacer> replacers, Class
currentReplacers.Remove(replacer);
if (replacer.GetReplacementType() == BundleReplacementType.AddOrModify)
{
newInfo = new AssetBundleDirectoryInfo06()
newInfo = new AssetBundleDirectoryInfo06
{
offset = currentOffset,
decompressedSize = replacer.GetSize(),
flags = info.flags,
name = replacer.GetEntryName()
};
}
else if (replacer.GetReplacementType() == BundleReplacementType.Rename)
{
newInfo = new AssetBundleDirectoryInfo06()
newInfo = new AssetBundleDirectoryInfo06
{
offset = currentOffset,
decompressedSize = info.decompressedSize,
flags = info.flags,
name = replacer.GetEntryName()
Expand All @@ -160,11 +248,6 @@ public bool Write(AssetsFileWriter writer, List<BundleReplacer> replacers, Class
newToOriginalDirInfoLookup[newInfo] = info;
}

if (newInfo.decompressedSize != -1)
{
currentOffset += newInfo.decompressedSize;
}

dirInfos.Add(newInfo);
}

Expand All @@ -176,12 +259,9 @@ public bool Write(AssetsFileWriter writer, List<BundleReplacer> replacers, Class
{
AssetBundleDirectoryInfo06 info = new AssetBundleDirectoryInfo06()
{
offset = currentOffset,
decompressedSize = replacer.GetSize(),
flags = (uint)(replacer.HasSerializedData() ? 0x04 : 0x00),
name = replacer.GetEntryName()
};
currentOffset += info.decompressedSize;

dirInfos.Add(info);
}
Expand Down Expand Up @@ -595,12 +675,24 @@ public int NumFiles

public bool IsAssetsFile(int index)
{
GetFileRange(index, out long offset, out long length);
return AssetsFile.IsAssetsFile(reader, offset, length);
if (bundleHeader3 != null)
return IsAssetsFile(assetsLists3.entries[index]);

if (bundleHeader6 != null)
return IsAssetsFile(bundleInf6.dirInf[index]);

return false;
}

[Obsolete]
public bool IsAssetsFile(AssetsBundleEntry entry)
{
long offset = bundleHeader3.bundleDataOffs + entry.offset;
return AssetsFile.IsAssetsFile(reader, offset, entry.length);
}

[Obsolete]
public bool IsAssetsFile(AssetsFileReader reader, AssetBundleDirectoryInfo06 entry)
public bool IsAssetsFile(AssetBundleDirectoryInfo06 entry)
{
long offset = bundleHeader6.GetFileDataOffset() + entry.offset;
return AssetsFile.IsAssetsFile(reader, offset, entry.decompressedSize);
Expand Down
Loading