/*****************************************************************************
 * Copyright (c) 2014-2020 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 "../common.h"
#include "../core/FileSystem.hpp"
#include "../object/Object.h"
#include "../rct12/RCT12.h"
#include "../ride/RideRatings.h"
#include "../ride/VehicleColour.h"
#include "../world/EntityList.h"

#include <tuple>
#include <vector>

constexpr const uint8_t RCT2_MAX_STAFF = 200;
constexpr const uint8_t RCT2_MAX_BANNERS_IN_PARK = 250;
constexpr const uint8_t RCT2_MAX_VEHICLES_PER_RIDE = 31;
constexpr const uint8_t RCT2_MAX_CARS_PER_TRAIN = 32;
constexpr const uint8_t RCT2_MAX_CATEGORIES_PER_RIDE = 2;
constexpr const uint8_t RCT2_MAX_RIDE_TYPES_PER_RIDE_ENTRY = 3;
constexpr const uint8_t RCT2_MAX_VEHICLES_PER_RIDE_ENTRY = 4;
constexpr const uint8_t RCT2_DOWNTIME_HISTORY_SIZE = 8;
constexpr const uint8_t RCT2_CUSTOMER_HISTORY_SIZE = 10;
constexpr const uint16_t RCT2_MAX_SPRITES = 10000;
constexpr const uint32_t RCT2_MAX_TILE_ELEMENTS = 0x30000;
constexpr const uint16_t RCT2_MAX_ANIMATED_OBJECTS = 2000;
constexpr const uint8_t RCT2_MAX_RESEARCHED_RIDE_TYPE_QUADS = 8;  // With 32 bits per uint32_t, this means there is room for 256
                                                                  // types.
constexpr const uint8_t RCT2_MAX_RESEARCHED_RIDE_ENTRY_QUADS = 8; // With 32 bits per uint32_t, this means there is room for 256
                                                                  // entries.
constexpr const uint8_t RCT2_MAX_RESEARCHED_SCENERY_ITEM_QUADS = 56;
constexpr const uint16_t RCT2_MAX_RESEARCHED_SCENERY_ITEMS = (RCT2_MAX_RESEARCHED_SCENERY_ITEM_QUADS * 32); // There are 32 bits
                                                                                                            // per quad.
constexpr const uint16_t RCT2_MAX_RESEARCH_ITEMS = 500;

constexpr uint16_t TD6MaxTrackElements = 8192;

constexpr const uint8_t RCT2_MAX_SMALL_SCENERY_OBJECTS = 252;
constexpr const uint8_t RCT2_MAX_LARGE_SCENERY_OBJECTS = 128;
constexpr const uint8_t RCT2_MAX_WALL_SCENERY_OBJECTS = 128;
constexpr const uint8_t RCT2_MAX_BANNER_OBJECTS = 32;
constexpr const uint8_t RCT2_MAX_PATH_OBJECTS = 16;
constexpr const uint8_t RCT2_MAX_PATH_ADDITION_OBJECTS = 15;
constexpr const uint8_t RCT2_MAX_SCENERY_GROUP_OBJECTS = 19;
constexpr const uint8_t RCT2_MAX_PARK_ENTRANCE_OBJECTS = 1;
constexpr const uint8_t RCT2_MAX_WATER_OBJECTS = 1;
constexpr const uint8_t RCT2_MAX_SCENARIO_TEXT_OBJECTS = 1;
constexpr const uint8_t RCT2_RIDE_TYPE_COUNT = 91;

constexpr const rct_string_id RCT2_RIDE_STRING_START = 2;

constexpr const uint16_t RCT2_MAXIMUM_MAP_SIZE_TECHNICAL = 256;

// clang-format off
constexpr const uint16_t RCT2_OBJECT_ENTRY_COUNT =
    RCT12_MAX_RIDE_OBJECTS +
    RCT2_MAX_SMALL_SCENERY_OBJECTS +
    RCT2_MAX_LARGE_SCENERY_OBJECTS +
    RCT2_MAX_WALL_SCENERY_OBJECTS +
    RCT2_MAX_BANNER_OBJECTS +
    RCT2_MAX_PATH_OBJECTS +
    RCT2_MAX_PATH_ADDITION_OBJECTS +
    RCT2_MAX_SCENERY_GROUP_OBJECTS +
    RCT2_MAX_PARK_ENTRANCE_OBJECTS +
    RCT2_MAX_WATER_OBJECTS +
    RCT2_MAX_SCENARIO_TEXT_OBJECTS;
// clang-format on
static_assert(RCT2_OBJECT_ENTRY_COUNT == 721);

// clang-format off
constexpr const int32_t rct2_object_entry_group_counts[] = {
    RCT12_MAX_RIDE_OBJECTS,
    RCT2_MAX_SMALL_SCENERY_OBJECTS,
    RCT2_MAX_LARGE_SCENERY_OBJECTS,
    RCT2_MAX_WALL_SCENERY_OBJECTS,
    RCT2_MAX_BANNER_OBJECTS,
    RCT2_MAX_PATH_OBJECTS,
    RCT2_MAX_PATH_ADDITION_OBJECTS,
    RCT2_MAX_SCENERY_GROUP_OBJECTS,
    RCT2_MAX_PARK_ENTRANCE_OBJECTS,
    RCT2_MAX_WATER_OBJECTS,
    RCT2_MAX_SCENARIO_TEXT_OBJECTS,
};
// clang-format on

#pragma pack(push, 1)

struct rct_ride_entry;

/**
 * Ride structure.
 * size: 0x0260
 */
