This commit is contained in:
Marijn van der Werf 2021-08-16 21:14:35 +00:00 committed by GitHub
commit f5e5e2b702
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
62 changed files with 1998 additions and 574 deletions

View File

@ -1,6 +1,8 @@
cmake_minimum_required(VERSION 3.9)
cmake_policy(VERSION 3.9)
set(MACOSX_DEPLOYMENT_TARGET "10.14")
set (PROJECT openloco)
# Note: Searching for CCache must be before project() so project() can use CCache too
@ -18,13 +20,13 @@ if (CCache_FOUND)
endif (OPENLOCO_USE_CCACHE)
endif (CCache_FOUND)
project(${PROJECT} CXX)
project(${PROJECT} C CXX)
if (APPLE)
# Detection of this variable seems to fail with CMake.
# Since we only support 32-bit builds at the moment, fix it this way.
# TODO: find out a proper fix for this.
set(CMAKE_SIZEOF_VOID_P 4)
# set(CMAKE_SIZEOF_VOID_P 4)
endif()
if (CMAKE_BINARY_DIR STREQUAL CMAKE_SOURCE_DIR)
@ -35,6 +37,7 @@ INCLUDE(FindPkgConfig)
include(CheckCXXCompilerFlag)
option(STRICT "Build with warnings as errors" YES)
option(I386 "Compile for native x86" ON)
set(CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake;${CMAKE_MODULE_PATH}")
@ -110,6 +113,13 @@ set(COMMON_COMPILE_OPTIONS
set(OBJ_FORMAT "elf32-i386")
set(LINKER_SCRIPT "ld_script_i386.xc")
if (I386)
set(TARGET_M "-m32")
else ()
# Turn off for 64-bit builds for now
set(COMMON_COMPILE_OPTIONS "${COMMON_COMPILE_OPTIONS} -Wno-int-to-pointer-cast")
endif ()
# Handle creating the rct2 text and data files on OS X and Linux
if (UNIX)
set(OLOCO_EXE ${CMAKE_CURRENT_SOURCE_DIR}/loco.exe)
@ -129,7 +139,19 @@ if (UNIX)
)
add_custom_target(segfiles DEPENDS ${OLOCO_TEXT} ${OLOCO_DATA})
if (APPLE)
set(LOCO_SEGMENT_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -sectcreate loco_text __text \"${OLOCO_TEXT}\" -sectcreate loco_data __data ${OLOCO_DATA} -segaddr loco_data 0x4d7000 -segprot loco_data rwx rwx -segaddr loco_text 0x401000 -segprot loco_text rwx rwx -segaddr __TEXT 0x2000000 -read_only_relocs suppress")
set(LOCO_SEGMENT_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} ")
set(LOCO_SEGMENT_LINKER_FLAGS "${LOCO_SEGMENT_LINKER_FLAGS} -Xlinker -map -Xlinker \"${CMAKE_BINARY_DIR}/openloco.map\"")
set(LOCO_SEGMENT_LINKER_FLAGS "${LOCO_SEGMENT_LINKER_FLAGS} -sectcreate loco_text __text \"${OLOCO_TEXT}\"")
set(LOCO_SEGMENT_LINKER_FLAGS "${LOCO_SEGMENT_LINKER_FLAGS} -sectcreate loco_data __data \"${OLOCO_DATA}\"")
set(LOCO_SEGMENT_LINKER_FLAGS "${LOCO_SEGMENT_LINKER_FLAGS} -segaddr loco_data 0x4d7000")
set(LOCO_SEGMENT_LINKER_FLAGS "${LOCO_SEGMENT_LINKER_FLAGS} -segprot loco_data rwx rwx")
# set(LOCO_SEGMENT_LINKER_FLAGS "${LOCO_SEGMENT_LINKER_FLAGS} -segaddr loco_text 0x401000")
set(LOCO_SEGMENT_LINKER_FLAGS "${LOCO_SEGMENT_LINKER_FLAGS} -segprot loco_text rwx rwx")
set(LOCO_SEGMENT_LINKER_FLAGS "${LOCO_SEGMENT_LINKER_FLAGS} -segaddr __TEXT 0x2000000")
set(LOCO_SEGMENT_LINKER_FLAGS "${LOCO_SEGMENT_LINKER_FLAGS} -read_only_relocs suppress")
if (NOT I386)
set(LOCO_SEGMENT_LINKER_FLAGS "${LOCO_SEGMENT_LINKER_FLAGS} -pagezero_size 10000 -image_base 100000000")
endif()
else ()
# For Linux we have to use objcopy to wrap regular binaries into a linkable
# format. We use specific section names which are then referenced in a
@ -164,8 +186,8 @@ set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -DDEBUG=${DEBUG_LEVEL}")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DDEBUG=${DEBUG_LEVEL}")
# add source files
file(GLOB_RECURSE OLOCO_SOURCES "src/*.cpp")
file(GLOB_RECURSE OLOCO_HEADERS "src/*.h" "src/*.hpp")
file(GLOB_RECURSE OLOCO_SOURCES "src/OpenLoco/*.cpp")
file(GLOB_RECURSE OLOCO_HEADERS "src/OpenLoco/*.h" "src/OpenLoco/*.hpp")
if (APPLE)
file(GLOB_RECURSE OLOCO_MM_SOURCES "src/*.mm")
@ -173,7 +195,6 @@ if (APPLE)
endif ()
set(PIE_FLAG "-fno-pie")
set(TARGET_M "-m32")
# Check if a flag exists and add it to the list of compiler (so, not linker) options
function (ADD_CHECK_CXX_COMPILER_FLAG _CXXFLAGS _CACHE_VAR _FLAG)
@ -204,6 +225,9 @@ set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${TARGET_M} ${COMMON_COMPILE_OPTIONS}")
set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} ${TARGET_M}")
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} ${CMAKE_SHARED_LINKER_FLAGS} ${TARGET_M} ${PIE_FLAG}")
add_library(tinyalloc src/tinyalloc/tinyalloc.c)
target_include_directories(tinyalloc PUBLIC src/tinyalloc/)
find_package(SDL2 REQUIRED)
if (NOT SDL2_LIBRARIES)
if (TARGET SDL2::SDL2-static)
@ -233,6 +257,10 @@ if (MINGW)
target_link_libraries(${PROJECT} winmm)
else ()
add_executable(${PROJECT} ${OLOCO_SOURCES} ${OLOCO_MM_SOURCES} ${LOCO_SECTIONS})
if (NOT I386)
find_library(UNICORN unicorn)
target_link_libraries(${PROJECT} ${UNICORN})
endif ()
set_target_properties(${PROJECT} PROPERTIES LINK_FLAGS ${LOCO_SEGMENT_LINKER_FLAGS})
endif ()
@ -240,6 +268,7 @@ target_link_libraries(${PROJECT} ${SDL2_LIBRARIES} ${SDL2_MIXER_LIBRARIES})
target_link_libraries(${PROJECT} yaml-cpp ${YAML_CPP_LIBRARIES})
target_link_libraries(${PROJECT} ${PNG_LIBRARIES})
target_link_libraries(${PROJECT} tinyalloc)
if (NOT MINGW)
add_dependencies(${PROJECT} segfiles)

View File

