Merge pull request #11375 from pchote/asset-installer-rework
New mod content installer
This commit is contained in:
@@ -1,41 +0,0 @@
|
||||
#region Copyright & License Information
|
||||
/*
|
||||
* Copyright 2007-2016 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.Collections.Generic;
|
||||
|
||||
namespace OpenRA
|
||||
{
|
||||
// Referenced from ModMetadata, so needs to be in OpenRA.Game :(
|
||||
public class ContentInstaller : IGlobalModData
|
||||
{
|
||||
public enum FilenameCase { Input, ForceLower, ForceUpper }
|
||||
|
||||
public readonly string[] TestFiles = { };
|
||||
public readonly string[] DiskTestFiles = { };
|
||||
public readonly string PackageToExtractFromCD = null;
|
||||
public readonly bool OverwriteFiles = true;
|
||||
|
||||
public readonly FilenameCase OutputFilenameCase = FilenameCase.ForceLower;
|
||||
public readonly Dictionary<string, string[]> CopyFilesFromCD = new Dictionary<string, string[]>();
|
||||
public readonly Dictionary<string, string[]> ExtractFilesFromCD = new Dictionary<string, string[]>();
|
||||
|
||||
public readonly string PackageMirrorList = null;
|
||||
|
||||
public readonly string MusicPackageMirrorList = null;
|
||||
public readonly int ShippedSoundtracks = 0;
|
||||
|
||||
/// <summary> InstallShield .CAB file IDs, used to extract Mod-specific files. </summary>
|
||||
public readonly HashSet<int> InstallShieldCABFileIds = new HashSet<int>();
|
||||
|
||||
/// <summary> InstallShield .CAB file IDs, used to extract Mod-specific archives and extract contents of ExtractFilesFromCD. </summary>
|
||||
public readonly HashSet<string> InstallShieldCABFilePackageIds = new HashSet<string>();
|
||||
}
|
||||
}
|
||||
@@ -18,7 +18,7 @@ namespace OpenRA.FileSystem
|
||||
{
|
||||
public sealed class InstallShieldPackage : IReadOnlyPackage
|
||||
{
|
||||
struct Entry
|
||||
public struct Entry
|
||||
{
|
||||
public readonly uint Offset;
|
||||
public readonly uint Length;
|
||||
@@ -124,6 +124,8 @@ namespace OpenRA.FileSystem
|
||||
return index.ContainsKey(filename);
|
||||
}
|
||||
|
||||
public IReadOnlyDictionary<string, Entry> Index { get { return new ReadOnlyDictionary<string, Entry>(index); } }
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
s.Dispose();
|
||||
|
||||
@@ -205,6 +205,15 @@ namespace OpenRA.FileSystem
|
||||
return GetContent(e);
|
||||
}
|
||||
|
||||
public IReadOnlyDictionary<string, PackageEntry> Index
|
||||
{
|
||||
get
|
||||
{
|
||||
var absoluteIndex = index.ToDictionary(e => e.Key, e => new PackageEntry(e.Value.Hash, (uint)(e.Value.Offset + dataStart), e.Value.Length));
|
||||
return new ReadOnlyDictionary<string, PackageEntry>(absoluteIndex);
|
||||
}
|
||||
}
|
||||
|
||||
public bool Contains(string filename)
|
||||
{
|
||||
return index.ContainsKey(filename);
|
||||
|
||||
@@ -344,8 +344,10 @@ namespace OpenRA
|
||||
using (new PerfTimer("LoadMaps"))
|
||||
ModData.MapCache.LoadMaps();
|
||||
|
||||
var installData = ModData.Manifest.Get<ContentInstaller>();
|
||||
var isModContentInstalled = installData.TestFiles.All(f => File.Exists(Platform.ResolvePath(f)));
|
||||
var content = ModData.Manifest.Get<ModContent>();
|
||||
var isModContentInstalled = content.Packages
|
||||
.Where(p => p.Value.Required)
|
||||
.All(p => p.Value.TestFiles.All(f => File.Exists(Platform.ResolvePath(f))));
|
||||
|
||||
// Mod assets are missing!
|
||||
if (!isModContentInstalled)
|
||||
|
||||
118
OpenRA.Game/ModContent.cs
Normal file
118
OpenRA.Game/ModContent.cs
Normal file
@@ -0,0 +1,118 @@
|
||||
#region Copyright & License Information
|
||||
/*
|
||||
* Copyright 2007-2016 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.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
|
||||
namespace OpenRA
|
||||
{
|
||||
public class ModContent : IGlobalModData
|
||||
{
|
||||
public class ModPackage
|
||||
{
|
||||
public readonly string Title;
|
||||
public readonly string[] TestFiles = { };
|
||||
public readonly string[] Discs = { };
|
||||
public readonly bool Required;
|
||||
public readonly string Download;
|
||||
|
||||
public ModPackage(MiniYaml yaml)
|
||||
{
|
||||
Title = yaml.Value;
|
||||
FieldLoader.Load(this, yaml);
|
||||
}
|
||||
|
||||
public bool IsInstalled()
|
||||
{
|
||||
return TestFiles.All(file => File.Exists(Path.GetFullPath(Platform.ResolvePath(file))));
|
||||
}
|
||||
}
|
||||
|
||||
public class ModDisc
|
||||
{
|
||||
public readonly string Title;
|
||||
public readonly Dictionary<string, string> IDFiles;
|
||||
|
||||
[FieldLoader.Ignore] public readonly List<MiniYamlNode> Install;
|
||||
|
||||
public ModDisc(MiniYaml yaml)
|
||||
{
|
||||
Title = yaml.Value;
|
||||
var installNode = yaml.Nodes.FirstOrDefault(n => n.Key == "Install");
|
||||
if (installNode != null)
|
||||
Install = installNode.Value.Nodes;
|
||||
|
||||
FieldLoader.Load(this, yaml);
|
||||
}
|
||||
}
|
||||
|
||||
public class ModDownload
|
||||
{
|
||||
public readonly string Title;
|
||||
public readonly string URL;
|
||||
public readonly string MirrorList;
|
||||
public readonly Dictionary<string, string> Extract;
|
||||
|
||||
public ModDownload(MiniYaml yaml)
|
||||
{
|
||||
Title = yaml.Value;
|
||||
FieldLoader.Load(this, yaml);
|
||||
}
|
||||
}
|
||||
|
||||
public readonly string InstallPromptMessage;
|
||||
public readonly string QuickDownload;
|
||||
public readonly string HeaderMessage;
|
||||
|
||||
[FieldLoader.LoadUsing("LoadPackages")]
|
||||
public readonly Dictionary<string, ModPackage> Packages = new Dictionary<string, ModPackage>();
|
||||
|
||||
static object LoadPackages(MiniYaml yaml)
|
||||
{
|
||||
var packages = new Dictionary<string, ModPackage>();
|
||||
var packageNode = yaml.Nodes.FirstOrDefault(n => n.Key == "Packages");
|
||||
if (packageNode != null)
|
||||
foreach (var node in packageNode.Value.Nodes)
|
||||
packages.Add(node.Key, new ModPackage(node.Value));
|
||||
|
||||
return packages;
|
||||
}
|
||||
|
||||
[FieldLoader.LoadUsing("LoadDownloads")]
|
||||
public readonly Dictionary<string, ModDownload> Downloads;
|
||||
|
||||
static object LoadDownloads(MiniYaml yaml)
|
||||
{
|
||||
var downloads = new Dictionary<string, ModDownload>();
|
||||
var downloadNode = yaml.Nodes.FirstOrDefault(n => n.Key == "Downloads");
|
||||
if (downloadNode != null)
|
||||
foreach (var node in downloadNode.Value.Nodes)
|
||||
downloads.Add(node.Key, new ModDownload(node.Value));
|
||||
|
||||
return downloads;
|
||||
}
|
||||
|
||||
[FieldLoader.LoadUsing("LoadDiscs")]
|
||||
public readonly Dictionary<string, ModDisc> Discs;
|
||||
|
||||
static object LoadDiscs(MiniYaml yaml)
|
||||
{
|
||||
var discs = new Dictionary<string, ModDisc>();
|
||||
var discNode = yaml.Nodes.FirstOrDefault(n => n.Key == "Discs");
|
||||
if (discNode != null)
|
||||
foreach (var node in discNode.Value.Nodes)
|
||||
discs.Add(node.Key, new ModDisc(node.Value));
|
||||
|
||||
return discs;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -30,7 +30,7 @@ namespace OpenRA
|
||||
public bool Hidden;
|
||||
|
||||
public Dictionary<string, string> RequiresMods;
|
||||
public ContentInstaller Content;
|
||||
public ModContent ModContent;
|
||||
public IReadOnlyPackage Package;
|
||||
|
||||
static Dictionary<string, ModMetadata> ValidateMods()
|
||||
@@ -78,8 +78,8 @@ namespace OpenRA
|
||||
else
|
||||
metadata.RequiresMods = new Dictionary<string, string>();
|
||||
|
||||
if (nd.ContainsKey("ContentInstaller"))
|
||||
metadata.Content = FieldLoader.Load<ContentInstaller>(nd["ContentInstaller"]);
|
||||
if (nd.ContainsKey("ModContent"))
|
||||
metadata.ModContent = FieldLoader.Load<ModContent>(nd["ModContent"]);
|
||||
|
||||
// Mods in the support directory and oramod packages (which are listed later
|
||||
// in the CandidateMods list) override mods in the main install.
|
||||
|
||||
@@ -103,7 +103,6 @@
|
||||
<Compile Include="FileSystem\BagFile.cs" />
|
||||
<Compile Include="Map\MapGrid.cs" />
|
||||
<Compile Include="Map\MapPlayers.cs" />
|
||||
<Compile Include="ContentInstaller.cs" />
|
||||
<Compile Include="MPos.cs" />
|
||||
<Compile Include="Download.cs" />
|
||||
<Compile Include="Effects\AsyncAction.cs" />
|
||||
@@ -249,6 +248,7 @@
|
||||
<Compile Include="Traits\ActivityUtils.cs" />
|
||||
<Compile Include="FileSystem\ZipFolder.cs" />
|
||||
<Compile Include="Primitives\float3.cs" />
|
||||
<Compile Include="ModContent.cs" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="FileSystem\D2kSoundResources.cs" />
|
||||
|
||||
@@ -33,9 +33,23 @@ namespace OpenRA.Widgets
|
||||
public static void CloseWindow()
|
||||
{
|
||||
if (WindowList.Count > 0)
|
||||
Root.RemoveChild(WindowList.Pop());
|
||||
{
|
||||
var hidden = WindowList.Pop();
|
||||
Root.RemoveChild(hidden);
|
||||
if (hidden.LogicObjects != null)
|
||||
foreach (var l in hidden.LogicObjects)
|
||||
l.BecameHidden();
|
||||
}
|
||||
|
||||
if (WindowList.Count > 0)
|
||||
Root.AddChild(WindowList.Peek());
|
||||
{
|
||||
var restore = WindowList.Peek();
|
||||
Root.AddChild(restore);
|
||||
|
||||
if (restore.LogicObjects != null)
|
||||
foreach (var l in restore.LogicObjects)
|
||||
l.BecameVisible();
|
||||
}
|
||||
}
|
||||
|
||||
public static Widget OpenWindow(string id)
|
||||
@@ -148,6 +162,8 @@ namespace OpenRA.Widgets
|
||||
{
|
||||
public void Dispose() { Dispose(true); GC.SuppressFinalize(this); }
|
||||
public virtual void Tick() { }
|
||||
public virtual void BecameHidden() { }
|
||||
public virtual void BecameVisible() { }
|
||||
protected virtual void Dispose(bool disposing) { }
|
||||
}
|
||||
|
||||
|
||||
131
OpenRA.Mods.Common/FileFormats/MSCabCompression.cs
Normal file
131
OpenRA.Mods.Common/FileFormats/MSCabCompression.cs
Normal file
@@ -0,0 +1,131 @@
|
||||
#region Copyright & License Information
|
||||
/*
|
||||
* Copyright 2007-2016 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.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using ICSharpCode.SharpZipLib.Zip.Compression;
|
||||
using ICSharpCode.SharpZipLib.Zip.Compression.Streams;
|
||||
|
||||
namespace OpenRA.Mods.Common.FileFormats
|
||||
{
|
||||
public sealed class MSCabCompression
|
||||
{
|
||||
class CabFolder
|
||||
{
|
||||
public readonly uint BlockOffset;
|
||||
public readonly ushort BlockCount;
|
||||
public readonly ushort CompressionType;
|
||||
|
||||
public CabFolder(Stream stream)
|
||||
{
|
||||
BlockOffset = stream.ReadUInt32();
|
||||
BlockCount = stream.ReadUInt16();
|
||||
CompressionType = stream.ReadUInt16();
|
||||
}
|
||||
}
|
||||
|
||||
class CabFile
|
||||
{
|
||||
public readonly string FileName;
|
||||
public readonly uint DecompressedLength;
|
||||
public readonly uint DecompressedOffset;
|
||||
public readonly ushort FolderIndex;
|
||||
|
||||
public CabFile(Stream stream)
|
||||
{
|
||||
DecompressedLength = stream.ReadUInt32();
|
||||
DecompressedOffset = stream.ReadUInt32();
|
||||
FolderIndex = stream.ReadUInt16();
|
||||
stream.Position += 6;
|
||||
FileName = stream.ReadASCIIZ();
|
||||
}
|
||||
}
|
||||
|
||||
readonly CabFolder[] folders;
|
||||
readonly CabFile[] files;
|
||||
readonly Stream stream;
|
||||
|
||||
public MSCabCompression(Stream stream)
|
||||
{
|
||||
this.stream = stream;
|
||||
|
||||
var signature = stream.ReadASCII(4);
|
||||
if (signature != "MSCF")
|
||||
throw new InvalidDataException("Not a Microsoft CAB package!");
|
||||
|
||||
stream.Position += 12;
|
||||
var filesOffset = stream.ReadUInt32();
|
||||
stream.Position += 6;
|
||||
var folderCount = stream.ReadUInt16();
|
||||
var fileCount = stream.ReadUInt16();
|
||||
if (stream.ReadUInt16() != 0)
|
||||
throw new InvalidDataException("Only plain packages (without reserved header space or prev/next archives) are supported!");
|
||||
|
||||
stream.Position += 4;
|
||||
|
||||
folders = new CabFolder[folderCount];
|
||||
for (var i = 0; i < folderCount; i++)
|
||||
{
|
||||
folders[i] = new CabFolder(stream);
|
||||
if (folders[i].CompressionType != 1)
|
||||
throw new InvalidDataException("Compression type is not supported");
|
||||
}
|
||||
|
||||
files = new CabFile[fileCount];
|
||||
stream.Seek(filesOffset, SeekOrigin.Begin);
|
||||
for (var i = 0; i < fileCount; i++)
|
||||
files[i] = new CabFile(stream);
|
||||
}
|
||||
|
||||
public byte[] ExtractFile(string filename, Action<int> onProgress = null)
|
||||
{
|
||||
var file = files.FirstOrDefault(f => f.FileName == filename);
|
||||
if (file == null)
|
||||
return null;
|
||||
|
||||
var folder = folders[file.FolderIndex];
|
||||
stream.Seek(folder.BlockOffset, SeekOrigin.Begin);
|
||||
|
||||
using (var outputStream = new MemoryStream())
|
||||
{
|
||||
var inflater = new Inflater(true);
|
||||
var buffer = new byte[4096];
|
||||
for (var i = 0; i < folder.BlockCount; i++)
|
||||
{
|
||||
if (onProgress != null)
|
||||
onProgress((int)(100 * outputStream.Position / file.DecompressedLength));
|
||||
|
||||
// Ignore checksums
|
||||
stream.Position += 4;
|
||||
var blockLength = stream.ReadUInt16();
|
||||
stream.Position += 4;
|
||||
|
||||
using (var batch = new MemoryStream(stream.ReadBytes(blockLength - 2)))
|
||||
using (var inflaterStream = new InflaterInputStream(batch, inflater))
|
||||
{
|
||||
int n;
|
||||
while ((n = inflaterStream.Read(buffer, 0, buffer.Length)) > 0)
|
||||
outputStream.Write(buffer, 0, n);
|
||||
}
|
||||
|
||||
inflater.Reset();
|
||||
}
|
||||
|
||||
outputStream.Seek(file.DecompressedOffset, SeekOrigin.Begin);
|
||||
return outputStream.ReadBytes((int)file.DecompressedLength);
|
||||
}
|
||||
}
|
||||
|
||||
public IEnumerable<string> Contents { get { return files.Select(f => f.FileName); } }
|
||||
}
|
||||
}
|
||||
@@ -1,189 +0,0 @@
|
||||
#region Copyright & License Information
|
||||
/*
|
||||
* Copyright 2007-2016 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.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using ICSharpCode.SharpZipLib;
|
||||
using ICSharpCode.SharpZipLib.Zip;
|
||||
using OpenRA.FileSystem;
|
||||
|
||||
namespace OpenRA.Mods.Common
|
||||
{
|
||||
public static class InstallUtils
|
||||
{
|
||||
static IEnumerable<ZipEntry> GetEntries(this ZipInputStream z)
|
||||
{
|
||||
for (;;)
|
||||
{
|
||||
var e = z.GetNextEntry();
|
||||
if (e != null) yield return e; else break;
|
||||
}
|
||||
}
|
||||
|
||||
public static string GetMountedDisk(Func<string, bool> isValidDisk)
|
||||
{
|
||||
var volumes = DriveInfo.GetDrives()
|
||||
.Where(v => v.DriveType == DriveType.CDRom && v.IsReady)
|
||||
.Select(v => v.RootDirectory.FullName);
|
||||
|
||||
return volumes.FirstOrDefault(isValidDisk);
|
||||
}
|
||||
|
||||
static string GetFileName(string path, ContentInstaller.FilenameCase caseModifier)
|
||||
{
|
||||
// Gets the file path, splitting on both / and \
|
||||
var index = path.LastIndexOfAny(new[] { '\\', '/' });
|
||||
var output = path.Substring(index + 1);
|
||||
|
||||
switch (caseModifier)
|
||||
{
|
||||
case ContentInstaller.FilenameCase.ForceLower:
|
||||
return output.ToLowerInvariant();
|
||||
case ContentInstaller.FilenameCase.ForceUpper:
|
||||
return output.ToUpperInvariant();
|
||||
default:
|
||||
return output;
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: The package should be mounted into its own context to avoid name collisions with installed files
|
||||
public static bool ExtractFromPackage(FileSystem.FileSystem fileSystem, string srcPath, string package, Dictionary<string, string[]> filesByDirectory,
|
||||
string destPath, bool overwrite, ContentInstaller.FilenameCase caseModifier, Action<string> onProgress, Action<string> onError)
|
||||
{
|
||||
Directory.CreateDirectory(destPath);
|
||||
|
||||
Log.Write("debug", "Mounting {0}".F(srcPath));
|
||||
fileSystem.Mount(srcPath);
|
||||
Log.Write("debug", "Mounting {0}".F(package));
|
||||
fileSystem.Mount(package);
|
||||
|
||||
foreach (var directory in filesByDirectory)
|
||||
{
|
||||
var targetDir = directory.Key;
|
||||
|
||||
foreach (var file in directory.Value)
|
||||
{
|
||||
var containingDir = Path.Combine(destPath, targetDir);
|
||||
var dest = Path.Combine(containingDir, GetFileName(file, caseModifier));
|
||||
if (File.Exists(dest))
|
||||
{
|
||||
if (overwrite)
|
||||
File.Delete(dest);
|
||||
else
|
||||
{
|
||||
Log.Write("debug", "Skipping {0}".F(dest));
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
Directory.CreateDirectory(containingDir);
|
||||
|
||||
using (var sourceStream = fileSystem.Open(file))
|
||||
using (var destStream = File.Create(dest))
|
||||
{
|
||||
Log.Write("debug", "Extracting {0} to {1}".F(file, dest));
|
||||
onProgress("Extracting " + file);
|
||||
destStream.Write(sourceStream.ReadAllBytes());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public static bool CopyFiles(string srcPath, Dictionary<string, string[]> files, string destPath,
|
||||
bool overwrite, ContentInstaller.FilenameCase caseModifier, Action<string> onProgress, Action<string> onError)
|
||||
{
|
||||
Directory.CreateDirectory(destPath);
|
||||
|
||||
foreach (var folder in files)
|
||||
{
|
||||
var targetDir = folder.Key;
|
||||
|
||||
foreach (var file in folder.Value)
|
||||
{
|
||||
var sourcePath = Path.Combine(srcPath, file);
|
||||
if (!File.Exists(sourcePath))
|
||||
{
|
||||
onError("Cannot find " + file);
|
||||
return false;
|
||||
}
|
||||
|
||||
var destFile = GetFileName(file, caseModifier);
|
||||
var containingDir = Path.Combine(destPath, targetDir);
|
||||
var dest = Path.Combine(containingDir, destFile);
|
||||
if (File.Exists(dest) && !overwrite)
|
||||
{
|
||||
Log.Write("debug", "Skipping {0}".F(dest));
|
||||
continue;
|
||||
}
|
||||
|
||||
Directory.CreateDirectory(containingDir);
|
||||
|
||||
onProgress("Copying " + destFile);
|
||||
Log.Write("debug", "Copy {0} to {1}".F(sourcePath, dest));
|
||||
File.Copy(sourcePath, dest, true);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public static bool ExtractZip(string zipFile, string dest, Action<string> onProgress, Action<string> onError)
|
||||
{
|
||||
if (!File.Exists(zipFile))
|
||||
{
|
||||
onError("Invalid path: " + zipFile);
|
||||
return false;
|
||||
}
|
||||
|
||||
var extracted = new List<string>();
|
||||
try
|
||||
{
|
||||
using (var stream = File.OpenRead(zipFile))
|
||||
using (var z = new ZipInputStream(stream))
|
||||
z.ExtractZip(dest, extracted, s => onProgress("Extracting " + s));
|
||||
}
|
||||
catch (SharpZipBaseException)
|
||||
{
|
||||
foreach (var f in extracted)
|
||||
File.Delete(f);
|
||||
|
||||
onError("Invalid archive");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// TODO: this belongs in FileSystem/ZipFile
|
||||
static void ExtractZip(this ZipInputStream z, string destPath, List<string> extracted, Action<string> onProgress)
|
||||
{
|
||||
foreach (var entry in z.GetEntries())
|
||||
{
|
||||
if (!entry.IsFile) continue;
|
||||
|
||||
onProgress(entry.Name);
|
||||
|
||||
Directory.CreateDirectory(Path.Combine(destPath, Path.GetDirectoryName(entry.Name)));
|
||||
var path = Path.Combine(destPath, entry.Name);
|
||||
extracted.Add(path);
|
||||
|
||||
using (var f = File.Create(path))
|
||||
z.CopyTo(f);
|
||||
}
|
||||
|
||||
z.Close();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -50,14 +50,7 @@ namespace OpenRA.Mods.Common.LoadScreens
|
||||
var widgetArgs = new WidgetArgs();
|
||||
|
||||
Ui.LoadWidget("MODCHOOSER_BACKGROUND", Ui.Root, widgetArgs);
|
||||
|
||||
if (args != null && args.Contains("installMusic"))
|
||||
{
|
||||
widgetArgs.Add("modId", args.GetValue("installMusic", ""));
|
||||
Ui.OpenWindow("INSTALL_MUSIC_PANEL", widgetArgs);
|
||||
}
|
||||
else
|
||||
Ui.OpenWindow("MODCHOOSER_DIALOG", widgetArgs);
|
||||
Ui.OpenWindow("MODCHOOSER_DIALOG", widgetArgs);
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
|
||||
@@ -610,11 +610,7 @@
|
||||
<Compile Include="Widgets\Logic\Ingame\SupportPowerBinLogic.cs" />
|
||||
<Compile Include="Widgets\Logic\Ingame\SupportPowerTooltipLogic.cs" />
|
||||
<Compile Include="Widgets\Logic\Ingame\WorldTooltipLogic.cs" />
|
||||
<Compile Include="Widgets\Logic\Installation\InstallFromCDLogic.cs" />
|
||||
<Compile Include="Widgets\Logic\Installation\DownloadPackagesLogic.cs" />
|
||||
<Compile Include="Widgets\Logic\Installation\InstallModLogic.cs" />
|
||||
<Compile Include="Widgets\Logic\Installation\InstallLogic.cs" />
|
||||
<Compile Include="Widgets\Logic\Installation\InstallMusicLogic.cs" />
|
||||
<Compile Include="Widgets\Logic\Lobby\ClientTooltipLogic.cs" />
|
||||
<Compile Include="Widgets\Logic\Lobby\KickClientLogic.cs" />
|
||||
<Compile Include="Widgets\Logic\Lobby\KickSpectatorsLogic.cs" />
|
||||
@@ -662,7 +658,6 @@
|
||||
<Compile Include="LoadScreens\LogoStripeLoadScreen.cs" />
|
||||
<Compile Include="LoadScreens\BlankLoadScreen.cs" />
|
||||
<Compile Include="Widgets\Logic\ReplayUtils.cs" />
|
||||
<Compile Include="InstallUtils.cs" />
|
||||
<Compile Include="Graphics\DefaultSpriteSequence.cs" />
|
||||
<Compile Include="Widgets\BackgroundWidget.cs" />
|
||||
<Compile Include="Widgets\ButtonWidget.cs" />
|
||||
@@ -751,6 +746,15 @@
|
||||
<Compile Include="Traits\Radar\RadarColorFromTerrain.cs" />
|
||||
<Compile Include="Traits\Radar\JamsRadar.cs" />
|
||||
<Compile Include="AI\DummyAI.cs" />
|
||||
<Compile Include="UtilityCommands\ListInstallShieldContentsCommand.cs" />
|
||||
<Compile Include="Widgets\Logic\Installation\ModContentLogic.cs" />
|
||||
<Compile Include="Widgets\Logic\Installation\DownloadPackageLogic.cs" />
|
||||
<Compile Include="Widgets\Logic\Installation\ModContentDiscTooltipLogic.cs" />
|
||||
<Compile Include="Widgets\Logic\Installation\InstallFromDiscLogic.cs" />
|
||||
<Compile Include="Widgets\Logic\Installation\ModContentPromptLogic.cs" />
|
||||
<Compile Include="UtilityCommands\ListMixContentsCommand.cs" />
|
||||
<Compile Include="UtilityCommands\ListMSCabContentsCommand.cs" />
|
||||
<Compile Include="FileFormats\MSCabCompression.cs" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||
<PropertyGroup>
|
||||
|
||||
@@ -74,10 +74,8 @@ namespace OpenRA.Mods.Common.Traits
|
||||
}
|
||||
else
|
||||
{
|
||||
// Start playback with a random song, but only if the player has installed more music
|
||||
var installData = Game.ModData.Manifest.Get<ContentInstaller>();
|
||||
if (playlist.Length > installData.ShippedSoundtracks)
|
||||
currentSong = random.FirstOrDefault();
|
||||
// Start playback with a random song
|
||||
currentSong = random.FirstOrDefault();
|
||||
}
|
||||
|
||||
if (SongExists(info.StartingMusic))
|
||||
|
||||
@@ -0,0 +1,46 @@
|
||||
#region Copyright & License Information
|
||||
/*
|
||||
* Copyright 2007-2016 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;
|
||||
using OpenRA.FileSystem;
|
||||
|
||||
namespace OpenRA.Mods.Common.UtilityCommands
|
||||
{
|
||||
class ListInstallShieldContents : IUtilityCommand
|
||||
{
|
||||
public string Name { get { return "--list-installshield"; } }
|
||||
|
||||
public bool ValidateArguments(string[] args)
|
||||
{
|
||||
return args.Length == 2;
|
||||
}
|
||||
|
||||
[Desc("ARCHIVE.Z", "Lists the content ranges for a InstallShield V3 file")]
|
||||
public void Run(ModData modData, string[] args)
|
||||
{
|
||||
var filename = Path.GetFileName(args[1]);
|
||||
var path = Path.GetDirectoryName(args[1]);
|
||||
|
||||
var fs = new OpenRA.FileSystem.FileSystem();
|
||||
fs.Mount(path, "parent");
|
||||
var package = new InstallShieldPackage(fs, "parent|" + filename);
|
||||
|
||||
foreach (var kv in package.Index)
|
||||
{
|
||||
Console.WriteLine("{0}:", kv.Key);
|
||||
Console.WriteLine("\tOffset: {0}", 255 + kv.Value.Offset);
|
||||
Console.WriteLine("\tLength: {0}", kv.Value.Length);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
#region Copyright & License Information
|
||||
/*
|
||||
* Copyright 2007-2016 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 OpenRA.Mods.Common.FileFormats;
|
||||
|
||||
namespace OpenRA.Mods.Common.UtilityCommands
|
||||
{
|
||||
class ListMSCabContentsCommand : IUtilityCommand
|
||||
{
|
||||
public string Name { get { return "--list-mscab"; } }
|
||||
|
||||
public bool ValidateArguments(string[] args)
|
||||
{
|
||||
return args.Length == 2;
|
||||
}
|
||||
|
||||
[Desc("ARCHIVE.CAB", "Lists the filenames contained within a MSCAB file")]
|
||||
public void Run(ModData modData, string[] args)
|
||||
{
|
||||
var package = new MSCabCompression(File.OpenRead(args[1]));
|
||||
foreach (var file in package.Contents)
|
||||
Console.WriteLine("{0}", file);
|
||||
}
|
||||
}
|
||||
}
|
||||
50
OpenRA.Mods.Common/UtilityCommands/ListMixContentsCommand.cs
Normal file
50
OpenRA.Mods.Common/UtilityCommands/ListMixContentsCommand.cs
Normal file
@@ -0,0 +1,50 @@
|
||||
#region Copyright & License Information
|
||||
/*
|
||||
* Copyright 2007-2016 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;
|
||||
using OpenRA.FileSystem;
|
||||
|
||||
namespace OpenRA.Mods.Common.UtilityCommands
|
||||
{
|
||||
class ListMixContents : IUtilityCommand
|
||||
{
|
||||
public string Name { get { return "--list-mix"; } }
|
||||
|
||||
public bool ValidateArguments(string[] args)
|
||||
{
|
||||
return args.Length == 2;
|
||||
}
|
||||
|
||||
[Desc("ARCHIVE.Z", "Lists the content ranges for a mix file")]
|
||||
public void Run(ModData modData, string[] args)
|
||||
{
|
||||
var filename = Path.GetFileName(args[1]);
|
||||
var path = Path.GetDirectoryName(args[1]);
|
||||
|
||||
var fs = new OpenRA.FileSystem.FileSystem();
|
||||
|
||||
// Needed to access the global mix database
|
||||
fs.LoadFromManifest(modData.Manifest);
|
||||
|
||||
fs.Mount(path, "parent");
|
||||
var package = new MixFile(fs, "parent|" + filename);
|
||||
|
||||
foreach (var kv in package.Index.OrderBy(kv => kv.Value.Offset))
|
||||
{
|
||||
Console.WriteLine("{0}:", kv.Key);
|
||||
Console.WriteLine("\tOffset: {0}", kv.Value.Offset);
|
||||
Console.WriteLine("\tLength: {0}", kv.Value.Length);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -17,16 +17,27 @@ namespace OpenRA.Mods.Common.Widgets
|
||||
{
|
||||
public class ImageWidget : Widget
|
||||
{
|
||||
public readonly string TooltipTemplate;
|
||||
public readonly string TooltipContainer;
|
||||
|
||||
public string ImageCollection = "";
|
||||
public string ImageName = "";
|
||||
public bool ClickThrough = true;
|
||||
public Func<string> GetImageName;
|
||||
public Func<string> GetImageCollection;
|
||||
|
||||
[Translate] public string TooltipText;
|
||||
|
||||
Lazy<TooltipContainerWidget> tooltipContainer;
|
||||
public Func<string> GetTooltipText;
|
||||
|
||||
public ImageWidget()
|
||||
{
|
||||
GetImageName = () => ImageName;
|
||||
GetImageCollection = () => ImageCollection;
|
||||
GetTooltipText = () => TooltipText;
|
||||
tooltipContainer = Exts.Lazy(() =>
|
||||
Ui.Root.Get<TooltipContainerWidget>(TooltipContainer));
|
||||
}
|
||||
|
||||
protected ImageWidget(ImageWidget other)
|
||||
@@ -37,6 +48,12 @@ namespace OpenRA.Mods.Common.Widgets
|
||||
GetImageName = other.GetImageName;
|
||||
ImageCollection = other.ImageCollection;
|
||||
GetImageCollection = other.GetImageCollection;
|
||||
|
||||
TooltipTemplate = other.TooltipTemplate;
|
||||
TooltipContainer = other.TooltipContainer;
|
||||
GetTooltipText = other.GetTooltipText;
|
||||
tooltipContainer = Exts.Lazy(() =>
|
||||
Ui.Root.Get<TooltipContainerWidget>(TooltipContainer));
|
||||
}
|
||||
|
||||
public override Widget Clone() { return new ImageWidget(this); }
|
||||
@@ -57,5 +74,21 @@ namespace OpenRA.Mods.Common.Widgets
|
||||
{
|
||||
return !ClickThrough && RenderBounds.Contains(mi.Location);
|
||||
}
|
||||
|
||||
public override void MouseEntered()
|
||||
{
|
||||
if (TooltipContainer == null || GetTooltipText == null)
|
||||
return;
|
||||
|
||||
tooltipContainer.Value.SetTooltip(TooltipTemplate, new WidgetArgs() { { "getText", GetTooltipText } });
|
||||
}
|
||||
|
||||
public override void MouseExited()
|
||||
{
|
||||
if (TooltipContainer == null)
|
||||
return;
|
||||
|
||||
tooltipContainer.Value.RemoveTooltip();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,234 @@
|
||||
#region Copyright & License Information
|
||||
/*
|
||||
* Copyright 2007-2016 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.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.IO;
|
||||
using System.Net;
|
||||
using System.Text;
|
||||
using ICSharpCode.SharpZipLib;
|
||||
using ICSharpCode.SharpZipLib.Zip;
|
||||
using OpenRA.Support;
|
||||
using OpenRA.Widgets;
|
||||
|
||||
namespace OpenRA.Mods.Common.Widgets.Logic
|
||||
{
|
||||
public class DownloadPackageLogic : ChromeLogic
|
||||
{
|
||||
static readonly string[] SizeSuffixes = { "bytes", "KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB" };
|
||||
readonly ModContent.ModDownload download;
|
||||
readonly Action onSuccess;
|
||||
|
||||
readonly Widget panel;
|
||||
readonly ProgressBarWidget progressBar;
|
||||
|
||||
Func<string> getStatusText = () => "";
|
||||
string downloadHost;
|
||||
|
||||
[ObjectCreator.UseCtor]
|
||||
public DownloadPackageLogic(Widget widget, ModContent.ModDownload download, Action onSuccess)
|
||||
{
|
||||
this.download = download;
|
||||
this.onSuccess = onSuccess;
|
||||
|
||||
Log.AddChannel("install", "install.log");
|
||||
|
||||
panel = widget.Get("PACKAGE_DOWNLOAD_PANEL");
|
||||
progressBar = panel.Get<ProgressBarWidget>("PROGRESS_BAR");
|
||||
|
||||
var statusLabel = panel.Get<LabelWidget>("STATUS_LABEL");
|
||||
var statusFont = Game.Renderer.Fonts[statusLabel.Font];
|
||||
var status = new CachedTransform<string, string>(s => WidgetUtils.TruncateText(s, statusLabel.Bounds.Width, statusFont));
|
||||
statusLabel.GetText = () => status.Update(getStatusText());
|
||||
|
||||
var text = "Downloading {0}".F(download.Title);
|
||||
panel.Get<LabelWidget>("TITLE").Text = text;
|
||||
|
||||
ShowDownloadDialog();
|
||||
}
|
||||
|
||||
void ShowDownloadDialog()
|
||||
{
|
||||
getStatusText = () => "Fetching list of mirrors...";
|
||||
progressBar.Indeterminate = true;
|
||||
|
||||
var retryButton = panel.Get<ButtonWidget>("RETRY_BUTTON");
|
||||
retryButton.IsVisible = () => false;
|
||||
|
||||
var cancelButton = panel.Get<ButtonWidget>("CANCEL_BUTTON");
|
||||
|
||||
var file = Path.Combine(Path.GetTempPath(), Path.GetRandomFileName());
|
||||
|
||||
Action deleteTempFile = () =>
|
||||
{
|
||||
Log.Write("install", "Deleting temporary file " + file);
|
||||
File.Delete(file);
|
||||
};
|
||||
|
||||
Action<DownloadProgressChangedEventArgs> onDownloadProgress = i =>
|
||||
{
|
||||
var dataReceived = 0.0f;
|
||||
var dataTotal = 0.0f;
|
||||
var mag = 0;
|
||||
var dataSuffix = "";
|
||||
|
||||
if (i.TotalBytesToReceive < 0)
|
||||
{
|
||||
dataTotal = float.NaN;
|
||||
dataReceived = i.BytesReceived;
|
||||
dataSuffix = SizeSuffixes[0];
|
||||
}
|
||||
else
|
||||
{
|
||||
mag = (int)Math.Log(i.TotalBytesToReceive, 1024);
|
||||
dataTotal = i.TotalBytesToReceive / (float)(1L << (mag * 10));
|
||||
dataReceived = i.BytesReceived / (float)(1L << (mag * 10));
|
||||
dataSuffix = SizeSuffixes[mag];
|
||||
}
|
||||
|
||||
progressBar.Indeterminate = false;
|
||||
progressBar.Percentage = i.ProgressPercentage;
|
||||
|
||||
getStatusText = () => "Downloading from {4} {1:0.00}/{2:0.00} {3} ({0}%)".F(i.ProgressPercentage,
|
||||
dataReceived, dataTotal, dataSuffix,
|
||||
downloadHost ?? "unknown host");
|
||||
};
|
||||
|
||||
Action<string> onExtractProgress = s => Game.RunAfterTick(() => getStatusText = () => s);
|
||||
|
||||
Action<string> onError = s => Game.RunAfterTick(() =>
|
||||
{
|
||||
Log.Write("install", "Download failed: " + s);
|
||||
|
||||
progressBar.Indeterminate = false;
|
||||
progressBar.Percentage = 100;
|
||||
getStatusText = () => "Error: " + s;
|
||||
retryButton.IsVisible = () => true;
|
||||
});
|
||||
|
||||
Action<AsyncCompletedEventArgs, bool> onDownloadComplete = (i, cancelled) =>
|
||||
{
|
||||
if (i.Error != null)
|
||||
{
|
||||
onError(Download.FormatErrorMessage(i.Error));
|
||||
return;
|
||||
}
|
||||
|
||||
if (cancelled)
|
||||
{
|
||||
onError("Download cancelled");
|
||||
return;
|
||||
}
|
||||
|
||||
// Automatically extract
|
||||
getStatusText = () => "Extracting...";
|
||||
progressBar.Indeterminate = true;
|
||||
|
||||
var extracted = new List<string>();
|
||||
try
|
||||
{
|
||||
using (var stream = File.OpenRead(file))
|
||||
using (var z = new ZipFile(stream))
|
||||
{
|
||||
foreach (var kv in download.Extract)
|
||||
{
|
||||
var entry = z.GetEntry(kv.Value);
|
||||
if (!entry.IsFile)
|
||||
continue;
|
||||
|
||||
onExtractProgress("Extracting " + entry.Name);
|
||||
Log.Write("install", "Extracting " + entry.Name);
|
||||
var targetPath = Platform.ResolvePath(kv.Key);
|
||||
Directory.CreateDirectory(Path.GetDirectoryName(targetPath));
|
||||
extracted.Add(targetPath);
|
||||
|
||||
using (var zz = z.GetInputStream(entry))
|
||||
using (var f = File.Create(targetPath))
|
||||
zz.CopyTo(f);
|
||||
}
|
||||
|
||||
z.Close();
|
||||
}
|
||||
|
||||
Game.RunAfterTick(() => { Ui.CloseWindow(); onSuccess(); });
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
Log.Write("install", "Extraction failed");
|
||||
|
||||
foreach (var f in extracted)
|
||||
{
|
||||
Log.Write("install", "Deleting " + f);
|
||||
File.Delete(f);
|
||||
}
|
||||
|
||||
onError("Invalid archive");
|
||||
}
|
||||
finally
|
||||
{
|
||||
deleteTempFile();
|
||||
}
|
||||
};
|
||||
|
||||
Action<string> downloadUrl = url =>
|
||||
{
|
||||
Log.Write("install", "Downloading " + url);
|
||||
|
||||
downloadHost = new Uri(url).Host;
|
||||
var dl = new Download(url, file, onDownloadProgress, onDownloadComplete);
|
||||
cancelButton.OnClick = () => { dl.Cancel(); deleteTempFile(); Ui.CloseWindow(); };
|
||||
retryButton.OnClick = () => { dl.Cancel(); ShowDownloadDialog(); };
|
||||
};
|
||||
|
||||
if (download.MirrorList != null)
|
||||
{
|
||||
Log.Write("install", "Fetching mirrors from " + download.MirrorList);
|
||||
|
||||
Action<DownloadDataCompletedEventArgs, bool> onFetchMirrorsComplete = (i, cancelled) =>
|
||||
{
|
||||
progressBar.Indeterminate = true;
|
||||
|
||||
if (i.Error != null)
|
||||
{
|
||||
onError(Download.FormatErrorMessage(i.Error));
|
||||
return;
|
||||
}
|
||||
|
||||
if (cancelled)
|
||||
{
|
||||
onError("Download cancelled");
|
||||
return;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
var data = Encoding.UTF8.GetString(i.Result);
|
||||
var mirrorList = data.Split(new[] { '\n' }, StringSplitOptions.RemoveEmptyEntries);
|
||||
downloadUrl(mirrorList.Random(new MersenneTwister()));
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Log.Write("install", "Mirror selection failed with error:");
|
||||
Log.Write("install", e.ToString());
|
||||
onError("Online mirror is not available. Please install from an original disc.");
|
||||
}
|
||||
};
|
||||
|
||||
var updateMirrors = new Download(download.MirrorList, onDownloadProgress, onFetchMirrorsComplete);
|
||||
cancelButton.OnClick = () => { updateMirrors.Cancel(); Ui.CloseWindow(); };
|
||||
retryButton.OnClick = () => { updateMirrors.Cancel(); ShowDownloadDialog(); };
|
||||
}
|
||||
else
|
||||
downloadUrl(download.URL);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,159 +0,0 @@
|
||||
#region Copyright & License Information
|
||||
/*
|
||||
* Copyright 2007-2016 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.ComponentModel;
|
||||
using System.IO;
|
||||
using System.Net;
|
||||
using System.Text;
|
||||
using OpenRA.Support;
|
||||
using OpenRA.Widgets;
|
||||
|
||||
namespace OpenRA.Mods.Common.Widgets.Logic
|
||||
{
|
||||
public class DownloadPackagesLogic : ChromeLogic
|
||||
{
|
||||
static readonly string[] SizeSuffixes = { "bytes", "KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB" };
|
||||
readonly Widget panel;
|
||||
readonly string modId;
|
||||
readonly string mirrorListUrl;
|
||||
readonly ProgressBarWidget progressBar;
|
||||
readonly LabelWidget statusLabel;
|
||||
readonly Action afterInstall;
|
||||
string mirror;
|
||||
|
||||
[ObjectCreator.UseCtor]
|
||||
public DownloadPackagesLogic(Widget widget, Action afterInstall, string mirrorListUrl, string modId)
|
||||
{
|
||||
this.mirrorListUrl = mirrorListUrl;
|
||||
this.afterInstall = afterInstall;
|
||||
this.modId = modId;
|
||||
|
||||
panel = widget.Get("INSTALL_DOWNLOAD_PANEL");
|
||||
progressBar = panel.Get<ProgressBarWidget>("PROGRESS_BAR");
|
||||
statusLabel = panel.Get<LabelWidget>("STATUS_LABEL");
|
||||
|
||||
var text = "Downloading {0} assets...".F(ModMetadata.AllMods[modId].Title);
|
||||
panel.Get<LabelWidget>("TITLE").Text = text;
|
||||
|
||||
ShowDownloadDialog();
|
||||
}
|
||||
|
||||
void ShowDownloadDialog()
|
||||
{
|
||||
statusLabel.GetText = () => "Fetching list of mirrors...";
|
||||
progressBar.Indeterminate = true;
|
||||
|
||||
var retryButton = panel.Get<ButtonWidget>("RETRY_BUTTON");
|
||||
retryButton.IsVisible = () => false;
|
||||
|
||||
var cancelButton = panel.Get<ButtonWidget>("CANCEL_BUTTON");
|
||||
|
||||
var file = Path.Combine(Path.GetTempPath(), Path.GetRandomFileName());
|
||||
var dest = Platform.ResolvePath("^", "Content", modId);
|
||||
|
||||
Action<DownloadProgressChangedEventArgs> onDownloadProgress = i =>
|
||||
{
|
||||
var dataReceived = 0.0f;
|
||||
var dataTotal = 0.0f;
|
||||
var mag = 0;
|
||||
var dataSuffix = "";
|
||||
|
||||
if (i.TotalBytesToReceive < 0)
|
||||
{
|
||||
dataTotal = float.NaN;
|
||||
dataReceived = i.BytesReceived;
|
||||
dataSuffix = SizeSuffixes[0];
|
||||
}
|
||||
else
|
||||
{
|
||||
mag = (int)Math.Log(i.TotalBytesToReceive, 1024);
|
||||
dataTotal = i.TotalBytesToReceive / (float)(1L << (mag * 10));
|
||||
dataReceived = i.BytesReceived / (float)(1L << (mag * 10));
|
||||
dataSuffix = SizeSuffixes[mag];
|
||||
}
|
||||
|
||||
progressBar.Indeterminate = false;
|
||||
progressBar.Percentage = i.ProgressPercentage;
|
||||
|
||||
statusLabel.GetText = () => "Downloading from {4} {1:0.00}/{2:0.00} {3} ({0}%)".F(i.ProgressPercentage,
|
||||
dataReceived, dataTotal, dataSuffix,
|
||||
mirror != null ? new Uri(mirror).Host : "unknown host");
|
||||
};
|
||||
|
||||
Action<string> onExtractProgress = s => Game.RunAfterTick(() => statusLabel.GetText = () => s);
|
||||
|
||||
Action<string> onError = s => Game.RunAfterTick(() =>
|
||||
{
|
||||
statusLabel.GetText = () => "Error: " + s;
|
||||
retryButton.IsVisible = () => true;
|
||||
});
|
||||
|
||||
Action<AsyncCompletedEventArgs, bool> onDownloadComplete = (i, cancelled) =>
|
||||
{
|
||||
if (i.Error != null)
|
||||
{
|
||||
onError(Download.FormatErrorMessage(i.Error));
|
||||
return;
|
||||
}
|
||||
|
||||
if (cancelled)
|
||||
{
|
||||
onError("Download cancelled");
|
||||
return;
|
||||
}
|
||||
|
||||
// Automatically extract
|
||||
statusLabel.GetText = () => "Extracting...";
|
||||
progressBar.Indeterminate = true;
|
||||
if (InstallUtils.ExtractZip(file, dest, onExtractProgress, onError))
|
||||
{
|
||||
Game.RunAfterTick(() =>
|
||||
{
|
||||
Ui.CloseWindow();
|
||||
afterInstall();
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
Action<DownloadDataCompletedEventArgs, bool> onFetchMirrorsComplete = (i, cancelled) =>
|
||||
{
|
||||
progressBar.Indeterminate = true;
|
||||
|
||||
if (i.Error != null)
|
||||
{
|
||||
onError(Download.FormatErrorMessage(i.Error));
|
||||
return;
|
||||
}
|
||||
|
||||
if (cancelled)
|
||||
{
|
||||
onError("Download cancelled");
|
||||
return;
|
||||
}
|
||||
|
||||
var data = Encoding.UTF8.GetString(i.Result);
|
||||
var mirrorList = data.Split(new[] { '\n' }, StringSplitOptions.RemoveEmptyEntries);
|
||||
mirror = mirrorList.Random(new MersenneTwister());
|
||||
|
||||
// Save the package to a temp file
|
||||
var dl = new Download(mirror, file, onDownloadProgress, onDownloadComplete);
|
||||
cancelButton.OnClick = () => { dl.Cancel(); Ui.CloseWindow(); };
|
||||
retryButton.OnClick = () => { dl.Cancel(); ShowDownloadDialog(); };
|
||||
};
|
||||
|
||||
// Get the list of mirrors
|
||||
var updateMirrors = new Download(mirrorListUrl, onDownloadProgress, onFetchMirrorsComplete);
|
||||
cancelButton.OnClick = () => { updateMirrors.Cancel(); Ui.CloseWindow(); };
|
||||
retryButton.OnClick = () => { updateMirrors.Cancel(); ShowDownloadDialog(); };
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,218 +0,0 @@
|
||||
#region Copyright & License Information
|
||||
/*
|
||||
* Copyright 2007-2016 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;
|
||||
using System.Threading;
|
||||
using OpenRA.FileSystem;
|
||||
using OpenRA.Widgets;
|
||||
|
||||
namespace OpenRA.Mods.Common.Widgets.Logic
|
||||
{
|
||||
public class InstallFromCDLogic : ChromeLogic
|
||||
{
|
||||
readonly ModData modData;
|
||||
readonly string modId;
|
||||
readonly Widget panel;
|
||||
readonly ProgressBarWidget progressBar;
|
||||
readonly LabelWidget statusLabel;
|
||||
readonly Action afterInstall;
|
||||
readonly ButtonWidget retryButton, backButton;
|
||||
readonly Widget installingContainer, insertDiskContainer;
|
||||
readonly ContentInstaller installData;
|
||||
|
||||
[ObjectCreator.UseCtor]
|
||||
public InstallFromCDLogic(Widget widget, ModData modData, Action afterInstall, string modId)
|
||||
{
|
||||
this.modData = modData;
|
||||
this.modId = modId;
|
||||
installData = ModMetadata.AllMods[modId].Content;
|
||||
this.afterInstall = afterInstall;
|
||||
panel = widget.Get("INSTALL_FROMCD_PANEL");
|
||||
progressBar = panel.Get<ProgressBarWidget>("PROGRESS_BAR");
|
||||
statusLabel = panel.Get<LabelWidget>("STATUS_LABEL");
|
||||
|
||||
backButton = panel.Get<ButtonWidget>("BACK_BUTTON");
|
||||
backButton.OnClick = Ui.CloseWindow;
|
||||
|
||||
retryButton = panel.Get<ButtonWidget>("RETRY_BUTTON");
|
||||
retryButton.OnClick = CheckForDisk;
|
||||
|
||||
installingContainer = panel.Get("INSTALLING");
|
||||
insertDiskContainer = panel.Get("INSERT_DISK");
|
||||
CheckForDisk();
|
||||
}
|
||||
|
||||
bool IsValidDisk(string diskRoot)
|
||||
{
|
||||
return installData.DiskTestFiles.All(f => File.Exists(Path.Combine(diskRoot, f)));
|
||||
}
|
||||
|
||||
bool IsTFD(string diskpath)
|
||||
{
|
||||
var test = File.Exists(Path.Combine(diskpath, "data1.hdr"));
|
||||
var i = 0;
|
||||
|
||||
while (test && i < 14)
|
||||
test &= File.Exists(Path.Combine(diskpath, "data{0}.cab".F(++i)));
|
||||
|
||||
return test;
|
||||
}
|
||||
|
||||
void CheckForDisk()
|
||||
{
|
||||
var path = InstallUtils.GetMountedDisk(IsValidDisk);
|
||||
|
||||
if (path != null)
|
||||
Install(path);
|
||||
else if ((installData.InstallShieldCABFileIds.Count != 0 || installData.InstallShieldCABFilePackageIds.Count != 0)
|
||||
&& (path = InstallUtils.GetMountedDisk(IsTFD)) != null)
|
||||
InstallTFD(Platform.ResolvePath(path, "data1.hdr"));
|
||||
else
|
||||
{
|
||||
var text = "Please insert a {0} install CD and click Retry.".F(ModMetadata.AllMods[modId].Title);
|
||||
insertDiskContainer.Get<LabelWidget>("INFO2").Text = text;
|
||||
|
||||
insertDiskContainer.IsVisible = () => true;
|
||||
installingContainer.IsVisible = () => false;
|
||||
}
|
||||
}
|
||||
|
||||
void InstallTFD(string source)
|
||||
{
|
||||
backButton.IsDisabled = () => true;
|
||||
retryButton.IsDisabled = () => true;
|
||||
insertDiskContainer.IsVisible = () => false;
|
||||
installingContainer.IsVisible = () => true;
|
||||
progressBar.Percentage = 0;
|
||||
|
||||
new Thread(() =>
|
||||
{
|
||||
using (var cabExtractor = new InstallShieldCABExtractor(modData.ModFiles, source))
|
||||
{
|
||||
var denom = installData.InstallShieldCABFileIds.Count;
|
||||
var extractFiles = installData.ExtractFilesFromCD;
|
||||
|
||||
if (installData.InstallShieldCABFilePackageIds.Count > 0)
|
||||
denom += extractFiles.SelectMany(x => x.Value).Count();
|
||||
|
||||
var installPercent = 100 / denom;
|
||||
|
||||
foreach (uint index in installData.InstallShieldCABFileIds)
|
||||
{
|
||||
var filename = cabExtractor.FileName(index);
|
||||
statusLabel.GetText = () => "Extracting {0}".F(filename);
|
||||
var dest = Platform.ResolvePath("^", "Content", modId, filename.ToLowerInvariant());
|
||||
cabExtractor.ExtractFile(index, dest);
|
||||
progressBar.Percentage += installPercent;
|
||||
}
|
||||
|
||||
var ArchivesToExtract = installData.InstallShieldCABFilePackageIds.Select(x => x.Split(':'));
|
||||
var destDir = Platform.ResolvePath("^", "Content", modId);
|
||||
var onError = (Action<string>)(s => { });
|
||||
var overwrite = installData.OverwriteFiles;
|
||||
|
||||
var onProgress = (Action<string>)(s => Game.RunAfterTick(() =>
|
||||
{
|
||||
progressBar.Percentage += installPercent;
|
||||
|
||||
statusLabel.GetText = () => s;
|
||||
}));
|
||||
|
||||
foreach (var archive in ArchivesToExtract)
|
||||
{
|
||||
var filename = cabExtractor.FileName(uint.Parse(archive[0]));
|
||||
statusLabel.GetText = () => "Extracting {0}".F(filename);
|
||||
var destFile = Platform.ResolvePath("^", "Content", modId, filename.ToLowerInvariant());
|
||||
cabExtractor.ExtractFile(uint.Parse(archive[0]), destFile);
|
||||
InstallUtils.ExtractFromPackage(modData.ModFiles, source, destFile, extractFiles, destDir, overwrite, installData.OutputFilenameCase, onProgress, onError);
|
||||
progressBar.Percentage += installPercent;
|
||||
}
|
||||
}
|
||||
|
||||
Game.RunAfterTick(() =>
|
||||
{
|
||||
Ui.CloseWindow();
|
||||
afterInstall();
|
||||
});
|
||||
}) { IsBackground = true }.Start();
|
||||
}
|
||||
|
||||
void Install(string source)
|
||||
{
|
||||
backButton.IsDisabled = () => true;
|
||||
retryButton.IsDisabled = () => true;
|
||||
insertDiskContainer.IsVisible = () => false;
|
||||
installingContainer.IsVisible = () => true;
|
||||
var dest = Platform.ResolvePath("^", "Content", modId);
|
||||
var copyFiles = installData.CopyFilesFromCD;
|
||||
|
||||
var packageToExtract = installData.PackageToExtractFromCD.Split(':');
|
||||
var extractPackage = packageToExtract.First();
|
||||
|
||||
var extractFiles = installData.ExtractFilesFromCD;
|
||||
|
||||
var overwrite = installData.OverwriteFiles;
|
||||
var installCounter = 0;
|
||||
var installTotal = copyFiles.SelectMany(x => x.Value).Count() + extractFiles.SelectMany(x => x.Value).Count();
|
||||
var onProgress = (Action<string>)(s => Game.RunAfterTick(() =>
|
||||
{
|
||||
progressBar.Percentage = installCounter * 100 / installTotal;
|
||||
installCounter++;
|
||||
|
||||
statusLabel.GetText = () => s;
|
||||
}));
|
||||
|
||||
var onError = (Action<string>)(s => Game.RunAfterTick(() =>
|
||||
{
|
||||
statusLabel.GetText = () => "Error: " + s;
|
||||
backButton.IsDisabled = () => false;
|
||||
retryButton.IsDisabled = () => false;
|
||||
}));
|
||||
|
||||
new Thread(() =>
|
||||
{
|
||||
try
|
||||
{
|
||||
if (!InstallUtils.CopyFiles(source, copyFiles, dest, overwrite, installData.OutputFilenameCase, onProgress, onError))
|
||||
{
|
||||
onError("Copying files from CD failed.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(extractPackage))
|
||||
{
|
||||
if (!InstallUtils.ExtractFromPackage(modData.ModFiles, source, extractPackage, extractFiles, dest,
|
||||
overwrite, installData.OutputFilenameCase, onProgress, onError))
|
||||
{
|
||||
onError("Extracting files from CD failed.");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
Game.RunAfterTick(() =>
|
||||
{
|
||||
statusLabel.GetText = () => "Game assets have been extracted.";
|
||||
Ui.CloseWindow();
|
||||
afterInstall();
|
||||
});
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
onError("Installation failed.\n{0}".F(e.Message));
|
||||
Log.Write("debug", e.ToString());
|
||||
return;
|
||||
}
|
||||
}) { IsBackground = true }.Start();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,422 @@
|
||||
#region Copyright & License Information
|
||||
/*
|
||||
* Copyright 2007-2016 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.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Security.Cryptography;
|
||||
using System.Threading.Tasks;
|
||||
using OpenRA.FileFormats;
|
||||
using OpenRA.Mods.Common.FileFormats;
|
||||
using OpenRA.Widgets;
|
||||
|
||||
namespace OpenRA.Mods.Common.Widgets.Logic
|
||||
{
|
||||
public class InstallFromDiscLogic : ChromeLogic
|
||||
{
|
||||
enum Mode { Progress, Message, List }
|
||||
|
||||
readonly ModContent content;
|
||||
|
||||
readonly Widget panel;
|
||||
readonly LabelWidget titleLabel;
|
||||
readonly ButtonWidget primaryButton;
|
||||
readonly ButtonWidget secondaryButton;
|
||||
|
||||
// Progress panel
|
||||
readonly Widget progressContainer;
|
||||
readonly ProgressBarWidget progressBar;
|
||||
readonly LabelWidget progressLabel;
|
||||
|
||||
// Message panel
|
||||
readonly Widget messageContainer;
|
||||
readonly LabelWidget messageLabel;
|
||||
|
||||
// List Panel
|
||||
readonly Widget listContainer;
|
||||
readonly ScrollPanelWidget listPanel;
|
||||
readonly LabelWidget listTemplate;
|
||||
readonly LabelWidget listLabel;
|
||||
|
||||
Mode visible = Mode.Progress;
|
||||
|
||||
[ObjectCreator.UseCtor]
|
||||
public InstallFromDiscLogic(Widget widget, ModContent content, Action afterInstall)
|
||||
{
|
||||
this.content = content;
|
||||
|
||||
Log.AddChannel("install", "install.log");
|
||||
|
||||
// this.afterInstall = afterInstall;
|
||||
panel = widget.Get("DISC_INSTALL_PANEL");
|
||||
|
||||
titleLabel = panel.Get<LabelWidget>("TITLE");
|
||||
|
||||
primaryButton = panel.Get<ButtonWidget>("PRIMARY_BUTTON");
|
||||
secondaryButton = panel.Get<ButtonWidget>("SECONDARY_BUTTON");
|
||||
|
||||
// Progress view
|
||||
progressContainer = panel.Get("PROGRESS");
|
||||
progressContainer.IsVisible = () => visible == Mode.Progress;
|
||||
progressBar = panel.Get<ProgressBarWidget>("PROGRESS_BAR");
|
||||
progressLabel = panel.Get<LabelWidget>("PROGRESS_MESSAGE");
|
||||
progressLabel.IsVisible = () => visible == Mode.Progress;
|
||||
|
||||
// Message view
|
||||
messageContainer = panel.Get("MESSAGE");
|
||||
messageContainer.IsVisible = () => visible == Mode.Message;
|
||||
messageLabel = messageContainer.Get<LabelWidget>("MESSAGE_MESSAGE");
|
||||
|
||||
// List view
|
||||
listContainer = panel.Get("LIST");
|
||||
listContainer.IsVisible = () => visible == Mode.List;
|
||||
|
||||
listPanel = listContainer.Get<ScrollPanelWidget>("LIST_PANEL");
|
||||
listTemplate = listPanel.Get<LabelWidget>("LIST_TEMPLATE");
|
||||
listPanel.RemoveChildren();
|
||||
|
||||
listLabel = listContainer.Get<LabelWidget>("LIST_MESSAGE");
|
||||
|
||||
DetectContentDisks();
|
||||
}
|
||||
|
||||
void DetectContentDisks()
|
||||
{
|
||||
var message = "Detecting drives";
|
||||
ShowProgressbar("Checking Discs", () => message);
|
||||
ShowBackRetry(DetectContentDisks);
|
||||
|
||||
new Task(() =>
|
||||
{
|
||||
var volumes = DriveInfo.GetDrives()
|
||||
.Where(v => v.DriveType == DriveType.CDRom && v.IsReady)
|
||||
.Select(v => v.RootDirectory.FullName);
|
||||
|
||||
foreach (var kv in content.Discs)
|
||||
{
|
||||
message = "Searching for " + kv.Value.Title;
|
||||
|
||||
foreach (var volume in volumes)
|
||||
{
|
||||
if (PathIsDiscMount(volume, kv.Value))
|
||||
{
|
||||
var packages = content.Packages.Values
|
||||
.Where(p => p.Discs.Contains(kv.Key) && !p.IsInstalled())
|
||||
.Select(p => p.Title);
|
||||
|
||||
// Ignore disc if content is already installed
|
||||
if (packages.Any())
|
||||
{
|
||||
Game.RunAfterTick(() =>
|
||||
{
|
||||
ShowList(kv.Value.Title, "The following content packages will be installed:", packages);
|
||||
ShowContinueCancel(() => InstallFromDisc(volume, kv.Value));
|
||||
});
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var discTitles = content.Packages.Values
|
||||
.Where(p => !p.IsInstalled())
|
||||
.SelectMany(p => p.Discs)
|
||||
.Select(d => content.Discs[d].Title)
|
||||
.Distinct();
|
||||
|
||||
Game.RunAfterTick(() =>
|
||||
{
|
||||
ShowList("Disc Content Not Found", "Please insert or mount one of the following discs and try again", discTitles);
|
||||
ShowBackRetry(DetectContentDisks);
|
||||
});
|
||||
}).Start();
|
||||
}
|
||||
|
||||
void InstallFromDisc(string path, ModContent.ModDisc disc)
|
||||
{
|
||||
var message = "";
|
||||
ShowProgressbar("Installing Content", () => message);
|
||||
ShowDisabledCancel();
|
||||
|
||||
new Task(() =>
|
||||
{
|
||||
var extracted = new List<string>();
|
||||
|
||||
try
|
||||
{
|
||||
foreach (var i in disc.Install)
|
||||
{
|
||||
switch (i.Key)
|
||||
{
|
||||
case "copy":
|
||||
{
|
||||
var sourceDir = Path.Combine(path, i.Value.Value);
|
||||
foreach (var node in i.Value.Nodes)
|
||||
{
|
||||
var sourcePath = Path.Combine(sourceDir, node.Value.Value);
|
||||
var targetPath = Platform.ResolvePath(node.Key);
|
||||
if (File.Exists(targetPath))
|
||||
{
|
||||
Log.Write("install", "Ignoring installed file " + targetPath);
|
||||
continue;
|
||||
}
|
||||
|
||||
Log.Write("install", "Copying {0} -> {1}".F(sourcePath, targetPath));
|
||||
message = "Copying " + Path.GetFileName(sourcePath);
|
||||
extracted.Add(targetPath);
|
||||
Directory.CreateDirectory(Path.GetDirectoryName(targetPath));
|
||||
File.Copy(sourcePath, targetPath);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case "extract-raw":
|
||||
{
|
||||
ExtractFromPackage(ExtractionType.Raw, path, i.Value, extracted, ref message);
|
||||
break;
|
||||
}
|
||||
|
||||
case "extract-blast":
|
||||
{
|
||||
ExtractFromPackage(ExtractionType.Blast, path, i.Value, extracted, ref message);
|
||||
break;
|
||||
}
|
||||
|
||||
case "extract-mscab":
|
||||
{
|
||||
ExtractFromMSCab(path, i.Value, extracted, m => message = m);
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
Game.Debug("debug", "Unknown installation command {0} - ignoring", i.Key);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
Game.RunAfterTick(Ui.CloseWindow);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Log.Write("install", e.ToString());
|
||||
|
||||
foreach (var f in extracted)
|
||||
{
|
||||
Log.Write("install", "Deleting " + f);
|
||||
File.Delete(f);
|
||||
}
|
||||
|
||||
Game.RunAfterTick(() =>
|
||||
{
|
||||
ShowMessage("Installation Failed", "Refer to install.log in the logs directory for details.");
|
||||
ShowBackRetry(() => InstallFromDisc(path, disc));
|
||||
});
|
||||
}
|
||||
}).Start();
|
||||
}
|
||||
|
||||
enum ExtractionType { Raw, Blast }
|
||||
|
||||
static void ExtractFromPackage(ExtractionType type, string path, MiniYaml actionYaml, List<string> extractedFiles, ref string progressMessage)
|
||||
{
|
||||
var sourcePath = Path.Combine(path, actionYaml.Value);
|
||||
using (var source = File.OpenRead(sourcePath))
|
||||
{
|
||||
foreach (var node in actionYaml.Nodes)
|
||||
{
|
||||
var targetPath = Platform.ResolvePath(node.Key);
|
||||
|
||||
if (File.Exists(targetPath))
|
||||
{
|
||||
Log.Write("install", "Skipping installed file " + targetPath);
|
||||
continue;
|
||||
}
|
||||
|
||||
var offsetNode = node.Value.Nodes.FirstOrDefault(n => n.Key == "Offset");
|
||||
if (offsetNode == null)
|
||||
{
|
||||
Log.Write("install", "Skipping entry with missing Offset definition " + targetPath);
|
||||
continue;
|
||||
}
|
||||
|
||||
var lengthNode = node.Value.Nodes.FirstOrDefault(n => n.Key == "Length");
|
||||
if (lengthNode == null)
|
||||
{
|
||||
Log.Write("install", "Skipping entry with missing Length definition " + targetPath);
|
||||
continue;
|
||||
}
|
||||
|
||||
var length = FieldLoader.GetValue<int>("Length", lengthNode.Value.Value);
|
||||
|
||||
source.Position = FieldLoader.GetValue<int>("Offset", offsetNode.Value.Value);
|
||||
|
||||
extractedFiles.Add(targetPath);
|
||||
Directory.CreateDirectory(Path.GetDirectoryName(targetPath));
|
||||
using (var target = File.OpenWrite(targetPath))
|
||||
{
|
||||
// This is a bit dumb memory-wise, but we load the whole thing when running the game anyway
|
||||
Log.Write("install", "Extracting {0} -> {1}".F(sourcePath, targetPath));
|
||||
progressMessage = "Extracting " + Path.GetFileName(Path.GetFileName(targetPath));
|
||||
|
||||
var data = source.ReadBytes(length);
|
||||
if (type == ExtractionType.Blast)
|
||||
data = Blast.Decompress(data);
|
||||
|
||||
target.Write(data);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void ExtractFromMSCab(string path, MiniYaml actionYaml, List<string> extractedFiles, Action<string> updateMessage)
|
||||
{
|
||||
var sourcePath = Path.Combine(path, actionYaml.Value);
|
||||
using (var source = File.OpenRead(sourcePath))
|
||||
{
|
||||
var reader = new MSCabCompression(source);
|
||||
foreach (var node in actionYaml.Nodes)
|
||||
{
|
||||
var targetPath = Platform.ResolvePath(node.Key);
|
||||
|
||||
if (File.Exists(targetPath))
|
||||
{
|
||||
Log.Write("install", "Skipping installed file " + targetPath);
|
||||
continue;
|
||||
}
|
||||
|
||||
extractedFiles.Add(targetPath);
|
||||
Directory.CreateDirectory(Path.GetDirectoryName(targetPath));
|
||||
using (var target = File.OpenWrite(targetPath))
|
||||
{
|
||||
// This is a bit dumb memory-wise, but we load the whole thing when running the game anyway
|
||||
Log.Write("install", "Extracting {0} -> {1}".F(sourcePath, targetPath));
|
||||
|
||||
var displayFilename = Path.GetFileName(Path.GetFileName(targetPath));
|
||||
Action<int> onProgress = percent => updateMessage("Extracting {0} ({1}%)".F(displayFilename, percent));
|
||||
target.Write(reader.ExtractFile(node.Value.Value, onProgress));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool PathIsDiscMount(string path, ModContent.ModDisc disc)
|
||||
{
|
||||
try
|
||||
{
|
||||
foreach (var kv in disc.IDFiles)
|
||||
{
|
||||
var filePath = Path.Combine(path, kv.Key);
|
||||
if (!File.Exists(filePath))
|
||||
return false;
|
||||
|
||||
using (var fileStream = File.OpenRead(filePath))
|
||||
using (var csp = SHA1.Create())
|
||||
{
|
||||
var hash = new string(csp.ComputeHash(fileStream).SelectMany(a => a.ToString("x2")).ToArray());
|
||||
if (hash != kv.Value)
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void ShowMessage(string title, string message)
|
||||
{
|
||||
visible = Mode.Message;
|
||||
titleLabel.Text = title;
|
||||
messageLabel.Text = message;
|
||||
|
||||
primaryButton.Bounds.Y += messageContainer.Bounds.Height - panel.Bounds.Height;
|
||||
secondaryButton.Bounds.Y += messageContainer.Bounds.Height - panel.Bounds.Height;
|
||||
panel.Bounds.Y -= (messageContainer.Bounds.Height - panel.Bounds.Height) / 2;
|
||||
panel.Bounds.Height = messageContainer.Bounds.Height;
|
||||
}
|
||||
|
||||
void ShowProgressbar(string title, Func<string> getMessage)
|
||||
{
|
||||
visible = Mode.Progress;
|
||||
titleLabel.Text = title;
|
||||
progressBar.IsIndeterminate = () => true;
|
||||
|
||||
var font = Game.Renderer.Fonts[progressLabel.Font];
|
||||
var status = new CachedTransform<string, string>(s => WidgetUtils.TruncateText(s, progressLabel.Bounds.Width, font));
|
||||
progressLabel.GetText = () => status.Update(getMessage());
|
||||
|
||||
primaryButton.Bounds.Y += progressContainer.Bounds.Height - panel.Bounds.Height;
|
||||
secondaryButton.Bounds.Y += progressContainer.Bounds.Height - panel.Bounds.Height;
|
||||
panel.Bounds.Y -= (progressContainer.Bounds.Height - panel.Bounds.Height) / 2;
|
||||
panel.Bounds.Height = progressContainer.Bounds.Height;
|
||||
}
|
||||
|
||||
void ShowList(string title, string message, IEnumerable<string> items)
|
||||
{
|
||||
visible = Mode.List;
|
||||
titleLabel.Text = title;
|
||||
listLabel.Text = message;
|
||||
|
||||
listPanel.RemoveChildren();
|
||||
foreach (var i in items)
|
||||
{
|
||||
var item = i;
|
||||
var labelWidget = (LabelWidget)listTemplate.Clone();
|
||||
labelWidget.GetText = () => item;
|
||||
listPanel.AddChild(labelWidget);
|
||||
}
|
||||
|
||||
primaryButton.Bounds.Y += listContainer.Bounds.Height - panel.Bounds.Height;
|
||||
secondaryButton.Bounds.Y += listContainer.Bounds.Height - panel.Bounds.Height;
|
||||
panel.Bounds.Y -= (listContainer.Bounds.Height - panel.Bounds.Height) / 2;
|
||||
panel.Bounds.Height = listContainer.Bounds.Height;
|
||||
}
|
||||
|
||||
void ShowContinueCancel(Action continueAction)
|
||||
{
|
||||
primaryButton.OnClick = continueAction;
|
||||
primaryButton.Text = "Continue";
|
||||
primaryButton.Visible = true;
|
||||
|
||||
secondaryButton.OnClick = Ui.CloseWindow;
|
||||
secondaryButton.Text = "Cancel";
|
||||
secondaryButton.Visible = true;
|
||||
secondaryButton.Disabled = false;
|
||||
Game.RunAfterTick(Ui.ResetTooltips);
|
||||
}
|
||||
|
||||
void ShowBackRetry(Action retryAction)
|
||||
{
|
||||
primaryButton.OnClick = retryAction;
|
||||
primaryButton.Text = "Retry";
|
||||
primaryButton.Visible = true;
|
||||
|
||||
secondaryButton.OnClick = Ui.CloseWindow;
|
||||
secondaryButton.Text = "Back";
|
||||
secondaryButton.Visible = true;
|
||||
secondaryButton.Disabled = false;
|
||||
Game.RunAfterTick(Ui.ResetTooltips);
|
||||
}
|
||||
|
||||
void ShowDisabledCancel()
|
||||
{
|
||||
primaryButton.Visible = false;
|
||||
secondaryButton.Disabled = true;
|
||||
Game.RunAfterTick(Ui.ResetTooltips);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,43 +0,0 @@
|
||||
#region Copyright & License Information
|
||||
/*
|
||||
* Copyright 2007-2016 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 OpenRA.Widgets;
|
||||
|
||||
namespace OpenRA.Mods.Common.Widgets.Logic
|
||||
{
|
||||
public class InstallLogic : ChromeLogic
|
||||
{
|
||||
[ObjectCreator.UseCtor]
|
||||
public InstallLogic(Widget widget, string mirrorListUrl, string modId)
|
||||
{
|
||||
var panel = widget.Get("INSTALL_PANEL");
|
||||
var widgetArgs = new WidgetArgs
|
||||
{
|
||||
{ "afterInstall", () => { Game.InitializeMod(modId, new Arguments()); } },
|
||||
{ "mirrorListUrl", mirrorListUrl },
|
||||
{ "modId", modId }
|
||||
};
|
||||
|
||||
var mod = ModMetadata.AllMods[modId];
|
||||
var text = "OpenRA requires the original {0} game content.".F(mod.Title);
|
||||
panel.Get<LabelWidget>("DESC1").Text = text;
|
||||
|
||||
var downloadButton = panel.Get<ButtonWidget>("DOWNLOAD_BUTTON");
|
||||
downloadButton.OnClick = () => Ui.OpenWindow("INSTALL_DOWNLOAD_PANEL", widgetArgs);
|
||||
downloadButton.IsDisabled = () => string.IsNullOrEmpty(mod.Content.PackageMirrorList);
|
||||
|
||||
panel.Get<ButtonWidget>("INSTALL_BUTTON").OnClick = () =>
|
||||
Ui.OpenWindow("INSTALL_FROMCD_PANEL", widgetArgs);
|
||||
|
||||
panel.Get<ButtonWidget>("BACK_BUTTON").OnClick = Ui.CloseWindow;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,60 +0,0 @@
|
||||
#region Copyright & License Information
|
||||
/*
|
||||
* Copyright 2007-2016 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 OpenRA.Widgets;
|
||||
|
||||
namespace OpenRA.Mods.Common.Widgets.Logic
|
||||
{
|
||||
public class InstallMusicLogic : ChromeLogic
|
||||
{
|
||||
[ObjectCreator.UseCtor]
|
||||
public InstallMusicLogic(Widget widget, string modId)
|
||||
{
|
||||
var installMusicContainer = widget.Get("INSTALL_MUSIC_PANEL");
|
||||
|
||||
Action loadDefaultMod = () => Game.RunAfterTick(() => Game.InitializeMod(modId, null));
|
||||
|
||||
var cancelButton = installMusicContainer.GetOrNull<ButtonWidget>("BACK_BUTTON");
|
||||
if (cancelButton != null)
|
||||
cancelButton.OnClick = loadDefaultMod;
|
||||
|
||||
var copyFromDiscButton = installMusicContainer.GetOrNull<ButtonWidget>("INSTALL_MUSIC_BUTTON");
|
||||
if (copyFromDiscButton != null)
|
||||
{
|
||||
copyFromDiscButton.OnClick = () =>
|
||||
{
|
||||
Ui.OpenWindow("INSTALL_FROMCD_PANEL", new WidgetArgs
|
||||
{
|
||||
{ "afterInstall", loadDefaultMod },
|
||||
{ "modId", modId }
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
var downloadButton = installMusicContainer.GetOrNull<ButtonWidget>("DOWNLOAD_MUSIC_BUTTON");
|
||||
if (downloadButton != null)
|
||||
{
|
||||
var installData = ModMetadata.AllMods[modId].Content;
|
||||
downloadButton.IsDisabled = () => string.IsNullOrEmpty(installData.MusicPackageMirrorList);
|
||||
downloadButton.OnClick = () =>
|
||||
{
|
||||
Ui.OpenWindow("INSTALL_DOWNLOAD_PANEL", new WidgetArgs
|
||||
{
|
||||
{ "afterInstall", loadDefaultMod },
|
||||
{ "mirrorListUrl", installData.MusicPackageMirrorList },
|
||||
{ "modId", modId }
|
||||
});
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
#region Copyright & License Information
|
||||
/*
|
||||
* Copyright 2007-2016 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;
|
||||
using OpenRA.Widgets;
|
||||
|
||||
namespace OpenRA.Mods.Common.Widgets.Logic
|
||||
{
|
||||
public class ModContentDiscTooltipLogic : ChromeLogic
|
||||
{
|
||||
[ObjectCreator.UseCtor]
|
||||
public ModContentDiscTooltipLogic(Widget widget, Func<string> getText)
|
||||
{
|
||||
var discs = widget.Get<ContainerWidget>("DISCS");
|
||||
var template = discs.Get<LabelWidget>("DISC_TEMPLATE");
|
||||
discs.RemoveChildren();
|
||||
|
||||
var desc = widget.Get<LabelWidget>("DESCRIPTION");
|
||||
|
||||
var font = Game.Renderer.Fonts[template.Font];
|
||||
var discTitles = getText().Split('\n');
|
||||
|
||||
var maxWidth = 0;
|
||||
var sideMargin = desc.Bounds.X;
|
||||
var bottomMargin = discs.Bounds.Height;
|
||||
foreach (var disc in discTitles)
|
||||
{
|
||||
var label = (LabelWidget)template.Clone();
|
||||
var title = disc;
|
||||
label.GetText = () => title;
|
||||
label.Bounds.Y = discs.Bounds.Height;
|
||||
label.Bounds.Width = font.Measure(disc).X;
|
||||
|
||||
maxWidth = Math.Max(maxWidth, label.Bounds.Width + label.Bounds.X);
|
||||
discs.AddChild(label);
|
||||
discs.Bounds.Height += label.Bounds.Height;
|
||||
}
|
||||
|
||||
widget.Bounds.Width = 2 * sideMargin + maxWidth;
|
||||
widget.Bounds.Height = discs.Bounds.Y + bottomMargin + discs.Bounds.Height;
|
||||
}
|
||||
}
|
||||
}
|
||||
127
OpenRA.Mods.Common/Widgets/Logic/Installation/ModContentLogic.cs
Normal file
127
OpenRA.Mods.Common/Widgets/Logic/Installation/ModContentLogic.cs
Normal file
@@ -0,0 +1,127 @@
|
||||
#region Copyright & License Information
|
||||
/*
|
||||
* Copyright 2007-2016 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;
|
||||
using OpenRA.Widgets;
|
||||
|
||||
namespace OpenRA.Mods.Common.Widgets.Logic
|
||||
{
|
||||
public class ModContentLogic : ChromeLogic
|
||||
{
|
||||
readonly ModContent content;
|
||||
readonly ScrollPanelWidget scrollPanel;
|
||||
readonly Widget template;
|
||||
bool discAvailable;
|
||||
|
||||
[ObjectCreator.UseCtor]
|
||||
public ModContentLogic(Widget widget, string modId, Action onCancel)
|
||||
{
|
||||
var panel = widget.Get("CONTENT_PANEL");
|
||||
|
||||
content = ModMetadata.AllMods[modId].ModContent;
|
||||
scrollPanel = panel.Get<ScrollPanelWidget>("PACKAGES");
|
||||
template = scrollPanel.Get<ContainerWidget>("PACKAGE_TEMPLATE");
|
||||
|
||||
var headerTemplate = panel.Get<LabelWidget>("HEADER_TEMPLATE");
|
||||
var headerLines = !string.IsNullOrEmpty(content.HeaderMessage) ? content.HeaderMessage.Replace("\\n", "\n").Split('\n') : new string[0];
|
||||
var headerHeight = 0;
|
||||
foreach (var l in headerLines)
|
||||
{
|
||||
var line = (LabelWidget)headerTemplate.Clone();
|
||||
line.GetText = () => l;
|
||||
line.Bounds.Y += headerHeight;
|
||||
panel.AddChild(line);
|
||||
|
||||
headerHeight += headerTemplate.Bounds.Height;
|
||||
}
|
||||
|
||||
panel.Bounds.Height += headerHeight;
|
||||
panel.Bounds.Y -= headerHeight / 2;
|
||||
scrollPanel.Bounds.Y += headerHeight;
|
||||
|
||||
var discButton = panel.Get<ButtonWidget>("CHECK_DISC_BUTTON");
|
||||
discButton.Bounds.Y += headerHeight;
|
||||
discButton.IsVisible = () => discAvailable;
|
||||
|
||||
discButton.OnClick = () => Ui.OpenWindow("DISC_INSTALL_PANEL", new WidgetArgs
|
||||
{
|
||||
{ "afterInstall", () => { } },
|
||||
{ "content", content }
|
||||
});
|
||||
|
||||
var backButton = panel.Get<ButtonWidget>("BACK_BUTTON");
|
||||
backButton.Bounds.Y += headerHeight;
|
||||
backButton.OnClick = () => { Ui.CloseWindow(); onCancel(); };
|
||||
|
||||
PopulateContentList();
|
||||
Game.RunAfterTick(Ui.ResetTooltips);
|
||||
}
|
||||
|
||||
public override void BecameVisible()
|
||||
{
|
||||
PopulateContentList();
|
||||
}
|
||||
|
||||
void PopulateContentList()
|
||||
{
|
||||
scrollPanel.RemoveChildren();
|
||||
|
||||
foreach (var p in content.Packages)
|
||||
{
|
||||
var container = template.Clone();
|
||||
var titleWidget = container.Get<LabelWidget>("TITLE");
|
||||
var title = p.Value.Title;
|
||||
titleWidget.GetText = () => title;
|
||||
|
||||
var requiredWidget = container.Get<LabelWidget>("REQUIRED");
|
||||
requiredWidget.IsVisible = () => p.Value.Required;
|
||||
|
||||
var discWidget = container.Get<ImageWidget>("DISC");
|
||||
var discs = p.Value.Discs.Select(s => content.Discs[s].Title).Distinct();
|
||||
var discList = discs.JoinWith("\n");
|
||||
var discAvailable = discs.Any();
|
||||
discWidget.GetTooltipText = () => discList;
|
||||
discWidget.IsVisible = () => discAvailable;
|
||||
|
||||
var installed = p.Value.IsInstalled();
|
||||
var downloadButton = container.Get<ButtonWidget>("DOWNLOAD");
|
||||
var downloadEnabled = !installed && p.Value.Download != null;
|
||||
downloadButton.IsVisible = () => downloadEnabled;
|
||||
|
||||
if (downloadEnabled)
|
||||
{
|
||||
var download = content.Downloads[p.Value.Download];
|
||||
var widgetArgs = new WidgetArgs
|
||||
{
|
||||
{ "download", download },
|
||||
{ "onSuccess", () => { } }
|
||||
};
|
||||
|
||||
downloadButton.OnClick = () => Ui.OpenWindow("PACKAGE_DOWNLOAD_PANEL", widgetArgs);
|
||||
}
|
||||
|
||||
var installedWidget = container.Get<LabelWidget>("INSTALLED");
|
||||
installedWidget.IsVisible = () => installed;
|
||||
|
||||
var requiresDiscWidget = container.Get<LabelWidget>("REQUIRES_DISC");
|
||||
requiresDiscWidget.IsVisible = () => !installed && !downloadEnabled;
|
||||
if (!discAvailable)
|
||||
requiresDiscWidget.GetText = () => "Manual Install";
|
||||
|
||||
scrollPanel.AddChild(container);
|
||||
}
|
||||
|
||||
discAvailable = content.Packages.Values.Any(p => p.Discs.Any() && !p.IsInstalled());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
#region Copyright & License Information
|
||||
/*
|
||||
* Copyright 2007-2016 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 OpenRA.Widgets;
|
||||
|
||||
namespace OpenRA.Mods.Common.Widgets.Logic
|
||||
{
|
||||
public class ModContentPromptLogic : ChromeLogic
|
||||
{
|
||||
[ObjectCreator.UseCtor]
|
||||
public ModContentPromptLogic(Widget widget, string modId, Action continueLoading)
|
||||
{
|
||||
var panel = widget.Get("CONTENT_PROMPT_PANEL");
|
||||
|
||||
var mod = ModMetadata.AllMods[modId];
|
||||
var content = ModMetadata.AllMods[modId].ModContent;
|
||||
|
||||
var headerTemplate = panel.Get<LabelWidget>("HEADER_TEMPLATE");
|
||||
var headerLines = !string.IsNullOrEmpty(content.InstallPromptMessage) ? content.InstallPromptMessage.Replace("\\n", "\n").Split('\n') : new string[0];
|
||||
var headerHeight = 0;
|
||||
foreach (var l in headerLines)
|
||||
{
|
||||
var line = (LabelWidget)headerTemplate.Clone();
|
||||
line.GetText = () => l;
|
||||
line.Bounds.Y += headerHeight;
|
||||
panel.AddChild(line);
|
||||
|
||||
headerHeight += headerTemplate.Bounds.Height;
|
||||
}
|
||||
|
||||
panel.Bounds.Height += headerHeight;
|
||||
panel.Bounds.Y -= headerHeight / 2;
|
||||
|
||||
var advancedButton = panel.Get<ButtonWidget>("ADVANCED_BUTTON");
|
||||
advancedButton.Bounds.Y += headerHeight;
|
||||
advancedButton.OnClick = () =>
|
||||
{
|
||||
Ui.OpenWindow("CONTENT_PANEL", new WidgetArgs
|
||||
{
|
||||
{ "modId", modId },
|
||||
{ "onCancel", Ui.CloseWindow }
|
||||
});
|
||||
};
|
||||
|
||||
var quickButton = panel.Get<ButtonWidget>("QUICK_BUTTON");
|
||||
quickButton.IsVisible = () => !string.IsNullOrEmpty(content.QuickDownload);
|
||||
quickButton.Bounds.Y += headerHeight;
|
||||
quickButton.OnClick = () =>
|
||||
{
|
||||
Ui.OpenWindow("PACKAGE_DOWNLOAD_PANEL", new WidgetArgs
|
||||
{
|
||||
{ "download", content.Downloads[content.QuickDownload] },
|
||||
{ "onSuccess", continueLoading }
|
||||
});
|
||||
};
|
||||
|
||||
var backButton = panel.Get<ButtonWidget>("BACK_BUTTON");
|
||||
backButton.Bounds.Y += headerHeight;
|
||||
backButton.OnClick = Ui.CloseWindow;
|
||||
Game.RunAfterTick(Ui.ResetTooltips);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -27,8 +27,6 @@ namespace OpenRA.Mods.Common.Widgets.Logic
|
||||
readonly ModMetadata[] allMods;
|
||||
readonly Dictionary<string, Sprite> previews = new Dictionary<string, Sprite>();
|
||||
readonly Dictionary<string, Sprite> logos = new Dictionary<string, Sprite>();
|
||||
readonly Cache<ModMetadata, bool> modInstallStatus;
|
||||
readonly Cache<string, bool> modPrerequisitesFulfilled;
|
||||
readonly Widget modChooserPanel;
|
||||
readonly ButtonWidget loadButton;
|
||||
readonly SheetBuilder sheetBuilder;
|
||||
@@ -40,14 +38,24 @@ namespace OpenRA.Mods.Common.Widgets.Logic
|
||||
[ObjectCreator.UseCtor]
|
||||
public ModBrowserLogic(Widget widget, ModData modData)
|
||||
{
|
||||
modInstallStatus = new Cache<ModMetadata, bool>(IsModInstalled);
|
||||
modPrerequisitesFulfilled = new Cache<string, bool>(Game.IsModInstalled);
|
||||
|
||||
modChooserPanel = widget;
|
||||
loadButton = modChooserPanel.Get<ButtonWidget>("LOAD_BUTTON");
|
||||
loadButton.OnClick = () => LoadMod(selectedMod);
|
||||
loadButton.IsDisabled = () => selectedMod.Id == modData.Manifest.Mod.Id;
|
||||
|
||||
var contentButton = modChooserPanel.Get<ButtonWidget>("CONFIGURE_BUTTON");
|
||||
contentButton.IsDisabled = () => selectedMod.ModContent == null;
|
||||
contentButton.OnClick = () =>
|
||||
{
|
||||
var widgetArgs = new WidgetArgs
|
||||
{
|
||||
{ "modId", selectedMod.Id },
|
||||
{ "onCancel", () => { } }
|
||||
};
|
||||
|
||||
Ui.OpenWindow("CONTENT_PANEL", widgetArgs);
|
||||
};
|
||||
|
||||
modChooserPanel.Get<ButtonWidget>("QUIT_BUTTON").OnClick = Game.Exit;
|
||||
|
||||
modList = modChooserPanel.Get("MOD_LIST");
|
||||
@@ -160,14 +168,11 @@ namespace OpenRA.Mods.Common.Widgets.Logic
|
||||
var selectedIndex = Array.IndexOf(allMods, mod);
|
||||
if (selectedIndex - modOffset > 4)
|
||||
modOffset = selectedIndex - 4;
|
||||
|
||||
loadButton.Text = !modPrerequisitesFulfilled[mod.Id] ? "Install mod" :
|
||||
modInstallStatus[mod] ? "Load Mod" : "Install Assets";
|
||||
}
|
||||
|
||||
void LoadMod(ModMetadata mod)
|
||||
{
|
||||
if (!modPrerequisitesFulfilled[mod.Id])
|
||||
if (!Game.IsModInstalled(mod.Id))
|
||||
{
|
||||
var widgetArgs = new WidgetArgs
|
||||
{
|
||||
@@ -178,17 +183,16 @@ namespace OpenRA.Mods.Common.Widgets.Logic
|
||||
return;
|
||||
}
|
||||
|
||||
if (!modInstallStatus[mod])
|
||||
if (!IsModInstalled(mod))
|
||||
{
|
||||
var widgetArgs = new WidgetArgs
|
||||
{
|
||||
{ "continueLoading", () =>
|
||||
Game.RunAfterTick(() => Game.InitializeMod(Game.Settings.Game.Mod, new Arguments())) },
|
||||
{ "mirrorListUrl", mod.Content.PackageMirrorList },
|
||||
Game.RunAfterTick(() => Game.InitializeMod(mod.Id, new Arguments())) },
|
||||
{ "modId", mod.Id }
|
||||
};
|
||||
|
||||
Ui.OpenWindow("INSTALL_PANEL", widgetArgs);
|
||||
Ui.OpenWindow("CONTENT_PROMPT_PANEL", widgetArgs);
|
||||
|
||||
return;
|
||||
}
|
||||
@@ -203,7 +207,9 @@ namespace OpenRA.Mods.Common.Widgets.Logic
|
||||
|
||||
static bool IsModInstalled(ModMetadata mod)
|
||||
{
|
||||
return mod.Content.TestFiles.All(file => File.Exists(Path.GetFullPath(Platform.ResolvePath(file))));
|
||||
return mod.ModContent.Packages
|
||||
.Where(p => p.Value.Required)
|
||||
.All(p => p.Value.TestFiles.All(f => File.Exists(Platform.ResolvePath(f))));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -103,18 +103,6 @@ namespace OpenRA.Mods.Common.Widgets.Logic
|
||||
musicSlider.OnChange += x => Game.Sound.MusicVolume = x;
|
||||
musicSlider.Value = Game.Sound.MusicVolume;
|
||||
|
||||
var installButton = widget.GetOrNull<ButtonWidget>("INSTALL_BUTTON");
|
||||
if (installButton != null)
|
||||
{
|
||||
installButton.IsDisabled = () => world.Type != WorldType.Shellmap;
|
||||
var args = new[] { "installMusic={0}".F(modData.Manifest.Mod.Id) };
|
||||
installButton.OnClick = () =>
|
||||
Game.RunAfterTick(() => Game.InitializeMod("modchooser", new Arguments(args)));
|
||||
|
||||
var installData = modData.Manifest.Get<ContentInstaller>();
|
||||
installButton.IsVisible = () => modData.DefaultRules.InstalledMusic.ToArray().Length <= installData.ShippedSoundtracks;
|
||||
}
|
||||
|
||||
var songWatcher = widget.GetOrNull<LogicTickerWidget>("SONG_WATCHER");
|
||||
if (songWatcher != null)
|
||||
{
|
||||
|
||||
@@ -34,6 +34,7 @@ namespace OpenRA.Mods.Common.Widgets
|
||||
{
|
||||
readonly Ruleset modRules;
|
||||
public int ScrollbarWidth = 24;
|
||||
public int BorderWidth = 1;
|
||||
public int TopBottomSpacing = 2;
|
||||
public int ItemSpacing = 0;
|
||||
public int ButtonDepth = ChromeMetrics.Get<int>("ButtonDepth");
|
||||
@@ -165,7 +166,7 @@ namespace OpenRA.Mods.Common.Widgets
|
||||
WidgetUtils.DrawRGBA(ChromeProvider.GetImage("scrollbar", downPressed || downDisabled ? "down_pressed" : "down_arrow"),
|
||||
new float2(downButtonRect.Left + downOffset, downButtonRect.Top + downOffset));
|
||||
|
||||
var drawBounds = backgroundRect.InflateBy(-1, -1, -1, -1);
|
||||
var drawBounds = backgroundRect.InflateBy(-BorderWidth, -BorderWidth, -BorderWidth, -BorderWidth);
|
||||
Game.Renderer.EnableScissor(drawBounds);
|
||||
|
||||
drawBounds.Offset((-ChildOrigin).ToPoint());
|
||||
|
||||
@@ -170,21 +170,27 @@ Container@LOBBY_MUSIC_BIN:
|
||||
Visible: false
|
||||
Children:
|
||||
Label@TITLE:
|
||||
Y: 75
|
||||
Y: 65
|
||||
Width: PARENT_RIGHT-24
|
||||
Height: 25
|
||||
Font: Bold
|
||||
Align: Center
|
||||
Text: No Music Available
|
||||
Text: Music Not Installed
|
||||
Label@DESCA:
|
||||
Y: 95
|
||||
Y: 85
|
||||
Width: PARENT_RIGHT-24
|
||||
Height: 25
|
||||
Align: Center
|
||||
Text: Music can be installed from the
|
||||
Label@DESCA:
|
||||
Y: 115
|
||||
Text: The game music can be installed
|
||||
Label@DESCB:
|
||||
Y: 105
|
||||
Width: PARENT_RIGHT-24
|
||||
Height: 25
|
||||
Align: Center
|
||||
Text: game Extras menu.
|
||||
Text: from the "Manage Content" menu
|
||||
Label@DESCC:
|
||||
Y: 125
|
||||
Width: PARENT_RIGHT-24
|
||||
Height: 25
|
||||
Align: Center
|
||||
Text: in the mod chooser.
|
||||
|
||||
@@ -154,15 +154,37 @@ Container@MUSIC_PANEL:
|
||||
Height: 20
|
||||
Font: Regular
|
||||
Text: Loop
|
||||
Label@NO_MUSIC_LABEL:
|
||||
Container@NO_MUSIC_LABEL:
|
||||
X: 15
|
||||
Y: 147
|
||||
Y: 120
|
||||
Width: 330
|
||||
Height: 25
|
||||
Font: Bold
|
||||
Align: Center
|
||||
Height: 80
|
||||
Visible: false
|
||||
Text: No Music Installed
|
||||
Children:
|
||||
Label@TITLE:
|
||||
Width: PARENT_RIGHT-24
|
||||
Height: 25
|
||||
Font: Bold
|
||||
Align: Center
|
||||
Text: Music Not Installed
|
||||
Label@DESCA:
|
||||
Y: 20
|
||||
Width: PARENT_RIGHT-24
|
||||
Height: 25
|
||||
Align: Center
|
||||
Text: The game music can be installed
|
||||
Label@DESCB:
|
||||
Y: 40
|
||||
Width: PARENT_RIGHT-24
|
||||
Height: 25
|
||||
Align: Center
|
||||
Text: from the "Manage Content" menu
|
||||
Label@DESCC:
|
||||
Y: 60
|
||||
Width: PARENT_RIGHT-24
|
||||
Height: 25
|
||||
Align: Center
|
||||
Text: in the mod chooser.
|
||||
Button@BACK_BUTTON:
|
||||
Key: escape
|
||||
X: 0
|
||||
@@ -170,73 +192,9 @@ Container@MUSIC_PANEL:
|
||||
Width: 140
|
||||
Height: 35
|
||||
Text: Back
|
||||
Button@INSTALL_BUTTON:
|
||||
X: 220
|
||||
Y: 399
|
||||
Width: 140
|
||||
Height: 35
|
||||
Text: Install Music
|
||||
Label@MUTE_LABEL:
|
||||
X: 165
|
||||
Y: 399
|
||||
Width: 300
|
||||
Height: 20
|
||||
Font: Small
|
||||
|
||||
Container@INSTALL_MUSIC_PANEL:
|
||||
Logic: InstallMusicLogic
|
||||
X: (WINDOW_RIGHT - WIDTH)/2
|
||||
Y: (WINDOW_BOTTOM - 150)/2
|
||||
Width: 640
|
||||
Height: 150
|
||||
Children:
|
||||
Label@TITLE:
|
||||
Width: PARENT_RIGHT
|
||||
Y: 0-25
|
||||
Font: BigBold
|
||||
Contrast: true
|
||||
Align: Center
|
||||
Text: Install Music
|
||||
Background@bg:
|
||||
Width: 640
|
||||
Height: 150
|
||||
Background: panel-black
|
||||
Children:
|
||||
Image@INSTALL:
|
||||
X: 11
|
||||
Y: 11
|
||||
ImageCollection: logos
|
||||
ImageName: install
|
||||
Label@INFO:
|
||||
X: 170
|
||||
Y: 35
|
||||
Width: PARENT_RIGHT-30
|
||||
Height: 25
|
||||
Text: The soundtrack is currently unavailable.
|
||||
Font: Bold
|
||||
Label@INFO2:
|
||||
X: 170
|
||||
Y: 70
|
||||
Width: PARENT_RIGHT-185
|
||||
Height: 25
|
||||
WordWrap: true
|
||||
Text: OpenRA can download the music files from the internet, or you can install from the original C&C CDs.
|
||||
Font: Bold
|
||||
Button@CANCEL_BUTTON:
|
||||
Key: escape
|
||||
Y: 149
|
||||
Width: 140
|
||||
Height: 35
|
||||
Text: Back
|
||||
Button@DOWNLOAD_BUTTON:
|
||||
X: 350
|
||||
Y: 149
|
||||
Width: 140
|
||||
Height: 35
|
||||
Text: Download
|
||||
Button@COPY_FROM_CD_BUTTON:
|
||||
X: 500
|
||||
Y: 149
|
||||
Width: 140
|
||||
Height: 35
|
||||
Text: Use CD
|
||||
Font: Small
|
||||
@@ -24,6 +24,7 @@ Packages:
|
||||
~movies.mix
|
||||
~scores.mix
|
||||
~scores2.mix
|
||||
~scores-covertops.mix
|
||||
~transit.mix
|
||||
cnc|bits/snow.mix
|
||||
cnc|bits
|
||||
@@ -143,19 +144,6 @@ LoadScreen: CncLoadScreen
|
||||
Image: cnc|uibits/chrome.png
|
||||
Text: Loading
|
||||
|
||||
ContentInstaller:
|
||||
TestFiles: ^Content/cnc/conquer.mix, ^Content/cnc/desert.mix, ^Content/cnc/sounds.mix, ^Content/cnc/speech.mix, ^Content/cnc/temperat.mix, ^Content/cnc/tempicnh.mix, ^Content/cnc/winter.mix
|
||||
PackageMirrorList: http://www.openra.net/packages/cnc-mirrors.txt
|
||||
DiskTestFiles: conquer.mix, desert.mix, install/setup.z
|
||||
PackageToExtractFromCD: install/setup.z
|
||||
ExtractFilesFromCD:
|
||||
.: C&C95\SPEECH.MIX, C&C95\TEMPICNH.MIX, C&C95\TRANSIT.MIX
|
||||
CopyFilesFromCD:
|
||||
.: conquer.mix, desert.mix, general.mix, scores.mix, sounds.mix, temperat.mix, winter.mix
|
||||
ShippedSoundtracks: 4
|
||||
MusicPackageMirrorList: http://www.openra.net/packages/cnc-music-mirrors.txt
|
||||
InstallShieldCABFileIds: 66, 45, 42, 65, 68, 67, 71, 47, 49, 60, 75, 73, 53
|
||||
|
||||
ServerTraits:
|
||||
LobbyCommands
|
||||
PlayerPinger
|
||||
@@ -232,3 +220,155 @@ GameSpeeds:
|
||||
OrderLatency: 6
|
||||
|
||||
ColorValidator:
|
||||
|
||||
ModContent:
|
||||
InstallPromptMessage: Tiberian Dawn requires artwork and audio from the original game.\n\nQuick Install will automatically download this content (without music\nor videos) from a mirror of the 2007 C&C Gold freeware release.\n\nAdvanced Install includes options for downloading the music and for\ncopying the videos and other content from an original game disc.
|
||||
QuickDownload: basefiles
|
||||
HeaderMessage: The original game content may be copied from an original game disc,\nor downloaded from an online mirror of the 2007 freeware release.
|
||||
Packages:
|
||||
base: Base Game Files
|
||||
TestFiles: ^Content/cnc/conquer.mix, ^Content/cnc/desert.mix, ^Content/cnc/sounds.mix, ^Content/cnc/speech.mix, ^Content/cnc/temperat.mix, ^Content/cnc/tempicnh.mix, ^Content/cnc/winter.mix
|
||||
Discs: gdi95, gdi95-linux, nod95, nod95-linux
|
||||
Required: true
|
||||
Download: basefiles
|
||||
music: Base Game Music
|
||||
TestFiles: ^Content/cnc/scores.mix
|
||||
Discs: gdi95, gdi95-linux, nod95, nod95-linux
|
||||
Download: music
|
||||
movies-gdi: GDI Campaign Briefings
|
||||
TestFiles: ^Content/cnc/movies-gdi.mix
|
||||
Discs: gdi95, gdi95-linux
|
||||
movies-nod: Nod Campaign Briefings
|
||||
TestFiles: ^Content/cnc/movies-nod.mix
|
||||
Discs: nod95, nod95-linux
|
||||
music-covertops: Covert Operations Music
|
||||
TestFiles: ^Content/cnc/scores-covertops.mix
|
||||
Discs: covertops, covertops-linux
|
||||
Downloads:
|
||||
basefiles: Base Freeware Content
|
||||
MirrorList: http://www.openra.net/packages/cnc-mirrors.txt
|
||||
Extract:
|
||||
^Content/cnc/conquer.mix: conquer.mix
|
||||
^Content/cnc/desert.mix: desert.mix
|
||||
^Content/cnc/general.mix: general.mix
|
||||
^Content/cnc/sounds.mix: sounds.mix
|
||||
^Content/cnc/speech.mix: speech.mix
|
||||
^Content/cnc/temperat.mix: temperat.mix
|
||||
^Content/cnc/tempicnh.mix: tempicnh.mix
|
||||
^Content/cnc/transit.mix: transit.mix
|
||||
^Content/cnc/winter.mix: winter.mix
|
||||
music: Freeware Music
|
||||
MirrorList: http://www.openra.net/packages/cnc-music-mirrors.txt
|
||||
Extract:
|
||||
^Content/cnc/scores.mix: scores.mix
|
||||
Discs:
|
||||
gdi95: C&C Gold (GDI Disc, English)
|
||||
IDFiles:
|
||||
DISK.WAV: 8bef9a6687c0fe0afd57c6561df58fa6e64f145d
|
||||
CONQUER.MIX: 833e02a09aae694659eb312d3838367f681d1b30
|
||||
Install:
|
||||
copy: .
|
||||
^Content/cnc/conquer.mix: CONQUER.MIX
|
||||
^Content/cnc/desert.mix: DESERT.MIX
|
||||
^Content/cnc/general.mix: GENERAL.MIX
|
||||
^Content/cnc/scores.mix: SCORES.MIX
|
||||
^Content/cnc/sounds.mix: SOUNDS.MIX
|
||||
^Content/cnc/temperat.mix: TEMPERAT.MIX
|
||||
^Content/cnc/winter.mix: WINTER.MIX
|
||||
^Content/cnc/movies-gdi.mix: MOVIES.MIX
|
||||
extract-blast: INSTALL/SETUP.Z
|
||||
^Content/cnc/speech.mix:
|
||||
Offset: 10203213
|
||||
Length: 603293
|
||||
^Content/cnc/tempicnh.mix:
|
||||
Offset: 10925941
|
||||
Length: 119096
|
||||
^Content/cnc/transit.mix:
|
||||
Offset: 11078017
|
||||
Length: 3724462
|
||||
gdi95-linux: C&C Gold (GDI Disc, English)
|
||||
IDFiles:
|
||||
disk.wav: 8bef9a6687c0fe0afd57c6561df58fa6e64f145d
|
||||
conquer.mix: 833e02a09aae694659eb312d3838367f681d1b30
|
||||
Install:
|
||||
copy: .
|
||||
^Content/cnc/conquer.mix: conquer.mix
|
||||
^Content/cnc/desert.mix: desert.mix
|
||||
^Content/cnc/general.mix: general.mix
|
||||
^Content/cnc/scores.mix: scores.mix
|
||||
^Content/cnc/sounds.mix: sounds.mix
|
||||
^Content/cnc/temperat.mix: temperat.mix
|
||||
^Content/cnc/winter.mix: winter.mix
|
||||
^Content/cnc/movies-gdi.mix: movies.mix
|
||||
extract-blast: install/setup.z
|
||||
^Content/cnc/speech.mix:
|
||||
Offset: 10203213
|
||||
Length: 603293
|
||||
^Content/cnc/tempicnh.mix:
|
||||
Offset: 10925941
|
||||
Length: 119096
|
||||
^Content/cnc/transit.mix:
|
||||
Offset: 11078017
|
||||
Length: 3724462
|
||||
nod95: C&C Gold (Nod Disc, English)
|
||||
IDFiles:
|
||||
DISK.WAV: 83a0235525afa5cd6096f2967e3eae032996e38c
|
||||
CONQUER.MIX: 833e02a09aae694659eb312d3838367f681d1b30
|
||||
Install:
|
||||
copy: .
|
||||
^Content/cnc/conquer.mix: CONQUER.MIX
|
||||
^Content/cnc/desert.mix: DESERT.MIX
|
||||
^Content/cnc/general.mix: GENERAL.MIX
|
||||
^Content/cnc/scores.mix: SCORES.MIX
|
||||
^Content/cnc/sounds.mix: SOUNDS.MIX
|
||||
^Content/cnc/temperat.mix: TEMPERAT.MIX
|
||||
^Content/cnc/winter.mix: WINTER.MIX
|
||||
^Content/cnc/movies-nod.mix: MOVIES.MIX
|
||||
extract-blast: INSTALL/SETUP.Z
|
||||
^Content/cnc/speech.mix:
|
||||
Offset: 10203213
|
||||
Length: 603293
|
||||
^Content/cnc/tempicnh.mix:
|
||||
Offset: 10925941
|
||||
Length: 119096
|
||||
^Content/cnc/transit.mix:
|
||||
Offset: 11078017
|
||||
Length: 3724462
|
||||
nod95-linux: C&C Gold (Nod Disc, English)
|
||||
IDFiles:
|
||||
disk.wav: 83a0235525afa5cd6096f2967e3eae032996e38c
|
||||
conquer.mix: 833e02a09aae694659eb312d3838367f681d1b30
|
||||
Install:
|
||||
copy: .
|
||||
^Content/cnc/conquer.mix: conquer.mix
|
||||
^Content/cnc/desert.mix: desert.mix
|
||||
^Content/cnc/general.mix: general.mix
|
||||
^Content/cnc/scores.mix: scores.mix
|
||||
^Content/cnc/sounds.mix: sounds.mix
|
||||
^Content/cnc/temperat.mix: temperat.mix
|
||||
^Content/cnc/winter.mix: winter.mix
|
||||
^Content/cnc/movies-nod.mix: movies.mix
|
||||
extract-blast: install/setup.z
|
||||
^Content/cnc/speech.mix:
|
||||
Offset: 10203213
|
||||
Length: 603293
|
||||
^Content/cnc/tempicnh.mix:
|
||||
Offset: 10925941
|
||||
Length: 119096
|
||||
^Content/cnc/transit.mix:
|
||||
Offset: 11078017
|
||||
Length: 3724462
|
||||
covertops: Covert Operations Expansion (English)
|
||||
IDFiles:
|
||||
GAME/GAME.DAT: be5a6c4c0a581da09e8f34a3bbf7bd17e525085c
|
||||
CONQUER.MIX: 713b53fa4c188ca9619c6bbeadbfc86513704266
|
||||
Install:
|
||||
copy: .
|
||||
^Content/cnc/scores-covertops.mix: SCORES.MIX
|
||||
covertops-linux: Covert Operations Expansion (English)
|
||||
IDFiles:
|
||||
game/game.dat: be5a6c4c0a581da09e8f34a3bbf7bd17e525085c
|
||||
conquer.mix: 713b53fa4c188ca9619c6bbeadbfc86513704266
|
||||
Install:
|
||||
copy: .
|
||||
^Content/cnc/scores-covertops.mix: scores.mix
|
||||
|
||||
2001
mods/d2k/mod.yaml
2001
mods/d2k/mod.yaml
File diff suppressed because one or more lines are too long
Binary file not shown.
|
Before Width: | Height: | Size: 64 KiB After Width: | Height: | Size: 80 KiB |
@@ -31,6 +31,17 @@ panel-thinborder: chrome.png
|
||||
corner-bl: 1,573,2,2
|
||||
corner-br: 61,573,2,2
|
||||
|
||||
panel-thinborder-light: chrome.png
|
||||
background: 643,515,58,58
|
||||
border-r: 701,515,2,58
|
||||
border-l: 641,515,2,58
|
||||
border-b: 643,573,58,2
|
||||
border-t: 643,513,58,2
|
||||
corner-tl: 641,513,2,2
|
||||
corner-tr: 701,513,2,2
|
||||
corner-bl: 641,573,2,2
|
||||
corner-br: 701,573,2,2
|
||||
|
||||
button: chrome.png
|
||||
background: 138,522,44,44
|
||||
border-r: 182,522,10,44
|
||||
@@ -53,19 +64,9 @@ button-hover: chrome.png
|
||||
corner-bl: 192,566,10,10
|
||||
corner-br: 246,566,10,10
|
||||
|
||||
# Copy of button
|
||||
button-disabled: chrome.png
|
||||
background: 138,522,44,44
|
||||
border-r: 182,522,10,44
|
||||
border-l: 128,522,10,44
|
||||
border-b: 138,566,44,10
|
||||
border-t: 138,512,44,10
|
||||
corner-tl: 128,512,10,10
|
||||
corner-tr: 182,512,10,10
|
||||
corner-bl: 128,566,10,10
|
||||
corner-br: 182,566,10,10
|
||||
Inherits: button
|
||||
|
||||
# Copy of button-highlighted-hover
|
||||
button-pressed: chrome.png
|
||||
background: 330,522,44,44
|
||||
border-r: 374,522,10,44
|
||||
@@ -89,40 +90,42 @@ button-highlighted: chrome.png
|
||||
corner-br: 310,566,10,10
|
||||
|
||||
button-highlighted-hover: chrome.png
|
||||
background: 330,522,44,44
|
||||
border-r: 374,522,10,44
|
||||
border-l: 320,522,10,44
|
||||
border-b: 330,566,44,10
|
||||
border-t: 330,512,44,10
|
||||
corner-tl: 320,512,10,10
|
||||
corner-tr: 374,512,10,10
|
||||
corner-bl: 320,566,10,10
|
||||
corner-br: 374,566,10,10
|
||||
Inherits: button-pressed
|
||||
|
||||
# Copy of button-mod-highlighted-hover
|
||||
button-highlighted-pressed: chrome.png
|
||||
background: 330,522,44,44
|
||||
border-r: 374,522,10,44
|
||||
border-l: 320,522,10,44
|
||||
border-b: 330,566,44,10
|
||||
border-t: 330,512,44,10
|
||||
corner-tl: 320,512,10,10
|
||||
corner-tr: 374,512,10,10
|
||||
corner-bl: 320,566,10,10
|
||||
corner-br: 374,566,10,10
|
||||
Inherits: button-pressed
|
||||
|
||||
|
||||
# Copy of button-mod-highlighted
|
||||
button-highlighted-disabled: chrome.png
|
||||
background: 266,522,44,44
|
||||
border-r: 310,522,10,44
|
||||
border-l: 256,522,10,44
|
||||
border-b: 266,566,44,10
|
||||
border-t: 266,512,44,10
|
||||
corner-tl: 256,512,10,10
|
||||
corner-tr: 310,512,10,10
|
||||
corner-bl: 256,566,10,10
|
||||
corner-br: 310,566,10,10
|
||||
Inherits: button-highlighted
|
||||
|
||||
button-highlighted-thin: chrome.png
|
||||
background: 522,522,44,44
|
||||
border-r: 566,522,10,44
|
||||
border-l: 512,522,10,44
|
||||
border-b: 522,566,44,10
|
||||
border-t: 522,512,44,10
|
||||
corner-tl: 512,512,10,10
|
||||
corner-tr: 566,512,10,10
|
||||
corner-bl: 512,566,10,10
|
||||
corner-br: 566,566,10,10
|
||||
|
||||
button-highlighted-thin-hover: chrome.png
|
||||
Inherits: button-highlighted-thin-pressed
|
||||
|
||||
button-highlighted-thin-pressed: chrome.png
|
||||
background: 586,522,44,44
|
||||
border-r: 630,522,10,44
|
||||
border-l: 576,522,10,44
|
||||
border-b: 586,566,44,10
|
||||
border-t: 586,512,44,10
|
||||
corner-tl: 576,512,10,10
|
||||
corner-tr: 630,512,10,10
|
||||
corner-bl: 576,566,10,10
|
||||
corner-br: 630,566,10,10
|
||||
|
||||
button-highlighted-thin-disabled: chrome.png
|
||||
Inherits: button-highlighted-thin
|
||||
|
||||
|
||||
progressbar-bg: chrome.png
|
||||
background: 453,565,56,6
|
||||
@@ -155,4 +158,26 @@ background: chrome.png
|
||||
modchooser: chrome.png
|
||||
logo: 0,576,280,128
|
||||
leftarrow:384,512,20,64
|
||||
rightarrow:404,512,20,64
|
||||
rightarrow:404,512,20,64
|
||||
cdicon: 448,512,20,20
|
||||
|
||||
scrollpanel-bg: chrome.png
|
||||
Inherits: panel-thinborder
|
||||
|
||||
scrollpanel-button: chrome.png
|
||||
Inherits: panel-thinborder
|
||||
|
||||
scrollpanel-button-hover: chrome.png
|
||||
Inherits: panel-thinborder-light
|
||||
|
||||
scrollpanel-button-disabled: chrome.png
|
||||
Inherits: panel-thinborder
|
||||
|
||||
scrollpanel-button-pressed: chrome.png
|
||||
Inherits: panel-thinborder-light
|
||||
|
||||
scrollbar: chrome.png
|
||||
down_arrow: 480,512,16,16
|
||||
down_pressed: 480,512,16,16
|
||||
up_arrow: 480,528,16,16
|
||||
up_pressed: 480,528,16,16
|
||||
|
||||
370
mods/modchooser/content.yaml
Normal file
370
mods/modchooser/content.yaml
Normal file
@@ -0,0 +1,370 @@
|
||||
Background@CONTENT_PANEL:
|
||||
Logic: ModContentLogic
|
||||
X: (WINDOW_RIGHT - WIDTH) / 2
|
||||
Y: (WINDOW_BOTTOM - HEIGHT) / 2
|
||||
Width: 500
|
||||
Height: 268
|
||||
Background: panel-bg
|
||||
Children:
|
||||
Background@RULE:
|
||||
X: 30
|
||||
Y: 50
|
||||
Width: 440
|
||||
Height: 150
|
||||
Background: panel-rule
|
||||
Label@TITLE:
|
||||
X: 0
|
||||
Y: 12
|
||||
Width: PARENT_RIGHT
|
||||
Height: 25
|
||||
Text: Manage Content
|
||||
Align: Center
|
||||
Font: MediumBold
|
||||
Label@HEADER_TEMPLATE:
|
||||
Y: 65
|
||||
Width: PARENT_RIGHT
|
||||
Height: 16
|
||||
Align: Center
|
||||
ScrollPanel@PACKAGES:
|
||||
X: 30
|
||||
Y: 84
|
||||
Width: PARENT_RIGHT - 60
|
||||
Height: 115
|
||||
TopBottomSpacing: 4
|
||||
ItemSpacing: 2
|
||||
BorderWidth: 2
|
||||
Children:
|
||||
Container@PACKAGE_TEMPLATE:
|
||||
X: 6
|
||||
Width: PARENT_RIGHT - 16
|
||||
Height: 23
|
||||
Children:
|
||||
Label@TITLE:
|
||||
Width: 275
|
||||
Height: 23
|
||||
Label@REQUIRED:
|
||||
X: 185
|
||||
Y: 0-2
|
||||
Width: 90
|
||||
Height: 23
|
||||
Align: Center
|
||||
Font: Bold
|
||||
TextColor: CC0000
|
||||
Text: Required
|
||||
Image@DISC:
|
||||
X: 275
|
||||
Y: 1
|
||||
Width: 20
|
||||
Height: 20
|
||||
ImageCollection: modchooser
|
||||
ImageName: cdicon
|
||||
TooltipContainer: TOOLTIP_CONTAINER
|
||||
TooltipTemplate: DISC_TOOLTIP
|
||||
Button@DOWNLOAD:
|
||||
X: 304
|
||||
Y: 0
|
||||
Width: 100
|
||||
Height: 23
|
||||
Background: button-highlighted-thin
|
||||
Text: Download
|
||||
Label@INSTALLED:
|
||||
X: 304
|
||||
Y: 0-2
|
||||
Width: 100
|
||||
Height: 23
|
||||
Align: Center
|
||||
Font: Bold
|
||||
TextColor: 00CC00
|
||||
Text: Installed
|
||||
Label@REQUIRES_DISC:
|
||||
X: 304
|
||||
Y: 0-2
|
||||
Width: 100
|
||||
Height: 23
|
||||
Align: Center
|
||||
Font: Bold
|
||||
TextColor: DDDDDD
|
||||
Text: Requires Disc
|
||||
Button@CHECK_DISC_BUTTON:
|
||||
X: 30
|
||||
Y: PARENT_BOTTOM - 52
|
||||
Background: button-highlighted
|
||||
Width: 110
|
||||
Height: 32
|
||||
Text: Detect Disc
|
||||
Font: Bold
|
||||
Button@BACK_BUTTON:
|
||||
X: PARENT_RIGHT - 140
|
||||
Y: PARENT_BOTTOM - 52
|
||||
Background: button-highlighted
|
||||
Width: 110
|
||||
Height: 32
|
||||
Text: Back
|
||||
Font: Bold
|
||||
Key: escape
|
||||
TooltipContainer@TOOLTIP_CONTAINER:
|
||||
|
||||
Background@DISC_TOOLTIP:
|
||||
Logic: ModContentDiscTooltipLogic
|
||||
Background: panel-thinborder
|
||||
Height: 25
|
||||
Children:
|
||||
Label@DESCRIPTION:
|
||||
X: 5
|
||||
Height: 23
|
||||
Font: Bold
|
||||
Text: Content available from:
|
||||
Container@DISCS:
|
||||
Y: 15
|
||||
Width: PARENT_RIGHT - 10
|
||||
Height: 7 # used as bottom margin
|
||||
Children:
|
||||
Label@DISC_TEMPLATE:
|
||||
X: 20
|
||||
Height: 14
|
||||
Font: TinyBold
|
||||
|
||||
Container@PACKAGE_DOWNLOAD_PANEL:
|
||||
Logic: DownloadPackageLogic
|
||||
X: (WINDOW_RIGHT - WIDTH) / 2
|
||||
Y: (WINDOW_BOTTOM - HEIGHT) / 2
|
||||
Width: 500
|
||||
Height: 177
|
||||
Children:
|
||||
Background:
|
||||
Width: PARENT_RIGHT
|
||||
Height: PARENT_BOTTOM
|
||||
Background: panel-bg
|
||||
Background@RULE:
|
||||
X: 30
|
||||
Y: 50
|
||||
Width: 440
|
||||
Height: 150
|
||||
Background: panel-rule
|
||||
Label@TITLE:
|
||||
X: 0
|
||||
Y: 12
|
||||
Width: PARENT_RIGHT
|
||||
Height: 25
|
||||
Align: Center
|
||||
Font: MediumBold
|
||||
ProgressBar@PROGRESS_BAR:
|
||||
X: 30
|
||||
Y: 64
|
||||
Width: PARENT_RIGHT - 60
|
||||
Height: 16
|
||||
BarMargin: 0, 0
|
||||
Label@STATUS_LABEL:
|
||||
X: 30
|
||||
Y: 85
|
||||
Width: PARENT_RIGHT - 60
|
||||
Height: 25
|
||||
Align: Left
|
||||
Button@RETRY_BUTTON:
|
||||
X: 30
|
||||
Y: PARENT_BOTTOM - 52
|
||||
Background: button-highlighted
|
||||
Width: 120
|
||||
Height: 32
|
||||
Visible: false
|
||||
Text: Retry
|
||||
Font: Bold
|
||||
Key: return
|
||||
Button@CANCEL_BUTTON:
|
||||
X: PARENT_RIGHT - 30 - WIDTH
|
||||
Y: PARENT_BOTTOM - 52
|
||||
Background: button-highlighted
|
||||
Width: 110
|
||||
Height: 32
|
||||
Text: Cancel
|
||||
Font: Bold
|
||||
Key: escape
|
||||
|
||||
Background@DISC_INSTALL_PANEL:
|
||||
Logic: InstallFromDiscLogic
|
||||
X: (WINDOW_RIGHT - WIDTH)/2
|
||||
Y: (WINDOW_BOTTOM - HEIGHT)/2
|
||||
Width: 500
|
||||
Height: 177
|
||||
Background: panel-bg
|
||||
Children:
|
||||
Background@RULE:
|
||||
X: 30
|
||||
Y: 50
|
||||
Width: 440
|
||||
Height:150
|
||||
Background: panel-rule
|
||||
Label@TITLE:
|
||||
Y: 12
|
||||
Width: PARENT_RIGHT
|
||||
Height: 25
|
||||
Align: Center
|
||||
Font: MediumBold
|
||||
Container@PROGRESS:
|
||||
Width: PARENT_RIGHT
|
||||
Height: PARENT_BOTTOM
|
||||
Visible: false
|
||||
Children:
|
||||
ProgressBar@PROGRESS_BAR:
|
||||
X: 30
|
||||
Y: 60
|
||||
Width: PARENT_RIGHT - 60
|
||||
Height: 16
|
||||
BarMargin: 0, 0
|
||||
Label@PROGRESS_MESSAGE:
|
||||
X: 30
|
||||
Y: 80
|
||||
Width: PARENT_RIGHT - 60
|
||||
Height: 25
|
||||
Align: Left
|
||||
Container@MESSAGE:
|
||||
Width: PARENT_RIGHT
|
||||
Height: PARENT_BOTTOM
|
||||
Height: 157
|
||||
Visible: false
|
||||
Children:
|
||||
Label@MESSAGE_MESSAGE:
|
||||
Y: 65
|
||||
Width: PARENT_RIGHT
|
||||
Height: 25
|
||||
Align: Center
|
||||
Container@LIST:
|
||||
Width: PARENT_RIGHT
|
||||
Height: 268
|
||||
Visible: false
|
||||
Children:
|
||||
Label@LIST_MESSAGE:
|
||||
Y: 65
|
||||
Width: PARENT_RIGHT
|
||||
Height: 16
|
||||
Align: Center
|
||||
ScrollPanel@LIST_PANEL:
|
||||
X: 30
|
||||
Y: 99
|
||||
Width: PARENT_RIGHT - 60
|
||||
Height: 100
|
||||
TopBottomSpacing: 4
|
||||
ItemSpacing: 2
|
||||
BorderWidth: 2
|
||||
Children:
|
||||
Label@LIST_TEMPLATE:
|
||||
X: 6
|
||||
Width: PARENT_RIGHT - 16
|
||||
Height: 23
|
||||
Button@PRIMARY_BUTTON:
|
||||
X: 30
|
||||
Y: PARENT_BOTTOM - 52
|
||||
Background: button-highlighted
|
||||
Width: 110
|
||||
Height: 32
|
||||
Font: Bold
|
||||
Key: return
|
||||
Button@SECONDARY_BUTTON:
|
||||
X: PARENT_RIGHT - 140
|
||||
Y: PARENT_BOTTOM - 52
|
||||
Background: button-highlighted
|
||||
Width: 110
|
||||
Height: 32
|
||||
Font: Bold
|
||||
Key: escape
|
||||
|
||||
Background@CONTENT_PROMPT_PANEL:
|
||||
Logic: ModContentPromptLogic
|
||||
X: (WINDOW_RIGHT - WIDTH)/2
|
||||
Y: (WINDOW_BOTTOM - HEIGHT)/2
|
||||
Width: 500
|
||||
Height: 140
|
||||
Background: panel-bg
|
||||
Children:
|
||||
Background@RULE:
|
||||
X: 30
|
||||
Y: 50
|
||||
Width: 440
|
||||
Height: 150
|
||||
Background: panel-rule
|
||||
Label@TITLE:
|
||||
X: 0
|
||||
Y: 12
|
||||
Width: PARENT_RIGHT
|
||||
Height: 25
|
||||
Text: Install Content
|
||||
Align: Center
|
||||
Font: MediumBold
|
||||
Label@HEADER_TEMPLATE:
|
||||
Y: 65
|
||||
Width: PARENT_RIGHT
|
||||
Height: 16
|
||||
Align: Center
|
||||
Button@ADVANCED_BUTTON:
|
||||
X: 30
|
||||
Y: PARENT_BOTTOM - 52
|
||||
Background: button-highlighted
|
||||
Width: 140
|
||||
Height: 32
|
||||
Text: Advanced Install
|
||||
Font: Bold
|
||||
Button@QUICK_BUTTON:
|
||||
X: 185
|
||||
Y: PARENT_BOTTOM - 52
|
||||
Background: button-highlighted
|
||||
Width: 110
|
||||
Height: 32
|
||||
Text: Quick Install
|
||||
Font: Bold
|
||||
Button@BACK_BUTTON:
|
||||
X: PARENT_RIGHT - WIDTH - 30
|
||||
Y: PARENT_BOTTOM - 52
|
||||
Background: button-highlighted
|
||||
Width: 110
|
||||
Height: 32
|
||||
Text: Back
|
||||
Font: Bold
|
||||
Key: escape
|
||||
|
||||
Container@INSTALL_MOD_PANEL:
|
||||
Logic: InstallModLogic
|
||||
X: (WINDOW_RIGHT - WIDTH)/2
|
||||
Y: (WINDOW_BOTTOM - HEIGHT)/2
|
||||
Width: 500
|
||||
Height: 177
|
||||
Children:
|
||||
Background:
|
||||
Width: PARENT_RIGHT
|
||||
Height: PARENT_BOTTOM
|
||||
Background: panel-bg
|
||||
Background@RULE:
|
||||
X: 30
|
||||
Y: 50
|
||||
Width: 440
|
||||
Height: 150
|
||||
Background: panel-rule
|
||||
Label@TITLE:
|
||||
X: 0
|
||||
Y: 12
|
||||
Width: PARENT_RIGHT
|
||||
Height: 25
|
||||
Text: Missing dependencies
|
||||
Align: Center
|
||||
Font: MediumBold
|
||||
Label@DESC:
|
||||
X: 0
|
||||
Y: 65
|
||||
Width: PARENT_RIGHT
|
||||
Height: 25
|
||||
Align: Center
|
||||
Text: Please fully install the following mods then try again:
|
||||
Label@MOD_LIST:
|
||||
X: 0
|
||||
Y: 85
|
||||
Width: PARENT_RIGHT
|
||||
Height: 25
|
||||
Align: Center
|
||||
Button@BACK_BUTTON:
|
||||
X: PARENT_RIGHT - 130
|
||||
Y: PARENT_BOTTOM - 52
|
||||
Background: button-highlighted
|
||||
Width: 110
|
||||
Height: 32
|
||||
Text: Back
|
||||
Font: Bold
|
||||
Key: escape
|
||||
@@ -1,309 +0,0 @@
|
||||
Container@INSTALL_PANEL:
|
||||
Logic: InstallLogic
|
||||
X: (WINDOW_RIGHT - WIDTH)/2
|
||||
Y: (WINDOW_BOTTOM - HEIGHT)/2
|
||||
Width: 500
|
||||
Height: 177
|
||||
Children:
|
||||
Background:
|
||||
Width: PARENT_RIGHT
|
||||
Height: PARENT_BOTTOM
|
||||
Background: panel-bg
|
||||
Background@RULE:
|
||||
X: 30
|
||||
Y: 50
|
||||
Width: 440
|
||||
Height:150
|
||||
Background:panel-rule
|
||||
Label@TITLE:
|
||||
X: 0
|
||||
Y: 12
|
||||
Width: PARENT_RIGHT
|
||||
Height: 25
|
||||
Text: Install Assets
|
||||
Align: Center
|
||||
Font: MediumBold
|
||||
Label@DESC1:
|
||||
X: 0
|
||||
Y: 65
|
||||
Width: PARENT_RIGHT
|
||||
Height: 25
|
||||
Align: Center
|
||||
Label@DESC2:
|
||||
X: 0
|
||||
Y: 85
|
||||
Width: PARENT_RIGHT
|
||||
Height: 25
|
||||
Text: Content can be downloaded (if available), or copied from the install CD.
|
||||
Align: Center
|
||||
Button@DOWNLOAD_BUTTON:
|
||||
X: 20
|
||||
Y: PARENT_BOTTOM - 52
|
||||
Background:button-highlighted
|
||||
Width: 110
|
||||
Height: 32
|
||||
Text: Download
|
||||
Font: Bold
|
||||
Button@INSTALL_BUTTON:
|
||||
X: 140
|
||||
Y: PARENT_BOTTOM - 52
|
||||
Background:button-highlighted
|
||||
Width: 110
|
||||
Height: 32
|
||||
Text: Use CD
|
||||
Font: Bold
|
||||
Button@BACK_BUTTON:
|
||||
X: PARENT_RIGHT - 130
|
||||
Y: PARENT_BOTTOM - 52
|
||||
Background:button-highlighted
|
||||
Width: 110
|
||||
Height: 32
|
||||
Text: Back
|
||||
Font: Bold
|
||||
Key: escape
|
||||
|
||||
Container@INSTALL_DOWNLOAD_PANEL:
|
||||
Logic: DownloadPackagesLogic
|
||||
X: (WINDOW_RIGHT - WIDTH)/2
|
||||
Y: (WINDOW_BOTTOM - HEIGHT)/2
|
||||
Width: 500
|
||||
Height: 177
|
||||
Children:
|
||||
Background:
|
||||
Width: PARENT_RIGHT
|
||||
Height: PARENT_BOTTOM
|
||||
Background: panel-bg
|
||||
Background@RULE:
|
||||
X: 30
|
||||
Y: 50
|
||||
Width: 440
|
||||
Height:150
|
||||
Background:panel-rule
|
||||
Label@TITLE:
|
||||
X: 0
|
||||
Y: 12
|
||||
Width: PARENT_RIGHT
|
||||
Height: 25
|
||||
Align: Center
|
||||
Font: MediumBold
|
||||
ProgressBar@PROGRESS_BAR:
|
||||
X: 50
|
||||
Y: 64
|
||||
Width: PARENT_RIGHT - 100
|
||||
Height: 16
|
||||
BarMargin: 0, 0
|
||||
Label@STATUS_LABEL:
|
||||
X: 36
|
||||
Y: 85
|
||||
Width: PARENT_RIGHT - 100
|
||||
Height: 25
|
||||
Align: Left
|
||||
Button@RETRY_BUTTON:
|
||||
X: PARENT_RIGHT - 280
|
||||
Y: PARENT_BOTTOM - 52
|
||||
Background:button-highlighted
|
||||
Width: 120
|
||||
Height: 32
|
||||
Visible: false
|
||||
Text: Retry
|
||||
Font: Bold
|
||||
Key: return
|
||||
Button@CANCEL_BUTTON:
|
||||
X: PARENT_RIGHT - 130
|
||||
Y: PARENT_BOTTOM - 52
|
||||
Background:button-highlighted
|
||||
Width: 110
|
||||
Height: 32
|
||||
Text: Cancel
|
||||
Font: Bold
|
||||
Key: escape
|
||||
|
||||
Container@INSTALL_FROMCD_PANEL:
|
||||
Logic: InstallFromCDLogic
|
||||
X: (WINDOW_RIGHT - WIDTH)/2
|
||||
Y: (WINDOW_BOTTOM - HEIGHT)/2
|
||||
Width: 500
|
||||
Height: 177
|
||||
Children:
|
||||
Background:
|
||||
Width: PARENT_RIGHT
|
||||
Height: PARENT_BOTTOM
|
||||
Background: panel-bg
|
||||
Background@RULE:
|
||||
X: 30
|
||||
Y: 50
|
||||
Width: 440
|
||||
Height:150
|
||||
Background:panel-rule
|
||||
Label@TITLE:
|
||||
X: 0
|
||||
Y: 12
|
||||
Width: PARENT_RIGHT
|
||||
Height: 25
|
||||
Text: Fetching assets from CD...
|
||||
Align: Center
|
||||
Font: MediumBold
|
||||
Container@INSTALLING:
|
||||
Width: PARENT_RIGHT
|
||||
Height: PARENT_BOTTOM
|
||||
Visible: false
|
||||
Children:
|
||||
ProgressBar@PROGRESS_BAR:
|
||||
X: 50
|
||||
Y: 60
|
||||
Width: PARENT_RIGHT - 100
|
||||
Height: 16
|
||||
BarMargin: 0, 0
|
||||
Label@STATUS_LABEL:
|
||||
X: 36
|
||||
Y: 80
|
||||
Width: PARENT_RIGHT - 100
|
||||
Height: 25
|
||||
Align: Left
|
||||
Container@INSERT_DISK:
|
||||
Width: PARENT_RIGHT
|
||||
Height: PARENT_BOTTOM
|
||||
Visible: false
|
||||
Children:
|
||||
Label@INFO1:
|
||||
Y: 65
|
||||
Width: PARENT_RIGHT
|
||||
Height: 25
|
||||
Text: Disk not found.
|
||||
Align: Center
|
||||
Label@INFO2:
|
||||
Y: 85
|
||||
Width: PARENT_RIGHT
|
||||
Height: 25
|
||||
Align: Center
|
||||
Button@RETRY_BUTTON:
|
||||
X: 20
|
||||
Y: PARENT_BOTTOM - 52
|
||||
Background:button-highlighted
|
||||
Width: 110
|
||||
Height: 32
|
||||
Text: Retry
|
||||
Font: Bold
|
||||
Key: return
|
||||
Button@BACK_BUTTON:
|
||||
X: PARENT_RIGHT - 130
|
||||
Y: PARENT_BOTTOM - 52
|
||||
Background:button-highlighted
|
||||
Width: 110
|
||||
Height: 32
|
||||
Text: Back
|
||||
Font: Bold
|
||||
Key: escape
|
||||
|
||||
|
||||
Container@INSTALL_MUSIC_PANEL:
|
||||
Logic: InstallMusicLogic
|
||||
X: (WINDOW_RIGHT - WIDTH)/2
|
||||
Y: (WINDOW_BOTTOM - HEIGHT)/2
|
||||
Width: 500
|
||||
Height: 177
|
||||
Children:
|
||||
Background:
|
||||
Width: PARENT_RIGHT
|
||||
Height: PARENT_BOTTOM
|
||||
Background: panel-bg
|
||||
Background@RULE:
|
||||
X: 30
|
||||
Y: 50
|
||||
Width: 440
|
||||
Height:150
|
||||
Background:panel-rule
|
||||
Label@TITLE:
|
||||
X: 0
|
||||
Y: 12
|
||||
Width: PARENT_RIGHT
|
||||
Height: 25
|
||||
Text: Install Music
|
||||
Align: Center
|
||||
Font: MediumBold
|
||||
Label@DESC1:
|
||||
X: 0
|
||||
Y: 65
|
||||
Width: PARENT_RIGHT
|
||||
Height: 25
|
||||
Text: OpenRA can download the music files from the internet (if available),
|
||||
Align: Center
|
||||
Label@DESC2:
|
||||
X: 0
|
||||
Y: 85
|
||||
Width: PARENT_RIGHT
|
||||
Height: 25
|
||||
Text: or you can install them from an original CD.
|
||||
Align: Center
|
||||
Button@DOWNLOAD_MUSIC_BUTTON:
|
||||
X: 20
|
||||
Y: PARENT_BOTTOM - 52
|
||||
Background:button-highlighted
|
||||
Width: 110
|
||||
Height: 32
|
||||
Text: Download
|
||||
Font: Bold
|
||||
Button@INSTALL_MUSIC_BUTTON:
|
||||
X: 140
|
||||
Y: PARENT_BOTTOM - 52
|
||||
Background:button-highlighted
|
||||
Width: 110
|
||||
Height: 32
|
||||
Text: Use CD
|
||||
Font: Bold
|
||||
Button@BACK_BUTTON:
|
||||
X: PARENT_RIGHT - 130
|
||||
Y: PARENT_BOTTOM - 52
|
||||
Background:button-highlighted
|
||||
Width: 110
|
||||
Height: 32
|
||||
Text: Back
|
||||
Font: Bold
|
||||
Key: escape
|
||||
Container@INSTALL_MOD_PANEL:
|
||||
Logic: InstallModLogic
|
||||
X: (WINDOW_RIGHT - WIDTH)/2
|
||||
Y: (WINDOW_BOTTOM - HEIGHT)/2
|
||||
Width: 500
|
||||
Height: 177
|
||||
Children:
|
||||
Background:
|
||||
Width: PARENT_RIGHT
|
||||
Height: PARENT_BOTTOM
|
||||
Background: panel-bg
|
||||
Background@RULE:
|
||||
X: 30
|
||||
Y: 50
|
||||
Width: 440
|
||||
Height: 150
|
||||
Background: panel-rule
|
||||
Label@TITLE:
|
||||
X: 0
|
||||
Y: 12
|
||||
Width: PARENT_RIGHT
|
||||
Height: 25
|
||||
Text: Missing dependencies
|
||||
Align: Center
|
||||
Font: MediumBold
|
||||
Label@DESC:
|
||||
X: 0
|
||||
Y: 65
|
||||
Width: PARENT_RIGHT
|
||||
Height: 25
|
||||
Align: Center
|
||||
Text: Please fully install the following mods then try again:
|
||||
Label@MOD_LIST:
|
||||
X: 0
|
||||
Y: 85
|
||||
Width: PARENT_RIGHT
|
||||
Height: 25
|
||||
Align: Center
|
||||
Button@BACK_BUTTON:
|
||||
X: PARENT_RIGHT - 130
|
||||
Y: PARENT_BOTTOM - 52
|
||||
Background: button-highlighted
|
||||
Width: 110
|
||||
Height: 32
|
||||
Text: Back
|
||||
Font: Bold
|
||||
Key: escape
|
||||
@@ -22,7 +22,7 @@ Assemblies:
|
||||
|
||||
ChromeLayout:
|
||||
modchooser|modchooser.yaml
|
||||
modchooser|install.yaml
|
||||
modchooser|content.yaml
|
||||
|
||||
Notifications:
|
||||
modchooser|notifications.yaml
|
||||
|
||||
@@ -1,162 +1,169 @@
|
||||
Background@MODCHOOSER_BACKGROUND:
|
||||
Background: background
|
||||
Width:WINDOW_RIGHT
|
||||
Height:WINDOW_BOTTOM
|
||||
Width: WINDOW_RIGHT
|
||||
Height: WINDOW_BOTTOM
|
||||
|
||||
Background@MODCHOOSER_DIALOG:
|
||||
Logic:ModBrowserLogic
|
||||
Logic: ModBrowserLogic
|
||||
Children:
|
||||
Container:
|
||||
X:(WINDOW_RIGHT - WIDTH)/2
|
||||
Y:(WINDOW_BOTTOM - 500)/2
|
||||
Width:750
|
||||
Height:550-4-32
|
||||
X: (WINDOW_RIGHT - WIDTH) / 2
|
||||
Y: (WINDOW_BOTTOM - 500) / 2
|
||||
Width: 750
|
||||
Height: 514
|
||||
Children:
|
||||
Background@DIALOG_BACKGROUND:
|
||||
Y:69
|
||||
Width:PARENT_RIGHT
|
||||
Height:PARENT_BOTTOM - 69
|
||||
Background:panel-bg
|
||||
Y: 69
|
||||
Width: PARENT_RIGHT
|
||||
Height: PARENT_BOTTOM - 69
|
||||
Background: panel-bg
|
||||
Children:
|
||||
Label:
|
||||
X:53
|
||||
Y:30
|
||||
Align:Left
|
||||
Font:MediumBold
|
||||
Text:Choose your Battlefield:
|
||||
X: 53
|
||||
Y: 30
|
||||
Align: Left
|
||||
Font: MediumBold
|
||||
Text: Choose your Battlefield:
|
||||
Container@MOD_LIST:
|
||||
X:53
|
||||
Y:60
|
||||
Width:PARENT_RIGHT-106
|
||||
Height:150
|
||||
X: 53
|
||||
Y: 60
|
||||
Width: PARENT_RIGHT-106
|
||||
Height: 150
|
||||
Children:
|
||||
Button@MOD_TEMPLATE:
|
||||
X:16
|
||||
Width:114
|
||||
Height:114
|
||||
TooltipContainer:TOOLTIP_CONTAINER
|
||||
IgnoreChildMouseOver:true
|
||||
X: 16
|
||||
Width: 114
|
||||
Height: 114
|
||||
TooltipContainer: TOOLTIP_CONTAINER
|
||||
IgnoreChildMouseOver: true
|
||||
Children:
|
||||
Container@MOD_NO_LOGO:
|
||||
Width:PARENT_RIGHT
|
||||
Height:PARENT_BOTTOM
|
||||
Width: PARENT_RIGHT
|
||||
Height: PARENT_BOTTOM
|
||||
Children:
|
||||
Label@A:
|
||||
Width:PARENT_RIGHT
|
||||
Height:PARENT_BOTTOM-20
|
||||
Text:Missing or
|
||||
Align:center
|
||||
Width: PARENT_RIGHT
|
||||
Height: PARENT_BOTTOM-20
|
||||
Text: Missing or
|
||||
Align: Center
|
||||
Label@B:
|
||||
Y:20
|
||||
Width:PARENT_RIGHT
|
||||
Height:PARENT_BOTTOM-20
|
||||
Text:invalid logo
|
||||
Align:center
|
||||
Y: 20
|
||||
Width: PARENT_RIGHT
|
||||
Height: PARENT_BOTTOM-20
|
||||
Text: invalid logo
|
||||
Align: Center
|
||||
RGBASprite@MOD_LOGO:
|
||||
X:9
|
||||
Y:9
|
||||
X: 9
|
||||
Y: 9
|
||||
Button@PREV_MOD:
|
||||
X:15
|
||||
Y:60+41-16
|
||||
Width:25
|
||||
Height:64
|
||||
IgnoreChildMouseOver:true
|
||||
X: 15
|
||||
Y: 85
|
||||
Width: 25
|
||||
Height: 64
|
||||
IgnoreChildMouseOver: true
|
||||
Children:
|
||||
Image:
|
||||
X:2
|
||||
ImageCollection:modchooser
|
||||
ImageName:leftarrow
|
||||
X: 2
|
||||
ImageCollection: modchooser
|
||||
ImageName: leftarrow
|
||||
Button@NEXT_MOD:
|
||||
X:PARENT_RIGHT - WIDTH - 20
|
||||
Y:60+41-16
|
||||
Width:25
|
||||
Height:64
|
||||
IgnoreChildMouseOver:true
|
||||
X: PARENT_RIGHT - WIDTH - 20
|
||||
Y: 85
|
||||
Width: 25
|
||||
Height: 64
|
||||
IgnoreChildMouseOver: true
|
||||
Children:
|
||||
Image:
|
||||
X:3
|
||||
ImageCollection:modchooser
|
||||
ImageName:rightarrow
|
||||
X: 3
|
||||
ImageCollection: modchooser
|
||||
ImageName: rightarrow
|
||||
Background@RULE:
|
||||
X:53
|
||||
Y:PARENT_BOTTOM - 249
|
||||
Width:PARENT_RIGHT-106
|
||||
Height:150
|
||||
Background:panel-rule
|
||||
X: 30
|
||||
Y: PARENT_BOTTOM - 249
|
||||
Width: PARENT_RIGHT - 60
|
||||
Height: 150
|
||||
Background: panel-rule
|
||||
Label@MOD_TITLE:
|
||||
X:PARENT_RIGHT - 53 - 140 - 170
|
||||
Y:PARENT_BOTTOM-220
|
||||
Align:Left
|
||||
Font:Bold
|
||||
X: PARENT_RIGHT - 400
|
||||
Y: PARENT_BOTTOM-220
|
||||
Align: Left
|
||||
Font: Bold
|
||||
Label@MOD_AUTHOR:
|
||||
X:PARENT_RIGHT - 53 - 140 - 170
|
||||
Y:PARENT_BOTTOM-205
|
||||
Align:Left
|
||||
Font:TinyBold
|
||||
X: PARENT_RIGHT - 400
|
||||
Y: PARENT_BOTTOM-205
|
||||
Align: Left
|
||||
Font: TinyBold
|
||||
Label@MOD_VERSION:
|
||||
X:PARENT_RIGHT - 53 - 140 - 170
|
||||
Y:PARENT_BOTTOM-192
|
||||
Align:Left
|
||||
Font:Tiny
|
||||
X: PARENT_RIGHT - 400
|
||||
Y: PARENT_BOTTOM-192
|
||||
Align: Left
|
||||
Font: Tiny
|
||||
Label@MOD_DESC:
|
||||
X:PARENT_RIGHT - 53 - 140 - 170
|
||||
Y:PARENT_BOTTOM-175
|
||||
Align:Left
|
||||
VAlign:Top
|
||||
Font:Tiny
|
||||
X: PARENT_RIGHT - 400
|
||||
Y: PARENT_BOTTOM-175
|
||||
Align: Left
|
||||
VAlign: Top
|
||||
Font: Tiny
|
||||
Background@PREVIEW:
|
||||
X:53
|
||||
Y:PARENT_BOTTOM - 25 - HEIGHT
|
||||
Width:300
|
||||
Height:200
|
||||
Background:panel-thinborder
|
||||
X: 30
|
||||
Y: PARENT_BOTTOM - 25 - HEIGHT
|
||||
Width: 300
|
||||
Height: 200
|
||||
Background: panel-thinborder
|
||||
Children:
|
||||
Label:
|
||||
Width:PARENT_RIGHT
|
||||
Height:PARENT_BOTTOM
|
||||
Text:Missing or invalid preview
|
||||
Align:Center
|
||||
Width: PARENT_RIGHT
|
||||
Height: PARENT_BOTTOM
|
||||
Text: Missing or invalid preview
|
||||
Align: Center
|
||||
RGBASprite@MOD_PREVIEW:
|
||||
X:2
|
||||
Y:2
|
||||
X: 2
|
||||
Y: 2
|
||||
Button@LOAD_BUTTON:
|
||||
Background:button-highlighted
|
||||
Key:return
|
||||
X:PARENT_RIGHT - 53 - WIDTH - 170
|
||||
Y:PARENT_BOTTOM - 25 - HEIGHT
|
||||
Width:140
|
||||
Height:32
|
||||
Text:Load Mod
|
||||
X: PARENT_RIGHT - 300 - WIDTH
|
||||
Y: PARENT_BOTTOM - 25 - HEIGHT
|
||||
Width: 100
|
||||
Height: 32
|
||||
Text: Play
|
||||
Background: button-highlighted
|
||||
Key: return
|
||||
Button@CONFIGURE_BUTTON:
|
||||
X: PARENT_RIGHT - 145 - WIDTH
|
||||
Y: PARENT_BOTTOM - 25 - HEIGHT
|
||||
Width: 140
|
||||
Height: 32
|
||||
Text: Manage Content
|
||||
Background: button-highlighted
|
||||
Button@QUIT_BUTTON:
|
||||
Background:button-highlighted
|
||||
X:PARENT_RIGHT - 53 - WIDTH
|
||||
Y:PARENT_BOTTOM - 25 - HEIGHT
|
||||
Width:140
|
||||
Height:32
|
||||
Text:Quit
|
||||
X: PARENT_RIGHT - 30 - WIDTH
|
||||
Y: PARENT_BOTTOM - 25 - HEIGHT
|
||||
Width: 100
|
||||
Height: 32
|
||||
Text: Quit
|
||||
Background: button-highlighted
|
||||
Background@DIALOG_HEADER:
|
||||
Width:PARENT_RIGHT
|
||||
Height:72
|
||||
Background:panel-header
|
||||
Width: PARENT_RIGHT
|
||||
Height: 72
|
||||
Background: panel-header
|
||||
Children:
|
||||
Image:
|
||||
X:(PARENT_RIGHT - WIDTH)/2
|
||||
Y:0-28
|
||||
Width:280
|
||||
ImageCollection:modchooser
|
||||
ImageName:logo
|
||||
X: (PARENT_RIGHT - WIDTH) / 2
|
||||
Y: 0-28
|
||||
Width: 280
|
||||
ImageCollection: modchooser
|
||||
ImageName: logo
|
||||
TooltipContainer@TOOLTIP_CONTAINER:
|
||||
|
||||
Background@BUTTON_TOOLTIP:
|
||||
Logic:ButtonTooltipLogic
|
||||
Background:panel-thinborder
|
||||
Height:25
|
||||
Logic: ButtonTooltipLogic
|
||||
Background: panel-thinborder
|
||||
Height: 25
|
||||
Children:
|
||||
Label@LABEL:
|
||||
X:5
|
||||
Height:23
|
||||
Font:Bold
|
||||
X: 5
|
||||
Height: 23
|
||||
Font: Bold
|
||||
Label@HOTKEY:
|
||||
TextColor:255,255,0
|
||||
Height:23
|
||||
Font:Bold
|
||||
TextColor: FFFF00
|
||||
Height: 23
|
||||
Font: Bold
|
||||
@@ -160,21 +160,27 @@ Container@LOBBY_MUSIC_BIN:
|
||||
Visible: false
|
||||
Children:
|
||||
Label@TITLE:
|
||||
Y: 75
|
||||
Y: 65
|
||||
Width: PARENT_RIGHT-24
|
||||
Height: 25
|
||||
Font: Bold
|
||||
Align: Center
|
||||
Text: No Music Available
|
||||
Text: Music Not Installed
|
||||
Label@DESCA:
|
||||
Y: 95
|
||||
Y: 85
|
||||
Width: PARENT_RIGHT-24
|
||||
Height: 25
|
||||
Align: Center
|
||||
Text: Music can be installed from the
|
||||
Label@DESCA:
|
||||
Y: 115
|
||||
Text: The game music can be installed
|
||||
Label@DESCB:
|
||||
Y: 105
|
||||
Width: PARENT_RIGHT-24
|
||||
Height: 25
|
||||
Align: Center
|
||||
Text: game Extras menu.
|
||||
Text: from the "Manage Content" menu
|
||||
Label@DESCC:
|
||||
Y: 125
|
||||
Width: PARENT_RIGHT-24
|
||||
Height: 25
|
||||
Align: Center
|
||||
Text: in the mod chooser.
|
||||
|
||||
@@ -141,22 +141,37 @@ Background@MUSIC_PANEL:
|
||||
Width: 70
|
||||
Height: 20
|
||||
Text: Loop
|
||||
Label@NO_MUSIC_LABEL:
|
||||
Container@NO_MUSIC_LABEL:
|
||||
X: 15
|
||||
Y: 147
|
||||
Y: 135
|
||||
Width: 330
|
||||
Height: 25
|
||||
Font: Bold
|
||||
Align: Center
|
||||
Height: 80
|
||||
Visible: false
|
||||
Text: No Music Installed
|
||||
Button@INSTALL_BUTTON:
|
||||
X: 20
|
||||
Y: PARENT_BOTTOM - 45
|
||||
Width: 120
|
||||
Height: 25
|
||||
Text: Install Music
|
||||
Font: Bold
|
||||
Children:
|
||||
Label@TITLE:
|
||||
Width: PARENT_RIGHT-24
|
||||
Height: 25
|
||||
Font: Bold
|
||||
Align: Center
|
||||
Text: Music Not Installed
|
||||
Label@DESCA:
|
||||
Y: 20
|
||||
Width: PARENT_RIGHT-24
|
||||
Height: 25
|
||||
Align: Center
|
||||
Text: The game music can be installed
|
||||
Label@DESCB:
|
||||
Y: 40
|
||||
Width: PARENT_RIGHT-24
|
||||
Height: 25
|
||||
Align: Center
|
||||
Text: from the "Manage Content" menu
|
||||
Label@DESCC:
|
||||
Y: 60
|
||||
Width: PARENT_RIGHT-24
|
||||
Height: 25
|
||||
Align: Center
|
||||
Text: in the mod chooser.
|
||||
Button@BACK_BUTTON:
|
||||
X: PARENT_RIGHT - 140
|
||||
Y: PARENT_BOTTOM - 45
|
||||
@@ -171,54 +186,3 @@ Background@MUSIC_PANEL:
|
||||
Width: 300
|
||||
Height: 20
|
||||
Font: Small
|
||||
|
||||
Background@INSTALL_MUSIC_PANEL:
|
||||
Logic: InstallMusicLogic
|
||||
X: (WINDOW_RIGHT - WIDTH)/2
|
||||
Y: (WINDOW_BOTTOM - HEIGHT)/2
|
||||
Width: 500
|
||||
Height: 160
|
||||
Children:
|
||||
Label@TITLE:
|
||||
X: 0
|
||||
Y: 20
|
||||
Width: PARENT_RIGHT
|
||||
Height: 25
|
||||
Text: Install Music
|
||||
Align: Center
|
||||
Font: Bold
|
||||
Label@DESC1:
|
||||
X: 0
|
||||
Y: 50
|
||||
Width: PARENT_RIGHT
|
||||
Height: 25
|
||||
Text: The soundtrack is currently unavailable.
|
||||
Align: Center
|
||||
Label@DESC2:
|
||||
X: 0
|
||||
Y: 70
|
||||
Width: PARENT_RIGHT
|
||||
Height: 25
|
||||
Text: Please choose an installation method.
|
||||
Align: Center
|
||||
Button@COPY_FROM_CD_BUTTON:
|
||||
X: 20
|
||||
Y: PARENT_BOTTOM - 45
|
||||
Width: 110
|
||||
Height: 25
|
||||
Text: Use CD
|
||||
Font: Bold
|
||||
Button@DOWNLOAD_BUTTON:
|
||||
X: 140
|
||||
Y: PARENT_BOTTOM - 45
|
||||
Width: 110
|
||||
Height: 25
|
||||
Text: Download
|
||||
Font: Bold
|
||||
Button@CANCEL_BUTTON:
|
||||
X: PARENT_RIGHT - 130
|
||||
Y: PARENT_BOTTOM - 45
|
||||
Width: 110
|
||||
Height: 25
|
||||
Text: Back
|
||||
Font: Bold
|
||||
|
||||
214
mods/ra/mod.yaml
214
mods/ra/mod.yaml
@@ -145,20 +145,6 @@ LoadScreen: LogoStripeLoadScreen
|
||||
Image: ra|uibits/loadscreen.png
|
||||
Text: Filling Crates..., Charging Capacitors..., Reticulating Splines..., Planting Trees..., Building Bridges..., Aging Empires..., Compiling EVA..., Constructing Pylons..., Activating Skynet..., Splitting Atoms...
|
||||
|
||||
ContentInstaller:
|
||||
TestFiles: ^Content/ra/allies.mix, ^Content/ra/conquer.mix, ^Content/ra/interior.mix, ^Content/ra/redalert.mix, ^Content/ra/russian.mix, ^Content/ra/snow.mix, ^Content/ra/sounds.mix, ^Content/ra/temperat.mix
|
||||
PackageMirrorList: http://www.openra.net/packages/ra-mirrors.txt
|
||||
DiskTestFiles: MAIN.MIX, INSTALL/REDALERT.MIX
|
||||
PackageToExtractFromCD: MAIN.MIX
|
||||
ExtractFilesFromCD:
|
||||
.: conquer.mix, russian.mix, allies.mix, sounds.mix, scores.mix, snow.mix, interior.mix, temperat.mix, general.mix
|
||||
CopyFilesFromCD:
|
||||
.: INSTALL/REDALERT.MIX
|
||||
ShippedSoundtracks: 2
|
||||
MusicPackageMirrorList: http://www.openra.net/packages/ra-music-mirrors.txt
|
||||
InstallShieldCABFilePackageIds: 105
|
||||
InstallShieldCABFileIds: 116
|
||||
|
||||
ServerTraits:
|
||||
LobbyCommands
|
||||
PlayerPinger
|
||||
@@ -234,3 +220,203 @@ GameSpeeds:
|
||||
OrderLatency: 6
|
||||
|
||||
ColorValidator:
|
||||
|
||||
ModContent:
|
||||
InstallPromptMessage: Red Alert requires artwork and audio from the original game.\n\nQuick Install will automatically download this content (without music\nor videos) from a mirror of the 2008 Red Alert freeware release.\n\nAdvanced Install includes options for downloading the music and for\ncopying the videos and other content from an original game disc.
|
||||
QuickDownload: basefiles
|
||||
HeaderMessage: The original game content may be copied from an original game disc,\nor downloaded from an online mirror of the 2008 freeware release.
|
||||
Packages:
|
||||
base: Base Game Files
|
||||
TestFiles: ^Content/ra/allies.mix, ^Content/ra/conquer.mix, ^Content/ra/interior.mix, ^Content/ra/redalert.mix, ^Content/ra/russian.mix, ^Content/ra/snow.mix, ^Content/ra/sounds.mix, ^Content/ra/temperat.mix
|
||||
Discs: allied, allied-linux, soviet, soviet-linux
|
||||
Required: true
|
||||
Download: basefiles
|
||||
music: Base Game Music
|
||||
TestFiles: ^Content/ra/scores.mix
|
||||
Discs: allied, allied-linux, soviet, soviet-linux
|
||||
Download: music
|
||||
movies-allied: Allied Campaign Briefings
|
||||
TestFiles: ^Content/ra/movies1.mix
|
||||
Discs: allied, allied-linux
|
||||
movies-soviet: Soviet Campaign Briefings
|
||||
TestFiles: ^Content/ra/movies2.mix
|
||||
Discs: soviet, soviet-linux
|
||||
music-covertops: Counterstrike Music
|
||||
TestFiles: ^Content/ra/scores-counterstrike.mix
|
||||
Discs: counterstrike, counterstrike-linux
|
||||
Downloads:
|
||||
basefiles: Base Freeware Content
|
||||
MirrorList: http://www.openra.net/packages/ra-mirrors.txt
|
||||
Extract:
|
||||
^Content/ra/allies.mix: allies.mix
|
||||
^Content/ra/conquer.mix: conquer.mix
|
||||
^Content/ra/general.mix: general.mix
|
||||
^Content/ra/interior.mix: interior.mix
|
||||
^Content/ra/redalert.mix: redalert.mix
|
||||
^Content/ra/russian.mix: russian.mix
|
||||
^Content/ra/snow.mix: snow.mix
|
||||
^Content/ra/sounds.mix: sounds.mix
|
||||
^Content/ra/temperat.mix: temperat.mix
|
||||
music: Freeware Music
|
||||
MirrorList: http://www.openra.net/packages/ra-music-mirrors.txt
|
||||
Extract:
|
||||
^Content/ra/scores.mix: scores.mix
|
||||
Discs:
|
||||
allied: Red Alert 95 (Allied Disc, English)
|
||||
IDFiles:
|
||||
eahelp.GID: 13a8a4a1e7d9d6d893c38df5a39262c4689aeba5
|
||||
INSTALL/REDALERT.MIX: 0e58f4b54f44f6cd29fecf8cf379d33cf2d4caef
|
||||
Install:
|
||||
copy: INSTALL
|
||||
^Content/ra/redalert.mix: REDALERT.MIX
|
||||
extract-raw: MAIN.MIX
|
||||
^Content/ra/conquer.mix:
|
||||
Offset: 236
|
||||
Length: 2177047
|
||||
^Content/ra/interior.mix:
|
||||
Offset: 17172192
|
||||
Length: 247425
|
||||
^Content/ra/movies1.mix:
|
||||
Offset: 17419617
|
||||
Length: 369362336
|
||||
^Content/ra/scores.mix:
|
||||
Offset: 386781953
|
||||
Length: 64171360
|
||||
^Content/ra/snow.mix:
|
||||
Offset: 450953313
|
||||
Length: 1030861
|
||||
^Content/ra/sounds.mix:
|
||||
Offset: 451984174
|
||||
Length: 1006778
|
||||
^Content/ra/russian.mix:
|
||||
Offset: 452990952
|
||||
Length: 266077
|
||||
^Content/ra/allies.mix:
|
||||
Offset: 453257029
|
||||
Length: 309406
|
||||
^Content/ra/temperat.mix:
|
||||
Offset: 453566435
|
||||
Length: 1038859
|
||||
allied-linux: Red Alert 95 (Allied Disc, English)
|
||||
IDFiles:
|
||||
eahelp.gid: 13a8a4a1e7d9d6d893c38df5a39262c4689aeba5
|
||||
install/redalert.mix: 0e58f4b54f44f6cd29fecf8cf379d33cf2d4caef
|
||||
Install:
|
||||
copy: install
|
||||
^Content/ra/redalert.mix: redalert.mix
|
||||
extract-raw: main.mix
|
||||
^Content/ra/conquer.mix:
|
||||
Offset: 236
|
||||
Length: 2177047
|
||||
^Content/ra/interior.mix:
|
||||
Offset: 17172192
|
||||
Length: 247425
|
||||
^Content/ra/movies1.mix:
|
||||
Offset: 17419617
|
||||
Length: 369362336
|
||||
^Content/ra/scores.mix:
|
||||
Offset: 386781953
|
||||
Length: 64171360
|
||||
^Content/ra/snow.mix:
|
||||
Offset: 450953313
|
||||
Length: 1030861
|
||||
^Content/ra/sounds.mix:
|
||||
Offset: 451984174
|
||||
Length: 1006778
|
||||
^Content/ra/russian.mix:
|
||||
Offset: 452990952
|
||||
Length: 266077
|
||||
^Content/ra/allies.mix:
|
||||
Offset: 453257029
|
||||
Length: 309406
|
||||
^Content/ra/temperat.mix:
|
||||
Offset: 453566435
|
||||
Length: 1038859
|
||||
soviet: Red Alert 95 (Soviet Disc, English)
|
||||
IDFiles:
|
||||
automenu.apm: bb61132a492bfb37069a0139f95671da3655d916
|
||||
INSTALL/REDALERT.MIX: 0e58f4b54f44f6cd29fecf8cf379d33cf2d4caef
|
||||
Install:
|
||||
copy: INSTALL
|
||||
^Content/cnc/redalert.mix: REDALERT.MIX
|
||||
extract-raw: MAIN.MIX
|
||||
^Content/ra/conquer.mix:
|
||||
Offset: 236
|
||||
Length: 2177047
|
||||
^Content/ra/interior.mix:
|
||||
Offset: 17172192
|
||||
Length: 247425
|
||||
^Content/ra/movies2.mix:
|
||||
Offset: 17419617
|
||||
Length: 415334456
|
||||
^Content/ra/scores.mix:
|
||||
Offset: 432754073
|
||||
Length: 64171360
|
||||
^Content/ra/snow.mix:
|
||||
Offset: 496925433
|
||||
Length: 1030861
|
||||
^Content/ra/russian.mix:
|
||||
Offset: 497956294
|
||||
Length: 266077
|
||||
^Content/ra/allies.mix:
|
||||
Offset: 498222371
|
||||
Length: 309406
|
||||
^Content/ra/sounds.mix:
|
||||
Offset: 498531777
|
||||
Length: 1006778
|
||||
^Content/ra/temperat.mix:
|
||||
Offset: 499538555
|
||||
Length: 1038859
|
||||
soviet-linux: Red Alert 95 (Soviet Disc, English)
|
||||
IDFiles:
|
||||
automenu.apm: bb61132a492bfb37069a0139f95671da3655d916
|
||||
install/redalert.mix: 0e58f4b54f44f6cd29fecf8cf379d33cf2d4caef
|
||||
Install:
|
||||
copy: install
|
||||
^Content/cnc/redalert.mix: redalert.mix
|
||||
extract-raw: main.mix
|
||||
^Content/ra/conquer.mix:
|
||||
Offset: 236
|
||||
Length: 2177047
|
||||
^Content/ra/interior.mix:
|
||||
Offset: 17172192
|
||||
Length: 247425
|
||||
^Content/ra/movies2.mix:
|
||||
Offset: 17419617
|
||||
Length: 415334456
|
||||
^Content/ra/scores.mix:
|
||||
Offset: 432754073
|
||||
Length: 64171360
|
||||
^Content/ra/snow.mix:
|
||||
Offset: 496925433
|
||||
Length: 1030861
|
||||
^Content/ra/russian.mix:
|
||||
Offset: 497956294
|
||||
Length: 266077
|
||||
^Content/ra/allies.mix:
|
||||
Offset: 498222371
|
||||
Length: 309406
|
||||
^Content/ra/sounds.mix:
|
||||
Offset: 498531777
|
||||
Length: 1006778
|
||||
^Content/ra/temperat.mix:
|
||||
Offset: 499538555
|
||||
Length: 1038859
|
||||
counterstrike: Counterstrike Expansion (English)
|
||||
IDFiles:
|
||||
README.TXT: 0efe8087383f0b159a9633f891fb5f53c6097cd4
|
||||
SETUP/INSTALL/CSTRIKE.RTP: fae8ba82db71574f6ecd8fb4ff4026fcb65d2adc
|
||||
Install:
|
||||
extract-raw: MAIN.MIX
|
||||
^Content/ra/scores-counterstrike.mix:
|
||||
Offset: 144899491
|
||||
Length: 87483135
|
||||
counterstrike-linux: Counterstrike Expansion (English)
|
||||
IDFiles:
|
||||
readme.txt: 0efe8087383f0b159a9633f891fb5f53c6097cd4
|
||||
setup/install/cstrike.rtp: fae8ba82db71574f6ecd8fb4ff4026fcb65d2adc
|
||||
Install:
|
||||
extract-raw: main.mix
|
||||
^Content/ra/scores-counterstrike.mix:
|
||||
Offset: 144899491
|
||||
Length: 87483135
|
||||
|
||||
154
mods/ts/mod.yaml
154
mods/ts/mod.yaml
@@ -1,4 +1,4 @@
|
||||
Metadata:
|
||||
Metadata:
|
||||
Title: Tiberian Sun
|
||||
Description: Developer stub, not yet ready for release!
|
||||
Version: {DEV_VERSION}
|
||||
@@ -189,20 +189,6 @@ LoadScreen: LogoStripeLoadScreen
|
||||
Image: ts|uibits/loadscreen.png
|
||||
Text: Updating EVA installation..., Changing perspective...
|
||||
|
||||
ContentInstaller:
|
||||
TestFiles: ^Content/ts/cache.mix, ^Content/ts/conquer.mix, ^Content/ts/isosnow.mix, ^Content/ts/isotemp.mix, ^Content/ts/local.mix, ^Content/ts/sidec01.mix, ^Content/ts/sidec02.mix, ^Content/ts/sno.mix, ^Content/ts/snow.mix, ^Content/ts/sounds.mix, ^Content/ts/speech01.mix, ^Content/ts/tem.mix, ^Content/ts/temperat.mix
|
||||
PackageMirrorList: http://www.openra.net/packages/ts-mirrors.txt
|
||||
DiskTestFiles: MULTI.MIX, INSTALL/TIBSUN.MIX
|
||||
CopyFilesFromCD:
|
||||
.: INSTALL/TIBSUN.MIX, SCORES.MIX, MULTI.MIX
|
||||
PackageToExtractFromCD: INSTALL/TIBSUN.MIX
|
||||
ExtractFilesFromCD:
|
||||
.: cache.mix, conquer.mix, isosnow.mix, isotemp.mix, local.mix, sidec01.mix, sidec02.mix, sno.mix, snow.mix, sounds.mix, speech01.mix, tem.mix, temperat.mix
|
||||
ShippedSoundtracks: 2
|
||||
MusicPackageMirrorList: http://www.openra.net/packages/ts-music-mirrors.txt
|
||||
InstallShieldCABFilePackageIds: 332
|
||||
InstallShieldCABFileIds: 323, 364
|
||||
|
||||
ServerTraits:
|
||||
LobbyCommands
|
||||
PlayerPinger
|
||||
@@ -272,3 +258,141 @@ GameSpeeds:
|
||||
OrderLatency: 6
|
||||
|
||||
ColorValidator:
|
||||
|
||||
ModContent:
|
||||
InstallPromptMessage: Tiberian Sun requires artwork and audio from the original game.\n\nQuick Install will automatically download this content (without music\nor videos) from a mirror of the 2012 Tiberian Sun freeware release.\n\nAdvanced Install includes options for downloading the music and for\ncopying the videos and other content from an original game disc.
|
||||
QuickDownload: basefiles
|
||||
HeaderMessage: The original game content may be copied from an original game disc,\nor downloaded from an online mirror of the 2012 freeware release.
|
||||
Packages:
|
||||
base: Base Game Files
|
||||
TestFiles: ^Content/ts/cache.mix, ^Content/ts/conquer.mix, ^Content/ts/isosnow.mix, ^Content/ts/isotemp.mix, ^Content/ts/local.mix, ^Content/ts/sidec01.mix, ^Content/ts/sidec02.mix, ^Content/ts/sno.mix, ^Content/ts/snow.mix, ^Content/ts/sounds.mix, ^Content/ts/speech01.mix, ^Content/ts/tem.mix, ^Content/ts/temperat.mix
|
||||
Discs: tibsun, tibsun-linux
|
||||
Required: true
|
||||
Download: basefiles
|
||||
music: Base Game Music
|
||||
TestFiles: ^Content/ts/scores.mix
|
||||
Discs: tibsun, tibsun-linux
|
||||
Download: music
|
||||
Downloads:
|
||||
basefiles: Base Freeware Content
|
||||
MirrorList: http://www.openra.net/packages/ts-mirrors.txt
|
||||
Extract:
|
||||
^Content/ts/cache.mix: cache.mix
|
||||
^Content/ts/conquer.mix: conquer.mix
|
||||
^Content/ts/isosnow.mix: isosnow.mix
|
||||
^Content/ts/isotemp.mix: isotemp.mix
|
||||
^Content/ts/local.mix: local.mix
|
||||
^Content/ts/sidec01.mix: sidec01.mix
|
||||
^Content/ts/sidec02.mix: sidec02.mix
|
||||
^Content/ts/sno.mix: sno.mix
|
||||
^Content/ts/snow.mix: snow.mix
|
||||
^Content/ts/sounds.mix: sounds.mix
|
||||
^Content/ts/speech01.mix: speech01.mix
|
||||
^Content/ts/speech02.mix: speech02.mix
|
||||
^Content/ts/tem.mix: tem.mix
|
||||
^Content/ts/temperat.mix: temperat.mix
|
||||
music: Freeware Music
|
||||
MirrorList: http://www.openra.net/packages/ts-music-mirrors.txt
|
||||
Extract:
|
||||
^Content/ts/scores.mix: scores.mix
|
||||
Discs:
|
||||
tibsun: Tiberan Sun (GDI or Nod Disc, English)
|
||||
IDFiles:
|
||||
README.TXT: 45745c4a0c888317ec900208a426472779c42bf7
|
||||
AUTOPLAY.WAV: 2dfce5d00f98b641849c29942b651f4e98d30e30
|
||||
Install:
|
||||
copy: .
|
||||
^Content/ts/scores.mix: SCORES.MIX
|
||||
extract-raw: INSTALL/TIBSUN.MIX
|
||||
^Content/ts/cache.mix:
|
||||
Offset: 300
|
||||
Length: 169176
|
||||
^Content/ts/conquer.mix:
|
||||
Offset: 169484
|
||||
Length: 5700088
|
||||
^Content/ts/isosnow.mix:
|
||||
Offset: 5869580
|
||||
Length: 7624750
|
||||
^Content/ts/isotemp.mix:
|
||||
Offset: 13494332
|
||||
Length: 8617646
|
||||
^Content/ts/local.mix:
|
||||
Offset: 22111980
|
||||
Length: 18044736
|
||||
^Content/ts/sidec01.mix:
|
||||
Offset: 40156716
|
||||
Length: 998476
|
||||
^Content/ts/sidec02.mix:
|
||||
Offset: 41155196
|
||||
Length: 1039996
|
||||
^Content/ts/snow.mix:
|
||||
Offset: 56104508
|
||||
Length: 2087806
|
||||
^Content/ts/sno.mix:
|
||||
Offset: 58192316
|
||||
Length: 7826
|
||||
^Content/ts/sounds.mix:
|
||||
Offset: 58200156
|
||||
Length: 3224136
|
||||
^Content/ts/speech01.mix:
|
||||
Offset: 61424300
|
||||
Length: 6028236
|
||||
^Content/ts/speech02.mix:
|
||||
Offset: 67452540
|
||||
Length: 5596628
|
||||
^Content/ts/tem.mix:
|
||||
Offset: 73049180
|
||||
Length: 7746
|
||||
^Content/ts/temperat.mix:
|
||||
Offset: 73056940
|
||||
Length: 2037606
|
||||
tibsun-linux: Tiberan Sun (GDI or Nod Disc, English)
|
||||
IDFiles:
|
||||
readme.txt: 45745c4a0c888317ec900208a426472779c42bf7
|
||||
autoplay.wav: 2dfce5d00f98b641849c29942b651f4e98d30e30
|
||||
Install:
|
||||
copy: .
|
||||
^Content/ts/scores.mix: scores.mix
|
||||
extract-raw: install/tibsun.mix
|
||||
^Content/ts/cache.mix:
|
||||
Offset: 300
|
||||
Length: 169176
|
||||
^Content/ts/conquer.mix:
|
||||
Offset: 169484
|
||||
Length: 5700088
|
||||
^Content/ts/isosnow.mix:
|
||||
Offset: 5869580
|
||||
Length: 7624750
|
||||
^Content/ts/isotemp.mix:
|
||||
Offset: 13494332
|
||||
Length: 8617646
|
||||
^Content/ts/local.mix:
|
||||
Offset: 22111980
|
||||
Length: 18044736
|
||||
^Content/ts/sidec01.mix:
|
||||
Offset: 40156716
|
||||
Length: 998476
|
||||
^Content/ts/sidec02.mix:
|
||||
Offset: 41155196
|
||||
Length: 1039996
|
||||
^Content/ts/snow.mix:
|
||||
Offset: 56104508
|
||||
Length: 2087806
|
||||
^Content/ts/sno.mix:
|
||||
Offset: 58192316
|
||||
Length: 7826
|
||||
^Content/ts/sounds.mix:
|
||||
Offset: 58200156
|
||||
Length: 3224136
|
||||
^Content/ts/speech01.mix:
|
||||
Offset: 61424300
|
||||
Length: 6028236
|
||||
^Content/ts/speech02.mix:
|
||||
Offset: 67452540
|
||||
Length: 5596628
|
||||
^Content/ts/tem.mix:
|
||||
Offset: 73049180
|
||||
Length: 7746
|
||||
^Content/ts/temperat.mix:
|
||||
Offset: 73056940
|
||||
Length: 2037606
|
||||
|
||||
Reference in New Issue
Block a user