struct rct2_ride
{
    uint8_t type; // 0x000
    // pointer to static info. for example, wild mouse type is 0x36, subtype is
    // 0x4c.
    RCT12ObjectEntryIndex subtype;                               // 0x001
    uint16_t pad_002;                                            // 0x002
    uint8_t mode;                                                // 0x004
    uint8_t colour_scheme_type;                                  // 0x005
    rct_vehicle_colour vehicle_colours[RCT2_MAX_CARS_PER_TRAIN]; // 0x006
    uint8_t pad_046[0x03];                                       // 0x046, Used to be track colours in RCT1 without expansions
    // 0 = closed, 1 = open, 2 = test
    uint8_t status;     // 0x049
    rct_string_id name; // 0x04A
    union
    {
        uint32_t name_arguments; // 0x04C
        struct
        {
            rct_string_id name_arguments_type_name; // 0x04C
            uint16_t name_arguments_number;         // 0x04E
        };
    };
    RCT12xy8 overall_view;                                // 0x050
    RCT12xy8 station_starts[RCT12_MAX_STATIONS_PER_RIDE]; // 0x052
    uint8_t station_heights[RCT12_MAX_STATIONS_PER_RIDE]; // 0x05A
    uint8_t station_length[RCT12_MAX_STATIONS_PER_RIDE];  // 0x05E
    uint8_t station_depart[RCT12_MAX_STATIONS_PER_RIDE];  // 0x062
    // ride->vehicle index for current train waiting for passengers
    // at station
    uint8_t train_at_station[RCT12_MAX_STATIONS_PER_RIDE];    // 0x066
    RCT12xy8 entrances[RCT12_MAX_STATIONS_PER_RIDE];          // 0x06A
    RCT12xy8 exits[RCT12_MAX_STATIONS_PER_RIDE];              // 0x072
    uint16_t last_peep_in_queue[RCT12_MAX_STATIONS_PER_RIDE]; // 0x07A
    uint8_t pad_082[RCT12_MAX_STATIONS_PER_RIDE]; // 0x082, Used to be number of peeps in queue in RCT1, but this has moved.
    uint16_t vehicles[RCT2_MAX_VEHICLES_PER_RIDE + 1]; // 0x086, Points to the first car in the train
    uint8_t depart_flags;                              // 0x0C6

    // Not sure if these should be uint or sint.
    uint8_t num_stations;                // 0x0C7
    uint8_t num_vehicles;                // 0x0C8
    uint8_t num_cars_per_train;          // 0x0C9
    uint8_t proposed_num_vehicles;       // 0x0CA
    uint8_t proposed_num_cars_per_train; // 0x0CB
    uint8_t max_trains;                  // 0x0CC
    uint8_t min_max_cars_per_train;      // 0x0CD
    uint8_t min_waiting_time;            // 0x0CE
    uint8_t max_waiting_time;            // 0x0CF
    union
    {
        uint8_t operation_option; // 0x0D0
        uint8_t time_limit;       // 0x0D0
        uint8_t num_laps;         // 0x0D0
        uint8_t launch_speed;     // 0x0D0
        uint8_t speed;            // 0x0D0
        uint8_t rotations;        // 0x0D0
    };

