Merge pull request #10530 from pchote/remove-external-hashes

Index filesystem contents by filename instead of hash.
This commit is contained in:
Oliver Brakmann
2016-01-29 21:12:31 +01:00
22 changed files with 138 additions and 332 deletions

View File

@@ -15,11 +15,14 @@ namespace OpenRA
// Referenced from ModMetadata, so needs to be in OpenRA.Game :( // Referenced from ModMetadata, so needs to be in OpenRA.Game :(
public class ContentInstaller : IGlobalModData public class ContentInstaller : IGlobalModData
{ {
public enum FilenameCase { Input, ForceLower, ForceUpper }
public readonly string[] TestFiles = { }; public readonly string[] TestFiles = { };
public readonly string[] DiskTestFiles = { }; public readonly string[] DiskTestFiles = { };
public readonly string PackageToExtractFromCD = null; public readonly string PackageToExtractFromCD = null;
public readonly bool OverwriteFiles = true; public readonly bool OverwriteFiles = true;
public readonly FilenameCase OutputFilenameCase = FilenameCase.ForceLower;
public readonly Dictionary<string, string[]> CopyFilesFromCD = new Dictionary<string, string[]>(); public readonly Dictionary<string, string[]> CopyFilesFromCD = new Dictionary<string, string[]>();
public readonly Dictionary<string, string[]> ExtractFilesFromCD = new Dictionary<string, string[]>(); public readonly Dictionary<string, string[]> ExtractFilesFromCD = new Dictionary<string, string[]>();

View File

@@ -21,19 +21,15 @@ namespace OpenRA.FileSystem
{ {
public sealed class BagFile : IReadOnlyPackage public sealed class BagFile : IReadOnlyPackage
{ {
static readonly uint[] Nothing = { };
readonly string bagFilename; readonly string bagFilename;
readonly Stream s; readonly Stream s;
readonly int bagFilePriority; readonly int bagFilePriority;
readonly Dictionary<uint, IdxEntry> index; readonly Dictionary<string, IdxEntry> index;
readonly FileSystem context;
public BagFile(FileSystem context, string filename, int priority) public BagFile(FileSystem context, string filename, int priority)
{ {
bagFilename = filename; bagFilename = filename;
bagFilePriority = priority; bagFilePriority = priority;
this.context = context;
// A bag file is always accompanied with an .idx counterpart // A bag file is always accompanied with an .idx counterpart
// For example: audio.bag requires the audio.idx file // For example: audio.bag requires the audio.idx file
@@ -44,7 +40,7 @@ namespace OpenRA.FileSystem
using (var indexStream = context.Open(indexFilename)) using (var indexStream = context.Open(indexFilename))
entries = new IdxReader(indexStream).Entries; entries = new IdxReader(indexStream).Entries;
index = entries.ToDictionaryWithConflictLog(x => x.Hash, index = entries.ToDictionaryWithConflictLog(x => x.Filename,
"{0} (bag format)".F(filename), "{0} (bag format)".F(filename),
null, x => "(offs={0}, len={1})".F(x.Offset, x.Length)); null, x => "(offs={0}, len={1})".F(x.Offset, x.Length));
@@ -54,10 +50,10 @@ namespace OpenRA.FileSystem
public int Priority { get { return 1000 + bagFilePriority; } } public int Priority { get { return 1000 + bagFilePriority; } }
public string Name { get { return bagFilename; } } public string Name { get { return bagFilename; } }
public Stream GetContent(uint hash) public Stream GetContent(string filename)
{ {
IdxEntry entry; IdxEntry entry;
if (!index.TryGetValue(hash, out entry)) if (!index.TryGetValue(filename, out entry))
return null; return null;
s.Seek(entry.Offset, SeekOrigin.Begin); s.Seek(entry.Offset, SeekOrigin.Begin);
@@ -120,59 +116,14 @@ namespace OpenRA.FileSystem
return mergedStream; return mergedStream;
} }
uint? FindMatchingHash(string filename)
{
var hash = IdxEntry.HashFilename(filename, PackageHashType.CRC32);
if (index.ContainsKey(hash))
return hash;
// Maybe we were given a raw hash?
uint raw;
if (!uint.TryParse(filename, NumberStyles.AllowHexSpecifier, CultureInfo.InvariantCulture, out raw))
return null;
if ("{0:X}".F(raw) == filename && index.ContainsKey(raw))
return raw;
return null;
}
public Stream GetContent(string filename)
{
var hash = FindMatchingHash(filename);
return hash.HasValue ? GetContent(hash.Value) : null;
}
public bool Exists(string filename) public bool Exists(string filename)
{ {
return FindMatchingHash(filename).HasValue; return index.ContainsKey(filename);
}
public IEnumerable<uint> ClassicHashes()
{
return Nothing;
}
public IEnumerable<uint> CrcHashes()
{
return index.Keys;
} }
public IEnumerable<string> AllFileNames() public IEnumerable<string> AllFileNames()
{ {
var lookup = new Dictionary<uint, string>(); return index.Keys;
if (context.Exists("global mix database.dat"))
{
var db = new XccGlobalDatabase(context.Open("global mix database.dat"));
foreach (var e in db.Entries)
{
var hash = IdxEntry.HashFilename(e, PackageHashType.CRC32);
if (!lookup.ContainsKey(hash))
lookup.Add(hash, e);
}
}
return index.Keys.Select(k => lookup.ContainsKey(k) ? lookup[k] : "{0:X}".F(k));
} }
public void Dispose() public void Dispose()

View File

@@ -96,16 +96,6 @@ namespace OpenRA.FileSystem
return entries.ContainsKey(filename); return entries.ContainsKey(filename);
} }
public IEnumerable<uint> ClassicHashes()
{
return entries.Keys.Select(filename => PackageEntry.HashFilename(filename, PackageHashType.Classic));
}
public IEnumerable<uint> CrcHashes()
{
return Enumerable.Empty<uint>();
}
public IEnumerable<string> AllFileNames() public IEnumerable<string> AllFileNames()
{ {
return entries.Keys; return entries.Keys;

View File

@@ -10,18 +10,29 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.IO; using System.IO;
using OpenRA.Primitives;
namespace OpenRA.FileSystem namespace OpenRA.FileSystem
{ {
public sealed class D2kSoundResources : IReadOnlyPackage public sealed class D2kSoundResources : IReadOnlyPackage
{ {
struct Entry
{
public readonly uint Offset;
public readonly uint Length;
public Entry(uint offset, uint length)
{
Offset = offset;
Length = length;
}
}
readonly Stream s; readonly Stream s;
readonly string filename; readonly string filename;
readonly List<string> filenames;
readonly int priority; readonly int priority;
readonly Dictionary<string, Entry> index = new Dictionary<string, Entry>();
readonly Dictionary<uint, PackageEntry> index = new Dictionary<uint, PackageEntry>();
public D2kSoundResources(FileSystem context, string filename, int priority) public D2kSoundResources(FileSystem context, string filename, int priority)
{ {
@@ -31,20 +42,13 @@ namespace OpenRA.FileSystem
s = context.Open(filename); s = context.Open(filename);
try try
{ {
filenames = new List<string>();
var headerLength = s.ReadUInt32(); var headerLength = s.ReadUInt32();
while (s.Position < headerLength + 4) while (s.Position < headerLength + 4)
{ {
var name = s.ReadASCIIZ(); var name = s.ReadASCIIZ();
var offset = s.ReadUInt32(); var offset = s.ReadUInt32();
var length = s.ReadUInt32(); var length = s.ReadUInt32();
index.Add(name, new Entry(offset, length));
var hash = PackageEntry.HashFilename(name, PackageHashType.Classic);
if (!index.ContainsKey(hash))
index.Add(hash, new PackageEntry(hash, offset, length));
filenames.Add(name);
} }
} }
catch catch
@@ -54,45 +58,30 @@ namespace OpenRA.FileSystem
} }
} }
public Stream GetContent(uint hash) public Stream GetContent(string filename)
{ {
PackageEntry e; Entry e;
if (!index.TryGetValue(hash, out e)) if (!index.TryGetValue(filename, out e))
return null; return null;
s.Seek(e.Offset, SeekOrigin.Begin); s.Seek(e.Offset, SeekOrigin.Begin);
return new MemoryStream(s.ReadBytes((int)e.Length)); return new MemoryStream(s.ReadBytes((int)e.Length));
} }
public Stream GetContent(string filename)
{
return GetContent(PackageEntry.HashFilename(filename, PackageHashType.Classic));
}
public bool Exists(string filename) public bool Exists(string filename)
{ {
return index.ContainsKey(PackageEntry.HashFilename(filename, PackageHashType.Classic)); return index.ContainsKey(filename);
} }
public IEnumerable<string> AllFileNames() public IEnumerable<string> AllFileNames()
{ {
return filenames; return index.Keys;
} }
public string Name { get { return filename; } } public string Name { get { return filename; } }
public int Priority { get { return 1000 + priority; } } public int Priority { get { return 1000 + priority; } }
public IEnumerable<uint> ClassicHashes()
{
return index.Keys;
}
public IEnumerable<uint> CrcHashes()
{
yield break;
}
public void Dispose() public void Dispose()
{ {
s.Dispose(); s.Dispose();

View File

@@ -19,14 +19,12 @@ namespace OpenRA.FileSystem
{ {
public class FileSystem public class FileSystem
{ {
public readonly List<string> PackagePaths = new List<string>();
public readonly List<IReadOnlyPackage> MountedPackages = new List<IReadOnlyPackage>(); public readonly List<IReadOnlyPackage> MountedPackages = new List<IReadOnlyPackage>();
static readonly Dictionary<string, Assembly> AssemblyCache = new Dictionary<string, Assembly>(); static readonly Dictionary<string, Assembly> AssemblyCache = new Dictionary<string, Assembly>();
int order; int order;
Cache<uint, List<IReadOnlyPackage>> crcHashIndex = new Cache<uint, List<IReadOnlyPackage>>(_ => new List<IReadOnlyPackage>()); Cache<string, List<IReadOnlyPackage>> fileIndex = new Cache<string, List<IReadOnlyPackage>>(_ => new List<IReadOnlyPackage>());
Cache<uint, List<IReadOnlyPackage>> classicHashIndex = new Cache<uint, List<IReadOnlyPackage>>(_ => new List<IReadOnlyPackage>());
public IReadWritePackage CreatePackage(string filename, int order, Dictionary<string, byte[]> content) public IReadWritePackage CreatePackage(string filename, int order, Dictionary<string, byte[]> content)
{ {
@@ -93,7 +91,6 @@ namespace OpenRA.FileSystem
name = Platform.ResolvePath(name); name = Platform.ResolvePath(name);
PackagePaths.Add(name);
Action a = () => MountInner(OpenPackage(name, annotation, order++)); Action a = () => MountInner(OpenPackage(name, annotation, order++));
if (optional) if (optional)
@@ -107,27 +104,23 @@ namespace OpenRA.FileSystem
{ {
MountedPackages.Add(package); MountedPackages.Add(package);
foreach (var hash in package.ClassicHashes()) foreach (var filename in package.AllFileNames())
{ {
var packageList = classicHashIndex[hash]; var packageList = fileIndex[filename];
if (!packageList.Contains(package))
packageList.Add(package);
}
foreach (var hash in package.CrcHashes())
{
var packageList = crcHashIndex[hash];
if (!packageList.Contains(package)) if (!packageList.Contains(package))
packageList.Add(package); packageList.Add(package);
} }
} }
public bool Unmount(IReadOnlyPackage mount) public bool Unmount(IReadOnlyPackage package)
{ {
if (MountedPackages.Contains(mount)) foreach (var packagesForFile in fileIndex.Values)
mount.Dispose(); packagesForFile.RemoveAll(p => p == package);
return MountedPackages.RemoveAll(f => f == mount) > 0; if (MountedPackages.Contains(package))
package.Dispose();
return MountedPackages.RemoveAll(p => p == package) > 0;
} }
public void UnmountAll() public void UnmountAll()
@@ -136,9 +129,7 @@ namespace OpenRA.FileSystem
package.Dispose(); package.Dispose();
MountedPackages.Clear(); MountedPackages.Clear();
PackagePaths.Clear(); fileIndex = new Cache<string, List<IReadOnlyPackage>>(_ => new List<IReadOnlyPackage>());
classicHashIndex = new Cache<uint, List<IReadOnlyPackage>>(_ => new List<IReadOnlyPackage>());
crcHashIndex = new Cache<uint, List<IReadOnlyPackage>>(_ => new List<IReadOnlyPackage>());
} }
public void LoadFromManifest(Manifest manifest) public void LoadFromManifest(Manifest manifest)
@@ -151,10 +142,9 @@ namespace OpenRA.FileSystem
Mount(pkg.Key, pkg.Value); Mount(pkg.Key, pkg.Value);
} }
Stream GetFromCache(PackageHashType type, string filename) Stream GetFromCache(string filename)
{ {
var index = type == PackageHashType.CRC32 ? crcHashIndex : classicHashIndex; var package = fileIndex[filename]
var package = index[PackageEntry.HashFilename(filename, type)]
.Where(x => x.Exists(filename)) .Where(x => x.Exists(filename))
.MinByOrDefault(x => x.Priority); .MinByOrDefault(x => x.Priority);
@@ -191,11 +181,7 @@ namespace OpenRA.FileSystem
// TODO: This disables caching for explicit package requests // TODO: This disables caching for explicit package requests
if (filename.IndexOfAny(new[] { '/', '\\' }) == -1 && !explicitPackage) if (filename.IndexOfAny(new[] { '/', '\\' }) == -1 && !explicitPackage)
{ {
s = GetFromCache(PackageHashType.Classic, filename); s = GetFromCache(filename);
if (s != null)
return true;
s = GetFromCache(PackageHashType.CRC32, filename);
if (s != null) if (s != null)
return true; return true;
} }

View File

@@ -43,17 +43,6 @@ namespace OpenRA.FileSystem
catch { return null; } catch { return null; }
} }
public IEnumerable<uint> ClassicHashes()
{
foreach (var filename in Directory.GetFiles(path, "*", SearchOption.TopDirectoryOnly))
yield return PackageEntry.HashFilename(Path.GetFileName(filename), PackageHashType.Classic);
}
public IEnumerable<uint> CrcHashes()
{
yield break;
}
public IEnumerable<string> AllFileNames() public IEnumerable<string> AllFileNames()
{ {
foreach (var filename in Directory.GetFiles(path, "*", SearchOption.TopDirectoryOnly)) foreach (var filename in Directory.GetFiles(path, "*", SearchOption.TopDirectoryOnly))

View File

@@ -18,8 +18,6 @@ namespace OpenRA.FileSystem
{ {
Stream GetContent(string filename); Stream GetContent(string filename);
bool Exists(string filename); bool Exists(string filename);
IEnumerable<uint> ClassicHashes();
IEnumerable<uint> CrcHashes();
IEnumerable<string> AllFileNames(); IEnumerable<string> AllFileNames();
int Priority { get; } int Priority { get; }
string Name { get; } string Name { get; }

View File

@@ -15,80 +15,31 @@ namespace OpenRA.FileSystem
{ {
public class IdxEntry public class IdxEntry
{ {
public const string DefaultExtension = "wav"; public readonly string Filename;
public readonly uint Hash;
public readonly string Name;
public readonly string Extension;
public readonly uint Offset; public readonly uint Offset;
public readonly uint Length; public readonly uint Length;
public readonly uint SampleRate; public readonly uint SampleRate;
public readonly uint Flags; public readonly uint Flags;
public readonly uint ChunkSize; public readonly uint ChunkSize;
public IdxEntry(uint hash, uint offset, uint length, uint sampleRate, uint flags, uint chuckSize)
{
Hash = hash;
Offset = offset;
Length = length;
SampleRate = sampleRate;
Flags = flags;
ChunkSize = chuckSize;
}
public IdxEntry(Stream s) public IdxEntry(Stream s)
{ {
var asciiname = s.ReadASCII(16); var name = s.ReadASCII(16);
var pos = name.IndexOf('\0');
var pos = asciiname.IndexOf('\0');
if (pos != 0) if (pos != 0)
asciiname = asciiname.Substring(0, pos); name = name.Substring(0, pos);
Name = asciiname; Filename = string.Concat(name, ".wav");
Extension = DefaultExtension;
Offset = s.ReadUInt32(); Offset = s.ReadUInt32();
Length = s.ReadUInt32(); Length = s.ReadUInt32();
SampleRate = s.ReadUInt32(); SampleRate = s.ReadUInt32();
Flags = s.ReadUInt32(); Flags = s.ReadUInt32();
ChunkSize = s.ReadUInt32(); ChunkSize = s.ReadUInt32();
Hash = HashFilename(string.Concat(Name, ".", Extension), PackageHashType.CRC32);
}
public void Write(BinaryWriter w)
{
w.Write(Name.PadRight(16, '\0'));
w.Write(Offset);
w.Write(Length);
w.Write(SampleRate);
w.Write(Flags);
w.Write(ChunkSize);
} }
public override string ToString() public override string ToString()
{ {
string filename; return "{0} - offset 0x{1:x8} - length 0x{2:x8}".F(Filename, Offset, Length);
if (names.TryGetValue(Hash, out filename))
return "{0} - offset 0x{1:x8} - length 0x{2:x8}".F(filename, Offset, Length);
else
return "0x{0:x8} - offset 0x{1:x8} - length 0x{2:x8}".F(Hash, Offset, Length);
}
public static uint HashFilename(string name, PackageHashType type)
{
return PackageEntry.HashFilename(name, type);
}
static Dictionary<uint, string> names = new Dictionary<uint, string>();
public static void AddStandardName(string s)
{
// RA1 and TD
var hash = HashFilename(s, PackageHashType.Classic);
names.Add(hash, s);
// TS
var crcHash = HashFilename(s, PackageHashType.CRC32);
names.Add(crcHash, s);
} }
} }
} }

View File

@@ -451,11 +451,6 @@ namespace OpenRA.FileSystem
GetContentById(index, destfile); GetContentById(index, destfile);
} }
public IEnumerable<uint> ClassicHashes()
{
return fileLookup.Keys.Select(k => PackageEntry.HashFilename(k, PackageHashType.Classic));
}
public Stream GetContentById(uint index) public Stream GetContentById(uint index)
{ {
var fileDes = fileDescriptors[index]; var fileDes = fileDescriptors[index];
@@ -507,11 +502,6 @@ namespace OpenRA.FileSystem
return GetContentById(fileLookup[fileName]); return GetContentById(fileLookup[fileName]);
} }
public IEnumerable<uint> CrcHashes()
{
yield break;
}
public IEnumerable<string> AllFileNames() public IEnumerable<string> AllFileNames()
{ {
return fileLookup.Keys; return fileLookup.Keys;

View File

@@ -17,8 +17,19 @@ namespace OpenRA.FileSystem
{ {
public sealed class InstallShieldPackage : IReadOnlyPackage public sealed class InstallShieldPackage : IReadOnlyPackage
{ {
readonly Dictionary<uint, PackageEntry> index = new Dictionary<uint, PackageEntry>(); struct Entry
readonly List<string> filenames; {
public readonly uint Offset;
public readonly uint Length;
public Entry(uint offset, uint length)
{
Offset = offset;
Length = length;
}
}
readonly Dictionary<string, Entry> index = new Dictionary<string, Entry>();
readonly Stream s; readonly Stream s;
readonly long dataStart = 255; readonly long dataStart = 255;
readonly int priority; readonly int priority;
@@ -29,40 +40,45 @@ namespace OpenRA.FileSystem
this.filename = filename; this.filename = filename;
this.priority = priority; this.priority = priority;
filenames = new List<string>();
s = context.Open(filename); s = context.Open(filename);
try try
{ {
// Parse package header // Parse package header
var reader = new BinaryReader(s); var signature = s.ReadUInt32();
var signature = reader.ReadUInt32();
if (signature != 0x8C655D13) if (signature != 0x8C655D13)
throw new InvalidDataException("Not an Installshield package"); throw new InvalidDataException("Not an Installshield package");
reader.ReadBytes(8); s.Position += 8;
/*var FileCount = */reader.ReadUInt16(); /*var FileCount = */s.ReadUInt16();
reader.ReadBytes(4); s.Position += 4;
/*var ArchiveSize = */reader.ReadUInt32(); /*var ArchiveSize = */s.ReadUInt32();
reader.ReadBytes(19); s.Position += 19;
var tocAddress = reader.ReadInt32(); var tocAddress = s.ReadInt32();
reader.ReadBytes(4); s.Position += 4;
var dirCount = reader.ReadUInt16(); var dirCount = s.ReadUInt16();
// Parse the directory list // Parse the directory list
s.Seek(tocAddress, SeekOrigin.Begin); s.Position = tocAddress;
var tocReader = new BinaryReader(s);
var fileCountInDirs = new List<uint>();
// Parse directories // Parse directories
var directories = new Dictionary<string, uint>();
for (var i = 0; i < dirCount; i++) for (var i = 0; i < dirCount; i++)
fileCountInDirs.Add(ParseDirectory(tocReader)); {
// Parse directory header
var fileCount = s.ReadUInt16();
var chunkSize = s.ReadUInt16();
var nameLength = s.ReadUInt16();
var dirName = s.ReadASCII(nameLength);
// Skip to the end of the chunk
s.ReadBytes(chunkSize - nameLength - 6);
directories.Add(dirName, fileCount);
}
// Parse files // Parse files
foreach (var fileCount in fileCountInDirs) foreach (var dir in directories)
for (var i = 0; i < fileCount; i++) for (var i = 0; i < dir.Value; i++)
ParseFile(reader); ParseFile(s, dir.Key);
} }
catch catch
{ {
@@ -71,44 +87,29 @@ namespace OpenRA.FileSystem
} }
} }
static uint ParseDirectory(BinaryReader reader)
{
// Parse directory header
var fileCount = reader.ReadUInt16();
var chunkSize = reader.ReadUInt16();
var nameLength = reader.ReadUInt16();
reader.ReadChars(nameLength); // var DirName = new String(reader.ReadChars(NameLength));
// Skip to the end of the chunk
reader.ReadBytes(chunkSize - nameLength - 6);
return fileCount;
}
uint accumulatedData = 0; uint accumulatedData = 0;
void ParseFile(BinaryReader reader) void ParseFile(Stream s, string dirName)
{ {
reader.ReadBytes(7); s.Position += 7;
var compressedSize = reader.ReadUInt32(); var compressedSize = s.ReadUInt32();
reader.ReadBytes(12); s.Position += 12;
var chunkSize = reader.ReadUInt16(); var chunkSize = s.ReadUInt16();
reader.ReadBytes(4); s.Position += 4;
var nameLength = reader.ReadByte(); var nameLength = s.ReadByte();
var fileName = new string(reader.ReadChars(nameLength)); var fileName = dirName + "\\" + s.ReadASCII(nameLength);
var hash = PackageEntry.HashFilename(fileName, PackageHashType.Classic); // Use index syntax to overwrite any duplicate entries with the last value
if (!index.ContainsKey(hash)) index[fileName] = new Entry(accumulatedData, compressedSize);
index.Add(hash, new PackageEntry(hash, accumulatedData, compressedSize));
filenames.Add(fileName);
accumulatedData += compressedSize; accumulatedData += compressedSize;
// Skip to the end of the chunk // Skip to the end of the chunk
reader.ReadBytes(chunkSize - nameLength - 30); s.Position += chunkSize - nameLength - 30;
} }
public Stream GetContent(uint hash) public Stream GetContent(string filename)
{ {
PackageEntry e; Entry e;
if (!index.TryGetValue(hash, out e)) if (!index.TryGetValue(filename, out e))
return null; return null;
s.Seek(dataStart + e.Offset, SeekOrigin.Begin); s.Seek(dataStart + e.Offset, SeekOrigin.Begin);
@@ -117,39 +118,19 @@ namespace OpenRA.FileSystem
return new MemoryStream(Blast.Decompress(data)); return new MemoryStream(Blast.Decompress(data));
} }
public Stream GetContent(string filename) public IEnumerable<string> AllFileNames()
{
return GetContent(PackageEntry.HashFilename(filename, PackageHashType.Classic));
}
public IEnumerable<uint> ClassicHashes()
{ {
return index.Keys; return index.Keys;
} }
public IEnumerable<uint> CrcHashes()
{
yield break;
}
public IEnumerable<string> AllFileNames()
{
return filenames;
}
public bool Exists(string filename) public bool Exists(string filename)
{ {
return index.ContainsKey(PackageEntry.HashFilename(filename, PackageHashType.Classic)); return index.ContainsKey(filename);
} }
public int Priority { get { return 2000 + priority; } } public int Priority { get { return 2000 + priority; } }
public string Name { get { return filename; } } public string Name { get { return filename; } }
public void Write(Dictionary<string, byte[]> contents)
{
throw new NotImplementedException("Cannot save InstallShieldPackages.");
}
public void Dispose() public void Dispose()
{ {
s.Dispose(); s.Dispose();

View File

@@ -170,23 +170,6 @@ namespace OpenRA.FileSystem
return hash.HasValue ? GetContent(hash.Value) : null; return hash.HasValue ? GetContent(hash.Value) : null;
} }
static readonly uint[] Nothing = { };
public IEnumerable<uint> ClassicHashes()
{
if (type == PackageHashType.Classic)
return index.Keys;
return Nothing;
}
public IEnumerable<uint> CrcHashes()
{
if (type == PackageHashType.CRC32)
return index.Keys;
return Nothing;
}
public IEnumerable<string> AllFileNames() public IEnumerable<string> AllFileNames()
{ {
var lookup = new Dictionary<uint, string>(); var lookup = new Dictionary<uint, string>();

View File

@@ -70,17 +70,6 @@ namespace OpenRA.FileSystem
return new MemoryStream(data); return new MemoryStream(data);
} }
public IEnumerable<uint> ClassicHashes()
{
foreach (var filename in index.Keys)
yield return PackageEntry.HashFilename(filename, PackageHashType.Classic);
}
public IEnumerable<uint> CrcHashes()
{
yield break;
}
public IEnumerable<string> AllFileNames() public IEnumerable<string> AllFileNames()
{ {
foreach (var filename in index.Keys) foreach (var filename in index.Keys)

View File

@@ -71,17 +71,6 @@ namespace OpenRA.FileSystem
} }
} }
public IEnumerable<uint> ClassicHashes()
{
foreach (ZipEntry entry in pkg)
yield return PackageEntry.HashFilename(entry.Name, PackageHashType.Classic);
}
public IEnumerable<uint> CrcHashes()
{
yield break;
}
public IEnumerable<string> AllFileNames() public IEnumerable<string> AllFileNames()
{ {
foreach (ZipEntry entry in pkg) foreach (ZipEntry entry in pkg)

View File

@@ -37,9 +37,26 @@ namespace OpenRA.Mods.Common
return volumes.FirstOrDefault(isValidDisk); return volumes.FirstOrDefault(isValidDisk);
} }
static string GetFileName(string path, ContentInstaller.FilenameCase caseModifier)
{
// Gets the file path, splitting on both / and \
var index = path.LastIndexOfAny(new[] { '\\', '/' });
var output = path.Substring(index + 1);
switch (caseModifier)
{
case ContentInstaller.FilenameCase.ForceLower:
return output.ToLowerInvariant();
case ContentInstaller.FilenameCase.ForceUpper:
return output.ToUpperInvariant();
default:
return output;
}
}
// TODO: The package should be mounted into its own context to avoid name collisions with installed files // TODO: The package should be mounted into its own context to avoid name collisions with installed files
public static bool ExtractFromPackage(string srcPath, string package, string annotation, Dictionary<string, string[]> filesByDirectory, public static bool ExtractFromPackage(string srcPath, string package, string annotation, Dictionary<string, string[]> filesByDirectory,
string destPath, bool overwrite, Action<string> onProgress, Action<string> onError) string destPath, bool overwrite, ContentInstaller.FilenameCase caseModifier, Action<string> onProgress, Action<string> onError)
{ {
Directory.CreateDirectory(destPath); Directory.CreateDirectory(destPath);
@@ -55,7 +72,7 @@ namespace OpenRA.Mods.Common
foreach (var file in directory.Value) foreach (var file in directory.Value)
{ {
var containingDir = Path.Combine(destPath, targetDir); var containingDir = Path.Combine(destPath, targetDir);
var dest = Path.Combine(containingDir, file.ToLowerInvariant()); var dest = Path.Combine(containingDir, GetFileName(file, caseModifier));
if (File.Exists(dest)) if (File.Exists(dest))
{ {
if (overwrite) if (overwrite)
@@ -83,7 +100,7 @@ namespace OpenRA.Mods.Common
} }
public static bool CopyFiles(string srcPath, Dictionary<string, string[]> files, string destPath, public static bool CopyFiles(string srcPath, Dictionary<string, string[]> files, string destPath,
bool overwrite, Action<string> onProgress, Action<string> onError) bool overwrite, ContentInstaller.FilenameCase caseModifier, Action<string> onProgress, Action<string> onError)
{ {
Directory.CreateDirectory(destPath); Directory.CreateDirectory(destPath);
@@ -100,9 +117,9 @@ namespace OpenRA.Mods.Common
return false; return false;
} }
var destFile = Path.GetFileName(file); var destFile = GetFileName(file, caseModifier);
var containingDir = Path.Combine(destPath, targetDir); var containingDir = Path.Combine(destPath, targetDir);
var dest = Path.Combine(containingDir, destFile.ToLowerInvariant()); var dest = Path.Combine(containingDir, destFile);
if (File.Exists(dest) && !overwrite) if (File.Exists(dest) && !overwrite)
{ {
Log.Write("debug", "Skipping {0}".F(dest)); Log.Write("debug", "Skipping {0}".F(dest));

View File

@@ -132,7 +132,7 @@ namespace OpenRA.Mods.Common.Widgets.Logic
var destFile = Platform.ResolvePath("^", "Content", modId, filename.ToLowerInvariant()); var destFile = Platform.ResolvePath("^", "Content", modId, filename.ToLowerInvariant());
cabExtractor.ExtractFile(uint.Parse(archive[0]), destFile); cabExtractor.ExtractFile(uint.Parse(archive[0]), destFile);
var annotation = archive.Length > 1 ? archive[1] : null; var annotation = archive.Length > 1 ? archive[1] : null;
InstallUtils.ExtractFromPackage(source, destFile, annotation, extractFiles, destDir, overwrite, onProgress, onError); InstallUtils.ExtractFromPackage(source, destFile, annotation, extractFiles, destDir, overwrite, installData.OutputFilenameCase, onProgress, onError);
progressBar.Percentage += installPercent; progressBar.Percentage += installPercent;
} }
} }
@@ -183,7 +183,7 @@ namespace OpenRA.Mods.Common.Widgets.Logic
{ {
try try
{ {
if (!InstallUtils.CopyFiles(source, copyFiles, dest, overwrite, onProgress, onError)) if (!InstallUtils.CopyFiles(source, copyFiles, dest, overwrite, installData.OutputFilenameCase, onProgress, onError))
{ {
onError("Copying files from CD failed."); onError("Copying files from CD failed.");
return; return;
@@ -191,7 +191,8 @@ namespace OpenRA.Mods.Common.Widgets.Logic
if (!string.IsNullOrEmpty(extractPackage)) if (!string.IsNullOrEmpty(extractPackage))
{ {
if (!InstallUtils.ExtractFromPackage(source, extractPackage, annotation, extractFiles, dest, overwrite, onProgress, onError)) if (!InstallUtils.ExtractFromPackage(source, extractPackage, annotation, extractFiles, dest,
overwrite, installData.OutputFilenameCase, onProgress, onError))
{ {
onError("Extracting files from CD failed."); onError("Extracting files from CD failed.");
return; return;

View File

@@ -146,13 +146,11 @@ LoadScreen: CncLoadScreen
ContentInstaller: ContentInstaller:
TestFiles: ^Content/cnc/conquer.mix, ^Content/cnc/desert.mix, ^Content/cnc/sounds.mix, ^Content/cnc/speech.mix, ^Content/cnc/temperat.mix, ^Content/cnc/tempicnh.mix, ^Content/cnc/winter.mix 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
FilesToCopy: CONQUER.MIX, DESERT.MIX, SCORES.MIX, SOUNDS.MIX, TEMPERAT.MIX, WINTER.MIX
FilesToExtract: speech.mix, tempicnh.mix, transit.mix
PackageMirrorList: http://www.openra.net/packages/cnc-mirrors.txt PackageMirrorList: http://www.openra.net/packages/cnc-mirrors.txt
DiskTestFiles: conquer.mix, desert.mix, install/setup.z DiskTestFiles: conquer.mix, desert.mix, install/setup.z
PackageToExtractFromCD: install/setup.z PackageToExtractFromCD: install/setup.z
ExtractFilesFromCD: ExtractFilesFromCD:
.: speech.mix, tempicnh.mix, transit.mix .: C&C95\SPEECH.MIX, C&C95\TEMPICNH.MIX, C&C95\TRANSIT.MIX
CopyFilesFromCD: CopyFilesFromCD:
.: 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: 4 ShippedSoundtracks: 4

View File

@@ -19,7 +19,7 @@ Visibility: MissionSelector
Type: Campaign Type: Campaign
Videos: Videos:
Briefing: a_br01_e.vqa Briefing: A_BR01_E.VQA
Options: Options:
Crates: False Crates: False

View File

@@ -19,7 +19,7 @@ Visibility: MissionSelector
Type: Campaign Type: Campaign
Videos: Videos:
Briefing: a_br01_e.vqa Briefing: A_BR01_E.VQA
Options: Options:
Crates: False Crates: False

View File

@@ -19,7 +19,7 @@ Visibility: MissionSelector
Type: Campaign Type: Campaign
Videos: Videos:
Briefing: a_br02_e.vqa Briefing: A_BR02_E.VQA
Options: Options:
Crates: False Crates: False

View File

@@ -19,7 +19,7 @@ Visibility: MissionSelector
Type: Campaign Type: Campaign
Videos: Videos:
Briefing: a_br02_e.vqa Briefing: A_BR02_E.VQA
Options: Options:
Crates: False Crates: False

View File

@@ -19,7 +19,7 @@ Visibility: MissionSelector
Type: Campaign Type: Campaign
Videos: Videos:
Briefing: a_br03_e.vqa Briefing: A_BR03_E.VQA
Options: Options:
Crates: False Crates: False

File diff suppressed because one or more lines are too long