Implement new syntax for package-specific filesystem requests.
This commit is contained in:
@@ -21,6 +21,8 @@ namespace OpenRA.FileSystem
|
|||||||
{
|
{
|
||||||
public IEnumerable<IReadOnlyPackage> MountedPackages { get { return mountedPackages.Keys; } }
|
public IEnumerable<IReadOnlyPackage> MountedPackages { get { return mountedPackages.Keys; } }
|
||||||
readonly Dictionary<IReadOnlyPackage, int> mountedPackages = new Dictionary<IReadOnlyPackage, int>();
|
readonly Dictionary<IReadOnlyPackage, int> mountedPackages = new Dictionary<IReadOnlyPackage, int>();
|
||||||
|
readonly Dictionary<string, IReadOnlyPackage> explicitMounts = new Dictionary<string, IReadOnlyPackage>();
|
||||||
|
|
||||||
Cache<string, List<IReadOnlyPackage>> fileIndex = new Cache<string, List<IReadOnlyPackage>>(_ => new List<IReadOnlyPackage>());
|
Cache<string, List<IReadOnlyPackage>> fileIndex = new Cache<string, List<IReadOnlyPackage>>(_ => new List<IReadOnlyPackage>());
|
||||||
|
|
||||||
public IReadWritePackage CreatePackage(string filename, Dictionary<string, byte[]> content)
|
public IReadWritePackage CreatePackage(string filename, Dictionary<string, byte[]> content)
|
||||||
@@ -54,7 +56,13 @@ namespace OpenRA.FileSystem
|
|||||||
if (filename.EndsWith(".hdr", StringComparison.InvariantCultureIgnoreCase))
|
if (filename.EndsWith(".hdr", StringComparison.InvariantCultureIgnoreCase))
|
||||||
return new InstallShieldCABExtractor(this, filename);
|
return new InstallShieldCABExtractor(this, filename);
|
||||||
|
|
||||||
return new Folder(filename);
|
IReadOnlyPackage parent;
|
||||||
|
string subPath = null;
|
||||||
|
if (TryGetPackageContaining(filename, out parent, out subPath))
|
||||||
|
if (parent is Folder)
|
||||||
|
return new Folder(Path.Combine(((Folder)parent).Name, subPath));
|
||||||
|
|
||||||
|
return new Folder(Platform.ResolvePath(filename));
|
||||||
}
|
}
|
||||||
|
|
||||||
public IReadWritePackage OpenWritablePackage(string filename)
|
public IReadWritePackage OpenWritablePackage(string filename)
|
||||||
@@ -67,15 +75,13 @@ namespace OpenRA.FileSystem
|
|||||||
return new Folder(filename);
|
return new Folder(filename);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Mount(string name)
|
public void Mount(string name, string explicitName = null)
|
||||||
{
|
{
|
||||||
var optional = name.StartsWith("~");
|
var optional = name.StartsWith("~");
|
||||||
if (optional)
|
if (optional)
|
||||||
name = name.Substring(1);
|
name = name.Substring(1);
|
||||||
|
|
||||||
name = Platform.ResolvePath(name);
|
Action a = () => Mount(OpenPackage(name), explicitName);
|
||||||
|
|
||||||
Action a = () => Mount(OpenPackage(name));
|
|
||||||
if (optional)
|
if (optional)
|
||||||
try { a(); }
|
try { a(); }
|
||||||
catch { }
|
catch { }
|
||||||
@@ -83,7 +89,7 @@ namespace OpenRA.FileSystem
|
|||||||
a();
|
a();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Mount(IReadOnlyPackage package)
|
public void Mount(IReadOnlyPackage package, string explicitName = null)
|
||||||
{
|
{
|
||||||
var mountCount = 0;
|
var mountCount = 0;
|
||||||
if (mountedPackages.TryGetValue(package, out mountCount))
|
if (mountedPackages.TryGetValue(package, out mountCount))
|
||||||
@@ -101,6 +107,10 @@ namespace OpenRA.FileSystem
|
|||||||
{
|
{
|
||||||
// Mounting the package for the first time
|
// Mounting the package for the first time
|
||||||
mountedPackages.Add(package, 1);
|
mountedPackages.Add(package, 1);
|
||||||
|
|
||||||
|
if (explicitName != null)
|
||||||
|
explicitMounts.Add(explicitName, package);
|
||||||
|
|
||||||
foreach (var filename in package.Contents)
|
foreach (var filename in package.Contents)
|
||||||
fileIndex[filename].Add(package);
|
fileIndex[filename].Add(package);
|
||||||
}
|
}
|
||||||
@@ -118,6 +128,7 @@ namespace OpenRA.FileSystem
|
|||||||
packagesForFile.RemoveAll(p => p == package);
|
packagesForFile.RemoveAll(p => p == package);
|
||||||
|
|
||||||
mountedPackages.Remove(package);
|
mountedPackages.Remove(package);
|
||||||
|
explicitMounts.Remove(package.Name);
|
||||||
package.Dispose();
|
package.Dispose();
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@@ -132,14 +143,15 @@ namespace OpenRA.FileSystem
|
|||||||
package.Dispose();
|
package.Dispose();
|
||||||
|
|
||||||
mountedPackages.Clear();
|
mountedPackages.Clear();
|
||||||
|
explicitMounts.Clear();
|
||||||
fileIndex = new Cache<string, List<IReadOnlyPackage>>(_ => new List<IReadOnlyPackage>());
|
fileIndex = new Cache<string, List<IReadOnlyPackage>>(_ => new List<IReadOnlyPackage>());
|
||||||
}
|
}
|
||||||
|
|
||||||
public void LoadFromManifest(Manifest manifest)
|
public void LoadFromManifest(Manifest manifest)
|
||||||
{
|
{
|
||||||
UnmountAll();
|
UnmountAll();
|
||||||
foreach (var pkg in manifest.Packages)
|
foreach (var kv in manifest.Packages)
|
||||||
Mount(pkg);
|
Mount(kv.Key, kv.Value);
|
||||||
}
|
}
|
||||||
|
|
||||||
Stream GetFromCache(string filename)
|
Stream GetFromCache(string filename)
|
||||||
@@ -162,58 +174,64 @@ namespace OpenRA.FileSystem
|
|||||||
return s;
|
return s;
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool TryOpen(string name, out Stream s)
|
public bool TryGetPackageContaining(string path, out IReadOnlyPackage package, out string filename)
|
||||||
{
|
{
|
||||||
var filename = name;
|
var explicitSplit = path.IndexOf('|');
|
||||||
var packageName = string.Empty;
|
if (explicitSplit > 0 && explicitMounts.TryGetValue(path.Substring(0, explicitSplit), out package))
|
||||||
|
|
||||||
// Used for faction specific packages; rule out false positive on Windows C:\ drive notation
|
|
||||||
var explicitPackage = name.Contains(':') && !Directory.Exists(Path.GetDirectoryName(name));
|
|
||||||
if (explicitPackage)
|
|
||||||
{
|
{
|
||||||
var divide = name.Split(':');
|
filename = path.Substring(explicitSplit + 1);
|
||||||
packageName = divide.First();
|
return true;
|
||||||
filename = divide.Last();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check the cache for a quick lookup if the package name is unknown
|
package = fileIndex[path].LastOrDefault(x => x.Contains(path));
|
||||||
// TODO: This disables caching for explicit package requests
|
filename = path;
|
||||||
if (filename.IndexOfAny(new[] { '/', '\\' }) == -1 && !explicitPackage)
|
|
||||||
|
return package != null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool TryOpen(string filename, out Stream s)
|
||||||
|
{
|
||||||
|
var explicitSplit = filename.IndexOf('|');
|
||||||
|
if (explicitSplit > 0)
|
||||||
{
|
{
|
||||||
s = GetFromCache(filename);
|
IReadOnlyPackage explicitPackage;
|
||||||
if (s != null)
|
if (explicitMounts.TryGetValue(filename.Substring(0, explicitSplit), out explicitPackage))
|
||||||
return true;
|
{
|
||||||
|
s = explicitPackage.GetStream(filename.Substring(explicitSplit + 1));
|
||||||
|
if (s != null)
|
||||||
|
return true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
s = GetFromCache(filename);
|
||||||
|
if (s != null)
|
||||||
|
return true;
|
||||||
|
|
||||||
// Ask each package individually
|
// Ask each package individually
|
||||||
IReadOnlyPackage package;
|
// TODO: This fallback can be removed once the filesystem cleanups are complete
|
||||||
if (explicitPackage && !string.IsNullOrEmpty(packageName))
|
var package = mountedPackages.Keys.LastOrDefault(x => x.Contains(filename));
|
||||||
package = mountedPackages.Keys.LastOrDefault(x => x.Name == packageName);
|
|
||||||
else
|
|
||||||
package = mountedPackages.Keys.LastOrDefault(x => x.Contains(filename));
|
|
||||||
|
|
||||||
if (package != null)
|
if (package != null)
|
||||||
{
|
{
|
||||||
s = package.GetStream(filename);
|
s = package.GetStream(filename);
|
||||||
return true;
|
return s != null;
|
||||||
}
|
}
|
||||||
|
|
||||||
s = null;
|
s = null;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool Exists(string name)
|
public bool Exists(string filename)
|
||||||
{
|
{
|
||||||
var explicitPackage = name.Contains(':') && !Directory.Exists(Path.GetDirectoryName(name));
|
var explicitSplit = filename.IndexOf('|');
|
||||||
if (explicitPackage)
|
if (explicitSplit > 0)
|
||||||
{
|
{
|
||||||
var divide = name.Split(':');
|
IReadOnlyPackage explicitPackage;
|
||||||
var packageName = divide.First();
|
if (explicitMounts.TryGetValue(filename.Substring(0, explicitSplit), out explicitPackage))
|
||||||
var filename = divide.Last();
|
if (explicitPackage.Contains(filename.Substring(explicitSplit + 1)))
|
||||||
return mountedPackages.Keys.Where(n => n.Name == packageName).Any(f => f.Contains(filename));
|
return true;
|
||||||
}
|
}
|
||||||
else
|
|
||||||
return mountedPackages.Keys.Any(f => f.Contains(name));
|
return fileIndex.ContainsKey(filename);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -35,11 +35,12 @@ namespace OpenRA
|
|||||||
{
|
{
|
||||||
public readonly ModMetadata Mod;
|
public readonly ModMetadata Mod;
|
||||||
public readonly string[]
|
public readonly string[]
|
||||||
Packages, Rules, ServerTraits,
|
Rules, ServerTraits,
|
||||||
Sequences, VoxelSequences, Cursors, Chrome, Assemblies, ChromeLayout,
|
Sequences, VoxelSequences, Cursors, Chrome, Assemblies, ChromeLayout,
|
||||||
Weapons, Voices, Notifications, Music, Translations, TileSets,
|
Weapons, Voices, Notifications, Music, Translations, TileSets,
|
||||||
ChromeMetrics, MapCompatibility, Missions;
|
ChromeMetrics, MapCompatibility, Missions;
|
||||||
|
|
||||||
|
public readonly IReadOnlyDictionary<string, string> Packages;
|
||||||
public readonly IReadOnlyDictionary<string, string> MapFolders;
|
public readonly IReadOnlyDictionary<string, string> MapFolders;
|
||||||
public readonly MiniYaml LoadScreen;
|
public readonly MiniYaml LoadScreen;
|
||||||
public readonly MiniYaml LobbyDefaults;
|
public readonly MiniYaml LobbyDefaults;
|
||||||
@@ -70,7 +71,11 @@ namespace OpenRA
|
|||||||
|
|
||||||
// TODO: Use fieldloader
|
// TODO: Use fieldloader
|
||||||
MapFolders = YamlDictionary(yaml, "MapFolders", true);
|
MapFolders = YamlDictionary(yaml, "MapFolders", true);
|
||||||
Packages = YamlList(yaml, "Packages", true);
|
|
||||||
|
MiniYaml packages;
|
||||||
|
if (yaml.TryGetValue("Packages", out packages))
|
||||||
|
Packages = packages.ToDictionary(x => Platform.ResolvePath(x), x => x.Value).AsReadOnly();
|
||||||
|
|
||||||
Rules = YamlList(yaml, "Rules", true);
|
Rules = YamlList(yaml, "Rules", true);
|
||||||
Sequences = YamlList(yaml, "Sequences", true);
|
Sequences = YamlList(yaml, "Sequences", true);
|
||||||
VoxelSequences = YamlList(yaml, "VoxelSequences", true);
|
VoxelSequences = YamlList(yaml, "VoxelSequences", true);
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
Speech:
|
Speech:
|
||||||
Prefixes:
|
Prefixes:
|
||||||
gdi: speech01.mix:
|
gdi: speech-gdi|
|
||||||
nod: speech02.mix:
|
nod: speech-nod|
|
||||||
Notifications:
|
Notifications:
|
||||||
AirUnitLost: 00-i074
|
AirUnitLost: 00-i074
|
||||||
AirstrikeReady: 00-n160
|
AirstrikeReady: 00-n160
|
||||||
|
|||||||
@@ -32,13 +32,13 @@ Packages:
|
|||||||
isosnow.mix
|
isosnow.mix
|
||||||
isotemp.mix
|
isotemp.mix
|
||||||
local.mix
|
local.mix
|
||||||
sidec01.mix
|
sidec01.mix: sidebar-gdi
|
||||||
sidec02.mix
|
sidec02.mix: sidebar-nod
|
||||||
sno.mix
|
sno.mix
|
||||||
snow.mix
|
snow.mix
|
||||||
sounds.mix
|
sounds.mix
|
||||||
speech01.mix # EVA
|
speech01.mix: speech-gdi
|
||||||
speech02.mix # Cabal
|
speech02.mix: speech-nod
|
||||||
tem.mix
|
tem.mix
|
||||||
temperat.mix
|
temperat.mix
|
||||||
# Firestorm
|
# Firestorm
|
||||||
|
|||||||
@@ -43,7 +43,7 @@
|
|||||||
Filename: anim.pal
|
Filename: anim.pal
|
||||||
PaletteFromFile@sidebar:
|
PaletteFromFile@sidebar:
|
||||||
Name: sidebar
|
Name: sidebar
|
||||||
Filename: sidec02.mix:sidebar.pal
|
Filename: sidebar-nod|sidebar.pal
|
||||||
PaletteFromPaletteWithAlpha@clock:
|
PaletteFromPaletteWithAlpha@clock:
|
||||||
Name: iconclock
|
Name: iconclock
|
||||||
BasePalette: sidebar
|
BasePalette: sidebar
|
||||||
|
|||||||
@@ -66,7 +66,7 @@ e1.gdi:
|
|||||||
ShadowStart: 190
|
ShadowStart: 190
|
||||||
die6: electro
|
die6: electro
|
||||||
Length: *
|
Length: *
|
||||||
icon: sidec01.mix:e1icon
|
icon: sidebar-gdi|e1icon
|
||||||
|
|
||||||
e1.nod:
|
e1.nod:
|
||||||
Defaults: e1
|
Defaults: e1
|
||||||
@@ -136,7 +136,7 @@ e1.nod:
|
|||||||
ShadowStart: 190
|
ShadowStart: 190
|
||||||
die6: electro
|
die6: electro
|
||||||
Length: *
|
Length: *
|
||||||
icon: sidec02.mix:e1icon
|
icon: sidebar-nod|e1icon
|
||||||
|
|
||||||
e2:
|
e2:
|
||||||
Defaults:
|
Defaults:
|
||||||
@@ -477,7 +477,7 @@ engineer.gdi:
|
|||||||
ShadowStart: 190
|
ShadowStart: 190
|
||||||
die6: electro
|
die6: electro
|
||||||
Length: *
|
Length: *
|
||||||
icon: sidec01.mix:engnicon
|
icon: sidebar-gdi|engnicon
|
||||||
|
|
||||||
engineer.nod:
|
engineer.nod:
|
||||||
Defaults: engineer
|
Defaults: engineer
|
||||||
@@ -539,7 +539,7 @@ engineer.nod:
|
|||||||
ShadowStart: 190
|
ShadowStart: 190
|
||||||
die6: electro
|
die6: electro
|
||||||
Length: *
|
Length: *
|
||||||
icon: sidec02.mix:engnicon
|
icon: sidebar-nod|engnicon
|
||||||
|
|
||||||
umagon:
|
umagon:
|
||||||
Defaults:
|
Defaults:
|
||||||
|
|||||||
@@ -912,7 +912,7 @@ napuls.gdi:
|
|||||||
UseTilesetCode: false
|
UseTilesetCode: false
|
||||||
ZOffset: 512
|
ZOffset: 512
|
||||||
BlendMode: Additive
|
BlendMode: Additive
|
||||||
icon: sidec01.mix:empicon
|
icon: sidebar-gdi|empicon
|
||||||
Offset: 0, 0
|
Offset: 0, 0
|
||||||
UseTilesetCode: false
|
UseTilesetCode: false
|
||||||
|
|
||||||
@@ -940,7 +940,7 @@ napuls.nod:
|
|||||||
UseTilesetCode: false
|
UseTilesetCode: false
|
||||||
ZOffset: 512
|
ZOffset: 512
|
||||||
BlendMode: Additive
|
BlendMode: Additive
|
||||||
icon: sidec02.mix:empicon
|
icon: sidebar-nod|empicon
|
||||||
Offset: 0, 0
|
Offset: 0, 0
|
||||||
UseTilesetCode: false
|
UseTilesetCode: false
|
||||||
|
|
||||||
@@ -1190,7 +1190,7 @@ proc.gdi:
|
|||||||
UseTilesetCode: false
|
UseTilesetCode: false
|
||||||
ZOffset: 512
|
ZOffset: 512
|
||||||
BlendMode: Additive
|
BlendMode: Additive
|
||||||
icon: sidec01.mix:reficon
|
icon: sidebar-gdi|reficon
|
||||||
Offset: 0, 0
|
Offset: 0, 0
|
||||||
UseTilesetCode: false
|
UseTilesetCode: false
|
||||||
|
|
||||||
@@ -1232,7 +1232,7 @@ proc.nod:
|
|||||||
UseTilesetCode: false
|
UseTilesetCode: false
|
||||||
ZOffset: 512
|
ZOffset: 512
|
||||||
BlendMode: Additive
|
BlendMode: Additive
|
||||||
icon: sidec02.mix:reficon
|
icon: sidebar-nod|reficon
|
||||||
Offset: 0, 0
|
Offset: 0, 0
|
||||||
UseTilesetCode: false
|
UseTilesetCode: false
|
||||||
|
|
||||||
@@ -1323,7 +1323,7 @@ gasilo.gdi:
|
|||||||
UseTilesetCode: false
|
UseTilesetCode: false
|
||||||
ZOffset: 512
|
ZOffset: 512
|
||||||
BlendMode: Additive
|
BlendMode: Additive
|
||||||
icon: sidec01.mix:siloicon
|
icon: sidebar-gdi|siloicon
|
||||||
Offset: 0, 0
|
Offset: 0, 0
|
||||||
UseTilesetCode: false
|
UseTilesetCode: false
|
||||||
|
|
||||||
@@ -1367,7 +1367,7 @@ gasilo.nod:
|
|||||||
UseTilesetCode: false
|
UseTilesetCode: false
|
||||||
ZOffset: 512
|
ZOffset: 512
|
||||||
BlendMode: Additive
|
BlendMode: Additive
|
||||||
icon: sidec02.mix:siloicon
|
icon: sidebar-nod|siloicon
|
||||||
Offset: 0, 0
|
Offset: 0, 0
|
||||||
UseTilesetCode: false
|
UseTilesetCode: false
|
||||||
|
|
||||||
@@ -1431,7 +1431,7 @@ gadept.gdi:
|
|||||||
UseTilesetCode: false
|
UseTilesetCode: false
|
||||||
ZOffset: 512
|
ZOffset: 512
|
||||||
BlendMode: Additive
|
BlendMode: Additive
|
||||||
icon: sidec01.mix:fixicon
|
icon: sidebar-gdi|fixicon
|
||||||
Offset: 0, 0
|
Offset: 0, 0
|
||||||
UseTilesetCode: false
|
UseTilesetCode: false
|
||||||
|
|
||||||
@@ -1495,7 +1495,7 @@ gadept.nod:
|
|||||||
UseTilesetCode: false
|
UseTilesetCode: false
|
||||||
ZOffset: 512
|
ZOffset: 512
|
||||||
BlendMode: Additive
|
BlendMode: Additive
|
||||||
icon: sidec02.mix:fixicon
|
icon: sidebar-nod|fixicon
|
||||||
Offset: 76, 66
|
Offset: 76, 66
|
||||||
UseTilesetCode: false
|
UseTilesetCode: false
|
||||||
|
|
||||||
|
|||||||
@@ -2,13 +2,13 @@ mcv.gdi:
|
|||||||
emp-overlay: emp_fx01
|
emp-overlay: emp_fx01
|
||||||
Length: *
|
Length: *
|
||||||
BlendMode: Additive
|
BlendMode: Additive
|
||||||
icon: sidec01.mix:mcvicon
|
icon: sidebar-gdi|mcvicon
|
||||||
|
|
||||||
mcv.nod:
|
mcv.nod:
|
||||||
emp-overlay: emp_fx01
|
emp-overlay: emp_fx01
|
||||||
Length: *
|
Length: *
|
||||||
BlendMode: Additive
|
BlendMode: Additive
|
||||||
icon: sidec02.mix:mcvicon
|
icon: sidebar-nod|mcvicon
|
||||||
|
|
||||||
apc:
|
apc:
|
||||||
emp-overlay: emp_fx01
|
emp-overlay: emp_fx01
|
||||||
@@ -22,7 +22,7 @@ harv.gdi:
|
|||||||
BlendMode: Additive
|
BlendMode: Additive
|
||||||
harvest: harvestr
|
harvest: harvestr
|
||||||
Length: *
|
Length: *
|
||||||
icon: sidec01.mix:harvicon
|
icon: sidebar-gdi|harvicon
|
||||||
|
|
||||||
harv.nod:
|
harv.nod:
|
||||||
emp-overlay: emp_fx01
|
emp-overlay: emp_fx01
|
||||||
@@ -30,7 +30,7 @@ harv.nod:
|
|||||||
BlendMode: Additive
|
BlendMode: Additive
|
||||||
harvest: harvestr
|
harvest: harvestr
|
||||||
Length: *
|
Length: *
|
||||||
icon: sidec02.mix:harvicon
|
icon: sidebar-nod|harvicon
|
||||||
|
|
||||||
hvr:
|
hvr:
|
||||||
emp-overlay: emp_fx01
|
emp-overlay: emp_fx01
|
||||||
@@ -56,7 +56,7 @@ lpst.gdi:
|
|||||||
emp-overlay: emp_fx01
|
emp-overlay: emp_fx01
|
||||||
Length: *
|
Length: *
|
||||||
BlendMode: Additive
|
BlendMode: Additive
|
||||||
icon: sidec01.mix:lpsticon
|
icon: sidebar-gdi|lpsticon
|
||||||
|
|
||||||
lpst.nod:
|
lpst.nod:
|
||||||
idle: gadpsa
|
idle: gadpsa
|
||||||
@@ -69,7 +69,7 @@ lpst.nod:
|
|||||||
emp-overlay: emp_fx01
|
emp-overlay: emp_fx01
|
||||||
Length: *
|
Length: *
|
||||||
BlendMode: Additive
|
BlendMode: Additive
|
||||||
icon: sidec02.mix:lpsticon
|
icon: sidebar-nod|lpsticon
|
||||||
|
|
||||||
repair:
|
repair:
|
||||||
emp-overlay: emp_fx01
|
emp-overlay: emp_fx01
|
||||||
|
|||||||
Reference in New Issue
Block a user