Skip to content

Commit 78458c1

Browse files
committed
Merge branch 'staging'
2 parents de2174e + 48c2efd commit 78458c1

84 files changed

Lines changed: 19489 additions & 3614 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

AlienBML

CathodeLib/CathodeLib.csproj

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

CathodeLib/Resources/info.dat

-50.1 KB
Binary file not shown.

CathodeLib/Scripts/Base Classes/CathodeFile.cs

Lines changed: 33 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -21,35 +21,50 @@ public class CathodeFile
2121

2222
public static Implementation Implementation = Implementation.NONE;
2323

24+
/// <summary>
25+
/// Override this property to return true if the derived class handles loading manually in its constructor
26+
/// </summary>
27+
protected virtual bool HandlesLoadingManually => false;
28+
2429
public CathodeFile(string filepath)
2530
{
2631
_filepath = filepath;
27-
_loaded = Load();
32+
33+
if (!HandlesLoadingManually)
34+
_loaded = Load();
2835
}
2936

3037
public CathodeFile(MemoryStream stream, string virtualPath = "")
3138
{
3239
_filepath = virtualPath;
33-
_loaded = Load(stream);
40+
if (!HandlesLoadingManually)
41+
_loaded = Load(stream);
3442
}
3543

3644
public CathodeFile(byte[] data, string virtualPath = "")
3745
{
3846
_filepath = virtualPath;
39-
using (var stream = new MemoryStream(data))
47+
if (!HandlesLoadingManually)
4048
{
41-
_loaded = Load(stream);
49+
using (var stream = new MemoryStream(data))
50+
{
51+
_loaded = Load(stream);
52+
}
4253
}
4354
}
4455

4556
#region EXTERNAL_FUNCS
46-
/* Try and load the file, if it exists */
57+
/// <summary>
58+
/// Try and load the file, if it exists
59+
/// </summary>
4760
protected bool Load()
4861
{
4962
return Load(File.Exists(_filepath) ? new MemoryStream(File.ReadAllBytes(_filepath)) : null);
5063
}
5164

52-
/* Load from MemoryStream */
65+
/// <summary>
66+
/// Load from MemoryStream
67+
/// </summary>
5368
protected bool Load(MemoryStream stream)
5469
{
5570
OnLoadBegin?.Invoke(_filepath);
@@ -74,7 +89,9 @@ protected bool Load(MemoryStream stream)
7489
#endif
7590
}
7691

77-
/* Save the file back to its original filepath */
92+
/// <summary>
93+
/// Save the file back to its original filepath
94+
/// </summary>
7895
public bool Save()
7996
{
8097
OnSaveBegin?.Invoke(_filepath);
@@ -99,7 +116,9 @@ public bool Save()
99116
#endif
100117
}
101118