    uint8_t boat_hire_return_direction; // 0x0D1
    RCT12xy8 boat_hire_return_position; // 0x0D2
    uint8_t measurement_index;          // 0x0D4
    // bits 0 through 4 are the number of helix sections
    // bit 5: spinning tunnel, water splash, or rapids
    // bit 6: log reverser, waterfall
    // bit 7: whirlpool
    uint8_t special_track_elements; // 0x0D5
    uint8_t pad_0D6[2];             // 0x0D6
    // Divide this value by 29127 to get the human-readable max speed
    // (in RCT2, display_speed = (max_speed * 9) >> 18)
    int32_t max_speed;                           // 0x0D8
    int32_t average_speed;                       // 0x0DC
    uint8_t current_test_segment;                // 0x0E0
    uint8_t average_speed_test_timeout;          // 0x0E1
    uint8_t pad_0E2[0x2];                        // 0x0E2
    int32_t length[RCT12_MAX_STATIONS_PER_RIDE]; // 0x0E4
    uint16_t time[RCT12_MAX_STATIONS_PER_RIDE];  // 0x0F4
    fixed16_2dp max_positive_vertical_g;         // 0x0FC
    fixed16_2dp max_negative_vertical_g;         // 0x0FE
    fixed16_2dp max_lateral_g;                   // 0x100
    fixed16_2dp previous_vertical_g;             // 0x102
    fixed16_2dp previous_lateral_g;              // 0x104
    uint8_t pad_106[0x2];                        // 0x106
    uint32_t testing_flags;                      // 0x108
    // x y map location of the current track piece during a test
    // this is to prevent counting special tracks multiple times
    RCT12xy8 cur_test_track_location; // 0x10C
    // Next 3 variables are related (XXXX XYYY ZZZa aaaa)
    uint16_t turn_count_default; // 0x10E X = current turn count
    uint16_t turn_count_banked;  // 0x110
    uint16_t turn_count_sloped;  // 0x112 X = number turns > 3 elements
    union
    {
        uint8_t inversions; // 0x114 (???X XXXX)
        uint8_t holes;      // 0x114 (???X XXXX)
        // This is a very rough approximation of how much of the ride is undercover.
        // It reaches the maximum value of 7 at about 50% undercover and doesn't increase beyond that.
        uint8_t sheltered_eighths; // 0x114 (XXX?-????)
    };
    // Y is number of powered lifts, X is drops
    uint8_t drops;               // 0x115 (YYXX XXXX)
    uint8_t start_drop_height;   // 0x116
    uint8_t highest_drop_height; // 0x117
    int32_t sheltered_length;    // 0x118
    // Unused always 0? Should affect nausea
    uint16_t var_11C;               // 0x11C
    uint8_t num_sheltered_sections; // 0x11E (?abY YYYY)
    // see cur_test_track_location
    uint8_t cur_test_track_z; // 0x11F
    // Customer counter in the current 960 game tick (about 30 seconds) interval
    uint16_t cur_num_customers; // 0x120
    // Counts ticks to update customer intervals, resets each 960 game ticks.
    uint16_t num_customers_timeout; // 0x122
    // Customer count in the last 10 * 960 game ticks (sliding window)
    uint16_t num_customers[RCT2_CUSTOMER_HISTORY_SIZE]; // 0x124
    money16 price;                                      // 0x138
    RCT12xy8 chairlift_bullwheel_location[2];           // 0x13A
    uint8_t chairlift_bullwheel_z[2];                   // 0x13E
    union
    {
        RatingTuple ratings; // 0x140
        struct
        {
            ride_rating excitement; // 0x140
            ride_rating intensity;  // 0x142
            ride_rating nausea;     // 0x144
        };
    };
    uint16_t value;                        // 0x146
    uint16_t chairlift_bullwheel_rotation; // 0x148
    uint8_t satisfaction;                  // 0x14A
    uint8_t satisfaction_time_out;         // 0x14B
    uint8_t satisfaction_next;             // 0x14C
    // Various flags stating whether a window needs to be refreshed
    uint8_t window_invalidate_flags; // 0x14D
    uint8_t pad_14E[0x02];           // 0x14E
    uint32_t total_customers;        // 0x150
    money32 total_profit;            // 0x154
    uint8_t popularity;              // 0x158
    uint8_t popularity_time_out;     // 0x159 Updated every purchase and ?possibly by time?
    uint8_t popularity_next;         // 0x15A When timeout reached this will be the next popularity
    uint8_t num_riders;              // 0x15B
    uint8_t music_tune_id;           // 0x15C
    uint8_t slide_in_use;            // 0x15D
    union
    {
        uint16_t slide_peep; // 0x15E
        uint16_t maze_tiles; // 0x15E
    };
    uint8_t pad_160[0xE];              // 0x160
    uint8_t slide_peep_t_shirt_colour; // 0x16E
    uint8_t pad_16F[0x7];              // 0x16F
    uint8_t spiral_slide_progress;     // 0x176
    uint8_t pad_177[0x9];              // 0x177
    int16_t build_date;                // 0x180
    money16 upkeep_cost;               // 0x182
    uint16_t race_winner;              // 0x184
    uint8_t pad_186[0x02];             // 0x186
    uint32_t music_position;           // 0x188
    uint8_t breakdown_reason_pending;  // 0x18C
    uint8_t mechanic_status;           // 0x18D
    uint16_t mechanic;                 // 0x18E
    uint8_t inspection_station;        // 0x190
    uint8_t broken_vehicle;            // 0x191
    uint8_t broken_car;                // 0x192
    uint8_t breakdown_reason;          // 0x193
    money16 price_secondary;           // 0x194
    union
    {
        struct
        {
            uint8_t reliability_subvalue;   // 0x196, 0 - 255, acts like the decimals for reliability_percentage
            uint8_t reliability_percentage; // 0x197, Starts at 100 and decreases from there.
        };
        uint16_t reliability; // 0x196
    };
    // Small constant used to increase the unreliability as the game continues,
    // making breakdowns more and more likely.
    uint8_t unreliability_factor; // 0x198
    // Range from [0, 100]
    uint8_t downtime;                                     // 0x199
    uint8_t inspection_interval;                          // 0x19A
    uint8_t last_inspection;                              // 0x19B
    uint8_t downtime_history[RCT2_DOWNTIME_HISTORY_SIZE]; // 0x19C
    uint32_t no_primary_items_sold;                       // 0x1A4
    uint32_t no_secondary_items_sold;                     // 0x1A8
    uint8_t breakdown_sound_modifier;                     // 0x1AC
    // Used to oscillate the sound when ride breaks down.
    // 0 = no change, 255 = max change
    uint8_t not_fixed_timeout;                                 // 0x1AD
    uint8_t last_crash_type;                                   // 0x1AE
    uint8_t connected_message_throttle;                        // 0x1AF
    money32 income_per_hour;                                   // 0x1B0
    money32 profit;                                            // 0x1B4
    uint8_t queue_time[RCT12_MAX_STATIONS_PER_RIDE];           // 0x1B8
    uint8_t track_colour_main[RCT12_NUM_COLOUR_SCHEMES];       // 0x1BC
    uint8_t track_colour_additional[RCT12_NUM_COLOUR_SCHEMES]; // 0x1C0
    uint8_t track_colour_supports[RCT12_NUM_COLOUR_SCHEMES];   // 0x1C4
    uint8_t music;                                             // 0x1C8
    uint8_t entrance_style;                                    // 0x1C9
    uint16_t vehicle_change_timeout;                           // 0x1CA
    uint8_t num_block_brakes;                                  // 0x1CC
    uint8_t lift_hill_speed;                                   // 0x1CD
    uint16_t guests_favourite;                                 // 0x1CE
    uint32_t lifecycle_flags;                                  // 0x1D0
    uint8_t vehicle_colours_extended[RCT2_MAX_CARS_PER_TRAIN]; // 0x1D4
    uint16_t total_air_time;                                   // 0x1F4
    uint8_t current_test_station;                              // 0x1F6
    uint8_t num_circuits;                                      // 0x1F7
    int16_t cable_lift_x;                                      // 0x1F8
    int16_t cable_lift_y;                                      // 0x1FA
    uint8_t cable_lift_z;                                      // 0x1FC
    uint8_t pad_1FD;                                           // 0x1FD
    uint16_t cable_lift;                                       // 0x1FE
    uint16_t queue_length[RCT12_MAX_STATIONS_PER_RIDE];        // 0x200
    uint8_t pad_208[0x58];                                     // 0x208

    uint8_t GetMinCarsPerTrain() const;
    uint8_t GetMaxCarsPerTrain() const;
    void SetMinCarsPerTrain(uint8_t newValue);
    void SetMaxCarsPerTrain(uint8_t newValue);
};
assert_struct_size(rct2_ride, 0x260);

/* Track Entrance entry size: 0x06 */
struct rct_td6_entrance_element
{
    int8_t z;          // 0x00
    uint8_t direction; // 0x01
    int16_t x;         // 0x02
    int16_t y;         // 0x04
};
assert_struct_size(rct_td6_entrance_element, 0x06);

