Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Track Object Load #1842

Merged
merged 5 commits into from Feb 15, 2023
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
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