102-
/* Save the file to a new path, and optionally remember it for future saves */
119+
/// <summary>
120+
/// Save the file to a new path, and optionally remember it for future saves
121+
/// </summary>
103122
public bool Save(string path = "", bool updatePath = true)
104123
{
105124
string origFilepath = updatePath && path != "" ? path : _filepath;
@@ -111,14 +130,18 @@ public bool Save(string path = "", bool updatePath = true)
111130
#endregion
112131

113132
#region TO_OVERRIDE
114-
/* Virtual function to override in inherited classes for loading the file */
133+
/// <summary>
134+
/// Virtual function to override in inherited classes for loading the file
135+
/// </summary>
115136
protected virtual bool LoadInternal(MemoryStream stream)
116137
{
117138
Console.WriteLine("WARNING: This class does not implement loading functionality!");
118139
return false;
119140
}
120141

121-
/* Virtual function to override in inherited classes for saving the file */
142+
/// <summary>
143+
/// Virtual function to override in inherited classes for saving the file
144+
/// </summary>
122145
protected virtual bool SaveInternal()
123146
{
124147
Console.WriteLine("WARNING: This class does not implement saving functionality!");

CathodeLib/Scripts/CATHODE/AlphaLightLevel.cs

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
using CathodeLib;
1+
using CathodeLib;
22
using System;
33
using System.Collections.Generic;
44
using System.IO;
@@ -11,7 +11,9 @@
1111

1212
namespace CATHODE
1313
{
14-
/* DATA/ENV/PRODUCTION/x/WORLD/ALPHALIGHT_LEVEL.BIN */
14+
/// <summary>
15+
/// DATA/ENV/x/WORLD/ALPHALIGHT_LEVEL.BIN
16+
/// </summary>
1517
public class AlphaLightLevel : CathodeFile
1618
{
1719
public static new Implementation Implementation = Implementation.CREATE | Implementation.LOAD | Implementation.SAVE;
@@ -30,7 +32,11 @@ override protected bool LoadInternal(MemoryStream stream)
3032
{
3133
reader.BaseStream.Position += 8;
3234
Resolution = new Vector2(reader.ReadInt32(), reader.ReadInt32());
35+
#if UNITY_EDITOR || UNITY_STANDALONE_WIN
36+
ImageData = reader.ReadBytes((int)Resolution.x * (int)Resolution.y * 8);
37+
#else
3338
ImageData = reader.ReadBytes((int)Resolution.X * (int)Resolution.Y * 8);
39+
#endif
3440
}
3541
return true;
3642
}
@@ -42,8 +48,13 @@ override protected bool SaveInternal()
4248
writer.BaseStream.SetLength(0);
4349
Utilities.WriteString("alph", writer);
4450
writer.Write(0);
45-
writer.Write(Resolution.X);
46-
writer.Write(Resolution.Y);
51+
#if UNITY_EDITOR || UNITY_STANDALONE_WIN
52+
writer.Write((int)Resolution.x);
53+
writer.Write((int)Resolution.y);
54+
#else
55+
writer.Write((int)Resolution.X);
56+
writer.Write((int)Resolution.Y);
57+
#endif
4758
writer.Write(ImageData);
4859
}
4960
return true;
Lines changed: 141 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -1,93 +1,185 @@
1-
using System;
1+
using CATHODE.Animations;
2+
using CATHODE.Scripting;
3+
using CathodeLib;
4+
using System;
25
using System.Collections.Generic;
36
using System.IO;
47
using System.Linq;
58
using System.Numerics;
69
using System.Runtime.InteropServices;
710
using System.Runtime.InteropServices.ComTypes;
811
using System.Text;
9-
using CATHODE.Scripting;
10-
using CathodeLib;
12+
using static System.Collections.Specialized.BitVector32;
1113

1214
namespace CATHODE
1315
{
14-
/* DATA/GLOBAL/ANIMATION.PAK -> ANIM_CLIP_DB.BIN */
16+
/// <summary>
17+
/// DATA/GLOBAL/ANIMATION.PAK -> x_ANIM_CLIP_DB.BIN
18+
/// </summary>
1519
public class AnimClipDB : CathodeFile
1620
{
17-
public Dictionary<uint, string> Entries = new Dictionary<uint, string>();
18-
public static new Implementation Implementation = Implementation.LOAD;
21+
public List<AnimClip> Animations = new List<AnimClip>();
22+
public List<Tuple<string, string>> BlendSets = new List<Tuple<string, string>>();
23+
public List<Context> Contexts = new List<Context>();
24+
25+
public string Character { get; set; }
26+
27+
public static new Implementation Implementation = Implementation.LOAD | Implementation.CREATE;
1928

20-
public AnimClipDB(string path) : base(path) { }
21-
public AnimClipDB(MemoryStream stream, string path = "") : base(stream, path) { }
22-
public AnimClipDB(byte[] data, string path = "") : base(data, path) { }
29+
public AnimClipDB(string path, AnimationStrings strings) : base(path)
30+
{
31+
_strings = strings;
32+
_loaded = Load();
33+
}
34+
public AnimClipDB(MemoryStream stream, AnimationStrings strings, string path) : base(stream, path)
35+
{
36+
_strings = strings;
37+
_loaded = Load(stream);
38+
}
39+
public AnimClipDB(byte[] data, AnimationStrings strings, string path) : base(data, path)
40+
{
41+
_strings = strings;
42+
using (MemoryStream stream = new MemoryStream(data))
43+
{
44+
_loaded = Load(stream);
45+
}
46+
}
47+
48+
private AnimationStrings _strings;
2349

2450
#region FILE_IO
2551
override protected bool LoadInternal(MemoryStream stream)
2652
{
53+
if (_strings == null || _filepath == null || _filepath == "")
54+
return false;
55+
2756
using (BinaryReader reader = new BinaryReader(stream))
2857
{
29-
int EntryCount1 = reader.ReadInt32();
30-
int EntryCount2 = reader.ReadInt32();
31-
IndexPair[] Entries1 = Utilities.ConsumeArray<IndexPair>(reader, EntryCount1);
32-
IndexPair[] Entries2 = Utilities.ConsumeArray<IndexPair>(reader, EntryCount2);
33-
34-
int Count0 = reader.ReadInt32();
35-
int Count1 = reader.ReadInt32();
36-
IndexPair[] Stuff0 = Utilities.ConsumeArray<IndexPair>(reader, Count0);
37-
OffsetPair[] Stuff1 = Utilities.ConsumeArray<OffsetPair>(reader, Count1);
58+
reader.BaseStream.Position += 8;
59+
Character = _strings.GetString(reader.ReadUInt32()); //note - the name of the file is this hashed string, followed by _ANIM_CLIP_DB.BIN
60+
reader.BaseStream.Position += 4;
3861

39-
int Count2 = reader.ReadInt32();
40-
int[] Stuff2 = Utilities.ConsumeArray<int>(reader, Count2);
62+
//anim_db.cpp line 4482
4163

42-
int Count4 = reader.ReadInt32();
43-
int Count5 = reader.ReadInt32();
44-
int Count6 = reader.ReadInt32();
45-
IndexPair[] Stuff5 = Utilities.ConsumeArray<IndexPair>(reader, Count5);
46-
int[] Stuff6 = Utilities.ConsumeArray<int>(reader, Count6);
64+
Animations = ReadAnimationHashTable(reader);
4765

48-
int Count7 = reader.ReadInt32();
49-
int[] Stuff7 = Utilities.ConsumeArray<int>(reader, Count7);
66+
BlendSets = HashTable.Read(reader, (r, n) =>
67+
new Tuple<string, string>(n, _strings.GetString(r.ReadUInt32()))
68+
, _strings);
5069

51-
byte[] HeaderCounts0 = Utilities.ConsumeArray<byte>(reader, 5);
52-
float[] HeaderFloats0 = Utilities.ConsumeArray<float>(reader, 6); // TODO: Is this HKX min/max floats for compression?
53-
int[] HeaderStuff0 = Utilities.ConsumeArray<int>(reader, 4);
70+
List<Tuple<string, uint>> contextNames = ReadStringHashTable(reader);
71+
for (int i = 0; i < contextNames.Count; i++)
72+
{
73+
Context context = new Context();
74+
context.Name = contextNames.FirstOrDefault(o => o.Item2 == i)?.Item1; //sometimes this is null - when it's null, the index of the string seems to be a hash?
75+
context.Animations = ReadAnimationHashTable(reader);
76+
context.BlendSets = HashTable.Read(reader, (r, n) =>
77+
new Tuple<string, string>(n, _strings.GetString(r.ReadUInt32()))
78+
, _strings);
79+
Contexts.Add(context);
80+
}
5481

55-
int[] ContentStuff0 = Utilities.ConsumeArray<int>(reader, HeaderCounts0[1] * 4);
56-
Vector2[] ContentStuff1 = Utilities.ConsumeArray<Vector2>(reader, HeaderCounts0[2]);
82+
reader.BaseStream.Position += 4;
83+
return true;
84+
}
85+
}
5786

58-
bone_entry[] BoneEntries = Utilities.ConsumeArray<bone_entry>(reader, HeaderCounts0[3]);
87+
private List<AnimClip> ReadAnimationHashTable(BinaryReader reader)
88+
{
89+
return HashTable.Read(reader, (r, n) => new AnimClip
90+
{
91+
Name = n,
92+
Path = _strings.GetString(r.ReadUInt32()),
93+
MetadataInstance = r.ReadInt32() //what does this map to
94+
}, _strings);
95+
}
5996

60-
// NOTE: Following content seems to be 4 unknown u8s followed by 4 u8s of which the 0th is ff and 1, 2 and 3 seem to
61-
// sum to 255. I would guess those are bone weights? Bone weights tend to sum to 1.
62-
}
63-
return true;
97+
private List<Tuple<string, uint>> ReadStringHashTable(BinaryReader reader)
98+
{
99+
return HashTable.Read(reader, (r, n) =>
100+
new Tuple<string, uint>(n, r.ReadUInt32())
101+
, _strings);
64102
}
65103

66104
override protected bool SaveInternal()
67105
{
106+
return false;
107+
108+
/*
68109
using (BinaryWriter writer = new BinaryWriter(File.OpenWrite(_filepath)))
69110
{
70111
writer.BaseStream.SetLength(0);
112+
113+
writer.Write(new byte[8]);
114+
writer.Write(_strings.GetID(Character));
115+
writer.Write(0);
116+
117+
WriteHashTable(writer, Animations, (w, item) => {
118+
w.Write(_strings.GetID(item.PathLabel));
119+
w.Write(item.MetadataInstance);
120+
});
121+
122+
WriteHashTable(writer, BlendSets, (w, item) => {
123+
w.Write(_strings.GetID(item));
124+
});
125+
126+
WriteHashTable(writer, PatchNames, (w, item) => {
127+
w.Write(_strings.GetID(item));
128+
});
129+
130+
foreach (var context in Contexts)
131+
{
132+
WriteHashTable(writer, context.Animations, (w, item) => {
133+
w.Write(_strings.GetID(item.Path));
134+
w.Write(item.MetadataInstance);
135+
});
136+
WriteHashTable(writer, context.BlendSets, (w, item) => {
137+
w.Write(_strings.GetID(item));
138+
});
139+
}
140+
141+
// Write termination
142+
writer.Write(0);
143+
144+
return true;
145+
}
146+
*/
147+
}
148+
149+
private void WriteHashTable<T>(BinaryWriter writer, List<T> data, Action<BinaryWriter, T> itemWriter)
150+
{
151+
writer.Write(data.Count);
152+
writer.Write(data.Count);
153+
154+
// Write hash table entries
155+
for (int i = 0; i < data.Count; i++)
156+
{
157+
writer.Write(Utilities.AnimationHashedString($"item_{i}")); // Generate hash
158+
writer.Write(i);
159+
}
160+
161+
// Write data entries
162+
foreach (var item in data)
163+
{
164+
itemWriter(writer, item);
71165
}
72-
return true;
73166
}
74167
#endregion
75168

76169
#region STRUCTURES
77-
[StructLayout(LayoutKind.Sequential, Pack = 1)]
78-
private struct IndexPair
170+
public struct AnimClip
79171
{
80-
public uint id;
81-
public int index;
172+
public string Name;
173+
public string Path;
174+
public int MetadataInstance;
82175
}
83-
[StructLayout(LayoutKind.Sequential, Pack = 1)]
84-
private struct bone_entry
176+
177+
public class Context
85178
{
86-
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)]
87-
byte[] Joints;
88-
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)]
89-
byte[] Weights;
90-
};
179+
public string Name;
180+
public List<AnimClip> Animations = new List<AnimClip>();
181+
public List<Tuple<string, string>> BlendSets = new List<Tuple<string, string>>();
182+
}
91183
#endregion
92184
}
93185
}

0 commit comments

Comments
 (0)