/* Track Scenery entry  size: 0x16 */
struct rct_td6_scenery_element
{
    rct_object_entry scenery_object; // 0x00
    int8_t x;                        // 0x10
    int8_t y;                        // 0x11
    int8_t z;                        // 0x12
    uint8_t flags;                   // 0x13 direction quadrant tertiary colour
    uint8_t primary_colour;          // 0x14
    uint8_t secondary_colour;        // 0x15
};
assert_struct_size(rct_td6_scenery_element, 0x16);

/**
 * Track design structure.
 * size: 0xA3
 */
struct rct_track_td6
{
    uint8_t type; // 0x00
    RCT12ObjectEntryIndex vehicle_type;
    union
    {
        // After loading the track this is converted to
        // a cost but before its a flags register
        money32 cost;   // 0x02
        uint32_t flags; // 0x02
    };
    union
    {
        // After loading the track this is converted to
        // a flags register
        uint8_t ride_mode;   // 0x06
        uint8_t track_flags; // 0x06
    };
    uint8_t version_and_colour_scheme;                           // 0x07 0b0000_VVCC
    rct_vehicle_colour vehicle_colours[RCT2_MAX_CARS_PER_TRAIN]; // 0x08
    union
    {
        uint8_t pad_48;
        uint8_t track_spine_colour_rct1; // 0x48
    };
    union
    {
        uint8_t entrance_style;         // 0x49
        uint8_t track_rail_colour_rct1; // 0x49
    };
    union
    {
        uint8_t total_air_time;            // 0x4A
        uint8_t track_support_colour_rct1; // 0x4A
    };
    uint8_t depart_flags;             // 0x4B
    uint8_t number_of_trains;         // 0x4C
    uint8_t number_of_cars_per_train; // 0x4D
    uint8_t min_waiting_time;         // 0x4E
    uint8_t max_waiting_time;         // 0x4F
    uint8_t operation_setting;
    int8_t max_speed;                // 0x51
    int8_t average_speed;            // 0x52
    uint16_t ride_length;            // 0x53
    uint8_t max_positive_vertical_g; // 0x55
    int8_t max_negative_vertical_g;  // 0x56
    uint8_t max_lateral_g;           // 0x57
    union
    {
        uint8_t inversions; // 0x58
        uint8_t holes;      // 0x58
    };
    uint8_t drops;                                              // 0x59
    uint8_t highest_drop_height;                                // 0x5A
    uint8_t excitement;                                         // 0x5B
    uint8_t intensity;                                          // 0x5C
    uint8_t nausea;                                             // 0x5D
    money16 upkeep_cost;                                        // 0x5E
    uint8_t track_spine_colour[RCT12_NUM_COLOUR_SCHEMES];       // 0x60
    uint8_t track_rail_colour[RCT12_NUM_COLOUR_SCHEMES];        // 0x64
    uint8_t track_support_colour[RCT12_NUM_COLOUR_SCHEMES];     // 0x68
    uint32_t flags2;                                            // 0x6C
    rct_object_entry vehicle_object;                            // 0x70
    uint8_t space_required_x;                                   // 0x80
    uint8_t space_required_y;                                   // 0x81
    uint8_t vehicle_additional_colour[RCT2_MAX_CARS_PER_TRAIN]; // 0x82
    uint8_t lift_hill_speed_num_circuits;                       // 0xA2 0bCCCL_LLLL
    // 0xA3 (data starts here in file)
};
assert_struct_size(rct_track_td6, 0xA3);

/**
 * scores.dat file header.
 * size: 0x10
 */
struct rct_scores_header
{
    uint32_t var_0;
    uint32_t var_4;
    uint32_t var_8;
    uint32_t ScenarioCount;
};
assert_struct_size(rct_scores_header, 0x10);

/**
 * An entry of scores.dat
 * size: 0x02B0
 */
struct rct_scores_entry
{
    char Path[256];
    uint8_t Category;
    uint8_t pad_0101[0x1F];
    int8_t ObjectiveType;
    int8_t ObjectiveArg1;
    int32_t objectiveArg2;
    int16_t objectiveArg3;
    char Name[64];
    char Details[256];
    int32_t Flags;
    money32 CompanyValue;
    char CompletedBy[64];
};
assert_struct_size(rct_scores_entry, 0x02B0);

