From 27736ef13fd376e6e6d4de45192c79be82f53a8b Mon Sep 17 00:00:00 2001 From: Will Date: Sat, 1 Dec 2018 16:20:44 -0700 Subject: [PATCH] Add partial support for Titanfall. So far I'm not sure this format can be decompiled like the others. Need to do more research and reverse engineering. --- LibBSP/Source/Extensions/PlaneExtensions.cs | 9 +- LibBSP/Source/Extensions/VertexExtensions.cs | 10 +- LibBSP/Source/Structs/BSP/BSP.cs | 4 + LibBSP/Source/Structs/BSP/Lumps/GameLump.cs | 6 +- .../Source/Structs/BSP/Lumps/StaticProps.cs | 12 +- LibBSP/Source/Structs/BSP/Model.cs | 7 +- LibBSP/Source/Structs/BSP/StaticProp.cs | 180 ++++--- LibBSP/Source/Structs/BSP/Texture.cs | 9 +- LibBSP/Source/Structs/BSP/TextureData.cs | 8 +- LibBSP/Source/Structs/Common/Entity.cs | 3 +- LibBSP/Source/Structs/Common/Lumps/NumList.cs | 3 +- LibBSP/Source/Util/BSPReader.cs | 452 +++++++++--------- 12 files changed, 401 insertions(+), 302 deletions(-) diff --git a/LibBSP/Source/Extensions/PlaneExtensions.cs b/LibBSP/Source/Extensions/PlaneExtensions.cs index 8d5f940..e2448e4 100644 --- a/LibBSP/Source/Extensions/PlaneExtensions.cs +++ b/LibBSP/Source/Extensions/PlaneExtensions.cs @@ -318,7 +318,8 @@ namespace LibBSP { case MapType.Vindictus: case MapType.Quake2: case MapType.Daikatana: - case MapType.TacticalInterventionEncrypted: { + case MapType.TacticalInterventionEncrypted: + case MapType.Titanfall: { BitConverter.GetBytes(BestAxis(p)).CopyTo(bytes, 16); break; } @@ -382,7 +383,8 @@ namespace LibBSP { case MapType.Source23: case MapType.Source27: case MapType.L4D2: - case MapType.DMoMaM: { + case MapType.DMoMaM: + case MapType.Titanfall: { return 1; } case MapType.CoD: @@ -432,7 +434,8 @@ namespace LibBSP { case MapType.FAKK: case MapType.CoD: case MapType.CoD2: - case MapType.CoD4: { + case MapType.CoD4: + case MapType.Titanfall: { structLength = 16; break; } diff --git a/LibBSP/Source/Extensions/VertexExtensions.cs b/LibBSP/Source/Extensions/VertexExtensions.cs index ec7ca9c..98e788d 100644 --- a/LibBSP/Source/Extensions/VertexExtensions.cs +++ b/LibBSP/Source/Extensions/VertexExtensions.cs @@ -173,6 +173,7 @@ namespace LibBSP { case MapType.Source27: case MapType.L4D2: case MapType.TacticalInterventionEncrypted: + case MapType.Titanfall: case MapType.Quake2: case MapType.Daikatana: case MapType.Vindictus: @@ -238,7 +239,8 @@ namespace LibBSP { case MapType.Source23: case MapType.Source27: case MapType.L4D2: - case MapType.DMoMaM: { + case MapType.DMoMaM: + case MapType.Titanfall: { return 3; } case MapType.MOHAA: @@ -308,7 +310,8 @@ namespace LibBSP { case MapType.Quake2: case MapType.Daikatana: case MapType.Vindictus: - case MapType.DMoMaM: { + case MapType.DMoMaM: + case MapType.Titanfall: { structLength = 12; break; } @@ -374,7 +377,8 @@ namespace LibBSP { case MapType.Quake2: case MapType.Daikatana: case MapType.Vindictus: - case MapType.DMoMaM: { + case MapType.DMoMaM: + case MapType.Titanfall: { v.position.GetBytes().CopyTo(bytes, 0); break; } diff --git a/LibBSP/Source/Structs/BSP/BSP.cs b/LibBSP/Source/Structs/BSP/BSP.cs index 9dace54..b55aacd 100644 --- a/LibBSP/Source/Structs/BSP/BSP.cs +++ b/LibBSP/Source/Structs/BSP/BSP.cs @@ -52,6 +52,7 @@ namespace LibBSP { Quake3 = 1347633783, // TYPE_RTCW = 1347633784, // Uses same structures as Quake 3 CoD = 1347633796, + Titanfall = 1347633807, DMoMaM = 1347895914, } @@ -775,6 +776,9 @@ namespace LibBSP { case MapType.Vindictus: { return 64; } + case MapType.Titanfall: { + return 128; + } default: { return -1; } diff --git a/LibBSP/Source/Structs/BSP/Lumps/GameLump.cs b/LibBSP/Source/Structs/BSP/Lumps/GameLump.cs index e37e18e..7570868 100644 --- a/LibBSP/Source/Structs/BSP/Lumps/GameLump.cs +++ b/LibBSP/Source/Structs/BSP/Lumps/GameLump.cs @@ -44,7 +44,8 @@ namespace LibBSP { case MapType.Source22: case MapType.Source23: case MapType.L4D2: - case MapType.Source27: { + case MapType.Source27: + case MapType.Titanfall: { structLength = 16; break; } @@ -126,7 +127,8 @@ namespace LibBSP { case MapType.Source23: case MapType.Source27: case MapType.L4D2: - case MapType.DMoMaM: { + case MapType.DMoMaM: + case MapType.Titanfall: { return 35; } default: { diff --git a/LibBSP/Source/Structs/BSP/Lumps/StaticProps.cs b/LibBSP/Source/Structs/BSP/Lumps/StaticProps.cs index 8961134..1268290 100644 --- a/LibBSP/Source/Structs/BSP/Lumps/StaticProps.cs +++ b/LibBSP/Source/Structs/BSP/Lumps/StaticProps.cs @@ -28,19 +28,21 @@ namespace LibBSP { dictionary = new string[BitConverter.ToInt32(data, 0)]; offset += 4; for (int i = 0; i < dictionary.Length; ++i) { - byte[] temp = new byte[128]; - Array.Copy(data, offset, temp, 0, 128); - dictionary[i] = temp.ToNullTerminatedString(); + dictionary[i] = data.ToNullTerminatedString(offset, 128); offset += 128; } - int numLeafDefinitions = BitConverter.ToInt32(data, (dictionary.Length * 128) + 4); + int numLeafDefinitions = BitConverter.ToInt32(data, offset); offset += 4 + (numLeafDefinitions * 2); if (type == MapType.Vindictus && version == 6) { int numPropScales = BitConverter.ToInt32(data, offset); offset += 4 + (numPropScales * 16); } int numProps = BitConverter.ToInt32(data, offset); - offset += 4; + if (version == 12) { // So far only Titanfall + offset += 12; + } else { + offset += 4; + } if (numProps > 0) { structLength = (data.Length - offset) / numProps; for (int i = 0; i < numProps; ++i) { diff --git a/LibBSP/Source/Structs/BSP/Model.cs b/LibBSP/Source/Structs/BSP/Model.cs index a35f288..4e82abb 100644 --- a/LibBSP/Source/Structs/BSP/Model.cs +++ b/LibBSP/Source/Structs/BSP/Model.cs @@ -473,6 +473,10 @@ namespace LibBSP { } int structLength = 0; switch (type) { + case MapType.Titanfall: { + structLength = 32; + break; + } case MapType.Quake3: case MapType.Raven: case MapType.STEF2: @@ -561,7 +565,8 @@ namespace LibBSP { case MapType.Source23: case MapType.Source27: case MapType.L4D2: - case MapType.DMoMaM: { + case MapType.DMoMaM: + case MapType.Titanfall: { return 14; } case MapType.STEF2: diff --git a/LibBSP/Source/Structs/BSP/StaticProp.cs b/LibBSP/Source/Structs/BSP/StaticProp.cs index 401bfc8..bda5222 100644 --- a/LibBSP/Source/Structs/BSP/StaticProp.cs +++ b/LibBSP/Source/Structs/BSP/StaticProp.cs @@ -39,7 +39,8 @@ namespace LibBSP { case MapType.L4D2: case MapType.TacticalInterventionEncrypted: case MapType.Vindictus: - case MapType.DMoMaM: { + case MapType.DMoMaM: + case MapType.Titanfall: { switch (version) { case 4: case 5: @@ -48,7 +49,8 @@ namespace LibBSP { case 8: case 9: case 10: - case 11: { + case 11: + case 12: { return new Vector3d(BitConverter.ToSingle(data, 0), BitConverter.ToSingle(data, 4), BitConverter.ToSingle(data, 8)); } default: { @@ -74,7 +76,8 @@ namespace LibBSP { case MapType.L4D2: case MapType.TacticalInterventionEncrypted: case MapType.Vindictus: - case MapType.DMoMaM: { + case MapType.DMoMaM: + case MapType.Titanfall: { switch (version) { case 4: case 5: @@ -83,7 +86,8 @@ namespace LibBSP { case 8: case 9: case 10: - case 11: { + case 11: + case 12: { value.GetBytes().CopyTo(data, 0); break; } @@ -108,7 +112,8 @@ namespace LibBSP { case MapType.L4D2: case MapType.TacticalInterventionEncrypted: case MapType.Vindictus: - case MapType.DMoMaM: { + case MapType.DMoMaM: + case MapType.Titanfall: { switch (version) { case 4: case 5: @@ -117,7 +122,8 @@ namespace LibBSP { case 8: case 9: case 10: - case 11: { + case 11: + case 12: { return new Vector3d(BitConverter.ToSingle(data, 12), BitConverter.ToSingle(data, 16), BitConverter.ToSingle(data, 20)); } default: { @@ -143,7 +149,8 @@ namespace LibBSP { case MapType.L4D2: case MapType.TacticalInterventionEncrypted: case MapType.Vindictus: - case MapType.DMoMaM: { + case MapType.DMoMaM: + case MapType.Titanfall: { switch (version) { case 4: case 5: @@ -152,7 +159,8 @@ namespace LibBSP { case 8: case 9: case 10: - case 11: { + case 11: + case 12: { value.GetBytes().CopyTo(data, 12); break; } @@ -177,7 +185,8 @@ namespace LibBSP { case MapType.L4D2: case MapType.TacticalInterventionEncrypted: case MapType.Vindictus: - case MapType.DMoMaM: { + case MapType.DMoMaM: + case MapType.Titanfall: { switch (version) { case 4: case 5: @@ -186,7 +195,8 @@ namespace LibBSP { case 8: case 9: case 10: - case 11: { + case 11: + case 12: { return BitConverter.ToInt16(data, 24); } default: { @@ -213,7 +223,8 @@ namespace LibBSP { case MapType.L4D2: case MapType.TacticalInterventionEncrypted: case MapType.Vindictus: - case MapType.DMoMaM: { + case MapType.DMoMaM: + case MapType.Titanfall: { switch (version) { case 4: case 5: @@ -222,7 +233,8 @@ namespace LibBSP { case 8: case 9: case 10: - case 11: { + case 11: + case 12: { bytes.CopyTo(data, 24); break; } @@ -387,7 +399,8 @@ namespace LibBSP { case MapType.L4D2: case MapType.TacticalInterventionEncrypted: case MapType.Vindictus: - case MapType.DMoMaM: { + case MapType.DMoMaM: + case MapType.Titanfall: { switch (version) { case 4: case 5: @@ -396,7 +409,8 @@ namespace LibBSP { case 8: case 9: case 10: - case 11: { + case 11: + case 12: { return data[30]; } default: { @@ -422,7 +436,8 @@ namespace LibBSP { case MapType.L4D2: case MapType.TacticalInterventionEncrypted: case MapType.Vindictus: - case MapType.DMoMaM: { + case MapType.DMoMaM: + case MapType.Titanfall: { switch (version) { case 4: case 5: @@ -431,7 +446,8 @@ namespace LibBSP { case 8: case 9: case 10: - case 11: { + case 11: + case 12: { data[30] = value; break; } @@ -456,7 +472,8 @@ namespace LibBSP { case MapType.L4D2: case MapType.TacticalInterventionEncrypted: case MapType.Vindictus: - case MapType.DMoMaM: { + case MapType.DMoMaM: + case MapType.Titanfall: { switch (version) { case 4: case 5: @@ -465,7 +482,8 @@ namespace LibBSP { case 8: case 9: case 10: - case 11: { + case 11: + case 12: { return data[31]; } default: { @@ -491,7 +509,8 @@ namespace LibBSP { case MapType.L4D2: case MapType.TacticalInterventionEncrypted: case MapType.Vindictus: - case MapType.DMoMaM: { + case MapType.DMoMaM: + case MapType.Titanfall: { switch (version) { case 4: case 5: @@ -500,7 +519,8 @@ namespace LibBSP { case 8: case 9: case 10: - case 11: { + case 11: + case 12: { data[31] = value; break; } @@ -525,7 +545,8 @@ namespace LibBSP { case MapType.L4D2: case MapType.TacticalInterventionEncrypted: case MapType.Vindictus: - case MapType.DMoMaM: { + case MapType.DMoMaM: + case MapType.Titanfall: { switch (version) { case 4: case 5: @@ -533,7 +554,8 @@ namespace LibBSP { case 7: case 8: case 10: - case 11: { + case 11: + case 12: { return BitConverter.ToInt32(data, 32); } case 9: { @@ -567,7 +589,8 @@ namespace LibBSP { case MapType.L4D2: case MapType.TacticalInterventionEncrypted: case MapType.Vindictus: - case MapType.DMoMaM: { + case MapType.DMoMaM: + case MapType.Titanfall: { switch (version) { case 4: case 5: @@ -575,7 +598,8 @@ namespace LibBSP { case 7: case 8: case 10: - case 11: { + case 11: + case 12: { bytes.CopyTo(data, 32); break; } @@ -608,7 +632,8 @@ namespace LibBSP { case MapType.L4D2: case MapType.TacticalInterventionEncrypted: case MapType.Vindictus: - case MapType.DMoMaM: { + case MapType.DMoMaM: + case MapType.Titanfall: { switch (version) { case 4: case 5: @@ -616,7 +641,8 @@ namespace LibBSP { case 7: case 8: case 10: - case 11: { + case 11: + case 12: { return BitConverter.ToSingle(data, 36); } case 9: { @@ -650,7 +676,8 @@ namespace LibBSP { case MapType.L4D2: case MapType.TacticalInterventionEncrypted: case MapType.Vindictus: - case MapType.DMoMaM: { + case MapType.DMoMaM: + case MapType.Titanfall: { switch (version) { case 4: case 5: @@ -658,7 +685,8 @@ namespace LibBSP { case 7: case 8: case 10: - case 11: { + case 11: + case 12: { bytes.CopyTo(data, 36); break; } @@ -691,7 +719,8 @@ namespace LibBSP { case MapType.L4D2: case MapType.TacticalInterventionEncrypted: case MapType.Vindictus: - case MapType.DMoMaM: { + case MapType.DMoMaM: + case MapType.Titanfall: { switch (version) { case 4: case 5: @@ -699,7 +728,8 @@ namespace LibBSP { case 7: case 8: case 10: - case 11: { + case 11: + case 12: { return BitConverter.ToSingle(data, 40); } case 9: { @@ -733,7 +763,8 @@ namespace LibBSP { case MapType.L4D2: case MapType.TacticalInterventionEncrypted: case MapType.Vindictus: - case MapType.DMoMaM: { + case MapType.DMoMaM: + case MapType.Titanfall: { switch (version) { case 4: case 5: @@ -741,7 +772,8 @@ namespace LibBSP { case 7: case 8: case 10: - case 11: { + case 11: + case 12: { bytes.CopyTo(data, 40); break; } @@ -774,7 +806,8 @@ namespace LibBSP { case MapType.L4D2: case MapType.TacticalInterventionEncrypted: case MapType.Vindictus: - case MapType.DMoMaM: { + case MapType.DMoMaM: + case MapType.Titanfall: { switch (version) { case 4: case 5: @@ -782,7 +815,8 @@ namespace LibBSP { case 7: case 8: case 10: - case 11: { + case 11: + case 12: { return new Vector3d(BitConverter.ToSingle(data, 44), BitConverter.ToSingle(data, 48), BitConverter.ToSingle(data, 52)); } case 9: { @@ -816,7 +850,8 @@ namespace LibBSP { case MapType.L4D2: case MapType.TacticalInterventionEncrypted: case MapType.Vindictus: - case MapType.DMoMaM: { + case MapType.DMoMaM: + case MapType.Titanfall: { switch (version) { case 4: case 5: @@ -824,7 +859,8 @@ namespace LibBSP { case 7: case 8: case 10: - case 11: { + case 11: + case 12: { bytes.CopyTo(data, 44); break; } @@ -857,14 +893,16 @@ namespace LibBSP { case MapType.L4D2: case MapType.TacticalInterventionEncrypted: case MapType.Vindictus: - case MapType.DMoMaM: { + case MapType.DMoMaM: + case MapType.Titanfall: { switch (version) { case 5: case 6: case 7: case 8: case 10: - case 11: { + case 11: + case 12: { return BitConverter.ToSingle(data, 56); } case 9: { @@ -898,14 +936,16 @@ namespace LibBSP { case MapType.L4D2: case MapType.TacticalInterventionEncrypted: case MapType.Vindictus: - case MapType.DMoMaM: { + case MapType.DMoMaM: + case MapType.Titanfall: { switch (version) { case 5: case 6: case 7: case 8: case 10: - case 11: { + case 11: + case 12: { bytes.CopyTo(data, 56); break; } @@ -1046,11 +1086,13 @@ namespace LibBSP { case MapType.L4D2: case MapType.TacticalInterventionEncrypted: case MapType.Vindictus: - case MapType.DMoMaM: { + case MapType.DMoMaM: + case MapType.Titanfall: { switch (version) { case 8: case 10: - case 11: { + case 11: + case 12: { return data[60]; } case 9: { @@ -1083,11 +1125,13 @@ namespace LibBSP { case MapType.L4D2: case MapType.TacticalInterventionEncrypted: case MapType.Vindictus: - case MapType.DMoMaM: { + case MapType.DMoMaM: + case MapType.Titanfall: { switch (version) { case 8: case 10: - case 11: { + case 11: + case 12: { data[60] = value; break; } @@ -1120,11 +1164,13 @@ namespace LibBSP { case MapType.L4D2: case MapType.TacticalInterventionEncrypted: case MapType.Vindictus: - case MapType.DMoMaM: { + case MapType.DMoMaM: + case MapType.Titanfall: { switch (version) { case 8: case 10: - case 11: { + case 11: + case 12: { return data[61]; } case 9: { @@ -1157,11 +1203,13 @@ namespace LibBSP { case MapType.L4D2: case MapType.TacticalInterventionEncrypted: case MapType.Vindictus: - case MapType.DMoMaM: { + case MapType.DMoMaM: + case MapType.Titanfall: { switch (version) { case 8: case 10: - case 11: { + case 11: + case 12: { data[61] = value; break; } @@ -1194,11 +1242,13 @@ namespace LibBSP { case MapType.L4D2: case MapType.TacticalInterventionEncrypted: case MapType.Vindictus: - case MapType.DMoMaM: { + case MapType.DMoMaM: + case MapType.Titanfall: { switch (version) { case 8: case 10: - case 11: { + case 11: + case 12: { return data[62]; } case 9: { @@ -1231,11 +1281,13 @@ namespace LibBSP { case MapType.L4D2: case MapType.TacticalInterventionEncrypted: case MapType.Vindictus: - case MapType.DMoMaM: { + case MapType.DMoMaM: + case MapType.Titanfall: { switch (version) { case 8: case 10: - case 11: { + case 11: + case 12: { data[62] = value; break; } @@ -1268,11 +1320,13 @@ namespace LibBSP { case MapType.L4D2: case MapType.TacticalInterventionEncrypted: case MapType.Vindictus: - case MapType.DMoMaM: { + case MapType.DMoMaM: + case MapType.Titanfall: { switch (version) { case 8: case 10: - case 11: { + case 11: + case 12: { return data[63]; } case 9: { @@ -1305,11 +1359,13 @@ namespace LibBSP { case MapType.L4D2: case MapType.TacticalInterventionEncrypted: case MapType.Vindictus: - case MapType.DMoMaM: { + case MapType.DMoMaM: + case MapType.Titanfall: { switch (version) { case 8: case 10: - case 11: { + case 11: + case 12: { data[63] = value; break; } @@ -1342,12 +1398,14 @@ namespace LibBSP { case MapType.L4D2: case MapType.TacticalInterventionEncrypted: case MapType.Vindictus: - case MapType.DMoMaM: { + case MapType.DMoMaM: + case MapType.Titanfall: { switch (version) { case 7: case 8: case 10: - case 11: { + case 11: + case 12: { return ColorExtensions.FromArgb(data[67], data[64], data[65], data[66]); } case 9: { @@ -1380,12 +1438,14 @@ namespace LibBSP { case MapType.L4D2: case MapType.TacticalInterventionEncrypted: case MapType.Vindictus: - case MapType.DMoMaM: { + case MapType.DMoMaM: + case MapType.Titanfall: { switch (version) { case 7: case 8: case 10: - case 11: { + case 11: + case 12: { value.GetBytes().CopyTo(data, 64); break; } diff --git a/LibBSP/Source/Structs/BSP/Texture.cs b/LibBSP/Source/Structs/BSP/Texture.cs index b95ff88..1d1b156 100644 --- a/LibBSP/Source/Structs/BSP/Texture.cs +++ b/LibBSP/Source/Structs/BSP/Texture.cs @@ -63,7 +63,8 @@ namespace LibBSP { case MapType.L4D2: case MapType.TacticalInterventionEncrypted: case MapType.Vindictus: - case MapType.DMoMaM: { + case MapType.DMoMaM: + case MapType.Titanfall: { return data.ToRawString(); } case MapType.SiN: { @@ -119,7 +120,8 @@ namespace LibBSP { case MapType.L4D2: case MapType.TacticalInterventionEncrypted: case MapType.Vindictus: - case MapType.DMoMaM: { + case MapType.DMoMaM: + case MapType.Titanfall: { data = bytes; break; } @@ -354,7 +356,8 @@ namespace LibBSP { case MapType.Source23: case MapType.Source27: case MapType.L4D2: - case MapType.DMoMaM: { + case MapType.DMoMaM: + case MapType.Titanfall: { return 43; } default: { diff --git a/LibBSP/Source/Structs/BSP/TextureData.cs b/LibBSP/Source/Structs/BSP/TextureData.cs index 5eb9bc7..651b35d 100644 --- a/LibBSP/Source/Structs/BSP/TextureData.cs +++ b/LibBSP/Source/Structs/BSP/TextureData.cs @@ -105,9 +105,12 @@ namespace LibBSP { throw new ArgumentNullException(); } int structLength = 32; + if (type == MapType.Titanfall) { + structLength = 36; + } int numObjects = data.Length / structLength; List lump = new List(numObjects); - for (int i = 0; i < numObjects; i++) { + for (int i = 0; i < numObjects; ++i) { byte[] bytes = new byte[structLength]; Array.Copy(data, (i * structLength), bytes, 0, structLength); lump.Add(new TextureData(bytes, type, version)); @@ -133,7 +136,8 @@ namespace LibBSP { case MapType.Source23: case MapType.Source27: case MapType.L4D2: - case MapType.DMoMaM: { + case MapType.DMoMaM: + case MapType.Titanfall: { return 2; } default: { diff --git a/LibBSP/Source/Structs/Common/Entity.cs b/LibBSP/Source/Structs/Common/Entity.cs index 0d7091d..e525cf3 100644 --- a/LibBSP/Source/Structs/Common/Entity.cs +++ b/LibBSP/Source/Structs/Common/Entity.cs @@ -573,7 +573,8 @@ namespace LibBSP { case MapType.Source22: case MapType.Source23: case MapType.Source27: - case MapType.DMoMaM: { + case MapType.DMoMaM: + case MapType.Titanfall: { return 0; } case MapType.FAKK: diff --git a/LibBSP/Source/Structs/Common/Lumps/NumList.cs b/LibBSP/Source/Structs/Common/Lumps/NumList.cs index 47bc5b9..deac714 100644 --- a/LibBSP/Source/Structs/Common/Lumps/NumList.cs +++ b/LibBSP/Source/Structs/Common/Lumps/NumList.cs @@ -326,7 +326,8 @@ namespace LibBSP { case MapType.Source23: case MapType.Source27: case MapType.L4D2: - case MapType.DMoMaM: { + case MapType.DMoMaM: + case MapType.Titanfall: { dataType = DataType.Int32; return 44; } diff --git a/LibBSP/Source/Util/BSPReader.cs b/LibBSP/Source/Util/BSPReader.cs index 830e4b5..bd4247e 100644 --- a/LibBSP/Source/Util/BSPReader.cs +++ b/LibBSP/Source/Util/BSPReader.cs @@ -88,6 +88,13 @@ namespace LibBSP { if (lumpFiles.ContainsKey(index)) { return lumpFiles[index]; } return GetLumpInfoAtOffset(8 + (16 * index), version); } + case MapType.Titanfall: { + if (lumpFiles == null) { + LoadLumpFiles(); + } + if (lumpFiles.ContainsKey(index)) { return lumpFiles[index]; } + return GetLumpInfoAtOffset((16 * (index + 1)), version); + } case MapType.CoD4: { using (FileStream stream = new FileStream(bspFile.FullName, FileMode.Open, FileAccess.Read)) { BinaryReader binaryReader = new BinaryReader(stream); @@ -157,7 +164,8 @@ namespace LibBSP { version == MapType.Source23 || version == MapType.Vindictus || version == MapType.DMoMaM || - version == MapType.TacticalInterventionEncrypted) { + version == MapType.TacticalInterventionEncrypted || + version == MapType.Titanfall) { lumpOffset = BitConverter.ToInt32(input, 0); lumpLength = BitConverter.ToInt32(input, 4); lumpVersion = BitConverter.ToInt32(input, 8); @@ -305,244 +313,246 @@ namespace LibBSP { using (FileStream stream = new FileStream(bspFile.FullName, FileMode.Open, FileAccess.Read)) { BinaryReader binaryReader = new BinaryReader(stream); stream.Seek(0, SeekOrigin.Begin); - int data = binaryReader.ReadInt32(); + int num = binaryReader.ReadInt32(); if (bigEndian) { - byte[] bytes = BitConverter.GetBytes(data); + byte[] bytes = BitConverter.GetBytes(num); Array.Reverse(bytes); - data = BitConverter.ToInt32(bytes, 0); + num = BitConverter.ToInt32(bytes, 0); } - if (data == 1347633737) { - // 1347633737 reads in ASCII as "IBSP" - // Versions: CoD, CoD2, CoD4, Quake 2, Daikatana, Quake 3 (RtCW), Soldier of Fortune - data = binaryReader.ReadInt32(); - if (bigEndian) { - byte[] bytes = BitConverter.GetBytes(data); - Array.Reverse(bytes); - data = BitConverter.ToInt32(bytes, 0); - } - switch (data) { - case 4: { - current = MapType.CoD2; - break; + switch (num) { + case 1347633737: { + // 1347633737 reads in ASCII as "IBSP" + // Versions: CoD, CoD2, CoD4, Quake 2, Daikatana, Quake 3 (RtCW), Soldier of Fortune + int num2 = binaryReader.ReadInt32(); + if (bigEndian) { + byte[] bytes = BitConverter.GetBytes(num2); + Array.Reverse(bytes); + num2 = BitConverter.ToInt32(bytes, 0); } - case 22: { - current = MapType.CoD4; - break; - } - case 38: { - current = MapType.Quake2; - break; - } - case 41: { - current = MapType.Daikatana; - break; - } - case 46: { - current = MapType.Quake3; - // This version number is both Quake 3 and Soldier of Fortune. Find out the length of the - // header, based on offsets. - for (int i = 0; i < 17; i++) { - stream.Seek((i + 1) * 8, SeekOrigin.Begin); - int temp = binaryReader.ReadInt32(); - if (bigEndian) { - byte[] bytes = BitConverter.GetBytes(temp); - Array.Reverse(bytes); - temp = BitConverter.ToInt32(bytes, 0); - } - if (temp == 184) { - current = MapType.SoF; - break; - } else { - if (temp == 144) { - break; - } - } + switch (num2) { + case 4: { + current = MapType.CoD2; + break; } - break; - } - case 47: { - current = MapType.Quake3; - break; - } - case 59: { - current = MapType.CoD; - break; - } - } - } else { - if (data == 892416050) { - // 892416050 reads in ASCII as "2015," the game studio which developed MoHAA - current = MapType.MOHAA; - } else { - if (data == 1095516485) { - // 1095516485 reads in ASCII as "EALA," the ones who developed MoHAA Spearhead and Breakthrough - current = MapType.MOHAA; - } else { - if (data == 1347633750) { - // 1347633750 reads in ASCII as "VBSP." Indicates Source engine. - // Some source games handle this as 2 shorts. - // TODO: Big endian? - // Formats: Source 17-23 and 27, DMoMaM, Vindictus - data = (int)binaryReader.ReadUInt16(); - switch (data) { - case 17: { - current = MapType.Source17; - break; + case 22: { + current = MapType.CoD4; + break; + } + case 38: { + current = MapType.Quake2; + break; + } + case 41: { + current = MapType.Daikatana; + break; + } + case 46: { + current = MapType.Quake3; + // This version number is both Quake 3 and Soldier of Fortune. Find out the length of the + // header, based on offsets. + for (int i = 0; i < 17; i++) { + stream.Seek((i + 1) * 8, SeekOrigin.Begin); + int temp = binaryReader.ReadInt32(); + if (bigEndian) { + byte[] bytes = BitConverter.GetBytes(temp); + Array.Reverse(bytes); + temp = BitConverter.ToInt32(bytes, 0); } - case 18: { - current = MapType.Source18; + if (temp == 184) { + current = MapType.SoF; break; - } - case 19: { - current = MapType.Source19; - break; - } - case 20: { - int version2 = (int)binaryReader.ReadUInt16(); - if (version2 == 4) { - // TODO: This doesn't necessarily mean the whole map should be read as DMoMaM. - current = MapType.DMoMaM; - } else { - current = MapType.Source20; - // Hack for detecting Vindictus: Look in the GameLump for offset/length/flags outside of ranges we'd expect - LumpInfo gameLumpInfo = GetLumpInfo(35, MapType.Source20); - stream.Seek(gameLumpInfo.offset, SeekOrigin.Begin); - int numGameLumps = binaryReader.ReadInt32(); - if (numGameLumps > 0) { - // Normally this would be the offset and length for the first game lump. - // But in Vindictus it's the version indicator for it instead. - stream.Seek(gameLumpInfo.offset + 12, SeekOrigin.Begin); - int testOffset = binaryReader.ReadInt32(); - if (numGameLumps > 1) { - if (testOffset < 24) { - current = MapType.Vindictus; - break; - } - } else { - // Normally this would be the ident for the second game lump. - // But in Vindictus it's the length for the first instead. - stream.Seek(gameLumpInfo.offset + 20, SeekOrigin.Begin); - int testName = binaryReader.ReadInt32(); - // A lump ident tends to have a value far above 1090519040, longer than any GameLump should be. - if (testOffset < 24 && testName < gameLumpInfo.length) { - current = MapType.Vindictus; - break; - } - } - } + } else { + if (temp == 144) { + break; } - break; - } - case 21: { - current = MapType.Source21; - // Hack to determine if this is a L4D2 map. Read what would normally be the offset of - // a lump. If it is less than the header length it's probably not an offset, indicating L4D2. - stream.Seek(8, SeekOrigin.Begin); - int test = binaryReader.ReadInt32(); - if (bigEndian) { - byte[] bytes = BitConverter.GetBytes(test); - Array.Reverse(bytes); - test = BitConverter.ToInt32(bytes, 0); - } - if (test < 1032) { - current = MapType.L4D2; - } - break; - } - case 22: { - current = MapType.Source22; - break; - } - case 23: { - current = MapType.Source23; - break; - } - case 27: { - current = MapType.Source27; - break; } } - } else { - if (data == 1347633746) { - // Reads in ASCII as "RBSP". Raven software's modification of Q3BSP, or Ritual's modification of Q2. - // Formats: Raven, SiN - current = MapType.Raven; - for (int i = 0; i < 17; i++) { - // Find out where the first lump starts, based on offsets. - stream.Seek((i + 1) * 8, SeekOrigin.Begin); - int temp = binaryReader.ReadInt32(); - if (bigEndian) { - byte[] bytes = BitConverter.GetBytes(temp); - Array.Reverse(bytes); - temp = BitConverter.ToInt32(bytes, 0); - } - if (temp == 168) { - current = MapType.SiN; - break; + break; + } + case 47: { + current = MapType.Quake3; + break; + } + case 59: { + current = MapType.CoD; + break; + } + } + break; + } + case 892416050: + case 1095516485: { + // 892416050 reads in ASCII as "2015," the game studio which developed MoHAA + // 1095516485 reads in ASCII as "EALA," the ones who developed MoHAA Spearhead and Breakthrough + current = MapType.MOHAA; + break; + } + case 1347633750: { + // 1347633750 reads in ASCII as "VBSP." Indicates Source engine. + // Some source games handle this as 2 shorts. + // TODO: Big endian? + // Formats: Source 17-23 and 27, DMoMaM, Vindictus + int num2 = (int)binaryReader.ReadUInt16(); + switch (num2) { + case 17: { + current = MapType.Source17; + break; + } + case 18: { + current = MapType.Source18; + break; + } + case 19: { + current = MapType.Source19; + break; + } + case 20: { + int version2 = (int)binaryReader.ReadUInt16(); + if (version2 == 4) { + // TODO: This doesn't necessarily mean the whole map should be read as DMoMaM. + current = MapType.DMoMaM; + } else { + current = MapType.Source20; + // Hack for detecting Vindictus: Look in the GameLump for offset/length/flags outside of ranges we'd expect + LumpInfo gameLumpInfo = GetLumpInfo(35, MapType.Source20); + stream.Seek(gameLumpInfo.offset, SeekOrigin.Begin); + int numGameLumps = binaryReader.ReadInt32(); + if (numGameLumps > 0) { + // Normally this would be the offset and length for the first game lump. + // But in Vindictus it's the version indicator for it instead. + stream.Seek(gameLumpInfo.offset + 12, SeekOrigin.Begin); + int testOffset = binaryReader.ReadInt32(); + if (numGameLumps > 1) { + if (testOffset < 24) { + current = MapType.Vindictus; + break; + } } else { - if (temp == 152) { + // Normally this would be the ident for the second game lump. + // But in Vindictus it's the length for the first instead. + stream.Seek(gameLumpInfo.offset + 20, SeekOrigin.Begin); + int testName = binaryReader.ReadInt32(); + // A lump ident tends to have a value far above 1090519040, longer than any GameLump should be. + if (testOffset < 24 && testName < gameLumpInfo.length) { + current = MapType.Vindictus; break; } } } - } else { - if (data == 556942917) { - // "EF2!" - current = MapType.STEF2; - } else { - if (data == 1263223110) { - // "FAKK" - // Formats: STEF2 demo, Heavy Metal FAKK2 (American McGee's Alice) - data = binaryReader.ReadInt32(); - if (bigEndian) { - byte[] bytes = BitConverter.GetBytes(data); - Array.Reverse(bytes); - data = BitConverter.ToInt32(bytes, 0); - } - switch (data) { - case 19: { - current = MapType.STEF2Demo; - break; - } - case 12: - case 42: {// American McGee's Alice - current = MapType.FAKK; - break; - } - } - } else { - switch (data) { - // Various numbers not representing a string - // Formats: HL1, Quake, Nightfire, or perhaps Tactical Intervention's encrypted format - case 29: - case 30: { - current = MapType.Quake; - break; - } - case 42: { - current = MapType.Nightfire; - break; - } - default: { - // Hack to get Tactical Intervention's encryption key. At offset 384, there are two unused lumps whose - // values in the header are always 0s. Grab these 32 bytes (256 bits) and see if they match an expected value. - stream.Seek(384, SeekOrigin.Begin); - key = binaryReader.ReadBytes(32); - stream.Seek(0, SeekOrigin.Begin); - data = BitConverter.ToInt32(XorWithKeyStartingAtIndex(binaryReader.ReadBytes(4)), 0); - if (data == 1347633750) { - current = MapType.TacticalInterventionEncrypted; - } else { - current = MapType.Undefined; - } - break; - } - } - } - } + } + break; + } + case 21: { + current = MapType.Source21; + // Hack to determine if this is a L4D2 map. Read what would normally be the offset of + // a lump. If it is less than the header length it's probably not an offset, indicating L4D2. + stream.Seek(8, SeekOrigin.Begin); + int test = binaryReader.ReadInt32(); + if (bigEndian) { + byte[] bytes = BitConverter.GetBytes(test); + Array.Reverse(bytes); + test = BitConverter.ToInt32(bytes, 0); + } + if (test < 1032) { + current = MapType.L4D2; + } + break; + } + case 22: { + current = MapType.Source22; + break; + } + case 23: { + current = MapType.Source23; + break; + } + case 27: { + current = MapType.Source27; + break; + } + } + break; + } + case 1347633746: { + // Reads in ASCII as "RBSP". Raven software's modification of Q3BSP, or Ritual's modification of Q2. + // Formats: Raven, SiN + current = MapType.Raven; + for (int i = 0; i < 17; i++) { + // Find out where the first lump starts, based on offsets. + stream.Seek((i + 1) * 8, SeekOrigin.Begin); + int temp = binaryReader.ReadInt32(); + if (bigEndian) { + byte[] bytes = BitConverter.GetBytes(temp); + Array.Reverse(bytes); + temp = BitConverter.ToInt32(bytes, 0); + } + if (temp == 168) { + current = MapType.SiN; + break; + } else { + if (temp == 152) { + break; } } } + break; + } + case 556942917: { + // "EF2!" + current = MapType.STEF2; + break; + } + case 1347633778: { + // "rBSP". Respawn's format for Titanfall + current = MapType.Titanfall; + break; + } + case 1263223110: { + // "FAKK" + // Formats: STEF2 demo, Heavy Metal FAKK2 (American McGee's Alice) + int num2 = binaryReader.ReadInt32(); + if (bigEndian) { + byte[] bytes = BitConverter.GetBytes(num2); + Array.Reverse(bytes); + num2 = BitConverter.ToInt32(bytes, 0); + } + switch (num2) { + case 19: { + current = MapType.STEF2Demo; + break; + } + case 12: + case 42: {// American McGee's Alice + current = MapType.FAKK; + break; + } + } + break; + } + // Various numbers not representing a string + // Formats: HL1, Quake, Nightfire, or perhaps Tactical Intervention's encrypted format + case 29: + case 30: { + current = MapType.Quake; + break; + } + case 42: { + current = MapType.Nightfire; + break; + } + default: { + // Hack to get Tactical Intervention's encryption key. At offset 384, there are two unused lumps whose + // values in the header are always 0s. Grab these 32 bytes (256 bits) and see if they match an expected value. + stream.Seek(384, SeekOrigin.Begin); + key = binaryReader.ReadBytes(32); + stream.Seek(0, SeekOrigin.Begin); + int num2 = BitConverter.ToInt32(XorWithKeyStartingAtIndex(binaryReader.ReadBytes(4)), 0); + if (num2 == 1347633750) { + current = MapType.TacticalInterventionEncrypted; + } else { + current = MapType.Undefined; + } + break; } } binaryReader.Close();