/*****************************************************************************
 * Copyright (c) 2014-2025 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 "../core/FileSystem.hpp"
#include "../core/FixedPoint.hpp"
#include "../rct12/RCT12.h"
#include "../ride/RideRatings.h"
#include "../world/ParkData.h"
#include "Limits.h"

#include <tuple>
#include <vector>

struct RideObjectEntry;
enum class EditorStep : uint8_t;
enum class VehicleColourSettings : uint8_t;

namespace OpenRCT2::Scenario
{
    enum class Category : uint8_t;
    enum class ObjectiveType : uint8_t;
} // namespace OpenRCT2::Scenario

enum
{
    S6_TYPE_SAVEDGAME,
    S6_TYPE_SCENARIO
};

// For legacy scores file
enum
{
    SCENARIO_FLAGS_VISIBLE = (1 << 0),
    SCENARIO_FLAGS_COMPLETED = (1 << 1),
    SCENARIO_FLAGS_SIXFLAGS = (1 << 2)
};

namespace OpenRCT2::RCT2
{
    constexpr StringId kRCT2RideStringStart = 2;

    // clang-format off
    constexpr uint16_t kRCT2ObjectEntryCount =
        Limits::kMaxRideObjects +
        Limits::kMaxSmallSceneryObjects +
        Limits::kMaxLargeSceneryObjects +
        Limits::kMaxWallSceneryObjects +
        Limits::kMaxBannerObjects +
        Limits::kMaxPathObjects +
        Limits::kMaxPathAdditionObjects +
        Limits::kMaxSceneryGroupObjects +
        Limits::kMaxParkEntranceObjects +
        Limits::kMaxWaterObjects +
        Limits::kMaxScenarioMetaObjects;
    // clang-format on
    static_assert(kRCT2ObjectEntryCount == 721);

    // clang-format off
    constexpr int32_t kRCT2ObjectEntryGroupCounts[] = {
        Limits::kMaxRideObjects,
        Limits::kMaxSmallSceneryObjects,
        Limits::kMaxLargeSceneryObjects,
        Limits::kMaxWallSceneryObjects,
        Limits::kMaxBannerObjects,
        Limits::kMaxPathObjects,
        Limits::kMaxPathAdditionObjects,
        Limits::kMaxSceneryGroupObjects,
        Limits::kMaxParkEntranceObjects,
        Limits::kMaxWaterObjects,
        Limits::kMaxScenarioMetaObjects,
    };
    // clang-format on

    enum class EntityListId : uint8_t
    {
        Count = 6,
    };
#pragma pack(push, 1)
    /**
     * Ride structure.
     * size: 0x0260
     */
    struct Ride
    {
        uint8_t type; // 0x000
        // pointer to static info. for example, wild mouse type is 0x36, subtype is
        // 0x4c.
        RCT12ObjectEntryIndex subtype;                                 // 0x001
        uint16_t pad002;                                               // 0x002
        uint8_t mode;                                                  // 0x004
        VehicleColourSettings vehicleColourSettings;                   // 0x005
        RCT12VehicleColour vehicleColours[Limits::kMaxVehicleColours]; // 0x006
        uint8_t pad046[0x03]; // 0x046, Used to be track colours in RCT1 without expansions
        // 0 = closed, 1 = open, 2 = test
        uint8_t status; // 0x049
        StringId name;  // 0x04A
        union
        {
            uint32_t nameArguments; // 0x04C
            struct
            {
                StringId nameArgumentsTypeName; // 0x04C
                uint16_t nameArgumentsNumber;   // 0x04E
            };
        };
        RCT12xy8 overallView;                                // 0x050
        RCT12xy8 stationStarts[Limits::kMaxStationsPerRide]; // 0x052
        uint8_t stationHeights[Limits::kMaxStationsPerRide]; // 0x05A
        uint8_t stationLength[Limits::kMaxStationsPerRide];  // 0x05E
        uint8_t stationDepart[Limits::kMaxStationsPerRide];  // 0x062
        // ride->vehicle index for current train waiting for passengers
        // at station
        uint8_t trainAtStation[Limits::kMaxStationsPerRide];   // 0x066
        RCT12xy8 entrances[Limits::kMaxStationsPerRide];       // 0x06A
        RCT12xy8 exits[Limits::kMaxStationsPerRide];           // 0x072
        uint16_t lastPeepInQueue[Limits::kMaxStationsPerRide]; // 0x07A
        uint8_t pad082[Limits::kMaxStationsPerRide];           // 0x082, Used to be number of peeps in queue in RCT1, but this
                                                               // has moved.
        uint16_t vehicles[Limits::kMaxTrainsPerRide];          // 0x086, Points to the first car in the train
        uint8_t departFlags;                                   // 0x0C6

        // Not sure if these should be uint or sint.
        uint8_t numStations;             // 0x0C7
        uint8_t numTrains;               // 0x0C8
        uint8_t numCarsPerTrain;         // 0x0C9
        uint8_t proposedNumTrains;       // 0x0CA
        uint8_t proposedNumCarsPerTrain; // 0x0CB
        uint8_t maxTrains;               // 0x0CC
        uint8_t minMaxCarsPerTrain;      // 0x0CD
        uint8_t minWaitingTime;          // 0x0CE
        uint8_t maxWaitingTime;          // 0x0CF
        union
        {
            uint8_t operationOption; // 0x0D0
            uint8_t timeLimit;       // 0x0D0
            uint8_t numLaps;         // 0x0D0
            uint8_t launchSpeed;     // 0x0D0
            uint8_t speed;           // 0x0D0
            uint8_t rotations;       // 0x0D0
        };

        uint8_t boatHireReturnDirection; // 0x0D1
        RCT12xy8 boatHireReturnPosition; // 0x0D2
        uint8_t measurementIndex;        // 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 specialTrackElements; // 0x0D5
        uint8_t pad0D6[2];            // 0x0D6
        // Use ToHumanReadableSpeed if converting to display
        int32_t maxSpeed;                            // 0x0D8
        int32_t averageSpeed;                        // 0x0DC
        uint8_t currentTestSegment;                  // 0x0E0
        uint8_t averageSpeedTestTimeout;             // 0x0E1
        uint8_t pad0E2[0x2];                         // 0x0E2
        int32_t length[Limits::kMaxStationsPerRide]; // 0x0E4
        uint16_t time[Limits::kMaxStationsPerRide];  // 0x0F4
        fixed16_2dp maxPositiveVerticalG;            // 0x0FC
        fixed16_2dp maxNegativeVerticalG;            // 0x0FE
        fixed16_2dp maxLateralG;                     // 0x100
        fixed16_2dp previousVerticalG;               // 0x102
        fixed16_2dp previousLateralG;                // 0x104
        uint8_t pad106[0x2];                         // 0x106
        uint32_t testingFlags;                       // 0x108
        // x y map location of the current track piece during a test
        // this is to prevent counting special tracks multiple times
        RCT12xy8 curTestTrackLocation; // 0x10C
        // Next 3 variables are related (XXXX XYYY ZZZa aaaa)
        uint16_t turnCountDefault; // 0x10E X = current turn count
        uint16_t turnCountBanked;  // 0x110
        uint16_t turnCountSloped;  // 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 shelteredEighths; // 0x114 (XXX?-????)
        };
        // Y is number of powered lifts, X is drops
        uint8_t drops;             // 0x115 (YYXX XXXX)
        uint8_t startDropHeight;   // 0x116
        uint8_t highestDropHeight; // 0x117
        int32_t shelteredLength;   // 0x118
        // Unused always 0? Should affect nausea
        uint16_t var11C;              // 0x11C
        uint8_t numShelteredSections; // 0x11E (?abY YYYY)
        // see CurTestTrackLocation
        uint8_t curTestTrackZ; // 0x11F
        // Customer counter in the current 960 game tick (about 30 seconds) interval
        uint16_t curNumCustomers; // 0x120
        // Counts ticks to update customer intervals, resets each 960 game ticks.
        uint16_t numCustomersTimeout; // 0x122
        // Customer count in the last 10 * 960 game ticks (sliding window)
        uint16_t numCustomers[Limits::kCustomerHistorySize]; // 0x124
        money16 price;                                       // 0x138
        RCT12xy8 chairliftBullwheelLocation[2];              // 0x13A
        uint8_t chairliftBullwheelZ[2];                      // 0x13E
        OpenRCT2::RideRating::Tuple ratings;                 // 0x140
        money16 value;                                       // 0x146
        uint16_t chairliftBullwheelRotation;                 // 0x148
        uint8_t satisfaction;                                // 0x14A
        uint8_t satisfactionTimeOut;                         // 0x14B
        uint8_t satisfactionNext;                            // 0x14C
        // Various flags stating whether a window needs to be refreshed
        uint8_t windowInvalidateFlags; // 0x14D
        uint8_t pad14E[0x02];          // 0x14E
        uint32_t totalCustomers;       // 0x150
        money32 totalProfit;           // 0x154
        uint8_t popularity;            // 0x158
        uint8_t popularityTimeOut;     // 0x159 Updated every purchase and ?possibly by time?
        uint8_t popularityNext;        // 0x15A When timeout reached this will be the next popularity
        uint8_t numRiders;             // 0x15B
        uint8_t musicTuneId;           // 0x15C
        uint8_t slideInUse;            // 0x15D
        union
        {
            uint16_t slidePeep; // 0x15E
            uint16_t mazeTiles; // 0x15E
        };
        uint8_t pad160[0xE];            // 0x160
        uint8_t slidePeepTShirtColour;  // 0x16E
        uint8_t pad16F[0x7];            // 0x16F
        uint8_t spiralSlideProgress;    // 0x176
        uint8_t pad177[0x9];            // 0x177
        int16_t buildDate;              // 0x180
        money16 upkeepCost;             // 0x182
        uint16_t raceWinner;            // 0x184
        uint8_t pad186[0x02];           // 0x186
        uint32_t musicPosition;         // 0x188
        uint8_t breakdownReasonPending; // 0x18C
        uint8_t mechanicStatus;         // 0x18D
        uint16_t mechanic;              // 0x18E
        uint8_t inspectionStation;      // 0x190
        uint8_t brokenTrain;            // 0x191
        uint8_t brokenCar;              // 0x192
        uint8_t breakdownReason;        // 0x193
        money16 priceSecondary;         // 0x194
        union
        {
            struct
            {
                uint8_t reliabilitySubvalue;   // 0x196, 0 - 255, acts like the decimals for reliabilityPercentage
                uint8_t reliabilityPercentage; // 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 unreliabilityFactor; // 0x198
        // Range from [0, 100]
        uint8_t downtime;                                      // 0x199
        uint8_t inspectionInterval;                            // 0x19A
        uint8_t lastInspection;                                // 0x19B
        uint8_t downtimeHistory[Limits::kDowntimeHistorySize]; // 0x19C
        uint32_t numPrimaryItemsSold;                          // 0x1A4
        uint32_t numSecondaryItemsSold;                        // 0x1A8
        uint8_t breakdownSoundModifier;                        // 0x1AC
        // Used to oscillate the sound when ride breaks down.
        // 0 = no change, 255 = max change
        uint8_t notFixedTimeout;                                    // 0x1AD
        uint8_t lastCrashType;                                      // 0x1AE
        uint8_t connectedMessageThrottle;                           // 0x1AF
        money32 incomePerHour;                                      // 0x1B0
        money32 profit;                                             // 0x1B4
        uint8_t queueTime[Limits::kMaxStationsPerRide];             // 0x1B8
        uint8_t trackColourMain[Limits::kNumColourSchemes];         // 0x1BC
        uint8_t trackColourAdditional[Limits::kNumColourSchemes];   // 0x1C0
        uint8_t trackColourSupports[Limits::kNumColourSchemes];     // 0x1C4
        uint8_t music;                                              // 0x1C8
        uint8_t entranceStyle;                                      // 0x1C9
        uint16_t vehicleChangeTimeout;                              // 0x1CA
        uint8_t numBlockBrakes;                                     // 0x1CC
        uint8_t liftHillSpeed;                                      // 0x1CD
        uint16_t guestsFavourite;                                   // 0x1CE
        uint32_t lifecycleFlags;                                    // 0x1D0
        uint8_t vehicleColoursExtended[Limits::kMaxVehicleColours]; // 0x1D4
        uint16_t totalAirTime;                                      // 0x1F4
        uint8_t currentTestStation;                                 // 0x1F6
        uint8_t numCircuits;                                        // 0x1F7
        int16_t cableLiftX;                                         // 0x1F8
        int16_t cableLiftY;                                         // 0x1FA
        uint8_t cableLiftZ;                                         // 0x1FC
        uint8_t pad1FD;                                             // 0x1FD
        uint16_t cableLift;                                         // 0x1FE
        uint16_t queueLength[Limits::kMaxStationsPerRide];          // 0x200
        uint8_t pad208[0x58];                                       // 0x208

        uint8_t getMinCarsPerTrain() const;
        uint8_t getMaxCarsPerTrain() const;
    };
    static_assert(sizeof(Ride) == 0x260);

    /* Track Entrance entry size: 0x06 */
    struct TD6EntranceElement
    {
        int8_t z;          // 0x00
        uint8_t direction; // 0x01
        int16_t x;         // 0x02
        int16_t y;         // 0x04

        constexpr Direction GetDirection() const
        {
            return (direction & 0b00001111);
        }

        constexpr bool IsExit() const
        {
            return !!(direction >> 7);
        }
    };
    static_assert(sizeof(TD6EntranceElement) == 0x06);

    /* Track Scenery entry  size: 0x16 */
    struct TD6SceneryElement
    {
        RCTObjectEntry SceneryObject; // 0x00
        int8_t x;                     // 0x10
        int8_t y;                     // 0x11
        int8_t z;                     // 0x12
        uint8_t Flags;                // 0x13 direction quadrant tertiary colour
        uint8_t PrimaryColour;        // 0x14
        uint8_t SecondaryColour;      // 0x15

        colour_t getTertiaryWallColour() const;
    };
    static_assert(sizeof(TD6SceneryElement) == 0x16);

    /**
     * Track design structure.
     * size: 0xA3
     */
    struct TD6Track
    {
        uint8_t Type; // 0x00
        RCT12ObjectEntryIndex VehicleType;
        uint32_t Flags;                                               // 0x02
        uint8_t RideMode;                                             // 0x06
        uint8_t VersionAndColourScheme;                               // 0x07 0b0000_VVCC
        RCT12VehicleColour VehicleColours[Limits::kMaxTrainsPerRide]; // 0x08
        uint8_t Pad48;                                                // 0x48
        uint8_t EntranceStyle;                                        // 0x49
        uint8_t TotalAirTime;                                         // 0x4A
        uint8_t DepartFlags;                                          // 0x4B
        uint8_t NumberOfTrains;                                       // 0x4C
        uint8_t NumberOfCarsPerTrain;                                 // 0x4D
        uint8_t MinWaitingTime;                                       // 0x4E
        uint8_t MaxWaitingTime;                                       // 0x4F
        uint8_t OperationSetting;                                     // 0x50
        int8_t MaxSpeed;                                              // 0x51
        int8_t AverageSpeed;                                          // 0x52
        uint16_t RideLength;                                          // 0x53
        uint8_t MaxPositiveVerticalG;                                 // 0x55
        int8_t MaxNegativeVerticalG;                                  // 0x56
        uint8_t MaxLateralG;                                          // 0x57
        union
        {
            uint8_t Inversions; // 0x58
            uint8_t Holes;      // 0x58
        };
        uint8_t Drops;                                              // 0x59
        uint8_t HighestDropHeight;                                  // 0x5A
        uint8_t Excitement;                                         // 0x5B
        uint8_t Intensity;                                          // 0x5C
        uint8_t Nausea;                                             // 0x5D
        money16 UpkeepCost;                                         // 0x5E
        uint8_t TrackSpineColour[Limits::kNumColourSchemes];        // 0x60
        uint8_t TrackRailColour[Limits::kNumColourSchemes];         // 0x64
        uint8_t TrackSupportColour[Limits::kNumColourSchemes];      // 0x68
        uint32_t Flags2;                                            // 0x6C
        RCTObjectEntry VehicleObject;                               // 0x70
        uint8_t SpaceRequiredX;                                     // 0x80
        uint8_t SpaceRequiredY;                                     // 0x81
        uint8_t VehicleAdditionalColour[Limits::kMaxTrainsPerRide]; // 0x82
        uint8_t LiftHillSpeedNumCircuits;                           // 0xA2 0bCCCL_LLLL
        // 0xA3 (data starts here in file)
    };
    static_assert(sizeof(TD6Track) == 0xA3);

    /**
     * scores.dat file header.
     * size: 0x10
     */
    struct ScoresHeader
    {
        uint32_t Var0;
        uint32_t Var4;
        uint32_t Var8;
        uint32_t ScenarioCount;
    };
    static_assert(sizeof(ScoresHeader) == 0x10);

    /**
     * An entry of scores.dat
     * size: 0x02B0
     */
    struct ScoresEntry
    {
        char Path[256];
        uint8_t Category;
        uint8_t Pad0101[0x1F];
        Scenario::ObjectiveType ObjectiveType;
        int8_t ObjectiveArg1;
        int32_t objectiveArg2;
        uint16_t objectiveArg3;
        char Name[64];
        char Details[256];
        int32_t Flags;
        money32 CompanyValue;
        char CompletedBy[64];
    };
    static_assert(sizeof(ScoresEntry) == 0x02B0);

    struct Vehicle : RCT12EntityBase
    {
        uint8_t Pitch;        // 0x1F
        uint8_t BankRotation; // 0x20
        uint8_t Pad21[3];
        int32_t RemainingDistance;  // 0x24
        int32_t Velocity;           // 0x28
        int32_t Acceleration;       // 0x2C
        uint8_t Ride;               // 0x30
        uint8_t VehicleType;        // 0x31
        RCT12VehicleColour Colours; // 0x32
        union
        {
            uint16_t TrackProgress; // 0x34
            struct
            {
                int8_t Var34;
                uint8_t Var35;
            };
        };
        union
        {
            int16_t TrackTypeAndDirection; // 0x36
            RCT12xy8 BoatLocation;         // 0x36
        };
        uint16_t TrackX;             // 0x38
        uint16_t TrackY;             // 0x3A
        uint16_t TrackZ;             // 0x3C
        uint16_t NextVehicleOnTrain; // 0x3E
        uint16_t PrevVehicleOnRide;  // 0x40
        uint16_t NextVehicleOnRide;  // 0x42
        uint16_t Var44;
        uint16_t Mass;        // 0x46
        uint16_t UpdateFlags; // 0x48
        uint8_t SwingSprite;
        uint8_t CurrentStation; // 0x4B
        union
        {
            int16_t SwingPosition; // 0x4C
            int16_t CurrentTime;   // 0x4C
            struct
            {
                int8_t FerrisWheelVar0; // 0x4C
                int8_t FerrisWheelVar1; // 0x4D
            };
        };
        union
        {
            int16_t SwingSpeed;
            int16_t CrashZ; // 0x4E
        };
        uint8_t Status;                // 0x50
        uint8_t SubState;              // 0x51
        uint16_t Peep[32];             // 0x52
        uint8_t PeepTshirtColours[32]; // 0x92
        uint8_t NumSeats;              // 0xB2
        uint8_t NumPeeps;              // 0xB3
        uint8_t NextFreeSeat;          // 0xB4
        uint8_t RestraintsPosition;    // 0xB5
        union
        {
            int16_t SpinSpeed; // 0xB6
            int16_t CrashX;    // 0xB6
        };
        uint16_t Sound2Flags; // 0xB8
        uint8_t SpinSprite;   // 0xBA
        uint8_t Sound1Id;     // 0xBB
        uint8_t Sound1Volume; // 0xBC
        uint8_t Sound2Id;     // 0xBD
        uint8_t Sound2Volume; // 0xBE
        int8_t SoundVectorFactor;
        union
        {
            uint16_t VarC0;
            int16_t CrashY;           // 0xC0
            uint16_t TimeWaiting;     // 0xC0
            uint16_t CableLiftTarget; // 0xC0
        };
        uint8_t Speed;               // 0xC2
        uint8_t PoweredAcceleration; // 0xC3
        union
        {
            uint8_t DodgemsCollisionDirection; // 0xC4
            uint8_t CollisionDetectionTimer;   // 0xC4
        };
        uint8_t AnimationFrame; // 0xC5
        uint8_t PadC6[0x2];
        uint32_t AnimationState;
        uint8_t ScreamSoundId; // 0xCC
        uint8_t TrackSubposition;
        uint8_t NumLaps;              // 0xCE
        uint8_t BrakeSpeed;           // 0xCF
        uint16_t LostTimeOut;         // 0xD0
        int8_t VerticalDropCountdown; // 0xD1
        uint8_t VarD3;
        uint8_t MiniGolfCurrentAnimation;
        uint8_t MiniGolfFlags;      // 0xD5
        uint8_t RideSubtype;        // 0xD6
        uint8_t ColoursExtended;    // 0xD7
        uint8_t SeatRotation;       // 0xD8
        uint8_t TargetSeatRotation; // 0xD9

        OpenRCT2::RCT12::TrackElemType GetTrackType() const
        {
            return static_cast<OpenRCT2::RCT12::TrackElemType>(TrackTypeAndDirection >> 2);
        }
        uint8_t GetTrackDirection() const
        {
            return TrackTypeAndDirection & kRCT12VehicleTrackDirectionMask;
        }
        void SetTrackType(OpenRCT2::RCT12::TrackElemType trackType)
        {
            // set the upper 14 bits to 0
            TrackTypeAndDirection &= ~kRCT12VehicleTrackTypeMask;
            TrackTypeAndDirection |= EnumValue(trackType) << 2;
        }
        void SetTrackDirection(uint8_t trackDirection)
        {
            // set the lower 2 bits only
            TrackTypeAndDirection &= ~kRCT12VehicleTrackDirectionMask;
            TrackTypeAndDirection |= trackDirection & kRCT12VehicleTrackDirectionMask;
        }
    };
    static_assert(sizeof(Vehicle) == 0xDA);

    struct Peep : RCT12EntityBase
    {
        uint8_t Pad1F[0x22 - 0x1F];
        StringId NameStringIdx; // 0x22
        uint16_t NextX;         // 0x24
        uint16_t NextY;         // 0x26
        uint8_t NextZ;          // 0x28
        uint8_t NextFlags;      // 0x29
        uint8_t OutsideOfPark;  // 0x2A
        uint8_t State;          // 0x2B
        uint8_t SubState;       // 0x2C
        uint8_t AnimationGroup; // 0x2D
        uint8_t PeepType;       // 0x2E
        union
        {
            uint8_t StaffType; // 0x2F
            uint8_t NoOfRides; // 0x2F
        };
        uint8_t TshirtColour;         // 0x30
        uint8_t TrousersColour;       // 0x31
        uint16_t DestinationX;        // 0x32
        uint16_t DestinationY;        // 0x34
        uint8_t DestinationTolerance; // 0x36
        uint8_t Var37;
        uint8_t Energy;                // 0x38
        uint8_t EnergyTarget;          // 0x39
        uint8_t Happiness;             // 0x3A
        uint8_t HappinessTarget;       // 0x3B
        uint8_t Nausea;                // 0x3C
        uint8_t NauseaTarget;          // 0x3D
        uint8_t Hunger;                // 0x3E
        uint8_t Thirst;                // 0x3F
        uint8_t Toilet;                // 0x40
        uint8_t Mass;                  // 0x41
        uint8_t TimeToConsume;         // 0x42
        uint8_t Intensity;             // 0x43
        uint8_t NauseaTolerance;       // 0x44
        uint8_t WindowInvalidateFlags; // 0x45
        money16 PaidOnDrink;           // 0x46
        uint8_t RideTypesBeenOn[16];   // 0x48
        uint32_t ItemExtraFlags;       // 0x58
        RCT12RideId Photo2RideRef;     // 0x5C
        RCT12RideId Photo3RideRef;     // 0x5D
        RCT12RideId Photo4RideRef;     // 0x5E
        uint8_t Pad5F[0x09];           // 0x5F
        RCT12RideId CurrentRide;       // 0x68
        uint8_t CurrentRideStation;    // 0x69
        uint8_t CurrentTrain;          // 0x6A
        union
        {
            struct
            {
                uint8_t CurrentCar;  // 0x6B
                uint8_t CurrentSeat; // 0x6C
            };
            uint16_t TimeToSitdown; // 0x6B
            struct
            {
                uint8_t TimeToStand;   // 0x6B
                uint8_t StandingFlags; // 0x6C
            };
        };
        uint8_t SpecialSprite;          // 0x6D
        uint8_t AnimationType;          // 0x6E
        uint8_t NextAnimationType;      // 0x6F
        uint8_t AnimationImageIdOffset; // 0x70
        uint8_t Action;                 // 0x71
        uint8_t AnimationFrameNum;      // 0x72
        uint8_t StepProgress;           // 0x73
        union
        {
            uint16_t MechanicTimeSinceCall;
            uint16_t NextInQueue; // 0x74
        };
        uint8_t Pad76;
        uint8_t Pad77;
        union
        {
            uint8_t MazeLastEdge; // 0x78
            uint8_t Direction;
        };
        RCT12RideId InteractionRideIndex;
        uint16_t TimeInQueue;                                // 0x7A
        uint8_t RidesBeenOn[32];                             // 0x7C
        uint32_t Id;                                         // 0x9C
        money32 CashInPocket;                                // 0xA0
        money32 CashSpent;                                   // 0xA4
        int32_t ParkEntryTime;                               // 0xA8
        int8_t RejoinQueueTimeout;                           // 0xAC
        RCT12RideId PreviousRide;                            // 0xAD
        uint16_t PreviousRideTimeOut;                        // 0xAE
        RCT12PeepThought Thoughts[Limits::kMaxPeepThoughts]; // 0xB0
        uint8_t PathCheckOptimisation;                       // 0xC4
        union
        {
            uint8_t StaffId;                  // 0xC5
            RCT12RideId GuestHeadingToRideId; // 0xC5
        };
        union
        {
            uint8_t StaffOrders;         // 0xC6
            uint8_t PeepIsLostCountdown; // 0xC6
        };
        RCT12RideId Photo1RideRef;     // 0xC7
        uint32_t PeepFlags;            // 0xC8
        RCT12xyzd8 PathfindGoal;       // 0xCC
        RCT12xyzd8 PathfindHistory[4]; // 0xD0
        uint8_t NoActionFrameNum;      // 0xE0
        uint8_t LitterCount;           // 0xE1
        union
        {
            uint8_t TimeOnRide;         // 0xE2
            uint8_t StaffMowingTimeout; // 0xE2
        };
        uint8_t DisgustingCount; // 0xE3
        union
        {
            money16 PaidToEnter;      // 0xE4
            uint16_t StaffLawnsMown;  // 0xE4
            uint16_t StaffRidesFixed; // 0xE4
        };
        union
        {
            money16 PaidOnRides;          // 0xE6
            uint16_t StaffGardensWatered; // 0xE6
            uint16_t StaffRidesInspected; // 0xE6
        };
        union
        {
            money16 PaidOnFood;        // 0xE8
            uint16_t StaffLitterSwept; // 0xE8
        };
        union
        {
            money16 PaidOnSouvenirs;   // 0xEA
            uint16_t StaffBinsEmptied; // 0xEA
        };
        uint8_t NoOfFood;                   // 0xEC
        uint8_t NoOfDrinks;                 // 0xED
        uint8_t NoOfSouvenirs;              // 0xEE
        uint8_t VandalismSeen;              // 0xEF 0xC0 vandalism thought timeout, 0x3F vandalism tiles seen
        uint8_t VoucherType;                // 0xF0
        RCT12RideId VoucherArguments;       // 0xF1 ride_id or string_offset_id
        uint8_t SurroundingsThoughtTimeout; // 0xF2
        uint8_t Angriness;                  // 0xF3
        uint8_t TimeLost;            // 0xF4 the time the peep has been lost when it reaches 254 generates the lost thought
        uint8_t DaysInQueue;         // 0xF5
        uint8_t BalloonColour;       // 0xF6
        uint8_t UmbrellaColour;      // 0xF7
        uint8_t HatColour;           // 0xF8
        RCT12RideId FavouriteRide;   // 0xF9
        uint8_t FavouriteRideRating; // 0xFA
        uint8_t PadFB;
        uint32_t ItemStandardFlags; // 0xFC
        uint64_t GetItemFlags() const
        {
            return ItemStandardFlags | (static_cast<uint64_t>(ItemExtraFlags) << 32);
        }
    };
    static_assert(sizeof(Peep) == 0x100);

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

    union Entity
    {
    private:
        uint8_t Pad00[0x100];

    public:
        RCT12EntityBase Unknown;
        RCT2::Vehicle Vehicle;
        RCT2::Peep Peep;
        RCT12EntityLitter Litter;
        RCT12EntityBalloon Balloon;
        RCT12EntityDuck Duck;
        RCT12EntityJumpingFountain JumpingFountain{};
        RCT12EntityMoneyEffect MoneyEffect;
        RCT12EntityCrashedVehicleParticle CrashedVehicleParticle;
        RCT12EntityCrashSplash CrashSplash;
        RCT12EntitySteamParticle SteamParticle;
        RCT12EntityParticle MiscParticle;
    };
    static_assert(sizeof(Entity) == 0x100);

    struct RideRatingCalculationData
    {
        uint16_t ProximityX;
        uint16_t ProximityY;
        uint16_t ProximityZ;
        uint16_t ProximityStartX;
        uint16_t ProximityStartY;
        uint16_t ProximityStartZ;
        uint8_t CurrentRide;
        uint8_t State;
        OpenRCT2::RCT12::TrackElemType ProximityTrackType;
        uint8_t ProximityBaseHeight;
        uint16_t ProximityTotal;
        uint16_t ProximityScores[26];
        uint16_t NumBrakes;
        uint16_t NumReversers;
        uint16_t StationFlags;
    };
    static_assert(sizeof(RideRatingCalculationData) == 76);

    /**
     * SV6/SC6 header chunk
     * size: 0x20
     */
    struct S6Header
    {
        uint8_t Type;              // 0x00
        uint8_t ClassicFlag;       // 0x01
        uint16_t NumPackedObjects; // 0x02
        uint32_t Version;          // 0x04
        uint32_t MagicNumber;      // 0x08
        uint8_t Pad0C[0x14];
    };
    static_assert(sizeof(S6Header) == 0x20);

    /**
     * SC6 information chunk
     * size: 0x198
     */
    struct S6Info
    {
        ::EditorStep EditorStep;
        Scenario::Category Category;           // 0x01
        Scenario::ObjectiveType ObjectiveType; // 0x02
        uint8_t ObjectiveArg1;                 // 0x03
        int32_t ObjectiveArg2;                 // 0x04
        uint16_t ObjectiveArg3;                // 0x08
        uint8_t Pad00A[0x3E];
        char Name[64];        // 0x48
        char Details[256];    // 0x88
        RCTObjectEntry Entry; // 0x188
    };
    static_assert(sizeof(S6Info) == 0x198);

    struct S6Data
    {
        // SC6[0]
        S6Header Header;

        // SC6[1]
        S6Info Info;

        // SC6[2]
        // packed objects

        // SC6[3]
        union
        {
            RCTObjectEntry Objects[kRCT2ObjectEntryCount];
            struct
            {
                RCTObjectEntry RideObjects[Limits::kMaxRideObjects];
                RCTObjectEntry SceneryObjects[Limits::kMaxSmallSceneryObjects];
                RCTObjectEntry LargeSceneryObjects[Limits::kMaxLargeSceneryObjects];
                RCTObjectEntry WallSceneryObjects[Limits::kMaxWallSceneryObjects];
                RCTObjectEntry BannerObjects[Limits::kMaxBannerObjects];
                RCTObjectEntry PathObjects[Limits::kMaxPathObjects];
                RCTObjectEntry PathAdditionObjects[Limits::kMaxPathAdditionObjects];
                RCTObjectEntry SceneryGroupObjects[Limits::kMaxSceneryGroupObjects];
                RCTObjectEntry ParkEntranceObjects[Limits::kMaxParkEntranceObjects];
                RCTObjectEntry WaterObjects[Limits::kMaxWaterObjects];
                RCTObjectEntry ScenarioMetaObjects[Limits::kMaxScenarioMetaObjects];
            };
        };

        // SC6[4]
        uint16_t ElapsedMonths;
        uint16_t CurrentDay;
        uint32_t ScenarioTicks;
        uint32_t ScenarioSrand0;
        uint32_t ScenarioSrand1;

        // SC6[5]
        RCT12TileElement TileElements[Limits::kMaxTileElements];

        // SC6[6]
        uint32_t NextFreeTileElementPointerIndex;
        Entity Entities[Limits::kMaxEntitiesRCTCExtended];
        uint16_t EntityListsHead[EnumValue(EntityListId::Count)];
        uint16_t EntityListsCount[EnumValue(EntityListId::Count)];
        StringId ParkName;
        uint8_t Pad013573D6[2];
        uint32_t ParkNameArgs;
        money32 InitialCash;
        money32 CurrentLoan;
        uint32_t ParkFlags;
        money16 ParkEntranceFee;
        uint16_t RCT1ParkEntranceX;
        uint16_t RCT1ParkEntranceY;
        uint8_t Pad013573EE[2];
        uint8_t RCT1ParkEntranceZ;
        uint8_t Pad013573F1;
        RCT12PeepSpawn PeepSpawns[Limits::kMaxPeepSpawns];
        uint8_t GuestCountChangeModifier;
        uint8_t CurrentResearchLevel;
        uint8_t Pad01357400[4];
        uint32_t ResearchedRideTypes[Limits::kMaxResearchedRideTypeQuads];
        uint32_t ResearchedRideEntries[Limits::kMaxResearchedRideEntryQuads];
        uint32_t ResearchedTrackTypesA[128];
        uint32_t ResearchedTrackTypesB[128];

        // SC6[7]
        uint16_t GuestsInPark;
        uint16_t GuestsHeadingForPark;

        // Ignored in scenario
        money32 ExpenditureTable[Limits::kExpenditureTableMonthCount][Limits::kExpenditureTypeCount];

        // SC6[8]
        uint16_t LastGuestsInPark;
        uint8_t Pad01357BCA[3];
        uint8_t HandymanColour;
        uint8_t MechanicColour;
        uint8_t SecurityColour;

        // Ignored in scenario
        uint32_t ResearchedSceneryItems[Limits::kMaxResearchedSceneryItemQuads];

        // SC6[9]
        uint16_t ParkRating;

        // Ignored in scenario
        uint8_t ParkRatingHistory[kParkRatingHistorySize];
        uint8_t GuestsInParkHistory[kGuestsInParkHistorySize];

        // SC6[10]
        uint8_t ActiveResearchTypes;
        uint8_t ResearchProgressStage;
        uint32_t LastResearchedItemSubject;
        uint8_t Pad01357CF8[1000];
        uint32_t NextResearchItem;
        uint16_t ResearchProgress;
        uint8_t NextResearchCategory;
        uint8_t NextResearchExpectedDay;
        uint8_t NextResearchExpectedMonth;
        uint8_t GuestInitialHappiness;
        uint16_t ParkSize;
        uint16_t GuestGenerationProbability;
        uint16_t TotalRideValueForMoney;
        money32 MaximumLoan;
        money16 GuestInitialCash;
        uint8_t GuestInitialHunger;
        uint8_t GuestInitialThirst;
        Scenario::ObjectiveType ObjectiveType;
        uint8_t ObjectiveYear;
        uint8_t Pad013580FA[2];
        money32 ObjectiveCurrency;
        uint16_t ObjectiveGuests;
        uint8_t CampaignWeeksLeft[20];
        uint8_t CampaignRideIndex[22];

        // Ignored in scenario
        money32 BalanceHistory[Limits::kFinanceGraphSize];

        // SC6[11]
        money32 CurrentExpenditure;
        money32 CurrentProfit;
        money32 WeeklyProfitAverageDividend;
        uint16_t WeeklyProfitAverageDivisor;
        uint8_t Pad0135833A[2];

        // Ignored in scenario
        money32 WeeklyProfitHistory[Limits::kFinanceGraphSize];

        // SC6[12]
        money32 ParkValue;

        // Ignored in scenario
        money32 ParkValueHistory[Limits::kFinanceGraphSize];

        // SC6[13]
        money32 CompletedCompanyValue;
        uint32_t TotalAdmissions;
        money32 IncomeFromAdmissions;
        money32 CompanyValue;
        uint8_t PeepWarningThrottle[16];
        RCT12Award Awards[Limits::kMaxAwards];
        money16 LandPrice;
        money16 ConstructionRightsPrice;
        uint16_t Word01358774;
        uint8_t Pad01358776[2];
        uint32_t CDKey;
        uint8_t Pad0135877C[64];
        uint32_t GameVersionNumber;
        money32 CompletedCompanyValueRecord;
        uint32_t LoanHash;
        uint16_t RideCount;
        uint8_t Pad013587CA[6];
        money32 HistoricalProfit;
        uint8_t Pad013587D4[4];
        char ScenarioCompletedName[32];
        money32 Cash;
        uint8_t Pad013587FC[50];
        uint16_t ParkRatingCasualtyPenalty;
        uint16_t MapSizeUnits;
        uint16_t MapSizeMinus2;
        uint16_t MapSize;
        uint16_t MapMaxXy;
        uint32_t SamePriceThroughout;
        uint16_t SuggestedMaxGuests;
        uint16_t ParkRatingWarningDays;
        uint8_t LastEntranceStyle;
        uint8_t RCT1WaterColour;
        uint8_t Pad01358842[2];
        RCT12ResearchItem ResearchItems[Limits::kMaxResearchItems];
        uint16_t MapBaseZ;
        char ScenarioName[64];
        char ScenarioDescription[256];
        uint8_t CurrentInterestRate;
        uint8_t Pad0135934B;
        uint32_t SamePriceThroughoutExtended;
        int16_t ParkEntranceX[Limits::kMaxParkEntrances];
        int16_t ParkEntranceY[Limits::kMaxParkEntrances];
        int16_t ParkEntranceZ[Limits::kMaxParkEntrances];
        uint8_t ParkEntranceDirection[Limits::kMaxParkEntrances];
        char ScenarioFilename[256];
        uint8_t SavedExpansionPackNames[3256];
        RCT12Banner Banners[Limits::kMaxBanners];
        char CustomStrings[Limits::kMaxUserStrings][Limits::kMaxUserStringLength];
        uint32_t GameTicks1;
        Ride Rides[Limits::kMaxRidesInPark];
        uint16_t SavedAge; // unused
        int16_t SavedViewX;
        int16_t SavedViewY;
        uint8_t SavedViewZoom;
        uint8_t SavedViewRotation;
        RCT12MapAnimation MapAnimations[Limits::kMaxAnimatedObjects];
        uint16_t NumMapAnimations;
        uint8_t Pad0138B582[2];
        RideRatingCalculationData RideRatingsCalcData;
        uint8_t Pad0138B5D0[60];
        RCT12RideMeasurement RideMeasurements[8];
        uint32_t NextGuestIndex;
        uint16_t GrassAndSceneryTilepos;
        uint32_t PatrolAreas[(Limits::kMaxStaff + Limits::kStaffTypeCount) * Limits::kPatrolAreaSize];
        StaffMode StaffModes[Limits::kMaxStaff + Limits::kStaffTypeCount];
        uint8_t Pad13CA73E;
        uint8_t Pad13CA73F;
        uint8_t Byte13CA740;
        uint8_t Pad13CA741;
        uint8_t Byte13CA742[4]; // unused
        RCT12::ClimateType Climate;
        uint8_t Pad013CA747;
        uint16_t WeatherUpdateTimer;
        uint8_t CurrentWeather;
        uint8_t NextWeather;
        uint8_t Temperature;
        uint8_t NextTemperature;
        uint8_t CurrentWeatherEffect;
        uint8_t NextWeatherEffect;
        uint8_t CurrentWeatherGloom;
        uint8_t NextWeatherGloom;
        uint8_t CurrentWeatherLevel;
        uint8_t NextWeatherLevel;
        RCT12NewsItem recentMessages[Limits::kMaxRecentNewsItems];
        RCT12NewsItem archivedMessages[Limits::kMaxArchivedNewsItems];
        char RCT1ScenarioName[62];      // Unused in RCT2
        uint16_t RCT1ScenarioSlotIndex; // Unused in RCT2
        uint32_t RCT1ScenarioFlags;     // Unused in RCT2
        uint16_t WidePathTileLoopX;
        uint16_t WidePathTileLoopY;
        uint8_t Pad13CE778[434];
    };
    static_assert(sizeof(S6Data) == 0x5a3c4a);

    struct StexEntry
    {
        StringId ScenarioName; // 0x00
        StringId ParkName;     // 0x02
        StringId Details;      // 0x04
        uint8_t Var06;
    };
    static_assert(sizeof(StexEntry) == 7);
#pragma pack(pop)

    ObjectEntryIndex RCT2RideTypeToOpenRCT2RideType(uint8_t rct2RideType, const RideObjectEntry& rideEntry);
    bool RCT2RideTypeNeedsConversion(uint8_t rct2RideType);
    uint8_t OpenRCT2RideTypeToRCT2RideType(ObjectEntryIndex openrct2Type);

    enum class OriginalRideClass
    {
        Regular,
        WildMouse,
        FlatRide,
    };

    OpenRCT2::TrackElemType RCT2TrackTypeToOpenRCT2(
        OpenRCT2::RCT12::TrackElemType origTrackType, ride_type_t rideType, bool isFlatRide);
    OpenRCT2::TrackElemType RCT2TrackTypeToOpenRCT2(
        OpenRCT2::RCT12::TrackElemType origTrackType, OriginalRideClass originalClass);
    OpenRCT2::RCT12::TrackElemType OpenRCT2TrackTypeToRCT2(OpenRCT2::TrackElemType origTrackType);

    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<RCTObjectEntry> GetBestObjectEntryForSurface(std::string_view surface, std::string_view railings);

    static constexpr std::string_view DefaultTerrainSurfaces[] = {
        "rct2.terrain_surface.grass",        "rct2.terrain_surface.sand",        "rct2.terrain_surface.dirt",
        "rct2.terrain_surface.rock",         "rct2.terrain_surface.martian",     "rct2.terrain_surface.chequerboard",
        "rct2.terrain_surface.grass_clumps", "rct2.terrain_surface.ice",         "rct2.terrain_surface.grid_red",
        "rct2.terrain_surface.grid_yellow",  "rct2.terrain_surface.grid_purple", "rct2.terrain_surface.grid_green",
        "rct2.terrain_surface.sand_red",     "rct2.terrain_surface.sand_brown",
    };

    // Additional surface styles added to OpenRCT2 as a feature if RCT1 linked
    static constexpr std::string_view OpenRCT2HybridTerrainSurfaces[] = {
        "rct1aa.terrain_surface.roof_red",
        "rct1ll.terrain_surface.roof_grey",
        "rct1ll.terrain_surface.rust",
        "rct1ll.terrain_surface.wood",
    };

    static constexpr std::string_view DefaultTerrainEdges[] = {
        "rct2.terrain_edge.rock",
        "rct2.terrain_edge.wood_red",
        "rct2.terrain_edge.wood_black",
        "rct2.terrain_edge.ice",
    };

    // Additional surface edges added to OpenRCT2 as a feature if RCT1 was linked
    static constexpr std::string_view OpenRCT2HybridTerrainEdges[] = {
        "rct1.terrain_edge.brick",          "rct1.terrain_edge.iron",           "rct1aa.terrain_edge.grey",
        "rct1aa.terrain_edge.yellow",       "rct1aa.terrain_edge.red",          "rct1ll.terrain_edge.purple",
        "rct1ll.terrain_edge.green",        "rct1ll.terrain_edge.stone_brown",  "rct1ll.terrain_edge.stone_grey",
        "rct1ll.terrain_edge.skyscraper_a", "rct1ll.terrain_edge.skyscraper_b",
    };
} // namespace OpenRCT2::RCT2

std::vector<uint8_t> DecryptSea(const fs::path& path);