@ -478,7 +478,7 @@ namespace OpenLoco::Audio
auto w = WindowManager::find(WindowType::main, 0);
if (w != nullptr)
{
auto viewport = w->viewports[0];
auto viewport = w->viewports[0].get();
if (viewport != nullptr && viewport->contains(vpos))
{
return viewport;
@ -490,7 +490,7 @@ namespace OpenLoco::Audio
w = WindowManager::get(i);
if (w != nullptr && w->type != WindowType::main && w->type != WindowType::news)
{
auto viewport = w->viewports[0];
auto viewport = w->viewports[0].get();
if (viewport != nullptr && viewport->contains(vpos))
{
return viewport;
@ -830,7 +830,7 @@ namespace OpenLoco::Audio
auto main = WindowManager::getMainWindow();
if (main != nullptr && main->viewports[0] != nullptr)
{
auto viewport = main->viewports[0];
auto viewport = main->viewports[0].get();
ViewportRect extendedViewport = {};
auto quarterWidth = viewport->view_width / 4;
@ -864,7 +864,7 @@ namespace OpenLoco::Audio
if (w->type == WindowType::news)
continue;
auto viewport = w->viewports[0];
auto viewport = w->viewports[0].get();
if (viewport == nullptr)
continue;

View File

@ -317,7 +317,7 @@ namespace OpenLoco::CompanyManager
if (main == nullptr)
return;
auto viewport = main->viewports[0];
auto viewport = main->viewports[0].get();
if (viewport == nullptr)
return;

View File

@ -165,7 +165,7 @@ namespace OpenLoco::Drawing
windowContext.height = rect.height();
windowContext.x = rect.left();
windowContext.y = rect.top();
windowContext.bits = screen_info->context.bits + rect.left() + ((screen_info->context.width + screen_info->context.pitch) * rect.top());
windowContext.bits = screen_info->context.bits.get() + rect.left() + ((screen_info->context.width + screen_info->context.pitch) * rect.top());
windowContext.pitch = screen_info->context.width + screen_info->context.pitch - rect.width();
windowContext.zoom_level = 0;

View File

@ -50,7 +50,7 @@ namespace OpenLoco::Game
Audio::pauseSound();
setPauseFlag(1 << 2);
Gfx::invalidateScreen();
Ui::ProgressBar::sub_4CF63B();
Gfx::render();
bool confirm = Ui::Windows::PromptBrowse::open(type, &_savePath[0], filter, titleBuffer);
@ -58,7 +58,7 @@ namespace OpenLoco::Game
Ui::processMessagesMini();
unsetPauseFlag(1 << 2);
Gfx::invalidateScreen();
Ui::ProgressBar::sub_4CF63B();
Gfx::render();
return confirm;
}

View File

@ -19,21 +19,22 @@ namespace OpenLoco::Colour
{
assert(i + 2170 < 2201);
auto image = Gfx::getG1Element(2170 + i);
_colour_map_a[i][0] = image->offset[9];
auto offset = image->offset.get();
_colour_map_a[i][0] = offset[9];
_colour_map_a[i][1] = image->offset[246];
_colour_map_a[i][2] = image->offset[247];
_colour_map_a[i][3] = image->offset[248];
_colour_map_a[i][1] = offset[246];
_colour_map_a[i][2] = offset[247];
_colour_map_a[i][3] = offset[248];
_colour_map_a[i][4] = image->offset[249];
_colour_map_a[i][5] = image->offset[250];
_colour_map_a[i][6] = image->offset[251];
_colour_map_a[i][7] = image->offset[252];
_colour_map_a[i][4] = offset[249];
_colour_map_a[i][5] = offset[250];
_colour_map_a[i][6] = offset[251];
_colour_map_a[i][7] = offset[252];
_colour_map_b[i][8 - 8] = image->offset[253];
_colour_map_b[i][9 - 8] = image->offset[254];
_colour_map_b[i][10 - 8] = image->offset[255];
_colour_map_b[i][11 - 8] = image->offset[256];
_colour_map_b[i][8 - 8] = offset[253];
_colour_map_b[i][9 - 8] = offset[254];
_colour_map_b[i][10 - 8] = offset[255];
_colour_map_b[i][11 - 8] = offset[256];
}
}

View File

@ -45,7 +45,7 @@ namespace OpenLoco::Gfx
static loco_global<G1Element[g1_expected_count::disc + g1_count_temporary + g1_count_objects], 0x9E2424> _g1Elements;
static std::unique_ptr<std::byte[]> _g1Buffer;
static std::byte* _g1Buffer;
static loco_global<uint16_t[147], 0x050B8C8> _paletteToG1Offset;
static loco_global<uint16_t, 0x112C824> _currentFontFlags;
@ -148,7 +148,7 @@ namespace OpenLoco::Gfx
auto g1 = getG1Element(*g1Index);
if (g1 != nullptr)
{
return PaletteMap(g1->offset, g1->height, g1->width);
return PaletteMap(g1->offset.get(), g1->height, g1->width);
}
}
return std::nullopt;
@ -201,8 +201,8 @@ namespace OpenLoco::Gfx
auto elements = convertElements(elements32);
// Read element data
auto elementData = std::make_unique<std::byte[]>(header.total_size);
if (!readData(stream, elementData.get(), header.total_size))
auto elementData = (std::byte*)malloc(header.total_size);
if (!readData(stream, elementData, header.total_size))
{
throw std::runtime_error("Reading g1 elements failed.");
}
@ -226,10 +226,10 @@ namespace OpenLoco::Gfx
// Adjust memory offsets
for (auto& element : elements)
{
element.offset += (uintptr_t)elementData.get();
element.offset = element.offset.get() + (uintptr_t)elementData;
}
_g1Buffer = std::move(elementData);
_g1Buffer = elementData;
std::copy(elements.begin(), elements.end(), _g1Elements.get());
}
@ -240,7 +240,7 @@ namespace OpenLoco::Gfx
{
int32_t w = context.width / (1 << context.zoom_level);
int32_t h = context.height / (1 << context.zoom_level);
uint8_t* ptr = context.bits;
uint8_t* ptr = context.bits.get();
for (int32_t y = 0; y < h; y++)
{
@ -554,7 +554,8 @@ namespace OpenLoco::Gfx
static void setTextColour(int colour)
{
auto el = &_g1Elements[ImageIds::text_palette];
setTextColours(el->offset[colour * 4 + 0], el->offset[colour * 4 + 1], el->offset[colour * 4 + 2]);
auto offset = el->offset.get();
setTextColours(offset[colour * 4 + 0], offset[colour * 4 + 1], offset[colour * 4 + 2]);
}
// 0x00451189
@ -1386,7 +1387,7 @@ namespace OpenLoco::Gfx
return ImageIdFlags::translucent | (colour << 19) | image;
}
loco_global<uint8_t*, 0x0050B860> _50B860;
loco_global<uint32_t, 0x0050B860> _50B860;
loco_global<uint32_t, 0x00E04324> _E04324;
void drawImageSolid(Gfx::Context* context, int16_t x, int16_t y, uint32_t image, uint8_t palette_index)
@ -1400,7 +1401,7 @@ namespace OpenLoco::Gfx
void drawImagePaletteSet(Gfx::Context* context, int16_t x, int16_t y, uint32_t image, uint8_t* palette)
{
_50B860 = palette;
_50B860 = (uintptr_t)palette;
_E04324 = 0x20000000;
registers regs;
regs.cx = x;
@ -1417,7 +1418,7 @@ namespace OpenLoco::Gfx
Ui::Rect intersect = oldRect.intersection(newRect);
const auto stride = oldRect.size.width + src.pitch;
const int16_t newPitch = stride - intersect.size.width;
auto* newBits = src.bits + (stride * (intersect.origin.y - oldRect.origin.y) + (intersect.origin.x - oldRect.origin.x));
auto* newBits = src.bits.get() + (stride * (intersect.origin.y - oldRect.origin.y) + (intersect.origin.x - oldRect.origin.x));
intersect.origin.x = std::max(0, oldRect.origin.x - newRect.origin.x);
intersect.origin.y = std::max(0, oldRect.origin.y - newRect.origin.y);
Gfx::Context newContext{ newBits, static_cast<int16_t>(intersect.origin.x), static_cast<int16_t>(intersect.origin.y), static_cast<int16_t>(intersect.size.width), static_cast<int16_t>(intersect.size.height), newPitch, 0 };

View File

@ -1,6 +1,7 @@
#pragma once
#include "../Core/Optional.hpp"
#include "../Interop/Interop.hpp"
#include "../OpenLoco.h"
#include "../Types.hpp"
#include "../Ui/Rect.h"
@ -18,20 +19,20 @@ namespace OpenLoco::Gfx
struct Context
{
uint8_t* bits; // 0x00
int16_t x; // 0x04
int16_t y; // 0x06
int16_t width; // 0x08
int16_t height; // 0x0A
int16_t pitch; // 0x0C note: this is actually (pitch - width)
uint16_t zoom_level; // 0x0E
loco_ptr2<uint8_t> bits; // 0x00
int16_t x; // 0x04
int16_t y; // 0x06
int16_t width; // 0x08
int16_t height; // 0x0A
int16_t pitch; // 0x0C note: this is actually (pitch - width)
uint16_t zoom_level; // 0x0E
Ui::Rect getUiRect() const;
Ui::Rect getDrawableRect() const;
};
assert_struct_size(Context, 0x10);
Context& screenContext();
struct G1Header
{
uint32_t num_entries;
@ -48,11 +49,12 @@ namespace OpenLoco::Gfx
uint16_t flags; // 0x0C
int16_t unused; // 0x0E
};
assert_struct_size(G1Element32, 0x10);
// A version that can be 64-bit when ready...
struct G1Element
{
uint8_t* offset = nullptr;
loco_ptr2<uint8_t> offset = nullptr;
int16_t width = 0;
int16_t height = 0;
int16_t x_offset = 0;
@ -73,6 +75,8 @@ namespace OpenLoco::Gfx
}
};
assert_struct_size(G1Element, 0x10);
#pragma pack(pop)
namespace ImageIdFlags
{

View File

@ -61,17 +61,17 @@ namespace OpenLoco::Gui
{
window->width = uiWidth;
window->height = uiHeight;
if (window->widgets)
if (window->widgets.get())
{
window->widgets[0].right = uiWidth;
window->widgets[0].bottom = uiHeight;
window->widgets.get()[0].right = uiWidth;
window->widgets.get()[0].bottom = uiHeight;
}
if (window->viewports[0])
if (window->viewports[0].get())
{
window->viewports[0]->width = uiWidth;
window->viewports[0]->height = uiHeight;
window->viewports[0]->view_width = uiWidth << window->viewports[0]->zoom;
window->viewports[0]->view_height = uiHeight << window->viewports[0]->zoom;
window->viewports[0].get()->width = uiWidth;
window->viewports[0].get()->height = uiHeight;
window->viewports[0].get()->view_width = uiWidth << window->viewports[0].get()->zoom;
window->viewports[0].get()->view_height = uiHeight << window->viewports[0].get()->zoom;
}
}

View File

@ -51,7 +51,9 @@ namespace OpenLoco::Input
// 0x00406FEC
void enqueueMouseButton(int32_t button)
{
#ifdef __i386__
((void (*)(int))0x00406FEC)(button);
#endif
}
void sub_407218()

View File

@ -426,7 +426,7 @@ namespace OpenLoco::Input
if (OpenLoco::isTitleMode())
return;
auto viewport = main->viewports[0];
auto viewport = main->viewports[0].get();
if (viewport == nullptr)
return;
@ -472,7 +472,7 @@ namespace OpenLoco::Input
if (OpenLoco::isTitleMode())
return;
auto viewport = main->viewports[0];
auto viewport = main->viewports[0].get();
if (viewport == nullptr)
return;

View File

@ -477,10 +477,10 @@ namespace OpenLoco::Input
case MouseButton::released:
{
// 0x4C735D
auto viewport = window->viewports[0];
auto viewport = window->viewports[0].get();
if (viewport == nullptr)
{
viewport = window->viewports[1];
viewport = window->viewports[1].get();
}
if (viewport == nullptr)
{
@ -768,10 +768,10 @@ namespace OpenLoco::Input
{
// 4C74E4
_ticksSinceDragStart += time_since_last_tick;
auto vp = window->viewports[0];
auto vp = window->viewports[0].get();
if (vp == nullptr)
{
vp = window->viewports[1];
vp = window->viewports[1].get();
}
if (vp == nullptr)
{

View File

@ -244,7 +244,7 @@ namespace OpenLoco::Input::ShortcutManager
if (window == nullptr)
return;
auto viewport = WindowManager::getMainWindow()->viewports[0];
auto viewport = WindowManager::getMainWindow()->viewports[0].get();
viewport->flags ^= ViewportFlags::underground_view;
window->invalidate();
}
@ -256,7 +256,7 @@ namespace OpenLoco::Input::ShortcutManager
if (window == nullptr)
return;
auto viewport = WindowManager::getMainWindow()->viewports[0];
auto viewport = WindowManager::getMainWindow()->viewports[0].get();
viewport->flags ^= ViewportFlags::hide_foreground_tracks_roads;
window->invalidate();
}
@ -268,7 +268,7 @@ namespace OpenLoco::Input::ShortcutManager
if (window == nullptr)
return;
auto viewport = WindowManager::getMainWindow()->viewports[0];
auto viewport = WindowManager::getMainWindow()->viewports[0].get();
viewport->flags ^= ViewportFlags::hide_foreground_scenery_buildings;
window->invalidate();
}
@ -280,7 +280,7 @@ namespace OpenLoco::Input::ShortcutManager
if (window == nullptr)
return;
auto viewport = WindowManager::getMainWindow()->viewports[0];
auto viewport = WindowManager::getMainWindow()->viewports[0].get();
viewport->flags ^= ViewportFlags::height_marks_on_land;
window->invalidate();
}
@ -292,7 +292,7 @@ namespace OpenLoco::Input::ShortcutManager
if (window == nullptr)
return;
auto viewport = WindowManager::getMainWindow()->viewports[0];
auto viewport = WindowManager::getMainWindow()->viewports[0].get();
viewport->flags ^= ViewportFlags::height_marks_on_tracks_roads;
window->invalidate();
}
@ -304,7 +304,7 @@ namespace OpenLoco::Input::ShortcutManager
if (window == nullptr)
return;
auto viewport = WindowManager::getMainWindow()->viewports[0];
auto viewport = WindowManager::getMainWindow()->viewports[0].get();
viewport->flags ^= ViewportFlags::one_way_direction_arrows;
window->invalidate();
}

View File

@ -13,6 +13,7 @@
#include "../Console.h"
#include "Interop.hpp"
#ifdef __i386__
namespace OpenLoco::Interop
{
static void* _hookTableAddress;
@ -186,6 +187,7 @@ namespace OpenLoco::Interop
Console::error("Failed registering hook for 0x%08x. Ran out of hook table space", address);
return;
}
<<<<<<< HEAD
// Do a few retries here. This can fail on some versions of wine which inexplicably would fail on
// WriteProcessMemory for specific addresses that we fully own, but skipping over failing entry would work.
bool done = false;
@ -196,6 +198,13 @@ namespace OpenLoco::Interop
uint8_t data[9];
int32_t i = 0;
data[i++] = 0xE9; // jmp
=======
int32_t ptr = (loco_ptr)_hookTableAddress;
uint32_t hookaddress = ptr + (_hookTableOffset * HOOK_BYTE_COUNT);
uint8_t data[9];
int32_t i = 0;
data[i++] = 0xE9; // jmp
>>>>>>> f02af53d... WIP
WRITE_ADDRESS_STRICTALIAS(&data[i], hookaddress - address - i - 4);
i += 4;
@ -243,12 +252,12 @@ namespace OpenLoco::Interop
#ifdef _WIN32
_smallHooks = VirtualAllocEx(GetCurrentProcess(), NULL, size, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
#else
_smallHooks = mmap(NULL, size, PROT_EXEC | PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
if (_smallHooks == MAP_FAILED)
{
perror("mmap");
exit(1);
}
_smallHooks = mmap(NULL, size, PROT_EXEC | PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
if (_smallHooks == MAP_FAILED)
{
perror("mmap");
exit(1);
}
#endif // _WIN32
_offset = static_cast<uint8_t*>(_smallHooks);
}
@ -306,3 +315,4 @@ namespace OpenLoco::Interop
writeMemory(address, buffer.data(), buffer.size());
}
}
#endif

View File

@ -3,6 +3,7 @@
#include <cstring>
#include <system_error>
#ifndef _WIN32
#include <cinttypes>
#include <sys/mman.h>
#include <unistd.h>
#endif
@ -41,6 +42,10 @@ using namespace OpenLoco;
#define STUB() Console::logVerbose(__FUNCTION__)
#ifdef __i386__
namespace compat = std;
#endif
#ifdef _MSC_VER
#define STDCALL __stdcall
#define CDECL __cdecl
@ -51,6 +56,13 @@ using namespace OpenLoco;
#error Unknown compiler, please define STDCALL and CDECL
#endif
#ifdef __x86_64__
#undef STDCALL
#define STDCALL
#undef CDECL
#define CDECL
#endif
#pragma warning(push)
// MSVC ignores C++17's [[maybe_unused]] attribute on functions, so just disable the warning
#pragma warning(disable : 4505) // unreferenced local function has been removed.
@ -86,11 +98,6 @@ static int32_t CDECL audioIsChannelPlaying(int a0)
}
#ifdef _NO_LOCO_WIN32_
static void STDCALL fn_40447f()
{
STUB();
return;
}
static void STDCALL fn_404b68(int a0, int a1, int a2, int a3)
{
@ -123,7 +130,7 @@ static void CDECL fn_4054a3(const palette_entry_t* palette, int32_t index, int32
}
FORCE_ALIGN_ARG_POINTER
static bool STDCALL fn_4054b9()
static bool STDCALL fn_40726d()
{
STUB();
return true;
@ -149,18 +156,6 @@ static void STDCALL fn_4078be()
return;
}
static void STDCALL fn_4078fe()
{
STUB();
return;
}
static void STDCALL fn_407b26()
{
STUB();
return;
}
///region Progress bar
static void CDECL fn_4080bb(char* lpWindowName, uint32_t a1)
@ -186,35 +181,41 @@ static void CDECL fn_4081ad(int32_t wParam)
///endregion
struct FileWrapper
{
FILE* file;
std::string name;
};
FORCE_ALIGN_ARG_POINTER
static uint32_t CDECL fn_FileSeekSet(FILE* a0, int32_t distance)
static uint32_t CDECL fn_FileSeekSet(FileWrapper* a0, int32_t distance)
{
Console::logVerbose("seek %d bytes from start", distance);
fseek(a0, distance, SEEK_SET);
return ftell(a0);
fseek(a0->file, distance, SEEK_SET);
return ftell(a0->file);
}
FORCE_ALIGN_ARG_POINTER
static uint32_t CDECL fn_FileSeekFromCurrent(FILE* a0, int32_t distance)
static uint32_t CDECL fn_FileSeekFromCurrent(FileWrapper* a0, int32_t distance)
{
Console::logVerbose("seek %d bytes from current", distance);
fseek(a0, distance, SEEK_CUR);
return ftell(a0);
fseek(a0->file, distance, SEEK_CUR);
return ftell(a0->file);
}
FORCE_ALIGN_ARG_POINTER
static uint32_t CDECL fn_FileSeekFromEnd(FILE* a0, int32_t distance)
static uint32_t CDECL fn_FileSeekFromEnd(FileWrapper* a0, int32_t distance)
{
Console::logVerbose("seek %d bytes from end", distance);
fseek(a0, distance, SEEK_END);
return ftell(a0);
fseek(a0->file, distance, SEEK_END);
return ftell(a0->file);
}
FORCE_ALIGN_ARG_POINTER
static int32_t CDECL fn_FileRead(FILE* a0, char* buffer, int32_t size)
static int32_t CDECL fn_FileRead(FileWrapper* a0, char* buffer, int32_t size)
{
Console::logVerbose("read %d bytes (%d)", size, fileno(a0));
size = fread(buffer, 1, size, a0);
Console::logVerbose("read %d bytes (%d)", size, fileno(a0->file));
size = fread(buffer, 1, size, a0->file);
return size;
}
@ -336,29 +337,27 @@ static void CDECL fn_FindClose(Session* data)
}
FORCE_ALIGN_ARG_POINTER
static void* CDECL fn_malloc(uint32_t size)
static uint32_t CDECL fn_malloc(uint32_t size)
{
return malloc(size);
void* pVoid = malloc(size);
Console::log("Allocated 0x%X bytes at 0x%" PRIXPTR, size, (uintptr_t)pVoid);
return (loco_ptr)pVoid;
}
FORCE_ALIGN_ARG_POINTER
static void* CDECL fn_realloc(void* block, uint32_t size)
static uint32_t CDECL fn_realloc(void* block, uint32_t size)
{
return realloc(block, size);
Console::log("Reallocated %" PRIXPTR " to 0x%X bytes", (uintptr_t)block, size);
return (loco_ptr)realloc(block, size);
}
#ifdef _NO_LOCO_WIN32_
FORCE_ALIGN_ARG_POINTER
static void CDECL fn_free(void* block)
{
return free(block);
}
#ifdef _NO_LOCO_WIN32_
static void STDCALL fn_dump(uint32_t address)
{
Console::log("Missing hook: 0x%x", address);
}
enum
{
DS_OK = 0,
@ -401,13 +400,15 @@ static bool STDCALL lib_DeleteFileA(char* lpFileName)
FORCE_ALIGN_ARG_POINTER
static bool STDCALL lib_WriteFile(
FILE* hFile,
FileWrapper* hFile,
char* buffer,
size_t nNumberOfBytesToWrite,
uint32_t* lpNumberOfBytesWritten,
uintptr_t lpOverlapped)
{
*lpNumberOfBytesWritten = fwrite(buffer, 1, nNumberOfBytesToWrite, hFile);
auto str = std::string(buffer, nNumberOfBytesToWrite);
size_t i = fwrite(buffer, 1, nNumberOfBytesToWrite, hFile->file);
*lpNumberOfBytesWritten = i;
Console::logVerbose("WriteFile(%s)", buffer);
return true;
@ -453,7 +454,10 @@ static int32_t STDCALL lib_CreateFileA(
return -1;
}
return (int32_t)pFILE;
auto wrapper = new FileWrapper;
wrapper->file = pFILE;
wrapper->name = lpFileName;
return (loco_ptr)wrapper;
}
FORCE_ALIGN_ARG_POINTER
@ -484,9 +488,9 @@ static void* STDCALL lib_CreateMutexA(uintptr_t lmMutexAttributes, bool bInitial
FORCE_ALIGN_ARG_POINTER
static bool STDCALL lib_CloseHandle(void* hObject)
{
auto file = (FILE*)hObject;
auto file = (FileWrapper*)hObject;
return fclose(file) == 0;
return fclose(file->file) == 0;
}
FORCE_ALIGN_ARG_POINTER
@ -504,9 +508,9 @@ static void registerMemoryHooks()
// Hook Locomotion's alloc / free routines so that we don't
// allocate a block in one module and freeing it in another.
writeJmp(0x4d1401, (void*)&fn_malloc);
writeJmp(0x4D1B28, (void*)&fn_realloc);
writeJmp(0x4D1355, (void*)&fn_free);
hookFunction(0x4d1401, CallingConvention::cdecl, 1, (void (*)()) & fn_malloc);
hookFunction(0x4D1B28, CallingConvention::cdecl, 2, (void (*)()) & fn_realloc);
hookFunction(0x4D1355, CallingConvention::cdecl, 1, (void (*)()) & fn_free);
}
#ifdef _NO_LOCO_WIN32_
@ -514,54 +518,53 @@ static void registerNoWin32Hooks()
{
using namespace OpenLoco::Interop;
writeJmp(0x40447f, (void*)&fn_40447f);
writeJmp(0x404b68, (void*)&fn_404b68);
writeJmp(0x404e8c, (void*)&getNumDSoundDevices);
writeJmp(0x4054b9, (void*)&fn_4054b9);
writeJmp(0x4064fa, (void*)&fn0);
writeJmp(0x4054a3, (void*)&fn_4054a3);
writeJmp(0x4072ec, (void*)&fn0);
writeJmp(0x4078be, (void*)&fn_4078be);
writeJmp(0x4078fe, (void*)&fn_4078fe);
writeJmp(0x407b26, (void*)&fn_407b26);
writeJmp(0x4080bb, (void*)&fn_4080bb);
writeJmp(0x408163, (void*)&fn_408163);
writeJmp(0x40817b, (void*)&fn_40817b);
writeJmp(0x4081ad, (void*)&fn_4081ad);
writeJmp(0x4081c5, (void*)&fn_FileSeekSet);
writeJmp(0x4081d8, (void*)&fn_FileSeekFromCurrent);
writeJmp(0x4081eb, (void*)&fn_FileSeekFromEnd);
writeJmp(0x4081fe, (void*)&fn_FileRead);
writeJmp(0x40830e, (void*)&fn_FindFirstFile);
writeJmp(0x40831d, (void*)&fn_FindNextFile);
writeJmp(0x40832c, (void*)&fn_FindClose);
writeJmp(0x4d0fac, (void*)&fn_DirectSoundEnumerateA);
hookFunction(0x404b68, CallingConvention::stdcall, 4, (void (*)()) & fn_404b68);
hookFunction(0x404e8c, CallingConvention::stdcall, 0, (void (*)()) & getNumDSoundDevices);
hookFunction(0x4064fa, CallingConvention::stdcall, 0, (void (*)()) & fn0);
hookFunction(0x40726d, CallingConvention::stdcall, 0, (void (*)()) & fn_40726d);
hookFunction(0x4054a3, CallingConvention::cdecl, 3, (void (*)()) & fn_4054a3);
hookFunction(0x4072ec, CallingConvention::stdcall, 0, (void (*)()) & fn0);
hookFunction(0x4078be, CallingConvention::stdcall, 0, (void (*)()) & fn_4078be);
hookFunction(0x4080bb, CallingConvention::cdecl, 2, (void (*)()) & fn_4080bb);
hookFunction(0x408163, CallingConvention::cdecl, 0, (void (*)()) & fn_408163);
hookFunction(0x40817b, CallingConvention::cdecl, 1, (void (*)()) & fn_40817b);
hookFunction(0x4081ad, CallingConvention::cdecl, 1, (void (*)()) & fn_4081ad);
hookFunction(0x4081c5, CallingConvention::cdecl, 2, (void (*)()) & fn_FileSeekSet);
hookFunction(0x4081d8, CallingConvention::cdecl, 2, (void (*)()) & fn_FileSeekFromCurrent);
hookFunction(0x4081eb, CallingConvention::cdecl, 2, (void (*)()) & fn_FileSeekFromEnd);
hookFunction(0x4081fe, CallingConvention::cdecl, 3, (void (*)()) & fn_FileRead);
hookFunction(0x40830e, CallingConvention::cdecl, 2, (void (*)()) & fn_FindFirstFile);
hookFunction(0x40831d, CallingConvention::cdecl, 2, (void (*)()) & fn_FindNextFile);
hookFunction(0x40832c, CallingConvention::cdecl, 1, (void (*)()) & fn_FindClose);
hookFunction(0x4d0fac, CallingConvention::stdcall, 2, (void (*)()) & fn_DirectSoundEnumerateA);
// fill DLL hooks for ease of debugging
for (int i = 0x4d7000; i <= 0x4d72d8; i += 4)
for (uint32_t address = 0x4d7000; address <= 0x4d72d8; address += 4)
{
hookDump(i, (void*)&fn_dump);
hookLibrary(address, [address]() {
Console::log("Missing hook: 0x%x", address);
});
}
// dsound.dll
hookLib(0x4d7024, (void*)&lib_DirectSoundCreate);
hookLibrary(0x4d7024, CallingConvention::stdcall, 3, (void (*)()) & lib_DirectSoundCreate);
// gdi32.dll
hookLib(0x4d7078, (void*)&lib_CreateRectRgn);
hookLibrary(0x4d7078, CallingConvention::stdcall, 4, (void (*)()) & lib_CreateRectRgn);
// kernel32.dll
hookLib(0x4d70e0, (void*)&lib_CreateMutexA);
hookLib(0x4d70e4, (void*)&lib_OpenMutexA);
hookLib(0x4d70f0, (void*)&lib_WriteFile);
hookLib(0x4d70f4, (void*)&lib_DeleteFileA);
hookLib(0x4d70f8, (void*)&lib_SetFileAttributesA);
hookLib(0x4d70fC, (void*)&lib_CreateFileA);
hookLibrary(0x4d70e0, CallingConvention::stdcall, 3, (void (*)()) & lib_CreateMutexA);
hookLibrary(0x4d70e4, CallingConvention::stdcall, 3, (void (*)()) & lib_OpenMutexA);
hookLibrary(0x4d70f0, CallingConvention::stdcall, 5, (void (*)()) & lib_WriteFile);
hookLibrary(0x4d70f4, CallingConvention::stdcall, 1, (void (*)()) & lib_DeleteFileA);
hookLibrary(0x4d70f8, CallingConvention::stdcall, 2, (void (*)()) & lib_SetFileAttributesA);
hookLibrary(0x4d70fC, CallingConvention::stdcall, 7, (void (*)()) & lib_CreateFileA);
// user32.dll
hookLib(0x4d71e8, (void*)&lib_PostQuitMessage);
hookLib(0x4d714c, (void*)&lib_CloseHandle);
hookLib(0x4d7248, (void*)&lib_GetUpdateRgn);
hookLib(0x4d72b0, (void*)&lib_timeGetTime);
hookLibrary(0x4d71e8, CallingConvention::stdcall, 1, (void (*)()) & lib_PostQuitMessage);
hookLibrary(0x4d714c, CallingConvention::stdcall, 1, (void (*)()) & lib_CloseHandle);
hookLibrary(0x4d7248, CallingConvention::stdcall, 3, (void (*)()) & lib_GetUpdateRgn);
hookLibrary(0x4d72b0, CallingConvention::stdcall, 0, (void (*)()) & lib_timeGetTime);
}
#endif // _NO_LOCO_WIN32_
@ -586,11 +589,11 @@ static void registerAudioHooks()
{
using namespace OpenLoco::Interop;
writeJmp(0x0040194E, (void*)&audioLoadChannel);
writeJmp(0x00401999, (void*)&audioPlayChannel);
writeJmp(0x00401A05, (void*)&audioStopChannel);
writeJmp(0x00401AD3, (void*)&audioSetChannelVolume);
writeJmp(0x00401B10, (void*)&audioIsChannelPlaying);
hookFunction(0x0040194E, CallingConvention::cdecl, 5, (void (*)()) & audioLoadChannel);
hookFunction(0x00401999, CallingConvention::cdecl, 5, (void (*)()) & audioPlayChannel);
hookFunction(0x00401A05, CallingConvention::cdecl, 5, (void (*)()) & audioStopChannel);
hookFunction(0x00401AD3, CallingConvention::cdecl, 2, (void (*)()) & audioSetChannelVolume);
hookFunction(0x00401B10, CallingConvention::cdecl, 1, (void (*)()) & audioIsChannelPlaying);
writeRet(0x0048AB36);
writeRet(0x00404B40);

View File

@ -1,7 +1,9 @@
#include "emu.h"
#include <algorithm>
#include <cinttypes>
#include <cstring>
#include <stdexcept>
#include <unicorn/unicorn.h>
#ifdef _WIN32
#ifndef NOMINMAX
@ -14,8 +16,12 @@
#include "../Console.h"
#include "Interop.hpp"
#define _LOG_INTEROP_CALLS_ 1
#pragma warning(disable : 4731) // frame pointer register 'ebp' modified by inline assembly code
#ifdef __i386__
#define PLATFORM_X86
#endif
#if defined(__GNUC__)
#ifdef __clang__
@ -86,7 +92,11 @@ namespace OpenLoco::Interop
);
// clang-format on
#endif
#endif // PLATFORM_X86
#else // PLATFORM_X86
emu::call(address, _eax, _ebx, _ecx, _edx, _esi, _edi, _ebp);
#endif
_originalAddress = 0;
// lahf only modifies ah, zero out the rest
return result & 0xFF00;
@ -96,7 +106,7 @@ namespace OpenLoco::Interop
static int32_t DISABLE_OPT callByRef(int32_t address, int32_t* _eax, int32_t* _ebx, int32_t* _ecx, int32_t* _edx, int32_t* _esi, int32_t* _edi, int32_t* _ebp)
{
#ifdef _LOG_INTEROP_CALLS_
OpenLoco::Console::group("0x%x", address);
OpenLoco::Console::group("call 0x%x", address);
#endif
int32_t result = 0;
_originalAddress = address;
@ -249,10 +259,14 @@ namespace OpenLoco::Interop
);
// clang-format on
#endif
#endif // PLATFORM_X86
#else // PLATFORM_X86
emu::call(address, _eax, _ebx, _ecx, _edx, _esi, _edi, _ebp);
#endif
_originalAddress = 0;
#ifdef _LOG_INTEROP_CALLS_
OpenLoco::Console::log("Returning %x", address);
OpenLoco::Console::groupEnd();
#endif
// lahf only modifies ah, zero out the rest
@ -303,7 +317,7 @@ namespace OpenLoco::Interop
}
#else
// We own the pages with PROT_WRITE | PROT_EXEC, we can simply just memcpy the data
std::memcpy(data, (void*)address, size);
std::memcpy(data, (void*)(uintptr_t)address, size);
#endif // _WIN32
}
@ -317,7 +331,7 @@ namespace OpenLoco::Interop
}
#else
// We own the pages with PROT_WRITE | PROT_EXEC, we can simply just memcpy the data
std::memcpy((void*)address, data, size);
std::memcpy((void*)(uintptr_t)address, data, size);
#endif // _WIN32
}

