private void OnDownloadProgress(long done, long total) { ProgressValue = done; ProgressMaximum = total; ProgressIndeterminate = false; DownloadStatus = $@"{FileSize.FormatSize(ProgressValue)}/{FileSize.FormatSize(ProgressMaximum)}"; }
private void Refresh() { TrackedMemoryObjects.Where(x => !x.IsAlive()).ToList().ForEach(x => x.RemainingLifetimeAfterGC--); TrackedMemoryObjects.RemoveAll(x => !x.IsAlive() && x.RemainingLifetimeAfterGC < 0); InstancedTrackedMemoryObjects.ReplaceAll(TrackedMemoryObjects); LastRefreshText = "Last refreshed: " + DateTime.Now; var sizeUsed = System.Diagnostics.Process.GetCurrentProcess().PrivateMemorySize64; CurrentMemoryUsageText = "Current memory usage: " + FileSize.FormatSize(sizeUsed); TotalLargeInUseStr = FileSize.FormatSize(MemoryManager.LargePoolTotalSize); LargeInUseStr = FileSize.FormatSize(MemoryManager.LargePoolInUseSize); LargeFreeStr = FileSize.FormatSize(MemoryManager.LargePoolFreeSize); TotalSmallInUseStr = FileSize.FormatSize(MemoryManager.SmallPoolTotalSize); SmallInUseStr = FileSize.FormatSize(MemoryManager.SmallPoolInUseSize); SmallFreeStr = FileSize.FormatSize(MemoryManager.SmallPoolFreeSize); MaxBufferSize = FileSize.FormatSize(MemoryManager.MaximumBufferSize); MemoryBlockSize = FileSize.FormatSize(MemoryManager.BlockSize); SmallBlocksAvailable = MemoryManager.SmallBlocksAvailable; LargeBlocksAvailable = MemoryManager.LargeBlocksAvailable; MaxMemoryObserved = Math.Max(MaxMemoryObserved, sizeUsed); MaxMemoryObservedText = $"Max memory used: {FileSize.FormatSize(MaxMemoryObserved)}"; //foreach (var item in InstancedTrackedMemoryObjects) //{ // item.RefreshStatus(); //} }
public object Convert(object value, Type targetType, object parameter, CultureInfo culture) { if (value is long l) { return(FileSize.FormatSize(l)); } return($"Invalid value {value}"); }
public override string ToString() { if (!Selectable) { return(filepath); } return(Path.GetFileName(filepath) + @" - " + FileSize.FormatSize(new FileInfo(filepath).Length)); }
public object Convert(object value, Type targetType, object parameter, CultureInfo culture) { if (value is DLCPackage.FileEntryStruct fes) { var compressed = fes.BlockSizeTableIndex != 0xFFFFFFFF; var uncompSize = FileSize.FormatSize(fes.RealUncompressedSize); return($"{(compressed ? $"Compressed" : "Uncompressed")}, uncompressed size: {uncompSize}"); } return($"Invalid value {value}"); }
public void GetDirectoryContents(ListView l) { foreach (PAZ.PAZFileEntry entry in this.Entries) { ListViewItem l1 = new ListViewItem(entry.Name); l1.SubItems.Add(FileSize.FormatSize(entry.Data.Length)); ByteFileInfo file = new ByteFileInfo(entry); string type = FileHandler.GetType(file); if (file.IsLZ77Compressed) { l1.ForeColor = Color.Blue; } file.Dispose(); FileHandler.SetListViewItemInfo(l1, type, l); l.Items.Add(l1); } }
private void Refresh() { TrackedMemoryObjects.Where(x => !x.IsAlive()).ToList().ForEach(x => x.RemainingLifetimeAfterGC--); TrackedMemoryObjects.RemoveAll(x => !x.IsAlive() && x.RemainingLifetimeAfterGC < 0); InstancedTrackedMemoryObjects.ReplaceAll(TrackedMemoryObjects); LastRefreshText = "Last refreshed: " + DateTime.Now; CurrentMemoryUsageText = "Current process allocation: " + FileSize.FormatSize(System.Diagnostics.Process.GetCurrentProcess().PrivateMemorySize64); LargeInUseStr = FileSize.FormatSize(MixinHandler.MixinMemoryStreamManager.LargePoolInUseSize); LargeFreeStr = FileSize.FormatSize(MixinHandler.MixinMemoryStreamManager.LargePoolFreeSize); SmallInUseStr = FileSize.FormatSize(MixinHandler.MixinMemoryStreamManager.SmallPoolInUseSize); SmallFreeStr = FileSize.FormatSize(MixinHandler.MixinMemoryStreamManager.SmallPoolFreeSize); MaxBufferSize = FileSize.FormatSize(MixinHandler.MixinMemoryStreamManager.MaximumBufferSize); MemoryBlockSize = FileSize.FormatSize(MixinHandler.MixinMemoryStreamManager.BlockSize); //foreach (var item in InstancedTrackedMemoryObjects) //{ // item.RefreshStatus(); //} }
public void GetDirectoryContents(ListView l) { foreach (NARC.DirectoryEntry subdir in this.Subdirs) { l.Items.Add(new ListViewItem(subdir.Name, 0) { Group = l.Groups[0] }); } foreach (NARC.FileEntry file1 in this.Files) { ListViewItem l1 = new ListViewItem(file1.Name); l1.SubItems.Add(FileSize.FormatSize(file1.Content.Length)); ByteFileInfo file2 = new ByteFileInfo(file1); string type = FileHandler.GetType(file2); if (file2.IsLZ77Compressed) { l1.ForeColor = Color.Blue; } file2.Dispose(); FileHandler.SetListViewItemInfo(l1, type, l); l.Items.Add(l1); } }
public static async void BeginFlow(MainWindow window) { // PRE LIBRARY LOAD RegistryHandler.RegistrySettingsPath = @"HKEY_CURRENT_USER\Software\MassEffect2Randomizer"; RegistryHandler.CurrentUserRegistrySubpath = @"Software\MassEffect2Randomizer"; LegendaryExplorerCoreLib.SetSynchronizationContext(TaskScheduler.FromCurrentSynchronizationContext()); try { // This is in a try catch because this is a critical no-crash zone that is before launch window.Title = $"Mass Effect 2 Randomizer {App.AppVersion}"; } catch { } if (Utilities.GetExecutablePath().StartsWith(Path.GetTempPath(), StringComparison.InvariantCultureIgnoreCase)) { // Running from temp! This is not allowed await window.ShowMessageAsync("Cannot run from temp directory", $"Mass Effect 2 Randomizer cannot be run from the system's Temp directory. If this executable was run from within an archive, it needs to be extracted first."); Environment.Exit(1); } var pd = await window.ShowProgressAsync("Starting up", $"Mass Effect 2 Randomizer is starting up. Please wait."); pd.SetIndeterminate(); NamedBackgroundWorker bw = new NamedBackgroundWorker("StartupThread"); bw.DoWork += (a, b) => { ALOTInstallerCoreLib.Startup(SetWrapperLogger, RunOnUIThread, startTelemetry, stopTelemetry, $"Mass Effect 2 Randomizer {App.AppVersion} starting up", false); // Logger is now available // Setup telemetry handlers CoreAnalytics.TrackEvent = TelemetryController.TrackEvent; CoreCrashes.TrackError = TelemetryController.TrackError; CoreCrashes.TrackError2 = TelemetryController.TrackError2; CoreCrashes.TrackError3 = TelemetryController.TrackError3; // Setup the InteropPackage for the update check #region Update interop CancellationTokenSource ct = new CancellationTokenSource(); AppUpdateInteropPackage interopPackage = new AppUpdateInteropPackage() { GithubOwner = "Mgamerz", GithubReponame = "MassEffect2Randomizer", UpdateAssetPrefix = "ME2Randomizer", UpdateFilenameInArchive = "ME2Randomizer.exe", ShowUpdatePromptCallback = (title, text, updateButtonText, declineButtonText) => { bool response = false; object syncObj = new object(); Application.Current.Dispatcher.Invoke(async() => { if (Application.Current.MainWindow is MainWindow mw) { var result = await mw.ShowMessageAsync(title, text, MessageDialogStyle.AffirmativeAndNegative, new MetroDialogSettings() { AffirmativeButtonText = updateButtonText, NegativeButtonText = declineButtonText, DefaultButtonFocus = MessageDialogResult.Affirmative }, 75); response = result == MessageDialogResult.Affirmative; lock (syncObj) { Monitor.Pulse(syncObj); } } }); lock (syncObj) { Monitor.Wait(syncObj); } return(response); }, ShowUpdateProgressDialogCallback = (title, initialmessage, canCancel) => { // We don't use this as we are already in a progress dialog pd.SetCancelable(canCancel); pd.SetMessage(initialmessage); pd.SetTitle(title); }, SetUpdateDialogTextCallback = s => { pd.SetMessage(s); }, ProgressCallback = (done, total) => { pd.SetProgress(done * 1d / total); pd.SetMessage($"Downloading update {FileSize.FormatSize(done)} / {FileSize.FormatSize(total)}"); }, ProgressIndeterminateCallback = () => { pd.SetIndeterminate(); }, ShowMessageCallback = (title, message) => { object syncObj = new object(); Application.Current.Dispatcher.Invoke(async() => { if (Application.Current.MainWindow is MainWindow mw) { await mw.ShowMessageAsync(title, message); lock (syncObj) { Monitor.Pulse(syncObj); } } }); lock (syncObj) { Monitor.Wait(syncObj); } }, NotifyBetaAvailable = () => { App.BetaAvailable = true; }, DownloadCompleted = () => { pd.SetCancelable(false); }, cancellationTokenSource = ct, ApplicationName = "Mass Effect 2 Randomizer", RequestHeader = "ME2Randomizer", ForcedUpgradeMaxReleaseAge = 3 }; #endregion pd.SetMessage("Checking for application updates"); pd.Canceled += (sender, args) => { ct.Cancel(); }; AppUpdater.PerformGithubAppUpdateCheck(interopPackage); // If user aborts download pd.SetCancelable(false); pd.SetIndeterminate(); pd.SetTitle("Starting up"); void setStatus(string message) { pd.SetIndeterminate(); pd.SetMessage(message); } GameTarget target = null; try { pd.SetMessage("Loading Mass Effect 2 Randomizer framework"); ToolTipService.ShowOnDisabledProperty.OverrideMetadata(typeof(Control), new FrameworkPropertyMetadata(true)); ToolTipService.ShowDurationProperty.OverrideMetadata(typeof(DependencyObject), new FrameworkPropertyMetadata(int.MaxValue)); ALOTInstallerCoreLib.PostCriticalStartup(x => pd.SetMessage(x), RunOnUIThread, false); #if __LE2__ LE2Directory.ReloadDefaultGamePath(true); if (LE2Directory.DefaultGamePath != null) { GameTarget gt = new GameTarget(MEGame.LE2, LE2Directory.DefaultGamePath, true); if (gt.ValidateTarget() == null) { Locations.SetTarget(gt, false); } } #endif MEPackageHandler.GlobalSharedCacheEnabled = false; // ME2R does not use the global shared cache. handleM3Passthrough(); target = Locations.GetTarget(MERFileSystem.Game); if (target == null) { var gamePath = MEDirectories.GetDefaultGamePath(MERFileSystem.Game); if (Directory.Exists(gamePath)) { target = new GameTarget(MERFileSystem.Game, gamePath, true); var validationFailedReason = target.ValidateTarget(); if (validationFailedReason == null) { // CHECK NOT TEXTURE MODIFIED if (target.TextureModded) { MERLog.Error($@"Game target is texture modded: {target.TargetPath}. This game target is not targetable by ME2R"); object o = new object(); Application.Current.Dispatcher.Invoke(async() => { if (Application.Current.MainWindow is MainWindow mw) { await mw.ShowMessageAsync("Mass Effect 2 target is texture modded", $"The game located at {target.TargetPath} has had textures modified. Mass Effect 2 Randomizer cannot randomize texture modified games, as it adds package files. If you want to texture mod your game, it must be done after randomization.", ContentWidthPercent: 75); lock (o) { Monitor.Pulse(o); } } }); lock (o) { Monitor.Wait(o); } } // We still set target so we can restore game if necessary Locations.SetTarget(target, false); } } } pd.SetMessage("Performing startup checks"); MERStartupCheck.PerformStartupCheck((title, message) => { object o = new object(); Application.Current.Dispatcher.Invoke(async() => { if (Application.Current.MainWindow is MainWindow mw) { await mw.ShowMessageAsync(title, message, ContentWidthPercent: 75); lock (o) { Monitor.Pulse(o); } } }); lock (o) { Monitor.Wait(o); } }, x => pd.SetMessage(x)); // force initial refresh MERPeriodicRefresh(null, null); } catch (Exception e) { MERLog.Exception(e, @"There was an error starting up the framework!"); } pd.SetMessage("Preparing interface"); Thread.Sleep(250); // This will allow this message to show up for moment so user can see it. Application.Current.Dispatcher.Invoke(async() => { if (Application.Current.MainWindow is MainWindow mw) { mw.SetupTargetDescriptionText(); var backupStatus = BackupService.GetBackupStatus(MERFileSystem.Game); mw.BackupRestoreText = backupStatus?.BackupActionText; mw.BackupRestore_Button.ToolTip = backupStatus != null && backupStatus.BackedUp ? "Click to restore game/uninstall randomizer mod" : "Click to backup game"; mw.FinalizeInterfaceLoad(); /* * if (!hasWorkingMEM) * { * await mw.ShowMessageAsync("Required components are not available", * "Some components for installation are not available, likely due to network issues (blocking, no internet, etc). To install these components, folow the 'How to install the Installer Support Package' directions on any of the ALOT pages on NexusMods. The installer will not work without these files installed.", * ContentWidthPercent: 75); * }*/ PeriodicRefresh.OnPeriodicRefresh += MERPeriodicRefresh; } }); }; bw.RunWorkerCompleted += async(a, b) => { // Post critical startup Random random = new Random(); var preseed = random.Next(); window.ImageCredits.ReplaceAll(ImageCredit.LoadImageCredits("imagecredits.txt", false)); window.ContributorCredits.ReplaceAll(window.GetContributorCredits()); window.LibraryCredits.ReplaceAll(LibraryCredit.LoadLibraryCredits("librarycredits.txt")); #if DEBUG window.SeedTextBox.Text = 529572808.ToString(); #else window.SeedTextBox.Text = preseed.ToString(); #endif window.TextBlock_AssemblyVersion.Text = $"Version {App.AppVersion}"; window.SelectedRandomizeMode = MainWindow.RandomizationMode.ERandomizationMode_SelectAny; var hasFirstRun = RegistryHandler.GetRegistrySettingBool(MainWindow.SETTING_FIRSTRUN); if (hasFirstRun == null || !hasFirstRun.Value) { window.FirstRunFlyoutOpen = true; } await pd.CloseAsync(); }; bw.RunWorkerAsync(); }
private void ImportSelectedFolder() { //Check destination path var destinationName = Utilities.SanitizePath(ModNameText); if (string.IsNullOrWhiteSpace(destinationName)) { //cannot use this name Log.Error(@"Invalid mod name: " + ModNameText); M3L.ShowDialog(mainwindow, M3L.GetString(M3L.string_dialog_invalidModNameWillResolveToNothing), M3L.GetString(M3L.string_invalidModName), MessageBoxButton.OK, MessageBoxImage.Error); return; } //Check free space. var sourceDir = Path.Combine(M3Directories.GetDLCPath(SelectedTarget), SelectedDLCFolder.DLCFolderName); var library = Utilities.GetModDirectoryForGame(SelectedTarget.Game); if (Utilities.DriveFreeBytes(library, out var freeBytes)) { //Check enough space var sourceSize = Utilities.GetSizeOfDirectory(sourceDir); if (sourceSize > (long)freeBytes) { //Not enough space Log.Error($@"Not enough disk space to import mod. Required space: {FileSize.FormatSize(sourceSize)}, available space: {FileSize.FormatSize(freeBytes)}"); M3L.ShowDialog(mainwindow, M3L.GetString(M3L.string_interp_insufficientDiskSpaceToImport, Path.GetPathRoot(library), FileSize.FormatSize(sourceSize), FileSize.FormatSize(freeBytes)), M3L.GetString(M3L.string_insufficientFreeDiskSpace), MessageBoxButton.OK, MessageBoxImage.Error); return; } } //Check directory doesn't exist already var outDir = Path.Combine(library, destinationName); if (Directory.Exists(outDir)) { var okToDelete = M3L.ShowDialog(mainwindow, M3L.GetString(M3L.string_interp_dialog_importingWillDeleteExistingMod, outDir), M3L.GetString(M3L.string_sameNamedModInLibrary), MessageBoxButton.YesNo, MessageBoxImage.Warning); if (okToDelete == MessageBoxResult.No) { return; //cancel } try { Utilities.DeleteFilesAndFoldersRecursively(outDir); } catch (Exception e) { M3L.ShowDialog(mainwindow, M3L.GetString(M3L.string_interp_couldNotDeleteExistingModDirectory, e.Message), M3L.GetString(M3L.string_errorDeletingModFolder), MessageBoxButton.OK, MessageBoxImage.Error); return; //abort } } NamedBackgroundWorker nbw = new NamedBackgroundWorker(@"GameDLCModImporter"); nbw.DoWork += ImportDLCFolder_BackgroundThread; nbw.RunWorkerCompleted += (a, b) => { if (b.Error != null) { Log.Error($@"Exception occurred in {nbw.Name} thread: {b.Error.Message}"); } else { if (b.Error == null && b.Result != null) { Analytics.TrackEvent(@"Imported a mod from game installation", new Dictionary <string, string>() { { @"Game", SelectedTarget.Game.ToString() }, { @"Folder", SelectedDLCFolder.DLCFolderName } }); } OperationInProgress = false; if (b.Error == null && b.Result != null) { OnClosing(new DataEventArgs(b.Result)); //avoid accessing b.Result if error occurred } } }; nbw.RunWorkerAsync(); }
private UploadModResult UploadMod(Action <double> progressCallback = null, Action <TaskbarProgressBarState> setTaskbarProgressState = null) { #region online fetch //Fetch current production manifest for mod (it may not exist) setTaskbarProgressState?.Invoke(TaskbarProgressBarState.Indeterminate); using var wc = new System.Net.WebClient(); try { CurrentActionText = M3L.GetString(M3L.string_checkingIfUpdaterServiceIsConfiguredForMod); string validationUrl = $@"{UpdaterServiceCodeValidationEndpoint}?updatecode={mod.ModClassicUpdateCode}&updatexmlname={mod.UpdaterServiceServerFolderShortname}.xml"; string isBeingServed = wc.DownloadStringAwareOfEncoding(validationUrl); if (string.IsNullOrWhiteSpace(isBeingServed) || isBeingServed != @"true") //we don't parse for bool because it might have a different text that is not specifically true or false. It might // have an error for example { //Not being served Log.Error(@"This mod is not configured for serving on the Updater Service. Please contact Mgamerz."); CurrentActionText = M3L.GetString(M3L.string_serverNotConfiguredForModContactMgamerz); HideChangelogArea(); return(UploadModResult.NOT_BEING_SERVED); } } catch (Exception ex) { Log.Error(@"Error validating mod is configured on updater service: " + ex.Message); CurrentActionText = M3L.GetString(M3L.string_interp_errorCheckingUpdaterServiceConfiguration, ex.Message); HideChangelogArea(); return(UploadModResult.ERROR_VALIDATING_MOD_IS_CONFIGURED); } #endregion #region get current production version to see if we should prompt user var latestVersionOnServer = OnlineContent.GetLatestVersionOfModOnUpdaterService(mod.ModClassicUpdateCode); if (latestVersionOnServer != null) { if (latestVersionOnServer >= mod.ParsedModVersion) { bool cancel = false; setTaskbarProgressState?.Invoke(TaskbarProgressBarState.Paused); Application.Current.Dispatcher.Invoke(delegate { // server is newer or same as version we are pushing var response = M3L.ShowDialog(mainwindow, M3L.GetString(M3L.string_interp_dialog_serverVersionSameOrNewerThanLocal, mod.ParsedModVersion, latestVersionOnServer), M3L.GetString(M3L.string_serverVersionSameOrNewerThanLocal), MessageBoxButton.YesNo, MessageBoxImage.Warning); if (response == MessageBoxResult.No) { CurrentActionText = M3L.GetString(M3L.string_uploadAbortedModOnServerIsSameOrNewerThanLocalOneBeingUploaded); HideChangelogArea(); cancel = true; return; } }); setTaskbarProgressState?.Invoke(TaskbarProgressBarState.Indeterminate); if (cancel) { return(UploadModResult.ABORTED_BY_USER_SAME_VERSION_UPLOADED); } } } #endregion #region mod variables //get refs var files = mod.GetAllRelativeReferences(true); files = files.OrderByDescending(x => new FileInfo(Path.Combine(mod.ModPath, x)).Length).ToList(); long totalModSizeUncompressed = files.Sum(x => new FileInfo(Path.Combine(mod.ModPath, x)).Length); #endregion #region Check package files are not natively compressed { // In brackets to scope Log.Information(@"UpdaterServiceUpload: Checking for compressed packages"); double numDone = 0; int totalFiles = files.Count; var compressedPackages = new List <string>(); foreach (var f in files) { CurrentActionText = M3L.GetString(M3L.string_interp_checkingPackagesBeforeUploadX, (numDone * 100 / totalFiles)); numDone++; if (f.RepresentsPackageFilePath()) { //var p = MEPackageHandler.OpenMEPackage(Path.Combine(mod.ModPath, f)); //p.Save(compress: true); var quickP = MEPackageHandler.QuickOpenMEPackage(Path.Combine(mod.ModPath, f)); if (quickP.IsCompressed) { if (quickP.NumCompressedChunksAtLoad > 0) { Log.Error($@"Found compressed package: {quickP.FilePath}"); } else { Log.Error($@"Found package with IsCompressed flag but no compressed chunks: {quickP.FilePath}"); } compressedPackages.Add(f); } } } if (compressedPackages.Any()) { CurrentActionText = M3L.GetString(M3L.string_uploadAborted_foundCompressedPackage); // Abort Application.Current.Dispatcher.InvokeAsync(() => { M3L.ShowDialog(mainwindow, M3L.GetString(M3L.string_dialog_uploadAborted_foundCompressedPackage, string.Join('\n', compressedPackages.OrderBy(x => x))), M3L.GetString(M3L.string_cannotUploadMod), MessageBoxButton.OK, MessageBoxImage.Error); }); CancelOperations = true; return(UploadModResult.CANT_UPLOAD_NATIVE_COMPRESSED_PACKAGES); } } #endregion #region compress and stage mod void updateCurrentTextCallback(string newText) { CurrentActionText = newText; } bool?canceledCheckCallback() => CancelOperations; CurrentActionText = M3L.GetString(M3L.string_compressingModForUpdaterService); progressCallback?.Invoke(0); setTaskbarProgressState?.Invoke(TaskbarProgressBarState.Normal); var lzmaStagingPath = OnlineContent.StageModForUploadToUpdaterService(mod, files, totalModSizeUncompressed, canceledCheckCallback, updateCurrentTextCallback, progressCallback); #endregion if (CancelOperations) { AbortUpload(); return(UploadModResult.ABORTED_BY_USER); } setTaskbarProgressState?.Invoke(TaskbarProgressBarState.Indeterminate); #region hash mod and build server manifest CurrentActionText = M3L.GetString(M3L.string_buildingServerManifest); long amountHashed = 0; ConcurrentDictionary <string, SourceFile> manifestFiles = new ConcurrentDictionary <string, SourceFile>(); Parallel.ForEach(files, new ParallelOptions() { MaxDegreeOfParallelism = Math.Max(1, Environment.ProcessorCount - 1) }, x => { if (CancelOperations) { return; } SourceFile sf = new SourceFile(); var sFile = Path.Combine(mod.ModPath, x); var lFile = Path.Combine(lzmaStagingPath, x + @".lzma"); sf.hash = Utilities.CalculateMD5(sFile); sf.lzmahash = Utilities.CalculateMD5(lFile); var fileInfo = new FileInfo(sFile); sf.size = fileInfo.Length; sf.timestamp = fileInfo.LastWriteTimeUtc.Ticks; sf.relativefilepath = x; sf.lzmasize = new FileInfo(lFile).Length; manifestFiles.TryAdd(x, sf); var done = Interlocked.Add(ref amountHashed, sf.size); CurrentActionText = M3L.GetString(M3L.string_buildingServerManifest) + $@" {Math.Round(done * 100.0 / totalModSizeUncompressed)}%"; }); if (CancelOperations) { AbortUpload(); return(UploadModResult.ABORTED_BY_USER); } //Build document XmlDocument xmlDoc = new XmlDocument(); XmlNode rootNode = xmlDoc.CreateElement(@"mod"); xmlDoc.AppendChild(rootNode); foreach (var mf in manifestFiles) { if (CancelOperations) { AbortUpload(); return(UploadModResult.ABORTED_BY_USER); } XmlNode sourceNode = xmlDoc.CreateElement(@"sourcefile"); var size = xmlDoc.CreateAttribute(@"size"); size.InnerText = mf.Value.size.ToString(); var hash = xmlDoc.CreateAttribute(@"hash"); hash.InnerText = mf.Value.hash; var lzmasize = xmlDoc.CreateAttribute(@"lzmasize"); lzmasize.InnerText = mf.Value.lzmasize.ToString(); var lzmahash = xmlDoc.CreateAttribute(@"lzmahash"); lzmahash.InnerText = mf.Value.lzmahash; var timestamp = xmlDoc.CreateAttribute(@"timestamp"); timestamp.InnerText = mf.Value.timestamp.ToString(); sourceNode.InnerText = mf.Key; sourceNode.Attributes.Append(size); sourceNode.Attributes.Append(hash); sourceNode.Attributes.Append(lzmasize); sourceNode.Attributes.Append(lzmahash); sourceNode.Attributes.Append(timestamp); rootNode.AppendChild(sourceNode); } if (CancelOperations) { AbortUpload(); return(UploadModResult.ABORTED_BY_USER); } foreach (var bf in mod.UpdaterServiceBlacklistedFiles) { if (CancelOperations) { AbortUpload(); return(UploadModResult.ABORTED_BY_USER); } var bfn = xmlDoc.CreateElement(@"blacklistedfile"); bfn.InnerText = bf; rootNode.AppendChild(bfn); } if (CancelOperations) { AbortUpload(); return(UploadModResult.ABORTED_BY_USER); } var updatecode = xmlDoc.CreateAttribute(@"updatecode"); updatecode.InnerText = mod.ModClassicUpdateCode.ToString(); rootNode.Attributes.Append(updatecode); var version = xmlDoc.CreateAttribute(@"version"); version.InnerText = mod.ParsedModVersion.ToString(); rootNode.Attributes.Append(version); var serverfolder = xmlDoc.CreateAttribute(@"folder"); serverfolder.InnerText = mod.UpdaterServiceServerFolder; rootNode.Attributes.Append(serverfolder); setTaskbarProgressState?.Invoke(TaskbarProgressBarState.Indeterminate); #endregion //wait to ensure changelog is set. while (ChangelogNotYetSet) { setTaskbarProgressState?.Invoke(TaskbarProgressBarState.Paused); if (CancelOperations) { AbortUpload(); return(UploadModResult.ABORTED_BY_USER); } CurrentActionText = M3L.GetString(M3L.string_waitingForChangelogToBeSet); Thread.Sleep(250); //wait for changelog to be set. } setTaskbarProgressState?.Invoke(TaskbarProgressBarState.Indeterminate); #region Finish building manifest var changelog = xmlDoc.CreateAttribute(@"changelog"); changelog.InnerText = ChangelogText; rootNode.Attributes.Append(changelog); using var stringWriter = new StringWriterWithEncoding(Encoding.UTF8); XmlWriterSettings settings = new XmlWriterSettings(); settings.Indent = true; settings.IndentChars = @" "; settings.Encoding = Encoding.UTF8; using var xmlTextWriter = XmlWriter.Create(stringWriter, settings); xmlDoc.WriteTo(xmlTextWriter); xmlTextWriter.Flush(); #endregion var finalManifestText = stringWriter.GetStringBuilder().ToString(); #region Connect to ME3Tweaks CurrentActionText = M3L.GetString(M3L.string_connectingToME3TweaksUpdaterService); Log.Information(@"Connecting to ME3Tweaks as " + Username); string host = @"ftp.me3tweaks.com"; string username = Username; string password = Settings.DecryptUpdaterServicePassword(); using SftpClient sftp = new SftpClient(host, username, password); sftp.Connect(); Log.Information(@"Connected to ME3Tweaks over SSH (SFTP)"); CurrentActionText = M3L.GetString(M3L.string_connectedToME3TweaksUpdaterService); var serverFolderName = mod.UpdaterServiceServerFolderShortname; //sftp.ChangeDirectory(LZMAStoragePath); //Log.Information(@"Listing files/folders for " + LZMAStoragePath); //var lzmaStorageDirectoryItems = sftp.ListDirectory(LZMAStoragePath); var serverModPath = LZMAStoragePath + @"/" + serverFolderName; bool justMadeFolder = false; if (!sftp.Exists(serverModPath)) { CurrentActionText = M3L.GetString(M3L.string_creatingServerFolderForMod); Log.Information(@"Creating server folder for mod: " + serverModPath); sftp.CreateDirectory(serverModPath); justMadeFolder = true; } var dirContents = sftp.ListDirectory(serverModPath).ToList(); Dictionary <string, string> serverHashes = new Dictionary <string, string>(); //Open SSH connection as we will need to hash files out afterwards. Log.Information(@"Connecting to ME3Tweaks Updater Service over SSH (SSH Shell)"); using SshClient sshClient = new SshClient(host, username, password); sshClient.Connect(); Log.Information(@"Connected to ME3Tweaks Updater Service over SSH (SSH Shell)"); if (!justMadeFolder && dirContents.Any(x => x.Name != @"." && x.Name != @"..")) { CurrentActionText = M3L.GetString(M3L.string_hashingFilesOnServerForDelta); Log.Information(@"Hashing existing files on server to compare for delta"); serverHashes = getServerHashes(sshClient, serverFolderName, serverModPath); } //Calculate what needs to be updated or removed from server List <string> filesToUploadToServer = new List <string>(); List <string> filesToDeleteOffServer = new List <string>(); //Files to upload foreach (var sourceFile in manifestFiles) { //find matching server file if (serverHashes.TryGetValue(sourceFile.Key.Replace('\\', '/') + @".lzma", out var matchingHash)) { //exists on server, compare hash if (matchingHash != sourceFile.Value.lzmahash) { //server hash is different! Upload new file. Log.Information(@"Server version of file is different from local: " + sourceFile.Key); filesToUploadToServer.Add(sourceFile.Key); } else { Log.Information(@"Server version of file is same as local: " + sourceFile.Key); } } else { Log.Information(@"Server does not have file: " + sourceFile.Key); filesToUploadToServer.Add(sourceFile.Key); } } //Files to remove from server foreach (var serverfile in serverHashes.Keys) { if (!manifestFiles.Any(x => (x.Key + @".lzma") == serverfile.Replace('/', '\\'))) { Log.Information(@"File exists on server but not locally: " + serverfile); filesToDeleteOffServer.Add(serverfile); } } #endregion long amountUploaded = 0, amountToUpload = 1; //Confirm changes if (filesToDeleteOffServer.Any() || filesToUploadToServer.Any()) { var text = M3L.GetString(M3L.string_interp_updaterServiceDeltaConfirmationHeader, mod.ModName); if (filesToUploadToServer.Any()) { text += M3L.GetString(M3L.string_nnFilesToUploadToServern) + @" " + string.Join('\n' + @" - ", filesToUploadToServer); //weird stuff to deal with localizer } if (filesToDeleteOffServer.Any()) { text += M3L.GetString(M3L.string_nnFilesToDeleteOffServern) + @" " + string.Join('\n' + @" - ", filesToDeleteOffServer); //weird stuff to deal with localizer } text += M3L.GetString(M3L.string_interp_updaterServiceDeltaConfirmationFooter); bool performUpload = false; Log.Information(@"Prompting user to accept server delta"); setTaskbarProgressState?.Invoke(TaskbarProgressBarState.Paused); Application.Current.Dispatcher.Invoke(() => { performUpload = M3L.ShowDialog(mainwindow, text, M3L.GetString(M3L.string_confirmChanges), MessageBoxButton.OKCancel, MessageBoxImage.Exclamation) == MessageBoxResult.OK; }); setTaskbarProgressState?.Invoke(TaskbarProgressBarState.Indeterminate); if (performUpload) { Log.Information(@"User has accepted the delta, applying delta to server"); #region upload files //Create directories SortedSet <string> directoriesToCreate = new SortedSet <string>(); foreach (var f in filesToUploadToServer) { string foldername = f; var lastIndex = foldername.LastIndexOf(@"\"); while (lastIndex > 0) { foldername = foldername.Substring(0, lastIndex); directoriesToCreate.Add(foldername.Replace('\\', '/')); lastIndex = foldername.LastIndexOf(@"\"); } } #endregion //UploadDirectory(sftp, lzmaStagingPath, serverModPath, (ucb) => Debug.WriteLine("UCB: " + ucb)); var dirsToCreateOnServerSorted = directoriesToCreate.ToList(); dirsToCreateOnServerSorted.Sort((a, b) => a.Length.CompareTo(b.Length)); //short to longest so we create top levels first! int numFoldersToCreate = dirsToCreateOnServerSorted.Count(); int numDone = 0; if (dirsToCreateOnServerSorted.Count > 0) { CurrentActionText = M3L.GetString(M3L.string_creatingModDirectoriesOnServer); foreach (var f in dirsToCreateOnServerSorted) { var serverFolderStr = serverModPath + @"/" + f; if (!sftp.Exists(serverFolderStr)) { Log.Information(@"Creating directory on server: " + serverFolderStr); sftp.CreateDirectory(serverFolderStr); } else { Log.Information(@"Server folder already exists, skipping: " + serverFolderStr); } numDone++; CurrentActionText = M3L.GetString(M3L.string_creatingModDirectoriesOnServer) + @" " + Math.Round(numDone * 100.0 / numFoldersToCreate) + @"%"; } } //Upload files progressCallback?.Invoke(0); setTaskbarProgressState?.Invoke(TaskbarProgressBarState.Normal); amountToUpload = filesToUploadToServer.Sum(x => new FileInfo(Path.Combine(lzmaStagingPath, x + @".lzma")).Length); foreach (var file in filesToUploadToServer) { if (CancelOperations) { AbortUpload(); return(UploadModResult.ABORTED_BY_USER); } var fullPath = Path.Combine(lzmaStagingPath, file + @".lzma"); var serverFilePath = serverModPath + @"/" + file.Replace(@"\", @"/") + @".lzma"; Log.Information(@"Uploading file " + fullPath + @" to " + serverFilePath); long amountUploadedBeforeChunk = amountUploaded; using Stream fileStream = new FileStream(fullPath, FileMode.Open); try { sftp.UploadFile(fileStream, serverFilePath, true, (x) => { if (CancelOperations) { CurrentActionText = M3L.GetString(M3L.string_abortingUpload); return; } amountUploaded = amountUploadedBeforeChunk + (long)x; var uploadedHR = FileSize.FormatSize(amountUploaded); var totalUploadHR = FileSize.FormatSize(amountToUpload); if (amountToUpload > 0) { progressCallback?.Invoke(amountUploaded * 1.0 / amountToUpload); } CurrentActionText = M3L.GetString(M3L.string_interp_uploadingFilesToServerXY, uploadedHR, totalUploadHR); }); } catch (Exception e) { Log.Error($@"Error uploading file {fullPath} to server: {e.Message}"); CurrentActionText = M3L.GetString(M3L.string_interp_uploadFailedX, e.Message); // Abort Application.Current.Dispatcher.InvokeAsync(() => { M3L.ShowDialog(mainwindow, M3L.GetString(M3L.string_interp_dialog_updaterServiceUploadFailed, fullPath, e.Message), M3L.GetString(M3L.string_uploadFailed), MessageBoxButton.OK, MessageBoxImage.Error); }); return(UploadModResult.ERROR_UPLOADING_FILE); } } setTaskbarProgressState?.Invoke(TaskbarProgressBarState.Indeterminate); if (CancelOperations) { AbortUpload(); return(UploadModResult.ABORTED_BY_USER); } //delete extra files int numdone = 0; foreach (var file in filesToDeleteOffServer) { CurrentActionText = M3L.GetString(M3L.string_interp_deletingObsoleteFiles, numdone, filesToDeleteOffServer.Count); var fullPath = $@"{LZMAStoragePath}/{serverFolderName}/{file}"; Log.Information(@"Deleting unused file off server: " + fullPath); sftp.DeleteFile(fullPath); numdone++; } //Upload manifest using var manifestStream = finalManifestText.ToStream(); var serverManifestPath = $@"{ManifestStoragePath}/{serverFolderName}.xml"; Log.Information(@"Uploading manifest to server: " + serverManifestPath); sftp.UploadFile(manifestStream, serverManifestPath, true, (x) => { var uploadedAmountHR = FileSize.FormatSize(amountUploaded); var uploadAmountTotalHR = FileSize.FormatSize(amountToUpload); CurrentActionText = M3L.GetString(M3L.string_uploadingUpdateManifestToServer) + $@"{uploadedAmountHR}/{uploadAmountTotalHR}"; }); } else { Log.Warning(@"User has declined uploading the delta. We will not change anything on the server."); CancelOperations = true; AbortUpload(); return(UploadModResult.ABORTED_BY_USER); } CurrentActionText = M3L.GetString(M3L.string_validatingModOnServer); Log.Information(@"Verifying hashes on server for new files"); var newServerhashes = getServerHashes(sshClient, serverFolderName, serverModPath); var badHashes = verifyHashes(manifestFiles, newServerhashes); if (badHashes.Any()) { CurrentActionText = M3L.GetString(M3L.string_someHashesOnServerAreIncorrectContactMgamerz); return(UploadModResult.BAD_SERVER_HASHES_AFTER_VALIDATION); } else { CurrentActionText = M3L.GetString(M3L.string_modUploadedToUpdaterService); return(UploadModResult.UPLOAD_OK); } } else { //Upload manifest Log.Information(@"Hashes on server match local already"); // This ensures version on server is same as expected. using var manifestStream = finalManifestText.ToStream(); var serverManifestPath = $@"{ManifestStoragePath}/{serverFolderName}.xml"; Log.Information(@"Uploading manifest to server: " + serverManifestPath); sftp.UploadFile(manifestStream, serverManifestPath, true, (x) => { var uploadedAmountHR = FileSize.FormatSize(amountUploaded); var uploadAmountTotalHR = FileSize.FormatSize(amountToUpload); CurrentActionText = M3L.GetString(M3L.string_uploadingUpdateManifestToServer) + $@"{uploadedAmountHR}/{uploadAmountTotalHR}"; }); CurrentActionText = M3L.GetString(M3L.string_manifestUpdatedOnServer); return(UploadModResult.UPLOAD_OK); } //return UploadModResult.ABORTED_BY_USER; }
private bool validateBackupPath(string backupPath, GameTarget targetToBackup) { //Check empty if (!targetToBackup.IsCustomOption && Directory.Exists(backupPath)) { if (Directory.GetFiles(backupPath).Length > 0 || Directory.GetDirectories(backupPath).Length > 0) { //Directory not empty Log.Error(@"Selected backup directory is not empty."); M3L.ShowDialog(window, M3L.GetString(M3L.string_directoryIsNotEmptyMustBeEmpty), M3L.GetString(M3L.string_directoryNotEmpty), MessageBoxButton.OK, MessageBoxImage.Error); return(false); } } //Check is Documents folder var docsPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments), @"BioWare", Utilities.GetGameName(Game)); if (backupPath.Equals(docsPath, StringComparison.InvariantCultureIgnoreCase) || backupPath.IsSubPathOf(docsPath)) { Log.Error(@"User chose path in or around the documents path for the game - not allowed as game can load files from here."); M3L.ShowDialog(window, M3L.GetString(M3L.string_interp_dialog_linkFailedSubdirectoryOfGameDocumentsFolder, Utilities.GetGameName(Game)), M3L.GetString(M3L.string_locationNotAllowedForBackup), MessageBoxButton.OK, MessageBoxImage.Error); return(false); } //Check space if (!targetToBackup.IsCustomOption) { Utilities.GetDiskFreeSpaceEx(backupPath, out var freeBytes, out var totalBytes, out var totalFreeBytes); var requiredSpace = (ulong)(Utilities.GetSizeOfDirectory(targetToBackup.TargetPath) * 1.1); //10% buffer Log.Information( $@"Backup space check. Backup size: {FileSize.FormatSize(requiredSpace)}, free space: {FileSize.FormatSize(freeBytes)}"); if (freeBytes < requiredSpace) { //Not enough space. Log.Error( $@"Not enough disk space to create backup at {backupPath}. Required space: {FileSize.FormatSize(requiredSpace)} Free space: {FileSize.FormatSize(freeBytes)}"); M3L.ShowDialog(window, M3L.GetString(M3L.string_dialogInsufficientDiskSpace, Path.GetPathRoot(backupPath), FileSize.FormatSize(freeBytes).ToString(), FileSize.FormatSize(requiredSpace).ToString()), M3L.GetString(M3L.string_insufficientDiskSpace), MessageBoxButton.OK, MessageBoxImage.Error); return(false); } //Check writable var writable = Utilities.IsDirectoryWritable(backupPath); if (!writable) { //Not enough space. Log.Error( $@"Backup destination selected is not writable."); M3L.ShowDialog(window, M3L.GetString(M3L.string_dialog_userAccountDoesntHaveWritePermissionsBackup), M3L.GetString(M3L.string_cannotCreateBackup), MessageBoxButton.OK, MessageBoxImage.Error); return(false); } } //Check it is not subdirectory of the game (we might want to check its not subdir of a target) foreach (var target in AvailableBackupSources) { if (backupPath.IsSubPathOf(target.TargetPath)) { //Not enough space. Log.Error( $@"A backup cannot be created in a subdirectory of a game. {backupPath} is a subdir of {targetToBackup.TargetPath}"); M3L.ShowDialog(window, M3L.GetString(M3L.string_dialogBackupCannotBeSubdirectoryOfGame, backupPath, target.TargetPath), M3L.GetString(M3L.string_cannotCreateBackup), MessageBoxButton.OK, MessageBoxImage.Error); return(false); } } return(true); }
public void Extract(string outPath) { if (!File.Exists(filePath)) { throw new Exception("filename missing"); } UnpackCanceled = false; IndeterminateState = true; CurrentOverallStatus = $"Extracting {GetPrettyDLCNameFromPath(filePath)}"; CurrentStatus = $"Loading {GetPrettyDLCNameFromPath(filePath)} into memory ({FileSize.FormatSize(new FileInfo(filePath).Length)})"; byte[] buffer = File.ReadAllBytes(filePath); CurrentFilesProcessed = 0; IndeterminateState = false; using (MemoryStream stream = new MemoryStream(buffer)) { for (int i = 0; i < TotalFilesInDLC; i++, CurrentFilesProcessed++) { if (UnpackCanceled) { break; } if (filenamesIndex == i) { continue; } if (filesList[i].filenamePath == null) { throw new Exception("filename missing"); } CurrentStatus = "File " + (i + 1) + " of " + filesList.Count + " - " + Path.GetFileName(filesList[i].filenamePath); CurrentProgress = (int)(100.0 * CurrentFilesProcessed) / (int)TotalFilesInDLC; int pos = filesList[i].filenamePath.IndexOf("\\BIOGame\\DLC\\", StringComparison.OrdinalIgnoreCase); string filename = filesList[i].filenamePath.Substring(pos + ("\\BIOGame\\DLC\\").Length); string dir = Path.GetDirectoryName(outPath); Directory.CreateDirectory(Path.GetDirectoryName(Path.Combine(dir, filename))); using (FileStream outputFile = new FileStream(Path.Combine(dir, filename), FileMode.Create, FileAccess.Write)) { ExtractEntry(filesList[i], stream, outputFile); } } } if (UnpackCanceled) { CurrentStatus = "Canceling, cleaning up..."; IndeterminateState = true; string dir = Path.GetDirectoryName(outPath); for (int i = 0; i < TotalFilesInDLC; i++) { if (filenamesIndex == i) { continue; } int pos = filesList[i].filenamePath.IndexOf("\\BIOGame\\DLC\\", StringComparison.OrdinalIgnoreCase); string filename = filesList[i].filenamePath.Substring(pos + ("\\BIOGame\\DLC\\").Length); if (File.Exists(Path.Combine(dir, filename))) { File.Delete(Path.Combine(dir, filename)); } } IndeterminateState = false; return; } File.Delete(filePath); using (FileStream outputFile = new FileStream(filePath, FileMode.Create, FileAccess.Write)) { outputFile.WriteUInt32(SfarTag); outputFile.WriteUInt32(SfarVersion); outputFile.WriteUInt32(HeaderSize); outputFile.WriteUInt32(HeaderSize); outputFile.WriteUInt32(0); outputFile.WriteUInt32(HeaderSize); outputFile.WriteUInt32((uint)MaxBlockSize); outputFile.WriteUInt32(LZMATag); } }