Cache the global mix database in MixLoader

We can reuse this global database as it doesn't change, rather than loading a new copy each time a new mix file is parsed.
This commit is contained in:
RoosterDragon
2022-01-29 20:23:52 +00:00
committed by reaperrr
parent f5d1fe4bc4
commit d8a4d7fd1d
2 changed files with 16 additions and 17 deletions

View File

@@ -32,7 +32,7 @@ namespace OpenRA.Mods.Cnc.FileSystem
readonly long dataStart; readonly long dataStart;
readonly Stream s; readonly Stream s;
public MixFile(Stream s, string filename, HashSet<string> allPossibleFilenames) public MixFile(Stream s, string filename, string[] globalFilenames)
{ {
Name = filename; Name = filename;
this.s = s; this.s = s;
@@ -55,7 +55,7 @@ namespace OpenRA.Mods.Cnc.FileSystem
index = ParseIndex(entries.ToDictionaryWithConflictLog(x => x.Hash, index = ParseIndex(entries.ToDictionaryWithConflictLog(x => x.Hash,
$"{filename} ({(isCncMix ? "C&C" : "RA/TS/RA2")} format, Encrypted: {isEncrypted}, DataStart: {dataStart})", $"{filename} ({(isCncMix ? "C&C" : "RA/TS/RA2")} format, Encrypted: {isEncrypted}, DataStart: {dataStart})",
null, x => $"(offs={x.Offset}, len={x.Length})"), allPossibleFilenames); null, x => $"(offs={x.Offset}, len={x.Length})"), globalFilenames);
} }
catch (Exception) catch (Exception)
{ {
@@ -64,10 +64,11 @@ namespace OpenRA.Mods.Cnc.FileSystem
} }
} }
Dictionary<string, PackageEntry> ParseIndex(Dictionary<uint, PackageEntry> entries, HashSet<string> allPossibleFilenames) Dictionary<string, PackageEntry> ParseIndex(Dictionary<uint, PackageEntry> entries, string[] globalFilenames)
{ {
var classicIndex = new Dictionary<string, PackageEntry>(); var classicIndex = new Dictionary<string, PackageEntry>();
var crcIndex = new Dictionary<string, PackageEntry>(); var crcIndex = new Dictionary<string, PackageEntry>();
IEnumerable<string> allPossibleFilenames = globalFilenames;
// Try and find a local mix database // Try and find a local mix database
var dbNameClassic = PackageEntry.HashFilename("local mix database.dat", PackageHashType.Classic); var dbNameClassic = PackageEntry.HashFilename("local mix database.dat", PackageHashType.Classic);
@@ -79,15 +80,14 @@ namespace OpenRA.Mods.Cnc.FileSystem
using (var content = GetContent(kv.Value)) using (var content = GetContent(kv.Value))
{ {
var db = new XccLocalDatabase(content); var db = new XccLocalDatabase(content);
foreach (var e in db.Entries) allPossibleFilenames = allPossibleFilenames.Concat(db.Entries);
allPossibleFilenames.Add(e);
} }
break; break;
} }
} }
foreach (var filename in allPossibleFilenames) foreach (var filename in allPossibleFilenames.Distinct())
{ {
var classicHash = PackageEntry.HashFilename(filename, PackageHashType.Classic); var classicHash = PackageEntry.HashFilename(filename, PackageHashType.Classic);
var crcHash = PackageEntry.HashFilename(filename, PackageHashType.CRC32); var crcHash = PackageEntry.HashFilename(filename, PackageHashType.CRC32);
@@ -223,6 +223,8 @@ namespace OpenRA.Mods.Cnc.FileSystem
} }
} }
string[] globalFilenames;
bool IPackageLoader.TryParsePackage(Stream s, string filename, FS context, out IReadOnlyPackage package) bool IPackageLoader.TryParsePackage(Stream s, string filename, FS context, out IReadOnlyPackage package)
{ {
if (!filename.EndsWith(".mix", StringComparison.InvariantCultureIgnoreCase)) if (!filename.EndsWith(".mix", StringComparison.InvariantCultureIgnoreCase))
@@ -232,13 +234,12 @@ namespace OpenRA.Mods.Cnc.FileSystem
} }
// Load the global mix database // Load the global mix database
var allPossibleFilenames = new HashSet<string>(); if (globalFilenames == null)
if (context.TryOpen("global mix database.dat", out var mixDatabase)) if (context.TryOpen("global mix database.dat", out var mixDatabase))
using (var db = new XccGlobalDatabase(mixDatabase)) using (var db = new XccGlobalDatabase(mixDatabase))
foreach (var e in db.Entries) globalFilenames = db.Entries.Distinct().ToArray();
allPossibleFilenames.Add(e);
package = new MixFile(s, filename, allPossibleFilenames); package = new MixFile(s, filename, globalFilenames ?? Array.Empty<string>());
return true; return true;
} }
} }

View File

@@ -10,7 +10,6 @@
#endregion #endregion
using System; using System;
using System.Collections.Generic;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using OpenRA.Mods.Cnc.FileFormats; using OpenRA.Mods.Cnc.FileFormats;
@@ -30,12 +29,11 @@ namespace OpenRA.Mods.Cnc.UtilityCommands
[Desc("ARCHIVE.MIX", "MIXDATABASE.DAT", "Lists the content ranges for a mix file")] [Desc("ARCHIVE.MIX", "MIXDATABASE.DAT", "Lists the content ranges for a mix file")]
void IUtilityCommand.Run(Utility utility, string[] args) void IUtilityCommand.Run(Utility utility, string[] args)
{ {
var allPossibleFilenames = new HashSet<string>(); string[] globalFilenames;
using (var db = new XccGlobalDatabase(File.OpenRead(args[2]))) using (var db = new XccGlobalDatabase(File.OpenRead(args[2])))
foreach (var e in db.Entries) globalFilenames = db.Entries;
allPossibleFilenames.Add(e);
var package = new MixLoader.MixFile(File.OpenRead(args[1]), args[1], allPossibleFilenames); var package = new MixLoader.MixFile(File.OpenRead(args[1]), args[1], globalFilenames);
foreach (var kv in package.Index.OrderBy(kv => kv.Value.Offset)) foreach (var kv in package.Index.OrderBy(kv => kv.Value.Offset))
{ {
Console.WriteLine("{0}:", kv.Key); Console.WriteLine("{0}:", kv.Key);