struct RCT2SpriteVehicle : RCT12SpriteBase
{
    uint8_t Pitch;         // 0x1F
    uint8_t bank_rotation; // 0x20
    uint8_t pad_21[3];
    int32_t remaining_distance; // 0x24
    int32_t velocity;           // 0x28
    int32_t acceleration;       // 0x2C
    uint8_t ride;               // 0x30
    uint8_t vehicle_type;       // 0x31
    rct_vehicle_colour colours; // 0x32
    union
    {
        uint16_t track_progress; // 0x34
        struct
        {
            int8_t var_34;
            uint8_t var_35;
        };
    };
    union
    {
        int16_t TrackTypeAndDirection; // 0x36
        RCT12xy8 boat_location;        // 0x36
    };
    uint16_t track_x;               // 0x38
    uint16_t track_y;               // 0x3A
    uint16_t track_z;               // 0x3C
    uint16_t next_vehicle_on_train; // 0x3E
    uint16_t prev_vehicle_on_ride;  // 0x40
    uint16_t next_vehicle_on_ride;  // 0x42
    uint16_t var_44;
    uint16_t mass;         // 0x46
    uint16_t update_flags; // 0x48
    uint8_t SwingSprite;
    uint8_t current_station; // 0x4B
    union
    {
        int16_t SwingPosition; // 0x4C
        int16_t current_time;  // 0x4C
        struct
        {
            int8_t ferris_wheel_var_0; // 0x4C
            int8_t ferris_wheel_var_1; // 0x4D
        };
    };
    union
    {
        int16_t SwingSpeed;
        int16_t crash_z; // 0x4E
    };
    uint8_t status;                  // 0x50
    uint8_t sub_state;               // 0x51
    uint16_t peep[32];               // 0x52
    uint8_t peep_tshirt_colours[32]; // 0x92
    uint8_t num_seats;               // 0xB2
    uint8_t num_peeps;               // 0xB3
    uint8_t next_free_seat;          // 0xB4
    uint8_t restraints_position;     // 0xB5
    union
    {
        int16_t spin_speed; // 0xB6
        int16_t crash_x;    // 0xB6
    };
    uint16_t sound2_flags; // 0xB8
    uint8_t spin_sprite;   // 0xBA
    uint8_t sound1_id;     // 0xBB
    uint8_t sound1_volume; // 0xBC
    uint8_t sound2_id;     // 0xBD
    uint8_t sound2_volume; // 0xBE
    int8_t sound_vector_factor;
    union
    {
        uint16_t var_C0;
        int16_t crash_y;            // 0xC0
        uint16_t time_waiting;      // 0xC0
        uint16_t cable_lift_target; // 0xC0
    };
    uint8_t speed;                // 0xC2
    uint8_t powered_acceleration; // 0xC3
    union
    {
        uint8_t dodgems_collision_direction; // 0xC4
        uint8_t var_C4;
    };
    uint8_t animation_frame; // 0xC5
    uint8_t pad_C6[0x2];
    uint32_t animationState;
    uint8_t scream_sound_id; // 0xCC
    uint8_t TrackSubposition;
    union
    {
        uint8_t var_CE;
        uint8_t num_laps; // 0xCE
    };
    union
    {
        uint8_t var_CF;
        uint8_t brake_speed; // 0xCF
    };
    uint16_t lost_time_out;         // 0xD0
    int8_t vertical_drop_countdown; // 0xD1
    uint8_t var_D3;
    uint8_t mini_golf_current_animation;
    uint8_t mini_golf_flags;      // 0xD5
    uint8_t ride_subtype;         // 0xD6
    uint8_t colours_extended;     // 0xD7
    uint8_t seat_rotation;        // 0xD8
    uint8_t target_seat_rotation; // 0xD9

    uint16_t GetTrackType() const
    {
        return TrackTypeAndDirection >> 2;
    }
    uint8_t GetTrackDirection() const
    {
        return TrackTypeAndDirection & RCT12VehicleTrackDirectionMask;
    }
    void SetTrackType(uint16_t trackType)
    {
        // set the upper 14 bits to 0
        TrackTypeAndDirection &= ~RCT12VehicleTrackTypeMask;
        TrackTypeAndDirection |= trackType << 2;
    }
    void SetTrackDirection(uint8_t trackDirection)
    {
        // set the lower 2 bits only
        TrackTypeAndDirection &= ~RCT12VehicleTrackDirectionMask;
        TrackTypeAndDirection |= trackDirection & RCT12VehicleTrackDirectionMask;
    }
};
assert_struct_size(RCT2SpriteVehicle, 0xDA);

