private static void CreateSceneMetadataTypeTree(ClassDatabaseFile cldb, AssetsFileInstance inst) { Type_0D type = C2T5.Cldb2TypeTree(cldb, 0x72); type.scriptIndex = 0x0001; type.unknown1 = Constants.sceneMetadataScriptNEHash[0]; type.unknown2 = Constants.sceneMetadataScriptNEHash[1]; type.unknown3 = Constants.sceneMetadataScriptNEHash[2]; type.unknown4 = Constants.sceneMetadataScriptNEHash[3]; TypeTreeEditor editor = new TypeTreeEditor(type); TypeField_0D baseField = type.pTypeFieldsEx[0]; uint sceneName = editor.AddField(baseField, editor.CreateTypeField("string", "sceneName", 1, uint.MaxValue, 0, false, false, Flags.AnyChildUsesAlignBytesFlag)); uint Array = editor.AddField(editor.type.pTypeFieldsEx[sceneName], editor.CreateTypeField("Array", "Array", 2, uint.MaxValue, 0, true, true, Flags.HideInEditorMask)); editor.AddField(editor.type.pTypeFieldsEx[Array], editor.CreateTypeField("int", "size", 3, 4, 0, false, false, Flags.HideInEditorMask)); editor.AddField(editor.type.pTypeFieldsEx[Array], editor.CreateTypeField("char", "data", 3, 1, 0, false, false, Flags.HideInEditorMask)); uint usedIds = editor.AddField(baseField, editor.CreateTypeField("vector", "usedIds", 1, uint.MaxValue, 0, false, false, Flags.AnyChildUsesAlignBytesFlag)); uint Array2 = editor.AddField(editor.type.pTypeFieldsEx[usedIds], editor.CreateTypeField("Array", "Array", 2, uint.MaxValue, 0, true, true)); editor.AddField(editor.type.pTypeFieldsEx[Array2], editor.CreateTypeField("int", "size", 3, 4, 0, false)); editor.AddField(editor.type.pTypeFieldsEx[Array2], editor.CreateTypeField("SInt64", "data", 3, 8, 0, false)); editor.AddField(baseField, editor.CreateTypeField("int", "hkweVersion", 1, 4, 0, false)); type = editor.SaveType(); inst.file.typeTree.pTypes_Unity5 = inst.file.typeTree.pTypes_Unity5.Concat(new Type_0D[] { type }).ToArray(); inst.file.typeTree.fieldCount++; }
///////////////////////////////////////////////////////// private static Type_0D CreateEditDifferTypeTree(ClassDatabaseFile cldb) { Type_0D type = C2T5.Cldb2TypeTree(cldb, 0x72); type.scriptIndex = 0x0000; type.unknown1 = Constants.editDifferScriptNEHash[0]; type.unknown2 = Constants.editDifferScriptNEHash[1]; type.unknown3 = Constants.editDifferScriptNEHash[2]; type.unknown4 = Constants.editDifferScriptNEHash[3]; TypeTreeEditor editor = new TypeTreeEditor(type); TypeField_0D baseField = type.pTypeFieldsEx[0]; editor.AddField(baseField, editor.CreateTypeField("unsigned int", "fileId", 1, 4, 0, false)); editor.AddField(baseField, editor.CreateTypeField("UInt64", "pathId", 1, 8, 0, false)); editor.AddField(baseField, editor.CreateTypeField("UInt64", "origPathId", 1, 8, 0, false)); editor.AddField(baseField, editor.CreateTypeField("UInt8", "newAsset", 1, 1, 0, true)); uint componentIds = editor.AddField(baseField, editor.CreateTypeField("vector", "componentIds", 1, uint.MaxValue, 0, false, false, Flags.AnyChildUsesAlignBytesFlag)); uint Array = editor.AddField(editor.type.pTypeFieldsEx[componentIds], editor.CreateTypeField("Array", "Array", 2, uint.MaxValue, 0, true, true)); editor.AddField(editor.type.pTypeFieldsEx[Array], editor.CreateTypeField("int", "size", 3, 4, 0, false)); editor.AddField(editor.type.pTypeFieldsEx[Array], editor.CreateTypeField("SInt64", "data", 3, 8, 0, false)); editor.AddField(baseField, editor.CreateTypeField("int", "instanceId", 1, 4, 0, false)); type = editor.SaveType(); return(type); }
private static Type_0D CreateTk2dEmuTypeTree(ClassDatabaseFile cldb) { Type_0D type = C2T5.Cldb2TypeTree(cldb, 0x72); type.scriptIndex = 0x0001; type.scriptHash1 = Constants.tk2dEmuScriptNEHash[0]; type.scriptHash2 = Constants.tk2dEmuScriptNEHash[1]; type.scriptHash3 = Constants.tk2dEmuScriptNEHash[2]; type.scriptHash4 = Constants.tk2dEmuScriptNEHash[3]; TypeTreeEditor editor = new TypeTreeEditor(type); TypeField_0D baseField = type.typeFieldsEx[0]; uint vertices = editor.AddField(baseField, editor.CreateTypeField("vector", "vertices", 1, -1, 0, false, false, Flags.AnyChildUsesAlignBytesFlag)); uint Array = editor.AddField(editor.type.typeFieldsEx[vertices], editor.CreateTypeField("Array", "Array", 2, -1, 0, true, true)); editor.AddField(editor.type.typeFieldsEx[Array], editor.CreateTypeField("int", "size", 3, 4, 0, false)); uint data = editor.AddField(editor.type.typeFieldsEx[Array], editor.CreateTypeField("Vector3", "data", 3, -1, 0, false)); editor.AddField(editor.type.typeFieldsEx[data], editor.CreateTypeField("float", "x", 4, 4, 0, false)); editor.AddField(editor.type.typeFieldsEx[data], editor.CreateTypeField("float", "y", 4, 4, 0, false)); editor.AddField(editor.type.typeFieldsEx[data], editor.CreateTypeField("float", "z", 4, 4, 0, false)); uint uvs = editor.AddField(baseField, editor.CreateTypeField("vector", "uvs", 1, -1, 0, false, false, Flags.AnyChildUsesAlignBytesFlag)); Array = editor.AddField(editor.type.typeFieldsEx[uvs], editor.CreateTypeField("Array", "Array", 2, -1, 0, true, true)); editor.AddField(editor.type.typeFieldsEx[Array], editor.CreateTypeField("int", "size", 3, 4, 0, false)); data = editor.AddField(editor.type.typeFieldsEx[Array], editor.CreateTypeField("Vector2", "data", 3, -1, 0, false)); editor.AddField(editor.type.typeFieldsEx[data], editor.CreateTypeField("float", "x", 4, 4, 0, false)); editor.AddField(editor.type.typeFieldsEx[data], editor.CreateTypeField("float", "y", 4, 4, 0, false)); uint indices = editor.AddField(baseField, editor.CreateTypeField("vector", "indices", 1, -1, 0, false, false, Flags.AnyChildUsesAlignBytesFlag)); Array = editor.AddField(editor.type.typeFieldsEx[indices], editor.CreateTypeField("Array", "Array", 2, -1, 0, true, true)); editor.AddField(editor.type.typeFieldsEx[Array], editor.CreateTypeField("int", "size", 3, 4, 0, false)); editor.AddField(editor.type.typeFieldsEx[Array], editor.CreateTypeField("int", "data", 3, 4, 0, false)); type = editor.SaveType(); return(type); }
///////////////////////////////////////////////////////// private static void CreateEditDifferTypeTree(ClassDatabaseFile cldb, AssetsFileInstance inst) { Type_0D type = C2T5.Cldb2TypeTree(cldb, 0x72); type.scriptIndex = 0x0000; type.unknown1 = Constants.editDifferScriptNEHash[0]; type.unknown2 = Constants.editDifferScriptNEHash[1]; type.unknown3 = Constants.editDifferScriptNEHash[2]; type.unknown4 = Constants.editDifferScriptNEHash[3]; TypeTreeEditor editor = new TypeTreeEditor(type); TypeField_0D baseField = type.pTypeFieldsEx[0]; editor.AddField(baseField, editor.CreateTypeField("unsigned int", "fileId", 1, 4, 0, false)); editor.AddField(baseField, editor.CreateTypeField("UInt64", "pathId", 1, 8, 0, false)); editor.AddField(baseField, editor.CreateTypeField("UInt64", "origPathId", 1, 8, 0, false)); editor.AddField(baseField, editor.CreateTypeField("UInt8", "newAsset", 1, 1, 0, true)); editor.AddField(baseField, editor.CreateTypeField("int", "instanceId", 1, 4, 0, false)); type = editor.SaveType(); inst.file.typeTree.pTypes_Unity5 = inst.file.typeTree.pTypes_Unity5.Concat(new Type_0D[] { type }).ToArray(); inst.file.typeTree.fieldCount++; }
public static void GenerateLevelFiles(AssetsManager am, AssetsFileInstance inst) { EditorUtility.DisplayProgressBar("HKEdit", "Reading dependencies...", 0.5f); am.UpdateDependencies(); //quicker asset id lookup for (int i = 0; i < am.files.Count; i++) { AssetsFileInstance afi = am.files[i]; if (i % 100 == 0) { EditorUtility.DisplayProgressBar("HKEdit", "Generating QLTs...", (float)i / am.files.Count); } afi.table.GenerateQuickLookupTree(); } ClassDatabaseFile cldb = am.classFile; AssetsFileTable table = inst.table; ReferenceCrawler crawler = new ReferenceCrawler(am); List <AssetFileInfoEx> initialGameObjects = table.GetAssetsOfType(0x01); for (int i = 0; i < initialGameObjects.Count; i++) { if (i % 100 == 0) { EditorUtility.DisplayProgressBar("HKEdit", "Recursing GameObject dependencies... (step 1/3)", (float)i / initialGameObjects.Count); } AssetFileInfoEx inf = initialGameObjects[i]; crawler.AddReference(new AssetID(inst.path, (long)inf.index), false); crawler.FindReferences(inst, inf); } Dictionary <AssetID, AssetID> glblToLcl = crawler.references; List <Type_0D> types = new List <Type_0D>(); List <string> typeNames = new List <string>(); Dictionary <string, AssetsFileInstance> fileToInst = am.files.ToDictionary(d => d.path); int j = 0; foreach (KeyValuePair <AssetID, AssetID> id in glblToLcl) { if (j % 100 == 0) { EditorUtility.DisplayProgressBar("HKEdit", "Rewiring asset pointers... (step 2/3)", (float)j / glblToLcl.Count); } AssetsFileInstance depInst = fileToInst[id.Key.fileName]; AssetFileInfoEx depInf = depInst.table.getAssetInfo((ulong)id.Key.pathId); ClassDatabaseType clType = AssetHelper.FindAssetClassByID(cldb, depInf.curFileType); string clName = clType.name.GetString(cldb); if (!typeNames.Contains(clName)) { Type_0D type0d = C2T5.Cldb2TypeTree(cldb, clName); type0d.classId = (int)depInf.curFileType; types.Add(type0d); typeNames.Add(clName); } crawler.ReplaceReferences(depInst, depInf, id.Value.pathId); j++; } EditorUtility.DisplayProgressBar("HKEdit", "Saving scene... (step 3/3)", 1f); types.Add(CreateEditDifferTypeTree(cldb)); List <Type_0D> assetTypes = new List <Type_0D>() { C2T5.Cldb2TypeTree(cldb, 0x1c), C2T5.Cldb2TypeTree(cldb, 0x30), C2T5.Cldb2TypeTree(cldb, 0x53) }; string origFileName = Path.GetFileNameWithoutExtension(inst.path); string sceneGuid = CreateMD5(origFileName); string ExportedScenes = Path.Combine("Assets", "ExportedScenes"); //circumvents "!BeginsWithCaseInsensitive(file.pathName, AssetDatabase::kAssetsPathWithSlash)' assertion string ExportedScenesData = "ExportedScenesData"; CreateMetaFile(sceneGuid, Path.Combine(ExportedScenes, origFileName + ".unity.meta")); AssetsFile sceneFile = new AssetsFile(new AssetsFileReader(new MemoryStream(BundleCreator.CreateBlankAssets(ver, types)))); AssetsFile assetFile = new AssetsFile(new AssetsFileReader(new MemoryStream(BundleCreator.CreateBlankAssets(ver, assetTypes)))); byte[] sceneFileData; using (MemoryStream ms = new MemoryStream()) using (AssetsFileWriter w = new AssetsFileWriter(ms)) { w.bigEndian = false; //unity editor won't load whole assets files by guid, so we have to use hardcoded paths sceneFile.dependencies.pDependencies = new AssetsFileDependency[] { CreateDependency(ExportedScenesData + "/" + origFileName + "-data.assets"), CreateScriptDependency(Constants.editDifferMsEditorScriptHash, Constants.editDifferLsEditorScriptHash) }; sceneFile.dependencies.dependencyCount = 2; sceneFile.preloadTable.items = new AssetPPtr[] { new AssetPPtr(2, 11500000) }; sceneFile.preloadTable.len = 1; sceneFile.Write(w, 0, crawler.sceneReplacers.Concat(crawler.sceneMonoReplacers).ToArray(), 0); sceneFileData = ms.ToArray(); } byte[] assetFileData; using (MemoryStream ms = new MemoryStream()) using (AssetsFileWriter w = new AssetsFileWriter(ms)) { w.bigEndian = false; assetFile.Write(w, 0, crawler.assetReplacers.ToArray(), 0); assetFileData = ms.ToArray(); } File.WriteAllBytes(Path.Combine(ExportedScenes, origFileName + ".unity"), sceneFileData); File.WriteAllBytes(Path.Combine(ExportedScenesData, origFileName + "-data.assets"), assetFileData); File.WriteAllText(Path.Combine(ExportedScenesData, origFileName + ".metadata"), CreateHKWEMetaFile(am, inst)); EditorUtility.ClearProgressBar(); }
private async void ReturnAssetToAdd() { int fileId = ddFileId.SelectedIndex; //hopefully in order string pathIdText = boxPathId.Text; string typeIdText = boxTypeId.Text; string monoIdText = boxMonoId.Text; bool createBlankAsset = chkCreateZerodAsset.IsChecked ?? false; AssetsFileInstance file = workspace.LoadedFiles[fileId]; AssetTypeTemplateField tempField; byte[] assetBytes; long pathId; int typeId; ushort monoId; if (!long.TryParse(pathIdText, out pathId)) { await MessageBoxUtil.ShowDialog(this, "Bad input", "Path ID was invalid."); return; } if (file.file.typeTree.hasTypeTree) { if (!TryParseTypeTree(file, typeIdText, createBlankAsset, out tempField, out typeId)) { if (!TryParseClassDatabase(typeIdText, createBlankAsset, out tempField, out typeId)) { await MessageBoxUtil.ShowDialog(this, "Bad input", "Class type was invalid."); return; } else { //has typetree but had to lookup to cldb //we need to add a new typetree entry because this is //probably not a type that existed in this bundle file.file.typeTree.unity5Types.Add(C2T5.Cldb2TypeTree(workspace.am.classFile, typeId)); } } } else { if (!TryParseClassDatabase(typeIdText, createBlankAsset, out tempField, out typeId)) { await MessageBoxUtil.ShowDialog(this, "Bad input", "Class type was invalid."); return; } } if (monoIdText == "-1") { monoId = 0xffff; } else if (!ushort.TryParse(monoIdText, out monoId)) { await MessageBoxUtil.ShowDialog(this, "Bad input", "Mono ID was invalid."); return; } if (createBlankAsset) { AssetTypeValueField baseField = ValueBuilder.DefaultValueFieldFromTemplate(tempField); assetBytes = baseField.WriteToByteArray(); } else { assetBytes = new byte[0]; } workspace.AddReplacer(file, new AssetsReplacerFromMemory(0, pathId, typeId, monoId, assetBytes), new MemoryStream(assetBytes)); Close(true); }
public void Write(AssetsFileWriter writer, long filePos, List <AssetsReplacer> replacers, uint fileID = 0, ClassDatabaseFile typeMeta = null) { if (filePos == -1) { filePos = writer.Position; } else { writer.Position = filePos; } header.Write(writer); foreach (AssetsReplacer replacer in replacers) { int replacerClassId = replacer.GetClassID(); ushort replacerScriptIndex = replacer.GetMonoScriptID(); if (!typeTree.unity5Types.Any(t => t.classId == replacerClassId && t.scriptIndex == replacerScriptIndex)) { Type_0D type = null; if (typeMeta != null) { ClassDatabaseType cldbType = AssetHelper.FindAssetClassByID(typeMeta, (uint)replacerClassId); if (cldbType != null) { type = C2T5.Cldb2TypeTree(typeMeta, cldbType); //in original AssetsTools, if you tried to use a new monoId it would just try to use //the highest existing scriptIndex that existed without making a new one (unless there //were no monobehavours ofc) this isn't any better as we just assign a plain monobehaviour //typetree to a type that probably has more fields. I don't really know of a better way to //handle this at the moment as cldbs cannot differentiate monoids. type.scriptIndex = replacerScriptIndex; } } if (type == null) { type = new Type_0D { classId = replacerClassId, unknown16_1 = 0, scriptIndex = replacerScriptIndex, typeHash1 = 0, typeHash2 = 0, typeHash3 = 0, typeHash4 = 0, typeFieldsExCount = 0, stringTableLen = 0, stringTable = "" }; } typeTree.unity5Types.Add(type); } } typeTree.Write(writer, header.format); Dictionary <long, AssetFileInfo> oldAssetInfosByPathId = new Dictionary <long, AssetFileInfo>(); Dictionary <long, AssetsReplacer> replacersByPathId = replacers.ToDictionary(r => r.GetPathID()); List <AssetFileInfo> newAssetInfos = new List <AssetFileInfo>(); // Collect unchanged assets (that aren't getting removed) reader.Position = assetTablePos; for (int i = 0; i < assetCount; i++) { AssetFileInfo oldAssetInfo = new AssetFileInfo(); oldAssetInfo.Read(header.format, reader); oldAssetInfosByPathId.Add(oldAssetInfo.index, oldAssetInfo); if (replacersByPathId.ContainsKey(oldAssetInfo.index)) { continue; } AssetFileInfo newAssetInfo = new AssetFileInfo { index = oldAssetInfo.index, curFileTypeOrIndex = oldAssetInfo.curFileTypeOrIndex, inheritedUnityClass = oldAssetInfo.inheritedUnityClass, scriptIndex = oldAssetInfo.scriptIndex, unknown1 = oldAssetInfo.unknown1 }; newAssetInfos.Add(newAssetInfo); } // Collect modified and new assets foreach (AssetsReplacer replacer in replacers.Where(r => r.GetReplacementType() == AssetsReplacementType.AddOrModify)) { AssetFileInfo newAssetInfo = new AssetFileInfo { index = replacer.GetPathID(), inheritedUnityClass = (ushort)replacer.GetClassID(), //for older unity versions scriptIndex = replacer.GetMonoScriptID(), unknown1 = 0 }; if (header.format < 0x10) { newAssetInfo.curFileTypeOrIndex = replacer.GetClassID(); } else { if (replacer.GetMonoScriptID() == 0xFFFF) { newAssetInfo.curFileTypeOrIndex = typeTree.unity5Types.FindIndex(t => t.classId == replacer.GetClassID()); } else { newAssetInfo.curFileTypeOrIndex = typeTree.unity5Types.FindIndex(t => t.classId == replacer.GetClassID() && t.scriptIndex == replacer.GetMonoScriptID()); } } newAssetInfos.Add(newAssetInfo); } newAssetInfos.Sort((i1, i2) => i1.index.CompareTo(i2.index)); // Write asset infos (will write again later on to update the offsets and sizes) writer.Write(newAssetInfos.Count); writer.Align(); long newAssetTablePos = writer.Position; foreach (AssetFileInfo newAssetInfo in newAssetInfos) { newAssetInfo.Write(header.format, writer); } preloadTable.Write(writer); dependencies.Write(writer); // Temporary fix for secondaryTypeCount and friends if (header.format >= 0x14) { writer.Write(0); //secondaryTypeCount } uint newMetadataSize = (uint)(writer.Position - filePos - 0x13); //0x13 is header - "endianness byte"? (if that's what it even is) if (header.format >= 0x16) { // Remove larger variation fields as well newMetadataSize -= 0x1c; } // For padding only. if all initial data before assetData is more than 0x1000, this is skipped if (writer.Position < 0x1000) { while (writer.Position < 0x1000) { writer.Write((byte)0x00); } } else { if (writer.Position % 16 == 0) { writer.Position += 16; } else { writer.Align16(); } } long newFirstFileOffset = writer.Position; // Write all asset data for (int i = 0; i < newAssetInfos.Count; i++) { AssetFileInfo newAssetInfo = newAssetInfos[i]; newAssetInfo.curFileOffset = writer.Position - newFirstFileOffset; if (replacersByPathId.TryGetValue(newAssetInfo.index, out AssetsReplacer replacer)) { replacer.Write(writer); } else { AssetFileInfo oldAssetInfo = oldAssetInfosByPathId[newAssetInfo.index]; reader.Position = header.firstFileOffset + oldAssetInfo.curFileOffset; reader.BaseStream.CopyToCompat(writer.BaseStream, oldAssetInfo.curFileSize); } newAssetInfo.curFileSize = (uint)(writer.Position - (newFirstFileOffset + newAssetInfo.curFileOffset)); if (i != newAssetInfos.Count - 1) { writer.Align8(); } } long newFileSize = writer.Position - filePos; // Write new header AssetsFileHeader newHeader = new AssetsFileHeader { metadataSize = newMetadataSize, fileSize = newFileSize, format = header.format, firstFileOffset = newFirstFileOffset, endianness = header.endianness, unknown = header.unknown, unknown1 = header.unknown1, unknown2 = header.unknown2 }; writer.Position = filePos; newHeader.Write(writer); // Write new asset infos again (this time with offsets and sizes filled in) writer.Position = newAssetTablePos; foreach (AssetFileInfo newAssetInfo in newAssetInfos) { newAssetInfo.Write(header.format, writer); } // Set writer position back to end of file writer.Position = filePos + newFileSize; }
private static void LoadScene() { am = new AssetsManager(); am.LoadClassPackage("cldb.dat"); am.useTemplateFieldCache = true; am.updateAfterLoad = false; hkDir = Util.SteamHelper.GetHollowKnightDataPath(); var globalgamemanagers = am.LoadAssetsFile(Path.Combine(hkDir, "globalgamemanagers"), false); var buildSettings = globalgamemanagers.table.getAssetInfo(11); var baseField = am.GetATI(globalgamemanagers.file, buildSettings).GetBaseField(); var scenesArray = baseField.Get("scenes").Get("Array"); // TODO: Allow level to be selected const int level = 0; var levelName = scenesArray[level].GetValue().AsString().Substring(14); levelName = levelName.Substring(0, levelName.Length - 6); progressBarTitle = "Loading Scene #" + level + ": " + levelName; if (ShouldCancel("Checking workspace", 0.2f)) { return; } if (!baseField.Get("m_Version").GetValue().AsString().Equals(Application.unityVersion)) { EditorUtility.ClearProgressBar(); EditorUtility.DisplayDialog("Incorrect Unity version!", " You are using " + Application.unityVersion + " but the assets are compiled for version " + baseField.Get("m_Version").GetValue().AsString(), "Ok"); return; } unityVersion = Application.unityVersion; if (!Directory.Exists(scenesDir)) { Directory.CreateDirectory(scenesDir); } if (!Directory.Exists(dataDir)) { Directory.CreateDirectory(dataDir); } var metaFilePath = Path.Combine(scenesDir, "level" + level + ".unity.meta"); var sceneFilePath = Path.Combine(scenesDir, "level" + level + ".unity"); var assetsFilePath = Path.Combine(dataDir, "level" + level + ".assets"); if (File.Exists(sceneFilePath)) { if (EditorUtility.DisplayDialog("Overwrite scene?", "" + " You have already exported this scene. Would you like to overwrite it or open the existing scene? ", "Open Existing", "Overwrite")) { if (ShouldCancel("Opening Scene", 0.5f)) { return; } EditorSceneManager.OpenScene(sceneFilePath); EditorUtility.ClearProgressBar(); return; } else { File.Delete(sceneFilePath); } } if (File.Exists(metaFilePath)) { File.Delete(metaFilePath); } if (File.Exists(assetsFilePath)) { File.Delete(assetsFilePath); } curSceneId = 1; curAssetId = 1; pointers = new Dictionary <AssetID, AssetID>(); sceneReplacers = new List <AssetsReplacer>(); assetReplacers = new List <AssetsReplacer>(); monoReplacers = new List <AssetsReplacer>(); if (ShouldCancel("Reading level file", 0.5f)) { return; } var scenePath = Path.Combine(Util.SteamHelper.GetHollowKnightDataPath(), "level" + level); var scene = am.LoadAssetsFile(scenePath, true); if (ShouldCancel("Updating Dependencies", 0.8f)) { return; } am.UpdateDependencies(); for (var i = 0; i < am.files.Count; i++) { if (i % 100 == 0 && ShouldCancel("Generating QLTs", (float)i / am.files.Count)) { return; } am.files[i].table.GenerateQuickLookupTree(); } var table = scene.table; var gameObjects = table.GetAssetsOfType(0x01); var gameObjectBaseFields = new Dictionary <AssetFileInfoEx, AssetTypeValueField>(); var c = 0; for (c = 0; c < gameObjects.Count; c++) { if (c % 100 == 0 && ShouldCancel("Finding initial GameObjects", (float)c / gameObjects.Count)) { return; } var gameObjectInfo = gameObjects[c]; var gameObjectBaseField = am.GetATI(scene.file, gameObjectInfo).GetBaseField(); AddPointer(new AssetID(scene.path, (long)gameObjectInfo.index), false); gameObjectBaseFields.Add(gameObjectInfo, gameObjectBaseField); } c = 0; foreach (var pair in gameObjectBaseFields) { if (c % 100 == 0 && ShouldCancel("Recursing GameObject dependencies", (float)c / gameObjectBaseFields.Count)) { return; } FindNestedPointers(scene, pair.Value, pair.Key, false); c++; } var types = new List <Type_0D>(); var typeNames = new List <string>(); var fileToInst = am.files.ToDictionary(d => d.path); var j = 0; foreach (var pair in pointers) { if (j % 100 == 0 && ShouldCancel("Rewiring asset pointers", (float)j / pointers.Count)) { return; } var file = fileToInst[pair.Key.fileName]; var info = file.table.getAssetInfo((ulong)pair.Key.pathId); var assetClass = AssetHelper.FindAssetClassByID(am.classFile, info.curFileType); var assetName = assetClass.name.GetString(am.classFile); if (!typeNames.Contains(assetName)) { var type0d = C2T5.Cldb2TypeTree(am.classFile, assetName); type0d.classId = (int)info.curFileType; types.Add(type0d); typeNames.Add(assetName); } ReplacePointers(file, info, pair.Value.pathId); j++; } if (ShouldCancel("Saving scene", 1)) { return; } List <Type_0D> assetTypes = new List <Type_0D>() { C2T5.Cldb2TypeTree(am.classFile, 0x1c), // audioclip C2T5.Cldb2TypeTree(am.classFile, 0x30), // shader C2T5.Cldb2TypeTree(am.classFile, 0x53) // texture2d }; var sceneGuid = Util.UnityHelper.CreateMD5(levelName); UnityHelper.CreateMetaFile(sceneGuid, metaFilePath); var sceneFile = new AssetsFile(new AssetsFileReader(new MemoryStream(BundleCreator.CreateBlankAssets(unityVersion, types)))); var assetFile = new AssetsFile(new AssetsFileReader(new MemoryStream(BundleCreator.CreateBlankAssets(unityVersion, assetTypes)))); byte[] sceneFileData; using (MemoryStream ms = new MemoryStream()) using (AssetsFileWriter w = new AssetsFileWriter(ms)) { w.bigEndian = false; sceneFile.dependencies.pDependencies = new AssetsFileDependency[] { UnityHelper.CreateDependency(assetsFilePath), }; sceneFile.dependencies.dependencyCount = 1; sceneFile.preloadTable.items = new AssetPPtr[] {}; sceneFile.preloadTable.len = 0; sceneFile.Write(w, 0, sceneReplacers.Concat(monoReplacers).ToArray(), 0); sceneFileData = ms.ToArray(); } byte[] assetFileData; using (var ms = new MemoryStream()) using (var w = new AssetsFileWriter(ms)) { w.bigEndian = false; assetFile.Write(w, 0, assetReplacers.ToArray(), 0); assetFileData = ms.ToArray(); } File.WriteAllBytes(sceneFilePath, sceneFileData); File.WriteAllBytes(assetsFilePath, assetFileData); if (ShouldCancel("Refreshing Assets", 0.95f)) { return; } AssetDatabase.Refresh(); EditorUtility.ClearProgressBar(); }
public static byte[] CreateBundleFromLevel(AssetsManager am, AssetsFileInstance inst) { AssetsFile file = inst.file; AssetsFileTable table = inst.table; string folderName = Path.GetDirectoryName(inst.path); ulong pathId = 2; List <AssetsReplacer> assets = new List <AssetsReplacer>(); Dictionary <AssetID, ulong> assetMap = new Dictionary <AssetID, ulong>(); //get all sprites int i = 0; List <AssetFileInfoEx> infos = table.GetAssetsOfType(0xD4); List <ulong> spriteIds = new List <ulong>(); foreach (AssetFileInfoEx info in infos) { //honestly this is a really trash way to do this //we have a better scene exporter but it would //take some work to fix it up and block certain assets EditorUtility.DisplayProgressBar("HKEdit", "Creating scene bundle", i / (float)infos.Count); AssetTypeValueField baseField = GetBaseField(am, file, info); AssetTypeValueField m_Sprite = baseField.Get("m_Sprite"); AssetFileInfoEx spriteInfo = am.GetExtAsset(inst, m_Sprite, true).info; AssetsFileInstance spriteInst; if (m_Sprite.Get("m_FileID").GetValue().AsInt() == 0) { spriteInst = inst; } else { spriteInst = inst.dependencies[m_Sprite.Get("m_FileID").GetValue().AsInt() - 1]; } int spriteFileId = m_Sprite.Get("m_FileID").GetValue().AsInt(); long spritePathId = m_Sprite.Get("m_PathID").GetValue().AsInt64(); if (assetMap.ContainsKey(new AssetID(Path.GetFileName(spriteInst.path), spritePathId)) || (spriteFileId == 0 && spritePathId == 0)) { i++; continue; } AssetTypeValueField spriteBaseField = GetBaseField(am, spriteInst.file, spriteInfo); AssetTypeValueField m_RD = spriteBaseField.Get("m_RD"); AssetTypeValueField texture = m_RD.Get("texture"); AssetTypeValueField alphaTexture = m_RD.Get("alphaTexture"); AssetsFileInstance textureInst, alphaTextureInst; if (texture.Get("m_FileID").GetValue().AsInt() == 0) { textureInst = spriteInst; } else { textureInst = spriteInst.dependencies[texture.Get("m_FileID").GetValue().AsInt() - 1]; } if (alphaTexture.Get("m_FileID").GetValue().AsInt() == 0) { alphaTextureInst = spriteInst; } else { alphaTextureInst = spriteInst.dependencies[alphaTexture.Get("m_FileID").GetValue().AsInt() - 1]; } AssetTypeInstance textureAti = am.GetExtAsset(spriteInst, texture, false).instance; AssetTypeInstance alphaTextureAti = am.GetExtAsset(spriteInst, alphaTexture, false).instance; ulong textureId = 0, alphaTextureId = 0; if (textureAti != null) { AssetID id = new AssetID(Path.GetFileName(textureInst.path), texture.Get("m_PathID").GetValue().AsInt64()); if (!assetMap.ContainsKey(id)) { textureId = pathId; assetMap.Add(id, pathId); assets.Add(TextureConverter.ConvertTexture(textureAti.GetBaseField(), pathId++, folderName)); } else { textureId = assetMap[id]; } } if (alphaTextureAti != null) { AssetID id = new AssetID(Path.GetFileName(alphaTextureInst.path), alphaTexture.Get("m_PathID").GetValue().AsInt64()); if (!assetMap.ContainsKey(id)) { alphaTextureId = pathId; assetMap.Add(id, pathId); assets.Add(TextureConverter.ConvertTexture(alphaTextureAti.GetBaseField(), pathId++, folderName)); } else { alphaTextureId = assetMap[id]; } } AssetTypeValueField m_Materials = baseField.Get("m_Materials").Get("Array"); if (m_Materials.GetValue().AsArray().size > 0) { AssetTypeValueField material = baseField.Get("m_Materials").Get("Array")[0]; AssetsFileInstance materialInst; int materialFileId = material.Get("m_FileID").GetValue().AsInt(); if (materialFileId == 0) { materialInst = inst; } else { materialInst = inst.dependencies[materialFileId - 1]; } AssetID materialId = new AssetID(Path.GetFileName(materialInst.path), material.Get("m_PathID").GetValue().AsInt64()); if (!assetMap.ContainsKey(materialId)) { AssetTypeValueField materialBaseField = am.GetExtAsset(inst, material).instance.GetBaseField(); AssetTypeValueField shader = materialBaseField.Get("m_Shader"); ulong shaderPathId = RecurseShaderDependencies(am, materialInst, pathId, shader, assets, assetMap, out pathId); assetMap.Add(materialId, pathId); assets.Add(MaterialConverter.ConvertMaterial(materialBaseField, pathId++, shaderPathId)); } } assetMap.Add(new AssetID(Path.GetFileName(spriteInst.path), spritePathId), pathId); spriteIds.Add(pathId); assets.Add(SpriteConverter.ConvertSprite(spriteBaseField, pathId++, textureId, alphaTextureId)); i++; } assetMap.Add(new AssetID(0), pathId); assets.Add(HeaderInformation.CreateHeaderInformation(assetMap, pathId++)); assets.Insert(0, BundleMeta.CreateBundleInformation(assetMap, spriteIds, 1)); //assets.Add(BundleMeta.CreateBundleInformation(assetMap, 1)); //todo: pull from original assets file, cldb is not always update to date List <Type_0D> types = new List <Type_0D> { FixTypeTree(C2T5.Cldb2TypeTree(am.classFile, 0x8E)), //AssetBundle FixTypeTree(C2T5.Cldb2TypeTree(am.classFile, 0x1C)), //Texture2D FixTypeTree(C2T5.Cldb2TypeTree(am.classFile, 0x31)), //TextAsset FixTypeTree(C2T5.Cldb2TypeTree(am.classFile, 0xD4)), //SpriteRenderer FixTypeTree(C2T5.Cldb2TypeTree(am.classFile, 0xD5)), //Sprite //FixTypeTree(C2T5.Cldb2TypeTree(am.classFile, 0x31)), //TextAsset FixTypeTree(C2T5.Cldb2TypeTree(am.classFile, 0x15)), //Material FixTypeTree(C2T5.Cldb2TypeTree(am.classFile, 0x30)) //Shader }; const string ver = "2017.4.10f1"; //const string ver = "2018.2.1f1"; byte[] blankData = BundleCreator.CreateBlankAssets(ver, types); AssetsFile blankFile = new AssetsFile(new AssetsFileReader(new MemoryStream(blankData))); byte[] data = null; using (MemoryStream ms = new MemoryStream()) using (AssetsFileWriter writer = new AssetsFileWriter(ms)) { blankFile.WriteFix(writer, 0, assets.ToArray(), 0); data = ms.ToArray(); } EditorUtility.DisplayProgressBar("HKEdit", "Creating bundle", 1); using (MemoryStream ms = new MemoryStream()) using (AssetsFileWriter writer = new AssetsFileWriter(ms)) { BundleCreator.CreateBlankBundle(ver, data.Length).Write(writer, data); return(ms.ToArray()); } }
public static byte[] CreateBundleFromLevel(AssetsManager am, /*byte[] data,*/ AssetsFileInstance inst, string sceneName, DiffFile diffFile, string bunPath) { AssetsFile file = inst.file; AssetsFileTable table = inst.table; string folderName = Path.GetDirectoryName(inst.path); //string sceneName = Path.GetFileNameWithoutExtension(inst.path) + "_mod"; List <AssetsReplacer> assetsSA = new List <AssetsReplacer>(); List <AssetFileInfoEx> infos = table.pAssetFileInfo.ToList(); //List<int> typeIds = new List<int>(); //foreach (AssetFileInfoEx info in infos) //{ // int typeId = (int)info.curFileType; // if (!typeIds.Contains(typeId) && typeId != 0x72) // typeIds.Add(typeId); //} assetsSA.Add(PreloadData.CreatePreloadData(1)); assetsSA.Add(BundleMeta.CreateBundleInformation(sceneName, 2)); //todo: pull from original assets file, cldb is not always update to date List <Type_0D> types = new List <Type_0D>(); //foreach (int typeId in typeIds) //{ // types.Add(C2T5.Cldb2TypeTree(am.classFile, typeId)); //} List <Type_0D> typesSA = new List <Type_0D> { C2T5.Cldb2TypeTree(am.classFile, 0x96), //PreloadData C2T5.Cldb2TypeTree(am.classFile, 0x8E) //AssetBundle }; const string ver = "2017.4.10f1"; List <AssetsReplacer> replacers = new List <AssetsReplacer>(); //UnityEngine.Debug.Log("HKWE DM " + diffFile.magic); //UnityEngine.Debug.Log("HKWE GC " + diffFile.changes.Count + diffFile.adds.Count + diffFile.removes.Count); //AssetsReplacerFromMemory mem = MoveTest.RunMoveTest(table.getAssetInfo(2642), am.GetATI(file, table.getAssetInfo(2642)).GetBaseField(), 2642) as AssetsReplacerFromMemory; foreach (GameObjectChange goChange in diffFile.changes) { //UnityEngine.Debug.Log("HKWE GO " + goChange.pathId); foreach (ComponentChangeOrAdd compChange in goChange.changes) { AssetFileInfoEx goInfo = table.getAssetInfo((ulong)goChange.pathId); AssetTypeValueField goBaseField = am.GetATI(file, goInfo).GetBaseField(); AssetTypeValueField compPptr = goBaseField.Get("m_Component").Get("Array")[(uint)compChange.componentIndex].Get("component"); AssetsManager.AssetExternal compExt = am.GetExtAsset(inst, compPptr); AssetFileInfoEx compInfo = compExt.info; AssetTypeValueField compBaseField = compExt.instance.GetBaseField(); //UnityEngine.Debug.Log("HKWE LR " + compInfo.index); AssetsReplacer imAlreadyReplacer = ComponentDiffReplacer.DiffComponent(compInfo, compBaseField, am.classFile, compChange, compInfo.index); replacers.Add(imAlreadyReplacer); } } AssetsManager amBun = new AssetsManager(); //we create a new manager because the two filenames will probably conflict amBun.classFile = am.classFile; //we can just reuse the classfile which is kinda hacky AssetsFileInstance bunInst = amBun.LoadAssetsFile(new MemoryStream(GetBundleData(bunPath, 0)), "HKWEDiffs", false); //placeholder path since we have no deps //rearrange the pathids immediately after the //last one from the level to keep unity happy ulong levelLargestPathID = 0; foreach (AssetFileInfoEx inf in table.pAssetFileInfo) { if (inf.index > levelLargestPathID) { levelLargestPathID = inf.index; } } ReferenceCrawler.ReorderIds(amBun, bunInst, levelLargestPathID + 1); byte[] bunSAInst = GetBundleData(bunPath, 1); //HashSet<ulong> addedDeps = new HashSet<ulong>(); foreach (AssetFileInfoEx inf in bunInst.table.pAssetFileInfo) { replacers.Add(MakeReplacer(inf.index, am, bunInst, inst, inf, bunSAInst, types)); } //foreach (GameObjectInfo inf in diffFile.infos) //{ // Debug.Log("7"); // ulong bunPathId = GetBundlePathId(amBun, bunInst, inf); // // AssetFileInfoEx objInf = bunInst.table.getAssetInfo(bunPathId); // replacers.Add(MakeReplacer(bunPathId, am, bunInst, inst, objInf, bunSAInst, types)); // // List<ulong> deps = ReferenceCrawler.CrawlPPtrs(amBun, bunInst, bunPathId); // foreach (ulong dep in deps) // { // if (!addedDeps.Contains(dep)) // { // addedDeps.Add(dep); // AssetFileInfoEx depInf = bunInst.table.getAssetInfo(dep); // //if (depInf.curFileType == 0x01 || depInf.curFileType == 0x04 || depInf.curFileType == 0xD4 || depInf.curFileType == 0x15 || depInf.curFileType == 0xD5) // //{ // // continue; // //} // replacers.Add(MakeReplacer(dep, am, bunInst, inst, depInf, bunSAInst, types)); // } // } // ////its possible to get a collision but very unlikely since unity already randomizes ids which are 8 bytes long // ////there's nothing here to test if a collision would be created so just hope that you don't win the lottery // //ulong bunPathId = GetBundlePathId(amBun, bunInst, inf); // ////AssetFileInfoEx afInf = bunInst.table.getAssetInfo(bunPathId); // ////replacers.Add(MakeReplacer(bunPathId, afInf, bunInst.stream)); // //List<ulong> deps = ReferenceCrawler.CrawlPPtrs(am, bunInst, bunPathId); // ////if (info.curFileType == 0x01 || info.curFileType == 0x04 || info.curFileType == 0xD4) // ////{ // //// continue; // ////} // //foreach (ulong dep in deps) // //{ // // AssetFileInfoEx depInf = bunInst.table.getAssetInfo(dep); // // //MakeReplacer(dep, am, bunInst, inst, depInf, bunSAInst, types); // // AssetsReplacerFromMemory ar = MakeReplacer(dep, am, bunInst, inst, depInf, bunSAInst, types); // // //todo- I guess this was just for testing purposes to block out everything, remove this at some point // // if (depInf.curFileType == 0x01 || depInf.curFileType == 0x04 || depInf.curFileType == 0xD4 || depInf.curFileType == 0x15 || depInf.curFileType == 0xD5) //depInf.curFileType == 0x1C // // { // // continue; // // } // // replacers.Add(ar); // //} //} byte[] data = null; using (MemoryStream ms = new MemoryStream()) using (AssetsFileWriter writer = new AssetsFileWriter(ms)) { //file.typeTree.hasTypeTree = true; //so we don't have to calculate hashes //foreach (Type_0D type in file.typeTree.pTypes_Unity5) //{ // if (!types.Any(t => t.classId == type.classId)) // { // types.Insert(0, C2T5.Cldb2TypeTree(am.classFile, type.classId)); // } //} file.typeTree.pTypes_Unity5 = file.typeTree.pTypes_Unity5.Concat(types.ToArray()).ToArray(); //file.typeTree.pTypes_Unity5 = types.ToArray(); file.typeTree.fieldCount = (uint)file.typeTree.pTypes_Unity5.Length; //file.typeTree.fieldCount = (uint)types.Count; file.Write(writer, 0, replacers.ToArray(), 0); data = ms.ToArray(); } //File.WriteAllBytes("_bundlefinal1.unity3d", data); byte[] blankDataSA = BundleCreator.CreateBlankAssets(ver, typesSA); AssetsFile blankFileSA = new AssetsFile(new AssetsFileReader(new MemoryStream(blankDataSA))); byte[] dataSA = null; using (MemoryStream ms = new MemoryStream()) using (AssetsFileWriter writer = new AssetsFileWriter(ms)) { blankFileSA.Write(writer, 0, assetsSA.ToArray(), 0); dataSA = ms.ToArray(); } using (MemoryStream ms = new MemoryStream()) using (AssetsFileWriter writer = new AssetsFileWriter(ms)) { AssetsBundleFile bundle = BundleCreator.CreateBlankBundle(ver, data.Length, dataSA.Length, sceneName); bundle.Write(writer); writer.Write(dataSA); writer.Write(data); return(ms.ToArray()); } }
public static void GenerateDiffFile(AssetsManager am, AssetsFileInstance buildInst, AssetsFileInstance sceneInst, HKWEMeta meta) { EditorUtility.DisplayProgressBar("HKEdit", "Reading dependencies...", 0.5f); am.UpdateDependencies(); ClassDatabaseFile cldb = am.classFile; DiffData result = new DiffData() { goChanges = new List <GameObjectChange>(), goAdditions = new List <GameObjectAddition>() }; Dictionary <EditDifferData, long> differToSceneId = new Dictionary <EditDifferData, long>(); Dictionary <long, EditDifferData> buildIdToDiffer = new Dictionary <long, EditDifferData>(); List <EditDifferData> differData = new List <EditDifferData>(); AssetsFileTable sceneTable = sceneInst.table; List <AssetFileInfoEx> sceneGos = sceneTable.GetAssetsOfType(0x01); for (int i = 0; i < sceneGos.Count; i++) { if (i % 100 == 0) { EditorUtility.DisplayProgressBar("HKEdit", "Finding diff IDs... (step 1/3)", (float)i / sceneGos.Count); } AssetFileInfoEx inf = sceneGos[i]; AssetTypeValueField baseField = am.GetATI(sceneInst.file, inf).GetBaseField(); AssetTypeValueField editDifferMono = GetEDMono(am, sceneInst, baseField); EditDifferData diff = new EditDifferData() { fileId = editDifferMono.Get("fileId").GetValue().AsInt(), pathId = editDifferMono.Get("pathId").GetValue().AsInt64(), origPathId = editDifferMono.Get("origPathId").GetValue().AsInt64(), newAsset = editDifferMono.Get("newAsset").GetValue().AsBool() }; buildIdToDiffer[diff.origPathId] = diff; differToSceneId[diff] = inf.index; differData.Add(diff); } ////////////////////////// AssetsFileTable origTable = buildInst.table; List <AssetFileInfoEx> origGos = origTable.GetAssetsOfType(0x01); List <long> origDeletIds = new List <long>(); //int nextBundleId = 1; //// == delete changes == // //for (int i = 0; i < origGos.Count; i++) //{ // if (i % 100 == 0) // EditorUtility.DisplayProgressBar("HKEdit", "Checking for deletes... (step 2/3)", (float)i / origGos.Count); // AssetFileInfoEx inf = sceneGos[i]; // if (!differData.Any(d => d.origPathId == inf.index)) // { // GameObjectChange change = new GameObjectChange // { // flags = GameObjectChangeFlags.Deleted // }; // result.goChanges.Add(change); // origDeletIds.Add(inf.index); // } //} // == add changes == // //to get this working in a built game, we need //built assets (ie pngs -> texture2d) the problem //is there's no easy way to direct unity to do that //without loading the scene and using unity's api //but we can pull out assets into a prefab and build //the prefab but there are problems with duplicate //dependencies being copied, so we pack them all //into one place so that doesn't happen //(for reference, in hkwe1, each gameobject got //its own prefab) //find dependencies ReferenceCrawlerBundle createdCrawler = new ReferenceCrawlerBundle(am); //assets created by the user in the ditor ReferenceCrawlerBundle existingCrawler = new ReferenceCrawlerBundle(am); //assets that already existed in the scene for (int i = 0; i < differData.Count; i++) { if (i % 100 == 0) { EditorUtility.DisplayProgressBar("HKEdit", "Checking for additions... (step 2/3)", (float)i / differData.Count); } EditDifferData dat = differData[i]; if (dat.newAsset) { long sceneId = differToSceneId[dat]; AssetFileInfoEx inf = sceneInst.table.GetAssetInfo(sceneId); createdCrawler.SetReferences(sceneInst, inf); GameObjectAddition addition = new GameObjectAddition { bundleId = createdCrawler.GetNextId(), //? sceneId = dat.pathId, dependencies = new List <GameObjectAdditionDependency>() }; //nextBundleId++; foreach (KeyValuePair <AssetID, AssetID> goRef in createdCrawler.references) { addition.dependencies.Add(new GameObjectAdditionDependency { sceneId = goRef.Key.pathId, bundleId = goRef.Value.pathId }); } result.goAdditions.Add(addition); } else { long newPathId = differToSceneId[dat]; AssetFileInfoEx inf = sceneInst.table.GetAssetInfo(newPathId); existingCrawler.SetReferences(sceneInst, inf); } } //load up all created assets into a prefab List <Type_0D> types = new List <Type_0D>(); List <string> typeNames = new List <string>(); foreach (AssetsReplacer rep in createdCrawler.sceneReplacers) { ClassDatabaseType clType = AssetHelper.FindAssetClassByID(cldb, (uint)rep.GetClassID()); string clName = clType.name.GetString(cldb); if (!typeNames.Contains(clName)) { Type_0D type0d = C2T5.Cldb2TypeTree(cldb, clName); type0d.classId = clType.classId; types.Add(type0d); typeNames.Add(clName); } } List <AssetsReplacer> replacers = new List <AssetsReplacer>(); replacers.Add(CreatePrefabAsset(2)); //better hope id 2 is a gameobject replacers.AddRange(createdCrawler.sceneReplacers); AssetsFile createdFile = new AssetsFile(new AssetsFileReader(new MemoryStream(BundleCreator.CreateBlankAssets(ver, types)))); byte[] data; using (MemoryStream ms = new MemoryStream()) using (AssetsFileWriter writer = new AssetsFileWriter(ms)) { createdFile.Write(writer, 0, replacers, 0); data = ms.ToArray(); } }
private void btnOK_Click(object sender, EventArgs e) { var cldb = Workspace.Am.classFile; var fileInst = Workspace.LoadedFiles[cboxFileID.SelectedIndex]; AssetTypeTemplateField templateField; var fileId = cboxFileID.SelectedIndex; if (!long.TryParse(boxPathID.Text, out var pathId)) { MsgBoxUtils.ShowErrorDialog("Path ID is invalid!"); return; } var type = boxTypeNameOrID.Text; int typeId; var createBlankAsset = chboxCreateBlankAssets.Checked; if (fileInst.file.typeTree.hasTypeTree) { if (!TryParseTypeTree(fileInst, ref type, createBlankAsset, out templateField, out typeId)) { if (!TryParseClassDatabase(ref type, createBlankAsset, out templateField, out typeId)) { MsgBoxUtils.ShowErrorDialog("Class type is invalid!"); return; } //has typetree but had to lookup to cldb //we need to add a new typetree entry because this is //probably not a type that existed in this bundle fileInst.file.typeTree.unity5Types.Add(C2T5.Cldb2TypeTree(cldb, typeId)); } } else { if (!TryParseClassDatabase(ref type, createBlankAsset, out templateField, out typeId)) { MsgBoxUtils.ShowErrorDialog("Class type is invalid!"); return; } } var monoSelected = cboxTypePreset.SelectedIndex == 1; ushort monoId; if (typeId != 0x72 || !monoSelected) { monoId = ushort.MaxValue; } else { if (!ushort.TryParse(boxMonoID.Text, out monoId)) { MsgBoxUtils.ShowErrorDialog("Mono ID is invalid!"); return; } } if (!int.TryParse(boxCount.Text, out var count)) { MsgBoxUtils.ShowErrorDialog("The count of assets being created is invalid!"); return; } byte[] assetBytes; if (createBlankAsset) { var baseField = ValueBuilder.DefaultValueFieldFromTemplate(templateField); assetBytes = baseField.WriteToByteArray(); } else { assetBytes = Array.Empty <byte>(); } for (var i = 0; i < count; i++) { var item = new AssetItem { Type = type, TypeID = (uint)typeId, FileID = fileId, PathID = pathId + i, Size = assetBytes.Length, Modified = "*", MonoID = monoId }; Items.Add(item); Workspace.AddReplacer(AssetModifier.CreateAssetReplacer(item, assetBytes), new MemoryStream(assetBytes)); } DialogResult = DialogResult.OK; }
public void Write(AssetsFileWriter writer, long filePos, List <AssetsReplacer> replacers, uint fileID, ClassDatabaseFile typeMeta = null) { if (filePos == -1) { filePos = writer.Position; } else { writer.Position = filePos; } header.Write(writer); for (int i = 0; i < replacers.Count; i++) { AssetsReplacer replacer = replacers[i]; int replacerClassId = replacer.GetClassID(); if (!typeTree.unity5Types.Any(t => t.classId == replacerClassId)) { 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 = "" }; if (typeMeta != null) { int cldbIndex = typeMeta.classes.FindIndex(c => c.classId == replacerClassId); if (cldbIndex != -1) { int cldbId = typeMeta.classes[cldbIndex].classId; type = C2T5.Cldb2TypeTree(typeMeta, cldbId); } } typeTree.unity5Types.Add(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.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.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[0]; if (replacer.GetReplacementType() == AssetsReplacementType.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 >= 0x14) { writer.Write(0); //secondaryTypeCount //writer.Write((byte)0); //unknownString length } uint metadataSize = (uint)(writer.Position - filePos - 0x13); //0x13 is header - "endianness byte"? (if that's what it even is) if (header.format >= 0x16) { //remove larger variation fields as well metadataSize -= 0x1c; } //-for padding only. if all initial data before assetData is more than 0x1000, this is skipped if (writer.Position < 0x1000) { while (writer.Position < 0x1000) { writer.Write((byte)0x00); } } else { if (writer.Position % 16 == 0) { writer.Position += 16; } else { writer.Align16(); } } long offs_firstFile = 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.AddOrModify) { replacer.Write(writer); if (i != assetInfos.Count - 1) { writer.Align8(); } } else if (replacer.GetReplacementType() == AssetsReplacementType.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 - filePos; reader.Position = header.firstFileOffset; writer.Position = filePos; header.metadataSize = metadataSize; header.fileSize = fileSizeMarker; header.Write(writer); writer.Position = fileSizeMarker + filePos; }
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); }