Skip to content

Commit

Permalink
Track Object Load (#1842)
Browse files Browse the repository at this point in the history
* Start work on trackobj

* Implement findObjectHandleFuzzy

* Finish implementing function and cleanup

* Fix missing roads object cleanup

* Add changelog entry
  • Loading branch information
duncanspumpkin committed Feb 15, 2023
1 parent 837276b commit 6209690
Show file tree
Hide file tree
Showing 7 changed files with 186 additions and 44 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Expand Up @@ -3,6 +3,7 @@
- Fix: [#1763] Title music does not stop when unchecked in options window.
- Fix: [#1772] Toggling edge scrolling option does not work.
- Fix: [#1798] Memory leak when resizing the window.
- Fix: [#1842] Track and Dock objects incorrectly unloaded causing packing issues.
- Change: [#1823] Prevent edge scroll if the window has no input focus.

23.01 (2023-01-25)
Expand Down
6 changes: 6 additions & 0 deletions src/OpenLoco/src/Objects/Object.h
Expand Up @@ -50,6 +50,7 @@ namespace OpenLoco
{
private:
static constexpr char cFF = static_cast<char>(0xFF);
static constexpr uint32_t kFuzzyFlagsMask = 0xFFFE0000;

public:
uint32_t flags = 0xFFFFFFFF;
Expand All @@ -71,6 +72,11 @@ namespace OpenLoco
return static_cast<ObjectType>(flags & 0x3F);
}

constexpr uint32_t getFuzzyFlags() const
{
return flags & kFuzzyFlagsMask;
}

constexpr bool isCustom() const
{
return getSourceGame() == 0;
Expand Down
45 changes: 45 additions & 0 deletions src/OpenLoco/src/Objects/ObjectManager.cpp
Expand Up @@ -153,6 +153,51 @@ namespace OpenLoco::ObjectManager
return std::nullopt;
}

// 0x0047206C
// Returns std::nullopt if not loaded
std::optional<LoadedObjectHandle> findObjectHandleFuzzy(const ObjectHeader& header)
{
auto res = findObjectHandle(header);
if (res.has_value())
{
return res;
}

auto objectType = header.getType();
const auto& typedObjectList = getRepositoryItem(objectType);
auto maxObjectsForType = getMaxObjects(objectType);
for (LoadedObjectId i = 0; i < maxObjectsForType; i++)
{
auto obj = typedObjectList.objects[i];
if (obj == nullptr || obj != reinterpret_cast<Object*>(-1))
{
continue;
}
const auto& objHeader = typedObjectList.objectEntryExtendeds[i];

if (!objHeader.isCustom())
{
continue;
}
if (header.getType() != objHeader.getType())
{
continue;
}
if (header.getName() != objHeader.getName())
{
continue;
}
if (header.getFuzzyFlags() != objHeader.getFuzzyFlags())
{
continue;
}

return { LoadedObjectHandle{ objectType, i } };
}

return std::nullopt;
}

enum class ObjectProcedure
{
load,
Expand Down
3 changes: 3 additions & 0 deletions src/OpenLoco/src/Objects/ObjectManager.h
Expand Up @@ -157,6 +157,9 @@ namespace OpenLoco::ObjectManager
bool isTemporaryObjectLoad();

std::optional<LoadedObjectHandle> findObjectHandle(const ObjectHeader& header);
// Calls findObjectHandle and if can't find performs a secondary check with slightly looser
// definitions of what a matching custom header is (no checksum, partial flags)
std::optional<LoadedObjectHandle> findObjectHandleFuzzy(const ObjectHeader& header);
void reloadAll();
ObjectHeader& getHeader(const LoadedObjectHandle& handle);
std::vector<ObjectHeader> getHeaders();
Expand Down
121 changes: 104 additions & 17 deletions src/OpenLoco/src/Objects/TrackObject.cpp
Expand Up @@ -2,7 +2,9 @@
#include "Drawing/SoftwareDrawingEngine.h"
#include "Graphics/Colour.h"
#include "Graphics/Gfx.h"
#include "ObjectImageTable.h"
#include "ObjectManager.h"
#include "ObjectStringTable.h"
#include <OpenLoco/Interop/Interop.hpp>

namespace OpenLoco
Expand Down Expand Up @@ -59,36 +61,121 @@ namespace OpenLoco
// 0x004A6A5F
void TrackObject::load(const LoadedObjectHandle& handle, stdx::span<const std::byte> data, ObjectManager::DependentObjects* dependencies)
{
Interop::registers regs;
regs.esi = Interop::X86Pointer(this);
regs.ebx = handle.id;
regs.ecx = enumValue(handle.type);
Interop::call(0x004A6A5F, regs);
if (dependencies != nullptr)
auto remainingData = data.subspan(sizeof(TrackObject));

auto strRes = ObjectManager::loadStringTable(remainingData, handle, 0);
name = strRes.str;
remainingData = remainingData.subspan(strRes.tableLength);

// NOTE: These aren't dependent (objects unless otherwise stated) as this object can load without the
// related object.
// Load compatible roads/tracks
compatibleTracks = 0;
compatibleRoads = 0;
for (auto i = 0; i < numCompatible; ++i)
{
ObjectHeader modHeader = *reinterpret_cast<const ObjectHeader*>(remainingData.data());
auto res = ObjectManager::findObjectHandle(modHeader);
if (res.has_value())
{
if (res->type == ObjectType::track)
{
compatibleTracks |= 1U << res->id;
}
else if (res->type == ObjectType::road)
{
compatibleRoads |= 1U << res->id;
}
}
remainingData = remainingData.subspan(sizeof(ObjectHeader));
}

// Load Extra
std::fill(std::begin(mods), std::end(mods), 0xFF);
for (auto i = 0U, index = 0U; i < numMods; ++i)
{
ObjectHeader modHeader = *reinterpret_cast<const ObjectHeader*>(remainingData.data());
auto res = ObjectManager::findObjectHandle(modHeader);
if (res.has_value() && res->type == ObjectType::trackExtra)
{
mods[index++] = res->id;
}
remainingData = remainingData.subspan(sizeof(ObjectHeader));
}

// Load Signals
signals = 0;
for (auto i = 0; i < numSignals; ++i)
{
ObjectHeader modHeader = *reinterpret_cast<const ObjectHeader*>(remainingData.data());
auto res = ObjectManager::findObjectHandle(modHeader);
if (res.has_value() && res->type == ObjectType::trackSignal)
{
signals |= 1U << res->id;
}
remainingData = remainingData.subspan(sizeof(ObjectHeader));
}

// Load Tunnel (DEPENDENT OBJECT)
tunnel = 0xFF;
{
auto* depObjs = Interop::addr<0x0050D158, uint8_t*>();
dependencies->required.resize(*depObjs++);
if (!dependencies->required.empty())
ObjectHeader unkHeader = *reinterpret_cast<const ObjectHeader*>(remainingData.data());
if (dependencies != nullptr)
{
std::copy(reinterpret_cast<ObjectHeader*>(depObjs), reinterpret_cast<ObjectHeader*>(depObjs) + dependencies->required.size(), dependencies->required.data());
depObjs += sizeof(ObjectHeader) * dependencies->required.size();
dependencies->required.push_back(unkHeader);
}
dependencies->willLoad.resize(*depObjs++);
if (!dependencies->willLoad.empty())
auto res = ObjectManager::findObjectHandle(unkHeader);
if (res.has_value())
{
std::copy(reinterpret_cast<ObjectHeader*>(depObjs), reinterpret_cast<ObjectHeader*>(depObjs) + dependencies->willLoad.size(), dependencies->willLoad.data());
tunnel = res->id;
}
remainingData = remainingData.subspan(sizeof(ObjectHeader));
}

// Load bridges (DEPENDENT OBJECT)
std::fill(std::begin(bridges), std::end(bridges), 0xFF);
for (auto i = 0U; i < numBridges; ++i)
{
ObjectHeader bridgeHeader = *reinterpret_cast<const ObjectHeader*>(remainingData.data());
if (dependencies != nullptr)
{
dependencies->required.push_back(bridgeHeader);
}
auto res = ObjectManager::findObjectHandle(bridgeHeader);
if (res.has_value())
{
bridges[i] = res->id;
}
remainingData = remainingData.subspan(sizeof(ObjectHeader));
}

// Load stations
std::fill(std::begin(stations), std::end(stations), 0xFF);
for (auto i = 0U; i < numStations; ++i)
{
ObjectHeader stationHeader = *reinterpret_cast<const ObjectHeader*>(remainingData.data());
auto res = ObjectManager::findObjectHandle(stationHeader);
if (res.has_value())
{
stations[i] = res->id;
}
remainingData = remainingData.subspan(sizeof(ObjectHeader));
}

auto imgRes = ObjectManager::loadImageTable(remainingData);
image = imgRes.imageOffset;
assert(remainingData.size() == imgRes.tableLength);
}

// 0x004A6C2D
void TrackObject::unload()
{
name = 0;
var_10 = 0;
compatibleTracks = 0;
compatibleRoads = 0;
std::fill(std::begin(mods), std::end(mods), 0);
var_0E = 0;
var_1B = 0;
signals = 0;
tunnel = 0;
image = 0;
std::fill(std::begin(bridges), std::end(bridges), 0);
std::fill(std::begin(stations), std::end(stations), 0);
Expand Down
40 changes: 20 additions & 20 deletions src/OpenLoco/src/Objects/TrackObject.h
Expand Up @@ -51,26 +51,26 @@ namespace OpenLoco
TrackObjectPieceFlags trackPieces; // 0x02
uint16_t stationTrackPieces; // 0x04
uint8_t var_06;
uint8_t numCompatible; // 0x07
uint8_t numMods; // 0x08
uint8_t numSignals; // 0x09
uint8_t mods[4]; // 0x0A
uint16_t var_0E; // ?compatible signals bitset?
uint16_t var_10;
uint8_t pad_12[0x14 - 0x12];
int16_t buildCostFactor; // 0x14
int16_t sellCostFactor; // 0x16
int16_t tunnelCostFactor; // 0x18
uint8_t costIndex; // 0x1A
uint8_t var_1B;
uint16_t curveSpeed; // 0x1C
uint32_t image; // 0x1E
TrackObjectFlags flags; // 0x22
uint8_t numBridges; // 0x24
uint8_t bridges[7]; // 0x25
uint8_t numStations; // 0x2C
uint8_t stations[7]; // 0x2D
uint8_t displayOffset; // 0x34
uint8_t numCompatible; // 0x07
uint8_t numMods; // 0x08
uint8_t numSignals; // 0x09
uint8_t mods[4]; // 0x0A
uint16_t signals; // 0x0E bitset
uint16_t compatibleTracks; // 0x10 bitset
uint16_t compatibleRoads; // 0x12 bitset
int16_t buildCostFactor; // 0x14
int16_t sellCostFactor; // 0x16
int16_t tunnelCostFactor; // 0x18
uint8_t costIndex; // 0x1A
uint8_t tunnel; // 0x1B
uint16_t curveSpeed; // 0x1C
uint32_t image; // 0x1E
TrackObjectFlags flags; // 0x22
uint8_t numBridges; // 0x24
uint8_t bridges[7]; // 0x25
uint8_t numStations; // 0x2C
uint8_t stations[7]; // 0x2D
uint8_t displayOffset; // 0x34
uint8_t pad_35;

void drawPreviewImage(Gfx::RenderTarget& rt, const int16_t x, const int16_t y) const;
Expand Down
14 changes: 7 additions & 7 deletions src/OpenLoco/src/Windows/Construction/Common.cpp
Expand Up @@ -1477,20 +1477,20 @@ namespace OpenLoco::Ui::Windows::Construction
auto currentYear = getCurrentYear();
auto trackObj = ObjectManager::get<TrackObject>(trackType);
auto signalCount = 0;
auto var_0E = trackObj->var_0E;
while (var_0E > 0)
auto signals = trackObj->signals;
while (signals > 0)
{
auto ecx = Utility::bitScanForward(var_0E);
if (ecx == -1)
const auto signalId = Utility::bitScanForward(signals);
if (signalId == -1)
break;
var_0E &= ~(1 << ecx);
auto signalObj = ObjectManager::get<TrainSignalObject>(ecx);
signals &= ~(1 << signalId);
auto signalObj = ObjectManager::get<TrainSignalObject>(signalId);

if (currentYear > signalObj->obsoleteYear)
continue;
if (currentYear < signalObj->designedYear)
continue;
signalList[signalCount] = ecx;
signalList[signalCount] = signalId;
signalCount++;
}

Expand Down

0 comments on commit 6209690

Please sign in to comment.