private void ReplaceReferencesRecurse(AssetsFileInstance inst, AssetTypeValueField field, AssetFileInfoEx inf) { foreach (AssetTypeValueField child in field.children) { //not a value (ie int) if (!child.templateField.hasValue) { //not null if (child == null) { return; } //not array of values either if (child.templateField.isArray && child.templateField.children[1].valueType != EnumValueTypes.ValueType_None) { break; } string typeName = child.templateField.type; //is a pptr if (typeName.StartsWith("PPtr<") && typeName.EndsWith(">") && child.childrenCount == 2) { int fileId = child.Get("m_FileID").GetValue().AsInt(); long pathId = child.Get("m_PathID").GetValue().AsInt64(); //not a null pptr if (pathId == 0) { continue; } AssetID aid = ConvertToAssetID(inst, fileId, pathId); //not already visited if (references.ContainsKey(aid)) { AssetID id = references[aid]; //normally, I would've just checked if the path names //matched up, but this is faster than looking up names //I check type of this asset and compare with the name //of the assetid to see if it should be itself or if //it should be the dependency file bool isSelfAsset = IsAsset(inf); bool isDepAsset = id.fileName == ASSET_LEVEL_NAME; int newFileId = isDepAsset ^ isSelfAsset ? 1 : 0; child.Get("m_FileID").GetValue().Set(newFileId); child.Get("m_PathID").GetValue().Set(id.pathId); } else { /////////////// todo move to another method AssetsFileInstance depInst = ConvertToInstance(inst, fileId); AssetFileInfoEx depInf = depInst.table.GetAssetInfo(pathId); if (depInf.curFileType == 0x72) { ushort scriptIndex = depInst.file.typeTree.unity5Types[depInf.curFileTypeOrIndex].scriptIndex; if (tk2dSpriteScriptIndex == 0xffff) { AssetTypeValueField monoBase = am.GetATI(depInst.file, depInf).GetBaseField(); AssetExternal scriptBaseExt = am.GetExtAsset(depInst, monoBase.Get("m_Script")); if (scriptBaseExt.instance != null) { AssetTypeValueField scriptBase = scriptBaseExt.instance.GetBaseField(); string scriptName = scriptBase.Get("m_ClassName").GetValue().AsString(); if (scriptName == "tk2dSprite") { tk2dSpriteScriptIndex = scriptIndex; } } } if (tk2dSpriteScriptIndex == depInst.file.typeTree.unity5Types[depInf.curFileTypeOrIndex].scriptIndex) { string managedPath = Path.Combine(Path.GetDirectoryName(depInst.path), "Managed"); AssetTypeValueField spriteBase = am.GetMonoBaseFieldCached(depInst, depInf, managedPath); int spriteId = spriteBase.Get("_spriteId").GetValue().AsInt(); AssetExternal colBaseExt = am.GetExtAsset(depInst, spriteBase.Get("collection")); AssetsFileInstance colInst = colBaseExt.file; AssetTypeValueField colBase = am.GetMonoBaseFieldCached(colInst, colBaseExt.info, managedPath); AssetTypeValueField spriteDefinitions = colBase.Get("spriteDefinitions")[spriteId]; AssetTypeValueField positionsField = spriteDefinitions.Get("positions"); AssetTypeValueField uvsField = spriteDefinitions.Get("uvs"); AssetTypeValueField indicesField = spriteDefinitions.Get("indices"); Vector3[] positions = new Vector3[positionsField.GetChildrenCount()]; Vector2[] uvs = new Vector2[uvsField.GetChildrenCount()]; int[] indices = new int[indicesField.GetChildrenCount()]; for (int i = 0; i < positions.Length; i++) { AssetTypeValueField positionField = positionsField[i]; positions[i] = new Vector3() { x = positionField.Get("x").GetValue().AsFloat(), y = positionField.Get("y").GetValue().AsFloat(), z = positionField.Get("z").GetValue().AsFloat() }; } for (int i = 0; i < uvs.Length; i++) { AssetTypeValueField uvField = uvsField[i]; uvs[i] = new Vector2() { x = uvField.Get("x").GetValue().AsFloat(), y = uvField.Get("y").GetValue().AsFloat() }; } for (int i = 0; i < indices.Length; i++) { AssetTypeValueField indexField = indicesField[i]; indices[i] = indexField.GetValue().AsInt(); } AssetID thisAid = ConvertToAssetID(inst, 0, inf.index); tk2dFromGoLookup[thisAid] = new Tk2dInfo(positions, uvs, indices); } } /////////////// child.Get("m_FileID").GetValue().Set(0); child.Get("m_PathID").GetValue().Set(0); } } ReplaceReferencesRecurse(inst, child, inf); } } }
private byte[] CreateEditDifferMonoBehaviour(long goPid, AssetTypeValueField componentArray, AssetID origGoPptr) { byte[] data; using (MemoryStream ms = new MemoryStream()) using (AssetsFileWriter w = new AssetsFileWriter(ms)) { w.bigEndian = false; w.Write(0); w.Write(goPid); w.Write(1); w.Write(2); w.Write((long)11500000); w.WriteCountStringInt32(""); w.Align(); w.Write(0); w.Write(origGoPptr.pathId); w.Write(origGoPptr.pathId); w.Write(0); int componentArrayLength = componentArray.GetValue().AsArray().size; w.Write(componentArrayLength); for (int i = 0; i < componentArrayLength; i++) { AssetTypeValueField component = componentArray[i].Get("component"); int m_FileID = component.Get("m_FileID").GetValue().AsInt(); long m_PathID = component.Get("m_PathID").GetValue().AsInt64(); if (m_PathID == 0) //removed (monobehaviour) { w.Write((long)-1); } else if (m_FileID == 0) //correct file { w.Write(m_PathID); } else //another file (shouldn't happen?) { w.Write((long)0); } } w.Write(0 /*rand.Next()*/); data = ms.ToArray(); } return(data); }
public static byte[] CreateBundleFromLevel(AssetsManager am, AssetsFileInstance inst) { EditorUtility.DisplayProgressBar("HKEdit", "Reading Files...", 0f); am.UpdateDependencies(); //quicker asset id lookup for (int i = 0; i < am.files.Count; i++) { AssetsFileInstance afi = am.files[i]; EditorUtility.DisplayProgressBar("HKEdit", "Generating QLTs", (float)i / am.files.Count); afi.table.GenerateQuickLookupTree(); } //setup AssetsFile file = inst.file; AssetsFileTable table = inst.table; string folderName = Path.GetDirectoryName(inst.path); List <AssetFileInfoEx> infos = table.pAssetFileInfo.ToList(); List <string> fileNames = new List <string>(); Dictionary <AssetID, byte[]> deps = new Dictionary <AssetID, byte[]>(); fileNames.Add(inst.name); //add own ids to list so we don't reread them foreach (AssetFileInfoEx info in infos) { if (info.curFileType != 1) { continue; } AssetID id = new AssetID(inst.name, (long)info.index); deps.Add(id, null); } //look through each field in each asset in this file for (int i = 0; i < infos.Count; i++) { AssetFileInfoEx info = infos[i]; if (info.curFileType != 1) { continue; } EditorUtility.DisplayProgressBar("HKEdit", "Crawling PPtrs", (float)i / infos.Count); ReferenceCrawler.CrawlPPtrs(am, inst, info.index, fileNames, deps); } //add typetree data for dependencies long curId = 1; List <Type_0D> types = new List <Type_0D>(); List <string> typeNames = new List <string>(); List <AssetsReplacer> assets = new List <AssetsReplacer>(); Dictionary <string, AssetsFileInstance> insts = new Dictionary <string, AssetsFileInstance>(); //asset id is our custom id that uses filename/pathid instead of fileid/pathid //asset id to path id Dictionary <AssetID, long> aidToPid = new Dictionary <AssetID, long>(); //script id to mono id Dictionary <ScriptID, ushort> sidToMid = new Dictionary <ScriptID, ushort>(); uint lastId = 0; ushort nextMonoId = 0; int depCount = 0; foreach (KeyValuePair <AssetID, byte[]> dep in deps) { EditorUtility.DisplayProgressBar("HKEdit", "Fixing Dependencies", (float)depCount / deps.Keys.Count); AssetID id = dep.Key; byte[] assetData = dep.Value; AssetsFileInstance afInst = null; if (insts.ContainsKey(id.fileName)) { afInst = insts[id.fileName]; } else { afInst = am.files.First(f => f.name == id.fileName); } if (afInst == null) { continue; } AssetFileInfoEx inf = afInst.table.getAssetInfo((ulong)id.pathId); if (lastId != inf.curFileType) { lastId = inf.curFileType; } ClassDatabaseType clType = AssetHelper.FindAssetClassByID(am.classFile, inf.curFileType); string clName = clType.name.GetString(am.classFile); ushort monoIndex = 0xFFFF; if (inf.curFileType != 0x72) { if (!typeNames.Contains(clName)) { Type_0D type0d = C2T5.Cldb2TypeTree(am.classFile, clName); type0d.classId = (int)inf.curFileType; //? types.Add(type0d); typeNames.Add(clName); } } else { //unused for now AssetTypeValueField baseField = am.GetATI(afInst.file, inf).GetBaseField(); AssetTypeValueField m_Script = baseField.Get("m_Script"); AssetTypeValueField scriptBaseField = am.GetExtAsset(afInst, m_Script).instance.GetBaseField(); string m_ClassName = scriptBaseField.Get("m_ClassName").GetValue().AsString(); string m_Namespace = scriptBaseField.Get("m_Namespace").GetValue().AsString(); string m_AssemblyName = scriptBaseField.Get("m_AssemblyName").GetValue().AsString(); ScriptID sid = new ScriptID(m_ClassName, m_Namespace, m_AssemblyName); if (!sidToMid.ContainsKey(sid)) { MonoClass mc = new MonoClass(); mc.Read(m_ClassName, Path.Combine(Path.Combine(Path.GetDirectoryName(inst.path), "Managed"), m_AssemblyName), afInst.file.header.format); Type_0D type0d = C2T5.Cldb2TypeTree(am.classFile, clName); TemplateFieldToType0D typeConverter = new TemplateFieldToType0D(); TypeField_0D[] monoFields = typeConverter.TemplateToTypeField(mc.children, type0d); type0d.pStringTable = typeConverter.stringTable; type0d.stringTableLen = (uint)type0d.pStringTable.Length; type0d.scriptIndex = nextMonoId; type0d.pTypeFieldsEx = type0d.pTypeFieldsEx.Concat(monoFields).ToArray(); type0d.typeFieldsExCount = (uint)type0d.pTypeFieldsEx.Length; types.Add(type0d); sidToMid.Add(sid, nextMonoId); nextMonoId++; } monoIndex = sidToMid[sid]; } aidToPid.Add(id, curId); AssetsReplacer rep = new AssetsReplacerFromMemory(0, (ulong)curId, (int)inf.curFileType, monoIndex, assetData); assets.Add(rep); curId++; depCount++; } byte[] blankData = BundleCreator.CreateBlankAssets(ver, types); AssetsFile blankFile = new AssetsFile(new AssetsFileReader(new MemoryStream(blankData))); EditorUtility.DisplayProgressBar("HKEdit", "Writing first file...", 0f); byte[] data = null; using (MemoryStream ms = new MemoryStream()) using (AssetsFileWriter writer = new AssetsFileWriter(ms)) { blankFile.Write(writer, 0, assets.ToArray(), 0); data = ms.ToArray(); } //File.WriteAllBytes("debug.assets", data); MemoryStream msn = new MemoryStream(data); AssetsManager amn = new AssetsManager(); amn.classFile = am.classFile; AssetsFileInstance instn = amn.LoadAssetsFile(msn, ((FileStream)inst.file.reader.BaseStream).Name, false); instn.table.GenerateQuickLookupTree(); deps.Clear(); List <AssetsReplacer> assetsn = new List <AssetsReplacer>(); //gameobject id to mono id Dictionary <long, long> gidToMid = new Dictionary <long, long>(); long nextBehaviourId = (long)instn.table.pAssetFileInfo.Max(i => i.index) + 1; CreateEditDifferTypeTree(amn.classFile, instn); CreateSceneMetadataTypeTree(amn.classFile, instn); Random rand = new Random(); rand.Next(); foreach (KeyValuePair <AssetID, long> kvp in aidToPid) { AssetFileInfoEx inf = instn.table.getAssetInfo((ulong)kvp.Value); if (inf.curFileType == 0x01) { gidToMid.Add(kvp.Value, nextBehaviourId); assetsn.Add(CreateEditDifferMonoBehaviour(kvp.Value, kvp.Key, nextBehaviourId++, rand)); } } for (int i = 0; i < instn.table.pAssetFileInfo.Length; i++) { AssetFileInfoEx inf = instn.table.pAssetFileInfo[i]; EditorUtility.DisplayProgressBar("HKEdit", "Crawling PPtrs", (float)i / instn.table.pAssetFileInfo.Length); ReferenceCrawler.CrawlReplacePPtrs(amn, instn, inf.index, fileNames, deps, aidToPid, gidToMid); } //add monoscript assets to preload table to make unity happy List <AssetPPtr> preloadPptrs = new List <AssetPPtr>(); preloadPptrs.Add(new AssetPPtr(1, 11500000)); preloadPptrs.Add(new AssetPPtr(2, 11500000)); foreach (KeyValuePair <AssetID, byte[]> dep in deps) { AssetID id = dep.Key; byte[] assetData = dep.Value; long pid = id.pathId; if (pid == 1) { assetData = AddMetadataMonobehaviour(assetData, nextBehaviourId); } AssetFileInfoEx inf = instn.table.getAssetInfo((ulong)pid); ushort monoId = instn.file.typeTree.pTypes_Unity5[inf.curFileTypeOrIndex].scriptIndex; assetsn.Add(new AssetsReplacerFromMemory(0, (ulong)pid, (int)inf.curFileType, monoId, assetData)); if (inf.curFileType == 0x73) { preloadPptrs.Add(new AssetPPtr(0, (ulong)pid)); } } List <long> usedIds = assetsn.Select(a => (long)a.GetPathID()).ToList(); //will break if no gameobjects but I don't really care at this point assetsn.Add(CreateSceneMetadataMonoBehaviour(1, nextBehaviourId++, inst.name, usedIds)); instn.file.preloadTable.items = preloadPptrs.ToArray(); instn.file.preloadTable.len = (uint)instn.file.preloadTable.items.Length; //add dependencies to monobehaviours List <AssetsFileDependency> fileDeps = new List <AssetsFileDependency>(); AddScriptDependency(fileDeps, Constants.editDifferMsEditorScriptHash, Constants.editDifferLsEditorScriptHash); AddScriptDependency(fileDeps, Constants.sceneMetadataMsEditorScriptHash, Constants.sceneMetadataLsEditorScriptHash); instn.file.dependencies.pDependencies = fileDeps.ToArray(); instn.file.dependencies.dependencyCount = (uint)fileDeps.Count; EditorUtility.DisplayProgressBar("HKEdit", "Writing second file...", 0f); byte[] datan = null; using (MemoryStream ms = new MemoryStream()) using (AssetsFileWriter writer = new AssetsFileWriter(ms)) { instn.file.Write(writer, 0, assetsn.ToArray(), 0); datan = ms.ToArray(); } EditorUtility.ClearProgressBar(); return(datan); }