View File

@ -1,26 +1,19 @@
#pragma once
#include "../Utility/String.hpp"
#include "i386.h"
#include <cstddef>
#include <cstdint>
#include <cstdio>
#include <stdexcept>
#include <vector>
#if defined(__i386__)
#define assert_struct_size(x, y) static_assert(sizeof(x) == (y), "Improper struct size")
#else
#define assert_struct_size(x, y)
#endif
#if defined(__clang__) || (defined(__GNUC__) && !defined(__MINGW32__))
#define FORCE_ALIGN_ARG_POINTER __attribute__((force_align_arg_pointer))
#else
#define FORCE_ALIGN_ARG_POINTER
#endif
constexpr int32_t DEFAULT_REG_VAL = 0xCCCCCCCC;
namespace OpenLoco::Interop
{
template<typename T = void>
@ -51,71 +44,6 @@ namespace OpenLoco::Interop
};
};
#pragma pack(push, 1)
/**
* x86 register structure, only used for easy interop to Locomotion code.
*/
struct registers
{
union
{
int32_t eax{ DEFAULT_REG_VAL };
int16_t ax;
struct
{
int8_t al;
int8_t ah;
};
};
union
{
int32_t ebx{ DEFAULT_REG_VAL };
int16_t bx;
struct
{
int8_t bl;
int8_t bh;
};
};
union
{
int32_t ecx{ DEFAULT_REG_VAL };
int16_t cx;
struct
{
int8_t cl;
int8_t ch;
};
};
union
{
int32_t edx{ DEFAULT_REG_VAL };
int16_t dx;
struct
{
int8_t dl;
int8_t dh;
};
};
union
{
int32_t esi{ DEFAULT_REG_VAL };
int16_t si;
};
union
{
int32_t edi{ DEFAULT_REG_VAL };
int16_t di;
};
union
{
int32_t ebp{ DEFAULT_REG_VAL };
int16_t bp;
};
};
assert_struct_size(registers, 7 * 4);
#pragma pack(pop)
#ifndef USE_MMAP
constexpr uintptr_t GOOD_PLACE_FOR_DATA_SEGMENT = 0x008A4000;
#else
@ -405,17 +333,6 @@ namespace OpenLoco::Interop
void readMemory(uint32_t address, void* data, size_t size);
void writeMemory(uint32_t address, const void* data, size_t size);
using hook_function = uint8_t (*)(registers& regs);
void registerHook(uintptr_t address, hook_function function);
void writeRet(uint32_t address);
void writeJmp(uint32_t address, void* fn);
void writeNop(uint32_t address, size_t count);
void hookDump(uint32_t address, void* fn);
void hookLib(uint32_t address, void* fn);
void registerHooks();
void loadSections();
}
// these safe string function convenience overloads are located in this header, rather than in Utility/String.hpp,

View File

@ -0,0 +1,746 @@
#ifndef __i386__
#include "emu.h"
#include "../OpenLoco.h"
#include "i386.h"
#include <cstdio>
#include <unicorn/unicorn.h>
#include <unicorn/x86.h>
#include "../Console.h"
#include "Interop.hpp"
#include <cassert>
#include <cinttypes>
#include <map>
#include <sys/mman.h>
#ifdef __linux__
#include <sys/mman.h>
#endif
extern "C" {
#include <tinyalloc.h>
}
uint32_t heapStart = 0x20000000;
uint32_t heapSize = 1024 * 1024 * 512; // 16MiB
void* heap;
namespace OpenLoco::Interop
{
struct InteropFunction
{
void (*function)();
hook_function hookFunction;
CallingConvention convention;
size_t argumentCount;
std::function<void()> simple;
};
std::vector<InteropFunction> _newHooks;
static bool _log = false;
std::map<uint32_t, InteropFunction> _hooks;
std::map<uint32_t, InteropFunction> _libraryHooks;
constexpr uint32_t executionEnd = 0xfffff000;
void* hookMem;
uintptr_t hookMemCur;
const uint32_t hookMemStart = 0x10000000;
const uint32_t hookMemSize = 0x1000;
static const int softwareInterruptNumber = 0x80;
uintptr_t stackStart = 0xA0000000;
uint32_t stackSize = 16 * 1024 * 1024;
void* stack;
static uint32_t _esp;
void emu_init()
{
auto fh = fopen("/Users/hamster/Projects/OpenLoco/Wissel/cmake-build-x64/openloco_text", "r");
auto test = mmap((void*)(uintptr_t)0x401000, 0xD6000, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_FIXED, fileno(fh), 0);
if (test == MAP_FAILED)
exit(EXIT_FAILURE);
hookMem = malloc(0x1000);
hookMemCur = reinterpret_cast<uintptr_t>(hookMem);
stack = mmap((void*)stackStart, stackSize, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
OpenLoco::Console::log("allocated stack space from 0x%" PRIXPTR " to 0x%" PRIXPTR, (uintptr_t)stack, (uintptr_t)stack + stackSize);
_esp = stackStart + stackSize;
}
static void write_address_strictalias(uint8_t* data, uint32_t addr)
{
*(data + 0) = ((addr)&0x000000ff) >> 0;
*(data + 1) = ((addr)&0x0000ff00) >> 8;
*(data + 2) = ((addr)&0x00ff0000) >> 16;
*(data + 3) = ((addr)&0xff000000) >> 24;
}
// Callback for tracing all kinds of memory errors
static void UcErrorHook(uc_engine* uc, uc_mem_type type, uint64_t address, int size, int64_t value, void* user_data)
{
/*
FIXME: type is one of
UC_MEM_READ_UNMAPPED = 19, // Unmapped memory is read from
UC_MEM_WRITE_UNMAPPED = 20, // Unmapped memory is written to
UC_MEM_FETCH_UNMAPPED = 21, // Unmapped memory is fetched
UC_MEM_WRITE_PROT = 22, // Write to write protected, but mapped, memory
UC_MEM_READ_PROT = 23, // Read from read protected, but mapped, memory
UC_MEM_FETCH_PROT = 24, // Fetch from non-executable, but mapped, memory
*/
OpenLoco::Console::log("Unicorn-Engine error of type %d at 0x%" PRIx64 ", size = 0x%" PRIX32 "\n", type, address, size);
std::byte buffer[8] = {};
uc_mem_read(uc, 0x50B864, buffer, 8);
uc_emu_stop(uc);
// ThreadContext ctx;
// TransferContext(&ctx, false);
// PrintContext(&ctx);
std::map<int, std::string> data = {
{ UC_X86_REG_EIP, "EIP" },
{ UC_X86_REG_EAX, "EAX" },
{ UC_X86_REG_EBX, "EBX" },
{ UC_X86_REG_ECX, "ECX" },
{ UC_X86_REG_EDX, "EDX" },
{ UC_X86_REG_EDI, "EDI" },
{ UC_X86_REG_ESI, "ESI" },
{ UC_X86_REG_EBP, "EBP" },
{ UC_X86_REG_ESP, "ESP" },
};
for (auto const& x : data)
{
int eip;
auto succ = uc_reg_read(uc, x.first, &eip);
printf(" %s: %X (%d)\n", x.second.c_str(), eip, succ);
}
uint32_t esp;
uc_reg_read(uc, UC_X86_REG_ESP, &esp);
for (int i = -32; i < 32; i++)
{
uint32_t val;
uintptr_t addr = esp - i * 4;
auto succ = uc_mem_read(uc, addr, &val, sizeof val);
if (addr >= stackStart + stackSize)
continue;
;
if (addr < stackStart)
continue;
printf("Stack [%X] = %X (%d) %X\n", addr, val, succ, *((uint32_t*)addr));
}
uc_mem_region* regions;
uint32_t regionCount;
auto err = uc_mem_regions(uc, &regions, &regionCount);
for (int i = 0; i < regionCount; i++)
{
printf("%08X | %08X | ", regions[i].begin, regions[i].end);
if (regions[i].perms & UC_PROT_READ)
{
printf("R");
}
else
{
printf(" ");
}
if (regions[i].perms & UC_PROT_WRITE)
{
printf("W");
}
else
{
printf(" ");
}
if (regions[i].perms & UC_PROT_EXEC)
{
printf("X");
}
else
{
printf(" ");
}
printf("\n");
}
assert(false);
}
uint64_t lastEIP;
static void hook_code(uc_engine* uc, uint64_t address, uint32_t size, void* user_data)
{
lastEIP = address;
OpenLoco::Console::log("* %p\n", (void*)address);
}
void registerHook(uintptr_t address, hook_function function)
{
auto newIndex = _newHooks.size();
InteropFunction item = {};
item.convention = CallingConvention::sawyer;
item.hookFunction = function;
_newHooks.push_back(item);
uint8_t assembly[8] = {};
// push imm32
assembly[0] = 0x68;
write_address_strictalias(&assembly[1], newIndex);
// int imm8
assembly[5] = 0xCD;
assembly[6] = softwareInterruptNumber;
// ret
assembly[7] = 0xC3;
writeMemory(address, assembly, sizeof(assembly));
}
void writeRet(uint32_t address) {}
void writeJmp(uint32_t address, void* fn) {}
void writeNop(uint32_t address, size_t count) {}
void hookDump(uint32_t address, void* fn) {}
void hookLibrary(uint32_t address, CallingConvention convention, size_t arguments, void (*fn)())
{
auto newIndex = _newHooks.size();
InteropFunction item = {};
item.convention = convention;
item.argumentCount = arguments;
item.function = fn;
_newHooks.push_back(item);
uint8_t assembly[8] = {};
// push imm32
assembly[0] = 0x68;
write_address_strictalias(&assembly[1], newIndex);
// int imm8
assembly[5] = 0xCD;
assembly[6] = softwareInterruptNumber;
// ret
assembly[7] = 0xC3;
uint32_t data = hookMemStart + (hookMemCur - reinterpret_cast<uintptr_t>(hookMem));
writeMemory(address, &data, sizeof(data));
memcpy((void*)hookMemCur, assembly, sizeof(assembly));
hookMemCur += sizeof(assembly);
}
void hookLibrary(uint32_t address, std::function<void()> fn)
{
auto newIndex = _newHooks.size();
InteropFunction item = {};
item.simple = fn;
_newHooks.push_back(item);
uint8_t assembly[8] = {};
// push imm32
assembly[0] = 0x68;
write_address_strictalias(&assembly[1], newIndex);
// int imm8
assembly[5] = 0xCD;
assembly[6] = softwareInterruptNumber;
// ret
assembly[7] = 0xC3;
uint32_t data = hookMemStart + (hookMemCur - reinterpret_cast<uintptr_t>(hookMem));
writeMemory(address, &data, sizeof(data));
memcpy((void*)hookMemCur, assembly, sizeof(assembly));
hookMemCur += sizeof(assembly);
}
void hookFunction(uint32_t address, CallingConvention convention, size_t arguments, void (*fn)())
{
auto newIndex = _newHooks.size();
InteropFunction item = {};
item.convention = convention;
item.argumentCount = arguments;
item.function = fn;
_newHooks.push_back(item);
uint8_t assembly[8] = {};
// push imm32
assembly[0] = 0x68;
write_address_strictalias(&assembly[1], newIndex);
// int imm8
assembly[5] = 0xCD;
assembly[6] = softwareInterruptNumber;
// ret
assembly[7] = 0xC3;
writeMemory(address, assembly, sizeof(assembly));
}
static uint32_t pop(uc_engine* uc)
{
uint32_t esp;
uc_reg_read(uc, UC_X86_REG_ESP, &esp);
uint32_t value;
uc_mem_read(uc, esp, &value, 4);
esp += 4;
uc_reg_write(uc, UC_X86_REG_ESP, &esp);
return value;
}
static uint32_t push(uc_engine* uc, uint32_t value)
{
uint32_t esp;
uc_reg_read(uc, UC_X86_REG_ESP, &esp);
esp -= 4;
uc_reg_write(uc, UC_X86_REG_ESP, &esp);
uc_mem_write(uc, esp, &value, 4);
return value;
}
static void hook_interrupt(uc_engine* uc, uint32_t intno, void* user_data)
{
if (intno != softwareInterruptNumber)
return;
uint32_t index = pop(uc);
Console::log("Interupt 0x%X (%d)", intno, index);
auto& hook = _newHooks[index];
if (hook.convention == CallingConvention::sawyer)
{
uint32_t backupEsp = _esp;
uc_reg_read(uc, UC_X86_REG_ESP, &_esp);
registers regs = {};
uc_reg_read(uc, UC_X86_REG_EAX, &regs.eax);
uc_reg_read(uc, UC_X86_REG_EBX, &regs.ebx);
uc_reg_read(uc, UC_X86_REG_ECX, &regs.ecx);
uc_reg_read(uc, UC_X86_REG_EDX, &regs.edx);
uc_reg_read(uc, UC_X86_REG_ESI, &regs.esi);
uc_reg_read(uc, UC_X86_REG_EDI, &regs.edi);
uc_reg_read(uc, UC_X86_REG_EBP, &regs.ebp);
hook.hookFunction(regs);
uc_reg_write(uc, UC_X86_REG_EAX, &regs.eax);
uc_reg_write(uc, UC_X86_REG_EBX, &regs.ebx);
uc_reg_write(uc, UC_X86_REG_ECX, &regs.ecx);
uc_reg_write(uc, UC_X86_REG_EDX, &regs.edx);
uc_reg_write(uc, UC_X86_REG_ESI, &regs.esi);
uc_reg_write(uc, UC_X86_REG_EDI, &regs.edi);
uc_reg_write(uc, UC_X86_REG_EBP, &regs.ebp);
_esp = backupEsp;
return;
}
if (hook.simple != nullptr)
{
hook.simple();
return;
}
uint32_t retaddr = pop(uc);
uint32_t retVal;
switch (hook.argumentCount)
{
case 0:
retVal = ((uint32_t(*)())hook.function)();
break;
case 1:
{
uint32_t arg1 = pop(uc);
retVal = ((uint32_t(*)(uint32_t))hook.function)(arg1);
break;
}
case 2:
{
uint32_t arg1 = pop(uc);
uint32_t arg2 = pop(uc);
retVal = ((uint32_t(*)(uint32_t, uint32_t))hook.function)(arg1, arg2);
break;
}
case 3:
{
uint32_t arg1 = pop(uc);
uint32_t arg2 = pop(uc);
uint32_t arg3 = pop(uc);
retVal = ((uint32_t(*)(uint32_t, uint32_t, uint32_t))hook.function)(arg1, arg2, arg3);
break;
}
case 5:
{
uint32_t arg1 = pop(uc);
uint32_t arg2 = pop(uc);
uint32_t arg3 = pop(uc);
uint32_t arg4 = pop(uc);
uint32_t arg5 = pop(uc);
retVal = ((uint32_t(*)(uint32_t, uint32_t, uint32_t, uint32_t, uint32_t))hook.function)(arg1, arg2, arg3, arg4, arg5);
break;
}
case 7:
{
uint32_t arg1 = pop(uc);
uint32_t arg2 = pop(uc);
uint32_t arg3 = pop(uc);
uint32_t arg4 = pop(uc);
uint32_t arg5 = pop(uc);
uint32_t arg6 = pop(uc);
uint32_t arg7 = pop(uc);
retVal = ((uint32_t(*)(uint32_t, uint32_t, uint32_t, uint32_t, uint32_t, uint32_t, uint32_t))hook.function)(arg1, arg2, arg3, arg4, arg5, arg6, arg7);
break;
}
default:
assert(0);
break;
}
if (hook.convention == CallingConvention::cdecl)
{
for (size_t i = 0; i < hook.argumentCount; i++)
{
push(uc, 0xFFFFFFFF);
}
}
uc_reg_write(uc, UC_X86_REG_EAX, &retVal);
push(uc, retaddr);
}
namespace emu
{
static uc_engine* initialise()
{
uc_engine* uc = nullptr;
uc_err err;
err = uc_open(UC_ARCH_X86, UC_MODE_32, &uc);
if (err)
{
throw std::runtime_error("Failed on uc_open() with error returned " + std::to_string(err) + ": " + std::string(uc_strerror(err)));
}
// Add hooks to catch errors
uc_hook errorHooks[2];
{
// Hook for memory access on unmapped memory
uc_hook_add(uc, &errorHooks[0], UC_HOOK_MEM_UNMAPPED, (void*)UcErrorHook, nullptr, 0, -1);
// Hook for memory access on protected memory
uc_hook_add(uc, &errorHooks[1], UC_HOOK_MEM_PROT, (void*)UcErrorHook, nullptr, 0, -1);
}
if (_log)
{
uc_hook hook2;
uc_hook_add(uc, &hook2, UC_HOOK_CODE, (void*)hook_code, NULL, 0, -1);
}
uc_hook hook1;
uc_hook_add(uc, &hook1, UC_HOOK_INTR, (void*)hook_interrupt, NULL, 0, -1);
uc_mem_map_ptr(uc, 0x2000000, 0x1000000, UC_PROT_READ | UC_PROT_EXEC, (void*)0x2000000);
uc_mem_map_ptr(uc, 0x401000, 0x4d7000 - 0x401000, UC_PROT_READ | UC_PROT_EXEC, (void*)0x401000);
uc_mem_map_ptr(uc, 0x4d7000, 0x1162000 - 0x4d7000, UC_PROT_READ | UC_PROT_WRITE, (void*)0x4d7000);
uc_mem_map_ptr(uc, (uintptr_t)heap, heapSize, UC_PROT_READ | UC_PROT_WRITE, heap);
uc_mem_map_ptr(uc, hookMemStart, hookMemSize, UC_PROT_READ | UC_PROT_EXEC, hookMem);
uc_mem_map_ptr(uc, stackStart, stackSize, UC_PROT_READ | UC_PROT_WRITE, (void*)stack);
uc_mem_map(uc, executionEnd, 0x1000, UC_PROT_ALL);
return uc;
}
static int depth = 0;
void log()
{
_log = true;
}
void call(uint32_t address, int32_t* _eax, int32_t* _ebx, int32_t* _ecx, int32_t* _edx, int32_t* _esi, int32_t* _edi, int32_t* _ebp)
{
auto uc = initialise();
if (false)
{
uc_hook hook1;
uc_hook_add(uc, &hook1, UC_HOOK_CODE, (void*)hook_code, NULL, 0, -1);
}
depth++;
uint32_t espStart = _esp;
uc_reg_write(uc, UC_X86_REG_ESP, &espStart);
uc_reg_write(uc, UC_X86_REG_EAX, _eax);
uc_reg_write(uc, UC_X86_REG_EBX, _ebx);
uc_reg_write(uc, UC_X86_REG_ECX, _ecx);
uc_reg_write(uc, UC_X86_REG_EDX, _edx);
uc_reg_write(uc, UC_X86_REG_ESI, _esi);
uc_reg_write(uc, UC_X86_REG_EDI, _edi);
uc_reg_write(uc, UC_X86_REG_EBP, _ebp);
// Push return address to stack
push(uc, executionEnd);
auto err = uc_emu_start(uc, address, executionEnd, 0, 0);
if (err)
{
throw std::runtime_error("Failed on uc_emu_start() with error returned " + std::to_string(err) + ": " + std::string(uc_strerror(err)));
}
uint32_t espEnd;
uc_reg_read(uc, UC_X86_REG_ESP, &espEnd);
assert(espEnd == espStart);
uc_reg_read(uc, UC_X86_REG_EAX, _eax);
uc_reg_read(uc, UC_X86_REG_EBX, _ebx);
uc_reg_read(uc, UC_X86_REG_ECX, _ecx);
uc_reg_read(uc, UC_X86_REG_EDX, _edx);
uc_reg_read(uc, UC_X86_REG_ESI, _esi);
uc_reg_read(uc, UC_X86_REG_EDI, _edi);
uc_reg_read(uc, UC_X86_REG_EBP, _ebp);
uc_close(uc);
depth--;
}
}
}
/*
extern "C" {
int has_hook(void* emu)
{
uint32_t address =0;
auto it = _hooks.find(address);
if (it == _hooks.end())
{
return false;
}
printf(" hook @ 0x%X\n", address);
uint32_t retaddr = pop_long(emu);
uint32_t retVal;
switch (it->second.argumentCount)
{
case 0:
retVal = ((uint32_t(*)())it->second.function)();
break;
case 1:
{
uint32_t arg1 = pop_long(emu);
retVal = ((uint32_t(*)(uint32_t))it->second.function)(arg1);
break;
}
case 2:
{
uint32_t arg1 = pop_long(emu);
uint32_t arg2 = pop_long(emu);
retVal = ((uint32_t(*)(uint32_t, uint32_t))it->second.function)(arg1, arg2);
break;
}
case 3:
{
uint32_t arg1 = pop_long(emu);
uint32_t arg2 = pop_long(emu);
uint32_t arg3 = pop_long(emu);
retVal = ((uint32_t(*)(uint32_t, uint32_t, uint32_t))it->second.function)(arg1, arg2, arg3);
break;
}
default:
assert(0);
break;
}
if (it->second.convention == openloco::interop::CallingConvention::cdecl)
{
for (int i = 0; i < it->second.argumentCount; i++)
{
push_long(emu, 0xFFFFFF);
}
}
emu->x86.R_EAX = retVal;
emu->x86.R_EIP = retaddr;
return 1;
}
int has_lib_hook(x86emu_t* emu, uint32_t addr)
{
auto it = _libraryHooks.find(addr);
if (it == _libraryHooks.end())
{
return false;
}
printf(" hook @ 0x%X\n", addr);
uint32_t retVal;
switch (it->second.argumentCount)
{
case 0:
retVal = ((uint32_t(*)())it->second.function)();
break;
case 1:
{
uint32_t arg1 = pop_long(emu);
retVal = ((uint32_t(*)(uint32_t))it->second.function)(arg1);
break;
}
case 7:
{
uint32_t arg1 = pop_long(emu);
uint32_t arg2 = pop_long(emu);
uint32_t arg3 = pop_long(emu);
uint32_t arg4 = pop_long(emu);
uint32_t arg5 = pop_long(emu);
uint32_t arg6 = pop_long(emu);
uint32_t arg7 = pop_long(emu);
retVal = ((uint32_t(*)(uint32_t, uint32_t, uint32_t, uint32_t, uint32_t, uint32_t, uint32_t))it->second.function)(arg1, arg2, arg3, arg4, arg5, arg6, arg7);
break;
}
default:
assert(0);
break;
}
if (it->second.convention == openloco::interop::CallingConvention::cdecl)
{
for (int i = 0; i < it->second.argumentCount; i++)
{
push_long(emu, 0xFFFFFF);
}
}
emu->x86.R_EAX = retVal;
return 1;
}
}*/
//void * operator new(std::size_t n) noexcept(false)
//{
// return malloc(n);
//}
//void operator delete(void * p) throw()
//{
// free(p);
//}
static bool initialized = false;
void init()
{
heap = mmap((void*)heapStart, heapSize, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED, -1, 0);
if (heap == MAP_FAILED)
exit(EXIT_FAILURE);
auto heapEnd = (void*)(heapStart + heapSize);
auto success = ta_init(heap, heapEnd, 1024 * 1024, 16, 32);
if (!success)
exit(EXIT_FAILURE);
OpenLoco::Console::log("Initialized heap from %p to %p\n", heapStart, heapEnd);
initialized = true;
}
extern "C" void* malloc(size_t);
extern "C" void* calloc(size_t, size_t);
extern "C" void free(void*);
extern "C" void* realloc(void* ptr, size_t size);
void* malloc(size_t size)
{
if (!initialized)
init();
return ta_alloc(size);
}
void* calloc(size_t nitems, size_t size)
{
if (!initialized)
init();
return malloc(size * nitems);
}
void free(void* ptr)
{
if (!initialized)
init();
ta_free(ptr);
}
void* realloc(void* ptr, size_t size)
{
if (!initialized)
init();
ta_free(ptr);
return ta_alloc(size);
}
void* operator new(size_t count)
{
void* pVoid = malloc(count);
return pVoid;
}
void operator delete(void* p) noexcept
{
free(p);
}
#endif

View File

@ -0,0 +1,7 @@
#include <cstdint>
namespace OpenLoco::Interop::emu
{
void log();
void call(uint32_t address, int32_t* _eax, int32_t* _ebx, int32_t* _ecx, int32_t* _edx, int32_t* _esi, int32_t* _edi, int32_t* _ebp);
}

106
src/OpenLoco/Interop/i386.h Normal file
View File

@ -0,0 +1,106 @@
#pragma once
#include <cstddef>
#include <cstdint>
#include <functional>
#include <unicorn/unicorn.h>
//#if defined(__i386__)
#define assert_struct_size(x, y) static_assert(sizeof(x) == (y), "Improper struct size")
//#else
//#define assert_struct_size(x, y)
//#endif
constexpr int32_t DEFAULT_REG_VAL = 0xCCCCCCCC;
namespace OpenLoco::Interop
{
#pragma pack(push, 1)
/**
* x86 register structure, only used for easy interop to Locomotion code.
*/
struct registers
{
union
{
int32_t eax{ DEFAULT_REG_VAL };
int16_t ax;
struct
{
int8_t al;
int8_t ah;
};
};
union
{
int32_t ebx{ DEFAULT_REG_VAL };
int16_t bx;
struct
{
int8_t bl;
int8_t bh;
};
};
union
{
int32_t ecx{ DEFAULT_REG_VAL };
int16_t cx;
struct
{
int8_t cl;
int8_t ch;
};
};
union
{
int32_t edx{ DEFAULT_REG_VAL };
int16_t dx;
struct
{
int8_t dl;
int8_t dh;
};
};
union
{
int32_t esi{ DEFAULT_REG_VAL };
int16_t si;
};
union
{
int32_t edi{ DEFAULT_REG_VAL };
int16_t di;
};
union
{
int32_t ebp{ DEFAULT_REG_VAL };
int16_t bp;
};
};
assert_struct_size(registers, 7 * 4);
#pragma pack(pop)
using hook_function = uint8_t (*)(registers& regs);
void registerHook(uintptr_t address, hook_function function);
void writeRet(uint32_t address);
void writeJmp(uint32_t address, void* fn);
void writeNop(uint32_t address, size_t count);
void hookDump(uint32_t address, void* fn);
void hookLib(uint32_t address, void* fn);
void emu_init();
void registerHooks();
void loadSections();
enum CallingConvention
{
cdecl,
stdcall,
sawyer,
};
void hookFunction(uint32_t address, CallingConvention convention, size_t arguments, void (*)());
void hookLibrary(uint32_t address, CallingConvention convention, size_t arguments, void (*)());
void hookLibrary(uint32_t address, std::function<void()>);
}

View File

@ -1,5 +1,8 @@
#include "Intro.h"
#include "Gui.h"
#include "Interop/Interop.hpp"
#include "Station.h"
#include "Title.h"
using namespace OpenLoco::Interop;
@ -22,9 +25,57 @@ namespace OpenLoco::Intro
_state = (uint8_t)state;
}
static bool _50C196 = false;
static int _50C190 = 0;
// 0x0046AE0C
void update()
{
call(0x0046AE0C);
addr<0x0005252E0, int32_t>() = 1;
switch (state())
{
case State::none:
case State::begin:
case State::state_8:
case State::state_9:
// TODO: implement
break;
case State::end:
Gfx::clear(Gfx::screenContext(), 0xA0B0C0D0);
if (_50C196)
{
_50C196 = false;
}
state(State::done);
_50C190 = 0;
break;
case State::done:
state(State::none);
call(0x004523F4);
addr<0x0005252E0, int32_t>() = 0;
Gfx::invalidateScreen();
initialiseViewports();
Gui::init();
Title::reset();
break;
}
OpenLoco::sub_431695(0);
return;
//
// return;
// switch (state())
// {
//
// case State::none: break;
// case State::begin: break;
// case State::state_8: break;
// case State::state_9: break;
// case State::end: break;
// }
// call(0x0046AE0C);
}
}

