Skip to content

Commit

Permalink
object manager (#10)
Browse files Browse the repository at this point in the history
* first pass

* working

* cleanup

* add some logging around this
  • Loading branch information
LeftofZen authored Oct 7, 2023
1 parent a4bf4c2 commit b16dcb8
Show file tree
Hide file tree
Showing 8 changed files with 111 additions and 17 deletions.
6 changes: 6 additions & 0 deletions OpenLocoTool/DatFileParsing/LocoObject.cs
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,12 @@ public interface ILocoStructVariableData
ReadOnlySpan<byte> Save();
}

[TypeConverter(typeof(ExpandableObjectConverter))]
public interface ILocoStructStringTablePostLoad
{
void LoadPostStringTable(StringTable stringTable);
}

[TypeConverter(typeof(ExpandableObjectConverter))]
public interface ILocoObject
{
Expand Down
24 changes: 13 additions & 11 deletions OpenLocoTool/DatFileParsing/SawyerStreamReader.cs
Original file line number Diff line number Diff line change
Expand Up @@ -82,24 +82,26 @@ public ILocoObject LoadFull(string filename, bool loadExtra = true)
var (stringTable, stringTableBytesRead) = LoadStringTable(remainingData, locoStruct);
remainingData = remainingData[stringTableBytesRead..];

if (locoStruct is ILocoStructStringTablePostLoad locoStructString)
{
locoStructString.LoadPostStringTable(stringTable);
}

// special handling per object type
if (loadExtra && locoStruct is ILocoStructVariableData locoStructExtra)
{
remainingData = locoStructExtra.Load(remainingData);
}

//try
{
var (g1Header, imageTable, imageTableBytesRead) = LoadImageTable(remainingData);
Logger.Log(LogLevel.Info, $"FileLength={new FileInfo(filename).Length} HeaderLength={S5Header.StructLength} DataLength={objectHeader.DataLength} StringTableLength={stringTableBytesRead} ImageTableLength={imageTableBytesRead}");
var (g1Header, imageTable, imageTableBytesRead) = LoadImageTable(remainingData);
Logger.Log(LogLevel.Info, $"FileLength={new FileInfo(filename).Length} HeaderLength={S5Header.StructLength} DataLength={objectHeader.DataLength} StringTableLength={stringTableBytesRead} ImageTableLength={imageTableBytesRead}");

return new LocoObject(s5Header, objectHeader, locoStruct, stringTable, g1Header, imageTable);
}
//catch (Exception ex)
//{
// Logger.Error(ex.ToString());
// return new LocoObject(objectHeader, locoStruct, stringTable, new G1Header(0, 0), new List<G1Element32>());
//}
var newObj = new LocoObject(s5Header, objectHeader, locoStruct, stringTable, g1Header, imageTable);

// add to object manager
SObjectManager.Add(newObj);

return newObj;
}

(StringTable table, int bytesRead) LoadStringTable(ReadOnlySpan<byte> data, ILocoStruct locoStruct)
Expand Down
44 changes: 44 additions & 0 deletions OpenLocoTool/ObjectManager.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
using OpenLocoTool.DatFileParsing;
using OpenLocoTool.Headers;

