Skip to content

Commit 797202d

Browse files
committed
Improve support for Invader-built maps
Adds a new attribute that allows for partial build string matching since Invader uses the format "Invader [version]". CacheFileLoader also now verifies the footer magic as false positives began to show up. Support for h1 maps built with the modified "Hell Tools" was also added.
1 parent ca2702a commit 797202d

File tree

5 files changed

+101
-15
lines changed

5 files changed

+101
-15
lines changed

src/Blamite/Blam/CacheFileLoader.cs

Lines changed: 70 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ public static ICacheFile LoadCacheFile(IReader reader, string filePath, EngineDa
4242
engineInfo = FindEngineDescription(reader, engineDb);
4343

4444
if (engineInfo == null)
45-
throw new NotSupportedException("Engine build of given cache file \"" + Path.GetFileName(filePath) + "\" not supported");
45+
throw new NotSupportedException("Engine build of given cache file \"" + Path.GetFileName(filePath) + "\" not supported.");
4646

4747
return LoadCacheFileWithEngineDescription(reader, filePath, engineInfo);
4848
}
@@ -58,7 +58,7 @@ public static ICacheFile LoadCacheFile(IReader reader, string filePath, EngineDa
5858
public static ICacheFile LoadCacheFileWithEngineDescription(IReader reader, string filePath, EngineDescription engineInfo)
5959
{
6060
if (engineInfo == null)
61-
throw new NotSupportedException("Engine build of given cache file \"" + Path.GetFileName(filePath) + "\" not supported");
61+
throw new NotSupportedException("Engine build of given cache file \"" + Path.GetFileName(filePath) + "\" not supported.");
6262

6363
//reader is often initialized with Big Endian, switch it to match the EngineDescription
6464
reader.Endianness = engineInfo.Endian;
@@ -110,10 +110,15 @@ public static List<EngineDescription> FindEngineDescriptions(IReader reader, Eng
110110
reader.SeekTo(0);
111111
byte[] headerMagic = reader.ReadBlock(4);
112112
var endian = DetermineCacheFileEndianness(headerMagic);
113+
var endianMod = DetermineModuleFileEndianness(headerMagic);
113114
int fileVersion;
115+
bool trial = false;
114116

115117
List<EngineDescription> matches = new List<EngineDescription>();
116118

119+
if (endianMod != null)
120+
return matches;
121+
117122
if (endian != null)
118123
{
119124
reader.Endianness = endian.Value;
@@ -128,26 +133,61 @@ public static List<EngineDescription> FindEngineDescriptions(IReader reader, Eng
128133
var trialendian = DetermineTrialCacheFileEndianness(headerMagic);
129134

130135
if (trialendian == null)
131-
return matches;
136+
throw new ArgumentException("Invalid cache file header magic.");
132137

133138
reader.Endianness = trialendian.Value;
134139
reader.SeekTo(0x588);
135140
fileVersion = reader.ReadInt32();
141+
trial = true;
136142
}
137143

138144
var possibleEngines = engineDb.FindEnginesByVersion(fileVersion, reader.Endianness);
139145

140146
//reduce extra reads by caching the value at each offset
141147
Dictionary<int, string> offsetCache = new Dictionary<int, string>();
148+
Dictionary<int, bool> footCache = new Dictionary<int, bool>();
142149

143150
foreach (EngineDescription engine in possibleEngines)
144151
{
152+
int footOffset;
153+
154+
if (trial)
155+
{
156+
var headerLayout = engine.Layouts.GetLayout("header");
157+
if (!headerLayout.HasField("footer magic"))
158+
continue;
159+
160+
footOffset = headerLayout.GetFieldOffset("footer magic");
161+
}
162+
else
163+
footOffset = engine.HeaderSize - 4;
164+
165+
if (footCache.ContainsKey(footOffset))
166+
{
167+
if (!footCache[footOffset])
168+
continue;
169+
}
170+
else
171+
{
172+
reader.SeekTo(footOffset);
173+
var footMagic = reader.ReadBlock(4);
174+
175+
bool realFeet = CheckFooter(footMagic);
176+
177+
footCache.Add(footOffset, realFeet);
178+
179+
if (!realFeet)
180+
continue;
181+
}
182+
145183
if (offsetCache.ContainsKey(engine.BuildStringOffset))
146184
{
147-
if (offsetCache[engine.BuildStringOffset] == engine.BuildVersion)
185+
if ((engine.LooseBuildCheck && offsetCache[engine.BuildStringOffset].StartsWith(engine.BuildVersion)) ||
186+
offsetCache[engine.BuildStringOffset] == engine.BuildVersion)
148187
{
149188
matches.Add(engine);
150-
if (!string.IsNullOrEmpty(engine.BuildVersion))
189+
190+
if (!engine.LooseBuildCheck && !string.IsNullOrEmpty(engine.BuildVersion))
151191
break;
152192
}
153193

@@ -157,10 +197,12 @@ public static List<EngineDescription> FindEngineDescriptions(IReader reader, Eng
157197
reader.SeekTo(engine.BuildStringOffset);
158198
string buildString = reader.ReadAscii(0x20);
159199

160-
if (buildString == engine.BuildVersion)
200+
if ((engine.LooseBuildCheck && buildString.StartsWith(engine.BuildVersion)) ||
201+
buildString == engine.BuildVersion)
161202
{
162203
matches.Add(engine);
163-
if (!string.IsNullOrEmpty(engine.BuildVersion))
204+
205+
if (!engine.LooseBuildCheck && !string.IsNullOrEmpty(engine.BuildVersion))
164206
break;
165207
}
166208

@@ -178,7 +220,6 @@ public static List<EngineDescription> FindEngineDescriptions(IReader reader, Eng
178220
return Endian.LittleEndian;
179221

180222
return null;
181-
//throw new ArgumentException("Invalid cache file header magic");
182223
}
183224

184225
public static Endian? DetermineTrialCacheFileEndianness(byte[] headerMagic)
@@ -189,7 +230,27 @@ public static List<EngineDescription> FindEngineDescriptions(IReader reader, Eng
189230
return Endian.LittleEndian;
190231

191232
return null;
192-
//throw new ArgumentException("Invalid cache file header magic");
233+
}
234+
235+
public static Endian? DetermineModuleFileEndianness(byte[] headerMagic)
236+
{
237+
if (headerMagic[0] == 'd' && headerMagic[1] == 'h' && headerMagic[2] == 'o' && headerMagic[3] == 'm')
238+
return Endian.BigEndian;
239+
if (headerMagic[0] == 'm' && headerMagic[1] == 'o' && headerMagic[2] == 'h' && headerMagic[3] == 'd')
240+
return Endian.LittleEndian;
241+
242+
return null;
243+
}
244+
245+
public static bool CheckFooter(byte[] footerMagic)
246+
{
247+
if ((footerMagic[0] == 'f' && footerMagic[1] == 'o' && footerMagic[2] == 'o' && footerMagic[3] == 't') ||
248+
(footerMagic[0] == 't' && footerMagic[1] == 'o' && footerMagic[2] == 'o' && footerMagic[3] == 'f') ||
249+
(footerMagic[0] == 'G' && footerMagic[1] == 'f' && footerMagic[2] == 'o' && footerMagic[3] == 't') ||
250+
(footerMagic[0] == 't' && footerMagic[1] == 'o' && footerMagic[2] == 'f' && footerMagic[3] == 'G'))
251+
return true;
252+
253+
return false;
193254
}
194255
}
195256
}

src/Blamite/Blam/FirstGen/Structures/FirstGenHeader.cs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,10 @@ public class FirstGenHeader
88
private FileSegment _eofSegment;
99
public FirstGenHeader(StructureValueCollection values, EngineDescription info, FileSegmenter segmenter)
1010
{
11-
BuildString = info.BuildVersion;
11+
if (info.LooseBuildCheck)
12+
BuildString = values.GetString("build string");
13+
else
14+
BuildString = info.BuildVersion;
1215
HeaderSize = info.HeaderSize;
1316
Load(values, segmenter);
1417
}

src/Blamite/Formats/Engines.xml

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,8 @@
99
version="{version number}" - The number located at offset 0x4.
1010
versionAlt="{version number}" - An alternate version number used by the engine, see above.
1111
build="{build string}" - The full build string.
12-
inherits="{name of another defined engine}" - Uses the given engine as a base, anything else defined in this new engine overwrites the inherited value. You cannot chain multiple inherits. Any engine using this attribute should be placed AFTER the engine you wish to inherit.
12+
looseBuild={"true", "false"} - Instead of checking for an exact build string match, Blamite should check if the cache's string starts with the defined one for the engine. Intended for H1 cache files built with the tool Invader which sets the build string to the tool's version. Default if not present is false.
13+
inherits="{name of another defined engine}" - Uses the given engine as a base, anything else defined in this new engine overwrites the inherited value. You cannot chain multiple inherits. Any engine using this attribute should be placed AFTER the engine you wish to inherit within this file.
1314
>
1415
1516
<shortname>{short name}</shortname> - A shorthand name for the engine, displayed on the recent files list. Will also get used as the "game" attribute in Assembly's plugin generator.
@@ -233,7 +234,7 @@
233234

234235
<engine name="Halo 1 PC v10" version="7" build="01.00.10.0621" inherits="Halo 1 PC" />
235236

236-
<engine name="Halo 1 PC Invader" version="7" build="Invader 0.44.0.r3320" inherits="Halo 1 PC" />
237+
<engine name="Halo 1 PC Invader" version="7" build="Invader " looseBuild="true" inherits="Halo 1 PC" />
237238

238239
<engine name="Halo 1 Custom Edition" version="609" versionAlt="343" build="01.00.00.0609">
239240
<shortName>H1CE</shortName>
@@ -259,7 +260,7 @@
259260

260261
<engine name="Halo 1 Custom Edition v10" version="609" versionAlt="343" build="01.00.10.0621" inherits="Halo 1 Custom Edition" />
261262

262-
<engine name="Halo 1 Custom Edition Invader" version="609" versionAlt="343" build="Invader 0.44.0.r3320" inherits="Halo 1 Custom Edition" />
263+
<engine name="Halo 1 Custom Edition Invader" version="609" versionAlt="343" build="Invader " looseBuild="true" inherits="Halo 1 Custom Edition" />
263264

264265
<!-- NOTE: CEA 360 and early MCC have the same build strings, this makes things harder -->
265266
<engine name="Halo 1 Anniversary" version="7" build="01.00.01.0563">
@@ -1010,6 +1011,20 @@
10101011
<usesCompression>false</usesCompression>
10111012
</engineInfo>
10121013
</engine>
1014+
1015+
<engine name="Halo 1 Anniversary MCC Invader" version="13" build="Invader " looseBuild="true" inherits="Halo 1 Anniversary MCC">
1016+
<engineInfo>
1017+
<segmentAlignment>0x1</segmentAlignment>
1018+
<usesCompression>false</usesCompression>
1019+
</engineInfo>
1020+
</engine>
1021+
1022+
<engine name="Halo 1 Anniversary MCC Hell" version="13" build="01.06.66.HELL" inherits="Halo 1 Anniversary MCC">
1023+
<engineInfo>
1024+
<segmentAlignment>0x1</segmentAlignment>
1025+
<usesCompression>false</usesCompression>
1026+
</engineInfo>
1027+
</engine>
10131028

10141029
<!-- Halo 2 MCC -->
10151030
<engine name="Halo 2 MCC" version="10" build=""><!-- gee thanks for the empty string 343 I appreciate it -->

src/Blamite/Serialization/EngineDescription.cs

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,12 +18,13 @@ public class EngineDescription
1818
/// <param name="name">The engine's name.</param>
1919
/// <param name="version">The engine's version.</param>
2020
/// <param name="settings">The engine's settings.</param>
21-
public EngineDescription(string name, int version, int versionalt, string build, SettingsGroup settings)
21+
public EngineDescription(string name, int version, int versionalt, string build, bool looseBuild, SettingsGroup settings)
2222
{
2323
Name = name;
2424
Version = version;
2525
VersionAlt = versionalt;
2626
BuildVersion = build;
27+
LooseBuildCheck = looseBuild;
2728
Settings = settings;
2829

2930
LoadSettings();
@@ -202,6 +203,11 @@ public EngineDescription(string name, int version, int versionalt, string build,
202203
/// </summary>
203204
public bool ReverseChecksum { get; private set; }
204205

206+
/// <summary>
207+
/// Don't look for an exact match on the build string when checking against this EngineDescription during load. See if it starts with the string instead.
208+
/// </summary>
209+
public bool LooseBuildCheck { get; private set; }
210+
205211
private void LoadSettings()
206212
{
207213
LoadEngineSettings();

src/Blamite/Serialization/Settings/XMLEngineDatabaseLoader.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ public static EngineDatabase LoadDatabase(XContainer container)
4545
string build = XMLUtil.GetStringAttribute(elem, "build");
4646
var version = XMLUtil.GetNumericAttribute(elem, "version");
4747
var versionAlt = XMLUtil.GetNumericAttribute(elem, "versionAlt", -1);
48+
bool looseBuild = XMLUtil.GetBoolAttribute(elem, "looseBuild", false);
4849
string inherits = XMLUtil.GetStringAttribute(elem, "inherits", null);
4950
SettingsGroup settings = loader.LoadSettingsGroup(elem);
5051
if (!string.IsNullOrWhiteSpace(inherits))
@@ -54,7 +55,7 @@ public static EngineDatabase LoadDatabase(XContainer container)
5455
baseSettings.Import(settings);
5556
settings = baseSettings;
5657
}
57-
var desc = new EngineDescription(name, (int)version, (int)versionAlt, build, settings);
58+
var desc = new EngineDescription(name, (int)version, (int)versionAlt, build, looseBuild, settings);
5859
result.RegisterEngine(desc);
5960
}
6061
return result;

0 commit comments

Comments
 (0)