mirror of https://github.com/OpenRCT2/OpenRCT2.git
Remove Testpaint (#17333)
TestPaint served us well, but it hasn’t compiled for years, and before that it produced incorrect results for years.
This commit is contained in:
parent
e9c749d69b
commit
88b3e88bdd
|
@ -404,9 +404,6 @@ endif ()
|
||||||
# Include tests
|
# Include tests
|
||||||
if (WITH_TESTS)
|
if (WITH_TESTS)
|
||||||
enable_testing()
|
enable_testing()
|
||||||
if (UNIX AND (NOT USE_MMAP))
|
|
||||||
include("${ROOT_DIR}/test/testpaint/CMakeLists.txt" NO_POLICY_SCOPE)
|
|
||||||
endif ()
|
|
||||||
include("${ROOT_DIR}/test/tests/CMakeLists.txt" NO_POLICY_SCOPE)
|
include("${ROOT_DIR}/test/tests/CMakeLists.txt" NO_POLICY_SCOPE)
|
||||||
endif ()
|
endif ()
|
||||||
|
|
||||||
|
|
|
@ -38,9 +38,5 @@ else
|
||||||
export DESTDIR=install
|
export DESTDIR=install
|
||||||
cmake .. -G Ninja -DCMAKE_INSTALL_PREFIX=/usr "$@"
|
cmake .. -G Ninja -DCMAKE_INSTALL_PREFIX=/usr "$@"
|
||||||
|
|
||||||
if [[ "$TESTPAINT" == "true" ]]; then
|
ninja -k0 all install
|
||||||
testpaint_target=testpaint
|
|
||||||
fi
|
|
||||||
|
|
||||||
ninja -k0 $testpaint_target all install
|
|
||||||
fi
|
fi
|
||||||
|
|
|
@ -1,14 +0,0 @@
|
||||||
#!/usr/bin/env bash
|
|
||||||
|
|
||||||
set -e
|
|
||||||
|
|
||||||
# Ensure we are in root directory
|
|
||||||
basedir="$(readlink -f `dirname $0`/..)"
|
|
||||||
cd $basedir/bin
|
|
||||||
|
|
||||||
# Scan objects first so that does not happen within a test
|
|
||||||
echo -e "\033[0;36mBuilding OpenRCT2 repository indexes...\033[0m"
|
|
||||||
./openrct2 scan-objects
|
|
||||||
|
|
||||||
echo -e "\033[0;36mRunning OpenRCT2 testpaint tests...\033[0m"
|
|
||||||
./testpaint || true
|
|
|
@ -336,17 +336,3 @@ void PaintSessionGenerate(paint_session& session);
|
||||||
void PaintSessionArrange(PaintSessionCore& session);
|
void PaintSessionArrange(PaintSessionCore& session);
|
||||||
void PaintDrawStructs(paint_session& session);
|
void PaintDrawStructs(paint_session& session);
|
||||||
void PaintDrawMoneyStructs(rct_drawpixelinfo* dpi, paint_string_struct* ps);
|
void PaintDrawMoneyStructs(rct_drawpixelinfo* dpi, paint_string_struct* ps);
|
||||||
|
|
||||||
// TESTING
|
|
||||||
#ifdef __TESTPAINT__
|
|
||||||
void testpaint_clear_ignore();
|
|
||||||
void testpaint_ignore(uint8_t direction, uint8_t trackSequence);
|
|
||||||
void testpaint_ignore_all();
|
|
||||||
bool testpaint_is_ignored(uint8_t direction, uint8_t trackSequence);
|
|
||||||
|
|
||||||
# define TESTPAINT_IGNORE(direction, trackSequence) testpaint_ignore(direction, trackSequence)
|
|
||||||
# define TESTPAINT_IGNORE_ALL() testpaint_ignore_all()
|
|
||||||
#else
|
|
||||||
# define TESTPAINT_IGNORE(direction, trackSequence)
|
|
||||||
# define TESTPAINT_IGNORE_ALL()
|
|
||||||
#endif
|
|
||||||
|
|
|
@ -34,10 +34,6 @@
|
||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
|
|
||||||
#ifdef __TESTPAINT__
|
|
||||||
uint16_t testPaintVerticalTunnelHeight;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
static void blank_tiles_paint(paint_session& session, int32_t x, int32_t y);
|
static void blank_tiles_paint(paint_session& session, int32_t x, int32_t y);
|
||||||
static void PaintTileElementBase(paint_session& session, const CoordsXY& origCoords);
|
static void PaintTileElementBase(paint_session& session, const CoordsXY& origCoords);
|
||||||
|
|
||||||
|
@ -146,12 +142,11 @@ static void PaintTileElementBase(paint_session& session, const CoordsXY& origCoo
|
||||||
uint8_t rotation = session.CurrentRotation;
|
uint8_t rotation = session.CurrentRotation;
|
||||||
|
|
||||||
bool partOfVirtualFloor = false;
|
bool partOfVirtualFloor = false;
|
||||||
#ifndef __TESTPAINT__
|
|
||||||
if (gConfigGeneral.virtual_floor_style != VirtualFloorStyles::Off)
|
if (gConfigGeneral.virtual_floor_style != VirtualFloorStyles::Off)
|
||||||
{
|
{
|
||||||
partOfVirtualFloor = virtual_floor_tile_is_floor(session.MapPosition);
|
partOfVirtualFloor = virtual_floor_tile_is_floor(session.MapPosition);
|
||||||
}
|
}
|
||||||
#endif // __TESTPAINT__
|
|
||||||
|
|
||||||
switch (rotation)
|
switch (rotation)
|
||||||
{
|
{
|
||||||
|
@ -205,13 +200,11 @@ static void PaintTileElementBase(paint_session& session, const CoordsXY& origCoo
|
||||||
max_height = element->AsSurface()->GetWaterHeight();
|
max_height = element->AsSurface()->GetWaterHeight();
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifndef __TESTPAINT__
|
|
||||||
if (partOfVirtualFloor)
|
if (partOfVirtualFloor)
|
||||||
{
|
{
|
||||||
// We must pretend this tile is at least as tall as the virtual floor
|
// We must pretend this tile is at least as tall as the virtual floor
|
||||||
max_height = std::max(max_height, virtual_floor_get_height());
|
max_height = std::max(max_height, virtual_floor_get_height());
|
||||||
}
|
}
|
||||||
#endif // __TESTPAINT__
|
|
||||||
|
|
||||||
if (screenMinY - (max_height + 32) >= dpi->y + dpi->height)
|
if (screenMinY - (max_height + 32) >= dpi->y + dpi->height)
|
||||||
return;
|
return;
|
||||||
|
@ -295,12 +288,10 @@ static void PaintTileElementBase(paint_session& session, const CoordsXY& origCoo
|
||||||
session.MapPosition = mapPosition;
|
session.MapPosition = mapPosition;
|
||||||
} while (!(tile_element++)->IsLastForTile());
|
} while (!(tile_element++)->IsLastForTile());
|
||||||
|
|
||||||
#ifndef __TESTPAINT__
|
|
||||||
if (gConfigGeneral.virtual_floor_style != VirtualFloorStyles::Off && partOfVirtualFloor)
|
if (gConfigGeneral.virtual_floor_style != VirtualFloorStyles::Off && partOfVirtualFloor)
|
||||||
{
|
{
|
||||||
virtual_floor_paint(session);
|
virtual_floor_paint(session);
|
||||||
}
|
}
|
||||||
#endif // __TESTPAINT__
|
|
||||||
|
|
||||||
if (!gShowSupportSegmentHeights)
|
if (!gShowSupportSegmentHeights)
|
||||||
{
|
{
|
||||||
|
@ -370,9 +361,6 @@ void paint_util_push_tunnel_right(paint_session& session, uint16_t height, uint8
|
||||||
|
|
||||||
void paint_util_set_vertical_tunnel(paint_session& session, uint16_t height)
|
void paint_util_set_vertical_tunnel(paint_session& session, uint16_t height)
|
||||||
{
|
{
|
||||||
#ifdef __TESTPAINT__
|
|
||||||
testPaintVerticalTunnelHeight = height;
|
|
||||||
#endif
|
|
||||||
session.VerticalTunnelHeight = height / 16;
|
session.VerticalTunnelHeight = height / 16;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -77,10 +77,6 @@ namespace PaintSessionFlags
|
||||||
constexpr uint8_t IsTrackPiecePreview = 1u << 1;
|
constexpr uint8_t IsTrackPiecePreview = 1u << 1;
|
||||||
} // namespace PaintSessionFlags
|
} // namespace PaintSessionFlags
|
||||||
|
|
||||||
#ifdef __TESTPAINT__
|
|
||||||
extern uint16_t testPaintVerticalTunnelHeight;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
extern const int32_t SEGMENTS_ALL;
|
extern const int32_t SEGMENTS_ALL;
|
||||||
extern const uint16_t segment_offsets[9];
|
extern const uint16_t segment_offsets[9];
|
||||||
|
|
||||||
|
|
|
@ -260,8 +260,6 @@ static void paint_reverse_freefall_rc_slope(
|
||||||
paint_session& session, const Ride& ride, uint8_t trackSequence, uint8_t direction, int32_t height,
|
paint_session& session, const Ride& ride, uint8_t trackSequence, uint8_t direction, int32_t height,
|
||||||
const TrackElement& trackElement)
|
const TrackElement& trackElement)
|
||||||
{
|
{
|
||||||
TESTPAINT_IGNORE_ALL();
|
|
||||||
|
|
||||||
static constexpr const int8_t bbHeights03[] = { 1, 6, 14, 37, 64 };
|
static constexpr const int8_t bbHeights03[] = { 1, 6, 14, 37, 64 };
|
||||||
static constexpr const int8_t bbHeights12[] = { 1, 6, 14, 27, 59 };
|
static constexpr const int8_t bbHeights12[] = { 1, 6, 14, 27, 59 };
|
||||||
static constexpr const int32_t supportHeights[] = { 48, 64, 128, 176, 208, 240, 240 };
|
static constexpr const int32_t supportHeights[] = { 48, 64, 128, 176, 208, 240, 240 };
|
||||||
|
|
|
@ -116,11 +116,8 @@ static void paint_observation_tower_base(
|
||||||
paint_util_set_vertical_tunnel(session, height + 96);
|
paint_util_set_vertical_tunnel(session, height + 96);
|
||||||
paint_util_set_segment_support_height(session, SEGMENTS_ALL, 0xFFFF, 0);
|
paint_util_set_segment_support_height(session, SEGMENTS_ALL, 0xFFFF, 0);
|
||||||
|
|
||||||
#ifdef __TESTPAINT__
|
|
||||||
paint_util_set_general_support_height(session, height + 32, 0x20);
|
|
||||||
#else
|
|
||||||
paint_util_set_general_support_height(session, height + 96, 0x20);
|
paint_util_set_general_support_height(session, height + 96, 0x20);
|
||||||
#endif
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -135,11 +135,7 @@ static void paint_roto_drop_base(
|
||||||
paint_util_set_vertical_tunnel(session, height + 96);
|
paint_util_set_vertical_tunnel(session, height + 96);
|
||||||
paint_util_set_segment_support_height(session, SEGMENTS_ALL, 0xFFFF, 0);
|
paint_util_set_segment_support_height(session, SEGMENTS_ALL, 0xFFFF, 0);
|
||||||
|
|
||||||
#ifdef __TESTPAINT__
|
|
||||||
paint_util_set_general_support_height(session, height + 32, 0x20);
|
|
||||||
#else
|
|
||||||
paint_util_set_general_support_height(session, height + 96, 0x20);
|
paint_util_set_general_support_height(session, height + 96, 0x20);
|
||||||
#endif
|
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
|
@ -63,11 +63,7 @@ static void paint_lift_base(
|
||||||
paint_util_set_vertical_tunnel(session, height + 96);
|
paint_util_set_vertical_tunnel(session, height + 96);
|
||||||
paint_util_set_segment_support_height(session, SEGMENTS_ALL, 0xFFFF, 0);
|
paint_util_set_segment_support_height(session, SEGMENTS_ALL, 0xFFFF, 0);
|
||||||
|
|
||||||
#ifdef __TESTPAINT__
|
|
||||||
paint_util_set_general_support_height(session, height + 32, 0x20);
|
|
||||||
#else
|
|
||||||
paint_util_set_general_support_height(session, height + 96, 0x20);
|
paint_util_set_general_support_height(session, height + 96, 0x20);
|
||||||
#endif
|
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
|
@ -710,21 +710,11 @@ static void paint_splash_boats_track_25_deg_down_to_flat(
|
||||||
|
|
||||||
if (direction == 0 || direction == 3)
|
if (direction == 0 || direction == 3)
|
||||||
{
|
{
|
||||||
#ifdef __TESTPAINT__
|
|
||||||
// FIXME: For some reason, Testpaint does not detect this as an error.
|
|
||||||
paint_util_push_tunnel_rotated(session, direction, height - 8, TUNNEL_SQUARE_7);
|
|
||||||
#else
|
|
||||||
paint_util_push_tunnel_rotated(session, direction, height, TUNNEL_SQUARE_8);
|
paint_util_push_tunnel_rotated(session, direction, height, TUNNEL_SQUARE_8);
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
#ifdef __TESTPAINT__
|
|
||||||
// FIXME: For some reason, Testpaint does not detect this as an error.
|
|
||||||
paint_util_push_tunnel_rotated(session, direction, height + 24, TUNNEL_SQUARE_8);
|
|
||||||
#else
|
|
||||||
paint_util_push_tunnel_rotated(session, direction, height, TUNNEL_SQUARE_FLAT);
|
paint_util_push_tunnel_rotated(session, direction, height, TUNNEL_SQUARE_FLAT);
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
paint_util_set_segment_support_height(session, SEGMENTS_ALL, 0xFFFF, 0);
|
paint_util_set_segment_support_height(session, SEGMENTS_ALL, 0xFFFF, 0);
|
||||||
paint_util_set_general_support_height(session, height + 48, 0x20);
|
paint_util_set_general_support_height(session, height + 48, 0x20);
|
||||||
|
|
|
@ -1,80 +0,0 @@
|
||||||
/*****************************************************************************
|
|
||||||
* Copyright (c) 2014-2018 OpenRCT2 developers
|
|
||||||
*
|
|
||||||
* For a complete list of all authors, please refer to contributors.md
|
|
||||||
* Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2
|
|
||||||
*
|
|
||||||
* OpenRCT2 is licensed under the GNU General Public License version 3.
|
|
||||||
*****************************************************************************/
|
|
||||||
|
|
||||||
#include "Addresses.h"
|
|
||||||
|
|
||||||
#if defined(__GNUC__)
|
|
||||||
# ifdef __clang__
|
|
||||||
# define DISABLE_OPT __attribute__((noinline, optnone))
|
|
||||||
# else
|
|
||||||
# define DISABLE_OPT __attribute__((noinline, optimize("O0")))
|
|
||||||
# endif // __clang__
|
|
||||||
#else
|
|
||||||
# define DISABLE_OPT
|
|
||||||
#endif // defined(__GNUC__)
|
|
||||||
|
|
||||||
// This variable serves a purpose of identifying a crash if it has happened inside original code.
|
|
||||||
// When switching to original code, stack frame pointer is modified and prevents breakpad from providing stack trace.
|
|
||||||
volatile int32_t _originalAddress = 0;
|
|
||||||
|
|
||||||
int32_t DISABLE_OPT RCT2_CALLPROC_X(
|
|
||||||
int32_t address, int32_t _eax, int32_t _ebx, int32_t _ecx, int32_t _edx, int32_t _esi, int32_t _edi, int32_t _ebp)
|
|
||||||
{
|
|
||||||
int32_t result = 0;
|
|
||||||
_originalAddress = address;
|
|
||||||
#if defined(PLATFORM_X86) && !defined(NO_RCT2)
|
|
||||||
# ifdef _MSC_VER
|
|
||||||
__asm {
|
|
||||||
push ebp
|
|
||||||
push address
|
|
||||||
mov eax, _eax
|
|
||||||
mov ebx, _ebx
|
|
||||||
mov ecx, _ecx
|
|
||||||
mov edx, _edx
|
|
||||||
mov esi, _esi
|
|
||||||
mov edi, _edi
|
|
||||||
mov ebp, _ebp
|
|
||||||
call [esp]
|
|
||||||
lahf
|
|
||||||
pop ebp
|
|
||||||
pop ebp
|
|
||||||
/* Load result with flags */
|
|
||||||
mov result, eax
|
|
||||||
}
|
|
||||||
# else
|
|
||||||
// clang-format off
|
|
||||||
__asm__ volatile("\
|
|
||||||
\n\
|
|
||||||
push %%ebx \n\
|
|
||||||
push %%ebp \n\
|
|
||||||
push %[address] \n\
|
|
||||||
mov %[eax], %%eax \n\
|
|
||||||
mov %[ebx], %%ebx \n\
|
|
||||||
mov %[ecx], %%ecx \n\
|
|
||||||
mov %[edx], %%edx \n\
|
|
||||||
mov %[esi], %%esi \n\
|
|
||||||
mov %[edi], %%edi \n\
|
|
||||||
mov %[ebp], %%ebp \n\
|
|
||||||
call *(%%esp) \n\
|
|
||||||
lahf \n\
|
|
||||||
add $4, %%esp \n\
|
|
||||||
pop %%ebp \n\
|
|
||||||
pop %%ebx \n\
|
|
||||||
/* Load result with flags */ \n\
|
|
||||||
mov %%eax, %[result] \n\
|
|
||||||
" : [address] "+m" (address), [eax] "+m" (_eax), [ebx] "+m" (_ebx), [ecx] "+m" (_ecx), [edx] "+m" (_edx), [esi] "+m" (_esi), [edi] "+m" (_edi), [ebp] "+m" (_ebp), [result] "+m" (result)
|
|
||||||
:
|
|
||||||
: "eax","ecx","edx","esi","edi","memory");
|
|
||||||
// clang-format on
|
|
||||||
# endif // _MSC_VER
|
|
||||||
#endif // PLATFORM_X86
|
|
||||||
_originalAddress = 0;
|
|
||||||
// lahf only modifies ah, zero out the rest
|
|
||||||
return result & 0xFF00;
|
|
||||||
}
|
|
|
@ -1,42 +0,0 @@
|
||||||
/*****************************************************************************
|
|
||||||
* Copyright (c) 2014-2018 OpenRCT2 developers
|
|
||||||
*
|
|
||||||
* For a complete list of all authors, please refer to contributors.md
|
|
||||||
* Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2
|
|
||||||
*
|
|
||||||
* OpenRCT2 is licensed under the GNU General Public License version 3.
|
|
||||||
*****************************************************************************/
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <openrct2/common.h>
|
|
||||||
|
|
||||||
#ifdef USE_MMAP
|
|
||||||
# if defined(PLATFORM_64BIT)
|
|
||||||
# define GOOD_PLACE_FOR_DATA_SEGMENT ((uintptr_t)0x200000000)
|
|
||||||
# elif defined(PLATFORM_32BIT)
|
|
||||||
# define GOOD_PLACE_FOR_DATA_SEGMENT ((uintptr_t)0x09000000)
|
|
||||||
# else
|
|
||||||
# error "Unknown platform"
|
|
||||||
# endif
|
|
||||||
#else
|
|
||||||
# define GOOD_PLACE_FOR_DATA_SEGMENT ((uintptr_t)0x8a4000)
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#define RCT2_ADDRESS(address, type) ((type*)(GOOD_PLACE_FOR_DATA_SEGMENT - 0x8a4000 + (address)))
|
|
||||||
#define RCT2_GLOBAL(address, type) (*((type*)(GOOD_PLACE_FOR_DATA_SEGMENT - 0x8a4000 + (address))))
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the flags register
|
|
||||||
*
|
|
||||||
* Flags register is as follows:
|
|
||||||
* 0bSZ0A_0P0C_0000_0000
|
|
||||||
* S = Signed flag
|
|
||||||
* Z = Zero flag
|
|
||||||
* C = Carry flag
|
|
||||||
* A = Adjust flag
|
|
||||||
* P = Parity flag
|
|
||||||
* All other bits are undefined.
|
|
||||||
*/
|
|
||||||
int32_t RCT2_CALLPROC_X(
|
|
||||||
int32_t address, int32_t _eax, int32_t _ebx, int32_t _ecx, int32_t _edx, int32_t _esi, int32_t _edi, int32_t _ebp);
|
|
|
@ -1,105 +0,0 @@
|
||||||
# CMAKE project for testpaint
|
|
||||||
cmake_minimum_required(VERSION 2.6)
|
|
||||||
if (CMAKE_BINARY_DIR STREQUAL CMAKE_SOURCE_DIR)
|
|
||||||
message(FATAL_ERROR "Building in-source is not supported! Create a build dir and remove ${CMAKE_SOURCE_DIR}/CMakeCache.txt")
|
|
||||||
endif ()
|
|
||||||
|
|
||||||
set(OPENRCT2_EXE "${ROOT_DIR}/openrct2.exe")
|
|
||||||
add_custom_command(
|
|
||||||
OUTPUT openrct2_text
|
|
||||||
COMMAND dd if="${OPENRCT2_EXE}" of="${CMAKE_BINARY_DIR}/openrct2_text" bs=4096 skip=1 count=1187
|
|
||||||
DEPENDS ${OPENRCT2_EXE}
|
|
||||||
)
|
|
||||||
add_custom_command(
|
|
||||||
OUTPUT openrct2_data
|
|
||||||
COMMAND dd if="${OPENRCT2_EXE}" of="${CMAKE_BINARY_DIR}/openrct2_data" bs=4096 skip=1188 count=318
|
|
||||||
COMMAND dd if=/dev/zero of="${CMAKE_BINARY_DIR}/openrct2_data" bs=4096 seek=318 count=2630 conv=notrunc
|
|
||||||
COMMAND dd if="${OPENRCT2_EXE}" of="${CMAKE_BINARY_DIR}/openrct2_data" bs=4096 skip=1506 seek=2948 count=1 conv=notrunc
|
|
||||||
DEPENDS ${OPENRCT2_EXE}
|
|
||||||
)
|
|
||||||
add_custom_target(segfiles DEPENDS openrct2_text openrct2_data)
|
|
||||||
if (NOT USE_MMAP AND FORCE32)
|
|
||||||
set(OBJ_FORMAT "elf32-i386")
|
|
||||||
set(LINKER_SCRIPT "ld_script_i386.xc")
|
|
||||||
if (APPLE)
|
|
||||||
set(RCT2_SEGMENT_LINKER_FLAGS "-sectcreate rct2_text __text ${CMAKE_BINARY_DIR}/openrct2_text -sectcreate rct2_data __data ${CMAKE_BINARY_DIR}/openrct2_data -segaddr rct2_data 0x8a4000 -segprot rct2_data rwx rwx -segaddr rct2_text 0x401000 -segprot rct2_text rwx rwx -segaddr __TEXT 0x2000000 -read_only_relocs suppress")
|
|
||||||
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
|
|
||||||
# bespoke linker script so they can be placed at predefined VMAs.
|
|
||||||
add_custom_command(
|
|
||||||
OUTPUT openrct2_text_section.o
|
|
||||||
COMMAND objcopy --input binary --output ${OBJ_FORMAT} --binary-architecture i386 openrct2_text openrct2_text_section.o --rename-section .data=.rct2_text,contents,alloc,load,readonly,code
|
|
||||||
DEPENDS segfiles
|
|
||||||
WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
|
|
||||||
)
|
|
||||||
add_custom_command(
|
|
||||||
OUTPUT openrct2_data_section.o
|
|
||||||
COMMAND objcopy --input binary --output ${OBJ_FORMAT} --binary-architecture i386 openrct2_data openrct2_data_section.o --rename-section .data=.rct2_data,contents,alloc,load,readonly,data
|
|
||||||
DEPENDS segfiles
|
|
||||||
WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
|
|
||||||
)
|
|
||||||
add_custom_target(linkable_sections DEPENDS openrct2_text_section.o openrct2_data_section.o)
|
|
||||||
set_source_files_properties(
|
|
||||||
openrct2_text_section.o openrct2_data_section.o
|
|
||||||
PROPERTIES
|
|
||||||
EXTERNAL_OBJECT true
|
|
||||||
GENERATED true
|
|
||||||
)
|
|
||||||
# can't use GLOB here, as the files don't exist yet at cmake-time
|
|
||||||
set(RCT2_SECTIONS "${CMAKE_BINARY_DIR}/openrct2_data_section.o" "${CMAKE_BINARY_DIR}/openrct2_text_section.o")
|
|
||||||
set(RCT2_SEGMENT_LINKER_FLAGS "-Wl,-T,\"${ROOT_DIR}/distribution/linux/${LINKER_SCRIPT}\"")
|
|
||||||
endif ()
|
|
||||||
endif ()
|
|
||||||
|
|
||||||
set(OPENRCT2_SRCPATH "${ROOT_DIR}/src/openrct2")
|
|
||||||
file(GLOB_RECURSE ORCT2_RIDE_SOURCES "${OPENRCT2_SRCPATH}/ride/*/*.cpp")
|
|
||||||
file(GLOB_RECURSE ORCT2_RIDE_DEP_SOURCES
|
|
||||||
"${OPENRCT2_SRCPATH}/Diagnostic.cpp"
|
|
||||||
"${OPENRCT2_SRCPATH}/paint/PaintHelpers.cpp"
|
|
||||||
"${OPENRCT2_SRCPATH}/paint/tile_element/Paint.TileElement.cpp"
|
|
||||||
"${OPENRCT2_SRCPATH}/ride/RideData.cpp"
|
|
||||||
"${OPENRCT2_SRCPATH}/ride/TrackData.cpp"
|
|
||||||
"${OPENRCT2_SRCPATH}/ride/TrackPaint.cpp"
|
|
||||||
"${OPENRCT2_SRCPATH}/core/Console.cpp"
|
|
||||||
"${OPENRCT2_SRCPATH}/core/Diagnostics.cpp"
|
|
||||||
"${OPENRCT2_SRCPATH}/core/Guard.cpp"
|
|
||||||
"${OPENRCT2_SRCPATH}/core/String.cpp"
|
|
||||||
"${OPENRCT2_SRCPATH}/Diagnostic.cpp"
|
|
||||||
"${OPENRCT2_SRCPATH}/interface/ZoomLevel.cpp"
|
|
||||||
"${OPENRCT2_SRCPATH}/localisation/ConversionTables.cpp"
|
|
||||||
"${OPENRCT2_SRCPATH}/localisation/Convert.cpp"
|
|
||||||
"${OPENRCT2_SRCPATH}/localisation/FormatCodes.cpp"
|
|
||||||
"${OPENRCT2_SRCPATH}/localisation/UTF8.cpp"
|
|
||||||
"${OPENRCT2_SRCPATH}/util/Util.cpp"
|
|
||||||
"${OPENRCT2_SRCPATH}/Version.cpp"
|
|
||||||
)
|
|
||||||
file(GLOB_RECURSE ORCT2_TESTPAINT_SOURCES "${CMAKE_CURRENT_LIST_DIR}/*.c"
|
|
||||||
"${CMAKE_CURRENT_LIST_DIR}/*.cpp"
|
|
||||||
"${CMAKE_CURRENT_LIST_DIR}/*.h")
|
|
||||||
|
|
||||||
# Disable optimizations for addresses.c for all compilers, to allow optimized
|
|
||||||
# builds without need for -fno-omit-frame-pointer
|
|
||||||
set_source_files_properties(${CMAKE_CURRENT_LIST_DIR}/addresses.c PROPERTIES COMPILE_FLAGS -O0)
|
|
||||||
|
|
||||||
add_executable(testpaint EXCLUDE_FROM_ALL ${ORCT2_RIDE_SOURCES} ${ORCT2_RIDE_DEP_SOURCES} ${ORCT2_TESTPAINT_SOURCES} ${RCT2_SECTIONS})
|
|
||||||
SET_CHECK_CXX_FLAGS(testpaint)
|
|
||||||
target_compile_options(testpaint PRIVATE -Wno-old-style-cast)
|
|
||||||
target_include_directories(testpaint PRIVATE "${ROOT_DIR}/src/")
|
|
||||||
target_link_libraries(testpaint z)
|
|
||||||
|
|
||||||
if (NOT MINGW AND NOT MSVC)
|
|
||||||
# For unicode code page conversion
|
|
||||||
find_package(ICU 59.0 REQUIRED COMPONENTS uc)
|
|
||||||
target_link_libraries(testpaint ${ICU_LIBRARIES})
|
|
||||||
target_include_directories(testpaint SYSTEM PUBLIC ${ICU_INCLUDE_DIRS})
|
|
||||||
endif ()
|
|
||||||
|
|
||||||
# Only use custom linker script for 32 bit builds. For 64 bit builds, it should still _compile_.
|
|
||||||
if (FORCE32)
|
|
||||||
set_target_properties(testpaint PROPERTIES LINK_FLAGS ${RCT2_SEGMENT_LINKER_FLAGS})
|
|
||||||
else ()
|
|
||||||
set(TESTPAINT_64BIT_FLAGS "-Wno-int-to-pointer-cast -fpermissive -Wno-error")
|
|
||||||
endif ()
|
|
||||||
set_target_properties(testpaint PROPERTIES COMPILE_FLAGS "-DNO_VEHICLES -D__TESTPAINT__ -Wno-unused ${TESTPAINT_64BIT_FLAGS} -DDISABLE_HTTP")
|
|
||||||
add_dependencies(testpaint segfiles)
|
|
|
@ -1,903 +0,0 @@
|
||||||
/*****************************************************************************
|
|
||||||
* Copyright (c) 2014-2018 OpenRCT2 developers
|
|
||||||
*
|
|
||||||
* For a complete list of all authors, please refer to contributors.md
|
|
||||||
* Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2
|
|
||||||
*
|
|
||||||
* OpenRCT2 is licensed under the GNU General Public License version 3.
|
|
||||||
*****************************************************************************/
|
|
||||||
|
|
||||||
#include "Addresses.h"
|
|
||||||
|
|
||||||
#include <array>
|
|
||||||
#include <openrct2/Context.h>
|
|
||||||
#include <openrct2/config/Config.h>
|
|
||||||
#include <openrct2/interface/Colour.h>
|
|
||||||
#include <openrct2/interface/Viewport.h>
|
|
||||||
#include <openrct2/object/Object.h>
|
|
||||||
#include <openrct2/paint/tile_element/Paint.TileElement.h>
|
|
||||||
#include <openrct2/ride/Ride.h>
|
|
||||||
#include <openrct2/ride/RideData.h>
|
|
||||||
#include <openrct2/ride/Station.h>
|
|
||||||
#include <openrct2/ride/Track.h>
|
|
||||||
#include <openrct2/ride/Vehicle.h>
|
|
||||||
#include <openrct2/world/Location.hpp>
|
|
||||||
#include <openrct2/world/Sprite.h>
|
|
||||||
#include <openrct2/world/Surface.h>
|
|
||||||
|
|
||||||
class StationObject;
|
|
||||||
|
|
||||||
#define gRideEntries RCT2_ADDRESS(0x009ACFA4, rct_ride_entry*)
|
|
||||||
#define gTileElementTilePointers RCT2_ADDRESS(0x013CE9A4, TileElement*)
|
|
||||||
rct_sprite* sprite_list = RCT2_ADDRESS(0x010E63BC, rct_sprite);
|
|
||||||
|
|
||||||
bool gCheatsEnableAllDrawableTrackPieces = false;
|
|
||||||
|
|
||||||
Ride gRideList[MAX_RIDES];
|
|
||||||
int16_t gMapBaseZ;
|
|
||||||
bool gTrackDesignSaveMode = false;
|
|
||||||
ride_id_t gTrackDesignSaveRideIndex = RIDE_ID_NULL;
|
|
||||||
uint8_t gClipHeight = 255;
|
|
||||||
CoordsXY gClipSelectionA = { 0, 0 };
|
|
||||||
CoordsXY gClipSelectionB = { MAXIMUM_TILE_START_XY, MAXIMUM_TILE_START_XY };
|
|
||||||
uint32_t gScenarioTicks;
|
|
||||||
uint8_t gCurrentRotation;
|
|
||||||
|
|
||||||
// clang-format off
|
|
||||||
constexpr const std::array<CoordsXY, 8> CoordsDirectionDelta = {
|
|
||||||
CoordsXY{ -COORDS_XY_STEP, 0 },
|
|
||||||
CoordsXY{ 0, +COORDS_XY_STEP },
|
|
||||||
CoordsXY{ +COORDS_XY_STEP, 0 },
|
|
||||||
CoordsXY{ 0, -COORDS_XY_STEP },
|
|
||||||
CoordsXY{ -COORDS_XY_STEP, +COORDS_XY_STEP },
|
|
||||||
CoordsXY{ +COORDS_XY_STEP, +COORDS_XY_STEP },
|
|
||||||
CoordsXY{ +COORDS_XY_STEP, -COORDS_XY_STEP },
|
|
||||||
CoordsXY{ -COORDS_XY_STEP, -COORDS_XY_STEP },
|
|
||||||
};
|
|
||||||
|
|
||||||
const TileCoordsXY TileDirectionDelta[] = {
|
|
||||||
{ -1, 0 },
|
|
||||||
{ 0, +1 },
|
|
||||||
{ +1, 0 },
|
|
||||||
{ 0, -1 },
|
|
||||||
{ -1, +1 },
|
|
||||||
{ +1, +1 },
|
|
||||||
{ +1, -1 },
|
|
||||||
{ -1, -1 },
|
|
||||||
};
|
|
||||||
// clang-format on
|
|
||||||
|
|
||||||
uint8_t get_current_rotation()
|
|
||||||
{
|
|
||||||
return gCurrentRotation & 3;
|
|
||||||
}
|
|
||||||
|
|
||||||
int object_entry_group_counts[] = {
|
|
||||||
128, // ObjectType::Ride
|
|
||||||
252, // ObjectType::SmallScenery
|
|
||||||
128, // ObjectType::LargeScenery
|
|
||||||
128, // ObjectType::Walls
|
|
||||||
32, // ObjectType::Banners
|
|
||||||
16, // ObjectType::Paths
|
|
||||||
15, // ObjectType::PathBits
|
|
||||||
19, // ObjectType::SceneryGroup
|
|
||||||
1, // ObjectType::ParkEntrance
|
|
||||||
1, // ObjectType::Water
|
|
||||||
1, // ObjectType::ScenarioText
|
|
||||||
0, // ObjectType::TerrainSurface
|
|
||||||
0, // ObjectType::TerrainEdge
|
|
||||||
0, // ObjectType::Station
|
|
||||||
0, // ObjectType::Music
|
|
||||||
0, // ObjectType::FootpathSurface
|
|
||||||
0, // ObjectType::FootpathRailings
|
|
||||||
};
|
|
||||||
static_assert(std::size(object_entry_group_counts) == EnumValue(ObjectType::Count));
|
|
||||||
|
|
||||||
GeneralConfiguration gConfigGeneral;
|
|
||||||
uint16_t gMapSelectFlags;
|
|
||||||
uint16_t gMapSelectType;
|
|
||||||
CoordsXY gMapSelectPositionA;
|
|
||||||
CoordsXY gMapSelectPositionB;
|
|
||||||
CoordsXYZ gMapSelectArrowPosition;
|
|
||||||
uint8_t gMapSelectArrowDirection;
|
|
||||||
|
|
||||||
void entrance_paint(paint_session* session, uint8_t direction, int height, const TileElement* tile_element)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
void banner_paint(paint_session* session, uint8_t direction, int height, const TileElement* tile_element)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
void surface_paint(paint_session* session, uint8_t direction, uint16_t height, const TileElement* tileElement)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
void path_paint(paint_session* session, uint16_t height, const TileElement* tileElement)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
void scenery_paint(paint_session* session, uint8_t direction, int height, const TileElement* tileElement)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
void fence_paint(paint_session* session, uint8_t direction, int height, const TileElement* tileElement)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
void large_scenery_paint(paint_session* session, uint8_t direction, uint16_t height, const TileElement* tileElement)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
Ride* get_ride(ride_id_t index)
|
|
||||||
{
|
|
||||||
if (index >= RCT12_MAX_RIDES_IN_PARK)
|
|
||||||
{
|
|
||||||
log_error("invalid index %d for ride", index);
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
return &gRideList[index];
|
|
||||||
}
|
|
||||||
|
|
||||||
rct_ride_entry* get_ride_entry(ObjectEntryIndex index)
|
|
||||||
{
|
|
||||||
if (index >= object_entry_group_counts[static_cast<int>(ObjectType::Ride)])
|
|
||||||
{
|
|
||||||
log_error("invalid index %d for ride type", index);
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
return gRideEntries[index];
|
|
||||||
}
|
|
||||||
|
|
||||||
rct_ride_entry* Ride::GetRideEntry() const
|
|
||||||
{
|
|
||||||
rct_ride_entry* rideEntry = get_ride_entry(subtype);
|
|
||||||
if (rideEntry == nullptr)
|
|
||||||
{
|
|
||||||
log_error("Invalid ride subtype for ride");
|
|
||||||
}
|
|
||||||
return rideEntry;
|
|
||||||
}
|
|
||||||
|
|
||||||
template<> bool SpriteBase::Is<SpriteBase>() const
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
template<> bool SpriteBase::Is<Peep>() const
|
|
||||||
{
|
|
||||||
return Type == EntityType::Guest || Type == EntityType::Staff;
|
|
||||||
}
|
|
||||||
|
|
||||||
template<> bool SpriteBase::Is<Guest>() const
|
|
||||||
{
|
|
||||||
return Type == EntityType::Guest;
|
|
||||||
}
|
|
||||||
|
|
||||||
template<> bool SpriteBase::Is<Vehicle>() const
|
|
||||||
{
|
|
||||||
return Type == EntityType::Vehicle;
|
|
||||||
}
|
|
||||||
|
|
||||||
SpriteBase* get_sprite(size_t sprite_idx)
|
|
||||||
{
|
|
||||||
assert(sprite_idx < MAX_ENTITIES);
|
|
||||||
return reinterpret_cast<SpriteBase*>(&sprite_list[sprite_idx]);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool TileElementBase::IsLastForTile() const
|
|
||||||
{
|
|
||||||
return (this->Flags & TILE_ELEMENT_FLAG_LAST_TILE) != 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
void TileElementBase::SetLastForTile(bool on)
|
|
||||||
{
|
|
||||||
if (on)
|
|
||||||
Flags |= TILE_ELEMENT_FLAG_LAST_TILE;
|
|
||||||
else
|
|
||||||
Flags &= ~TILE_ELEMENT_FLAG_LAST_TILE;
|
|
||||||
}
|
|
||||||
|
|
||||||
uint8_t TileElementBase::GetType() const
|
|
||||||
{
|
|
||||||
return this->type & TILE_ELEMENT_TYPE_MASK;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool TileElementBase::IsGhost() const
|
|
||||||
{
|
|
||||||
return (this->Flags & TILE_ELEMENT_FLAG_GHOST) != 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool TrackElement::BlockBrakeClosed() const
|
|
||||||
{
|
|
||||||
return (Flags2 & TRACK_ELEMENT_FLAGS2_BLOCK_BRAKE_CLOSED) != 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
TileElement* map_get_first_element_at(const CoordsXY& elementPos)
|
|
||||||
{
|
|
||||||
if (elementPos.x < 0 || elementPos.y < 0 || elementPos.x > 255 || elementPos.y > 255)
|
|
||||||
{
|
|
||||||
log_error("Trying to access element outside of range");
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
auto tileElementPos = TileCoordsXY{ elementPos };
|
|
||||||
return gTileElementTilePointers[tileElementPos.x + tileElementPos.y * 256];
|
|
||||||
}
|
|
||||||
|
|
||||||
int32_t get_height_marker_offset()
|
|
||||||
{
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool is_csg_loaded()
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
uint8_t TrackElement::GetSeatRotation() const
|
|
||||||
{
|
|
||||||
const auto* ride = get_ride(GetRideIndex());
|
|
||||||
if (ride != nullptr && ride->GetRideTypeDescriptor().HasFlag(RIDE_TYPE_FLAG_HAS_LANDSCAPE_DOORS))
|
|
||||||
return DEFAULT_SEAT_ROTATION;
|
|
||||||
|
|
||||||
return ColourScheme >> 4;
|
|
||||||
}
|
|
||||||
|
|
||||||
void TrackElement::SetSeatRotation(uint8_t newSeatRotation)
|
|
||||||
{
|
|
||||||
ColourScheme &= ~TRACK_ELEMENT_COLOUR_SEAT_ROTATION_MASK;
|
|
||||||
ColourScheme |= (newSeatRotation << 4);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool TrackElement::IsTakingPhoto() const
|
|
||||||
{
|
|
||||||
return OnridePhotoBits != 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
void TrackElement::SetPhotoTimeout()
|
|
||||||
{
|
|
||||||
OnridePhotoBits = 3;
|
|
||||||
}
|
|
||||||
|
|
||||||
void TrackElement::SetPhotoTimeout(uint8_t value)
|
|
||||||
{
|
|
||||||
OnridePhotoBits = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
uint8_t TrackElement::GetPhotoTimeout() const
|
|
||||||
{
|
|
||||||
return OnridePhotoBits;
|
|
||||||
}
|
|
||||||
|
|
||||||
void TrackElement::DecrementPhotoTimeout()
|
|
||||||
{
|
|
||||||
OnridePhotoBits = std::max(0, OnridePhotoBits - 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
uint16_t TrackElement::GetMazeEntry() const
|
|
||||||
{
|
|
||||||
return MazeEntry;
|
|
||||||
}
|
|
||||||
|
|
||||||
void TrackElement::SetMazeEntry(uint16_t newMazeEntry)
|
|
||||||
{
|
|
||||||
MazeEntry = newMazeEntry;
|
|
||||||
}
|
|
||||||
|
|
||||||
void TrackElement::MazeEntryAdd(uint16_t addVal)
|
|
||||||
{
|
|
||||||
MazeEntry |= addVal;
|
|
||||||
}
|
|
||||||
|
|
||||||
void TrackElement::MazeEntrySubtract(uint16_t subVal)
|
|
||||||
{
|
|
||||||
MazeEntry &= ~subVal;
|
|
||||||
}
|
|
||||||
|
|
||||||
track_type_t TrackElement::GetTrackType() const
|
|
||||||
{
|
|
||||||
return TrackType;
|
|
||||||
}
|
|
||||||
|
|
||||||
void TrackElement::SetTrackType(track_type_t newType)
|
|
||||||
{
|
|
||||||
TrackType = newType;
|
|
||||||
}
|
|
||||||
|
|
||||||
uint8_t TrackElement::GetSequenceIndex() const
|
|
||||||
{
|
|
||||||
return Sequence;
|
|
||||||
}
|
|
||||||
|
|
||||||
void TrackElement::SetSequenceIndex(uint8_t newSequenceIndex)
|
|
||||||
{
|
|
||||||
Sequence = newSequenceIndex;
|
|
||||||
}
|
|
||||||
|
|
||||||
uint8_t TrackElement::GetStationIndex() const
|
|
||||||
{
|
|
||||||
return StationIndex;
|
|
||||||
}
|
|
||||||
|
|
||||||
void TrackElement::SetStationIndex(uint8_t newStationIndex)
|
|
||||||
{
|
|
||||||
StationIndex = newStationIndex;
|
|
||||||
}
|
|
||||||
|
|
||||||
uint8_t TrackElement::GetDoorAState() const
|
|
||||||
{
|
|
||||||
return (ColourScheme & TRACK_ELEMENT_COLOUR_DOOR_A_MASK) >> 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
uint8_t TrackElement::GetDoorBState() const
|
|
||||||
{
|
|
||||||
return (ColourScheme & TRACK_ELEMENT_COLOUR_DOOR_B_MASK) >> 5;
|
|
||||||
}
|
|
||||||
|
|
||||||
ride_id_t TrackElement::GetRideIndex() const
|
|
||||||
{
|
|
||||||
return RideIndex;
|
|
||||||
}
|
|
||||||
|
|
||||||
void TrackElement::SetRideIndex(ride_id_t newRideIndex)
|
|
||||||
{
|
|
||||||
RideIndex = newRideIndex;
|
|
||||||
}
|
|
||||||
|
|
||||||
uint8_t TrackElement::GetColourScheme() const
|
|
||||||
{
|
|
||||||
return ColourScheme & TRACK_ELEMENT_COLOUR_SCHEME_MASK;
|
|
||||||
}
|
|
||||||
|
|
||||||
void TrackElement::SetColourScheme(uint8_t newColourScheme)
|
|
||||||
{
|
|
||||||
ColourScheme &= ~TRACK_ELEMENT_COLOUR_SCHEME_MASK;
|
|
||||||
ColourScheme |= (newColourScheme & TRACK_ELEMENT_COLOUR_SCHEME_MASK);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool TrackElement::HasCableLift() const
|
|
||||||
{
|
|
||||||
return Flags2 & TRACK_ELEMENT_FLAGS2_CABLE_LIFT;
|
|
||||||
}
|
|
||||||
|
|
||||||
void TrackElement::SetHasCableLift(bool on)
|
|
||||||
{
|
|
||||||
Flags2 &= ~TRACK_ELEMENT_FLAGS2_CABLE_LIFT;
|
|
||||||
if (on)
|
|
||||||
Flags2 |= TRACK_ELEMENT_FLAGS2_CABLE_LIFT;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool TrackElement::IsInverted() const
|
|
||||||
{
|
|
||||||
return Flags2 & TRACK_ELEMENT_FLAGS2_INVERTED;
|
|
||||||
}
|
|
||||||
|
|
||||||
void TrackElement::SetInverted(bool inverted)
|
|
||||||
{
|
|
||||||
if (inverted)
|
|
||||||
{
|
|
||||||
Flags2 |= TRACK_ELEMENT_FLAGS2_INVERTED;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
Flags2 &= ~TRACK_ELEMENT_FLAGS2_INVERTED;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
uint8_t TrackElement::GetBrakeBoosterSpeed() const
|
|
||||||
{
|
|
||||||
return BrakeBoosterSpeed << 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
void TrackElement::SetBrakeBoosterSpeed(uint8_t speed)
|
|
||||||
{
|
|
||||||
BrakeBoosterSpeed = (speed >> 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool TrackElement::HasGreenLight() const
|
|
||||||
{
|
|
||||||
return (Flags2 & TRACK_ELEMENT_FLAGS2_HAS_GREEN_LIGHT) != 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
void TrackElement::SetHasGreenLight(bool on)
|
|
||||||
{
|
|
||||||
Flags2 &= ~TRACK_ELEMENT_FLAGS2_HAS_GREEN_LIGHT;
|
|
||||||
if (on)
|
|
||||||
{
|
|
||||||
Flags2 |= TRACK_ELEMENT_FLAGS2_HAS_GREEN_LIGHT;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool TrackElement::HasChain() const
|
|
||||||
{
|
|
||||||
return (Flags2 & TRACK_ELEMENT_FLAGS2_CHAIN_LIFT) != 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
void TrackElement::SetHasChain(bool on)
|
|
||||||
{
|
|
||||||
if (on)
|
|
||||||
{
|
|
||||||
Flags2 |= TRACK_ELEMENT_FLAGS2_CHAIN_LIFT;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
Flags2 &= ~TRACK_ELEMENT_FLAGS2_CHAIN_LIFT;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
TileCoordsXYZD ride_get_entrance_location(const Ride* ride, const StationIndex stationIndex)
|
|
||||||
{
|
|
||||||
return ride->stations[stationIndex].Entrance;
|
|
||||||
}
|
|
||||||
|
|
||||||
TileCoordsXYZD ride_get_exit_location(const Ride* ride, const StationIndex stationIndex)
|
|
||||||
{
|
|
||||||
return ride->stations[stationIndex].Exit;
|
|
||||||
}
|
|
||||||
|
|
||||||
void TileElementBase::SetType(uint8_t newType)
|
|
||||||
{
|
|
||||||
this->type &= ~TILE_ELEMENT_TYPE_MASK;
|
|
||||||
this->type |= (newType & TILE_ELEMENT_TYPE_MASK);
|
|
||||||
}
|
|
||||||
|
|
||||||
uint8_t TileElementBase::GetDirection() const
|
|
||||||
{
|
|
||||||
return this->type & TILE_ELEMENT_DIRECTION_MASK;
|
|
||||||
}
|
|
||||||
|
|
||||||
uint8_t TileElementBase::GetDirectionWithOffset(uint8_t offset) const
|
|
||||||
{
|
|
||||||
return ((this->type & TILE_ELEMENT_DIRECTION_MASK) + offset) & TILE_ELEMENT_DIRECTION_MASK;
|
|
||||||
}
|
|
||||||
|
|
||||||
uint8_t SurfaceElement::GetSlope() const
|
|
||||||
{
|
|
||||||
return Slope;
|
|
||||||
}
|
|
||||||
|
|
||||||
int32_t SurfaceElement::GetWaterHeight() const
|
|
||||||
{
|
|
||||||
return WaterHeight * 16;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool TrackElement::IsHighlighted() const
|
|
||||||
{
|
|
||||||
return (Flags2 & TRACK_ELEMENT_FLAGS2_HIGHLIGHT);
|
|
||||||
}
|
|
||||||
|
|
||||||
uint8_t PathElement::GetEdges() const
|
|
||||||
{
|
|
||||||
return EdgesAndCorners & 0xF;
|
|
||||||
}
|
|
||||||
|
|
||||||
StationObject* ride_get_station_object(const Ride* ride)
|
|
||||||
{
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
Ride* Vehicle::GetRide() const
|
|
||||||
{
|
|
||||||
return get_ride(ride);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Vehicle::IsGhost() const
|
|
||||||
{
|
|
||||||
auto r = GetRide();
|
|
||||||
return r != nullptr && r->status == RideStatus::Simulating;
|
|
||||||
}
|
|
||||||
|
|
||||||
uint8_t TileElementBase::GetOccupiedQuadrants() const
|
|
||||||
{
|
|
||||||
return Flags & TILE_ELEMENT_OCCUPIED_QUADRANTS_MASK;
|
|
||||||
}
|
|
||||||
|
|
||||||
void TileElementBase::SetOccupiedQuadrants(uint8_t quadrants)
|
|
||||||
{
|
|
||||||
Flags &= ~TILE_ELEMENT_OCCUPIED_QUADRANTS_MASK;
|
|
||||||
Flags |= (quadrants & TILE_ELEMENT_OCCUPIED_QUADRANTS_MASK);
|
|
||||||
}
|
|
||||||
|
|
||||||
int32_t TileElementBase::GetBaseZ() const
|
|
||||||
{
|
|
||||||
return base_height * COORDS_Z_STEP;
|
|
||||||
}
|
|
||||||
|
|
||||||
void TileElementBase::SetBaseZ(int32_t newZ)
|
|
||||||
{
|
|
||||||
base_height = (newZ / COORDS_Z_STEP);
|
|
||||||
}
|
|
||||||
|
|
||||||
int32_t TileElementBase::GetClearanceZ() const
|
|
||||||
{
|
|
||||||
return clearance_height * COORDS_Z_STEP;
|
|
||||||
}
|
|
||||||
|
|
||||||
void TileElementBase::SetClearanceZ(int32_t newZ)
|
|
||||||
{
|
|
||||||
clearance_height = (newZ / COORDS_Z_STEP);
|
|
||||||
}
|
|
||||||
|
|
||||||
int32_t RideStation::GetBaseZ() const
|
|
||||||
{
|
|
||||||
return Height * COORDS_Z_STEP;
|
|
||||||
}
|
|
||||||
|
|
||||||
void RideStation::SetBaseZ(int32_t newZ)
|
|
||||||
{
|
|
||||||
Height = newZ / COORDS_Z_STEP;
|
|
||||||
}
|
|
||||||
|
|
||||||
CoordsXYZ RideStation::GetStart() const
|
|
||||||
{
|
|
||||||
TileCoordsXYZ stationTileCoords{ Start.x, Start.y, Height };
|
|
||||||
return stationTileCoords.ToCoordsXYZ();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool TrackElement::IsStation() const
|
|
||||||
{
|
|
||||||
return track_type_is_station(GetTrackType());
|
|
||||||
}
|
|
||||||
|
|
||||||
bool track_type_is_station(track_type_t trackType)
|
|
||||||
{
|
|
||||||
switch (trackType)
|
|
||||||
{
|
|
||||||
case TrackElemType::EndStation:
|
|
||||||
case TrackElemType::BeginStation:
|
|
||||||
case TrackElemType::MiddleStation:
|
|
||||||
return true;
|
|
||||||
default:
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void ride_ratings_calculate_spiral_roller_coaster([[maybe_unused]] Ride* ride)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
void ride_ratings_calculate_stand_up_roller_coaster([[maybe_unused]] Ride* ride)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
void ride_ratings_calculate_suspended_swinging_coaster([[maybe_unused]] Ride* ride)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
void ride_ratings_calculate_inverted_roller_coaster([[maybe_unused]] Ride* ride)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
void ride_ratings_calculate_junior_roller_coaster([[maybe_unused]] Ride* ride)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
void ride_ratings_calculate_miniature_railway([[maybe_unused]] Ride* ride)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
void ride_ratings_calculate_monorail([[maybe_unused]] Ride* ride)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
void ride_ratings_calculate_mini_suspended_coaster([[maybe_unused]] Ride* ride)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
void ride_ratings_calculate_boat_hire([[maybe_unused]] Ride* ride)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
void ride_ratings_calculate_wooden_wild_mouse([[maybe_unused]] Ride* ride)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
void ride_ratings_calculate_steeplechase([[maybe_unused]] Ride* ride)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
void ride_ratings_calculate_car_ride([[maybe_unused]] Ride* ride)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
void ride_ratings_calculate_launched_freefall([[maybe_unused]] Ride* ride)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
void ride_ratings_calculate_bobsleigh_coaster([[maybe_unused]] Ride* ride)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
void ride_ratings_calculate_observation_tower([[maybe_unused]] Ride* ride)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
void ride_ratings_calculate_looping_roller_coaster([[maybe_unused]] Ride* ride)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
void ride_ratings_calculate_dinghy_slide([[maybe_unused]] Ride* ride)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
void ride_ratings_calculate_mine_train_coaster([[maybe_unused]] Ride* ride)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
void ride_ratings_calculate_chairlift([[maybe_unused]] Ride* ride)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
void ride_ratings_calculate_corkscrew_roller_coaster([[maybe_unused]] Ride* ride)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
void ride_ratings_calculate_maze([[maybe_unused]] Ride* ride)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
void ride_ratings_calculate_spiral_slide([[maybe_unused]] Ride* ride)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
void ride_ratings_calculate_go_karts([[maybe_unused]] Ride* ride)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
void ride_ratings_calculate_log_flume([[maybe_unused]] Ride* ride)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
void ride_ratings_calculate_river_rapids([[maybe_unused]] Ride* ride)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
void ride_ratings_calculate_dodgems([[maybe_unused]] Ride* ride)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
void ride_ratings_calculate_swinging_ship([[maybe_unused]] Ride* ride)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
void ride_ratings_calculate_inverter_ship([[maybe_unused]] Ride* ride)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
void ride_ratings_calculate_food_stall([[maybe_unused]] Ride* ride)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
void ride_ratings_calculate_shop([[maybe_unused]] Ride* ride)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
void ride_ratings_calculate_merry_go_round([[maybe_unused]] Ride* ride)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
void ride_ratings_calculate_information_kiosk([[maybe_unused]] Ride* ride)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
void ride_ratings_calculate_toilets([[maybe_unused]] Ride* ride)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
void ride_ratings_calculate_ferris_wheel([[maybe_unused]] Ride* ride)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
void ride_ratings_calculate_motion_simulator([[maybe_unused]] Ride* ride)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
void ride_ratings_calculate_3d_cinema([[maybe_unused]] Ride* ride)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
void ride_ratings_calculate_top_spin([[maybe_unused]] Ride* ride)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
void ride_ratings_calculate_space_rings([[maybe_unused]] Ride* ride)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
void ride_ratings_calculate_reverse_freefall_coaster([[maybe_unused]] Ride* ride)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
void ride_ratings_calculate_lift([[maybe_unused]] Ride* ride)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
void ride_ratings_calculate_vertical_drop_roller_coaster([[maybe_unused]] Ride* ride)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
void ride_ratings_calculate_cash_machine([[maybe_unused]] Ride* ride)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
void ride_ratings_calculate_twist([[maybe_unused]] Ride* ride)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
void ride_ratings_calculate_haunted_house([[maybe_unused]] Ride* ride)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
void ride_ratings_calculate_first_aid([[maybe_unused]] Ride* ride)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
void ride_ratings_calculate_circus([[maybe_unused]] Ride* ride)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
void ride_ratings_calculate_ghost_train([[maybe_unused]] Ride* ride)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
void ride_ratings_calculate_twister_roller_coaster([[maybe_unused]] Ride* ride)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
void ride_ratings_calculate_wooden_roller_coaster([[maybe_unused]] Ride* ride)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
void ride_ratings_calculate_side_friction_roller_coaster([[maybe_unused]] Ride* ride)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
void ride_ratings_calculate_wild_mouse([[maybe_unused]] Ride* ride)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
void ride_ratings_calculate_multi_dimension_roller_coaster([[maybe_unused]] Ride* ride)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
void ride_ratings_calculate_flying_roller_coaster([[maybe_unused]] Ride* ride)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
void ride_ratings_calculate_virginia_reel([[maybe_unused]] Ride* ride)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
void ride_ratings_calculate_splash_boats([[maybe_unused]] Ride* ride)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
void ride_ratings_calculate_mini_helicopters([[maybe_unused]] Ride* ride)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
void ride_ratings_calculate_lay_down_roller_coaster([[maybe_unused]] Ride* ride)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
void ride_ratings_calculate_suspended_monorail([[maybe_unused]] Ride* ride)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
void ride_ratings_calculate_reverser_roller_coaster([[maybe_unused]] Ride* ride)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
void ride_ratings_calculate_heartline_twister_coaster([[maybe_unused]] Ride* ride)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
void ride_ratings_calculate_mini_golf([[maybe_unused]] Ride* ride)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
void ride_ratings_calculate_giga_coaster([[maybe_unused]] Ride* ride)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
void ride_ratings_calculate_roto_drop([[maybe_unused]] Ride* ride)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
void ride_ratings_calculate_flying_saucers([[maybe_unused]] Ride* ride)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
void ride_ratings_calculate_crooked_house([[maybe_unused]] Ride* ride)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
void ride_ratings_calculate_monorail_cycles([[maybe_unused]] Ride* ride)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
void ride_ratings_calculate_compact_inverted_coaster([[maybe_unused]] Ride* ride)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
void ride_ratings_calculate_water_coaster([[maybe_unused]] Ride* ride)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
void ride_ratings_calculate_air_powered_vertical_coaster([[maybe_unused]] Ride* ride)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
void ride_ratings_calculate_inverted_hairpin_coaster([[maybe_unused]] Ride* ride)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
void ride_ratings_calculate_magic_carpet([[maybe_unused]] Ride* ride)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
void ride_ratings_calculate_submarine_ride([[maybe_unused]] Ride* ride)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
void ride_ratings_calculate_river_rafts([[maybe_unused]] Ride* ride)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
void ride_ratings_calculate_enterprise([[maybe_unused]] Ride* ride)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
void ride_ratings_calculate_inverted_impulse_coaster([[maybe_unused]] Ride* ride)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
void ride_ratings_calculate_mini_roller_coaster([[maybe_unused]] Ride* ride)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
void ride_ratings_calculate_mine_ride([[maybe_unused]] Ride* ride)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
void ride_ratings_calculate_lim_launched_roller_coaster([[maybe_unused]] Ride* ride)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
void ride_ratings_calculate_drink_stall([[maybe_unused]] Ride* ride)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
void ride_ratings_calculate_hybrid_coaster([[maybe_unused]] Ride* ride)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
void ride_ratings_calculate_single_rail_roller_coaster([[maybe_unused]] Ride* ride)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
const RideTypeDescriptor& Ride::GetRideTypeDescriptor() const
|
|
||||||
{
|
|
||||||
return ::GetRideTypeDescriptor(type);
|
|
||||||
}
|
|
||||||
|
|
||||||
uint8_t TileElementBase::GetOwner() const
|
|
||||||
{
|
|
||||||
return owner & OWNER_MASK;
|
|
||||||
}
|
|
||||||
|
|
||||||
void TileElementBase::SetOwner(uint8_t newOwner)
|
|
||||||
{
|
|
||||||
owner &= ~OWNER_MASK;
|
|
||||||
owner |= (newOwner & OWNER_MASK);
|
|
||||||
}
|
|
||||||
|
|
||||||
namespace OpenRCT2
|
|
||||||
{
|
|
||||||
IContext* GetContext()
|
|
||||||
{
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
} // namespace OpenRCT2
|
|
||||||
|
|
||||||
ScreenCoordsXY translate_3d_to_2d_with_z(int32_t rotation, const CoordsXYZ& pos)
|
|
||||||
{
|
|
||||||
auto rotated = pos.Rotate(rotation);
|
|
||||||
// Use right shift to avoid issues like #9301
|
|
||||||
return ScreenCoordsXY{ rotated.y - rotated.x, ((rotated.x + rotated.y) >> 1) - pos.z };
|
|
||||||
}
|
|
File diff suppressed because it is too large
Load Diff
|
@ -1,21 +0,0 @@
|
||||||
/*****************************************************************************
|
|
||||||
* Copyright (c) 2014-2018 OpenRCT2 developers
|
|
||||||
*
|
|
||||||
* For a complete list of all authors, please refer to contributors.md
|
|
||||||
* Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2
|
|
||||||
*
|
|
||||||
* OpenRCT2 is licensed under the GNU General Public License version 3.
|
|
||||||
*****************************************************************************/
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <openrct2/common.h>
|
|
||||||
#include <openrct2/ride/Ride.h>
|
|
||||||
|
|
||||||
extern const utf8string RideNames[RCT2_RIDE_TYPE_COUNT];
|
|
||||||
extern const utf8string TrackNames[256];
|
|
||||||
extern const utf8string FlatTrackNames[256];
|
|
||||||
extern const utf8string TrackElemNames[256];
|
|
||||||
extern const utf8string RideCodeNames[RCT2_RIDE_TYPE_COUNT];
|
|
||||||
extern const utf8string TrackCodeNames[256];
|
|
||||||
extern const uint32_t* RideTypeTrackPaintFunctionsOld[RCT2_RIDE_TYPE_COUNT];
|
|
|
@ -1,186 +0,0 @@
|
||||||
/*****************************************************************************
|
|
||||||
* Copyright (c) 2014-2018 OpenRCT2 developers
|
|
||||||
*
|
|
||||||
* For a complete list of all authors, please refer to contributors.md
|
|
||||||
* Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2
|
|
||||||
*
|
|
||||||
* OpenRCT2 is licensed under the GNU General Public License version 3.
|
|
||||||
*****************************************************************************/
|
|
||||||
|
|
||||||
#include "FunctionCall.hpp"
|
|
||||||
|
|
||||||
#include <openrct2/sprites.h>
|
|
||||||
|
|
||||||
enum SpriteGroup
|
|
||||||
{
|
|
||||||
SPRITEGROUP_NONE,
|
|
||||||
|
|
||||||
SPRITEGROUP_FENCE_METAL_A, // 14568
|
|
||||||
SPRITEGROUP_FENCE_METAL_B, // 14990
|
|
||||||
SPRITEGROUP_FENCE_SPIRAL_SLIDE, // 20564
|
|
||||||
SPRITEGROUP_FLOOR_CORK, // 22134
|
|
||||||
SPRITEGROUP_FENCE_ROPE, // 22138
|
|
||||||
};
|
|
||||||
|
|
||||||
static void canonicalizeFunctionCall(function_call* call);
|
|
||||||
static SpriteGroup getSpriteGroup(uint16_t spriteIndex);
|
|
||||||
|
|
||||||
bool FunctionCall::AssertsEquals(std::vector<function_call> expected, std::vector<function_call> actual)
|
|
||||||
{
|
|
||||||
if (expected.size() != actual.size())
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (size_t i = 0; i < expected.size(); i++)
|
|
||||||
{
|
|
||||||
function_call expectedCall = expected[i];
|
|
||||||
function_call actualCall = actual[i];
|
|
||||||
|
|
||||||
if (!AssertsEquals(expectedCall, actualCall))
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool FunctionCall::AssertsEquals(function_call expected, function_call actual)
|
|
||||||
{
|
|
||||||
canonicalizeFunctionCall(&actual);
|
|
||||||
canonicalizeFunctionCall(&expected);
|
|
||||||
|
|
||||||
if (expected.function != actual.function)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
uint8_t function = expected.function;
|
|
||||||
|
|
||||||
if (function == SUPPORTS_WOOD_A || function == SUPPORTS_WOOD_B)
|
|
||||||
{
|
|
||||||
if (expected.supports.type != actual.supports.type)
|
|
||||||
return false;
|
|
||||||
if (expected.supports.special != actual.supports.special)
|
|
||||||
return false;
|
|
||||||
if (expected.supports.height != actual.supports.height)
|
|
||||||
return false;
|
|
||||||
if (expected.supports.colour_flags != actual.supports.colour_flags)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
if (expected.supports.special == 14 || expected.supports.special == 15 || expected.supports.special == 18
|
|
||||||
|| expected.supports.special == 19 || expected.supports.special == 22 || expected.supports.special == 23)
|
|
||||||
{
|
|
||||||
if (expected.supports.prepend_to != actual.supports.prepend_to)
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (function == SUPPORTS_METAL_A || function == SUPPORTS_METAL_B)
|
|
||||||
{
|
|
||||||
if (expected.supports.type != actual.supports.type)
|
|
||||||
return false;
|
|
||||||
if (expected.supports.segment != actual.supports.segment)
|
|
||||||
return false;
|
|
||||||
if (expected.supports.special != actual.supports.special)
|
|
||||||
return false;
|
|
||||||
if (expected.supports.height != actual.supports.height)
|
|
||||||
return false;
|
|
||||||
if (expected.supports.colour_flags != actual.supports.colour_flags)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (function == SET_SEGMENT_HEIGHT)
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (expected.paint.image_id != actual.paint.image_id)
|
|
||||||
{
|
|
||||||
SpriteGroup expectedSpriteGroup = getSpriteGroup(expected.paint.image_id & 0x7FFFF);
|
|
||||||
SpriteGroup actualSpriteGroup = getSpriteGroup(actual.paint.image_id & 0x7FFFF);
|
|
||||||
|
|
||||||
if (expectedSpriteGroup != actualSpriteGroup)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
if (expectedSpriteGroup == SPRITEGROUP_NONE)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (expected.paint.offset.x != actual.paint.offset.x)
|
|
||||||
return false;
|
|
||||||
if (expected.paint.offset.y != actual.paint.offset.y)
|
|
||||||
return false;
|
|
||||||
if (expected.paint.bound_box_length.x != actual.paint.bound_box_length.x)
|
|
||||||
return false;
|
|
||||||
if (expected.paint.bound_box_length.y != actual.paint.bound_box_length.y)
|
|
||||||
return false;
|
|
||||||
if (expected.paint.bound_box_length.z != actual.paint.bound_box_length.z)
|
|
||||||
return false;
|
|
||||||
if (function != PAINT_98196C)
|
|
||||||
{
|
|
||||||
if (expected.paint.bound_box_offset.x != actual.paint.bound_box_offset.x)
|
|
||||||
return false;
|
|
||||||
if (expected.paint.bound_box_offset.y != actual.paint.bound_box_offset.y)
|
|
||||||
return false;
|
|
||||||
if (expected.paint.bound_box_offset.z != actual.paint.bound_box_offset.z)
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (expected.paint.z_offset != actual.paint.z_offset)
|
|
||||||
return false;
|
|
||||||
if (expected.paint.rotation != actual.paint.rotation)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void canonicalizeFunctionCall(function_call* call)
|
|
||||||
{
|
|
||||||
if (call->function != PAINT_98197C)
|
|
||||||
return;
|
|
||||||
if (call->paint.offset.x != call->paint.bound_box_offset.x)
|
|
||||||
return;
|
|
||||||
if (call->paint.offset.y != call->paint.bound_box_offset.y)
|
|
||||||
return;
|
|
||||||
if (call->paint.z_offset != call->paint.bound_box_offset.z)
|
|
||||||
return;
|
|
||||||
|
|
||||||
call->function = PAINT_98196C;
|
|
||||||
}
|
|
||||||
|
|
||||||
static SpriteGroup getSpriteGroup(uint16_t spriteIndex)
|
|
||||||
{
|
|
||||||
if (spriteIndex >= 14568 && spriteIndex <= 14571)
|
|
||||||
{
|
|
||||||
return SPRITEGROUP_FENCE_METAL_A;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (spriteIndex >= 14990 && spriteIndex <= 14993)
|
|
||||||
{
|
|
||||||
return SPRITEGROUP_FENCE_METAL_B;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (spriteIndex >= 20564 && spriteIndex <= 20567)
|
|
||||||
{
|
|
||||||
return SPRITEGROUP_FENCE_SPIRAL_SLIDE;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (spriteIndex >= 22134 && spriteIndex <= 22137)
|
|
||||||
{
|
|
||||||
return SPRITEGROUP_FLOOR_CORK;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (spriteIndex >= 22138 && spriteIndex <= 22141)
|
|
||||||
{
|
|
||||||
return SPRITEGROUP_FENCE_ROPE;
|
|
||||||
}
|
|
||||||
|
|
||||||
return SPRITEGROUP_NONE;
|
|
||||||
}
|
|
|
@ -1,61 +0,0 @@
|
||||||
/*****************************************************************************
|
|
||||||
* Copyright (c) 2014-2018 OpenRCT2 developers
|
|
||||||
*
|
|
||||||
* For a complete list of all authors, please refer to contributors.md
|
|
||||||
* Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2
|
|
||||||
*
|
|
||||||
* OpenRCT2 is licensed under the GNU General Public License version 3.
|
|
||||||
*****************************************************************************/
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include "TestPaint.hpp"
|
|
||||||
|
|
||||||
#include <openrct2/common.h>
|
|
||||||
|
|
||||||
enum
|
|
||||||
{
|
|
||||||
PAINT_98196C,
|
|
||||||
PAINT_98197C,
|
|
||||||
PAINT_98198C,
|
|
||||||
PAINT_98199C,
|
|
||||||
|
|
||||||
SUPPORTS_METAL_A,
|
|
||||||
SUPPORTS_METAL_B,
|
|
||||||
SUPPORTS_WOOD_A,
|
|
||||||
SUPPORTS_WOOD_B,
|
|
||||||
|
|
||||||
SET_SEGMENT_HEIGHT,
|
|
||||||
PAINT_98198C_COORDS
|
|
||||||
};
|
|
||||||
|
|
||||||
struct function_call
|
|
||||||
{
|
|
||||||
uint8_t function;
|
|
||||||
struct paint
|
|
||||||
{
|
|
||||||
uint32_t image_id;
|
|
||||||
LocationXY16 offset;
|
|
||||||
LocationXYZ16 bound_box_length;
|
|
||||||
int16_t z_offset;
|
|
||||||
LocationXYZ16 bound_box_offset;
|
|
||||||
uint32_t rotation;
|
|
||||||
paint_struct output_struct;
|
|
||||||
} paint;
|
|
||||||
struct supports
|
|
||||||
{
|
|
||||||
int type;
|
|
||||||
uint8_t segment;
|
|
||||||
int special;
|
|
||||||
int height;
|
|
||||||
uint32_t colour_flags;
|
|
||||||
int32_t prepend_to;
|
|
||||||
} supports;
|
|
||||||
};
|
|
||||||
|
|
||||||
class FunctionCall
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
static bool AssertsEquals(function_call expected, function_call actual);
|
|
||||||
static bool AssertsEquals(std::vector<function_call> expected, std::vector<function_call> actual);
|
|
||||||
};
|
|
|
@ -1,78 +0,0 @@
|
||||||
/*****************************************************************************
|
|
||||||
* Copyright (c) 2014-2018 OpenRCT2 developers
|
|
||||||
*
|
|
||||||
* For a complete list of all authors, please refer to contributors.md
|
|
||||||
* Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2
|
|
||||||
*
|
|
||||||
* OpenRCT2 is licensed under the GNU General Public License version 3.
|
|
||||||
*****************************************************************************/
|
|
||||||
|
|
||||||
#include "GeneralSupportHeightCall.hpp"
|
|
||||||
|
|
||||||
#include <map>
|
|
||||||
|
|
||||||
bool GeneralSupportHeightCall::CallsMatch(SupportCall tileSupportCalls[4])
|
|
||||||
{
|
|
||||||
SupportCall baseCall = tileSupportCalls[0];
|
|
||||||
for (int i = 1; i < 4; i++)
|
|
||||||
{
|
|
||||||
if (tileSupportCalls[i] != baseCall)
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool GeneralSupportHeightCall::FindMostCommonSupportCall(SupportCall calls[4], SupportCall* out)
|
|
||||||
{
|
|
||||||
std::map<SupportCall, int> map;
|
|
||||||
|
|
||||||
for (int i = 0; i < 4; ++i)
|
|
||||||
{
|
|
||||||
if (map.count(calls[i]) == 0)
|
|
||||||
{
|
|
||||||
map[calls[i]] = 1;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
map[calls[i]] += 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (map.size() == 1)
|
|
||||||
{
|
|
||||||
(*out) = calls[0];
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (map.size() == 2)
|
|
||||||
{
|
|
||||||
for (auto&& item : map)
|
|
||||||
{
|
|
||||||
if (item.second == 3)
|
|
||||||
{
|
|
||||||
(*out) = item.first;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool GeneralSupportHeightCall::AssertEquals(const SupportCall* lhs, const SupportCall* rhs)
|
|
||||||
{
|
|
||||||
if (lhs == nullptr && rhs == nullptr)
|
|
||||||
return true;
|
|
||||||
if (lhs == nullptr || rhs == nullptr)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
if (lhs->height != rhs->height)
|
|
||||||
return false;
|
|
||||||
if (lhs->slope != rhs->slope)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
|
@ -1,52 +0,0 @@
|
||||||
/*****************************************************************************
|
|
||||||
* Copyright (c) 2014-2018 OpenRCT2 developers
|
|
||||||
*
|
|
||||||
* For a complete list of all authors, please refer to contributors.md
|
|
||||||
* Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2
|
|
||||||
*
|
|
||||||
* OpenRCT2 is licensed under the GNU General Public License version 3.
|
|
||||||
*****************************************************************************/
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <openrct2/common.h>
|
|
||||||
|
|
||||||
struct SupportCall
|
|
||||||
{
|
|
||||||
int32_t height;
|
|
||||||
int16_t slope;
|
|
||||||
|
|
||||||
friend bool operator==(const SupportCall& lhs, const SupportCall& rhs)
|
|
||||||
{
|
|
||||||
if (lhs.height != rhs.height)
|
|
||||||
return false;
|
|
||||||
if (lhs.slope != rhs.slope)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool operator!=(const SupportCall& other) const
|
|
||||||
{
|
|
||||||
return !(*this == other);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool operator<(const SupportCall& other) const
|
|
||||||
{
|
|
||||||
if (height != other.height)
|
|
||||||
{
|
|
||||||
return height < other.height;
|
|
||||||
}
|
|
||||||
|
|
||||||
return slope < other.slope;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
namespace GeneralSupportHeightCall
|
|
||||||
{
|
|
||||||
bool CallsMatch(SupportCall tileSupportCalls[4]);
|
|
||||||
|
|
||||||
bool FindMostCommonSupportCall(SupportCall calls[4], SupportCall* out);
|
|
||||||
|
|
||||||
bool AssertEquals(const SupportCall* lhs, const SupportCall* rhs);
|
|
||||||
}; // namespace GeneralSupportHeightCall
|
|
|
@ -1,210 +0,0 @@
|
||||||
/*****************************************************************************
|
|
||||||
* Copyright (c) 2014-2018 OpenRCT2 developers
|
|
||||||
*
|
|
||||||
* For a complete list of all authors, please refer to contributors.md
|
|
||||||
* Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2
|
|
||||||
*
|
|
||||||
* OpenRCT2 is licensed under the GNU General Public License version 3.
|
|
||||||
*****************************************************************************/
|
|
||||||
|
|
||||||
#include <cstdio>
|
|
||||||
#include <cstdlib>
|
|
||||||
#include <cstring>
|
|
||||||
#include <openrct2/common.h>
|
|
||||||
|
|
||||||
#ifndef NO_RCT2
|
|
||||||
|
|
||||||
# ifdef _WIN32
|
|
||||||
# include <windows.h>
|
|
||||||
# else
|
|
||||||
# include <sys/mman.h>
|
|
||||||
# endif // _WIN32
|
|
||||||
|
|
||||||
# include "Hook.h"
|
|
||||||
|
|
||||||
void* _hookTableAddress = 0;
|
|
||||||
int32_t _hookTableOffset = 0;
|
|
||||||
int32_t _maxHooks = 1000;
|
|
||||||
# define HOOK_BYTE_COUNT (140)
|
|
||||||
|
|
||||||
registers gHookRegisters = {};
|
|
||||||
|
|
||||||
// This macro writes a little-endian 4-byte long value into *data
|
|
||||||
// It is used to avoid type punning.
|
|
||||||
# define write_address_strictalias(data, addr) \
|
|
||||||
*(data + 0) = ((addr)&0x000000ff) >> 0; \
|
|
||||||
*(data + 1) = ((addr)&0x0000ff00) >> 8; \
|
|
||||||
*(data + 2) = ((addr)&0x00ff0000) >> 16; \
|
|
||||||
*(data + 3) = ((addr)&0xff000000) >> 24;
|
|
||||||
|
|
||||||
static void hookfunc(uintptr_t address, uintptr_t hookAddress, int32_t stacksize)
|
|
||||||
{
|
|
||||||
int32_t i = 0;
|
|
||||||
uint8_t data[HOOK_BYTE_COUNT] = {};
|
|
||||||
|
|
||||||
uintptr_t registerAddress = (uintptr_t)&gHookRegisters;
|
|
||||||
|
|
||||||
data[i++] = 0x89; // mov [gHookRegisters], eax
|
|
||||||
data[i++] = (0b000 << 3) | 0b101;
|
|
||||||
write_address_strictalias(&data[i], registerAddress);
|
|
||||||
i += 4;
|
|
||||||
|
|
||||||
data[i++] = 0x89; // mov [gHookRegisters + 4], ebx
|
|
||||||
data[i++] = (0b011 << 3) | 0b101;
|
|
||||||
write_address_strictalias(&data[i], registerAddress + 4);
|
|
||||||
i += 4;
|
|
||||||
|
|
||||||
data[i++] = 0x89; // mov [gHookRegisters + 8], ecx
|
|
||||||
data[i++] = (0b001 << 3) | 0b101;
|
|
||||||
write_address_strictalias(&data[i], registerAddress + 8);
|
|
||||||
i += 4;
|
|
||||||
|
|
||||||
data[i++] = 0x89; // mov [gHookRegisters + 12], edx
|
|
||||||
data[i++] = (0b010 << 3) | 0b101;
|
|
||||||
write_address_strictalias(&data[i], registerAddress + 12);
|
|
||||||
i += 4;
|
|
||||||
|
|
||||||
data[i++] = 0x89; // mov [gHookRegisters + 16], esi
|
|
||||||
data[i++] = (0b110 << 3) | 0b101;
|
|
||||||
write_address_strictalias(&data[i], registerAddress + 16);
|
|
||||||
i += 4;
|
|
||||||
|
|
||||||
data[i++] = 0x89; // mov [gHookRegisters + 20], edi
|
|
||||||
data[i++] = (0b111 << 3) | 0b101;
|
|
||||||
write_address_strictalias(&data[i], registerAddress + 20);
|
|
||||||
i += 4;
|
|
||||||
|
|
||||||
data[i++] = 0x89; // mov [gHookRegisters + 24], ebp
|
|
||||||
data[i++] = (0b101 << 3) | 0b101;
|
|
||||||
write_address_strictalias(&data[i], registerAddress + 24);
|
|
||||||
i += 4;
|
|
||||||
|
|
||||||
// work out distance to nearest 0xC
|
|
||||||
// (esp - numargs * 4) & 0xC
|
|
||||||
// move to align - 4
|
|
||||||
// save that amount
|
|
||||||
|
|
||||||
// push the registers to be on the stack to access as arguments
|
|
||||||
data[i++] = 0x68; // push gHookRegisters
|
|
||||||
write_address_strictalias(&data[i], registerAddress);
|
|
||||||
i += 4;
|
|
||||||
|
|
||||||
data[i++] = 0xE8; // call
|
|
||||||
|
|
||||||
write_address_strictalias(&data[i], hookAddress - address - i - 4);
|
|
||||||
i += 4;
|
|
||||||
|
|
||||||
data[i++] = 0x83; // add esp, 4
|
|
||||||
data[i++] = 0xC4;
|
|
||||||
data[i++] = 0x04;
|
|
||||||
|
|
||||||
data[i++] = 0x25; // and eax,0xff
|
|
||||||
data[i++] = 0xff;
|
|
||||||
data[i++] = 0x00;
|
|
||||||
data[i++] = 0x00;
|
|
||||||
data[i++] = 0x00;
|
|
||||||
data[i++] = 0xc1; // shl eax, 8
|
|
||||||
data[i++] = 0xe0;
|
|
||||||
data[i++] = 0x08;
|
|
||||||
data[i++] = 0x9e; // sahf
|
|
||||||
data[i++] = 0x9c; // pushf
|
|
||||||
|
|
||||||
data[i++] = 0x8B; // mov eax, [gHookRegisters]
|
|
||||||
data[i++] = (0b000 << 3) | 0b101;
|
|
||||||
write_address_strictalias(&data[i], registerAddress);
|
|
||||||
i += 4;
|
|
||||||
|
|
||||||
data[i++] = 0x8B; // mov ebx, [gHookRegisters + 4]
|
|
||||||
data[i++] = (0b011 << 3) | 0b101;
|
|
||||||
write_address_strictalias(&data[i], registerAddress + 4);
|
|
||||||
i += 4;
|
|
||||||
|
|
||||||
data[i++] = 0x8B; // mov ecx, [gHookRegisters + 8]
|
|
||||||
data[i++] = (0b001 << 3) | 0b101;
|
|
||||||
write_address_strictalias(&data[i], registerAddress + 8);
|
|
||||||
i += 4;
|
|
||||||
|
|
||||||
data[i++] = 0x8B; // mov edx, [gHookRegisters + 12]
|
|
||||||
data[i++] = (0b010 << 3) | 0b101;
|
|
||||||
write_address_strictalias(&data[i], registerAddress + 12);
|
|
||||||
i += 4;
|
|
||||||
|
|
||||||
data[i++] = 0x8B; // mov esi, [gHookRegisters + 16]
|
|
||||||
data[i++] = (0b110 << 3) | 0b101;
|
|
||||||
write_address_strictalias(&data[i], registerAddress + 16);
|
|
||||||
i += 4;
|
|
||||||
|
|
||||||
data[i++] = 0x8B; // mov edi, [gHookRegisters + 20]
|
|
||||||
data[i++] = (0b111 << 3) | 0b101;
|
|
||||||
write_address_strictalias(&data[i], registerAddress + 20);
|
|
||||||
i += 4;
|
|
||||||
|
|
||||||
data[i++] = 0x8B; // mov ebp, [gHookRegisters + 24]
|
|
||||||
data[i++] = (0b101 << 3) | 0b101;
|
|
||||||
write_address_strictalias(&data[i], registerAddress + 24);
|
|
||||||
i += 4;
|
|
||||||
|
|
||||||
data[i++] = 0x9d; // popf
|
|
||||||
|
|
||||||
data[i++] = 0xC3; // retn
|
|
||||||
|
|
||||||
# ifdef _WIN32
|
|
||||||
WriteProcessMemory(GetCurrentProcess(), (LPVOID)address, data, i, 0);
|
|
||||||
# else
|
|
||||||
// We own the pages with PROT_WRITE | PROT_EXEC, we can simply just memcpy the data
|
|
||||||
memcpy((void*)address, data, i);
|
|
||||||
# endif // _WIN32
|
|
||||||
}
|
|
||||||
|
|
||||||
void addhook(uintptr_t address, hook_function function)
|
|
||||||
{
|
|
||||||
if (!_hookTableAddress)
|
|
||||||
{
|
|
||||||
size_t size = _maxHooks * HOOK_BYTE_COUNT;
|
|
||||||
# ifdef _WIN32
|
|
||||||
_hookTableAddress = VirtualAllocEx(GetCurrentProcess(), NULL, size, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
|
|
||||||
# else
|
|
||||||
_hookTableAddress = mmap(NULL, size, PROT_EXEC | PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
|
|
||||||
if (_hookTableAddress == MAP_FAILED)
|
|
||||||
{
|
|
||||||
perror("mmap");
|
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
# endif // _WIN32
|
|
||||||
}
|
|
||||||
if (_hookTableOffset > _maxHooks)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
uint32_t hookaddress = (uint32_t)((uint64_t)(_hookTableAddress)&0xFFFFFFFF) + (_hookTableOffset * HOOK_BYTE_COUNT);
|
|
||||||
uint8_t data[9];
|
|
||||||
int32_t i = 0;
|
|
||||||
data[i++] = 0xE9; // jmp
|
|
||||||
|
|
||||||
write_address_strictalias(&data[i], hookaddress - address - i - 4);
|
|
||||||
i += 4;
|
|
||||||
|
|
||||||
data[i++] = 0xC3; // retn
|
|
||||||
# ifdef _WIN32
|
|
||||||
WriteProcessMemory(GetCurrentProcess(), (LPVOID)address, data, i, 0);
|
|
||||||
# else
|
|
||||||
// We own the pages with PROT_WRITE | PROT_EXEC, we can simply just memcpy the data
|
|
||||||
int32_t err = mprotect((void*)0x401000, 0x8a4000 - 0x401000, PROT_READ | PROT_WRITE);
|
|
||||||
if (err != 0)
|
|
||||||
{
|
|
||||||
perror("mprotect");
|
|
||||||
}
|
|
||||||
|
|
||||||
memcpy((void*)address, data, i);
|
|
||||||
|
|
||||||
err = mprotect((void*)0x401000, 0x8a4000 - 0x401000, PROT_READ | PROT_EXEC);
|
|
||||||
if (err != 0)
|
|
||||||
{
|
|
||||||
perror("mprotect");
|
|
||||||
}
|
|
||||||
# endif // _WIN32
|
|
||||||
hookfunc(hookaddress, (uintptr_t)function, 0);
|
|
||||||
_hookTableOffset++;
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
|
@ -1,96 +0,0 @@
|
||||||
/*****************************************************************************
|
|
||||||
* Copyright (c) 2014-2018 OpenRCT2 developers
|
|
||||||
*
|
|
||||||
* For a complete list of all authors, please refer to contributors.md
|
|
||||||
* Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2
|
|
||||||
*
|
|
||||||
* OpenRCT2 is licensed under the GNU General Public License version 3.
|
|
||||||
*****************************************************************************/
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#ifndef NO_RCT2
|
|
||||||
|
|
||||||
# include <openrct2/common.h>
|
|
||||||
|
|
||||||
enum
|
|
||||||
{
|
|
||||||
X86_FLAG_CARRY = 1 << 0,
|
|
||||||
X86_FLAG_PARITY = 1 << 2,
|
|
||||||
|
|
||||||
X86_FLAG_ADJUST = 1 << 4,
|
|
||||||
|
|
||||||
X86_FLAG_ZERO = 1 << 6,
|
|
||||||
X86_FLAG_SIGN = 1 << 7,
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* x86 register structure, only used for easy interop to RCT2 code.
|
|
||||||
*/
|
|
||||||
# pragma pack(push, 1)
|
|
||||||
struct registers
|
|
||||||
{
|
|
||||||
union
|
|
||||||
{
|
|
||||||
int32_t eax;
|
|
||||||
int16_t ax;
|
|
||||||
struct
|
|
||||||
{
|
|
||||||
char al;
|
|
||||||
char ah;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
union
|
|
||||||
{
|
|
||||||
int32_t ebx;
|
|
||||||
int16_t bx;
|
|
||||||
struct
|
|
||||||
{
|
|
||||||
char bl;
|
|
||||||
char bh;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
union
|
|
||||||
{
|
|
||||||
int32_t ecx;
|
|
||||||
int16_t cx;
|
|
||||||
struct
|
|
||||||
{
|
|
||||||
char cl;
|
|
||||||
char ch;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
union
|
|
||||||
{
|
|
||||||
int32_t edx;
|
|
||||||
int16_t dx;
|
|
||||||
struct
|
|
||||||
{
|
|
||||||
char dl;
|
|
||||||
char dh;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
union
|
|
||||||
{
|
|
||||||
int32_t esi;
|
|
||||||
int16_t si;
|
|
||||||
};
|
|
||||||
union
|
|
||||||
{
|
|
||||||
int32_t edi;
|
|
||||||
int16_t di;
|
|
||||||
};
|
|
||||||
union
|
|
||||||
{
|
|
||||||
int32_t ebp;
|
|
||||||
int16_t bp;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
assert_struct_size(registers, 7 * 4);
|
|
||||||
# pragma pack(pop)
|
|
||||||
|
|
||||||
using hook_function = uint8_t (*)(registers* regs);
|
|
||||||
|
|
||||||
void addhook(uintptr_t address, hook_function function);
|
|
||||||
|
|
||||||
#endif
|
|
|
@ -1,412 +0,0 @@
|
||||||
/*****************************************************************************
|
|
||||||
* Copyright (c) 2014-2018 OpenRCT2 developers
|
|
||||||
*
|
|
||||||
* For a complete list of all authors, please refer to contributors.md
|
|
||||||
* Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2
|
|
||||||
*
|
|
||||||
* OpenRCT2 is licensed under the GNU General Public License version 3.
|
|
||||||
*****************************************************************************/
|
|
||||||
|
|
||||||
#include "PaintIntercept.hpp"
|
|
||||||
|
|
||||||
#include "FunctionCall.hpp"
|
|
||||||
#include "Hook.h"
|
|
||||||
#include "TestPaint.hpp"
|
|
||||||
|
|
||||||
#include <cstring>
|
|
||||||
#include <openrct2/common.h>
|
|
||||||
#include <openrct2/interface/Viewport.h>
|
|
||||||
#include <openrct2/paint/Supports.h>
|
|
||||||
#include <openrct2/sprites.h>
|
|
||||||
|
|
||||||
paint_session gPaintSession;
|
|
||||||
|
|
||||||
static bool _woodenSupports = false;
|
|
||||||
static uint8_t _callCount = 0;
|
|
||||||
static function_call _calls[256] = {};
|
|
||||||
static paint_struct _paintStructs = {};
|
|
||||||
|
|
||||||
namespace PaintIntercept
|
|
||||||
{
|
|
||||||
static uint8_t InterceptWoodenASupports(registers* regs);
|
|
||||||
static uint8_t InterceptWoodenBSupports(registers* regs);
|
|
||||||
static uint8_t InterceptMetalASupports(registers* regs);
|
|
||||||
static uint8_t InterceptMetalBSupports(registers* regs);
|
|
||||||
static uint8_t InterceptPaint6C(registers* regs);
|
|
||||||
static uint8_t InterceptPaint7C(registers* regs);
|
|
||||||
static uint8_t InterceptPaint8C(registers* regs);
|
|
||||||
static uint8_t InterceptPaint9C(registers* regs);
|
|
||||||
static uint8_t InterceptPaintFull(uint8_t function, registers* regs);
|
|
||||||
|
|
||||||
bool PaintMetalSupports(
|
|
||||||
uint8_t function, int supportType, uint8_t segment, int special, int height, uint32_t imageColourFlags,
|
|
||||||
const support_height* supportSegments);
|
|
||||||
bool PaintWoodenSupports(
|
|
||||||
uint8_t function, int supportType, int special, int height, uint32_t imageColourFlags, bool* underground,
|
|
||||||
const paint_struct* prependTo);
|
|
||||||
static void CheckSegmentSupportHeight(const support_height* supportSegments);
|
|
||||||
|
|
||||||
void InitHooks()
|
|
||||||
{
|
|
||||||
addhook(0x006629BC, InterceptWoodenASupports);
|
|
||||||
addhook(0x00662D5C, InterceptWoodenBSupports);
|
|
||||||
|
|
||||||
addhook(0x00663105, InterceptMetalASupports);
|
|
||||||
addhook(0x00663584, InterceptMetalBSupports);
|
|
||||||
|
|
||||||
addhook(0x006861AC, InterceptPaint6C);
|
|
||||||
addhook(0x00686337, InterceptPaint6C);
|
|
||||||
addhook(0x006864D0, InterceptPaint6C);
|
|
||||||
addhook(0x0068666B, InterceptPaint6C);
|
|
||||||
|
|
||||||
addhook(0x00686806, InterceptPaint7C);
|
|
||||||
addhook(0x006869B2, InterceptPaint7C);
|
|
||||||
addhook(0x00686B6F, InterceptPaint7C);
|
|
||||||
addhook(0x00686D31, InterceptPaint7C);
|
|
||||||
|
|
||||||
addhook(0x00686EF0, InterceptPaint8C);
|
|
||||||
addhook(0x00687056, InterceptPaint8C);
|
|
||||||
addhook(0x006871C8, InterceptPaint8C);
|
|
||||||
addhook(0x0068733C, InterceptPaint8C);
|
|
||||||
|
|
||||||
addhook(0x006874B0, InterceptPaint9C);
|
|
||||||
addhook(0x00687618, InterceptPaint9C);
|
|
||||||
addhook(0x0068778C, InterceptPaint9C);
|
|
||||||
addhook(0x00687902, InterceptPaint9C);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool PaintWoodenSupports(
|
|
||||||
uint8_t function, int supportType, int special, int height, uint32_t imageColourFlags, bool* underground,
|
|
||||||
const paint_struct* prependTo)
|
|
||||||
{
|
|
||||||
function_call* call = &_calls[_callCount];
|
|
||||||
call->function = function;
|
|
||||||
call->supports.type = supportType;
|
|
||||||
call->supports.special = special;
|
|
||||||
call->supports.height = height;
|
|
||||||
call->supports.colour_flags = imageColourFlags;
|
|
||||||
|
|
||||||
call->supports.prepend_to = SPR_NONE;
|
|
||||||
if (prependTo != nullptr)
|
|
||||||
{
|
|
||||||
for (int i = 0; i < _callCount; i++)
|
|
||||||
{
|
|
||||||
if (&_calls[i].paint.output_struct == prependTo)
|
|
||||||
{
|
|
||||||
call->supports.prepend_to = _calls[i].paint.image_id;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
_callCount++;
|
|
||||||
|
|
||||||
return _woodenSupports;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool PaintMetalSupports(
|
|
||||||
uint8_t function, int supportType, uint8_t segment, int special, int height, uint32_t imageColourFlags,
|
|
||||||
const support_height* supportSegments)
|
|
||||||
{
|
|
||||||
CheckSegmentSupportHeight(supportSegments);
|
|
||||||
|
|
||||||
function_call* call = &_calls[_callCount];
|
|
||||||
call->function = function;
|
|
||||||
call->supports.type = supportType;
|
|
||||||
call->supports.segment = segment;
|
|
||||||
call->supports.special = special;
|
|
||||||
call->supports.height = height;
|
|
||||||
call->supports.colour_flags = imageColourFlags;
|
|
||||||
|
|
||||||
_callCount++;
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
static paint_struct* Paint6C(
|
|
||||||
uint32_t imageID, int8_t xOffset, int8_t yOffset, int16_t boundBoxLengthX, int16_t boundBoxLengthY,
|
|
||||||
int8_t boundBoxLengthZ, int16_t zOffset, uint32_t rotation)
|
|
||||||
{
|
|
||||||
function_call* call = &_calls[_callCount];
|
|
||||||
call->function = PAINT_98196C;
|
|
||||||
call->paint.image_id = imageID;
|
|
||||||
call->paint.offset = { xOffset, yOffset };
|
|
||||||
call->paint.bound_box_length = { boundBoxLengthX, boundBoxLengthY, boundBoxLengthZ };
|
|
||||||
call->paint.z_offset = zOffset;
|
|
||||||
call->paint.rotation = rotation;
|
|
||||||
|
|
||||||
_callCount++;
|
|
||||||
|
|
||||||
return &call->paint.output_struct;
|
|
||||||
}
|
|
||||||
|
|
||||||
static paint_struct* PaintFull(
|
|
||||||
uint8_t function, uint32_t imageID, int8_t xOffset, int8_t yOffset, int16_t boundBoxLengthX, int16_t boundBoxLengthY,
|
|
||||||
int8_t boundBoxLengthZ, int16_t zOffset, int16_t boundBoxOffsetX, int16_t boundBoxOffsetY, int16_t boundBoxOffsetZ,
|
|
||||||
uint32_t rotation)
|
|
||||||
{
|
|
||||||
function_call* call = &_calls[_callCount];
|
|
||||||
call->function = function;
|
|
||||||
call->paint.image_id = imageID;
|
|
||||||
call->paint.offset = { xOffset, yOffset };
|
|
||||||
call->paint.bound_box_length = { boundBoxLengthX, boundBoxLengthY, boundBoxLengthZ };
|
|
||||||
call->paint.bound_box_offset = { boundBoxOffsetX, boundBoxOffsetY, boundBoxOffsetZ };
|
|
||||||
call->paint.z_offset = zOffset;
|
|
||||||
call->paint.rotation = rotation;
|
|
||||||
|
|
||||||
_callCount++;
|
|
||||||
|
|
||||||
return &call->paint.output_struct;
|
|
||||||
}
|
|
||||||
|
|
||||||
static paint_struct* PaintFull(
|
|
||||||
uint8_t function, uint32_t image_id, const CoordsXYZ& offset, const CoordsXYZ& boundBoxLength,
|
|
||||||
const CoordsXYZ& boundBoxOffset, uint32_t rotation)
|
|
||||||
{
|
|
||||||
return PaintFull(
|
|
||||||
function, image_id, static_cast<int8_t>(offset.x), static_cast<int8_t>(offset.y),
|
|
||||||
static_cast<int16_t>(boundBoxLength.x), static_cast<int16_t>(boundBoxLength.y),
|
|
||||||
static_cast<int16_t>(boundBoxLength.z), static_cast<int16_t>(offset.z), static_cast<int16_t>(boundBoxOffset.x),
|
|
||||||
static_cast<int16_t>(boundBoxOffset.y), static_cast<int16_t>(boundBoxOffset.z), rotation);
|
|
||||||
}
|
|
||||||
|
|
||||||
void ClearCalls()
|
|
||||||
{
|
|
||||||
_callCount = 0;
|
|
||||||
memset(_calls, 0, sizeof(_calls));
|
|
||||||
}
|
|
||||||
|
|
||||||
int GetCalls(function_call* buffer)
|
|
||||||
{
|
|
||||||
memcpy(buffer, _calls, _callCount * sizeof(function_call));
|
|
||||||
return _callCount;
|
|
||||||
}
|
|
||||||
|
|
||||||
void SetSimulateWoodenSupports(bool enabled)
|
|
||||||
{
|
|
||||||
_woodenSupports = enabled;
|
|
||||||
}
|
|
||||||
|
|
||||||
static uint8_t InterceptMetalASupports(registers* regs)
|
|
||||||
{
|
|
||||||
bool output = PaintMetalSupports(
|
|
||||||
SUPPORTS_METAL_A, regs->edi, regs->ebx, (int16_t)regs->ax, regs->dx, regs->ebp, gSupportSegments);
|
|
||||||
|
|
||||||
return output ? X86_FLAG_CARRY : 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static uint8_t InterceptMetalBSupports(registers* regs)
|
|
||||||
{
|
|
||||||
bool output = PaintMetalSupports(
|
|
||||||
SUPPORTS_METAL_B, regs->edi, regs->ebx, (int16_t)regs->ax, regs->dx, regs->ebp, gSupportSegments);
|
|
||||||
|
|
||||||
return output ? X86_FLAG_CARRY : 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void CheckSegmentSupportHeight(const support_height* supportSegments)
|
|
||||||
{
|
|
||||||
bool hasChanged = false;
|
|
||||||
for (int i = 0; i < 9; i++)
|
|
||||||
{
|
|
||||||
if (supportSegments[i].height != 0)
|
|
||||||
hasChanged = true;
|
|
||||||
if (supportSegments[i].slope != 0xFF)
|
|
||||||
hasChanged = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!hasChanged)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
function_call call = {};
|
|
||||||
call.function = SET_SEGMENT_HEIGHT;
|
|
||||||
|
|
||||||
_calls[_callCount] = call;
|
|
||||||
_callCount++;
|
|
||||||
}
|
|
||||||
|
|
||||||
static uint8_t InterceptWoodenASupports(registers* regs)
|
|
||||||
{
|
|
||||||
bool cf = false;
|
|
||||||
regs->al = PaintWoodenSupports(
|
|
||||||
SUPPORTS_WOOD_A, regs->edi, regs->ax, regs->dx, regs->ebp, &cf, gWoodenSupportsPrependTo);
|
|
||||||
|
|
||||||
if (cf)
|
|
||||||
{
|
|
||||||
return X86_FLAG_CARRY;
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static uint8_t InterceptWoodenBSupports(registers* regs)
|
|
||||||
{
|
|
||||||
bool cf = false;
|
|
||||||
regs->al = PaintWoodenSupports(
|
|
||||||
SUPPORTS_WOOD_B, regs->edi, regs->ax, regs->dx, regs->ebp, &cf, gWoodenSupportsPrependTo);
|
|
||||||
|
|
||||||
if (cf)
|
|
||||||
{
|
|
||||||
return X86_FLAG_CARRY;
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static uint8_t InterceptPaint6C(registers* regs)
|
|
||||||
{
|
|
||||||
if ((regs->ebp & 0x03) != RCT2_CurrentRotation)
|
|
||||||
{
|
|
||||||
// Log error
|
|
||||||
log_error("Ebp is different from current rotation");
|
|
||||||
}
|
|
||||||
|
|
||||||
paint_struct* out = Paint6C(
|
|
||||||
regs->ebx, (int8_t)regs->al, (int8_t)regs->cl, (int16_t)regs->di, (int16_t)regs->si, (int8_t)regs->ah, regs->dx,
|
|
||||||
regs->ebp & 0x03);
|
|
||||||
|
|
||||||
if (out == nullptr)
|
|
||||||
{
|
|
||||||
return X86_FLAG_CARRY;
|
|
||||||
}
|
|
||||||
|
|
||||||
regs->ebp = (int)out;
|
|
||||||
regs->al = 1;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static uint8_t InterceptPaint7C(registers* regs)
|
|
||||||
{
|
|
||||||
return InterceptPaintFull(PAINT_98197C, regs);
|
|
||||||
}
|
|
||||||
|
|
||||||
static uint8_t InterceptPaint8C(registers* regs)
|
|
||||||
{
|
|
||||||
return InterceptPaintFull(PAINT_98198C, regs);
|
|
||||||
}
|
|
||||||
|
|
||||||
static uint8_t InterceptPaint9C(registers* regs)
|
|
||||||
{
|
|
||||||
return InterceptPaintFull(PAINT_98199C, regs);
|
|
||||||
}
|
|
||||||
|
|
||||||
static uint8_t InterceptPaintFull(uint8_t function, registers* regs)
|
|
||||||
{
|
|
||||||
if ((regs->ebp & 0x03) != RCT2_CurrentRotation)
|
|
||||||
{
|
|
||||||
// Log error
|
|
||||||
log_error("Ebp is different from current rotation");
|
|
||||||
}
|
|
||||||
|
|
||||||
LocationXYZ16 boundOffset = { RCT2_PaintBoundBoxOffsetX, RCT2_PaintBoundBoxOffsetY, RCT2_PaintBoundBoxOffsetZ };
|
|
||||||
|
|
||||||
paint_struct* out = PaintFull(
|
|
||||||
function, regs->ebx, (int8_t)regs->al, (int8_t)regs->cl, (int16_t)regs->di, (int16_t)regs->si, (int8_t)regs->ah,
|
|
||||||
regs->dx, boundOffset.x, boundOffset.y, boundOffset.z, regs->ebp & 0x03);
|
|
||||||
|
|
||||||
if (out == nullptr)
|
|
||||||
{
|
|
||||||
return X86_FLAG_CARRY;
|
|
||||||
}
|
|
||||||
|
|
||||||
regs->ebp = (int)out;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
}; // namespace PaintIntercept
|
|
||||||
|
|
||||||
bool wooden_a_supports_paint_setup(
|
|
||||||
paint_session* session, int supportType, int special, int height, uint32_t imageColourFlags, bool* underground)
|
|
||||||
{
|
|
||||||
return PaintIntercept::PaintWoodenSupports(
|
|
||||||
SUPPORTS_WOOD_A, supportType, special, height, imageColourFlags, underground, gPaintSession.WoodenSupportsPrependTo);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool wooden_b_supports_paint_setup(
|
|
||||||
paint_session* session, int supportType, int special, int height, uint32_t imageColourFlags, bool* underground)
|
|
||||||
{
|
|
||||||
return PaintIntercept::PaintWoodenSupports(
|
|
||||||
SUPPORTS_WOOD_B, supportType, special, height, imageColourFlags, underground, gPaintSession.WoodenSupportsPrependTo);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool metal_a_supports_paint_setup(
|
|
||||||
paint_session* session, uint8_t supportType, uint8_t segment, int special, int height, uint32_t imageColourFlags)
|
|
||||||
{
|
|
||||||
return PaintIntercept::PaintMetalSupports(
|
|
||||||
SUPPORTS_METAL_A, supportType, segment, special, height, imageColourFlags, gPaintSession.SupportSegments);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool metal_b_supports_paint_setup(
|
|
||||||
paint_session* session, uint8_t supportType, uint8_t segment, int special, int height, uint32_t imageColourFlags)
|
|
||||||
{
|
|
||||||
return PaintIntercept::PaintMetalSupports(
|
|
||||||
SUPPORTS_METAL_B, supportType, segment, special, height, imageColourFlags, gPaintSession.SupportSegments);
|
|
||||||
}
|
|
||||||
|
|
||||||
paint_struct* PaintAddImageAsParent(
|
|
||||||
paint_session* session, uint32_t image_id, int8_t x_offset, int8_t y_offset, int16_t bound_box_length_x,
|
|
||||||
int16_t bound_box_length_y, int8_t bound_box_length_z, int16_t z_offset)
|
|
||||||
{
|
|
||||||
return PaintIntercept::Paint6C(
|
|
||||||
image_id, x_offset, y_offset, bound_box_length_x, bound_box_length_y, bound_box_length_z, z_offset,
|
|
||||||
session->CurrentRotation);
|
|
||||||
}
|
|
||||||
|
|
||||||
paint_struct* PaintAddImageAsParent(
|
|
||||||
paint_session* session, uint32_t image_id, const CoordsXYZ& offset, const CoordsXYZ& boundBoxSize)
|
|
||||||
{
|
|
||||||
return PaintIntercept::Paint6C(
|
|
||||||
image_id, offset.x, offset.y, boundBoxSize.x, boundBoxSize.y, boundBoxSize.z, offset.z, session->CurrentRotation);
|
|
||||||
}
|
|
||||||
|
|
||||||
paint_struct* PaintAddImageAsParent(
|
|
||||||
paint_session* session, uint32_t image_id, int8_t x_offset, int8_t y_offset, int16_t bound_box_length_x,
|
|
||||||
int16_t bound_box_length_y, int8_t bound_box_length_z, int16_t z_offset, int16_t bound_box_offset_x,
|
|
||||||
int16_t bound_box_offset_y, int16_t bound_box_offset_z)
|
|
||||||
{
|
|
||||||
return PaintIntercept::PaintFull(
|
|
||||||
PAINT_98197C, image_id, x_offset, y_offset, bound_box_length_x, bound_box_length_y, bound_box_length_z, z_offset,
|
|
||||||
bound_box_offset_x, bound_box_offset_y, bound_box_offset_z, session->CurrentRotation);
|
|
||||||
}
|
|
||||||
|
|
||||||
paint_struct* PaintAddImageAsParent(
|
|
||||||
paint_session* session, uint32_t image_id, const CoordsXYZ& offset, const CoordsXYZ& boundBoxSize,
|
|
||||||
const CoordsXYZ& boundBoxOffset)
|
|
||||||
{
|
|
||||||
return PaintAddImageAsParent(
|
|
||||||
session, image_id, offset.x, offset.y, boundBoxSize.x, boundBoxSize.y, boundBoxSize.z, offset.z, boundBoxOffset.x,
|
|
||||||
boundBoxOffset.y, boundBoxOffset.z);
|
|
||||||
}
|
|
||||||
|
|
||||||
paint_struct* PaintAddImageAsChild(
|
|
||||||
paint_session* session, uint32_t image_id, const CoordsXYZ& offset, const CoordsXYZ& boundBoxLength,
|
|
||||||
const CoordsXYZ& boundBoxOffset)
|
|
||||||
{
|
|
||||||
return PaintIntercept::PaintFull(
|
|
||||||
PAINT_98198C_COORDS, image_id, offset, boundBoxLength, boundBoxOffset, session->CurrentRotation);
|
|
||||||
}
|
|
||||||
|
|
||||||
paint_struct* PaintAddImageAsOrphan(
|
|
||||||
paint_session* session, uint32_t image_id, int8_t x_offset, int8_t y_offset, int16_t bound_box_length_x,
|
|
||||||
int16_t bound_box_length_y, int8_t bound_box_length_z, int16_t z_offset, int16_t bound_box_offset_x,
|
|
||||||
int16_t bound_box_offset_y, int16_t bound_box_offset_z)
|
|
||||||
{
|
|
||||||
return PaintIntercept::PaintFull(
|
|
||||||
PAINT_98198C, image_id, x_offset, y_offset, bound_box_length_x, bound_box_length_y, bound_box_length_z, z_offset,
|
|
||||||
bound_box_offset_x, bound_box_offset_y, bound_box_offset_z, session->CurrentRotation);
|
|
||||||
}
|
|
||||||
|
|
||||||
paint_struct* PaintAddImageAsChild(
|
|
||||||
paint_session* session, uint32_t image_id, int8_t x_offset, int8_t y_offset, int16_t bound_box_length_x,
|
|
||||||
int16_t bound_box_length_y, int8_t bound_box_length_z, int16_t z_offset, int16_t bound_box_offset_x,
|
|
||||||
int16_t bound_box_offset_y, int16_t bound_box_offset_z)
|
|
||||||
{
|
|
||||||
return PaintIntercept::PaintFull(
|
|
||||||
PAINT_98199C, image_id, x_offset, y_offset, bound_box_length_x, bound_box_length_y, bound_box_length_z, z_offset,
|
|
||||||
bound_box_offset_x, bound_box_offset_y, bound_box_offset_z, session->CurrentRotation);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool PaintAttachToPreviousPS(paint_session* session, uint32_t image_id, int16_t x, int16_t y)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
|
@ -1,24 +0,0 @@
|
||||||
/*****************************************************************************
|
|
||||||
* Copyright (c) 2014-2018 OpenRCT2 developers
|
|
||||||
*
|
|
||||||
* For a complete list of all authors, please refer to contributors.md
|
|
||||||
* Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2
|
|
||||||
*
|
|
||||||
* OpenRCT2 is licensed under the GNU General Public License version 3.
|
|
||||||
*****************************************************************************/
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include "FunctionCall.hpp"
|
|
||||||
|
|
||||||
#include <openrct2/common.h>
|
|
||||||
|
|
||||||
namespace PaintIntercept
|
|
||||||
{
|
|
||||||
void InitHooks();
|
|
||||||
|
|
||||||
void ClearCalls();
|
|
||||||
int GetCalls(function_call* buffer);
|
|
||||||
|
|
||||||
void SetSimulateWoodenSupports(bool enabled);
|
|
||||||
}; // namespace PaintIntercept
|
|
|
@ -1,309 +0,0 @@
|
||||||
/*****************************************************************************
|
|
||||||
* Copyright (c) 2014-2018 OpenRCT2 developers
|
|
||||||
*
|
|
||||||
* For a complete list of all authors, please refer to contributors.md
|
|
||||||
* Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2
|
|
||||||
*
|
|
||||||
* OpenRCT2 is licensed under the GNU General Public License version 3.
|
|
||||||
*****************************************************************************/
|
|
||||||
|
|
||||||
#include "Printer.hpp"
|
|
||||||
|
|
||||||
#include "FunctionCall.hpp"
|
|
||||||
#include "String.hpp"
|
|
||||||
|
|
||||||
#include <iterator>
|
|
||||||
#include <openrct2/sprites.h>
|
|
||||||
|
|
||||||
namespace Printer
|
|
||||||
{
|
|
||||||
static const char* functionNames[] = {
|
|
||||||
"sub_98196C",
|
|
||||||
"sub_98197C",
|
|
||||||
"sub_98198C",
|
|
||||||
"sub_98199C",
|
|
||||||
"metal_a_supports_paint_setup",
|
|
||||||
"metal_b_supports_paint_setup",
|
|
||||||
"wooden_a_supports_paint_setup",
|
|
||||||
"wooden_b_supports_paint_setup",
|
|
||||||
"paint_util_set_segment_support_height",
|
|
||||||
};
|
|
||||||
|
|
||||||
static std::string GetImageIdString(uint32_t imageId);
|
|
||||||
|
|
||||||
static std::string GetOffsetExpressionString(int offset);
|
|
||||||
|
|
||||||
static std::string PrintSegmentSupportHeightCall(SegmentSupportCall call);
|
|
||||||
|
|
||||||
static std::string PrintSideTunnelEdge(TunnelCall edge);
|
|
||||||
|
|
||||||
std::string PrintFunctionCalls(std::vector<function_call> calls, uint16_t baseHeight)
|
|
||||||
{
|
|
||||||
std::string out;
|
|
||||||
|
|
||||||
for (auto&& call : calls)
|
|
||||||
{
|
|
||||||
out += PrintFunctionCall(call, baseHeight).c_str();
|
|
||||||
out += "\n";
|
|
||||||
}
|
|
||||||
|
|
||||||
return out;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string PrintFunctionCall(function_call call, uint16_t baseHeight)
|
|
||||||
{
|
|
||||||
std::string imageId = GetImageIdString(call.supports.colour_flags);
|
|
||||||
assert(call.function < std::size(functionNames));
|
|
||||||
const char* functionName = functionNames[call.function];
|
|
||||||
std::string out = "";
|
|
||||||
|
|
||||||
switch (call.function)
|
|
||||||
{
|
|
||||||
case SUPPORTS_WOOD_A:
|
|
||||||
case SUPPORTS_WOOD_B:
|
|
||||||
out += String::Format(
|
|
||||||
"%s(%d, %d, %s, %s)", functionName, call.supports.type, call.supports.special,
|
|
||||||
PrintHeightOffset(call.supports.height, baseHeight).c_str(), imageId.c_str());
|
|
||||||
if (call.supports.special == 14 || call.supports.special == 15 || call.supports.special == 18
|
|
||||||
|| call.supports.special == 19 || call.supports.special == 22 || call.supports.special == 23)
|
|
||||||
{
|
|
||||||
if (call.supports.prepend_to == SPR_NONE)
|
|
||||||
{
|
|
||||||
out += " [prependTo:SPR_NONE]";
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
std::string prependId = GetImageIdString(call.supports.prepend_to);
|
|
||||||
out += String::Format(" [prependTo:%s]", prependId.c_str());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (call.supports.prepend_to != SPR_NONE)
|
|
||||||
{
|
|
||||||
std::string prependId = GetImageIdString(call.supports.prepend_to);
|
|
||||||
out += String::Format(" [prependTo:%s]", prependId.c_str());
|
|
||||||
}
|
|
||||||
return out;
|
|
||||||
|
|
||||||
case SUPPORTS_METAL_A:
|
|
||||||
case SUPPORTS_METAL_B:
|
|
||||||
return String::Format(
|
|
||||||
"%s(%d, %d, %d, %s, %s)", functionName, call.supports.type, call.supports.segment, call.supports.special,
|
|
||||||
PrintHeightOffset(call.supports.height, baseHeight).c_str(), imageId.c_str());
|
|
||||||
|
|
||||||
case SET_SEGMENT_HEIGHT:
|
|
||||||
return "paint_util_set_segment_support_height";
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string s = String::Format("%s(", functionName);
|
|
||||||
|
|
||||||
imageId = GetImageIdString(call.paint.image_id);
|
|
||||||
s += String::Format("%s, ", imageId.c_str());
|
|
||||||
s += String::Format("%d, %d, ", call.paint.offset.x, call.paint.offset.y);
|
|
||||||
s += String::Format(
|
|
||||||
"%d, %d, %d, ", call.paint.bound_box_length.x, call.paint.bound_box_length.y, call.paint.bound_box_length.z);
|
|
||||||
s += String::Format("%s, ", PrintHeightOffset(call.paint.z_offset, baseHeight).c_str());
|
|
||||||
|
|
||||||
if (call.function != PAINT_98196C)
|
|
||||||
{
|
|
||||||
s += String::Format(
|
|
||||||
"%d, %d, %s, ", call.paint.bound_box_offset.x, call.paint.bound_box_offset.y,
|
|
||||||
PrintHeightOffset(call.paint.bound_box_offset.z, baseHeight).c_str());
|
|
||||||
}
|
|
||||||
|
|
||||||
s += String::Format("%d)", call.paint.rotation);
|
|
||||||
|
|
||||||
if (call.function != PAINT_98196C)
|
|
||||||
{
|
|
||||||
s += String::Format(
|
|
||||||
" = { %d, %d, %s }, { %d, %d, %s }, { %d, %d, %d }", call.paint.offset.x, call.paint.offset.y,
|
|
||||||
PrintHeightOffset(call.paint.z_offset, baseHeight).c_str(), call.paint.bound_box_offset.x,
|
|
||||||
call.paint.bound_box_offset.y, PrintHeightOffset(call.paint.bound_box_offset.z, baseHeight).c_str(),
|
|
||||||
call.paint.bound_box_length.x, call.paint.bound_box_length.y, call.paint.bound_box_length.z);
|
|
||||||
}
|
|
||||||
|
|
||||||
return s;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string PrintSegmentSupportHeightCalls(std::vector<SegmentSupportCall> calls)
|
|
||||||
{
|
|
||||||
std::string out = "";
|
|
||||||
|
|
||||||
for (auto&& call : calls)
|
|
||||||
{
|
|
||||||
out += PrintSegmentSupportHeightCall(call);
|
|
||||||
}
|
|
||||||
|
|
||||||
return out;
|
|
||||||
}
|
|
||||||
|
|
||||||
static std::string PrintSegmentSupportHeightCall(SegmentSupportCall call)
|
|
||||||
{
|
|
||||||
std::string out = "";
|
|
||||||
|
|
||||||
if (call.segments == SEGMENTS_ALL)
|
|
||||||
{
|
|
||||||
out += "SEGMENTS_ALL";
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
int segmentsPrinted = 0;
|
|
||||||
for (int i = 0; i < 9; i++)
|
|
||||||
{
|
|
||||||
if (call.segments & segment_offsets[i])
|
|
||||||
{
|
|
||||||
if (segmentsPrinted > 0)
|
|
||||||
{
|
|
||||||
out += " | ";
|
|
||||||
}
|
|
||||||
out += String::Format("SEGMENT_%02X", 0xB4 + 4 * i);
|
|
||||||
segmentsPrinted++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (call.height == 0xFFFF)
|
|
||||||
{
|
|
||||||
out += ", 0xFFFF";
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
out += String::Format(", %d", call.height);
|
|
||||||
}
|
|
||||||
|
|
||||||
out += String::Format(", 0x%02X\n", call.slope);
|
|
||||||
|
|
||||||
return out;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string PrintSideTunnelCalls(TunnelCall tunnelCalls[4][4])
|
|
||||||
{
|
|
||||||
std::string s;
|
|
||||||
|
|
||||||
for (int direction = 0; direction < 4; ++direction)
|
|
||||||
{
|
|
||||||
s += " + ";
|
|
||||||
}
|
|
||||||
s += "\n";
|
|
||||||
|
|
||||||
for (int direction = 0; direction < 4; ++direction)
|
|
||||||
{
|
|
||||||
std::string leftEdge = PrintSideTunnelEdge(tunnelCalls[direction][3]);
|
|
||||||
std::string rightEdge = PrintSideTunnelEdge(tunnelCalls[direction][2]);
|
|
||||||
s += String::Format(" %s %s ", leftEdge.c_str(), rightEdge.c_str());
|
|
||||||
}
|
|
||||||
s += "\n";
|
|
||||||
|
|
||||||
for (int direction = 0; direction < 4; ++direction)
|
|
||||||
{
|
|
||||||
s += " + + ";
|
|
||||||
}
|
|
||||||
s += "\n";
|
|
||||||
|
|
||||||
for (int direction = 0; direction < 4; ++direction)
|
|
||||||
{
|
|
||||||
std::string leftEdge = PrintSideTunnelEdge(tunnelCalls[direction][0]);
|
|
||||||
std::string rightEdge = PrintSideTunnelEdge(tunnelCalls[direction][1]);
|
|
||||||
s += String::Format(" %s %s ", leftEdge.c_str(), rightEdge.c_str());
|
|
||||||
}
|
|
||||||
s += "\n";
|
|
||||||
|
|
||||||
for (int direction = 0; direction < 4; ++direction)
|
|
||||||
{
|
|
||||||
s += " + ";
|
|
||||||
}
|
|
||||||
s += "\n";
|
|
||||||
|
|
||||||
for (int direction = 0; direction < 4; ++direction)
|
|
||||||
{
|
|
||||||
s += String::Format(" direction %d ", direction);
|
|
||||||
}
|
|
||||||
s += "\n";
|
|
||||||
|
|
||||||
return s;
|
|
||||||
}
|
|
||||||
|
|
||||||
static std::string PrintSideTunnelEdge(TunnelCall edge)
|
|
||||||
{
|
|
||||||
std::string s;
|
|
||||||
|
|
||||||
switch (edge.call)
|
|
||||||
{
|
|
||||||
case TUNNELCALL_SKIPPED:
|
|
||||||
s = " ";
|
|
||||||
break;
|
|
||||||
|
|
||||||
case TUNNELCALL_NONE:
|
|
||||||
s = " - ";
|
|
||||||
break;
|
|
||||||
|
|
||||||
case TUNNELCALL_CALL:
|
|
||||||
std::string offset;
|
|
||||||
|
|
||||||
if (edge.offset <= 0)
|
|
||||||
{
|
|
||||||
offset = String::Format("%d", edge.offset);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
offset = String::Format("+%d", edge.offset);
|
|
||||||
}
|
|
||||||
s = String::Format("%3s/%X", offset.c_str(), edge.type);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
return s;
|
|
||||||
}
|
|
||||||
|
|
||||||
static std::string GetImageIdString(uint32_t imageId)
|
|
||||||
{
|
|
||||||
std::string result;
|
|
||||||
|
|
||||||
uint32_t image = imageId & 0x7FFFF;
|
|
||||||
uint32_t palette = imageId & ~0x7FFFF;
|
|
||||||
|
|
||||||
std::string paletteName;
|
|
||||||
if (palette == TestPaint::DEFAULT_SCHEME_TRACK)
|
|
||||||
paletteName = "SCHEME_TRACK";
|
|
||||||
else if (palette == TestPaint::DEFAULT_SCHEME_SUPPORTS)
|
|
||||||
paletteName = "SCHEME_SUPPORTS";
|
|
||||||
else if (palette == TestPaint::DEFAULT_SCHEME_MISC)
|
|
||||||
paletteName = "SCHEME_MISC";
|
|
||||||
else if (palette == TestPaint::DEFAULT_SCHEME_3)
|
|
||||||
paletteName = "SCHEME_3";
|
|
||||||
else
|
|
||||||
{
|
|
||||||
paletteName = String::Format("0x%08X", palette);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (image == 0)
|
|
||||||
{
|
|
||||||
result = paletteName;
|
|
||||||
}
|
|
||||||
else if (image & 0x70000)
|
|
||||||
{
|
|
||||||
result = String::Format("%s | vehicle.base_image_id + %d", paletteName.c_str(), image & ~0x70000);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
result = String::Format("%s | %d", paletteName.c_str(), image);
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string PrintHeightOffset(uint16_t height, uint16_t baseHeight)
|
|
||||||
{
|
|
||||||
int offset = height - baseHeight;
|
|
||||||
|
|
||||||
return String::Format("height%s", GetOffsetExpressionString(offset).c_str());
|
|
||||||
}
|
|
||||||
|
|
||||||
static std::string GetOffsetExpressionString(int offset)
|
|
||||||
{
|
|
||||||
if (offset < 0)
|
|
||||||
return std::string(" - ") + std::to_string(-offset);
|
|
||||||
if (offset > 0)
|
|
||||||
return std::string(" + ") + std::to_string(offset);
|
|
||||||
return std::string();
|
|
||||||
}
|
|
||||||
}; // namespace Printer
|
|
|
@ -1,29 +0,0 @@
|
||||||
/*****************************************************************************
|
|
||||||
* Copyright (c) 2014-2018 OpenRCT2 developers
|
|
||||||
*
|
|
||||||
* For a complete list of all authors, please refer to contributors.md
|
|
||||||
* Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2
|
|
||||||
*
|
|
||||||
* OpenRCT2 is licensed under the GNU General Public License version 3.
|
|
||||||
*****************************************************************************/
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include "FunctionCall.hpp"
|
|
||||||
#include "SegmentSupportHeightCall.hpp"
|
|
||||||
#include "SideTunnelCall.hpp"
|
|
||||||
|
|
||||||
#include <string>
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
namespace Printer
|
|
||||||
{
|
|
||||||
std::string PrintFunctionCall(function_call call, uint16_t baseHeight);
|
|
||||||
std::string PrintFunctionCalls(std::vector<function_call> calls, uint16_t baseHeight);
|
|
||||||
|
|
||||||
std::string PrintSegmentSupportHeightCalls(std::vector<SegmentSupportCall> calls);
|
|
||||||
|
|
||||||
std::string PrintSideTunnelCalls(TunnelCall tunnelCalls[4][4]);
|
|
||||||
|
|
||||||
std::string PrintHeightOffset(uint16_t height, uint16_t baseHeight);
|
|
||||||
} // namespace Printer
|
|
|
@ -1,155 +0,0 @@
|
||||||
/*****************************************************************************
|
|
||||||
* Copyright (c) 2014-2018 OpenRCT2 developers
|
|
||||||
*
|
|
||||||
* For a complete list of all authors, please refer to contributors.md
|
|
||||||
* Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2
|
|
||||||
*
|
|
||||||
* OpenRCT2 is licensed under the GNU General Public License version 3.
|
|
||||||
*****************************************************************************/
|
|
||||||
|
|
||||||
#include "SegmentSupportHeightCall.hpp"
|
|
||||||
|
|
||||||
#include <algorithm>
|
|
||||||
#include <map>
|
|
||||||
#include <openrct2/paint/tile_element/Paint.TileElement.h>
|
|
||||||
|
|
||||||
static bool SortSegmentSupportCalls(SegmentSupportCall lhs, SegmentSupportCall rhs)
|
|
||||||
{
|
|
||||||
if (lhs.height != rhs.height)
|
|
||||||
{
|
|
||||||
return lhs.height < rhs.height;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (lhs.slope != rhs.slope)
|
|
||||||
{
|
|
||||||
return lhs.slope < rhs.slope;
|
|
||||||
}
|
|
||||||
|
|
||||||
return lhs.segments < rhs.segments;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::vector<SegmentSupportCall> SegmentSupportHeightCall::getSegmentCalls(support_height* supports, uint8_t rotation)
|
|
||||||
{
|
|
||||||
uint16_t positionsRemaining = SEGMENTS_ALL;
|
|
||||||
|
|
||||||
for (int i = 0; i < 9; i++)
|
|
||||||
{
|
|
||||||
if (supports[i].height == 0 && supports[i].slope == 0xFF)
|
|
||||||
{
|
|
||||||
positionsRemaining &= ~segment_offsets[i];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
std::vector<SegmentSupportCall> calls;
|
|
||||||
|
|
||||||
while (positionsRemaining != 0)
|
|
||||||
{
|
|
||||||
SegmentSupportCall call = {};
|
|
||||||
call.height = -1;
|
|
||||||
call.slope = -1;
|
|
||||||
|
|
||||||
support_height referenceSupport = {};
|
|
||||||
|
|
||||||
for (int i = 0; i < 9; i++)
|
|
||||||
{
|
|
||||||
if (positionsRemaining & segment_offsets[i])
|
|
||||||
{
|
|
||||||
referenceSupport = supports[i];
|
|
||||||
if (supports[i].height != 0)
|
|
||||||
{
|
|
||||||
call.height = supports[i].height;
|
|
||||||
}
|
|
||||||
if (supports[i].slope != 0xFF)
|
|
||||||
{
|
|
||||||
call.slope = supports[i].slope;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
uint16_t positionsMatched = 0;
|
|
||||||
for (int i = 0; i < 9; i++)
|
|
||||||
{
|
|
||||||
if (supports[i].height == referenceSupport.height && supports[i].slope == referenceSupport.slope)
|
|
||||||
{
|
|
||||||
positionsMatched |= segment_offsets[i];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
positionsRemaining &= ~positionsMatched;
|
|
||||||
|
|
||||||
call.segments = paint_util_rotate_segments(positionsMatched, (4 - rotation) % 4);
|
|
||||||
|
|
||||||
calls.push_back(call);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (calls.size() > 1)
|
|
||||||
{
|
|
||||||
std::sort(calls.begin(), calls.end(), SortSegmentSupportCalls);
|
|
||||||
}
|
|
||||||
|
|
||||||
return calls;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool SegmentSupportHeightCall::CallsMatch(std::vector<SegmentSupportCall> tileSegmentSupportCalls[4])
|
|
||||||
{
|
|
||||||
std::vector<SegmentSupportCall> baseCallList = tileSegmentSupportCalls[0];
|
|
||||||
for (int i = 1; i < 4; i++)
|
|
||||||
{
|
|
||||||
if (!CallsEqual(baseCallList, tileSegmentSupportCalls[i]))
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool SegmentSupportHeightCall::CallsEqual(std::vector<SegmentSupportCall> lhs, std::vector<SegmentSupportCall> rhs)
|
|
||||||
{
|
|
||||||
if (lhs.size() != rhs.size())
|
|
||||||
return false;
|
|
||||||
for (size_t i = 0; i < lhs.size(); ++i)
|
|
||||||
{
|
|
||||||
if (lhs[i].segments != rhs[i].segments)
|
|
||||||
return false;
|
|
||||||
if (lhs[i].height != rhs[i].height)
|
|
||||||
return false;
|
|
||||||
if (lhs[i].slope != rhs[i].slope)
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool SegmentSupportHeightCall::FindMostCommonSupportCall(
|
|
||||||
std::vector<SegmentSupportCall> calls[4], std::vector<SegmentSupportCall>* out)
|
|
||||||
{
|
|
||||||
std::map<std::vector<SegmentSupportCall>, int> map;
|
|
||||||
|
|
||||||
for (int i = 0; i < 4; ++i)
|
|
||||||
{
|
|
||||||
map[calls[i]] += 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (map.size() == 1)
|
|
||||||
{
|
|
||||||
(*out) = calls[0];
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (map.size() == 2)
|
|
||||||
{
|
|
||||||
for (auto&& item : map)
|
|
||||||
{
|
|
||||||
if (item.second == 3)
|
|
||||||
{
|
|
||||||
(*out) = item.first;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
|
@ -1,45 +0,0 @@
|
||||||
/*****************************************************************************
|
|
||||||
* Copyright (c) 2014-2018 OpenRCT2 developers
|
|
||||||
*
|
|
||||||
* For a complete list of all authors, please refer to contributors.md
|
|
||||||
* Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2
|
|
||||||
*
|
|
||||||
* OpenRCT2 is licensed under the GNU General Public License version 3.
|
|
||||||
*****************************************************************************/
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <openrct2/common.h>
|
|
||||||
#include <openrct2/paint/Paint.h>
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
struct SegmentSupportCall
|
|
||||||
{
|
|
||||||
uint16_t segments;
|
|
||||||
int32_t height;
|
|
||||||
int16_t slope;
|
|
||||||
|
|
||||||
bool operator<(const SegmentSupportCall& other) const
|
|
||||||
{
|
|
||||||
if (height != other.height)
|
|
||||||
{
|
|
||||||
return height < other.height;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (segments != other.segments)
|
|
||||||
{
|
|
||||||
return segments < other.segments;
|
|
||||||
}
|
|
||||||
|
|
||||||
return slope < other.slope;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
class SegmentSupportHeightCall
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
static std::vector<SegmentSupportCall> getSegmentCalls(support_height supports[9], uint8_t rotation);
|
|
||||||
static bool CallsMatch(std::vector<SegmentSupportCall> tileSegmentSupportCalls[4]);
|
|
||||||
static bool CallsEqual(std::vector<SegmentSupportCall> lhs, std::vector<SegmentSupportCall> rhs);
|
|
||||||
static bool FindMostCommonSupportCall(std::vector<SegmentSupportCall> calls[4], std::vector<SegmentSupportCall>* out);
|
|
||||||
};
|
|
|
@ -1,121 +0,0 @@
|
||||||
/*****************************************************************************
|
|
||||||
* Copyright (c) 2014-2018 OpenRCT2 developers
|
|
||||||
*
|
|
||||||
* For a complete list of all authors, please refer to contributors.md
|
|
||||||
* Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2
|
|
||||||
*
|
|
||||||
* OpenRCT2 is licensed under the GNU General Public License version 3.
|
|
||||||
*****************************************************************************/
|
|
||||||
|
|
||||||
#include "SideTunnelCall.hpp"
|
|
||||||
|
|
||||||
int16_t SideTunnelCall::GetTunnelOffset(uint32_t baseHeight, tunnel_entry calls[3])
|
|
||||||
{
|
|
||||||
for (int16_t offset = -56; offset <= 56; offset += 8)
|
|
||||||
{
|
|
||||||
if (calls[0].height != (baseHeight - 8 + offset) / 16)
|
|
||||||
continue;
|
|
||||||
if (calls[1].height != (baseHeight + 0 + offset) / 16)
|
|
||||||
continue;
|
|
||||||
if (calls[2].height != (baseHeight + 8 + offset) / 16)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
return offset;
|
|
||||||
}
|
|
||||||
|
|
||||||
log_error("Unknown tunnel height. (%d, %d, %d)", calls[0].height, calls[1].height, calls[2].height);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
TunnelCall SideTunnelCall::ExtractTunnelCalls(tunnel_entry* calls, uint8_t count, uint16_t baseHeight, bool* error)
|
|
||||||
{
|
|
||||||
TunnelCall tunnelCall = {};
|
|
||||||
|
|
||||||
if (count == 0)
|
|
||||||
{
|
|
||||||
tunnelCall.call = TUNNELCALL_NONE;
|
|
||||||
return tunnelCall;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (count == 3)
|
|
||||||
{
|
|
||||||
tunnelCall.call = TUNNELCALL_CALL;
|
|
||||||
tunnelCall.offset = GetTunnelOffset(baseHeight, calls);
|
|
||||||
tunnelCall.type = calls[0].type;
|
|
||||||
return tunnelCall;
|
|
||||||
}
|
|
||||||
|
|
||||||
*error = true;
|
|
||||||
return tunnelCall;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool SideTunnelCall::TunnelCallsLineUp(TunnelCall tunnelCalls[4][4])
|
|
||||||
{
|
|
||||||
for (int side = 0; side < 4; ++side)
|
|
||||||
{
|
|
||||||
TunnelCall* referenceCall = nullptr;
|
|
||||||
for (int direction = 0; direction < 4; ++direction)
|
|
||||||
{
|
|
||||||
if (tunnelCalls[direction][side].call == TUNNELCALL_SKIPPED)
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (referenceCall == nullptr)
|
|
||||||
{
|
|
||||||
referenceCall = &tunnelCalls[direction][side];
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (referenceCall->call != tunnelCalls[direction][side].call)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
if (referenceCall->call == TUNNELCALL_CALL)
|
|
||||||
{
|
|
||||||
if (referenceCall->type != tunnelCalls[direction][side].type)
|
|
||||||
return false;
|
|
||||||
if (referenceCall->offset != tunnelCalls[direction][side].offset)
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void SideTunnelCall::GetTunnelCallReferencePattern(TunnelCall tunnelCalls[4][4], TunnelCall (*out)[4])
|
|
||||||
{
|
|
||||||
for (int side = 0; side < 4; ++side)
|
|
||||||
{
|
|
||||||
for (int direction = 0; direction < 4; ++direction)
|
|
||||||
{
|
|
||||||
if (tunnelCalls[direction][side].call == TUNNELCALL_SKIPPED)
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
(*out)[side].call = tunnelCalls[direction][side].call;
|
|
||||||
(*out)[side].type = tunnelCalls[direction][side].type;
|
|
||||||
(*out)[side].offset = tunnelCalls[direction][side].offset;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool SideTunnelCall::TunnelPatternsMatch(TunnelCall expected[4], TunnelCall actual[4])
|
|
||||||
{
|
|
||||||
for (int side = 0; side < 4; side++)
|
|
||||||
{
|
|
||||||
if (expected[side].call != actual[side].call)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
if (expected[side].call == TUNNELCALL_CALL)
|
|
||||||
{
|
|
||||||
if (expected[side].type != actual[side].type)
|
|
||||||
return false;
|
|
||||||
if (expected[side].offset != actual[side].offset)
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
|
@ -1,37 +0,0 @@
|
||||||
/*****************************************************************************
|
|
||||||
* Copyright (c) 2014-2018 OpenRCT2 developers
|
|
||||||
*
|
|
||||||
* For a complete list of all authors, please refer to contributors.md
|
|
||||||
* Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2
|
|
||||||
*
|
|
||||||
* OpenRCT2 is licensed under the GNU General Public License version 3.
|
|
||||||
*****************************************************************************/
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <openrct2/common.h>
|
|
||||||
#include <openrct2/paint/Paint.h>
|
|
||||||
|
|
||||||
enum
|
|
||||||
{
|
|
||||||
TUNNELCALL_SKIPPED,
|
|
||||||
TUNNELCALL_NONE,
|
|
||||||
TUNNELCALL_CALL,
|
|
||||||
};
|
|
||||||
|
|
||||||
struct TunnelCall
|
|
||||||
{
|
|
||||||
uint8_t call;
|
|
||||||
int16_t offset;
|
|
||||||
uint8_t type;
|
|
||||||
};
|
|
||||||
|
|
||||||
namespace SideTunnelCall
|
|
||||||
{
|
|
||||||
int16_t GetTunnelOffset(uint32_t baseHeight, tunnel_entry calls[3]);
|
|
||||||
TunnelCall ExtractTunnelCalls(tunnel_entry* list, uint8_t count, uint16_t baseHeight, bool* error);
|
|
||||||
|
|
||||||
bool TunnelPatternsMatch(TunnelCall expected[4], TunnelCall actual[4]);
|
|
||||||
void GetTunnelCallReferencePattern(TunnelCall tunnelCalls[4][4], TunnelCall (*out)[4]);
|
|
||||||
bool TunnelCallsLineUp(TunnelCall tunnelCalls[4][4]);
|
|
||||||
}; // namespace SideTunnelCall
|
|
|
@ -1,27 +0,0 @@
|
||||||
/*****************************************************************************
|
|
||||||
* Copyright (c) 2014-2018 OpenRCT2 developers
|
|
||||||
*
|
|
||||||
* For a complete list of all authors, please refer to contributors.md
|
|
||||||
* Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2
|
|
||||||
*
|
|
||||||
* OpenRCT2 is licensed under the GNU General Public License version 3.
|
|
||||||
*****************************************************************************/
|
|
||||||
|
|
||||||
#include "String.hpp"
|
|
||||||
|
|
||||||
#include <cstdarg>
|
|
||||||
|
|
||||||
namespace String
|
|
||||||
{
|
|
||||||
std::string Format(const char* format, ...)
|
|
||||||
{
|
|
||||||
va_list args;
|
|
||||||
char buffer[512];
|
|
||||||
|
|
||||||
va_start(args, format);
|
|
||||||
vsnprintf(buffer, sizeof(buffer), format, args);
|
|
||||||
va_end(args);
|
|
||||||
|
|
||||||
return std::string(buffer);
|
|
||||||
}
|
|
||||||
}; // namespace String
|
|
|
@ -1,17 +0,0 @@
|
||||||
/*****************************************************************************
|
|
||||||
* Copyright (c) 2014-2018 OpenRCT2 developers
|
|
||||||
*
|
|
||||||
* For a complete list of all authors, please refer to contributors.md
|
|
||||||
* Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2
|
|
||||||
*
|
|
||||||
* OpenRCT2 is licensed under the GNU General Public License version 3.
|
|
||||||
*****************************************************************************/
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <string>
|
|
||||||
|
|
||||||
namespace String
|
|
||||||
{
|
|
||||||
std::string Format(const char* format, ...);
|
|
||||||
}
|
|
|
@ -1,174 +0,0 @@
|
||||||
/*****************************************************************************
|
|
||||||
* Copyright (c) 2014-2018 OpenRCT2 developers
|
|
||||||
*
|
|
||||||
* For a complete list of all authors, please refer to contributors.md
|
|
||||||
* Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2
|
|
||||||
*
|
|
||||||
* OpenRCT2 is licensed under the GNU General Public License version 3.
|
|
||||||
*****************************************************************************/
|
|
||||||
|
|
||||||
#include "TestPaint.hpp"
|
|
||||||
|
|
||||||
#include "GeneralSupportHeightCall.hpp"
|
|
||||||
#include "Hook.h"
|
|
||||||
#include "Printer.hpp"
|
|
||||||
#include "SegmentSupportHeightCall.hpp"
|
|
||||||
#include "Utils.hpp"
|
|
||||||
|
|
||||||
#include <algorithm>
|
|
||||||
#include <openrct2/interface/Viewport.h>
|
|
||||||
#include <openrct2/paint/Paint.h>
|
|
||||||
#include <openrct2/paint/Supports.h>
|
|
||||||
#include <openrct2/ride/TrackData.h>
|
|
||||||
#include <openrct2/scenario/Scenario.h>
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
namespace TestPaint
|
|
||||||
{
|
|
||||||
void ResetEnvironment()
|
|
||||||
{
|
|
||||||
gPaintInteractionType = EnumValue(ViewportInteractionItem::Ride);
|
|
||||||
gPaintSession.InteractionType = ViewportInteractionItem::Ride;
|
|
||||||
|
|
||||||
gTrackColours[SCHEME_TRACK] = DEFAULT_SCHEME_TRACK;
|
|
||||||
gTrackColours[SCHEME_SUPPORTS] = DEFAULT_SCHEME_SUPPORTS;
|
|
||||||
gTrackColours[SCHEME_MISC] = DEFAULT_SCHEME_MISC;
|
|
||||||
gTrackColours[SCHEME_3] = DEFAULT_SCHEME_3;
|
|
||||||
|
|
||||||
gPaintSession.TrackColours[SCHEME_TRACK] = DEFAULT_SCHEME_TRACK;
|
|
||||||
gPaintSession.TrackColours[SCHEME_SUPPORTS] = DEFAULT_SCHEME_SUPPORTS;
|
|
||||||
gPaintSession.TrackColours[SCHEME_MISC] = DEFAULT_SCHEME_MISC;
|
|
||||||
gPaintSession.TrackColours[SCHEME_3] = DEFAULT_SCHEME_3;
|
|
||||||
|
|
||||||
rct_drawpixelinfo dpi = {};
|
|
||||||
dpi.zoom_level = 1;
|
|
||||||
RCT2_Unk140E9A8 = &dpi;
|
|
||||||
gPaintSession.DPI = dpi;
|
|
||||||
|
|
||||||
{
|
|
||||||
static rct_ride_entry rideEntry = {};
|
|
||||||
rct_ride_entry_vehicle vehicleEntry{};
|
|
||||||
vehicleEntry.base_image_id = 0x70000;
|
|
||||||
rideEntry.vehicles[0] = vehicleEntry;
|
|
||||||
gRideList[0] = {};
|
|
||||||
gRideEntries[0] = &rideEntry;
|
|
||||||
}
|
|
||||||
{
|
|
||||||
rct2_ride ride = {};
|
|
||||||
ride.entrance_style = 0;
|
|
||||||
RCT2_Rides[0] = ride;
|
|
||||||
}
|
|
||||||
|
|
||||||
g141E9DB = G141E9DB_FLAG_1 | G141E9DB_FLAG_2;
|
|
||||||
gPaintSession.Unk141E9DB = G141E9DB_FLAG_1 | G141E9DB_FLAG_2;
|
|
||||||
|
|
||||||
RCT2_CurrentViewportFlags = 0;
|
|
||||||
|
|
||||||
gScenarioTicks = 0;
|
|
||||||
RCT2_ScenarioTicks = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
void ResetTunnels()
|
|
||||||
{
|
|
||||||
gLeftTunnelCount = 0;
|
|
||||||
gRightTunnelCount = 0;
|
|
||||||
gPaintSession.LeftTunnelCount = 0;
|
|
||||||
gPaintSession.RightTunnelCount = 0;
|
|
||||||
|
|
||||||
for (int i = 0; i < TUNNEL_MAX_COUNT; i++)
|
|
||||||
{
|
|
||||||
gLeftTunnels[i].height = 0;
|
|
||||||
gLeftTunnels[i].type = 0;
|
|
||||||
gRightTunnels[i].height = 0;
|
|
||||||
gRightTunnels[i].type = 0;
|
|
||||||
gPaintSession.LeftTunnels[i].height = 0;
|
|
||||||
gPaintSession.LeftTunnels[i].type = 0;
|
|
||||||
gPaintSession.RightTunnels[i].height = 0;
|
|
||||||
gPaintSession.RightTunnels[i].type = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
gLeftTunnels[0].height = 0xFF;
|
|
||||||
gLeftTunnels[0].type = 0xFF;
|
|
||||||
gRightTunnels[0].height = 0xFF;
|
|
||||||
gRightTunnels[0].type = 0xFF;
|
|
||||||
gPaintSession.LeftTunnels[0].height = 0xFF;
|
|
||||||
gPaintSession.LeftTunnels[0].type = 0xFF;
|
|
||||||
gPaintSession.RightTunnels[0].height = 0xFF;
|
|
||||||
gPaintSession.RightTunnels[0].type = 0xFF;
|
|
||||||
}
|
|
||||||
|
|
||||||
void ResetSupportHeights()
|
|
||||||
{
|
|
||||||
for (int s = 0; s < 9; ++s)
|
|
||||||
{
|
|
||||||
gSupportSegments[s].height = 0;
|
|
||||||
gSupportSegments[s].slope = 0xFF;
|
|
||||||
gPaintSession.SupportSegments[s].height = 0;
|
|
||||||
gPaintSession.SupportSegments[s].slope = 0xFF;
|
|
||||||
}
|
|
||||||
|
|
||||||
gSupport.height = 0;
|
|
||||||
gSupport.slope = 0xFF;
|
|
||||||
gPaintSession.Support.height = 0;
|
|
||||||
gPaintSession.Support.slope = 0xFF;
|
|
||||||
}
|
|
||||||
|
|
||||||
struct IgnoredEntry
|
|
||||||
{
|
|
||||||
uint8_t Direction;
|
|
||||||
uint8_t TrackSequence;
|
|
||||||
};
|
|
||||||
|
|
||||||
static bool _ignoredAll;
|
|
||||||
static std::vector<IgnoredEntry> _ignoredEntries;
|
|
||||||
|
|
||||||
void testClearIgnore()
|
|
||||||
{
|
|
||||||
_ignoredAll = false;
|
|
||||||
_ignoredEntries.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
void testIgnore(uint8_t direction, uint8_t trackSequence)
|
|
||||||
{
|
|
||||||
_ignoredEntries.push_back({ direction, trackSequence });
|
|
||||||
}
|
|
||||||
|
|
||||||
void testIgnoreAll()
|
|
||||||
{
|
|
||||||
_ignoredAll = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool testIsIgnored(uint8_t direction, uint8_t trackSequence)
|
|
||||||
{
|
|
||||||
if (_ignoredAll)
|
|
||||||
return true;
|
|
||||||
for (const IgnoredEntry& entry : _ignoredEntries)
|
|
||||||
{
|
|
||||||
if (entry.Direction == direction && entry.TrackSequence == trackSequence)
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
} // namespace TestPaint
|
|
||||||
|
|
||||||
void testpaint_clear_ignore()
|
|
||||||
{
|
|
||||||
TestPaint::testClearIgnore();
|
|
||||||
}
|
|
||||||
|
|
||||||
void testpaint_ignore(uint8_t direction, uint8_t trackSequence)
|
|
||||||
{
|
|
||||||
TestPaint::testIgnore(direction, trackSequence);
|
|
||||||
}
|
|
||||||
|
|
||||||
void testpaint_ignore_all()
|
|
||||||
{
|
|
||||||
TestPaint::testIgnoreAll();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool testpaint_is_ignored(uint8_t direction, uint8_t trackSequence)
|
|
||||||
{
|
|
||||||
return TestPaint::testIsIgnored(direction, trackSequence);
|
|
||||||
}
|
|
|
@ -1,81 +0,0 @@
|
||||||
/*****************************************************************************
|
|
||||||
* Copyright (c) 2014-2018 OpenRCT2 developers
|
|
||||||
*
|
|
||||||
* For a complete list of all authors, please refer to contributors.md
|
|
||||||
* Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2
|
|
||||||
*
|
|
||||||
* OpenRCT2 is licensed under the GNU General Public License version 3.
|
|
||||||
*****************************************************************************/
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include "Addresses.h"
|
|
||||||
|
|
||||||
#include <openrct2/common.h>
|
|
||||||
#include <openrct2/drawing/Drawing.h>
|
|
||||||
#include <openrct2/interface/Colour.h>
|
|
||||||
#include <openrct2/paint/Paint.h>
|
|
||||||
#include <openrct2/paint/tile_element/Paint.TileElement.h>
|
|
||||||
#include <openrct2/rct12/RCT12.h>
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
#define gRideEntries RCT2_ADDRESS(0x009ACFA4, rct_ride_entry*)
|
|
||||||
#define gSupportSegments RCT2_ADDRESS(0x0141E9B4, support_height)
|
|
||||||
#define gWoodenSupportsPrependTo RCT2_GLOBAL(0x009DEA58, paint_struct*)
|
|
||||||
#define g_currently_drawn_item RCT2_GLOBAL(0x009DE578, void*)
|
|
||||||
#define gPaintInteractionType RCT2_GLOBAL(0x009DE570, uint8_t)
|
|
||||||
#define gSupportSegments RCT2_ADDRESS(0x0141E9B4, support_height)
|
|
||||||
#define gSupport RCT2_GLOBAL(0x0141E9D8, support_height)
|
|
||||||
#define gWoodenSupportsPrependTo RCT2_GLOBAL(0x009DEA58, paint_struct*)
|
|
||||||
#define gPaintMapPosition RCT2_GLOBAL(0x009DE574, LocationXY16)
|
|
||||||
#define gLeftTunnels RCT2_ADDRESS(0x009E3138, tunnel_entry)
|
|
||||||
#define gLeftTunnelCount RCT2_GLOBAL(0x0141F56A, uint8_t)
|
|
||||||
#define gRightTunnels RCT2_ADDRESS(0x009E30B6, tunnel_entry)
|
|
||||||
#define gRightTunnelCount RCT2_GLOBAL(0x0141F56B, uint8_t)
|
|
||||||
#define gVerticalTunnelHeight RCT2_GLOBAL(0x009E323C, uint8_t)
|
|
||||||
#define gSurfaceElement RCT2_GLOBAL(0x009E3250, TileElement*)
|
|
||||||
#define gDidPassSurface RCT2_GLOBAL(0x009DE57C, bool)
|
|
||||||
#define g141E9DB RCT2_GLOBAL(0x0141E9DB, uint8_t)
|
|
||||||
#define gTrackColours RCT2_ADDRESS(0x00F44198, uint32_t)
|
|
||||||
#define RCT2_CurrentViewportFlags RCT2_GLOBAL(0x0141E9E4, uint32_t)
|
|
||||||
#define RCT2_CurrentRotation RCT2_GLOBAL(0x0141E9E0, uint8_t)
|
|
||||||
#define RCT2_ScenarioTicks RCT2_GLOBAL(0x00F663AC, uint32_t)
|
|
||||||
#define RCT2_Rides RCT2_ADDRESS(0x013628F8, rct2_ride)
|
|
||||||
#define RCT2_Unk140E9A8 RCT2_GLOBAL(0x0140E9A8, rct_drawpixelinfo*)
|
|
||||||
#define RCT2_PaintBoundBoxOffsetX RCT2_GLOBAL(0x009DEA52, int16_t)
|
|
||||||
#define RCT2_PaintBoundBoxOffsetY RCT2_GLOBAL(0x009DEA54, int16_t)
|
|
||||||
#define RCT2_PaintBoundBoxOffsetZ RCT2_GLOBAL(0x009DEA56, int16_t)
|
|
||||||
|
|
||||||
enum
|
|
||||||
{
|
|
||||||
TEST_SUCCESS,
|
|
||||||
TEST_FAILED,
|
|
||||||
TEST_SKIPPED,
|
|
||||||
};
|
|
||||||
|
|
||||||
namespace TestPaint
|
|
||||||
{
|
|
||||||
static const uint32_t DEFAULT_SCHEME_TRACK = SPRITE_ID_PALETTE_COLOUR_2(COLOUR_GREY, COLOUR_WHITE);
|
|
||||||
static const uint32_t DEFAULT_SCHEME_SUPPORTS = SPRITE_ID_PALETTE_COLOUR_2(COLOUR_LIGHT_BLUE, COLOUR_ICY_BLUE);
|
|
||||||
static const uint32_t DEFAULT_SCHEME_MISC = SPRITE_ID_PALETTE_COLOUR_2(COLOUR_DARK_PURPLE, COLOUR_LIGHT_PURPLE);
|
|
||||||
static const uint32_t DEFAULT_SCHEME_3 = SPRITE_ID_PALETTE_COLOUR_2(COLOUR_BRIGHT_PURPLE, COLOUR_DARK_BLUE);
|
|
||||||
|
|
||||||
void ResetEnvironment();
|
|
||||||
void ResetTunnels();
|
|
||||||
void ResetSupportHeights();
|
|
||||||
|
|
||||||
void testClearIgnore();
|
|
||||||
void testIgnore(uint8_t direction, uint8_t trackSequence);
|
|
||||||
void testIgnoreAll();
|
|
||||||
bool testIsIgnored(uint8_t direction, uint8_t trackSequence);
|
|
||||||
} // namespace TestPaint
|
|
||||||
|
|
||||||
enum Verbosity
|
|
||||||
{
|
|
||||||
QUIET,
|
|
||||||
NORMAL,
|
|
||||||
};
|
|
||||||
|
|
||||||
extern Ride gRideList[RCT12_MAX_RIDES_IN_PARK];
|
|
||||||
|
|
||||||
int generatePaintCode(uint8_t rideType);
|
|
|
@ -1,819 +0,0 @@
|
||||||
/*****************************************************************************
|
|
||||||
* Copyright (c) 2014-2018 OpenRCT2 developers
|
|
||||||
*
|
|
||||||
* For a complete list of all authors, please refer to contributors.md
|
|
||||||
* Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2
|
|
||||||
*
|
|
||||||
* OpenRCT2 is licensed under the GNU General Public License version 3.
|
|
||||||
*****************************************************************************/
|
|
||||||
|
|
||||||
#include "TestTrack.hpp"
|
|
||||||
|
|
||||||
#include "../../src/openrct2/ride/RideData.h"
|
|
||||||
#include "Data.h"
|
|
||||||
#include "FunctionCall.hpp"
|
|
||||||
#include "GeneralSupportHeightCall.hpp"
|
|
||||||
#include "PaintIntercept.hpp"
|
|
||||||
#include "Printer.hpp"
|
|
||||||
#include "SegmentSupportHeightCall.hpp"
|
|
||||||
#include "SideTunnelCall.hpp"
|
|
||||||
#include "String.hpp"
|
|
||||||
#include "TestPaint.hpp"
|
|
||||||
#include "Utils.hpp"
|
|
||||||
#include "VerticalTunnelCall.hpp"
|
|
||||||
|
|
||||||
#include <openrct2/paint/Supports.h>
|
|
||||||
#include <openrct2/paint/tile_element/Paint.TileElement.h>
|
|
||||||
#include <openrct2/ride/Ride.h>
|
|
||||||
#include <openrct2/ride/Track.h>
|
|
||||||
#include <openrct2/ride/TrackData.h>
|
|
||||||
#include <string>
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
struct ITestTrackFilter
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
virtual ~ITestTrackFilter()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual bool AppliesTo(uint8_t rideType, uint8_t trackType) abstract;
|
|
||||||
|
|
||||||
virtual int Variations(uint8_t rideType, uint8_t trackType) abstract;
|
|
||||||
|
|
||||||
virtual std::string VariantName(uint8_t rideType, uint8_t trackType, int variant) abstract;
|
|
||||||
|
|
||||||
virtual void ApplyTo(
|
|
||||||
uint8_t rideType, uint8_t trackType, int variant, TileElement* tileElement, TileElement* surfaceElement, Ride* ride,
|
|
||||||
rct_ride_entry* rideEntry) abstract;
|
|
||||||
};
|
|
||||||
|
|
||||||
class CableLiftFilter : public ITestTrackFilter
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
bool AppliesTo(uint8_t rideType, uint8_t trackType) override
|
|
||||||
{
|
|
||||||
return rideType == RIDE_TYPE_GIGA_COASTER;
|
|
||||||
}
|
|
||||||
|
|
||||||
int Variations(uint8_t rideType, uint8_t trackType) override
|
|
||||||
{
|
|
||||||
return 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string VariantName(uint8_t rideType, uint8_t trackType, int variant) override
|
|
||||||
{
|
|
||||||
return String::Format("cableLift:%d", variant);
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual void ApplyTo(
|
|
||||||
uint8_t rideType, uint8_t trackType, int variant, TileElement* tileElement, TileElement* surfaceElement, Ride* ride,
|
|
||||||
rct_ride_entry* rideEntry) override
|
|
||||||
{
|
|
||||||
if (variant == 0)
|
|
||||||
{
|
|
||||||
tileElement->AsTrack()->SetHasCableLift(false);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
tileElement->AsTrack()->SetHasCableLift(true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
class ChainLiftFilter : public ITestTrackFilter
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
bool AppliesTo(uint8_t rideType, uint8_t trackType) override
|
|
||||||
{
|
|
||||||
return !GetRideTypeDescriptor(rideType).HasFlag(RIDE_TYPE_FLAG_FLAT_RIDE);
|
|
||||||
}
|
|
||||||
|
|
||||||
int Variations(uint8_t rideType, uint8_t trackType) override
|
|
||||||
{
|
|
||||||
return 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string VariantName(uint8_t rideType, uint8_t trackType, int variant) override
|
|
||||||
{
|
|
||||||
return String::Format("chainLift:%d", variant);
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual void ApplyTo(
|
|
||||||
uint8_t rideType, uint8_t trackType, int variant, TileElement* tileElement, TileElement* surfaceElement, Ride* ride,
|
|
||||||
rct_ride_entry* rideEntry) override
|
|
||||||
{
|
|
||||||
tileElement->AsTrack()->SetHasChain(variant != 0);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
class InvertedFilter : public ITestTrackFilter
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
bool AppliesTo(uint8_t rideType, uint8_t trackType) override
|
|
||||||
{
|
|
||||||
if (rideType == RIDE_TYPE_MULTI_DIMENSION_ROLLER_COASTER || rideType == RIDE_TYPE_FLYING_ROLLER_COASTER
|
|
||||||
|| rideType == RIDE_TYPE_LAY_DOWN_ROLLER_COASTER)
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
int Variations(uint8_t rideType, uint8_t trackType) override
|
|
||||||
{
|
|
||||||
return 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string VariantName(uint8_t rideType, uint8_t trackType, int variant) override
|
|
||||||
{
|
|
||||||
return String::Format("inverted:%d", variant);
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual void ApplyTo(
|
|
||||||
uint8_t rideType, uint8_t trackType, int variant, TileElement* tileElement, TileElement* surfaceElement, Ride* ride,
|
|
||||||
rct_ride_entry* rideEntry) override
|
|
||||||
{
|
|
||||||
if (variant == 0)
|
|
||||||
{
|
|
||||||
tileElement->AsTrack()->SetHasCableLift(false);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
tileElement->AsTrack()->SetHasCableLift(true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
class EntranceStyleFilter : public ITestTrackFilter
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
bool AppliesTo(uint8_t rideType, uint8_t trackType) override
|
|
||||||
{
|
|
||||||
if (track_type_is_station(trackType))
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
int Variations(uint8_t rideType, uint8_t trackType) override
|
|
||||||
{
|
|
||||||
return 12;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string VariantName(uint8_t rideType, uint8_t trackType, int variant) override
|
|
||||||
{
|
|
||||||
return String::Format("entranceStyle:%d", variant);
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual void ApplyTo(
|
|
||||||
uint8_t rideType, uint8_t trackType, int variant, TileElement* tileElement, TileElement* surfaceElement, Ride* ride,
|
|
||||||
rct_ride_entry* rideEntry) override
|
|
||||||
{
|
|
||||||
ride->entrance_style = variant;
|
|
||||||
RCT2_Rides[0].entrance_style = variant;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
static void CallOriginal(
|
|
||||||
uint8_t rideType, uint8_t trackType, uint8_t direction, uint8_t trackSequence, uint16_t height, TileElement* tileElement)
|
|
||||||
{
|
|
||||||
uint32_t* trackDirectionList = (uint32_t*)RideTypeTrackPaintFunctionsOld[rideType][trackType];
|
|
||||||
const uint8_t rideIndex = 0;
|
|
||||||
|
|
||||||
// Have to call from this point as it pushes esi and expects callee to pop it
|
|
||||||
RCT2_CALLPROC_X(
|
|
||||||
0x006C4934, rideType, (int)trackDirectionList, direction, height, (int)tileElement, rideIndex * sizeof(Ride),
|
|
||||||
trackSequence);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void CallNew(
|
|
||||||
uint8_t rideType, uint8_t trackType, uint8_t direction, uint8_t trackSequence, uint16_t height, TileElement* tileElement)
|
|
||||||
{
|
|
||||||
TRACK_PAINT_FUNCTION_GETTER newPaintFunctionGetter = GetRideTypeDescriptor(rideType).TrackPaintFunction;
|
|
||||||
TRACK_PAINT_FUNCTION newPaintFunction = newPaintFunctionGetter(trackType);
|
|
||||||
|
|
||||||
newPaintFunction(&gPaintSession, 0, trackSequence, direction, height, tileElement);
|
|
||||||
}
|
|
||||||
|
|
||||||
using TestFunction = uint8_t (*)(uint8_t, uint8_t, uint8_t, std::string*);
|
|
||||||
|
|
||||||
static uint8_t TestTrackElementPaintCalls(uint8_t rideType, uint8_t trackType, uint8_t trackSequence, std::string* error);
|
|
||||||
|
|
||||||
static uint8_t TestTrackElementSegmentSupportHeight(
|
|
||||||
uint8_t rideType, uint8_t trackType, uint8_t trackSequence, std::string* error);
|
|
||||||
|
|
||||||
static uint8_t TestTrackElementGeneralSupportHeight(
|
|
||||||
uint8_t rideType, uint8_t trackType, uint8_t trackSequence, std::string* error);
|
|
||||||
|
|
||||||
static uint8_t TestTrackElementSideTunnels(uint8_t rideType, uint8_t trackType, uint8_t trackSequence, std::string* error);
|
|
||||||
|
|
||||||
static uint8_t TestTrackElementVerticalTunnels(uint8_t rideType, uint8_t trackType, uint8_t trackSequence, std::string* error);
|
|
||||||
|
|
||||||
uint8_t TestTrack::TestPaintTrackElement(uint8_t rideType, uint8_t trackType, std::string* out)
|
|
||||||
{
|
|
||||||
if (!Utils::rideSupportsTrackType(rideType, trackType))
|
|
||||||
{
|
|
||||||
return TEST_FAILED;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (rideType == RIDE_TYPE_CHAIRLIFT)
|
|
||||||
{
|
|
||||||
if (track_type_is_station(trackType))
|
|
||||||
{
|
|
||||||
// These rides check neighbouring tiles for tracks
|
|
||||||
return TEST_SKIPPED;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
int sequenceCount = Utils::getTrackSequenceCount(rideType, trackType);
|
|
||||||
std::string error = String::Format("rct2: 0x%08X\n", RideTypeTrackPaintFunctionsOld[rideType][trackType]);
|
|
||||||
|
|
||||||
uint8_t retVal = TEST_SUCCESS;
|
|
||||||
|
|
||||||
static TestFunction functions[] = {
|
|
||||||
TestTrackElementPaintCalls, TestTrackElementSegmentSupportHeight, TestTrackElementGeneralSupportHeight,
|
|
||||||
TestTrackElementSideTunnels, TestTrackElementVerticalTunnels,
|
|
||||||
};
|
|
||||||
|
|
||||||
for (int trackSequence = 0; trackSequence < sequenceCount; trackSequence++)
|
|
||||||
{
|
|
||||||
for (auto&& function : functions)
|
|
||||||
{
|
|
||||||
retVal = function(rideType, trackType, trackSequence, &error);
|
|
||||||
|
|
||||||
if (retVal != TEST_SUCCESS)
|
|
||||||
{
|
|
||||||
*out += error + "\n";
|
|
||||||
return retVal;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return retVal;
|
|
||||||
}
|
|
||||||
|
|
||||||
static uint8_t TestTrackElementPaintCalls(uint8_t rideType, uint8_t trackType, uint8_t trackSequence, std::string* error)
|
|
||||||
{
|
|
||||||
uint16_t height = 3 * 16;
|
|
||||||
|
|
||||||
TileElement tileElement = {};
|
|
||||||
tileElement.SetType(TILE_ELEMENT_TYPE_TRACK);
|
|
||||||
tileElement.SetLastForTile(true);
|
|
||||||
tileElement.AsTrack()->SetTrackType(trackType);
|
|
||||||
tileElement.base_height = height / 16;
|
|
||||||
g_currently_drawn_item = &tileElement;
|
|
||||||
|
|
||||||
TileElement surfaceElement = {};
|
|
||||||
surfaceElement.SetType(TILE_ELEMENT_TYPE_SURFACE);
|
|
||||||
surfaceElement.base_height = MINIMUM_LAND_HEIGHT;
|
|
||||||
gSurfaceElement = &surfaceElement;
|
|
||||||
gDidPassSurface = true;
|
|
||||||
|
|
||||||
gPaintSession.CurrentlyDrawnTileElement = &tileElement;
|
|
||||||
gPaintSession.SurfaceElement = &surfaceElement;
|
|
||||||
gPaintSession.DidPassSurface = true;
|
|
||||||
|
|
||||||
TestPaint::ResetEnvironment();
|
|
||||||
TestPaint::ResetTunnels();
|
|
||||||
|
|
||||||
function_call callBuffer[256] = {};
|
|
||||||
int callCount = 0;
|
|
||||||
|
|
||||||
// TODO: test supports
|
|
||||||
// TODO: test flat rides
|
|
||||||
// TODO: test mazes
|
|
||||||
// TODO: test underground (Wooden RC)
|
|
||||||
// TODO: test station fences
|
|
||||||
|
|
||||||
std::vector<ITestTrackFilter*> filters;
|
|
||||||
filters.push_back(new CableLiftFilter());
|
|
||||||
filters.push_back(new ChainLiftFilter());
|
|
||||||
filters.push_back(new InvertedFilter());
|
|
||||||
filters.push_back(new EntranceStyleFilter());
|
|
||||||
|
|
||||||
std::vector<ITestTrackFilter*> activeFilters;
|
|
||||||
|
|
||||||
for (auto&& filter : filters)
|
|
||||||
{
|
|
||||||
if (filter->AppliesTo(rideType, trackType))
|
|
||||||
{
|
|
||||||
activeFilters.push_back(filter);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add an element so there's always something to add to
|
|
||||||
std::vector<uint8_t> filler;
|
|
||||||
filler.push_back(0);
|
|
||||||
|
|
||||||
std::vector<std::vector<uint8_t>> argumentPermutations;
|
|
||||||
argumentPermutations.push_back(filler);
|
|
||||||
for (size_t filterIndex = 0; filterIndex < activeFilters.size(); ++filterIndex)
|
|
||||||
{
|
|
||||||
ITestTrackFilter* filter = activeFilters[filterIndex];
|
|
||||||
uint8_t variantCount = filter->Variations(rideType, trackType);
|
|
||||||
|
|
||||||
std::vector<std::vector<uint8_t>> newArgumentPermutations;
|
|
||||||
for (int variant = 0; variant < variantCount; variant++)
|
|
||||||
{
|
|
||||||
for (auto&& oldPermutation : argumentPermutations)
|
|
||||||
{
|
|
||||||
std::vector<uint8_t> permutation;
|
|
||||||
permutation.insert(permutation.begin(), oldPermutation.begin(), oldPermutation.end());
|
|
||||||
permutation.push_back(variant);
|
|
||||||
newArgumentPermutations.push_back(permutation);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
argumentPermutations.clear();
|
|
||||||
argumentPermutations.insert(
|
|
||||||
argumentPermutations.begin(), newArgumentPermutations.begin(), newArgumentPermutations.end());
|
|
||||||
}
|
|
||||||
|
|
||||||
for (auto&& arguments : argumentPermutations)
|
|
||||||
{
|
|
||||||
std::string baseCaseName = "[";
|
|
||||||
|
|
||||||
for (size_t filterIndex = 0; filterIndex < activeFilters.size(); ++filterIndex)
|
|
||||||
{
|
|
||||||
uint8_t& variant = arguments[1 + filterIndex];
|
|
||||||
baseCaseName += activeFilters[filterIndex]->VariantName(rideType, trackType, variant);
|
|
||||||
baseCaseName += " ";
|
|
||||||
|
|
||||||
activeFilters[filterIndex]->ApplyTo(
|
|
||||||
rideType, trackType, variant, &tileElement, &surfaceElement, &(gRideList[0]), gRideEntries[0]);
|
|
||||||
}
|
|
||||||
|
|
||||||
for (int currentRotation = 0; currentRotation < 4; currentRotation++)
|
|
||||||
{
|
|
||||||
gCurrentRotation = currentRotation;
|
|
||||||
RCT2_CurrentRotation = currentRotation;
|
|
||||||
gPaintSession.CurrentRotation = currentRotation;
|
|
||||||
for (int direction = 0; direction < 4; direction++)
|
|
||||||
{
|
|
||||||
RCT2_GLOBAL(0x009DE56A, int16_t) = 64; // x
|
|
||||||
RCT2_GLOBAL(0x009DE56E, int16_t) = 64; // y
|
|
||||||
|
|
||||||
std::string caseName = String::Format(
|
|
||||||
"%srotation:%d direction:%d trackSequence:%d]", baseCaseName.c_str(), currentRotation, direction,
|
|
||||||
trackSequence);
|
|
||||||
|
|
||||||
PaintIntercept::ClearCalls();
|
|
||||||
TestPaint::ResetSupportHeights();
|
|
||||||
gWoodenSupportsPrependTo = nullptr;
|
|
||||||
|
|
||||||
CallOriginal(rideType, trackType, direction, trackSequence, height, &tileElement);
|
|
||||||
|
|
||||||
callCount = PaintIntercept::GetCalls(callBuffer);
|
|
||||||
std::vector<function_call> oldCalls;
|
|
||||||
oldCalls.insert(oldCalls.begin(), callBuffer, callBuffer + callCount);
|
|
||||||
|
|
||||||
PaintIntercept::ClearCalls();
|
|
||||||
TestPaint::testClearIgnore();
|
|
||||||
TestPaint::ResetSupportHeights();
|
|
||||||
gPaintSession.WoodenSupportsPrependTo = nullptr;
|
|
||||||
|
|
||||||
CallNew(rideType, trackType, direction, trackSequence, height, &tileElement);
|
|
||||||
|
|
||||||
if (TestPaint::testIsIgnored(direction, trackSequence))
|
|
||||||
{
|
|
||||||
*error += String::Format("[ IGNORED ] %s\n", caseName.c_str());
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
callCount = PaintIntercept::GetCalls(callBuffer);
|
|
||||||
std::vector<function_call> newCalls;
|
|
||||||
newCalls.insert(newCalls.begin(), callBuffer, callBuffer + callCount);
|
|
||||||
|
|
||||||
bool success = true;
|
|
||||||
if (oldCalls.size() != newCalls.size())
|
|
||||||
{
|
|
||||||
*error += String::Format(
|
|
||||||
"Call counts don't match (was %d, expected %d). %s\n", newCalls.size(), oldCalls.size(),
|
|
||||||
caseName.c_str());
|
|
||||||
success = false;
|
|
||||||
}
|
|
||||||
else if (!FunctionCall::AssertsEquals(oldCalls, newCalls))
|
|
||||||
{
|
|
||||||
*error += String::Format("Calls don't match. %s\n", caseName.c_str());
|
|
||||||
success = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!success)
|
|
||||||
{
|
|
||||||
*error += " Expected:\n";
|
|
||||||
*error += Printer::PrintFunctionCalls(oldCalls, height);
|
|
||||||
*error += " Actual:\n";
|
|
||||||
*error += Printer::PrintFunctionCalls(newCalls, height);
|
|
||||||
|
|
||||||
return TEST_FAILED;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return TEST_SUCCESS;
|
|
||||||
}
|
|
||||||
|
|
||||||
static uint8_t TestTrackElementSegmentSupportHeight(
|
|
||||||
uint8_t rideType, uint8_t trackType, uint8_t trackSequence, std::string* error)
|
|
||||||
{
|
|
||||||
uint16_t height = 3 * 16;
|
|
||||||
|
|
||||||
TileElement tileElement = {};
|
|
||||||
tileElement.SetType(TILE_ELEMENT_TYPE_TRACK);
|
|
||||||
tileElement.SetLastForTile(true);
|
|
||||||
tileElement.AsTrack()->SetTrackType(trackType);
|
|
||||||
tileElement.base_height = height / 16;
|
|
||||||
g_currently_drawn_item = &tileElement;
|
|
||||||
|
|
||||||
TileElement surfaceElement = {};
|
|
||||||
surfaceElement.SetType(TILE_ELEMENT_TYPE_SURFACE);
|
|
||||||
surfaceElement.base_height = MINIMUM_LAND_HEIGHT;
|
|
||||||
gSurfaceElement = &surfaceElement;
|
|
||||||
gDidPassSurface = true;
|
|
||||||
|
|
||||||
gPaintSession.CurrentlyDrawnTileElement = &tileElement;
|
|
||||||
gPaintSession.SurfaceElement = &surfaceElement;
|
|
||||||
gPaintSession.DidPassSurface = true;
|
|
||||||
|
|
||||||
TestPaint::ResetEnvironment();
|
|
||||||
TestPaint::ResetTunnels();
|
|
||||||
|
|
||||||
// TODO: Test Chainlift
|
|
||||||
// TODO: Test Maze
|
|
||||||
// TODO: Allow skip
|
|
||||||
|
|
||||||
std::string state = String::Format("[trackSequence:%d chainLift:%d]", trackSequence, 0);
|
|
||||||
|
|
||||||
std::vector<SegmentSupportCall> tileSegmentSupportCalls[4];
|
|
||||||
|
|
||||||
for (int direction = 0; direction < 4; direction++)
|
|
||||||
{
|
|
||||||
TestPaint::ResetSupportHeights();
|
|
||||||
|
|
||||||
CallOriginal(rideType, trackType, direction, trackSequence, height, &tileElement);
|
|
||||||
|
|
||||||
tileSegmentSupportCalls[direction] = SegmentSupportHeightCall::getSegmentCalls(gSupportSegments, direction);
|
|
||||||
}
|
|
||||||
|
|
||||||
std::vector<SegmentSupportCall> referenceCalls = tileSegmentSupportCalls[0];
|
|
||||||
|
|
||||||
if (!SegmentSupportHeightCall::CallsMatch(tileSegmentSupportCalls))
|
|
||||||
{
|
|
||||||
bool success = SegmentSupportHeightCall::FindMostCommonSupportCall(tileSegmentSupportCalls, &referenceCalls);
|
|
||||||
if (!success)
|
|
||||||
{
|
|
||||||
*error += String::Format("Original segment calls didn't match. %s\n", state.c_str());
|
|
||||||
for (int direction = 0; direction < 4; direction++)
|
|
||||||
{
|
|
||||||
*error += String::Format("# %d\n", direction);
|
|
||||||
*error += Printer::PrintSegmentSupportHeightCalls(tileSegmentSupportCalls[direction]);
|
|
||||||
}
|
|
||||||
return TEST_FAILED;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for (int direction = 0; direction < 4; direction++)
|
|
||||||
{
|
|
||||||
TestPaint::ResetSupportHeights();
|
|
||||||
|
|
||||||
TestPaint::testClearIgnore();
|
|
||||||
CallNew(rideType, trackType, direction, trackSequence, height, &tileElement);
|
|
||||||
if (TestPaint::testIsIgnored(direction, trackSequence))
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::vector<SegmentSupportCall> newCalls = SegmentSupportHeightCall::getSegmentCalls(
|
|
||||||
gPaintSession.SupportSegments, direction);
|
|
||||||
if (!SegmentSupportHeightCall::CallsEqual(referenceCalls, newCalls))
|
|
||||||
{
|
|
||||||
*error += String::Format("Segment support heights didn't match. [direction:%d] %s\n", direction, state.c_str());
|
|
||||||
*error += " Expected:\n";
|
|
||||||
*error += Printer::PrintSegmentSupportHeightCalls(referenceCalls);
|
|
||||||
*error += " Actual:\n";
|
|
||||||
*error += Printer::PrintSegmentSupportHeightCalls(newCalls);
|
|
||||||
|
|
||||||
return TEST_FAILED;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return TEST_SUCCESS;
|
|
||||||
}
|
|
||||||
|
|
||||||
static uint8_t TestTrackElementGeneralSupportHeight(
|
|
||||||
uint8_t rideType, uint8_t trackType, uint8_t trackSequence, std::string* error)
|
|
||||||
{
|
|
||||||
uint16_t height = 3 * 16;
|
|
||||||
|
|
||||||
TileElement tileElement = {};
|
|
||||||
tileElement.SetType(TILE_ELEMENT_TYPE_TRACK);
|
|
||||||
tileElement.SetLastForTile(true);
|
|
||||||
tileElement.AsTrack()->SetTrackType(trackType);
|
|
||||||
tileElement.base_height = height / 16;
|
|
||||||
g_currently_drawn_item = &tileElement;
|
|
||||||
|
|
||||||
TileElement surfaceElement = {};
|
|
||||||
surfaceElement.SetType(TILE_ELEMENT_TYPE_SURFACE);
|
|
||||||
surfaceElement.base_height = MINIMUM_LAND_HEIGHT;
|
|
||||||
gSurfaceElement = &surfaceElement;
|
|
||||||
gDidPassSurface = true;
|
|
||||||
|
|
||||||
gPaintSession.CurrentlyDrawnTileElement = &tileElement;
|
|
||||||
gPaintSession.SurfaceElement = &surfaceElement;
|
|
||||||
gPaintSession.DidPassSurface = true;
|
|
||||||
|
|
||||||
TestPaint::ResetEnvironment();
|
|
||||||
TestPaint::ResetTunnels();
|
|
||||||
|
|
||||||
// TODO: Test Chainlift
|
|
||||||
// TODO: Test Maze
|
|
||||||
// TODO: Allow skip
|
|
||||||
|
|
||||||
std::string state = String::Format("[trackSequence:%d chainLift:%d]", trackSequence, 0);
|
|
||||||
|
|
||||||
SupportCall tileGeneralSupportCalls[4];
|
|
||||||
for (int direction = 0; direction < 4; direction++)
|
|
||||||
{
|
|
||||||
TestPaint::ResetSupportHeights();
|
|
||||||
|
|
||||||
CallOriginal(rideType, trackType, direction, trackSequence, height, &tileElement);
|
|
||||||
|
|
||||||
tileGeneralSupportCalls[direction].height = -1;
|
|
||||||
tileGeneralSupportCalls[direction].slope = -1;
|
|
||||||
if (gSupport.height != 0)
|
|
||||||
{
|
|
||||||
tileGeneralSupportCalls[direction].height = gSupport.height;
|
|
||||||
}
|
|
||||||
if (gSupport.slope != 0xFF)
|
|
||||||
{
|
|
||||||
tileGeneralSupportCalls[direction].slope = gSupport.slope;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
SupportCall referenceCall = tileGeneralSupportCalls[0];
|
|
||||||
if (!GeneralSupportHeightCall::CallsMatch(tileGeneralSupportCalls))
|
|
||||||
{
|
|
||||||
bool success = GeneralSupportHeightCall::FindMostCommonSupportCall(tileGeneralSupportCalls, &referenceCall);
|
|
||||||
if (!success)
|
|
||||||
{
|
|
||||||
*error += String::Format("Original support calls didn't match. %s\n", state.c_str());
|
|
||||||
for (int i = 0; i < 4; ++i)
|
|
||||||
{
|
|
||||||
*error += String::Format("[%d, 0x%02X] ", tileGeneralSupportCalls[i].height, tileGeneralSupportCalls[i].slope);
|
|
||||||
}
|
|
||||||
*error += "\n";
|
|
||||||
return TEST_FAILED;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for (int direction = 0; direction < 4; direction++)
|
|
||||||
{
|
|
||||||
TestPaint::ResetSupportHeights();
|
|
||||||
|
|
||||||
TestPaint::testClearIgnore();
|
|
||||||
CallNew(rideType, trackType, direction, trackSequence, height, &tileElement);
|
|
||||||
if (TestPaint::testIsIgnored(direction, trackSequence))
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (referenceCall.height != -1)
|
|
||||||
{
|
|
||||||
if (gPaintSession.Support.height != referenceCall.height)
|
|
||||||
{
|
|
||||||
*error += String::Format(
|
|
||||||
"General support heights didn't match. (expected height + %d, actual: height + %d) [direction:%d] %s\n",
|
|
||||||
referenceCall.height - height, gPaintSession.Support.height - height, direction, state.c_str());
|
|
||||||
return TEST_FAILED;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (referenceCall.slope != -1)
|
|
||||||
{
|
|
||||||
if (gPaintSession.Support.slope != referenceCall.slope)
|
|
||||||
{
|
|
||||||
*error += String::Format(
|
|
||||||
"General support slopes didn't match. (expected 0x%02X, actual: 0x%02X) [direction:%d] %s\n",
|
|
||||||
referenceCall.slope, gPaintSession.Support.slope, direction, state.c_str());
|
|
||||||
return TEST_FAILED;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return TEST_SUCCESS;
|
|
||||||
}
|
|
||||||
|
|
||||||
static uint8_t TestTrackElementSideTunnels(uint8_t rideType, uint8_t trackType, uint8_t trackSequence, std::string* error)
|
|
||||||
{
|
|
||||||
uint16_t height = 3 * 16;
|
|
||||||
|
|
||||||
TileElement tileElement = {};
|
|
||||||
tileElement.SetType(TILE_ELEMENT_TYPE_TRACK);
|
|
||||||
tileElement.SetLastForTile(true);
|
|
||||||
tileElement.AsTrack()->SetTrackType(trackType);
|
|
||||||
tileElement.base_height = height / 16;
|
|
||||||
g_currently_drawn_item = &tileElement;
|
|
||||||
|
|
||||||
TileElement surfaceElement = {};
|
|
||||||
surfaceElement.SetType(TILE_ELEMENT_TYPE_SURFACE);
|
|
||||||
surfaceElement.base_height = MINIMUM_LAND_HEIGHT;
|
|
||||||
gSurfaceElement = &surfaceElement;
|
|
||||||
gDidPassSurface = true;
|
|
||||||
|
|
||||||
gPaintSession.CurrentlyDrawnTileElement = &tileElement;
|
|
||||||
gPaintSession.SurfaceElement = &surfaceElement;
|
|
||||||
gPaintSession.DidPassSurface = true;
|
|
||||||
|
|
||||||
TestPaint::ResetEnvironment();
|
|
||||||
TestPaint::ResetTunnels();
|
|
||||||
|
|
||||||
TunnelCall tileTunnelCalls[4][4];
|
|
||||||
|
|
||||||
// TODO: test inverted tracks
|
|
||||||
|
|
||||||
for (int direction = 0; direction < 4; direction++)
|
|
||||||
{
|
|
||||||
TestPaint::ResetTunnels();
|
|
||||||
|
|
||||||
for (int8_t offset = -8; offset <= 8; offset += 8)
|
|
||||||
{
|
|
||||||
CallOriginal(rideType, trackType, direction, trackSequence, height + offset, &tileElement);
|
|
||||||
}
|
|
||||||
|
|
||||||
uint8_t rightIndex = (direction + 1) % 4;
|
|
||||||
uint8_t leftIndex = direction;
|
|
||||||
|
|
||||||
for (int i = 0; i < 4; ++i)
|
|
||||||
{
|
|
||||||
tileTunnelCalls[direction][i].call = TUNNELCALL_SKIPPED;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool err = false;
|
|
||||||
tileTunnelCalls[direction][rightIndex] = SideTunnelCall::ExtractTunnelCalls(
|
|
||||||
gRightTunnels, gRightTunnelCount, height, &err);
|
|
||||||
|
|
||||||
tileTunnelCalls[direction][leftIndex] = SideTunnelCall::ExtractTunnelCalls(
|
|
||||||
gLeftTunnels, gLeftTunnelCount, height, &err);
|
|
||||||
|
|
||||||
if (err)
|
|
||||||
{
|
|
||||||
*error += "Multiple tunnels on one side aren't supported.\n";
|
|
||||||
return TEST_FAILED;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
TunnelCall newTileTunnelCalls[4][4];
|
|
||||||
for (int direction = 0; direction < 4; direction++)
|
|
||||||
{
|
|
||||||
TestPaint::ResetTunnels();
|
|
||||||
|
|
||||||
TestPaint::testClearIgnore();
|
|
||||||
|
|
||||||
for (int8_t offset = -8; offset <= 8; offset += 8)
|
|
||||||
{
|
|
||||||
// TODO: move tunnel pushing to interface so we don't have to check the output 3 times
|
|
||||||
CallNew(rideType, trackType, direction, trackSequence, height + offset, &tileElement);
|
|
||||||
}
|
|
||||||
|
|
||||||
uint8_t rightIndex = (direction + 1) % 4;
|
|
||||||
uint8_t leftIndex = direction;
|
|
||||||
|
|
||||||
for (int i = 0; i < 4; ++i)
|
|
||||||
{
|
|
||||||
newTileTunnelCalls[direction][i].call = TUNNELCALL_SKIPPED;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool err = false;
|
|
||||||
newTileTunnelCalls[direction][rightIndex] = SideTunnelCall::ExtractTunnelCalls(
|
|
||||||
gPaintSession.RightTunnels, gPaintSession.RightTunnelCount, height, &err);
|
|
||||||
newTileTunnelCalls[direction][leftIndex] = SideTunnelCall::ExtractTunnelCalls(
|
|
||||||
gPaintSession.LeftTunnels, gPaintSession.LeftTunnelCount, height, &err);
|
|
||||||
if (err)
|
|
||||||
{
|
|
||||||
*error += "Multiple tunnels on one side aren't supported.\n";
|
|
||||||
return TEST_FAILED;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!SideTunnelCall::TunnelCallsLineUp(tileTunnelCalls))
|
|
||||||
{
|
|
||||||
// TODO: Check that new pattern uses the same tunnel group (round, big round, etc.)
|
|
||||||
*error += String::Format(
|
|
||||||
"Original tunnel calls don\'t line up. Skipping tunnel validation [trackSequence:%d].\n", trackSequence);
|
|
||||||
*error += Printer::PrintSideTunnelCalls(tileTunnelCalls);
|
|
||||||
|
|
||||||
if (!SideTunnelCall::TunnelCallsLineUp(newTileTunnelCalls))
|
|
||||||
{
|
|
||||||
*error += String::Format("Decompiled tunnel calls don\'t line up. [trackSequence:%d].\n", trackSequence);
|
|
||||||
*error += Printer::PrintSideTunnelCalls(newTileTunnelCalls);
|
|
||||||
return TEST_FAILED;
|
|
||||||
}
|
|
||||||
|
|
||||||
return TEST_SUCCESS;
|
|
||||||
}
|
|
||||||
|
|
||||||
TunnelCall referencePattern[4];
|
|
||||||
SideTunnelCall::GetTunnelCallReferencePattern(tileTunnelCalls, &referencePattern);
|
|
||||||
|
|
||||||
TunnelCall actualPattern[4];
|
|
||||||
SideTunnelCall::GetTunnelCallReferencePattern(newTileTunnelCalls, &actualPattern);
|
|
||||||
|
|
||||||
if (!SideTunnelCall::TunnelPatternsMatch(referencePattern, actualPattern))
|
|
||||||
{
|
|
||||||
*error += String::Format("Tunnel calls don't match expected pattern. [trackSequence:%d]\n", trackSequence);
|
|
||||||
*error += " Expected:\n";
|
|
||||||
*error += Printer::PrintSideTunnelCalls(tileTunnelCalls);
|
|
||||||
*error += " Actual:\n";
|
|
||||||
*error += Printer::PrintSideTunnelCalls(newTileTunnelCalls);
|
|
||||||
return TEST_FAILED;
|
|
||||||
}
|
|
||||||
|
|
||||||
return TEST_SUCCESS;
|
|
||||||
}
|
|
||||||
|
|
||||||
static uint8_t TestTrackElementVerticalTunnels(uint8_t rideType, uint8_t trackType, uint8_t trackSequence, std::string* error)
|
|
||||||
{
|
|
||||||
uint16_t height = 3 * 16;
|
|
||||||
|
|
||||||
TileElement tileElement = {};
|
|
||||||
tileElement.SetType(TILE_ELEMENT_TYPE_TRACK);
|
|
||||||
tileElement.SetLastForTile(true);
|
|
||||||
tileElement.AsTrack()->SetTrackType(trackType);
|
|
||||||
tileElement.base_height = height / 16;
|
|
||||||
g_currently_drawn_item = &tileElement;
|
|
||||||
|
|
||||||
TileElement surfaceElement = {};
|
|
||||||
surfaceElement.SetType(TILE_ELEMENT_TYPE_SURFACE);
|
|
||||||
surfaceElement.base_height = MINIMUM_LAND_HEIGHT;
|
|
||||||
gSurfaceElement = &surfaceElement;
|
|
||||||
gDidPassSurface = true;
|
|
||||||
|
|
||||||
gPaintSession.CurrentlyDrawnTileElement = &tileElement;
|
|
||||||
gPaintSession.SurfaceElement = &surfaceElement;
|
|
||||||
gPaintSession.DidPassSurface = true;
|
|
||||||
|
|
||||||
TestPaint::ResetEnvironment();
|
|
||||||
TestPaint::ResetTunnels();
|
|
||||||
|
|
||||||
uint16_t verticalTunnelHeights[4];
|
|
||||||
|
|
||||||
for (int direction = 0; direction < 4; direction++)
|
|
||||||
{
|
|
||||||
uint8_t tunnelHeights[3] = { 0 };
|
|
||||||
|
|
||||||
for (uint8_t i = 0; i < 3; i++)
|
|
||||||
{
|
|
||||||
gVerticalTunnelHeight = 0;
|
|
||||||
CallOriginal(rideType, trackType, direction, trackSequence, height - 8 + i * 8, &tileElement);
|
|
||||||
tunnelHeights[i] = gVerticalTunnelHeight;
|
|
||||||
}
|
|
||||||
|
|
||||||
verticalTunnelHeights[direction] = VerticalTunnelCall::GetTunnelHeight(height, tunnelHeights);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!VerticalTunnelCall::HeightIsConsistent(verticalTunnelHeights))
|
|
||||||
{
|
|
||||||
*error += String::Format(
|
|
||||||
"Original vertical tunnel height is inconsistent, skipping test. [trackSequence:%d]\n", trackSequence);
|
|
||||||
return TEST_SUCCESS;
|
|
||||||
}
|
|
||||||
|
|
||||||
uint16_t referenceHeight = verticalTunnelHeights[0];
|
|
||||||
|
|
||||||
for (int direction = 0; direction < 4; direction++)
|
|
||||||
{
|
|
||||||
TestPaint::testClearIgnore();
|
|
||||||
|
|
||||||
testPaintVerticalTunnelHeight = 0;
|
|
||||||
CallNew(rideType, trackType, direction, trackSequence, height, &tileElement);
|
|
||||||
|
|
||||||
if (TestPaint::testIsIgnored(direction, trackSequence))
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (testPaintVerticalTunnelHeight != referenceHeight)
|
|
||||||
{
|
|
||||||
if (referenceHeight == 0)
|
|
||||||
{
|
|
||||||
*error += String::Format(
|
|
||||||
"Expected no tunnel. Actual: %s [trackSequence:%d]\n",
|
|
||||||
Printer::PrintHeightOffset(testPaintVerticalTunnelHeight, height).c_str(), trackSequence);
|
|
||||||
return TEST_FAILED;
|
|
||||||
}
|
|
||||||
|
|
||||||
*error += String::Format(
|
|
||||||
"Expected vertical tunnel height to be `%s`, was `%s`. [trackSequence:%d direction:%d]\n",
|
|
||||||
Printer::PrintHeightOffset(referenceHeight, height).c_str(),
|
|
||||||
Printer::PrintHeightOffset(testPaintVerticalTunnelHeight, height).c_str(), trackSequence, direction);
|
|
||||||
|
|
||||||
return TEST_FAILED;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return TEST_SUCCESS;
|
|
||||||
}
|
|
|
@ -1,19 +0,0 @@
|
||||||
/*****************************************************************************
|
|
||||||
* Copyright (c) 2014-2018 OpenRCT2 developers
|
|
||||||
*
|
|
||||||
* For a complete list of all authors, please refer to contributors.md
|
|
||||||
* Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2
|
|
||||||
*
|
|
||||||
* OpenRCT2 is licensed under the GNU General Public License version 3.
|
|
||||||
*****************************************************************************/
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <openrct2/common.h>
|
|
||||||
#include <string>
|
|
||||||
|
|
||||||
class TestTrack
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
static uint8_t TestPaintTrackElement(uint8_t rideType, uint8_t trackType, std::string* out);
|
|
||||||
};
|
|
File diff suppressed because it is too large
Load Diff
|
@ -1,65 +0,0 @@
|
||||||
/*****************************************************************************
|
|
||||||
* Copyright (c) 2014-2018 OpenRCT2 developers
|
|
||||||
*
|
|
||||||
* For a complete list of all authors, please refer to contributors.md
|
|
||||||
* Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2
|
|
||||||
*
|
|
||||||
* OpenRCT2 is licensed under the GNU General Public License version 3.
|
|
||||||
*****************************************************************************/
|
|
||||||
|
|
||||||
#include "Utils.hpp"
|
|
||||||
|
|
||||||
#include "Data.h"
|
|
||||||
|
|
||||||
#include <openrct2/ride/Ride.h>
|
|
||||||
#include <openrct2/ride/RideData.h>
|
|
||||||
#include <openrct2/ride/Track.h>
|
|
||||||
#include <openrct2/ride/TrackData.h>
|
|
||||||
|
|
||||||
namespace Utils
|
|
||||||
{
|
|
||||||
int getTrackSequenceCount(uint8_t rideType, uint8_t trackType)
|
|
||||||
{
|
|
||||||
int sequenceCount = 0;
|
|
||||||
|
|
||||||
for (int i = 0; i < 256; i++)
|
|
||||||
{
|
|
||||||
if (TrackBlocks[trackType][i].index == 0xFF)
|
|
||||||
{
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
sequenceCount++;
|
|
||||||
}
|
|
||||||
|
|
||||||
return sequenceCount;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool rideSupportsTrackType(uint8_t rideType, uint8_t trackType)
|
|
||||||
{
|
|
||||||
TRACK_PAINT_FUNCTION_GETTER newPaintGetter = GetRideTypeDescriptor(rideType).TrackPaintFunction;
|
|
||||||
|
|
||||||
if (newPaintGetter == nullptr)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (newPaintGetter(trackType) == nullptr)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (RideTypeTrackPaintFunctionsOld[rideType][trackType] == 0)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool rideIsImplemented(uint8_t rideType)
|
|
||||||
{
|
|
||||||
TRACK_PAINT_FUNCTION_GETTER newPaintGetter = GetRideTypeDescriptor(rideType).TrackPaintFunction;
|
|
||||||
return (newPaintGetter != 0);
|
|
||||||
}
|
|
||||||
} // namespace Utils
|
|
|
@ -1,19 +0,0 @@
|
||||||
/*****************************************************************************
|
|
||||||
* Copyright (c) 2014-2018 OpenRCT2 developers
|
|
||||||
*
|
|
||||||
* For a complete list of all authors, please refer to contributors.md
|
|
||||||
* Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2
|
|
||||||
*
|
|
||||||
* OpenRCT2 is licensed under the GNU General Public License version 3.
|
|
||||||
*****************************************************************************/
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <openrct2/common.h>
|
|
||||||
|
|
||||||
namespace Utils
|
|
||||||
{
|
|
||||||
int getTrackSequenceCount(uint8_t rideType, uint8_t trackType);
|
|
||||||
bool rideSupportsTrackType(uint8_t rideType, uint8_t trackType);
|
|
||||||
bool rideIsImplemented(uint8_t rideType);
|
|
||||||
} // namespace Utils
|
|
|
@ -1,44 +0,0 @@
|
||||||
/*****************************************************************************
|
|
||||||
* Copyright (c) 2014-2018 OpenRCT2 developers
|
|
||||||
*
|
|
||||||
* For a complete list of all authors, please refer to contributors.md
|
|
||||||
* Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2
|
|
||||||
*
|
|
||||||
* OpenRCT2 is licensed under the GNU General Public License version 3.
|
|
||||||
*****************************************************************************/
|
|
||||||
|
|
||||||
#include "VerticalTunnelCall.hpp"
|
|
||||||
|
|
||||||
uint16_t VerticalTunnelCall::GetTunnelHeight(uint16_t baseHeight, uint8_t* calls)
|
|
||||||
{
|
|
||||||
if (calls[0] == 0 && calls[1] == 0 && calls[2] == 0)
|
|
||||||
{
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (int16_t offset = 0; offset <= 256; offset += 8)
|
|
||||||
{
|
|
||||||
if (calls[0] != (baseHeight - 8 + offset) / 16)
|
|
||||||
continue;
|
|
||||||
if (calls[1] != (baseHeight + 0 + offset) / 16)
|
|
||||||
continue;
|
|
||||||
if (calls[2] != (baseHeight + 8 + offset) / 16)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
return baseHeight + offset;
|
|
||||||
}
|
|
||||||
|
|
||||||
log_error("Unknown tunnel height. (%d, %d, %d)", calls[0], calls[1], calls[2]);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool VerticalTunnelCall::HeightIsConsistent(uint16_t* heights)
|
|
||||||
{
|
|
||||||
for (int i = 1; i < 4; ++i)
|
|
||||||
{
|
|
||||||
if (heights[i] != heights[0])
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
|
@ -1,18 +0,0 @@
|
||||||
/*****************************************************************************
|
|
||||||
* Copyright (c) 2014-2018 OpenRCT2 developers
|
|
||||||
*
|
|
||||||
* For a complete list of all authors, please refer to contributors.md
|
|
||||||
* Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2
|
|
||||||
*
|
|
||||||
* OpenRCT2 is licensed under the GNU General Public License version 3.
|
|
||||||
*****************************************************************************/
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <openrct2/common.h>
|
|
||||||
|
|
||||||
namespace VerticalTunnelCall
|
|
||||||
{
|
|
||||||
uint16_t GetTunnelHeight(uint16_t baseHeight, uint8_t* calls);
|
|
||||||
bool HeightIsConsistent(uint16_t* heights);
|
|
||||||
}; // namespace VerticalTunnelCall
|
|
File diff suppressed because it is too large
Load Diff
|
@ -1,644 +0,0 @@
|
||||||
/*****************************************************************************
|
|
||||||
* Copyright (c) 2014-2018 OpenRCT2 developers
|
|
||||||
*
|
|
||||||
* For a complete list of all authors, please refer to contributors.md
|
|
||||||
* Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2
|
|
||||||
*
|
|
||||||
* OpenRCT2 is licensed under the GNU General Public License version 3.
|
|
||||||
*****************************************************************************/
|
|
||||||
|
|
||||||
#include <algorithm>
|
|
||||||
#include <cstdarg>
|
|
||||||
#include <cstdio>
|
|
||||||
#include <cstring>
|
|
||||||
#include <string>
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
#if defined(__unix__)
|
|
||||||
# include <sys/mman.h>
|
|
||||||
# include <unistd.h>
|
|
||||||
#endif // defined(__unix__)
|
|
||||||
|
|
||||||
#include "Data.h"
|
|
||||||
#include "PaintIntercept.hpp"
|
|
||||||
#include "TestTrack.hpp"
|
|
||||||
#include "Utils.hpp"
|
|
||||||
|
|
||||||
#include <openrct2/rct2/RCT2.h>
|
|
||||||
#include <openrct2/ride/Ride.h>
|
|
||||||
#include <openrct2/ride/RideData.h>
|
|
||||||
#include <openrct2/ride/Track.h>
|
|
||||||
#include <openrct2/ride/TrackData.h>
|
|
||||||
|
|
||||||
struct TestCase
|
|
||||||
{
|
|
||||||
uint8_t rideType;
|
|
||||||
std::vector<uint8_t> trackTypes;
|
|
||||||
};
|
|
||||||
|
|
||||||
enum CLIColour
|
|
||||||
{
|
|
||||||
DEFAULT,
|
|
||||||
RED,
|
|
||||||
YELLOW,
|
|
||||||
GREEN,
|
|
||||||
};
|
|
||||||
|
|
||||||
bool gTestColor = true;
|
|
||||||
Verbosity _verbosity = NORMAL;
|
|
||||||
|
|
||||||
static bool CStringEquals(const char* lhs, const char* rhs)
|
|
||||||
{
|
|
||||||
if (lhs == nullptr)
|
|
||||||
return rhs == nullptr;
|
|
||||||
|
|
||||||
if (rhs == nullptr)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
return strcmp(lhs, rhs) == 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
enum COLOUR_METHOD
|
|
||||||
{
|
|
||||||
COLOUR_METHOD_NONE,
|
|
||||||
COLOUR_METHOD_ANSI,
|
|
||||||
COLOUR_METHOD_WINDOWS,
|
|
||||||
};
|
|
||||||
|
|
||||||
static COLOUR_METHOD GetColourMethod()
|
|
||||||
{
|
|
||||||
if (!gTestColor)
|
|
||||||
{
|
|
||||||
return COLOUR_METHOD_NONE;
|
|
||||||
}
|
|
||||||
|
|
||||||
const char* const term = getenv("TERM");
|
|
||||||
const bool term_supports_color = CStringEquals(term, "xterm") || CStringEquals(term, "xterm-color")
|
|
||||||
|| CStringEquals(term, "xterm-256color") || CStringEquals(term, "screen") || CStringEquals(term, "screen-256color")
|
|
||||||
|| CStringEquals(term, "tmux") || CStringEquals(term, "tmux-256color") || CStringEquals(term, "rxvt-unicode")
|
|
||||||
|| CStringEquals(term, "rxvt-unicode-256color") || CStringEquals(term, "linux") || CStringEquals(term, "cygwin");
|
|
||||||
|
|
||||||
if (term_supports_color)
|
|
||||||
{
|
|
||||||
return COLOUR_METHOD_ANSI;
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifdef __WINDOWS__
|
|
||||||
return COLOUR_METHOD_WINDOWS;
|
|
||||||
#else
|
|
||||||
return COLOUR_METHOD_NONE;
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
static const char* GetAnsiColorCode(CLIColour color)
|
|
||||||
{
|
|
||||||
switch (color)
|
|
||||||
{
|
|
||||||
case RED:
|
|
||||||
return "1";
|
|
||||||
case GREEN:
|
|
||||||
return "2";
|
|
||||||
case YELLOW:
|
|
||||||
return "3";
|
|
||||||
default:
|
|
||||||
return nullptr;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifdef __WINDOWS__
|
|
||||||
|
|
||||||
static WORD GetCurrentWindowsConsoleAttribute(HANDLE hConsoleOutput)
|
|
||||||
{
|
|
||||||
CONSOLE_SCREEN_BUFFER_INFO csbi;
|
|
||||||
GetConsoleScreenBufferInfo(hConsoleOutput, &csbi);
|
|
||||||
return csbi.wAttributes;
|
|
||||||
}
|
|
||||||
|
|
||||||
static WORD GetWindowsConsoleAttribute(CLIColour color, WORD defaultAttr)
|
|
||||||
{
|
|
||||||
switch (color)
|
|
||||||
{
|
|
||||||
case RED:
|
|
||||||
return FOREGROUND_RED;
|
|
||||||
case GREEN:
|
|
||||||
return FOREGROUND_GREEN;
|
|
||||||
case YELLOW:
|
|
||||||
return FOREGROUND_RED | FOREGROUND_GREEN;
|
|
||||||
default:
|
|
||||||
return defaultAttr;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
||||||
static void Write_VA(Verbosity verbosity, CLIColour colour, const char* fmt, va_list args)
|
|
||||||
{
|
|
||||||
if (_verbosity < verbosity)
|
|
||||||
return;
|
|
||||||
|
|
||||||
COLOUR_METHOD colourMethod = GetColourMethod();
|
|
||||||
|
|
||||||
if (colour == CLIColour::DEFAULT || colourMethod == COLOUR_METHOD_NONE)
|
|
||||||
{
|
|
||||||
vprintf(fmt, args);
|
|
||||||
}
|
|
||||||
else if (colourMethod == COLOUR_METHOD_ANSI)
|
|
||||||
{
|
|
||||||
printf("\033[0;3%sm", GetAnsiColorCode(colour));
|
|
||||||
vprintf(fmt, args);
|
|
||||||
printf("\033[m");
|
|
||||||
}
|
|
||||||
else if (colourMethod == COLOUR_METHOD_WINDOWS)
|
|
||||||
{
|
|
||||||
#ifdef __WINDOWS__
|
|
||||||
HANDLE hStdOut = GetStdHandle(STD_OUTPUT_HANDLE);
|
|
||||||
WORD defaultAttr = GetCurrentWindowsConsoleAttribute(hStdOut);
|
|
||||||
SetConsoleTextAttribute(hStdOut, GetWindowsConsoleAttribute(colour, defaultAttr));
|
|
||||||
vprintf(fmt, args);
|
|
||||||
SetConsoleTextAttribute(hStdOut, defaultAttr);
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void Write(Verbosity verbosity, CLIColour colour, const char* fmt, ...)
|
|
||||||
{
|
|
||||||
va_list args;
|
|
||||||
va_start(args, fmt);
|
|
||||||
Write_VA(verbosity, colour, fmt, args);
|
|
||||||
va_end(args);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void Write(Verbosity verbosity, const char* fmt, ...)
|
|
||||||
{
|
|
||||||
va_list args;
|
|
||||||
va_start(args, fmt);
|
|
||||||
Write_VA(verbosity, DEFAULT, fmt, args);
|
|
||||||
va_end(args);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void Write(CLIColour colour, const char* fmt, ...)
|
|
||||||
{
|
|
||||||
va_list args;
|
|
||||||
va_start(args, fmt);
|
|
||||||
Write_VA(NORMAL, colour, fmt, args);
|
|
||||||
va_end(args);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void Write(const char* fmt, ...)
|
|
||||||
{
|
|
||||||
va_list args;
|
|
||||||
va_start(args, fmt);
|
|
||||||
Write_VA(NORMAL, DEFAULT, fmt, args);
|
|
||||||
va_end(args);
|
|
||||||
}
|
|
||||||
|
|
||||||
#if defined(__WINDOWS__)
|
|
||||||
|
|
||||||
# include <shellapi.h>
|
|
||||||
|
|
||||||
int main(int argc, char* argv[]);
|
|
||||||
|
|
||||||
# define OPENRCT2_DLL_MODULE_NAME "openrct2.dll"
|
|
||||||
|
|
||||||
static HMODULE _dllModule = nullptr;
|
|
||||||
|
|
||||||
utf8* utf8_write_codepoint(utf8* dst, uint32_t codepoint)
|
|
||||||
{
|
|
||||||
if (codepoint <= 0x7F)
|
|
||||||
{
|
|
||||||
dst[0] = (utf8)codepoint;
|
|
||||||
return dst + 1;
|
|
||||||
}
|
|
||||||
else if (codepoint <= 0x7FF)
|
|
||||||
{
|
|
||||||
dst[0] = 0xC0 | ((codepoint >> 6) & 0x1F);
|
|
||||||
dst[1] = 0x80 | (codepoint & 0x3F);
|
|
||||||
return dst + 2;
|
|
||||||
}
|
|
||||||
else if (codepoint <= 0xFFFF)
|
|
||||||
{
|
|
||||||
dst[0] = 0xE0 | ((codepoint >> 12) & 0x0F);
|
|
||||||
dst[1] = 0x80 | ((codepoint >> 6) & 0x3F);
|
|
||||||
dst[2] = 0x80 | (codepoint & 0x3F);
|
|
||||||
return dst + 3;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
dst[0] = 0xF0 | ((codepoint >> 18) & 0x07);
|
|
||||||
dst[1] = 0x80 | ((codepoint >> 12) & 0x3F);
|
|
||||||
dst[2] = 0x80 | ((codepoint >> 6) & 0x3F);
|
|
||||||
dst[3] = 0x80 | (codepoint & 0x3F);
|
|
||||||
return dst + 4;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
utf8* widechar_to_utf8(const wchar_t* src)
|
|
||||||
{
|
|
||||||
utf8* result = (utf8*)malloc((wcslen(src) * 4) + 1);
|
|
||||||
utf8* dst = result;
|
|
||||||
|
|
||||||
for (; *src != 0; src++)
|
|
||||||
{
|
|
||||||
dst = utf8_write_codepoint(dst, *src);
|
|
||||||
}
|
|
||||||
*dst++ = 0;
|
|
||||||
|
|
||||||
size_t size = (size_t)(dst - result);
|
|
||||||
return (utf8*)realloc(result, size);
|
|
||||||
}
|
|
||||||
|
|
||||||
utf8** windows_get_command_line_args(int* outNumArgs)
|
|
||||||
{
|
|
||||||
int argc;
|
|
||||||
|
|
||||||
// Get command line arguments as widechar
|
|
||||||
LPWSTR commandLine = GetCommandLineW();
|
|
||||||
LPWSTR* argvW = CommandLineToArgvW(commandLine, &argc);
|
|
||||||
|
|
||||||
// Convert to UTF-8
|
|
||||||
utf8** argvUtf8 = (utf8**)malloc(argc * sizeof(utf8*));
|
|
||||||
for (int i = 0; i < argc; i++)
|
|
||||||
{
|
|
||||||
argvUtf8[i] = widechar_to_utf8(argvW[i]);
|
|
||||||
}
|
|
||||||
LocalFree(argvW);
|
|
||||||
|
|
||||||
*outNumArgs = argc;
|
|
||||||
return argvUtf8;
|
|
||||||
}
|
|
||||||
|
|
||||||
BOOL APIENTRY DllMain(HANDLE hModule, DWORD dwReason, LPVOID lpReserved)
|
|
||||||
{
|
|
||||||
_dllModule = (HMODULE)hModule;
|
|
||||||
return TRUE;
|
|
||||||
}
|
|
||||||
|
|
||||||
__declspec(dllexport) int StartOpenRCT2(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
|
|
||||||
{
|
|
||||||
if (_dllModule == nullptr)
|
|
||||||
{
|
|
||||||
_dllModule = GetModuleHandleA(OPENRCT2_DLL_MODULE_NAME);
|
|
||||||
}
|
|
||||||
|
|
||||||
int argc;
|
|
||||||
char** argv = (char**)windows_get_command_line_args(&argc);
|
|
||||||
|
|
||||||
int gExitCode = main(argc, argv);
|
|
||||||
|
|
||||||
// Free argv
|
|
||||||
for (int i = 0; i < argc; i++)
|
|
||||||
{
|
|
||||||
free(argv[i]);
|
|
||||||
}
|
|
||||||
free(argv);
|
|
||||||
|
|
||||||
exit(gExitCode);
|
|
||||||
return gExitCode;
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
||||||
char* segments = (char*)(GOOD_PLACE_FOR_DATA_SEGMENT);
|
|
||||||
|
|
||||||
static uint32_t sawyercoding_calculate_checksum(const uint8_t* buffer, size_t length)
|
|
||||||
{
|
|
||||||
size_t i;
|
|
||||||
uint32_t checksum = 0;
|
|
||||||
for (i = 0; i < length; i++)
|
|
||||||
checksum += buffer[i];
|
|
||||||
|
|
||||||
return checksum;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Loads RCT2's data model and remaps the addresses.
|
|
||||||
* @returns true if the data integrity check succeeded, otherwise false.
|
|
||||||
*/
|
|
||||||
static bool openrct2_setup_rct2_segment()
|
|
||||||
{
|
|
||||||
// OpenRCT2 on Linux and macOS is wired to have the original Windows PE sections loaded
|
|
||||||
// necessary. Windows does not need to do this as OpenRCT2 runs as a DLL loaded from the Windows PE.
|
|
||||||
// in some configurations err and len may be unused
|
|
||||||
[[maybe_unused]] int len = 0x01429000 - 0x8a4000; // 0xB85000, 12079104 bytes or around 11.5MB
|
|
||||||
[[maybe_unused]] int err = 0;
|
|
||||||
#if defined(__unix__)
|
|
||||||
int pageSize = getpagesize();
|
|
||||||
int numPages = (len + pageSize - 1) / pageSize;
|
|
||||||
unsigned char* dummy = (unsigned char*)malloc(numPages);
|
|
||||||
|
|
||||||
err = mincore((void*)segments, len, dummy);
|
|
||||||
bool pagesMissing = false;
|
|
||||||
if (err != 0)
|
|
||||||
{
|
|
||||||
err = errno;
|
|
||||||
# ifdef __LINUX__
|
|
||||||
// On Linux ENOMEM means all requested range is unmapped
|
|
||||||
if (err != ENOMEM)
|
|
||||||
{
|
|
||||||
pagesMissing = true;
|
|
||||||
perror("mincore");
|
|
||||||
}
|
|
||||||
# else
|
|
||||||
pagesMissing = true;
|
|
||||||
perror("mincore");
|
|
||||||
# endif // __LINUX__
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
for (int i = 0; i < numPages; i++)
|
|
||||||
{
|
|
||||||
if (dummy[i] != 1)
|
|
||||||
{
|
|
||||||
pagesMissing = true;
|
|
||||||
void* start = (void*)(segments + i * pageSize);
|
|
||||||
void* end = (void*)(segments + (i + 1) * pageSize - 1);
|
|
||||||
log_warning("required page %p - %p is not in memory!", start, end);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
free(dummy);
|
|
||||||
if (pagesMissing)
|
|
||||||
{
|
|
||||||
log_error("At least one of required pages was not found in memory. This can cause segfaults later on.");
|
|
||||||
}
|
|
||||||
|
|
||||||
// section: text
|
|
||||||
err = mprotect((void*)0x401000, 0x8a4000 - 0x401000, PROT_READ | PROT_EXEC | PROT_WRITE);
|
|
||||||
if (err != 0)
|
|
||||||
{
|
|
||||||
perror("mprotect");
|
|
||||||
}
|
|
||||||
|
|
||||||
// section: rw data
|
|
||||||
err = mprotect((void*)segments, 0x01429000 - 0x8a4000, PROT_READ | PROT_WRITE);
|
|
||||||
if (err != 0)
|
|
||||||
{
|
|
||||||
perror("mprotect");
|
|
||||||
}
|
|
||||||
#endif // defined(__unix__)
|
|
||||||
|
|
||||||
// Check that the expected data is at various addresses.
|
|
||||||
// Start at 0x9a6000, which is start of .data, to skip the region containing addresses to DLL
|
|
||||||
// calls, which can be changed by windows/wine loader.
|
|
||||||
const uint32_t c1 = sawyercoding_calculate_checksum(
|
|
||||||
(const uint8_t*)(segments + (uintptr_t)(0x009A6000 - 0x8a4000)), 0x009E0000 - 0x009A6000);
|
|
||||||
const uint32_t c2 = sawyercoding_calculate_checksum(
|
|
||||||
(const uint8_t*)(segments + (uintptr_t)(0x01428000 - 0x8a4000)), 0x014282BC - 0x01428000);
|
|
||||||
const uint32_t exp_c1 = 10114815;
|
|
||||||
const uint32_t exp_c2 = 23564;
|
|
||||||
if (c1 != exp_c1 || c2 != exp_c2)
|
|
||||||
{
|
|
||||||
log_warning("c1 = %u, expected %u, match %d", c1, exp_c1, c1 == exp_c1);
|
|
||||||
log_warning("c2 = %u, expected %u, match %d", c2, exp_c2, c2 == exp_c2);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void PrintRideTypes()
|
|
||||||
{
|
|
||||||
for (uint8_t rideType = 0; rideType < RCT2_RIDE_TYPE_COUNT; rideType++)
|
|
||||||
{
|
|
||||||
CLIColour colour = CLIColour::DEFAULT;
|
|
||||||
bool implemented = Utils::rideIsImplemented(rideType);
|
|
||||||
const char* rideName = RideNames[rideType];
|
|
||||||
const char* status = "";
|
|
||||||
if (implemented)
|
|
||||||
{
|
|
||||||
status = " [IMPLEMENTED]";
|
|
||||||
colour = CLIColour::GREEN;
|
|
||||||
}
|
|
||||||
|
|
||||||
Write(colour, "%2d: %-30s%s\n", rideType, rideName, status);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#include "GeneralSupportHeightCall.hpp"
|
|
||||||
|
|
||||||
static void TestGeneralSupportHeightCall()
|
|
||||||
{
|
|
||||||
SupportCall callA = { 16, 0x20 };
|
|
||||||
SupportCall callB = { 32, 0x20 };
|
|
||||||
SupportCall callC = { 48, 0x20 };
|
|
||||||
SupportCall callD = { 48, 0x1F };
|
|
||||||
|
|
||||||
SupportCall out = { 0, 0 };
|
|
||||||
bool success;
|
|
||||||
|
|
||||||
SupportCall groupA[4] = { callA, callA, callA, callA };
|
|
||||||
success = GeneralSupportHeightCall::FindMostCommonSupportCall(groupA, &out);
|
|
||||||
assert(success);
|
|
||||||
assert(out == callA);
|
|
||||||
|
|
||||||
SupportCall groupB[4] = { callB, callA, callA, callA };
|
|
||||||
success = GeneralSupportHeightCall::FindMostCommonSupportCall(groupB, &out);
|
|
||||||
assert(success);
|
|
||||||
assert(out == callA);
|
|
||||||
|
|
||||||
SupportCall groupC[4] = { callB, callA, callB, callA };
|
|
||||||
success = GeneralSupportHeightCall::FindMostCommonSupportCall(groupC, &out);
|
|
||||||
assert(!success);
|
|
||||||
|
|
||||||
SupportCall groupD[4] = { callB, callC, callB, callA };
|
|
||||||
success = GeneralSupportHeightCall::FindMostCommonSupportCall(groupD, &out);
|
|
||||||
assert(!success);
|
|
||||||
|
|
||||||
SupportCall groupE[4] = { callD, callC, callB, callA };
|
|
||||||
success = GeneralSupportHeightCall::FindMostCommonSupportCall(groupE, &out);
|
|
||||||
assert(!success);
|
|
||||||
}
|
|
||||||
|
|
||||||
int main(int argc, char* argv[])
|
|
||||||
{
|
|
||||||
#if !defined(__i386__)
|
|
||||||
fprintf(stderr, "Testpaint can only be properly executed on x86\n");
|
|
||||||
return 1;
|
|
||||||
#else
|
|
||||||
TestGeneralSupportHeightCall();
|
|
||||||
|
|
||||||
std::vector<TestCase> testCases;
|
|
||||||
|
|
||||||
bool generate = false;
|
|
||||||
uint8_t specificRideType = 0xFF;
|
|
||||||
for (int i = 0; i < argc; ++i)
|
|
||||||
{
|
|
||||||
char* arg = argv[i];
|
|
||||||
if (strcmp(arg, "--gtest_color=no") == 0)
|
|
||||||
{
|
|
||||||
gTestColor = false;
|
|
||||||
}
|
|
||||||
else if (strcmp(arg, "--quiet") == 0)
|
|
||||||
{
|
|
||||||
_verbosity = Verbosity::QUIET;
|
|
||||||
}
|
|
||||||
else if (strcmp(arg, "--ride-type") == 0)
|
|
||||||
{
|
|
||||||
if (i + 1 < argc)
|
|
||||||
{
|
|
||||||
i++;
|
|
||||||
specificRideType = atoi(argv[i]);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
PrintRideTypes();
|
|
||||||
return 2;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (strcmp(arg, "--generate") == 0)
|
|
||||||
{
|
|
||||||
generate = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (generate)
|
|
||||||
{
|
|
||||||
if (specificRideType > 90)
|
|
||||||
{
|
|
||||||
fprintf(stderr, "No ride or invalid ride specified.\n");
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
openrct2_setup_rct2_segment();
|
|
||||||
PaintIntercept::InitHooks();
|
|
||||||
|
|
||||||
return generatePaintCode(specificRideType);
|
|
||||||
}
|
|
||||||
|
|
||||||
for (uint8_t rideType = 0; rideType < RCT2_RIDE_TYPE_COUNT; rideType++)
|
|
||||||
{
|
|
||||||
if (specificRideType != RIDE_TYPE_NULL && rideType != specificRideType)
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!Utils::rideIsImplemented(rideType))
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
TestCase testCase = {};
|
|
||||||
testCase.rideType = rideType;
|
|
||||||
|
|
||||||
if (GetRideTypeDescriptor(rideType).HasFlag(RIDE_TYPE_FLAG_FLAT_RIDE))
|
|
||||||
{
|
|
||||||
testCase.trackTypes.push_back(GetRideTypeDescriptor(rideType).StartTrackPiece);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
for (int trackType = 0; trackType < 256; trackType++)
|
|
||||||
{
|
|
||||||
if (Utils::rideSupportsTrackType(rideType, trackType))
|
|
||||||
{
|
|
||||||
testCase.trackTypes.push_back(trackType);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
testCases.push_back(testCase);
|
|
||||||
}
|
|
||||||
|
|
||||||
int testCaseCount = (int)testCases.size();
|
|
||||||
int testCount = 0;
|
|
||||||
for (auto&& tc : testCases)
|
|
||||||
{
|
|
||||||
testCount += tc.trackTypes.size();
|
|
||||||
}
|
|
||||||
|
|
||||||
Write(CLIColour::GREEN, "[==========] ");
|
|
||||||
Write("Running %d tests from %d test cases.\n", testCount, testCaseCount);
|
|
||||||
|
|
||||||
Write(CLIColour::GREEN, "[----------] ");
|
|
||||||
Write("Global test environment set-up.\n");
|
|
||||||
openrct2_setup_rct2_segment();
|
|
||||||
PaintIntercept::InitHooks();
|
|
||||||
|
|
||||||
int successCount = 0;
|
|
||||||
std::vector<utf8string> failures;
|
|
||||||
for (auto&& tc : testCases)
|
|
||||||
{
|
|
||||||
const utf8string rideTypeName = RideNames[tc.rideType];
|
|
||||||
Write(CLIColour::GREEN, "[----------] ");
|
|
||||||
Write("%d tests from %s\n", (int)tc.trackTypes.size(), rideTypeName);
|
|
||||||
|
|
||||||
for (auto&& trackType : tc.trackTypes)
|
|
||||||
{
|
|
||||||
utf8string trackTypeName;
|
|
||||||
if (GetRideTypeDescriptor(tc.rideType).HasFlag(RIDE_TYPE_FLAG_FLAT_RIDE))
|
|
||||||
{
|
|
||||||
trackTypeName = FlatTrackNames[trackType];
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
trackTypeName = TrackNames[trackType];
|
|
||||||
}
|
|
||||||
|
|
||||||
Write(CLIColour::GREEN, "[ RUN ] ");
|
|
||||||
Write("%s.%s\n", rideTypeName, trackTypeName);
|
|
||||||
|
|
||||||
std::string out;
|
|
||||||
int retVal = TestTrack::TestPaintTrackElement(tc.rideType, trackType, &out);
|
|
||||||
Write("%s", out.c_str());
|
|
||||||
switch (retVal)
|
|
||||||
{
|
|
||||||
case TEST_SUCCESS:
|
|
||||||
Write(CLIColour::GREEN, "[ OK ] ");
|
|
||||||
Write("%s.%s (0 ms)\n", rideTypeName, trackTypeName);
|
|
||||||
successCount++;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case TEST_SKIPPED:
|
|
||||||
Write("Skipped\n");
|
|
||||||
// Outputting this as OK because CLion only allows FAILED or OK
|
|
||||||
Write(CLIColour::YELLOW, "[ OK ] ");
|
|
||||||
Write("%s.%s (0 ms)\n", rideTypeName, trackTypeName);
|
|
||||||
successCount++;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case TEST_FAILED:
|
|
||||||
utf8string testCaseName = new utf8[64];
|
|
||||||
snprintf(testCaseName, 64, "%s.%s", rideTypeName, trackTypeName);
|
|
||||||
|
|
||||||
Write(CLIColour::RED, "[ FAILED ] ");
|
|
||||||
Write("%s (0 ms)\n", testCaseName);
|
|
||||||
failures.push_back(testCaseName);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Write(CLIColour::GREEN, "[----------] ");
|
|
||||||
Write("%d tests from %s (0 ms total)\n", (int)tc.trackTypes.size(), rideTypeName);
|
|
||||||
}
|
|
||||||
Write("\n");
|
|
||||||
|
|
||||||
Write(CLIColour::GREEN, "[----------] ");
|
|
||||||
Write("Global test environment tear-down\n");
|
|
||||||
|
|
||||||
Write(CLIColour::GREEN, "[==========] ");
|
|
||||||
Write("%d tests from %d test cases ran. (0 ms total).\n", testCount, testCaseCount);
|
|
||||||
|
|
||||||
Write(Verbosity::QUIET, CLIColour::GREEN, "[ PASSED ] ");
|
|
||||||
Write(Verbosity::QUIET, "%d tests.\n", successCount);
|
|
||||||
|
|
||||||
if (!failures.empty())
|
|
||||||
{
|
|
||||||
Write(Verbosity::QUIET, CLIColour::RED, "[ FAILED ] ");
|
|
||||||
Write(Verbosity::QUIET, "%d tests, listed below:\n", (int)failures.size());
|
|
||||||
|
|
||||||
for (auto&& failure : failures)
|
|
||||||
{
|
|
||||||
Write(Verbosity::QUIET, CLIColour::RED, "[ FAILED ] ");
|
|
||||||
Write(Verbosity::QUIET, "%s\n", failure);
|
|
||||||
delete[] failure;
|
|
||||||
}
|
|
||||||
|
|
||||||
Write(Verbosity::QUIET, "\n");
|
|
||||||
|
|
||||||
Write(Verbosity::QUIET, "%d FAILED TESTS\n", (int)failures.size());
|
|
||||||
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
#endif
|
|
||||||
}
|
|
Loading…
Reference in New Issue