Replaced hardcoded SourceType with custom defined ISourceResolver.

This commit is contained in:
IceReaper
2022-11-06 19:33:01 +01:00
committed by Matthias Mailänder
parent 7188f88ba1
commit 35eb246080
34 changed files with 313 additions and 117 deletions

View File

@@ -0,0 +1,20 @@
#region Copyright & License Information
/*
* Copyright 2007-2022 The OpenRA Developers (see AUTHORS)
* This file is part of OpenRA, which is free software. It is made
* available to you under the terms of the GNU General Public License
* as published by the Free Software Foundation, either version 3 of
* the License, or (at your option) any later version. For more
* information, see COPYING.
*/
#endregion
namespace OpenRA.Mods.Common.Installer
{
public enum Availability
{
Unavailable,
GameSource,
DigitalInstall
}
}

View File

@@ -0,0 +1,19 @@
#region Copyright & License Information
/*
* Copyright 2007-2022 The OpenRA Developers (see AUTHORS)
* This file is part of OpenRA, which is free software. It is made
* available to you under the terms of the GNU General Public License
* as published by the Free Software Foundation, either version 3 of
* the License, or (at your option) any later version. For more
* information, see COPYING.
*/
#endregion
namespace OpenRA.Mods.Common.Installer
{
public interface ISourceResolver
{
string FindSourcePath(ModContent.ModSource modSource);
Availability GetAvailability();
}
}

View File

@@ -43,10 +43,10 @@ namespace OpenRA.Mods.Common.Installer
var length = source.Length; var length = source.Length;
Action<long> onProgress = null; Action<long> onProgress = null;
if (length < InstallFromDiscLogic.ShowPercentageThreshold) if (length < InstallFromSourceLogic.ShowPercentageThreshold)
updateMessage(modData.Translation.GetString(InstallFromDiscLogic.CopyingFilename, Translation.Arguments("filename", displayFilename))); updateMessage(modData.Translation.GetString(InstallFromSourceLogic.CopyingFilename, Translation.Arguments("filename", displayFilename)));
else else
onProgress = b => updateMessage(modData.Translation.GetString(InstallFromDiscLogic.CopyingFilenameProgress, Translation.Arguments("filename", displayFilename, "progress", 100 * b / length))); onProgress = b => updateMessage(modData.Translation.GetString(InstallFromSourceLogic.CopyingFilenameProgress, Translation.Arguments("filename", displayFilename, "progress", 100 * b / length)));
InstallerUtils.CopyStream(source, target, length, onProgress); InstallerUtils.CopyStream(source, target, length, onProgress);
} }

View File

@@ -60,10 +60,10 @@ namespace OpenRA.Mods.Common.Installer
var displayFilename = Path.GetFileName(Path.GetFileName(targetPath)); var displayFilename = Path.GetFileName(Path.GetFileName(targetPath));
Action<long> onProgress = null; Action<long> onProgress = null;
if (length < InstallFromDiscLogic.ShowPercentageThreshold) if (length < InstallFromSourceLogic.ShowPercentageThreshold)
updateMessage(modData.Translation.GetString(InstallFromDiscLogic.Extracing, Translation.Arguments("filename", displayFilename))); updateMessage(modData.Translation.GetString(InstallFromSourceLogic.Extracing, Translation.Arguments("filename", displayFilename)));
else else
onProgress = b => updateMessage(modData.Translation.GetString(InstallFromDiscLogic.ExtracingProgress, Translation.Arguments("filename", displayFilename, "progress", 100 * b / length))); onProgress = b => updateMessage(modData.Translation.GetString(InstallFromSourceLogic.ExtracingProgress, Translation.Arguments("filename", displayFilename, "progress", 100 * b / length)));
using (var target = File.OpenWrite(targetPath)) using (var target = File.OpenWrite(targetPath))
{ {

View File

@@ -63,7 +63,7 @@ namespace OpenRA.Mods.Common.Installer
{ {
Log.Write("install", $"Extracting {sourcePath} -> {targetPath}"); Log.Write("install", $"Extracting {sourcePath} -> {targetPath}");
var displayFilename = Path.GetFileName(Path.GetFileName(targetPath)); var displayFilename = Path.GetFileName(Path.GetFileName(targetPath));
Action<int> onProgress = percent => updateMessage(modData.Translation.GetString(InstallFromDiscLogic.ExtracingProgress, Translation.Arguments("filename", displayFilename, "progress", percent))); Action<int> onProgress = percent => updateMessage(modData.Translation.GetString(InstallFromSourceLogic.ExtracingProgress, Translation.Arguments("filename", displayFilename, "progress", percent)));
reader.ExtractFile(node.Value.Value, target, onProgress); reader.ExtractFile(node.Value.Value, target, onProgress);
} }
} }

View File

@@ -44,7 +44,7 @@ namespace OpenRA.Mods.Common.Installer
{ {
Log.Write("install", $"Extracting {sourcePath} -> {targetPath}"); Log.Write("install", $"Extracting {sourcePath} -> {targetPath}");
var displayFilename = Path.GetFileName(Path.GetFileName(targetPath)); var displayFilename = Path.GetFileName(Path.GetFileName(targetPath));
Action<int> onProgress = percent => updateMessage(modData.Translation.GetString(InstallFromDiscLogic.ExtracingProgress, Translation.Arguments("filename", displayFilename, "progress", percent))); Action<int> onProgress = percent => updateMessage(modData.Translation.GetString(InstallFromSourceLogic.ExtracingProgress, Translation.Arguments("filename", displayFilename, "progress", percent)));
reader.ExtractFile(node.Value.Value, target, onProgress); reader.ExtractFile(node.Value.Value, target, onProgress);
} }
} }

