Merge pull request #8170 from DSUK/master
TFD Installer for Tiberian dawn, Red Alert and Tiberian Sun
This commit is contained in:
@@ -74,6 +74,8 @@ namespace OpenRA.FileSystem
|
|||||||
throw new NotImplementedException("The creation of .PAK archives is unimplemented");
|
throw new NotImplementedException("The creation of .PAK archives is unimplemented");
|
||||||
if (filename.EndsWith(".big", StringComparison.InvariantCultureIgnoreCase))
|
if (filename.EndsWith(".big", StringComparison.InvariantCultureIgnoreCase))
|
||||||
throw new NotImplementedException("The creation of .big archives is unimplemented");
|
throw new NotImplementedException("The creation of .big archives is unimplemented");
|
||||||
|
if (filename.EndsWith(".cab", StringComparison.InvariantCultureIgnoreCase))
|
||||||
|
throw new NotImplementedException("The creation of .cab archives is unimplemented");
|
||||||
|
|
||||||
return new Folder(filename, order, content);
|
return new Folder(filename, order, content);
|
||||||
}
|
}
|
||||||
@@ -103,6 +105,8 @@ namespace OpenRA.FileSystem
|
|||||||
return new BigFile(filename, order);
|
return new BigFile(filename, order);
|
||||||
if (filename.EndsWith(".bag", StringComparison.InvariantCultureIgnoreCase))
|
if (filename.EndsWith(".bag", StringComparison.InvariantCultureIgnoreCase))
|
||||||
return new BagFile(filename, order);
|
return new BagFile(filename, order);
|
||||||
|
if (filename.EndsWith(".hdr", StringComparison.InvariantCultureIgnoreCase))
|
||||||
|
return new InstallShieldCABExtractor(filename, order);
|
||||||
|
|
||||||
return new Folder(filename, order);
|
return new Folder(filename, order);
|
||||||
}
|
}
|
||||||
|
|||||||
526
OpenRA.Game/FileSystem/InstallShieldCABExtractor.cs
Normal file
526
OpenRA.Game/FileSystem/InstallShieldCABExtractor.cs
Normal file
@@ -0,0 +1,526 @@
|
|||||||
|
#region Copyright & License Information
|
||||||
|
/*
|
||||||
|
* Copyright 2007-2015 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. 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;
|
||||||
|
using ICSharpCode.SharpZipLib.Zip.Compression.Streams;
|
||||||
|
|
||||||
|
namespace OpenRA.FileSystem
|
||||||
|
{
|
||||||
|
public class InstallShieldCABExtractor : IDisposable, IFolder
|
||||||
|
{
|
||||||
|
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 + (long)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 = (long)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 = (long)reader.ReadUInt32();
|
||||||
|
/* unknown */ reader.ReadUInt32();
|
||||||
|
FileTableSize = reader.ReadUInt32();
|
||||||
|
|
||||||
|
FileTableSize2 = reader.ReadUInt32();
|
||||||
|
DirectoryCount = reader.ReadUInt32();
|
||||||
|
/* unknown */ reader.ReadBytes(8);
|
||||||
|
FileCount = reader.ReadUInt32();
|
||||||
|
|
||||||
|
FileTableOffset2 = (long)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 FileDescriptor fileDes;
|
||||||
|
public uint RemainingArchiveStream;
|
||||||
|
public uint RemainingFileStream;
|
||||||
|
readonly uint index;
|
||||||
|
readonly string commonName;
|
||||||
|
ushort volumeNumber;
|
||||||
|
Stream cabFile;
|
||||||
|
|
||||||
|
public CabReader(FileDescriptor fileDes, uint index, string commonName)
|
||||||
|
{
|
||||||
|
this.fileDes = fileDes;
|
||||||
|
this.index = index;
|
||||||
|
this.commonName = commonName;
|
||||||
|
volumeNumber = (ushort)((uint)fileDes.Volume - 1u);
|
||||||
|
RemainingArchiveStream = 0;
|
||||||
|
if ((fileDes.Flags & FileCompressed) > 0)
|
||||||
|
RemainingFileStream = fileDes.CompressedSize;
|
||||||
|
else
|
||||||
|
RemainingFileStream = fileDes.ExpandedSize;
|
||||||
|
|
||||||
|
cabFile = null;
|
||||||
|
NextFile();
|
||||||
|
}
|
||||||
|
|
||||||
|
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();
|
||||||
|
RemainingArchiveStream -= (uint)cabFile.Read(outArray, read, (int)count - read);
|
||||||
|
}
|
||||||
|
|
||||||
|
return outArray;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
cabFile.Dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void NextFile()
|
||||||
|
{
|
||||||
|
if (cabFile != null)
|
||||||
|
cabFile.Dispose();
|
||||||
|
|
||||||
|
++volumeNumber;
|
||||||
|
cabFile = GlobalFileSystem.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> fileLookup = new Dictionary<string, uint>();
|
||||||
|
int priority;
|
||||||
|
string commonName;
|
||||||
|
public int Priority { get { return priority; } }
|
||||||
|
|
||||||
|
public string Name { get { return commonName; } }
|
||||||
|
|
||||||
|
public InstallShieldCABExtractor(string hdrFilename, int priority = -1)
|
||||||
|
{
|
||||||
|
var fileGroups = new List<FileGroup>();
|
||||||
|
var fileGroupOffsets = new List<uint>();
|
||||||
|
|
||||||
|
this.priority = priority;
|
||||||
|
hdrFile = GlobalFileSystem.Open(hdrFilename);
|
||||||
|
|
||||||
|
// Strips archive number AND file extension
|
||||||
|
commonName = 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((long)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 index = fileGroup.FirstFile; index <= fileGroup.LastFile; ++index)
|
||||||
|
{
|
||||||
|
AddFileDescriptorToList(index);
|
||||||
|
var fileDescriptor = fileDescriptors[index];
|
||||||
|
var fullFilePath = "{0}\\{1}\\{2}".F(fileGroup.Name, DirectoryName((uint)fileDescriptor.DirectoryIndex), fileDescriptor.Filename);
|
||||||
|
fileLookup.Add(fullFilePath, index);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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 Exists(string filename)
|
||||||
|
{
|
||||||
|
return fileLookup.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)
|
||||||
|
{
|
||||||
|
using (var destfile = File.Open(fileName, FileMode.Create))
|
||||||
|
GetContentById(index, destfile);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Write(Dictionary<string, byte[]> input)
|
||||||
|
{
|
||||||
|
throw new NotImplementedException("Cannot Add Files To Cab");
|
||||||
|
}
|
||||||
|
|
||||||
|
public IEnumerable<uint> ClassicHashes()
|
||||||
|
{
|
||||||
|
return fileLookup.Keys.Select(k => PackageEntry.HashFilename(k, PackageHashType.Classic));
|
||||||
|
}
|
||||||
|
|
||||||
|
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 obfustcated files");
|
||||||
|
|
||||||
|
var output = new MemoryStream((int)fileDes.ExpandedSize);
|
||||||
|
|
||||||
|
using (var reader = new CabReader(fileDes, index, commonName))
|
||||||
|
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 obfustcated files");
|
||||||
|
|
||||||
|
using (var reader = new CabReader(fileDes, index, commonName))
|
||||||
|
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 GetContent(string fileName)
|
||||||
|
{
|
||||||
|
return GetContentById(fileLookup[fileName]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public IEnumerable<uint> CrcHashes()
|
||||||
|
{
|
||||||
|
yield break;
|
||||||
|
}
|
||||||
|
|
||||||
|
public IEnumerable<string> AllFileNames()
|
||||||
|
{
|
||||||
|
return fileLookup.Keys;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
hdrFile.Dispose();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -253,6 +253,7 @@
|
|||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Compile Include="FileSystem\D2kSoundResources.cs" />
|
<Compile Include="FileSystem\D2kSoundResources.cs" />
|
||||||
<Compile Include="FileSystem\Folder.cs" />
|
<Compile Include="FileSystem\Folder.cs" />
|
||||||
|
<Compile Include="FileSystem\InstallShieldCABExtractor.cs" />
|
||||||
<Compile Include="FileSystem\InstallShieldPackage.cs" />
|
<Compile Include="FileSystem\InstallShieldPackage.cs" />
|
||||||
<Compile Include="FileSystem\MixFile.cs" />
|
<Compile Include="FileSystem\MixFile.cs" />
|
||||||
<Compile Include="FileSystem\Pak.cs" />
|
<Compile Include="FileSystem\Pak.cs" />
|
||||||
|
|||||||
@@ -39,6 +39,12 @@ namespace OpenRA.Mods.Common
|
|||||||
public readonly string MusicPackageMirrorList = null;
|
public readonly string MusicPackageMirrorList = null;
|
||||||
public readonly int ShippedSoundtracks = 0;
|
public readonly int ShippedSoundtracks = 0;
|
||||||
|
|
||||||
|
/// <summary> InstallShield .cab File Ids, used to extract Mod specific files </summary>
|
||||||
|
public readonly int[] InstallShieldCABFileIds = { };
|
||||||
|
|
||||||
|
/// <summary> InstallShield .cab File Ids, used to extract Mod specific archives and extract contents of ExtractFilesFromCD </summary>
|
||||||
|
public readonly string[] InstallShieldCABFilePackageIds = { };
|
||||||
|
|
||||||
public static Dictionary<string, string[]> LoadFilesToExtract(MiniYaml yaml)
|
public static Dictionary<string, string[]> LoadFilesToExtract(MiniYaml yaml)
|
||||||
{
|
{
|
||||||
var md = yaml.ToDictionary();
|
var md = yaml.ToDictionary();
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ using System;
|
|||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
|
using OpenRA.FileSystem;
|
||||||
using OpenRA.Widgets;
|
using OpenRA.Widgets;
|
||||||
|
|
||||||
namespace OpenRA.Mods.Common.Widgets.Logic
|
namespace OpenRA.Mods.Common.Widgets.Logic
|
||||||
@@ -51,12 +52,26 @@ namespace OpenRA.Mods.Common.Widgets.Logic
|
|||||||
return installData.DiskTestFiles.All(f => File.Exists(Path.Combine(diskRoot, f)));
|
return installData.DiskTestFiles.All(f => File.Exists(Path.Combine(diskRoot, f)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool IsTFD(string diskpath)
|
||||||
|
{
|
||||||
|
var test = File.Exists(Path.Combine(diskpath, "data1.hdr"));
|
||||||
|
var i = 0;
|
||||||
|
|
||||||
|
while (test && i < 14)
|
||||||
|
test &= File.Exists(Path.Combine(diskpath, "data{0}.cab".F(++i)));
|
||||||
|
|
||||||
|
return test;
|
||||||
|
}
|
||||||
|
|
||||||
void CheckForDisk()
|
void CheckForDisk()
|
||||||
{
|
{
|
||||||
var path = InstallUtils.GetMountedDisk(IsValidDisk);
|
var path = InstallUtils.GetMountedDisk(IsValidDisk);
|
||||||
|
|
||||||
if (path != null)
|
if (path != null)
|
||||||
Install(path);
|
Install(path);
|
||||||
|
else if ((installData.InstallShieldCABFileIds.Length != 0 || installData.InstallShieldCABFilePackageIds.Length != 0)
|
||||||
|
&& (path = InstallUtils.GetMountedDisk(IsTFD)) != null)
|
||||||
|
InstallTFD(Platform.ResolvePath(path, "data1.hdr"));
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
insertDiskContainer.IsVisible = () => true;
|
insertDiskContainer.IsVisible = () => true;
|
||||||
@@ -64,6 +79,63 @@ namespace OpenRA.Mods.Common.Widgets.Logic
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void InstallTFD(string source)
|
||||||
|
{
|
||||||
|
backButton.IsDisabled = () => true;
|
||||||
|
retryButton.IsDisabled = () => true;
|
||||||
|
insertDiskContainer.IsVisible = () => false;
|
||||||
|
installingContainer.IsVisible = () => true;
|
||||||
|
progressBar.Percentage = 0;
|
||||||
|
|
||||||
|
new Thread(() =>
|
||||||
|
{
|
||||||
|
using (var cabExtractor = new InstallShieldCABExtractor(source))
|
||||||
|
{
|
||||||
|
var denom = installData.InstallShieldCABFileIds.Length;
|
||||||
|
var extractFiles = installData.ExtractFilesFromCD;
|
||||||
|
|
||||||
|
if (installData.InstallShieldCABFilePackageIds.Length > 0)
|
||||||
|
denom += extractFiles.SelectMany(x => x.Value).Count();
|
||||||
|
|
||||||
|
var installPercent = 100 / denom;
|
||||||
|
|
||||||
|
foreach (uint index in installData.InstallShieldCABFileIds)
|
||||||
|
{
|
||||||
|
var filename = cabExtractor.FileName(index);
|
||||||
|
statusLabel.GetText = () => "Extracting {0}".F(filename);
|
||||||
|
var dest = Platform.ResolvePath("^", "Content", Game.ModData.Manifest.Mod.Id, filename.ToLowerInvariant());
|
||||||
|
cabExtractor.ExtractFile(index, dest);
|
||||||
|
progressBar.Percentage += installPercent;
|
||||||
|
}
|
||||||
|
|
||||||
|
var ArchivesToExtract = installData.InstallShieldCABFilePackageIds.Select(x => x.Split(':'));
|
||||||
|
var destDir = Platform.ResolvePath("^", "Content", Game.ModData.Manifest.Mod.Id);
|
||||||
|
var onError = (Action<string>)(s => { });
|
||||||
|
var overwrite = installData.OverwriteFiles;
|
||||||
|
|
||||||
|
var onProgress = (Action<string>)(s => Game.RunAfterTick(() =>
|
||||||
|
{
|
||||||
|
progressBar.Percentage += installPercent;
|
||||||
|
|
||||||
|
statusLabel.GetText = () => s;
|
||||||
|
}));
|
||||||
|
|
||||||
|
foreach (var archive in ArchivesToExtract)
|
||||||
|
{
|
||||||
|
var filename = cabExtractor.FileName(uint.Parse(archive[0]));
|
||||||
|
statusLabel.GetText = () => "Extracting {0}".F(filename);
|
||||||
|
var destFile = Platform.ResolvePath("^", "Content", Game.ModData.Manifest.Mod.Id, filename.ToLowerInvariant());
|
||||||
|
cabExtractor.ExtractFile(uint.Parse(archive[0]), destFile);
|
||||||
|
var annotation = archive.Length > 1 ? archive[1] : null;
|
||||||
|
InstallUtils.ExtractFromPackage(source, destFile, annotation, extractFiles, destDir, overwrite, onProgress, onError);
|
||||||
|
progressBar.Percentage += installPercent;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
continueLoading();
|
||||||
|
}) { IsBackground = true }.Start();
|
||||||
|
}
|
||||||
|
|
||||||
void Install(string source)
|
void Install(string source)
|
||||||
{
|
{
|
||||||
backButton.IsDisabled = () => true;
|
backButton.IsDisabled = () => true;
|
||||||
|
|||||||
@@ -153,6 +153,7 @@ ContentInstaller:
|
|||||||
.: conquer.mix, desert.mix, general.mix, scores.mix, sounds.mix, temperat.mix, winter.mix
|
.: conquer.mix, desert.mix, general.mix, scores.mix, sounds.mix, temperat.mix, winter.mix
|
||||||
ShippedSoundtracks: 2
|
ShippedSoundtracks: 2
|
||||||
MusicPackageMirrorList: http://www.openra.net/packages/cnc-music-mirrors.txt
|
MusicPackageMirrorList: http://www.openra.net/packages/cnc-music-mirrors.txt
|
||||||
|
InstallShieldCABFileIds: 66, 45, 42, 65, 68, 67, 71, 47, 49, 60, 75, 73, 53
|
||||||
|
|
||||||
ServerTraits:
|
ServerTraits:
|
||||||
LobbyCommands
|
LobbyCommands
|
||||||
|
|||||||
@@ -152,6 +152,8 @@ ContentInstaller:
|
|||||||
.: INSTALL/REDALERT.MIX
|
.: INSTALL/REDALERT.MIX
|
||||||
ShippedSoundtracks: 2
|
ShippedSoundtracks: 2
|
||||||
MusicPackageMirrorList: http://www.openra.net/packages/ra-music-mirrors.txt
|
MusicPackageMirrorList: http://www.openra.net/packages/ra-music-mirrors.txt
|
||||||
|
InstallShieldCABFilePackageIds: 105
|
||||||
|
InstallShieldCABFileIds: 116
|
||||||
|
|
||||||
ServerTraits:
|
ServerTraits:
|
||||||
LobbyCommands
|
LobbyCommands
|
||||||
|
|||||||
@@ -201,6 +201,8 @@ ContentInstaller:
|
|||||||
.: cache.mix, conquer.mix, isosnow.mix, isotemp.mix, local.mix, sidec01.mix, sidec02.mix, sno.mix, snow.mix, sounds.mix, speech01.mix, tem.mix, temperat.mix
|
.: cache.mix, conquer.mix, isosnow.mix, isotemp.mix, local.mix, sidec01.mix, sidec02.mix, sno.mix, snow.mix, sounds.mix, speech01.mix, tem.mix, temperat.mix
|
||||||
ShippedSoundtracks: 2
|
ShippedSoundtracks: 2
|
||||||
MusicPackageMirrorList: http://www.openra.net/packages/ts-music-mirrors.txt
|
MusicPackageMirrorList: http://www.openra.net/packages/ts-music-mirrors.txt
|
||||||
|
InstallShieldCABFilePackageIds: 332:CRC32
|
||||||
|
InstallShieldCABFileIds: 323, 364
|
||||||
|
|
||||||
ServerTraits:
|
ServerTraits:
|
||||||
LobbyCommands
|
LobbyCommands
|
||||||
|
|||||||
Reference in New Issue
Block a user