Merge pull request #11536 from pchote/tfd
Add The First Decade metadata to the new content installer.
This commit is contained in:
@@ -57,8 +57,6 @@ namespace OpenRA.FileSystem
|
||||
return new BigFile(this, filename);
|
||||
if (filename.EndsWith(".bag", StringComparison.InvariantCultureIgnoreCase))
|
||||
return new BagFile(this, filename);
|
||||
if (filename.EndsWith(".hdr", StringComparison.InvariantCultureIgnoreCase))
|
||||
return new InstallShieldCABExtractor(this, filename);
|
||||
|
||||
IReadOnlyPackage parent;
|
||||
string subPath = null;
|
||||
|
||||
@@ -1,508 +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 System.Text.RegularExpressions;
|
||||
using ICSharpCode.SharpZipLib.Zip.Compression;
|
||||
|
||||
namespace OpenRA.FileSystem
|
||||
{
|
||||
public sealed class InstallShieldCABExtractor : IReadOnlyPackage
|
||||
{
|
||||
const uint FileSplit = 0x1;
|
||||
const uint FileObfuscated = 0x2;
|
||||
const uint FileCompressed = 0x4;
|
||||
const uint FileInvalid = 0x8;
|
||||
|
||||
const uint LinkPrev = 0x1;
|
||||
const uint LinkNext = 0x2;
|
||||
const uint MaxFileGroupCount = 71;
|
||||
|
||||
#region Nested Structs
|
||||
|
||||
struct FileGroup
|
||||
{
|
||||
public readonly string Name;
|
||||
public readonly uint FirstFile;
|
||||
public readonly uint LastFile;
|
||||
|
||||
public FileGroup(Stream reader, long offset)
|
||||
{
|
||||
var nameOffset = reader.ReadUInt32();
|
||||
/* unknown */ reader.ReadBytes(18);
|
||||
FirstFile = reader.ReadUInt32();
|
||||
LastFile = reader.ReadUInt32();
|
||||
|
||||
reader.Seek(offset + nameOffset, SeekOrigin.Begin);
|
||||
Name = reader.ReadASCIIZ();
|
||||
}
|
||||
}
|
||||
|
||||
struct VolumeHeader
|
||||
{
|
||||
public readonly uint DataOffset;
|
||||
public readonly uint DataOffsetHigh;
|
||||
public readonly uint FirstFileIndex;
|
||||
public readonly uint LastFileIndex;
|
||||
|
||||
public readonly uint FirstFileOffset;
|
||||
public readonly uint FirstFileOffsetHigh;
|
||||
public readonly uint FirstFileSizeExpanded;
|
||||
public readonly uint FirstFileSizeExpandedHigh;
|
||||
|
||||
public readonly uint FirstFileSizeCompressed;
|
||||
public readonly uint FirstFileSizeCompressedHigh;
|
||||
public readonly uint LastFileOffset;
|
||||
public readonly uint LastFileOffsetHigh;
|
||||
|
||||
public readonly uint LastFileSizeExpanded;
|
||||
public readonly uint LastFileSizeExpandedHigh;
|
||||
public readonly uint LastFileSizeCompressed;
|
||||
public readonly uint LastFileSizeCompressedHigh;
|
||||
|
||||
public VolumeHeader(Stream reader)
|
||||
{
|
||||
DataOffset = reader.ReadUInt32();
|
||||
DataOffsetHigh = reader.ReadUInt32();
|
||||
|
||||
FirstFileIndex = reader.ReadUInt32();
|
||||
LastFileIndex = reader.ReadUInt32();
|
||||
FirstFileOffset = reader.ReadUInt32();
|
||||
FirstFileOffsetHigh = reader.ReadUInt32();
|
||||
|
||||
FirstFileSizeExpanded = reader.ReadUInt32();
|
||||
FirstFileSizeExpandedHigh = reader.ReadUInt32();
|
||||
FirstFileSizeCompressed = reader.ReadUInt32();
|
||||
FirstFileSizeCompressedHigh = reader.ReadUInt32();
|
||||
|
||||
LastFileOffset = reader.ReadUInt32();
|
||||
LastFileOffsetHigh = reader.ReadUInt32();
|
||||
LastFileSizeExpanded = reader.ReadUInt32();
|
||||
LastFileSizeExpandedHigh = reader.ReadUInt32();
|
||||
|
||||
LastFileSizeCompressed = reader.ReadUInt32();
|
||||
LastFileSizeCompressedHigh = reader.ReadUInt32();
|
||||
}
|
||||
}
|
||||
|
||||
struct CommonHeader
|
||||
{
|
||||
public const long Size = 16;
|
||||
public readonly uint Version;
|
||||
public readonly uint VolumeInfo;
|
||||
public readonly long CabDescriptorOffset;
|
||||
public readonly uint CabDescriptorSize;
|
||||
|
||||
public CommonHeader(Stream reader)
|
||||
{
|
||||
Version = reader.ReadUInt32();
|
||||
VolumeInfo = reader.ReadUInt32();
|
||||
CabDescriptorOffset = reader.ReadUInt32();
|
||||
CabDescriptorSize = reader.ReadUInt32();
|
||||
}
|
||||
}
|
||||
|
||||
struct CabDescriptor
|
||||
{
|
||||
public readonly long FileTableOffset;
|
||||
public readonly uint FileTableSize;
|
||||
public readonly uint FileTableSize2;
|
||||
public readonly uint DirectoryCount;
|
||||
|
||||
public readonly uint FileCount;
|
||||
public readonly long FileTableOffset2;
|
||||
|
||||
public CabDescriptor(Stream reader, CommonHeader commonHeader)
|
||||
{
|
||||
reader.Seek(commonHeader.CabDescriptorOffset + 12, SeekOrigin.Begin);
|
||||
FileTableOffset = reader.ReadUInt32();
|
||||
/* unknown */ reader.ReadUInt32();
|
||||
FileTableSize = reader.ReadUInt32();
|
||||
|
||||
FileTableSize2 = reader.ReadUInt32();
|
||||
DirectoryCount = reader.ReadUInt32();
|
||||
/* unknown */ reader.ReadBytes(8);
|
||||
FileCount = reader.ReadUInt32();
|
||||
|
||||
FileTableOffset2 = reader.ReadUInt32();
|
||||
}
|
||||
}
|
||||
|
||||
struct FileDescriptor
|
||||
{
|
||||
public readonly ushort Flags;
|
||||
public readonly uint ExpandedSize;
|
||||
public readonly uint CompressedSize;
|
||||
public readonly uint DataOffset;
|
||||
|
||||
public readonly byte[] MD5;
|
||||
public readonly uint NameOffset;
|
||||
public readonly ushort DirectoryIndex;
|
||||
public readonly uint LinkToPrevious;
|
||||
|
||||
public readonly uint LinkToNext;
|
||||
public readonly byte LinkFlags;
|
||||
public readonly ushort Volume;
|
||||
public readonly string Filename;
|
||||
|
||||
public FileDescriptor(Stream reader, long tableOffset)
|
||||
{
|
||||
Flags = reader.ReadUInt16();
|
||||
ExpandedSize = reader.ReadUInt32();
|
||||
/* unknown */ reader.ReadUInt32();
|
||||
CompressedSize = reader.ReadUInt32();
|
||||
|
||||
/* unknown */ reader.ReadUInt32();
|
||||
DataOffset = reader.ReadUInt32();
|
||||
/* unknown */ reader.ReadUInt32();
|
||||
MD5 = reader.ReadBytes(16);
|
||||
|
||||
/* unknown */ reader.ReadBytes(16);
|
||||
NameOffset = reader.ReadUInt32();
|
||||
DirectoryIndex = reader.ReadUInt16();
|
||||
/* unknown */ reader.ReadBytes(12);
|
||||
LinkToPrevious = reader.ReadUInt32();
|
||||
LinkToNext = reader.ReadUInt32();
|
||||
|
||||
LinkFlags = reader.ReadBytes(1)[0];
|
||||
Volume = reader.ReadUInt16();
|
||||
var posSave = reader.Position;
|
||||
|
||||
reader.Seek(tableOffset + NameOffset, SeekOrigin.Begin);
|
||||
Filename = reader.ReadASCIIZ();
|
||||
reader.Seek(posSave, SeekOrigin.Begin);
|
||||
}
|
||||
}
|
||||
|
||||
class CabReader : IDisposable
|
||||
{
|
||||
readonly FileSystem context;
|
||||
readonly FileDescriptor fileDes;
|
||||
public uint RemainingArchiveStream;
|
||||
public uint RemainingFileStream;
|
||||
readonly uint index;
|
||||
readonly string commonName;
|
||||
ushort volumeNumber;
|
||||
Stream cabFile;
|
||||
|
||||
public CabReader(FileSystem context, FileDescriptor fileDes, uint index, string commonName)
|
||||
{
|
||||
this.fileDes = fileDes;
|
||||
this.index = index;
|
||||
this.commonName = commonName;
|
||||
this.context = context;
|
||||
volumeNumber = (ushort)(fileDes.Volume - 1u);
|
||||
RemainingArchiveStream = 0;
|
||||
if ((fileDes.Flags & FileCompressed) > 0)
|
||||
RemainingFileStream = fileDes.CompressedSize;
|
||||
else
|
||||
RemainingFileStream = fileDes.ExpandedSize;
|
||||
|
||||
cabFile = null;
|
||||
NextFile(context);
|
||||
}
|
||||
|
||||
public void CopyTo(Stream dest)
|
||||
{
|
||||
if ((fileDes.Flags & FileCompressed) != 0)
|
||||
{
|
||||
var inf = new Inflater(true);
|
||||
var buffer = new byte[165535];
|
||||
do
|
||||
{
|
||||
var bytesToExtract = cabFile.ReadUInt16();
|
||||
RemainingArchiveStream -= 2u;
|
||||
RemainingFileStream -= 2u;
|
||||
inf.SetInput(GetBytes(bytesToExtract));
|
||||
RemainingFileStream -= bytesToExtract;
|
||||
while (!inf.IsNeedingInput)
|
||||
{
|
||||
var inflated = inf.Inflate(buffer);
|
||||
dest.Write(buffer, 0, inflated);
|
||||
}
|
||||
|
||||
inf.Reset();
|
||||
}
|
||||
while (RemainingFileStream > 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
do
|
||||
{
|
||||
RemainingFileStream -= RemainingArchiveStream;
|
||||
dest.Write(GetBytes(RemainingArchiveStream), 0, (int)RemainingArchiveStream);
|
||||
}
|
||||
while (RemainingFileStream > 0);
|
||||
}
|
||||
}
|
||||
|
||||
public byte[] GetBytes(uint count)
|
||||
{
|
||||
if (count < RemainingArchiveStream)
|
||||
{
|
||||
RemainingArchiveStream -= count;
|
||||
return cabFile.ReadBytes((int)count);
|
||||
}
|
||||
else
|
||||
{
|
||||
var outArray = new byte[count];
|
||||
var read = cabFile.Read(outArray, 0, (int)RemainingArchiveStream);
|
||||
if (RemainingFileStream > RemainingArchiveStream)
|
||||
{
|
||||
NextFile(context);
|
||||
RemainingArchiveStream -= (uint)cabFile.Read(outArray, read, (int)count - read);
|
||||
}
|
||||
|
||||
return outArray;
|
||||
}
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
cabFile.Dispose();
|
||||
}
|
||||
|
||||
void NextFile(FileSystem context)
|
||||
{
|
||||
if (cabFile != null)
|
||||
cabFile.Dispose();
|
||||
|
||||
++volumeNumber;
|
||||
cabFile = context.Open("{0}{1}.cab".F(commonName, volumeNumber));
|
||||
if (cabFile.ReadUInt32() != 0x28635349)
|
||||
throw new InvalidDataException("Not an Installshield CAB package");
|
||||
|
||||
uint fileOffset;
|
||||
if ((fileDes.Flags & FileSplit) != 0)
|
||||
{
|
||||
cabFile.Seek(CommonHeader.Size, SeekOrigin.Current);
|
||||
var head = new VolumeHeader(cabFile);
|
||||
if (index == head.LastFileIndex)
|
||||
{
|
||||
if ((fileDes.Flags & FileCompressed) != 0)
|
||||
RemainingArchiveStream = head.LastFileSizeCompressed;
|
||||
else
|
||||
RemainingArchiveStream = head.LastFileSizeExpanded;
|
||||
|
||||
fileOffset = head.LastFileOffset;
|
||||
}
|
||||
else if (index == head.FirstFileIndex)
|
||||
{
|
||||
if ((fileDes.Flags & FileCompressed) != 0)
|
||||
RemainingArchiveStream = head.FirstFileSizeCompressed;
|
||||
else
|
||||
RemainingArchiveStream = head.FirstFileSizeExpanded;
|
||||
|
||||
fileOffset = head.FirstFileOffset;
|
||||
}
|
||||
else
|
||||
throw new Exception("Cannot Resolve Remaining Stream");
|
||||
}
|
||||
else
|
||||
{
|
||||
if ((fileDes.Flags & FileCompressed) != 0)
|
||||
RemainingArchiveStream = fileDes.CompressedSize;
|
||||
else
|
||||
RemainingArchiveStream = fileDes.ExpandedSize;
|
||||
|
||||
fileOffset = fileDes.DataOffset;
|
||||
}
|
||||
|
||||
cabFile.Seek(fileOffset, SeekOrigin.Begin);
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
readonly Stream hdrFile;
|
||||
readonly CommonHeader commonHeader;
|
||||
readonly CabDescriptor cabDescriptor;
|
||||
readonly List<uint> directoryTable;
|
||||
readonly Dictionary<uint, string> directoryNames = new Dictionary<uint, string>();
|
||||
readonly Dictionary<uint, FileDescriptor> fileDescriptors = new Dictionary<uint, FileDescriptor>();
|
||||
readonly Dictionary<string, uint> index = new Dictionary<string, uint>();
|
||||
readonly FileSystem context;
|
||||
|
||||
public string Name { get; private set; }
|
||||
public IEnumerable<string> Contents { get { return index.Keys; } }
|
||||
|
||||
public InstallShieldCABExtractor(FileSystem context, string hdrFilename)
|
||||
{
|
||||
var fileGroups = new List<FileGroup>();
|
||||
var fileGroupOffsets = new List<uint>();
|
||||
|
||||
hdrFile = context.Open(hdrFilename);
|
||||
this.context = context;
|
||||
|
||||
// Strips archive number AND file extension
|
||||
Name = Regex.Replace(hdrFilename, @"\d*\.[^\.]*$", "");
|
||||
var signature = hdrFile.ReadUInt32();
|
||||
|
||||
if (signature != 0x28635349)
|
||||
throw new InvalidDataException("Not an Installshield CAB package");
|
||||
|
||||
commonHeader = new CommonHeader(hdrFile);
|
||||
cabDescriptor = new CabDescriptor(hdrFile, commonHeader);
|
||||
/* unknown */ hdrFile.ReadBytes(14);
|
||||
|
||||
for (var i = 0U; i < MaxFileGroupCount; ++i)
|
||||
fileGroupOffsets.Add(hdrFile.ReadUInt32());
|
||||
|
||||
hdrFile.Seek(commonHeader.CabDescriptorOffset + cabDescriptor.FileTableOffset, SeekOrigin.Begin);
|
||||
directoryTable = new List<uint>();
|
||||
|
||||
for (var i = 0U; i < cabDescriptor.DirectoryCount; ++i)
|
||||
directoryTable.Add(hdrFile.ReadUInt32());
|
||||
|
||||
foreach (var offset in fileGroupOffsets)
|
||||
{
|
||||
var nextOffset = offset;
|
||||
while (nextOffset != 0)
|
||||
{
|
||||
hdrFile.Seek((long)nextOffset + 4 + commonHeader.CabDescriptorOffset, SeekOrigin.Begin);
|
||||
var descriptorOffset = hdrFile.ReadUInt32();
|
||||
nextOffset = hdrFile.ReadUInt32();
|
||||
hdrFile.Seek(descriptorOffset + commonHeader.CabDescriptorOffset, SeekOrigin.Begin);
|
||||
|
||||
fileGroups.Add(new FileGroup(hdrFile, commonHeader.CabDescriptorOffset));
|
||||
}
|
||||
}
|
||||
|
||||
hdrFile.Seek(commonHeader.CabDescriptorOffset + cabDescriptor.FileTableOffset + cabDescriptor.FileTableOffset2, SeekOrigin.Begin);
|
||||
foreach (var fileGroup in fileGroups)
|
||||
{
|
||||
for (var i = fileGroup.FirstFile; i <= fileGroup.LastFile; ++i)
|
||||
{
|
||||
AddFileDescriptorToList(i);
|
||||
var fileDescriptor = fileDescriptors[i];
|
||||
var fullFilePath = "{0}\\{1}\\{2}".F(fileGroup.Name, DirectoryName(fileDescriptor.DirectoryIndex), fileDescriptor.Filename);
|
||||
index.Add(fullFilePath, i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public string DirectoryName(uint index)
|
||||
{
|
||||
if (directoryNames.ContainsKey(index))
|
||||
return directoryNames[index];
|
||||
|
||||
hdrFile.Seek(commonHeader.CabDescriptorOffset +
|
||||
cabDescriptor.FileTableOffset +
|
||||
directoryTable[(int)index],
|
||||
SeekOrigin.Begin);
|
||||
|
||||
var test = hdrFile.ReadASCIIZ();
|
||||
return test;
|
||||
}
|
||||
|
||||
public bool Contains(string filename)
|
||||
{
|
||||
return index.ContainsKey(filename);
|
||||
}
|
||||
|
||||
public uint DirectoryCount()
|
||||
{
|
||||
return cabDescriptor.DirectoryCount;
|
||||
}
|
||||
|
||||
public string FileName(uint index)
|
||||
{
|
||||
if (!fileDescriptors.ContainsKey(index))
|
||||
AddFileDescriptorToList(index);
|
||||
|
||||
return fileDescriptors[index].Filename;
|
||||
}
|
||||
|
||||
void AddFileDescriptorToList(uint index)
|
||||
{
|
||||
hdrFile.Seek(commonHeader.CabDescriptorOffset +
|
||||
cabDescriptor.FileTableOffset +
|
||||
cabDescriptor.FileTableOffset2 +
|
||||
index * 0x57,
|
||||
SeekOrigin.Begin);
|
||||
|
||||
var fd = new FileDescriptor(hdrFile,
|
||||
commonHeader.CabDescriptorOffset + cabDescriptor.FileTableOffset);
|
||||
|
||||
fileDescriptors.Add(index, fd);
|
||||
}
|
||||
|
||||
public uint FileCount()
|
||||
{
|
||||
return cabDescriptor.FileCount;
|
||||
}
|
||||
|
||||
public void ExtractFile(uint index, string fileName)
|
||||
{
|
||||
Directory.CreateDirectory(Path.GetDirectoryName(fileName));
|
||||
using (var destfile = File.Open(fileName, FileMode.Create))
|
||||
GetContentById(index, destfile);
|
||||
}
|
||||
|
||||
public Stream GetContentById(uint index)
|
||||
{
|
||||
var fileDes = fileDescriptors[index];
|
||||
if ((fileDes.Flags & FileInvalid) != 0)
|
||||
throw new Exception("File Invalid");
|
||||
|
||||
if ((fileDes.LinkFlags & LinkPrev) != 0)
|
||||
return GetContentById(fileDes.LinkToPrevious);
|
||||
|
||||
if ((fileDes.Flags & FileObfuscated) != 0)
|
||||
throw new NotImplementedException("Haven't implemented obfuscated files");
|
||||
|
||||
var output = new MemoryStream((int)fileDes.ExpandedSize);
|
||||
|
||||
using (var reader = new CabReader(context, fileDes, index, Name))
|
||||
reader.CopyTo(output);
|
||||
|
||||
if (output.Length != fileDes.ExpandedSize)
|
||||
throw new Exception("Did not fully extract Expected = {0}, Got = {1}".F(fileDes.ExpandedSize, output.Length));
|
||||
|
||||
output.Position = 0;
|
||||
return output;
|
||||
}
|
||||
|
||||
public void GetContentById(uint index, Stream output)
|
||||
{
|
||||
var fileDes = fileDescriptors[index];
|
||||
if ((fileDes.Flags & FileInvalid) != 0)
|
||||
throw new Exception("File Invalid");
|
||||
|
||||
if ((fileDes.LinkFlags & LinkPrev) != 0)
|
||||
{
|
||||
GetContentById(fileDes.LinkToPrevious, output);
|
||||
return;
|
||||
}
|
||||
|
||||
if ((fileDes.Flags & FileObfuscated) != 0)
|
||||
throw new NotImplementedException("Haven't implemented obfuscated files");
|
||||
|
||||
using (var reader = new CabReader(context, fileDes, index, Name))
|
||||
reader.CopyTo(output);
|
||||
|
||||
if (output.Length != fileDes.ExpandedSize)
|
||||
throw new Exception("Did not fully extract Expected = {0}, Got = {1}".F(fileDes.ExpandedSize, output.Length));
|
||||
}
|
||||
|
||||
public Stream GetStream(string fileName)
|
||||
{
|
||||
return GetContentById(index[fileName]);
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
hdrFile.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -250,7 +250,6 @@
|
||||
<ItemGroup>
|
||||
<Compile Include="FileSystem\D2kSoundResources.cs" />
|
||||
<Compile Include="FileSystem\Folder.cs" />
|
||||
<Compile Include="FileSystem\InstallShieldCABExtractor.cs" />
|
||||
<Compile Include="FileSystem\InstallShieldPackage.cs" />
|
||||
<Compile Include="FileSystem\MixFile.cs" />
|
||||
<Compile Include="FileSystem\Pak.cs" />
|
||||
|
||||
438
OpenRA.Mods.Common/FileFormats/InstallShieldCABCompression.cs
Normal file
438
OpenRA.Mods.Common/FileFormats/InstallShieldCABCompression.cs
Normal file
@@ -0,0 +1,438 @@
|
||||
#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.Text.RegularExpressions;
|
||||
using ICSharpCode.SharpZipLib.Zip.Compression;
|
||||
|
||||
namespace OpenRA.Mods.Common.FileFormats
|
||||
{
|
||||
public sealed class InstallShieldCABCompression
|
||||
{
|
||||
const uint MaxFileGroupCount = 71;
|
||||
|
||||
enum CABFlags : ushort
|
||||
{
|
||||
FileSplit = 0x1,
|
||||
FileObfuscated = 0x2,
|
||||
FileCompressed = 0x4,
|
||||
FileInvalid = 0x8,
|
||||
}
|
||||
|
||||
enum LinkFlags : byte
|
||||
{
|
||||
Prev = 0x1,
|
||||
Next = 0x2
|
||||
}
|
||||
|
||||
struct FileGroup
|
||||
{
|
||||
public readonly string Name;
|
||||
public readonly uint FirstFile;
|
||||
public readonly uint LastFile;
|
||||
|
||||
public FileGroup(Stream stream, long offset)
|
||||
{
|
||||
var nameOffset = stream.ReadUInt32();
|
||||
stream.Position += 18;
|
||||
FirstFile = stream.ReadUInt32();
|
||||
LastFile = stream.ReadUInt32();
|
||||
|
||||
var pos = stream.Position;
|
||||
stream.Position = offset + nameOffset;
|
||||
Name = stream.ReadASCIIZ();
|
||||
stream.Position = pos;
|
||||
}
|
||||
}
|
||||
|
||||
struct CabDescriptor
|
||||
{
|
||||
public readonly long FileTableOffset;
|
||||
public readonly uint FileTableSize;
|
||||
public readonly uint FileTableSize2;
|
||||
public readonly uint DirectoryCount;
|
||||
|
||||
public readonly uint FileCount;
|
||||
public readonly long FileTableOffset2;
|
||||
|
||||
public CabDescriptor(Stream stream)
|
||||
{
|
||||
FileTableOffset = stream.ReadUInt32();
|
||||
stream.Position += 4;
|
||||
FileTableSize = stream.ReadUInt32();
|
||||
FileTableSize2 = stream.ReadUInt32();
|
||||
DirectoryCount = stream.ReadUInt32();
|
||||
stream.Position += 8;
|
||||
FileCount = stream.ReadUInt32();
|
||||
FileTableOffset2 = stream.ReadUInt32();
|
||||
}
|
||||
}
|
||||
|
||||
struct DirectoryDescriptor
|
||||
{
|
||||
public readonly string Name;
|
||||
|
||||
public DirectoryDescriptor(Stream stream, long nameTableOffset)
|
||||
{
|
||||
var nameOffset = stream.ReadUInt32();
|
||||
var pos = stream.Position;
|
||||
|
||||
stream.Position = nameTableOffset + nameOffset;
|
||||
|
||||
Name = stream.ReadASCIIZ();
|
||||
stream.Position = pos;
|
||||
}
|
||||
}
|
||||
|
||||
struct FileDescriptor
|
||||
{
|
||||
public readonly uint Index;
|
||||
public readonly CABFlags Flags;
|
||||
public readonly uint ExpandedSize;
|
||||
public readonly uint CompressedSize;
|
||||
public readonly uint DataOffset;
|
||||
|
||||
public readonly byte[] MD5;
|
||||
public readonly uint NameOffset;
|
||||
public readonly ushort DirectoryIndex;
|
||||
public readonly uint LinkToPrevious;
|
||||
|
||||
public readonly uint LinkToNext;
|
||||
public readonly LinkFlags LinkFlags;
|
||||
public readonly ushort Volume;
|
||||
public readonly string Filename;
|
||||
|
||||
public FileDescriptor(Stream stream, uint index, long tableOffset)
|
||||
{
|
||||
Index = index;
|
||||
Flags = (CABFlags)stream.ReadUInt16();
|
||||
ExpandedSize = stream.ReadUInt32();
|
||||
stream.Position += 4;
|
||||
CompressedSize = stream.ReadUInt32();
|
||||
|
||||
stream.Position += 4;
|
||||
DataOffset = stream.ReadUInt32();
|
||||
stream.Position += 4;
|
||||
MD5 = stream.ReadBytes(16);
|
||||
|
||||
stream.Position += 16;
|
||||
NameOffset = stream.ReadUInt32();
|
||||
DirectoryIndex = stream.ReadUInt16();
|
||||
stream.Position += 12;
|
||||
LinkToPrevious = stream.ReadUInt32();
|
||||
LinkToNext = stream.ReadUInt32();
|
||||
|
||||
LinkFlags = (LinkFlags)stream.ReadUInt8();
|
||||
Volume = stream.ReadUInt16();
|
||||
|
||||
var pos = stream.Position;
|
||||
stream.Position = tableOffset + NameOffset;
|
||||
Filename = stream.ReadASCIIZ();
|
||||
stream.Position = pos;
|
||||
}
|
||||
}
|
||||
|
||||
struct CommonHeader
|
||||
{
|
||||
public const long Size = 16;
|
||||
public readonly uint Version;
|
||||
public readonly uint VolumeInfo;
|
||||
public readonly long CabDescriptorOffset;
|
||||
public readonly uint CabDescriptorSize;
|
||||
|
||||
public CommonHeader(Stream stream)
|
||||
{
|
||||
Version = stream.ReadUInt32();
|
||||
VolumeInfo = stream.ReadUInt32();
|
||||
CabDescriptorOffset = stream.ReadUInt32();
|
||||
CabDescriptorSize = stream.ReadUInt32();
|
||||
}
|
||||
}
|
||||
|
||||
struct VolumeHeader
|
||||
{
|
||||
public readonly uint DataOffset;
|
||||
public readonly uint DataOffsetHigh;
|
||||
public readonly uint FirstFileIndex;
|
||||
public readonly uint LastFileIndex;
|
||||
|
||||
public readonly uint FirstFileOffset;
|
||||
public readonly uint FirstFileOffsetHigh;
|
||||
public readonly uint FirstFileSizeExpanded;
|
||||
public readonly uint FirstFileSizeExpandedHigh;
|
||||
|
||||
public readonly uint FirstFileSizeCompressed;
|
||||
public readonly uint FirstFileSizeCompressedHigh;
|
||||
public readonly uint LastFileOffset;
|
||||
public readonly uint LastFileOffsetHigh;
|
||||
|
||||
public readonly uint LastFileSizeExpanded;
|
||||
public readonly uint LastFileSizeExpandedHigh;
|
||||
public readonly uint LastFileSizeCompressed;
|
||||
public readonly uint LastFileSizeCompressedHigh;
|
||||
|
||||
public VolumeHeader(Stream stream)
|
||||
{
|
||||
DataOffset = stream.ReadUInt32();
|
||||
DataOffsetHigh = stream.ReadUInt32();
|
||||
|
||||
FirstFileIndex = stream.ReadUInt32();
|
||||
LastFileIndex = stream.ReadUInt32();
|
||||
FirstFileOffset = stream.ReadUInt32();
|
||||
FirstFileOffsetHigh = stream.ReadUInt32();
|
||||
|
||||
FirstFileSizeExpanded = stream.ReadUInt32();
|
||||
FirstFileSizeExpandedHigh = stream.ReadUInt32();
|
||||
FirstFileSizeCompressed = stream.ReadUInt32();
|
||||
FirstFileSizeCompressedHigh = stream.ReadUInt32();
|
||||
|
||||
LastFileOffset = stream.ReadUInt32();
|
||||
LastFileOffsetHigh = stream.ReadUInt32();
|
||||
LastFileSizeExpanded = stream.ReadUInt32();
|
||||
LastFileSizeExpandedHigh = stream.ReadUInt32();
|
||||
|
||||
LastFileSizeCompressed = stream.ReadUInt32();
|
||||
LastFileSizeCompressedHigh = stream.ReadUInt32();
|
||||
}
|
||||
}
|
||||
|
||||
class CabExtracter
|
||||
{
|
||||
readonly FileDescriptor file;
|
||||
readonly Dictionary<int, Stream> volumes;
|
||||
|
||||
uint remainingInArchive;
|
||||
uint toExtract;
|
||||
|
||||
int currentVolumeID;
|
||||
Stream currentVolume;
|
||||
|
||||
public CabExtracter(FileDescriptor file, Dictionary<int, Stream> volumes)
|
||||
{
|
||||
this.file = file;
|
||||
this.volumes = volumes;
|
||||
|
||||
remainingInArchive = 0;
|
||||
toExtract = file.Flags.HasFlag(CABFlags.FileCompressed) ? file.CompressedSize : file.ExpandedSize;
|
||||
|
||||
SetVolume(file.Volume);
|
||||
}
|
||||
|
||||
public void CopyTo(Stream output, Action<int> onProgress)
|
||||
{
|
||||
if (file.Flags.HasFlag(CABFlags.FileCompressed))
|
||||
{
|
||||
var inf = new Inflater(true);
|
||||
var buffer = new byte[165535];
|
||||
do
|
||||
{
|
||||
var bytesToExtract = currentVolume.ReadUInt16();
|
||||
remainingInArchive -= 2;
|
||||
toExtract -= 2;
|
||||
inf.SetInput(GetBytes(bytesToExtract));
|
||||
toExtract -= bytesToExtract;
|
||||
while (!inf.IsNeedingInput)
|
||||
{
|
||||
if (onProgress != null)
|
||||
onProgress((int)(100 * output.Position / file.ExpandedSize));
|
||||
|
||||
var inflated = inf.Inflate(buffer);
|
||||
output.Write(buffer, 0, inflated);
|
||||
}
|
||||
|
||||
inf.Reset();
|
||||
} while (toExtract > 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
do
|
||||
{
|
||||
if (onProgress != null)
|
||||
onProgress((int)(100 * output.Position / file.ExpandedSize));
|
||||
|
||||
toExtract -= remainingInArchive;
|
||||
output.Write(GetBytes(remainingInArchive), 0, (int)remainingInArchive);
|
||||
} while (toExtract > 0);
|
||||
}
|
||||
}
|
||||
|
||||
public byte[] GetBytes(uint count)
|
||||
{
|
||||
if (count < remainingInArchive)
|
||||
{
|
||||
remainingInArchive -= count;
|
||||
return currentVolume.ReadBytes((int)count);
|
||||
}
|
||||
else
|
||||
{
|
||||
var outArray = new byte[count];
|
||||
var read = currentVolume.Read(outArray, 0, (int)remainingInArchive);
|
||||
if (toExtract > remainingInArchive)
|
||||
{
|
||||
SetVolume(currentVolumeID + 1);
|
||||
remainingInArchive -= (uint)currentVolume.Read(outArray, read, (int)count - read);
|
||||
}
|
||||
|
||||
return outArray;
|
||||
}
|
||||
}
|
||||
|
||||
void SetVolume(int newVolume)
|
||||
{
|
||||
currentVolumeID = newVolume;
|
||||
if (!volumes.TryGetValue(currentVolumeID, out currentVolume))
|
||||
throw new FileNotFoundException("Volume {0} is not available".F(currentVolumeID));
|
||||
|
||||
currentVolume.Position = 0;
|
||||
if (currentVolume.ReadUInt32() != 0x28635349)
|
||||
throw new InvalidDataException("Not an Installshield CAB package");
|
||||
|
||||
uint fileOffset;
|
||||
if (file.Flags.HasFlag(CABFlags.FileSplit))
|
||||
{
|
||||
currentVolume.Position += CommonHeader.Size;
|
||||
var head = new VolumeHeader(currentVolume);
|
||||
if (file.Index == head.LastFileIndex)
|
||||
{
|
||||
if (file.Flags.HasFlag(CABFlags.FileCompressed))
|
||||
remainingInArchive = head.LastFileSizeCompressed;
|
||||
else
|
||||
remainingInArchive = head.LastFileSizeExpanded;
|
||||
|
||||
fileOffset = head.LastFileOffset;
|
||||
}
|
||||
else if (file.Index == head.FirstFileIndex)
|
||||
{
|
||||
if (file.Flags.HasFlag(CABFlags.FileCompressed))
|
||||
remainingInArchive = head.FirstFileSizeCompressed;
|
||||
else
|
||||
remainingInArchive = head.FirstFileSizeExpanded;
|
||||
|
||||
fileOffset = head.FirstFileOffset;
|
||||
}
|
||||
else
|
||||
throw new Exception("Cannot Resolve Remaining Stream");
|
||||
}
|
||||
else
|
||||
{
|
||||
if (file.Flags.HasFlag(CABFlags.FileCompressed))
|
||||
remainingInArchive = file.CompressedSize;
|
||||
else
|
||||
remainingInArchive = file.ExpandedSize;
|
||||
|
||||
fileOffset = file.DataOffset;
|
||||
}
|
||||
|
||||
currentVolume.Position = fileOffset;
|
||||
}
|
||||
}
|
||||
|
||||
readonly Dictionary<string, FileDescriptor> index = new Dictionary<string, FileDescriptor>();
|
||||
readonly Dictionary<int, Stream> volumes;
|
||||
|
||||
public InstallShieldCABCompression(Stream header, Dictionary<int, Stream> volumes)
|
||||
{
|
||||
this.volumes = volumes;
|
||||
|
||||
if (header.ReadUInt32() != 0x28635349)
|
||||
throw new InvalidDataException("Not an Installshield CAB package");
|
||||
|
||||
header.Position += 8;
|
||||
var cabDescriptorOffset = header.ReadUInt32();
|
||||
header.Position = cabDescriptorOffset + 12;
|
||||
var cabDescriptor = new CabDescriptor(header);
|
||||
header.Position += 14;
|
||||
|
||||
var fileGroupOffsets = new uint[MaxFileGroupCount];
|
||||
for (var i = 0; i < MaxFileGroupCount; i++)
|
||||
fileGroupOffsets[i] = header.ReadUInt32();
|
||||
|
||||
header.Position = cabDescriptorOffset + cabDescriptor.FileTableOffset;
|
||||
var directories = new DirectoryDescriptor[cabDescriptor.DirectoryCount];
|
||||
for (var i = 0; i < directories.Length; i++)
|
||||
directories[i] = new DirectoryDescriptor(header, cabDescriptorOffset + cabDescriptor.FileTableOffset);
|
||||
|
||||
var fileGroups = new List<FileGroup>();
|
||||
foreach (var offset in fileGroupOffsets)
|
||||
{
|
||||
var nextOffset = offset;
|
||||
while (nextOffset != 0)
|
||||
{
|
||||
header.Position = cabDescriptorOffset + (long)nextOffset + 4;
|
||||
var descriptorOffset = header.ReadUInt32();
|
||||
nextOffset = header.ReadUInt32();
|
||||
header.Position = cabDescriptorOffset + descriptorOffset;
|
||||
|
||||
fileGroups.Add(new FileGroup(header, cabDescriptorOffset));
|
||||
}
|
||||
}
|
||||
|
||||
header.Position = cabDescriptorOffset + cabDescriptor.FileTableOffset + cabDescriptor.FileTableOffset2;
|
||||
foreach (var fileGroup in fileGroups)
|
||||
{
|
||||
for (var i = fileGroup.FirstFile; i <= fileGroup.LastFile; i++)
|
||||
{
|
||||
header.Position = cabDescriptorOffset + cabDescriptor.FileTableOffset + cabDescriptor.FileTableOffset2 + i * 0x57;
|
||||
var file = new FileDescriptor(header, i, cabDescriptorOffset + cabDescriptor.FileTableOffset);
|
||||
var path = "{0}\\{1}\\{2}".F(fileGroup.Name, directories[file.DirectoryIndex].Name, file.Filename);
|
||||
index[path] = file;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void ExtractFile(string filename, Stream output, Action<int> onProgress = null)
|
||||
{
|
||||
FileDescriptor file;
|
||||
if (!index.TryGetValue(filename, out file))
|
||||
throw new FileNotFoundException(filename);
|
||||
|
||||
ExtractFile(file, output, onProgress);
|
||||
}
|
||||
|
||||
void ExtractFile(FileDescriptor file, Stream output, Action<int> onProgress = null)
|
||||
{
|
||||
if (file.Flags.HasFlag(CABFlags.FileInvalid))
|
||||
throw new Exception("File Invalid");
|
||||
|
||||
if (file.LinkFlags.HasFlag(LinkFlags.Prev))
|
||||
{
|
||||
var prev = index.Values.First(f => f.Index == file.LinkToPrevious);
|
||||
ExtractFile(prev, output, onProgress);
|
||||
return;
|
||||
}
|
||||
|
||||
if (file.Flags.HasFlag(CABFlags.FileObfuscated))
|
||||
throw new NotImplementedException("Obfuscated files are not supported");
|
||||
|
||||
var extracter = new CabExtracter(file, volumes);
|
||||
extracter.CopyTo(output, onProgress);
|
||||
|
||||
if (output.Length != file.ExpandedSize)
|
||||
throw new InvalidDataException("File expanded to wrong length. Expected = {0}, Got = {1}".F(file.ExpandedSize, output.Length));
|
||||
}
|
||||
|
||||
public IReadOnlyDictionary<int, IEnumerable<string>> Contents
|
||||
{
|
||||
get
|
||||
{
|
||||
var contents = new Dictionary<int, List<string>>();
|
||||
foreach (var kv in index)
|
||||
contents.GetOrAdd(kv.Value.Volume).Add(kv.Key);
|
||||
|
||||
return new ReadOnlyDictionary<int, IEnumerable<string>>(contents
|
||||
.ToDictionary(x => x.Key, x => x.Value.AsEnumerable()));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -775,6 +775,8 @@
|
||||
<Compile Include="Traits\ExitsDebugOverlay.cs" />
|
||||
<Compile Include="Traits\World\ExitsDebugOverlayManager.cs" />
|
||||
<Compile Include="Activities\Air\FlyCircleTimed.cs" />
|
||||
<Compile Include="UtilityCommands\ListInstallShieldCabContentsCommand.cs" />
|
||||
<Compile Include="FileFormats\InstallShieldCABCompression.cs" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||
<PropertyGroup>
|
||||
|
||||
@@ -0,0 +1,41 @@
|
||||
#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 OpenRA.Mods.Common.FileFormats;
|
||||
|
||||
namespace OpenRA.Mods.Common.UtilityCommands
|
||||
{
|
||||
class ListInstallShieldCabContentsCommand : IUtilityCommand
|
||||
{
|
||||
public string Name { get { return "--list-installshield-cab"; } }
|
||||
|
||||
public bool ValidateArguments(string[] args)
|
||||
{
|
||||
return args.Length == 2;
|
||||
}
|
||||
|
||||
[Desc("DATA.HDR", "Lists the filenames contained within an Installshield CAB volume set")]
|
||||
public void Run(ModData modData, string[] args)
|
||||
{
|
||||
var package = new InstallShieldCABCompression(File.OpenRead(args[1]), null);
|
||||
foreach (var volume in package.Contents.OrderBy(kv => kv.Key))
|
||||
{
|
||||
Console.WriteLine("Volume: {0}", volume.Key);
|
||||
foreach (var filename in volume.Value)
|
||||
Console.WriteLine("\t{0}", filename);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -234,6 +234,25 @@ namespace OpenRA.Mods.Common.Widgets.Logic
|
||||
break;
|
||||
}
|
||||
|
||||
case "extract-iscab":
|
||||
{
|
||||
ExtractFromISCab(path, i.Value, extracted, m => message = m);
|
||||
break;
|
||||
}
|
||||
|
||||
case "delete":
|
||||
{
|
||||
var sourcePath = Path.Combine(path, i.Value.Value);
|
||||
|
||||
// Try as an absolute path
|
||||
if (!File.Exists(sourcePath))
|
||||
sourcePath = Platform.ResolvePath(i.Value.Value);
|
||||
|
||||
Log.Write("debug", "Deleting {0}", sourcePath);
|
||||
File.Delete(sourcePath);
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
Log.Write("debug", "Unknown installation command {0} - ignoring", i.Key);
|
||||
break;
|
||||
@@ -282,6 +301,11 @@ namespace OpenRA.Mods.Common.Widgets.Logic
|
||||
static void ExtractFromPackage(ExtractionType type, string path, MiniYaml actionYaml, List<string> extractedFiles, Action<string> updateMessage)
|
||||
{
|
||||
var sourcePath = Path.Combine(path, actionYaml.Value);
|
||||
|
||||
// Try as an absolute path
|
||||
if (!File.Exists(sourcePath))
|
||||
sourcePath = Platform.ResolvePath(actionYaml.Value);
|
||||
|
||||
using (var source = File.OpenRead(sourcePath))
|
||||
{
|
||||
foreach (var node in actionYaml.Nodes)
|
||||
@@ -344,6 +368,11 @@ namespace OpenRA.Mods.Common.Widgets.Logic
|
||||
static void ExtractFromMSCab(string path, MiniYaml actionYaml, List<string> extractedFiles, Action<string> updateMessage)
|
||||
{
|
||||
var sourcePath = Path.Combine(path, actionYaml.Value);
|
||||
|
||||
// Try as an absolute path
|
||||
if (!File.Exists(sourcePath))
|
||||
sourcePath = Platform.ResolvePath(actionYaml.Value);
|
||||
|
||||
using (var source = File.OpenRead(sourcePath))
|
||||
{
|
||||
var reader = new MSCabCompression(source);
|
||||
@@ -370,6 +399,64 @@ namespace OpenRA.Mods.Common.Widgets.Logic
|
||||
}
|
||||
}
|
||||
|
||||
static void ExtractFromISCab(string path, MiniYaml actionYaml, List<string> extractedFiles, Action<string> updateMessage)
|
||||
{
|
||||
var sourcePath = Path.Combine(path, actionYaml.Value);
|
||||
|
||||
// Try as an absolute path
|
||||
if (!File.Exists(sourcePath))
|
||||
sourcePath = Platform.ResolvePath(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(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 {0} -> {1}".F(sourcePath, targetPath));
|
||||
var displayFilename = Path.GetFileName(Path.GetFileName(targetPath));
|
||||
Action<int> onProgress = percent => updateMessage("Extracting {0} ({1}%)".F(displayFilename, 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.Install)
|
||||
|
||||
@@ -227,22 +227,22 @@ ModContent:
|
||||
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
|
||||
Sources: gdi95, gdi95-linux, nod95, nod95-linux, origin
|
||||
Sources: gdi95, gdi95-linux, nod95, nod95-linux, tfd, origin
|
||||
Required: true
|
||||
Download: basefiles
|
||||
music: Base Game Music
|
||||
TestFiles: ^Content/cnc/scores.mix
|
||||
Sources: gdi95, gdi95-linux, nod95, nod95-linux, origin
|
||||
Sources: gdi95, gdi95-linux, nod95, nod95-linux, tfd, origin
|
||||
Download: music
|
||||
movies-gdi: GDI Campaign Briefings
|
||||
TestFiles: ^Content/cnc/movies/visor.vqa, ^Content/cnc/movies/turtkill.vqa, ^Content/cnc/movies/trailer.vqa, ^Content/cnc/movies/tbrinfo3.vqa, ^Content/cnc/movies/tbrinfo2.vqa, ^Content/cnc/movies/tbrinfo1.vqa, ^Content/cnc/movies/seige.vqa, ^Content/cnc/movies/samsite.vqa, ^Content/cnc/movies/samdie.vqa, ^Content/cnc/movies/sabotage.vqa, ^Content/cnc/movies/retro.vqa, ^Content/cnc/movies/podium.vqa, ^Content/cnc/movies/planecra.vqa, ^Content/cnc/movies/pintle.vqa, ^Content/cnc/movies/paratrop.vqa, ^Content/cnc/movies/nodsweep.vqa, ^Content/cnc/movies/nodlose.vqa, ^Content/cnc/movies/nodflees.vqa, ^Content/cnc/movies/nod1.vqa, ^Content/cnc/movies/nitejump.vqa, ^Content/cnc/movies/napalm.vqa, ^Content/cnc/movies/logo.vqa, ^Content/cnc/movies/landing.vqa, ^Content/cnc/movies/intro2.vqa, ^Content/cnc/movies/hellvaly.vqa, ^Content/cnc/movies/gunboat.vqa, ^Content/cnc/movies/generic.vqa, ^Content/cnc/movies/gdilose.vqa, ^Content/cnc/movies/gdifinb.vqa, ^Content/cnc/movies/gdifina.vqa, ^Content/cnc/movies/gdiend2.vqa, ^Content/cnc/movies/gdiend1.vqa, ^Content/cnc/movies/gdi9.vqa, ^Content/cnc/movies/gdi8b.vqa, ^Content/cnc/movies/gdi8a.vqa, ^Content/cnc/movies/gdi7.vqa, ^Content/cnc/movies/gdi6.vqa, ^Content/cnc/movies/gdi5.vqa, ^Content/cnc/movies/gdi4b.vqa, ^Content/cnc/movies/gdi4a.vqa, ^Content/cnc/movies/gdi3lose.vqa, ^Content/cnc/movies/gdi3.vqa, ^Content/cnc/movies/gdi2.vqa, ^Content/cnc/movies/gdi15.vqa, ^Content/cnc/movies/gdi14.vqa, ^Content/cnc/movies/gdi13.vqa, ^Content/cnc/movies/gdi12.vqa, ^Content/cnc/movies/gdi11.vqa, ^Content/cnc/movies/gdi10.vqa, ^Content/cnc/movies/gdi1.vqa, ^Content/cnc/movies/gameover.vqa, ^Content/cnc/movies/forestkl.vqa, ^Content/cnc/movies/flyy.vqa, ^Content/cnc/movies/flag.vqa, ^Content/cnc/movies/dino.vqa, ^Content/cnc/movies/desolat.vqa, ^Content/cnc/movies/consyard.vqa, ^Content/cnc/movies/cc2tease.vqa, ^Content/cnc/movies/burdet2.vqa, ^Content/cnc/movies/burdet1.vqa, ^Content/cnc/movies/bombflee.vqa, ^Content/cnc/movies/bombaway.vqa, ^Content/cnc/movies/bkground.vqa, ^Content/cnc/movies/bcanyon.vqa, ^Content/cnc/movies/banner.vqa
|
||||
Sources: gdi95, gdi95-linux, origin
|
||||
Sources: gdi95, gdi95-linux, tfd, origin
|
||||
movies-nod: Nod Campaign Briefings
|
||||
TestFiles: ^Content/cnc/movies/visor.vqa, ^Content/cnc/movies/trtkil_d.vqa, ^Content/cnc/movies/trailer.vqa, ^Content/cnc/movies/tiberfx.vqa, ^Content/cnc/movies/tankkill.vqa, ^Content/cnc/movies/tankgo.vqa, ^Content/cnc/movies/sundial.vqa, ^Content/cnc/movies/stealth.vqa, ^Content/cnc/movies/spycrash.vqa, ^Content/cnc/movies/sethpre.vqa, ^Content/cnc/movies/seige.vqa, ^Content/cnc/movies/samsite.vqa, ^Content/cnc/movies/retro.vqa, ^Content/cnc/movies/refint.vqa, ^Content/cnc/movies/obel.vqa, ^Content/cnc/movies/nuke.vqa, ^Content/cnc/movies/nodlose.vqa, ^Content/cnc/movies/nodfinal.vqa, ^Content/cnc/movies/nodend4.vqa, ^Content/cnc/movies/nodend3.vqa, ^Content/cnc/movies/nodend2.vqa, ^Content/cnc/movies/nodend1.vqa, ^Content/cnc/movies/nod9.vqa, ^Content/cnc/movies/nod8.vqa, ^Content/cnc/movies/nod7b.vqa, ^Content/cnc/movies/nod7a.vqa, ^Content/cnc/movies/nod6.vqa, ^Content/cnc/movies/nod5.vqa, ^Content/cnc/movies/nod4b.vqa, ^Content/cnc/movies/nod4a.vqa, ^Content/cnc/movies/nod3.vqa, ^Content/cnc/movies/nod2.vqa, ^Content/cnc/movies/nod1pre.vqa, ^Content/cnc/movies/nod13.vqa, ^Content/cnc/movies/nod12.vqa, ^Content/cnc/movies/nod11.vqa, ^Content/cnc/movies/nod10b.vqa, ^Content/cnc/movies/nod10a.vqa, ^Content/cnc/movies/nod1.vqa, ^Content/cnc/movies/logo.vqa, ^Content/cnc/movies/landing.vqa, ^Content/cnc/movies/kanepre.vqa, ^Content/cnc/movies/intro2.vqa, ^Content/cnc/movies/insites.vqa, ^Content/cnc/movies/generic.vqa, ^Content/cnc/movies/gdi1.vqa, ^Content/cnc/movies/gameover.vqa, ^Content/cnc/movies/forestkl.vqa, ^Content/cnc/movies/flag.vqa, ^Content/cnc/movies/dino.vqa, ^Content/cnc/movies/dessweep.vqa, ^Content/cnc/movies/deskill.vqa, ^Content/cnc/movies/desflees.vqa, ^Content/cnc/movies/consyard.vqa, ^Content/cnc/movies/cc2tease.vqa, ^Content/cnc/movies/bombflee.vqa, ^Content/cnc/movies/bombaway.vqa, ^Content/cnc/movies/bcanyon.vqa, ^Content/cnc/movies/banner.vqa, ^Content/cnc/movies/akira.vqa, ^Content/cnc/movies/airstrk.vqa
|
||||
Sources: nod95, nod95-linux, origin
|
||||
Sources: nod95, nod95-linux, tfd, origin
|
||||
music-covertops: Covert Operations Music
|
||||
TestFiles: ^Content/cnc/scores-covertops.mix
|
||||
Sources: covertops, covertops-linux, origin
|
||||
Sources: covertops, covertops-linux, tfd, origin
|
||||
Downloads:
|
||||
basefiles: Base Freeware Content
|
||||
MirrorList: http://www.openra.net/packages/cnc-mirrors.txt
|
||||
@@ -1127,6 +1127,342 @@ ModContent:
|
||||
Install:
|
||||
copy: .
|
||||
^Content/cnc/scores-covertops.mix: scores.mix
|
||||
tfd: C&C The First Decade (English)
|
||||
IDFiles:
|
||||
data1.hdr: bef3a08c3fc1b1caf28ca0dbb97c1f900005930e
|
||||
data1.cab: 12ad6113a6890a1b4d5651a75378c963eaf513b9
|
||||
Install:
|
||||
extract-iscab: data1.hdr
|
||||
Volumes:
|
||||
2: data2.cab
|
||||
3: data3.cab
|
||||
Extract:
|
||||
^Content/cnc/conquer.mix: CnC\\CONQUER.MIX
|
||||
^Content/cnc/desert.mix: CnC\\DESERT.MIX
|
||||
^Content/cnc/general.mix: CnC\\GENERAL.MIX
|
||||
^Content/cnc/scores.mix: CnC\\SCORES.MIX
|
||||
^Content/cnc/sounds.mix: CnC\\SOUNDS.MIX
|
||||
^Content/cnc/temperat.mix: CnC\\TEMPERAT.MIX
|
||||
^Content/cnc/winter.mix: CnC\\WINTER.MIX
|
||||
^Content/cnc/speech.mix: CnC\\SPEECH.MIX
|
||||
^Content/cnc/tempicnh.mix: CnC\\TEMPICNH.MIX
|
||||
^Content/cnc/transit.mix: CnC\\TRANSIT.MIX
|
||||
^Content/cnc/scores-covertops.mix: CnC\covert\SCORES.MIX
|
||||
^Content/cnc/movies.mix: CnC\\MOVIES.MIX
|
||||
extract-raw: ^Content/cnc/movies.mix
|
||||
^Content/cnc/movies/airstrk.vqa:
|
||||
Offset: 1266
|
||||
Length: 4444442
|
||||
^Content/cnc/movies/akira.vqa:
|
||||
Offset: 4445708
|
||||
Length: 7803444
|
||||
^Content/cnc/movies/bkground.vqa:
|
||||
Offset: 12249152
|
||||
Length: 15267052
|
||||
^Content/cnc/movies/burdet1.vqa:
|
||||
Offset: 27516204
|
||||
Length: 10410614
|
||||
^Content/cnc/movies/burdet2.vqa:
|
||||
Offset: 37926818
|
||||
Length: 2062190
|
||||
^Content/cnc/movies/gdi4a.vqa:
|
||||
Offset: 39989008
|
||||
Length: 4450582
|
||||
^Content/cnc/movies/gdi4b.vqa:
|
||||
Offset: 44439590
|
||||
Length: 6603530
|
||||
^Content/cnc/movies/gdifina.vqa:
|
||||
Offset: 51043120
|
||||
Length: 12888650
|
||||
^Content/cnc/movies/gdifinb.vqa:
|
||||
Offset: 63931770
|
||||
Length: 17769978
|
||||
^Content/cnc/movies/nod7a.vqa:
|
||||
Offset: 81701748
|
||||
Length: 4740470
|
||||
^Content/cnc/movies/nod7b.vqa:
|
||||
Offset: 86442218
|
||||
Length: 4671402
|
||||
^Content/cnc/movies/visor.vqa:
|
||||
Offset: 91113620
|
||||
Length: 4407162
|
||||
^Content/cnc/movies/turtkill.vqa:
|
||||
Offset: 95520782
|
||||
Length: 3323444
|
||||
^Content/cnc/movies/trtkil_d.vqa:
|
||||
Offset: 98844226
|
||||
Length: 3511836
|
||||
^Content/cnc/movies/trailer.vqa:
|
||||
Offset: 102356062
|
||||
Length: 23163930
|
||||
^Content/cnc/movies/tiberfx.vqa:
|
||||
Offset: 125519992
|
||||
Length: 8735102
|
||||
^Content/cnc/movies/tbrinfo3.vqa:
|
||||
Offset: 134255094
|
||||
Length: 14972046
|
||||
^Content/cnc/movies/tbrinfo2.vqa:
|
||||
Offset: 149227140
|
||||
Length: 16195290
|
||||
^Content/cnc/movies/tbrinfo1.vqa:
|
||||
Offset: 165422430
|
||||
Length: 23479052
|
||||
^Content/cnc/movies/tankkill.vqa:
|
||||
Offset: 188901482
|
||||
Length: 3298978
|
||||
^Content/cnc/movies/tankgo.vqa:
|
||||
Offset: 192200460
|
||||
Length: 1403876
|
||||
^Content/cnc/movies/sundial.vqa:
|
||||
Offset: 193604336
|
||||
Length: 3653636
|
||||
^Content/cnc/movies/stealth.vqa:
|
||||
Offset: 197257972
|
||||
Length: 4521860
|
||||
^Content/cnc/movies/spycrash.vqa:
|
||||
Offset: 201779832
|
||||
Length: 1886288
|
||||
^Content/cnc/movies/sethpre.vqa:
|
||||
Offset: 203666120
|
||||
Length: 10914610
|
||||
^Content/cnc/movies/seige.vqa:
|
||||
Offset: 214580730
|
||||
Length: 2705978
|
||||
^Content/cnc/movies/samsite.vqa:
|
||||
Offset: 217286708
|
||||
Length: 1410418
|
||||
^Content/cnc/movies/samdie.vqa:
|
||||
Offset: 218697126
|
||||
Length: 3741942
|
||||
^Content/cnc/movies/sabotage.vqa:
|
||||
Offset: 222439068
|
||||
Length: 1386202
|
||||
^Content/cnc/movies/retro.vqa:
|
||||
Offset: 223825270
|
||||
Length: 6434382
|
||||
^Content/cnc/movies/refint.vqa:
|
||||
Offset: 230259652
|
||||
Length: 5090040
|
||||
^Content/cnc/movies/podium.vqa:
|
||||
Offset: 235349692
|
||||
Length: 2790664
|
||||
^Content/cnc/movies/planecra.vqa:
|
||||
Offset: 238140356
|
||||
Length: 5085426
|
||||
^Content/cnc/movies/pintle.vqa:
|
||||
Offset: 243225782
|
||||
Length: 2757536
|
||||
^Content/cnc/movies/paratrop.vqa:
|
||||
Offset: 245983318
|
||||
Length: 3180272
|
||||
^Content/cnc/movies/obel.vqa:
|
||||
Offset: 249163590
|
||||
Length: 3851370
|
||||
^Content/cnc/movies/nuke.vqa:
|
||||
Offset: 253014960
|
||||
Length: 3126662
|
||||
^Content/cnc/movies/nodsweep.vqa:
|
||||
Offset: 256141622
|
||||
Length: 3642174
|
||||
^Content/cnc/movies/nodlose.vqa:
|
||||
Offset: 259783796
|
||||
Length: 5148924
|
||||
^Content/cnc/movies/nodflees.vqa:
|
||||
Offset: 264932720
|
||||
Length: 3056426
|
||||
^Content/cnc/movies/nodfinal.vqa:
|
||||
Offset: 267989146
|
||||
Length: 23326586
|
||||
^Content/cnc/movies/nodend4.vqa:
|
||||
Offset: 291315732
|
||||
Length: 25535232
|
||||
^Content/cnc/movies/nodend3.vqa:
|
||||
Offset: 316850964
|
||||
Length: 25725824
|
||||
^Content/cnc/movies/nodend2.vqa:
|
||||
Offset: 342576788
|
||||
Length: 27214048
|
||||
^Content/cnc/movies/nodend1.vqa:
|
||||
Offset: 369790836
|
||||
Length: 27172272
|
||||
^Content/cnc/movies/nod9.vqa:
|
||||
Offset: 396963108
|
||||
Length: 9357980
|
||||
^Content/cnc/movies/nod8.vqa:
|
||||
Offset: 406321088
|
||||
Length: 11597940
|
||||
^Content/cnc/movies/nod6.vqa:
|
||||
Offset: 417919028
|
||||
Length: 5007744
|
||||
^Content/cnc/movies/nod5.vqa:
|
||||
Offset: 422926772
|
||||
Length: 6641700
|
||||
^Content/cnc/movies/nod4b.vqa:
|
||||
Offset: 429568472
|
||||
Length: 3867618
|
||||
^Content/cnc/movies/nod4a.vqa:
|
||||
Offset: 433436090
|
||||
Length: 3838924
|
||||
^Content/cnc/movies/nod3.vqa:
|
||||
Offset: 437275014
|
||||
Length: 3603314
|
||||
^Content/cnc/movies/nod2.vqa:
|
||||
Offset: 440878328
|
||||
Length: 7200720
|
||||
^Content/cnc/movies/nod1pre.vqa:
|
||||
Offset: 448079048
|
||||
Length: 395720
|
||||
^Content/cnc/movies/nod13.vqa:
|
||||
Offset: 448474768
|
||||
Length: 2746626
|
||||
^Content/cnc/movies/nod12.vqa:
|
||||
Offset: 451221394
|
||||
Length: 6576562
|
||||
^Content/cnc/movies/nod11.vqa:
|
||||
Offset: 457797956
|
||||
Length: 4629270
|
||||
^Content/cnc/movies/nod10b.vqa:
|
||||
Offset: 462427226
|
||||
Length: 6386444
|
||||
^Content/cnc/movies/nod10a.vqa:
|
||||
Offset: 468813670
|
||||
Length: 7205632
|
||||
^Content/cnc/movies/nod1.vqa:
|
||||
Offset: 476019302
|
||||
Length: 4663258
|
||||
^Content/cnc/movies/nitejump.vqa:
|
||||
Offset: 480682560
|
||||
Length: 3702838
|
||||
^Content/cnc/movies/napalm.vqa:
|
||||
Offset: 484385398
|
||||
Length: 4004146
|
||||
^Content/cnc/movies/logo.vqa:
|
||||
Offset: 488389544
|
||||
Length: 3562630
|
||||
^Content/cnc/movies/landing.vqa:
|
||||
Offset: 491952174
|
||||
Length: 1617094
|
||||
^Content/cnc/movies/kanepre.vqa:
|
||||
Offset: 493569268
|
||||
Length: 17220712
|
||||
^Content/cnc/movies/intro2.vqa:
|
||||
Offset: 510789980
|
||||
Length: 21058732
|
||||
^Content/cnc/movies/insites.vqa:
|
||||
Offset: 531848712
|
||||
Length: 460482
|
||||
^Content/cnc/movies/hellvaly.vqa:
|
||||
Offset: 532309194
|
||||
Length: 6658950
|
||||
^Content/cnc/movies/gunboat.vqa:
|
||||
Offset: 538968144
|
||||
Length: 3203706
|
||||
^Content/cnc/movies/generic.vqa:
|
||||
Offset: 542171850
|
||||
Length: 1452820
|
||||
^Content/cnc/movies/gdilose.vqa:
|
||||
Offset: 543624670
|
||||
Length: 2097912
|
||||
^Content/cnc/movies/gdiend2.vqa:
|
||||
Offset: 545722582
|
||||
Length: 25242946
|
||||
^Content/cnc/movies/gdiend1.vqa:
|
||||
Offset: 570965528
|
||||
Length: 25311636
|
||||
^Content/cnc/movies/gdi9.vqa:
|
||||
Offset: 596277164
|
||||
Length: 11806994
|
||||
^Content/cnc/movies/gdi8b.vqa:
|
||||
Offset: 608084158
|
||||
Length: 5324410
|
||||
^Content/cnc/movies/gdi8a.vqa:
|
||||
Offset: 613408568
|
||||
Length: 4672548
|
||||
^Content/cnc/movies/gdi7.vqa:
|
||||
Offset: 618081116
|
||||
Length: 4241952
|
||||
^Content/cnc/movies/gdi6.vqa:
|
||||
Offset: 622323068
|
||||
Length: 3959650
|
||||
^Content/cnc/movies/gdi5.vqa:
|
||||
Offset: 626282718
|
||||
Length: 3818244
|
||||
^Content/cnc/movies/gdi3lose.vqa:
|
||||
Offset: 630100962
|
||||
Length: 2265770
|
||||
^Content/cnc/movies/gdi3.vqa:
|
||||
Offset: 632366732
|
||||
Length: 5443848
|
||||
^Content/cnc/movies/gdi2.vqa:
|
||||
Offset: 637810580
|
||||
Length: 7883500
|
||||
^Content/cnc/movies/gdi15.vqa:
|
||||
Offset: 645694080
|
||||
Length: 11684610
|
||||
^Content/cnc/movies/gdi14.vqa:
|
||||
Offset: 657378690
|
||||
Length: 5282770
|
||||
^Content/cnc/movies/gdi13.vqa:
|
||||
Offset: 662661460
|
||||
Length: 6900914
|
||||
^Content/cnc/movies/gdi12.vqa:
|
||||
Offset: 669562374
|
||||
Length: 3669404
|
||||
^Content/cnc/movies/gdi11.vqa:
|
||||
Offset: 673231778
|
||||
Length: 5895754
|
||||
^Content/cnc/movies/gdi10.vqa:
|
||||
Offset: 679127532
|
||||
Length: 5761514
|
||||
^Content/cnc/movies/gdi1.vqa:
|
||||
Offset: 684889046
|
||||
Length: 6341298
|
||||
^Content/cnc/movies/gameover.vqa:
|
||||
Offset: 691230344
|
||||
Length: 2087322
|
||||
^Content/cnc/movies/forestkl.vqa:
|
||||
Offset: 693317666
|
||||
Length: 1500986
|
||||
^Content/cnc/movies/flyy.vqa:
|
||||
Offset: 694818652
|
||||
Length: 1373532
|
||||
^Content/cnc/movies/flag.vqa:
|
||||
Offset: 696192184
|
||||
Length: 4308680
|
||||
^Content/cnc/movies/dino.vqa:
|
||||
Offset: 700500864
|
||||
Length: 1347896
|
||||
^Content/cnc/movies/dessweep.vqa:
|
||||
Offset: 701848760
|
||||
Length: 3563646
|
||||
^Content/cnc/movies/desolat.vqa:
|
||||
Offset: 705412406
|
||||
Length: 8385122
|
||||
^Content/cnc/movies/deskill.vqa:
|
||||
Offset: 713797528
|
||||
Length: 1483634
|
||||
^Content/cnc/movies/desflees.vqa:
|
||||
Offset: 715281162
|
||||
Length: 2698426
|
||||
^Content/cnc/movies/consyard.vqa:
|
||||
Offset: 717979588
|
||||
Length: 7864652
|
||||
^Content/cnc/movies/cc2tease.vqa:
|
||||
Offset: 725844240
|
||||
Length: 12506712
|
||||
^Content/cnc/movies/bombflee.vqa:
|
||||
Offset: 738350952
|
||||
Length: 2859726
|
||||
^Content/cnc/movies/bombaway.vqa:
|
||||
Offset: 741210678
|
||||
Length: 5579588
|
||||
^Content/cnc/movies/bcanyon.vqa:
|
||||
Offset: 746790266
|
||||
Length: 5172288
|
||||
^Content/cnc/movies/banner.vqa:
|
||||
Offset: 751962554
|
||||
Length: 2229408
|
||||
delete: ^Content/cnc/movies.mix
|
||||
origin: C&C The Ultimate Collection (Origin version, English)
|
||||
Type: Install
|
||||
RegistryKey: HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node\EA Games\CNC and The Covert Operations
|
||||
|
||||
@@ -233,19 +233,19 @@ ModContent:
|
||||
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
|
||||
Sources: allied, allied-linux, soviet, soviet-linux, origin
|
||||
Sources: allied, allied-linux, soviet, soviet-linux, tfd, origin
|
||||
Required: true
|
||||
Download: basefiles
|
||||
music: Base Game Music
|
||||
TestFiles: ^Content/ra/scores.mix
|
||||
Sources: allied, allied-linux, soviet, soviet-linux, origin
|
||||
Sources: allied, allied-linux, soviet, soviet-linux, tfd, origin
|
||||
Download: music
|
||||
movies-allied: Allied Campaign Briefings
|
||||
TestFiles: ^Content/ra/movies1.mix
|
||||
Sources: allied, allied-linux, origin
|
||||
Sources: allied, allied-linux, tfd, origin
|
||||
movies-soviet: Soviet Campaign Briefings
|
||||
TestFiles: ^Content/ra/movies2.mix
|
||||
Sources: soviet, soviet-linux, origin
|
||||
Sources: soviet, soviet-linux, tfd, origin
|
||||
music-counterstrike: Counterstrike Music
|
||||
TestFiles: ^Content/ra/music/araziod.aud, ^Content/ra/music/backstab.aud, ^Content/ra/music/chaos2.aud, ^Content/ra/music/shut_it.aud, ^Content/ra/music/2nd_hand.aud, ^Content/ra/music/twinmix1.aud, ^Content/ra/music/under3.aud, ^Content/ra/music/vr2.aud,
|
||||
Sources: counterstrike, counterstrike-linux, origin
|
||||
@@ -536,6 +536,51 @@ ModContent:
|
||||
^Content/ra/music/wastelnd.aud:
|
||||
Offset: 263929718
|
||||
Length: 2721563
|
||||
tfd: C&C The First Decade (English)
|
||||
IDFiles:
|
||||
data1.hdr: bef3a08c3fc1b1caf28ca0dbb97c1f900005930e
|
||||
data1.cab: 12ad6113a6890a1b4d5651a75378c963eaf513b9
|
||||
Install:
|
||||
extract-iscab: data1.hdr
|
||||
Volumes:
|
||||
3: data3.cab
|
||||
4: data4.cab
|
||||
5: data5.cab
|
||||
Extract:
|
||||
^Content/ra/main.mix: Red Alert\\MAIN.MIX
|
||||
^Content/ra/redalert.mix: Red Alert\\REDALERT.MIX
|
||||
extract-raw: ^Content/ra/main.mix
|
||||
^Content/ra/movies1.mix:
|
||||
Offset: 417051805
|
||||
Length: 404691306
|
||||
^Content/ra/interior.mix:
|
||||
Offset: 821743111
|
||||
Length: 249490
|
||||
^Content/ra/conquer.mix:
|
||||
Offset: 840028549
|
||||
Length: 2192279
|
||||
^Content/ra/allies.mix:
|
||||
Offset: 842220828
|
||||
Length: 319181
|
||||
^Content/ra/temperat.mix:
|
||||
Offset: 842540009
|
||||
Length: 1043672
|
||||
^Content/ra/sounds.mix:
|
||||
Offset: 843583681
|
||||
Length: 1385637
|
||||
^Content/ra/snow.mix:
|
||||
Offset: 844969318
|
||||
Length: 1035716
|
||||
^Content/ra/scores.mix:
|
||||
Offset: 846005034
|
||||
Length: 67742203
|
||||
^Content/ra/russian.mix:
|
||||
Offset: 913747237
|
||||
Length: 274732
|
||||
^Content/ra/movies2.mix:
|
||||
Offset: 914022190
|
||||
Length: 417051731
|
||||
delete: ^Content/ra/main.mix
|
||||
origin: C&C The Ultimate Collection (Origin version, English)
|
||||
Type: Install
|
||||
RegistryKey: HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node\EA Games\Command and Conquer Red Alert
|
||||
|
||||
@@ -265,12 +265,12 @@ ModContent:
|
||||
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
|
||||
Sources: tibsun, tibsun-linux, origin
|
||||
Sources: tibsun, tibsun-linux, tfd, origin
|
||||
Required: true
|
||||
Download: basefiles
|
||||
music: Base Game Music
|
||||
TestFiles: ^Content/ts/scores.mix
|
||||
Sources: tibsun, tibsun-linux, origin
|
||||
Sources: tibsun, tibsun-linux, tfd, origin
|
||||
Download: music
|
||||
Downloads:
|
||||
basefiles: Base Freeware Content
|
||||
@@ -395,6 +395,62 @@ ModContent:
|
||||
^Content/ts/temperat.mix:
|
||||
Offset: 73056940
|
||||
Length: 2037606
|
||||
tfd: C&C The First Decade (English)
|
||||
IDFiles:
|
||||
data1.hdr: bef3a08c3fc1b1caf28ca0dbb97c1f900005930e
|
||||
data1.cab: 12ad6113a6890a1b4d5651a75378c963eaf513b9
|
||||
Install:
|
||||
extract-iscab: data1.hdr
|
||||
Volumes:
|
||||
6: data6.cab
|
||||
7: data7.cab
|
||||
Extract:
|
||||
^Content/ts/scores.mix: Tiberian Sun\SUN\SCORES.MIX
|
||||
^Content/ts/tibsun.mix: Tiberian Sun\SUN\TIBSUN.MIX
|
||||
extract-raw: ^Content/ts/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
|
||||
delete: ^Content/ts/tibsun.mix
|
||||
origin: C&C The Ultimate Collection (Origin version, English)
|
||||
Type: Install
|
||||
RegistryKey: HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node\EA Games\Command and Conquer Tiberian Sun
|
||||
|
||||
Reference in New Issue
Block a user