Skip to content

Commit a837615

Browse files
committed
Merge branch 'staging'
2 parents 78458c1 + 0bc8c6f commit a837615

4 files changed

Lines changed: 145 additions & 64 deletions

File tree

CathodeLib/CathodeLib.csproj

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,10 +10,10 @@
1010
<Authors>Matt Filer</Authors>
1111
<Description>Provides support for parsing and writing common Alien: Isolation formats from the Cathode engine.</Description>
1212
<Copyright>Matt Filer 2026</Copyright>
13-
<Version>0.11.0</Version>
13+
<Version>0.11.1</Version>
1414
<OutputType>Library</OutputType>
15-
<AssemblyVersion>0.11.0.0</AssemblyVersion>
16-
<FileVersion>0.11.0.0</FileVersion>
15+
<AssemblyVersion>0.11.1.0</AssemblyVersion>
16+
<FileVersion>0.11.1.0</FileVersion>
1717
<AllowUnsafeBlocks>False</AllowUnsafeBlocks>
1818
<PackageReadmeFile>README.md</PackageReadmeFile>
1919
<PackageTags>alien, modding, alien isolation, mod tool, file utility</PackageTags>

CathodeLib/Scripts/CATHODE/Models.cs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -615,6 +615,16 @@ public CS2.Component FindModelComponentForSubmesh(CS2.Component.LOD.Submesh subm
615615
return null;
616616
}
617617
/// <summary>
618+
/// Find a model for a component
619+
/// </summary>
620+
public CS2 FindModelForComponent(CS2.Component component)
621+
{
622+
for (int i = 0; i < Entries.Count; i++)
623+
if (Entries[i].Components.Contains(component))
624+
return Entries[i];
625+
return null;
626+
}
627+
/// <summary>
618628
/// Find a LOD that contains a given submesh
619629
/// </summary>
620630
public CS2.Component.LOD FindModelLODForSubmesh(CS2.Component.LOD.Submesh submesh)

CathodeLib/Scripts/Level.cs

