Replaced hardcoded source actions by user defined ISourceAction.
This commit is contained in:
committed by
Matthias Mailänder
parent
fcc8f53b59
commit
7188f88ba1
21
OpenRA.Mods.Common/Installer/ISourceAction.cs
Normal file
21
OpenRA.Mods.Common/Installer/ISourceAction.cs
Normal file
@@ -0,0 +1,21 @@
|
||||
#region Copyright & License Information
|
||||
/*
|
||||
* Copyright 2007-2022 The OpenRA Developers (see AUTHORS)
|
||||
* This file is part of OpenRA, which is free software. It is made
|
||||
* available to you under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation, either version 3 of
|
||||
* the License, or (at your option) any later version. For more
|
||||
* information, see COPYING.
|
||||
*/
|
||||
#endregion
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace OpenRA.Mods.Common.Installer
|
||||
{
|
||||
public interface ISourceAction
|
||||
{
|
||||
void RunActionOnSource(MiniYaml actionYaml, string path, ModData modData, List<string> extracted, Action<string> updateMessage);
|
||||
}
|
||||
}
|
||||
78
OpenRA.Mods.Common/Installer/InstallerUtils.cs
Normal file
78
OpenRA.Mods.Common/Installer/InstallerUtils.cs
Normal file
@@ -0,0 +1,78 @@
|
||||
#region Copyright & License Information
|
||||
/*
|
||||
* Copyright 2007-2022 The OpenRA Developers (see AUTHORS)
|
||||
* This file is part of OpenRA, which is free software. It is made
|
||||
* available to you under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation, either version 3 of
|
||||
* the License, or (at your option) any later version. For more
|
||||
* information, see COPYING.
|
||||
*/
|
||||
#endregion
|
||||
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using FS = OpenRA.FileSystem.FileSystem;
|
||||
|
||||
namespace OpenRA.Mods.Common.Installer
|
||||
{
|
||||
public class InstallerUtils
|
||||
{
|
||||
public static bool IsValidSourcePath(string path, ModContent.ModSource source)
|
||||
{
|
||||
try
|
||||
{
|
||||
foreach (var kv in source.IDFiles.Nodes)
|
||||
{
|
||||
var filePath = FS.ResolveCaseInsensitivePath(Path.Combine(path, kv.Key));
|
||||
if (!File.Exists(filePath))
|
||||
return false;
|
||||
|
||||
using (var fileStream = File.OpenRead(filePath))
|
||||
{
|
||||
var offsetNode = kv.Value.Nodes.FirstOrDefault(n => n.Key == "Offset");
|
||||
var lengthNode = kv.Value.Nodes.FirstOrDefault(n => n.Key == "Length");
|
||||
if (offsetNode != null || lengthNode != null)
|
||||
{
|
||||
var offset = 0L;
|
||||
if (offsetNode != null)
|
||||
offset = FieldLoader.GetValue<long>("Offset", offsetNode.Value.Value);
|
||||
|
||||
var length = fileStream.Length - offset;
|
||||
if (lengthNode != null)
|
||||
length = FieldLoader.GetValue<long>("Length", lengthNode.Value.Value);
|
||||
|
||||
fileStream.Position = offset;
|
||||
var data = fileStream.ReadBytes((int)length);
|
||||
if (CryptoUtil.SHA1Hash(data) != kv.Value.Value)
|
||||
return false;
|
||||
}
|
||||
else if (CryptoUtil.SHA1Hash(fileStream) != kv.Value.Value)
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public static void CopyStream(Stream input, Stream output, long length, Action<long> onProgress = null)
|
||||
{
|
||||
var buffer = new byte[4096];
|
||||
var copied = 0L;
|
||||
while (copied < length)
|
||||
{
|
||||
var read = (int)Math.Min(buffer.Length, length - copied);
|
||||
var write = input.Read(buffer, 0, read);
|
||||
output.Write(buffer, 0, write);
|
||||
copied += write;
|
||||
|
||||
onProgress?.Invoke(copied);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
#region Copyright & License Information
|
||||
/*
|
||||
* Copyright 2007-2022 The OpenRA Developers (see AUTHORS)
|
||||
* This file is part of OpenRA, which is free software. It is made
|
||||
* available to you under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation, either version 3 of
|
||||
* the License, or (at your option) any later version. For more
|
||||
* information, see COPYING.
|
||||
*/
|
||||
#endregion
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using OpenRA.Mods.Common.Widgets.Logic;
|
||||
using FS = OpenRA.FileSystem.FileSystem;
|
||||
|
||||
namespace OpenRA.Mods.Common.Installer
|
||||
{
|
||||
public class CopySourceAction : ISourceAction
|
||||
{
|
||||
public void RunActionOnSource(MiniYaml actionYaml, string path, ModData modData, List<string> extracted, Action<string> updateMessage)
|
||||
{
|
||||
var sourceDir = Path.Combine(path, actionYaml.Value);
|
||||
foreach (var node in actionYaml.Nodes)
|
||||
{
|
||||
var sourcePath = FS.ResolveCaseInsensitivePath(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 {sourcePath} -> {targetPath}");
|
||||
extracted.Add(targetPath);
|
||||
Directory.CreateDirectory(Path.GetDirectoryName(targetPath));
|
||||
|
||||
using (var source = File.OpenRead(sourcePath))
|
||||
using (var target = File.OpenWrite(targetPath))
|
||||
{
|
||||
var displayFilename = Path.GetFileName(targetPath);
|
||||
var length = source.Length;
|
||||
|
||||
Action<long> onProgress = null;
|
||||
if (length < InstallFromDiscLogic.ShowPercentageThreshold)
|
||||
updateMessage(modData.Translation.GetString(InstallFromDiscLogic.CopyingFilename, Translation.Arguments("filename", displayFilename)));
|
||||
else
|
||||
onProgress = b => updateMessage(modData.Translation.GetString(InstallFromDiscLogic.CopyingFilenameProgress, Translation.Arguments("filename", displayFilename, "progress", 100 * b / length)));
|
||||
|
||||
InstallerUtils.CopyStream(source, target, length, onProgress);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
#region Copyright & License Information
|
||||
/*
|
||||
* Copyright 2007-2022 The OpenRA Developers (see AUTHORS)
|
||||
* This file is part of OpenRA, which is free software. It is made
|
||||
* available to you under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation, either version 3 of
|
||||
* the License, or (at your option) any later version. For more
|
||||
* information, see COPYING.
|
||||
*/
|
||||
#endregion
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
|
||||
namespace OpenRA.Mods.Common.Installer
|
||||
{
|
||||
public class DeleteSourceAction : ISourceAction
|
||||
{
|
||||
public void RunActionOnSource(MiniYaml actionYaml, string path, ModData modData, List<string> extracted, Action<string> updateMessage)
|
||||
{
|
||||
// Yaml path must be specified relative to a named directory (e.g. ^SupportDir)
|
||||
if (!actionYaml.Value.StartsWith("^"))
|
||||
return;
|
||||
|
||||
var sourcePath = Platform.ResolvePath(actionYaml.Value);
|
||||
|
||||
Log.Write("debug", $"Deleting {sourcePath}");
|
||||
File.Delete(sourcePath);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,77 @@
|
||||
#region Copyright & License Information
|
||||
/*
|
||||
* Copyright 2007-2022 The OpenRA Developers (see AUTHORS)
|
||||
* This file is part of OpenRA, which is free software. It is made
|
||||
* available to you under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation, either version 3 of
|
||||
* the License, or (at your option) any later version. For more
|
||||
* information, see COPYING.
|
||||
*/
|
||||
#endregion
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using OpenRA.Mods.Common.FileFormats;
|
||||
using OpenRA.Mods.Common.Widgets.Logic;
|
||||
using FS = OpenRA.FileSystem.FileSystem;
|
||||
|
||||
namespace OpenRA.Mods.Common.Installer
|
||||
{
|
||||
public class ExtractBlastSourceAction : ISourceAction
|
||||
{
|
||||
public void RunActionOnSource(MiniYaml actionYaml, string path, ModData modData, List<string> extracted, Action<string> updateMessage)
|
||||
{
|
||||
// Yaml path may be specified relative to a named directory (e.g. ^SupportDir) or the detected source path
|
||||
var sourcePath = actionYaml.Value.StartsWith("^") ? Platform.ResolvePath(actionYaml.Value) : FS.ResolveCaseInsensitivePath(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);
|
||||
|
||||
extracted.Add(targetPath);
|
||||
Directory.CreateDirectory(Path.GetDirectoryName(targetPath));
|
||||
var displayFilename = Path.GetFileName(Path.GetFileName(targetPath));
|
||||
|
||||
Action<long> onProgress = null;
|
||||
if (length < InstallFromDiscLogic.ShowPercentageThreshold)
|
||||
updateMessage(modData.Translation.GetString(InstallFromDiscLogic.Extracing, Translation.Arguments("filename", displayFilename)));
|
||||
else
|
||||
onProgress = b => updateMessage(modData.Translation.GetString(InstallFromDiscLogic.ExtracingProgress, Translation.Arguments("filename", displayFilename, "progress", 100 * b / length)));
|
||||
|
||||
using (var target = File.OpenWrite(targetPath))
|
||||
{
|
||||
Log.Write("install", $"Extracting {sourcePath} -> {targetPath}");
|
||||
Blast.Decompress(source, target, (read, _) => onProgress?.Invoke(read));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,79 @@
|
||||
#region Copyright & License Information
|
||||
/*
|
||||
* Copyright 2007-2022 The OpenRA Developers (see AUTHORS)
|
||||
* This file is part of OpenRA, which is free software. It is made
|
||||
* available to you under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation, either version 3 of
|
||||
* the License, or (at your option) any later version. For more
|
||||
* information, see COPYING.
|
||||
*/
|
||||
#endregion
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using OpenRA.Mods.Common.FileFormats;
|
||||
using OpenRA.Mods.Common.Widgets.Logic;
|
||||
using FS = OpenRA.FileSystem.FileSystem;
|
||||
|
||||
namespace OpenRA.Mods.Common.Installer
|
||||
{
|
||||
public class ExtractIscabSourceAction : ISourceAction
|
||||
{
|
||||
public void RunActionOnSource(MiniYaml actionYaml, string path, ModData modData, List<string> extracted, Action<string> updateMessage)
|
||||
{
|
||||
// Yaml path may be specified relative to a named directory (e.g. ^SupportDir) or the detected source path
|
||||
var sourcePath = actionYaml.Value.StartsWith("^") ? Platform.ResolvePath(actionYaml.Value) : FS.ResolveCaseInsensitivePath(Path.Combine(path, actionYaml.Value));
|
||||
|
||||
var volumeNode = actionYaml.Nodes.FirstOrDefault(n => n.Key == "Volumes");
|
||||
if (volumeNode == null)
|
||||
throw new InvalidDataException("extract-iscab entry doesn't define a Volumes node");
|
||||
|
||||
var extractNode = actionYaml.Nodes.FirstOrDefault(n => n.Key == "Extract");
|
||||
if (extractNode == null)
|
||||
throw new InvalidDataException("extract-iscab entry doesn't define an Extract node");
|
||||
|
||||
var volumes = new Dictionary<int, Stream>();
|
||||
try
|
||||
{
|
||||
foreach (var node in volumeNode.Value.Nodes)
|
||||
{
|
||||
var volume = FieldLoader.GetValue<int>("(key)", node.Key);
|
||||
var stream = File.OpenRead(FS.ResolveCaseInsensitivePath(Path.Combine(path, node.Value.Value)));
|
||||
volumes.Add(volume, stream);
|
||||
}
|
||||
|
||||
using (var source = File.OpenRead(sourcePath))
|
||||
{
|
||||
var reader = new InstallShieldCABCompression(source, volumes);
|
||||
foreach (var node in extractNode.Value.Nodes)
|
||||
{
|
||||
var targetPath = Platform.ResolvePath(node.Key);
|
||||
|
||||
if (File.Exists(targetPath))
|
||||
{
|
||||
Log.Write("install", "Skipping installed file " + targetPath);
|
||||
continue;
|
||||
}
|
||||
|
||||
extracted.Add(targetPath);
|
||||
Directory.CreateDirectory(Path.GetDirectoryName(targetPath));
|
||||
using (var target = File.OpenWrite(targetPath))
|
||||
{
|
||||
Log.Write("install", $"Extracting {sourcePath} -> {targetPath}");
|
||||
var displayFilename = Path.GetFileName(Path.GetFileName(targetPath));
|
||||
Action<int> onProgress = percent => updateMessage(modData.Translation.GetString(InstallFromDiscLogic.ExtracingProgress, Translation.Arguments("filename", displayFilename, "progress", percent)));
|
||||
reader.ExtractFile(node.Value.Value, target, onProgress);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
foreach (var kv in volumes)
|
||||
kv.Value.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
#region Copyright & License Information
|
||||
/*
|
||||
* Copyright 2007-2022 The OpenRA Developers (see AUTHORS)
|
||||
* This file is part of OpenRA, which is free software. It is made
|
||||
* available to you under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation, either version 3 of
|
||||
* the License, or (at your option) any later version. For more
|
||||
* information, see COPYING.
|
||||
*/
|
||||
#endregion
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using OpenRA.Mods.Common.FileFormats;
|
||||
using OpenRA.Mods.Common.Widgets.Logic;
|
||||
using FS = OpenRA.FileSystem.FileSystem;
|
||||
|
||||
namespace OpenRA.Mods.Common.Installer
|
||||
{
|
||||
public class ExtractMscabSourceAction : ISourceAction
|
||||
{
|
||||
public void RunActionOnSource(MiniYaml actionYaml, string path, ModData modData, List<string> extracted, Action<string> updateMessage)
|
||||
{
|
||||
// Yaml path may be specified relative to a named directory (e.g. ^SupportDir) or the detected source path
|
||||
var sourcePath = actionYaml.Value.StartsWith("^") ? Platform.ResolvePath(actionYaml.Value) : FS.ResolveCaseInsensitivePath(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;
|
||||
}
|
||||
|
||||
extracted.Add(targetPath);
|
||||
Directory.CreateDirectory(Path.GetDirectoryName(targetPath));
|
||||
using (var target = File.OpenWrite(targetPath))
|
||||
{
|
||||
Log.Write("install", $"Extracting {sourcePath} -> {targetPath}");
|
||||
var displayFilename = Path.GetFileName(Path.GetFileName(targetPath));
|
||||
Action<int> onProgress = percent => updateMessage(modData.Translation.GetString(InstallFromDiscLogic.ExtracingProgress, Translation.Arguments("filename", displayFilename, "progress", percent)));
|
||||
reader.ExtractFile(node.Value.Value, target, onProgress);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,76 @@
|
||||
#region Copyright & License Information
|
||||
/*
|
||||
* Copyright 2007-2022 The OpenRA Developers (see AUTHORS)
|
||||
* This file is part of OpenRA, which is free software. It is made
|
||||
* available to you under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation, either version 3 of
|
||||
* the License, or (at your option) any later version. For more
|
||||
* information, see COPYING.
|
||||
*/
|
||||
#endregion
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using OpenRA.Mods.Common.Widgets.Logic;
|
||||
using FS = OpenRA.FileSystem.FileSystem;
|
||||
|
||||
namespace OpenRA.Mods.Common.Installer
|
||||
{
|
||||
public class ExtractRawSourceAction : ISourceAction
|
||||
{
|
||||
public void RunActionOnSource(MiniYaml actionYaml, string path, ModData modData, List<string> extracted, Action<string> updateMessage)
|
||||
{
|
||||
// Yaml path may be specified relative to a named directory (e.g. ^SupportDir) or the detected source path
|
||||
var sourcePath = actionYaml.Value.StartsWith("^") ? Platform.ResolvePath(actionYaml.Value) : FS.ResolveCaseInsensitivePath(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);
|
||||
|
||||
extracted.Add(targetPath);
|
||||
Directory.CreateDirectory(Path.GetDirectoryName(targetPath));
|
||||
var displayFilename = Path.GetFileName(Path.GetFileName(targetPath));
|
||||
|
||||
Action<long> onProgress = null;
|
||||
if (length < InstallFromDiscLogic.ShowPercentageThreshold)
|
||||
updateMessage(modData.Translation.GetString(InstallFromDiscLogic.Extracing, Translation.Arguments("filename", displayFilename)));
|
||||
else
|
||||
onProgress = b => updateMessage(modData.Translation.GetString(InstallFromDiscLogic.ExtracingProgress, Translation.Arguments("filename", displayFilename, "progress", 100 * b / length)));
|
||||
|
||||
using (var target = File.OpenWrite(targetPath))
|
||||
{
|
||||
Log.Write("install", $"Extracting {sourcePath} -> {targetPath}");
|
||||
InstallerUtils.CopyStream(source, target, length, onProgress);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -41,6 +41,7 @@ namespace OpenRA
|
||||
|
||||
public class ModSource
|
||||
{
|
||||
public readonly ObjectCreator ObjectCreator;
|
||||
public readonly SourceType Type = SourceType.Disc;
|
||||
|
||||
// Used to find installation locations for SourceType.Install
|
||||
@@ -56,8 +57,9 @@ namespace OpenRA
|
||||
[FieldLoader.Ignore]
|
||||
public readonly List<MiniYamlNode> Install;
|
||||
|
||||
public ModSource(MiniYaml yaml)
|
||||
public ModSource(MiniYaml yaml, ObjectCreator objectCreator)
|
||||
{
|
||||
ObjectCreator = objectCreator;
|
||||
Title = yaml.Value;
|
||||
|
||||
var idFiles = yaml.Nodes.FirstOrDefault(n => n.Key == "IDFiles");
|
||||
|
||||
@@ -13,18 +13,16 @@ using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Threading.Tasks;
|
||||
using OpenRA.Mods.Common.FileFormats;
|
||||
using OpenRA.Mods.Common.Installer;
|
||||
using OpenRA.Widgets;
|
||||
using FS = OpenRA.FileSystem.FileSystem;
|
||||
|
||||
namespace OpenRA.Mods.Common.Widgets.Logic
|
||||
{
|
||||
public class InstallFromDiscLogic : ChromeLogic
|
||||
{
|
||||
// Hide percentage indicators for files smaller than 25 MB
|
||||
const int ShowPercentageThreshold = 26214400;
|
||||
public const int ShowPercentageThreshold = 26214400;
|
||||
|
||||
enum Mode { Progress, Message, List }
|
||||
|
||||
@@ -83,10 +81,10 @@ namespace OpenRA.Mods.Common.Widgets.Logic
|
||||
static readonly string InstallingContent = "installing-content";
|
||||
|
||||
[TranslationReference("filename")]
|
||||
static readonly string CopyingFilename = "copying-filename";
|
||||
public static readonly string CopyingFilename = "copying-filename";
|
||||
|
||||
[TranslationReference("filename", "progress")]
|
||||
static readonly string CopyingFilenameProgress = "copying-filename-progress";
|
||||
public static readonly string CopyingFilenameProgress = "copying-filename-progress";
|
||||
|
||||
[TranslationReference]
|
||||
static readonly string InstallationFailed = "installation-failed";
|
||||
@@ -95,10 +93,10 @@ namespace OpenRA.Mods.Common.Widgets.Logic
|
||||
static readonly string CheckInstallLog = "check-install-log";
|
||||
|
||||
[TranslationReference("filename")]
|
||||
static readonly string Extracing = "extracting-filename";
|
||||
public static readonly string Extracing = "extracting-filename";
|
||||
|
||||
[TranslationReference("filename", "progress")]
|
||||
static readonly string ExtracingProgress = "extracting-filename-progress";
|
||||
public static readonly string ExtracingProgress = "extracting-filename-progress";
|
||||
|
||||
[TranslationReference]
|
||||
static readonly string Continue = "continue";
|
||||
@@ -264,82 +262,8 @@ namespace OpenRA.Mods.Common.Widgets.Logic
|
||||
{
|
||||
foreach (var i in modSource.Install)
|
||||
{
|
||||
switch (i.Key)
|
||||
{
|
||||
case "copy":
|
||||
{
|
||||
var sourceDir = Path.Combine(path, i.Value.Value);
|
||||
foreach (var node in i.Value.Nodes)
|
||||
{
|
||||
var sourcePath = FS.ResolveCaseInsensitivePath(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 {sourcePath} -> {targetPath}");
|
||||
extracted.Add(targetPath);
|
||||
Directory.CreateDirectory(Path.GetDirectoryName(targetPath));
|
||||
|
||||
using (var source = File.OpenRead(sourcePath))
|
||||
using (var target = File.OpenWrite(targetPath))
|
||||
{
|
||||
var displayFilename = Path.GetFileName(targetPath);
|
||||
var length = source.Length;
|
||||
|
||||
Action<long> onProgress = null;
|
||||
if (length < ShowPercentageThreshold)
|
||||
message = modData.Translation.GetString(CopyingFilename, Translation.Arguments("filename", displayFilename));
|
||||
else
|
||||
onProgress = b => message = modData.Translation.GetString(CopyingFilenameProgress, Translation.Arguments("filename", displayFilename, "progress", 100 * b / length));
|
||||
|
||||
CopyStream(source, target, length, onProgress);
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case "extract-raw":
|
||||
{
|
||||
ExtractFromPackage(modData, ExtractionType.Raw, path, i.Value, extracted, m => message = m);
|
||||
break;
|
||||
}
|
||||
|
||||
case "extract-blast":
|
||||
{
|
||||
ExtractFromPackage(modData, ExtractionType.Blast, path, i.Value, extracted, m => message = m);
|
||||
break;
|
||||
}
|
||||
|
||||
case "extract-mscab":
|
||||
{
|
||||
ExtractFromMSCab(modData, path, i.Value, extracted, m => message = m);
|
||||
break;
|
||||
}
|
||||
|
||||
case "extract-iscab":
|
||||
{
|
||||
ExtractFromISCab(modData, path, i.Value, extracted, m => message = m);
|
||||
break;
|
||||
}
|
||||
|
||||
case "delete":
|
||||
{
|
||||
// Yaml path may be specified relative to a named directory (e.g. ^SupportDir) or the detected disc path
|
||||
var sourcePath = i.Value.Value.StartsWith("^") ? Platform.ResolvePath(i.Value.Value) : Path.Combine(path, i.Value.Value);
|
||||
|
||||
Log.Write("debug", $"Deleting {sourcePath}");
|
||||
File.Delete(sourcePath);
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
Log.Write("debug", $"Unknown installation command {i.Key} - ignoring");
|
||||
break;
|
||||
}
|
||||
var sourceAction = modSource.ObjectCreator.CreateObject<ISourceAction>($"{i.Key}SourceAction");
|
||||
sourceAction.RunActionOnSource(i.Value, path, modData, extracted, m => message = m);
|
||||
}
|
||||
|
||||
Game.RunAfterTick(Ui.CloseWindow);
|
||||
@@ -363,244 +287,6 @@ namespace OpenRA.Mods.Common.Widgets.Logic
|
||||
}).Start();
|
||||
}
|
||||
|
||||
static void CopyStream(Stream input, Stream output, long length, Action<long> onProgress = null)
|
||||
{
|
||||
var buffer = new byte[4096];
|
||||
var copied = 0L;
|
||||
while (copied < length)
|
||||
{
|
||||
var read = (int)Math.Min(buffer.Length, length - copied);
|
||||
var write = input.Read(buffer, 0, read);
|
||||
output.Write(buffer, 0, write);
|
||||
copied += write;
|
||||
|
||||
onProgress?.Invoke(copied);
|
||||
}
|
||||
}
|
||||
|
||||
enum ExtractionType { Raw, Blast }
|
||||
|
||||
static void ExtractFromPackage(ModData modData, ExtractionType type, string path, MiniYaml actionYaml, List<string> extractedFiles, Action<string> updateMessage)
|
||||
{
|
||||
// Yaml path may be specified relative to a named directory (e.g. ^SupportDir) or the detected disc path
|
||||
var sourcePath = actionYaml.Value.StartsWith("^") ? Platform.ResolvePath(actionYaml.Value) : FS.ResolveCaseInsensitivePath(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));
|
||||
var displayFilename = Path.GetFileName(Path.GetFileName(targetPath));
|
||||
|
||||
Action<long> onProgress = null;
|
||||
if (length < ShowPercentageThreshold)
|
||||
updateMessage(modData.Translation.GetString(Extracing, Translation.Arguments("filename", displayFilename)));
|
||||
else
|
||||
onProgress = b => updateMessage(modData.Translation.GetString(ExtracingProgress, Translation.Arguments("filename", displayFilename, "progress", 100 * b / length)));
|
||||
|
||||
using (var target = File.OpenWrite(targetPath))
|
||||
{
|
||||
Log.Write("install", $"Extracting {sourcePath} -> {targetPath}");
|
||||
if (type == ExtractionType.Blast)
|
||||
Blast.Decompress(source, target, (read, _) => onProgress?.Invoke(read));
|
||||
else
|
||||
CopyStream(source, target, length, onProgress);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void ExtractFromMSCab(ModData modData, string path, MiniYaml actionYaml, List<string> extractedFiles, Action<string> updateMessage)
|
||||
{
|
||||
// Yaml path may be specified relative to a named directory (e.g. ^SupportDir) or the detected disc path
|
||||
var sourcePath = actionYaml.Value.StartsWith("^") ? Platform.ResolvePath(actionYaml.Value) : FS.ResolveCaseInsensitivePath(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))
|
||||
{
|
||||
Log.Write("install", $"Extracting {sourcePath} -> {targetPath}");
|
||||
var displayFilename = Path.GetFileName(Path.GetFileName(targetPath));
|
||||
Action<int> onProgress = percent => updateMessage(modData.Translation.GetString(ExtracingProgress, Translation.Arguments("filename", displayFilename, "progress", percent)));
|
||||
reader.ExtractFile(node.Value.Value, target, onProgress);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void ExtractFromISCab(ModData modData, string path, MiniYaml actionYaml, List<string> extractedFiles, Action<string> updateMessage)
|
||||
{
|
||||
// Yaml path may be specified relative to a named directory (e.g. ^SupportDir) or the detected disc path
|
||||
var sourcePath = actionYaml.Value.StartsWith("^") ? Platform.ResolvePath(actionYaml.Value) : FS.ResolveCaseInsensitivePath(Path.Combine(path, actionYaml.Value));
|
||||
|
||||
var volumeNode = actionYaml.Nodes.FirstOrDefault(n => n.Key == "Volumes");
|
||||
if (volumeNode == null)
|
||||
throw new InvalidDataException("extract-iscab entry doesn't define a Volumes node");
|
||||
|
||||
var extractNode = actionYaml.Nodes.FirstOrDefault(n => n.Key == "Extract");
|
||||
if (extractNode == null)
|
||||
throw new InvalidDataException("extract-iscab entry doesn't define an Extract node");
|
||||
|
||||
var volumes = new Dictionary<int, Stream>();
|
||||
try
|
||||
{
|
||||
foreach (var node in volumeNode.Value.Nodes)
|
||||
{
|
||||
var volume = FieldLoader.GetValue<int>("(key)", node.Key);
|
||||
var stream = File.OpenRead(FS.ResolveCaseInsensitivePath(Path.Combine(path, node.Value.Value)));
|
||||
volumes.Add(volume, stream);
|
||||
}
|
||||
|
||||
using (var source = File.OpenRead(sourcePath))
|
||||
{
|
||||
var reader = new InstallShieldCABCompression(source, volumes);
|
||||
foreach (var node in extractNode.Value.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))
|
||||
{
|
||||
Log.Write("install", $"Extracting {sourcePath} -> {targetPath}");
|
||||
var displayFilename = Path.GetFileName(Path.GetFileName(targetPath));
|
||||
Action<int> onProgress = percent => updateMessage(modData.Translation.GetString(ExtracingProgress, Translation.Arguments("filename", displayFilename, "progress", percent)));
|
||||
reader.ExtractFile(node.Value.Value, target, onProgress);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
foreach (var kv in volumes)
|
||||
kv.Value.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
string FindSourcePath(ModContent.ModSource source, IEnumerable<string> volumes)
|
||||
{
|
||||
if (source.Type == ModContent.SourceType.RegistryDirectory || source.Type == ModContent.SourceType.RegistryDirectoryFromFile)
|
||||
{
|
||||
if (source.RegistryKey == null)
|
||||
return null;
|
||||
|
||||
if (Platform.CurrentPlatform != PlatformType.Windows)
|
||||
return null;
|
||||
|
||||
// We need an extra check for the platform here to silence a warning when the registry is accessed
|
||||
// TODO: Remove this once our platform checks use the same method
|
||||
if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
|
||||
return null;
|
||||
|
||||
foreach (var prefix in source.RegistryPrefixes)
|
||||
{
|
||||
if (!(Microsoft.Win32.Registry.GetValue(prefix + source.RegistryKey, source.RegistryValue, null) is string path))
|
||||
continue;
|
||||
|
||||
if (source.Type == ModContent.SourceType.RegistryDirectoryFromFile)
|
||||
path = Path.GetDirectoryName(path);
|
||||
|
||||
return IsValidSourcePath(path, source) ? path : null;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
if (source.Type == ModContent.SourceType.Disc)
|
||||
foreach (var volume in volumes)
|
||||
if (IsValidSourcePath(volume, source))
|
||||
return volume;
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
static bool IsValidSourcePath(string path, ModContent.ModSource source)
|
||||
{
|
||||
try
|
||||
{
|
||||
foreach (var kv in source.IDFiles.Nodes)
|
||||
{
|
||||
var filePath = FS.ResolveCaseInsensitivePath(Path.Combine(path, kv.Key));
|
||||
if (!File.Exists(filePath))
|
||||
return false;
|
||||
|
||||
using (var fileStream = File.OpenRead(filePath))
|
||||
{
|
||||
var offsetNode = kv.Value.Nodes.FirstOrDefault(n => n.Key == "Offset");
|
||||
var lengthNode = kv.Value.Nodes.FirstOrDefault(n => n.Key == "Length");
|
||||
if (offsetNode != null || lengthNode != null)
|
||||
{
|
||||
var offset = 0L;
|
||||
if (offsetNode != null)
|
||||
offset = FieldLoader.GetValue<long>("Offset", offsetNode.Value.Value);
|
||||
|
||||
var length = fileStream.Length - offset;
|
||||
if (lengthNode != null)
|
||||
length = FieldLoader.GetValue<long>("Length", lengthNode.Value.Value);
|
||||
|
||||
fileStream.Position = offset;
|
||||
var data = fileStream.ReadBytes((int)length);
|
||||
if (CryptoUtil.SHA1Hash(data) != kv.Value.Value)
|
||||
return false;
|
||||
}
|
||||
else if (CryptoUtil.SHA1Hash(fileStream) != kv.Value.Value)
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void ShowMessage(string title, string message)
|
||||
{
|
||||
visible = Mode.Message;
|
||||
|
||||
@@ -48,7 +48,7 @@ namespace OpenRA.Mods.Common.Widgets.Logic
|
||||
|
||||
var sourceYaml = MiniYaml.Load(modFileSystem, content.Sources, null);
|
||||
foreach (var s in sourceYaml)
|
||||
sources.Add(s.Key, new ModContent.ModSource(s.Value));
|
||||
sources.Add(s.Key, new ModContent.ModSource(s.Value, modObjectCreator));
|
||||
|
||||
var downloadYaml = MiniYaml.Load(modFileSystem, content.Downloads, null);
|
||||
foreach (var d in downloadYaml)
|
||||
|
||||
@@ -3,5 +3,5 @@ covertops: Covert Operations Expansion (English)
|
||||
GAME/GAME.DAT: be5a6c4c0a581da09e8f34a3bbf7bd17e525085c
|
||||
CONQUER.MIX: 713b53fa4c188ca9619c6bbeadbfc86513704266
|
||||
Install:
|
||||
copy: .
|
||||
Copy: .
|
||||
^SupportDir|Content/cnc/scores-covertops.mix: SCORES.MIX
|
||||
|
||||
@@ -3,7 +3,7 @@ tfd: C&C The First Decade (English)
|
||||
data1.hdr: bef3a08c3fc1b1caf28ca0dbb97c1f900005930e
|
||||
data1.cab: 12ad6113a6890a1b4d5651a75378c963eaf513b9
|
||||
Install:
|
||||
extract-iscab: data1.hdr
|
||||
ExtractIscab: data1.hdr
|
||||
Volumes:
|
||||
2: data2.cab
|
||||
3: data3.cab
|
||||
@@ -20,7 +20,7 @@ tfd: C&C The First Decade (English)
|
||||
^SupportDir|Content/cnc/transit.mix: CnC\\TRANSIT.MIX
|
||||
^SupportDir|Content/cnc/scores-covertops.mix: CnC\covert\SCORES.MIX
|
||||
^SupportDir|Content/cnc/movies.mix: CnC\\MOVIES.MIX
|
||||
extract-raw: ^SupportDir|Content/cnc/movies.mix
|
||||
ExtractRaw: ^SupportDir|Content/cnc/movies.mix
|
||||
^SupportDir|Content/cnc/movies/airstrk.vqa:
|
||||
Offset: 1266
|
||||
Length: 4444442
|
||||
@@ -333,4 +333,4 @@ tfd: C&C The First Decade (English)
|
||||
^SupportDir|Content/cnc/movies/banner.vqa:
|
||||
Offset: 751962554
|
||||
Length: 2229408
|
||||
delete: ^SupportDir|Content/cnc/movies.mix
|
||||
Delete: ^SupportDir|Content/cnc/movies.mix
|
||||
|
||||
@@ -3,7 +3,7 @@ gdi95: C&C Gold (GDI Disc, English)
|
||||
DISK.WAV: 8bef9a6687c0fe0afd57c6561df58fa6e64f145d
|
||||
CONQUER.MIX: 833e02a09aae694659eb312d3838367f681d1b30
|
||||
Install:
|
||||
copy: .
|
||||
Copy: .
|
||||
^SupportDir|Content/cnc/conquer.mix: CONQUER.MIX
|
||||
^SupportDir|Content/cnc/desert.mix: DESERT.MIX
|
||||
^SupportDir|Content/cnc/general.mix: GENERAL.MIX
|
||||
@@ -11,7 +11,7 @@ gdi95: C&C Gold (GDI Disc, English)
|
||||
^SupportDir|Content/cnc/sounds.mix: SOUNDS.MIX
|
||||
^SupportDir|Content/cnc/temperat.mix: TEMPERAT.MIX
|
||||
^SupportDir|Content/cnc/winter.mix: WINTER.MIX
|
||||
extract-blast: INSTALL/SETUP.Z
|
||||
ExtractBlast: INSTALL/SETUP.Z
|
||||
^SupportDir|Content/cnc/speech.mix:
|
||||
Offset: 10203213
|
||||
Length: 603293
|
||||
@@ -21,7 +21,7 @@ gdi95: C&C Gold (GDI Disc, English)
|
||||
^SupportDir|Content/cnc/transit.mix:
|
||||
Offset: 11078017
|
||||
Length: 3724462
|
||||
extract-raw: MOVIES.MIX
|
||||
ExtractRaw: MOVIES.MIX
|
||||
^SupportDir|Content/cnc/movies/visor.vqa:
|
||||
Offset: 786
|
||||
Length: 4407162
|
||||
|
||||
@@ -3,7 +3,7 @@ nod95: C&C Gold (Nod Disc, English)
|
||||
DISK.WAV: 83a0235525afa5cd6096f2967e3eae032996e38c
|
||||
CONQUER.MIX: 833e02a09aae694659eb312d3838367f681d1b30
|
||||
Install:
|
||||
copy: .
|
||||
Copy: .
|
||||
^SupportDir|Content/cnc/conquer.mix: CONQUER.MIX
|
||||
^SupportDir|Content/cnc/desert.mix: DESERT.MIX
|
||||
^SupportDir|Content/cnc/general.mix: GENERAL.MIX
|
||||
@@ -11,7 +11,7 @@ nod95: C&C Gold (Nod Disc, English)
|
||||
^SupportDir|Content/cnc/sounds.mix: SOUNDS.MIX
|
||||
^SupportDir|Content/cnc/temperat.mix: TEMPERAT.MIX
|
||||
^SupportDir|Content/cnc/winter.mix: WINTER.MIX
|
||||
extract-blast: INSTALL/SETUP.Z
|
||||
ExtractBlast: INSTALL/SETUP.Z
|
||||
^SupportDir|Content/cnc/speech.mix:
|
||||
Offset: 10203213
|
||||
Length: 603293
|
||||
@@ -21,7 +21,7 @@ nod95: C&C Gold (Nod Disc, English)
|
||||
^SupportDir|Content/cnc/transit.mix:
|
||||
Offset: 11078017
|
||||
Length: 3724462
|
||||
extract-raw: MOVIES.MIX
|
||||
ExtractRaw: MOVIES.MIX
|
||||
^SupportDir|Content/cnc/movies/visor.vqa:
|
||||
Offset: 738
|
||||
Length: 4407162
|
||||
|
||||
@@ -6,7 +6,7 @@ origin: C&C The Ultimate Collection (Origin version, English)
|
||||
IDFiles:
|
||||
CONQUER.MIX: 833e02a09aae694659eb312d3838367f681d1b30
|
||||
Install:
|
||||
copy: .
|
||||
Copy: .
|
||||
^SupportDir|Content/cnc/conquer.mix: CONQUER.MIX
|
||||
^SupportDir|Content/cnc/desert.mix: DESERT.MIX
|
||||
^SupportDir|Content/cnc/general.mix: GENERAL.MIX
|
||||
@@ -18,7 +18,7 @@ origin: C&C The Ultimate Collection (Origin version, English)
|
||||
^SupportDir|Content/cnc/tempicnh.mix: TEMPICNH.MIX
|
||||
^SupportDir|Content/cnc/transit.mix: TRANSIT.MIX
|
||||
^SupportDir|Content/cnc/scores-covertops.mix: covert/SCORES.MIX
|
||||
extract-raw: movies.mix
|
||||
ExtractRaw: movies.mix
|
||||
^SupportDir|Content/cnc/movies/akira.vqa:
|
||||
Offset: 4445708
|
||||
Length: 7803444
|
||||
|
||||
@@ -3,7 +3,7 @@ d2k-a: Dune 2000 (English)
|
||||
MUSIC/AMBUSH.AUD: bd5926a56a83bc0e49f96037e1f909014ac0772a
|
||||
SETUP/SETUP.Z: 937f5ceb1236bf3f3d4e43929305ffe5004078e7
|
||||
Install:
|
||||
copy: MOVIES
|
||||
Copy: MOVIES
|
||||
^SupportDir|Content/d2k/v2/Movies/A_BR01_E.VQA: A_BR01_E.VQA
|
||||
^SupportDir|Content/d2k/v2/Movies/A_BR02_E.VQA: A_BR02_E.VQA
|
||||
^SupportDir|Content/d2k/v2/Movies/A_BR03_E.VQA: A_BR03_E.VQA
|
||||
@@ -46,7 +46,7 @@ d2k-a: Dune 2000 (English)
|
||||
^SupportDir|Content/d2k/v2/Movies/G_PLN2_E.VQA: G_PLN2_E.VQA
|
||||
^SupportDir|Content/d2k/v2/Movies/G_PLNT_E.VQA: G_PLNT_E.VQA
|
||||
^SupportDir|Content/d2k/v2/Movies/T_TITL_E.VQA: T_TITL_E.VQA
|
||||
copy: MUSIC
|
||||
Copy: MUSIC
|
||||
^SupportDir|Content/d2k/v2/Music/AMBUSH.AUD: AMBUSH.AUD
|
||||
^SupportDir|Content/d2k/v2/Music/ARAKATAK.AUD: ARAKATAK.AUD
|
||||
^SupportDir|Content/d2k/v2/Music/ATREGAIN.AUD: ATREGAIN.AUD
|
||||
@@ -64,7 +64,7 @@ d2k-a: Dune 2000 (English)
|
||||
^SupportDir|Content/d2k/v2/Music/SPICESCT.AUD: SPICESCT.AUD
|
||||
^SupportDir|Content/d2k/v2/Music/UNDERCON.AUD: UNDERCON.AUD
|
||||
^SupportDir|Content/d2k/v2/Music/WAITGAME.AUD: WAITGAME.AUD
|
||||
extract-blast: SETUP/SETUP.Z
|
||||
ExtractBlast: SETUP/SETUP.Z
|
||||
^SupportDir|Content/d2k/v2/BLOXBAT.R8:
|
||||
Offset: 1156652
|
||||
Length: 512750
|
||||
|
||||
@@ -3,7 +3,7 @@ d2k-b: Dune 2000 (English)
|
||||
MUSIC/AMBUSH.AUD: bd5926a56a83bc0e49f96037e1f909014ac0772a
|
||||
SETUP/SETUP.Z: 029722e70fb7636f8120028f5c9b6ce81627ff90
|
||||
Install:
|
||||
copy: MOVIES
|
||||
Copy: MOVIES
|
||||
^SupportDir|Content/d2k/v2/Movies/A_BR01_E.VQA: A_BR01_E.VQA
|
||||
^SupportDir|Content/d2k/v2/Movies/A_BR02_E.VQA: A_BR02_E.VQA
|
||||
^SupportDir|Content/d2k/v2/Movies/A_BR03_E.VQA: A_BR03_E.VQA
|
||||
@@ -46,7 +46,7 @@ d2k-b: Dune 2000 (English)
|
||||
^SupportDir|Content/d2k/v2/Movies/G_PLN2_E.VQA: G_PLN2_E.VQA
|
||||
^SupportDir|Content/d2k/v2/Movies/G_PLNT_E.VQA: G_PLNT_E.VQA
|
||||
^SupportDir|Content/d2k/v2/Movies/T_TITL_E.VQA: T_TITL_E.VQA
|
||||
copy: MUSIC
|
||||
Copy: MUSIC
|
||||
^SupportDir|Content/d2k/v2/Music/AMBUSH.AUD: AMBUSH.AUD
|
||||
^SupportDir|Content/d2k/v2/Music/ARAKATAK.AUD: ARAKATAK.AUD
|
||||
^SupportDir|Content/d2k/v2/Music/ATREGAIN.AUD: ATREGAIN.AUD
|
||||
@@ -64,7 +64,7 @@ d2k-b: Dune 2000 (English)
|
||||
^SupportDir|Content/d2k/v2/Music/SPICESCT.AUD: SPICESCT.AUD
|
||||
^SupportDir|Content/d2k/v2/Music/UNDERCON.AUD: UNDERCON.AUD
|
||||
^SupportDir|Content/d2k/v2/Music/WAITGAME.AUD: WAITGAME.AUD
|
||||
extract-blast: SETUP/SETUP.Z
|
||||
ExtractBlast: SETUP/SETUP.Z
|
||||
^SupportDir|Content/d2k/v2/BLOXBAT.R8:
|
||||
Offset: 1156877
|
||||
Length: 512750
|
||||
|
||||
@@ -3,7 +3,7 @@ d2k-c: Dune 2000 (English)
|
||||
MUSIC/AMBUSH.AUD: bd5926a56a83bc0e49f96037e1f909014ac0772a
|
||||
SETUP/SETUP.Z: d939b39bdbc952b259ce2b45c0bbedefa534b7f2
|
||||
Install:
|
||||
copy: MOVIES
|
||||
Copy: MOVIES
|
||||
^SupportDir|Content/d2k/v2/Movies/A_BR01_E.VQA: A_BR01_E.VQA
|
||||
^SupportDir|Content/d2k/v2/Movies/A_BR02_E.VQA: A_BR02_E.VQA
|
||||
^SupportDir|Content/d2k/v2/Movies/A_BR03_E.VQA: A_BR03_E.VQA
|
||||
@@ -46,7 +46,7 @@ d2k-c: Dune 2000 (English)
|
||||
^SupportDir|Content/d2k/v2/Movies/G_PLN2_E.VQA: G_PLN2_E.VQA
|
||||
^SupportDir|Content/d2k/v2/Movies/G_PLNT_E.VQA: G_PLNT_E.VQA
|
||||
^SupportDir|Content/d2k/v2/Movies/T_TITL_E.VQA: T_TITL_E.VQA
|
||||
copy: MUSIC
|
||||
Copy: MUSIC
|
||||
^SupportDir|Content/d2k/v2/Music/AMBUSH.AUD: AMBUSH.AUD
|
||||
^SupportDir|Content/d2k/v2/Music/ARAKATAK.AUD: ARAKATAK.AUD
|
||||
^SupportDir|Content/d2k/v2/Music/ATREGAIN.AUD: ATREGAIN.AUD
|
||||
@@ -64,7 +64,7 @@ d2k-c: Dune 2000 (English)
|
||||
^SupportDir|Content/d2k/v2/Music/SPICESCT.AUD: SPICESCT.AUD
|
||||
^SupportDir|Content/d2k/v2/Music/UNDERCON.AUD: UNDERCON.AUD
|
||||
^SupportDir|Content/d2k/v2/Music/WAITGAME.AUD: WAITGAME.AUD
|
||||
extract-blast: SETUP/SETUP.Z
|
||||
ExtractBlast: SETUP/SETUP.Z
|
||||
^SupportDir|Content/d2k/v2/BLOXBAT.R8:
|
||||
Offset: 704502
|
||||
Length: 512750
|
||||
|
||||
@@ -3,7 +3,7 @@ d2k-d: Dune 2000 (English)
|
||||
MUSIC/AMBUSH.AUD: bd5926a56a83bc0e49f96037e1f909014ac0772a
|
||||
SETUP/SETUP.Z: 2411cc5df36954ebd534ceafa3007c8aa9232909
|
||||
Install:
|
||||
copy: MOVIES
|
||||
Copy: MOVIES
|
||||
^SupportDir|Content/d2k/v2/Movies/A_BR01_E.VQA: A_BR01_E.VQA
|
||||
^SupportDir|Content/d2k/v2/Movies/A_BR02_E.VQA: A_BR02_E.VQA
|
||||
^SupportDir|Content/d2k/v2/Movies/A_BR03_E.VQA: A_BR03_E.VQA
|
||||
@@ -46,7 +46,7 @@ d2k-d: Dune 2000 (English)
|
||||
^SupportDir|Content/d2k/v2/Movies/G_PLN2_E.VQA: G_PLN2_E.VQA
|
||||
^SupportDir|Content/d2k/v2/Movies/G_PLNT_E.VQA: G_PLNT_E.VQA
|
||||
^SupportDir|Content/d2k/v2/Movies/T_TITL_E.VQA: T_TITL_E.VQA
|
||||
copy: MUSIC
|
||||
Copy: MUSIC
|
||||
^SupportDir|Content/d2k/v2/Music/AMBUSH.AUD: AMBUSH.AUD
|
||||
^SupportDir|Content/d2k/v2/Music/ARAKATAK.AUD: ARAKATAK.AUD
|
||||
^SupportDir|Content/d2k/v2/Music/ATREGAIN.AUD: ATREGAIN.AUD
|
||||
@@ -64,7 +64,7 @@ d2k-d: Dune 2000 (English)
|
||||
^SupportDir|Content/d2k/v2/Music/SPICESCT.AUD: SPICESCT.AUD
|
||||
^SupportDir|Content/d2k/v2/Music/UNDERCON.AUD: UNDERCON.AUD
|
||||
^SupportDir|Content/d2k/v2/Music/WAITGAME.AUD: WAITGAME.AUD
|
||||
extract-blast: SETUP/SETUP.Z
|
||||
ExtractBlast: SETUP/SETUP.Z
|
||||
^SupportDir|Content/d2k/v2/BLOXBAT.R8:
|
||||
Offset: 702406
|
||||
Length: 512750
|
||||
|
||||
@@ -3,7 +3,7 @@ d2k-e: Dune 2000 (English)
|
||||
MUSIC/AMBUSH.AUD: bd5926a56a83bc0e49f96037e1f909014ac0772a
|
||||
SETUP/SETUP.Z: b476661e82eeb05949e97fa1f75fed2343174be5
|
||||
Install:
|
||||
copy: MOVIES
|
||||
Copy: MOVIES
|
||||
^SupportDir|Content/d2k/v2/Movies/A_BR01_E.VQA: A_BR01_E.VQA
|
||||
^SupportDir|Content/d2k/v2/Movies/A_BR02_E.VQA: A_BR02_E.VQA
|
||||
^SupportDir|Content/d2k/v2/Movies/A_BR03_E.VQA: A_BR03_E.VQA
|
||||
@@ -46,7 +46,7 @@ d2k-e: Dune 2000 (English)
|
||||
^SupportDir|Content/d2k/v2/Movies/G_PLN2_E.VQA: G_PLN2_E.VQA
|
||||
^SupportDir|Content/d2k/v2/Movies/G_PLNT_E.VQA: G_PLNT_E.VQA
|
||||
^SupportDir|Content/d2k/v2/Movies/T_TITL_E.VQA: T_TITL_E.VQA
|
||||
copy: MUSIC
|
||||
Copy: MUSIC
|
||||
^SupportDir|Content/d2k/v2/Music/AMBUSH.AUD: AMBUSH.AUD
|
||||
^SupportDir|Content/d2k/v2/Music/ARAKATAK.AUD: ARAKATAK.AUD
|
||||
^SupportDir|Content/d2k/v2/Music/ATREGAIN.AUD: ATREGAIN.AUD
|
||||
@@ -64,7 +64,7 @@ d2k-e: Dune 2000 (English)
|
||||
^SupportDir|Content/d2k/v2/Music/SPICESCT.AUD: SPICESCT.AUD
|
||||
^SupportDir|Content/d2k/v2/Music/UNDERCON.AUD: UNDERCON.AUD
|
||||
^SupportDir|Content/d2k/v2/Music/WAITGAME.AUD: WAITGAME.AUD
|
||||
extract-blast: SETUP/SETUP.Z
|
||||
ExtractBlast: SETUP/SETUP.Z
|
||||
^SupportDir|Content/d2k/v2/BLOXBAT.R8:
|
||||
Offset: 702405
|
||||
Length: 512750
|
||||
|
||||
@@ -6,7 +6,7 @@ gruntmods: Dune 2000: GruntMods Edition
|
||||
Dune 2000/data/BLOXXMAS.R8: afc818feda44f5873e3af07bd2191573ba9965db
|
||||
Dune 2000/data/DATA.R8: 2b229cf4be47104a6214237039a55329f6c45bc9
|
||||
Install:
|
||||
copy: Dune 2000/data/Music
|
||||
Copy: Dune 2000/data/Music
|
||||
^SupportDir|Content/d2k/v2/Music/AMBUSH.AUD: AMBUSH.AUD
|
||||
^SupportDir|Content/d2k/v2/Music/ARAKATAK.AUD: ARAKATAK.AUD
|
||||
^SupportDir|Content/d2k/v2/Music/ATREGAIN.AUD: ATREGAIN.AUD
|
||||
@@ -24,7 +24,7 @@ gruntmods: Dune 2000: GruntMods Edition
|
||||
^SupportDir|Content/d2k/v2/Music/SPICESCT.AUD: SPICESCT.AUD
|
||||
^SupportDir|Content/d2k/v2/Music/UNDERCON.AUD: UNDERCON.AUD
|
||||
^SupportDir|Content/d2k/v2/Music/WAITGAME.AUD: WAITGAME.AUD
|
||||
copy: Dune 2000/data
|
||||
Copy: Dune 2000/data
|
||||
^SupportDir|Content/d2k/v2/BLOXBAT.R8: BLOXBAT.R8
|
||||
^SupportDir|Content/d2k/v2/BLOXBASE.R8: BLOXBASE.R8
|
||||
^SupportDir|Content/d2k/v2/BLOXBGBS.R8: BLOXBGBS.R8
|
||||
@@ -36,10 +36,10 @@ gruntmods: Dune 2000: GruntMods Edition
|
||||
^SupportDir|Content/d2k/v2/MOUSE.R8: MOUSE.R8
|
||||
^SupportDir|Content/d2k/v2/FONTCOL.FNT: FONTCOL.FNT
|
||||
^SupportDir|Content/d2k/v2/FONTCOL.FPL: FONTCOL.FPL
|
||||
copy: Dune 2000/data/bin
|
||||
Copy: Dune 2000/data/bin
|
||||
^SupportDir|Content/d2k/v2/PALETTE.BIN: PALETTE.BIN
|
||||
^SupportDir|Content/d2k/v2/FONT.BIN: FONT.BIN
|
||||
copy: Dune 2000/data/GAMESFX
|
||||
Copy: Dune 2000/data/GAMESFX
|
||||
^SupportDir|Content/d2k/v2/GAMESFX/A_ECONF2.AUD: A_ECONF2.AUD
|
||||
^SupportDir|Content/d2k/v2/GAMESFX/A_ECONF1.AUD: A_ECONF1.AUD
|
||||
^SupportDir|Content/d2k/v2/GAMESFX/A_ECONF3.AUD: A_ECONF3.AUD
|
||||
|
||||
@@ -3,7 +3,7 @@ aftermath: Aftermath Expansion Disc (English)
|
||||
README.TXT: 9902fb74c019df1b76ff5634e68f0371d790b5e0
|
||||
SETUP/INSTALL/PATCH.RTP: 5bce93f834f9322ddaa7233242e5b6c7fea0bf17
|
||||
Install:
|
||||
extract-raw: SETUP/INSTALL/PATCH.RTP
|
||||
ExtractRaw: SETUP/INSTALL/PATCH.RTP
|
||||
^SupportDir|Content/ra/v2/expand/expand2.mix:
|
||||
Offset: 4712984
|
||||
Length: 469922
|
||||
@@ -13,7 +13,7 @@ aftermath: Aftermath Expansion Disc (English)
|
||||
^SupportDir|Content/ra/v2/expand/lores1.mix:
|
||||
Offset: 5273320
|
||||
Length: 57076
|
||||
extract-raw: MAIN.MIX
|
||||
ExtractRaw: MAIN.MIX
|
||||
^SupportDir|Content/ra/v2/expand/await.aud:
|
||||
Offset: 158698809
|
||||
Length: 2972788
|
||||
|
||||
@@ -4,7 +4,7 @@ allied: Red Alert 95 (Allied Disc, English)
|
||||
Length: 4096
|
||||
INSTALL/REDALERT.MIX: 0e58f4b54f44f6cd29fecf8cf379d33cf2d4caef
|
||||
Install:
|
||||
extract-raw: INSTALL/REDALERT.MIX
|
||||
ExtractRaw: INSTALL/REDALERT.MIX
|
||||
^SupportDir|Content/ra/v2/hires.mix:
|
||||
Offset: 650612
|
||||
Length: 5817417
|
||||
@@ -17,7 +17,7 @@ allied: Red Alert 95 (Allied Disc, English)
|
||||
^SupportDir|Content/ra/v2/speech.mix:
|
||||
Offset: 23042864
|
||||
Length: 2003464
|
||||
extract-raw: MAIN.MIX
|
||||
ExtractRaw: MAIN.MIX
|
||||
^SupportDir|Content/ra/v2/conquer.mix:
|
||||
Offset: 236
|
||||
Length: 2177047
|
||||
|
||||
@@ -2,5 +2,5 @@ cnc95: C&C Gold (GDI or Nod Disc, English)
|
||||
IDFiles:
|
||||
CONQUER.MIX: 833e02a09aae694659eb312d3838367f681d1b30
|
||||
Install:
|
||||
copy: .
|
||||
Copy: .
|
||||
^SupportDir|Content/ra/v2/cnc/desert.mix: DESERT.MIX
|
||||
|
||||
@@ -3,7 +3,7 @@ counterstrike: Counterstrike Expansion Disc (English)
|
||||
README.TXT: 0efe8087383f0b159a9633f891fb5f53c6097cd4
|
||||
SETUP/INSTALL/CSTRIKE.RTP: fae8ba82db71574f6ecd8fb4ff4026fcb65d2adc
|
||||
Install:
|
||||
extract-raw: MAIN.MIX
|
||||
ExtractRaw: MAIN.MIX
|
||||
^SupportDir|Content/ra/v2/expand/2nd_hand.aud:
|
||||
Offset: 209070947
|
||||
Length: 3070092
|
||||
|
||||
@@ -3,7 +3,7 @@ tfd: C&C The First Decade (English)
|
||||
data1.hdr: bef3a08c3fc1b1caf28ca0dbb97c1f900005930e
|
||||
data1.cab: 12ad6113a6890a1b4d5651a75378c963eaf513b9
|
||||
Install:
|
||||
extract-iscab: data1.hdr
|
||||
ExtractIscab: data1.hdr
|
||||
Volumes:
|
||||
2: data2.cab
|
||||
3: data3.cab
|
||||
@@ -16,7 +16,7 @@ tfd: C&C The First Decade (English)
|
||||
^SupportDir|Content/ra/v2/expand/lores1.mix: Red Alert\\LORES1.MIX
|
||||
^SupportDir|Content/ra/v2/expand/expand2.mix: Red Alert\\EXPAND2.MIX
|
||||
^SupportDir|Content/ra/v2/cnc/desert.mix: CnC\\DESERT.MIX
|
||||
extract-raw: ^SupportDir|Content/ra/v2/redalert.mix
|
||||
ExtractRaw: ^SupportDir|Content/ra/v2/redalert.mix
|
||||
^SupportDir|Content/ra/v2/hires.mix:
|
||||
Offset: 650612
|
||||
Length: 5817417
|
||||
@@ -29,8 +29,8 @@ tfd: C&C The First Decade (English)
|
||||
^SupportDir|Content/ra/v2/speech.mix:
|
||||
Offset: 23042864
|
||||
Length: 2003464
|
||||
delete: ^SupportDir|Content/ra/v2/redalert.mix
|
||||
extract-raw: ^SupportDir|Content/ra/v2/main.mix
|
||||
Delete: ^SupportDir|Content/ra/v2/redalert.mix
|
||||
ExtractRaw: ^SupportDir|Content/ra/v2/main.mix
|
||||
^SupportDir|Content/ra/v2/movies/aagun.vqa:
|
||||
Offset: 668669829
|
||||
Length: 3295512
|
||||
@@ -445,4 +445,4 @@ tfd: C&C The First Decade (English)
|
||||
^SupportDir|Content/ra/v2/expand/myes1.aud:
|
||||
Offset: 844509702
|
||||
Length: 9073
|
||||
delete: ^SupportDir|Content/ra/v2/main.mix
|
||||
Delete: ^SupportDir|Content/ra/v2/main.mix
|
||||
|
||||
@@ -6,7 +6,7 @@ ra-origin: C&C The Ultimate Collection (Origin version, English)
|
||||
IDFiles:
|
||||
REDALERT.MIX: 0e58f4b54f44f6cd29fecf8cf379d33cf2d4caef
|
||||
Install:
|
||||
copy: .
|
||||
Copy: .
|
||||
^SupportDir|Content/ra/v2/expand/2nd_hand.aud: 2nd_hand.aud
|
||||
^SupportDir|Content/ra/v2/expand/araziod.aud: araziod.aud
|
||||
^SupportDir|Content/ra/v2/expand/await.aud: await.aud
|
||||
@@ -27,7 +27,7 @@ ra-origin: C&C The Ultimate Collection (Origin version, English)
|
||||
^SupportDir|Content/ra/v2/expand/expand2.mix: EXPAND2.MIX
|
||||
^SupportDir|Content/ra/v2/expand/hires1.mix: HIRES1.MIX
|
||||
^SupportDir|Content/ra/v2/expand/lores1.mix: LORES1.MIX
|
||||
extract-raw: REDALERT.MIX
|
||||
ExtractRaw: REDALERT.MIX
|
||||
^SupportDir|Content/ra/v2/hires.mix:
|
||||
Offset: 650612
|
||||
Length: 5817417
|
||||
@@ -40,7 +40,7 @@ ra-origin: C&C The Ultimate Collection (Origin version, English)
|
||||
^SupportDir|Content/ra/v2/speech.mix:
|
||||
Offset: 23042864
|
||||
Length: 2003464
|
||||
extract-raw: MAIN.MIX
|
||||
ExtractRaw: MAIN.MIX
|
||||
^SupportDir|Content/ra/v2/movies/aagun.vqa:
|
||||
Offset: 668669829
|
||||
Length: 3295512
|
||||
@@ -464,5 +464,5 @@ cnc-origin: Command & Conquer (Origin version, English)
|
||||
IDFiles:
|
||||
CONQUER.MIX: 833e02a09aae694659eb312d3838367f681d1b30
|
||||
Install:
|
||||
copy: .
|
||||
Copy: .
|
||||
^SupportDir|Content/ra/v2/cnc/desert.mix: DESERT.MIX
|
||||
|
||||
@@ -4,7 +4,7 @@ soviet: Red Alert 95 (Soviet Disc, English)
|
||||
Length: 4096
|
||||
INSTALL/REDALERT.MIX: 0e58f4b54f44f6cd29fecf8cf379d33cf2d4caef
|
||||
Install:
|
||||
extract-raw: INSTALL/REDALERT.MIX
|
||||
ExtractRaw: INSTALL/REDALERT.MIX
|
||||
^SupportDir|Content/ra/v2/hires.mix:
|
||||
Offset: 650612
|
||||
Length: 5817417
|
||||
@@ -17,7 +17,7 @@ soviet: Red Alert 95 (Soviet Disc, English)
|
||||
^SupportDir|Content/ra/v2/speech.mix:
|
||||
Offset: 23042864
|
||||
Length: 2003464
|
||||
extract-raw: MAIN.MIX
|
||||
ExtractRaw: MAIN.MIX
|
||||
^SupportDir|Content/ra/v2/conquer.mix:
|
||||
Offset: 236
|
||||
Length: 2177047
|
||||
|
||||
@@ -3,9 +3,9 @@ fstorm: Firestorm Expansion Disc (English)
|
||||
Install/README.TXT: f2810b540fce8f3880250213ee08c57780d81c20
|
||||
Install/Language.dll: 4df87c1a2289da57dd14d0a7299546f37357fcca
|
||||
Install:
|
||||
copy: .
|
||||
Copy: .
|
||||
^SupportDir|Content/ts/firestorm/scores01.mix: scores01.mix
|
||||
extract-raw: Install/expand01.mix
|
||||
ExtractRaw: Install/expand01.mix
|
||||
^SupportDir|Content/ts/firestorm/m_emp.vxl:
|
||||
Offset: 2187652
|
||||
Length: 24652
|
||||
|
||||
@@ -3,7 +3,7 @@ tfd: C&C The First Decade (English)
|
||||
data1.hdr: bef3a08c3fc1b1caf28ca0dbb97c1f900005930e
|
||||
data1.cab: 12ad6113a6890a1b4d5651a75378c963eaf513b9
|
||||
Install:
|
||||
extract-iscab: data1.hdr
|
||||
ExtractIscab: data1.hdr
|
||||
Volumes:
|
||||
6: data6.cab
|
||||
7: data7.cab
|
||||
@@ -12,7 +12,7 @@ tfd: C&C The First Decade (English)
|
||||
^SupportDir|Content/ts/tibsun.mix: Tiberian Sun\SUN\TIBSUN.MIX
|
||||
^SupportDir|Content/ts/expand01.mix: Tiberian Sun\SUN\expand01.mix
|
||||
^SupportDir|Content/ts/firestorm/scores01.mix: Tiberian Sun\SUN\scores01.mix
|
||||
extract-raw: ^SupportDir|Content/ts/tibsun.mix
|
||||
ExtractRaw: ^SupportDir|Content/ts/tibsun.mix
|
||||
^SupportDir|Content/ts/cache.mix:
|
||||
Offset: 300
|
||||
Length: 169176
|
||||
@@ -55,8 +55,8 @@ tfd: C&C The First Decade (English)
|
||||
^SupportDir|Content/ts/temperat.mix:
|
||||
Offset: 73056940
|
||||
Length: 2037606
|
||||
delete: ^SupportDir|Content/ts/tibsun.mix
|
||||
extract-raw: ^SupportDir|Content/ts/expand01.mix
|
||||
Delete: ^SupportDir|Content/ts/tibsun.mix
|
||||
ExtractRaw: ^SupportDir|Content/ts/expand01.mix
|
||||
^SupportDir|Content/ts/firestorm/m_emp.vxl:
|
||||
Offset: 2187652
|
||||
Length: 24652
|
||||
@@ -591,4 +591,4 @@ tfd: C&C The First Decade (English)
|
||||
^SupportDir|Content/ts/firestorm/sounds01.mix:
|
||||
Offset: 28229956
|
||||
Length: 990096
|
||||
delete: ^SupportDir|Content/ts/expand01.mix
|
||||
Delete: ^SupportDir|Content/ts/expand01.mix
|
||||
|
||||
@@ -6,10 +6,10 @@ origin: C&C The Ultimate Collection (Origin version, English)
|
||||
IDFiles:
|
||||
GDFBinary_en_US.dll: 4bb56a449bd0003e7ae67625d90a11ae169319d6
|
||||
Install:
|
||||
copy: .
|
||||
Copy: .
|
||||
^SupportDir|Content/ts/scores.mix: SCORES.MIX
|
||||
^SupportDir|Content/ts/firestorm/scores01.mix: scores01.mix
|
||||
extract-raw: TIBSUN.MIX
|
||||
ExtractRaw: TIBSUN.MIX
|
||||
^SupportDir|Content/ts/cache.mix:
|
||||
Offset: 300
|
||||
Length: 169176
|
||||
@@ -52,7 +52,7 @@ origin: C&C The Ultimate Collection (Origin version, English)
|
||||
^SupportDir|Content/ts/temperat.mix:
|
||||
Offset: 73056940
|
||||
Length: 2037606
|
||||
extract-raw: expand01.mix
|
||||
ExtractRaw: expand01.mix
|
||||
^SupportDir|Content/ts/firestorm/m_emp.vxl:
|
||||
Offset: 2187652
|
||||
Length: 24652
|
||||
|
||||
@@ -3,9 +3,9 @@ tibsun: Tiberian Sun (GDI or Nod Disc, English)
|
||||
README.TXT: 45745c4a0c888317ec900208a426472779c42bf7
|
||||
AUTOPLAY.WAV: 2dfce5d00f98b641849c29942b651f4e98d30e30
|
||||
Install:
|
||||
copy: .
|
||||
Copy: .
|
||||
^SupportDir|Content/ts/scores.mix: SCORES.MIX
|
||||
extract-raw: INSTALL/TIBSUN.MIX
|
||||
ExtractRaw: INSTALL/TIBSUN.MIX
|
||||
^SupportDir|Content/ts/cache.mix:
|
||||
Offset: 300
|
||||
Length: 169176
|
||||
|
||||
Reference in New Issue
Block a user