struct RCT2SpritePeep : RCT12SpriteBase
{
    uint8_t pad_1F[0x22 - 0x1F];
    rct_string_id name_string_idx; // 0x22
    uint16_t next_x;               // 0x24
    uint16_t next_y;               // 0x26
    uint8_t next_z;                // 0x28
    uint8_t next_flags;            // 0x29
    uint8_t outside_of_park;       // 0x2A
    uint8_t state;                 // 0x2B
    uint8_t sub_state;             // 0x2C
    uint8_t sprite_type;           // 0x2D
    uint8_t peep_type;             // 0x2E
    union
    {
        uint8_t staff_type;  // 0x2F
        uint8_t no_of_rides; // 0x2F
    };
    uint8_t tshirt_colour;         // 0x30
    uint8_t trousers_colour;       // 0x31
    uint16_t destination_x;        // 0x32
    uint16_t destination_y;        // 0x34
    uint8_t destination_tolerance; // 0x36
    uint8_t var_37;
    uint8_t energy;                  // 0x38
    uint8_t energy_target;           // 0x39
    uint8_t happiness;               // 0x3A
    uint8_t happiness_target;        // 0x3B
    uint8_t nausea;                  // 0x3C
    uint8_t nausea_target;           // 0x3D
    uint8_t hunger;                  // 0x3E
    uint8_t thirst;                  // 0x3F
    uint8_t toilet;                  // 0x40
    uint8_t mass;                    // 0x41
    uint8_t time_to_consume;         // 0x42
    uint8_t intensity;               // 0x43
    uint8_t nausea_tolerance;        // 0x44
    uint8_t window_invalidate_flags; // 0x45
    money16 paid_on_drink;           // 0x46
    uint8_t ride_types_been_on[16];  // 0x48
    uint32_t item_extra_flags;       // 0x58
    RCT12RideId photo2_ride_ref;     // 0x5C
    RCT12RideId photo3_ride_ref;     // 0x5D
    RCT12RideId photo4_ride_ref;     // 0x5E
    uint8_t pad_5F[0x09];            // 0x5F
    RCT12RideId current_ride;        // 0x68
    uint8_t current_ride_station;    // 0x69
    uint8_t current_train;           // 0x6A
    union
    {
        struct
        {
            uint8_t current_car;  // 0x6B
            uint8_t current_seat; // 0x6C
        };
        uint16_t time_to_sitdown; // 0x6B
        struct
        {
            uint8_t time_to_stand;  // 0x6B
            uint8_t standing_flags; // 0x6C
        };
    };
    uint8_t special_sprite;             // 0x6D
    uint8_t action_sprite_type;         // 0x6E
    uint8_t next_action_sprite_type;    // 0x6F
    uint8_t action_sprite_image_offset; // 0x70
    uint8_t action;                     // 0x71
    uint8_t action_frame;               // 0x72
    uint8_t step_progress;              // 0x73
    union
    {
        uint16_t mechanic_time_since_call;
        uint16_t next_in_queue; // 0x74
    };
    uint8_t pad_76;
    uint8_t pad_77;
    union
    {
        uint8_t maze_last_edge; // 0x78
        uint8_t direction;
    };
    RCT12RideId interaction_ride_index;
    uint16_t time_in_queue;                             // 0x7A
    uint8_t rides_been_on[32];                          // 0x7C
    uint32_t id;                                        // 0x9C
    money32 cash_in_pocket;                             // 0xA0
    money32 cash_spent;                                 // 0xA4
    int32_t park_entry_time;                            // 0xA8
    int8_t rejoin_queue_timeout;                        // 0xAC
    RCT12RideId previous_ride;                          // 0xAD
    uint16_t previous_ride_time_out;                    // 0xAE
    RCT12PeepThought thoughts[RCT12_PEEP_MAX_THOUGHTS]; // 0xB0
    uint8_t path_check_optimisation;                    // 0xC4
    union
    {
        uint8_t staff_id;                     // 0xC5
        RCT12RideId guest_heading_to_ride_id; // 0xC5
    };
    union
    {
        uint8_t staff_orders;           // 0xC6
        uint8_t peep_is_lost_countdown; // 0xC6
    };
    RCT12RideId photo1_ride_ref;     // 0xC7
    uint32_t peep_flags;             // 0xC8
    rct12_xyzd8 pathfind_goal;       // 0xCC
    rct12_xyzd8 pathfind_history[4]; // 0xD0
    uint8_t no_action_frame_num;     // 0xE0
    uint8_t litter_count;            // 0xE1
    union
    {
        uint8_t time_on_ride;         // 0xE2
        uint8_t staff_mowing_timeout; // 0xE2
    };
    uint8_t disgusting_count; // 0xE3
    union
    {
        money16 paid_to_enter;      // 0xE4
        uint16_t staff_lawns_mown;  // 0xE4
        uint16_t staff_rides_fixed; // 0xE4
    };
    union
    {
        money16 paid_on_rides;          // 0xE6
        uint16_t staff_gardens_watered; // 0xE6
        uint16_t staff_rides_inspected; // 0xE6
    };
    union
    {
        money16 paid_on_food;        // 0xE8
        uint16_t staff_litter_swept; // 0xE8
    };
    union
    {
        money16 paid_on_souvenirs;   // 0xEA
        uint16_t staff_bins_emptied; // 0xEA
    };
    uint8_t no_of_food;                   // 0xEC
    uint8_t no_of_drinks;                 // 0xED
    uint8_t no_of_souvenirs;              // 0xEE
    uint8_t vandalism_seen;               // 0xEF 0xC0 vandalism thought timeout, 0x3F vandalism tiles seen
    uint8_t voucher_type;                 // 0xF0
    RCT12RideId voucher_arguments;        // 0xF1 ride_id or string_offset_id
    uint8_t surroundings_thought_timeout; // 0xF2
    uint8_t angriness;                    // 0xF3
    uint8_t time_lost;                    // 0xF4 the time the peep has been lost when it reaches 254 generates the lost thought
    uint8_t days_in_queue;                // 0xF5
    uint8_t balloon_colour;               // 0xF6
    uint8_t umbrella_colour;              // 0xF7
    uint8_t hat_colour;                   // 0xF8
    RCT12RideId favourite_ride;           // 0xF9
    uint8_t favourite_ride_rating;        // 0xFA
    uint8_t pad_FB;
    uint32_t item_standard_flags; // 0xFC
    uint64_t GetItemFlags() const
    {
        return item_standard_flags | (static_cast<uint64_t>(item_extra_flags) << 32);
    }
};
assert_struct_size(RCT2SpritePeep, 0x100);

enum class RCT2StaffMode : uint8_t
{
    None,
    Walk,
    Patrol = 3
};

union RCT2Sprite
{
private:
    uint8_t pad_00[0x100];

public:
    RCT12SpriteBase unknown;
    RCT2SpriteVehicle vehicle;
    RCT2SpritePeep peep;
    RCT12SpriteLitter litter;
    RCT12SpriteBalloon balloon;
    RCT12SpriteDuck duck;
    RCT12SpriteJumpingFountain jumping_fountain;
    RCT12SpriteMoneyEffect money_effect;
    RCT12SpriteCrashedVehicleParticle crashed_vehicle_particle;
    RCT12SpriteCrashSplash crash_splash;
    RCT12SpriteSteamParticle steam_particle;
    RCT12SpriteParticle misc_particle;
};
assert_struct_size(RCT2Sprite, 0x100);

struct RCT2RideRatingCalculationData
{
    uint16_t proximity_x;
    uint16_t proximity_y;
    uint16_t proximity_z;
    uint16_t proximity_start_x;
    uint16_t proximity_start_y;
    uint16_t proximity_start_z;
    uint8_t current_ride;
    uint8_t state;
    uint8_t proximity_track_type;
    uint8_t proximity_base_height;
    uint16_t proximity_total;
    uint16_t proximity_scores[26];
    uint16_t num_brakes;
    uint16_t num_reversers;
    uint16_t station_flags;
};
assert_struct_size(RCT2RideRatingCalculationData, 76);

/**
 * SV6/SC6 header chunk
 * size: 0x20
 */
struct rct_s6_header
{
    uint8_t type;                // 0x00
    uint8_t classic_flag;        // 0x01
    uint16_t num_packed_objects; // 0x02
    uint32_t version;            // 0x04
    uint32_t magic_number;       // 0x08
    uint8_t pad_0C[0x14];
};
assert_struct_size(rct_s6_header, 0x20);

enum class EditorStep : uint8_t;

/**
 * SC6 information chunk
 * size: 0x198
 */