Lines changed: 55 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -30,8 +30,6 @@ public Global(string path, PAK2 animPAK)
3030
/// </summary>
3131
public class Level
3232
{
33-
public string Name;
34-
3533
public Textures Textures;
3634
public Shaders Shaders;
3735
public Collisions WeightedCollisions;
@@ -71,6 +69,18 @@ public class State
7169

7270
public Dictionary<string, Dictionary<string, TextDB>> Strings;
7371

72+
public Global Global => _global;
73+
private Global _global;
74+
75+
public string Filepath => _filepath;
76+
private string _filepath = "";
77+
78+
public string Name => _name;
79+
private string _name = "";
80+
81+
public bool Patched => _patched;
82+
private bool _patched = false;
83+
7484
/// <summary>
7585
/// Triggered every time one of the files within the level loads.
7686
/// Keep a count of this and divide it by NumberOfTicks to get a loading percentage.
@@ -85,18 +95,15 @@ public class State
8595

8696
public const int NumberOfTicks = 30;
8797

88-
private Global _global;
89-
private string _path;
90-
9198
/// <summary>
9299
/// A container for data related to a level in the game's "ENV" folder
93100
/// </summary>
94101
public Level(string path, Global global, bool loadImmediately = true)
95102
{
96-
_path = path;
97103
_global = global;
98-
99-
Name = _path.ToUpper().Replace("\\", "/").Split(new string[] { "DATA/ENV/" }, StringSplitOptions.None)[1].TrimEnd('/');
104+
_filepath = path.Replace("\\", "/").TrimEnd('/').ToUpper();
105+
_name = _filepath.Split(new string[] { "DATA/ENV/" }, StringSplitOptions.None)[1];
106+
_patched = (Name == "PRODUCTION/DLC/BSPNOSTROMO_RIPLEY" || Name == "PRODUCTION/DLC/BSPNOSTROMO_TWOTEAMS") && Directory.Exists(_filepath + "_PATCH");
100107

101108
if (loadImmediately)
102109
Load();
@@ -112,44 +119,47 @@ public void Load()
112119
if (_global?.AnimationStrings_Debug == null)
113120
throw new Exception("Missing Global Animation Strings");
114121

122+
string renderable = _filepath + "/RENDERABLE/";
123+
string world = _filepath + (_patched ? "_PATCH" : "") + "/WORLD/";
124+
115125
Parallel.Invoke(
116-
() => { Textures = new Textures(_path + "/RENDERABLE/LEVEL_TEXTURES.ALL.PAK"); OnLoadTick?.Invoke(); },
117-
() => { Shaders = new Shaders(_path + "/RENDERABLE/LEVEL_SHADERS_DX11.PAK"); OnLoadTick?.Invoke(); },
118-
() => { WeightedCollisions = new Collisions(_path + "/WORLD/COLLISION.BIN"); OnLoadTick?.Invoke(); },
119-
() => { MorphTargetDB = new MorphTargets(_path + "/WORLD/MORPH_TARGET_DB.BIN"); OnLoadTick?.Invoke(); },
120-
() => { Resources = new Resources(_path + "/WORLD/RESOURCES.BIN"); OnLoadTick?.Invoke(); },
121-
() => { MaterialMaps = new MaterialMappings(_path + "/WORLD/MATERIAL_MAPPINGS.PAK"); OnLoadTick?.Invoke(); }
126+
() => { Textures = new Textures(renderable + "LEVEL_TEXTURES.ALL.PAK"); OnLoadTick?.Invoke(); },
127+
() => { Shaders = new Shaders(renderable + "LEVEL_SHADERS_DX11.PAK"); OnLoadTick?.Invoke(); },
128+
() => { WeightedCollisions = new Collisions(world + "COLLISION.BIN"); OnLoadTick?.Invoke(); },
129+
() => { MorphTargetDB = new MorphTargets(world + "MORPH_TARGET_DB.BIN"); OnLoadTick?.Invoke(); },
130+
() => { Resources = new Resources(world + "RESOURCES.BIN"); OnLoadTick?.Invoke(); },
131+
() => { MaterialMaps = new MaterialMappings(world + "MATERIAL_MAPPINGS.PAK"); OnLoadTick?.Invoke(); }
122132
);
123133

124-
Materials = new Materials(_path + "/RENDERABLE/LEVEL_MODELS.MTL", _global.Textures, Textures, Shaders); OnLoadTick?.Invoke();
125-
Models = new Models(_path + "/RENDERABLE/LEVEL_MODELS.PAK", Materials, WeightedCollisions, MorphTargetDB); OnLoadTick?.Invoke();
126-
RenderableElements = new RenderableElements(_path + "/WORLD/REDS.BIN", Models, Materials); OnLoadTick?.Invoke();
127-
Movers = new Movers(_path + "/WORLD/MODELS.MVR", RenderableElements, Resources); OnLoadTick?.Invoke();
134+
Materials = new Materials(renderable + "LEVEL_MODELS.MTL", _global.Textures, Textures, Shaders); OnLoadTick?.Invoke();
135+
Models = new Models(renderable + "LEVEL_MODELS.PAK", Materials, WeightedCollisions, MorphTargetDB); OnLoadTick?.Invoke();
136+
RenderableElements = new RenderableElements(world + "REDS.BIN", Models, Materials); OnLoadTick?.Invoke();
137+
Movers = new Movers(world + "MODELS.MVR", RenderableElements, Resources); OnLoadTick?.Invoke();
128138

129139
Parallel.Invoke(
130-
() => { EnvironmentMaps = new EnvironmentMaps(_path + "/WORLD/ENVIRONMENTMAP.BIN", Movers); OnLoadTick?.Invoke(); },
131-
() => { PathBarrierResources = new PathBarrierResources(_path + "/WORLD/PATH_BARRIER_RESOURCES", Resources); OnLoadTick?.Invoke(); },
132-
() => { SoundFlashModels = new SoundFlashModels(_path + "/WORLD/SOUNDFLASHMODELS.DAT", _global.Textures, Textures); OnLoadTick?.Invoke(); },
133-
() => { CollisionMaps = new CollisionMaps(_path + "/WORLD/COLLISION.MAP", Materials, MaterialMaps); OnLoadTick?.Invoke(); }
140+
() => { EnvironmentMaps = new EnvironmentMaps(world + "ENVIRONMENTMAP.BIN", Movers); OnLoadTick?.Invoke(); },
141+
() => { PathBarrierResources = new PathBarrierResources(world + "PATH_BARRIER_RESOURCES", Resources); OnLoadTick?.Invoke(); },
142+
() => { SoundFlashModels = new SoundFlashModels(world + "SOUNDFLASHMODELS.DAT", _global.Textures, Textures); OnLoadTick?.Invoke(); },
143+
() => { CollisionMaps = new CollisionMaps(world + "COLLISION.MAP", Materials, MaterialMaps); OnLoadTick?.Invoke(); }
134144
);
135145

136146
Parallel.Invoke(
137-
() => { RadInstanceMap = new RadiosityInstanceMap(_path + "/RENDERABLE/RADIOSITY_INSTANCE_MAP.TXT"); OnLoadTick?.Invoke(); },
138-
() => { AlphaLight = new AlphaLightLevel(_path + "/WORLD/ALPHALIGHT_LEVEL.BIN"); OnLoadTick?.Invoke(); },
139-
() => { AccessorySets = new CharacterAccessorySets(_path + "/WORLD/CHARACTERACCESSORYSETS.BIN"); OnLoadTick?.Invoke(); },
140-
() => { EnvironmentAnimations = new EnvironmentAnimations(_path + "/WORLD/ENVIRONMENT_ANIMATION.DAT", _global.AnimationStrings_Debug); OnLoadTick?.Invoke(); },
141-
() => { Lights = new Lights(_path + "/WORLD/LIGHTS.BIN"); OnLoadTick?.Invoke(); },
142-
() => { MaterialMappings = new MaterialMappings(_path + "/WORLD/MATERIAL_MAPPINGS.PAK"); OnLoadTick?.Invoke(); },
143-
() => { PhysicsMaps = new PhysicsMaps(_path + "/WORLD/PHYSICS.MAP"); OnLoadTick?.Invoke(); },
144-
() => { SoundNodeNetwork = new SoundNodeNetwork(_path + "/WORLD/SNDNODENETWORK.DAT"); OnLoadTick?.Invoke(); },
145-
() => { SoundBankData = new SoundBankData(_path + "/WORLD/SOUNDBANKDATA.DAT"); OnLoadTick?.Invoke(); },
146-
() => { SoundDialogueLookups = new SoundDialogueLookups(_path + "/WORLD/SOUNDDIALOGUELOOKUPS.DAT"); OnLoadTick?.Invoke(); },
147-
() => { SoundEnvironmentData = new SoundEnvironmentData(_path + "/WORLD/SOUNDENVIRONMENTDATA.DAT"); OnLoadTick?.Invoke(); },
148-
() => { SoundEventData = new SoundEventData(_path + "/WORLD/SOUNDEVENTDATA.DAT"); OnLoadTick?.Invoke(); },
149-
() => { SoundLoadZones = new SoundLoadZones(_path + "/WORLD/SOUNDLOADZONES.DAT"); OnLoadTick?.Invoke(); }
147+
() => { RadInstanceMap = new RadiosityInstanceMap(renderable + "RADIOSITY_INSTANCE_MAP.TXT"); OnLoadTick?.Invoke(); },
148+
() => { AlphaLight = new AlphaLightLevel(world + "ALPHALIGHT_LEVEL.BIN"); OnLoadTick?.Invoke(); },
149+
() => { AccessorySets = new CharacterAccessorySets(world + "CHARACTERACCESSORYSETS.BIN"); OnLoadTick?.Invoke(); },
150+
() => { EnvironmentAnimations = new EnvironmentAnimations(world + "ENVIRONMENT_ANIMATION.DAT", _global.AnimationStrings_Debug); OnLoadTick?.Invoke(); },
151+
() => { Lights = new Lights(world + "LIGHTS.BIN"); OnLoadTick?.Invoke(); },
152+
() => { MaterialMappings = new MaterialMappings(world + "MATERIAL_MAPPINGS.PAK"); OnLoadTick?.Invoke(); },
153+
() => { PhysicsMaps = new PhysicsMaps(world + "PHYSICS.MAP"); OnLoadTick?.Invoke(); },
154+
() => { SoundNodeNetwork = new SoundNodeNetwork(world + "SNDNODENETWORK.DAT"); OnLoadTick?.Invoke(); },
155+
() => { SoundBankData = new SoundBankData(world + "SOUNDBANKDATA.DAT"); OnLoadTick?.Invoke(); },
156+
() => { SoundDialogueLookups = new SoundDialogueLookups(world + "SOUNDDIALOGUELOOKUPS.DAT"); OnLoadTick?.Invoke(); },
157+
() => { SoundEnvironmentData = new SoundEnvironmentData(world + "SOUNDENVIRONMENTDATA.DAT"); OnLoadTick?.Invoke(); },
158+
() => { SoundEventData = new SoundEventData(world + "SOUNDEVENTDATA.DAT"); OnLoadTick?.Invoke(); },
159+
() => { SoundLoadZones = new SoundLoadZones(world + "SOUNDLOADZONES.DAT"); OnLoadTick?.Invoke(); }
150160
);
151161

152-
Commands = new Commands(_path + "/WORLD/COMMANDS" + (File.Exists(_path + "/WORLD/COMMANDS.PAK") ? ".PAK" : ".BIN"), EnvironmentAnimations, CollisionMaps, RenderableElements); OnLoadTick?.Invoke();
162+
Commands = new Commands(world + "COMMANDS" + (File.Exists(world + "COMMANDS.PAK") ? ".PAK" : ".BIN"), EnvironmentAnimations, CollisionMaps, RenderableElements); OnLoadTick?.Invoke();
153163

154164
//RENDERABLE/DAMAGE/*
155165
//RENDERABLE/GALAXY/*
@@ -158,15 +168,14 @@ public void Load()
158168
//WORLD/COLLISION.HKX
159169
//WORLD/COLLISION.HKX64
160170
//WORLD/CUTSCENE_DIRECTOR_DATA.BIN
161-
//WORLD/EXCLUSIVE_MASTER_RESOURCE_INDICES (loaded below)
162171
//WORLD/LEVEL.STR
163172
//WORLD/OCCLUDER_TRIANGLE_BVH.BIN
164173
//WORLD/PHYSICS.HKX
165174
//WORLD/PHYSICS.HKX64
166175
//WORLD/RADIOSITY_COLLISION_MAPPING.BIN
167176

168177
int stateCount = 1; // we always implicitly have one state (the default state: state zero)
169-
using (BinaryReader reader = new BinaryReader(File.OpenRead(_path + "/WORLD/EXCLUSIVE_MASTER_RESOURCE_INDICES")))
178+
using (BinaryReader reader = new BinaryReader(File.OpenRead(world + "EXCLUSIVE_MASTER_RESOURCE_INDICES")))
170179
{
171180
reader.BaseStream.Position = 4; // version: 1
172181
int states = reader.ReadInt32(); // number of changeable states
@@ -179,7 +188,7 @@ public void Load()
179188
}
180189
for (int i = 0; i < stateCount; i++)
181190
{
182-
string statePath = _path + "/WORLD/STATE_" + i + "/";
191+
string statePath = world + "STATE_" + i + "/";
183192

184193
State state = new State() { Index = i };
185194
//ASSAULT_POSITIONS
@@ -191,8 +200,8 @@ public void Load()
191200
}
192201
OnLoadTick?.Invoke();
193202

194-
string pathDATA = _path.Replace('\\', '/').Split(new string[] { "/DATA/ENV" }, StringSplitOptions.None)[0] + "/DATA";
195-
string levelName = Directory.GetParent(_path).Name;
203+
string pathDATA = _filepath.Replace('\\', '/').Split(new string[] { "/DATA/ENV" }, StringSplitOptions.None)[0] + "/DATA";
204+
string levelName = Directory.GetParent(_filepath).Name;
196205
XmlNodeList textDBsGlobal = new BML(pathDATA + "/LEVEL_TEXT_DATABASES.BML").Content.SelectNodes("//level_text_databases/level");
197206
List<string> globalDBs = new List<string>();
198207
for (int i = 0; i < textDBsGlobal.Count; i++)
@@ -201,12 +210,12 @@ public void Load()
201210
globalDBs.Add(textDBsGlobal[i].ChildNodes[x].Attributes["name"].Value);
202211
List<string> textList = Directory.GetFiles(pathDATA + "/TEXT/", "*.TXT", SearchOption.AllDirectories).ToList<string>();
203212
List<string> levelDBs = new List<string>();
204-
if (File.Exists(_path + "/TEXT/TEXT_DB_LIST.TXT"))
213+
if (File.Exists(_filepath + "/TEXT/TEXT_DB_LIST.TXT"))
205214
{
206-
string[] textDBsLevel = File.ReadAllLines(_path + "/TEXT/TEXT_DB_LIST.TXT");
215+
string[] textDBsLevel = File.ReadAllLines(_filepath + "/TEXT/TEXT_DB_LIST.TXT");
207216
for (int i = 0; i < textDBsLevel.Length; i++)
208217
levelDBs.Add(textDBsLevel[i]);
209-
textList.AddRange(Directory.GetFiles(_path + "/TEXT/", "*.TXT", SearchOption.AllDirectories));
218+
textList.AddRange(Directory.GetFiles(_filepath + "/TEXT/", "*.TXT", SearchOption.AllDirectories));
210219
}
211220
textList.Reverse();
212221
Strings = new Dictionary<string, Dictionary<string, TextDB>>();
@@ -300,7 +309,7 @@ public void ImportFromGlobal()
300309
/// <summary>
301310
/// Get all levels available within the ENV folder. Pass the path to the folder that contains AI.exe.
302311
/// </summary>
303-
public static List<string> GetLevels(string gameDirectory, bool swapNostromoForPatch = false)
312+
public static List<string> GetLevels(string gameDirectory)
304313
{
305314
string[] galaxyBins = Directory.GetFiles(gameDirectory + "/DATA/ENV/", "GALAXY.DEFINITION_BIN", SearchOption.AllDirectories);
306315
List<string> mapList = new List<string>();
@@ -320,9 +329,7 @@ public static List<string> GetLevels(string gameDirectory, bool swapNostromoForP
320329
int length = file.Length - extraLength;
321330
if (length <= 0) continue;
322331

323-
string mapName = file.Substring(0, length).ToUpper();
324-
if (swapNostromoForPatch && (mapName == "DLC/BSPNOSTROMO_RIPLEY" || mapName == "DLC/BSPNOSTROMO_TWOTEAMS")) mapName += "_PATCH";
325-
mapList.Add(mapName.ToUpper());
332+
mapList.Add(file.Substring(0, length).ToUpper());
326333
}
327334
return mapList;
328335
}

0 commit comments

Comments
 (0)