//set fileID to -1 if all replacers are for this .assets file but don't have the fileID set to the same one //typeMeta is used to add the type information (hash and type fields) for format >= 0x10 if necessary public void Write(AssetsFileWriter writer, ulong filePos, List <AssetsReplacer> replacers, uint fileID, ClassDatabaseFile typeMeta = null) { header.Write(writer); for (int i = 0; i < replacers.Count; i++) { AssetsReplacer replacer = replacers[i]; if (!typeTree.unity5Types.Any(t => t.classId == replacer.GetClassID())) { Type_0D type = new Type_0D() { classId = replacer.GetClassID(), unknown16_1 = 0, scriptIndex = 0xFFFF, typeHash1 = 0, typeHash2 = 0, typeHash3 = 0, typeHash4 = 0, typeFieldsExCount = 0, stringTableLen = 0, stringTable = "" }; typeTree.unity5Types.Concat(new Type_0D[] { type }); } } typeTree.Write(writer, header.format); int initialSize = (int)(AssetFileInfo.GetSize(header.format) * AssetCount); int newSize = (int)(AssetFileInfo.GetSize(header.format) * (AssetCount + replacers.Count)); int appendedSize = newSize - initialSize; reader.Position = AssetTablePos; List <AssetFileInfo> originalAssetInfos = new List <AssetFileInfo>(); List <AssetFileInfo> assetInfos = new List <AssetFileInfo>(); List <AssetsReplacer> currentReplacers = replacers.ToList(); uint currentOffset = 0; //-write all original assets, modify sizes if needed and skip those to be removed for (int i = 0; i < AssetCount; i++) { AssetFileInfo info = new AssetFileInfo(); info.Read(header.format, reader); originalAssetInfos.Add(info); AssetFileInfo newInfo = new AssetFileInfo() { index = info.index, curFileOffset = currentOffset, curFileSize = info.curFileSize, curFileTypeOrIndex = info.curFileTypeOrIndex, inheritedUnityClass = info.inheritedUnityClass, scriptIndex = info.scriptIndex, unknown1 = info.unknown1 }; AssetsReplacer replacer = currentReplacers.FirstOrDefault(n => n.GetPathID() == newInfo.index); if (replacer != null) { currentReplacers.Remove(replacer); if (replacer.GetReplacementType() == AssetsReplacementType.AssetsReplacement_AddOrModify) { int classIndex; if (replacer.GetMonoScriptID() == 0xFFFF) { classIndex = typeTree.unity5Types.FindIndex(t => t.classId == replacer.GetClassID()); } else { classIndex = typeTree.unity5Types.FindIndex(t => t.classId == replacer.GetClassID() && t.scriptIndex == replacer.GetMonoScriptID()); } newInfo = new AssetFileInfo() { index = replacer.GetPathID(), curFileOffset = currentOffset, curFileSize = (uint)replacer.GetSize(), curFileTypeOrIndex = classIndex, inheritedUnityClass = (ushort)replacer.GetClassID(), //for older unity versions scriptIndex = replacer.GetMonoScriptID(), unknown1 = 0 }; } else if (replacer.GetReplacementType() == AssetsReplacementType.AssetsReplacement_Remove) { continue; } } currentOffset += newInfo.curFileSize; uint pad = 8 - (currentOffset % 8); if (pad != 8) { currentOffset += pad; } assetInfos.Add(newInfo); } //-write new assets while (currentReplacers.Count > 0) { AssetsReplacer replacer = currentReplacers.First(); if (replacer.GetReplacementType() == AssetsReplacementType.AssetsReplacement_AddOrModify) { int classIndex; if (replacer.GetMonoScriptID() == 0xFFFF) { classIndex = typeTree.unity5Types.FindIndex(t => t.classId == replacer.GetClassID()); } else { classIndex = typeTree.unity5Types.FindIndex(t => t.classId == replacer.GetClassID() && t.scriptIndex == replacer.GetMonoScriptID()); } AssetFileInfo info = new AssetFileInfo() { index = replacer.GetPathID(), curFileOffset = currentOffset, curFileSize = (uint)replacer.GetSize(), curFileTypeOrIndex = classIndex, inheritedUnityClass = (ushort)replacer.GetClassID(), scriptIndex = replacer.GetMonoScriptID(), unknown1 = 0 }; currentOffset += info.curFileSize; uint pad = 8 - (currentOffset % 8); if (pad != 8) { currentOffset += pad; } assetInfos.Add(info); } currentReplacers.Remove(replacer); } writer.Write(assetInfos.Count); writer.Align(); for (int i = 0; i < assetInfos.Count; i++) { assetInfos[i].Write(header.format, writer); } preloadTable.Write(writer); dependencies.Write(writer); //temporary fix for secondarytypecount and friends if (header.format >= 14) { writer.Write(0); //secondaryTypeCount writer.Write((byte)0); //unknownString length } uint metadataSize = (uint)writer.Position - 0x14; //-for padding only. if all initial data before assetData is more than 0x1000, this is skipped while (writer.Position < 0x1000 /*header.offs_firstFile*/) { writer.Write((byte)0x00); } writer.Align16(); uint offs_firstFile = (uint)writer.Position; for (int i = 0; i < assetInfos.Count; i++) { AssetFileInfo info = assetInfos[i]; AssetsReplacer replacer = replacers.FirstOrDefault(n => n.GetPathID() == info.index); if (replacer != null) { if (replacer.GetReplacementType() == AssetsReplacementType.AssetsReplacement_AddOrModify) { replacer.Write(writer); if (i != assetInfos.Count - 1) { writer.Align8(); } } else if (replacer.GetReplacementType() == AssetsReplacementType.AssetsReplacement_Remove) { continue; } } else { AssetFileInfo originalInfo = originalAssetInfos.FirstOrDefault(n => n.index == info.index); if (originalInfo != null) { reader.Position = header.firstFileOffset + originalInfo.curFileOffset; byte[] assetData = reader.ReadBytes((int)originalInfo.curFileSize); writer.Write(assetData); if (i != assetInfos.Count - 1) { writer.Align8(); } } } } header.firstFileOffset = offs_firstFile; long fileSizeMarker = writer.Position; reader.Position = header.firstFileOffset; writer.Position = 0; header.metadataSize = metadataSize; header.fileSize = (uint)fileSizeMarker; header.Write(writer); }
//set fileID to -1 if all replacers are for this .assets file but don't have the fileID set to the same one //typeMeta is used to add the type information (hash and type fields) for format >= 0x10 if necessary public ulong Write(AssetsFileWriter writer, ulong filePos, AssetsReplacer[] pReplacers, uint fileID, ClassDatabaseFile typeMeta = null) { header.Write(writer.Position, writer); for (int i = 0; i < pReplacers.Length; i++) { AssetsReplacer replacer = pReplacers[i]; if (!typeTree.pTypes_Unity5.Any(t => t.classId == replacer.GetClassID())) { Type_0D type = new Type_0D() { classId = replacer.GetClassID(), unknown16_1 = 0, scriptIndex = 0xFFFF, unknown5 = 0, unknown6 = 0, unknown7 = 0, unknown8 = 0, typeFieldsExCount = 0, stringTableLen = 0, pStringTable = "" }; typeTree.pTypes_Unity5.Concat(new Type_0D[] { type }); } } typeTree.Write(writer.Position, writer, header.format); int initialSize = (int)(AssetFileInfo.GetSize(header.format) * AssetCount); int newSize = (int)(AssetFileInfo.GetSize(header.format) * (AssetCount + pReplacers.Length)); int appendedSize = newSize - initialSize; reader.Position = AssetTablePos; List <AssetFileInfo> originalAssetInfos = new List <AssetFileInfo>(); List <AssetFileInfo> assetInfos = new List <AssetFileInfo>(); List <AssetsReplacer> currentReplacers = pReplacers.ToList(); uint currentOffset = 0; //-write all original assets, modify sizes if needed and skip those to be removed for (int i = 0; i < AssetCount; i++) { AssetFileInfo info = new AssetFileInfo(); info.Read(header.format, reader.Position, reader, reader.bigEndian); originalAssetInfos.Add(info); AssetsReplacer replacer = currentReplacers.FirstOrDefault(n => n.GetPathID() == info.index); if (replacer != null) { if (replacer.GetReplacementType() == AssetsReplacementType.AssetsReplacement_AddOrModify) { int classIndex = Array.FindIndex(typeTree.pTypes_Unity5, t => t.classId == replacer.GetClassID()); info = new AssetFileInfo() { index = replacer.GetPathID(), offs_curFile = currentOffset, curFileSize = (uint)classIndex, curFileTypeOrIndex = (uint)replacer.GetClassID(), inheritedUnityClass = (ushort)replacer.GetClassID(), //-what is this scriptIndex = replacer.GetMonoScriptID(), unknown1 = 0 }; } else if (replacer.GetReplacementType() == AssetsReplacementType.AssetsReplacement_Remove) { continue; } } currentOffset += info.curFileSize; uint pad = 8 - (currentOffset % 8); if (pad != 8) { currentOffset += pad; } assetInfos.Add(info); } //-write new assets while (currentReplacers.Count > 0) { AssetsReplacer replacer = currentReplacers.First(); if (replacer.GetReplacementType() == AssetsReplacementType.AssetsReplacement_AddOrModify) { int classIndex = Array.FindIndex(typeTree.pTypes_Unity5, t => t.classId == replacer.GetClassID()); AssetFileInfo info = new AssetFileInfo() { index = replacer.GetPathID(), offs_curFile = currentOffset, curFileSize = (uint)replacer.GetSize(), curFileTypeOrIndex = (uint)classIndex, inheritedUnityClass = (ushort)replacer.GetClassID(), scriptIndex = replacer.GetMonoScriptID(), unknown1 = 0 }; currentOffset += info.curFileSize; uint pad = 8 - (currentOffset % 8); if (pad != 8) { currentOffset += pad; } assetInfos.Add(info); } currentReplacers.Remove(replacer); } writer.Write(assetInfos.Count); writer.Align(); for (int i = 0; i < assetInfos.Count; i++) { assetInfos[i].Write(header.format, writer.Position, writer); } preloadTable.Write(writer.Position, writer, header.format); dependencies.Write(writer.Position, writer, header.format); uint metadataSize = (uint)writer.Position - 0x13; //-for padding only. if all initial data before assetData is more than 0x1000, this is skipped while (writer.Position < 0x1000 /*header.offs_firstFile*/) { writer.Write((byte)0x00); } header.offs_firstFile = (uint)writer.Position; for (int i = 0; i < assetInfos.Count; i++) { AssetFileInfo info = assetInfos[i]; AssetsReplacer replacer = pReplacers.FirstOrDefault(n => n.GetPathID() == info.index); if (replacer != null) { if (replacer.GetReplacementType() == AssetsReplacementType.AssetsReplacement_AddOrModify) { replacer.Write(writer.Position, writer); writer.Align8(); } else if (replacer.GetReplacementType() == AssetsReplacementType.AssetsReplacement_Remove) { continue; } } else { AssetFileInfo originalInfo = originalAssetInfos.FirstOrDefault(n => n.index == info.index); if (originalInfo != null) { reader.Position = header.offs_firstFile + originalInfo.offs_curFile; byte[] assetData = reader.ReadBytes((int)originalInfo.curFileSize); writer.Write(assetData); writer.Align8(); } } } ulong fileSizeMarker = writer.Position; reader.Position = header.offs_firstFile; writer.Position = 0; header.metadataSize = metadataSize; header.fileSize = (uint)fileSizeMarker; header.Write(writer.Position, writer); return(writer.Position); }