private void onLoadProgress(LoadProgressMessage message) { if (message.IsComplete) { viewModel.JobProgressVisibility = false; viewModel.JobProgress = 0.0; viewModel.JobProgressText = "Idle"; lastCurrent = 0; return; } viewModel.JobProgressVisibility = message.Total > 0; viewModel.JobProgressText = message.Total > 0 ? $"{message.Text} [{message.Current:N0} / {message.Total:N0}]" : message.Text; if (message.Current == 0) { lastCurrent = 0; } if (message.Current > 0 && message.Total > 0 && message.Current > lastCurrent) { viewModel.JobProgress = message.Current / message.Total * 100.0; lastCurrent = message.Current; } if (message.StatusText != null) { viewModel.StatusText = message.StatusText; } }
private void onLoadProgress(LoadProgressMessage message) { if (!message.IsComplete) { return; } isLocalMode = message.IsLocalMode; }
private async Task onLoadProgressAsync(LoadProgressMessage message) { // // This method alwyas runs on a background thread. Executed at program start only. // // Due to requirements of the Bitmap class (used by FontAwesome) the entities must be created on the // UI thread. // await asyncService.RunUiContext(() => viewModel.Loaders = buildEntityList()); messenger.SendUi(new OpenDialogMessage { Name = DialogNames.LoadProgressDialog }); await commonDataStore.LoadDataFilesAsync(onProgressCallbackAsync, false); }
private void buildObjectParents(CancellationToken token) { LoadProgressMessage message = new LoadProgressMessage { Text = "Building Object Tree..." }; messenger.SendUi(message); try { List <ObjectTreeViewEntity> packages = imports.Where(import => import.OwnerReferenceNameIndex == null) .Select(import => import.PackageNameIndex.Name) .Distinct() .Select(packageName => new ObjectTreeViewEntity { Name = packageName, IsExpanded = packageName.Equals("Core", StringComparison.CurrentCultureIgnoreCase) }) .ToList(); viewModel.ObjectTree = new ObservableCollection <ObjectTreeViewEntity>(packages.OrderBy(package => package.Name)); foreach (ObjectTreeViewEntity package in packages) { List <ObjectTreeViewEntity> firstTypes = imports.Where(import => import.OwnerReferenceNameIndex == null && package.Name == import.PackageNameIndex.Name) .Select(import => import.TypeNameIndex.Name) .Distinct() .Select(typeName1 => new ObjectTreeViewEntity { Name = typeName1, Parent = package, IsExpanded = typeName1.Equals("Package", StringComparison.CurrentCultureIgnoreCase) }) .ToList(); package.Children = new ObservableCollection <ObjectTreeViewEntity>(firstTypes.OrderBy(type => type.Name)); List <string> names = new List <string> { "Engine", "Core" }; foreach (ObjectTreeViewEntity type1 in firstTypes) { List <ObjectTreeViewEntity> firstNames = imports.Where(import => import.OwnerReferenceNameIndex == null && package.Name == import.PackageNameIndex.Name && type1.Name == import.TypeNameIndex.Name) .Select(import => import.NameTableIndex.Name) .Distinct() .Select(name1 => new ObjectTreeViewEntity { Name = name1, Parent = type1, IsExpanded = names.Contains(name1) }) .ToList(); type1.Children = new ObservableCollection <ObjectTreeViewEntity>(firstNames.OrderBy(name => name.Name)); recursiveImports(firstNames); } } } catch (Exception ex) { messenger.SendUi(new ApplicationErrorMessage { HeaderText = "Error Building Object Tree", Exception = ex }); } message.IsComplete = true; messenger.SendUi(message); }
private async Task rebuildExports() { Dictionary <ExportedObjectViewEntity, List <ExportedObjectViewEntity> > filesToMod = viewModel.ExportsTree?.Traverse(e => Path.HasExtension(e.Filename) && e.IsChecked) .GroupBy(e => e.Parent) .ToDictionary(g => g.Key, g => g.ToList()); if (filesToMod == null || !filesToMod.Any()) { return; } LoadProgressMessage message = new LoadProgressMessage { Text = "Rebuilding...", Total = filesToMod.Count }; foreach (KeyValuePair <ExportedObjectViewEntity, List <ExportedObjectViewEntity> > pair in filesToMod) { string gameFilename = $"{pair.Key.Filename.Replace(settings.ExportPath, null)}.upk"; DomainUpkFile file = allFiles.SingleOrDefault(f => f.GameFilename.Equals(gameFilename)); if (file == null) { continue; } DomainHeader header = await repository.LoadUpkFile(Path.Combine(settings.PathToGame, file.GameFilename)); await header.ReadHeaderAsync(null); message.Current++; foreach (ExportedObjectViewEntity entity in pair.Value) { DomainExportTableEntry export = header.ExportTable.SingleOrDefault(ex => ex.NameTableIndex.Name.Equals(Path.GetFileNameWithoutExtension(entity.Filename), StringComparison.CurrentCultureIgnoreCase)); if (export == null) { continue; } await export.ParseDomainObject(header, false, false); int compressor = menuViewModel.IsCompressorClusterFit ? 0 : menuViewModel.IsCompressorRangeFit ? 1 : 2; int errorMetric = menuViewModel.IsErrorMetricPerceptual ? 0 : 1; FileFormat fileFormat = menuViewModel.IsDdsDefault ? FileFormat.Unknown : menuViewModel.IsDdsFormat1 ? FileFormat.DXT1 : menuViewModel.IsDdsFormat5 ? FileFormat.DXT5 : FileFormat.A8R8G8B8; DdsSaveConfig config = new DdsSaveConfig(fileFormat, compressor, errorMetric, menuViewModel.IsWeightColorByAlpha, false); await export.DomainObject.SetObject(entity.Filename, header.NameTable, config); message.StatusText = entity.Filename; messenger.Send(message); } string directory = Path.Combine(settings.PathToGame, Path.GetDirectoryName(file.GameFilename), "mod"); string filename = Path.Combine(directory, Path.GetFileName(file.GameFilename)); if (!Directory.Exists(directory)) { Directory.CreateDirectory(directory); } await repository.SaveUpkFile(header, filename); DomainUpkFile upkFile = new DomainUpkFile { GameFilename = filename.Replace(settings.PathToGame, null), FileSize = new FileInfo(filename).Length, Package = GetFileNameWithoutExtension(filename).ToLowerInvariant() }; messenger.Send(new ModFileBuiltMessage { UpkFile = upkFile }); } message.IsComplete = true; message.StatusText = null; messenger.Send(message); }
private async Task exportFileObjects(List <DomainUpkFile> files) { LoadProgressMessage message = new LoadProgressMessage { Text = "Exporting...", Total = files.Count }; int compressor = menuViewModel.IsCompressorClusterFit ? 0 : menuViewModel.IsCompressorRangeFit ? 1 : 2; int errorMetric = menuViewModel.IsErrorMetricPerceptual ? 0 : 1; DdsSaveConfig config = new DdsSaveConfig(FileFormat.Unknown, compressor, errorMetric, menuViewModel.IsWeightColorByAlpha, false); foreach (DomainUpkFile file in files) { FileViewEntity fileEntity = viewModel.Files.Single(fe => fe.Id == file.Id); string directory = Path.Combine(settings.ExportPath, Path.GetDirectoryName(file.GameFilename), Path.GetFileNameWithoutExtension(file.GameFilename)); if (!Directory.Exists(directory)) { Directory.CreateDirectory(directory); } DomainHeader header = file.Header; if (header == null) { try { header = await repository.LoadUpkFile(Path.Combine(settings.PathToGame, file.GameFilename)); await Task.Run(() => header.ReadHeaderAsync(null)); } catch (Exception ex) { messenger.Send(new ApplicationErrorMessage { HeaderText = "Error Loading UPK File", ErrorMessage = $"Filename: {file.GameFilename}", Exception = ex }); fileEntity.IsErrored = true; continue; } } message.Current++; foreach (DomainExportTableEntry export in header.ExportTable) { if (export.DomainObject == null) { try { await export.ParseDomainObject(header, false, false); } catch (Exception ex) { messenger.Send(new ApplicationErrorMessage { HeaderText = "Error Parsing Object", ErrorMessage = $"Filename: {header.Filename}\nExport Name: {export.NameTableIndex.Name}\nType: {export.TypeReferenceNameIndex.Name}", Exception = ex }); fileEntity.IsErrored = true; continue; } } if (!export.DomainObject.IsExportable) { continue; } string filename = Path.Combine(directory, $"{export.NameTableIndex.Name}{export.DomainObject.FileExtension}"); message.StatusText = filename; messenger.Send(message); await export.DomainObject.SaveObject(filename, config); } file.Header = null; } message.IsComplete = true; message.StatusText = null; messenger.Send(message); }
private async Task scanUpkFiles(List <DomainUpkFile> upkFiles) { isScanInProgress = true; LoadProgressMessage message = new LoadProgressMessage { Text = "Scanning UPK Files", Current = 0, Total = upkFiles.Count }; List <DomainUpkFile> saveCache = new List <DomainUpkFile>(); foreach (DomainUpkFile file in upkFiles) { FileViewEntity fileEntity = viewModel.Files.SingleOrDefault(fe => fe.Filename == file.Filename) ?? mapper.Map <FileViewEntity>(file); message.Current++; message.StatusText = Path.Combine(settings.PathToGame, file.GameFilename); messenger.Send(message); DomainExportVersion bestVersion = file.GetCurrentExports() ?? new DomainExportVersion { Versions = new List <DomainVersion>() }; if (bestVersion.Versions.Contains(version)) { // // This should never happen. Toss an error to the ui to track it. // messenger.Send(new ApplicationErrorMessage { HeaderText = "Odd Situation", ErrorMessage = $"Tried to scan a file, but the current version was already accounted for. {file.GameFilename} {version}/{locale}", OpenErrorWindow = true }); continue; } if (bestVersion.Filesize == file.Filesize) { bestVersion.Versions.Add(version); if (fileEntity.ExportTypes == null || !fileEntity.ExportTypes.Any()) { fileEntity.ExportTypes = new ObservableCollection <string>(bestVersion.Types.Select(t => t.Name)); } } else { await scanUpkFile(fileEntity, file); List <DomainExportType> exports = new List <DomainExportType>(); foreach (string type in file.Header.ExportTable.Select(e => e.TypeReferenceNameIndex.Name).Distinct().OrderBy(s => s)) { exports.Add(new DomainExportType { Name = type, ExportNames = file.Header.ExportTable.Where(e => e.TypeReferenceNameIndex.Name == type).Select(e => e.NameTableIndex.Name).Distinct().OrderBy(s => s).ToList() }); } file.Exports.Add(new DomainExportVersion { Versions = new List <DomainVersion> { version }, Locale = locale, Filesize = file.Header.FileSize, Types = exports }); fileEntity.FileSize = file.Header.FileSize; fileEntity.ExportTypes = new ObservableCollection <string>(exports.Select(e => e.Name)); file.Header = null; } string path = Path.GetDirectoryName(file.GameFilename); if (path != null && path.ToLowerInvariant().EndsWith("cookedpc")) { if (!menuViewModel.IsOfflineMode) { saveCache.Add(file); } if (saveCache.Count == 50) { remoteRepository.SaveUpkFile(saveCache.ToList()).FireAndForget(); saveCache.Clear(); } } } if (saveCache.Any()) { remoteRepository.SaveUpkFile(saveCache.ToList()).FireAndForget(); } message.IsComplete = true; messenger.Send(message); isScanInProgress = false; }
private async Task loadAllFiles() { isLoadInProgress = true; selectedFile = null; notesViewModel.SelectedFile = null; messenger.Send(new FileLoadingMessage()); allFileEntities.ForEach(fe => fe.PropertyChanged -= onFileEntityViewModelChanged); allFileEntities.Clear(); allFiles.Clear(); viewModel.Files.Clear(); if (String.IsNullOrEmpty(settings.PathToGame)) { return; } LoadProgressMessage progress = new LoadProgressMessage { Text = "Loading Game Files..." }; messenger.Send(progress); try { version = await repository.GetGameVersion(settings.PathToGame); } catch (FileNotFoundException) { messenger.Send(new MessageBoxDialogMessage { Header = "Game Version File Not Found", Message = "The ..\\bin\\Version.ini file could not be found.\n\nPlease ensure your settings are pointed to the 'contents' directory.", HasCancel = false }); progress.IsComplete = true; messenger.Send(progress); isLoadInProgress = false; return; } try { locale = await repository.GetGameLocale(settings.PathToGame); } catch (FileNotFoundException) { locale = CultureInfo.CurrentCulture.EnglishName.ToLowerInvariant(); } List <DomainUpkFile> localFiles = await loadGameFiles(); if (!localFiles.Any()) { progress.IsComplete = true; messenger.Send(progress); isLoadInProgress = false; return; } localFiles.ForEach(f => f.CurrentVersion = version); List <DomainUpkFile> mods = (from row in localFiles let path = Path.GetDirectoryName(row.GameFilename) where path != null && !path.EndsWith("CookedPC", StringComparison.CurrentCultureIgnoreCase) select row).ToList(); localFiles.RemoveAll(f => mods.Contains(f)); progress.Text = "Loading Remote Database..."; messenger.Send(progress); List <DomainUpkFile> remoteFiles = new List <DomainUpkFile>(); string message = "No files returned from repository."; CancellationToken token = resetToken(ref remoteTokenSource); bool loadError = false; try { if (!menuViewModel.IsOfflineMode) { remoteFiles = await remoteRepository.LoadUpkFiles(token); } } catch (Exception ex) { message = ex.Message; remoteFiles = new List <DomainUpkFile>(); loadError = true; } if ((loadError || token.IsCancellationRequested) && !remoteFiles.Any()) { if (loadError) { messenger.Send(new MessageBoxDialogMessage { Header = "Error Received from Remote Database", Message = $"The remote database returned an error. Please try again in a few minutes.\n\n{message}\n\nThe program will continue using local files only. Saving of file notes will be disabled.", HasCancel = false }); } progress.IsLocalMode = true; menuViewModel.IsOfflineMode = true; viewModel.IsShowFilesWithType = false; } remoteFiles.ForEach(f => { f.CurrentVersion = version; f.CurrentLocale = locale; }); List <DomainUpkFile> matches = (from row1 in localFiles join row2 in remoteFiles on new { row1.ContentsRoot, row1.Package } equals new { row2.ContentsRoot, row2.Package } where row2.Exports.Any(f => f.Versions.Contains(version) && f.Locale == locale && f.Filesize == row1.Filesize) let a = row2.GameFilename = row1.GameFilename select row2).ToList(); if (matches.Any()) { allFiles.AddRange(matches.OrderBy(f => f.Filename)); } List <DomainUpkFile> changes = (from row1 in localFiles join row2 in remoteFiles on new { row1.ContentsRoot, row1.Package } equals new { row2.ContentsRoot, row2.Package } where row2.Exports.All(f => !f.Versions.Contains(version) || f.Locale != locale) let a = row2.GameFilename = row1.GameFilename select row2).ToList(); if (changes.Any()) { allFiles.AddRange(changes.OrderBy(f => f.Filename)); allFiles.Sort(domainUpkfileComparison); await scanUpkFiles(changes); } List <DomainUpkFile> adds = (from row1 in localFiles join row2 in remoteFiles on new { row1.ContentsRoot, row1.Package } equals new { row2.ContentsRoot, row2.Package } into fileGroup from sub in fileGroup.DefaultIfEmpty() where sub == null select row1).ToList(); if (adds.Any()) { allFiles.AddRange(adds.OrderBy(f => f.Filename)); allFiles.Sort(domainUpkfileComparison); if (!menuViewModel.IsOfflineMode) { await scanUpkFiles(adds); } else { adds.ForEach(f => f.Id = Guid.NewGuid().ToString()); } } viewModel.AllTypes = menuViewModel.IsOfflineMode ? new ObservableCollection <string>() : new ObservableCollection <string>(allFiles.SelectMany(f => f.GetCurrentExports().Types.Select(e => e.Name)).Distinct().OrderBy(s => s)); allFiles.ForEach(f => { f.ModdedFiles.AddRange(mods.Where(mf => Path.GetFileName(mf.GameFilename) == Path.GetFileName(f.GameFilename) && (Path.GetDirectoryName(mf.GameFilename) ?? String.Empty).StartsWith(Path.GetDirectoryName(f.GameFilename) ?? String.Empty))); }); allFileEntities.AddRange(mapper.Map <List <FileViewEntity> >(allFiles)); allFileEntities.ForEach(fe => fe.PropertyChanged += onFileEntityViewModelChanged); showFileTypes(); progress.IsComplete = true; messenger.Send(progress); messenger.Send(new FileListingLoadedMessage { Allfiles = allFiles }); isLoadInProgress = false; }
private async Task scanUpkFiles(List <DomainUpkFile> upkFiles) { isScanInProgress = true; LoadProgressMessage message = new LoadProgressMessage { Text = "Scanning UPK Files", Current = 0, Total = upkFiles.Count }; List <DomainUpkFile> saveCache = new List <DomainUpkFile>(); foreach (DomainUpkFile file in upkFiles) { FileViewEntity fileEntity = viewModel.Files.SingleOrDefault(fe => fe.Filename == file.Filename) ?? mapper.Map <FileViewEntity>(file); message.Current++; message.StatusText = Path.Combine(settings.PathToGame, file.GameFilename); messenger.Send(message); await scanUpkFile(fileEntity, file); file.FileSize = file.Header.FileSize; List <DomainExportType> exports = new List <DomainExportType>(); foreach (string type in file.Header.ExportTable.Select(e => e.TypeReferenceNameIndex.Name).Distinct().OrderBy(s => s)) { exports.Add(new DomainExportType { Name = type, ExportNames = file.Header.ExportTable.Where(e => e.TypeReferenceNameIndex.Name == type).Select(e => e.NameTableIndex.Name).Distinct().OrderBy(s => s).ToList() }); } file.Exports.Where(e => e.Version == version).ToList().ForEach(e => file.Exports.Remove(e)); file.Exports.Add(new DomainExportVersion { Version = version, Types = exports }); fileEntity.FileSize = file.Header.FileSize; fileEntity.ExportTypes = new ObservableCollection <string>(exports.Select(e => e.Name)); file.Header = null; string path = Path.GetDirectoryName(file.GameFilename); if (path != null && path.ToLowerInvariant().EndsWith("cookedpc")) { if (!menuViewModel.IsOfflineMode) { saveCache.Add(file); } if (saveCache.Count == 50) { remoteRepository.SaveUpkFile(saveCache).FireAndForget(); saveCache.Clear(); } } } if (saveCache.Any()) { remoteRepository.SaveUpkFile(saveCache).FireAndForget(); } message.IsComplete = true; messenger.Send(message); isScanInProgress = false; }
private async Task loadAllFiles() { messenger.Send(new FileLoadingMessage()); allFileEntities.ForEach(fe => fe.PropertyChanged -= onFileEntityViewModelChanged); allFileEntities.Clear(); allFiles.Clear(); viewModel.Files.Clear(); if (String.IsNullOrEmpty(settings.PathToGame)) { return; } LoadProgressMessage progress = new LoadProgressMessage { Text = "Loading Game Files..." }; messenger.Send(progress); try { version = await repository.GetGameVersion(settings.PathToGame); } catch (FileNotFoundException) { messenger.Send(new MessageBoxDialogMessage { Header = "Game Version File Not Found", Message = "The VersionInfo_BnS*.ini file could not be found.\n\nPlease ensure your settings are pointed to the 'contents' directory.", HasCancel = false }); progress.IsComplete = true; messenger.Send(progress); return; } List <DomainUpkFile> localFiles = await loadGameFiles(); if (!localFiles.Any()) { progress.IsComplete = true; messenger.Send(progress); return; } List <DomainUpkFile> mods = (from row in localFiles let path = Path.GetDirectoryName(row.GameFilename) where path != null && !path.EndsWith("CookedPC", StringComparison.CurrentCultureIgnoreCase) select row).ToList(); localFiles.RemoveAll(f => mods.Contains(f)); progress.Text = "Loading Remote Database..."; messenger.Send(progress); List <DomainUpkFile> remoteFiles; try { remoteFiles = await remoteRepository.LoadUpkFiles(); } catch (Exception ex) { messenger.Send(new MessageBoxDialogMessage { Header = "Error Received from Remote Database", Message = $"The remote database returned an error. Please try again in a few minutes.\n\n{ex.Message}", HasCancel = false }); progress.IsComplete = true; messenger.Send(progress); return; } List <DomainUpkFile> matches = (from row1 in localFiles join row2 in remoteFiles on new { row1.ContentsRoot, row1.Package } equals new { row2.ContentsRoot, row2.Package } where row1.FileSize == row2.FileSize let a = row2.GameFilename = row1.GameFilename select row2).ToList(); if (matches.Any()) { allFiles.AddRange(matches.OrderBy(f => f.Filename)); } List <DomainUpkFile> changes = (from row1 in localFiles join row2 in remoteFiles on new { row1.ContentsRoot, row1.Package } equals new { row2.ContentsRoot, row2.Package } where row1.FileSize != row2.FileSize let a = row2.GameFilename = row1.GameFilename select row2).ToList(); if (changes.Any()) { allFiles.AddRange(changes.OrderBy(f => f.Filename)); allFiles.Sort(domainUpkfileComparison); await scanUpkFiles(changes); } List <DomainUpkFile> adds = (from row1 in localFiles join row2 in remoteFiles on new { row1.ContentsRoot, row1.Package } equals new { row2.ContentsRoot, row2.Package } into fileGroup from sub in fileGroup.DefaultIfEmpty() where sub == null select row1).ToList(); if (adds.Any()) { allFiles.AddRange(adds.OrderBy(f => f.Filename)); allFiles.Sort(domainUpkfileComparison); await scanUpkFiles(adds); } viewModel.AllTypes = new ObservableCollection <string>(allFiles.SelectMany(f => f.GetBestExports(version).Select(e => e.Name)).Distinct().OrderBy(s => s)); // ReSharper disable once PossibleNullReferenceException allFiles.ForEach(f => { f.ModdedFiles.AddRange(mods.Where(mf => Path.GetFileName(mf.GameFilename) == Path.GetFileName(f.GameFilename) && Path.GetDirectoryName(mf.GameFilename).StartsWith(Path.GetDirectoryName(f.GameFilename)))); }); allFileEntities.AddRange(mapper.Map <List <FileViewEntity> >(allFiles)); allFileEntities.ForEach(fe => fe.PropertyChanged += onFileEntityViewModelChanged); showFileTypes(); progress.IsComplete = true; messenger.Send(progress); messenger.Send(new FileListingLoadedMessage { Allfiles = allFiles }); }