struct rct_s6_info
{
    EditorStep editor_step;
    uint8_t category;        // 0x01
    uint8_t objective_type;  // 0x02
    uint8_t objective_arg_1; // 0x03
    int32_t objective_arg_2; // 0x04
    int16_t objective_arg_3; // 0x08
    uint8_t pad_00A[0x3E];
    char name[64];          // 0x48
    char details[256];      // 0x88
    rct_object_entry entry; // 0x188
};
assert_struct_size(rct_s6_info, 0x198);

struct rct_s6_data
{
    // SC6[0]
    rct_s6_header header;

    // SC6[1]
    rct_s6_info info;

    // SC6[2]
    // packed objects

    // SC6[3]
    union
    {
        rct_object_entry Objects[RCT2_OBJECT_ENTRY_COUNT];
        struct
        {
            rct_object_entry RideObjects[RCT12_MAX_RIDE_OBJECTS];
            rct_object_entry SceneryObjects[RCT2_MAX_SMALL_SCENERY_OBJECTS];
            rct_object_entry LargeSceneryObjects[RCT2_MAX_LARGE_SCENERY_OBJECTS];
            rct_object_entry WallSceneryObjects[RCT2_MAX_WALL_SCENERY_OBJECTS];
            rct_object_entry BannerObjects[RCT2_MAX_BANNER_OBJECTS];
            rct_object_entry PathObjects[RCT2_MAX_PATH_OBJECTS];
            rct_object_entry PathAdditionObjects[RCT2_MAX_PATH_ADDITION_OBJECTS];
            rct_object_entry SceneryGroupObjects[RCT2_MAX_SCENERY_GROUP_OBJECTS];
            rct_object_entry ParkEntranceObjects[RCT2_MAX_PARK_ENTRANCE_OBJECTS];
            rct_object_entry WaterObjects[RCT2_MAX_WATER_OBJECTS];
            rct_object_entry ScenarioTextObjects[RCT2_MAX_SCENARIO_TEXT_OBJECTS];
        };
    };

    // SC6[4]
    uint16_t elapsed_months;
    uint16_t current_day;
    uint32_t scenario_ticks;
    uint32_t scenario_srand_0;
    uint32_t scenario_srand_1;

    // SC6[5]
    RCT12TileElement tile_elements[RCT2_MAX_TILE_ELEMENTS];

    // SC6[6]
    uint32_t next_free_tile_element_pointer_index;
    RCT2Sprite sprites[RCT2_MAX_SPRITES];
    uint16_t sprite_lists_head[static_cast<uint8_t>(EntityListId::Count)];
    uint16_t sprite_lists_count[static_cast<uint8_t>(EntityListId::Count)];
    rct_string_id park_name;
    uint8_t pad_013573D6[2];
    uint32_t park_name_args;
    money32 initial_cash;
    money32 current_loan;
    uint32_t park_flags;
    money16 park_entrance_fee;
    uint16_t rct1_park_entrance_x;
    uint16_t rct1_park_entrance_y;
    uint8_t pad_013573EE[2];
    uint8_t rct1_park_entrance_z;
    uint8_t pad_013573F1;
    rct12_peep_spawn peep_spawns[RCT12_MAX_PEEP_SPAWNS];
    uint8_t guest_count_change_modifier;
    uint8_t current_research_level;
    uint8_t pad_01357400[4];
    uint32_t researched_ride_types[RCT2_MAX_RESEARCHED_RIDE_TYPE_QUADS];
    uint32_t researched_ride_entries[RCT2_MAX_RESEARCHED_RIDE_ENTRY_QUADS];
    uint32_t researched_track_types_a[128];
    uint32_t researched_track_types_b[128];

    // SC6[7]
    uint16_t guests_in_park;
    uint16_t guests_heading_for_park;

    // Ignored in scenario
    money32 expenditure_table[RCT12_EXPENDITURE_TABLE_MONTH_COUNT][RCT12_EXPENDITURE_TYPE_COUNT];

    // SC6[8]
    uint16_t last_guests_in_park;
    uint8_t pad_01357BCA[3];
    uint8_t handyman_colour;
    uint8_t mechanic_colour;
    uint8_t security_colour;

    // Ignored in scenario
    uint32_t researched_scenery_items[RCT2_MAX_RESEARCHED_SCENERY_ITEM_QUADS];

    // SC6[9]
    uint16_t park_rating;

    // Ignored in scenario
    uint8_t park_rating_history[32];
    uint8_t guests_in_park_history[32];

    // SC6[10]
    uint8_t active_research_types;
    uint8_t research_progress_stage;
    uint32_t last_researched_item_subject;
    uint8_t pad_01357CF8[1000];
    uint32_t next_research_item;
    uint16_t research_progress;
    uint8_t next_research_category;
    uint8_t next_research_expected_day;
    uint8_t next_research_expected_month;
    uint8_t guest_initial_happiness;
    uint16_t park_size;
    uint16_t guest_generation_probability;
    uint16_t total_ride_value_for_money;
    money32 maximum_loan;
    money16 guest_initial_cash;
    uint8_t guest_initial_hunger;
    uint8_t guest_initial_thirst;
    uint8_t objective_type;
    uint8_t objective_year;
    uint8_t pad_013580FA[2];
    money32 objective_currency;
    uint16_t objective_guests;
    uint8_t campaign_weeks_left[20];
    uint8_t campaign_ride_index[22];

    // Ignored in scenario
    money32 balance_history[RCT12_FINANCE_GRAPH_SIZE];

    // SC6[11]
    money32 current_expenditure;
    money32 current_profit;
    money32 weekly_profit_average_dividend;
    uint16_t weekly_profit_average_divisor;
    uint8_t pad_0135833A[2];

    // Ignored in scenario
    money32 weekly_profit_history[RCT12_FINANCE_GRAPH_SIZE];

    // SC6[12]
    money32 park_value;

    // Ignored in scenario
    money32 park_value_history[RCT12_FINANCE_GRAPH_SIZE];

