Skip to content

Commit 113f710

Browse files
authored
Merge pull request #132 from MichaelRelaxen/dl-missions
Deadlocked missions data files support
2 parents b28c0b2 + 8c50618 commit 113f710

6 files changed

Lines changed: 157 additions & 4 deletions

File tree

LibReplanetizer/Headers/MissionHeader.cs

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,21 @@ public static List<string> FindMissionFiles(GameType game, string enginePath)
5050

5151
return files;
5252
}
53+
public static List<string> FindMissionDataFiles(GameType game, string enginePath)
54+
{
55+
string? dir = Path.GetDirectoryName(enginePath);
56+
57+
List<string> files = new List<string>();
58+
59+
if (dir == null) return files;
60+
61+
if (game == GameType.DL)
62+
{
63+
files.AddRange(Directory.GetFiles(dir, "gameplay_mission_data*.dat"));
64+
}
65+
66+
return files;
67+
}
5368

5469
public byte[] Serialize()
5570
{

LibReplanetizer/Level.cs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
using System.Collections.Generic;
1515
using System.IO;
1616
using LibReplanetizer.Serializers;
17+
using System.Linq;
1718

1819
namespace LibReplanetizer
1920
{
@@ -348,6 +349,7 @@ public Level(string enginePath)
348349
}
349350

350351
List<string> missionPaths = MissionHeader.FindMissionFiles(game, enginePath);
352+
List<string> missionDataPaths = MissionHeader.FindMissionDataFiles(game, enginePath);
351353
missions = new List<Mission>();
352354

353355
for (int i = 0; i < missionPaths.Count; i++)
@@ -376,6 +378,15 @@ public Level(string enginePath)
376378
parser.GetTextures(mission.textures);
377379
}
378380

381+
string? datPath = missionDataPaths.FirstOrDefault(p =>
382+
Path.GetFileNameWithoutExtension(p).Contains($"[{i}]"));
383+
384+
if (datPath != null)
385+
{
386+
using var datParser = new MissionDataParser(datPath, game);
387+
mission.mobies = datParser.GetMobies(mission.models, mobyModels);
388+
}
389+
379390
missions.Add(mission);
380391
}
381392

LibReplanetizer/Parsers/MissionParser.cs

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,4 +64,57 @@ public void Dispose()
6464
fileStream.Close();
6565
}
6666
}
67+
68+
public class MissionDataParser : IDisposable
69+
{
70+
private FileStream fileStream;
71+
private GameType game;
72+
73+
public MissionDataParser(string datFilePath, GameType game)
74+
{
75+
this.game = game;
76+
this.fileStream = File.OpenRead(datFilePath);
77+
}
78+
79+
public List<Moby> GetMobies(List<Model> missionModels, List<Model> levelModels)
80+
{
81+
byte[] fileHeader = ReadBlock(fileStream, 0x00, 0x20);
82+
int mobyInstancesOffset = ReadInt(fileHeader, 0x04);
83+
84+
byte[] mobySecHeader = ReadBlock(fileStream, mobyInstancesOffset, 0x10);
85+
int mobyCount = ReadInt(mobySecHeader, 0x00);
86+
87+
if (mobyCount <= 0 || mobyCount > 10000)
88+
return new List<Moby>();
89+
90+
int mobyDataStart = mobyInstancesOffset + 0x10;
91+
byte[] mobyBlock = ReadBlock(fileStream, mobyDataStart, mobyCount * 0x70);
92+
93+
List<Model> allModels = new List<Model>(missionModels);
94+
foreach (var m in levelModels)
95+
if (!allModels.Exists(x => x.id == m.id))
96+
allModels.Add(m);
97+
98+
var mobies = new List<Moby>();
99+
for (int i = 0; i < mobyCount; i++)
100+
{
101+
int offset = i * 0x70;
102+
103+
// Null so the constructor doesnt try to index into an empty pvar list
104+
mobyBlock[offset + 0x50] = 0xFF;
105+
mobyBlock[offset + 0x51] = 0xFF;
106+
mobyBlock[offset + 0x52] = 0xFF;
107+
mobyBlock[offset + 0x53] = 0xFF;
108+
Moby moby = new Moby(GameType.DL, mobyBlock, i, allModels, new List<byte[]>());
109+
mobies.Add(moby);
110+
}
111+
112+
return mobies;
113+
}
114+
115+
public void Dispose()
116+
{
117+
fileStream?.Close();
118+
}
119+
}
67120
}

Replanetizer/Frames/LevelFrame.cs

Lines changed: 60 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ public class LevelFrame : Frame
3636
private FramebufferRenderer? renderer;
3737
public LevelRenderer? levelRenderer;
3838
private RendererPayload rendererPayload;
39+
private HashSet<Mission> selectedMissions = new HashSet<Mission>();
3940
public Level level { get; set; }
4041
private bool enableCameraInfo = true;
4142
public ShaderTable shaderTable;
@@ -336,6 +337,24 @@ private void RenderMenuBar()
336337
ImGui.EndMenu();
337338
}
338339

