public static PropertyCollection getDefaultStructValue(string className, bool stripTransients) { bool isImmutable = UnrealObjectInfo.IsImmutable(className, MEGame.ME1); if (Structs.ContainsKey(className)) { ClassInfo info = Structs[className]; try { PropertyCollection structProps = new PropertyCollection(); ClassInfo tempInfo = info; while (tempInfo != null) { foreach ((string propName, PropertyInfo propInfo) in tempInfo.properties) { if (stripTransients && propInfo.Transient) { continue; } if (getDefaultProperty(propName, propInfo, stripTransients, isImmutable) is UProperty uProp) { structProps.Add(uProp); } } if (!Structs.TryGetValue(tempInfo.baseClass, out tempInfo)) { tempInfo = null; } } structProps.Add(new NoneProperty()); string filepath = Path.Combine(ME1Directory.BioGamePath, info.pccPath); if (File.Exists(info.pccPath)) { filepath = info.pccPath; //Used for dynamic lookup } else if (info.pccPath == UnrealObjectInfo.Me3ExplorerCustomNativeAdditionsName) { filepath = App.CustomResourceFilePath(MEGame.ME3); } if (!File.Exists(filepath)) { filepath = Path.Combine(ME1Directory.gamePath, info.pccPath); //for files from ME1 DLC } if (File.Exists(filepath)) { using (IMEPackage importPCC = MEPackageHandler.OpenME1Package(filepath)) { var exportToRead = importPCC.GetUExport(info.exportIndex); byte[] buff = exportToRead.Data.Skip(0x30).ToArray(); PropertyCollection defaults = PropertyCollection.ReadProps(exportToRead, new MemoryStream(buff), className); foreach (var prop in defaults) { structProps.TryReplaceProp(prop); } } } return(structProps); } catch { return(null); } } return(null); }
private Mod GenerateCompatibilityPackForFiles(List <string> dlcModList, List <string> filesToBePatched, SevenZipExtractor uiArchive) { dlcModList = dlcModList.Select(x => { var tpmi = ThirdPartyServices.GetThirdPartyModInfo(x, MEGame.ME3); if (tpmi == null) { return(x); } return(tpmi.modname); }).ToList(); string dlcs = string.Join("\n - ", dlcModList); // do not localize StarterKitOptions sko = new StarterKitOptions { ModName = M3L.GetString(M3L.string_guiCompatibilityPack), ModDescription = M3L.GetString(M3L.string_interp_compatPackModDescription, dlcs, DateTime.Now.ToString()), ModDeveloper = App.AppVersionHR, ModDLCFolderName = UI_MOD_NAME, ModGame = MEGame.ME3, ModInternalName = @"UI Mod Compatibility Pack", ModInternalTLKID = 1420400890, ModMountFlag = EMountFileFlag.ME3_SPOnly_NoSaveFileDependency, ModMountPriority = 31050, ModURL = null, ModModuleNumber = 0 }; Mod generatedMod = null; void modGenerated(Mod mod) { generatedMod = mod; lock (modGeneratedSignaler) { Monitor.Pulse(modGeneratedSignaler); } } void skUicallback(string text) { ActionSubstring = text; } StarterKitGeneratorWindow.CreateStarterKitMod(sko, skUicallback, modGenerated); lock (modGeneratedSignaler) { Monitor.Wait(modGeneratedSignaler); } //Mod has been generated. string outputPath = Path.Combine(generatedMod.ModPath, @"DLC_MOD_" + UI_MOD_NAME, @"CookedPCConsole"); ActionString = M3L.GetString(M3L.string_preparingUiLibrary); ActionSubstring = M3L.GetString(M3L.string_decompressingData); int done = 0; CaseInsensitiveDictionary <byte[]> uiLibraryData = new CaseInsensitiveDictionary <byte[]>(); var filesToDecompress = uiArchive.ArchiveFileData.Where(x => x.FileName.EndsWith(@".swf")).ToList(); foreach (var f in filesToDecompress) { MemoryStream decompressedStream = new MemoryStream(); uiArchive.ExtractFile(f.Index, decompressedStream); string fname = f.FileName.Substring(f.FileName.IndexOf('\\') + 1); fname = fname.Substring(0, fname.IndexOf(@".swf", StringComparison.InvariantCultureIgnoreCase)); uiLibraryData[fname] = decompressedStream.ToArray(); done++; Percent = getPercent(done, filesToDecompress.Count); } ActionString = M3L.GetString(M3L.string_patchingFiles); Percent = 0; done = 0; string singlesuffix = M3L.GetString(M3L.string_singularFile); string pluralsuffix = M3L.GetString(M3L.string_pluralFiles); // Logging Log.Information(@"The following files will be promoted in the compatibility pack:"); foreach (var file in filesToBePatched) { Log.Information(@" - " + file); } foreach (var file in filesToBePatched) { Log.Information(@"Patching file: " + file); ActionSubstring = Path.GetFileName(file); var package = MEPackageHandler.OpenMEPackage(file); if (package == null) { Log.Error(@"pPackage object is null!!!"); } var guiExports = package.Exports.Where(x => !x.IsDefaultObject && x.ClassName == @"GFxMovieInfo").ToList(); if (guiExports.Count > 0) { //potential item needing replacement //Check GUI library to see if we have anything. foreach (var export in guiExports) { if (uiLibraryData.TryGetValue(export.GetFullPath, out var newData)) { Log.Information(@" >> Patching export " + export.GetFullPath); //Patching this export. var exportProperties = export.GetProperties(); var rawData = exportProperties.GetProp <ArrayProperty <ByteProperty> >(@"RawData"); if (rawData == null) { Log.Error(@"Rawdata is null!!"); } rawData.Clear(); rawData.AddRange(newData.Select(x => new ByteProperty(x))); //This will be terribly slow. Need to port over new ME3Exp binary data handler export.WriteProperties(exportProperties); } else { Debug.WriteLine(@"Not patching gui export, file not in library: " + export.GetFullPath); } } var outpath = Path.Combine(outputPath, Path.GetFileName(package.FilePath)); if (package.IsModified) { Log.Information(@"Saving patched package to " + outpath); package.save(outpath, true); done++; ActionSubstring = M3L.GetString(M3L.string_interp_patchedXY, done.ToString(), done == 1 ? singlesuffix : pluralsuffix); } else { done++; Log.Information(@"File was patched but data did not change! " + outpath); } Percent = getPercent(done, filesToBePatched.Count); } else { throw new Exception($@"Error: {Path.GetFileName(file)} doesn't contain GUI exports! This shouldn't have been possible."); } } // Write builtagainst for metacmm. IniData iniData = new FileIniDataParser().ReadFile(generatedMod.ModDescPath); var builtAgainst = VanillaDatabaseService.GetInstalledDLCMods(target); builtAgainst.Remove(@"DLC_MOD_" + UI_MOD_NAME); iniData[@"ControllerCompat"][@"builtagainst"] = string.Join(';', builtAgainst); File.WriteAllText(generatedMod.ModDescPath, iniData.ToString()); return(generatedMod); }
/// <summary> /// Copy ME2 Static art and collision into an ME3 file. /// By Kinkojiro /// </summary> /// <param name="Game">Target Game</param> /// <param name="BioPSource">Source BioP</param> /// <param name="tgtOutputfolder">OutputFolder</param> /// <param name="BioArtsToCopy">List of level source file locations</param> /// <param name="ActorsToMove">Dicitionary: key Actors, value filename, entry uid</param> /// <param name="AssetsToMove">Dictionary key: AssetInstancedPath, value filename, isimport, entry uid</param> /// <param name="fromreload">is reloaded json</param> public static async Task <List <string> > ConvertLevelToGame(MEGame Game, IMEPackage BioPSource, string tgtOutputfolder, string tgttfc, Action <string> callbackAction, LevelConversionData conversionData = null, bool fromreload = false, bool createtestlevel = false) { //VARIABLES / VALIDATION var actorclassesToMove = new List <string>() { "BlockingVolume", "SpotLight", "SpotLightToggleable", "PointLight", "PointLightToggleable", "SkyLight", "HeightFog", "LenseFlareSource", "StaticMeshActor", "BioTriggerStream", "BioBlockingVolume" }; var actorclassesToSubstitute = new Dictionary <string, string>() { { "BioBlockingVolume", "Engine.BlockingVolume" } }; var archetypesToSubstitute = new Dictionary <string, string>() { { "Default__BioBlockingVolume", "Default__BlockingVolume" } }; var fails = new List <string>(); string busytext = null; if ((BioPSource.Game == MEGame.ME2 && ME2Directory.DefaultGamePath == null) || (BioPSource.Game == MEGame.ME1 && ME1Directory.DefaultGamePath == null) || (BioPSource.Game == MEGame.ME3 && ME3Directory.DefaultGamePath == null) || BioPSource.Game == MEGame.UDK) { fails.Add("Source Game Directory not found"); return(fails); } //Get filelist from BioP, Save a copy in outputdirectory, Collate Actors and asset information if (!fromreload) { busytext = "Collating level files..."; callbackAction?.Invoke(busytext); conversionData = new LevelConversionData(Game, BioPSource.Game, null, null, null, new ConcurrentDictionary <string, string>(), new ConcurrentDictionary <string, (string, int)>(), new ConcurrentDictionary <string, (string, int, List <string>)>()); var supportedExtensions = new List <string> { ".pcc", ".u", ".upk", ".sfm" }; if (Path.GetFileName(BioPSource.FilePath).ToLowerInvariant().StartsWith("biop_") && BioPSource.Exports.FirstOrDefault(x => x.ClassName == "BioWorldInfo") is ExportEntry BioWorld) { string biopname = Path.GetFileNameWithoutExtension(BioPSource.FilePath); conversionData.GameLevelName = biopname.Substring(5, biopname.Length - 5); var lsks = BioWorld.GetProperty <ArrayProperty <ObjectProperty> >("StreamingLevels").ToList(); if (lsks.IsEmpty()) { fails.Add("No files found in level."); return(fails); } foreach (var l in lsks) { var lskexp = BioPSource.GetUExport(l.Value); var filename = lskexp.GetProperty <NameProperty>("PackageName"); if ((filename?.Value.ToString().ToLowerInvariant().StartsWith("bioa") ?? false) || (filename?.Value.ToString().ToLowerInvariant().StartsWith("biod") ?? false)) { var filePath = Directory.GetFiles(ME2Directory.DefaultGamePath, $"{filename.Value.Instanced}.pcc", SearchOption.AllDirectories).FirstOrDefault(); conversionData.FilesToCopy.TryAdd(filename.Value.Instanced, filePath); } } conversionData.BioPSource = $"{biopname}_{BioPSource.Game}"; BioPSource.Save(Path.Combine(tgtOutputfolder, $"{conversionData.BioPSource}.pcc")); BioPSource.Dispose(); } else { fails.Add("Requires an BioP to work with."); return(fails); } busytext = "Collating actors and assets..."; callbackAction?.Invoke(busytext); Parallel.ForEach(conversionData.FilesToCopy, (pccref) => { using IMEPackage pcc = MEPackageHandler.OpenMEPackage(pccref.Value); var sourcelevel = pcc.Exports.FirstOrDefault(l => l.ClassName == "Level"); if (ObjectBinary.From(sourcelevel) is Level levelbin) { foreach (var act in levelbin.Actors) { if (act < 1) { continue; } var actor = pcc.GetUExport(act); if (actorclassesToMove.Contains(actor.ClassName)) { conversionData.ActorsToMove.TryAdd($"{pccref.Key}.{actor.InstancedFullPath}", (pccref.Key, act)); HashSet <int> actorrefs = pcc.GetReferencedEntries(true, true, actor); foreach (var r in actorrefs) { var objref = pcc.GetEntry(r); if (objref != null) { if (objref.InstancedFullPath.Contains("PersistentLevel")) //remove components of actors { continue; } string instancedPath = objref.InstancedFullPath; if (objref.idxLink == 0) { instancedPath = $"{pccref.Key}.{instancedPath}"; } var added = conversionData.AssetsToMove.TryAdd(instancedPath, (pccref.Key, r, new List <string>() { pccref.Key })); if (!added) { conversionData.AssetsToMove[instancedPath].Item3.FindOrAdd(pccref.Key); if (r > 0 && conversionData.AssetsToMove[instancedPath].Item2 < 0) //Replace imports with exports if possible { var currentlist = conversionData.AssetsToMove[instancedPath].Item3; conversionData.AssetsToMove[instancedPath] = (pccref.Key, r, currentlist); } }
public static string GenerateUDKFileForLevel(string udkPath, IMEPackage pcc) { #region AssetPackage string meshPackageName = $"{Path.GetFileNameWithoutExtension(pcc.FilePath)}Meshes"; string meshFile = Path.Combine(udkPath, @"UDKGame\Content\Shared\", $"{meshPackageName}.upk"); MEPackageHandler.CreateAndSavePackage(meshFile, MEGame.UDK); using IMEPackage meshPackage = MEPackageHandler.OpenUDKPackage(meshFile); meshPackage.getEntryOrAddImport("Core.Package"); IEntry defMat = meshPackage.getEntryOrAddImport("EngineMaterials.DefaultMaterial", "Material", "Engine"); var allMats = new HashSet <int>(); var relinkMap = new Dictionary <IEntry, IEntry>(); #region StaticMeshes List <ExportEntry> staticMeshes = pcc.Exports.Where(exp => exp.ClassName == "StaticMesh").ToList(); foreach (ExportEntry mesh in staticMeshes) { var mats = new Queue <int>(); StaticMesh stm = ObjectBinary.From <StaticMesh>(mesh); foreach (StaticMeshRenderData lodModel in stm.LODModels) { foreach (StaticMeshElement meshElement in lodModel.Elements) { mats.Enqueue(meshElement.Material); allMats.Add(meshElement.Material); meshElement.Material = 0; } } if (pcc.GetEntry(stm.BodySetup) is ExportEntry rbBodySetup) { rbBodySetup.RemoveProperty("PhysMaterial"); } mesh.WriteBinary(stm); IEntry newParent = EntryImporter.GetOrAddCrossImportOrPackage(mesh.ParentFullPath, pcc, meshPackage); EntryImporter.ImportAndRelinkEntries(EntryImporter.PortingOption.CloneTreeAsChild, mesh, meshPackage, newParent, false, out IEntry ent, relinkMap); ExportEntry portedMesh = (ExportEntry)ent; stm = ObjectBinary.From <StaticMesh>(portedMesh); foreach (StaticMeshRenderData lodModel in stm.LODModels) { foreach (StaticMeshElement meshElement in lodModel.Elements) { meshElement.Material = mats.Dequeue(); } } portedMesh.WriteBinary(stm); } #endregion #region Materials using (IMEPackage udkResources = MEPackageHandler.OpenMEPackageFromStream(Utilities.GetCustomAppResourceStream(MEGame.UDK))) { ExportEntry normDiffMat = udkResources.Exports.First(exp => exp.ObjectName == "NormDiffMat"); foreach (int matUIndex in allMats) { if (pcc.GetEntry(matUIndex) is ExportEntry matExp) { List <IEntry> textures = new MaterialInstanceConstant(matExp).Textures; ExportEntry diff = null; ExportEntry norm = null; foreach (IEntry texEntry in textures) { if (texEntry is ExportEntry texport) { if (texport.ObjectName.Name.ToLower().Contains("diff")) { diff = texport; } else if (texport.ObjectName.Name.ToLower().Contains("norm")) { norm = texport; } } } if (diff == null) { relinkMap[matExp] = defMat; continue; } else { EntryImporter.ImportAndRelinkEntries(EntryImporter.PortingOption.AddSingularAsChild, diff, meshPackage, null, false, out IEntry ent); diff = (ExportEntry)ent; diff.RemoveProperty("TextureFileCacheName"); diff.RemoveProperty("TFCFileGuid"); diff.RemoveProperty("LODGroup"); } if (norm != null) { EntryImporter.ImportAndRelinkEntries(EntryImporter.PortingOption.AddSingularAsChild, norm, meshPackage, null, false, out IEntry ent); norm = (ExportEntry)ent; norm.RemoveProperty("TextureFileCacheName"); norm.RemoveProperty("TFCFileGuid"); norm.RemoveProperty("LODGroup"); } EntryImporter.ImportAndRelinkEntries(EntryImporter.PortingOption.CloneTreeAsChild, normDiffMat, meshPackage, null, true, out IEntry matEnt); ExportEntry newMat = (ExportEntry)matEnt; newMat.ObjectName = matExp.ObjectName; Material matBin = ObjectBinary.From <Material>(newMat); matBin.SM3MaterialResource.UniformExpressionTextures = new UIndex[] { norm?.UIndex ?? 0, diff.UIndex }; newMat.WriteBinary(matBin); relinkMap[matExp] = newMat; if (newMat.GetProperty <ArrayProperty <ObjectProperty> >("Expressions") is {} expressionsProp&& expressionsProp.Count >= 2) { ExportEntry diffExpression = meshPackage.GetUExport(expressionsProp[0].Value); ExportEntry normExpression = meshPackage.GetUExport(expressionsProp[1].Value); diffExpression.WriteProperty(new ObjectProperty(diff.UIndex, "Texture")); normExpression.WriteProperty(new ObjectProperty(norm?.UIndex ?? 0, "Texture")); } } else if (pcc.GetEntry(matUIndex) is ImportEntry matImp) { relinkMap[matImp] = defMat; } } var relinkMapping = new OrderedMultiValueDictionary <IEntry, IEntry>(relinkMap); foreach (ExportEntry stmExport in staticMeshes) { if (relinkMap.TryGetValue(stmExport, out IEntry destEnt) && destEnt is ExportEntry destExp) { Relinker.Relink(stmExport, destExp, relinkMapping); } } } #endregion meshPackage.Save(); #endregion var staticMeshActors = new List <ExportEntry>(); var lightActors = new List <ExportEntry>(); string tempPackagePath = Path.Combine(App.ExecFolder, $"{Path.GetFileNameWithoutExtension(pcc.FilePath)}.udk"); File.Copy(Path.Combine(App.ExecFolder, "empty.udk"), tempPackagePath, true); using IMEPackage udkPackage = MEPackageHandler.OpenUDKPackage(tempPackagePath); { var topLevelMeshPackages = new List <IEntry>(); foreach (ExportEntry exportEntry in staticMeshes) { IEntry imp = udkPackage.getEntryOrAddImport($"{exportEntry.FullPath}", "StaticMesh", "Engine", exportEntry.ObjectName.Number); while (imp.Parent != null) { imp = imp.Parent; } if (!topLevelMeshPackages.Contains(imp)) { topLevelMeshPackages.Add(imp); } } ExportEntry levelExport = udkPackage.Exports.First(exp => exp.ClassName == "Level"); List <int> actorsInLevel = ObjectBinary.From <Level>(pcc.Exports.First(exp => exp.ClassName == "Level")).Actors.Select(u => u.value).ToList(); var componentToMatrixMap = new Dictionary <int, Matrix>(); foreach (int uIndex in actorsInLevel) { if (pcc.GetEntry(uIndex) is ExportEntry stcExp) { if (stcExp.ClassName == "StaticMeshCollectionActor") { StaticMeshCollectionActor stmc = ObjectBinary.From <StaticMeshCollectionActor>(stcExp); var components = stcExp.GetProperty <ArrayProperty <ObjectProperty> >("StaticMeshComponents"); for (int i = 0; i < components.Count; i++) { componentToMatrixMap[components[i].Value] = stmc.LocalToWorldTransforms[i]; } } else if (stcExp.ClassName == "StaticLightCollectionActor") { StaticLightCollectionActor stlc = ObjectBinary.From <StaticLightCollectionActor>(stcExp); var components = stcExp.GetProperty <ArrayProperty <ObjectProperty> >("LightComponents"); for (int i = 0; i < components.Count; i++) { componentToMatrixMap[components[i].Value] = stlc.LocalToWorldTransforms[i]; } } } } #region StaticMeshActors { var emptySMCBin = new StaticMeshComponent(); IEntry staticMeshActorClass = udkPackage.getEntryOrAddImport("Engine.StaticMeshActor"); udkPackage.getEntryOrAddImport("Engine.Default__StaticMeshActor", "StaticMeshActor", "Engine"); IEntry staticMeshComponentArchetype = udkPackage.getEntryOrAddImport("Engine.Default__StaticMeshActor.StaticMeshComponent0", "StaticMeshComponent", "Engine"); int smaIndex = 2; int smcIndex = 2; foreach (ExportEntry smc in pcc.Exports.Where(exp => exp.ClassName == "StaticMeshComponent")) { if (smc.Parent is ExportEntry parent && actorsInLevel.Contains(parent.UIndex) && parent.IsA("StaticMeshActorBase")) { StructProperty locationProp; StructProperty rotationProp; StructProperty scaleProp = null; smc.CondenseArchetypes(); if (!(smc.GetProperty <ObjectProperty>("StaticMesh") is { } meshProp) || !pcc.IsUExport(meshProp.Value)) { continue; } smc.WriteBinary(emptySMCBin); smc.RemoveProperty("bBioIsReceivingDecals"); smc.RemoveProperty("bBioForcePrecomputedShadows"); //smc.RemoveProperty("bUsePreComputedShadows"); smc.RemoveProperty("bAcceptsLights"); smc.RemoveProperty("IrrelevantLights"); smc.RemoveProperty("Materials"); //should make use of this? smc.ObjectName = new NameReference("StaticMeshComponent", smcIndex++); if (parent.ClassName == "StaticMeshCollectionActor") { if (!componentToMatrixMap.TryGetValue(smc.UIndex, out Matrix m)) { continue; } (Vector3 posVec, Vector3 scaleVec, Rotator rotator) = m.UnrealDecompose(); locationProp = CommonStructs.Vector3Prop(posVec, "Location"); rotationProp = CommonStructs.RotatorProp(rotator, "Rotation"); scaleProp = CommonStructs.Vector3Prop(scaleVec, "DrawScale3D"); //smc.WriteProperty(CommonStructs.Matrix(m, "CachedParentToWorld")); } else { locationProp = parent.GetProperty <StructProperty>("Location"); rotationProp = parent.GetProperty <StructProperty>("Rotation"); scaleProp = parent.GetProperty <StructProperty>("DrawScale3D"); if (parent.GetProperty <FloatProperty>("DrawScale")?.Value is float scale) { Vector3 scaleVec = Vector3.One; if (scaleProp != null) { scaleVec = CommonStructs.GetVector3(scaleProp); } scaleProp = CommonStructs.Vector3Prop(scaleVec * scale, "DrawScale3D"); } } ExportEntry sma = new ExportEntry(udkPackage, EntryImporter.CreateStack(MEGame.UDK, staticMeshActorClass.UIndex)) { ObjectName = new NameReference("StaticMeshActor", smaIndex++), Class = staticMeshActorClass, Parent = levelExport }; sma.ObjectFlags |= UnrealFlags.EObjectFlags.HasStack; udkPackage.AddExport(sma); EntryImporter.ImportAndRelinkEntries(EntryImporter.PortingOption.AddSingularAsChild, smc, udkPackage, sma, true, out IEntry result); var props = new PropertyCollection { new ObjectProperty(result.UIndex, "StaticMeshComponent"), new NameProperty(new NameReference(Path.GetFileNameWithoutExtension(smc.FileRef.FilePath), smc.UIndex), "Tag"), new ObjectProperty(result.UIndex, "CollisionComponent") }; if (locationProp != null) { props.Add(locationProp); } if (rotationProp != null) { props.Add(rotationProp); } if (scaleProp != null) { props.Add(scaleProp); } sma.WriteProperties(props); staticMeshActors.Add(sma); } } IEntry topMeshPackageImport = udkPackage.getEntryOrAddImport(meshPackageName, "Package"); foreach (IEntry mp in topLevelMeshPackages) { mp.Parent = topMeshPackageImport; } } #endregion #region LightActors { IEntry pointLightClass = udkPackage.getEntryOrAddImport("Engine.PointLight"); IEntry spotLightClass = udkPackage.getEntryOrAddImport("Engine.SpotLight"); IEntry directionalLightClass = udkPackage.getEntryOrAddImport("Engine.DirectionalLight"); int plaIndex = 1; int plcIndex = 1; int slaIndex = 1; int slcIndex = 1; int dlaIndex = 1; int dlcIndex = 1; foreach (ExportEntry lightComponent in pcc.Exports) { if (!(lightComponent.Parent is ExportEntry parent && actorsInLevel.Contains(parent.UIndex))) { continue; } StructProperty locationProp; StructProperty rotationProp; StructProperty scaleProp; switch (lightComponent.ClassName) { case "PointLightComponent": lightComponent.CondenseArchetypes(); lightComponent.ObjectName = new NameReference("PointLightComponent", plcIndex++); if (parent.ClassName == "StaticLightCollectionActor") { if (!componentToMatrixMap.TryGetValue(lightComponent.UIndex, out Matrix m)) { continue; } (Vector3 posVec, Vector3 scaleVec, Rotator rotator) = m.UnrealDecompose(); locationProp = CommonStructs.Vector3Prop(posVec, "Location"); rotationProp = CommonStructs.RotatorProp(rotator, "Rotation"); scaleProp = CommonStructs.Vector3Prop(scaleVec, "DrawScale3D"); } else { locationProp = parent.GetProperty <StructProperty>("Location"); rotationProp = parent.GetProperty <StructProperty>("Rotation"); scaleProp = parent.GetProperty <StructProperty>("DrawScale3D"); if (parent.GetProperty <FloatProperty>("DrawScale")?.Value is float scale) { Vector3 scaleVec = Vector3.One; if (scaleProp != null) { scaleVec = CommonStructs.GetVector3(scaleProp); } scaleProp = CommonStructs.Vector3Prop(scaleVec * scale, "DrawScale3D"); } } ExportEntry pla = new ExportEntry(udkPackage, EntryImporter.CreateStack(MEGame.UDK, pointLightClass.UIndex)) { ObjectName = new NameReference("PointLight", plaIndex++), Class = pointLightClass, Parent = levelExport }; pla.ObjectFlags |= UnrealFlags.EObjectFlags.HasStack; udkPackage.AddExport(pla); EntryImporter.ImportAndRelinkEntries(EntryImporter.PortingOption.AddSingularAsChild, lightComponent, udkPackage, pla, true, out IEntry portedPLC); var plsProps = new PropertyCollection { new ObjectProperty(portedPLC.UIndex, "LightComponent"), new NameProperty("PointLight", "Tag"), }; if (locationProp != null) { plsProps.Add(locationProp); } if (rotationProp != null) { plsProps.Add(rotationProp); } if (scaleProp != null) { plsProps.Add(scaleProp); } pla.WriteProperties(plsProps); lightActors.Add(pla); break; case "SpotLightComponent": lightComponent.CondenseArchetypes(); lightComponent.ObjectName = new NameReference("SpotLightComponent", slcIndex++); if (parent.ClassName == "StaticLightCollectionActor") { if (!componentToMatrixMap.TryGetValue(lightComponent.UIndex, out Matrix m)) { continue; } (Vector3 posVec, Vector3 scaleVec, Rotator rotator) = m.UnrealDecompose(); locationProp = CommonStructs.Vector3Prop(posVec, "Location"); rotationProp = CommonStructs.RotatorProp(rotator, "Rotation"); scaleProp = CommonStructs.Vector3Prop(scaleVec, "DrawScale3D"); } else { locationProp = parent.GetProperty <StructProperty>("Location"); rotationProp = parent.GetProperty <StructProperty>("Rotation"); scaleProp = parent.GetProperty <StructProperty>("DrawScale3D"); if (parent.GetProperty <FloatProperty>("DrawScale")?.Value is float scale) { Vector3 scaleVec = Vector3.One; if (scaleProp != null) { scaleVec = CommonStructs.GetVector3(scaleProp); } scaleProp = CommonStructs.Vector3Prop(scaleVec * scale, "DrawScale3D"); } } ExportEntry sla = new ExportEntry(udkPackage, EntryImporter.CreateStack(MEGame.UDK, spotLightClass.UIndex)) { ObjectName = new NameReference("SpotLight", slaIndex++), Class = spotLightClass, Parent = levelExport }; sla.ObjectFlags |= UnrealFlags.EObjectFlags.HasStack; udkPackage.AddExport(sla); EntryImporter.ImportAndRelinkEntries(EntryImporter.PortingOption.AddSingularAsChild, lightComponent, udkPackage, sla, true, out IEntry portedSLC); var slaProps = new PropertyCollection { new ObjectProperty(portedSLC.UIndex, "LightComponent"), new NameProperty("SpotLight", "Tag"), }; if (locationProp != null) { slaProps.Add(locationProp); } if (rotationProp != null) { slaProps.Add(rotationProp); } if (scaleProp != null) { slaProps.Add(scaleProp); } sla.WriteProperties(slaProps); lightActors.Add(sla); break; case "DirectionalLightComponent": lightComponent.CondenseArchetypes(); lightComponent.ObjectName = new NameReference("DirectionalLightComponent", dlcIndex++); if (parent.ClassName == "StaticLightCollectionActor") { if (!componentToMatrixMap.TryGetValue(lightComponent.UIndex, out Matrix m)) { continue; } (Vector3 posVec, Vector3 scaleVec, Rotator rotator) = m.UnrealDecompose(); locationProp = CommonStructs.Vector3Prop(posVec, "Location"); rotationProp = CommonStructs.RotatorProp(rotator, "Rotation"); scaleProp = CommonStructs.Vector3Prop(scaleVec, "DrawScale3D"); } else { locationProp = parent.GetProperty <StructProperty>("Location"); rotationProp = parent.GetProperty <StructProperty>("Rotation"); scaleProp = parent.GetProperty <StructProperty>("DrawScale3D"); if (parent.GetProperty <FloatProperty>("DrawScale")?.Value is float scale) { Vector3 scaleVec = Vector3.One; if (scaleProp != null) { scaleVec = CommonStructs.GetVector3(scaleProp); } scaleProp = CommonStructs.Vector3Prop(scaleVec * scale, "DrawScale3D"); } } ExportEntry dla = new ExportEntry(udkPackage, EntryImporter.CreateStack(MEGame.UDK, directionalLightClass.UIndex)) { ObjectName = new NameReference("DirectionalLight", dlaIndex++), Class = directionalLightClass, Parent = levelExport }; dla.ObjectFlags |= UnrealFlags.EObjectFlags.HasStack; udkPackage.AddExport(dla); EntryImporter.ImportAndRelinkEntries(EntryImporter.PortingOption.AddSingularAsChild, lightComponent, udkPackage, dla, true, out IEntry portedDLC); var dlaProps = new PropertyCollection { new ObjectProperty(portedDLC.UIndex, "LightComponent"), new NameProperty("DirectionalLight", "Tag"), }; if (locationProp != null) { dlaProps.Add(locationProp); } if (rotationProp != null) { dlaProps.Add(rotationProp); } if (scaleProp != null) { dlaProps.Add(scaleProp); } dla.WriteProperties(dlaProps); lightActors.Add(dla); break; } } } UDKifyLights(udkPackage); #endregion Level level = ObjectBinary.From <Level>(levelExport); level.Actors = levelExport.GetChildren().Where(ent => ent.IsA("Actor")).Select(ent => new UIndex(ent.UIndex)).ToList(); levelExport.WriteBinary(level); udkPackage.Save(); } string resultFilePath = Path.Combine(udkPath, @"UDKGame\Content\Maps\", $"{Path.GetFileNameWithoutExtension(pcc.FilePath)}.udk"); using (IMEPackage udkPackage2 = MEPackageHandler.OpenUDKPackage(Path.Combine(App.ExecFolder, "empty.udk"))) { ExportEntry levelExport = udkPackage2.Exports.First(exp => exp.ClassName == "Level"); Level levelBin = ObjectBinary.From <Level>(levelExport); foreach (ExportEntry actor in staticMeshActors) { EntryImporter.ImportAndRelinkEntries(EntryImporter.PortingOption.CloneTreeAsChild, actor, udkPackage2, levelExport, true, out IEntry result); levelBin.Actors.Add(result.UIndex); } foreach (ExportEntry actor in lightActors) { EntryImporter.ImportAndRelinkEntries(EntryImporter.PortingOption.CloneTreeAsChild, actor, udkPackage2, levelExport, true, out IEntry result); levelBin.Actors.Add(result.UIndex); } levelExport.WriteBinary(levelBin); udkPackage2.Save(resultFilePath); } File.Delete(tempPackagePath); return(resultFilePath); }
private void scanToolStripMenuItem_Click(object sender, EventArgs e) { if (string.IsNullOrEmpty(ME3Directory.cookedPath)) { MessageBox.Show("This functionality requires ME3 to be installed. Set its path at:\n Options > Set Custom Path > Mass Effect 3"); return; } DebugOutput.StartDebugger("Meshplorer2"); int count = 0; timer1.Enabled = false; Entries = new List <EntryStruct>(); bool ScanDLC = false; if (MessageBox.Show("Scan DLCs too?", "Meshplorer 2", MessageBoxButtons.YesNo) == DialogResult.Yes) { ScanDLC = true; } if (ScanDLC) { #region DLC Stuff string dirDLC = ME3Directory.DLCPath; if (!Directory.Exists(dirDLC)) { DebugOutput.PrintLn("No DLC Folder found!"); } else { string[] subdirs = Directory.GetDirectories(dirDLC); string loc = Path.GetDirectoryName(Application.ExecutablePath) + "\\exec\\"; Directory.CreateDirectory(loc + "temp"); foreach (string DLCpath in subdirs) { if (!DLCpath.StartsWith("__") && !DLCpath.StartsWith("DLC_UPD")) { string path = DLCpath + "\\CookedPCConsole\\Default.sfar"; DLCBase dlcbase; try { dlcbase = new DLCBase(path); count = 0; pbar1.Maximum = dlcbase.fileList.Count; foreach (sfarFile file in dlcbase.fileList) { try { string filename = Path.GetFileName(file.fileName); if (Path.GetExtension(filename).ToLower().EndsWith(".pcc")) { using (Stream input = File.OpenRead(path), output = File.Create(loc + "temp\\" + filename)) { AmaroK86.MassEffect3.DLCUnpack.DecompressEntry(file, input, output, dlcbase.CompressionScheme); } FileInfo f = new FileInfo(loc + "temp\\" + filename); DebugOutput.PrintLn("checking DLC: " + Path.GetFileName(DLCpath) + " File: " + filename + " Size: " + f.Length + " bytes", count % 3 == 0); using (ME3Package pcc = MEPackageHandler.OpenME3Package(loc + "temp\\" + filename)) { IReadOnlyList <IExportEntry> Exports = pcc.Exports; for (int i = 0; i < Exports.Count; i++) { if (Exports[i].ClassName == "SkeletalMesh" || Exports[i].ClassName == "StaticMesh") { EntryStruct ent = new EntryStruct(); ent.DLCName = Path.GetFileName(DLCpath); ent.Filename = filename; ent.Index = i; ent.isDLC = true; ent.ObjectPath = Exports[i].GetFullPath; ent.isSkeletal = Exports[i].ClassName == "SkeletalMesh"; Entries.Add(ent); } } } File.Delete(loc + "temp\\" + filename); } if (count % 3 == 0) { pbar1.Value = count; Application.DoEvents(); } count++; } catch (Exception ex) { DebugOutput.PrintLn("=====ERROR=====\n" + ex.ToString() + "\n=====ERROR====="); } } } catch (Exception ex) { DebugOutput.PrintLn("=====ERROR=====\n" + ex.ToString() + "\n=====ERROR====="); } } } Directory.Delete(loc + "temp", true); } #endregion } #region Basegame Stuff string dir = ME3Directory.cookedPath; string[] files = Directory.GetFiles(dir, "*.pcc"); pbar1.Maximum = files.Length - 1; foreach (string file in files) { DebugOutput.PrintLn("Scan file #" + count + " : " + file, count % 10 == 0); try { using (ME3Package pcc = MEPackageHandler.OpenME3Package(file)) { IReadOnlyList <IExportEntry> Exports = pcc.Exports; for (int i = 0; i < Exports.Count; i++) { if (Exports[i].ClassName == "SkeletalMesh" || Exports[i].ClassName == "StaticMesh") { EntryStruct ent = new EntryStruct(); ent.DLCName = ""; ent.Filename = Path.GetFileName(file); ent.Index = i; ent.isDLC = false; ent.ObjectPath = Exports[i].GetFullPath; ent.isSkeletal = Exports[i].ClassName == "SkeletalMesh"; Entries.Add(ent); } } } if (count % 10 == 0) { Application.DoEvents(); pbar1.Value = count; } count++; } catch (Exception ex) { DebugOutput.PrintLn("=====ERROR=====\n" + ex.ToString() + "\n=====ERROR====="); } } #endregion #region Sorting bool run = true; DebugOutput.PrintLn("=====Info=====\n\nSorting names...\n\n=====Info====="); count = 0; while (run) { run = false; for (int i = 0; i < Entries.Count - 1; i++) { if (Entries[i].Filename.CompareTo(Entries[i + 1].Filename) > 0) { EntryStruct tmp = Entries[i]; Entries[i] = Entries[i + 1]; Entries[i + 1] = tmp; run = true; if (count++ % 100 == 0) { Application.DoEvents(); } } } } #endregion TreeRefresh(); timer1.Enabled = true; }
public static bool InstallOHKO(RandomizationOption arg) { MERLog.Information("Installing One-Hit KO"); var sfxgame = MERFileSystem.GetPackageFile("SFXGame.pcc"); if (sfxgame != null && File.Exists(sfxgame)) { var sfxgameP = MEPackageHandler.OpenMEPackage(sfxgame); // Blood on screen VFX sfxgameP.GetUExport(29336).RemoveProperty("BleedOutEventPair"); sfxgameP.GetUExport(29336).RemoveProperty("BleedOutVFXTemplate"); // Prevent weird landing glitch var fallingStateLanded = sfxgameP.GetUExport(11293); var landedData = fallingStateLanded.Data; NopRange(landedData, 0x2C, 0x14); fallingStateLanded.Data = landedData; MERFileSystem.SavePackage(sfxgameP); } else { } // ProCer tutorials setting min1Health SetupProCer(); // Player classes - Remove shields, set maxhealth to 1 string[] classes = new[] { "Adept", "Engineer", "Infiltrator", "Sentinel", "Soldier", "Vanguard" }; foreach (var c in classes) { var charClass = MERFileSystem.GetPackageFile($"SFXCharacterClass_{c}.pcc"); if (charClass != null && File.Exists(charClass)) { var charClassP = MEPackageHandler.OpenMEPackage(charClass); var ccLoadout = charClassP.FindExport($"BioChar_Loadouts.Player.PLY_{c}"); var ccProps = ccLoadout.GetProperties(); ccProps.GetProp <ArrayProperty <StructProperty> >("ShieldLoadouts").Clear(); // Remove shields // Set health to 1 var health = ccProps.GetProp <StructProperty>("MaxHealth"); health.GetProp <FloatProperty>("X").Value = 1; health.GetProp <FloatProperty>("Y").Value = 1; ccLoadout.WriteProperties(ccProps); // Remove any passive powers ZeroOutStatList(charClassP.FindExport($"SFXGameContent_Powers.SFXPower_{c}Passive"), "HealthBonus", false); ZeroOutStatList(charClassP.FindExport($"SFXGameContent_Powers.SFXPower_{c}Passive_Evolved1"), "HealthBonus", false); ZeroOutStatList(charClassP.FindExport($"SFXGameContent_Powers.SFXPower_{c}Passive_Evolved2"), "HealthBonus", false); if (c == "Vanguard") { // Patch the immunity effect var shieldEffectOnApplied = charClassP.GetUExport(530); var seData = shieldEffectOnApplied.Data; NopRange(seData, 0x2BF, 0x13); shieldEffectOnApplied.Data = seData; } MERFileSystem.SavePackage(charClassP); } } // Zero out stats in tables MERPackageCache cache = new MERPackageCache(); foreach (var asset in ZeroOutStatAssets) { var statClass = asset.GetAsset(cache); if (statClass != null) { foreach (var zos in asset.PropertiesToZeroOut) { ZeroOutStatList(statClass, zos, true); } } } foreach (var package in cache.GetPackages()) { MERFileSystem.SavePackage(package); } { //var reaveF = MERFileSystem.GetPackageFile("SFXPower_Reave_Player.pcc"); //if (reaveF != null && File.Exists(reaveF)) //{ // var reaveP = MEPackageHandler.OpenMEPackage(reaveF); // var defaultReave = reaveP.GetUExport(27); // var regen = defaultReave.GetProperty<ArrayProperty<FloatProperty>>("HealthRegenMult"); // regen[0].Value = 0; // regen[1].Value = 0; // regen[2].Value = 0; // regen[3].Value = 0; // defaultReave.WriteProperty(regen); // var bonusDuration = defaultReave.GetProperty<ArrayProperty<FloatProperty>>("HealthBonusDuration"); // bonusDuration[0].Value = 0; // bonusDuration[1].Value = 0; // bonusDuration[2].Value = 0; // bonusDuration[3].Value = 0; // defaultReave.WriteProperty(bonusDuration); // MERFileSystem.SavePackage(reaveP); //} } return(true); }
private void treeView1_AfterSelect(object sender, TreeViewEventArgs e) { TreeNode t = treeView1.SelectedNode; if (DisplayStyle == 0) { if (t.Parent == null || t.Name == "") { return; } preview?.Dispose(); preview = null; try { if (int.TryParse(t.Name, out int i)) { EntryStruct en = Entries[i]; if (!en.isDLC) { using (ME3Package pcc = MEPackageHandler.OpenME3Package(ME3Directory.cookedPath + en.Filename)) { if (en.isSkeletal) { SkeletalMesh skmesh = new SkeletalMesh(pcc, en.Index); // TODO: pass device preview = new ModelPreview(view.Device, skmesh, view.TextureCache); CenterView(); treeView2.Nodes.Clear(); if (previewWithTreeToolStripMenuItem.Checked) { treeView2.Visible = false; Application.DoEvents(); treeView2.Nodes.Add(skmesh.ToTree()); treeView2.Visible = true; } } else { } } } else { string loc = Path.GetDirectoryName(Application.ExecutablePath) + "\\exec\\"; string dirDLC = ME3Directory.DLCPath; dirDLC += en.DLCName; dirDLC += "\\CookedPCConsole\\Default.sfar"; DLCUnpack dlc = new DLCUnpack(dirDLC); foreach (DLCUnpack.DLCEntry file in dlc.filesList) { try { string filename = Path.GetFileName(file.filenamePath); if (Path.GetExtension(filename).ToLower().EndsWith(".pcc") && filename == en.Filename) { using (Stream input = File.OpenRead(dirDLC), output = File.Create(loc + filename)) { dlc.ExtractEntry(file, input, output); } if (File.Exists(loc + filename)) { try { if (en.isSkeletal) { using (ME3Package pcc = MEPackageHandler.OpenME3Package(loc + filename)) { SkeletalMesh skmesh = new SkeletalMesh(pcc, en.Index); CenterView(); treeView2.Nodes.Clear(); if (previewWithTreeToolStripMenuItem.Checked) { treeView2.Visible = false; Application.DoEvents(); treeView2.Nodes.Add(skmesh.ToTree()); treeView2.Visible = true; } } } } catch (Exception) { } File.Delete(loc + filename); } } } catch (Exception) { } } } } } catch (Exception ex) { MessageBox.Show(ex.ToString()); } } }
public static IEntry PortWeaponIntoPackage(IMEPackage targetPackage, GunInfo gunInfo) { IMEPackage sourcePackage; if (gunInfo.IsCorrectedPackage) { var sourceData = MERUtilities.GetEmbeddedStaticFilesBinaryFile("correctedloadouts.weapons." + gunInfo.PackageFileName); sourcePackage = MEPackageHandler.OpenMEPackageFromStream(new MemoryStream(sourceData)); if (gunInfo.ImportOnly) { // We need to install this file var outfname = Path.Combine(MERFileSystem.DLCModCookedPath, gunInfo.PackageFileName); if (!File.Exists(outfname)) { sourcePackage.Save(outfname, true); ThreadSafeDLCStartupPackage.AddStartupPackage(Path.GetFileNameWithoutExtension(gunInfo.PackageFileName)); } } } else { sourcePackage = NonSharedPackageCache.Cache.GetCachedPackage(gunInfo.PackageFileName); } if (sourcePackage != null) { var sourceExport = sourcePackage.GetUExport(gunInfo.SourceUIndex); if (!sourceExport.InheritsFrom("SFXWeapon") || sourceExport.IsDefaultObject) { throw new Exception("Wrong setup!"); } if (sourceExport.Parent != null && sourceExport.Parent.ClassName != "Package") { throw new Exception("Cannot port weapon - parent object is not Package!"); } // 1. Setup the link that will be used. //var newParent = EntryExporter.PortParents(sourceExport, targetPackage); var newParent = EntryExporter.PortParents(sourceExport, targetPackage, gunInfo.ImportOnly); void errorOccuredCB(string s) { Debugger.Break(); } IEntry newEntry = null; if (gunInfo.ImportOnly) { Debug.WriteLine($"Gun ImportOnly in file {targetPackage.FilePath}"); if (gunInfo.RequiresStartupPackage) { ThreadSafeDLCStartupPackage.AddStartupPackage(Path.GetFileNameWithoutExtension(gunInfo.PackageFileName)); } newEntry = PackageTools.CreateImportForClass(sourceExport, targetPackage, newParent); } else { List <EntryStringPair> relinkResults = null; if (gunInfo.IsCorrectedPackage || (PackageTools.IsPersistentPackage(gunInfo.PackageFileName) && MERFileSystem.GetPackageFile(gunInfo.PackageFileName.ToLocalizedFilename()) == null)) { // Faster this way, without having to check imports Dictionary <IEntry, IEntry> crossPCCObjectMap = new Dictionary <IEntry, IEntry>(); // Not sure what this is used for these days. SHould probably just be part of the method relinkResults = EntryImporter.ImportAndRelinkEntries(EntryImporter.PortingOption.CloneAllDependencies, sourceExport, targetPackage, newParent, true, out newEntry, crossPCCObjectMap); } else { // MEMORY SAFE (resolve imports to exports) MERPackageCache cache = new MERPackageCache(); relinkResults = EntryExporter.ExportExportToPackage(sourceExport, targetPackage, out newEntry, cache); } if (relinkResults.Any()) { Debugger.Break(); } } return(newEntry); } else { Debug.WriteLine($"Package for gun porting not found: {gunInfo.PackageFileName}"); } return(null); // No package was found }
public TreeNode BuildPccTree(string pcc) { if (!_isPackageHandlerInitialized) { MEPackageHandler.Initialize(); _isPackageHandlerInitialized = true;; } // check first if this pcc has already been added in the tree bool IsPccAlreadyInTree = PccList.Find(ptn => ptn.pcc.FileName == pcc) != null; if (IsPccAlreadyInTree) { return(null); } var Pcc = MEPackageHandler.OpenMEPackage(pcc); /* * ME3Package me3Pcc = Pcc as ME3Package; * * if (me3Pcc == null) * { * // supports only ME3 for the time being * throw new ArgumentException("This is not a ME3 package. The tool only supports ME3 at this time."); * } * */ PccTreeNode pccRootNode = null; var MorphExpIndexes = Pcc.Exports.Select((value, index) => new { value, index }) .Where(z => z.value.ClassName == "BioMorphFace") .Select(z => z.index); if (MorphExpIndexes.Count() > 0) { pccRootNode = new PccTreeNode(Pcc); // Morphs TreeNode ParentMorphNode = new TreeNode(); ParentMorphNode.Text = "BioMorphFace"; foreach (int morphIndex in MorphExpIndexes) { var morph = new Unreal.BioMorphFace(Pcc, morphIndex); MorphTreeNode morphNode = new MorphTreeNode(morph); ParentMorphNode.Nodes.Add(morphNode); } // Meshes TreeNode ParentMeshNode = new TreeNode(); ParentMeshNode.Text = "SkeletalMesh"; var MeshExpIndexes = Pcc.Exports.Select((value, index) => new { value, index }) .Where(z => z.value.ClassName == "SkeletalMesh") .Select(z => z.index); foreach (int meshIndex in MeshExpIndexes) { IExportEntry exp = Pcc.Exports[meshIndex]; //SkeletalMesh skMesh = new SkeletalMesh((ME3Package)Pcc, meshIndex); var skMesh = new Unreal.MESkeletalMesh(Pcc, meshIndex); MeshTreeNode meshNode = new MeshTreeNode(skMesh, exp); ParentMeshNode.Nodes.Add(meshNode); } // build hierarchy pccRootNode.Nodes.Add(ParentMeshNode); pccRootNode.Nodes.Add(ParentMorphNode); // add in the list PccList.Add(pccRootNode); } else { Pcc = null; } return(pccRootNode); }
public static void ApplyMixinsToModule(KeyValuePair <ModJob.JobHeader, Dictionary <string, List <Mixin> > > mapping, string modpath, Action completedSingleApplicationCallback, Action <string> failedApplicationCallback) { var dlcFolderName = ModMakerCompiler.ModmakerChunkNameToDLCFoldername(mapping.Key.ToString()); var outdir = Path.Combine(modpath, ModMakerCompiler.HeaderToDefaultFoldername(mapping.Key), @"CookedPCConsole"); Directory.CreateDirectory(outdir); if (mapping.Key == ModJob.JobHeader.BASEGAME) { //basegame foreach (var file in mapping.Value) { try { using var packageAsStream = VanillaDatabaseService.FetchBasegameFile(MEGame.ME3, Path.GetFileName(file.Key)); //packageAsStream.WriteToFile(@"C:\users\dev\desktop\compressed.pcc"); using var decompressedStream = MEPackage.GetDecompressedPackageStream(packageAsStream, false, true); using var finalStream = MixinHandler.ApplyMixins(decompressedStream, file.Value, true, completedSingleApplicationCallback, failedApplicationCallback); CLog.Information(@"Compressing package to mod directory: " + file.Key, Settings.LogModMakerCompiler); finalStream.Position = 0; var package = MEPackageHandler.OpenMEPackageFromStream(finalStream); var outfile = Path.Combine(outdir, Path.GetFileName(file.Key)); package.Save(outfile, false, includeAdditionalPackagesToCook: false, includeDependencyTable: true); // don't compress, use mixin saving rules for basegame files } catch (Exception e) { var mixinsStr = string.Join(@", ", file.Value.Select(x => x.PatchName)); Log.Error($@"Error in mixin application for file {file.Key}: {e.Message}"); failedApplicationCallback(M3L.GetString(M3L.string_interp_errorApplyingMixinsForFile, mixinsStr, file.Key, e.Message)); } } } else { //dlc var dlcPackage = VanillaDatabaseService.FetchVanillaSFAR(dlcFolderName); //do not have to open file multiple times. foreach (var file in mapping.Value) { try { using var packageAsStream = VanillaDatabaseService.FetchFileFromVanillaSFAR(dlcFolderName, file.Key, forcedDLC: dlcPackage); //as file comes from backup, we don't need to decompress it, it will always be decompressed in sfar using var finalStream = MixinHandler.ApplyMixins(packageAsStream, file.Value, true, completedSingleApplicationCallback, failedApplicationCallback); var outfile = Path.Combine(outdir, Path.GetFileName(file.Key)); if (mapping.Key != ModJob.JobHeader.TESTPATCH) { // TestPatch is never unpacked. So there is not really point to // compressing it's rather small files. The other DLC jobs likely will be packed still, but this will save some disk space. CLog.Information($@"Compressing package to mod directory: {outfile}", Settings.LogModMakerCompiler); finalStream.Position = 0; var package = MEPackageHandler.OpenMEPackageFromStream(finalStream); package.Save(outfile, true); } else { Log.Information($@"Writing patched file to disk: {outfile}"); finalStream.WriteToFile(outfile); } } catch (Exception e) { var mixinsStr = string.Join(@", ", file.Value.Select(x => x.PatchName)); Log.Error($@"Error in mixin application for file {file.Key}: {e.Message}"); failedApplicationCallback(M3L.GetString(M3L.string_interp_errorApplyingMixinsForFile, mixinsStr, file.Key, e.Message)); } //finalStream.WriteToFile(outfile); } } }
private void CompileIntoGame() { NamedBackgroundWorker nbw = new NamedBackgroundWorker(@"MixinManager CompileIntoGameThread"); List <string> failedApplications = new List <string>(); nbw.DoWork += (a, b) => { BottomLeftMessage = M3L.GetString(M3L.string_compilingMixins); OperationInProgress = true; //DEBUG STUFF #if DEBUG int numCoresToApplyWith = 1; #else var numCoresToApplyWith = Environment.ProcessorCount; if (numCoresToApplyWith > 4) { numCoresToApplyWith = 4; //no more than 4 as this uses a lot of memory } #endif var mixins = AvailableOfficialMixins.Where(x => x.UISelectedForUse).ToList(); MixinHandler.LoadPatchDataForMixins(mixins); //before dynamic void failedApplicationCallback(string str) { failedApplications.Add(str); } var compilingListsPerModule = MixinHandler.GetMixinApplicationList(mixins, failedApplicationCallback); if (Enumerable.Any(failedApplications)) { //Error building list Log.Information(@"Aborting mixin install due to incompatible selection of mixins"); return; } ProgressBarMax = mixins.Count(); ProgressBarValue = 0; int numdone = 0; void completedSingleApplicationCallback() { var val = Interlocked.Increment(ref numdone); ProgressBarValue = val; } //Mixins are ready to be applied Parallel.ForEach(compilingListsPerModule, new ParallelOptions { MaxDegreeOfParallelism = Environment.ProcessorCount > numCoresToApplyWith ? numCoresToApplyWith : Environment.ProcessorCount }, mapping => { var dlcFolderName = ModMakerCompiler.ModmakerChunkNameToDLCFoldername(mapping.Key.ToString()); //var outdir = Path.Combine(modpath, ModMakerCompiler.HeaderToDefaultFoldername(mapping.Key), @"CookedPCConsole"); //Directory.CreateDirectory(outdir); if (mapping.Key == ModJob.JobHeader.BASEGAME) { //basegame foreach (var file in mapping.Value) { try { using var vanillaPackageAsStream = VanillaDatabaseService.FetchBasegameFile(MEGame.ME3, Path.GetFileName(file.Key)); //packageAsStream.WriteToFile(@"C:\users\dev\desktop\compressed.pcc"); using var decompressedStream = MEPackage.GetDecompressedPackageStream(vanillaPackageAsStream, false, true); decompressedStream.Position = 0; var vanillaPackage = MEPackageHandler.OpenMEPackageFromStream(decompressedStream, $@"Vanilla - {Path.GetFileName(file.Key)}"); //decompressedStream.WriteToFile(@"C:\users\dev\desktop\decompressed.pcc"); using var mixinModifiedStream = MixinHandler.ApplyMixins(decompressedStream, file.Value, true, completedSingleApplicationCallback, failedApplicationCallback); mixinModifiedStream.Position = 0; var modifiedPackage = MEPackageHandler.OpenMEPackageFromStream(mixinModifiedStream, $@"Mixin Modified - {Path.GetFileName(file.Key)}"); // three way merge: get target stream var targetFile = Path.Combine(M3Directories.GetCookedPath(SelectedInstallTarget), Path.GetFileName(file.Key)); var targetPackage = MEPackageHandler.OpenMEPackage(targetFile); var merged = ThreeWayPackageMerge.AttemptMerge(vanillaPackage, modifiedPackage, targetPackage); if (merged) { targetPackage.Save(compress: true); Log.Information(@"Three way merge succeeded for " + targetFile); } else { Log.Error(@"Could not merge three way merge into " + targetFile); } //var outfile = Path.Combine(outdir, Path.GetFileName(file.Key)); //package.save(outfile, false); // don't compress //finalStream.WriteToFile(outfile); //File.WriteAllBytes(outfile, finalStream.ToArray()); } catch (Exception e) { var mixinsStr = string.Join(@", ", file.Value.Select(x => x.PatchName)); Log.Error($@"Error in mixin application for file {file.Key}: {e.Message}"); failedApplicationCallback(M3L.GetString(M3L.string_interp_errorApplyingMixinsForFile, mixinsStr, file.Key, e.Message)); } } } else { //dlc var dlcPackage = VanillaDatabaseService.FetchVanillaSFAR(dlcFolderName); //do not have to open file multiple times. var targetCookedPCDir = Path.Combine(M3Directories.GetDLCPath(SelectedInstallTarget), dlcFolderName, @"CookedPCConsole"); var sfar = mapping.Key == ModJob.JobHeader.TESTPATCH ? M3Directories.GetTestPatchSFARPath(SelectedInstallTarget) : Path.Combine(targetCookedPCDir, @"Default.sfar"); bool unpacked = new FileInfo(sfar).Length == 32; DLCPackage targetDLCPackage = unpacked ? null : new DLCPackage(sfar); //cache SFAR target foreach (var file in mapping.Value) { try { using var vanillaPackageAsStream = VanillaDatabaseService.FetchFileFromVanillaSFAR(dlcFolderName, file.Key, forcedDLC: dlcPackage); using var decompressedStream = MEPackage.GetDecompressedPackageStream(vanillaPackageAsStream); decompressedStream.Position = 0; var vanillaPackage = MEPackageHandler.OpenMEPackageFromStream(decompressedStream, $@"VanillaDLC - {Path.GetFileName(file.Key)}"); using var mixinModifiedStream = MixinHandler.ApplyMixins(decompressedStream, file.Value, true, completedSingleApplicationCallback, failedApplicationCallback); mixinModifiedStream.Position = 0; var modifiedPackage = MEPackageHandler.OpenMEPackageFromStream(mixinModifiedStream, $@"Mixin Modified - {Path.GetFileName(file.Key)}"); // three way merge: get target stream // must see if DLC is unpacked first MemoryStream targetFileStream = null; //Packed if (unpacked) { targetFileStream = new MemoryStream(File.ReadAllBytes(Path.Combine(targetCookedPCDir, file.Key))); } else { targetFileStream = VanillaDatabaseService.FetchFileFromVanillaSFAR(dlcFolderName, Path.GetFileName(file.Key), forcedDLC: targetDLCPackage); } var targetPackage = MEPackageHandler.OpenMEPackageFromStream(targetFileStream, $@"Target package {dlcFolderName} - {file.Key}, from SFAR: {unpacked}"); var merged = ThreeWayPackageMerge.AttemptMerge(vanillaPackage, modifiedPackage, targetPackage); if (merged) { if (unpacked) { targetPackage.Save(Path.Combine(targetCookedPCDir, file.Key)); Log.Information(@"Three way merge succeeded for " + targetPackage.FilePath); } else { var finalSTream = targetPackage.SaveToStream(false); // No compress. Not sure if we should support doing that though. targetDLCPackage.ReplaceEntry(finalSTream.ToArray(), targetDLCPackage.FindFileEntry(Path.GetFileName(file.Key))); Log.Information(@"Three way merge succeeded for " + targetPackage.FilePath); } } else { Log.Error(@"Could not three way merge into: " + targetFileStream); } } catch (Exception e) { var mixinsStr = string.Join(@", ", file.Value.Select(x => x.PatchName)); Log.Error($@"Error in mixin application for file {file.Key}: {e.Message}"); failedApplicationCallback(M3L.GetString(M3L.string_interp_errorApplyingMixinsForFile, mixinsStr, file.Key, e.Message)); } //finalStream.WriteToFile(outfile); } } }); MixinHandler.FreeME3TweaksPatchData(); var percent = 0; //this is used to save a localization BottomLeftMessage = M3L.GetString(M3L.string_interp_runningAutoTOCOnGamePercentX, percent); //Run autotoc void tocingUpdate(int percent) { BottomLeftMessage = M3L.GetString(M3L.string_interp_runningAutoTOCOnGamePercentX, percent); } AutoTOC.RunTOCOnGameTarget(SelectedInstallTarget, tocingUpdate); //Generate moddesc //IniData ini = new IniData(); //ini[@"ModManager"][@"cmmver"] = App.HighestSupportedModDesc.ToString(CultureInfo.InvariantCulture); //prevent commas //ini[@"ModInfo"][@"game"] = @"ME3"; //ini[@"ModInfo"][@"modname"] = modname; //ini[@"ModInfo"][@"moddev"] = App.AppVersionHR; //ini[@"ModInfo"][@"moddesc"] = M3L.GetString(M3L.string_compiledFromTheFollowingMixins); //ini[@"ModInfo"][@"modver"] = @"1.0"; //generateRepaceFilesMapping(ini, modpath); //File.WriteAllText(Path.Combine(modpath, @"moddesc.ini"), ini.ToString()); }; nbw.RunWorkerCompleted += (a, b) => { if (b.Error != null) { Log.Error($@"Exception occurred in {nbw.Name} thread: {b.Error.Message}"); } OperationInProgress = false; ClearMixinHandler(); if (failedApplications.Count > 0) { var ld = new ListDialog(failedApplications, M3L.GetString(M3L.string_failedToApplyAllMixins), M3L.GetString(M3L.string_theFollowingMixinsFailedToApply), mainwindow); ld.ShowDialog(); } /*if (modpath != null) * { * OnClosing(new DataEventArgs(modpath)); * } * else * {*/ BottomLeftMessage = M3L.GetString(M3L.string_mixinsInstalledMaybe); //} }; CompilePanelButton.IsOpen = false; nbw.RunWorkerAsync(); }
public static void RunGame2EmailMerge(GameTarget target) { M3MergeDLC.RemoveMergeDLC(target); var loadedFiles = MELoadedFiles.GetFilesLoadedInGame(target.Game, gameRootOverride: target.TargetPath); // File to base modifications on using IMEPackage pcc = MEPackageHandler.OpenMEPackage(loadedFiles[@"BioD_Nor103_Messages.pcc"]); // Path to Message templates file - different files for ME2/LE2 string ResourcesFilePath = $@"MassEffectModManagerCore.modmanager.emailmerge.{target.Game}.103Message_Template_{target.Game}"; using IMEPackage resources = MEPackageHandler.OpenMEPackageFromStream(Utilities.GetResourceStream(ResourcesFilePath)); // Startup file to place conditionals and transitions into using IMEPackage startup = MEPackageHandler.OpenMEPackageFromStream(Utilities.GetResourceStream( $@"MassEffectModManagerCore.modmanager.mergedlc.{target.Game}.Startup_{M3MergeDLC.MERGE_DLC_FOLDERNAME}.pcc")); var emailInfos = new List <ME2EmailMergeFile>(); var jsonSupercedances = M3Directories.GetFileSupercedances(target, new[] { @".json" }); if (jsonSupercedances.TryGetValue(EMAIL_MERGE_MANIFEST_FILE, out var jsonList)) { jsonList.Reverse(); foreach (var dlc in jsonList) { var jsonFile = Path.Combine(M3Directories.GetDLCPath(target), dlc, target.Game.CookedDirName(), EMAIL_MERGE_MANIFEST_FILE); emailInfos.Add(JsonConvert.DeserializeObject <ME2EmailMergeFile>(File.ReadAllText(jsonFile))); } } // Sanity checks if (!emailInfos.Any() || !emailInfos.SelectMany(e => e.Emails).Any()) { return; } if (emailInfos.Any(e => e.Game != target.Game)) { throw new Exception("ME2 email merge manifest targets incorrect game"); } // Startup File // Could replace this with full instanced path in M3 implementation ExportEntry stateEventMapExport = startup.Exports .First(e => e.ClassName == "BioStateEventMap" && e.ObjectName == "StateTransitionMap"); BioStateEventMap StateEventMap = stateEventMapExport.GetBinaryData <BioStateEventMap>(); ExportEntry ConditionalClass = startup.FindExport($@"PlotManager{M3MergeDLC.MERGE_DLC_FOLDERNAME}.BioAutoConditionals"); #region Sequence Exports // Send message - All email conditionals are checked and emails transitions are triggered ExportEntry SendMessageContainer = pcc.FindExport(@"TheWorld.PersistentLevel.Main_Sequence.Send_Messages"); ExportEntry LastSendMessage = KismetHelper.GetSequenceObjects(SendMessageContainer).OfType <ExportEntry>() .FirstOrDefault(e => { var outbound = KismetHelper.GetOutboundLinksOfNode(e); return(outbound.Count == 1 && outbound[0].Count == 0); }); ExportEntry TemplateSendMessage = resources.FindExport(@"TheWorld.PersistentLevel.Main_Sequence.Send_MessageTemplate"); ExportEntry TemplateSendMessageBoolCheck = resources.FindExport(@"TheWorld.PersistentLevel.Main_Sequence.Send_MessageTemplate_BoolCheck"); // Mark Read - email ints are set to read // This is the only section that does not gracefully handle different DLC installations - DLC_CER is required atm ExportEntry MarkReadContainer = pcc.FindExport(@"TheWorld.PersistentLevel.Main_Sequence.Mark_Read"); ExportEntry LastMarkRead = pcc.FindExport(@"TheWorld.PersistentLevel.Main_Sequence.Mark_Read.DLC_CER"); ExportEntry MarkReadOutLink = KismetHelper.GetOutboundLinksOfNode(LastMarkRead)[0][0].LinkedOp as ExportEntry; KismetHelper.RemoveOutputLinks(LastMarkRead); ExportEntry TemplateMarkRead = resources.FindExport(@"TheWorld.PersistentLevel.Main_Sequence.Mark_ReadTemplate"); ExportEntry TemplateMarkReadTransition = resources.FindExport(@"TheWorld.PersistentLevel.Main_Sequence.Mark_Read_Transition"); // Display Messages - Str refs are passed through to GUI ExportEntry DisplayMessageContainer = pcc.FindExport(@"TheWorld.PersistentLevel.Main_Sequence.Display_Messages"); ExportEntry DisplayMessageOutLink = pcc.FindExport(@"TheWorld.PersistentLevel.Main_Sequence.Display_Messages.SeqCond_CompareBool_0"); ExportEntry LastDisplayMessage = SeqTools.FindOutboundConnectionsToNode(DisplayMessageOutLink, KismetHelper.GetSequenceObjects(DisplayMessageContainer).OfType <ExportEntry>())[0]; KismetHelper.RemoveOutputLinks(LastDisplayMessage); var DisplayMessageVariableLinks = LastDisplayMessage.GetProperty <ArrayProperty <StructProperty> >("VariableLinks"); ExportEntry TemplateDisplayMessage = resources.FindExport(@"TheWorld.PersistentLevel.Main_Sequence.Display_MessageTemplate"); // Archive Messages - Message ints are set to 3 ExportEntry ArchiveContainer = pcc.FindExport(@"TheWorld.PersistentLevel.Main_Sequence.Archive_Message"); ExportEntry ArchiveSwitch = pcc.FindExport(@"TheWorld.PersistentLevel.Main_Sequence.Archive_Message.SeqAct_Switch_0"); ExportEntry ArchiveOutLink = pcc.FindExport( @"TheWorld.PersistentLevel.Main_Sequence.Archive_Message.BioSeqAct_PMCheckConditional_1"); ExportEntry ExampleSetInt = KismetHelper.GetOutboundLinksOfNode(ArchiveSwitch)[0][0].LinkedOp as ExportEntry; ExportEntry ExamplePlotInt = SeqTools.GetVariableLinksOfNode(ExampleSetInt)[0].LinkedNodes[0] as ExportEntry; #endregion int messageID = KismetHelper.GetOutboundLinksOfNode(ArchiveSwitch).Count + 1; int currentSwCount = ArchiveSwitch.GetProperty <IntProperty>("LinkCount").Value; foreach (var emailMod in emailInfos) { string modName = "DLC_MOD_" + emailMod.ModName; foreach (var email in emailMod.Emails) { string emailName = modName + "_" + email.EmailName; // Create send transition int transitionId = WriteTransition(StateEventMap, email.StatusPlotInt); int conditionalId = WriteConditional(email.TriggerConditional); #region SendMessage ////////////// // SendMessage ////////////// // Create seq object var SMTemp = emailMod.InMemoryBool.HasValue ? TemplateSendMessageBoolCheck : TemplateSendMessage; EntryImporter.ImportAndRelinkEntries(EntryImporter.PortingOption.CloneTreeAsChild, SMTemp, pcc, SendMessageContainer, true, new RelinkerOptionsPackage(), out var outSendEntry); var newSend = outSendEntry as ExportEntry; // Set name, comment, add to sequence newSend.ObjectName = new NameReference(emailName); KismetHelper.AddObjectToSequence(newSend, SendMessageContainer); KismetHelper.SetComment(newSend, emailName); if (target.Game == MEGame.ME2) { newSend.WriteProperty(new StrProperty(emailName, "ObjName")); } // Set Trigger Conditional var pmCheckConditionalSM = newSend.GetChildren() .FirstOrDefault(e => e.ClassName == "BioSeqAct_PMCheckConditional" && e is ExportEntry); if (pmCheckConditionalSM is ExportEntry conditional) { conditional.WriteProperty(new IntProperty(conditionalId, "m_nIndex")); KismetHelper.SetComment(conditional, "Time for " + email.EmailName + "?"); } // Set Send Transition var pmExecuteTransitionSM = newSend.GetChildren() .FirstOrDefault(e => e.ClassName == "BioSeqAct_PMExecuteTransition" && e is ExportEntry); if (pmExecuteTransitionSM is ExportEntry transition) { transition.WriteProperty(new IntProperty(transitionId, "m_nIndex")); KismetHelper.SetComment(transition, "Send " + email.EmailName + " message."); } // Set Send Transition if (emailMod.InMemoryBool.HasValue) { var pmCheckStateSM = newSend.GetChildren() .FirstOrDefault(e => e.ClassName == "BioSeqAct_PMCheckState" && e is ExportEntry); if (pmCheckStateSM is ExportEntry checkState) { checkState.WriteProperty(new IntProperty(emailMod.InMemoryBool.Value, "m_nIndex")); KismetHelper.SetComment(checkState, "Is " + emailMod.ModName + " installed?"); } } // Hook up output links KismetHelper.CreateOutputLink(LastSendMessage, "Out", newSend); LastSendMessage = newSend; #endregion #region MarkRead /////////// // MarkRead /////////// // Create seq object var MRTemp = email.ReadTransition.HasValue ? TemplateMarkReadTransition : TemplateMarkRead; EntryImporter.ImportAndRelinkEntries(EntryImporter.PortingOption.CloneTreeAsChild, MRTemp, pcc, MarkReadContainer, true, new RelinkerOptionsPackage(), out var outMarkReadEntry); var newMarkRead = outMarkReadEntry as ExportEntry; // Set name, comment, add to sequence newMarkRead.ObjectName = new NameReference(emailName); KismetHelper.AddObjectToSequence(newMarkRead, MarkReadContainer); KismetHelper.SetComment(newMarkRead, emailName); if (target.Game == MEGame.ME2) { newMarkRead.WriteProperty(new StrProperty(emailName, "ObjName")); } // Set Plot Int var storyManagerIntMR = newMarkRead.GetChildren() .FirstOrDefault(e => e.ClassName == "BioSeqVar_StoryManagerInt" && e is ExportEntry); if (storyManagerIntMR is ExportEntry plotIntMR) { plotIntMR.WriteProperty(new IntProperty(email.StatusPlotInt, "m_nIndex")); KismetHelper.SetComment(plotIntMR, email.EmailName); } if (email.ReadTransition.HasValue) { var pmExecuteTransitionMR = newMarkRead.GetChildren() .FirstOrDefault(e => e.ClassName == "BioSeqAct_PMExecuteTransition" && e is ExportEntry); if (pmExecuteTransitionMR is ExportEntry transitionMR) { transitionMR.WriteProperty(new IntProperty(email.ReadTransition.Value, "m_nIndex")); KismetHelper.SetComment(transitionMR, "Trigger " + email.EmailName + " read transition"); } } // Hook up output links KismetHelper.CreateOutputLink(LastMarkRead, "Out", newMarkRead); LastMarkRead = newMarkRead; #endregion #region DisplayEmail //////////////// // Display Email //////////////// // Create seq object EntryImporter.ImportAndRelinkEntries(EntryImporter.PortingOption.CloneTreeAsChild, TemplateDisplayMessage, pcc, DisplayMessageContainer, true, new RelinkerOptionsPackage(), out var outDisplayMessage); var newDisplayMessage = outDisplayMessage as ExportEntry; // Set name, comment, variable links, add to sequence newDisplayMessage.ObjectName = new NameReference(emailName); KismetHelper.AddObjectToSequence(newDisplayMessage, DisplayMessageContainer); newDisplayMessage.WriteProperty(DisplayMessageVariableLinks); KismetHelper.SetComment(newDisplayMessage, emailName); if (target.Game == MEGame.ME2) { newDisplayMessage.WriteProperty(new StrProperty(emailName, "ObjName")); } var displayChildren = newDisplayMessage.GetChildren(); // Set Plot Int var storyManagerIntDE = displayChildren.FirstOrDefault(e => e.ClassName == "BioSeqVar_StoryManagerInt" && e is ExportEntry); if (storyManagerIntDE is ExportEntry plotIntDE) { plotIntDE.WriteProperty(new IntProperty(email.StatusPlotInt, "m_nIndex")); } // Set Email ID var emailIdDE = displayChildren.FirstOrDefault(e => e.ClassName == "SeqVar_Int" && e is ExportEntry); if (emailIdDE is ExportEntry EmailIDDE) { EmailIDDE.WriteProperty(new IntProperty(messageID, "IntValue")); } // Set Title StrRef var titleStrRef = displayChildren.FirstOrDefault(e => e.ClassName == "BioSeqVar_StrRef" && e is ExportEntry ee && ee.GetProperty <NameProperty>("VarName").Value == "Title StrRef"); if (titleStrRef is ExportEntry Title) { Title.WriteProperty(new StringRefProperty(email.TitleStrRef, "m_srValue")); } // Set Description StrRef var descStrRef = displayChildren.FirstOrDefault(e => e.ClassName == "BioSeqVar_StrRef" && e is ExportEntry ee && ee.GetProperty <NameProperty>("VarName").Value == "Desc StrRef"); if (descStrRef is ExportEntry Desc) { Desc.WriteProperty(new StringRefProperty(email.DescStrRef, "m_srValue")); } // Hook up output links KismetHelper.CreateOutputLink(LastDisplayMessage, "Out", newDisplayMessage); LastDisplayMessage = newDisplayMessage; #endregion #region ArchiveEmail //////////////// // Archive Email //////////////// var NewSetInt = EntryCloner.CloneEntry(ExampleSetInt); KismetHelper.AddObjectToSequence(NewSetInt, ArchiveContainer); KismetHelper.CreateOutputLink(NewSetInt, "Out", ArchiveOutLink); KismetHelper.CreateNewOutputLink(ArchiveSwitch, "Link " + (messageID - 1), NewSetInt); var NewPlotInt = EntryCloner.CloneEntry(ExamplePlotInt); KismetHelper.AddObjectToSequence(NewPlotInt, ArchiveContainer); NewPlotInt.WriteProperty(new IntProperty(email.StatusPlotInt, "m_nIndex")); NewPlotInt.WriteProperty(new StrProperty(emailName, "m_sRefName")); var linkedVars = SeqTools.GetVariableLinksOfNode(NewSetInt); linkedVars[0].LinkedNodes = new List <IEntry>() { NewPlotInt }; SeqTools.WriteVariableLinksToNode(NewSetInt, linkedVars); messageID++; currentSwCount++; #endregion } } KismetHelper.CreateOutputLink(LastMarkRead, "Out", MarkReadOutLink); KismetHelper.CreateOutputLink(LastDisplayMessage, "Out", DisplayMessageOutLink); ArchiveSwitch.WriteProperty(new IntProperty(currentSwCount, "LinkCount")); stateEventMapExport.WriteBinary(StateEventMap); // Save Messages file into DLC var cookedDir = Path.Combine(M3Directories.GetDLCPath(target), M3MergeDLC.MERGE_DLC_FOLDERNAME, target.Game.CookedDirName()); var outMessages = Path.Combine(cookedDir, @"BioD_Nor103_Messages.pcc"); pcc.Save(outMessages); // Save Startup file into DLC var startupF = Path.Combine(cookedDir, $@"Startup_{M3MergeDLC.MERGE_DLC_FOLDERNAME}.pcc"); startup.Save(startupF); }
private PreviewTextureCache.PreviewTextureEntry FindTexture(PreviewTextureCache texcache, string FullTextureName, string ImportPCC) { string importfiledir = System.IO.Path.GetDirectoryName(ImportPCC).ToLower(); string importfilename = System.IO.Path.GetFileName(ImportPCC).ToLower(); string pccpath = ""; int id = 0; // First, check the pcc that contains the material using (ME3Package pcc = MEPackageHandler.OpenME3Package(ImportPCC)) { foreach (IExportEntry exp in pcc.Exports) { if (exp.GetFullPath == FullTextureName && exp.ClassName == "Texture2D") { pccpath = ImportPCC; id = exp.Index; break; } } } // Next, split the filename by underscores string[] parts = System.IO.Path.GetFileNameWithoutExtension(importfilename).Split('_'); if (pccpath == "" && (importfilename.StartsWith("bioa") || importfilename.StartsWith("biod"))) { // Maybe go for the one with one less segment? ex. for input BioA_Nor_201CIC.pcc, look in BioA_Nor.pcc if (parts.Length == 3) { string filename = importfiledir + "\\" + parts[0] + "_" + parts[1] + ".pcc"; if (System.IO.File.Exists(filename)) { using (ME3Package pcc = MEPackageHandler.OpenME3Package(filename)) { foreach (IExportEntry exp in pcc.Exports) { if (exp.GetFullPath == FullTextureName && exp.ClassName == "Texture2D") { pccpath = importfiledir + "\\" + parts[0] + "_" + parts[1] + ".pcc"; id = exp.Index; break; } } } } } // Now go for the BioP one. if (pccpath == "" && parts.Length >= 2) { string filename = importfiledir + "\\" + "BioP" + "_" + parts[1] + ".pcc"; if (System.IO.File.Exists(filename)) { using (ME3Package pcc = MEPackageHandler.OpenME3Package(filename)) { foreach (IExportEntry exp in pcc.Exports) { if (exp.GetFullPath == FullTextureName && exp.ClassName == "Texture2D") { pccpath = importfiledir + "\\" + "BioP" + "_" + parts[1] + ".pcc"; id = exp.Index; break; } } } } } } if (id > 0) { return(texcache.LoadTexture(pccpath, id)); } else { Console.WriteLine("[TEXLOAD]: Could not find texture \"" + FullTextureName + "\", imported in \"" + ImportPCC + "\"."); return(null); } }
//call this method to regenerate ME1ObjectInfo.json //Takes a long time (10 to 20 minutes maybe?). Application will be completely unresponsive during that time. public static void generateInfo() { Classes = new Dictionary <string, ClassInfo>(); Structs = new Dictionary <string, ClassInfo>(); Enums = new Dictionary <string, List <NameReference> >(); SequenceObjects = new Dictionary <string, SequenceObjectInfo>(); foreach (string file in MELoadedFiles.GetOfficialFiles(MEGame.ME1)) { if (Path.GetExtension(file) == ".upk" || Path.GetExtension(file) == ".sfm" || Path.GetExtension(file) == ".u") { Debug.WriteLine($"File: {file}"); using IMEPackage pcc = MEPackageHandler.OpenME1Package(file); for (int j = 1; j <= pcc.ExportCount; j++) { ExportEntry exportEntry = pcc.GetUExport(j); string className = exportEntry.ClassName; if (className == "Enum") { generateEnumValues(exportEntry); } else if (className == "Class") { string objectName = exportEntry.ObjectName; Debug.WriteLine($"Generating information for {objectName}"); if (!Classes.ContainsKey(objectName)) { Classes.Add(objectName, generateClassInfo(exportEntry)); } } else if (className == "ScriptStruct") { string objectName = exportEntry.ObjectName; if (!Structs.ContainsKey(exportEntry.ObjectName)) { Structs.Add(objectName, generateClassInfo(exportEntry, isStruct: true)); } } else if (exportEntry.IsOrInheritsFrom("SequenceObject")) { if (!SequenceObjects.TryGetValue(className, out SequenceObjectInfo seqObjInfo)) { seqObjInfo = new SequenceObjectInfo(); SequenceObjects.Add(className, seqObjInfo); } int objInstanceVersion = exportEntry.GetProperty <IntProperty>("ObjInstanceVersion"); if (objInstanceVersion > seqObjInfo.ObjInstanceVersion) { seqObjInfo.ObjInstanceVersion = objInstanceVersion; } } } } } //CUSTOM ADDITIONS Classes["LightMapTexture2D"] = new ClassInfo { baseClass = "Texture2D", exportIndex = 0, pccPath = UnrealObjectInfo.Me3ExplorerCustomNativeAdditionsName }; Classes["StaticMesh"] = new ClassInfo { baseClass = "Object", exportIndex = 0, pccPath = UnrealObjectInfo.Me3ExplorerCustomNativeAdditionsName, properties = { new KeyValuePair <string, PropertyInfo>("UseSimpleRigidBodyCollision", new PropertyInfo(PropertyType.BoolProperty)), new KeyValuePair <string, PropertyInfo>("UseSimpleLineCollision", new PropertyInfo(PropertyType.BoolProperty)), new KeyValuePair <string, PropertyInfo>("UseSimpleBoxCollision", new PropertyInfo(PropertyType.BoolProperty)), new KeyValuePair <string, PropertyInfo>("ForceDoubleSidedShadowVolumes", new PropertyInfo(PropertyType.BoolProperty)), new KeyValuePair <string, PropertyInfo>("BodySetup", new PropertyInfo(PropertyType.ObjectProperty, "RB_BodySetup")), new KeyValuePair <string, PropertyInfo>("LODDistanceRatio", new PropertyInfo(PropertyType.FloatProperty)), new KeyValuePair <string, PropertyInfo>("LightMapCoordinateIndex", new PropertyInfo(PropertyType.IntProperty)), new KeyValuePair <string, PropertyInfo>("LightMapResolution", new PropertyInfo(PropertyType.IntProperty)), new KeyValuePair <string, PropertyInfo>("SoundCue", new PropertyInfo(PropertyType.ObjectProperty, "SoundCue")), } }; File.WriteAllText(jsonPath, JsonConvert.SerializeObject(new { SequenceObjects, Classes, Structs, Enums }, Formatting.Indented)); }
/// <summary> /// Extracts the mod from the archive. The caller should handle exception that may be thrown. /// </summary> /// <param name="archivePath"></param> /// <param name="outputFolderPath"></param> /// <param name="compressPackages"></param> /// <param name="updateTextCallback"></param> /// <param name="extractingCallback"></param> /// <param name="compressedPackageCallback"></param> /// <param name="testRun"></param> public void ExtractFromArchive(string archivePath, string outputFolderPath, bool compressPackages, Action <string> updateTextCallback = null, Action <DetailedProgressEventArgs> extractingCallback = null, Action <string, int, int> compressedPackageCallback = null, bool testRun = false, Stream archiveStream = null) { if (!IsInArchive) { throw new Exception(@"Cannot extract a mod that is not part of an archive."); } if (archiveStream == null && !File.Exists(archivePath)) { throw new Exception(M3L.GetString(M3L.string_interp_theArchiveFileArchivePathIsNoLongerAvailable, archivePath)); } compressPackages &= Game >= MEGame.ME2; SevenZipExtractor archive; var isExe = archivePath.EndsWith(@".exe", StringComparison.InvariantCultureIgnoreCase); bool closeStreamOnFinish = true; if (archiveStream != null) { archive = isExe ? new SevenZipExtractor(archiveStream, InArchiveFormat.Nsis) : new SevenZipExtractor(archiveStream); closeStreamOnFinish = false; } else { archive = isExe ? new SevenZipExtractor(archivePath, InArchiveFormat.Nsis) : new SevenZipExtractor(archivePath); } var fileIndicesToExtract = new List <int>(); var filePathsToExtractTESTONLY = new List <string>(); var referencedFiles = GetAllRelativeReferences(!IsVirtualized, archive); if (isExe) { //remap to mod root. Not entirely sure if this needs to be done for sub mods? referencedFiles = Enumerable.Select <string, string>(referencedFiles, x => FilesystemInterposer.PathCombine(IsInArchive, ModPath, x)).ToList(); //remap to in-archive paths so they match entry paths } foreach (var info in archive.ArchiveFileData) { if (!info.IsDirectory && (ModPath == "" || info.FileName.Contains((string)ModPath))) { var relativedName = isExe ? info.FileName : info.FileName.Substring(ModPath.Length).TrimStart('\\'); if (referencedFiles.Contains(relativedName)) { Log.Information(@"Adding file to extraction list: " + info.FileName); fileIndicesToExtract.Add(info.Index); filePathsToExtractTESTONLY.Add(relativedName); } } } void archiveExtractionProgress(object?sender, DetailedProgressEventArgs args) { extractingCallback?.Invoke(args); } archive.Progressing += archiveExtractionProgress; string outputFilePathMapping(ArchiveFileInfo entryInfo) { Log.Information(@"Mapping extraction target for " + entryInfo.FileName); string entryPath = entryInfo.FileName; if (ExeExtractionTransform != null && Enumerable.Any <(int index, string outfile)>(ExeExtractionTransform.PatchRedirects, x => x.index == entryInfo.Index)) { Log.Information(@"Extracting vpatch file at index " + entryInfo.Index); return(Path.Combine(Utilities.GetVPatchRedirectsFolder(), Enumerable.First <(int index, string outfile)>(ExeExtractionTransform.PatchRedirects, x => x.index == entryInfo.Index).outfile)); } if (ExeExtractionTransform != null && Enumerable.Any <int>(ExeExtractionTransform.NoExtractIndexes, x => x == entryInfo.Index)) { Log.Information(@"Extracting file to trash (not used): " + entryPath); return(Path.Combine(Utilities.GetTempPath(), @"Trash", @"trashfile")); } if (ExeExtractionTransform != null && Enumerable.Any <(int index, string outfile)>(ExeExtractionTransform.AlternateRedirects, x => x.index == entryInfo.Index)) { var outfile = Enumerable.First <(int index, string outfile)>(ExeExtractionTransform.AlternateRedirects, x => x.index == entryInfo.Index).outfile; Log.Information($@"Extracting file with redirection: {entryPath} -> {outfile}"); return(Path.Combine(outputFolderPath, outfile)); } //Archive path might start with a \. Substring may return value that start with a \ var subModPath = entryPath /*.TrimStart('\\')*/.Substring(ModPath.Length).TrimStart('\\'); var path = Path.Combine(outputFolderPath, subModPath); //Debug.WriteLine("remapping output: " + entryPath + " -> " + path); return(path); } if (compressPackages) { compressionQueue = new BlockingCollection <string>(); } int numberOfPackagesToCompress = Enumerable.Count <string>(referencedFiles, x => StringExtensions.RepresentsPackageFilePath(x)); int compressedPackageCount = 0; NamedBackgroundWorker compressionThread; if (compressPackages) { compressionThread = new NamedBackgroundWorker(@"ImportingCompressionThread"); compressionThread.DoWork += (a, b) => { try { while (true) { var package = compressionQueue.Take(); var p = MEPackageHandler.OpenMEPackage(package); bool shouldNotCompress = Game == MEGame.ME1; if (!shouldNotCompress) { //updateTextCallback?.Invoke(M3L.GetString(M3L.string_interp_compressingX, Path.GetFileName(package))); FileInfo fileInfo = new FileInfo(package); var created = fileInfo.CreationTime; //File Creation var lastmodified = fileInfo.LastWriteTime; //File Modification compressedPackageCallback?.Invoke(M3L.GetString(M3L.string_interp_compressingX, Path.GetFileName(package)), compressedPackageCount, numberOfPackagesToCompress); Log.Information(@"Compressing package: " + package); p.Save(compress: true); File.SetCreationTime(package, created); File.SetLastWriteTime(package, lastmodified); } else { Log.Information(@"Skipping compression for ME1 package file: " + package); } Interlocked.Increment(ref compressedPackageCount); compressedPackageCallback?.Invoke(M3L.GetString(M3L.string_interp_compressedX, Path.GetFileName(package)), compressedPackageCount, numberOfPackagesToCompress); } } catch (InvalidOperationException) { //Done. lock (compressionCompletedSignaler) { Monitor.Pulse(compressionCompletedSignaler); } } }; compressionThread.RunWorkerAsync(); } void compressPackage(object?sender, FileInfoEventArgs args) { if (compressPackages) { var fToCompress = outputFilePathMapping(args.FileInfo); if (fToCompress.RepresentsPackageFilePath()) { //Debug.WriteLine("Adding to blocking queue"); compressionQueue.TryAdd(fToCompress); } } } archive.FileExtractionFinished += compressPackage; if (!testRun) { Log.Information(@"Extracting files..."); archive.ExtractFiles(outputFolderPath, outputFilePathMapping, fileIndicesToExtract.ToArray()); } else { // test run mode // exes can have duplicate filenames but different indexes so we must check for those here. if (fileIndicesToExtract.Count != referencedFiles.Count && filePathsToExtractTESTONLY.Distinct().ToList().Count != referencedFiles.Count) { throw new Exception(@"The amount of referenced files does not match the amount of files that are going to be extracted!"); } } Log.Information(@"File extraction completed."); archive.Progressing -= archiveExtractionProgress; compressionQueue?.CompleteAdding(); if (compressPackages && numberOfPackagesToCompress > 0 && numberOfPackagesToCompress > compressedPackageCount) { Log.Information(@"Waiting for compression of packages to complete."); while (!compressionQueue.IsCompleted) { lock (compressionCompletedSignaler) { Monitor.Wait(compressionCompletedSignaler); } } Log.Information(@"Package compression has completed."); } archive.FileExtractionFinished -= compressPackage; ModPath = outputFolderPath; if (IsVirtualized) { var parser = new IniDataParser().Parse(VirtualizedIniText); parser[@"ModInfo"][@"modver"] = ModVersionString; //In event relay service resolved this if (!testRun) { File.WriteAllText(Path.Combine(ModPath, @"moddesc.ini"), parser.ToString()); } IsVirtualized = false; //no longer virtualized } if (ExeExtractionTransform != null) { if (EnumerableExtensions.Any <ModArchiveImporter.ExeTransform.VPatchDirective>(ExeExtractionTransform.VPatches)) { // MEHEM uses Vpatching for its alternates. var vpat = Utilities.GetCachedExecutablePath(@"vpat.exe"); if (!testRun) { Utilities.ExtractInternalFile(@"MassEffectModManagerCore.modmanager.executables.vpat.exe", vpat, true); } //Handle VPatching foreach (var transform in ExeExtractionTransform.VPatches) { var patchfile = Path.Combine(Utilities.GetVPatchRedirectsFolder(), transform.patchfile); var inputfile = Path.Combine(ModPath, transform.inputfile); var outputfile = Path.Combine(ModPath, transform.outputfile); var args = $"\"{patchfile}\" \"{inputfile}\" \"{outputfile}\""; //do not localize if (!testRun) { Directory.CreateDirectory(Directory.GetParent(outputfile).FullName); //ensure output directory exists as vpatch will not make one. } Log.Information($@"VPatching file into alternate: {inputfile} to {outputfile}"); updateTextCallback?.Invoke(M3L.GetString(M3L.string_interp_vPatchingIntoAlternate, Path.GetFileName(inputfile))); if (!testRun) { Utilities.RunProcess(vpat, args, true, false, false, true); } } } //Handle copyfile foreach (var copyfile in ExeExtractionTransform.CopyFiles) { string srcfile = Path.Combine(ModPath, copyfile.inputfile); string destfile = Path.Combine(ModPath, copyfile.outputfile); Log.Information($@"Applying transform copyfile: {srcfile} -> {destfile}"); if (!testRun) { File.Copy(srcfile, destfile, true); } } if (ExeExtractionTransform.PostTransformModdesc != null) { //fetch online moddesc for this mod. Log.Information(@"Fetching post-transform third party moddesc."); var moddesc = OnlineContent.FetchThirdPartyModdesc(ExeExtractionTransform.PostTransformModdesc); if (!testRun) { File.WriteAllText(Path.Combine(ModPath, @"moddesc.ini"), moddesc); } } } //int packagesCompressed = 0; //if (compressPackages) //{ // var packages = Utilities.GetPackagesInDirectory(ModPath, true); // extractingCallback?.Invoke(new ProgressEventArgs((byte)(packagesCompressed * 100.0 / packages.Count), 0)); // foreach (var package in packages) // { // updateTextCallback?.Invoke(M3L.GetString(M3L.string_interp_compressingX, Path.GetFileName(package))); // Log.Information("Compressing package: " + package); // var p = MEPackageHandler.OpenMEPackage(package); // p.save(true); // packagesCompressed++; // extractingCallback?.Invoke(new ProgressEventArgs((byte)(packagesCompressed * 100.0 / packages.Count), 0)); // } //} if (closeStreamOnFinish) { archive?.Dispose(); } else { archive?.DisposeObjectOnly(); } }
public static PropertyCollection getDefaultStructValue(string className, bool stripTransients) { bool isImmutable = UnrealObjectInfo.IsImmutable(className, MEGame.ME2); if (Structs.ContainsKey(className)) { ClassInfo info = Structs[className]; try { PropertyCollection structProps = new PropertyCollection(); ClassInfo tempInfo = info; while (tempInfo != null) { foreach ((string propName, PropertyInfo propInfo) in tempInfo.properties) { if (stripTransients && propInfo.Transient) { continue; } if (getDefaultProperty(propName, propInfo, stripTransients, isImmutable) is Property uProp) { structProps.Add(uProp); } } if (!Structs.TryGetValue(tempInfo.baseClass, out tempInfo)) { tempInfo = null; } } structProps.Add(new NoneProperty()); string filepath = null; if (ME2Directory.BioGamePath != null) { filepath = Path.Combine(ME2Directory.BioGamePath, info.pccPath); } Stream loadStream = null; if (File.Exists(info.pccPath)) { filepath = info.pccPath; loadStream = new MemoryStream(File.ReadAllBytes(info.pccPath)); } else if (info.pccPath == UnrealObjectInfo.Me3ExplorerCustomNativeAdditionsName) { filepath = "GAMERESOURCES_ME2"; loadStream = Utilities.LoadFileFromCompressedResource("GameResources.zip", CoreLib.CustomResourceFileName(MEGame.ME2)); } else if (filepath != null && File.Exists(filepath)) { loadStream = new MemoryStream(File.ReadAllBytes(filepath)); } #if AZURE else if (MiniGameFilesPath != null && File.Exists(Path.Combine(MiniGameFilesPath, info.pccPath))) { // Load from test minigame folder. This is only really useful on azure where we don't have access to // games filepath = Path.Combine(MiniGameFilesPath, info.pccPath); loadStream = new MemoryStream(File.ReadAllBytes(filepath)); } #endif if (loadStream != null) { using (IMEPackage importPCC = MEPackageHandler.OpenMEPackageFromStream(loadStream, filepath, useSharedPackageCache: true)) { var exportToRead = importPCC.GetUExport(info.exportIndex); byte[] buff = exportToRead.Data.Skip(0x30).ToArray(); PropertyCollection defaults = PropertyCollection.ReadProps(exportToRead, new MemoryStream(buff), className); foreach (var prop in defaults) { structProps.TryReplaceProp(prop); } } } return(structProps); } catch { return(null); } } return(null); }
public void CreateLiveEditFile() { string filePath = LiveFaceFxEditorFilePath; File.Copy(Path.Combine(App.ExecFolder, "ME3EmptyLevel.pcc"), filePath); LiveFile = MEPackageHandler.OpenMEPackage(filePath); for (int i = 0; i < LiveFile.Names.Count; i++) { if (LiveFile.Names[i].Equals("ME3EmptyLevel")) { LiveFile.replaceName(i, Path.GetFileNameWithoutExtension(filePath)); } } var packguid = Guid.NewGuid(); var package = LiveFile.GetUExport(1); package.PackageGUID = packguid; LiveFile.PackageGuid = packguid; EntryImporter.ImportAndRelinkEntries(EntryImporter.PortingOption.CloneAllDependencies, SourceAnimSet.Export.Parent, LiveFile, null, true, out _); var gender = SourceAnimSet.Export.ObjectNameString.Last() switch { 'F' => FaceFXGender.Female, 'M' => FaceFXGender.Male, _ => FaceFXGender.NonSpkr }; ActorTag = null; try { if (gender is not FaceFXGender.NonSpkr) { bool isFemale = gender is FaceFXGender.Female; var bioConv = LiveFile.Exports.First(exp => exp.Class.ObjectName == "BioConversation"); var LiveFileAnimSet = LiveFile.FindExport(SourceAnimSet.Export.InstancedFullPath); var propName = isFemale ? "m_aMaleFaceSets" : "m_aFemaleFaceSets"; int idx = bioConv.GetProperty <ArrayProperty <ObjectProperty> >(propName).FindIndex(objProp => objProp.Value == LiveFileAnimSet.UIndex); if (idx is 0) { //player ActorTag = $"Player_{(isFemale ? 'F' : 'M')}"; IEntry ent; using (IMEPackage soldierFile = MEPackageHandler.OpenME3Package(Path.Combine(ME3Directory.CookedPCPath, "SFXCharacterClass_Soldier.pcc"))) { EntryImporter.ImportAndRelinkEntries(EntryImporter.PortingOption.CloneAllDependencies, soldierFile.GetUExport(isFemale ? 10327 : 10330), LiveFile, LiveFile.GetUExport(3), true, out ent); } ExportEntry actor = (ExportEntry)ent; LiveFile.AddToLevelActorsIfNotThere(actor); actor.WriteProperty(new NameProperty(ActorTag, "Tag")); using (IMEPackage clothingFile = MEPackageHandler.OpenME3Package(Path.Combine(ME3Directory.CookedPCPath, $"BIOG_HM{(isFemale ? "F" : "M")}_ARM_CTH_R.pcc"))) { var clothingPackage = EntryImporter.GetOrAddCrossImportOrPackage("CTHa", clothingFile, LiveFile); EntryImporter.ImportAndRelinkEntries(EntryImporter.PortingOption.CloneAllDependencies, clothingFile.GetUExport(isFemale ? 1625 : 1966), LiveFile, clothingPackage, true, out ent); } ExportEntry bodyComponent = LiveFile.GetUExport(actor.GetProperty <ObjectProperty>("BodyMesh").Value); bodyComponent.WriteProperty(new ObjectProperty(ent.UIndex, "SkeletalMesh")); } else if (idx is 1) { //owner using IMEPackage parentFile = getParentFile(); foreach (ExportEntry export in parentFile.Exports.Where(exp => exp.ClassName is "SFXSeqAct_StartConversation" or "SFXSeqAct_StartAmbientConv")) { if (export.GetProperty <ObjectProperty>("Conv") is ObjectProperty convProp && parentFile.TryGetImport(convProp.Value, out var convImport) && convImport.ObjectName == bioConv.ObjectName) { ExportEntry seqVar = parentFile.GetUExport(export.GetProperty <ArrayProperty <StructProperty> >("VariableLinks")[0].GetProp <ArrayProperty <ObjectProperty> >("LinkedVariables")[0].Value); if (seqVar.ClassName == "BioSeqVar_ObjectFindByTag") { ActorTag = seqVar.GetProperty <NameProperty>("m_sObjectTagToFind").Value; if (!ActorTag.StartsWith("hench_", StringComparison.OrdinalIgnoreCase)) { importTaggedActor(parentFile); } } else { EntryImporter.ImportAndRelinkEntries(EntryImporter.PortingOption.CloneAllDependencies, parentFile.GetUExport(seqVar.GetProperty <ObjectProperty>("ObjValue").Value), LiveFile, LiveFile.GetUExport(3), true, out var ent); ExportEntry actor = (ExportEntry)ent; LiveFile.AddToLevelActorsIfNotThere(actor); ActorTag = actor.GetProperty <NameProperty>("Tag")?.Value.Name; if (ActorTag is null) { ActorTag = "ConvoOwner"; actor.WriteProperty(new NameProperty(ActorTag, "Tag")); } } break; } } } else { ActorTag = bioConv.GetProperty <ArrayProperty <NameProperty> >("m_aSpeakerList")[idx - 2].Value; if (!ActorTag.StartsWith("hench_", StringComparison.OrdinalIgnoreCase)) { using IMEPackage parentFile = getParentFile(); importTaggedActor(parentFile); } } } else { //find nonspkr linkage somehow } }
//call this method to regenerate ME2ObjectInfo.json //Takes a long time (10 minutes maybe?). Application will be completely unresponsive during that time. public static void generateInfo(string outpath) { var NewClasses = new Dictionary <string, ClassInfo>(); var NewStructs = new Dictionary <string, ClassInfo>(); var NewEnums = new Dictionary <string, List <NameReference> >(); var NewSequenceObjects = new Dictionary <string, SequenceObjectInfo>(); foreach (string filePath in MELoadedFiles.GetOfficialFiles(MEGame.ME2)) { if (Path.GetExtension(filePath) == ".pcc") { using IMEPackage pcc = MEPackageHandler.OpenME2Package(filePath); for (int j = 1; j <= pcc.ExportCount; j++) { ExportEntry exportEntry = pcc.GetUExport(j); string className = exportEntry.ClassName; if (className == "Enum") { generateEnumValues(exportEntry, NewEnums); } else if (className == "Class") { string objectName = exportEntry.ObjectName; if (!NewClasses.ContainsKey(objectName)) { NewClasses.Add(objectName, generateClassInfo(exportEntry)); } } else if (className == "ScriptStruct") { string objectName = exportEntry.ObjectName; if (!NewStructs.ContainsKey(objectName)) { NewStructs.Add(objectName, generateClassInfo(exportEntry, isStruct: true)); } } else if (exportEntry.IsA("SequenceObject")) { if (!NewSequenceObjects.TryGetValue(className, out SequenceObjectInfo seqObjInfo)) { seqObjInfo = new SequenceObjectInfo(); NewSequenceObjects.Add(className, seqObjInfo); } int objInstanceVersion = exportEntry.GetProperty <IntProperty>("ObjInstanceVersion"); if (objInstanceVersion > seqObjInfo.ObjInstanceVersion) { seqObjInfo.ObjInstanceVersion = objInstanceVersion; } } } //Debug.WriteLine("Releasing " + pcc.FileName); } } //CUSTOM ADDITIONS try { NewClasses.Add("LightMapTexture2D", new ClassInfo { baseClass = "Texture2D", exportIndex = 0, pccPath = UnrealObjectInfo.Me3ExplorerCustomNativeAdditionsName }); NewClasses["StaticMesh"] = new ClassInfo { baseClass = "Object", exportIndex = 0, pccPath = UnrealObjectInfo.Me3ExplorerCustomNativeAdditionsName, properties = { new KeyValuePair <string, PropertyInfo>("UseSimpleRigidBodyCollision", new PropertyInfo(PropertyType.BoolProperty)), new KeyValuePair <string, PropertyInfo>("UseSimpleLineCollision", new PropertyInfo(PropertyType.BoolProperty)), new KeyValuePair <string, PropertyInfo>("UseSimpleBoxCollision", new PropertyInfo(PropertyType.BoolProperty)), new KeyValuePair <string, PropertyInfo>("UseFullPrecisionUVs", new PropertyInfo(PropertyType.BoolProperty)), new KeyValuePair <string, PropertyInfo>("BodySetup", new PropertyInfo(PropertyType.ObjectProperty, "RB_BodySetup")), new KeyValuePair <string, PropertyInfo>("LODDistanceRatio", new PropertyInfo(PropertyType.FloatProperty)), new KeyValuePair <string, PropertyInfo>("LightMapCoordinateIndex", new PropertyInfo(PropertyType.IntProperty)), new KeyValuePair <string, PropertyInfo>("LightMapResolution", new PropertyInfo(PropertyType.IntProperty)), } }; } catch (Exception) { } //SFXPhysicalMaterialDecals missing items ClassInfo sfxpmd = NewClasses["SFXPhysicalMaterialDecals"]; string[] decalComponentArrays = { "HeavyPistol", "AutoPistol", "HandCannon", "SMG", "Shotgun", "HeavyShotgun", "FlakGun", "AssaultRifle", "Needler", "Machinegun", "SniperRifle", "AntiMatRifle", "MassCannon", "ParticleBeam" }; foreach (string decal in decalComponentArrays) { sfxpmd.properties.Add(decal, new PropertyInfo(PropertyType.ArrayProperty, "DecalComponent")); } NewClasses["SFXWeapon"].properties.Add("InstantHitDamageTypes", new PropertyInfo(PropertyType.ArrayProperty, "Class")); File.WriteAllText("ME2ObjectInfo.json", JsonConvert.SerializeObject(new { SequenceObjects = NewSequenceObjects, Classes = NewClasses, Structs = NewStructs, Enums = NewEnums }, Formatting.Indented)); }
public void ExtractFromArchive(string archivePath, string outputFolderPath, bool compressPackages, Action <string> updateTextCallback = null, Action <DetailedProgressEventArgs> extractingCallback = null, Action <string, int, int> compressedPackageCallback = null, bool testRun = false) { if (!IsInArchive) { throw new Exception(@"Cannot extract a mod that is not part of an archive."); } compressPackages &= Game == MEGame.ME3; //ME3 ONLY FOR NOW var isExe = archivePath.EndsWith(@".exe", StringComparison.InvariantCultureIgnoreCase); var archiveFile = isExe ? new SevenZipExtractor(archivePath, InArchiveFormat.Nsis) : new SevenZipExtractor(archivePath); using (archiveFile) { var fileIndicesToExtract = new List <int>(); var referencedFiles = GetAllRelativeReferences(!IsVirtualized, archiveFile); if (isExe) { //remap to mod root. Not entirely sure if this needs to be done for sub mods? referencedFiles = referencedFiles.Select(x => FilesystemInterposer.PathCombine(IsInArchive, ModPath, x)).ToList(); //remap to in-archive paths so they match entry paths } foreach (var info in archiveFile.ArchiveFileData) { if (!info.IsDirectory && (ModPath == "" || info.FileName.Contains(ModPath))) { var relativedName = isExe ? info.FileName : info.FileName.Substring(ModPath.Length).TrimStart('\\'); if (referencedFiles.Contains(relativedName)) { Log.Information(@"Adding file to extraction list: " + info.FileName); fileIndicesToExtract.Add(info.Index); } } } #region old /* * bool fileAdded = false; * //moddesc.ini * if (info.FileName == ModDescPath) * { * //Debug.WriteLine("Add file to extraction list: " + info.FileName); * fileIndicesToExtract.Add(info.Index); * continue; * } * * //Check each job * foreach (ModJob job in InstallationJobs) * { * if (job.Header == ModJob.JobHeader.CUSTOMDLC) * { #region Extract Custom DLC * foreach (var localCustomDLCFolder in job.CustomDLCFolderMapping.Keys) * { * if (info.FileName.StartsWith(FilesystemInterposer.PathCombine(IsInArchive, ModPath, localCustomDLCFolder))) * { * //Debug.WriteLine("Add file to extraction list: " + info.FileName); * fileIndicesToExtract.Add(info.Index); * fileAdded = true; * break; * } * } * * if (fileAdded) break; * * //Alternate files * foreach (var alt in job.AlternateFiles) * { * if (alt.AltFile != null && info.FileName.Equals(FilesystemInterposer.PathCombine(IsInArchive, ModPath, alt.AltFile), StringComparison.InvariantCultureIgnoreCase)) * { * //Debug.WriteLine("Add alternate file to extraction list: " + info.FileName); * fileIndicesToExtract.Add(info.Index); * fileAdded = true; * break; * } * } * * if (fileAdded) break; * * //Alternate DLC * foreach (var alt in job.AlternateDLCs) * { * if (info.FileName.StartsWith(FilesystemInterposer.PathCombine(IsInArchive, ModPath, alt.AlternateDLCFolder), StringComparison.InvariantCultureIgnoreCase)) * { * //Debug.WriteLine("Add alternate dlc file to extraction list: " + info.FileName); * fileIndicesToExtract.Add(info.Index); * fileAdded = true; * break; * } * } * * if (fileAdded) break; * #endregion * } * else * { #region Official headers * * foreach (var inSubDirFile in job.FilesToInstall.Values) * { * var inArchivePath = FilesystemInterposer.PathCombine(IsInArchive, ModPath, job.JobDirectory, inSubDirFile); //keep relative if unpacked mod, otherwise use full in-archive path for extraction * if (info.FileName.Equals(inArchivePath, StringComparison.InvariantCultureIgnoreCase)) * { * //Debug.WriteLine("Add file to extraction list: " + info.FileName); * fileIndicesToExtract.Add(info.Index); * fileAdded = true; * break; * } * } * * if (fileAdded) break; * //Alternate files * foreach (var alt in job.AlternateFiles) * { * if (alt.AltFile != null && info.FileName.Equals(FilesystemInterposer.PathCombine(IsInArchive, ModPath, alt.AltFile), StringComparison.InvariantCultureIgnoreCase)) * { * //Debug.WriteLine("Add alternate file to extraction list: " + info.FileName); * fileIndicesToExtract.Add(info.Index); * fileAdded = true; * break; * } * } * * if (fileAdded) break; * #endregion * } * } * }*/ #endregion archiveFile.Progressing += (sender, args) => { extractingCallback?.Invoke(args); }; string outputFilePathMapping(ArchiveFileInfo entryInfo) { Log.Information(@"Mapping extraction target for " + entryInfo.FileName); string entryPath = entryInfo.FileName; if (ExeExtractionTransform != null && ExeExtractionTransform.PatchRedirects.Any(x => x.index == entryInfo.Index)) { Log.Information(@"Extracting vpatch file at index " + entryInfo.Index); return(Path.Combine(Utilities.GetVPatchRedirectsFolder(), ExeExtractionTransform.PatchRedirects.First(x => x.index == entryInfo.Index).outfile)); } if (ExeExtractionTransform != null && ExeExtractionTransform.NoExtractIndexes.Any(x => x == entryInfo.Index)) { Log.Information(@"Extracting file to trash (not used): " + entryPath); return(Path.Combine(Utilities.GetTempPath(), @"Trash", @"trashfile")); } if (ExeExtractionTransform != null && ExeExtractionTransform.AlternateRedirects.Any(x => x.index == entryInfo.Index)) { var outfile = ExeExtractionTransform.AlternateRedirects.First(x => x.index == entryInfo.Index).outfile; Log.Information($@"Extracting file with redirection: {entryPath} {outfile}"); return(Path.Combine(outputFolderPath, outfile)); } //Archive path might start with a \. Substring may return value that start with a \ var subModPath = entryPath /*.TrimStart('\\')*/.Substring(ModPath.Length).TrimStart('\\'); var path = Path.Combine(outputFolderPath, subModPath); //Debug.WriteLine("remapping output: " + entryPath + " -> " + path); return(path); } if (compressPackages) { compressionQueue = new BlockingCollection <string>(); } int numberOfPackagesToCompress = referencedFiles.Count(x => x.RepresentsPackageFilePath()); int compressedPackageCount = 0; NamedBackgroundWorker compressionThread; if (compressPackages) { compressionThread = new NamedBackgroundWorker(@"ImportingCompressionThread"); compressionThread.DoWork += (a, b) => { try { while (true) { var package = compressionQueue.Take(); //updateTextCallback?.Invoke(M3L.GetString(M3L.string_interp_compressingX, Path.GetFileName(package))); var p = MEPackageHandler.OpenMEPackage(package); //Check if any compressed textures. bool shouldNotCompress = false; foreach (var texture in p.Exports.Where(x => x.IsTexture())) { var storageType = Texture2D.GetTopMipStorageType(texture); shouldNotCompress |= storageType == ME3Explorer.Unreal.StorageTypes.pccLZO || storageType == ME3Explorer.Unreal.StorageTypes.pccZlib; if (!shouldNotCompress) { break; } } if (!shouldNotCompress) { compressedPackageCallback?.Invoke(M3L.GetString(M3L.string_interp_compressingX, Path.GetFileName(package)), compressedPackageCount, numberOfPackagesToCompress); Log.Information(@"Compressing package: " + package); p.save(true); } else { Log.Information(@"Not compressing package due to file containing compressed textures: " + package); } Interlocked.Increment(ref compressedPackageCount); compressedPackageCallback?.Invoke(M3L.GetString(M3L.string_interp_compressedX, Path.GetFileName(package)), compressedPackageCount, numberOfPackagesToCompress); } } catch (InvalidOperationException) { //Done. lock (compressionCompletedSignaler) { Monitor.Pulse(compressionCompletedSignaler); } } }; compressionThread.RunWorkerAsync(); } archiveFile.FileExtractionFinished += (sender, args) => { if (compressPackages) { var fToCompress = outputFilePathMapping(args.FileInfo); if (fToCompress.RepresentsPackageFilePath()) { //Debug.WriteLine("Adding to blocking queue"); compressionQueue.TryAdd(fToCompress); } } }; if (!testRun) { Log.Information(@"Extracting files..."); archiveFile.ExtractFiles(outputFolderPath, outputFilePathMapping, fileIndicesToExtract.ToArray()); } else { //test run mode if (fileIndicesToExtract.Count != referencedFiles.Count) { throw new Exception("The amount of referenced files does not match the amount of files that are going to be extracted!"); } } Log.Information(@"File extraction completed."); compressionQueue?.CompleteAdding(); if (compressPackages && numberOfPackagesToCompress > 0 && numberOfPackagesToCompress > compressedPackageCount) { Log.Information(@"Waiting for compression of packages to complete."); while (!compressionQueue.IsCompleted) { lock (compressionCompletedSignaler) { Monitor.Wait(compressionCompletedSignaler); } } Log.Information(@"Package compression has completed."); } ModPath = outputFolderPath; if (IsVirtualized) { var parser = new IniDataParser().Parse(VirtualizedIniText); parser[@"ModInfo"][@"modver"] = ModVersionString; //In event relay service resolved this if (!testRun) { File.WriteAllText(Path.Combine(ModPath, @"moddesc.ini"), parser.ToString()); } IsVirtualized = false; //no longer virtualized } if (ExeExtractionTransform != null) { if (ExeExtractionTransform.VPatches.Any()) { var vpat = Utilities.GetCachedExecutablePath(@"vpat.exe"); if (!testRun) { Utilities.ExtractInternalFile(@"MassEffectModManagerCore.modmanager.executables.vpat.exe", vpat, true); } //Handle VPatching foreach (var transform in ExeExtractionTransform.VPatches) { var patchfile = Path.Combine(Utilities.GetVPatchRedirectsFolder(), transform.patchfile); var inputfile = Path.Combine(ModPath, transform.inputfile); var outputfile = Path.Combine(ModPath, transform.outputfile); var args = $"\"{patchfile}\" \"{inputfile}\" \"{outputfile}\""; //do not localize if (!testRun) { Directory.CreateDirectory(Directory.GetParent(outputfile).FullName); //ensure output directory exists as vpatch will not make one. } Log.Information($@"VPatching file into alternate: {inputfile} to {outputfile}"); updateTextCallback?.Invoke(M3L.GetString(M3L.string_interp_vPatchingIntoAlternate, Path.GetFileName(inputfile))); if (!testRun) { Utilities.RunProcess(vpat, args, true, false, false, true); } } } //Handle copyfile foreach (var copyfile in ExeExtractionTransform.CopyFiles) { string srcfile = Path.Combine(ModPath, copyfile.inputfile); string destfile = Path.Combine(ModPath, copyfile.outputfile); Log.Information($@"Applying transform copyfile: {srcfile} -> {destfile}"); if (!testRun) { File.Copy(srcfile, destfile, true); } } if (ExeExtractionTransform.PostTransformModdesc != null) { //fetch online moddesc for this mod. Log.Information(@"Fetching post-transform third party moddesc."); var moddesc = OnlineContent.FetchThirdPartyModdesc(ExeExtractionTransform.PostTransformModdesc); if (!testRun) { File.WriteAllText(Path.Combine(ModPath, @"moddesc.ini"), moddesc); } } } //int packagesCompressed = 0; //if (compressPackages) //{ // var packages = Utilities.GetPackagesInDirectory(ModPath, true); // extractingCallback?.Invoke(new ProgressEventArgs((byte)(packagesCompressed * 100.0 / packages.Count), 0)); // foreach (var package in packages) // { // updateTextCallback?.Invoke(M3L.GetString(M3L.string_interp_compressingX, Path.GetFileName(package))); // Log.Information("Compressing package: " + package); // var p = MEPackageHandler.OpenMEPackage(package); // p.save(true); // packagesCompressed++; // extractingCallback?.Invoke(new ProgressEventArgs((byte)(packagesCompressed * 100.0 / packages.Count), 0)); // } //} } }
public static byte[] getDefaultClassValue(ME3Package pcc, string className, bool fullProps = false) { if (Structs.ContainsKey(className)) { bool immutable = isImmutable(className); ClassInfo info = Structs[className]; try { string filepath = (Path.Combine(ME3Directory.gamePath, @"BIOGame\" + info.pccPath)); if (File.Exists(info.pccPath)) { filepath = info.pccPath; //Used for dynamic lookup } using (ME3Package importPCC = MEPackageHandler.OpenME3Package(filepath)) { byte[] buff; //Plane and CoverReference inherit from other structs, meaning they don't have default values (who knows why) //thus, I have hardcoded what those default values should be if (className == "Plane") { buff = PlaneDefault; } else if (className == "CoverReference") { buff = CoverReferenceDefault; } else { buff = importPCC.Exports[info.exportIndex].Data.Skip(0x24).ToArray(); } List <PropertyReader.Property> Props = PropertyReader.ReadProp(importPCC, buff, 0); MemoryStream m = new MemoryStream(); foreach (PropertyReader.Property p in Props) { string propName = importPCC.getNameEntry(p.Name); //check if property is transient, if so, skip (neither of the structs that inherit have transient props) if (info.properties.ContainsKey(propName) || propName == "None" || info.baseClass != "Class") { if (immutable && !fullProps) { PropertyReader.ImportImmutableProperty(pcc, importPCC, p, className, m, true); } else { PropertyReader.ImportProperty(pcc, importPCC, p, className, m, true); } } } return(m.ToArray()); } } catch (Exception) { return(null); } } else if (Classes.ContainsKey(className)) { ClassInfo info = Structs[className]; try { string filepath = (Path.Combine(ME3Directory.gamePath, @"BIOGame\" + info.pccPath)); if (File.Exists(info.pccPath)) { filepath = info.pccPath; //Used for dynamic lookup } using (ME3Package importPCC = MEPackageHandler.OpenME3Package(filepath)) { IExportEntry entry = pcc.Exports[info.exportIndex + 1]; List <PropertyReader.Property> Props = PropertyReader.getPropList(entry); MemoryStream m = new MemoryStream(entry.DataSize - 4); foreach (PropertyReader.Property p in Props) { if (!info.properties.ContainsKey(importPCC.getNameEntry(p.Name))) { //property is transient continue; } PropertyReader.ImportProperty(pcc, importPCC, p, className, m); } return(m.ToArray()); } } catch (Exception) { return(null); } } return(null); }
private static void RandomizeAfterlifeShepDance() { var denDanceF = MERFileSystem.GetPackageFile(@"BioD_OmgHub_230DenDance.pcc"); if (denDanceF != null) { var loungeP = MEPackageHandler.OpenMEPackage(denDanceF); var sequence = loungeP.GetUExport(3924); MERPackageCache cache = new MERPackageCache(); List <InterpTools.InterpData> interpDatas = new List <InterpTools.InterpData>(); var interp1 = loungeP.GetUExport(3813); // Make 2 additional dance options by cloning the interp and the data tree var interp2 = MERSeqTools.CloneBasicSequenceObject(interp1); var interp3 = MERSeqTools.CloneBasicSequenceObject(interp1); // Clone the interp data for attaching to 2 and 3 var interpData1 = loungeP.GetUExport(1174); var interpData2 = EntryCloner.CloneTree(interpData1); var interpData3 = EntryCloner.CloneTree(interpData2); KismetHelper.AddObjectToSequence(interpData2, sequence); KismetHelper.AddObjectToSequence(interpData3, sequence); // Load ID for randomization interpDatas.Add(new InterpTools.InterpData(interpData1)); interpDatas.Add(new InterpTools.InterpData(interpData2)); interpDatas.Add(new InterpTools.InterpData(interpData3)); // Chance the data for interp2/3 var id2 = SeqTools.GetVariableLinksOfNode(interp2); id2[0].LinkedNodes[0] = interpData2; SeqTools.WriteVariableLinksToNode(interp2, id2); var id3 = SeqTools.GetVariableLinksOfNode(interp3); id3[0].LinkedNodes[0] = interpData3; SeqTools.WriteVariableLinksToNode(interp3, id3); // Add additional finished states for fadetoblack when done KismetHelper.CreateOutputLink(loungeP.GetUExport(958), "Finished", interp2, 2); KismetHelper.CreateOutputLink(loungeP.GetUExport(958), "Finished", interp3, 2); // Link up the random choice it makes var randSw = MERSeqTools.InstallRandomSwitchIntoSequence(sequence, 3); KismetHelper.CreateOutputLink(randSw, "Link 1", interp1); KismetHelper.CreateOutputLink(randSw, "Link 2", interp2); KismetHelper.CreateOutputLink(randSw, "Link 3", interp3); // Break the original output to start the interp, repoint it's output to the switch instead var sgm = loungeP.GetUExport(1003); //set gesture mode KismetHelper.RemoveOutputLinks(sgm); KismetHelper.CreateOutputLink(sgm, "Done", loungeP.GetUExport(960)); KismetHelper.CreateOutputLink(sgm, "Done", randSw); // Now install the dances foreach (var id in interpDatas) { var danceTrack = id.InterpGroups[0].Tracks[0]; OmegaHub.InstallShepardDanceGesture(danceTrack.Export, cache); } MERFileSystem.SavePackage(loungeP); } }
private void CompactAFCBackgroundThread(object sender, DoWorkEventArgs e) { var arguments = (Tuple <string, string>)e.Argument; string path = arguments.Item1; string NewAFCBaseName = arguments.Item2; string[] pccFiles = System.IO.Directory.GetFiles(path, "*.pcc"); string[] afcFiles = System.IO.Directory.GetFiles(path, "*.afc").Select(x => System.IO.Path.GetFileNameWithoutExtension(x).ToLower()).ToArray(); var ReferencedAFCAudio = new List <Tuple <string, int, int> >(); int i = 1; foreach (string pccPath in pccFiles) { BusyText = "Finding all referenced audio (" + i + "/" + pccFiles.Count() + ")"; using (IMEPackage pack = MEPackageHandler.OpenMEPackage(pccPath)) { List <IExportEntry> wwiseStreamExports = pack.Exports.Where(x => x.ClassName == "WwiseStream").ToList(); foreach (IExportEntry exp in wwiseStreamExports) { var afcNameProp = exp.GetProperty <NameProperty>("Filename"); if (afcNameProp != null && afcFiles.Contains(afcNameProp.ToString().ToLower())) { string afcName = afcNameProp.ToString().ToLower(); int readPos = exp.Data.Length - 8; int audioSize = BitConverter.ToInt32(exp.Data, exp.Data.Length - 8); int audioOffset = BitConverter.ToInt32(exp.Data, exp.Data.Length - 4); ReferencedAFCAudio.Add(new Tuple <string, int, int>(afcName, audioSize, audioOffset)); } } } i++; } ReferencedAFCAudio = ReferencedAFCAudio.Distinct().ToList(); //extract referenced audio BusyText = "Extracting referenced audio"; var extractedAudioMap = new Dictionary <Tuple <string, int, int>, byte[]>(); i = 1; foreach (Tuple <string, int, int> reference in ReferencedAFCAudio) { BusyText = "Extracting referenced audio (" + i + " / " + ReferencedAFCAudio.Count() + ")"; string afcPath = System.IO.Path.Combine(path, reference.Item1 + ".afc"); FileStream stream = new FileStream(afcPath, FileMode.Open, FileAccess.Read); stream.Seek(reference.Item3, SeekOrigin.Begin); byte[] extractedAudio = new byte[reference.Item2]; stream.Read(extractedAudio, 0, reference.Item2); stream.Close(); extractedAudioMap[reference] = extractedAudio; i++; } var newAFCEntryPointMap = new Dictionary <Tuple <string, int, int>, long>(); i = 1; string newAfcPath = System.IO.Path.Combine(path, NewAFCBaseName + ".afc"); if (File.Exists(newAfcPath)) { File.Delete(newAfcPath); } FileStream newAFCStream = new FileStream(newAfcPath, FileMode.CreateNew, FileAccess.Write); foreach (Tuple <string, int, int> reference in ReferencedAFCAudio) { BusyText = "Building new AFC file (" + i + " / " + ReferencedAFCAudio.Count() + ")"; newAFCEntryPointMap[reference] = newAFCStream.Position; //save entry point in map newAFCStream.Write(extractedAudioMap[reference], 0, extractedAudioMap[reference].Length); i++; } newAFCStream.Close(); extractedAudioMap = null; //clean out ram on next GC i = 1; foreach (string pccPath in pccFiles) { BusyText = "Updating audio references (" + i + "/" + pccFiles.Count() + ")"; using (IMEPackage pack = MEPackageHandler.OpenMEPackage(pccPath)) { bool shouldSave = false; List <IExportEntry> wwiseStreamExports = pack.Exports.Where(x => x.ClassName == "WwiseStream").ToList(); foreach (IExportEntry exp in wwiseStreamExports) { var afcNameProp = exp.GetProperty <NameProperty>("Filename"); if (afcNameProp != null && afcFiles.Contains(afcNameProp.ToString().ToLower())) { string afcName = afcNameProp.ToString().ToLower(); int readPos = exp.Data.Length - 8; int audioSize = BitConverter.ToInt32(exp.Data, exp.Data.Length - 8); int audioOffset = BitConverter.ToInt32(exp.Data, exp.Data.Length - 4); var key = new Tuple <string, int, int>(afcName, audioSize, audioOffset); long newOffset; if (newAFCEntryPointMap.TryGetValue(key, out newOffset)) { //its a match afcNameProp.Value = NewAFCBaseName; Application.Current.Dispatcher.Invoke(() => { exp.WriteProperty(afcNameProp); byte[] newData = exp.Data; Buffer.BlockCopy(BitConverter.GetBytes((int)newOffset), 0, newData, newData.Length - 4, 4); //update AFC audio offset exp.Data = newData; if (exp.DataChanged) { //don't mark for saving if the data didn't acutally change (e.g. trying to compact a compacted AFC). shouldSave = true; } }); } } } if (shouldSave) { Application.Current.Dispatcher.Invoke( () => { // Must run on the UI thread or the tool interop will throw an exception // because we are on a background thread. pack.save(); }); } } i++; } BusyText = "Rebuild complete"; System.Threading.Thread.Sleep(2000); }
private void treeView1_AfterSelect(object sender, TreeViewEventArgs e) { TreeNode t = treeView1.SelectedNode; if (DisplayStyle == 0) { if (t.Parent == null || t.Name == "") { return; } Renderer.STM?.pcc.Dispose(); Renderer.STM = null; Renderer.SKM?.Owner.Dispose(); Renderer.SKM = null; Renderer.CamDistance = 10; Renderer.CamOffset = new Microsoft.DirectX.Vector3(0, 0, 0); try { int i = 0; if (Int32.TryParse(t.Name, out i)) { EntryStruct en = Entries[i]; if (!en.isDLC) { ME3Package pcc = MEPackageHandler.OpenME3Package(ME3Directory.cookedPath + en.Filename); if (en.isSkeletal) { Renderer.SKM = new SkeletalMesh(pcc, en.Index); Renderer.CamDistance = Renderer.SKM.Bounding.r * 2.0f; Renderer.CamOffset = Renderer.SKM.Bounding.origin; treeView2.Nodes.Clear(); if (previewWithTreeToolStripMenuItem.Checked) { treeView2.Visible = false; Application.DoEvents(); treeView2.Nodes.Add(Renderer.SKM.ToTree()); treeView2.Visible = true; } } else { } } else { string loc = Path.GetDirectoryName(Application.ExecutablePath) + "\\exec\\"; string dirDLC = ME3Directory.DLCPath; dirDLC += en.DLCName; dirDLC += "\\CookedPCConsole\\Default.sfar"; DLCBase dlc = new DLCBase(dirDLC); foreach (sfarFile file in dlc.fileList) { try { string filename = Path.GetFileName(file.fileName); if (Path.GetExtension(filename).ToLower().EndsWith(".pcc") && filename == en.Filename) { using (Stream input = File.OpenRead(dirDLC), output = File.Create(loc + filename)) { AmaroK86.MassEffect3.DLCUnpack.DecompressEntry(file, input, output, dlc.CompressionScheme); } if (File.Exists(loc + filename)) { try { ME3Package pcc = MEPackageHandler.OpenME3Package(loc + filename); if (en.isSkeletal) { Renderer.SKM = new SkeletalMesh(pcc, en.Index); Renderer.CamDistance = Renderer.SKM.Bounding.r * 2.0f; Renderer.CamOffset = Renderer.SKM.Bounding.origin; treeView2.Nodes.Clear(); if (previewWithTreeToolStripMenuItem.Checked) { treeView2.Visible = false; Application.DoEvents(); treeView2.Nodes.Add(Renderer.SKM.ToTree()); treeView2.Visible = true; } } else { } } catch (Exception) { } File.Delete(loc + filename); } } } catch (Exception) { } } } } } catch (Exception ex) { MessageBox.Show(ex.ToString()); } } }
private void CheckModForAFCCompactability(DeploymentChecklistItem item) { bool hasError = false; item.HasError = false; item.ItemText = "Checking audio references in mod"; var referencedFiles = ModBeingDeployed.GetAllRelativeReferences().Select(x => Path.Combine(ModBeingDeployed.ModPath, x)).ToList(); int numChecked = 0; GameTarget validationTarget = mainWindow.InstallationTargets.FirstOrDefault(x => x.Game == ModBeingDeployed.Game); List <string> gameFiles = MEDirectories.EnumerateGameFiles(validationTarget.Game, validationTarget.TargetPath); var errors = new List <string>(); Dictionary <string, MemoryStream> cachedAudio = new Dictionary <string, MemoryStream>(); foreach (var f in referencedFiles) { if (_closed) { return; } numChecked++; item.ItemText = $"Checking audio references in mod [{numChecked}/{referencedFiles.Count}]"; if (f.RepresentsPackageFilePath()) { var package = MEPackageHandler.OpenMEPackage(f); var wwiseStreams = package.Exports.Where(x => x.ClassName == "WwiseStream" && !x.IsDefaultObject).ToList(); foreach (var wwisestream in wwiseStreams) { if (_closed) { return; } //Check each reference. var afcNameProp = wwisestream.GetProperty <NameProperty>("Filename"); if (afcNameProp != null) { string afcNameWithExtension = afcNameProp + ".afc"; int audioSize = BitConverter.ToInt32(wwisestream.Data, wwisestream.Data.Length - 8); int audioOffset = BitConverter.ToInt32(wwisestream.Data, wwisestream.Data.Length - 4); string afcPath = null; Stream audioStream = null; var localDirectoryAFCPath = Path.Combine(Path.GetDirectoryName(wwisestream.FileRef.FilePath), afcNameWithExtension); bool isInOfficialArea = false; if (File.Exists(localDirectoryAFCPath)) { //local afc afcPath = localDirectoryAFCPath; } else { //Check game var fullPath = gameFiles.FirstOrDefault(x => Path.GetFileName(x).Equals(afcNameWithExtension, StringComparison.InvariantCultureIgnoreCase)); if (fullPath != null) { afcPath = fullPath; isInOfficialArea = MEDirectories.IsInBasegame(afcPath, validationTarget) || MEDirectories.IsInOfficialDLC(afcPath, validationTarget); } else if (cachedAudio.TryGetValue(afcNameProp.Value.Name, out var cachedAudioStream)) { audioStream = cachedAudioStream; //isInOfficialArea = true; //cached from vanilla SFAR } else if (MEDirectories.OfficialDLC(validationTarget.Game).Any(x => afcNameProp.Value.Name.StartsWith(x))) { var dlcName = afcNameProp.Value.Name.Substring(0, afcNameProp.Value.Name.LastIndexOf("_", StringComparison.InvariantCultureIgnoreCase)); var audio = VanillaDatabaseService.FetchFileFromVanillaSFAR(validationTarget, dlcName, afcNameWithExtension); if (audio != null) { cachedAudio[afcNameProp.Value.Name] = audio; } audioStream = audio; //isInOfficialArea = true; as this is in a vanilla SFAR we don't test against this since it will be correct. continue; } else { hasError = true; item.Icon = FontAwesomeIcon.TimesCircle; item.Foreground = Brushes.Red; item.Spinning = false; errors.Add($"{wwisestream.FileRef.FilePath} - {wwisestream.GetInstancedFullPath}: Could not find referenced audio file cache: {afcNameProp}"); continue; } } if (afcPath != null) { audioStream = new FileStream(afcPath, FileMode.Open); } try { audioStream.Seek(audioOffset, SeekOrigin.Begin); if (audioStream.ReadStringASCIINull(4) != "RIFF") { hasError = true; item.Icon = FontAwesomeIcon.TimesCircle; item.Foreground = Brushes.Red; item.Spinning = false; errors.Add($"{wwisestream.FileRef.FilePath} - {wwisestream.GetInstancedFullPath}: Invalid audio pointer, does not point to RIFF header"); if (audioStream is FileStream) { audioStream.Close(); } continue; } //attempt to seek audio length. audioStream.Seek(audioSize + 4, SeekOrigin.Current); //Check if this file is in basegame if (isInOfficialArea) { //Verify offset is not greater than vanilla size var vanillaInfo = VanillaDatabaseService.GetVanillaFileInfo(validationTarget, afcPath.Substring(validationTarget.TargetPath.Length + 1)); if (vanillaInfo == null) { Crashes.TrackError(new Exception("Vanilla information was null when performing vanilla file check for " + afcPath.Substring(validationTarget.TargetPath.Length + 1))); } if (audioOffset >= vanillaInfo[0].size) { hasError = true; item.Icon = FontAwesomeIcon.TimesCircle; item.Foreground = Brushes.Red; item.Spinning = false; errors.Add($"{wwisestream.FileRef.FilePath} - {wwisestream.GetInstancedFullPath}: This audio is in a basegame/DLC AFC but is not vanilla audio. End-users will not have this audio available. Use AFC compactor in ME3Explorer to pull this audio locally into an AFC for this mod."); } } if (audioStream is FileStream) { audioStream.Close(); } } catch (Exception e) { hasError = true; item.Icon = FontAwesomeIcon.TimesCircle; item.Foreground = Brushes.Red; item.Spinning = false; errors.Add($"{wwisestream.FileRef.FilePath} - {wwisestream.GetInstancedFullPath}: Error validating audio reference: {e.Message}"); continue; } } } } } if (!hasError) { item.Foreground = Brushes.Green; item.Icon = FontAwesomeIcon.CheckCircle; item.ItemText = "No audio issues were detected"; item.ToolTip = "Validation passed"; } else { item.Errors = errors; item.ItemText = "Audio issues were detected"; item.ToolTip = "Validation failed"; } item.HasError = hasError; cachedAudio.Clear(); }
private void importLODToolStripMenuItem_Click(object sender, EventArgs e) { ME3Package pcc = null; try { int n = listBox1.SelectedIndex; if (n == -1) { return; } int m = listBox2.SelectedIndex; if (m == -1) { return; } TreeNode t1 = treeView1.SelectedNode; if (t1 == null || t1.Parent == null || t1.Name == "") { return; } SkeletalMesh skm = new SkeletalMesh(); EntryStruct en; string loc = Path.GetDirectoryName(Application.ExecutablePath) + "\\exec\\"; if (DisplayStyle == 0) { int o = 0; if (!Int32.TryParse(t1.Name, out o)) { return; } en = Entries[o]; if (!en.isDLC) { if (en.isSkeletal) { pcc = MEPackageHandler.OpenME3Package(ME3Directory.cookedPath + en.Filename); skm = new SkeletalMesh(pcc, en.Index); } else { return; } } else { string dirDLC = ME3Directory.DLCPath; dirDLC += en.DLCName; dirDLC += "\\CookedPCConsole\\Default.sfar"; DLCBase dlc = new DLCBase(dirDLC); foreach (sfarFile file in dlc.fileList) { try { string filename = Path.GetFileName(file.fileName); if (Path.GetExtension(filename).ToLower().EndsWith(".pcc") && filename == en.Filename) { if (File.Exists(loc + "dlc.pcc")) { File.Delete(loc + "dlc.pcc"); } using (Stream input = File.OpenRead(dirDLC), output = File.Create(loc + "dlc.pcc")) { AmaroK86.MassEffect3.DLCUnpack.DecompressEntry(file, input, output, dlc.CompressionScheme); } if (File.Exists(loc + "dlc.pcc")) { try { if (en.isSkeletal) { pcc = MEPackageHandler.OpenME3Package(loc + "dlc.pcc"); skm = new SkeletalMesh(pcc, en.Index); break; } else { return; } } catch (Exception) { return; } } } } catch (Exception) { return; } } } } else { return; } if (!skm.Loaded || pcc == null) { return; } SkeletalMesh.LODModelStruct lodpcc = skm.LODModels[0]; UDKExplorer.UDK.Classes.SkeletalMesh skmudk = new UDKExplorer.UDK.Classes.SkeletalMesh(udk, Objects[n]); UDKExplorer.UDK.Classes.SkeletalMesh.LODModelStruct lodudk = skmudk.LODModels[m]; lodpcc.Sections = new List <SkeletalMesh.SectionStruct>(); foreach (UDKExplorer.UDK.Classes.SkeletalMesh.SectionStruct secudk in lodudk.Sections) { SkeletalMesh.SectionStruct secpcc = new SkeletalMesh.SectionStruct(); secpcc.BaseIndex = secudk.BaseIndex; secpcc.ChunkIndex = secudk.ChunkIndex; secpcc.MaterialIndex = secudk.MaterialIndex; secpcc.NumTriangles = secudk.NumTriangles; lodpcc.Sections.Add(secpcc); } lodpcc.IndexBuffer = new SkeletalMesh.MultiSizeIndexContainerStruct(); lodpcc.IndexBuffer.IndexCount = lodudk.IndexBuffer.IndexCount; lodpcc.IndexBuffer.IndexSize = lodudk.IndexBuffer.IndexSize; lodpcc.IndexBuffer.Indexes = new List <ushort>(); foreach (ushort Idx in lodudk.IndexBuffer.Indexes) { lodpcc.IndexBuffer.Indexes.Add(Idx); } List <int> BoneMap = new List <int>(); for (int i = 0; i < skmudk.Bones.Count; i++) { string udkb = udk.GetName(skmudk.Bones[i].Name); bool found = false; for (int j = 0; j < skm.Bones.Count; j++) { string pccb = pcc.getNameEntry(skm.Bones[j].Name); if (pccb == udkb) { found = true; BoneMap.Add(j); if (importBonesToolStripMenuItem.Checked) { SkeletalMesh.BoneStruct bpcc = skm.Bones[j]; UDKExplorer.UDK.Classes.SkeletalMesh.BoneStruct budk = skmudk.Bones[i]; bpcc.Orientation = budk.Orientation; bpcc.Position = budk.Position; skm.Bones[j] = bpcc; } } } if (!found) { DebugOutput.PrintLn("ERROR: Cant Match Bone \"" + udkb + "\""); BoneMap.Add(0); } } lodpcc.ActiveBones = new List <ushort>(); foreach (ushort Idx in lodudk.ActiveBones) { lodpcc.ActiveBones.Add((ushort)BoneMap[Idx]); } lodpcc.Chunks = new List <SkeletalMesh.SkelMeshChunkStruct>(); foreach (UDKExplorer.UDK.Classes.SkeletalMesh.SkelMeshChunkStruct chunkudk in lodudk.Chunks) { SkeletalMesh.SkelMeshChunkStruct chunkpcc = new SkeletalMesh.SkelMeshChunkStruct(); chunkpcc.BaseVertexIndex = chunkudk.BaseVertexIndex; chunkpcc.MaxBoneInfluences = chunkudk.MaxBoneInfluences; chunkpcc.NumRigidVertices = chunkudk.NumRigidVertices; chunkpcc.NumSoftVertices = chunkudk.NumSoftVertices; chunkpcc.BoneMap = new List <ushort>(); chunkpcc.RiginSkinVertices = new List <SkeletalMesh.RigidSkinVertexStruct>(); chunkpcc.SoftSkinVertices = new List <SkeletalMesh.SoftSkinVertexStruct>(); foreach (ushort Idx in chunkudk.BoneMap) { chunkpcc.BoneMap.Add((ushort)BoneMap[Idx]); } lodpcc.Chunks.Add(chunkpcc); } lodpcc.Size = lodudk.Size; lodpcc.NumVertices = lodudk.NumVertices; lodpcc.RequiredBones = new List <byte>(); foreach (byte b in lodudk.RequiredBones) { lodpcc.RequiredBones.Add(b); } lodpcc.VertexBufferGPUSkin = new SkeletalMesh.VertexBufferGPUSkinStruct(); lodpcc.VertexBufferGPUSkin.NumTexCoords = lodudk.VertexBufferGPUSkin.NumTexCoords; lodpcc.VertexBufferGPUSkin.Extension = lodudk.VertexBufferGPUSkin.Extension; lodpcc.VertexBufferGPUSkin.Origin = lodudk.VertexBufferGPUSkin.Origin; lodpcc.VertexBufferGPUSkin.VertexSize = lodudk.VertexBufferGPUSkin.VertexSize; lodpcc.VertexBufferGPUSkin.Vertices = new List <SkeletalMesh.GPUSkinVertexStruct>(); foreach (UDKExplorer.UDK.Classes.SkeletalMesh.GPUSkinVertexStruct vudk in lodudk.VertexBufferGPUSkin.Vertices) { SkeletalMesh.GPUSkinVertexStruct vpcc = new SkeletalMesh.GPUSkinVertexStruct(); vpcc.TangentX = vudk.TangentX; vpcc.TangentZ = vudk.TangentZ; vpcc.Position = vudk.Position; vpcc.InfluenceBones = vudk.InfluenceBones; vpcc.InfluenceWeights = vudk.InfluenceWeights; vpcc.U = vudk.U; vpcc.V = vudk.V; lodpcc.VertexBufferGPUSkin.Vertices.Add(vpcc); } for (int i = 0; i < skm.LODModels.Count; i++) { skm.LODModels[i] = lodpcc; } SerializingContainer con = new SerializingContainer(); con.Memory = new MemoryStream(); con.isLoading = false; skm.Serialize(con); int end = skm.GetPropertyEnd(); MemoryStream mem = new MemoryStream(); mem.Write(pcc.Exports[en.Index].Data, 0, end); mem.Write(con.Memory.ToArray(), 0, (int)con.Memory.Length); pcc.Exports[en.Index].Data = mem.ToArray(); pcc.save(); if (!en.isDLC) { MessageBox.Show("Done"); } else { MessageBox.Show("Done. The file is now in following folder, please replace it back to DLC :\n" + loc + "dlc.pcc"); } globalTreeToolStripMenuItem.Visible = optionsToolStripMenuItem.Visible = transferToolStripMenuItem.Visible = splitContainer1.Visible = true; fileToolStripMenuItem.Visible = importLODToolStripMenuItem.Visible = splitContainer3.Visible = false; } finally { pcc?.Dispose(); } }
private void CheckModForTFCCompactability(DeploymentChecklistItem item) { // if (ModBeingDeployed.Game >= Mod.MEGame.ME2) //{ bool hasError = false; item.HasError = false; item.ItemText = "Checking textures in mod"; var referencedFiles = ModBeingDeployed.GetAllRelativeReferences().Select(x => Path.Combine(ModBeingDeployed.ModPath, x)).ToList(); int numChecked = 0; GameTarget validationTarget = mainWindow.InstallationTargets.FirstOrDefault(x => x.Game == ModBeingDeployed.Game); var errors = new List <string>(); foreach (var f in referencedFiles) { if (_closed) { return; } numChecked++; item.ItemText = $"Checking textures in mod [{numChecked}/{referencedFiles.Count}]"; if (f.RepresentsPackageFilePath()) { var package = MEPackageHandler.OpenMEPackage(f); var textures = package.Exports.Where(x => x.IsTexture()).ToList(); foreach (var texture in textures) { if (_closed) { return; } var cache = texture.GetProperty <NameProperty>("TextureFileCacheName"); if (cache != null) { if (!VanillaDatabaseService.IsBasegameTFCName(cache.Value, ModBeingDeployed.Game)) { var mips = Texture2D.GetTexture2DMipInfos(texture, cache.Value); Texture2D tex = new Texture2D(texture); try { tex.GetImageBytesForMip(tex.GetTopMip(), validationTarget, false); //use active target } catch (Exception e) { hasError = true; item.Icon = FontAwesomeIcon.TimesCircle; item.Foreground = Brushes.Red; item.Spinning = false; errors.Add($"{texture.FileRef.FilePath} - {texture.GetInstancedFullPath}: Could not load texture data: {e.Message}"); } } } } } } if (!hasError) { item.Foreground = Brushes.Green; item.Icon = FontAwesomeIcon.CheckCircle; item.ItemText = "No broken textures were found"; item.ToolTip = "Validation passed"; } else { item.Errors = errors; item.ItemText = "Texture issues were detected"; item.ToolTip = "Validation failed"; } item.HasError = hasError; //} //} else //{ // item.Foreground = Brushes.Green; // item.Icon = FontAwesomeIcon.CheckCircle; // item.ItemText = "Textures are not "; // item.ToolTip = "Validation passed"; //} }
private void startScanToolStripMenuItem_Click(object sender, EventArgs e) { if (string.IsNullOrEmpty(ME3Directory.cookedPath)) { MessageBox.Show("This functionality requires ME3 to be installed. Set its path at:\n Options > Set Custom Path > Mass Effect 3"); return; } string path = ME3Directory.cookedPath; string[] files = Directory.GetFiles(path, "*.pcc"); pb1.Maximum = files.Length; DebugOutput.Clear(); database = new List <DBEntry>(); int count = 0; foreach (string file in files) { pb1.Value = count++; DebugOutput.PrintLn("Scanning file : " + Path.GetFileName(file) + " ..."); try { using (ME3Package pcc = MEPackageHandler.OpenME3Package(file)) { DBEntry ent = new DBEntry(); ent.filename = Path.GetFileName(file); ent.Objects = new List <ObjInf>(); IReadOnlyList <IExportEntry> Exports = pcc.Exports; for (int i = 0; i < Exports.Count; i++) { IExportEntry ex = Exports[i]; ObjInf obj; switch (ex.ClassName) { case "StaticMesh": obj = new ObjInf(); obj.Index = i; obj.Type = 0; obj.name = ex.ObjectName; ent.Objects.Add(obj); break; case "SkeletalMesh": obj = new ObjInf(); obj.Index = i; obj.Type = 1; obj.name = ex.ObjectName; ent.Objects.Add(obj); break; } } if (ent.Objects.Count != 0) { DebugOutput.PrintLn("Found " + ent.Objects.Count + " Objects:", false); //foreach (ObjInf o in ent.Objects) // DebugOutput.PrintLn("\t" + o.Index + " : " + o.name + " (" + TypeToString(o.Type) + ")", false); //DebugOutput.Update(); database.Add(ent); } else { DebugOutput.PrintLn("Nothing...", false); } } } catch (Exception ex) { MessageBox.Show("Error:\n" + ex.Message); DebugOutput.PrintLn("Could not open file : " + Path.GetFileName(file)); } } RefreshLists(); pb1.Value = 0; }
public static void TestPropertiesInBinaryAssets() { var dlcModPath = @"C:\Users\mgame\source\repos\ME2Randomizer\ME2Randomizer\staticfiles\binary"; ReferenceCheckPackage rcp = new ReferenceCheckPackage(); bool checkCanceled = false; EntryChecker.CheckReferences(rcp, dlcModPath, EntryChecker.NonLocalizedStringConverter, x => Debug.WriteLine(x)); var cookedPC = ME2Directory.CookedPCPath; var sourcePackages = Directory.GetFiles(cookedPC, "SFX*.pcc", SearchOption.TopDirectoryOnly).Select(x => MEPackageHandler.OpenMEPackage(x)).ToList(); sourcePackages.AddRange(Directory.GetFiles(cookedPC, "Bio*.pcc", SearchOption.TopDirectoryOnly).Select(x => MEPackageHandler.OpenMEPackage(x))); foreach (var s in rcp.GetInfoWarnings()) { Debug.WriteLine($"INFO: {s.Message}"); } foreach (var s in rcp.GetSignificantIssues()) { Debug.WriteLine($"SIGNIFICANT: {s.Message}"); if (s.Entry is ExportEntry exp) { ExportEntry sourceToPull = null; foreach (var sp in sourcePackages) { sourceToPull = sp.FindExport(exp.InstancedFullPath); if (sourceToPull != null) { break; } } if (sourceToPull != null) { EntryImporter.ImportAndRelinkEntries(EntryImporter.PortingOption.ReplaceSingular, sourceToPull, exp.FileRef, exp, true, out _); Debug.WriteLine(" > REPLACED"); } } } foreach (var s in rcp.GetBlockingErrors()) { Debug.WriteLine($"BLOCKING: {s.Message}"); } var packagesToSave = rcp.GetSignificantIssues().Where(x => x.Entry != null && x.Entry.FileRef.IsModified).Select(x => x.Entry.FileRef).Distinct().ToList(); foreach (var p in packagesToSave) { p.Save(); } }
private void StartGuiCompatibilityScanner() { NamedBackgroundWorker bw = new NamedBackgroundWorker(@"GUICompatibilityScanner"); bw.DoWork += (a, b) => { Percent = 0; ActionString = M3L.GetString(M3L.string_preparingCompatGenerator); ActionSubstring = M3L.GetString(M3L.string_pleaseWait); var installedDLCMods = VanillaDatabaseService.GetInstalledDLCMods(target); var numTotalDLCMods = installedDLCMods.Count; var uiModInstalled = installedDLCMods.Intersect(DLCUIModFolderNames).Any(); var dlcRoot = MEDirectories.DLCPath(target); if (uiModInstalled) { var nonUIinstalledDLCMods = installedDLCMods.Except(DLCUIModFolderNamesIncludingPatch).ToList(); if (nonUIinstalledDLCMods.Count < numTotalDLCMods && nonUIinstalledDLCMods.Count > 0) { //Get UI library bool xbxLibrary = installedDLCMods.Contains(@"DLC_CON_XBX"); bool uiscalinglibrary = installedDLCMods.Contains(@"DLC_CON_UIScaling"); if (!xbxLibrary && !uiscalinglibrary) { uiscalinglibrary = installedDLCMods.Contains(@"DLC_CON_UIScaling_Shared"); } if (xbxLibrary && uiscalinglibrary) { //can't have both! Not supported. Application.Current.Dispatcher.Invoke(delegate { Log.Error(@"Cannot make compat pack: Both ISM and SP Controller are installed, this is not supported."); M3L.ShowDialog(window, M3L.GetString(M3L.string_dialogCannotGenerateCompatPackInvalidConfig), M3L.GetString(M3L.string_invalidConfiguration), MessageBoxButton.OK, MessageBoxImage.Error); OnClosing(DataEventArgs.Empty); }); b.Result = GUICompatibilityThreadResult.INVALID_UI_MOD_CONFIG; return; } void progressCallback(long done, long total) { ActionString = M3L.GetString(M3L.string_downloadingUiLibrary); ActionSubstring = xbxLibrary ? @"DLC_CON_XBX" : @"DLC_CON_UIScaling"; Percent = getPercent(done, total); } var uiLibraryPath = GetUILibraryPath(xbxLibrary ? @"DLC_CON_XBX" : @"DLC_CON_UIScaling", true, progressCallback); if (uiLibraryPath == null) { Log.Error(@"Required UI library could not be downloaded."); Application.Current.Dispatcher.Invoke(delegate { M3L.ShowDialog(window, M3L.GetString(M3L.string_cannotGeneratorCompatPackCouldNotDownload), M3L.GetString(M3L.string_couldNotAcquireUiLibrary), MessageBoxButton.OK, MessageBoxImage.Error); OnClosing(DataEventArgs.Empty); }); b.Result = GUICompatibilityThreadResult.NO_UI_LIBRARY; return; } //Open UI library SevenZipExtractor libraryArchive = new SevenZipExtractor(uiLibraryPath); List <string> libraryGUIs = libraryArchive.ArchiveFileData.Where(x => !x.IsDirectory).Select(x => x.FileName.Substring(Path.GetFileNameWithoutExtension(uiLibraryPath).Length + 1)).Select(x => x.Substring(0, x.Length - 4)).ToList(); //remove / on end too //We have UI mod(s) installed and at least one other DLC mod. var supercedanceList = getFileSupercedances().Where(x => x.Value.Any(x => !DLCUIModFolderNamesIncludingPatch.Contains(x))).ToDictionary(p => p.Key, p => p.Value); //Find GUIs ConcurrentDictionary <string, string> filesToBePatched = new ConcurrentDictionary <string, string>(); //Dictionary because there is no ConcurrentList. Keys and values are idenitcal. ActionString = M3L.GetString(M3L.string_scanningForGuiExports); ActionSubstring = M3L.GetString(M3L.string_pleaseWait); Percent = 0; int done = 0; string singlesuffix = M3L.GetString(M3L.string_singularFile); string pluralsuffix = M3L.GetString(M3L.string_pluralFiles); Parallel.ForEach(supercedanceList, new ParallelOptions() { MaxDegreeOfParallelism = 4 }, (pair) => { var firstNonUIModDlc = pair.Value.FirstOrDefault(x => !DLCUIModFolderNamesIncludingPatch.Contains(x)); if (firstNonUIModDlc != null) { //Scan file. var packagefile = Path.Combine(dlcRoot, firstNonUIModDlc, target.Game == MEGame.ME3 ? @"CookedPCConsole" : @"CookedPC", pair.Key); Log.Information(@"Scanning file for GFXMovieInfo exports: " + packagefile); if (!File.Exists(packagefile)) { throw new Exception($@"Package file for inspecting GUIs in was not found: {packagefile}"); } var package = MEPackageHandler.OpenMEPackage(packagefile); var guiExports = package.Exports.Where(x => !x.IsDefaultObject && x.ClassName == @"GFxMovieInfo").ToList(); if (guiExports.Count > 0) { //potential item needing replacement //Check GUI library to see if we have anything. foreach (var export in guiExports) { if (libraryGUIs.Contains(export.GetFullPath, StringComparer.InvariantCultureIgnoreCase)) { //match filesToBePatched[packagefile] = packagefile; ActionSubstring = M3L.GetString(M3L.string_interp_XFilesNeedToBePatched, filesToBePatched.Count.ToString()); Log.Information($@"{firstNonUIModDlc} {pair.Key} has GUI export that is in UI library, marking for patching. Trigger: {export.GetFullPath}"); break; } } } } Interlocked.Increment(ref done); Percent = getPercent(done, supercedanceList.Count); }); if (filesToBePatched.Count > 0) { Log.Information(@"A GUI compatibility patch is required for this game configuration"); b.Result = GUICompatibilityThreadResult.REQUIRED; var generatedMod = GenerateCompatibilityPackForFiles(nonUIinstalledDLCMods, filesToBePatched.Keys.ToList(), libraryArchive); b.Result = GUICompatibilityThreadResult.GENERATED_PACK; Application.Current.Dispatcher.Invoke(delegate { ((MainWindow)window).LoadMods(generatedMod); }); //reload to this mod return; } } Log.Information(@"A GUI compatibility patch is not required for this game configuration"); b.Result = GUICompatibilityThreadResult.NOT_REQUIRED; } else { Log.Information(@"No UI mods are installed - no GUI compatibility pack required"); b.Result = GUICompatibilityThreadResult.NO_UI_MODS_INSTALLED; } }; bw.RunWorkerCompleted += (a, b) => { if (b.Result is GUICompatibilityThreadResult gctr) { Analytics.TrackEvent(@"Generated a UI compatibility pack", new Dictionary <string, string>() { { @"Result", gctr.ToString() } }); OnClosing(DataEventArgs.Empty); if (gctr == GUICompatibilityThreadResult.NOT_REQUIRED) { M3L.ShowDialog(mainwindow, M3L.GetString(M3L.string_dialogNoCompatPackRequired), M3L.GetString(M3L.string_noCompatPackRequired), MessageBoxButton.OK); } } else { throw new Exception(@"GUI Compatibility generator thread did not return a result! Please report this to ME3Tweaks"); } }; bw.RunWorkerAsync(); }
public static bool PortPawnIntoPackage(PortablePawn pawn, IMEPackage targetPackage) { if (IsPawnAssetInPackageAlready(pawn, targetPackage)) { return(true); // Pawn asset to port in already ported in } IMEPackage pawnPackage = null; if (pawn.IsCorrectedPackage) { // DEBUG //if (pawn.PackageFilename == "BioPawn_Collector_Batarian.pcc") //{ // pawnPackage = MEPackageHandler.OpenMEPackage(@"C:\Users\mgame\source\repos\ME2Randomizer\ME2Randomizer\staticfiles\binary\correctedpawns\" + pawn.PackageFilename); //} //else //{ var correctedPawnData = MERUtilities.GetEmbeddedStaticFilesBinaryFile($"correctedpawns.{pawn.PackageFilename}"); pawnPackage = MEPackageHandler.OpenMEPackageFromStream(new MemoryStream(correctedPawnData)); //} } else { var pF = MERFileSystem.GetPackageFile(pawn.PackageFilename); if (pF != null) { pawnPackage = MERFileSystem.OpenMEPackage(pF); } else { Debug.WriteLine("Pawn package not found: {pawn.PackageFilename}"); } } if (pawnPackage != null) { PackageTools.PortExportIntoPackage(targetPackage, pawnPackage.FindExport(pawn.AssetToPortIn), useMemorySafeImport: !pawn.IsCorrectedPackage); // Ensure the assets are too as they may not be directly referenced except in the level instance foreach (var asset in pawn.AssetPaths) { if (targetPackage.FindExport(asset) == null) { PackageTools.PortExportIntoPackage(targetPackage, pawnPackage.FindExport(asset), useMemorySafeImport: !pawn.IsCorrectedPackage); } } if (pawn.TextureUpdates != null) { foreach (var tu in pawn.TextureUpdates) { var targetTextureExp = targetPackage.FindExport(tu.TextureInstancedFullPath); TFCBuilder.InstallTexture(tu, targetTextureExp); } } return(true); } return(false); }