public ClientHandler(string basePath, ClientCreateArgs createArgs) { basePath = basePath ?? ""; BasePath = basePath; CreateArgs = createArgs; string flavorInfoProductCode = null; if (createArgs.UseContainer && !Directory.Exists(basePath)) { throw new FileNotFoundException("invalid archive directory"); } var dbPath = Path.Combine(basePath, createArgs.ProductDatabaseFilename); try { if (File.Exists(dbPath)) { using (var _ = new PerfCounter("AgentDatabase::ctor`string`bool")) foreach (var install in new AgentDatabase(dbPath).Data.ProductInstall) { if (string.IsNullOrEmpty(createArgs.Flavor) || install.Settings.GameSubfolder.Contains(createArgs.Flavor)) { AgentProduct = install; break; } } if (AgentProduct == null) { throw new InvalidDataException(); } Product = ProductHelpers.ProductFromUID(AgentProduct.ProductCode); } else { throw new InvalidDataException(); } } catch { try { if (File.Exists(Path.Combine(basePath, ".flavor.info"))) { // mixed installation, store the product code to be used below flavorInfoProductCode = File.ReadLines(Path.Combine(basePath, ".flavor.info")).Skip(1).First(); Product = ProductHelpers.ProductFromUID(flavorInfoProductCode); BasePath = basePath = Path.Combine(basePath, "../"); // lmao Logger.Info("Core", $".flavor.info detected. Found product \"{flavorInfoProductCode}\""); } else { throw new InvalidDataException(); } } catch { try { Product = ProductHelpers.ProductFromLocalInstall(basePath); } catch { if (createArgs.VersionSource == ClientCreateArgs.InstallMode.Local) // if we need an archive then we should be able to detect the product { throw; } Product = createArgs.OnlineProduct; } } AgentProduct = new ProductInstall { ProductCode = flavorInfoProductCode ?? createArgs.Product ?? ProductHelpers.UIDFromProduct(Product), Settings = new UserSettings { SelectedTextLanguage = createArgs.TextLanguage ?? "enUS", SelectedSpeechLanguage = createArgs.SpeechLanguage ?? "enUS", PlayRegion = "us" } }; if (AgentProduct.Settings.SelectedSpeechLanguage == AgentProduct.Settings.SelectedTextLanguage) { AgentProduct.Settings.Languages.Add(new LanguageSetting { Language = AgentProduct.Settings.SelectedTextLanguage, Option = LanguageOption.LangoptionTextAndSpeech }); } else { AgentProduct.Settings.Languages.Add(new LanguageSetting { Language = AgentProduct.Settings.SelectedTextLanguage, Option = LanguageOption.LangoptionText }); AgentProduct.Settings.Languages.Add(new LanguageSetting { Language = AgentProduct.Settings.SelectedSpeechLanguage, Option = LanguageOption.LangoptionSpeech }); } } if (string.IsNullOrWhiteSpace(createArgs.TextLanguage)) { createArgs.TextLanguage = AgentProduct.Settings.SelectedTextLanguage; } if (string.IsNullOrWhiteSpace(createArgs.SpeechLanguage)) { createArgs.SpeechLanguage = AgentProduct.Settings.SelectedSpeechLanguage; } if (createArgs.Online) { using var _ = new PerfCounter("INetworkHandler::ctor`ClientHandler"); if (createArgs.OnlineRootHost.StartsWith("ribbit:")) { NetHandle = new RibbitCDNClient(this); } else { NetHandle = new NGDPClient(this); } } if (createArgs.VersionSource == ClientCreateArgs.InstallMode.Local) { var installationInfoPath = Path.Combine(basePath, createArgs.InstallInfoFileName) + createArgs.ExtraFileEnding; if (!File.Exists(installationInfoPath)) { throw new FileNotFoundException(installationInfoPath); } using var _ = new PerfCounter("InstallationInfo::ctor`string"); InstallationInfo = new InstallationInfo(installationInfoPath, AgentProduct.ProductCode); } else { using var _ = new PerfCounter("InstallationInfo::ctor`INetworkHandler"); InstallationInfo = new InstallationInfo(NetHandle, createArgs.OnlineRegion); } Logger.Info("CASC", $"{Product} build {InstallationInfo.Values["Version"]}"); if (createArgs.UseContainer) { Logger.Info("CASC", "Initializing..."); using var _ = new PerfCounter("ContainerHandler::ctor`ClientHandler"); ContainerHandler = new ContainerHandler(this); } using (var _ = new PerfCounter("ConfigHandler::ctor`ClientHandler")) ConfigHandler = new ConfigHandler(this); using (var _ = new PerfCounter("EncodingHandler::ctor`ClientHandler")) EncodingHandler = new EncodingHandler(this); if (ConfigHandler.BuildConfig.VFSRoot != null) { using var _ = new PerfCounter("VFSFileTree::ctor`ClientHandler"); VFS = new VFSFileTree(this); } if (createArgs.Online) { m_cdnIdx = CDNIndexHandler.Initialize(this); } using (var _ = new PerfCounter("ProductHandlerFactory::GetHandler`TACTProduct`ClientHandler`Stream")) ProductHandler = ProductHandlerFactory.GetHandler(Product, this, OpenCKey(ConfigHandler.BuildConfig.Root.ContentKey)); Logger.Info("CASC", "Ready"); }
public ClientHandler(string?basePath, ClientCreateArgs createArgs) { BasePath = basePath ?? ""; // should it be empty string? lol CreateArgs = createArgs; ProductCode = createArgs.Product; // If we are using a container OR if InstallMode == Local if (createArgs.UseContainer) { if (!Directory.Exists(BasePath)) { throw new FileNotFoundException($"Invalid archive directory. Directory {BasePath} does not exist. Please specify a valid directory."); } try { // if someone specified a flavor, try and see what flavor and fix the base path var flavorInfoPath = Path.Combine(BasePath, ".flavor.info"); if (File.Exists(flavorInfoPath)) { // mixed installation, store the product code to be used below ProductCode = File.ReadLines(flavorInfoPath).Skip(1).First(); Product = ProductHelpers.ProductFromUID(ProductCode); BasePath = Path.GetFullPath(Path.Combine(BasePath, "../")); // base path is a directory up from the flavor Logger.Info("Core", $".flavor.info detected. Found product \"{ProductCode}\""); } } catch (Exception ex) { Logger.Warn("Core", $"Failed reading .flavor.info file! {ex.Message}"); } // ensure to see the .build.info file exists. if it doesn't then we can't continue InstallationInfoPath = Path.Combine(BasePath, createArgs.InstallInfoFileName) + createArgs.ExtraFileEnding; if (!File.Exists(InstallationInfoPath)) { throw new FileNotFoundException($"Invalid archive directory! {InstallationInfoPath} was not found. You must provide the path to a valid install."); } // If there was no flavor specified, try to find the flavor in the .build.info file ProductCode ??= createArgs.Product ?? ProductHelpers.TryGetUIDFromProduct(ProductHelpers.TryGetProductFromLocalInstall(BasePath)); InstallationInfoFile = new InstallationInfoFile(InstallationInfoPath); // If product is unknown it means we loaded on the base path and not a flavor e.g. C:/Games/Overwatch // so we need to load the .build.info file and get the product from there if (Product == TACTProduct.Unknown) { var installationInfo = InstallationInfoFile.GetInstallationInfoForProduct(ProductCode); if (installationInfo == null) { Logger.Warn("Core", $"Failed to find product \"{ProductCode}\" in {createArgs.InstallInfoFileName} file! Using first available."); installationInfo = InstallationInfoFile.GetFirstOrDefault(); } // if there's no data in the .build.info file? Shouldn't really be possible if (installationInfo == null) { throw new Exception($"Failed to find a valid product in {createArgs.InstallInfoFileName} file!"); } // If product code is null this ProductFromUID will throw an exception ProductCode = installationInfo.Values.GetValueOrDefault("Product"); Product = ProductHelpers.ProductFromUID(ProductCode); Logger.Info("Core", $"Found product \"{ProductCode}\" via {createArgs.InstallInfoFileName}"); } } // If there is no product specified it's because we aren't using a container or we're using online mode. // Find the product from the productCode provided by createArgs or if there is none, find it from the local install path // tho i'm not sure what the chances are of there being an install path provided if you're loading from remote as it would be kind of redundant but whatever if (Product == TACTProduct.Unknown) { Product = ProductHelpers.TryGetProductFromUID(ProductCode); // if no product was specified via ClientCreateArgs, try and find it from the local install path if (Product == TACTProduct.Unknown) { Product = ProductHelpers.TryGetProductFromLocalInstall(BasePath); } if (Product == TACTProduct.Unknown) { if (createArgs.VersionSource == ClientCreateArgs.InstallMode.Remote) { throw new Exception("Failed to determine TACT Product. This is required if you're loading from remote."); } Logger.Warn("Core", "Failed to determine TACT Product! This could potentially cause issues!"); } } if (createArgs.Online) { using var _ = new PerfCounter("INetworkHandler::ctor`ClientHandler"); if (createArgs.OnlineRootHost.StartsWith("ribbit:")) { NetHandle = new RibbitCDNClient(this); } else { NetHandle = new NGDPClient(this); } } if (createArgs.VersionSource == ClientCreateArgs.InstallMode.Local) { // should always exist as it's fetched above but we can't continue without being able to load the installation info if (!File.Exists(InstallationInfoPath)) { throw new FileNotFoundException(InstallationInfoPath); } using var _ = new PerfCounter("InstallationInfo::ctor`string"); InstallationInfo = new InstallationInfo(InstallationInfoPath !, ProductCode !); } else { using var _ = new PerfCounter("InstallationInfo::ctor`INetworkHandler"); InstallationInfo = new InstallationInfo(NetHandle !, createArgs.OnlineRegion); } // try to load the agent database and use the selected language if we don't already have one specified if (createArgs.UseContainer) { AgentProduct = TryGetAgentDatabase(); if (AgentProduct != null) { if (string.IsNullOrWhiteSpace(createArgs.TextLanguage)) { createArgs.TextLanguage = AgentProduct.Settings.SelectedTextLanguage; CreateArgs.TextLanguage = AgentProduct.Settings.SelectedTextLanguage; } if (string.IsNullOrWhiteSpace(createArgs.SpeechLanguage)) { createArgs.SpeechLanguage = AgentProduct.Settings.SelectedSpeechLanguage; CreateArgs.SpeechLanguage = AgentProduct.Settings.SelectedSpeechLanguage; } } } Logger.Info("CASC", $"{Product} build {InstallationInfo.Values["Version"]}"); if (createArgs.UseContainer) { Logger.Info("CASC", "Initializing..."); using var _ = new PerfCounter("ContainerHandler::ctor`ClientHandler"); ContainerHandler = new ContainerHandler(this); } using (var _ = new PerfCounter("ConfigHandler::ctor`ClientHandler")) ConfigHandler = new ConfigHandler(this); using (var _ = new PerfCounter("EncodingHandler::ctor`ClientHandler")) EncodingHandler = new EncodingHandler(this); if (ConfigHandler.BuildConfig.VFSRoot != null) { using var _ = new PerfCounter("VFSFileTree::ctor`ClientHandler"); VFS = new VFSFileTree(this); } if (createArgs.Online) { m_cdnIdx = CDNIndexHandler.Initialize(this); } using (var _ = new PerfCounter("ProductHandlerFactory::GetHandler`TACTProduct`ClientHandler`Stream")) ProductHandler = ProductHandlerFactory.GetHandler(Product, this, OpenCKey(ConfigHandler.BuildConfig.Root.ContentKey) !); Logger.Info("CASC", "Ready"); }