    // SC6[13]
    money32 completed_company_value;
    uint32_t total_admissions;
    money32 income_from_admissions;
    money32 company_value;
    uint8_t peep_warning_throttle[16];
    rct12_award awards[RCT12_MAX_AWARDS];
    money16 land_price;
    money16 construction_rights_price;
    uint16_t word_01358774;
    uint8_t pad_01358776[2];
    uint32_t cd_key;
    uint8_t pad_0135877C[64];
    uint32_t game_version_number;
    money32 completed_company_value_record;
    uint32_t loan_hash;
    uint16_t ride_count;
    uint8_t pad_013587CA[6];
    money32 historical_profit;
    uint8_t pad_013587D4[4];
    char scenario_completed_name[32];
    money32 cash;
    uint8_t pad_013587FC[50];
    uint16_t park_rating_casualty_penalty;
    uint16_t map_size_units;
    uint16_t map_size_minus_2;
    uint16_t map_size;
    uint16_t map_max_xy;
    uint32_t same_price_throughout;
    uint16_t suggested_max_guests;
    uint16_t park_rating_warning_days;
    uint8_t last_entrance_style;
    uint8_t rct1_water_colour;
    uint8_t pad_01358842[2];
    RCT12ResearchItem research_items[RCT2_MAX_RESEARCH_ITEMS];
    uint16_t map_base_z;
    char scenario_name[64];
    char scenario_description[256];
    uint8_t current_interest_rate;
    uint8_t pad_0135934B;
    uint32_t same_price_throughout_extended;
    int16_t park_entrance_x[RCT12_MAX_PARK_ENTRANCES];
    int16_t park_entrance_y[RCT12_MAX_PARK_ENTRANCES];
    int16_t park_entrance_z[RCT12_MAX_PARK_ENTRANCES];
    uint8_t park_entrance_direction[RCT12_MAX_PARK_ENTRANCES];
    char scenario_filename[256];
    uint8_t saved_expansion_pack_names[3256];
    RCT12Banner banners[RCT2_MAX_BANNERS_IN_PARK];
    char custom_strings[RCT12_MAX_USER_STRINGS][RCT12_USER_STRING_MAX_LENGTH];
    uint32_t game_ticks_1;
    rct2_ride rides[RCT12_MAX_RIDES_IN_PARK];
    uint16_t saved_age;
    int16_t saved_view_x;
    int16_t saved_view_y;
    uint8_t saved_view_zoom;
    uint8_t saved_view_rotation;
    RCT12MapAnimation map_animations[RCT2_MAX_ANIMATED_OBJECTS];
    uint16_t num_map_animations;
    uint8_t pad_0138B582[2];
    RCT2RideRatingCalculationData ride_ratings_calc_data;
    uint8_t pad_0138B5D0[60];
    RCT12RideMeasurement ride_measurements[8];
    uint32_t next_guest_index;
    uint16_t grass_and_scenery_tilepos;
    uint32_t patrol_areas[(RCT2_MAX_STAFF + RCT12_STAFF_TYPE_COUNT) * RCT12_PATROL_AREA_SIZE];
    RCT2StaffMode staff_modes[RCT2_MAX_STAFF + RCT12_STAFF_TYPE_COUNT];
    uint8_t pad_13CA73E;
    uint8_t pad_13CA73F;
    uint8_t byte_13CA740;
    uint8_t pad_13CA741;
    uint8_t byte_13CA742[4]; // unused
    uint8_t climate;
    uint8_t pad_013CA747;
    uint16_t climate_update_timer;
    uint8_t current_weather;
    uint8_t next_weather;
    uint8_t temperature;
    uint8_t next_temperature;
    uint8_t current_weather_effect;
    uint8_t next_weather_effect;
    uint8_t current_weather_gloom;
    uint8_t next_weather_gloom;
    uint8_t current_weather_level;
    uint8_t next_weather_level;
    rct12_news_item news_items[RCT12_MAX_NEWS_ITEMS];
    char rct1_scenario_name[62];       // Unused in RCT2
    uint16_t rct1_scenario_slot_index; // Unused in RCT2
    uint32_t rct1_scenario_flags;      // Unused in RCT2
    uint16_t wide_path_tile_loop_x;
    uint16_t wide_path_tile_loop_y;
    uint8_t pad_13CE778[434];
};
assert_struct_size(rct_s6_data, 0x46b44a);

struct rct_stex_entry
{
    rct_string_id scenario_name; // 0x00
    rct_string_id park_name;     // 0x02
    rct_string_id details;       // 0x04
    uint8_t var_06;
};
assert_struct_size(rct_stex_entry, 7);
#pragma pack(pop)

std::vector<uint8_t> DecryptSea(const fs::path& path);
ObjectEntryIndex RCT2RideTypeToOpenRCT2RideType(uint8_t rct2RideType, const rct_ride_entry* rideEntry);
bool RCT2TrackTypeIsBooster(uint8_t rideType, uint16_t trackType);
bool RCT2RideTypeNeedsConversion(uint8_t rct2RideType);
uint8_t OpenRCT2RideTypeToRCT2RideType(ObjectEntryIndex openrct2Type);
track_type_t RCT2TrackTypeToOpenRCT2(RCT12TrackType origTrackType, uint8_t rideType, bool convertFlat);
RCT12TrackType OpenRCT2TrackTypeToRCT2(track_type_t origTrackType);

/**
 * Iterates an RCT2 string buffer and returns the length of the string in bytes.
 * Handles single and multi-byte strings.
 */
size_t GetRCT2StringBufferLen(const char* buffer, size_t maxBufferLen);

struct FootpathMapping
{
    std::string_view Original;
    std::string_view NormalSurface;
    std::string_view QueueSurface;
    std::string_view Railing;
};

const FootpathMapping* GetFootpathSurfaceId(
    const ObjectEntryDescriptor& desc, bool ideallyLoaded = false, bool isQueue = false);
std::optional<rct_object_entry> GetBestObjectEntryForSurface(std::string_view surface, std::string_view railings);
