/// <summary> /// Touches up any existing tutorial assets or downloads missing ones. /// </summary> public static void TouchupTutorial() { var fileRootPath = Utilities.GetTutorialServiceCache(); foreach (var step in App.TutorialService) { var imagePath = Path.Combine(fileRootPath, step.imagename); bool download = !File.Exists(imagePath) || Utilities.CalculateMD5(imagePath) != step.imagemd5; if (download) { foreach (var endpoint in StaticFilesBaseEndpoints) { Uri myUri = new Uri(endpoint); string host = myUri.Host; var fullurl = endpoint + "tutorial/" + step.imagename; Log.Information($"Downloading {step.imagename} from endpoint {host}"); var downloadedImage = OnlineContent.DownloadToMemory(fullurl, null, step.imagemd5); if (downloadedImage.errorMessage == null) { downloadedImage.result.WriteToFile(imagePath); break; } else { Log.Error($@"Unable to download {step.imagename} from endpoint {host}: {downloadedImage.errorMessage}"); } } } } }
private static void LoadLocalBasegameIdentificationService() { if (LocalBasegameFileIdentificationService != null) { return; } var file = Utilities.GetLocalBasegameIdentificationServiceFile(); if (File.Exists(file)) { try { LocalBasegameFileIdentificationService = JsonConvert .DeserializeObject < Dictionary <string, CaseInsensitiveDictionary < List <BasegameFileIdentificationService.BasegameCloudDBFile> > > >( File.ReadAllText(file)); Log.Information(@"Loaded Local Basegame File Identification Service"); } catch (Exception e) { Log.Error($@"Error loading local BGFIS: {e.Message}"); LocalBasegameFileIdentificationService = OnlineContent.getBlankBGFIDB(); } } else { Log.Information(@"Loaded blank Local Basegame File Identification Service"); LocalBasegameFileIdentificationService = OnlineContent.getBlankBGFIDB(); } }
private static bool DownloadLocalization(string lang) { var livelocmd5 = App.ServerManifest[$@"livelocalization-{lang}"]; // this was checked previously var url = LocalizationEndpoint + $@"?lang={lang}&build={App.BuildNumber}"; var result = OnlineContent.DownloadToMemory(url, hash: livelocmd5); if (result.errorMessage == null) { // OK! result.result.WriteToFile(getCachedLocalizationFile(lang)); Log.Information($@"Wrote updated cached localization file for {lang}"); return(true); } // RIP Log.Error($@"Error download updated localization file {lang}: {result.result}"); return(false); }
public static Dictionary <string, CaseInsensitiveDictionary <List <BasegameFileIdentificationService.BasegameCloudDBFile> > > FetchBasegameFileIdentificationServiceManifest(bool overrideThrottling = false) { Log.Information(@"Fetching basegame file identification manifest"); //read cached first. string cached = null; if (File.Exists(Utilities.GetBasegameIdentificationCacheFile())) { try { cached = File.ReadAllText(Utilities.GetBasegameIdentificationCacheFile()); } catch (Exception e) { var attachments = new List <ErrorAttachmentLog>(); string log = LogCollector.CollectLatestLog(true); if (log != null && log.Length < ByteSizeLib.ByteSize.BytesInMegaByte * 7) { attachments.Add(ErrorAttachmentLog.AttachmentWithText(log, @"applog.txt")); } Crashes.TrackError(e, new Dictionary <string, string>() { { @"Error type", @"Error reading cached online content" }, { @"Service", @"Basegame File Identification Service" }, { @"Message", e.Message } }, attachments.ToArray()); } } if (!File.Exists(Utilities.GetBasegameIdentificationCacheFile()) || overrideThrottling || OnlineContent.CanFetchContentThrottleCheck()) { var urls = new[] { BasegameFileIdentificationServiceURL, BasegameFileIdentificationServiceBackupURL }; foreach (var staticurl in urls) { Uri myUri = new Uri(staticurl); string host = myUri.Host; try { using var wc = new ShortTimeoutWebClient(); string json = wc.DownloadStringAwareOfEncoding(staticurl); File.WriteAllText(Utilities.GetBasegameIdentificationCacheFile(), json); return(JsonConvert.DeserializeObject <Dictionary <string, CaseInsensitiveDictionary <List <BasegameFileIdentificationService.BasegameCloudDBFile> > > >(json)); } catch (Exception e) { //Unable to fetch latest help. Log.Error($"Error fetching online basegame file identification service from endpoint {host}: {e.Message}"); } } if (cached == null) { Log.Error("Unable to load basegame file identification service and local file doesn't exist. Returning a blank copy."); Dictionary <string, CaseInsensitiveDictionary <List <BasegameFileIdentificationService.BasegameCloudDBFile> > > d = new Dictionary <string, CaseInsensitiveDictionary <List <BasegameFileIdentificationService.BasegameCloudDBFile> > > { ["ME1"] = new CaseInsensitiveDictionary <List <BasegameFileIdentificationService.BasegameCloudDBFile> >(), ["ME2"] = new CaseInsensitiveDictionary <List <BasegameFileIdentificationService.BasegameCloudDBFile> >(), ["ME3"] = new CaseInsensitiveDictionary <List <BasegameFileIdentificationService.BasegameCloudDBFile> >() }; return(d); } } Log.Information("Using cached BGFIS instead"); try { return(JsonConvert.DeserializeObject <Dictionary <string, CaseInsensitiveDictionary <List <BasegameFileIdentificationService.BasegameCloudDBFile> > > >(cached)); } catch (Exception e) { Log.Error("Could not parse cached basegame file identification service file. Returning blank BFIS data instead. Reason: " + e.Message); return(new Dictionary <string, CaseInsensitiveDictionary <List <BasegameFileIdentificationService.BasegameCloudDBFile> > > { ["ME1"] = new CaseInsensitiveDictionary <List <BasegameFileIdentificationService.BasegameCloudDBFile> >(), ["ME2"] = new CaseInsensitiveDictionary <List <BasegameFileIdentificationService.BasegameCloudDBFile> >(), ["ME3"] = new CaseInsensitiveDictionary <List <BasegameFileIdentificationService.BasegameCloudDBFile> >() }); } }
public static Dictionary <long, List <ThirdPartyServices.ThirdPartyImportingInfo> > FetchThirdPartyImportingService(bool overrideThrottling = false) { string cached = null; if (File.Exists(Utilities.GetThirdPartyImportingCachedFile())) { try { cached = File.ReadAllText(Utilities.GetThirdPartyImportingCachedFile()); } catch (Exception e) { var attachments = new List <ErrorAttachmentLog>(); string log = LogCollector.CollectLatestLog(true); if (log != null && log.Length < ByteSizeLib.ByteSize.BytesInMegaByte * 7) { attachments.Add(ErrorAttachmentLog.AttachmentWithText(log, "applog.txt")); } Crashes.TrackError(e, new Dictionary <string, string>() { { "Error type", "Error reading cached online content" }, { "Service", "Third Party Importing Service" }, { "Message", e.Message } }, attachments.ToArray()); } } if (!File.Exists(Utilities.GetThirdPartyImportingCachedFile()) || overrideThrottling || OnlineContent.CanFetchContentThrottleCheck()) { try { using var wc = new ShortTimeoutWebClient(); string json = wc.DownloadStringAwareOfEncoding(ThirdPartyImportingServiceURL); File.WriteAllText(Utilities.GetThirdPartyImportingCachedFile(), json); return(JsonConvert.DeserializeObject <Dictionary <long, List <ThirdPartyServices.ThirdPartyImportingInfo> > >(json)); } catch (Exception e) { //Unable to fetch latest help. Log.Error("Error fetching latest importing service file: " + e.Message); if (cached != null) { Log.Warning("Using cached third party importing service file instead"); } else { Log.Error("Unable to fetch latest third party importing service file from server and local file doesn't exist. Returning a blank copy."); return(new Dictionary <long, List <ThirdPartyServices.ThirdPartyImportingInfo> >()); } } } try { return(JsonConvert.DeserializeObject <Dictionary <long, List <ThirdPartyServices.ThirdPartyImportingInfo> > >(cached)); } catch (Exception e) { Log.Error("Unable to parse cached importing service file: " + e.Message); return(new Dictionary <long, List <ThirdPartyServices.ThirdPartyImportingInfo> >()); } }
public static bool UpdateMod(ModUpdateInfo updateInfo, string stagingDirectory, Action <string> errorMessageCallback) { Log.Information(@"Updating mod: " + updateInfo.mod.ModName + @" from " + updateInfo.LocalizedLocalVersionString + @" to " + updateInfo.LocalizedServerVersionString); string modPath = updateInfo.mod.ModPath; string serverRoot = UpdateStorageRoot + updateInfo.serverfolder + '/'; bool cancelDownloading = false; var stagedFileMapping = new ConcurrentDictionary <string, string>(); foreach (var sf in updateInfo.applicableUpdates) { sf.AmountDownloaded = 0; //reset in the event this is a second attempt } Parallel.ForEach(updateInfo.applicableUpdates, new ParallelOptions { MaxDegreeOfParallelism = 4 }, (sourcefile) => { if (!cancelDownloading) { void downloadProgressCallback(long received, long totalToReceived) { sourcefile.AmountDownloaded = received; updateInfo.RecalculateAmountDownloaded(); } string fullurl = serverRoot + sourcefile.relativefilepath.Replace('\\', '/') + @".lzma"; var downloadedFile = OnlineContent.DownloadToMemory(fullurl, downloadProgressCallback, sourcefile.lzmahash, true); if (downloadedFile.errorMessage != null && !cancelDownloading) { errorMessageCallback?.Invoke(downloadedFile.errorMessage); cancelDownloading = true; return; } if (cancelDownloading) { return; //Concurrency for long running download to memory } //Hash OK string stagingFile = Path.Combine(stagingDirectory, sourcefile.relativefilepath); Directory.CreateDirectory(Directory.GetParent(stagingFile).FullName); //Decompress file MemoryStream decompressedStream = new MemoryStream(); SevenZipHelper.LZMA.DecompressLZMAStream(downloadedFile.result, decompressedStream); //SevenZipExtractor.DecompressStream(downloadedFile.result, decompressedStream, null, null); //Hash check output if (decompressedStream.Length != sourcefile.size) { Log.Error($@"Decompressed file ({sourcefile.relativefilepath}) is not of correct size. Expected: {sourcefile.size}, got: {decompressedStream.Length}"); errorMessageCallback?.Invoke(M3L.GetString(M3L.string_interp_decompressedFileNotCorrectSize, sourcefile.relativefilepath, sourcefile.size, decompressedStream.Length)); //force localize cancelDownloading = true; return; } var decompressedMD5 = Utilities.CalculateMD5(decompressedStream); if (decompressedMD5 != sourcefile.hash) { Log.Error($@"Decompressed file ({sourcefile.relativefilepath}) has the wrong hash. Expected: {sourcefile.hash}, got: {decompressedMD5}"); errorMessageCallback?.Invoke(M3L.GetString(M3L.string_interp_decompressedFileWrongHash, sourcefile.relativefilepath, sourcefile.hash, decompressedMD5)); //force localize cancelDownloading = true; return; } File.WriteAllBytes(stagingFile, decompressedStream.ToArray()); if (sourcefile.timestamp != 0) { File.SetLastWriteTimeUtc(stagingFile, new DateTime(sourcefile.timestamp)); } Log.Information(@"Wrote updater staged file: " + stagingFile); stagedFileMapping[stagingFile] = Path.Combine(modPath, sourcefile.relativefilepath); } }); if (cancelDownloading) { //callback already should have occured return(false); } //All files have been downloaded successfully. updateInfo.DownloadButtonText = M3L.GetString(M3L.string_applying); //Apply update if (stagedFileMapping.Count > 0) { Log.Information(@"Applying staged update to mod directory"); foreach (var file in stagedFileMapping) { Log.Information($@"Applying update file: {file.Key} => {file.Value}"); Directory.CreateDirectory(Directory.GetParent(file.Value).FullName); File.Copy(file.Key, file.Value, true); } } //Delete files no longer in manifest foreach (var file in updateInfo.filesToDelete) { var fileToDelete = Path.Combine(modPath, file); Log.Information(@"Deleting file for mod update: " + fileToDelete); File.Delete(fileToDelete); } //Delete empty subdirectories Utilities.DeleteEmptySubdirectories(modPath); Utilities.DeleteFilesAndFoldersRecursively(stagingDirectory); //We're done! return(true); }
//private static readonly string LatestHelpFileLink = StaticFilesBaseURL_Github + "dynamichelp/latesthelp-localized.xml"; //internal static readonly string HelpResourcesBaseURL = StaticFilesBaseURL_Github + "dynamichelp/resources"; public static List <SortableHelpElement> FetchLatestHelp(string language, bool preferLocal, bool overrideThrottling = false) { var localHelpExists = File.Exists(Utilities.GetLocalHelpFile()); string cached = null; if (localHelpExists) { try { cached = File.ReadAllText(Utilities.GetLocalHelpFile()); } catch (Exception e) { var attachments = new List <ErrorAttachmentLog>(); string log = LogCollector.CollectLatestLog(false); if (log.Length < ByteSizeLib.ByteSize.BytesInMegaByte * 7) { attachments.Add(ErrorAttachmentLog.AttachmentWithText(log, "applog.txt")); } Crashes.TrackError(e, new Dictionary <string, string>() { { "Error type", "Error reading cached online content" }, { "Service", "Dynamic Help" }, { "Message", e.Message } }, attachments.ToArray()); } } if (localHelpExists && preferLocal) { return(ParseLocalHelp(cached, language)); } if (!localHelpExists || overrideThrottling || OnlineContent.CanFetchContentThrottleCheck()) { foreach (var staticendpoint in StaticFilesBaseEndpoints) { using var wc = new System.Net.WebClient(); try { string xml = wc.DownloadStringAwareOfEncoding(staticendpoint + @"dynamichelp/latesthelp-localized.xml"); File.WriteAllText(Utilities.GetLocalHelpFile(), xml); return(ParseLocalHelp(xml, language)); } catch (Exception e) { Log.Error($"Error fetching online help from endpoint {staticendpoint}: {e.Message}"); } } if (cached != null) { Log.Warning("Using cached help instead"); } else { Log.Error("Unable to display dynamic help: Could not fetch online asset and cached help asset does not exist."); return(null); } } try { return(ParseLocalHelp(cached, language)); } catch (Exception e) { Log.Error("Unable to parse local dynamic help file: " + e.Message); return(null); } }
public static List <IntroTutorial.TutorialStep> FetchTutorialManifest(bool overrideThrottling = false) { Log.Information(@"Fetching tutorial manifest"); string cached = null; // Read cached first. if (File.Exists(Utilities.GetTutorialServiceCacheFile())) { try { cached = File.ReadAllText(Utilities.GetTutorialServiceCacheFile()); } catch (Exception e) { var attachments = new List <ErrorAttachmentLog>(); string log = LogCollector.CollectLatestLog(true); if (log != null && log.Length < FileSize.MebiByte * 7) { attachments.Add(ErrorAttachmentLog.AttachmentWithText(log, @"applog.txt")); } Crashes.TrackError(e, new Dictionary <string, string>() { { @"Error type", @"Error reading cached online content" }, { @"Service", @"Tutorial Service" }, { @"Message", e.Message } }, attachments.ToArray()); } } if (!File.Exists(Utilities.GetTutorialServiceCacheFile()) || overrideThrottling || OnlineContent.CanFetchContentThrottleCheck()) { string[] urls = new[] { TutorialServiceURL, TutorialServiceBackupURL }; foreach (var staticurl in urls) { Uri myUri = new Uri(staticurl); string host = myUri.Host; try { using var wc = new ShortTimeoutWebClient(); string json = wc.DownloadStringAwareOfEncoding(staticurl); File.WriteAllText(Utilities.GetTutorialServiceCacheFile(), json); return(JsonConvert.DeserializeObject <List <IntroTutorial.TutorialStep> >(json)); } catch (Exception e) { //Unable to fetch latest help. Log.Error($@"Error fetching latest tutorial service file from endpoint {host}: {e.Message}"); } } if (cached == null) { Log.Error(@"Unable to fetch latest tutorial service file from server and local file doesn't exist. Returning a blank copy."); return(new List <IntroTutorial.TutorialStep>()); } } Log.Information(@"Using cached tutorial service file"); try { return(JsonConvert.DeserializeObject <List <IntroTutorial.TutorialStep> >(cached)); } catch (Exception e) { Log.Error(@"Unable to parse cached importing service file: " + e.Message); return(new List <IntroTutorial.TutorialStep>()); } }
public static Dictionary <string, CaseInsensitiveDictionary <ThirdPartyServices.ThirdPartyModInfo> > FetchThirdPartyIdentificationManifest(bool overrideThrottling = false) { string cached = null; if (File.Exists(Utilities.GetThirdPartyIdentificationCachedFile())) { try { cached = File.ReadAllText(Utilities.GetThirdPartyIdentificationCachedFile()); } catch (Exception e) { var attachments = new List <ErrorAttachmentLog>(); string log = LogCollector.CollectLatestLog(true); if (log != null && log.Length < FileSize.MebiByte * 7) { attachments.Add(ErrorAttachmentLog.AttachmentWithText(log, @"applog.txt")); } Crashes.TrackError(e, new Dictionary <string, string>() { { @"Error type", @"Error reading cached online content" }, { @"Service", @"Third Party Identification Service" }, { @"Message", e.Message } }, attachments.ToArray()); } } if (!File.Exists(Utilities.GetThirdPartyIdentificationCachedFile()) || overrideThrottling || OnlineContent.CanFetchContentThrottleCheck()) { try { using var wc = new ShortTimeoutWebClient(); string json = wc.DownloadStringAwareOfEncoding(ThirdPartyIdentificationServiceURL); File.WriteAllText(Utilities.GetThirdPartyIdentificationCachedFile(), json); return(JsonConvert.DeserializeObject <Dictionary <string, CaseInsensitiveDictionary <ThirdPartyServices.ThirdPartyModInfo> > >(json)); } catch (Exception e) { //Unable to fetch latest help. Log.Error(@"Error fetching online third party identification service: " + e.Message); if (cached != null) { Log.Warning(@"Using cached third party identification service file instead"); } else { Log.Error(@"Unable to load third party identification service and local file doesn't exist. Returning a blank copy."); Dictionary <string, CaseInsensitiveDictionary <ThirdPartyServices.ThirdPartyModInfo> > d = new Dictionary <string, CaseInsensitiveDictionary <ThirdPartyServices.ThirdPartyModInfo> > { [@"ME1"] = new CaseInsensitiveDictionary <ThirdPartyServices.ThirdPartyModInfo>(), [@"ME2"] = new CaseInsensitiveDictionary <ThirdPartyServices.ThirdPartyModInfo>(), [@"ME3"] = new CaseInsensitiveDictionary <ThirdPartyServices.ThirdPartyModInfo>() }; return(d); } } } try { return(JsonConvert.DeserializeObject <Dictionary <string, CaseInsensitiveDictionary <ThirdPartyServices.ThirdPartyModInfo> > >(cached)); } catch (Exception e) { Log.Error(@"Could not parse cached third party identification service file. Returning blank TPMI data instead. Reason: " + e.Message); return(new Dictionary <string, CaseInsensitiveDictionary <ThirdPartyServices.ThirdPartyModInfo> > { [@"ME1"] = new CaseInsensitiveDictionary <ThirdPartyServices.ThirdPartyModInfo>(), [@"ME2"] = new CaseInsensitiveDictionary <ThirdPartyServices.ThirdPartyModInfo>(), [@"ME3"] = new CaseInsensitiveDictionary <ThirdPartyServices.ThirdPartyModInfo>() }); } }