View File

@ -9,6 +9,7 @@ namespace OpenLoco::Intro
state_8 = 8,
state_9 = 9,
end = 254,
done = 255,
};
bool isActive();

View File

@ -28,7 +28,7 @@ namespace OpenLoco::StringManager
const uint16_t TOWN_NAMES_START = 0x9EE7;
const uint16_t TOWN_NAMES_END = TOWN_NAMES_START + NUM_TOWN_NAMES;
static loco_global<char* [0xFFFF], 0x005183FC> _strings;
static loco_global<loco_ptr2<char>[0xFFFF], 0x005183FC> _strings;
static loco_global<char[NUM_USER_STRINGS][USER_STRING_SIZE], 0x0095885C> _userStrings;
static std::map<int32_t, string_id> day_to_string = {
@ -91,7 +91,7 @@ namespace OpenLoco::StringManager
const char* getString(string_id id)
{
char* str = _strings[id];
char* str = _strings[id].get();
return str;
}
@ -550,6 +550,7 @@ namespace OpenLoco::StringManager
const char* sourceStr = getString(id);
if (sourceStr == nullptr)
{
return buffer;
throw std::runtime_error("Got a nullptr for string id " + std::to_string(id) + " -- cowardly refusing");
}

View File

@ -46,7 +46,7 @@ namespace OpenLoco::Map
}
else
{
auto* unkVariation = buildingObj->variationsArr10[variation()];
auto* unkVariation = (uint8_t*)(uintptr_t)buildingObj->variationsArr10[variation()];
if (unkVariation[age() + 1] != 0xFF)
{
newUnk5u = 0;
@ -57,7 +57,8 @@ namespace OpenLoco::Map
auto totalHeight = 3;
for (; *unkVariation != 0xFF; unkVariation++)
{
totalHeight += buildingObj->varationHeights[*unkVariation];
uint8_t* vars = (uint8_t*)(uintptr_t)buildingObj->varationHeights;
totalHeight += vars[*unkVariation];
}
Ui::ViewportManager::invalidate(loc, baseZ() * 4, clearZ() * 4, ZoomLevel::quarter);

View File

@ -8,11 +8,33 @@
using namespace OpenLoco::Interop;
template<typename T>
class Pointer
{
private:
uint32_t _ptr;
public:
Pointer(void* ptr)
{
_ptr = (uint32_t)(uintptr_t)ptr;
}
operator T*() const { return (T*)(uintptr_t)_ptr; }
friend bool operator==(Pointer<T>& lhs, void* rhs)
{
return lhs._ptr == (uint32_t)(uintptr_t)rhs;
}
};
assert_struct_size(Pointer<int>, 4);
namespace OpenLoco::Map::TileManager
{
static loco_global<TileElement*, 0x005230C8> _elements;
static loco_global<TileElement* [0x30004], 0x00E40134> _tiles;
static loco_global<TileElement*, 0x00F00134> _elementsEnd;
static loco_global<Pointer<TileElement>, 0x005230C8> _elements;
static loco_global<Pointer<TileElement>[0x30004], 0x00E40134> _tiles;
static loco_global<Pointer<TileElement>, 0x00F00134> _elementsEnd;
static loco_global<coord_t, 0x00F24486> _mapSelectionAX;
static loco_global<coord_t, 0x00F24488> _mapSelectionBX;
static loco_global<coord_t, 0x00F2448A> _mapSelectionAY;
@ -34,7 +56,7 @@ namespace OpenLoco::Map::TileManager
stdx::span<TileElement> getElements()
{
return stdx::span<TileElement>(_elements, getElementsEnd());
return stdx::span<TileElement>((TileElement*)*_elements, getElementsEnd());
}
void setMapSelectionArea(const Pos2& locA, const Pos2& locB)
@ -62,12 +84,12 @@ namespace OpenLoco::Map::TileManager
TileElement* getElementsEnd()
{
return _elementsEnd;
return *_elementsEnd;
}
void setElements(stdx::span<TileElement> elements)
{
TileElement* dst = _elements;
TileElement* dst = *_elements;
std::memset(dst, 0, maxElements * sizeof(TileElement));
std::memcpy(dst, elements.data(), elements.size_bytes());
TileManager::updateTilePointers();
@ -82,13 +104,13 @@ namespace OpenLoco::Map::TileManager
TileElement** getElementIndex()
{
return _tiles.get();
return (TileElement**)_tiles.get();
}
Tile get(TilePos2 pos)
{
size_t index = (pos.y << 9) | pos.x;
auto data = _tiles[index];
auto data = (TileElement*)_tiles[index];
if (data == InvalidTile)
{
data = nullptr;
@ -273,7 +295,7 @@ namespace OpenLoco::Map::TileManager
{
clearTilePointers();
TileElement* el = _elements;
TileElement* el = *_elements;
for (tile_coord_t y = 0; y < map_rows; y++)
{
for (tile_coord_t x = 0; x < map_columns; x++)
@ -317,11 +339,11 @@ namespace OpenLoco::Map::TileManager
}
// Copy organised elements back to original element buffer
std::memcpy(_elements, tempBuffer.data(), numElements * sizeof(TileElement));
std::memcpy(*_elements, tempBuffer.data(), numElements * sizeof(TileElement));
// Zero all unused elements
auto remainingElements = maxElements - numElements;
std::memset(_elements + numElements, 0, remainingElements * sizeof(TileElement));
std::memset(*_elements + numElements, 0, remainingElements * sizeof(TileElement));
updateTilePointers();

View File

@ -1,6 +1,7 @@
#pragma once
#include "../Graphics/Colour.h"
#include "../Interop/Interop.hpp"
#include "../Localisation/StringManager.h"
namespace OpenLoco
@ -24,9 +25,9 @@ namespace OpenLoco
string_id name;
uint8_t pad_02[0x07 - 0x02];
uint8_t numVariations; //0x7
uint8_t* varationHeights; // 0x8
uint32_t varationHeights; // 0x8
uint8_t pad_0C[0x10 - 0x0C];
uint8_t* variationsArr10[32]; // 0x10
uint32_t variationsArr10[32]; // 0x10
uint32_t colours; // 0x90
uint16_t designedYear; // 0x94
uint16_t obsoleteYear; // 0x96
@ -45,5 +46,5 @@ namespace OpenLoco
void drawDescription(Gfx::Context& context, const int16_t x, const int16_t y, [[maybe_unused]] const int16_t width) const;
};
#pragma pack(pop)
static_assert(sizeof(BuildingObject) == 0xAE);
assert_struct_size(BuildingObject, 0xAE);
}

View File

@ -27,53 +27,75 @@ namespace OpenLoco::ObjectManager
{
}
};
static_assert(sizeof(ObjectEntry2) == 0x14);
assert_struct_size(ObjectEntry2, 0x14);
struct ObjectRepositoryItem
{
Object** objects;
ObjectEntry2* object_entry_extendeds;
loco_ptr2<loco_ptr2<Object>> objects;
loco_ptr2<ObjectEntry2> object_entry_extendeds;
};
assert_struct_size(ObjectRepositoryItem, 8);
#pragma pack(pop)
template<typename T>
class Pointer
{
private:
uint32_t _ptr;
public:
Pointer(void* ptr)
{
_ptr = (uint32_t)(uintptr_t)ptr;
}
operator T*() const { return (T*)(uintptr_t)_ptr; }
friend bool operator==(Pointer<T>& lhs, void* rhs)
{
return lhs._ptr == (uint32_t)(uintptr_t)rhs;
}
};
assert_struct_size(Pointer<int>, 4);
loco_global<ObjectEntry2[maxObjects], 0x1125A90> objectEntries;
loco_global<ObjectRepositoryItem[64], 0x4FE0B8> object_repository;
loco_global<Object* [maxObjects], 0x0050C3D0> _allObjects;
loco_global<InterfaceSkinObject* [1], 0x0050C3D0> _interfaceObjects;
loco_global<SoundObject* [128], 0x0050C3D4> _soundObjects;
loco_global<CurrencyObject* [1], 0x0050C5D4> _currencyObjects;
loco_global<SteamObject* [32], 0x0050C5D8> _steamObjects;
loco_global<RockObject* [8], 0x0050C658> _rockObjects;
loco_global<WaterObject* [1], 0x0050C678> _waterObjects;
loco_global<LandObject* [32], 0x0050C67C> _landObjects;
loco_global<TownNamesObject* [1], 0x0050C6FC> _townNamesObjects;
loco_global<CargoObject* [32], 0x0050C700> _cargoObjects;
loco_global<WallObject* [32], 0x0050C780> _wallObjects;
loco_global<TrainSignalObject* [16], 0x0050C800> _trainSignalObjects;
loco_global<LevelCrossingObject* [4], 0x0050C840> _levelCrossingObjects;
loco_global<StreetLightObject* [1], 0x0050C850> _streetLightObjects;
loco_global<TunnelObject* [16], 0x0050C854> _tunnelObjects;
loco_global<BridgeObject* [8], 0x0050C894> _bridgeObjects;
loco_global<TrainStationObject* [16], 0x0050C8B4> _trainStationObjects;
loco_global<TrackExtraObject* [8], 0x0050C8F4> _trackExtraObjects;
loco_global<TrackObject* [8], 0x0050C914> _trackObjects;
loco_global<RoadStationObject* [16], 0x0050C934> _roadStationObjects;
loco_global<RoadExtraObject* [4], 0x0050C974> _roadExtraObjects;
loco_global<RoadObject* [8], 0x0050C984> _roadObjects;
loco_global<AirportObject* [8], 0x0050C9A4> _airportObjects;
loco_global<DockObject* [8], 0x0050C9C4> _dockObjects;
loco_global<VehicleObject* [224], 0x0050C9E4> _vehicleObjects;
loco_global<TreeObject* [64], 0x0050CD64> _treeObjects;
loco_global<SnowObject* [1], 0x0050CE64> _snowObjects;
loco_global<ClimateObject* [1], 0x0050CE68> _climateObjects;
loco_global<HillShapesObject* [1], 0x0050CE6C> _hillShapeObjects;
loco_global<BuildingObject* [128], 0x0050CE70> _buildingObjects;
loco_global<ScaffoldingObject* [1], 0x0050D070> _scaffoldingObjects;
loco_global<IndustryObject* [16], 0x0050D074> _industryObjects;
loco_global<RegionObject* [1], 0x0050D0B4> _regionObjects;
loco_global<CompetitorObject* [32], 0x0050D0B8> _competitorObjects;
loco_global<ScenarioTextObject* [1], 0x0050D138> _scenarioTextObjects;
loco_global<Pointer<Object>[maxObjects], 0x0050C3D0> _allObjects;
loco_global<Pointer<InterfaceSkinObject>[1], 0x0050C3D0> _interfaceObjects;
loco_global<Pointer<SoundObject>[128], 0x0050C3D4> _soundObjects;
loco_global<Pointer<CurrencyObject>[1], 0x0050C5D4> _currencyObjects;
loco_global<Pointer<SteamObject>[32], 0x0050C5D8> _steamObjects;
loco_global<Pointer<RockObject>[8], 0x0050C658> _rockObjects;
loco_global<Pointer<WaterObject>[1], 0x0050C678> _waterObjects;
loco_global<Pointer<LandObject>[32], 0x0050C67C> _landObjects;
loco_global<Pointer<TownNamesObject>[1], 0x0050C6FC> _townNamesObjects;
loco_global<Pointer<CargoObject>[32], 0x0050C700> _cargoObjects;
loco_global<Pointer<WallObject>[32], 0x0050C780> _wallObjects;
loco_global<Pointer<TrainSignalObject>[16], 0x0050C800> _trainSignalObjects;
loco_global<Pointer<LevelCrossingObject>[4], 0x0050C840> _levelCrossingObjects;
loco_global<Pointer<StreetLightObject>[1], 0x0050C850> _streetLightObjects;
loco_global<Pointer<TunnelObject>[16], 0x0050C854> _tunnelObjects;
loco_global<Pointer<BridgeObject>[8], 0x0050C894> _bridgeObjects;
loco_global<Pointer<TrainStationObject>[16], 0x0050C8B4> _trainStationObjects;
loco_global<Pointer<TrackExtraObject>[8], 0x0050C8F4> _trackExtraObjects;
loco_global<Pointer<TrackObject>[8], 0x0050C914> _trackObjects;
loco_global<Pointer<RoadStationObject>[16], 0x0050C934> _roadStationObjects;
loco_global<Pointer<RoadExtraObject>[4], 0x0050C974> _roadExtraObjects;
loco_global<Pointer<RoadObject>[8], 0x0050C984> _roadObjects;
loco_global<Pointer<AirportObject>[8], 0x0050C9A4> _airportObjects;
loco_global<Pointer<DockObject>[8], 0x0050C9C4> _dockObjects;
loco_global<Pointer<VehicleObject>[224], 0x0050C9E4> _vehicleObjects;
loco_global<Pointer<TreeObject>[64], 0x0050CD64> _treeObjects;
loco_global<Pointer<SnowObject>[1], 0x0050CE64> _snowObjects;
loco_global<Pointer<ClimateObject>[1], 0x0050CE68> _climateObjects;
loco_global<Pointer<HillShapesObject>[1], 0x0050CE6C> _hillShapeObjects;
loco_global<Pointer<BuildingObject>[128], 0x0050CE70> _buildingObjects;
loco_global<Pointer<ScaffoldingObject>[1], 0x0050D070> _scaffoldingObjects;
loco_global<Pointer<IndustryObject>[16], 0x0050D074> _industryObjects;
loco_global<Pointer<RegionObject>[1], 0x0050D0B4> _regionObjects;
loco_global<Pointer<CompetitorObject>[32], 0x0050D0B8> _competitorObjects;
loco_global<Pointer<ScenarioTextObject>[1], 0x0050D138> _scenarioTextObjects;
loco_global<uint32_t, 0x0050D154> _totalNumImages;
@ -384,7 +406,7 @@ namespace OpenLoco::ObjectManager
return entry;
}
static loco_global<std::byte*, 0x0050D13C> _installedObjectList;
static loco_global<Pointer<std::byte>, 0x0050D13C> _installedObjectList;
static loco_global<uint32_t, 0x0112A110> _installedObjectCount;
uint32_t getNumInstalledObjects()
@ -394,7 +416,7 @@ namespace OpenLoco::ObjectManager
std::vector<std::pair<uint32_t, ObjectIndexEntry>> getAvailableObjects(ObjectType type)
{
auto ptr = (std::byte*)_installedObjectList;
auto ptr = (std::byte*)*_installedObjectList;
std::vector<std::pair<uint32_t, ObjectIndexEntry>> list;
for (uint32_t i = 0; i < _installedObjectCount; i++)
@ -445,10 +467,10 @@ namespace OpenLoco::ObjectManager
auto maxObjectsForType = getMaxObjects(objectType);
for (size_t i = 0; i < maxObjectsForType; i++)
{
auto obj = typedObjectList.objects[i];
auto obj = typedObjectList.objects.get()[i].get();
if (obj != nullptr && obj != reinterpret_cast<Object*>(-1))
{
const auto& objHeader = typedObjectList.object_entry_extendeds[i];
const auto& objHeader = typedObjectList.object_entry_extendeds.get()[i];
if (objHeader.isCustom())
{
if (header == objHeader)
@ -600,7 +622,7 @@ namespace OpenLoco::ObjectManager
size_t index = 0;
for (; index < getMaxObjects(type); ++index)
{
if (getRepositoryItem(type).objects[index] == reinterpret_cast<Object*>(-1))
if (getRepositoryItem(type).objects[index].get() == reinterpret_cast<Object*>(-1))
{
break;
}

View File

@ -2,6 +2,7 @@
#include "../Core/Optional.hpp"
#include "../Core/Span.hpp"
#include "../Interop/Interop.hpp"
#include "../Ui/Types.hpp"
#include <cstddef>
#include <cstdint>
@ -136,7 +137,7 @@ namespace OpenLoco
return std::memcmp(this, &rhs, sizeof(ObjectHeader)) == 0;
}
};
static_assert(sizeof(ObjectHeader) == 0x10);
assert_struct_size(ObjectHeader, 0x10);
/**
* Represents an index into the entire loaded object array. Not an index for

View File

@ -83,7 +83,7 @@ namespace OpenLoco
loco_global<HINSTANCE, 0x0113E0B4> ghInstance;
loco_global<LPSTR, 0x00525348> glpCmdLine;
#else
loco_global<char*, 0x00525348> glpCmdLine;
loco_global<uint32_t, 0x00525348> glpCmdLine;
#endif
loco_global<char[256], 0x005060D0> gCDKey;
@ -130,13 +130,15 @@ namespace OpenLoco
const char* lpCmdLine()
{
return glpCmdLine;
return (char*)(uintptr_t)glpCmdLine;
}
#ifndef _WIN32
void lpCmdLine(const char* path)
{
glpCmdLine = strdup(path);
char* p2 = (char*)malloc(strlen(path) + 1);
strcpy(p2, path);
glpCmdLine = (uint32_t)(uintptr_t)p2;
}
#endif
@ -258,37 +260,29 @@ namespace OpenLoco
static bool sub_4054B9()
{
#ifdef _WIN32
registers regs;
call(0x004054B9, regs);
return regs.eax != 0;
#else
return true;
#endif
}
#ifdef _NO_LOCO_WIN32_
/**
* Use this to allocate memory that will be freed in vanilla code or via loco_free.
*/
[[maybe_unused]] static void* malloc(size_t size)
static void get_system_info()
{
return ((void* (*)(size_t))0x004D1401)(size);
#ifdef _WIN32
call(0x004078FE);
#endif
}
/**
* Use this to reallocate memory that will be freed in vanilla code or via loco_free.
*/
[[maybe_unused]] static void* realloc(void* address, size_t size)
static void sub_407B26()
{
return ((void* (*)(void*, size_t))0x004D1B28)(address, size);
#ifdef _WIN32
call(0x00407B26);
#endif
}
/**
* Use this to free up memory allocated in vanilla code or via loco_malloc / loco_realloc.
*/
[[maybe_unused]] static void free(void* address)
{
((void (*)(void*))0x004D1355)(address);
}
#endif // _NO_LOCO_WIN32_
static void sub_4062D1()
{
call(0x004062D1);
@ -296,7 +290,9 @@ namespace OpenLoco
static void sub_406417()
{
#ifdef __i386__
((void (*)())0x00406417)();
#endif
}
static void sub_40567E()
@ -316,15 +312,23 @@ namespace OpenLoco
static bool sub_4034FC(int32_t& a, int32_t& b)
{
#ifdef __i386__
auto result = ((int32_t(*)(int32_t&, int32_t&))(0x004034FC))(a, b);
return result != 0;
#else
return false;
#endif
}
// 0x00407FFD
static bool isAlreadyRunning(const char* mutexName)
{
#ifdef __i386__
auto result = ((int32_t(*)(const char*))(0x00407FFD))(mutexName);
return result != 0;
#else
return false;
#endif
}
// 0x004BE621
@ -410,11 +414,18 @@ namespace OpenLoco
autosaveReset();
}
/**
* 0x004078BE
*/
static void getSystemTime()
{
}
static void initialise()
{
std::srand(std::time(0));
addr<0x0050C18C, int32_t>() = addr<0x00525348, int32_t>();
call(0x004078BE);
getSystemTime();
call(0x004BF476);
Environment::resolvePaths();
Localisation::enumerateLanguages();
@ -441,7 +452,7 @@ namespace OpenLoco
initialiseViewports();
Title::sub_4284C8();
call(0x004969DA);
call(0x0043C88C);
Scenario::reset();
setScreenFlag(ScreenFlags::initialised);
#ifdef _SHOW_INTRO_
Intro::state(Intro::State::begin);
@ -1179,8 +1190,8 @@ namespace OpenLoco
if (sub_4054B9())
{
Ui::createWindow(cfg.display);
call(0x004078FE);
call(0x00407B26);
get_system_info();
// call(0x00407B26);
Ui::initialiseInput();
Audio::initialiseDSound();
run();

View File

@ -30,6 +30,107 @@ namespace OpenLoco
constexpr uint16_t pauseOverrideEnabled = 1 << 8; // new in OpenLoco
}
#pragma pack(push, 1)
struct loco_ptr
{
uintptr_t _ptr;
loco_ptr(const void* x)
{
_ptr = reinterpret_cast<uintptr_t>(x);
}
loco_ptr(int32_t x)
{
_ptr = x;
}
operator int32_t() const
{
assert(_ptr < UINT32_MAX);
return (int32_t)_ptr;
}
operator uint32_t() const
{
assert(_ptr < UINT32_MAX);
return (uint32_t)_ptr;
}
#ifndef __i386__
operator uintptr_t() const
{
return _ptr;
}
#endif
};
#pragma pack(pop)
template<typename T>
class loco_ptr4
{
public:
uint32_t ptr;
loco_ptr4(uint32_t ptr)
: ptr(ptr)
{
}
loco_ptr4(T* ptr = nullptr)
{
assert((uintptr_t)ptr < UINT32_MAX);
this->ptr = static_cast<uint32_t>((uintptr_t)ptr);
}
T* get()
{
return (T*)(uintptr_t)ptr;
}
T* get() const
{
return (T*)(uintptr_t)ptr;
}
T* operator*() const
{
return this->get();
}
};
template<typename T>
class loco_ptr2
{
public:
uint32_t ptr;
loco_ptr2(uint32_t ptr)
: ptr(ptr)
{
}
loco_ptr2(T* ptr = nullptr)
{
assert((uintptr_t)ptr < UINT32_MAX);
this->ptr = static_cast<uint32_t>((uintptr_t)ptr);
}
T* get()
{
return (T*)(uintptr_t)ptr;
}
T* get() const
{
return (T*)(uintptr_t)ptr;
}
T* operator*() const
{
return this->get();
}
// Not allowed on void. No idea how enable_if is supposed to work.
T& operator[](int index)
{
return get()[index];
}
};
static_assert(sizeof(loco_ptr2<int>) == 4);
extern const char version[];
std::string getVersionInfo();
@ -70,3 +171,12 @@ namespace OpenLoco
void sub_444387();
}
#ifndef __i386__
namespace compat
{
void* malloc(size_t size);
void free(void* ptr);
void* realloc(void* ptr, size_t size);
}
#endif

View File

@ -1,6 +1,7 @@
#include "Paint.h"
#include "../Graphics/Gfx.h"
#include "../Interop/Interop.hpp"
#include "../Interop/emu.h"
#include "../Map/Tile.h"
#include "../StationManager.h"
#include "../TownManager.h"
@ -130,8 +131,8 @@ namespace OpenLoco::Paint
}
_quadrantBackIndex = -1;
_quadrantFrontIndex = 0;
_lastPaintString = 0;
_paintStringHead = 0;
_lastPaintString = nullptr;
_paintStringHead = nullptr;
}
// 0x0045A6CA
@ -159,6 +160,7 @@ namespace OpenLoco::Paint
// 0x00461CF8
static void paintTileElements(PaintSession& session, const Map::Pos2& loc)
{
emu::log();
registers regs{};
regs.eax = loc.x;
regs.ecx = loc.y;
@ -321,7 +323,7 @@ namespace OpenLoco::Paint
do
{
ps = psNext;
psNext = psNext->nextQuadrantPS;
psNext = psNext->nextQuadrantPS.get();
if (psNext == nullptr)
return ps;
} while (quadrantIndex > psNext->quadrantIndex);
@ -331,7 +333,7 @@ namespace OpenLoco::Paint
auto* psTemp = ps;
do
{
ps = ps->nextQuadrantPS;
ps = ps->nextQuadrantPS.get();
if (ps == nullptr)
break;
@ -354,7 +356,7 @@ namespace OpenLoco::Paint
{
while (true)
{
psNext = ps->nextQuadrantPS;
psNext = ps->nextQuadrantPS.get();
if (psNext == nullptr)
return psCache;
if (psNext->quadrantFlags & QuadrantFlags::bigger)
@ -372,7 +374,7 @@ namespace OpenLoco::Paint
while (true)
{
ps = psNext;
psNext = psNext->nextQuadrantPS;
psNext = psNext->nextQuadrantPS.get();
if (psNext == nullptr)
break;
if (psNext->quadrantFlags & QuadrantFlags::bigger)
@ -387,7 +389,7 @@ namespace OpenLoco::Paint
if (compareResult)
{
ps->nextQuadrantPS = psNext->nextQuadrantPS;
PaintStruct* ps_temp2 = psTemp->nextQuadrantPS;
PaintStruct* ps_temp2 = psTemp->nextQuadrantPS.get();
psTemp->nextQuadrantPS = psNext;
psNext->nextQuadrantPS = ps_temp2;
psNext = ps;
@ -417,10 +419,10 @@ namespace OpenLoco::Paint
// 0x0045E7B5
void PaintSession::arrangeStructs()
{
_paintHead = _nextFreePaintStruct;
_nextFreePaintStruct++;
_paintHead = _nextFreePaintStruct->get();
_nextFreePaintStruct = _nextFreePaintStruct->get() + 1;
PaintStruct* ps = &(*_paintHead)->basic;
PaintStruct* ps = &(_paintHead->get())->basic;
ps->nextQuadrantPS = nullptr;
uint32_t quadrantIndex = _quadrantBackIndex;
@ -438,14 +440,14 @@ namespace OpenLoco::Paint
do
{
ps = psNext;
psNext = psNext->nextQuadrantPS;
psNext = psNext->nextQuadrantPS.get();
} while (psNext != nullptr);
}
} while (++quadrantIndex <= _quadrantFrontIndex);
PaintStruct* psCache = arrangeStructsHelper(
&(*_paintHead)->basic, _quadrantBackIndex & 0xFFFF, QuadrantFlags::next, currentRotation);
&(_paintHead->get())->basic, _quadrantBackIndex & 0xFFFF, QuadrantFlags::next, currentRotation);
quadrantIndex = _quadrantBackIndex;
while (++quadrantIndex < _quadrantFrontIndex)
@ -541,7 +543,7 @@ namespace OpenLoco::Paint
{
InteractionArg info{};
for (auto* ps = (*_paintHead)->basic.nextQuadrantPS; ps != nullptr; ps = ps->nextQuadrantPS)
for (auto* ps = (_paintHead->get())->basic.nextQuadrantPS.get(); ps != nullptr; ps = ps->nextQuadrantPS.get())
{
auto* tempPS = ps;
auto* nextPS = ps;
@ -555,10 +557,10 @@ namespace OpenLoco::Paint
info = { *ps };
}
}
nextPS = ps->children;
nextPS = ps->children.get();
}
for (auto* attachedPS = ps->attachedPS; attachedPS != nullptr; attachedPS = attachedPS->next)
for (auto* attachedPS = ps->attachedPS.get(); attachedPS != nullptr; attachedPS = attachedPS->next.get())
{
if (isSpriteInteractedWith(getContext(), attachedPS->imageId, { static_cast<int16_t>(attachedPS->x + ps->x), static_cast<int16_t>(attachedPS->y + ps->y) }))
{
@ -584,7 +586,7 @@ namespace OpenLoco::Paint
return interaction;
}
auto rect = (*_context)->getDrawableRect();
auto rect = (*_context->get()).getDrawableRect();
for (auto& station : StationManager::stations())
{
@ -593,7 +595,7 @@ namespace OpenLoco::Paint
continue;
}
if (!station.labelFrame.contains(rect, (*_context)->zoom_level))
if (!station.labelFrame.contains(rect, (*_context->get()).zoom_level))
{
continue;
}
@ -616,11 +618,11 @@ namespace OpenLoco::Paint
return interaction;
}
auto rect = (*_context)->getDrawableRect();
auto rect = _context->get()->getDrawableRect();
for (auto& town : TownManager::towns())
{
if (!town.labelFrame.contains(rect, (*_context)->zoom_level))
if (!town.labelFrame.contains(rect, (*_context->get()).zoom_level))
{
continue;
}

View File

@ -1,6 +1,7 @@
#pragma once
#include "../Interop/Interop.hpp"
#include "../Map/Map.hpp"
#include "../OpenLoco.h"
#include "../Types.hpp"
namespace OpenLoco::Map
@ -41,20 +42,20 @@ namespace OpenLoco::Paint
int16_t y; // 0x0A
uint8_t flags; // 0x0C
uint8_t pad_0D;
AttachedPaintStruct* next; // 0x0E
loco_ptr2<AttachedPaintStruct> next; // 0x0E
};
assert_struct_size(AttachedPaintStruct, 0x12);
struct PaintStringStruct
{
string_id stringId; // 0x00
PaintStringStruct* next; // 0x02
int16_t x; // 0x06
int16_t y; // 0x08
uint16_t args[7]; // 0x0A
string_id stringId; // 0x00
loco_ptr2<PaintStringStruct> next; // 0x02
int16_t x; // 0x06
int16_t y; // 0x08
uint16_t args[7]; // 0x0A
uint8_t pad_18[0x1A - 0x18];
uint8_t* yOffsets; // 0x1A
uint16_t colour; // 0x1E
loco_ptr2<uint8_t> yOffsets; // 0x1A
uint16_t colour; // 0x1E
};
assert_struct_size(PaintStringStruct, 0x20);
@ -90,9 +91,9 @@ namespace OpenLoco::Paint
uint16_t quadrantIndex; // 0x18
uint8_t flags;
uint8_t quadrantFlags; // 0x1B
AttachedPaintStruct* attachedPS; // 0x1C
PaintStruct* children; // 0x20
PaintStruct* nextQuadrantPS; // 0x24
loco_ptr2<AttachedPaintStruct> attachedPS; // 0x1C
loco_ptr2<PaintStruct> children; // 0x20
loco_ptr2<PaintStruct> nextQuadrantPS; // 0x24
Ui::ViewportInteraction::InteractionItem type; // 0x28
uint8_t var_29;
uint16_t pad_2A;
@ -100,8 +101,8 @@ namespace OpenLoco::Paint
coord_t map_y; // 0x2E
union
{
Map::TileElement* tileElement; // 0x30 (or entity pointer)
EntityBase* entity; // 0x30
loco_ptr2<Map::TileElement> tileElement; // 0x30 (or entity pointer)
loco_ptr2<EntityBase> entity; // 0x30
};
};
assert_struct_size(PaintStruct, 0x34);
@ -125,7 +126,7 @@ namespace OpenLoco::Paint
[[nodiscard]] Ui::ViewportInteraction::InteractionArg getNormalInteractionInfo(const uint32_t flags);
[[nodiscard]] Ui::ViewportInteraction::InteractionArg getStationNameInteractionInfo(const uint32_t flags);
[[nodiscard]] Ui::ViewportInteraction::InteractionArg getTownNameInteractionInfo(const uint32_t flags);
Gfx::Context* getContext() { return _context; }
Gfx::Context* getContext() { return _context->get(); }
uint8_t getRotation() { return currentRotation; }
// TileElement or Entity
void setCurrentItem(void* item) { _currentItem = item; }
@ -206,22 +207,22 @@ namespace OpenLoco::Paint
private:
void generateTilesAndEntities(GenerationParameters&& p);
inline static Interop::loco_global<Gfx::Context*, 0x00E0C3E0> _context;
inline static Interop::loco_global<loco_ptr2<Gfx::Context>, 0x00E0C3E0> _context;
inline static Interop::loco_global<PaintEntry[4000], 0x00E0C410> _paintEntries;
inline static Interop::loco_global<PaintStruct* [1024], 0x00E3F0C0> _quadrants;
inline static Interop::loco_global<uint32_t, 0x00E400C0> _quadrantBackIndex;
inline static Interop::loco_global<uint32_t, 0x00E400C4> _quadrantFrontIndex;
inline static Interop::loco_global<const void*, 0x00E4F0B4> _currentlyDrawnItem;
inline static Interop::loco_global<PaintEntry*, 0x00E0C404> _endOfPaintStructArray;
inline static Interop::loco_global<PaintEntry*, 0x00E0C408> _paintHead;
inline static Interop::loco_global<PaintEntry*, 0x00E0C40C> _nextFreePaintStruct;
inline static Interop::loco_global<loco_ptr4<void>, 0x00E4F0B4> _currentlyDrawnItem;
inline static Interop::loco_global<loco_ptr2<PaintEntry>, 0x00E0C404> _endOfPaintStructArray;
inline static Interop::loco_global<loco_ptr2<PaintEntry>, 0x00E0C408> _paintHead;
inline static Interop::loco_global<loco_ptr2<PaintEntry>, 0x00E0C40C> _nextFreePaintStruct;
inline static Interop::loco_global<coord_t, 0x00E3F090> _spritePositionX;
inline static Interop::loco_global<coord_t, 0x00E3F096> _spritePositionY;
inline static Interop::loco_global<PaintStruct*, 0x00E40120> _lastPS;
inline static Interop::loco_global<loco_ptr2<PaintStruct>, 0x00E40120> _lastPS;
inline static Interop::loco_global<Ui::ViewportInteraction::InteractionItem, 0x00E3F0AC> _itemType;
inline static Interop::loco_global<void*, 0x00E3F0B4> _currentItem;
inline static Interop::loco_global<PaintStringStruct*, 0x00E40118> _paintStringHead;
inline static Interop::loco_global<PaintStringStruct*, 0x00E4011C> _lastPaintString;
inline static Interop::loco_global<loco_ptr4<void>, 0x00E3F0B4> _currentItem;
inline static Interop::loco_global<loco_ptr2<PaintStringStruct>, 0x00E40118> _paintStringHead;
inline static Interop::loco_global<loco_ptr2<PaintStringStruct>, 0x00E4011C> _lastPaintString;
inline static Interop::loco_global<Map::Pos2, 0x00E3F0B0> _mapPosition;
uint8_t currentRotation; // new field set from 0x00E3F0B8 but split out into this struct as seperate item

View File

@ -4,7 +4,8 @@
#include <limits.h>
#include <mach-o/dyld.h>
#import <Cocoa/Cocoa.h>
#import <Foundation/Foundation.h>
#import <AppKit/NSOpenPanel.h>
namespace OpenLoco::platform
{

View File

@ -1,9 +1,9 @@
#ifndef _WIN32
#include "Platform.h"
#include "../Console.h"
#include "../Interop/Interop.hpp"
#include "../OpenLoco.h"
#include "Platform.h"
#include <iostream>
#include <pwd.h>
#include <time.h>
@ -22,6 +22,7 @@
int main(int argc, const char** argv)
{
OpenLoco::Interop::emu_init();
OpenLoco::Interop::loadSections();
OpenLoco::lpCmdLine((char*)argv[0]);
OpenLoco::main();

View File

@ -88,9 +88,9 @@ namespace OpenLoco::S5
{
for (auto viewport : w->viewports)
{
if (viewport != nullptr)
if (viewport.get() != nullptr)
{
viewport->render(context);
viewport.get()->render(context);
}
}
}
@ -241,7 +241,7 @@ namespace OpenLoco::S5
static std::unique_ptr<S5File> prepareSaveFile(SaveFlags flags, const std::vector<ObjectHeader>& requiredObjects, const std::vector<ObjectHeader>& packedObjects)
{
auto mainWindow = WindowManager::getMainWindow();
auto savedView = mainWindow != nullptr ? mainWindow->viewports[0]->toSavedView() : SavedViewSimple();
auto savedView = mainWindow != nullptr ? mainWindow->viewports[0].get()->toSavedView() : SavedViewSimple();
auto file = std::make_unique<S5File>();
file->header = prepareHeader(flags, packedObjects.size());

View File

@ -204,7 +204,7 @@ namespace OpenLoco::Scenario
{
S5::getOptions().scenarioFlags &= ~(Scenario::flags::landscape_generation_done);
Ui::WindowManager::invalidate(Ui::WindowType::landscapeGeneration, 0);
call(0x0043C88C);
Scenario::reset();
S5::getOptions().madeAnyChanges = 0;
addr<0x00F25374, uint8_t>() = 0;
Gfx::invalidateScreen();

View File

@ -50,7 +50,7 @@ namespace OpenLoco::Title
// Explicit deduction guide (not needed as of C++20)
template<class... Ts>
overloaded(Ts...)->overloaded<Ts...>;
overloaded(Ts...) -> overloaded<Ts...>;
static const TitleSequence _titleSequence = {
MoveStep{ 231, 160 },
@ -145,7 +145,7 @@ namespace OpenLoco::Title
}
// 0x00444357
static void reset()
void reset()
{
_sequenceIterator = _titleSequence.begin();
_waitCounter = 0;
@ -204,44 +204,47 @@ namespace OpenLoco::Title
return;
auto& command = *_sequenceIterator++;
stdx::visit(overloaded{
[](WaitStep step) {
// This loop slightly deviates from the original, subtract 1 tick to make up for it.
_waitCounter = step.duration - 1;
},
[](ReloadStep step) {
reload();
},
[](MoveStep step) {
if (addr<0x00525E28, uint32_t>() & 1)
{
auto pos = Map::Pos2(step) + Map::Pos2(16, 16);
auto height = Map::TileManager::getHeight(pos);
auto main = Ui::WindowManager::getMainWindow();
if (main != nullptr)
{
auto pos3d = Map::Pos3(pos.x, pos.y, height.landHeight);
main->viewportCentreOnTile(pos3d);
main->flags &= ~Ui::WindowFlags::scrolling_to_location;
main->viewportsUpdatePosition();
}
}
},
[](RotateStep step) {
if (addr<0x00525E28, uint32_t>() & 1)
{
auto main = Ui::WindowManager::getMainWindow();
if (main != nullptr)
{
main->viewportRotateRight();
}
}
},
[](ResetStep step) {
_sequenceIterator = _titleSequence.begin();
},
},
command);
// std::visit(overloaded{
// [](WaitStep step) {
// // This loop slightly deviates from the original, subtract 1 tick to make up for it.
// _waitCounter = step.duration - 1;
// },
// [](ReloadStep step) {
// loadTitle();
// Gfx::invalidateScreen();
// resetScreenAge();
// addr<0x50C19A, uint16_t>() = 55000;
// },
// [](MoveStep step) {
// if (addr<0x00525E28, uint32_t>() & 1)
// {
// auto pos = Map::map_pos(step) + Map::map_pos(16, 16);
// auto height = Map::TileManager::getHeight(pos);
// auto main = Ui::WindowManager::getMainWindow();
// if (main != nullptr)
// {
// auto pos3d = Map::map_pos3(pos.x, pos.y, height.landHeight);
// main->viewportCentreOnTile(pos3d);
// main->flags &= ~Ui::WindowFlags::scrolling_to_location;
// main->viewportsUpdatePosition();
// }
// }
// },
// [](RotateStep step) {
// if (addr<0x00525E28, uint32_t>() & 1)
// {
// auto main = Ui::WindowManager::getMainWindow();
// if (main != nullptr)
// {
// main->viewportRotateRight();
// }
// }
// },
// [](ResetStep step) {
// _sequenceIterator = _titleSequence.begin();
// },
// },
// command);
} while (_waitCounter == 0);
}

View File

@ -2,6 +2,7 @@
namespace OpenLoco::Title
{
void reset();
void registerHooks();
void start();
void sub_4284C8();

View File

@ -206,6 +206,16 @@ namespace OpenLoco::Ui
palette = SDL_AllocPalette(256);
set_palette_callback = updatePalette;
SDL_Color base[256];
for (int i = 0; i < 256; i++)
{
base[i].r = i * 10;
base[i].g = i * 10;
base[i].b = i * 10;
base[i].a = 0;
}
SDL_SetPaletteColors(palette, base, 0, 256);
update(desc.width, desc.height);
#endif
}
@ -411,7 +421,7 @@ namespace OpenLoco::Ui
int32_t pitch = surface->pitch;
Gfx::Context context{};
context.bits = new uint8_t[surface->pitch * height];
context.bits = (uint8_t*)malloc(surface->pitch * height);
context.width = width;
context.height = height;
context.pitch = pitch - width;
@ -505,9 +515,9 @@ namespace OpenLoco::Ui
// Copy pixels from the virtual screen buffer to the surface
auto& context = Gfx::screenContext();
if (context.bits != nullptr)
if (context.bits.get() != nullptr)
{
std::memcpy(surface->pixels, context.bits, surface->pitch * surface->h);
std::memcpy(surface->pixels, context.bits.get(), surface->pitch * surface->h);
}
// Unlock the surface

View File

@ -57,12 +57,6 @@ namespace OpenLoco::Ui::ProgressBar
call(0x004CF631, regs);
}
// 0x004CF63B
void sub_4CF63B()
{
call(0x004CF63B);
}
// 0x004CF621
// eax: value
void setProgress(int32_t value)

View File

@ -8,7 +8,6 @@ namespace OpenLoco::Ui::ProgressBar
{
void begin(std::string_view string);
void begin(string_id stringId);
void sub_4CF63B();
void setProgress(int32_t value);
void end();
void registerHooks();

View File

@ -99,7 +99,7 @@ namespace OpenLoco::Input
png_set_IHDR(png_ptr, info_ptr, context.width, context.height, 8, PNG_COLOR_TYPE_PALETTE, PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT);
png_write_info(png_ptr, info_ptr);
uint8_t* data = context.bits;
uint8_t* data = context.bits.get();
for (int y = 0; y < context.height; y++)
{
png_write_row(png_ptr, data);

View File

@ -190,7 +190,7 @@ namespace OpenLoco::Ui::ScrollView
*output_y = y;
*output_scroll_area = ScrollPart::none;
for (const auto* winWidget = window->widgets; winWidget != widget; winWidget++)
for (const auto* winWidget = &window->widgets[0]; winWidget != widget; winWidget++)
{
if (winWidget->type == WidgetType::scrollview)
{

View File

@ -31,7 +31,7 @@ using namespace OpenLoco::Interop;
namespace OpenLoco::Ui::ViewportInteraction
{
InteractionArg::InteractionArg(const Paint::PaintStruct& ps)
: object(ps.entity)
: object(ps.entity.get())
, type(ps.type)
, unkBh(ps.var_29)
{
@ -368,7 +368,7 @@ namespace OpenLoco::Ui::ViewportInteraction
if (window == nullptr)
return InteractionArg{};
auto viewport = window->viewports[0];
auto viewport = window->viewports[0].get();
if (viewport == nullptr)
return InteractionArg{};
@ -883,8 +883,9 @@ namespace OpenLoco::Ui::ViewportInteraction
auto w = WindowManager::findAt(screenPos);
if (w != nullptr)
{
for (auto vp : w->viewports)
for (auto vp2 : w->viewports)
{
auto vp = vp2.get();
if (vp != nullptr && vp->containsUi({ screenPos.x, screenPos.y }))
{
if (vp->flags & ViewportFlags::hide_foreground_scenery_buildings)
@ -984,8 +985,9 @@ namespace OpenLoco::Ui::ViewportInteraction
}
Viewport* chosenV = nullptr;
for (auto vp : w->viewports)
for (auto vp2 : w->viewports)
{
auto vp = vp2.get();
if (vp == nullptr)
continue;

View File

@ -581,7 +581,7 @@ namespace OpenLoco::Ui::WindowManager
auto mainWindow = getMainWindow();
if (mainWindow != nullptr)
{
return mainWindow->viewports[0];
return mainWindow->viewports[0].get();
}
return nullptr;
}
@ -732,7 +732,7 @@ namespace OpenLoco::Ui::WindowManager
if (w->number != number)
continue;
auto widget = w->widgets[widget_index];
auto widget = w->widgets.get()[widget_index];
if (widget.left != -2)
{
@ -821,7 +821,7 @@ namespace OpenLoco::Ui::WindowManager
{
const auto shiftRight = 20 - w->x;
w->x = 20;
for (auto* vp : w->viewports)
for (auto vp : w->viewports)
{
if (vp != nullptr)
{
@ -1145,7 +1145,12 @@ namespace OpenLoco::Ui::WindowManager
void drawSingle(Gfx::Context* _context, Window* w, int32_t left, int32_t top, int32_t right, int32_t bottom)
{
// Copy context so we can crop it
auto context = *_context;
// auto context = *_context;
auto dpi2 = (Gfx::Context*)malloc(sizeof(Gfx::Context));
memcpy(dpi2, _context, sizeof(Gfx::Context));
auto& context = *dpi2;
// Clamp left to 0
int32_t overflow = left - context.x;
@ -1156,7 +1161,7 @@ namespace OpenLoco::Ui::WindowManager
if (context.width <= 0)
return;
context.pitch += overflow;
context.bits += overflow;
context.bits = context.bits.get() + overflow;
}
// Clamp width to right
@ -1177,7 +1182,7 @@ namespace OpenLoco::Ui::WindowManager
context.height -= overflow;
if (context.height <= 0)
return;
context.bits += (context.width + context.pitch) * overflow;
context.bits = context.bits.get() + (context.width + context.pitch) * overflow;
}
// Clamp height to bottom
@ -1212,6 +1217,8 @@ namespace OpenLoco::Ui::WindowManager
w->callPrepareDraw();
w->callDraw(&context);
free(dpi2);
}
// 0x004CD3D0
@ -1460,7 +1467,7 @@ namespace OpenLoco::Ui::WindowManager
{
int widgetIndex = -1;
int scrollIndex = -1;
for (Widget* widget = window->widgets; widget->type != WidgetType::end; widget++)
for (auto widget = window->widgets.get(); widget->type != WidgetType::end; widget++)
{
widgetIndex++;
@ -1630,7 +1637,7 @@ namespace OpenLoco::Ui::WindowManager
if (w->viewports[0] == nullptr)
continue;
auto viewport = w->viewports[0];
auto viewport = w->viewports[0].get();
if (viewport->zoom != 0)
continue;
@ -1656,10 +1663,10 @@ namespace OpenLoco::Ui::WindowManager
if ((w->flags & WindowFlags::transparent) == 0)
continue;
if (viewport == w->viewports[0])
if (viewport == w->viewports[0].get())
continue;
if (viewport == w->viewports[1])
if (viewport == w->viewports[1].get())
continue;
if (viewport->x + viewport->width <= w->x)
@ -1731,8 +1738,8 @@ namespace OpenLoco::Ui::WindowManager
height += tmargin + bmargin;
int32_t stride = _bitsContext.width + _bitsContext.pitch;
uint8_t* to = _bitsContext.bits + y * stride + x;
uint8_t* from = _bitsContext.bits + (y - dy) * stride + x - dx;
uint8_t* to = _bitsContext.bits.get() + y * stride + x;
uint8_t* from = _bitsContext.bits.get() + (y - dy) * stride + x - dx;
if (dy > 0)
{
@ -1764,7 +1771,7 @@ namespace OpenLoco::Ui::WindowManager
if (window != nullptr)
{
// skip current window and non-intersecting windows
if (viewport == window->viewports[0] || viewport == window->viewports[1] || viewport->x + viewport->width <= window->x || viewport->x >= window->x + window->width || viewport->y + viewport->height <= window->y || viewport->y >= window->y + window->height)
if (viewport == window->viewports[0].get() || viewport == window->viewports[1].get() || viewport->x + viewport->width <= window->x || viewport->x >= window->x + window->width || viewport->y + viewport->height <= window->y || viewport->y >= window->y + window->height)
{
size_t nextWindowIndex = WindowManager::indexOf(window) + 1;
auto nextWindow = nextWindowIndex >= count() ? nullptr : get(nextWindowIndex);
@ -1888,7 +1895,7 @@ namespace OpenLoco::Ui::WindowManager
if (window == nullptr)
return;
auto viewport = window->viewports[0];
auto viewport = window->viewports[0].get();
bool flagsChanged = false;
switch (visibility)
@ -2038,11 +2045,11 @@ namespace OpenLoco::Ui::Windows
auto window = WindowManager::getMainWindow();
if (window != nullptr)
{
if (!(window->viewports[0]->flags & ViewportFlags::gridlines_on_landscape))
if (!(window->viewports[0].get()->flags & ViewportFlags::gridlines_on_landscape))
{
window->invalidate();
}
window->viewports[0]->flags |= ViewportFlags::gridlines_on_landscape;
window->viewports[0].get()->flags |= ViewportFlags::gridlines_on_landscape;
}
}
_gridlinesState++;
@ -2059,11 +2066,11 @@ namespace OpenLoco::Ui::Windows
auto window = WindowManager::getMainWindow();
if (window != nullptr)
{
if ((window->viewports[0]->flags & ViewportFlags::gridlines_on_landscape) != 0)
if ((window->viewports[0].get()->flags & ViewportFlags::gridlines_on_landscape) != 0)
{
window->invalidate();
}
window->viewports[0]->flags &= ~ViewportFlags::gridlines_on_landscape;
window->viewports[0].get()->flags &= ~ViewportFlags::gridlines_on_landscape;
}
}
}
@ -2077,9 +2084,9 @@ namespace OpenLoco::Ui::Windows
auto mainWindow = WindowManager::getMainWindow();
if (mainWindow != nullptr)
{
if (!(mainWindow->viewports[0]->flags & ViewportFlags::one_way_direction_arrows))
if (!(mainWindow->viewports[0].get()->flags & ViewportFlags::one_way_direction_arrows))
{
mainWindow->viewports[0]->flags |= ViewportFlags::one_way_direction_arrows;
mainWindow->viewports[0].get()->flags |= ViewportFlags::one_way_direction_arrows;
mainWindow->invalidate();
}
}
@ -2096,9 +2103,9 @@ namespace OpenLoco::Ui::Windows
auto mainWindow = WindowManager::getMainWindow();
if (mainWindow != nullptr)
{
if ((mainWindow->viewports[0]->flags & ViewportFlags::one_way_direction_arrows))
if ((mainWindow->viewports[0].get()->flags & ViewportFlags::one_way_direction_arrows))
{
mainWindow->viewports[0]->flags &= ~ViewportFlags::one_way_direction_arrows;
mainWindow->viewports[0].get()->flags &= ~ViewportFlags::one_way_direction_arrows;
mainWindow->invalidate();
}
}

View File

@ -20,6 +20,9 @@ namespace OpenLoco
#elif defined(OPENLOCO_BRANCH)
OPENLOCO_BRANCH
#endif
#if __x86_64__
",x86-64"
#endif
#ifndef NDEBUG
", DEBUG"
#endif

View File

@ -43,7 +43,8 @@ namespace OpenLoco::Ui::ViewportManager
static Viewport* initViewport(Ui::Point origin, Ui::Size size, ZoomLevel zoom)
{
auto vp = _viewports.emplace_back(std::make_unique<Viewport>()).get();
auto vp = new Viewport();
_viewports.emplace_back(std::unique_ptr<Viewport>(vp));
vp->x = origin.x;
vp->y = origin.y;
@ -67,7 +68,7 @@ namespace OpenLoco::Ui::ViewportManager
static void focusViewportOn(Window* w, int index, EntityId_t dx)
{
assert(index >= 0 && index < viewportsPerWindow);
Viewport* viewport = w->viewports[index];
Viewport* viewport = w->viewports[index].get();
w->viewport_configurations[index].viewport_target_sprite = dx;
@ -83,7 +84,7 @@ namespace OpenLoco::Ui::ViewportManager
static void focusViewportOn(Window* w, int index, Map::Pos3 tile)
{
assert(index >= 0 && index < viewportsPerWindow);
Viewport* viewport = w->viewports[index];
Viewport* viewport = w->viewports[index].get();
w->viewport_configurations[index].viewport_target_sprite = 0xFFFF;

View File

@ -189,7 +189,7 @@ namespace OpenLoco::Ui
// 0x004CF487
void Widget::drawViewportCentreButton(Gfx::Context* context, const Window* window, const WidgetIndex_t widgetIndex)
{
auto& widget = window->widgets[widgetIndex];
auto widget = window->widgets[widgetIndex];
if (Input::isHovering(window->type, window->number, widgetIndex))
{
Gfx::drawRect(*context, widget.left + window->x, widget.top + window->y, widget.width(), widget.height(), 0x2000000 | 54);
@ -827,7 +827,7 @@ namespace OpenLoco::Ui
cropped.x = l;
cropped.pitch += offset;
cropped.bits += offset;
cropped.bits = cropped.bits.get() + offset;
}
int16_t bp = cropped.x + cropped.width - r;
@ -844,7 +844,7 @@ namespace OpenLoco::Ui
cropped.y = t;
int aex = (cropped.pitch + cropped.width) * offset;
cropped.bits += aex;
cropped.bits = cropped.bits.get() + aex;
}
bp = cropped.y + cropped.height - b;
@ -961,7 +961,7 @@ namespace OpenLoco::Ui
// 0x004CF194
void Widget::drawTab(Window* w, Gfx::Context* ctx, int32_t imageId, WidgetIndex_t index)
{
auto widget = &w->widgets[index];
auto widget = &w->widgets.get()[index];
Ui::Point pos = {};
pos.x = widget->left + w->x;

View File

@ -25,7 +25,7 @@ namespace OpenLoco::Ui
template<typename T>
static bool isInteropEvent(T e)
{
return (uint32_t)e < 0x004D7000;
return (uintptr_t)e < 0x004D7000;
}
Window::Window(Ui::Point position, Ui::Size size)
@ -111,10 +111,10 @@ namespace OpenLoco::Ui
// 0x0045A0B3
void Window::drawViewports(Gfx::Context* context)
{
if (viewports[0] != nullptr)
if (viewports[0].get() != nullptr)
viewports[0]->render(context);
if (viewports[1] != nullptr)
if (viewports[1].get() != nullptr)
viewports[1]->render(context);
}
@ -133,7 +133,7 @@ namespace OpenLoco::Ui
return std::nullopt;
}
Viewport* vp = w->viewports[0];
Viewport* vp = w->viewports[0].get();
if (vp == nullptr)
{
return std::nullopt;
@ -276,7 +276,7 @@ namespace OpenLoco::Ui
{
for (int i = 0; i < 2; i++)
{
Viewport* viewport = this->viewports[i];
Viewport* viewport = this->viewports[i].get();
ViewportConfig* config = &this->viewport_configurations[i];
if (viewport == nullptr)
@ -517,7 +517,7 @@ namespace OpenLoco::Ui
// Get viewport coordinates centring around the tile.
auto base_height = TileManager::getHeight({ *map_x, *map_y }).landHeight;
Viewport* v = this->viewports[0];
Viewport* v = this->viewports[0].get();
const auto dest = v->centre2dCoordinates({ *map_x, *map_y, base_height });
// Rebase mouse position onto centre of window, and compensate for zoom level.
@ -547,7 +547,7 @@ namespace OpenLoco::Ui
// 0x004C6827
void Window::viewportCentreOnTile(const Map::Pos3& loc)
{
auto viewport = this->viewports[0];
auto viewport = this->viewports[0].get();
if (viewport == nullptr)
return;
@ -583,7 +583,7 @@ namespace OpenLoco::Ui
void Window::viewportCentreMain()
{
if (viewports[0] == nullptr || saved_view.isEmpty())
if (viewports[0].get() == nullptr || saved_view.isEmpty())
return;
auto main = WindowManager::getMainWindow();
@ -607,7 +607,7 @@ namespace OpenLoco::Ui
{
// Get viewport coordinates centring around the tile.
auto base_height = TileManager::getHeight({ map_x, map_y }).landHeight;
Viewport* v = this->viewports[0];
Viewport* v = this->viewports[0].get();
const auto dest = v->centre2dCoordinates({ map_x, map_y, base_height });
// Get mouse position to offset against.
@ -625,7 +625,7 @@ namespace OpenLoco::Ui
void Window::viewportFocusOnEntity(uint16_t targetEntity)
{
if (viewports[0] == nullptr || saved_view.isEmpty())
if (viewports[0].get() == nullptr || saved_view.isEmpty())
return;
viewport_configurations[0].viewport_target_sprite = targetEntity;
@ -633,7 +633,7 @@ namespace OpenLoco::Ui
bool Window::viewportIsFocusedOnEntity() const
{
if (viewports[0] == nullptr || saved_view.isEmpty())
if (viewports[0].get() == nullptr || saved_view.isEmpty())
return false;
return viewport_configurations[0].viewport_target_sprite != EntityId::null;
@ -641,7 +641,7 @@ namespace OpenLoco::Ui
void Window::viewportUnfocusFromEntity()
{
if (viewports[0] == nullptr || saved_view.isEmpty())
if (viewports[0].get() == nullptr || saved_view.isEmpty())
return;
if (viewport_configurations[0].viewport_target_sprite == EntityId::null)
@ -654,7 +654,7 @@ namespace OpenLoco::Ui
void Window::viewportZoomSet(int8_t zoomLevel, bool toCursor)
{
Viewport* v = this->viewports[0];
Viewport* v = this->viewports[0].get();
ViewportConfig* vc = &this->viewport_configurations[0];
zoomLevel = std::clamp<int8_t>(zoomLevel, 0, 3);
@ -703,19 +703,19 @@ namespace OpenLoco::Ui
// 0x0045EFDB
void Window::viewportZoomIn(bool toCursor)
{
if (this->viewports[0] == nullptr)
if (this->viewports[0].get() == nullptr)
return;
this->viewportZoomSet(this->viewports[0]->zoom - 1, toCursor);
this->viewportZoomSet(this->viewports[0].get()->zoom - 1, toCursor);
}
// 0x0045F015
void Window::viewportZoomOut(bool toCursor)
{
if (this->viewports[0] == nullptr)
if (this->viewports[0].get() == nullptr)
return;
this->viewportZoomSet(this->viewports[0]->zoom + 1, toCursor);
this->viewportZoomSet(this->viewports[0].get()->zoom + 1, toCursor);
}
// 0x0045F04F
@ -736,9 +736,9 @@ namespace OpenLoco::Ui
void Window::viewportRemove(const uint8_t viewportId)
{
if (viewports[viewportId] != nullptr)
if (viewports[viewportId].get() != nullptr)
{
viewports[viewportId]->width = 0;
viewports[viewportId].get()->width = 0;
viewports[viewportId] = nullptr;
}
}
@ -746,7 +746,7 @@ namespace OpenLoco::Ui
// 0x004421FB
void Window::viewportFromSavedView(const SavedViewSimple& savedView)
{
auto viewport = viewports[0];
auto viewport = viewports[0].get();
if (viewport != nullptr)
{
auto& config = viewport_configurations[0];
@ -789,16 +789,16 @@ namespace OpenLoco::Ui
this->x += dx;
this->y += dy;
if (this->viewports[0] != nullptr)
if (this->viewports[0].get() != nullptr)
{
this->viewports[0]->x += dx;
this->viewports[0]->y += dy;
this->viewports[0].get()->x += dx;
this->viewports[0].get()->y += dy;
}
if (this->viewports[1] != nullptr)
if (this->viewports[1].get() != nullptr)
{
this->viewports[1]->x += dx;
this->viewports[1]->y += dy;
this->viewports[1].get()->x += dx;
this->viewports[1].get()->y += dy;
}
this->invalidate();
@ -839,16 +839,16 @@ namespace OpenLoco::Ui
this->y += offset.y;
this->invalidate();
if (this->viewports[0] != nullptr)
if (this->viewports[0].get() != nullptr)
{
this->viewports[0]->x += offset.x;
this->viewports[0]->y += offset.y;
this->viewports[0].get()->x += offset.x;
this->viewports[0].get()->y += offset.y;
}
if (this->viewports[1] != nullptr)
if (this->viewports[1].get() != nullptr)
{
this->viewports[1]->x += offset.x;
this->viewports[1]->y += offset.y;
this->viewports[1].get()->x += offset.x;
this->viewports[1].get()->y += offset.y;
}
}
@ -913,8 +913,7 @@ namespace OpenLoco::Ui
if (isInteropEvent(event_handlers->on_close))
{
registers regs;
regs.esi = X86Pointer(this);
call((uint32_t)this->event_handlers->on_close, regs);
// call((uint32_t)this->event_handlers->on_close, regs);
return;
}
@ -930,7 +929,7 @@ namespace OpenLoco::Ui
{
registers regs;
regs.esi = X86Pointer(this);
call((uint32_t)this->event_handlers->on_periodic_update, regs);
// call((uint32_t)this->event_handlers->on_periodic_update, regs);
return;
}
@ -1016,7 +1015,7 @@ namespace OpenLoco::Ui
regs.bx = yPos;
regs.dx = widget_index;
regs.esi = X86Pointer(this);
call((uint32_t)this->event_handlers->on_tool_down, regs);
// call((uint32_t)this->event_handlers->on_tool_down, regs);
return;
}
@ -1035,7 +1034,7 @@ namespace OpenLoco::Ui
regs.bx = yPos;
regs.dx = widget_index;
regs.esi = X86Pointer(this);
call((uint32_t)this->event_handlers->toolDragContinue, regs);
// call((uint32_t)this->event_handlers->toolDragContinue, regs);
return;
}
@ -1052,7 +1051,7 @@ namespace OpenLoco::Ui
registers regs;
regs.dx = widget_index;
regs.esi = X86Pointer(this);
call((uint32_t)this->event_handlers->toolDragEnd, regs);
// call((uint32_t)this->event_handlers->toolDragEnd, regs);
return;
}
@ -1069,7 +1068,7 @@ namespace OpenLoco::Ui
registers regs;
regs.dx = widget_index;
regs.esi = X86Pointer(this);
call((uint32_t)this->event_handlers->on_tool_abort, regs);
// call((uint32_t)this->event_handlers->on_tool_abort, regs);
return;
}
@ -1088,7 +1087,7 @@ namespace OpenLoco::Ui
regs.cx = yPos;
regs.edi = (int32_t)fallback;
regs.esi = X86Pointer(this);
call(reinterpret_cast<uint32_t>(this->event_handlers->event_15), regs);
// call(reinterpret_cast<uint32_t>(this->event_handlers->event_15), regs);
*out = regs.bl;
@ -1155,7 +1154,7 @@ namespace OpenLoco::Ui
{
registers regs;
regs.esi = X86Pointer(this);
call((uint32_t)event_handlers->on_resize, regs);
// call((uint32_t)event_handlers->on_resize, regs);
return (Window*)regs.esi;
}
@ -1174,7 +1173,7 @@ namespace OpenLoco::Ui
regs.edx = widget_index;
regs.esi = X86Pointer(this);
regs.edi = X86Pointer(&this->widgets[widget_index]);
call((uint32_t)this->event_handlers->event_03, regs);
// call((uint32_t)this->event_handlers->event_03, regs);
return;
}
@ -1192,7 +1191,7 @@ namespace OpenLoco::Ui
regs.edx = widget_index;
regs.esi = X86Pointer(this);
regs.edi = X86Pointer(&this->widgets[widget_index]);
call((uint32_t)this->event_handlers->on_mouse_down, regs);
// call((uint32_t)this->event_handlers->on_mouse_down, regs);
return;
}
@ -1210,7 +1209,7 @@ namespace OpenLoco::Ui
regs.ax = item_index;
regs.edx = widget_index;
regs.esi = X86Pointer(this);
call((uint32_t)this->event_handlers->on_dropdown, regs);
// call((uint32_t)this->event_handlers->on_dropdown, regs);
return;
}
@ -1227,7 +1226,7 @@ namespace OpenLoco::Ui
registers regs;
regs.eax = scrollIndex;
regs.esi = X86Pointer(this);
call((uint32_t)this->event_handlers->get_scroll_size, regs);
// call((uint32_t)this->event_handlers->get_scroll_size, regs);
*scrollWidth = regs.cx;
*scrollHeight = regs.dx;
return;
@ -1248,7 +1247,7 @@ namespace OpenLoco::Ui
regs.esi = X86Pointer(this);
regs.cx = xPos;
regs.dx = yPos;
call((uint32_t)this->event_handlers->scroll_mouse_down, regs);
// call((uint32_t)this->event_handlers->scroll_mouse_down, regs);
return;
}
@ -1267,7 +1266,7 @@ namespace OpenLoco::Ui
regs.esi = X86Pointer(this);
regs.cx = xPos;
regs.dx = yPos;
call((uint32_t)this->event_handlers->scroll_mouse_drag, regs);
// call((uint32_t)this->event_handlers->scroll_mouse_drag, regs);
return;
}
@ -1286,7 +1285,7 @@ namespace OpenLoco::Ui
regs.esi = X86Pointer(this);
regs.cx = xPos;
regs.dx = yPos;
call((uint32_t)this->event_handlers->scroll_mouse_over, regs);
// call((uint32_t)this->event_handlers->scroll_mouse_over, regs);
return;
}
@ -1339,7 +1338,7 @@ namespace OpenLoco::Ui
registers regs;
regs.ax = widget_index;
regs.esi = X86Pointer(this);
call((int32_t)this->event_handlers->tooltip, regs);
// call((int32_t)this->event_handlers->tooltip, regs);
auto args = FormatArguments();
if (regs.ax == (int16_t)StringIds::null)
return {};
@ -1360,7 +1359,7 @@ namespace OpenLoco::Ui
regs.cx = xPos;
regs.dx = yPos;
regs.esi = X86Pointer(this);
call(reinterpret_cast<int32_t>(this->event_handlers->on_move), regs);
// call(reinterpret_cast<int32_t>(this->event_handlers->on_move), regs);
}
this->event_handlers->on_move(*this, xPos, yPos);
}
@ -1374,10 +1373,11 @@ namespace OpenLoco::Ui
{
registers regs;
regs.esi = X86Pointer(this);
call((int32_t)this->event_handlers->prepare_draw, regs);
// call((int32_t)this->event_handlers->prepare_draw, regs);
return;
}
auto handlers = event_handlers.get();
event_handlers->prepare_draw(this);
}
@ -1391,7 +1391,7 @@ namespace OpenLoco::Ui
registers regs;
regs.esi = X86Pointer(this);
regs.edi = X86Pointer(context);
call((int32_t)this->event_handlers->draw, regs);
// call((int32_t)this->event_handlers->draw, regs);
return;
}
@ -1409,7 +1409,7 @@ namespace OpenLoco::Ui
regs.ax = scrollIndex;
regs.esi = X86Pointer(this);
regs.edi = X86Pointer(context);
call((int32_t)event_handlers->draw_scroll, regs);
// call((int32_t)event_handlers->draw_scroll, regs);
return;
}
@ -1449,7 +1449,7 @@ namespace OpenLoco::Ui
uint8_t scrollviewIndex = 0;
for (WidgetIndex_t widgetIndex = 0; widgetIndex < 64; widgetIndex++)
{
auto widget = &this->widgets[widgetIndex];
auto widget = &this->widgets.get()[widgetIndex];
if (widget->type == WidgetType::end)
{

View File

@ -144,7 +144,7 @@ namespace OpenLoco::Ui
void (*scroll_mouse_over)(Ui::Window* window, int16_t x, int16_t y, uint8_t scroll_index);
void (*text_input)(Window*, WidgetIndex_t, const char*);
void (*viewport_rotate)(Window*);
uint32_t event_22;
uintptr_t event_22;
std::optional<FormatArguments> (*tooltip)(Window*, WidgetIndex_t);
Ui::CursorId (*cursor)(Window*, int16_t, int16_t, int16_t, Ui::CursorId);
void (*on_move)(Window&, const int16_t x, const int16_t y);
@ -241,28 +241,84 @@ namespace OpenLoco::Ui
}
};
template<typename T>
class loco_ptr3
{
private:
uint32_t _ptr;
public:
loco_ptr3(uintptr_t ptr)
{
_ptr = ptr;
}
loco_ptr3(std::nullptr_t aNullptr)
{
_ptr = 0;
}
loco_ptr3(T* ptr = nullptr)
{
_ptr = static_cast<uint32_t>((uintptr_t)ptr);
}
T* get()
{
return (T*)(uintptr_t)_ptr;
}
T* operator->()
{
return get();
}
const T* get() const
{
return (T*)(uintptr_t)_ptr;
}
T& operator[](int index)
{
return get()[index];
}
const T& operator[](int index) const
{
return get()[index];
}
bool operator==(const void* other)
{
return this->get() == other;
}
bool operator!=(const void* other)
{
return !(*this == other);
}
};
struct Window
{
WindowEventList* event_handlers; // 0x00
Ui::Viewport* viewports[2] = { nullptr, nullptr }; // 0x04
uint64_t enabled_widgets = 0; // 0x0C
uint64_t disabled_widgets = 0; // 0x14
uint64_t activated_widgets = 0; // 0x1C
uint64_t holdable_widgets = 0; // 0x24
Widget* widgets; // 0x2C
int16_t x; // 0x30
int16_t y; // 0x32
uint16_t width; // 0x34
uint16_t height; // 0x36
uint16_t min_width; // 0x38
uint16_t max_width; // 0x3a
uint16_t min_height; // 0x3c
uint16_t max_height; // 0x3e
WindowNumber_t number = 0; // 0x40
uint32_t flags; // 0x42
ScrollArea scroll_areas[2]; // 0x46
int16_t row_info[1000]; // 0x6A
uint16_t row_count; // 0x83A
loco_ptr3<WindowEventList> event_handlers; // 0x00
loco_ptr3<Ui::Viewport> viewports[2] = { nullptr, nullptr }; // 0x04
uint64_t enabled_widgets = 0; // 0x0C
uint64_t disabled_widgets = 0; // 0x14
uint64_t activated_widgets = 0; // 0x1C
uint64_t holdable_widgets = 0; // 0x24
loco_ptr3<Widget> widgets; // 0x2C
int16_t x; // 0x30
int16_t y; // 0x32
uint16_t width; // 0x34
uint16_t height; // 0x36
uint16_t min_width; // 0x38
uint16_t max_width; // 0x3a
uint16_t min_height; // 0x3c
uint16_t max_height; // 0x3e
WindowNumber_t number = 0; // 0x40
uint32_t flags; // 0x42
ScrollArea scroll_areas[2]; // 0x46
int16_t row_info[1000]; // 0x6A
uint16_t row_count; // 0x83A
uint16_t var_83C;
uint16_t row_height; // 0x83E
int16_t row_hover = -1; // 0x840
@ -277,13 +333,13 @@ namespace OpenLoco::Ui
uint16_t var_858 = 0;
union
{
std::byte* object; // 0x85A union
loco_ptr2<std::byte> object; // 0x85A union
struct
{
int16_t var_85A;
int16_t var_85C;
};
uintptr_t info;
uint32_t info;
};
uint8_t pad_85E[0x870 - 0x85E];
uint16_t current_tab = 0; // 0x870

View File

@ -601,9 +601,9 @@ namespace OpenLoco::Ui::Windows::BuildVehicle
WindowManager::invalidate(WindowType::topToolbar, 0);
}
auto curViewport = window->viewports[0];
window->viewports[0] = 0;
if (curViewport != 0)
auto curViewport = window->viewports[0].get();
window->viewports[0] = nullptr;
if (curViewport != nullptr)
{
curViewport->width = 0;
}
@ -1285,7 +1285,7 @@ namespace OpenLoco::Ui::Windows::BuildVehicle
static void setTransportTypeTabs(Ui::Window* window)
{
auto disabledWidgets = window->disabled_widgets >> widx::tab_build_new_trains;
auto widget = window->widgets + widx::tab_build_new_trains;
auto widget = &window->widgets[widx::tab_build_new_trains];
auto tabWidth = widget->right - widget->left;
auto tabX = widget->left;
for (auto i = 0; i <= widx::tab_build_new_ships - widx::tab_build_new_trains; ++i, ++widget)

View File

@ -237,7 +237,7 @@ namespace OpenLoco::Ui::Windows::CompanyFaceSelection
const auto width = self->width - self->widgets[widx::scrollview].right - 6;
auto str = const_cast<char*>(StringManager::getString(StringIds::buffer_2039));
*str++ = ControlCodes::window_colour_2;
auto objectPtr = self->object;
auto objectPtr = self->object.get();
strcpy(str, ObjectManager::ObjectIndexEntry::read(&objectPtr)._name);
Gfx::drawStringCentredClipped(*context, x, y, width, Colour::black, StringIds::buffer_2039);
}

View File

@ -680,7 +680,7 @@ namespace OpenLoco::Ui::Windows::CompanyList
auto companyId = company.id();
auto companyColour = CompanyManager::getCompanyColour(companyId);
_graphYData[count] = reinterpret_cast<uint32_t>(&company.performance_index_history[0]);
// _graphYData[count] = reinterpret_cast<uint32_t>(&company.performance_index_history[0]);
_graphDataStart[count] = maxHistorySize - company.history_size;
_graphLineColour[count] = Colour::getShade(companyColour, 6);
_graphItemId[count] = companyId;
@ -771,7 +771,7 @@ namespace OpenLoco::Ui::Windows::CompanyList
auto companyId = company.id();
auto companyColour = CompanyManager::getCompanyColour(companyId);
_graphYData[count] = reinterpret_cast<uint32_t>(&company.cargo_units_delivered_history[0]);
// _graphYData[count] = reinterpret_cast<uint32_t>(&company.cargo_units_delivered_history[0]);
_graphDataStart[count] = maxHistorySize - company.history_size;
_graphLineColour[count] = Colour::getShade(companyColour, 6);
_graphItemId[count] = companyId;
@ -862,7 +862,7 @@ namespace OpenLoco::Ui::Windows::CompanyList
auto companyId = company.id();
auto companyColour = CompanyManager::getCompanyColour(companyId);
_graphYData[count] = reinterpret_cast<uint32_t>(&company.cargo_units_distance_history[0]);
// _graphYData[count] = reinterpret_cast<uint32_t>(&company.cargo_units_distance_history[0]);
_graphDataStart[count] = maxHistorySize - company.history_size;
_graphLineColour[count] = Colour::getShade(companyColour, 6);
_graphItemId[count] = companyId;
@ -953,7 +953,7 @@ namespace OpenLoco::Ui::Windows::CompanyList
auto companyId = company.id();
auto companyColour = CompanyManager::getCompanyColour(companyId);
_graphYData[count] = reinterpret_cast<uint32_t>(&company.companyValueHistory[0]);
// _graphYData[count] = reinterpret_cast<uint32_t>(&company.companyValueHistory[0]);
_graphDataStart[count] = maxHistorySize - company.history_size;
_graphLineColour[count] = Colour::getShade(companyColour, 6);
_graphItemId[count] = companyId;
@ -1072,7 +1072,7 @@ namespace OpenLoco::Ui::Windows::CompanyList
auto colour = _cargoLineColour[i];
_graphYData[count] = reinterpret_cast<uint32_t>(&_deliveredCargoPayment[i][0]);
// _graphYData[count] = reinterpret_cast<uint32_t>(&_deliveredCargoPayment[i][0]);
_graphDataStart[count] = 0;
_graphLineColour[count] = Colour::getShade(colour, 6);
_graphItemId[count] = i;

View File

@ -382,7 +382,7 @@ namespace OpenLoco::Ui::Windows::CompanyWindow
if (self->viewports[0] != nullptr)
{
Ui::Size proposedDims(self->width - 123, self->height - 59);
auto& viewport = self->viewports[0];
auto viewport = self->viewports[0].get();
if (proposedDims.width != viewport->width || proposedDims.height != viewport->height)
{
viewport->width = proposedDims.width;

View File

@ -204,7 +204,7 @@ namespace OpenLoco::Ui::Windows::Construction
// 0x004A0EAD
Window* openAtTrack(Window* main, TrackElement* track, const Pos2 pos)
{
auto* viewport = main->viewports[0];
auto* viewport = main->viewports[0].get();
_backupTileElement = *reinterpret_cast<TileElement*>(track);
auto* copyElement = (*_backupTileElement).asTrack();
if (copyElement == nullptr)
@ -299,7 +299,7 @@ namespace OpenLoco::Ui::Windows::Construction
// 0x004A147F
Window* openAtRoad(Window* main, RoadElement* road, const Pos2 pos)
{
auto* viewport = main->viewports[0];
auto* viewport = main->viewports[0].get();
_backupTileElement = *reinterpret_cast<TileElement*>(road);
auto* copyElement = (*_backupTileElement).asRoad();
if (copyElement == nullptr)

View File

@ -1240,7 +1240,7 @@ namespace OpenLoco::Ui::Windows::MapWindow
if (window == nullptr)
return;
auto viewport = window->viewports[0];
auto viewport = window->viewports[0].get();
if (viewport == nullptr)
return;
@ -1517,7 +1517,7 @@ namespace OpenLoco::Ui::Windows::MapWindow
if (mainWindow == nullptr)
return;
auto viewport = mainWindow->viewports[0];
auto viewport = mainWindow->viewports[0].get();
if (viewport == nullptr)
return;

View File

@ -507,9 +507,9 @@ namespace OpenLoco::Ui::Windows::ObjectSelectionWindow
drawTabs(self, context);
bool doDefault = true;
if (self->object != nullptr)
if (self->object.get() != nullptr)
{
auto objectPtr = self->object;
auto objectPtr = self->object.get();
auto var = ObjectManager::ObjectIndexEntry::read(&objectPtr)._header;
if (var->getType() != ObjectType::townNames && var->getType() != ObjectType::climate)
{
@ -547,7 +547,7 @@ namespace OpenLoco::Ui::Windows::ObjectSelectionWindow
return;
{
auto objectPtr = self->object;
auto objectPtr = self->object.get();
drawPreviewImage(
ObjectManager::ObjectIndexEntry::read(&objectPtr)._header,
@ -565,7 +565,7 @@ namespace OpenLoco::Ui::Windows::ObjectSelectionWindow
auto buffer = const_cast<char*>(StringManager::getString(StringIds::buffer_2039));
*buffer++ = ControlCodes::window_colour_2;
auto objectPtr = self->object;
auto objectPtr = self->object.get();
strncpy(buffer, ObjectManager::ObjectIndexEntry::read(&objectPtr)._name, 510);
@ -573,7 +573,7 @@ namespace OpenLoco::Ui::Windows::ObjectSelectionWindow
}
{
auto objectPtr = self->object;
auto objectPtr = self->object.get();
drawDescription(
ObjectManager::ObjectIndexEntry::read(&objectPtr)._header,
@ -602,7 +602,7 @@ namespace OpenLoco::Ui::Windows::ObjectSelectionWindow
uint8_t textColour = ControlCodes::colour_black;
auto objectPtr = self.object;
auto objectPtr = self.object.get();
if (objectPtr != nullptr)
{
auto windowObjectName = ObjectManager::ObjectIndexEntry::read(&objectPtr)._name;

View File

@ -83,7 +83,7 @@ namespace OpenLoco::Ui::Windows::ProgressBar
_progressBarValue = value;
WindowManager::invalidate(WindowType::progressBar);
Ui::ProgressBar::sub_4CF63B();
Gfx::render();
}
// 0x004CF78A

View File

@ -242,7 +242,7 @@ namespace OpenLoco::Ui::Windows::Town
uint16_t newHeight = self->height - 59;
auto& viewport = self->viewports[0];
auto viewport = self->viewports[0].get();
if (newWidth != viewport->width || newHeight != viewport->height)
{
viewport->width = newWidth;

View File

@ -493,7 +493,7 @@ namespace OpenLoco::Ui::Windows::VehicleList
static void setTransportTypeTabs(Window* self)
{
auto disabledWidgets = self->disabled_widgets >> Widx::tab_trains;
auto widget = self->widgets + Widx::tab_trains;
auto widget = &self->widgets[Widx::tab_trains];
auto tabWidth = widget->right - widget->left;
auto tabX = widget->left;
for (auto i = 0; i <= Widx::tab_ships - Widx::tab_trains; ++i, ++widget)

270
src/tinyalloc/tinyalloc.c Normal file
View File

@ -0,0 +1,270 @@
#include "tinyalloc.h"
#include <stdint.h>
#ifdef TA_DEBUG
extern void print_s(char *);
extern void print_i(size_t);
#else
#define print_s(X)
#define print_i(X)
#endif
typedef struct Block Block;
struct Block {
void *addr;
Block *next;
size_t size;
};
typedef struct {
Block *free; // first free block
Block *used; // first used block
Block *fresh; // first available blank block
size_t top; // top free addr
} Heap;
static Heap *heap = NULL;
static void *heap_limit = NULL;
static size_t heap_split_thresh;
static size_t heap_alignment;
static size_t heap_max_blocks;
/**
* If compaction is enabled, inserts block
* into free list, sorted by addr.
* If disabled, add block has new head of
* the free list.
*/
static void insert_block(Block *block) {
#ifndef TA_DISABLE_COMPACT
Block *ptr = heap->free;
Block *prev = NULL;
while (ptr != NULL) {
if ((size_t)block->addr <= (size_t)ptr->addr) {
print_s("insert");
print_i((size_t)ptr);
break;
}
prev = ptr;
ptr = ptr->next;
}
if (prev != NULL) {
if (ptr == NULL) {
print_s("new tail");
}
prev->next = block;
} else {
print_s("new head");
heap->free = block;
}
block->next = ptr;
#else
block->next = heap->free;
heap->free = block;
#endif
}
#ifndef TA_DISABLE_COMPACT
static void release_blocks(Block *scan, Block *to) {
Block *scan_next;
while (scan != to) {
print_s("release");
print_i((size_t)scan);
scan_next = scan->next;
scan->next = heap->fresh;
heap->fresh = scan;
scan->addr = 0;
scan->size = 0;
scan = scan_next;
}
}
static void compact() {
Block *ptr = heap->free;
Block *prev;
Block *scan;
while (ptr != NULL) {
prev = ptr;
scan = ptr->next;
while (scan != NULL &&
(size_t)prev->addr + prev->size == (size_t)scan->addr) {
print_s("merge");
print_i((size_t)scan);
prev = scan;
scan = scan->next;
}
if (prev != ptr) {
size_t new_size =
(size_t)prev->addr - (size_t)ptr->addr + prev->size;
print_s("new size");
print_i(new_size);
ptr->size = new_size;
Block *next = prev->next;
// make merged blocks available
release_blocks(ptr->next, prev->next);
// relink
ptr->next = next;
}
ptr = ptr->next;
}
}
#endif
bool ta_init(const void *base, const void *limit, const size_t heap_blocks, const size_t split_thresh, const size_t alignment) {
heap = (Heap *)base;
heap_limit = limit;
heap_split_thresh = split_thresh;
heap_alignment = alignment;
heap_max_blocks = heap_blocks;
heap->free = NULL;
heap->used = NULL;
heap->fresh = (Block *)(heap + 1);
heap->top = (size_t)(heap->fresh + heap_blocks);
Block *block = heap->fresh;
size_t i = heap_max_blocks - 1;
while (i--) {
block->next = block + 1;
block++;
}
block->next = NULL;
return true;
}
bool ta_free(void *free) {
Block *block = heap->used;
Block *prev = NULL;
while (block != NULL) {
if (free == block->addr) {
if (prev) {
prev->next = block->next;
} else {
heap->used = block->next;
}
insert_block(block);
#ifndef TA_DISABLE_COMPACT
compact();
#endif
return true;
}
prev = block;
block = block->next;
}
return false;
}
static Block *alloc_block(size_t num) {
Block *ptr = heap->free;
Block *prev = NULL;
size_t top = heap->top;
num = (num + heap_alignment - 1) & -heap_alignment;
while (ptr != NULL) {
const int is_top = ((size_t)ptr->addr + ptr->size >= top) && ((size_t)ptr->addr + num <= heap_limit);
if (is_top || ptr->size >= num) {
if (prev != NULL) {
prev->next = ptr->next;
} else {
heap->free = ptr->next;
}
ptr->next = heap->used;
heap->used = ptr;
if (is_top) {
print_s("resize top block");
ptr->size = num;
heap->top = (size_t)ptr->addr + num;
#ifndef TA_DISABLE_SPLIT
} else if (heap->fresh != NULL) {
size_t excess = ptr->size - num;
if (excess >= heap_split_thresh) {
ptr->size = num;
Block *split = heap->fresh;
heap->fresh = split->next;
split->addr = (void *)((size_t)ptr->addr + num);
print_s("split");
print_i((size_t)split->addr);
split->size = excess;
insert_block(split);
#ifndef TA_DISABLE_COMPACT
compact();
#endif
}
#endif
}
return ptr;
}
prev = ptr;
ptr = ptr->next;
}
// no matching free blocks
// see if any other blocks available
size_t new_top = top + num;
if (heap->fresh != NULL && new_top <= heap_limit) {
ptr = heap->fresh;
heap->fresh = ptr->next;
ptr->addr = (void *)top;
ptr->next = heap->used;
ptr->size = num;
heap->used = ptr;
heap->top = new_top;
return ptr;
}
return NULL;
}
void *ta_alloc(size_t num) {
Block *block = alloc_block(num);
if (block != NULL) {
return block->addr;
}
return NULL;
}
static void memclear(void *ptr, size_t num) {
size_t *ptrw = (size_t *)ptr;
size_t numw = (num & -sizeof(size_t)) / sizeof(size_t);
while (numw--) {
*ptrw++ = 0;
}
num &= (sizeof(size_t) - 1);
uint8_t *ptrb = (uint8_t *)ptrw;
while (num--) {
*ptrb++ = 0;
}
}
void *ta_calloc(size_t num, size_t size) {
num *= size;
Block *block = alloc_block(num);
if (block != NULL) {
memclear(block->addr, num);
return block->addr;
}
return NULL;
}
static size_t count_blocks(Block *ptr) {
size_t num = 0;
while (ptr != NULL) {
num++;
ptr = ptr->next;
}
return num;
}
size_t ta_num_free() {
return count_blocks(heap->free);
}
size_t ta_num_used() {
return count_blocks(heap->used);
}
size_t ta_num_fresh() {
return count_blocks(heap->fresh);
}
bool ta_check() {
return heap_max_blocks == ta_num_free() + ta_num_used() + ta_num_fresh();
}

12
src/tinyalloc/tinyalloc.h Normal file
View File

@ -0,0 +1,12 @@
#include <stdbool.h>
#include <stddef.h>
bool ta_init(const void *base, const void *limit, const size_t heap_blocks, const size_t split_thresh, const size_t alignment);
void *ta_alloc(size_t num);
void *ta_calloc(size_t num, size_t size);
bool ta_free(void *ptr);
size_t ta_num_free();
size_t ta_num_used();
size_t ta_num_fresh();
bool ta_check();