Merge pull request #3305 from pchote/mixdatabase
Parse XCC mix databases
This commit is contained in:
44
OpenRA.FileFormats/FileFormats/XccGlobalDatabase.cs
Normal file
44
OpenRA.FileFormats/FileFormats/XccGlobalDatabase.cs
Normal file
@@ -0,0 +1,44 @@
|
||||
#region Copyright & License Information
|
||||
/*
|
||||
* Copyright 2007-2013 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.Collections.Generic;
|
||||
using System.IO;
|
||||
|
||||
namespace OpenRA.FileFormats
|
||||
{
|
||||
public class XccGlobalDatabase
|
||||
{
|
||||
public readonly string[] Entries;
|
||||
public XccGlobalDatabase(Stream s)
|
||||
{
|
||||
var entries = new List<string>();
|
||||
var reader = new BinaryReader(s);
|
||||
while (reader.PeekChar() > -1)
|
||||
{
|
||||
var count = reader.ReadInt32();
|
||||
for (var i = 0; i < count; i++)
|
||||
{
|
||||
var chars = new List<char>();
|
||||
char c;
|
||||
|
||||
// Read filename
|
||||
while ((c = reader.ReadChar()) != 0)
|
||||
chars.Add(c);
|
||||
entries.Add(new string(chars.ToArray()));
|
||||
|
||||
// Skip comment
|
||||
while ((c = reader.ReadChar()) != 0);
|
||||
}
|
||||
}
|
||||
|
||||
Entries = entries.ToArray();
|
||||
}
|
||||
}
|
||||
}
|
||||
67
OpenRA.FileFormats/FileFormats/XccLocalDatabase.cs
Normal file
67
OpenRA.FileFormats/FileFormats/XccLocalDatabase.cs
Normal file
@@ -0,0 +1,67 @@
|
||||
#region Copyright & License Information
|
||||
/*
|
||||
* Copyright 2007-2013 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.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
namespace OpenRA.FileFormats
|
||||
{
|
||||
public class XccLocalDatabase
|
||||
{
|
||||
public readonly string[] Entries;
|
||||
public XccLocalDatabase(Stream s)
|
||||
{
|
||||
// Skip unnecessary header data
|
||||
s.Seek(48, SeekOrigin.Begin);
|
||||
var reader = new BinaryReader(s);
|
||||
var count = reader.ReadInt32();
|
||||
Entries = new string[count];
|
||||
for (var i = 0; i < count; i++)
|
||||
{
|
||||
var chars = new List<char>();
|
||||
char c;
|
||||
while ((c = reader.ReadChar()) != 0)
|
||||
chars.Add(c);
|
||||
|
||||
Entries[i] = new string(chars.ToArray());
|
||||
}
|
||||
}
|
||||
|
||||
public XccLocalDatabase(IEnumerable<string> filenames)
|
||||
{
|
||||
Entries = filenames.ToArray();
|
||||
}
|
||||
|
||||
public byte[] Data()
|
||||
{
|
||||
var data = new MemoryStream();
|
||||
using (var writer = new BinaryWriter(data))
|
||||
{
|
||||
writer.Write(Encoding.ASCII.GetBytes("XCC by Olaf van der Spek"));
|
||||
writer.Write(new byte[] {0x1A,0x04,0x17,0x27,0x10,0x19,0x80,0x00});
|
||||
|
||||
writer.Write((int)(Entries.Aggregate(Entries.Length, (a,b) => a + b.Length) + 52)); // Size
|
||||
writer.Write((int)0); // Type
|
||||
writer.Write((int)0); // Version
|
||||
writer.Write((int)0); // Game/Format (0 == TD)
|
||||
writer.Write((int)Entries.Length); // Entries
|
||||
foreach (var e in Entries)
|
||||
{
|
||||
writer.Write(Encoding.ASCII.GetBytes(e));
|
||||
writer.Write((byte)0);
|
||||
}
|
||||
}
|
||||
|
||||
return data.ToArray();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -18,9 +18,9 @@ namespace OpenRA.FileFormats
|
||||
{
|
||||
public static class FileSystem
|
||||
{
|
||||
static List<IFolder> MountedFolders = new List<IFolder>();
|
||||
|
||||
static Cache<uint, List<IFolder>> allFiles = new Cache<uint, List<IFolder>>( _ => new List<IFolder>() );
|
||||
public static List<IFolder> MountedFolders = new List<IFolder>();
|
||||
static Cache<uint, List<IFolder>> classicHashIndex = new Cache<uint, List<IFolder>>( _ => new List<IFolder>() );
|
||||
static Cache<uint, List<IFolder>> crcHashIndex = new Cache<uint, List<IFolder>>( _ => new List<IFolder>() );
|
||||
|
||||
public static List<string> FolderPaths = new List<string>();
|
||||
|
||||
@@ -28,9 +28,16 @@ namespace OpenRA.FileFormats
|
||||
{
|
||||
MountedFolders.Add(folder);
|
||||
|
||||
foreach (var hash in folder.AllFileHashes())
|
||||
foreach (var hash in folder.ClassicHashes())
|
||||
{
|
||||
var l = allFiles[hash];
|
||||
var l = classicHashIndex[hash];
|
||||
if (!l.Contains(folder))
|
||||
l.Add(folder);
|
||||
}
|
||||
|
||||
foreach (var hash in folder.CrcHashes())
|
||||
{
|
||||
var l = crcHashIndex[hash];
|
||||
if (!l.Contains(folder))
|
||||
l.Add(folder);
|
||||
}
|
||||
@@ -38,11 +45,6 @@ namespace OpenRA.FileFormats
|
||||
|
||||
static int order = 0;
|
||||
|
||||
static IFolder OpenPackage(string filename)
|
||||
{
|
||||
return OpenPackage(filename, order++);
|
||||
}
|
||||
|
||||
public static IFolder CreatePackage(string filename, int order, Dictionary<string, byte[]> content)
|
||||
{
|
||||
if (filename.EndsWith(".mix", StringComparison.InvariantCultureIgnoreCase))
|
||||
@@ -57,10 +59,14 @@ namespace OpenRA.FileFormats
|
||||
return new Folder(filename, order, content);
|
||||
}
|
||||
|
||||
public static IFolder OpenPackage(string filename, int order)
|
||||
public static IFolder OpenPackage(string filename, string annotation, int order)
|
||||
{
|
||||
if (filename.EndsWith(".mix", StringComparison.InvariantCultureIgnoreCase))
|
||||
return new MixFile(filename, order);
|
||||
{
|
||||
var type = string.IsNullOrEmpty(annotation) ? PackageHashType.Classic :
|
||||
FieldLoader.GetValue<PackageHashType>("(value)", annotation);
|
||||
return new MixFile(filename, type, order);
|
||||
}
|
||||
else if (filename.EndsWith(".zip", StringComparison.InvariantCultureIgnoreCase))
|
||||
return new ZipFile(filename, order);
|
||||
else if (filename.EndsWith(".oramap", StringComparison.InvariantCultureIgnoreCase))
|
||||
@@ -72,6 +78,11 @@ namespace OpenRA.FileFormats
|
||||
}
|
||||
|
||||
public static void Mount(string name)
|
||||
{
|
||||
Mount(name, null);
|
||||
}
|
||||
|
||||
public static void Mount(string name, string annotation)
|
||||
{
|
||||
var optional = name.StartsWith("~");
|
||||
if (optional) name = name.Substring(1);
|
||||
@@ -80,10 +91,8 @@ namespace OpenRA.FileFormats
|
||||
if (name.StartsWith("^"))
|
||||
name = Platform.SupportDir+name.Substring(1);
|
||||
|
||||
if (Directory.Exists(name))
|
||||
FolderPaths.Add(name);
|
||||
|
||||
var a = (Action)(() => FileSystem.MountInner(OpenPackage(name)));
|
||||
FolderPaths.Add(name);
|
||||
Action a = () => FileSystem.MountInner(OpenPackage(name, annotation, order++));
|
||||
|
||||
if (optional)
|
||||
try { a(); }
|
||||
@@ -96,7 +105,8 @@ namespace OpenRA.FileFormats
|
||||
{
|
||||
MountedFolders.Clear();
|
||||
FolderPaths.Clear();
|
||||
allFiles = new Cache<uint, List<IFolder>>( _ => new List<IFolder>() );
|
||||
classicHashIndex = new Cache<uint, List<IFolder>>(_ => new List<IFolder>());
|
||||
crcHashIndex = new Cache<uint, List<IFolder>>(_ => new List<IFolder>());
|
||||
}
|
||||
|
||||
public static bool Unmount(IFolder mount)
|
||||
@@ -112,13 +122,17 @@ namespace OpenRA.FileFormats
|
||||
public static void LoadFromManifest(Manifest manifest)
|
||||
{
|
||||
UnmountAll();
|
||||
foreach (var dir in manifest.Folders) Mount(dir);
|
||||
foreach (var pkg in manifest.Packages) Mount(pkg);
|
||||
foreach (var dir in manifest.Folders)
|
||||
Mount(dir);
|
||||
|
||||
foreach (var pkg in manifest.Packages)
|
||||
Mount(pkg.Key, pkg.Value);
|
||||
}
|
||||
|
||||
static Stream GetFromCache(Cache<uint, List<IFolder>> index, string filename)
|
||||
static Stream GetFromCache(PackageHashType type, string filename)
|
||||
{
|
||||
var folder = index[PackageEntry.HashFilename(filename)]
|
||||
var index = type == PackageHashType.CRC32 ? crcHashIndex : classicHashIndex;
|
||||
var folder = index[PackageEntry.HashFilename(filename, type)]
|
||||
.Where(x => x.Exists(filename))
|
||||
.OrderBy(x => x.Priority)
|
||||
.FirstOrDefault();
|
||||
@@ -133,11 +147,15 @@ namespace OpenRA.FileFormats
|
||||
|
||||
public static Stream OpenWithExts(string filename, params string[] exts)
|
||||
{
|
||||
if( filename.IndexOfAny( new char[] { '/', '\\' } ) == -1 )
|
||||
if (filename.IndexOfAny(new char[] { '/', '\\' } ) == -1)
|
||||
{
|
||||
foreach( var ext in exts )
|
||||
foreach (var ext in exts)
|
||||
{
|
||||
var s = GetFromCache(allFiles, filename + ext);
|
||||
var s = GetFromCache(PackageHashType.Classic, filename + ext);
|
||||
if (s != null)
|
||||
return s;
|
||||
|
||||
s = GetFromCache(PackageHashType.CRC32, filename + ext);
|
||||
if (s != null)
|
||||
return s;
|
||||
}
|
||||
|
||||
@@ -17,8 +17,7 @@ namespace OpenRA.FileFormats
|
||||
public class Folder : IFolder
|
||||
{
|
||||
readonly string path;
|
||||
|
||||
int priority;
|
||||
readonly int priority;
|
||||
|
||||
// Create a new folder package
|
||||
public Folder(string path, int priority, Dictionary<string, byte[]> contents)
|
||||
@@ -45,13 +44,21 @@ namespace OpenRA.FileFormats
|
||||
catch { return null; }
|
||||
}
|
||||
|
||||
public IEnumerable<uint> AllFileHashes()
|
||||
public IEnumerable<uint> ClassicHashes()
|
||||
{
|
||||
foreach (var filename in Directory.GetFiles(path, "*", SearchOption.TopDirectoryOnly))
|
||||
{
|
||||
yield return PackageEntry.HashFilename(Path.GetFileName(filename)); // RA1 and TD
|
||||
yield return PackageEntry.CrcHashFilename(Path.GetFileName(filename)); // TS
|
||||
}
|
||||
yield return PackageEntry.HashFilename(Path.GetFileName(filename), PackageHashType.Classic);
|
||||
}
|
||||
|
||||
public IEnumerable<uint> CrcHashes()
|
||||
{
|
||||
yield break;
|
||||
}
|
||||
|
||||
public IEnumerable<string> AllFileNames()
|
||||
{
|
||||
foreach (var filename in Directory.GetFiles(path, "*", SearchOption.TopDirectoryOnly))
|
||||
yield return Path.GetFileName(filename);
|
||||
}
|
||||
|
||||
public bool Exists(string filename)
|
||||
@@ -59,11 +66,8 @@ namespace OpenRA.FileFormats
|
||||
return File.Exists(Path.Combine(path, filename));
|
||||
}
|
||||
|
||||
|
||||
public int Priority
|
||||
{
|
||||
get { return priority; }
|
||||
}
|
||||
public int Priority { get { return priority; } }
|
||||
public string Name { get { return path; } }
|
||||
|
||||
public void Write(Dictionary<string, byte[]> contents)
|
||||
{
|
||||
|
||||
@@ -18,13 +18,17 @@ namespace OpenRA.FileFormats
|
||||
public class InstallShieldPackage : IFolder
|
||||
{
|
||||
readonly Dictionary<uint, PackageEntry> index = new Dictionary<uint, PackageEntry>();
|
||||
readonly List<string> filenames;
|
||||
readonly Stream s;
|
||||
readonly long dataStart = 255;
|
||||
int priority;
|
||||
readonly int priority;
|
||||
readonly string filename;
|
||||
|
||||
public InstallShieldPackage(string filename, int priority)
|
||||
{
|
||||
this.filename = filename;
|
||||
this.priority = priority;
|
||||
filenames = new List<string>();
|
||||
s = FileSystem.Open(filename);
|
||||
|
||||
// Parse package header
|
||||
@@ -76,8 +80,9 @@ namespace OpenRA.FileFormats
|
||||
var NameLength = reader.ReadByte();
|
||||
var FileName = new String(reader.ReadChars(NameLength));
|
||||
|
||||
var hash = PackageEntry.HashFilename(FileName);
|
||||
index.Add(hash, new PackageEntry(hash,AccumulatedData, CompressedSize));
|
||||
var hash = PackageEntry.HashFilename(FileName, PackageHashType.Classic);
|
||||
index.Add(hash, new PackageEntry(hash, AccumulatedData, CompressedSize));
|
||||
filenames.Add(FileName);
|
||||
AccumulatedData += CompressedSize;
|
||||
|
||||
// Skip to the end of the chunk
|
||||
@@ -99,24 +104,31 @@ namespace OpenRA.FileFormats
|
||||
|
||||
public Stream GetContent(string filename)
|
||||
{
|
||||
return GetContent(PackageEntry.HashFilename(filename));
|
||||
return GetContent(PackageEntry.HashFilename(filename, PackageHashType.Classic));
|
||||
}
|
||||
|
||||
public IEnumerable<uint> AllFileHashes()
|
||||
public IEnumerable<uint> ClassicHashes()
|
||||
{
|
||||
return index.Keys;
|
||||
}
|
||||
|
||||
public IEnumerable<uint> CrcHashes()
|
||||
{
|
||||
yield break;
|
||||
}
|
||||
|
||||
public IEnumerable<string> AllFileNames()
|
||||
{
|
||||
return filenames;
|
||||
}
|
||||
|
||||
public bool Exists(string filename)
|
||||
{
|
||||
return index.ContainsKey(PackageEntry.HashFilename(filename));
|
||||
return index.ContainsKey(PackageEntry.HashFilename(filename, PackageHashType.Classic));
|
||||
}
|
||||
|
||||
|
||||
public int Priority
|
||||
{
|
||||
get { return 2000 + priority; }
|
||||
}
|
||||
public int Priority { get { return 2000 + priority; }}
|
||||
public string Name { get { return filename; } }
|
||||
|
||||
public void Write(Dictionary<string, byte[]> contents)
|
||||
{
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
|
||||
@@ -19,9 +20,12 @@ namespace OpenRA.FileFormats
|
||||
{
|
||||
Stream GetContent(string filename);
|
||||
bool Exists(string filename);
|
||||
IEnumerable<uint> AllFileHashes();
|
||||
IEnumerable<uint> ClassicHashes();
|
||||
IEnumerable<uint> CrcHashes();
|
||||
IEnumerable<string> AllFileNames();
|
||||
void Write(Dictionary<string, byte[]> contents);
|
||||
int Priority { get; }
|
||||
string Name { get; }
|
||||
}
|
||||
|
||||
public class MixFile : IFolder
|
||||
@@ -29,24 +33,31 @@ namespace OpenRA.FileFormats
|
||||
readonly Dictionary<uint, PackageEntry> index;
|
||||
readonly long dataStart;
|
||||
readonly Stream s;
|
||||
int priority;
|
||||
readonly int priority;
|
||||
readonly string filename;
|
||||
readonly PackageHashType type;
|
||||
|
||||
// Save a mix to disk with the given contents
|
||||
public MixFile(string filename, int priority, Dictionary<string, byte[]> contents)
|
||||
{
|
||||
this.filename = filename;
|
||||
this.priority = priority;
|
||||
this.type = PackageHashType.Classic;
|
||||
|
||||
if (File.Exists(filename))
|
||||
File.Delete(filename);
|
||||
|
||||
s = File.Create(filename);
|
||||
|
||||
// TODO: Add a local mix database.dat for compatibility with XCC Mixer
|
||||
index = new Dictionary<uint, PackageEntry>();
|
||||
contents.Add("local mix database.dat", new XccLocalDatabase(contents.Keys.Append("local mix database.dat")).Data());
|
||||
Write(contents);
|
||||
}
|
||||
|
||||
public MixFile(string filename, int priority)
|
||||
public MixFile(string filename, PackageHashType type, int priority)
|
||||
{
|
||||
this.filename = filename;
|
||||
this.priority = priority;
|
||||
this.type = type;
|
||||
s = FileSystem.Open(filename);
|
||||
|
||||
// Detect format type
|
||||
@@ -137,6 +148,23 @@ namespace OpenRA.FileFormats
|
||||
return ret;
|
||||
}
|
||||
|
||||
uint? FindMatchingHash(string filename)
|
||||
{
|
||||
var hash = PackageEntry.HashFilename(filename, type);
|
||||
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(uint hash)
|
||||
{
|
||||
PackageEntry e;
|
||||
@@ -151,29 +179,63 @@ namespace OpenRA.FileFormats
|
||||
|
||||
public Stream GetContent(string filename)
|
||||
{
|
||||
var content = GetContent(PackageEntry.HashFilename(filename)); // RA1 and TD
|
||||
if (content != null)
|
||||
return content;
|
||||
else
|
||||
return GetContent(PackageEntry.CrcHashFilename(filename)); // TS
|
||||
var hash = FindMatchingHash(filename);
|
||||
return hash.HasValue ? GetContent(hash.Value) : null;
|
||||
}
|
||||
|
||||
public IEnumerable<uint> AllFileHashes()
|
||||
static readonly uint[] Nothing = {};
|
||||
public IEnumerable<uint> ClassicHashes()
|
||||
{
|
||||
return index.Keys;
|
||||
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()
|
||||
{
|
||||
var lookup = new Dictionary<uint, string>();
|
||||
if (Exists("local mix database.dat"))
|
||||
{
|
||||
var db = new XccLocalDatabase(GetContent("local mix database.dat"));
|
||||
foreach (var e in db.Entries)
|
||||
{
|
||||
var hash = PackageEntry.HashFilename(e, type);
|
||||
if (!lookup.ContainsKey(hash))
|
||||
lookup.Add(hash, e);
|
||||
}
|
||||
}
|
||||
|
||||
if (FileSystem.Exists("global mix database.dat"))
|
||||
{
|
||||
var db = new XccGlobalDatabase(FileSystem.Open("global mix database.dat"));
|
||||
foreach (var e in db.Entries)
|
||||
{
|
||||
var hash = PackageEntry.HashFilename(e, type);
|
||||
if (!lookup.ContainsKey(hash))
|
||||
lookup.Add(hash, e);
|
||||
}
|
||||
}
|
||||
|
||||
return index.Keys.Select(k => lookup.ContainsKey(k) ? lookup[k] : "{0:X}".F(k));
|
||||
}
|
||||
|
||||
public bool Exists(string filename)
|
||||
{
|
||||
return (index.ContainsKey(PackageEntry.HashFilename(filename)) || index.ContainsKey(PackageEntry.CrcHashFilename(filename)));
|
||||
}
|
||||
|
||||
|
||||
public int Priority
|
||||
{
|
||||
get { return 1000 + priority; }
|
||||
return FindMatchingHash(filename).HasValue;
|
||||
}
|
||||
|
||||
public int Priority { get { return 1000 + priority; } }
|
||||
public string Name { get { return filename; } }
|
||||
|
||||
public void Write(Dictionary<string, byte[]> contents)
|
||||
{
|
||||
// Cannot modify existing mixfile - rename existing file and
|
||||
@@ -190,8 +252,8 @@ namespace OpenRA.FileFormats
|
||||
foreach (var kv in contents)
|
||||
{
|
||||
var length = (uint)kv.Value.Length;
|
||||
var hash = PackageEntry.HashFilename(Path.GetFileName(kv.Key));
|
||||
items.Add(new PackageEntry(hash, dataSize, length)); // TODO: Tiberian Sun uses CRC hashes
|
||||
var hash = PackageEntry.HashFilename(Path.GetFileName(kv.Key), type);
|
||||
items.Add(new PackageEntry(hash, dataSize, length));
|
||||
dataSize += length;
|
||||
}
|
||||
|
||||
|
||||
@@ -51,7 +51,6 @@ namespace OpenRA.FileFormats
|
||||
|
||||
public Stream GetContent(string filename)
|
||||
{
|
||||
|
||||
using (var z = pkg.GetInputStream(pkg.GetEntry(filename)))
|
||||
{
|
||||
var ms = new MemoryStream();
|
||||
@@ -65,10 +64,21 @@ namespace OpenRA.FileFormats
|
||||
}
|
||||
}
|
||||
|
||||
public IEnumerable<uint> AllFileHashes()
|
||||
public IEnumerable<uint> ClassicHashes()
|
||||
{
|
||||
foreach(ZipEntry entry in pkg)
|
||||
yield return PackageEntry.HashFilename(entry.Name);
|
||||
yield return PackageEntry.HashFilename(entry.Name, PackageHashType.Classic);
|
||||
}
|
||||
|
||||
public IEnumerable<uint> CrcHashes()
|
||||
{
|
||||
yield break;
|
||||
}
|
||||
|
||||
public IEnumerable<string> AllFileNames()
|
||||
{
|
||||
foreach(ZipEntry entry in pkg)
|
||||
yield return entry.Name;
|
||||
}
|
||||
|
||||
public bool Exists(string filename)
|
||||
@@ -76,10 +86,8 @@ namespace OpenRA.FileFormats
|
||||
return pkg.GetEntry(filename) != null;
|
||||
}
|
||||
|
||||
public int Priority
|
||||
{
|
||||
get { return 500 + priority; }
|
||||
}
|
||||
public int Priority { get { return 500 + priority; } }
|
||||
public string Name { get { return filename; } }
|
||||
|
||||
public void Write(Dictionary<string, byte[]> contents)
|
||||
{
|
||||
|
||||
@@ -19,10 +19,12 @@ namespace OpenRA.FileFormats
|
||||
public class Manifest
|
||||
{
|
||||
public readonly string[]
|
||||
Mods, Folders, Packages, Rules, ServerTraits,
|
||||
Mods, Folders, Rules, ServerTraits,
|
||||
Sequences, Cursors, Chrome, Assemblies, ChromeLayout,
|
||||
Weapons, Voices, Notifications, Music, Movies, TileSets,
|
||||
ChromeMetrics, PackageContents;
|
||||
|
||||
public readonly Dictionary<string, string> Packages;
|
||||
public readonly MiniYaml LoadScreen;
|
||||
public readonly Dictionary<string, Pair<string,int>> Fonts;
|
||||
public readonly int TileSize = 24;
|
||||
@@ -36,7 +38,7 @@ namespace OpenRA.FileFormats
|
||||
|
||||
// TODO: Use fieldloader
|
||||
Folders = YamlList(yaml, "Folders");
|
||||
Packages = YamlList(yaml, "Packages");
|
||||
Packages = yaml["Packages"].NodesDict.ToDictionary(x => x.Key, x => x.Value.Value);
|
||||
Rules = YamlList(yaml, "Rules");
|
||||
ServerTraits = YamlList(yaml, "ServerTraits");
|
||||
Sequences = YamlList(yaml, "Sequences");
|
||||
|
||||
@@ -139,6 +139,8 @@
|
||||
<Compile Include="WRange.cs" />
|
||||
<Compile Include="HSLColor.cs" />
|
||||
<Compile Include="Graphics\ShpTSReader.cs" />
|
||||
<Compile Include="FileFormats\XccLocalDatabase.cs" />
|
||||
<Compile Include="FileFormats\XccGlobalDatabase.cs" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<BootstrapperPackage Include="Microsoft.Net.Client.3.5">
|
||||
|
||||
@@ -8,19 +8,21 @@
|
||||
*/
|
||||
#endregion
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
|
||||
namespace OpenRA.FileFormats
|
||||
{
|
||||
public enum PackageHashType { Classic, CRC32 }
|
||||
|
||||
public class PackageEntry
|
||||
{
|
||||
public readonly uint Hash;
|
||||
public readonly uint Offset;
|
||||
public readonly uint Length;
|
||||
|
||||
|
||||
public PackageEntry(uint hash, uint offset, uint length)
|
||||
{
|
||||
Hash = hash;
|
||||
@@ -51,49 +53,54 @@ namespace OpenRA.FileFormats
|
||||
return "0x{0:x8} - offset 0x{1:x8} - length 0x{2:x8}".F(Hash, Offset, Length);
|
||||
}
|
||||
|
||||
public static uint HashFilename(string name) // Red Alert 1 and Tiberian Dawn
|
||||
public static uint HashFilename(string name, PackageHashType type)
|
||||
{
|
||||
if (name.Length > 12)
|
||||
name = name.Substring(0, 12);
|
||||
|
||||
name = name.ToUpperInvariant();
|
||||
if (name.Length % 4 != 0)
|
||||
name = name.PadRight(name.Length + (4 - name.Length % 4), '\0');
|
||||
|
||||
MemoryStream ms = new MemoryStream(Encoding.ASCII.GetBytes(name));
|
||||
BinaryReader reader = new BinaryReader(ms);
|
||||
|
||||
int len = name.Length >> 2;
|
||||
uint result = 0;
|
||||
|
||||
while (len-- != 0)
|
||||
result = ((result << 1) | (result >> 31)) + reader.ReadUInt32();
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public static uint CrcHashFilename(string name) // Tiberian Sun
|
||||
{
|
||||
name = name.ToUpperInvariant();
|
||||
var l = name.Length;
|
||||
int a = l >> 2;
|
||||
if ((l & 3) != 0)
|
||||
switch(type)
|
||||
{
|
||||
name += (char)(l - (a << 2));
|
||||
int i = 3 - (l & 3);
|
||||
while (i-- != 0)
|
||||
name += name[a << 2];
|
||||
case PackageHashType.Classic:
|
||||
{
|
||||
name = name.ToUpperInvariant();
|
||||
if (name.Length % 4 != 0)
|
||||
name = name.PadRight(name.Length + (4 - name.Length % 4), '\0');
|
||||
|
||||
MemoryStream ms = new MemoryStream(Encoding.ASCII.GetBytes(name));
|
||||
BinaryReader reader = new BinaryReader(ms);
|
||||
|
||||
int len = name.Length >> 2;
|
||||
uint result = 0;
|
||||
|
||||
while (len-- != 0)
|
||||
result = ((result << 1) | (result >> 31)) + reader.ReadUInt32();
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
case PackageHashType.CRC32:
|
||||
{
|
||||
name = name.ToUpperInvariant();
|
||||
var l = name.Length;
|
||||
int a = l >> 2;
|
||||
if ((l & 3) != 0)
|
||||
{
|
||||
name += (char)(l - (a << 2));
|
||||
int i = 3 - (l & 3);
|
||||
while (i-- != 0)
|
||||
name += name[a << 2];
|
||||
}
|
||||
return CRC32.Calculate(Encoding.ASCII.GetBytes(name));
|
||||
}
|
||||
|
||||
default: throw new NotImplementedException("Unknown hash type `{0}`".F(type));
|
||||
}
|
||||
return CRC32.Calculate(Encoding.ASCII.GetBytes(name));
|
||||
}
|
||||
|
||||
static Dictionary<uint, string> Names = new Dictionary<uint,string>();
|
||||
|
||||
public static void AddStandardName(string s)
|
||||
{
|
||||
uint hash = HashFilename(s); // RA1 and TD
|
||||
uint hash = HashFilename(s, PackageHashType.Classic); // RA1 and TD
|
||||
Names.Add(hash, s);
|
||||
uint crcHash = CrcHashFilename(s); // TS
|
||||
uint crcHash = HashFilename(s, PackageHashType.CRC32); // TS
|
||||
Names.Add(crcHash, s);
|
||||
}
|
||||
|
||||
|
||||
@@ -311,9 +311,9 @@ namespace OpenRA
|
||||
Sound.StopVideo();
|
||||
Sound.Initialize();
|
||||
|
||||
modData = new ModData( mm );
|
||||
modData = new ModData(mm);
|
||||
Renderer.InitializeFonts(modData.Manifest);
|
||||
modData.LoadInitialAssets(true);
|
||||
modData.InitializeLoaders();
|
||||
|
||||
|
||||
PerfHistory.items["render"].hasNormalTick = false;
|
||||
|
||||
@@ -25,7 +25,6 @@ namespace OpenRA
|
||||
public static Dictionary<string, MusicInfo> Music;
|
||||
public static Dictionary<string, string> Movies;
|
||||
public static Dictionary<string, TileSet> TileSets;
|
||||
public static Dictionary<string, string> PackageContents;
|
||||
|
||||
public static void LoadRules(Manifest m, Map map)
|
||||
{
|
||||
@@ -36,7 +35,6 @@ namespace OpenRA
|
||||
Notifications = LoadYamlRules(m.Notifications, map.Notifications, (k, _) => new SoundInfo(k.Value));
|
||||
Music = LoadYamlRules(m.Music, new List<MiniYamlNode>(), (k, _) => new MusicInfo(k.Key, k.Value));
|
||||
Movies = LoadYamlRules(m.Movies, new List<MiniYamlNode>(), (k, v) => k.Value.Value);
|
||||
PackageContents = LoadYamlRules(m.PackageContents, new List<MiniYamlNode>(), (k, v) => k.Value.Value);
|
||||
|
||||
TileSets = new Dictionary<string, TileSet>();
|
||||
foreach (var file in m.TileSets)
|
||||
|
||||
@@ -104,7 +104,7 @@ namespace OpenRA
|
||||
public Map(string path)
|
||||
{
|
||||
Path = path;
|
||||
Container = FileSystem.OpenPackage(path, int.MaxValue);
|
||||
Container = FileSystem.OpenPackage(path, null, int.MaxValue);
|
||||
|
||||
AssertExists("map.yaml");
|
||||
AssertExists("map.bin");
|
||||
|
||||
@@ -37,20 +37,20 @@ namespace OpenRA
|
||||
LoadScreen.Init(Manifest.LoadScreen.NodesDict.ToDictionary(x => x.Key, x => x.Value.Value));
|
||||
LoadScreen.Display();
|
||||
WidgetLoader = new WidgetLoader(this);
|
||||
}
|
||||
|
||||
public void LoadInitialAssets(bool enumMaps)
|
||||
{
|
||||
// all this manipulation of static crap here is nasty and breaks
|
||||
// horribly when you use ModData in unexpected ways.
|
||||
AvailableMaps = FindMaps(Manifest.Mods);
|
||||
|
||||
// HACK: Mount only local folders so we have a half-working environment for the asset installer
|
||||
FileSystem.UnmountAll();
|
||||
foreach (var dir in Manifest.Folders)
|
||||
FileSystem.Mount(dir);
|
||||
|
||||
if (enumMaps)
|
||||
AvailableMaps = FindMaps(Manifest.Mods);
|
||||
}
|
||||
|
||||
public void InitializeLoaders()
|
||||
{
|
||||
// all this manipulation of static crap here is nasty and breaks
|
||||
// horribly when you use ModData in unexpected ways.
|
||||
ChromeMetrics.Initialize(Manifest.ChromeMetrics);
|
||||
ChromeProvider.Initialize(Manifest.Chrome);
|
||||
SheetBuilder = new SheetBuilder(SheetType.Indexed);
|
||||
@@ -66,12 +66,11 @@ namespace OpenRA
|
||||
var map = new Map(AvailableMaps[uid].Path);
|
||||
|
||||
// Reinit all our assets
|
||||
LoadInitialAssets(false);
|
||||
foreach (var pkg in Manifest.Packages)
|
||||
FileSystem.Mount(pkg);
|
||||
InitializeLoaders();
|
||||
FileSystem.LoadFromManifest(Manifest);
|
||||
|
||||
// Mount map package so custom assets can be used. TODO: check priority.
|
||||
FileSystem.Mount(FileSystem.OpenPackage(map.Path, int.MaxValue));
|
||||
FileSystem.Mount(FileSystem.OpenPackage(map.Path, null, int.MaxValue));
|
||||
|
||||
Rules.LoadRules(Manifest, map);
|
||||
SpriteLoader = new SpriteLoader(Rules.TileSets[map.Tileset].Extensions, SheetBuilder);
|
||||
|
||||
@@ -23,17 +23,15 @@ namespace OpenRA.Mods.RA.Widgets.Logic
|
||||
{
|
||||
Widget panel;
|
||||
|
||||
static ShpImageWidget spriteImage;
|
||||
static TextFieldWidget filenameInput;
|
||||
static SliderWidget frameSlider;
|
||||
static ButtonWidget playButton, pauseButton;
|
||||
static ScrollPanelWidget assetList;
|
||||
static ScrollItemWidget template;
|
||||
ShpImageWidget spriteImage;
|
||||
TextFieldWidget filenameInput;
|
||||
SliderWidget frameSlider;
|
||||
ButtonWidget playButton, pauseButton;
|
||||
ScrollPanelWidget assetList;
|
||||
ScrollItemWidget template;
|
||||
|
||||
public enum SourceType { Folders, Packages }
|
||||
public static SourceType AssetSource = SourceType.Folders;
|
||||
|
||||
public static List<string> AvailableShps = new List<string>();
|
||||
IFolder AssetSource = null;
|
||||
List<string> AvailableShps = new List<string>();
|
||||
|
||||
[ObjectCreator.UseCtor]
|
||||
public AssetBrowserLogic(Widget widget, Action onExit, World world)
|
||||
@@ -42,9 +40,16 @@ namespace OpenRA.Mods.RA.Widgets.Logic
|
||||
|
||||
var sourceDropdown = panel.Get<DropDownButtonWidget>("SOURCE_SELECTOR");
|
||||
sourceDropdown.OnMouseDown = _ => ShowSourceDropdown(sourceDropdown);
|
||||
sourceDropdown.GetText = () => AssetSource == SourceType.Folders ? "Folders"
|
||||
: AssetSource == SourceType.Packages ? "Packages" : "None";
|
||||
sourceDropdown.Disabled = !Rules.PackageContents.Keys.Any();
|
||||
sourceDropdown.GetText = () =>
|
||||
{
|
||||
var name = AssetSource != null ? AssetSource.Name : "All Packages";
|
||||
if (name.Length > 15)
|
||||
name = "..."+name.Substring(name.Length - 15);
|
||||
|
||||
return name;
|
||||
};
|
||||
|
||||
AssetSource = FileSystem.MountedFolders.First();
|
||||
|
||||
spriteImage = panel.Get<ShpImageWidget>("SPRITE");
|
||||
|
||||
@@ -177,30 +182,22 @@ namespace OpenRA.Mods.RA.Widgets.Logic
|
||||
panel.Get<ButtonWidget>("CLOSE_BUTTON").OnClick = () => { Ui.CloseWindow(); onExit(); };
|
||||
}
|
||||
|
||||
static void AddAsset(ScrollPanelWidget list, string filepath, ScrollItemWidget template)
|
||||
void AddAsset(ScrollPanelWidget list, string filepath, ScrollItemWidget template)
|
||||
{
|
||||
var sprite = Path.GetFileNameWithoutExtension(filepath);
|
||||
var filename = Path.GetFileName(filepath);
|
||||
|
||||
var item = ScrollItemWidget.Setup(template,
|
||||
() => spriteImage != null && spriteImage.Image == sprite,
|
||||
() => spriteImage.Image == sprite,
|
||||
() => LoadAsset(sprite));
|
||||
item.Get<LabelWidget>("TITLE").GetText = () => filename;
|
||||
item.Get<LabelWidget>("TITLE").GetText = () => filepath;
|
||||
|
||||
list.AddChild(item);
|
||||
}
|
||||
|
||||
static bool LoadAsset(string sprite)
|
||||
bool LoadAsset(string sprite)
|
||||
{
|
||||
if (sprite == null)
|
||||
return false;
|
||||
|
||||
if (!sprite.ToLower().Contains("r8"))
|
||||
{
|
||||
filenameInput.Text = sprite+".shp";
|
||||
sprite = Path.GetFileNameWithoutExtension(sprite);
|
||||
}
|
||||
|
||||
spriteImage.Frame = 0;
|
||||
spriteImage.Image = sprite;
|
||||
frameSlider.MaximumValue = (float)spriteImage.FrameCount;
|
||||
@@ -208,54 +205,46 @@ namespace OpenRA.Mods.RA.Widgets.Logic
|
||||
return true;
|
||||
}
|
||||
|
||||
public static bool ShowSourceDropdown(DropDownButtonWidget dropdown)
|
||||
bool ShowSourceDropdown(DropDownButtonWidget dropdown)
|
||||
{
|
||||
var options = new Dictionary<string, SourceType>()
|
||||
{
|
||||
{ "Folders", SourceType.Folders },
|
||||
{ "Packages", SourceType.Packages },
|
||||
};
|
||||
|
||||
Func<string, ScrollItemWidget, ScrollItemWidget> setupItem = (o, itemTemplate) =>
|
||||
Func<IFolder, ScrollItemWidget, ScrollItemWidget> setupItem = (source, itemTemplate) =>
|
||||
{
|
||||
var item = ScrollItemWidget.Setup(itemTemplate,
|
||||
() => AssetSource == options[o],
|
||||
() => { AssetSource = options[o]; PopulateAssetList(); });
|
||||
item.Get<LabelWidget>("LABEL").GetText = () => o;
|
||||
() => AssetSource == source,
|
||||
() => { AssetSource = source; PopulateAssetList(); });
|
||||
item.Get<LabelWidget>("LABEL").GetText = () => source != null ? source.Name : "All Packages";
|
||||
return item;
|
||||
};
|
||||
|
||||
dropdown.ShowDropDown("LABEL_DROPDOWN_TEMPLATE", 500, options.Keys, setupItem);
|
||||
|
||||
// TODO: Re-enable "All Packages" once list generation is done in a background thread
|
||||
//var sources = new[] { (IFolder)null }.Concat(FileSystem.MountedFolders);
|
||||
|
||||
var sources = FileSystem.MountedFolders;
|
||||
dropdown.ShowDropDown("LABEL_DROPDOWN_TEMPLATE", 250, sources, setupItem);
|
||||
return true;
|
||||
}
|
||||
|
||||
public static void PopulateAssetList()
|
||||
void PopulateAssetList()
|
||||
{
|
||||
assetList.RemoveChildren();
|
||||
AvailableShps.Clear();
|
||||
|
||||
if (AssetSource == SourceType.Folders)
|
||||
// TODO: This is too slow to run in the main thread
|
||||
//var files = AssetSource != null ? AssetSource.AllFileNames() :
|
||||
// FileSystem.MountedFolders.SelectMany(f => f.AllFileNames());
|
||||
|
||||
if (AssetSource == null)
|
||||
return;
|
||||
|
||||
var files = AssetSource.AllFileNames();
|
||||
foreach (var file in files)
|
||||
{
|
||||
foreach (var folder in FileSystem.FolderPaths)
|
||||
if (file.EndsWith(".shp"))
|
||||
{
|
||||
if (Directory.Exists(folder))
|
||||
{
|
||||
var shps = Directory.GetFiles(folder, "*.shp");
|
||||
foreach (var shp in shps)
|
||||
{
|
||||
AddAsset(assetList, shp, template);
|
||||
AvailableShps.Add(Path.GetFileName(shp));
|
||||
}
|
||||
}
|
||||
AddAsset(assetList, file, template);
|
||||
AvailableShps.Add(file);
|
||||
}
|
||||
}
|
||||
|
||||
if (AssetSource == SourceType.Packages)
|
||||
foreach (var hiddenFile in Rules.PackageContents.Keys)
|
||||
{
|
||||
AddAsset(assetList, hiddenFile, template);
|
||||
AvailableShps.Add(hiddenFile);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
BIN
global mix database.dat
Normal file
BIN
global mix database.dat
Normal file
Binary file not shown.
@@ -29,8 +29,6 @@ Packages:
|
||||
~scores2.mix
|
||||
~transit.mix
|
||||
|
||||
PackageContents:
|
||||
|
||||
Rules:
|
||||
mods/cnc/rules/defaults.yaml
|
||||
mods/cnc/rules/system.yaml
|
||||
|
||||
@@ -21,8 +21,6 @@ Packages:
|
||||
~main.mix
|
||||
conquer.mix
|
||||
|
||||
PackageContents:
|
||||
|
||||
Rules:
|
||||
mods/d2k/rules/system.yaml
|
||||
mods/d2k/rules/defaults.yaml
|
||||
|
||||
@@ -1,227 +0,0 @@
|
||||
# conquer.mix filename list for the game asset browser
|
||||
#appear1.aud:
|
||||
#beepy6.aud:
|
||||
#briefing.aud:
|
||||
#clock1.aud:
|
||||
#country1.aud:
|
||||
#country4.aud:
|
||||
#keystrok.aud:
|
||||
#mapwipe2.aud:
|
||||
#mapwipe5.aud:
|
||||
#scold1.aud:
|
||||
#sfx4.aud:
|
||||
#toney10.aud:
|
||||
#toney4.aud:
|
||||
#toney7.aud:
|
||||
#type.fnt:
|
||||
#alibackh.pcx:
|
||||
#sovback.pcx:
|
||||
120mm.shp:
|
||||
1tnk.shp:light tank
|
||||
2tnk.shp:medium tank
|
||||
3tnk.shp:heavy tank
|
||||
4tnk.shp:mammoth tank
|
||||
50cal.shp:
|
||||
afld.shp:
|
||||
afldmake.shp:
|
||||
agun.shp:
|
||||
agunmake.shp:
|
||||
apc.shp:
|
||||
apwr.shp:
|
||||
apwrmake.shp:
|
||||
armor.shp:
|
||||
art-exp1.shp:
|
||||
arty.shp:
|
||||
atek.shp:
|
||||
atekmake.shp:
|
||||
atomicdn.shp:
|
||||
atomicup.shp:
|
||||
atomsfx.shp:
|
||||
badr.shp:
|
||||
bar3bhr.shp:
|
||||
bar3blu.shp:
|
||||
bar3red.shp:
|
||||
bar3rhr.shp:
|
||||
barb.shp:
|
||||
barl.shp:
|
||||
barr.shp:
|
||||
barrmake.shp:
|
||||
bio.shp:
|
||||
biomake.shp:
|
||||
bomb.shp:
|
||||
bomblet.shp:
|
||||
brik.shp:
|
||||
brl3.shp:
|
||||
burn-l.shp:
|
||||
burn-m.shp:
|
||||
burn-s.shp:
|
||||
ca.shp:
|
||||
chronbox.shp:
|
||||
countrya.shp:
|
||||
countrye.shp:
|
||||
credsa.shp:
|
||||
credsahr.shp:
|
||||
credsu.shp:
|
||||
credsuhr.shp:
|
||||
cycl.shp:
|
||||
dd.shp:
|
||||
deviator.shp:
|
||||
dog.shp:
|
||||
dogbullt.shp:
|
||||
dollar.shp:
|
||||
dome.shp:
|
||||
domemake.shp:
|
||||
dragon.shp:
|
||||
earth.shp:
|
||||
ebtn-dn.shp:
|
||||
electdog.shp:
|
||||
empulse.shp:
|
||||
fact.shp:
|
||||
factmake.shp:
|
||||
fb1.shp:
|
||||
fb2.shp:
|
||||
fball1.shp:
|
||||
fcom.shp:
|
||||
fenc.shp:
|
||||
fire1.shp:
|
||||
fire2.shp:
|
||||
fire3.shp:
|
||||
fire4.shp:
|
||||
fix.shp:
|
||||
fixmake.shp:
|
||||
flagfly.shp:
|
||||
flak.shp:
|
||||
flmspt.shp:
|
||||
fpls.shp:
|
||||
fpower.shp:
|
||||
frag1.shp:
|
||||
ftnk.shp:
|
||||
ftur.shp:
|
||||
fturmake.shp:
|
||||
gap.shp:
|
||||
gapmake.shp:
|
||||
gpsbox.shp:
|
||||
gun.shp:
|
||||
gunfire.shp:
|
||||
gunmake.shp:
|
||||
h2o_exp1.shp:
|
||||
h2o_exp2.shp:
|
||||
h2o_exp3.shp:
|
||||
harv.shp:
|
||||
heli.shp:
|
||||
hind.shp:
|
||||
hisc1-hr.shp:
|
||||
hisc2-hr.shp:
|
||||
hiscore1.shp:
|
||||
hiscore2.shp:
|
||||
hosp.shp:
|
||||
hospmake.shp:
|
||||
hpad.shp:
|
||||
hpadmake.shp:
|
||||
invulbox.shp:
|
||||
invun.shp:
|
||||
iron.shp:
|
||||
ironmake.shp:
|
||||
jeep.shp:
|
||||
kenn.shp:
|
||||
kennmake.shp:
|
||||
litning.shp:
|
||||
lrotor.shp:
|
||||
lst.shp:
|
||||
mcv.shp:
|
||||
mgg.shp:
|
||||
mgun.shp:
|
||||
mhq.shp:
|
||||
mig.shp:
|
||||
mine.shp:
|
||||
minigun.shp:
|
||||
minp.shp:
|
||||
minpmake.shp:
|
||||
minv.shp:
|
||||
minvmake.shp:
|
||||
miss.shp:
|
||||
missile.shp:
|
||||
missile2.shp:
|
||||
mlrs.shp:
|
||||
mnly.shp:
|
||||
mrj.shp:
|
||||
napalm1.shp:
|
||||
napalm2.shp:
|
||||
napalm3.shp:
|
||||
orca.shp:
|
||||
parabomb.shp:
|
||||
parabox.shp:
|
||||
parach.shp:
|
||||
patriot.shp:
|
||||
pbox.shp:
|
||||
pboxmake.shp:
|
||||
pdox.shp:
|
||||
pdoxmake.shp:
|
||||
piff.shp:
|
||||
piffpiff.shp:
|
||||
powr.shp:
|
||||
powrmake.shp:
|
||||
pt.shp:
|
||||
pumpmake.shp:
|
||||
radarfrm.shp:
|
||||
rapid.shp:
|
||||
rrotor.shp:
|
||||
sam.shp:
|
||||
samfire.shp:
|
||||
sammake.shp:
|
||||
sbag.shp:
|
||||
scrate.shp:
|
||||
select.shp:
|
||||
repair.shp:
|
||||
shadow.shp:
|
||||
silo.shp:
|
||||
silomake.shp:
|
||||
smig.shp:
|
||||
smoke_m.shp:
|
||||
smokey.shp:
|
||||
smokland.shp:
|
||||
sonarbox.shp:
|
||||
speed.shp:
|
||||
spen.shp:
|
||||
spenmake.shp:
|
||||
sputdoor.shp:
|
||||
sputnik.shp:
|
||||
ss.shp:
|
||||
ssam.shp:
|
||||
stealth2.shp:
|
||||
stek.shp:
|
||||
stekmake.shp:
|
||||
stnk.shp:
|
||||
syrd.shp:
|
||||
syrdmake.shp:
|
||||
tent.shp:
|
||||
tentmake.shp:
|
||||
time.shp:
|
||||
timehr.shp:
|
||||
tquake.shp:
|
||||
tran.shp:
|
||||
truk.shp:
|
||||
tsla.shp:
|
||||
tslamake.shp:
|
||||
turr.shp:
|
||||
twinkle1.shp:
|
||||
twinkle2.shp:
|
||||
twinkle3.shp:
|
||||
u2.shp:
|
||||
v19.shp:
|
||||
v2.shp:
|
||||
v2rl.shp:
|
||||
veh-hit1.shp:
|
||||
veh-hit2.shp:
|
||||
wake.shp:
|
||||
wcrate.shp:
|
||||
weap.shp:
|
||||
weap2.shp:
|
||||
weapmake.shp:
|
||||
wood.shp:
|
||||
wwcrate.shp:
|
||||
yak.shp:
|
||||
#trans.icn:
|
||||
#ali-tran.wsa:
|
||||
#mltiplyr.wsa:
|
||||
#sov-tran.wsa:
|
||||
@@ -1,135 +0,0 @@
|
||||
# hires.mix filename list for the game asset browser
|
||||
1tnkicon.shp:
|
||||
2tnkicon.shp:
|
||||
3tnkicon.shp:
|
||||
4tnkicon.shp:
|
||||
afldicon.shp:
|
||||
agunicon.shp:
|
||||
apcicon.shp:
|
||||
apwricon.shp:
|
||||
artyicon.shp:
|
||||
atekicon.shp:
|
||||
atomicon.shp:
|
||||
badricon.shp:
|
||||
barricon.shp:
|
||||
brikicon.shp:
|
||||
btn-dn.shp:
|
||||
btn-pl.shp:
|
||||
btn-st.shp:
|
||||
btn-up.shp:
|
||||
c1.shp:
|
||||
c2.shp:
|
||||
caicon.shp:
|
||||
camicon.shp:
|
||||
chan.shp:
|
||||
clock.shp:
|
||||
dd-bkgnd.shp:
|
||||
dd-botm.shp:
|
||||
dd-crnr.shp:
|
||||
dd-edge.shp:
|
||||
dd-left.shp:
|
||||
dd-right.shp:
|
||||
dd-top.shp:
|
||||
ddicon.shp:
|
||||
delphi.shp:
|
||||
dogicon.shp:
|
||||
domeicon.shp:
|
||||
domficon.shp:
|
||||
e1.shp:
|
||||
e1icon.shp:
|
||||
e2.shp:
|
||||
e2icon.shp:
|
||||
e3.shp:
|
||||
e3icon.shp:
|
||||
e4.shp:
|
||||
e4icon.shp:
|
||||
e5.shp:
|
||||
e6.shp:
|
||||
e6icon.shp:
|
||||
e7.shp:
|
||||
e7icon.shp:
|
||||
einstein.shp:
|
||||
facficon.shp:
|
||||
facticon.shp:
|
||||
fencicon.shp:
|
||||
fixicon.shp:
|
||||
fturicon.shp:
|
||||
gapicon.shp:
|
||||
gnrl.shp:
|
||||
gpssicon.shp:
|
||||
gunicon.shp:
|
||||
harvicon.shp:
|
||||
hboxicon.shp:
|
||||
heliicon.shp:
|
||||
hindicon.shp:
|
||||
hpadicon.shp:
|
||||
infxicon.shp:
|
||||
ironicon.shp:
|
||||
jeepicon.shp:
|
||||
kennicon.shp:
|
||||
lsticon.shp:
|
||||
map.shp:
|
||||
mcvicon.shp:
|
||||
medi.shp:
|
||||
mediicon.shp:
|
||||
mggicon.shp:
|
||||
migicon.shp:
|
||||
mnlyicon.shp:
|
||||
mrjicon.shp:
|
||||
msloicon.shp:
|
||||
natoradr.shp:
|
||||
nradrfrm.shp:
|
||||
pbmbicon.shp:
|
||||
pboxicon.shp:
|
||||
pdoxicon.shp:
|
||||
pinficon.shp:
|
||||
pips.shp:
|
||||
power.shp:
|
||||
powerbar.shp:
|
||||
powricon.shp:
|
||||
procicon.shp:
|
||||
pticon.shp:
|
||||
pulse.shp:
|
||||
repair.shp:
|
||||
samicon.shp:
|
||||
sbagicon.shp:
|
||||
sell.shp:
|
||||
side1na.shp:
|
||||
side1us.shp:
|
||||
side2na.shp:
|
||||
side2us.shp:
|
||||
side3na.shp:
|
||||
side3us.shp:
|
||||
#sidebar.shp:will crash
|
||||
siloicon.shp:
|
||||
smigicon.shp:
|
||||
sonricon.shp:
|
||||
speficon.shp:
|
||||
spenicon.shp:
|
||||
spy.shp:
|
||||
spyicon.shp:
|
||||
ssicon.shp:
|
||||
stekicon.shp:
|
||||
strip.shp:
|
||||
stripdn.shp:
|
||||
stripna.shp:
|
||||
stripup.shp:
|
||||
stripus.shp:
|
||||
syrdicon.shp:
|
||||
syrficon.shp:
|
||||
tabs.shp:
|
||||
tenticon.shp:
|
||||
thf.shp:
|
||||
thficon.shp:
|
||||
tranicon.shp:
|
||||
trukicon.shp:
|
||||
tslaicon.shp:
|
||||
u2icon.shp:
|
||||
uradrfrm.shp:
|
||||
ussrradr.shp:
|
||||
v2rlicon.shp:
|
||||
warpicon.shp:
|
||||
weaficon.shp:
|
||||
weapicon.shp:
|
||||
yakicon.shp:
|
||||
#mouse.shp:Dune II format
|
||||
@@ -29,10 +29,6 @@ Packages:
|
||||
~movies1.mix
|
||||
~movies2.mix
|
||||
|
||||
PackageContents:
|
||||
mods/ra/mix/conquer.yaml
|
||||
mods/ra/mix/hires.yaml
|
||||
|
||||
Rules:
|
||||
mods/ra/rules/defaults.yaml
|
||||
mods/ra/rules/system.yaml
|
||||
|
||||
Reference in New Issue
Block a user