private static string GetLocalizedTextureExceptionInternalMessage(string exceptionMessage, string storageType) => M3L.GetString(M3L.string_interp_error_textureExceptionInternal, exceptionMessage, storageType);
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); 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); foreach (var file in filesToBePatched) { ActionSubstring = Path.GetFileName(file); var package = MEPackageHandler.OpenMEPackage(file); 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)) { //Patching this export. var exportProperties = export.GetProperties(); var rawData = exportProperties.GetProp <ArrayProperty <ByteProperty> >(@"RawData"); 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); 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."); } } return(generatedMod); }
public static void BuildBioPGlobal(GameTarget target) { M3MergeDLC.RemoveMergeDLC(target); var loadedFiles = MELoadedFiles.GetFilesLoadedInGame(target.Game, gameRootOverride: target.TargetPath); //var mergeFiles = loadedFiles.Where(x => // x.Key.StartsWith(@"BioH_") && x.Key.Contains(@"_DLC_MOD_") && x.Key.EndsWith(@".pcc") && !x.Key.Contains(@"_LOC_") && !x.Key.Contains(@"_Explore.")); Log.Information($@"SQMMERGE: Building BioP_Global"); var appearanceInfo = new CaseInsensitiveDictionary <List <SquadmateInfoSingle> >(); int appearanceId = 255; // starting int currentConditional = STARTING_OUTFIT_CONDITIONAL; // Scan squadmate merge files var sqmSupercedances = M3Directories.GetFileSupercedances(target, new[] { @".sqm" }); if (sqmSupercedances.TryGetValue(SQUADMATE_MERGE_MANIFEST_FILE, out var infoList)) { infoList.Reverse(); foreach (var dlc in infoList) { Log.Information($@"SQMMERGE: Processing {dlc}"); var jsonFile = Path.Combine(M3Directories.GetDLCPath(target), dlc, target.Game.CookedDirName(), SQUADMATE_MERGE_MANIFEST_FILE); var infoPackage = JsonConvert.DeserializeObject <SquadmateMergeInfo>(File.ReadAllText(jsonFile)); if (!infoPackage.Validate(dlc, target, loadedFiles)) { continue; // skip this } // Enumerate all outfits listed for a single squadmate foreach (var outfit in infoPackage.Outfits) { List <SquadmateInfoSingle> list; // See if we already have an outfit list for this squadmate, maybe from another mod... if (!appearanceInfo.TryGetValue(outfit.HenchName, out list)) { list = new List <SquadmateInfoSingle>(); appearanceInfo[outfit.HenchName] = list; } outfit.ConditionalIndex = currentConditional++; // This is always incremented, so it might appear out of order in game files depending on how mod order is processed, that should be okay though. outfit.AppearanceId = appearanceId++; // may need adjusted outfit.DLCName = dlc; list.Add(outfit); Log.Information($@"SQMMERGE: ConditionalIndex for {outfit.HenchName} appearanceid {outfit.AppearanceId}: {outfit.ConditionalIndex}"); } } } if (appearanceInfo.Any()) { var biopGlobal = MEPackageHandler.OpenMEPackage(loadedFiles[@"BioP_Global.pcc"]); var lsk = biopGlobal.Exports.FirstOrDefault(x => x.ClassName == @"LevelStreamingKismet"); var persistentLevel = biopGlobal.FindExport(@"TheWorld.PersistentLevel"); // Clone LevelStreamingKismets foreach (var sqm in appearanceInfo.Values) { foreach (var outfit in sqm) { var fName = outfit.HenchPackage; var newLSK = EntryCloner.CloneEntry(lsk); newLSK.WriteProperty(new NameProperty(fName, @"PackageName")); if (target.Game.IsGame3()) { // Game 3 has _Explore files too fName += @"_Explore"; newLSK = EntryCloner.CloneEntry(lsk); newLSK.WriteProperty(new NameProperty(fName, @"PackageName")); } } } // Update BioWorldInfo // Doesn't have consistent number so we can't find it by instanced full path var bioWorldInfo = biopGlobal.Exports.FirstOrDefault(x => x.ClassName == @"BioWorldInfo"); var props = bioWorldInfo.GetProperties(); // Update Plot Streaming var plotStreaming = props.GetProp <ArrayProperty <StructProperty> >(@"PlotStreaming"); foreach (var sqm in appearanceInfo.Values) { foreach (var outfit in sqm) { // find item to add to buildPlotElementObject(plotStreaming, outfit, target.Game, false); if (target.Game.IsGame3()) { buildPlotElementObject(plotStreaming, outfit, target.Game, true); } } } // Update StreamingLevels var streamingLevels = props.GetProp <ArrayProperty <ObjectProperty> >(@"StreamingLevels"); streamingLevels.ReplaceAll(biopGlobal.Exports.Where(x => x.ClassName == @"LevelStreamingKismet").Select(x => new ObjectProperty(x))); bioWorldInfo.WriteProperties(props); M3MergeDLC.GenerateMergeDLC(target, Guid.NewGuid()); // Save BioP_Global into DLC var cookedDir = Path.Combine(M3Directories.GetDLCPath(target), M3MergeDLC.MERGE_DLC_FOLDERNAME, target.Game.CookedDirName()); var outP = Path.Combine(cookedDir, @"BioP_Global.pcc"); biopGlobal.Save(outP); // Generate conditionals file if (target.Game.IsGame3()) { CNDFile cnd = new CNDFile(); cnd.ConditionalEntries = new List <CNDFile.ConditionalEntry>(); foreach (var sqm in appearanceInfo.Values) { foreach (var outfit in sqm) { var scText = $@"(plot.ints[{GetSquadmateOutfitInt(outfit.HenchName, target.Game)}] == i{outfit.MemberAppearanceValue})"; var compiled = ME3ConditionalsCompiler.Compile(scText); cnd.ConditionalEntries.Add(new CNDFile.ConditionalEntry() { Data = compiled, ID = outfit.ConditionalIndex }); } } cnd.ToFile(Path.Combine(cookedDir, $@"Conditionals{M3MergeDLC.MERGE_DLC_FOLDERNAME}.cnd")); } else if (target.Game.IsGame2()) { var startupF = Path.Combine(cookedDir, $@"Startup_{M3MergeDLC.MERGE_DLC_FOLDERNAME}.pcc"); var startup = MEPackageHandler.OpenMEPackageFromStream(Utilities.GetResourceStream( $@"MassEffectModManagerCore.modmanager.mergedlc.{target.Game}.Startup_{M3MergeDLC.MERGE_DLC_FOLDERNAME}.pcc")); var conditionalClass = startup.FindExport($@"PlotManager{M3MergeDLC.MERGE_DLC_FOLDERNAME}.BioAutoConditionals"); // Add Conditional Functions FileLib fl = new FileLib(startup); bool initialized = fl.Initialize(new RelativePackageCache() { RootPath = M3Directories.GetBioGamePath(target) }); if (!initialized) { throw new Exception( $@"FileLib for script update could not initialize, cannot install conditionals"); } var funcToClone = startup.FindExport($@"PlotManager{M3MergeDLC.MERGE_DLC_FOLDERNAME}.BioAutoConditionals.TemplateFunction"); foreach (var sqm in appearanceInfo.Values) { foreach (var outfit in sqm) { var func = EntryCloner.CloneEntry(funcToClone); func.ObjectName = $@"F{outfit.ConditionalIndex}"; func.indexValue = 0; var scText = new StreamReader(Utilities.GetResourceStream( $@"MassEffectModManagerCore.modmanager.squadmates.{target.Game}.HasOutfitOnConditional.txt")) .ReadToEnd(); scText = scText.Replace(@"%CONDITIONALNUM%", outfit.ConditionalIndex.ToString()); scText = scText.Replace(@"%SQUADMATEOUTFITPLOTINT%", outfit.AppearanceId.ToString()); scText = scText.Replace(@"%OUTFITINDEX%", outfit.MemberAppearanceValue.ToString()); (_, MessageLog log) = UnrealScriptCompiler.CompileFunction(func, scText, fl); if (log.AllErrors.Any()) { Log.Error($@"Error compiling function {func.InstancedFullPath}:"); foreach (var l in log.AllErrors) { Log.Error(l.Message); } throw new Exception(M3L.GetString(M3L.string_interp_errorCompilingConditionalFunction, func, string.Join('\n', log.AllErrors.Select(x => x.Message)))); } } } // Relink the conditionals chain UClass uc = ObjectBinary.From <UClass>(conditionalClass); uc.UpdateLocalFunctions(); uc.UpdateChildrenChain(); conditionalClass.WriteBinary(uc); startup.Save(startupF); } // Add startup package, member appearances if (target.Game.IsGame2()) { var bioEngine = Path.Combine(cookedDir, @"BIOEngine.ini"); var ini = DuplicatingIni.LoadIni(bioEngine); var startupSection = ini.GetOrAddSection(@"Engine.StartupPackages"); startupSection.Entries.Add(new DuplicatingIni.IniEntry(@"+DLCStartupPackage", $@"Startup_{M3MergeDLC.MERGE_DLC_FOLDERNAME}")); startupSection.Entries.Add(new DuplicatingIni.IniEntry(@"+Package", $@"PlotManager{M3MergeDLC.MERGE_DLC_FOLDERNAME}")); ini.WriteToFile(bioEngine); } else if (target.Game.IsGame3()) { var mergeCoalFile = Path.Combine(M3Directories.GetDLCPath(target), M3MergeDLC.MERGE_DLC_FOLDERNAME, target.Game.CookedDirName(), $@"Default_{M3MergeDLC.MERGE_DLC_FOLDERNAME}.bin"); var mergeCoal = CoalescedConverter.DecompileGame3ToMemory(new MemoryStream(File.ReadAllBytes(mergeCoalFile))); // Member appearances var bioUiDoc = XDocument.Parse(mergeCoal[@"BIOUI.xml"]); foreach (var sqm in appearanceInfo.Values) { foreach (var outfit in sqm) { var entry = new Game3CoalescedValueEntry() { Section = @"sfxgame.sfxguidata_teamselect", Name = @"selectappearances", Type = 3, Value = StringStructParser.BuildCommaSeparatedSplitValueList(outfit.ToPropertyDictionary(), @"AvailableImage", @"HighlightImage", @"DeadImage", @"SilhouetteImage") }; Game3CoalescedHelper.AddArrayEntry(bioUiDoc, entry); } } mergeCoal[@"BIOUI.xml"] = bioUiDoc.ToString(); // Dynamic load mapping var bioEngineDoc = XDocument.Parse(mergeCoal[@"BIOEngine.xml"]); foreach (var sqm in appearanceInfo.Values) { foreach (var outfit in sqm) { // // * <Section name="sfxgame.sfxengine"> // <Property name="dynamicloadmapping"> // <Value type="3">(ObjectName="BIOG_GesturesConfigDLC.RuntimeData",SeekFreePackageName="GesturesConfigDLC")</Value> var entry = new Game3CoalescedValueEntry() { Section = @"sfxgame.sfxengine", Name = @"dynamicloadmapping", Type = 3 }; entry.Values.Add($"(ObjectName=\"{outfit.AvailableImage}\",SeekFreePackageName=\"SFXHenchImages_{outfit.DLCName}\")"); // do not localize entry.Values.Add($"(ObjectName=\"{outfit.HighlightImage}\",SeekFreePackageName=\"SFXHenchImages_{outfit.DLCName}\")"); // do not localize Game3CoalescedHelper.AddArrayEntry(bioEngineDoc, entry); } } mergeCoal[@"BIOEngine.xml"] = bioEngineDoc.ToString(); CoalescedConverter.CompileFromMemory(mergeCoal).WriteToFile(mergeCoalFile); } } }
private static void extractTool(string tool, string executable, string extension, string downloadPath, Action <string> currentTaskUpdateCallback = null, Action <bool> setPercentVisibilityCallback = null, Action <int> setPercentTaskDone = null, Action <string> resultingExecutableStringCallback = null, Action <Exception, string, string> errorExtractingCallback = null) { //Todo: Account for errors var outputDirectory = Directory.CreateDirectory(Path.GetDirectoryName(executable)).FullName; switch (extension) { case @".exe": if (File.Exists(executable)) { File.Delete(executable); } File.Move(downloadPath, executable); resultingExecutableStringCallback?.Invoke(executable); break; case @".rar": case @".7z": case @".zip": Log.Information(@"Extracting tool archive: " + downloadPath); using (var archiveFile = new SevenZipExtractor(downloadPath)) { currentTaskUpdateCallback?.Invoke(M3L.GetString(M3L.string_interp_extractingX, tool)); setPercentTaskDone?.Invoke(0); void progressCallback(object sender, ProgressEventArgs progress) { setPercentTaskDone?.Invoke(progress.PercentDone); }; archiveFile.Extracting += progressCallback; try { archiveFile.ExtractArchive(outputDirectory); // extract all resultingExecutableStringCallback?.Invoke(executable); } catch (Exception e) { Log.Error($@"Could not extract/run tool {executable} after download: {e.Message}"); errorExtractingCallback?.Invoke(e, M3L.GetString(M3L.string_interp_errorDownloadingAndLaunchingTool, e.Message), M3L.GetString(M3L.string_errorLaunchingTool)); } } break; default: Log.Error($@"Failed to download correct file! We don't support this extension. The extension was {extension}"); var ex = new Exception(M3L.GetString(M3L.string_interp_unsupportedExtensionX, extension)); errorExtractingCallback?.Invoke(ex, M3L.GetString(M3L.string_interp_errorDownloadingAndLaunchingTool, ex.Message), M3L.GetString(M3L.string_errorLaunchingTool)); break; } }
//this is copied from NexusModsLogin.xaml.cs cause I'm too lazy to make it shared code for what will likely never change private void AuthorizeWithNexus() { NamedBackgroundWorker nbw = new NamedBackgroundWorker(@"NexusAPICredentialsCheck"); nbw.DoWork += async(a, b) => { IsAuthorizing = true; VisibleIcon = true; SpinIcon = true; ActiveIcon = FontAwesomeIcon.Spinner; AuthorizeCommand.RaiseCanExecuteChanged(); CloseCommand.RaiseCanExecuteChanged(); AuthorizeToNexusText = M3L.GetString(M3L.string_pleaseWait); var apiKeyReceived = await NexusModsUtilities.SetupNexusLogin(x => Debug.WriteLine(x)); Application.Current.Dispatcher.Invoke(delegate { mainwindow.Activate(); }); if (!string.IsNullOrWhiteSpace(apiKeyReceived)) { //Check api key AuthorizeToNexusText = M3L.GetString(M3L.string_checkingKey); try { var authInfo = NexusModsUtilities.AuthToNexusMods(apiKeyReceived).Result; if (authInfo != null) { using FileStream fs = new FileStream(System.IO.Path.Combine(Utilities.GetNexusModsCache(), @"nexusmodsapikey"), FileMode.Create); File.WriteAllBytes(System.IO.Path.Combine(Utilities.GetNexusModsCache(), @"entropy"), NexusModsUtilities.EncryptStringToStream(apiKeyReceived, fs)); fs.Close(); mainwindow.NexusUsername = authInfo.Name; mainwindow.NexusUserID = authInfo.UserID; SetAuthorized(true); mainwindow.RefreshNexusStatus(); Analytics.TrackEvent(@"Authenticated to NexusMods"); } else { Log.Error(@"Error authenticating to nexusmods, no userinfo was returned, possible network issue"); mainwindow.NexusUsername = null; mainwindow.NexusUserID = 0; SetAuthorized(false); mainwindow.RefreshNexusStatus(); } } catch (ApiException apiException) { Log.Error(@"Error authenticating to NexusMods: " + apiException.ToString()); Application.Current.Dispatcher.Invoke(delegate { M3L.ShowDialog(window, M3L.GetString(M3L.string_interp_nexusModsReturnedAnErrorX, apiException.ToString()), M3L.GetString(M3L.string_errorAuthenticatingToNexusMods), MessageBoxButton.OK, MessageBoxImage.Error); }); } catch (Exception e) { Log.Error(@"Other error authenticating to NexusMods: " + e.Message); } } else { Log.Error(@"No API key - setting authorized to false for NM"); SetAuthorized(false); } IsAuthorizing = false; }; nbw.RunWorkerCompleted += (a, b) => { if (b.Error != null) { Log.Error($@"Exception occured in {nbw.Name} thread: {b.Error.Message}"); } VisibleIcon = IsAuthorized; if (IsAuthorized) { ActiveIcon = FontAwesomeIcon.CheckCircle; } SpinIcon = false; AuthorizeCommand.RaiseCanExecuteChanged(); CloseCommand.RaiseCanExecuteChanged(); }; nbw.RunWorkerAsync(); }
private void InstallUninstallASI() { if (SelectedASIObject is InstalledASIMod instASI) { //Unknown ASI File.Delete(instASI.InstalledPath); RefreshASIStates(); } else if (SelectedASIObject is ASIMod asi) { InstallInProgress = true; var alreadyInstalledAndUpToDate = Games.First(x => x.Game == asi.Game).ApplyASI(asi, () => { InstallInProgress = false; RefreshASIStates(); UpdateSelectionTexts(SelectedASIObject); CommandManager.InvalidateRequerySuggested(); }); if (!alreadyInstalledAndUpToDate) { void exceptionCallback(Exception e) { M3L.ShowDialog(mainwindow, M3L.GetString(M3L.string_interp_anErrorOccuredDeletingTheASI, e.Message), M3L.GetString(M3L.string_errorDeletingASI), MessageBoxButton.OK, MessageBoxImage.Error); } Games.First(x => x.Game == asi.Game).DeleteASI(asi, exceptionCallback); //UI doesn't allow you to install on top of an already installed ASI that is up to date. So we delete ith ere. InstallInProgress = false; RefreshASIStates(); UpdateSelectionTexts(SelectedASIObject); } } }
/// <summary> /// Attempts to endorse/unendorse this mod on NexusMods. /// </summary> /// <param name="newEndorsementStatus"></param> /// <param name="endorse"></param> /// <param name="currentuserid"></param> public void EndorseMod(Action <Mod, bool, string> endorsementResultCallback, bool endorse) { if (NexusModsUtilities.UserInfo == null || !CanEndorse) { return; } NamedBackgroundWorker nbw = new NamedBackgroundWorker(@"ModSpecificEndorsement"); nbw.DoWork += (a, b) => { var client = NexusModsUtilities.GetClient(); string gamename = @"masseffect"; if (Game == MEGame.ME2) { gamename += @"2"; } if (Game == MEGame.ME3) { gamename += @"3"; } string telemetryOverride = null; string endorsementFailedReason = null; try { if (endorse) { client.Mods.Endorse(gamename, NexusModID, @"1.0").Wait(); } else { client.Mods.Unendorse(gamename, NexusModID, @"1.0").Wait(); } } catch (Exception e) { if (e.InnerException != null) { if (e.InnerException.Message == @"NOT_DOWNLOADED_MOD") { // User did not download this mod from NexusMods endorsementFailedReason = M3L.GetString(M3L.string_dialog_cannotEndorseNonDownloadedMod); } else if (e.InnerException.Message == @"TOO_SOON_AFTER_DOWNLOAD") { endorsementFailedReason = M3L.GetString(M3L.string_dialog_cannotEndorseUntil15min); } } else { telemetryOverride = e.ToString(); } Log.Error(@"Error endorsing/unendorsing: " + e.ToString()); } checkedEndorsementStatus = false; IsEndorsed = GetEndorsementStatus().Result ?? false; Analytics.TrackEvent(@"Set endorsement for mod", new Dictionary <string, string> { { @"Endorsed", endorse.ToString() }, { @"Succeeded", telemetryOverride ?? (endorse == IsEndorsed).ToString() } }); b.Result = endorsementFailedReason; }; nbw.RunWorkerCompleted += (a, b) => { if (b.Error != null) { Log.Error($@"Exception occurred in {nbw.Name} thread: {b.Error.Message}"); } else if (b.Result is string endorsementFailedReason) { endorsementResultCallback.Invoke(this, IsEndorsed, endorsementFailedReason); } else { endorsementResultCallback.Invoke(this, IsEndorsed, null); } }; nbw.RunWorkerAsync(); }
/// <summary> /// This method is run on a background thread so all UI calls needs to be wrapped /// </summary> private void PopulateUI() { BackgroundWorker bw = new BackgroundWorker(); bw.DoWork += (a, b) => { bool deleteConfirmationCallback(InstalledDLCMod mod) { if (Utilities.IsGameRunning(SelectedTarget.Game)) { M3L.ShowDialog(Window.GetWindow(this), M3L.GetString(M3L.string_interp_cannotDeleteModsWhileXIsRunning, Utilities.GetGameName(SelectedTarget.Game)), M3L.GetString(M3L.string_gameRunning), MessageBoxButton.OK, MessageBoxImage.Error); return(false); } if (SelectedTarget.TextureModded) { var res = M3L.ShowDialog(Window.GetWindow(this), M3L.GetString(M3L.string_interp_deletingXwhileAlotInstalledUnsupported, mod.ModName), M3L.GetString(M3L.string_deletingWillPutAlotInUnsupportedConfig), MessageBoxButton.YesNo, MessageBoxImage.Error); return(res == MessageBoxResult.Yes); } return(M3L.ShowDialog(Window.GetWindow(this), M3L.GetString(M3L.string_interp_removeXFromTheGameInstallationQuestion, mod.ModName), M3L.GetString(M3L.string_confirmDeletion), MessageBoxButton.YesNo, MessageBoxImage.Warning) == MessageBoxResult.Yes); } void notifyDeleted() { PopulateUI(); } SelectedTarget.PopulateDLCMods(true, deleteConfirmationCallback, notifyDeleted); SelectedTarget.PopulateExtras(); bool restoreBasegamefileConfirmationCallback(string filepath) { if (Utilities.IsGameRunning(SelectedTarget.Game)) { M3L.ShowDialog(Window.GetWindow(this), M3L.GetString(M3L.string_interp_cannotRestoreFilesWhileXIsRunning, Utilities.GetGameName(SelectedTarget.Game)), M3L.GetString(M3L.string_gameRunning), MessageBoxButton.OK, MessageBoxImage.Error); return(false); } if (SelectedTarget.TextureModded && filepath.RepresentsPackageFilePath()) { if (!Settings.DeveloperMode) { M3L.ShowDialog(Window.GetWindow(this), M3L.GetString(M3L.string_interp_restoringXWhileAlotInstalledIsNotAllowed, Path.GetFileName(filepath)), M3L.GetString(M3L.string_cannotRestorePackageFiles), MessageBoxButton.OK, MessageBoxImage.Error); return(false); } else { var res = M3L.ShowDialog(Window.GetWindow(this), M3L.GetString(M3L.string_interp_restoringXwhileAlotInstalledLikelyBreaksThingsDevMode, Path.GetFileName(filepath)), M3L.GetString(M3L.string_invalidTexturePointersWarning), MessageBoxButton.YesNo, MessageBoxImage.Warning); return(res == MessageBoxResult.Yes); } } bool?holdingShift = Keyboard.Modifiers == ModifierKeys.Shift; if (!holdingShift.Value) { holdingShift = null; } return(holdingShift ?? M3L.ShowDialog(Window.GetWindow(this), M3L.GetString(M3L.string_interp_restoreXquestion, Path.GetFileName(filepath)), M3L.GetString(M3L.string_confirmRestoration), MessageBoxButton.YesNo, MessageBoxImage.Warning) == MessageBoxResult.Yes); } bool restoreSfarConfirmationCallback(string sfarPath) { if (Utilities.IsGameRunning(SelectedTarget.Game)) { M3L.ShowDialog(Window.GetWindow(this), M3L.GetString(M3L.string_interp_cannotRestoreFilesWhileXIsRunning, Utilities.GetGameName(SelectedTarget.Game)), M3L.GetString(M3L.string_gameRunning), MessageBoxButton.OK, MessageBoxImage.Error); return(false); } if (SelectedTarget.TextureModded) { if (!Settings.DeveloperMode) { M3L.ShowDialog(Window.GetWindow(this), M3L.GetString(M3L.string_restoringSfarsAlotBlocked), M3L.GetString(M3L.string_cannotRestoreSfarFiles), MessageBoxButton.OK, MessageBoxImage.Error); return(false); } else { var res = M3L.ShowDialog(Window.GetWindow(this), M3L.GetString(M3L.string_restoringSfarsAlotDevMode), M3L.GetString(M3L.string_invalidTexturePointersWarning), MessageBoxButton.YesNo, MessageBoxImage.Warning); return(res == MessageBoxResult.Yes); } } //Todo: warn of unpacked file deletion bool?holdingShift = Keyboard.Modifiers == ModifierKeys.Shift; if (!holdingShift.Value) { holdingShift = null; } return(holdingShift ?? M3L.ShowDialog(Window.GetWindow(this), M3L.GetString(M3L.string_interp_restoreXquestion, sfarPath), M3L.GetString(M3L.string_confirmRestoration), MessageBoxButton.YesNo, MessageBoxImage.Warning) == MessageBoxResult.Yes); } void notifyStartingSfarRestoreCallback() { SFARBeingRestored = true; } void notifyStartingBasegameFileRestoreCallback() { BasegameFilesBeingRestored = true; } void notifyRestoredCallback(object itemRestored) { if (itemRestored is GameTarget.ModifiedFileObject mf) { Application.Current.Dispatcher.Invoke(delegate { SelectedTarget.ModifiedBasegameFiles.Remove(mf); }); bool resetBasegameFilesBeingRestored = SelectedTarget.ModifiedBasegameFiles.Count == 0; if (!resetBasegameFilesBeingRestored) { resetBasegameFilesBeingRestored = !SelectedTarget.ModifiedBasegameFiles.Any(x => x.Restoring); } if (resetBasegameFilesBeingRestored) { BasegameFilesBeingRestored = false; } } else if (itemRestored is GameTarget.SFARObject ms) { if (!ms.IsModified) { SelectedTarget.ModifiedSFARFiles.Remove(ms); } bool resetSfarsBeingRestored = SelectedTarget.ModifiedSFARFiles.Count == 0; if (!resetSfarsBeingRestored) { resetSfarsBeingRestored = !SelectedTarget.ModifiedSFARFiles.Any(x => x.Restoring); } if (resetSfarsBeingRestored) { SFARBeingRestored = false; } } else if (itemRestored == null) { //restore failed. bool resetSfarsBeingRestored = SelectedTarget.ModifiedSFARFiles.Count == 0; if (!resetSfarsBeingRestored) { resetSfarsBeingRestored = !SelectedTarget.ModifiedSFARFiles.Any(x => x.Restoring); } if (resetSfarsBeingRestored) { SFARBeingRestored = false; } bool resetBasegameFilesBeingRestored = SelectedTarget.ModifiedBasegameFiles.Count == 0; if (!resetBasegameFilesBeingRestored) { resetBasegameFilesBeingRestored = !SelectedTarget.ModifiedBasegameFiles.Any(x => x.Restoring); } if (resetBasegameFilesBeingRestored) { BasegameFilesBeingRestored = false; } } } SelectedTarget.PopulateModifiedBasegameFiles(restoreBasegamefileConfirmationCallback, restoreSfarConfirmationCallback, notifyStartingSfarRestoreCallback, notifyStartingBasegameFileRestoreCallback, notifyRestoredCallback); SFARBeingRestored = false; SelectedTarget.PopulateASIInfo(); SelectedTarget.PopulateBinkInfo(); if (SelectedTarget != null && !SelectedTarget.TextureModded) { NamedBackgroundWorker nbw = new NamedBackgroundWorker(@"BasegameSourceIdentifier"); nbw.DoWork += (a, b) => { foreach (var v in SelectedTarget.ModifiedBasegameFiles.ToList()) { v.DetermineSource(); } }; nbw.RunWorkerAsync(); } }; bw.RunWorkerAsync(); }
private void SetME1MiniKey() { KeyBeingAssigned = M3L.GetString(M3L.string_massEffectMiniConsole); IsListeningForKey = true; OnKeyPressed = SetME1MiniKeyCallback; }
private void RestoreAllBasegame() { bool restore = false; if (SelectedTarget.TextureModded) { if (!Settings.DeveloperMode) { M3L.ShowDialog(Window.GetWindow(this), M3L.GetString(M3L.string_dialogRestoringFilesWhileAlotIsInstalledNotAllowed), M3L.GetString(M3L.string_cannotRestoreSfarFiles), MessageBoxButton.OK, MessageBoxImage.Error); return; } else { var res = M3L.ShowDialog(Window.GetWindow(this), M3L.GetString(M3L.string_dialogRestoringFilesWhileAlotIsInstalledNotAllowedDevMode), M3L.GetString(M3L.string_invalidTexturePointersWarning), MessageBoxButton.YesNo, MessageBoxImage.Warning); restore = res == MessageBoxResult.Yes; } } else { restore = M3L.ShowDialog(Window.GetWindow(this), M3L.GetString(M3L.string_restoreAllModifiedFilesQuestion), M3L.GetString(M3L.string_confirmRestoration), MessageBoxButton.YesNo, MessageBoxImage.Warning) == MessageBoxResult.Yes; } if (restore) { NamedBackgroundWorker nbw = new NamedBackgroundWorker(@"RestoreAllBasegameFilesThread"); nbw.DoWork += (a, b) => { RestoreAllBasegameInProgress = true; var restorableFiles = SelectedTarget.ModifiedBasegameFiles.Where(x => x.CanRestoreFile()).ToList(); //Set UI states foreach (var v in restorableFiles) { v.Restoring = true; } //Restore files foreach (var v in restorableFiles) //to list will make sure this doesn't throw concurrent modification { v.RestoreFile(true); } }; nbw.RunWorkerCompleted += (a, b) => { if (b.Error != null) { Log.Error($@"Exception occurred in {nbw.Name} thread: {b.Error.Message}"); } RestoreAllBasegameInProgress = false; if (SelectedTarget.Game == Mod.MEGame.ME3) { AutoTOC.RunTOCOnGameTarget(SelectedTarget); } CommandManager.InvalidateRequerySuggested(); }; nbw.RunWorkerAsync(); } }
private void RestoreAllSFARs() { bool restore; if (SelectedTarget.TextureModded) { if (!Settings.DeveloperMode) { M3L.ShowDialog(Window.GetWindow(this), M3L.GetString(M3L.string_restoringSfarsAlotBlocked), M3L.GetString(M3L.string_cannotRestoreSfarFiles), MessageBoxButton.OK, MessageBoxImage.Error); return; } else { var res = M3L.ShowDialog(Window.GetWindow(this), M3L.GetString(M3L.string_restoringSfarsAlotDevMode), M3L.GetString(M3L.string_invalidTexturePointersWarning), MessageBoxButton.YesNo, MessageBoxImage.Warning); restore = res == MessageBoxResult.Yes; } } else { restore = M3L.ShowDialog(Window.GetWindow(this), M3L.GetString(M3L.string_restoreAllModifiedSfarsQuestion), M3L.GetString(M3L.string_confirmRestoration), MessageBoxButton.YesNo, MessageBoxImage.Warning) == MessageBoxResult.Yes; } if (restore) { foreach (var v in SelectedTarget.ModifiedSFARFiles) { SelectedTarget.RestoreSFAR(v, true); } } }
public override void OnPanelVisible() { SaveFileDialog d = new SaveFileDialog { Filter = $@"{M3L.GetString(M3L.string_7zipArchiveFile)}|*.7z", FileName = Utilities.SanitizePath($@"{ModForArchive.ModName}_{ModForArchive.ModVersionString}".Replace(@" ", ""), true) }; var outputarchive = d.ShowDialog(); if (outputarchive.HasValue && outputarchive.Value) { var nbw = new NamedBackgroundWorker(@"TestArchiveGenerator"); nbw.DoWork += (a, b) => { var stagingPath = Directory.CreateDirectory(Path.Combine(Utilities.GetTempPath(), @"TestGenerator")).FullName; var referencedFiles = ModForArchive.GetAllRelativeReferences(); int numdone = 0; ActionText = M3L.GetString(M3L.string_hashingFiles); Parallel.ForEach(referencedFiles, new ParallelOptions() { MaxDegreeOfParallelism = 3 }, (x) => { var sourcefile = Path.Combine(ModForArchive.ModPath, x); var destfile = Path.Combine(stagingPath, x); Log.Information(@"Hashing " + sourcefile); var md5 = Utilities.CalculateMD5(sourcefile); Directory.CreateDirectory(Directory.GetParent(destfile).FullName); Log.Information(@"Writing blank hash file " + destfile); File.WriteAllText(destfile, md5); var done = Interlocked.Increment(ref numdone); Percent = (int)(done * 100.0 / referencedFiles.Count); }); Log.Information(@"Copying moddesc.ini"); File.Copy(ModForArchive.ModDescPath, Path.Combine(stagingPath, @"moddesc.ini"), true); Mod testmod = new Mod(Path.Combine(stagingPath, @"moddesc.ini"), MEGame.Unknown); if (testmod.ValidMod) { ActionText = M3L.GetString(M3L.string_creatingArchive); SevenZipCompressor svc = new SevenZipCompressor(); svc.Progressing += (o, details) => { Percent = (int)(details.AmountCompleted * 100.0 / details.TotalAmount); }; svc.CompressDirectory(stagingPath, d.FileName); Utilities.HighlightInExplorer(d.FileName); } }; nbw.RunWorkerCompleted += (a, b) => { if (b.Error != null) { Log.Error($@"Exception occurred in {nbw.Name} thread: {b.Error.Message}"); } OnClosing(DataEventArgs.Empty); }; nbw.RunWorkerAsync(); } else { OnClosing(DataEventArgs.Empty); } }
private void ToggleShowingInstallOptions_Click(object sender, RoutedEventArgs e) { ShowInstalledOptions = !ShowInstalledOptions; ShowInstalledOptionsText = ShowInstalledOptions ? M3L.GetString(M3L.string_hideInstalledOptions) : M3L.GetString(M3L.string_showInstalledOptions); }
public static TelemetryPackage GetTelemetryPackageForDLC(MEGame game, string dlcDirectory, string dlcFoldername, string destinationDLCName, string modName, string modAuthor, string modSite, Mod telemetryMod) { try { TelemetryPackage tp = new TelemetryPackage(); var sourceDir = Path.Combine(dlcDirectory, dlcFoldername); tp.DLCFolderName = destinationDLCName; //this most times will be the same as dlcFoldername, but in case of alternates, it might not be if (telemetryMod != null && telemetryMod.HumanReadableCustomDLCNames.TryGetValue(dlcFoldername, out var modNameIni)) { tp.ModName = modNameIni; } else { tp.ModName = modName; } tp.ModAuthor = modAuthor; tp.ModSite = modSite; tp.Game = game; switch (game) { case MEGame.LE1: case MEGame.ME1: { var parsedIni = DuplicatingIni.LoadIni(Path.Combine(sourceDir, @"AutoLoad.ini")); tp.MountPriority = int.Parse(parsedIni[@"ME1DLCMOUNT"][@"ModMount"]?.Value); tp.ModMountTLK1 = int.Parse(parsedIni[@"GUI"][@"NameStrRef"]?.Value); tp.MountFlagHR = M3L.GetString(M3L.string_me1MountFlagsNotSupportedInM3); //No mount flag right now. } break; case MEGame.ME2: case MEGame.LE2: { var mountFile = Path.Combine(sourceDir, game.CookedDirName(), @"mount.dlc"); MountFile mf = new MountFile(mountFile); tp.ModMountTLK1 = mf.TLKID; tp.MountPriority = mf.MountPriority; tp.MountFlag = (int)mf.MountFlags.FlagValue; tp.MountFlagHR = mf.MountFlags.ToHumanReadableString(); var ini = DuplicatingIni.LoadIni(Path.Combine(sourceDir, game.CookedDirName(), @"BIOEngine.ini")); tp.ModuleNumber = ini[@"Engine.DLCModules"][dlcFoldername]?.Value; } break; case MEGame.ME3: case MEGame.LE3: { var mountFile = Path.Combine(sourceDir, @"CookedPCConsole", @"mount.dlc"); MountFile mf = new MountFile(mountFile); tp.ModMountTLK1 = mf.TLKID; tp.MountPriority = mf.MountPriority; tp.MountFlag = mf.MountFlags.FlagValue; tp.MountFlagHR = mf.MountFlags.ToHumanReadableString(); } break; } return(tp); } catch (Exception e) { Log.Error($@"Error building telemetry package for {dlcFoldername}: {e.Message}."); return(null); } }
private void ApplyUpdateToMod(object obj) { if (obj is OnlineContent.ModUpdateInfo ui) { NamedBackgroundWorker bw = new NamedBackgroundWorker(@"ModUpdaterThread-" + ui.mod.ModName); bw.DoWork += (a, b) => { ui.UpdateInProgress = true; ui.Indeterminate = false; ui.DownloadButtonText = M3L.GetString(M3L.string_downloading); //void updateProgressCallback(long bytesReceived, long totalBytes) //{ // ui.By //} bool errorShown = false; void errorCallback(string message) { if (!errorShown) { errorShown = true; Application.Current.Dispatcher.Invoke(delegate { M3L.ShowDialog(window, M3L.GetString(M3L.string_interp_errorOccuredWhileUpdatingXErrorMessage, ui.mod.ModName, message), M3L.GetString(M3L.string_interp_errorUpdatingX, ui.mod.ModName), MessageBoxButton.OK, MessageBoxImage.Error); } ); } } var stagingDirectory = Directory.CreateDirectory(Path.Combine(Utilities.GetTempPath(), Path.GetFileName(ui.mod.ModPath))).FullName; var modUpdated = OnlineContent.UpdateMod(ui, stagingDirectory, errorCallback); ui.UpdateInProgress = false; ui.CanUpdate = !modUpdated; AnyModUpdated |= modUpdated; ui.DownloadButtonText = ui.CanUpdate ? M3L.GetString(M3L.string_downloadUpdate) : M3L.GetString(M3L.string_updated); Utilities.DeleteFilesAndFoldersRecursively(stagingDirectory); }; bw.RunWorkerCompleted += (a, b) => { CommandManager.InvalidateRequerySuggested(); }; bw.RunWorkerAsync(); } }
private void LoadME2Keys(GameTarget target) { if (target.Game != Mod.MEGame.ME2) { throw new Exception(@"Cannot load ME2 keys from target that is not ME2"); } ME2Coalesced me2c = null; try { me2c = ME2Coalesced.OpenFromTarget(target, true); } catch (Exception e) { Application.Current.Dispatcher.Invoke(delegate { M3L.ShowDialog(window, M3L.GetString(M3L.string_interp_cannotOpenMassEffect2CoalescediniMessage, e.Message), M3L.GetString(M3L.string_errorReadingCoalescedini), MessageBoxButton.OK, MessageBoxImage.Error); }); ME2MiniConsoleKeyText = M3L.GetString(M3L.string_errorReadingCoalescedini); ME2MiniConsoleKeyText = M3L.GetString(M3L.string_errorReadingCoalescedini); return; } var bioinput = me2c.Inis.FirstOrDefault(x => Path.GetFileName(x.Key).Equals(@"BioInput.ini", StringComparison.InvariantCultureIgnoreCase)); var engineConsole = bioinput.Value.Sections.FirstOrDefault(x => x.Header == @"Engine.Console"); if (engineConsole != null) { var consoleKey = engineConsole.Entries.FirstOrDefault(x => x.Key == @"ConsoleKey"); if (consoleKey == null) { ME2FullConsoleKeyText = M3L.GetString(M3L.string_fullConsoleNotBoundToAKey); } else { ME2FullConsoleKeyText = M3L.GetString(M3L.string_interp_fullConsoleBoundToX, consoleKey.Value); } var typeKey = engineConsole.Entries.FirstOrDefault(x => x.Key == @"TypeKey"); if (typeKey == null) { ME2MiniConsoleKeyText = M3L.GetString(M3L.string_miniConsoleNotBoundToAKey); } else { ME2MiniConsoleKeyText = M3L.GetString(M3L.string_interp_miniConsoleBoundToX, typeKey.Value); } } }
/// <summary> /// Loads the information about this nxmlink into this object. Subscribe to OnInitialized() to know when it has initialized and is ready for download to begin. /// THIS IS A BLOCKING CALL DO NOT RUN ON THE UI /// </summary> public void Initialize() { Log.Information($@"Initializing {NXMLink}"); Task.Run(() => { try { DownloadLinks.Clear(); var nxmlink = NXMLink.Substring(6); var queryPos = nxmlink.IndexOf('?'); var info = queryPos > 0 ? nxmlink.Substring(0, queryPos) : nxmlink; var infos = info.Split('/'); domain = infos[0]; var modid = int.Parse(infos[2]); var fileid = int.Parse(infos[4]); if (!NexusModsUtilities.AllSupportedNexusDomains.Contains(domain)) { Log.Error($@"Cannot download file from unsupported domain: {domain}. Open your preferred mod manager from that game first"); Initialized = true; ProgressIndeterminate = false; OnModDownloadError?.Invoke(this, M3L.GetString(M3L.string_interp_dialog_modNotForThisModManager, domain)); return; } ModFile = NexusModsUtilities.GetClient().ModFiles.GetModFile(domain, modid, fileid).Result; if (ModFile != null) { if (ModFile.Category != FileCategory.Deleted) { if (queryPos > 0) { // download with manager string querystring = nxmlink.Substring(queryPos); var parameters = HttpUtility.ParseQueryString(querystring); // Check if parameters are correct! DownloadLinks.AddRange(NexusModsUtilities.GetDownloadLinkForFile(domain, modid, fileid, parameters[@"key"], int.Parse(parameters[@"expires"])).Result); } else { // premium? if (!NexusModsUtilities.UserInfo.IsPremium) { Log.Error( $@"Cannot download {ModFile.FileName}: User is not premium, but this link is not generated from NexusMods"); Initialized = true; ProgressIndeterminate = false; OnModDownloadError?.Invoke(this, M3L.GetString(M3L.string_dialog_mustBePremiumUserToDownload)); return; } DownloadLinks.AddRange(NexusModsUtilities.GetDownloadLinkForFile(domain, modid, fileid) ?.Result); } ProgressMaximum = ModFile.Size * 1024; // Bytes Initialized = true; Log.Error($@"ModDownload has initialized: {ModFile.FileName}"); OnInitialized?.Invoke(this, null); } else { Log.Error($@"Cannot download {ModFile.FileName}: File deleted from NexusMods"); Initialized = true; ProgressIndeterminate = false; OnModDownloadError?.Invoke(this, M3L.GetString(M3L.string_dialog_cannotDownloadDeletedFile)); } } } catch (Exception e) { Log.Error($@"Error downloading {ModFile?.FileName}: {e.Message}"); Initialized = true; ProgressIndeterminate = false; OnModDownloadError?.Invoke(this, M3L.GetString(M3L.string_interp_errorDownloadingModX, e.Message)); } }); }
private void LoadME3Keys(GameTarget target) { if (target.Game != Mod.MEGame.ME3) { throw new Exception(@"Cannot load ME3 keys from target that is not ME3"); } try { var coalPath = Path.Combine(target.TargetPath, @"BioGame", @"CookedPCConsole", @"Coalesced.bin"); Dictionary <string, string> coalescedFilemapping = null; if (File.Exists(coalPath)) { using FileStream fs = new FileStream(coalPath, FileMode.Open); coalescedFilemapping = MassEffect3.Coalesce.Converter.DecompileToMemory(fs); } else { Log.Error( @"Could not get file data for coalesced chunk BASEGAME as Coalesced.bin file was missing"); return; } var bioinputText = coalescedFilemapping[@"BioInput.xml"]; var coalFileDoc = XDocument.Parse(bioinputText); var consolekey = coalFileDoc.XPathSelectElement( @"/CoalesceAsset/Sections/Section[@name='engine.console']/Property[@name='consolekey']"); var typekey = coalFileDoc.XPathSelectElement( @"/CoalesceAsset/Sections/Section[@name='engine.console']/Property[@name='typekey']"); if (consolekey != null) { ME3FullConsoleKeyText = M3L.GetString(M3L.string_interp_fullConsoleBoundToX, consolekey.Value); } else { ME3FullConsoleKeyText = M3L.GetString(M3L.string_fullConsoleNotBoundToAKey); } if (typekey != null) { ME3MiniConsoleKeyText = M3L.GetString(M3L.string_interp_miniConsoleBoundToX, typekey.Value); } else { ME3MiniConsoleKeyText = M3L.GetString(M3L.string_miniConsoleNotBoundToAKey); } } catch (Exception e) { Log.Error(@"Error reading keybinds: " + e.Message); M3L.ShowDialog(window, M3L.GetString(M3L.string_interp_cannotReadME3Keybinds, e.Message), M3L.GetString(M3L.string_errorReadingKeybinds), MessageBoxButton.OK, MessageBoxImage.Error); } }
private void RefreshBinkStatus() { LoaderInstalled = SelectedTarget != null && Utilities.CheckIfBinkw32ASIIsInstalled(SelectedTarget); InstallLoaderText = LoaderInstalled ? M3L.GetString(M3L.string_loaderInstalled) : M3L.GetString(M3L.string_installLoader); }
private void CopyItemsToClipBoard_Click(object sender, RoutedEventArgs e) { string toClipboard = string.Join(Environment.NewLine, Messages.Select(x => x.ToRawString())); try { Clipboard.SetText(toClipboard); StatusText = M3L.GetString(M3L.string_copiedToClipboard); } catch (Exception ex) { //yes, this actually happens sometimes... M3L.ShowDialog(this, M3L.GetString(M3L.string_dialogCouldNotSetDataToClipboard) + ex.Message, M3L.GetString(M3L.string_errorCopyingDataToClipboard), MessageBoxButton.OK, MessageBoxImage.Error); } }
public static void DownloadToolGithub(string localToolFolderName, string tool, List <Release> releases, string executable, Action <string> currentTaskUpdateCallback = null, Action <bool> setPercentVisibilityCallback = null, Action <int> setPercentTaskDone = null, Action <string> resultingExecutableStringCallback = null, Action <Exception, string, string> errorExtractingCallback = null) { Release latestRelease = null; ReleaseAsset asset = null; Uri downloadLink = null; currentTaskUpdateCallback?.Invoke(M3L.GetString(M3L.string_interp_downloadingX, tool)); setPercentVisibilityCallback?.Invoke(true); setPercentTaskDone?.Invoke(0); foreach (var release in releases) { //Get asset info asset = release.Assets.FirstOrDefault(); if (Path.GetFileName(executable) == @"MassEffectModder.exe") { //Requires specific asset asset = release.Assets.FirstOrDefault(x => x.Name == @"MassEffectModder-v" + release.TagName + @".7z"); if (asset == null) { Log.Warning( $@"No applicable assets in release tag {release.TagName} for MassEffectModder, skipping"); continue; } latestRelease = release; downloadLink = new Uri(asset.BrowserDownloadUrl); break; } if (Path.GetFileName(executable) == @"MassEffectModderNoGui.exe") { //Requires specific asset asset = release.Assets.FirstOrDefault(x => x.Name == @"MassEffectModderNoGui-v" + release.TagName + @".7z"); if (asset == null) { Log.Warning( $@"No applicable assets in release tag {release.TagName} for MassEffectModderNoGui, skipping"); continue; } latestRelease = release; downloadLink = new Uri(asset.BrowserDownloadUrl); break; } if (asset != null) { latestRelease = release; Log.Information($@"Using release {latestRelease.Name}"); downloadLink = new Uri(asset.BrowserDownloadUrl); break; } } if (latestRelease == null || downloadLink == null) { return; } Analytics.TrackEvent(@"Downloading new external tool", new Dictionary <string, string>() { { @"Tool name", Path.GetFileName(executable) }, { @"Version", latestRelease.TagName } }); currentTaskUpdateCallback?.Invoke($@"{M3L.GetString(M3L.string_interp_downloadingX, tool)} {latestRelease.TagName}"); WebClient downloadClient = new WebClient(); downloadClient.Headers[@"Accept"] = @"application/vnd.github.v3+json"; downloadClient.Headers[@"user-agent"] = @"ME3TweaksModManager"; string temppath = Utilities.GetTempPath(); downloadClient.DownloadProgressChanged += (s, e) => { setPercentTaskDone?.Invoke(e.ProgressPercentage); }; var extension = Path.GetExtension(asset.BrowserDownloadUrl); string downloadPath = Path.Combine(temppath, tool.Replace(@" ", "") + extension); downloadClient.DownloadFileCompleted += (a, b) => { extractTool(tool, executable, extension, downloadPath, currentTaskUpdateCallback, setPercentVisibilityCallback, setPercentTaskDone, resultingExecutableStringCallback, errorExtractingCallback); }; Log.Information(@"Downloading file: " + downloadLink); downloadClient.DownloadFileAsync(downloadLink, downloadPath); }
/// <summary> /// This object now has enough variables set to resolve localization strings /// </summary> internal void SetLocalizedInfo() { LocalizedLocalVersionString = M3L.GetString(M3L.string_interp_localVersion, mod.ModVersionString); LocalizedServerVersionString = M3L.GetString(M3L.string_interp_serverVersion, versionstr); }
public static async void FetchAndLaunchTool(string tool, Action <string> currentTaskUpdateCallback = null, Action <bool> setPercentVisibilityCallback = null, Action <int> setPercentTaskDone = null, Action <string> resultingExecutableStringCallback = null, Action failedToDownloadCallback = null, Action <Exception, string, string> errorExtractingCallback = null) { Log.Information($@"FetchAndLaunchTool() for {tool}"); var toolName = tool.Replace(@" ", ""); var localToolFolderName = getToolStoragePath(tool); var localExecutable = Path.Combine(localToolFolderName, toolNameToExeName(tool)); bool needsDownloading = !File.Exists(localExecutable); if (!needsDownloading && ToolsCheckedForUpdatesInThisSession.Contains(tool)) { //Don't check for updates again. resultingExecutableStringCallback?.Invoke(localExecutable); return; } if (toolIsGithubBased(tool)) { currentTaskUpdateCallback?.Invoke(M3L.GetString(M3L.string_checkingForUpdates)); var releases = await FetchReleases(tool); //Failed to get release check if (releases == null) { if (!needsDownloading) { resultingExecutableStringCallback?.Invoke(localExecutable); return; } else { //Must run on UI thread //MessageBox.Show($"Unable to download {tool}.\nPlease check your network connection and try again.\nIf the issue persists, please come to the ME3Tweaks Discord."); Log.Error(@"Unable to launch tool - could not download, and does not exist locally: " + localExecutable); failedToDownloadCallback?.Invoke(); return; } } //Got a release if (needsDownloading) { DownloadToolGithub(localToolFolderName, tool, releases, localExecutable, s => currentTaskUpdateCallback?.Invoke(s), vis => setPercentVisibilityCallback?.Invoke(vis), percent => setPercentTaskDone?.Invoke(percent), exe => resultingExecutableStringCallback?.Invoke(exe), (exception, message, caption) => errorExtractingCallback?.Invoke(exception, message, caption) ); } else { //Check if it need updated bool needsUpdated = false; var latestRelease = releases.FirstOrDefault(x => hasApplicableAsset(tool, x)); if (latestRelease != null) { FileVersionInfo fvi = FileVersionInfo.GetVersionInfo(localExecutable); if (tool == MEM || tool == MEM_CMD) { //Checks based on major int releaseVer = int.Parse(latestRelease.TagName); if (releaseVer > fvi.ProductMajorPart) { needsUpdated = true; } } else { try { Version serverVersion = new Version(latestRelease.TagName); Version localVersion = new Version( $@"{fvi.FileMajorPart}.{fvi.FileMinorPart}.{fvi.FileBuildPart}.{fvi.FilePrivatePart}"); if (serverVersion > localVersion) { needsUpdated = true; } } catch (Exception e) { Log.Error($@"Invalid version number on release {latestRelease.TagName}: {e.Message}"); } } } if (!needsUpdated) { resultingExecutableStringCallback?.Invoke(localExecutable); } else { DownloadToolGithub(localToolFolderName, tool, releases, localExecutable, s => currentTaskUpdateCallback?.Invoke(s), vis => setPercentVisibilityCallback?.Invoke(vis), percent => setPercentTaskDone?.Invoke(percent), exe => resultingExecutableStringCallback?.Invoke(exe), (exception, message, caption) => errorExtractingCallback?.Invoke(exception, message, caption) ); } } } else { //Not github based string downloadLink = me3tweaksToolGetDownloadUrl(tool); Version downloadVersion = me3tweaksToolGetLatestVersion(tool); if (downloadVersion != null && downloadLink != null) // has enough info { if (needsDownloading) // not present locally { DownloadToolME3Tweaks(tool, downloadLink, downloadVersion, localExecutable, s => currentTaskUpdateCallback?.Invoke(s), vis => setPercentVisibilityCallback?.Invoke(vis), percent => setPercentTaskDone?.Invoke(percent), exe => resultingExecutableStringCallback?.Invoke(exe), (exception, message, caption) => errorExtractingCallback?.Invoke(exception, message, caption) ); ToolsCheckedForUpdatesInThisSession.Add(tool); return; //is this the right place for this? } else { //Check if it need updated FileVersionInfo fvi = FileVersionInfo.GetVersionInfo(localExecutable); Version localVersion = new Version($@"{fvi.FileMajorPart}.{fvi.FileMinorPart}.{fvi.FileBuildPart}.{fvi.FilePrivatePart}"); if (downloadVersion > localVersion) { needsDownloading = true; } } if (!needsDownloading) { resultingExecutableStringCallback?.Invoke(localExecutable); } else { DownloadToolME3Tweaks(tool, downloadLink, downloadVersion, localExecutable, s => currentTaskUpdateCallback?.Invoke(s), vis => setPercentVisibilityCallback?.Invoke(vis), percent => setPercentTaskDone?.Invoke(percent), exe => resultingExecutableStringCallback?.Invoke(exe), (exception, message, caption) => errorExtractingCallback?.Invoke(exception, message, caption) ); } } } ToolsCheckedForUpdatesInThisSession.Add(tool); }
private void FetchME2File() { BackupFile fileTofetch = SelectedME2File; SaveFileDialog m = new SaveFileDialog { Title = M3L.GetString(M3L.string_selectDestinationLocation), Filter = M3L.GetString(M3L.string_packageFile) + @"|*" + Path.GetExtension(fileTofetch.Filename), FileName = fileTofetch.Filename }; var result = m.ShowDialog(mainwindow); if (result.Value) { if (fileTofetch.Module == @"BASEGAME") { var fetchedfilestream = VanillaDatabaseService.FetchBasegameFile(Mod.MEGame.ME2, fileTofetch.Filename); fetchedfilestream.WriteToFile(m.FileName); } else { var fetchedfilestream = VanillaDatabaseService.FetchME1ME2DLCFile(Mod.MEGame.ME2, fileTofetch.Module, fileTofetch.Filename); fetchedfilestream.WriteToFile(m.FileName); } } M3L.ShowDialog(window, M3L.GetString(M3L.string_interp_fileFetchedAndWrittenToX, m.FileName), M3L.GetString(M3L.string_fileFetched)); }
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(); }
/// <summary> /// Validates a game directory by checking for multiple things that should be present in a working game. /// </summary> /// <param name="ignoreCmmVanilla">Ignore the check for a cmm_vanilla file. Presence of this file will cause validation to fail</param> /// <returns>String of failure reason, null if OK</returns> public string ValidateTarget(bool ignoreCmmVanilla = false) { if (!Selectable) { return(null); } IsValid = false; //set to invalid at first/s string[] validationFiles = null; switch (Game) { case Mod.MEGame.ME1: validationFiles = new[] { Path.Combine(TargetPath, @"Binaries", @"MassEffect.exe"), Path.Combine(TargetPath, @"BioGame", @"CookedPC", @"Maps", @"EntryMenu.SFM"), Path.Combine(TargetPath, @"BioGame", @"CookedPC", @"BIOC_Base.u"), Path.Combine(TargetPath, @"BioGame", @"CookedPC", @"Packages", @"Textures", @"BIOA_GLO_00_A_Opening_FlyBy_T.upk"), Path.Combine(TargetPath, @"BioGame", @"CookedPC", @"Maps", @"WAR", @"LAY", @"BIOA_WAR20_05_LAY.SFM"), Path.Combine(TargetPath, @"BioGame", @"CookedPC", @"Movies", @"MEvisionSEQ3.bik") }; break; case Mod.MEGame.ME2: validationFiles = new[] { Path.Combine(TargetPath, @"Binaries", @"MassEffect2.exe"), Path.Combine(TargetPath, @"BioGame", @"CookedPC", @"BioA_BchLmL.pcc"), Path.Combine(TargetPath, @"BioGame", @"Config", @"PC", @"Cooked", @"Coalesced.ini"), Path.Combine(TargetPath, @"BioGame", @"CookedPC", @"Wwise_Jack_Loy_Music.afc"), Path.Combine(TargetPath, @"BioGame", @"CookedPC", @"WwiseAudio.pcc"), Path.Combine(TargetPath, @"BioGame", @"Movies", @"Crit03_CollectArrive_Part2_1.bik") }; break; case Mod.MEGame.ME3: validationFiles = new[] { Path.Combine(TargetPath, @"Binaries", @"win32", @"MassEffect3.exe"), Path.Combine(TargetPath, @"BioGame", @"CookedPCConsole", @"Textures.tfc"), Path.Combine(TargetPath, @"BioGame", @"CookedPCConsole", @"Startup.pcc"), Path.Combine(TargetPath, @"BioGame", @"CookedPCConsole", @"Coalesced.bin"), Path.Combine(TargetPath, @"BioGame", @"Patches", @"PCConsole", @"Patch_001.sfar"), Path.Combine(TargetPath, @"BioGame", @"CookedPCConsole", @"Textures.tfc"), Path.Combine(TargetPath, @"BioGame", @"CookedPCConsole", @"citwrd_rp1_bailey_m_D_Int.afc") }; break; } if (validationFiles == null) { return(null); //Invalid game. } foreach (var f in validationFiles) { if (!File.Exists(f)) { return(M3L.GetString(M3L.string_interp_invalidTargetMissingFile, Path.GetFileName(f))); } } if (!ignoreCmmVanilla) { if (File.Exists(Path.Combine(TargetPath, @"cmm_vanilla"))) { return(M3L.GetString(M3L.string_invalidTargetProtectedByCmmvanilla)); } } IsValid = true; return(null); }
public override void OnPanelVisible() { var installedDLCMods = VanillaDatabaseService.GetInstalledDLCMods(target); var uiModInstalled = installedDLCMods.Intersect(DLCUIModFolderNames).Any(); if (uiModInstalled) { var result = M3L.ShowDialog(window, M3L.GetString(M3L.string_dialogWhatGuiCompatPackIsFor), M3L.GetString(M3L.string_confirmGeneration), MessageBoxButton.YesNo, MessageBoxImage.Warning); if (result == MessageBoxResult.No) { OnClosing(DataEventArgs.Empty); return; } Log.Information(@"Starting GUI compatibility scanner"); StartGuiCompatibilityScanner(); } else { Log.Information(@"No UI mods are installed. Cannot run GUI Compat Generator"); M3L.ShowDialog(window, M3L.GetString(M3L.string_dialogNoUiModsAreInstalled), M3L.GetString(M3L.string_noUiModsAreInstalled), MessageBoxButton.OK, MessageBoxImage.Error); OnClosing(DataEventArgs.Empty); } }
public void RestoreSFAR(bool batchRestore, Action signalRestoreCompleted = null) { bool?restore = batchRestore; if (!restore.Value) { restore = RestoreConfirmationCallback?.Invoke(FilePath); } if (restore.HasValue && restore.Value) { NamedBackgroundWorker nbw = new NamedBackgroundWorker(@"RestoreSFARThread"); nbw.DoWork += (a, b) => { var backupFile = Path.Combine(BackupService.GetGameBackupPath(target.Game), FilePath); var targetFile = Path.Combine(target.TargetPath, FilePath); Restoring = true; var unpackedFiles = Directory.GetFiles(DLCDirectory, @"*", SearchOption.AllDirectories); RestoreButtonContent = M3L.GetString(M3L.string_cleaningUp); foreach (var file in unpackedFiles) { if (!file.EndsWith(@".sfar")) { Log.Information(@"Deleting unpacked file: " + file); File.Delete(file); } } // Check if we actually need to restore SFAR if (new FileInfo(targetFile).Length == 32 || !VanillaDatabaseService.IsFileVanilla(target, targetFile, false)) { Log.Information($@"Restoring SFAR from backup: {backupFile} -> {targetFile}"); XCopy.Copy(backupFile, targetFile, true, true, (o, pce) => { RestoreButtonContent = M3L.GetString(M3L.string_interp_restoringXpercent, pce.ProgressPercentage.ToString()); }); } Utilities.DeleteEmptySubdirectories(DLCDirectory); RestoreButtonContent = M3L.GetString(M3L.string_restored); }; nbw.RunWorkerCompleted += (a, b) => { if (b.Error != null) { Log.Error($@"Exception occurred in {nbw.Name} thread: {b.Error.Message}"); } //File.Copy(backupFile, targetFile, true); //if (!batchRestore) //{ RevalidateIsModified(); //restoreCompletedCallback?.Invoke(); //} Restoring = false; signalRestoreCompleted?.Invoke(); }; startingRestoreCallback?.Invoke(); nbw.RunWorkerAsync(); } }
public void PopulateBinkInfo() { if (Game != Mod.MEGame.ME1) { Binkw32StatusText = Utilities.CheckIfBinkw32ASIIsInstalled(this) ? M3L.GetString(M3L.string_bypassInstalledASIAndDLCModsWillBeAbleToLoad) : M3L.GetString(M3L.string_bypassNotInstalledASIAndDLCModsWillBeUnableToLoad); } else { Binkw32StatusText = Utilities.CheckIfBinkw32ASIIsInstalled(this) ? M3L.GetString(M3L.string_bypassInstalledASIModsWillBeAbleToLoad) : M3L.GetString(M3L.string_bypassNotInstalledASIModsWillBeUnableToLoad); } }
private static string GetLocalizedTextureExceptionExternalMessage(string exceptionMessage, string file, string storageType, string offset) => M3L.GetString(M3L.string_interp_error_textureExceptionExternal, exceptionMessage, file, storageType, offset);