View File

@@ -59,10 +59,10 @@ namespace OpenRA.Mods.Common.Installer
var displayFilename = Path.GetFileName(Path.GetFileName(targetPath)); var displayFilename = Path.GetFileName(Path.GetFileName(targetPath));
Action<long> onProgress = null; Action<long> onProgress = null;
if (length < InstallFromDiscLogic.ShowPercentageThreshold) if (length < InstallFromSourceLogic.ShowPercentageThreshold)
updateMessage(modData.Translation.GetString(InstallFromDiscLogic.Extracing, Translation.Arguments("filename", displayFilename))); updateMessage(modData.Translation.GetString(InstallFromSourceLogic.Extracing, Translation.Arguments("filename", displayFilename)));
else else
onProgress = b => updateMessage(modData.Translation.GetString(InstallFromDiscLogic.ExtracingProgress, Translation.Arguments("filename", displayFilename, "progress", 100 * b / length))); onProgress = b => updateMessage(modData.Translation.GetString(InstallFromSourceLogic.ExtracingProgress, Translation.Arguments("filename", displayFilename, "progress", 100 * b / length)));
using (var target = File.OpenWrite(targetPath)) using (var target = File.OpenWrite(targetPath))
{ {

View File

@@ -0,0 +1,60 @@
#region Copyright & License Information
/*
* Copyright 2007-2022 The OpenRA Developers (see AUTHORS)
* This file is part of OpenRA, which is free software. It is made
* available to you under the terms of the GNU General Public License
* as published by the Free Software Foundation, either version 3 of
* the License, or (at your option) any later version. For more
* information, see COPYING.
*/
#endregion
using System;
using System.IO;
using System.Linq;
namespace OpenRA.Mods.Common.Installer
{
public class DiscSourceResolver : ISourceResolver
{
public string FindSourcePath(ModContent.ModSource source)
{
var volumes = DriveInfo.GetDrives()
.Where(d =>
{
if (d.DriveType == DriveType.CDRom && d.IsReady)
return true;
// HACK: the "TFD" DVD is detected as a fixed udf-formatted drive on OSX
if (d.DriveType == DriveType.Fixed && d.DriveFormat == "udf")
return true;
return false;
})
.Select(v => v.RootDirectory.FullName);
if (Platform.CurrentPlatform == PlatformType.Linux)
{
// Outside of Gnome, most mounting tools on Linux don't set DriveType.CDRom
// so provide a fallback by allowing users to manually mount images on known paths
volumes = volumes.Concat(new[]
{
"/media/openra",
"/media/" + Environment.UserName + "/openra",
"/mnt/openra"
});
}
foreach (var volume in volumes)
if (InstallerUtils.IsValidSourcePath(volume, source))
return volume;
return null;
}
public Availability GetAvailability()
{
return Availability.GameSource;
}
}
}

View File

@@ -0,0 +1,50 @@
#region Copyright & License Information
/*
* Copyright 2007-2022 The OpenRA Developers (see AUTHORS)
* This file is part of OpenRA, which is free software. It is made
* available to you under the terms of the GNU General Public License
* as published by the Free Software Foundation, either version 3 of
* the License, or (at your option) any later version. For more
* information, see COPYING.
*/
#endregion
using System.IO;
using System.Runtime.InteropServices;
namespace OpenRA.Mods.Common.Installer
{
public class RegistryDirectoryFromFileSourceResolver : ISourceResolver
{
public string FindSourcePath(ModContent.ModSource source)
{
if (source.RegistryKey == null)
return null;
if (Platform.CurrentPlatform != PlatformType.Windows)
return null;
// We need an extra check for the platform here to silence a warning when the registry is accessed
// TODO: Remove this once our platform checks use the same method
if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
return null;
foreach (var prefix in source.RegistryPrefixes)
{
if (!(Microsoft.Win32.Registry.GetValue(prefix + source.RegistryKey, source.RegistryValue, null) is string path))
continue;
path = Path.GetDirectoryName(path);
return InstallerUtils.IsValidSourcePath(path, source) ? path : null;
}
return null;
}
public Availability GetAvailability()
{
return Platform.CurrentPlatform != PlatformType.Windows ? Availability.DigitalInstall : Availability.Unavailable;
}
}
}

View File

@@ -0,0 +1,47 @@
#region Copyright & License Information
/*
* Copyright 2007-2022 The OpenRA Developers (see AUTHORS)
* This file is part of OpenRA, which is free software. It is made
* available to you under the terms of the GNU General Public License
* as published by the Free Software Foundation, either version 3 of
* the License, or (at your option) any later version. For more
* information, see COPYING.
*/
#endregion
using System.Runtime.InteropServices;
namespace OpenRA.Mods.Common.Installer
{
public class RegistryDirectorySourceResolver : ISourceResolver
{
public string FindSourcePath(ModContent.ModSource source)
{
if (source.RegistryKey == null)
return null;
if (Platform.CurrentPlatform != PlatformType.Windows)
return null;
// We need an extra check for the platform here to silence a warning when the registry is accessed
// TODO: Remove this once our platform checks use the same method
if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
return null;
foreach (var prefix in source.RegistryPrefixes)
{
if (!(Microsoft.Win32.Registry.GetValue(prefix + source.RegistryKey, source.RegistryValue, null) is string path))
continue;
return InstallerUtils.IsValidSourcePath(path, source) ? path : null;
}
return null;
}
public Availability GetAvailability()
{
return Platform.CurrentPlatform != PlatformType.Windows ? Availability.DigitalInstall : Availability.Unavailable;
}
}
}

View File

@@ -18,7 +18,6 @@ namespace OpenRA
{ {
public class ModContent : IGlobalModData public class ModContent : IGlobalModData
{ {
public enum SourceType { Disc, RegistryDirectory, RegistryDirectoryFromFile }
public class ModPackage public class ModPackage
{ {
public readonly string Title; public readonly string Title;
@@ -42,7 +41,9 @@ namespace OpenRA
public class ModSource public class ModSource
{ {
public readonly ObjectCreator ObjectCreator; public readonly ObjectCreator ObjectCreator;
public readonly SourceType Type = SourceType.Disc;
[FieldLoader.Ignore]
public readonly MiniYaml Type;
// Used to find installation locations for SourceType.Install // Used to find installation locations for SourceType.Install
public readonly string[] RegistryPrefixes = { string.Empty }; public readonly string[] RegistryPrefixes = { string.Empty };
@@ -62,6 +63,10 @@ namespace OpenRA
ObjectCreator = objectCreator; ObjectCreator = objectCreator;
Title = yaml.Value; Title = yaml.Value;
var type = yaml.Nodes.FirstOrDefault(n => n.Key == "Type");
if (type != null)
Type = type.Value;
var idFiles = yaml.Nodes.FirstOrDefault(n => n.Key == "IDFiles"); var idFiles = yaml.Nodes.FirstOrDefault(n => n.Key == "IDFiles");
if (idFiles != null) if (idFiles != null)
IDFiles = idFiles.Value; IDFiles = idFiles.Value;

View File

@@ -19,7 +19,7 @@ using OpenRA.Widgets;
namespace OpenRA.Mods.Common.Widgets.Logic namespace OpenRA.Mods.Common.Widgets.Logic
{ {
public class InstallFromDiscLogic : ChromeLogic public class InstallFromSourceLogic : ChromeLogic
{ {
// Hide percentage indicators for files smaller than 25 MB // Hide percentage indicators for files smaller than 25 MB
public const int ShowPercentageThreshold = 26214400; public const int ShowPercentageThreshold = 26214400;
@@ -54,19 +54,19 @@ namespace OpenRA.Mods.Common.Widgets.Logic
Mode visible = Mode.Progress; Mode visible = Mode.Progress;
[TranslationReference] [TranslationReference]
static readonly string DetectingDrives = "detecting-drives"; static readonly string DetectingSources = "detecting-sources";
[TranslationReference] [TranslationReference]
static readonly string CheckingDiscs = "checking-discs"; static readonly string CheckingSources = "checking-sources";
[TranslationReference("title")] [TranslationReference("title")]
static readonly string SearchingDiscFor = "searching-disc-for"; static readonly string SearchingSourceFor = "searching-source-for";
[TranslationReference] [TranslationReference]
static readonly string ContentPackageInstallation = "content-package-installation"; static readonly string ContentPackageInstallation = "content-package-installation";
[TranslationReference] [TranslationReference]
static readonly string GameDiscs = "game-discs"; static readonly string GameSources = "game-sources";
[TranslationReference] [TranslationReference]
static readonly string DigitalInstalls = "digital-installs"; static readonly string DigitalInstalls = "digital-installs";
@@ -111,7 +111,7 @@ namespace OpenRA.Mods.Common.Widgets.Logic
static readonly string Back = "back"; static readonly string Back = "back";
[ObjectCreator.UseCtor] [ObjectCreator.UseCtor]
public InstallFromDiscLogic(Widget widget, ModData modData, ModContent content, Dictionary<string, ModContent.ModSource> sources) public InstallFromSourceLogic(Widget widget, ModData modData, ModContent content, Dictionary<string, ModContent.ModSource> sources)
{ {
this.modData = modData; this.modData = modData;
this.content = content; this.content = content;
@@ -119,7 +119,7 @@ namespace OpenRA.Mods.Common.Widgets.Logic
Log.AddChannel("install", "install.log"); Log.AddChannel("install", "install.log");
panel = widget.Get("DISC_INSTALL_PANEL"); panel = widget.Get("SOURCE_INSTALL_PANEL");
titleLabel = panel.Get<LabelWidget>("TITLE"); titleLabel = panel.Get<LabelWidget>("TITLE");
@@ -149,65 +149,39 @@ namespace OpenRA.Mods.Common.Widgets.Logic
listLabel = listContainer.Get<LabelWidget>("LIST_MESSAGE"); listLabel = listContainer.Get<LabelWidget>("LIST_MESSAGE");
DetectContentDisks(); DetectContentSources();
} }
static bool IsValidDrive(DriveInfo d) void DetectContentSources()
{ {
if (d.DriveType == DriveType.CDRom && d.IsReady) var message = modData.Translation.GetString(DetectingSources);
return true; ShowProgressbar(modData.Translation.GetString(CheckingSources), () => message);
ShowBackRetry(DetectContentSources);
// HACK: the "TFD" DVD is detected as a fixed udf-formatted drive on OSX
if (d.DriveType == DriveType.Fixed && d.DriveFormat == "udf")
return true;
return false;
}
void DetectContentDisks()
{
var message = modData.Translation.GetString(DetectingDrives);
ShowProgressbar(modData.Translation.GetString(CheckingDiscs), () => message);
ShowBackRetry(DetectContentDisks);
new Task(() => new Task(() =>
{ {
var volumes = DriveInfo.GetDrives()
.Where(IsValidDrive)
.Select(v => v.RootDirectory.FullName);
if (Platform.CurrentPlatform == PlatformType.Linux)
{
// Outside of Gnome, most mounting tools on Linux don't set DriveType.CDRom
// so provide a fallback by allowing users to manually mount images on known paths
volumes = volumes.Concat(new[]
{
"/media/openra",
"/media/" + Environment.UserName + "/openra",
"/mnt/openra"
});
}
foreach (var kv in sources) foreach (var kv in sources)
{ {
message = modData.Translation.GetString(SearchingDiscFor, Translation.Arguments("title", kv.Value.Title)); message = modData.Translation.GetString(SearchingSourceFor, Translation.Arguments("title", kv.Value.Title));
var path = FindSourcePath(kv.Value, volumes); var sourceResolver = kv.Value.ObjectCreator.CreateObject<ISourceResolver>($"{kv.Value.Type.Value}SourceResolver");
var path = sourceResolver.FindSourcePath(kv.Value);
if (path != null) if (path != null)
{ {
Log.Write("install", $"Using installer `{kv.Key}: {kv.Value.Title}` of type `{kv.Value.Type}`:"); Log.Write("install", $"Using installer `{kv.Key}: {kv.Value.Title}` of type `{kv.Value.Type.Value}`:");
var packages = content.Packages.Values var packages = content.Packages.Values
.Where(p => p.Sources.Contains(kv.Key) && !p.IsInstalled()) .Where(p => p.Sources.Contains(kv.Key) && !p.IsInstalled())
.Select(p => p.Title); .Select(p => p.Title);
// Ignore disc if content is already installed // Ignore source if content is already installed
if (packages.Any()) if (packages.Any())
{ {
Game.RunAfterTick(() => Game.RunAfterTick(() =>
{ {
ShowList(kv.Value.Title, modData.Translation.GetString(ContentPackageInstallation), packages); ShowList(kv.Value.Title, modData.Translation.GetString(ContentPackageInstallation), packages);
ShowContinueCancel(() => InstallFromDisc(path, kv.Value)); ShowContinueCancel(() => InstallFromSource(path, kv.Value));
}); });
return; return;
@@ -220,35 +194,38 @@ namespace OpenRA.Mods.Common.Widgets.Logic
.SelectMany(p => p.Sources) .SelectMany(p => p.Sources)
.Select(d => sources[d]); .Select(d => sources[d]);
var discs = missingSources var gameSources = new HashSet<string>();
.Where(s => s.Type == ModContent.SourceType.Disc) var digitalInstalls = new HashSet<string>();
.Select(s => s.Title)
.Distinct();
var options = new Dictionary<string, IEnumerable<string>>() foreach (var source in missingSources)
{ {
{ modData.Translation.GetString(GameDiscs), discs }, var sourceResolver = source.ObjectCreator.CreateObject<ISourceResolver>($"{source.Type.Value}SourceResolver");
};
if (Platform.CurrentPlatform == PlatformType.Windows) var availability = sourceResolver.GetAvailability();
{
var installations = missingSources
.Where(s => s.Type == ModContent.SourceType.RegistryDirectory || s.Type == ModContent.SourceType.RegistryDirectoryFromFile)
.Select(s => s.Title)
.Distinct();
options.Add(modData.Translation.GetString(DigitalInstalls), installations); if (availability == Availability.GameSource)
gameSources.Add(source.Title);
else if (availability == Availability.DigitalInstall)
digitalInstalls.Add(source.Title);
} }
var options = new Dictionary<string, IEnumerable<string>>();
if (gameSources.Any())
options.Add(modData.Translation.GetString(GameSources), gameSources);
if (digitalInstalls.Any())
options.Add(modData.Translation.GetString(DigitalInstalls), digitalInstalls);
Game.RunAfterTick(() => Game.RunAfterTick(() =>
{ {
ShowList(modData.Translation.GetString(GameContentNotFound), modData.Translation.GetString(AlternativeContentSources), options); ShowList(modData.Translation.GetString(GameContentNotFound), modData.Translation.GetString(AlternativeContentSources), options);
ShowBackRetry(DetectContentDisks); ShowBackRetry(DetectContentSources);
}); });
}).Start(); }).Start();
} }
void InstallFromDisc(string path, ModContent.ModSource modSource) void InstallFromSource(string path, ModContent.ModSource modSource)
{ {
var message = ""; var message = "";
ShowProgressbar(modData.Translation.GetString(InstallingContent), () => message); ShowProgressbar(modData.Translation.GetString(InstallingContent), () => message);
@@ -281,7 +258,7 @@ namespace OpenRA.Mods.Common.Widgets.Logic
Game.RunAfterTick(() => Game.RunAfterTick(() =>
{ {
ShowMessage(modData.Translation.GetString(InstallationFailed), modData.Translation.GetString(CheckInstallLog)); ShowMessage(modData.Translation.GetString(InstallationFailed), modData.Translation.GetString(CheckInstallLog));
ShowBackRetry(() => InstallFromDisc(path, modSource)); ShowBackRetry(() => InstallFromSource(path, modSource));
}); });
} }
}).Start(); }).Start();

View File

@@ -28,7 +28,7 @@ namespace OpenRA.Mods.Common.Widgets.Logic
readonly Dictionary<string, ModContent.ModSource> sources = new Dictionary<string, ModContent.ModSource>(); readonly Dictionary<string, ModContent.ModSource> sources = new Dictionary<string, ModContent.ModSource>();
readonly Dictionary<string, ModContent.ModDownload> downloads = new Dictionary<string, ModContent.ModDownload>(); readonly Dictionary<string, ModContent.ModDownload> downloads = new Dictionary<string, ModContent.ModDownload>();
bool discAvailable; bool sourceAvailable;
[TranslationReference] [TranslationReference]
static readonly string ManualInstall = "manual-install"; static readonly string ManualInstall = "manual-install";
@@ -76,11 +76,11 @@ namespace OpenRA.Mods.Common.Widgets.Logic
panel.Bounds.Y -= headerHeight / 2; panel.Bounds.Y -= headerHeight / 2;
scrollPanel.Bounds.Y += headerHeight; scrollPanel.Bounds.Y += headerHeight;
var discButton = panel.Get<ButtonWidget>("CHECK_DISC_BUTTON"); var sourceButton = panel.Get<ButtonWidget>("CHECK_SOURCE_BUTTON");
discButton.Bounds.Y += headerHeight; sourceButton.Bounds.Y += headerHeight;
discButton.IsVisible = () => discAvailable; sourceButton.IsVisible = () => sourceAvailable;
discButton.OnClick = () => Ui.OpenWindow("DISC_INSTALL_PANEL", new WidgetArgs sourceButton.OnClick = () => Ui.OpenWindow("SOURCE_INSTALL_PANEL", new WidgetArgs
{ {
{ "sources", sources }, { "sources", sources },
{ "content", content } { "content", content }
@@ -113,7 +113,7 @@ namespace OpenRA.Mods.Common.Widgets.Logic
var requiredWidget = container.Get<LabelWidget>("REQUIRED"); var requiredWidget = container.Get<LabelWidget>("REQUIRED");
requiredWidget.IsVisible = () => p.Value.Required; requiredWidget.IsVisible = () => p.Value.Required;
var sourceWidget = container.Get<ImageWidget>("DISC"); var sourceWidget = container.Get<ImageWidget>("SOURCE");
var sourceTitles = p.Value.Sources.Select(s => sources[s].Title).Distinct(); var sourceTitles = p.Value.Sources.Select(s => sources[s].Title).Distinct();
var sourceList = sourceTitles.JoinWith("\n"); var sourceList = sourceTitles.JoinWith("\n");
var isSourceAvailable = sourceTitles.Any(); var isSourceAvailable = sourceTitles.Any();
@@ -139,18 +139,18 @@ namespace OpenRA.Mods.Common.Widgets.Logic
var installedWidget = container.Get<LabelWidget>("INSTALLED"); var installedWidget = container.Get<LabelWidget>("INSTALLED");
installedWidget.IsVisible = () => installed; installedWidget.IsVisible = () => installed;
var requiresDiscWidget = container.Get<LabelWidget>("REQUIRES_DISC"); var requiresSourceWidget = container.Get<LabelWidget>("REQUIRES_SOURCE");
requiresDiscWidget.IsVisible = () => !installed && !downloadEnabled; requiresSourceWidget.IsVisible = () => !installed && !downloadEnabled;
if (!isSourceAvailable) if (!isSourceAvailable)
{ {
var manualInstall = modData.Translation.GetString(ManualInstall); var manualInstall = modData.Translation.GetString(ManualInstall);
requiresDiscWidget.GetText = () => manualInstall; requiresSourceWidget.GetText = () => manualInstall;
} }
scrollPanel.AddChild(container); scrollPanel.AddChild(container);
} }
discAvailable = content.Packages.Values.Any(p => p.Sources.Length > 0 && !p.IsInstalled()); sourceAvailable = content.Packages.Values.Any(p => p.Sources.Length > 0 && !p.IsInstalled());
} }
} }
} }

View File

@@ -14,38 +14,38 @@ using OpenRA.Widgets;
namespace OpenRA.Mods.Common.Widgets.Logic namespace OpenRA.Mods.Common.Widgets.Logic
{ {
public class ModContentDiscTooltipLogic : ChromeLogic public class ModContentSourceTooltipLogic : ChromeLogic
{ {
[ObjectCreator.UseCtor] [ObjectCreator.UseCtor]
public ModContentDiscTooltipLogic(Widget widget, Func<string> getText) public ModContentSourceTooltipLogic(Widget widget, Func<string> getText)
{ {
var discs = widget.Get<ContainerWidget>("DISCS"); var sources = widget.Get<ContainerWidget>("SOURCES");
var template = discs.Get<LabelWidget>("DISC_TEMPLATE"); var template = sources.Get<LabelWidget>("SOURCE_TEMPLATE");
discs.RemoveChildren(); sources.RemoveChildren();
var desc = widget.Get<LabelWidget>("DESCRIPTION"); var desc = widget.Get<LabelWidget>("DESCRIPTION");
var font = Game.Renderer.Fonts[template.Font]; var font = Game.Renderer.Fonts[template.Font];
var discTitles = getText().Split('\n'); var sourceTitles = getText().Split('\n');
var maxWidth = Game.Renderer.Fonts[desc.Font].Measure(desc.Text).X; var maxWidth = Game.Renderer.Fonts[desc.Font].Measure(desc.Text).X;
var sideMargin = desc.Bounds.X; var sideMargin = desc.Bounds.X;
var bottomMargin = discs.Bounds.Height; var bottomMargin = sources.Bounds.Height;
foreach (var disc in discTitles) foreach (var source in sourceTitles)
{ {
var label = (LabelWidget)template.Clone(); var label = (LabelWidget)template.Clone();
var title = disc; var title = source;
label.GetText = () => title; label.GetText = () => title;
label.Bounds.Y = discs.Bounds.Height; label.Bounds.Y = sources.Bounds.Height;
label.Bounds.Width = font.Measure(disc).X; label.Bounds.Width = font.Measure(source).X;
maxWidth = Math.Max(maxWidth, label.Bounds.Width + label.Bounds.X); maxWidth = Math.Max(maxWidth, label.Bounds.Width + label.Bounds.X);
discs.AddChild(label); sources.AddChild(label);
discs.Bounds.Height += label.Bounds.Height; sources.Bounds.Height += label.Bounds.Height;
} }
widget.Bounds.Width = 2 * sideMargin + maxWidth; widget.Bounds.Width = 2 * sideMargin + maxWidth;
widget.Bounds.Height = discs.Bounds.Y + bottomMargin + discs.Bounds.Height; widget.Bounds.Height = sources.Bounds.Y + bottomMargin + sources.Bounds.Height;
} }
} }
} }

View File

@@ -1,4 +1,5 @@
covertops: Covert Operations Expansion (English) covertops: Covert Operations Expansion (English)
Type: Disc
IDFiles: IDFiles:
GAME/GAME.DAT: be5a6c4c0a581da09e8f34a3bbf7bd17e525085c GAME/GAME.DAT: be5a6c4c0a581da09e8f34a3bbf7bd17e525085c
CONQUER.MIX: 713b53fa4c188ca9619c6bbeadbfc86513704266 CONQUER.MIX: 713b53fa4c188ca9619c6bbeadbfc86513704266

View File

@@ -1,4 +1,5 @@
tfd: C&C The First Decade (English) tfd: C&C The First Decade (English)
Type: Disc
IDFiles: IDFiles:
data1.hdr: bef3a08c3fc1b1caf28ca0dbb97c1f900005930e data1.hdr: bef3a08c3fc1b1caf28ca0dbb97c1f900005930e
data1.cab: 12ad6113a6890a1b4d5651a75378c963eaf513b9 data1.cab: 12ad6113a6890a1b4d5651a75378c963eaf513b9

View File

@@ -1,4 +1,5 @@
gdi95: C&C Gold (GDI Disc, English) gdi95: C&C Gold (GDI Disc, English)
Type: Disc
IDFiles: IDFiles:
DISK.WAV: 8bef9a6687c0fe0afd57c6561df58fa6e64f145d DISK.WAV: 8bef9a6687c0fe0afd57c6561df58fa6e64f145d
CONQUER.MIX: 833e02a09aae694659eb312d3838367f681d1b30 CONQUER.MIX: 833e02a09aae694659eb312d3838367f681d1b30

View File

@@ -1,4 +1,5 @@
nod95: C&C Gold (Nod Disc, English) nod95: C&C Gold (Nod Disc, English)
Type: Disc
IDFiles: IDFiles:
DISK.WAV: 83a0235525afa5cd6096f2967e3eae032996e38c DISK.WAV: 83a0235525afa5cd6096f2967e3eae032996e38c
CONQUER.MIX: 833e02a09aae694659eb312d3838367f681d1b30 CONQUER.MIX: 833e02a09aae694659eb312d3838367f681d1b30

View File

@@ -247,14 +247,14 @@ archive-validation-failed = Archive validation failed
extracting = Extracting... extracting = Extracting...
extracting-entry = Extracting { $entry } extracting-entry = Extracting { $entry }
archive-extraction-failed = Archive extraction failed archive-extraction-failed = Archive extraction failed
mirror-selection-failed = Online mirror is not available. Please install from an original disc. mirror-selection-failed = Online mirror is not available. Please install from an original source.
## InstallFromDiscLogic ## InstallFromSourceLogic
detecting-drives = Detecting drives detecting-sources = Detecting drives
checking-discs = Checking Discs checking-sources = Checking Sources
searching-disc-for = Searching for { $title } searching-source-for = Searching for { $title }
content-package-installation = The following content packages will be installed: content-package-installation = The following content packages will be installed:
game-discs = Game Discs game-sources = Game Sources
digital-installs = Digital Installs digital-installs = Digital Installs
game-content-not-found = Game Content Not Found game-content-not-found = Game Content Not Found
alternative-content-sources = Please insert or install one of the following content sources: alternative-content-sources = Please insert or install one of the following content sources:
@@ -268,10 +268,10 @@ extracting-filename-progress = Extracting { $filename } ({ $progress }%)
cancel = Cancel cancel = Cancel
retry = Retry retry = Retry
## InstallFromDiscLogic, LobbyLogic ## InstallFromSourceLogic, LobbyLogic
back = Back back = Back
# InstallFromDiscLogic, ModContentPromptLogic # InstallFromSourceLogic, ModContentPromptLogic
continue = Continue continue = Continue
## ModContentLogic ## ModContentLogic

View File

@@ -1,4 +1,5 @@
d2k-a: Dune 2000 (English) d2k-a: Dune 2000 (English)
Type: Disc
IDFiles: IDFiles:
MUSIC/AMBUSH.AUD: bd5926a56a83bc0e49f96037e1f909014ac0772a MUSIC/AMBUSH.AUD: bd5926a56a83bc0e49f96037e1f909014ac0772a
SETUP/SETUP.Z: 937f5ceb1236bf3f3d4e43929305ffe5004078e7 SETUP/SETUP.Z: 937f5ceb1236bf3f3d4e43929305ffe5004078e7

View File

@@ -1,4 +1,5 @@
d2k-b: Dune 2000 (English) d2k-b: Dune 2000 (English)
Type: Disc
IDFiles: IDFiles:
MUSIC/AMBUSH.AUD: bd5926a56a83bc0e49f96037e1f909014ac0772a MUSIC/AMBUSH.AUD: bd5926a56a83bc0e49f96037e1f909014ac0772a
SETUP/SETUP.Z: 029722e70fb7636f8120028f5c9b6ce81627ff90 SETUP/SETUP.Z: 029722e70fb7636f8120028f5c9b6ce81627ff90

View File

@@ -1,4 +1,5 @@
d2k-c: Dune 2000 (English) d2k-c: Dune 2000 (English)
Type: Disc
IDFiles: IDFiles:
MUSIC/AMBUSH.AUD: bd5926a56a83bc0e49f96037e1f909014ac0772a MUSIC/AMBUSH.AUD: bd5926a56a83bc0e49f96037e1f909014ac0772a
SETUP/SETUP.Z: d939b39bdbc952b259ce2b45c0bbedefa534b7f2 SETUP/SETUP.Z: d939b39bdbc952b259ce2b45c0bbedefa534b7f2

View File

@@ -1,4 +1,5 @@
d2k-d: Dune 2000 (English) d2k-d: Dune 2000 (English)
Type: Disc
IDFiles: IDFiles:
MUSIC/AMBUSH.AUD: bd5926a56a83bc0e49f96037e1f909014ac0772a MUSIC/AMBUSH.AUD: bd5926a56a83bc0e49f96037e1f909014ac0772a
SETUP/SETUP.Z: 2411cc5df36954ebd534ceafa3007c8aa9232909 SETUP/SETUP.Z: 2411cc5df36954ebd534ceafa3007c8aa9232909

View File

@@ -1,4 +1,5 @@
d2k-e: Dune 2000 (English) d2k-e: Dune 2000 (English)
Type: Disc
IDFiles: IDFiles:
MUSIC/AMBUSH.AUD: bd5926a56a83bc0e49f96037e1f909014ac0772a MUSIC/AMBUSH.AUD: bd5926a56a83bc0e49f96037e1f909014ac0772a
SETUP/SETUP.Z: b476661e82eeb05949e97fa1f75fed2343174be5 SETUP/SETUP.Z: b476661e82eeb05949e97fa1f75fed2343174be5

View File

@@ -50,7 +50,7 @@ Background@CONTENT_PANEL:
Font: Bold Font: Bold
TextColor: CC0000 TextColor: CC0000
Text: Required Text: Required
Image@DISC: Image@SOURCE:
X: 275 X: 275
Y: 2 Y: 2
Width: 20 Width: 20
@@ -58,7 +58,7 @@ Background@CONTENT_PANEL:
ImageCollection: modcontent ImageCollection: modcontent
ImageName: cdicon ImageName: cdicon
TooltipContainer: TOOLTIP_CONTAINER TooltipContainer: TOOLTIP_CONTAINER
TooltipTemplate: DISC_TOOLTIP TooltipTemplate: SOURCE_TOOLTIP
Button@DOWNLOAD: Button@DOWNLOAD:
X: 304 X: 304
Y: 0 Y: 0
@@ -74,7 +74,7 @@ Background@CONTENT_PANEL:
Font: Bold Font: Bold
TextColor: 00CC00 TextColor: 00CC00
Text: Installed Text: Installed
Label@REQUIRES_DISC: Label@REQUIRES_SOURCE:
X: 304 X: 304
Width: 100 Width: 100
Height: 23 Height: 23
@@ -82,7 +82,7 @@ Background@CONTENT_PANEL:
Font: Bold Font: Bold
TextColor: DDDDDD TextColor: DDDDDD
Text: Requires Disc Text: Requires Disc
Button@CHECK_DISC_BUTTON: Button@CHECK_SOURCE_BUTTON:
X: 30 X: 30
Y: PARENT_BOTTOM - 52 Y: PARENT_BOTTOM - 52
Background: button-highlighted Background: button-highlighted
@@ -101,8 +101,8 @@ Background@CONTENT_PANEL:
Key: escape Key: escape
TooltipContainer@TOOLTIP_CONTAINER: TooltipContainer@TOOLTIP_CONTAINER:
Background@DISC_TOOLTIP: Background@SOURCE_TOOLTIP:
Logic: ModContentDiscTooltipLogic Logic: ModContentSourceTooltipLogic
Background: panel-thinborder Background: panel-thinborder
Height: 25 Height: 25
Children: Children:
@@ -111,12 +111,12 @@ Background@DISC_TOOLTIP:
Height: 23 Height: 23
Font: Bold Font: Bold
Text: Content available from: Text: Content available from:
Container@DISCS: Container@SOURCES:
Y: 15 Y: 15
Width: PARENT_RIGHT - 10 Width: PARENT_RIGHT - 10
Height: 7 # used as bottom margin Height: 7 # used as bottom margin
Children: Children:
Label@DISC_TEMPLATE: Label@SOURCE_TEMPLATE:
X: 20 X: 20
Height: 14 Height: 14
Font: TinyBold Font: TinyBold
@@ -177,8 +177,8 @@ Container@PACKAGE_DOWNLOAD_PANEL:
Font: Bold Font: Bold
Key: escape Key: escape
Background@DISC_INSTALL_PANEL: Background@SOURCE_INSTALL_PANEL:
Logic: InstallFromDiscLogic Logic: InstallFromSourceLogic
X: (WINDOW_RIGHT - WIDTH) / 2 X: (WINDOW_RIGHT - WIDTH) / 2
Y: (WINDOW_BOTTOM - HEIGHT) / 2 Y: (WINDOW_BOTTOM - HEIGHT) / 2
Width: 500 Width: 500

View File

@@ -1,4 +1,5 @@
aftermath: Aftermath Expansion Disc (English) aftermath: Aftermath Expansion Disc (English)
Type: Disc
IDFiles: IDFiles:
README.TXT: 9902fb74c019df1b76ff5634e68f0371d790b5e0 README.TXT: 9902fb74c019df1b76ff5634e68f0371d790b5e0
SETUP/INSTALL/PATCH.RTP: 5bce93f834f9322ddaa7233242e5b6c7fea0bf17 SETUP/INSTALL/PATCH.RTP: 5bce93f834f9322ddaa7233242e5b6c7fea0bf17

View File

@@ -1,4 +1,5 @@
allied: Red Alert 95 (Allied Disc, English) allied: Red Alert 95 (Allied Disc, English)
Type: Disc
IDFiles: IDFiles:
MAIN.MIX: 20ebe16f91ff79be2d672f1db5bae9048ff9357c MAIN.MIX: 20ebe16f91ff79be2d672f1db5bae9048ff9357c
Length: 4096 Length: 4096

View File

@@ -1,4 +1,5 @@
cnc95: C&C Gold (GDI or Nod Disc, English) cnc95: C&C Gold (GDI or Nod Disc, English)
Type: Disc
IDFiles: IDFiles:
CONQUER.MIX: 833e02a09aae694659eb312d3838367f681d1b30 CONQUER.MIX: 833e02a09aae694659eb312d3838367f681d1b30
Install: Install:

View File

@@ -1,4 +1,5 @@
counterstrike: Counterstrike Expansion Disc (English) counterstrike: Counterstrike Expansion Disc (English)
Type: Disc
IDFiles: IDFiles:
README.TXT: 0efe8087383f0b159a9633f891fb5f53c6097cd4 README.TXT: 0efe8087383f0b159a9633f891fb5f53c6097cd4
SETUP/INSTALL/CSTRIKE.RTP: fae8ba82db71574f6ecd8fb4ff4026fcb65d2adc SETUP/INSTALL/CSTRIKE.RTP: fae8ba82db71574f6ecd8fb4ff4026fcb65d2adc

View File

@@ -1,4 +1,5 @@
tfd: C&C The First Decade (English) tfd: C&C The First Decade (English)
Type: Disc
IDFiles: IDFiles:
data1.hdr: bef3a08c3fc1b1caf28ca0dbb97c1f900005930e data1.hdr: bef3a08c3fc1b1caf28ca0dbb97c1f900005930e
data1.cab: 12ad6113a6890a1b4d5651a75378c963eaf513b9 data1.cab: 12ad6113a6890a1b4d5651a75378c963eaf513b9

View File

@@ -1,4 +1,5 @@
soviet: Red Alert 95 (Soviet Disc, English) soviet: Red Alert 95 (Soviet Disc, English)
Type: Disc
IDFiles: IDFiles:
MAIN.MIX: 9d108f18560716b684ab8b1da42cc7f3d1b52519 MAIN.MIX: 9d108f18560716b684ab8b1da42cc7f3d1b52519
Length: 4096 Length: 4096

View File

@@ -1,4 +1,5 @@
fstorm: Firestorm Expansion Disc (English) fstorm: Firestorm Expansion Disc (English)
Type: Disc
IDFiles: IDFiles:
Install/README.TXT: f2810b540fce8f3880250213ee08c57780d81c20 Install/README.TXT: f2810b540fce8f3880250213ee08c57780d81c20
Install/Language.dll: 4df87c1a2289da57dd14d0a7299546f37357fcca Install/Language.dll: 4df87c1a2289da57dd14d0a7299546f37357fcca

View File

@@ -1,4 +1,5 @@
tfd: C&C The First Decade (English) tfd: C&C The First Decade (English)
Type: Disc
IDFiles: IDFiles:
data1.hdr: bef3a08c3fc1b1caf28ca0dbb97c1f900005930e data1.hdr: bef3a08c3fc1b1caf28ca0dbb97c1f900005930e
data1.cab: 12ad6113a6890a1b4d5651a75378c963eaf513b9 data1.cab: 12ad6113a6890a1b4d5651a75378c963eaf513b9

View File

@@ -1,4 +1,5 @@
tibsun: Tiberian Sun (GDI or Nod Disc, English) tibsun: Tiberian Sun (GDI or Nod Disc, English)
Type: Disc
IDFiles: IDFiles:
README.TXT: 45745c4a0c888317ec900208a426472779c42bf7 README.TXT: 45745c4a0c888317ec900208a426472779c42bf7
AUTOPLAY.WAV: 2dfce5d00f98b641849c29942b651f4e98d30e30 AUTOPLAY.WAV: 2dfce5d00f98b641849c29942b651f4e98d30e30