340+
if (level.game == GameType.DL && level.missions.Count > 0 && ImGui.BeginMenu("Missions"))
341+
{
342+
foreach (var mission in level.missions)
343+
{
344+
bool selected = selectedMissions.Contains(mission);
345+
if (ImGui.Checkbox($"Mission {mission.missionID}", ref selected))
346+
{
347+
if (selected)
348+
selectedMissions.Add(mission);
349+
else
350+
selectedMissions.Remove(mission);
351+
352+
RefreshMissionMobies();
353+
}
354+
}
355+
ImGui.EndMenu();
356+
}
357+
339358
ImGui.Separator();
340359

341360
if (interactiveSession)
@@ -611,7 +630,7 @@ private void LoadLevel(Level level)
611630
camera.SetRotation(0, 0);
612631
}
613632
selectedObjects.Clear();
614-
633+
activeMobies = level.mobs.ToList();
615634
InvalidateView();
616635
}
617636

@@ -1012,8 +1031,17 @@ protected void OnResize()
10121031
return level.shrubs.Find(x => x.globalID == hitId);
10131032
case RenderedObjectType.Tie:
10141033
return level.ties.Find(x => x.globalID == hitId);
1034+
10151035
case RenderedObjectType.Moby:
1016-
return level.mobs.Find(x => x.globalID == hitId);
1036+
Moby? foundMoby = level.mobs.Find(x => x.globalID == hitId);
1037+
if (foundMoby != null) return foundMoby;
1038+
foreach (var mission in selectedMissions)
1039+
{
1040+
Moby? missionMoby = mission.mobies.Find(x => x.globalID == hitId);
1041+
if (missionMoby != null) return missionMoby;
1042+
}
1043+
return null;
1044+
10171045
case RenderedObjectType.Spline:
10181046
// Both "Splines" and "GrindPaths" use Spline objects.
10191047
// Search for a hit in the Splines first then look
@@ -1102,6 +1130,36 @@ private void ExportListOfProperties<T>(string path, IEnumerable<T> list, Func<T,
11021130
}
11031131
}
11041132

1133+
private List<Moby> activeMobies = new List<Moby>();
1134+
private void RefreshMissionMobies()
1135+
{
1136+
if (levelRenderer == null) return;
1137+
1138+
foreach (var moby in activeMobies.ToList())
1139+
levelRenderer.Remove(moby, level);
1140+
1141+
var mobies = new List<Moby>(level.mobs);
1142+
foreach (var mission in selectedMissions)
1143+
mobies.AddRange(mission.mobies);
1144+
1145+
foreach (var moby in mobies)
1146+
{
1147+
Mission? sourceMission = selectedMissions.FirstOrDefault(m => m.mobies.Contains(moby));
1148+
if (sourceMission != null)
1149+
{
1150+
bool isMissionModel = sourceMission.models.Exists(m => m.id == moby.modelID);
1151+
levelRenderer.Include(moby, isMissionModel ? sourceMission.textures : level.textures);
1152+
}
1153+
else
1154+
{
1155+
levelRenderer.Include(moby);
1156+
}
1157+
}
1158+
1159+
activeMobies = mobies;
1160+
InvalidateView();
1161+
}
1162+
11051163
protected void OnPaint()
11061164
{
11071165
rendererPayload.visibility.hideCameraMoby = interactiveSession && hookLiveUpdate && hookUpdateCamera;

Replanetizer/Frames/ModelFrame.cs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -695,6 +695,18 @@ private void PrepareForArrowInput()
695695

696696
private void SelectModel(Model? model)
697697
{
698+
if (model == null)
699+
return;
700+
701+
foreach (var mission in level.missions)
702+
{
703+
if (mission.models.Contains(model))
704+
{
705+
SelectModel(model, mission.textures);
706+
return;
707+
}
708+
}
709+
698710
SelectModel(model, level.textures);
699711
}
700712

Replanetizer/Renderer/LevelRenderer.cs

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -183,14 +183,14 @@ private void Add(Tie tie)
183183
tieRenderers.Add(tieRenderer);
184184
}
185185

186-
private void Add(Moby mob)
186+
private void Add(Moby mob, List<Texture>? textureOverride = null)
187187
{
188188
if (textures == null)
189189
{
190190
throw new NullReferenceException();
191191
}
192192

193-
MeshRenderer mobRenderer = new MeshRenderer(shaderTable, textures, textureIDs);
193+
MeshRenderer mobRenderer = new MeshRenderer(shaderTable, textureOverride ?? textures, textureIDs);
194194
mobRenderer.Include(mob);
195195
mobyRenderers.Add(mobRenderer);
196196
}
@@ -215,6 +215,10 @@ public override void Include<T>(T obj)
215215

216216
throw new NotImplementedException();
217217
}
218+
public void Include(Moby mob, List<Texture>? textureOverride = null)
219+
{
220+
Add(mob, textureOverride);
221+
}
218222
public override void Include<T>(List<T> list) => throw new NotImplementedException();
219223

220224
public void Remove<T>(T obj, Level level)

0 commit comments

Comments
 (0)