namespace OpenLocoTool
{
public static class SObjectManager
{
static readonly Dictionary<ObjectType, List<ILocoObject>> Objects = new();

static SObjectManager()
{
foreach (var v in Enum.GetValues(typeof(ObjectType)))
{
Objects.Add((ObjectType)v, new List<ILocoObject>());
}
}

public static List<T> Get<T>(ObjectType type)
where T : ILocoStruct => Objects[type].Select(a => a.Object).Cast<T>().ToList();

public static void Add<T>(T obj) where T : ILocoObject
=> Objects[obj.S5Header.ObjectType].Add(obj);
}

// unused for now
//public class ObjectManager
//{
// readonly Dictionary<ObjectType, List<ILocoObject>> Objects = new();

// public ObjectManager()
// {
// foreach (var v in Enum.GetValues(typeof(ObjectType)))
// {
// Objects.Add((ObjectType)v, new List<ILocoObject>());
// }
// }

// public List<T> Get<T>(ObjectType type)
// where T : ILocoStruct => Objects[type].Select(a => a.Object).Cast<T>().ToList();

// public void Add<T>(T obj) where T : ILocoObject
// => Objects[obj.S5Header.ObjectType].Add(obj);
//}
}
11 changes: 9 additions & 2 deletions OpenLocoTool/Objects/CargoObject.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ public enum CargoObjectFlags : uint8_t
[LocoStructSize(0x1F)]
[LocoStringCount(4)]
public record CargoObject(
[property: LocoStructOffset(0x00)] string_id Name,
//[property: LocoStructOffset(0x00)] string_id Name,
[property: LocoStructOffset(0x02)] uint16_t var_02,
[property: LocoStructOffset(0x04)] uint16_t var_04,
[property: LocoStructOffset(0x06)] string_id UnitsAndCargoName,
Expand All @@ -34,9 +34,16 @@ public record CargoObject(
[property: LocoStructOffset(0x1B)] uint16_t PaymentFactor,
[property: LocoStructOffset(0x1D)] uint8_t PaymentIndex,
[property: LocoStructOffset(0x1E)] uint8_t UnitSize
) : ILocoStruct
) : ILocoStruct, ILocoStructStringTablePostLoad
{
public static ObjectType ObjectType => ObjectType.Cargo;
public static int StructSize => 0x1F;

public string Name { get; set; }

public void LoadPostStringTable(StringTable stringTable)
{
Name = stringTable[(0, (LanguageId)0)];
}
}
}
20 changes: 18 additions & 2 deletions OpenLocoTool/Objects/Vehicle/VehicleObject.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,13 @@ namespace OpenLocoTool.Objects
[LocoStructSize(0x15E)]
public class VehicleObject : ILocoStruct, ILocoStructVariableData
{
public const ObjectType ObjType = ObjectType.Vehicle;
public static ObjectType ObjectType => ObjectType.Vehicle;
public const int StructSize = 0x15E;
public const int MaxBodySprites = 4;
public List<uint16_t> CargoMatchFlags { get; set; } = new();

public List<CargoObject> CompatibleCargo { get; set; } = new();

public VehicleObject(ushort name, TransportMode mode, VehicleType type, byte var_04, byte trackType, byte numMods, byte costIndex, short costFactor, byte reliability, byte runCostIndex, short runCostFactor, byte colourType, byte numCompat, ushort[] compatibleVehicles, byte[] requiredTrackExtras, VehicleObjectUnk[] var_24, BodySprite[] bodySprites, BogieSprite[] bogieSprites, ushort power, short speed, short rackSpeed, ushort weight, VehicleObjectFlags flags, byte[] maxCargo, uint[] cargoTypes, byte[] cargoTypeSpriteOffsets, byte numSimultaneousCargoTypes, SimpleAnimation[] animation, byte var_113, ushort designed, ushort obsolete, byte rackRailType, DrivingSoundType drivingSoundType, byte[] pad_135, byte numStartSounds, byte[] startSounds)
{
Name = name;
Expand Down Expand Up @@ -77,7 +79,7 @@ public VehicleObject(ushort name, TransportMode mode, VehicleType type, byte var
[LocoStructOffset(0xDE)] public uint16_t Weight { get; set; }
[LocoStructOffset(0xE0)] public VehicleObjectFlags Flags { get; set; }
[LocoStructOffset(0xE2), LocoArrayLength(2)] public uint8_t[] MaxCargo { get; set; } // size is relative to the first cargoTypes
[LocoStructOffset(0xE4), LocoArrayLength(2)] public uint32_t[] CargoTypes { get; set; }
[LocoStructOffset(0xE4), LocoArrayLength(2), Browsable(false)] public uint32_t[] CargoTypes { get; set; }
[LocoStructOffset(0xEC), LocoArrayLength(32)] public uint8_t[] CargoTypeSpriteOffsets { get; set; }
[LocoStructOffset(0x10C)] public uint8_t NumSimultaneousCargoTypes { get; set; }
[LocoStructOffset(0x10D), LocoArrayLength(2)] public SimpleAnimation[] Animation { get; set; }
Expand Down Expand Up @@ -118,6 +120,9 @@ public ReadOnlySpan<byte> Load(ReadOnlySpan<byte> remainingData)
remainingData = remainingData[(S5Header.StructLength * NumMods)..];

// cargo types
// this whole bullshit is mostly copied and pasted from openloco
// but we need to do it to a) load the cargo match flags and b) to move the stream to the right offset to load the next variable data
// afterwards, we'll do nice c# load of the cargo based on the match flags
for (var i = 0; i < CargoTypes.Length; ++i)
{
var index = NumSimultaneousCargoTypes;
Expand All @@ -139,11 +144,14 @@ public ReadOnlySpan<byte> Load(ReadOnlySpan<byte> remainingData)
var unk = remainingData[0];
remainingData = remainingData[1..]; // uint8_t

var cargoObjs = SObjectManager.Get<CargoObject>(ObjectType.Cargo);

for (var cargoType = 0; cargoType < 32; ++cargoType) // 32 is ObjectType::MaxObjects[cargo]
{
// until the rest of this is implemented, these values will be wrong
// but as long as they're non-zero to pass the == 0 check below, it'll work
CargoTypes[index] |= 1U << cargoType;
CargoTypeSpriteOffsets[cargoType] = unk;
}

ptr = BitConverter.ToUInt16(remainingData[0..2]);
Expand All @@ -161,6 +169,14 @@ public ReadOnlySpan<byte> Load(ReadOnlySpan<byte> remainingData)
}
}

foreach (var cargo in SObjectManager.Get<CargoObject>(ObjectType.Cargo))
{
if (CargoMatchFlags.Contains(cargo.MatchFlags))
{
CompatibleCargo.Add(cargo);
}
}

// animation
foreach (var anim in Animation)
{
Expand Down
4 changes: 2 additions & 2 deletions OpenLocoToolCommon/Logger.cs
Original file line number Diff line number Diff line change
Expand Up @@ -20,15 +20,15 @@ public override string ToString()

public class Logger : ILogger
{
readonly List<LogLine> loglines = new();
public readonly List<LogLine> Logs = new();
public LogLevel Level = LogLevel.Info;

public event EventHandler<LogAddedEventArgs> LogAdded;

public void Log(LogLevel level, string message)
{
var log = new LogLine { Time = DateTime.Now, Level = level, Message = message };
loglines.Add(log);
Logs.Add(log);

if (Level <= level)
{
Expand Down
2 changes: 2 additions & 0 deletions OpenLocoToolGui/MainForm.cs
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,8 @@ public MainForm()

private void MainForm_Load(object sender, EventArgs e)
{
// pre-add any existing log lines
lbLogs.Items.AddRange(((Logger)logger).Logs.ToArray());
// can only do this after window handle has been created (so can't do in constructor)
((Logger)logger).LogAdded += (s, e) => lbLogs.Invoke(() => lbLogs.Items.Insert(0, e.Log.ToString()));

Expand Down
17 changes: 17 additions & 0 deletions OpenLocoToolGui/MainFormModel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
using OpenLocoTool.DatFileParsing;
using OpenLocoTool.Objects;
using OpenLocoToolCommon;
using OpenLocoTool.Headers;

namespace OpenLocoToolGui
{
Expand All @@ -20,6 +21,8 @@ class MainFormModel

public ObjectCache ObjectCache { get; private set; } = new();

//public OpenLocoTool.ObjectManager ObjectManager { get; private set; } = new();

public string PaletteFile
{
get => Settings.PaletteFile;
Expand Down Expand Up @@ -56,6 +59,20 @@ public MainFormModel(ILogger logger, string settingsFile)
writer = new SawyerStreamWriter(logger);

LoadSettings(settingsFile);

// Load all cargo objects on startup
// Until a better solution is found (dynamic load-on-demand) we'll just do this
// for now. We'll have to do this for every dependent object type

var dependentObjectTypes = new HashSet<ObjectType>() { ObjectType.Cargo };
foreach (var depObjectType in dependentObjectTypes)
{
logger.Debug($"Preloading dependent {depObjectType} objects");
}
foreach (var dep in HeaderIndex.Where(kvp => dependentObjectTypes.Contains(kvp.Value.ObjectType)))
{
reader.LoadFull(dep.Key);
}
}

public GuiSettings Settings { get; private set; }
Expand Down

0 comments on commit b16dcb8

Please sign in to comment.