diff --git a/src/openrct2-ui/windows/InstallTrack.cpp b/src/openrct2-ui/windows/InstallTrack.cpp index fe669ad2f3..e298eb6539 100644 --- a/src/openrct2-ui/windows/InstallTrack.cpp +++ b/src/openrct2-ui/windows/InstallTrack.cpp @@ -90,7 +90,7 @@ static rct_window_event_list window_install_track_events = { }; // clang-format on -static rct_track_td6* _trackDesign; +static std::unique_ptr _trackDesign; static std::string _trackPath; static std::string _trackName; static std::vector _trackDesignPreviewPixels; @@ -162,7 +162,6 @@ static void window_install_track_close(rct_window* w) _trackName.clear(); _trackDesignPreviewPixels.clear(); _trackDesignPreviewPixels.shrink_to_fit(); - track_design_dispose(_trackDesign); _trackDesign = nullptr; } @@ -245,7 +244,7 @@ static void window_install_track_paint(rct_window* w, rct_drawpixelinfo* dpi) y = w->y + widget->bottom - 12; // Warnings - rct_track_td6* td6 = _trackDesign; + const TrackDesign* td6 = _trackDesign.get(); if (td6->track_flags & TRACK_DESIGN_FLAG_SCENERY_UNAVAILABLE) { if (!gTrackDesignSceneryToggle) @@ -393,7 +392,8 @@ static void window_install_track_paint(rct_window* w, rct_drawpixelinfo* dpi) if (td6->cost != 0) { - gfx_draw_string_left(dpi, STR_TRACK_LIST_COST_AROUND, &td6->cost, COLOUR_BLACK, x, y); + set_format_arg(0, uint32_t, td6->cost); + gfx_draw_string_left(dpi, STR_TRACK_LIST_COST_AROUND, gCommonFormatArgs, COLOUR_BLACK, x, y); } } @@ -415,7 +415,7 @@ static void window_install_track_text_input(rct_window* w, rct_widgetindex widge static void window_install_track_update_preview() { - track_design_draw_preview(_trackDesign, _trackDesignPreviewPixels.data()); + track_design_draw_preview(_trackDesign.get(), _trackDesignPreviewPixels.data()); } static void window_install_track_design(rct_window* w) diff --git a/src/openrct2-ui/windows/LoadSave.cpp b/src/openrct2-ui/windows/LoadSave.cpp index 9da039e0fe..eb1e984f8e 100644 --- a/src/openrct2-ui/windows/LoadSave.cpp +++ b/src/openrct2-ui/windows/LoadSave.cpp @@ -1082,7 +1082,7 @@ static void window_loadsave_select(rct_window* w, const char* path) case (LOADSAVETYPE_SAVE | LOADSAVETYPE_TRACK): { path_set_extension(pathBuffer, "td6", sizeof(pathBuffer)); - int32_t success = track_design_save_to_file(pathBuffer); + int32_t success = 0; // track_design_save_to_file(pathBuffer); if (success) { diff --git a/src/openrct2-ui/windows/Ride.cpp b/src/openrct2-ui/windows/Ride.cpp index 983f1fafd5..926d781580 100644 --- a/src/openrct2-ui/windows/Ride.cpp +++ b/src/openrct2-ui/windows/Ride.cpp @@ -5357,7 +5357,7 @@ static void setup_scenery_selection(rct_window* w) gTrackDesignSaveRideIndex = (uint8_t)w->number; - track_design_save_init(); + // track_design_save_init(); gGamePaused |= GAME_PAUSED_SAVING_TRACK; gTrackDesignSaveMode = true; @@ -5379,7 +5379,7 @@ static void setup_scenery_selection(rct_window* w) */ static void window_ride_measurements_design_reset() { - track_design_save_reset_scenery(); + // track_design_save_reset_scenery(); } /** @@ -5388,7 +5388,7 @@ static void window_ride_measurements_design_reset() */ static void window_ride_measurements_design_select_nearby_scenery() { - track_design_save_select_nearby_scenery(gTrackDesignSaveRideIndex); + // track_design_save_select_nearby_scenery(gTrackDesignSaveRideIndex); } /** @@ -5409,7 +5409,7 @@ void window_ride_measurements_design_cancel() */ static void window_ride_measurements_design_save(rct_window* w) { - track_design_save((uint8_t)w->number); + // track_design_save((uint8_t)w->number); } /** @@ -5512,7 +5512,9 @@ static void window_ride_measurements_dropdown(rct_window* w, rct_widgetindex wid dropdownIndex = gDropdownHighlightedIndex; if (dropdownIndex == 0) - track_design_save((uint8_t)w->number); + { + } + // track_design_save((uint8_t)w->number); else setup_scenery_selection(w); } @@ -5549,8 +5551,8 @@ static void window_ride_measurements_tooldown(rct_window* w, rct_widgetindex wid case VIEWPORT_INTERACTION_ITEM_LARGE_SCENERY: case VIEWPORT_INTERACTION_ITEM_WALL: case VIEWPORT_INTERACTION_ITEM_FOOTPATH: - _collectTrackDesignScenery = !track_design_save_contains_tile_element(tileElement); - track_design_save_select_tile_element(interactionType, mapX, mapY, tileElement, _collectTrackDesignScenery); + //_collectTrackDesignScenery = !track_design_save_contains_tile_element(tileElement); + //track_design_save_select_tile_element(interactionType, mapX, mapY, tileElement, _collectTrackDesignScenery); break; } } @@ -5573,7 +5575,7 @@ static void window_ride_measurements_tooldrag(rct_window* w, rct_widgetindex wid case VIEWPORT_INTERACTION_ITEM_LARGE_SCENERY: case VIEWPORT_INTERACTION_ITEM_WALL: case VIEWPORT_INTERACTION_ITEM_FOOTPATH: - track_design_save_select_tile_element(interactionType, mapX, mapY, tileElement, _collectTrackDesignScenery); + //track_design_save_select_tile_element(interactionType, mapX, mapY, tileElement, _collectTrackDesignScenery); break; } } diff --git a/src/openrct2-ui/windows/TrackDesignPlace.cpp b/src/openrct2-ui/windows/TrackDesignPlace.cpp index 4ce4c2400c..9ff096d1f3 100644 --- a/src/openrct2-ui/windows/TrackDesignPlace.cpp +++ b/src/openrct2-ui/windows/TrackDesignPlace.cpp @@ -115,19 +115,19 @@ static int16_t _window_track_place_last_valid_y; static int16_t _window_track_place_last_valid_z; static money32 _window_track_place_last_cost; -static rct_track_td6* _trackDesign; +static std::unique_ptr _trackDesign; static void window_track_place_clear_provisional(); static int32_t window_track_place_get_base_z(int32_t x, int32_t y); static void window_track_place_attempt_placement( - rct_track_td6* td6, int32_t x, int32_t y, int32_t z, int32_t bl, money32* cost, ride_id_t* rideIndex); + TrackDesign* td6, int32_t x, int32_t y, int32_t z, int32_t bl, money32* cost, ride_id_t* rideIndex); static void window_track_place_clear_mini_preview(); -static void window_track_place_draw_mini_preview(rct_track_td6* td6); +static void window_track_place_draw_mini_preview(TrackDesign* td6); static void window_track_place_draw_mini_preview_track( - rct_track_td6* td6, int32_t pass, LocationXY16 origin, LocationXY16* min, LocationXY16* max); + TrackDesign* td6, int32_t pass, LocationXY16 origin, LocationXY16* min, LocationXY16* max); static void window_track_place_draw_mini_preview_maze( - rct_track_td6* td6, int32_t pass, LocationXY16 origin, LocationXY16* min, LocationXY16* max); + TrackDesign* td6, int32_t pass, LocationXY16 origin, LocationXY16* min, LocationXY16* max); static LocationXY16 draw_mini_preview_get_pixel_position(int16_t x, int16_t y); static bool draw_mini_preview_is_pixel_in_bounds(LocationXY16 pixel); static uint8_t* draw_mini_preview_get_pixel_ptr(LocationXY16 pixel); @@ -148,8 +148,8 @@ static void window_track_place_clear_mini_preview() */ rct_window* window_track_place_open(const track_design_file_ref* tdFileRef) { - rct_track_td6* td6 = track_design_open(tdFileRef->path); - if (td6 == nullptr) + _trackDesign = track_design_open(tdFileRef->path); + if (_trackDesign == nullptr) { return nullptr; } @@ -171,9 +171,7 @@ rct_window* window_track_place_open(const track_design_file_ref* tdFileRef) _currentTrackPieceDirection = (2 - get_current_rotation()) & 3; window_track_place_clear_mini_preview(); - window_track_place_draw_mini_preview(td6); - - _trackDesign = td6; + window_track_place_draw_mini_preview(_trackDesign.get()); return w; } @@ -192,7 +190,6 @@ static void window_track_place_close(rct_window* w) hide_gridlines(); _window_track_place_mini_preview.clear(); _window_track_place_mini_preview.shrink_to_fit(); - track_design_dispose(_trackDesign); _trackDesign = nullptr; } @@ -212,14 +209,14 @@ static void window_track_place_mouseup(rct_window* w, rct_widgetindex widgetInde _currentTrackPieceDirection = (_currentTrackPieceDirection + 1) & 3; window_invalidate(w); _window_track_place_last_x = -1; - window_track_place_draw_mini_preview(_trackDesign); + window_track_place_draw_mini_preview(_trackDesign.get()); break; case WIDX_MIRROR: - track_design_mirror(_trackDesign); + track_design_mirror(_trackDesign.get()); _currentTrackPieceDirection = (0 - _currentTrackPieceDirection) & 3; window_invalidate(w); _window_track_place_last_x = -1; - window_track_place_draw_mini_preview(_trackDesign); + window_track_place_draw_mini_preview(_trackDesign.get()); break; case WIDX_SELECT_DIFFERENT_DESIGN: window_close(w); @@ -267,7 +264,7 @@ static void window_track_place_toolupdate(rct_window* w, rct_widgetindex widgetI // Check if tool map position has changed since last update if (mapX == _window_track_place_last_x && mapY == _window_track_place_last_y) { - place_virtual_track(_trackDesign, PTD_OPERATION_DRAW_OUTLINES, true, GetOrAllocateRide(0), mapX, mapY, 0); + place_virtual_track(_trackDesign.get(), PTD_OPERATION_DRAW_OUTLINES, true, GetOrAllocateRide(0), mapX, mapY, 0); return; } @@ -284,7 +281,7 @@ static void window_track_place_toolupdate(rct_window* w, rct_widgetindex widgetI { ride_id_t rideIndex; uint16_t flags = GAME_COMMAND_FLAG_APPLY | GAME_COMMAND_FLAG_NO_SPEND | GAME_COMMAND_FLAG_GHOST; - window_track_place_attempt_placement(_trackDesign, mapX, mapY, mapZ, flags, &cost, &rideIndex); + window_track_place_attempt_placement(_trackDesign.get(), mapX, mapY, mapZ, flags, &cost, &rideIndex); if (cost != MONEY32_UNDEFINED) { _window_track_place_ride_index = rideIndex; @@ -306,7 +303,7 @@ static void window_track_place_toolupdate(rct_window* w, rct_widgetindex widgetI widget_invalidate(w, WIDX_PRICE); } - place_virtual_track(_trackDesign, PTD_OPERATION_DRAW_OUTLINES, true, GetOrAllocateRide(0), mapX, mapY, mapZ); + place_virtual_track(_trackDesign.get(), PTD_OPERATION_DRAW_OUTLINES, true, GetOrAllocateRide(0), mapX, mapY, mapZ); } /** @@ -335,7 +332,7 @@ static void window_track_place_tooldown(rct_window* w, rct_widgetindex widgetInd for (i = 0; i < 7; i++) { gDisableErrorWindowSound = true; - window_track_place_attempt_placement(_trackDesign, mapX, mapY, mapZ, 1, &cost, &rideIndex); + window_track_place_attempt_placement(_trackDesign.get(), mapX, mapY, mapZ, 1, &cost, &rideIndex); gDisableErrorWindowSound = false; if (cost != MONEY32_UNDEFINED) @@ -344,7 +341,7 @@ static void window_track_place_tooldown(rct_window* w, rct_widgetindex widgetInd audio_play_sound_at_location(SoundId::PlaceItem, mapX, mapY, mapZ); _currentRideIndex = rideIndex; - if (track_design_are_entrance_and_exit_placed()) + if (false)//track_design_are_entrance_and_exit_placed()) { auto intent = Intent(WC_RIDE); intent.putExtra(INTENT_EXTRA_RIDE_ID, rideIndex); @@ -386,12 +383,12 @@ static void window_track_place_toolabort(rct_window* w, rct_widgetindex widgetIn */ static void window_track_place_unknown14(rct_window* w) { - window_track_place_draw_mini_preview(_trackDesign); + window_track_place_draw_mini_preview(_trackDesign.get()); } static void window_track_place_invalidate(rct_window* w) { - window_track_place_draw_mini_preview(_trackDesign); + window_track_place_draw_mini_preview(_trackDesign.get()); } /** @@ -404,7 +401,7 @@ static void window_track_place_clear_provisional() { auto ride = get_ride(_window_track_place_ride_index); place_virtual_track( - _trackDesign, PTD_OPERATION_REMOVE_GHOST, true, ride, _window_track_place_last_valid_x, + _trackDesign.get(), PTD_OPERATION_REMOVE_GHOST, true, ride, _window_track_place_last_valid_x, _window_track_place_last_valid_y, _window_track_place_last_valid_z); _window_track_place_last_was_valid = false; } @@ -436,11 +433,11 @@ static int32_t window_track_place_get_base_z(int32_t x, int32_t y) if (tileElement->AsSurface()->GetWaterHeight() > 0) z = std::max(z, tileElement->AsSurface()->GetWaterHeight() << 4); - return z + place_virtual_track(_trackDesign, PTD_OPERATION_GET_PLACE_Z, true, GetOrAllocateRide(0), x, y, z); + return z + place_virtual_track(_trackDesign.get(), PTD_OPERATION_GET_PLACE_Z, true, GetOrAllocateRide(0), x, y, z); } static void window_track_place_attempt_placement( - rct_track_td6* td6, int32_t x, int32_t y, int32_t z, int32_t bl, money32* cost, ride_id_t* rideIndex) + TrackDesign* td6, int32_t x, int32_t y, int32_t z, int32_t bl, money32* cost, ride_id_t* rideIndex) { int32_t eax, ebx, ecx, edx, esi, edi, ebp; money32 result; @@ -451,7 +448,7 @@ static void window_track_place_attempt_placement( ecx = y; edi = z; - gActiveTrackDesign = _trackDesign; + gActiveTrackDesign = _trackDesign.get(); result = game_do_command_p(GAME_COMMAND_PLACE_TRACK_DESIGN, &eax, &ebx, &ecx, &edx, &esi, &edi, &ebp); gActiveTrackDesign = nullptr; @@ -467,7 +464,7 @@ static void window_track_place_attempt_placement( */ static void window_track_place_paint(rct_window* w, rct_drawpixelinfo* dpi) { - set_format_arg(0, char*, _trackDesign->name); + set_format_arg(0, char*, _trackDesign->name.get()); window_draw_widgets(w, dpi); // Draw mini tile preview @@ -493,7 +490,7 @@ static void window_track_place_paint(rct_window* w, rct_drawpixelinfo* dpi) * * rct2: 0x006D1845 */ -static void window_track_place_draw_mini_preview(rct_track_td6* td6) +static void window_track_place_draw_mini_preview(TrackDesign* td6) { window_track_place_clear_mini_preview(); @@ -521,16 +518,15 @@ static void window_track_place_draw_mini_preview(rct_track_td6* td6) } static void window_track_place_draw_mini_preview_track( - rct_track_td6* td6, int32_t pass, LocationXY16 origin, LocationXY16* min, LocationXY16* max) + TrackDesign* td6, int32_t pass, LocationXY16 origin, LocationXY16* min, LocationXY16* max) { uint8_t rotation = (_currentTrackPieceDirection + get_current_rotation()) & 3; - rct_td6_track_element* trackElement = td6->track_elements; const rct_preview_track** trackBlockArray = (ride_type_has_flag(td6->type, RIDE_TYPE_FLAG_HAS_TRACK)) ? TrackBlocks : FlatRideTrackBlocks; - while (trackElement->type != 255) + for (const auto& trackElement : td6->track_elements) { - int32_t trackType = trackElement->type; + int32_t trackType = trackElement.type; if (trackType == TRACK_ELEM_INVERTED_90_DEG_UP_TO_FLAT_QUARTER_LOOP) { trackType = 255; @@ -598,16 +594,14 @@ static void window_track_place_draw_mini_preview_track( origin.x += CoordsDirectionDelta[rotation].x; origin.y += CoordsDirectionDelta[rotation].y; } - trackElement++; } // Draw entrance and exit preview. - rct_td6_entrance_element* entrance = td6->entrance_elements; - for (; entrance->z != -1; entrance++) + for (const auto& entrance : td6->entrance_elements) { int16_t x = origin.x; int16_t y = origin.y; - map_offset_with_rotation(&x, &y, entrance->x, entrance->y, rotation); + map_offset_with_rotation(&x, &y, entrance.x, entrance.y, rotation); if (pass == 0) { @@ -624,7 +618,7 @@ static void window_track_place_draw_mini_preview_track( uint8_t* pixel = draw_mini_preview_get_pixel_ptr(pixelPosition); bool isExit = false; - if (entrance->direction & (1 << 7)) + if (entrance.direction & (1 << 7)) { isExit = true; } @@ -643,14 +637,13 @@ static void window_track_place_draw_mini_preview_track( } static void window_track_place_draw_mini_preview_maze( - rct_track_td6* td6, int32_t pass, LocationXY16 origin, LocationXY16* min, LocationXY16* max) + TrackDesign* td6, int32_t pass, LocationXY16 origin, LocationXY16* min, LocationXY16* max) { uint8_t rotation = (_currentTrackPieceDirection + get_current_rotation()) & 3; - rct_td6_maze_element* mazeElement = td6->maze_elements; - while (mazeElement->all != 0) + for (const auto& mazeElement : td6->maze_elements) { - int16_t x = mazeElement->x * 32; - int16_t y = mazeElement->y * 32; + int16_t x = mazeElement.x * 32; + int16_t y = mazeElement.y * 32; rotate_map_coordinates(&x, &y, rotation); x += origin.x; @@ -673,9 +666,9 @@ static void window_track_place_draw_mini_preview_maze( uint8_t colour = _PaletteIndexColourTrack; // Draw entrance and exit with different colours. - if (mazeElement->type == MAZE_ELEMENT_TYPE_ENTRANCE) + if (mazeElement.type == MAZE_ELEMENT_TYPE_ENTRANCE) colour = _PaletteIndexColourEntrance; - else if (mazeElement->type == MAZE_ELEMENT_TYPE_EXIT) + else if (mazeElement.type == MAZE_ELEMENT_TYPE_EXIT) colour = _PaletteIndexColourExit; for (int32_t i = 0; i < 4; i++) @@ -687,7 +680,6 @@ static void window_track_place_draw_mini_preview_maze( } } } - mazeElement++; } } diff --git a/src/openrct2-ui/windows/TrackList.cpp b/src/openrct2-ui/windows/TrackList.cpp index ba636eaa32..d632e8d492 100644 --- a/src/openrct2-ui/windows/TrackList.cpp +++ b/src/openrct2-ui/windows/TrackList.cpp @@ -105,7 +105,7 @@ static std::vector _trackDesigns; static utf8 _filterString[USER_STRING_MAX_LENGTH]; static std::vector _filteredTrackIds; static uint16_t _loadedTrackDesignIndex; -static rct_track_td6* _loadedTrackDesign; +static std::unique_ptr _loadedTrackDesign; static std::vector _trackDesignPreviewPixels; static void track_list_load_designs(ride_list_item item); @@ -208,7 +208,6 @@ static void window_track_list_filter_list() static void window_track_list_close(rct_window* w) { // Dispose track design and preview - track_design_dispose(_loadedTrackDesign); _loadedTrackDesign = nullptr; _trackDesignPreviewPixels.clear(); _trackDesignPreviewPixels.shrink_to_fit(); @@ -259,8 +258,7 @@ static void window_track_list_select(rct_window* w, int32_t listIndex) listIndex--; } - rct_track_td6* td6 = _loadedTrackDesign; - if (td6 != nullptr && (td6->track_flags & TRACK_DESIGN_FLAG_SCENERY_UNAVAILABLE)) + if (_loadedTrackDesign && (_loadedTrackDesign->track_flags & TRACK_DESIGN_FLAG_SCENERY_UNAVAILABLE)) { gTrackDesignSceneryToggle = true; } @@ -534,8 +532,7 @@ static void window_track_list_paint(rct_window* w, rct_drawpixelinfo* dpi) } } - rct_track_td6* td6 = _loadedTrackDesign; - if (td6 == nullptr) + if (!_loadedTrackDesign) { return; } @@ -556,14 +553,15 @@ static void window_track_list_paint(rct_window* w, rct_drawpixelinfo* dpi) y = w->y + widget->bottom - 12; // Warnings - if ((td6->track_flags & TRACK_DESIGN_FLAG_VEHICLE_UNAVAILABLE) && !(gScreenFlags & SCREEN_FLAGS_TRACK_MANAGER)) + if ((_loadedTrackDesign->track_flags & TRACK_DESIGN_FLAG_VEHICLE_UNAVAILABLE) + && !(gScreenFlags & SCREEN_FLAGS_TRACK_MANAGER)) { // Vehicle design not available gfx_draw_string_centred_clipped(dpi, STR_VEHICLE_DESIGN_UNAVAILABLE, nullptr, COLOUR_BLACK, x, y, 368); y -= SCROLLABLE_ROW_HEIGHT; } - if (td6->track_flags & TRACK_DESIGN_FLAG_SCENERY_UNAVAILABLE) + if (_loadedTrackDesign->track_flags & TRACK_DESIGN_FLAG_SCENERY_UNAVAILABLE) { if (!gTrackDesignSceneryToggle) { @@ -583,96 +581,96 @@ static void window_track_list_paint(rct_window* w, rct_drawpixelinfo* dpi) y = w->y + widget->bottom + 2; // Stats - fixed32_2dp rating = td6->excitement * 10; + fixed32_2dp rating = _loadedTrackDesign->excitement * 10; gfx_draw_string_left(dpi, STR_TRACK_LIST_EXCITEMENT_RATING, &rating, COLOUR_BLACK, x, y); y += LIST_ROW_HEIGHT; - rating = td6->intensity * 10; + rating = _loadedTrackDesign->intensity * 10; gfx_draw_string_left(dpi, STR_TRACK_LIST_INTENSITY_RATING, &rating, COLOUR_BLACK, x, y); y += LIST_ROW_HEIGHT; - rating = td6->nausea * 10; + rating = _loadedTrackDesign->nausea * 10; gfx_draw_string_left(dpi, STR_TRACK_LIST_NAUSEA_RATING, &rating, COLOUR_BLACK, x, y); y += LIST_ROW_HEIGHT + 4; // Information for tracked rides. - if (ride_type_has_flag(td6->type, RIDE_TYPE_FLAG_HAS_TRACK)) + if (ride_type_has_flag(_loadedTrackDesign->type, RIDE_TYPE_FLAG_HAS_TRACK)) { - if (td6->type != RIDE_TYPE_MAZE) + if (_loadedTrackDesign->type != RIDE_TYPE_MAZE) { - if (td6->type == RIDE_TYPE_MINI_GOLF) + if (_loadedTrackDesign->type == RIDE_TYPE_MINI_GOLF) { // Holes - uint16_t holes = td6->holes & 0x1F; + uint16_t holes = _loadedTrackDesign->holes & 0x1F; gfx_draw_string_left(dpi, STR_HOLES, &holes, COLOUR_BLACK, x, y); y += LIST_ROW_HEIGHT; } else { // Maximum speed - uint16_t speed = ((td6->max_speed << 16) * 9) >> 18; + uint16_t speed = ((_loadedTrackDesign->max_speed << 16) * 9) >> 18; gfx_draw_string_left(dpi, STR_MAX_SPEED, &speed, COLOUR_BLACK, x, y); y += LIST_ROW_HEIGHT; // Average speed - speed = ((td6->average_speed << 16) * 9) >> 18; + speed = ((_loadedTrackDesign->average_speed << 16) * 9) >> 18; gfx_draw_string_left(dpi, STR_AVERAGE_SPEED, &speed, COLOUR_BLACK, x, y); y += LIST_ROW_HEIGHT; } // Ride length set_format_arg(0, rct_string_id, STR_RIDE_LENGTH_ENTRY); - set_format_arg(2, uint16_t, td6->ride_length); + set_format_arg(2, uint16_t, _loadedTrackDesign->ride_length); gfx_draw_string_left_clipped(dpi, STR_TRACK_LIST_RIDE_LENGTH, gCommonFormatArgs, COLOUR_BLACK, x, y, 214); y += LIST_ROW_HEIGHT; } - if (ride_type_has_flag(td6->type, RIDE_TYPE_FLAG_HAS_G_FORCES)) + if (ride_type_has_flag(_loadedTrackDesign->type, RIDE_TYPE_FLAG_HAS_G_FORCES)) { // Maximum positive vertical Gs - int32_t gForces = td6->max_positive_vertical_g * 32; + int32_t gForces = _loadedTrackDesign->max_positive_vertical_g * 32; gfx_draw_string_left(dpi, STR_MAX_POSITIVE_VERTICAL_G, &gForces, COLOUR_BLACK, x, y); y += LIST_ROW_HEIGHT; // Maximum negative vertical Gs - gForces = td6->max_negative_vertical_g * 32; + gForces = _loadedTrackDesign->max_negative_vertical_g * 32; gfx_draw_string_left(dpi, STR_MAX_NEGATIVE_VERTICAL_G, &gForces, COLOUR_BLACK, x, y); y += LIST_ROW_HEIGHT; // Maximum lateral Gs - gForces = td6->max_lateral_g * 32; + gForces = _loadedTrackDesign->max_lateral_g * 32; gfx_draw_string_left(dpi, STR_MAX_LATERAL_G, &gForces, COLOUR_BLACK, x, y); y += LIST_ROW_HEIGHT; // If .TD6 - if (td6->version_and_colour_scheme / 4 >= 2) + if (_loadedTrackDesign->version_and_colour_scheme / 4 >= 2) { - if (td6->total_air_time != 0) + if (_loadedTrackDesign->total_air_time != 0) { // Total air time - int32_t airTime = td6->total_air_time * 25; + int32_t airTime = _loadedTrackDesign->total_air_time * 25; gfx_draw_string_left(dpi, STR_TOTAL_AIR_TIME, &airTime, COLOUR_BLACK, x, y); y += LIST_ROW_HEIGHT; } } } - if (ride_type_has_flag(td6->type, RIDE_TYPE_FLAG_HAS_DROPS)) + if (ride_type_has_flag(_loadedTrackDesign->type, RIDE_TYPE_FLAG_HAS_DROPS)) { // Drops - uint16_t drops = td6->drops & 0x3F; + uint16_t drops = _loadedTrackDesign->drops & 0x3F; gfx_draw_string_left(dpi, STR_DROPS, &drops, COLOUR_BLACK, x, y); y += LIST_ROW_HEIGHT; // Drop height is multiplied by 0.75 - uint16_t highestDropHeight = (td6->highest_drop_height * 3) / 4; + uint16_t highestDropHeight = (_loadedTrackDesign->highest_drop_height * 3) / 4; gfx_draw_string_left(dpi, STR_HIGHEST_DROP_HEIGHT, &highestDropHeight, COLOUR_BLACK, x, y); y += LIST_ROW_HEIGHT; } - if (td6->type != RIDE_TYPE_MINI_GOLF) + if (_loadedTrackDesign->type != RIDE_TYPE_MINI_GOLF) { - uint16_t inversions = td6->inversions & 0x1F; + uint16_t inversions = _loadedTrackDesign->inversions & 0x1F; if (inversions != 0) { // Inversions @@ -683,18 +681,19 @@ static void window_track_list_paint(rct_window* w, rct_drawpixelinfo* dpi) y += 4; } - if (td6->space_required_x != 0xFF) + if (_loadedTrackDesign->space_required_x != 0xFF) { // Space required - set_format_arg(0, uint16_t, td6->space_required_x); - set_format_arg(2, uint16_t, td6->space_required_y); + set_format_arg(0, uint16_t, _loadedTrackDesign->space_required_x); + set_format_arg(2, uint16_t, _loadedTrackDesign->space_required_y); gfx_draw_string_left(dpi, STR_TRACK_LIST_SPACE_REQUIRED, gCommonFormatArgs, COLOUR_BLACK, x, y); y += LIST_ROW_HEIGHT; } - if (td6->cost != 0) + if (_loadedTrackDesign->cost != 0) { - gfx_draw_string_left(dpi, STR_TRACK_LIST_COST_AROUND, &td6->cost, COLOUR_BLACK, x, y); + set_format_arg(0, uint32_t, _loadedTrackDesign->cost); + gfx_draw_string_left(dpi, STR_TRACK_LIST_COST_AROUND, gCommonFormatArgs, COLOUR_BLACK, x, y); } } @@ -800,14 +799,10 @@ static void track_list_load_designs(ride_list_item item) static bool track_list_load_design_for_preview(utf8* path) { - // Dispose currently loaded track - track_design_dispose(_loadedTrackDesign); - _loadedTrackDesign = nullptr; - _loadedTrackDesign = track_design_open(path); if (_loadedTrackDesign != nullptr) { - track_design_draw_preview(_loadedTrackDesign, _trackDesignPreviewPixels.data()); + track_design_draw_preview(_loadedTrackDesign.get(), _trackDesignPreviewPixels.data()); return true; } return false; diff --git a/src/openrct2/TrackImporter.cpp b/src/openrct2/TrackImporter.cpp new file mode 100644 index 0000000000..62dfd20369 --- /dev/null +++ b/src/openrct2/TrackImporter.cpp @@ -0,0 +1,38 @@ +/***************************************************************************** + * Copyright (c) 2014-2019 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 "TrackImporter.h" + +#include "core/Path.hpp" +#include "core/String.hpp" + +#include + +namespace TrackImporter +{ + std::unique_ptr Create(const std::string& hintPath) + { + std::unique_ptr trackImporter; + std::string extension = Path::GetExtension(hintPath); + if (ExtensionIsRCT1(extension)) + { + //trackImporter = CreateTD4(); + } + else + { + trackImporter = CreateTD6(); + } + return trackImporter; + } + + bool ExtensionIsRCT1(const std::string& extension) + { + return String::Equals(extension, ".td4", true); + } +} // namespace ParkImporter diff --git a/src/openrct2/TrackImporter.h b/src/openrct2/TrackImporter.h new file mode 100644 index 0000000000..01654939a3 --- /dev/null +++ b/src/openrct2/TrackImporter.h @@ -0,0 +1,42 @@ +/***************************************************************************** + * Copyright (c) 2014-2019 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 +#include +#include +#include "ride/TrackDesign.h" +#include "core/IStream.hpp" + + +/** + * Interface to import scenarios and saved games. + */ +interface ITrackImporter +{ +public: + virtual ~ITrackImporter() = default; + + virtual bool Load(const utf8* path) abstract; + virtual bool LoadFromStream( + IStream * stream) abstract; + + virtual std::unique_ptr Import() abstract; +}; + +namespace TrackImporter +{ + std::unique_ptr Create(const std::string& hintPath); + //std::unique_ptr CreateTD4(); + std::unique_ptr CreateTD6(); + + bool ExtensionIsRCT1(const std::string& extension); +} // namespace TrackImporter diff --git a/src/openrct2/paint/tile_element/Paint.LargeScenery.cpp b/src/openrct2/paint/tile_element/Paint.LargeScenery.cpp index 75489de1eb..64ff543d4b 100644 --- a/src/openrct2/paint/tile_element/Paint.LargeScenery.cpp +++ b/src/openrct2/paint/tile_element/Paint.LargeScenery.cpp @@ -240,13 +240,13 @@ void large_scenery_paint(paint_session* session, uint8_t direction, uint16_t hei LocationXYZ16 boxoffset; if (gTrackDesignSaveMode) { - if (!track_design_save_contains_tile_element(tileElement)) - { - sequenceNum = SPRITE_ID_PALETTE_COLOUR_1(PALETTE_46); - image_id &= 0x7FFFF; - dword_F4387C = sequenceNum; - image_id |= dword_F4387C; - } + //if (!track_design_save_contains_tile_element(tileElement)) + //{ + // sequenceNum = SPRITE_ID_PALETTE_COLOUR_1(PALETTE_46); + // image_id &= 0x7FFFF; + // dword_F4387C = sequenceNum; + // image_id |= dword_F4387C; + //} } if (tileElement->IsGhost()) { diff --git a/src/openrct2/paint/tile_element/Paint.Path.cpp b/src/openrct2/paint/tile_element/Paint.Path.cpp index 37d6c7b8f2..74611a68bc 100644 --- a/src/openrct2/paint/tile_element/Paint.Path.cpp +++ b/src/openrct2/paint/tile_element/Paint.Path.cpp @@ -822,10 +822,10 @@ void path_paint(paint_session* session, uint16_t height, const TileElement* tile } } - if (!track_design_save_contains_tile_element(tile_element)) - { - imageFlags = SPRITE_ID_PALETTE_COLOUR_1(PALETTE_46); - } + //if (!track_design_save_contains_tile_element(tile_element)) + //{ + // imageFlags = SPRITE_ID_PALETTE_COLOUR_1(PALETTE_46); + //} } if (session->ViewFlags & VIEWPORT_FLAG_HIGHLIGHT_PATH_ISSUES) diff --git a/src/openrct2/paint/tile_element/Paint.SmallScenery.cpp b/src/openrct2/paint/tile_element/Paint.SmallScenery.cpp index 5c204c352f..8d247b452a 100644 --- a/src/openrct2/paint/tile_element/Paint.SmallScenery.cpp +++ b/src/openrct2/paint/tile_element/Paint.SmallScenery.cpp @@ -48,10 +48,10 @@ void scenery_paint(paint_session* session, uint8_t direction, int32_t height, co const int32_t rotation = session->CurrentRotation; if (gTrackDesignSaveMode) { - if (!track_design_save_contains_tile_element(tileElement)) - { - baseImageid = SPRITE_ID_PALETTE_COLOUR_1(PALETTE_46); - } + //if (!track_design_save_contains_tile_element(tileElement)) + //{ + // baseImageid = SPRITE_ID_PALETTE_COLOUR_1(PALETTE_46); + //} } if (tileElement->IsGhost()) { diff --git a/src/openrct2/paint/tile_element/Paint.Wall.cpp b/src/openrct2/paint/tile_element/Paint.Wall.cpp index 15fe860495..81d21131ab 100644 --- a/src/openrct2/paint/tile_element/Paint.Wall.cpp +++ b/src/openrct2/paint/tile_element/Paint.Wall.cpp @@ -192,10 +192,10 @@ void fence_paint(paint_session* session, uint8_t direction, int32_t height, cons uint32_t dword_141F710 = 0; if (gTrackDesignSaveMode || (session->ViewFlags & VIEWPORT_FLAG_HIGHLIGHT_PATH_ISSUES)) { - if (!track_design_save_contains_tile_element(tile_element)) - { - dword_141F710 = SPRITE_ID_PALETTE_COLOUR_1(PALETTE_46); - } + //if (!track_design_save_contains_tile_element(tile_element)) + //{ + // dword_141F710 = SPRITE_ID_PALETTE_COLOUR_1(PALETTE_46); + //} } if (tile_element->IsGhost()) diff --git a/src/openrct2/rct12/RCT12.h b/src/openrct2/rct12/RCT12.h index 055c25e16d..a34b25df7b 100644 --- a/src/openrct2/rct12/RCT12.h +++ b/src/openrct2/rct12/RCT12.h @@ -49,6 +49,14 @@ constexpr uint16_t const RCT12_MAX_INVERSIONS = 31; constexpr uint16_t const RCT12_MAX_GOLF_HOLES = 31; constexpr uint16_t const RCT12_MAX_HELICES = 31; +enum class RCT12TrackDesignVersion : uint8_t +{ + TD4, + TD4_AA, + TD6, + unknown +}; + #pragma pack(push, 1) struct rct12_award diff --git a/src/openrct2/rct12/SawyerChunkReader.cpp b/src/openrct2/rct12/SawyerChunkReader.cpp index ca5d6e16d6..aac2d990bc 100644 --- a/src/openrct2/rct12/SawyerChunkReader.cpp +++ b/src/openrct2/rct12/SawyerChunkReader.cpp @@ -102,6 +102,43 @@ std::shared_ptr SawyerChunkReader::ReadChunk() } } +std::shared_ptr SawyerChunkReader::ReadChunkTrack() +{ + uint64_t originalPosition = _stream->GetPosition(); + try + { + // Remove 4 as we don't want to touch the checksum at the end of the file + auto compressedDataLength64 = _stream->GetLength() - _stream->GetPosition() - 4; + if (compressedDataLength64 < 0 || compressedDataLength64 > std::numeric_limits::max()) + { + throw SawyerChunkException(EXCEPTION_MSG_ZERO_SIZED_CHUNK); + } + uint32_t compressedDataLength = compressedDataLength64; + auto compressedData = std::make_unique(compressedDataLength); + + if (_stream->TryRead(compressedData.get(), compressedDataLength) != compressedDataLength) + { + throw SawyerChunkException(EXCEPTION_MSG_CORRUPT_CHUNK_SIZE); + } + + auto buffer = (uint8_t*)AllocateLargeTempBuffer(); + sawyercoding_chunk_header header{ CHUNK_ENCODING_RLE, compressedDataLength }; + size_t uncompressedLength = DecodeChunk(buffer, MAX_UNCOMPRESSED_CHUNK_SIZE, compressedData.get(), header); + if (uncompressedLength == 0) + { + throw SawyerChunkException(EXCEPTION_MSG_ZERO_SIZED_CHUNK); + } + buffer = (uint8_t*)FinaliseLargeTempBuffer(buffer, uncompressedLength); + return std::make_shared(SAWYER_ENCODING::RLE, buffer, uncompressedLength); + } + catch (const std::exception&) + { + // Rewind stream back to original position + _stream->SetPosition(originalPosition); + throw; + } +} + void SawyerChunkReader::ReadChunk(void* dst, size_t length) { auto chunk = ReadChunk(); diff --git a/src/openrct2/rct12/SawyerChunkReader.h b/src/openrct2/rct12/SawyerChunkReader.h index 57ca099d5c..2d68a1c501 100644 --- a/src/openrct2/rct12/SawyerChunkReader.h +++ b/src/openrct2/rct12/SawyerChunkReader.h @@ -40,6 +40,11 @@ public: */ std::shared_ptr ReadChunk(); + /** + * As above but for chunks without a header + */ + std::shared_ptr ReadChunkTrack(); + /** * Reads the next chunk from the stream and copies it directly to the * destination buffer. If the chunk is larger than length, only length diff --git a/src/openrct2/rct12/SawyerEncoding.cpp b/src/openrct2/rct12/SawyerEncoding.cpp index d9ac604cbc..e14a0475b4 100644 --- a/src/openrct2/rct12/SawyerEncoding.cpp +++ b/src/openrct2/rct12/SawyerEncoding.cpp @@ -10,7 +10,7 @@ #include "SawyerEncoding.h" #include "../core/IStream.hpp" - +#include "RCT12.h" #include namespace SawyerEncoding @@ -57,4 +57,48 @@ namespace SawyerEncoding return false; } } + + // Returns version number + RCT12TrackDesignVersion ValidateTrackChecksum(IStream* stream) + { + uint64_t initialPosition = stream->GetPosition(); + uint64_t dataSize = stream->GetLength() - initialPosition; + + if (dataSize < 4) + { + return RCT12TrackDesignVersion::unknown; + } + dataSize -= 4; + + try + { + auto data{stream->ReadArray(dataSize) }; + uint32_t checksum = 0; + for (size_t i = 0; i < dataSize; i++, ++data) + { + uint8_t newByte = ((checksum & 0xFF) + *data) & 0xFF; + checksum = (checksum & 0xFFFFFF00) + newByte; + checksum = rol32(checksum, 3); + } + + uint32_t fileChecksum = stream->ReadValue(); + // Rewind back to original position + stream->SetPosition(initialPosition); + + if (checksum - 0x1D4C1 == fileChecksum) + return RCT12TrackDesignVersion::TD6; + else if (checksum - 0x1A67C == fileChecksum) + return RCT12TrackDesignVersion::TD4_AA; + else if (checksum - 0x1A650 == fileChecksum) + return RCT12TrackDesignVersion::TD4; + else + return RCT12TrackDesignVersion::unknown; + } + catch (const std::exception&) + { + // Rewind back to original position + stream->SetPosition(initialPosition); + return RCT12TrackDesignVersion::unknown; + } + } } // namespace SawyerEncoding diff --git a/src/openrct2/rct12/SawyerEncoding.h b/src/openrct2/rct12/SawyerEncoding.h index aa46c5aee2..8a580b4a5d 100644 --- a/src/openrct2/rct12/SawyerEncoding.h +++ b/src/openrct2/rct12/SawyerEncoding.h @@ -13,7 +13,10 @@ interface IStream; +enum class RCT12TrackDesignVersion: uint8_t; + namespace SawyerEncoding { bool ValidateChecksum(IStream* stream); + RCT12TrackDesignVersion ValidateTrackChecksum(IStream* stream); } diff --git a/src/openrct2/rct2/T6Importer.cpp b/src/openrct2/rct2/T6Importer.cpp new file mode 100644 index 0000000000..0da4993fe8 --- /dev/null +++ b/src/openrct2/rct2/T6Importer.cpp @@ -0,0 +1,181 @@ +/***************************************************************************** + * Copyright (c) 2014-2019 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 "../TrackImporter.h" +#include "../config/Config.h" +#include "../core/FileStream.hpp" +#include "../core/MemoryStream.h" +#include "../core/Path.hpp" +#include "../core/String.hpp" +#include "../rct12/SawyerChunkReader.h" +#include "../rct12/SawyerEncoding.h" +#include "../ride/Ride.h" +#include "../ride/TrackDesign.h" +#include "../ride/TrackDesignRepository.h" + +/** + * Class to import RollerCoaster Tycoon 2 track designs (*.TD6). + */ +class TD6Importer final : public ITrackImporter +{ +private: + MemoryStream _stream; + std::unique_ptr name; + +public: + TD6Importer() + { + } + + bool Load(const utf8* path) override + { + const utf8* extension = Path::GetExtension(path); + if (String::Equals(extension, ".td6", true)) + { + name.reset(String::Duplicate(GetNameFromTrackPath(path).c_str())); + auto fs = FileStream(path, FILE_MODE_OPEN); + return LoadFromStream(&fs); + } + else + { + throw std::runtime_error("Invalid RCT2 track extension."); + } + } + + bool LoadFromStream(IStream* stream) override + { + if (!gConfigGeneral.allow_loading_with_incorrect_checksum + && SawyerEncoding::ValidateTrackChecksum(stream) != RCT12TrackDesignVersion::TD6) + { + throw IOException("Invalid checksum."); + } + + auto chunkReader = SawyerChunkReader(stream); + auto data = chunkReader.ReadChunkTrack(); + _stream.WriteArray(reinterpret_cast(data->GetData()), data->GetLength()); + _stream.SetPosition(0); + return true; + } + + std::unique_ptr Import() + { + std::unique_ptr td = std::make_unique(); + + rct_track_td6 td6{}; + // Rework td6 so that it is just the fields + _stream.Read(&td6, 0xA3); + + td->type = td6.type; // 0x00 + td->vehicle_type = td6.vehicle_type; + + td->cost = 0; + td->flags = td6.flags; + td->ride_mode = td6.ride_mode; + td->track_flags = 0; + td->colour_scheme = td6.version_and_colour_scheme & 0x3; + for (auto i = 0; i < RCT2_MAX_CARS_PER_TRAIN; ++i) + { + td->vehicle_colours[i] = td6.vehicle_colours[i]; + td->vehicle_additional_colour[i] = td6.vehicle_additional_colour[i]; + } + td->entrance_style = td6.entrance_style; + td->total_air_time = td6.total_air_time; + td->depart_flags = td6.depart_flags; + td->number_of_trains = td6.number_of_trains; + td->number_of_cars_per_train = td6.number_of_cars_per_train; + td->min_waiting_time = td6.min_waiting_time; + td->max_waiting_time = td6.max_waiting_time; + td->operation_setting = td6.operation_setting; + td->max_speed = td6.max_speed; + td->average_speed = td6.average_speed; + td->ride_length = td6.ride_length; + td->max_positive_vertical_g = td6.max_positive_vertical_g; + td->max_negative_vertical_g = td6.max_negative_vertical_g; + td->max_lateral_g = td6.max_lateral_g; + // Only one sort this out + td->inversions = td6.inversions; + td->holes = td6.holes; + + td->drops = td6.drops; + td->highest_drop_height = td6.highest_drop_height; + td->excitement = td6.excitement; + td->intensity = td6.intensity; + td->nausea = td6.nausea; + td->upkeep_cost = td6.upkeep_cost; + for (auto i = 0; i < RCT12_NUM_COLOUR_SCHEMES; ++i) + { + td->track_spine_colour[i] = td6.track_spine_colour[i]; + td->track_rail_colour[i] = td6.track_rail_colour[i]; + td->track_support_colour[i] = td6.track_support_colour[i]; + } + td->flags2 = td6.flags2; + td->vehicle_object = td6.vehicle_object; + td->space_required_x = td6.space_required_x; + td->space_required_y = td6.space_required_y; + td->lift_hill_speed = td6.lift_hill_speed_num_circuits & 0b00011111; + td->num_circuits = td6.lift_hill_speed_num_circuits >> 5; + + auto version = static_cast((td6.version_and_colour_scheme >> 2) & 3); + if (version != RCT12TrackDesignVersion::TD6) + { + log_error("Unsupported track design."); + return nullptr; + } + + td->operation_setting = std::min(td->operation_setting, RideProperties[td->type].max_value); + + if (td->type == RIDE_TYPE_MAZE) + { + rct_td6_maze_element mazeElement{}; + mazeElement.all = !0; + while (mazeElement.all != 0) + { + _stream.Read(&mazeElement, sizeof(rct_td6_maze_element)); + if (mazeElement.all != 0) + { + td->maze_elements.push_back(mazeElement); + } + } + } + else + { + rct_td6_track_element trackElement{}; + for (uint8_t endFlag = _stream.ReadValue(); endFlag != 0xFF; endFlag = _stream.ReadValue()) + { + _stream.SetPosition(_stream.GetPosition() - 1); + _stream.Read(&trackElement, sizeof(rct_td6_track_element)); + td->track_elements.push_back(trackElement); + } + + rct_td6_entrance_element entranceElement{}; + for (uint8_t endFlag = _stream.ReadValue(); endFlag != 0xFF; endFlag = _stream.ReadValue()) + { + _stream.SetPosition(_stream.GetPosition() - 1); + _stream.Read(&entranceElement, sizeof(rct_td6_entrance_element)); + td->entrance_elements.push_back(entranceElement); + } + } + + for (uint8_t endFlag = _stream.ReadValue(); endFlag != 0xFF; endFlag = _stream.ReadValue()) + { + _stream.SetPosition(_stream.GetPosition() - 1); + rct_td6_scenery_element sceneryElement{}; + _stream.Read(&sceneryElement, sizeof(rct_td6_scenery_element)); + td->scenery_elements.push_back(sceneryElement); + } + + td->name = std::move(name); + return td; + } +}; + +std::unique_ptr TrackImporter::CreateTD6() +{ + return std::make_unique(); +} diff --git a/src/openrct2/ride/TrackDesign.cpp b/src/openrct2/ride/TrackDesign.cpp index d7832fe44a..570a0f4851 100644 --- a/src/openrct2/ride/TrackDesign.cpp +++ b/src/openrct2/ride/TrackDesign.cpp @@ -12,6 +12,7 @@ #include "../Cheats.h" #include "../Game.h" #include "../OpenRCT2.h" +#include "../TrackImporter.h" #include "../actions/FootpathPlaceFromTrackAction.hpp" #include "../actions/FootpathRemoveAction.hpp" #include "../actions/LargeSceneryPlaceAction.hpp" @@ -69,7 +70,7 @@ struct map_backup uint8_t current_rotation; }; -rct_track_td6* gActiveTrackDesign; +TrackDesign* gActiveTrackDesign; bool gTrackDesignSceneryToggle; LocationXYZ16 gTrackPreviewMin; LocationXYZ16 gTrackPreviewMax; @@ -100,380 +101,392 @@ static void td6_reset_trailing_elements(rct_track_td6* td6); static void td6_set_element_helper_pointers(rct_track_td6* td6, bool clearScenery); -rct_track_td6* track_design_open(const utf8* path) +std::unique_ptr track_design_open(const utf8* path) { - log_verbose("track_design_open(\"%s\")", path); - + auto trackImporter = TrackImporter::Create(path); + trackImporter->Load(path); try { - auto buffer = File::ReadAllBytes(path); - if (!sawyercoding_validate_track_checksum(buffer.data(), buffer.size())) - { - log_error("Track checksum failed. %s", path); - return nullptr; - } - - // Decode the track data - uint8_t* decoded = (uint8_t*)malloc(0x10000); - size_t decodedLength = sawyercoding_decode_td6(buffer.data(), decoded, buffer.size()); - decoded = (uint8_t*)realloc(decoded, decodedLength); - if (decoded == nullptr) - { - log_error("failed to realloc"); - } - else - { - rct_track_td6* td6 = track_design_open_from_buffer(decoded, decodedLength); - free(decoded); - - if (td6 != nullptr) - { - td6->name = String::Duplicate(GetNameFromTrackPath(path).c_str()); - return td6; - } - } + auto track2 = trackImporter->Import(); + log_error("Test %d", track2->type); + return track2; } catch (const std::exception& e) { log_error("Unable to load track design: %s", e.what()); } + log_verbose("track_design_open(\"%s\")", path); return nullptr; + //try + //{ + // auto buffer = File::ReadAllBytes(path); + // if (!sawyercoding_validate_track_checksum(buffer.data(), buffer.size())) + // { + // log_error("Track checksum failed. %s", path); + // return nullptr; + // } + + // // Decode the track data + // uint8_t* decoded = (uint8_t*)malloc(0x10000); + // size_t decodedLength = sawyercoding_decode_td6(buffer.data(), decoded, buffer.size()); + // decoded = (uint8_t*)realloc(decoded, decodedLength); + // if (decoded == nullptr) + // { + // log_error("failed to realloc"); + // } + // else + // { + // rct_track_td6* td6 = track_design_open_from_buffer(decoded, decodedLength); + // free(decoded); + + // if (td6 != nullptr) + // { + // td6->name = String::Duplicate(GetNameFromTrackPath(path).c_str()); + // return td6; + // } + // } + //} + //catch (const std::exception& e) + //{ + // log_error("Unable to load track design: %s", e.what()); + //} + //return nullptr; } -static rct_track_td6* track_design_open_from_td4(uint8_t* src, size_t srcLength) -{ - rct_track_td4* td4 = (rct_track_td4*)calloc(1, sizeof(rct_track_td4)); - if (td4 == nullptr) - { - log_error("Unable to allocate memory for TD4 data."); - SafeFree(td4); - return nullptr; - } - - uint8_t version = (src[7] >> 2) & 3; - if (version == 0) - { - std::memcpy(td4, src, 0x38); - td4->elementsSize = srcLength - 0x38; - td4->elements = malloc(td4->elementsSize); - if (td4->elements == nullptr) - { - log_error("Unable to allocate memory for TD4 element data."); - SafeFree(td4); - return nullptr; - } - std::memcpy(td4->elements, src + 0x38, td4->elementsSize); - } - else if (version == 1) - { - std::memcpy(td4, src, 0xC4); - td4->elementsSize = srcLength - 0xC4; - td4->elements = malloc(td4->elementsSize); - if (td4->elements == nullptr) - { - log_error("Unable to allocate memory for TD4 element data."); - SafeFree(td4); - return nullptr; - } - std::memcpy(td4->elements, src + 0xC4, td4->elementsSize); - } - else - { - log_error("Unsupported track design."); - SafeFree(td4); - return nullptr; - } - - rct_track_td6* td6 = (rct_track_td6*)calloc(1, sizeof(rct_track_td6)); - if (td6 == nullptr) - { - log_error("Unable to allocate memory for TD6 data."); - SafeFree(td4); - return nullptr; - } - - td6->type = RCT1::GetRideType(td4->type); - - // All TD4s that use powered launch use the type that doesn't pass the station. - td6->ride_mode = td4->mode; - if (td4->mode == RCT1_RIDE_MODE_POWERED_LAUNCH) - { - td6->ride_mode = RIDE_MODE_POWERED_LAUNCH; - } - - // Convert RCT1 vehicle type to RCT2 vehicle type. Intialise with an string consisting of 8 spaces. - rct_object_entry vehicleObject = { 0x80, " " }; - if (td4->type == RIDE_TYPE_MAZE) - { - const char* name = RCT1::GetRideTypeObject(td4->type); - assert(name != nullptr); - std::memcpy(vehicleObject.name, name, std::min(String::SizeOf(name), (size_t)8)); - } - else - { - const char* name = RCT1::GetVehicleObject(td4->vehicle_type); - assert(name != nullptr); - std::memcpy(vehicleObject.name, name, std::min(String::SizeOf(name), (size_t)8)); - } - std::memcpy(&td6->vehicle_object, &vehicleObject, sizeof(rct_object_entry)); - td6->vehicle_type = td4->vehicle_type; - - td6->flags = td4->flags; - td6->version_and_colour_scheme = td4->version_and_colour_scheme; - - // Vehicle colours - for (int32_t i = 0; i < RCT1_MAX_TRAINS_PER_RIDE; i++) - { - // RCT1 had no third colour - RCT1::RCT1VehicleColourSchemeCopyDescriptor colourSchemeCopyDescriptor = RCT1::GetColourSchemeCopyDescriptor( - td4->vehicle_type); - if (colourSchemeCopyDescriptor.colour1 == COPY_COLOUR_1) - { - td6->vehicle_colours[i].body_colour = RCT1::GetColour(td4->vehicle_colours[i].body_colour); - } - else if (colourSchemeCopyDescriptor.colour1 == COPY_COLOUR_2) - { - td6->vehicle_colours[i].body_colour = RCT1::GetColour(td4->vehicle_colours[i].trim_colour); - } - else - { - td6->vehicle_colours[i].body_colour = colourSchemeCopyDescriptor.colour1; - } - - if (colourSchemeCopyDescriptor.colour2 == COPY_COLOUR_1) - { - td6->vehicle_colours[i].trim_colour = RCT1::GetColour(td4->vehicle_colours[i].body_colour); - } - else if (colourSchemeCopyDescriptor.colour2 == COPY_COLOUR_2) - { - td6->vehicle_colours[i].trim_colour = RCT1::GetColour(td4->vehicle_colours[i].trim_colour); - } - else - { - td6->vehicle_colours[i].trim_colour = colourSchemeCopyDescriptor.colour2; - } - - if (colourSchemeCopyDescriptor.colour3 == COPY_COLOUR_1) - { - td6->vehicle_additional_colour[i] = RCT1::GetColour(td4->vehicle_colours[i].body_colour); - } - else if (colourSchemeCopyDescriptor.colour3 == COPY_COLOUR_2) - { - td6->vehicle_additional_colour[i] = RCT1::GetColour(td4->vehicle_colours[i].trim_colour); - } - else - { - td6->vehicle_additional_colour[i] = colourSchemeCopyDescriptor.colour3; - } - } - // Set remaining vehicles to same colour as first vehicle - for (int32_t i = RCT1_MAX_TRAINS_PER_RIDE; i < MAX_VEHICLES_PER_RIDE; i++) - { - td6->vehicle_colours[i] = td6->vehicle_colours[0]; - td6->vehicle_additional_colour[i] = td6->vehicle_additional_colour[0]; - } - - // Track colours - if (version == 0) - { - for (int32_t i = 0; i < NUM_COLOUR_SCHEMES; i++) - { - td6->track_spine_colour[i] = RCT1::GetColour(td4->track_spine_colour_v0); - td6->track_rail_colour[i] = RCT1::GetColour(td4->track_rail_colour_v0); - td6->track_support_colour[i] = RCT1::GetColour(td4->track_support_colour_v0); - - // Mazes were only hedges - switch (td4->type) - { - case RCT1_RIDE_TYPE_HEDGE_MAZE: - td6->track_support_colour[i] = MAZE_WALL_TYPE_HEDGE; - break; - case RCT1_RIDE_TYPE_RIVER_RAPIDS: - td6->track_spine_colour[i] = COLOUR_WHITE; - td6->track_rail_colour[i] = COLOUR_WHITE; - break; - } - } - } - else - { - for (int32_t i = 0; i < RCT12_NUM_COLOUR_SCHEMES; i++) - { - td6->track_spine_colour[i] = RCT1::GetColour(td4->track_spine_colour[i]); - td6->track_rail_colour[i] = RCT1::GetColour(td4->track_rail_colour[i]); - td6->track_support_colour[i] = RCT1::GetColour(td4->track_support_colour[i]); - } - } - - td6->depart_flags = td4->depart_flags; - td6->number_of_trains = td4->number_of_trains; - td6->number_of_cars_per_train = td4->number_of_cars_per_train; - td6->min_waiting_time = td4->min_waiting_time; - td6->max_waiting_time = td4->max_waiting_time; - td6->operation_setting = std::min(td4->operation_setting, RideProperties[td6->type].max_value); - td6->max_speed = td4->max_speed; - td6->average_speed = td4->average_speed; - td6->ride_length = td4->ride_length; - td6->max_positive_vertical_g = td4->max_positive_vertical_g; - td6->max_negative_vertical_g = td4->max_negative_vertical_g; - td6->max_lateral_g = td4->max_lateral_g; - td6->inversions = td4->num_inversions; - td6->drops = td4->num_drops; - td6->highest_drop_height = td4->highest_drop_height / 2; - td6->excitement = td4->excitement; - td6->intensity = td4->intensity; - td6->nausea = td4->nausea; - td6->upkeep_cost = td4->upkeep_cost; - if (version == 1) - { - td6->flags2 = td4->flags2; - } - - td6->space_required_x = 255; - td6->space_required_y = 255; - td6->lift_hill_speed_num_circuits = 5; - - // Move elements across - td6->elements = td4->elements; - td6->elementsSize = td4->elementsSize; - - td6_reset_trailing_elements(td6); - td6_set_element_helper_pointers(td6, true); - - SafeFree(td4); - return td6; -} - -static rct_track_td6* track_design_open_from_buffer(uint8_t* src, size_t srcLength) -{ - uint8_t version = (src[7] >> 2) & 3; - if (version == 0 || version == 1) - { - return track_design_open_from_td4(src, srcLength); - } - else if (version != 2) - { - log_error("Unsupported track design."); - return nullptr; - } - - rct_track_td6* td6 = (rct_track_td6*)calloc(1, sizeof(rct_track_td6)); - if (td6 == nullptr) - { - log_error("Unable to allocate memory for TD6 data."); - return nullptr; - } - std::memcpy(td6, src, 0xA3); - td6->elementsSize = srcLength - 0xA3; - td6->elements = malloc(td6->elementsSize); - if (td6->elements == nullptr) - { - free(td6); - log_error("Unable to allocate memory for TD6 element data."); - return nullptr; - } - std::memcpy(td6->elements, src + 0xA3, td6->elementsSize); - - // Cap operation setting - td6->operation_setting = std::min(td6->operation_setting, RideProperties[td6->type].max_value); - - td6_set_element_helper_pointers(td6, false); - return td6; -} - -static void td6_reset_trailing_elements(rct_track_td6* td6) -{ - void* lastElement; - if (td6->type == RIDE_TYPE_MAZE) - { - rct_td6_maze_element* mazeElement = (rct_td6_maze_element*)td6->elements; - while (mazeElement->all != 0) - { - mazeElement++; - } - lastElement = (void*)((uintptr_t)mazeElement + 1); - - size_t trailingSize = td6->elementsSize - (size_t)((uintptr_t)lastElement - (uintptr_t)td6->elements); - std::memset(lastElement, 0, trailingSize); - } - else - { - rct_td6_track_element* trackElement = (rct_td6_track_element*)td6->elements; - while (trackElement->type != 0xFF) - { - trackElement++; - } - lastElement = (void*)((uintptr_t)trackElement + 1); - - size_t trailingSize = td6->elementsSize - (size_t)((uintptr_t)lastElement - (uintptr_t)td6->elements); - std::memset(lastElement, 0xFF, trailingSize); - } -} - -/** - * - * @param clearScenery Set when importing TD4 designs, to avoid corrupted data being interpreted as scenery. - */ -static void td6_set_element_helper_pointers(rct_track_td6* td6, bool clearScenery) -{ - uintptr_t sceneryElementsStart; - - if (td6->type == RIDE_TYPE_MAZE) - { - td6->track_elements = nullptr; - td6->maze_elements = (rct_td6_maze_element*)td6->elements; - - rct_td6_maze_element* maze = td6->maze_elements; - for (; maze->all != 0; maze++) - { - } - sceneryElementsStart = (uintptr_t)(++maze); - } - else - { - td6->maze_elements = nullptr; - td6->track_elements = (rct_td6_track_element*)td6->elements; - - rct_td6_track_element* track = td6->track_elements; - for (; track->type != 0xFF; track++) - { - } - uintptr_t entranceElementsStart = (uintptr_t)track + 1; - - rct_td6_entrance_element* entranceElement = (rct_td6_entrance_element*)entranceElementsStart; - td6->entrance_elements = entranceElement; - for (; entranceElement->z != -1; entranceElement++) - { - } - sceneryElementsStart = (uintptr_t)entranceElement + 1; - } - - if (clearScenery) - { - td6->scenery_elements = nullptr; - } - else - { - rct_td6_scenery_element* sceneryElement = (rct_td6_scenery_element*)sceneryElementsStart; - td6->scenery_elements = sceneryElement; - } -} - -void track_design_dispose(rct_track_td6* td6) -{ - if (td6 != nullptr) - { - free(td6->elements); - free(td6->name); - free(td6); - } -} +//static rct_track_td6* track_design_open_from_td4(uint8_t* src, size_t srcLength) +//{ +// rct_track_td4* td4 = (rct_track_td4*)calloc(1, sizeof(rct_track_td4)); +// if (td4 == nullptr) +// { +// log_error("Unable to allocate memory for TD4 data."); +// SafeFree(td4); +// return nullptr; +// } +// +// uint8_t version = (src[7] >> 2) & 3; +// if (version == 0) +// { +// std::memcpy(td4, src, 0x38); +// td4->elementsSize = srcLength - 0x38; +// td4->elements = malloc(td4->elementsSize); +// if (td4->elements == nullptr) +// { +// log_error("Unable to allocate memory for TD4 element data."); +// SafeFree(td4); +// return nullptr; +// } +// std::memcpy(td4->elements, src + 0x38, td4->elementsSize); +// } +// else if (version == 1) +// { +// std::memcpy(td4, src, 0xC4); +// td4->elementsSize = srcLength - 0xC4; +// td4->elements = malloc(td4->elementsSize); +// if (td4->elements == nullptr) +// { +// log_error("Unable to allocate memory for TD4 element data."); +// SafeFree(td4); +// return nullptr; +// } +// std::memcpy(td4->elements, src + 0xC4, td4->elementsSize); +// } +// else +// { +// log_error("Unsupported track design."); +// SafeFree(td4); +// return nullptr; +// } +// +// rct_track_td6* td6 = (rct_track_td6*)calloc(1, sizeof(rct_track_td6)); +// if (td6 == nullptr) +// { +// log_error("Unable to allocate memory for TD6 data."); +// SafeFree(td4); +// return nullptr; +// } +// +// td6->type = RCT1::GetRideType(td4->type); +// +// // All TD4s that use powered launch use the type that doesn't pass the station. +// td6->ride_mode = td4->mode; +// if (td4->mode == RCT1_RIDE_MODE_POWERED_LAUNCH) +// { +// td6->ride_mode = RIDE_MODE_POWERED_LAUNCH; +// } +// +// // Convert RCT1 vehicle type to RCT2 vehicle type. Intialise with an string consisting of 8 spaces. +// rct_object_entry vehicleObject = { 0x80, " " }; +// if (td4->type == RIDE_TYPE_MAZE) +// { +// const char* name = RCT1::GetRideTypeObject(td4->type); +// assert(name != nullptr); +// std::memcpy(vehicleObject.name, name, std::min(String::SizeOf(name), (size_t)8)); +// } +// else +// { +// const char* name = RCT1::GetVehicleObject(td4->vehicle_type); +// assert(name != nullptr); +// std::memcpy(vehicleObject.name, name, std::min(String::SizeOf(name), (size_t)8)); +// } +// std::memcpy(&td6->vehicle_object, &vehicleObject, sizeof(rct_object_entry)); +// td6->vehicle_type = td4->vehicle_type; +// +// td6->flags = td4->flags; +// td6->version_and_colour_scheme = td4->version_and_colour_scheme; +// +// // Vehicle colours +// for (int32_t i = 0; i < RCT1_MAX_TRAINS_PER_RIDE; i++) +// { +// // RCT1 had no third colour +// RCT1::RCT1VehicleColourSchemeCopyDescriptor colourSchemeCopyDescriptor = RCT1::GetColourSchemeCopyDescriptor( +// td4->vehicle_type); +// if (colourSchemeCopyDescriptor.colour1 == COPY_COLOUR_1) +// { +// td6->vehicle_colours[i].body_colour = RCT1::GetColour(td4->vehicle_colours[i].body_colour); +// } +// else if (colourSchemeCopyDescriptor.colour1 == COPY_COLOUR_2) +// { +// td6->vehicle_colours[i].body_colour = RCT1::GetColour(td4->vehicle_colours[i].trim_colour); +// } +// else +// { +// td6->vehicle_colours[i].body_colour = colourSchemeCopyDescriptor.colour1; +// } +// +// if (colourSchemeCopyDescriptor.colour2 == COPY_COLOUR_1) +// { +// td6->vehicle_colours[i].trim_colour = RCT1::GetColour(td4->vehicle_colours[i].body_colour); +// } +// else if (colourSchemeCopyDescriptor.colour2 == COPY_COLOUR_2) +// { +// td6->vehicle_colours[i].trim_colour = RCT1::GetColour(td4->vehicle_colours[i].trim_colour); +// } +// else +// { +// td6->vehicle_colours[i].trim_colour = colourSchemeCopyDescriptor.colour2; +// } +// +// if (colourSchemeCopyDescriptor.colour3 == COPY_COLOUR_1) +// { +// td6->vehicle_additional_colour[i] = RCT1::GetColour(td4->vehicle_colours[i].body_colour); +// } +// else if (colourSchemeCopyDescriptor.colour3 == COPY_COLOUR_2) +// { +// td6->vehicle_additional_colour[i] = RCT1::GetColour(td4->vehicle_colours[i].trim_colour); +// } +// else +// { +// td6->vehicle_additional_colour[i] = colourSchemeCopyDescriptor.colour3; +// } +// } +// // Set remaining vehicles to same colour as first vehicle +// for (int32_t i = RCT1_MAX_TRAINS_PER_RIDE; i < MAX_VEHICLES_PER_RIDE; i++) +// { +// td6->vehicle_colours[i] = td6->vehicle_colours[0]; +// td6->vehicle_additional_colour[i] = td6->vehicle_additional_colour[0]; +// } +// +// // Track colours +// if (version == 0) +// { +// for (int32_t i = 0; i < NUM_COLOUR_SCHEMES; i++) +// { +// td6->track_spine_colour[i] = RCT1::GetColour(td4->track_spine_colour_v0); +// td6->track_rail_colour[i] = RCT1::GetColour(td4->track_rail_colour_v0); +// td6->track_support_colour[i] = RCT1::GetColour(td4->track_support_colour_v0); +// +// // Mazes were only hedges +// switch (td4->type) +// { +// case RCT1_RIDE_TYPE_HEDGE_MAZE: +// td6->track_support_colour[i] = MAZE_WALL_TYPE_HEDGE; +// break; +// case RCT1_RIDE_TYPE_RIVER_RAPIDS: +// td6->track_spine_colour[i] = COLOUR_WHITE; +// td6->track_rail_colour[i] = COLOUR_WHITE; +// break; +// } +// } +// } +// else +// { +// for (int32_t i = 0; i < RCT12_NUM_COLOUR_SCHEMES; i++) +// { +// td6->track_spine_colour[i] = RCT1::GetColour(td4->track_spine_colour[i]); +// td6->track_rail_colour[i] = RCT1::GetColour(td4->track_rail_colour[i]); +// td6->track_support_colour[i] = RCT1::GetColour(td4->track_support_colour[i]); +// } +// } +// +// td6->depart_flags = td4->depart_flags; +// td6->number_of_trains = td4->number_of_trains; +// td6->number_of_cars_per_train = td4->number_of_cars_per_train; +// td6->min_waiting_time = td4->min_waiting_time; +// td6->max_waiting_time = td4->max_waiting_time; +// td6->operation_setting = std::min(td4->operation_setting, RideProperties[td6->type].max_value); +// td6->max_speed = td4->max_speed; +// td6->average_speed = td4->average_speed; +// td6->ride_length = td4->ride_length; +// td6->max_positive_vertical_g = td4->max_positive_vertical_g; +// td6->max_negative_vertical_g = td4->max_negative_vertical_g; +// td6->max_lateral_g = td4->max_lateral_g; +// td6->inversions = td4->num_inversions; +// td6->drops = td4->num_drops; +// td6->highest_drop_height = td4->highest_drop_height / 2; +// td6->excitement = td4->excitement; +// td6->intensity = td4->intensity; +// td6->nausea = td4->nausea; +// td6->upkeep_cost = td4->upkeep_cost; +// if (version == 1) +// { +// td6->flags2 = td4->flags2; +// } +// +// td6->space_required_x = 255; +// td6->space_required_y = 255; +// td6->lift_hill_speed_num_circuits = 5; +// +// // Move elements across +// td6->elements = td4->elements; +// td6->elementsSize = td4->elementsSize; +// +// td6_reset_trailing_elements(td6); +// td6_set_element_helper_pointers(td6, true); +// +// SafeFree(td4); +// return td6; +//} +// +//static rct_track_td6* track_design_open_from_buffer(uint8_t* src, size_t srcLength) +//{ +// uint8_t version = (src[7] >> 2) & 3; +// if (version == 0 || version == 1) +// { +// return track_design_open_from_td4(src, srcLength); +// } +// else if (version != 2) +// { +// log_error("Unsupported track design."); +// return nullptr; +// } +// +// rct_track_td6* td6 = (rct_track_td6*)calloc(1, sizeof(rct_track_td6)); +// if (td6 == nullptr) +// { +// log_error("Unable to allocate memory for TD6 data."); +// return nullptr; +// } +// std::memcpy(td6, src, 0xA3); +// td6->elementsSize = srcLength - 0xA3; +// td6->elements = malloc(td6->elementsSize); +// if (td6->elements == nullptr) +// { +// free(td6); +// log_error("Unable to allocate memory for TD6 element data."); +// return nullptr; +// } +// std::memcpy(td6->elements, src + 0xA3, td6->elementsSize); +// +// // Cap operation setting +// td6->operation_setting = std::min(td6->operation_setting, RideProperties[td6->type].max_value); +// +// td6_set_element_helper_pointers(td6, false); +// return td6; +//} +// +//static void td6_reset_trailing_elements(rct_track_td6* td6) +//{ +// void* lastElement; +// if (td6->type == RIDE_TYPE_MAZE) +// { +// rct_td6_maze_element* mazeElement = (rct_td6_maze_element*)td6->elements; +// while (mazeElement->all != 0) +// { +// mazeElement++; +// } +// lastElement = (void*)((uintptr_t)mazeElement + 1); +// +// size_t trailingSize = td6->elementsSize - (size_t)((uintptr_t)lastElement - (uintptr_t)td6->elements); +// std::memset(lastElement, 0, trailingSize); +// } +// else +// { +// rct_td6_track_element* trackElement = (rct_td6_track_element*)td6->elements; +// while (trackElement->type != 0xFF) +// { +// trackElement++; +// } +// lastElement = (void*)((uintptr_t)trackElement + 1); +// +// size_t trailingSize = td6->elementsSize - (size_t)((uintptr_t)lastElement - (uintptr_t)td6->elements); +// std::memset(lastElement, 0xFF, trailingSize); +// } +//} +// +///** +// * +// * @param clearScenery Set when importing TD4 designs, to avoid corrupted data being interpreted as scenery. +// */ +//static void td6_set_element_helper_pointers(rct_track_td6* td6, bool clearScenery) +//{ +// uintptr_t sceneryElementsStart; +// +// if (td6->type == RIDE_TYPE_MAZE) +// { +// td6->track_elements = nullptr; +// td6->maze_elements = (rct_td6_maze_element*)td6->elements; +// +// rct_td6_maze_element* maze = td6->maze_elements; +// for (; maze->all != 0; maze++) +// { +// } +// sceneryElementsStart = (uintptr_t)(++maze); +// } +// else +// { +// td6->maze_elements = nullptr; +// td6->track_elements = (rct_td6_track_element*)td6->elements; +// +// rct_td6_track_element* track = td6->track_elements; +// for (; track->type != 0xFF; track++) +// { +// } +// uintptr_t entranceElementsStart = (uintptr_t)track + 1; +// +// rct_td6_entrance_element* entranceElement = (rct_td6_entrance_element*)entranceElementsStart; +// td6->entrance_elements = entranceElement; +// for (; entranceElement->z != -1; entranceElement++) +// { +// } +// sceneryElementsStart = (uintptr_t)entranceElement + 1; +// } +// +// if (clearScenery) +// { +// td6->scenery_elements = nullptr; +// } +// else +// { +// rct_td6_scenery_element* sceneryElement = (rct_td6_scenery_element*)sceneryElementsStart; +// td6->scenery_elements = sceneryElement; +// } +//} +// +//void track_design_dispose(rct_track_td6* td6) +//{ +// if (td6 != nullptr) +// { +// free(td6->elements); +// free(td6->name); +// free(td6); +// } +//} /** * * rct2: 0x006ABDB0 */ -static void track_design_load_scenery_objects(rct_track_td6* td6) +static void track_design_load_scenery_objects(TrackDesign* td6) { object_manager_unload_all_objects(); @@ -482,10 +495,9 @@ static void track_design_load_scenery_objects(rct_track_td6* td6) object_manager_load_object(rideEntry); // Load scenery objects - rct_td6_scenery_element* scenery = td6->scenery_elements; - for (; scenery != nullptr && scenery->scenery_object.end_flag != 0xFF; scenery++) + for (const auto& scenery : td6->scenery_elements) { - rct_object_entry* sceneryEntry = &scenery->scenery_object; + const rct_object_entry* sceneryEntry = &scenery.scenery_object; object_manager_load_object(sceneryEntry); } } @@ -494,15 +506,14 @@ static void track_design_load_scenery_objects(rct_track_td6* td6) * * rct2: 0x006D247A */ -static void track_design_mirror_scenery(rct_track_td6* td6) +static void track_design_mirror_scenery(TrackDesign* td6) { - rct_td6_scenery_element* scenery = td6->scenery_elements; - for (; scenery != nullptr && scenery->scenery_object.end_flag != 0xFF; scenery++) + for (auto& scenery: td6->scenery_elements) { uint8_t entry_type{ 0 }, entry_index{ 0 }; - if (!find_object_in_entry_group(&scenery->scenery_object, &entry_type, &entry_index)) + if (!find_object_in_entry_group(&scenery.scenery_object, &entry_type, &entry_index)) { - entry_type = object_entry_get_type(&scenery->scenery_object); + entry_type = object_entry_get_type(&scenery.scenery_object); if (entry_type != OBJECT_TYPE_PATHS) { continue; @@ -537,67 +548,67 @@ static void track_design_mirror_scenery(rct_track_td6* td6) } } - switch (scenery->flags & 3) + switch (scenery.flags & 3) { case 0: - scenery->y = (-(scenery->y * 32 + y1) - y2) / 32; + scenery.y = (-(scenery.y * 32 + y1) - y2) / 32; break; case 1: - scenery->x = (scenery->x * 32 + y2 + y1) / 32; - scenery->y = (-(scenery->y * 32)) / 32; - scenery->flags ^= (1 << 1); - break; - case 2: - scenery->y = (-(scenery->y * 32 - y2) + y1) / 32; - break; - case 3: - scenery->x = (scenery->x * 32 - y2 - y1) / 32; - scenery->y = (-(scenery->y * 32)) / 32; - scenery->flags ^= (1 << 1); + scenery.x = (scenery.x * 32 + y2 + y1) / 32; + scenery.y = (-(scenery.y * 32)) / 32; + scenery.flags ^= (1 << 1); + break; + case 2: + scenery.y = (-(scenery.y * 32 - y2) + y1) / 32; + break; + case 3: + scenery.x = (scenery.x * 32 - y2 - y1) / 32; + scenery.y = (-(scenery.y * 32)) / 32; + scenery.flags ^= (1 << 1); break; } break; } case OBJECT_TYPE_SMALL_SCENERY: - scenery->y = -scenery->y; + scenery.y = -scenery.y; if (scenery_small_entry_has_flag(scenery_entry, SMALL_SCENERY_FLAG_DIAGONAL)) { - scenery->flags ^= (1 << 0); + scenery.flags ^= (1 << 0); if (!scenery_small_entry_has_flag(scenery_entry, SMALL_SCENERY_FLAG_FULL_TILE)) { - scenery->flags ^= (1 << 2); + scenery.flags ^= (1 << 2); } break; } - if (scenery->flags & (1 << 0)) + if (scenery.flags & (1 << 0)) { - scenery->flags ^= (1 << 1); + scenery.flags ^= (1 << 1); } - scenery->flags ^= (1 << 2); + scenery.flags ^= (1 << 2); break; case OBJECT_TYPE_WALLS: - scenery->y = -scenery->y; - if (scenery->flags & (1 << 0)) + scenery.y = -scenery.y; + if (scenery.flags & (1 << 0)) { - scenery->flags ^= (1 << 1); + scenery.flags ^= (1 << 1); } break; case OBJECT_TYPE_PATHS: - scenery->y = -scenery->y; + scenery.y = -scenery.y; - if (scenery->flags & (1 << 5)) + if (scenery.flags & (1 << 5)) { - scenery->flags ^= (1 << 6); + scenery.flags ^= (1 << 6); } - uint8_t flags = scenery->flags; + uint8_t flags = scenery.flags; flags = ((flags & (1 << 3)) >> 2) | ((flags & (1 << 1)) << 2); - scenery->flags &= 0xF5; - scenery->flags |= flags; + scenery.flags &= 0xF5; + scenery.flags |= flags; } } } @@ -606,21 +617,19 @@ static void track_design_mirror_scenery(rct_track_td6* td6) * * rct2: 0x006D2443 */ -static void track_design_mirror_ride(rct_track_td6* td6) +static void track_design_mirror_ride(TrackDesign* td6) { - rct_td6_track_element* track = td6->track_elements; - for (; track->type != 0xFF; track++) + for (auto& track: td6->track_elements) { - track->type = TrackElementMirrorMap[track->type]; + track.type = TrackElementMirrorMap[track.type]; } - rct_td6_entrance_element* entrance = td6->entrance_elements; - for (; entrance->z != -1; entrance++) + for (auto& entrance : td6->entrance_elements) { - entrance->y = -entrance->y; - if (entrance->direction & 1) + entrance.y = -entrance.y; + if (entrance.direction & 1) { - entrance->direction = direction_reverse(entrance->direction); + entrance.direction = direction_reverse(entrance.direction); } } } @@ -634,30 +643,29 @@ static constexpr const uint8_t maze_segment_mirror_map[] = { * * rct2: 0x006D25FA */ -static void track_design_mirror_maze(rct_track_td6* td6) +static void track_design_mirror_maze(TrackDesign* td6) { - rct_td6_maze_element* maze = td6->maze_elements; - for (; maze->all != 0; maze++) + for (auto& maze:td6->maze_elements) { - maze->y = -maze->y; + maze.y = -maze.y; - if (maze->type == 0x8 || maze->type == 0x80) + if (maze.type == 0x8 || maze.type == 0x80) { - if (maze->direction & 1) + if (maze.direction & 1) { - maze->direction = direction_reverse(maze->direction); + maze.direction = direction_reverse(maze.direction); } continue; } - uint16_t maze_entry = maze->maze_entry; + uint16_t maze_entry = maze.maze_entry; uint16_t new_entry = 0; for (uint8_t position = bitscanforward(maze_entry); position != 0xFF; position = bitscanforward(maze_entry)) { maze_entry &= ~(1 << position); new_entry |= (1 << maze_segment_mirror_map[position]); } - maze->maze_entry = new_entry; + maze.maze_entry = new_entry; } } @@ -665,7 +673,7 @@ static void track_design_mirror_maze(rct_track_td6* td6) * * rct2: 0x006D2436 */ -void track_design_mirror(rct_track_td6* td6) +void track_design_mirror(TrackDesign* td6) { if (td6->type == RIDE_TYPE_MAZE) { @@ -700,11 +708,12 @@ static void track_design_update_max_min_coordinates(int16_t x, int16_t y, int16_ gTrackPreviewMax.z = std::max(gTrackPreviewMax.z, z); } -static bool TrackDesignPlaceSceneryElementGetEntry(uint8_t& entry_type, uint8_t& entry_index, rct_td6_scenery_element* scenery) +static bool TrackDesignPlaceSceneryElementGetEntry( + uint8_t& entry_type, uint8_t& entry_index, const rct_td6_scenery_element& scenery) { - if (!find_object_in_entry_group(&scenery->scenery_object, &entry_type, &entry_index)) + if (!find_object_in_entry_group(&scenery.scenery_object, &entry_type, &entry_index)) { - entry_type = object_entry_get_type(&scenery->scenery_object); + entry_type = object_entry_get_type(&scenery.scenery_object); if (entry_type != OBJECT_TYPE_PATHS) { _trackDesignPlaceStateSceneryUnavailable = true; @@ -741,7 +750,7 @@ static bool TrackDesignPlaceSceneryElementGetEntry(uint8_t& entry_type, uint8_t& } static bool TrackDesignPlaceSceneryElementRemoveGhost( - CoordsXY mapCoord, rct_td6_scenery_element* scenery, uint8_t rotation, int32_t originZ) + CoordsXY mapCoord, const rct_td6_scenery_element& scenery, uint8_t rotation, int32_t originZ) { uint8_t entry_type, entry_index; if (TrackDesignPlaceSceneryElementGetEntry(entry_type, entry_index, scenery)) @@ -754,8 +763,8 @@ static bool TrackDesignPlaceSceneryElementRemoveGhost( return true; } - int32_t z = (scenery->z * 8 + originZ) / 8; - uint8_t sceneryRotation = (rotation + scenery->flags) & TILE_ELEMENT_DIRECTION_MASK; + int32_t z = (scenery.z * 8 + originZ) / 8; + uint8_t sceneryRotation = (rotation + scenery.flags) & TILE_ELEMENT_DIRECTION_MASK; const uint32_t flags = GAME_COMMAND_FLAG_APPLY | GAME_COMMAND_FLAG_ALLOW_DURING_PAUSED | GAME_COMMAND_FLAG_NO_SPEND | GAME_COMMAND_FLAG_GHOST; std::unique_ptr ga; @@ -763,7 +772,7 @@ static bool TrackDesignPlaceSceneryElementRemoveGhost( { case OBJECT_TYPE_SMALL_SCENERY: { - uint8_t quadrant = (scenery->flags >> 2) + _currentTrackPieceDirection; + uint8_t quadrant = (scenery.flags >> 2) + _currentTrackPieceDirection; quadrant &= 3; rct_scenery_entry* small_scenery = get_small_scenery_entry(entry_index); @@ -796,9 +805,9 @@ static bool TrackDesignPlaceSceneryElementRemoveGhost( return true; } -static bool TrackDesignPlaceSceneryElementGetPlaceZ(rct_td6_scenery_element* scenery) +static bool TrackDesignPlaceSceneryElementGetPlaceZ(const rct_td6_scenery_element& scenery) { - int32_t z = scenery->z * 8 + _trackDesignPlaceZ; + int32_t z = scenery.z * 8 + _trackDesignPlaceZ; if (z < _trackDesignPlaceSceneryZ) { _trackDesignPlaceSceneryZ = z; @@ -811,7 +820,7 @@ static bool TrackDesignPlaceSceneryElementGetPlaceZ(rct_td6_scenery_element* sce } static bool TrackDesignPlaceSceneryElement( - CoordsXY mapCoord, uint8_t mode, rct_td6_scenery_element* scenery, uint8_t rotation, int32_t originZ) + CoordsXY mapCoord, uint8_t mode, const rct_td6_scenery_element &scenery, uint8_t rotation, int32_t originZ) { if (_trackDesignPlaceOperation == PTD_OPERATION_DRAW_OUTLINES && mode == 0) { @@ -857,10 +866,10 @@ static bool TrackDesignPlaceSceneryElement( return true; } - rotation += scenery->flags; + rotation += scenery.flags; rotation &= 3; - z = scenery->z * 8 + originZ; - quadrant = ((scenery->flags >> 2) + _currentTrackPieceDirection) & 3; + z = scenery.z * 8 + originZ; + quadrant = ((scenery.flags >> 2) + _currentTrackPieceDirection) & 3; flags = GAME_COMMAND_FLAG_APPLY | GAME_COMMAND_FLAG_PATH_SCENERY; if (_trackDesignPlaceOperation == PTD_OPERATION_PLACE_TRACK_PREVIEW) @@ -881,8 +890,8 @@ static bool TrackDesignPlaceSceneryElement( gGameCommandErrorTitle = STR_CANT_POSITION_THIS_HERE; auto smallSceneryPlace = SmallSceneryPlaceAction( - { mapCoord.x, mapCoord.y, z, rotation }, quadrant, entry_index, scenery->primary_colour, - scenery->secondary_colour); + { mapCoord.x, mapCoord.y, z, rotation }, quadrant, entry_index, scenery.primary_colour, + scenery.secondary_colour); smallSceneryPlace.SetFlags(flags); auto res = flags & GAME_COMMAND_FLAG_APPLY ? GameActions::ExecuteNested(&smallSceneryPlace) @@ -902,10 +911,10 @@ static bool TrackDesignPlaceSceneryElement( return true; } - rotation += scenery->flags; + rotation += scenery.flags; rotation &= 3; - z = scenery->z * 8 + originZ; + z = scenery.z * 8 + originZ; flags = GAME_COMMAND_FLAG_APPLY | GAME_COMMAND_FLAG_PATH_SCENERY; if (_trackDesignPlaceOperation == PTD_OPERATION_PLACE_TRACK_PREVIEW) @@ -924,7 +933,7 @@ static bool TrackDesignPlaceSceneryElement( } auto sceneryPlaceAction = LargeSceneryPlaceAction( - { mapCoord.x, mapCoord.y, z, rotation }, entry_index, scenery->primary_colour, scenery->secondary_colour); + { mapCoord.x, mapCoord.y, z, rotation }, entry_index, scenery.primary_colour, scenery.secondary_colour); sceneryPlaceAction.SetFlags(flags); auto res = flags & GAME_COMMAND_FLAG_APPLY ? GameActions::Execute(&sceneryPlaceAction) : GameActions::Query(&sceneryPlaceAction); @@ -943,8 +952,8 @@ static bool TrackDesignPlaceSceneryElement( return true; } - z = scenery->z * 8 + originZ; - rotation += scenery->flags; + z = scenery.z * 8 + originZ; + rotation += scenery.flags; rotation &= 3; flags = GAME_COMMAND_FLAG_APPLY; @@ -964,8 +973,8 @@ static bool TrackDesignPlaceSceneryElement( } auto wallPlaceAction = WallPlaceAction( - entry_index, { mapCoord.x, mapCoord.y, z }, rotation, scenery->primary_colour, scenery->secondary_colour, - (scenery->flags & 0xFC) >> 2); + entry_index, { mapCoord.x, mapCoord.y, z }, rotation, scenery.primary_colour, scenery.secondary_colour, + (scenery.flags & 0xFC) >> 2); wallPlaceAction.SetFlags(flags); auto res = flags & GAME_COMMAND_FLAG_APPLY ? GameActions::ExecuteNested(&wallPlaceAction) : GameActions::QueryNested(&wallPlaceAction); @@ -979,22 +988,22 @@ static bool TrackDesignPlaceSceneryElement( return true; } - z = (scenery->z * 8 + originZ) / 8; + z = (scenery.z * 8 + originZ) / 8; if (mode == 0) { - if (scenery->flags & (1 << 7)) + if (scenery.flags & (1 << 7)) { // dh entry_index |= (1 << 7); } - uint8_t bh = ((scenery->flags & 0xF) << rotation); + uint8_t bh = ((scenery.flags & 0xF) << rotation); flags = bh >> 4; bh = (bh | flags) & 0xF; - flags = (((scenery->flags >> 5) + rotation) & 3) << 5; + flags = (((scenery.flags >> 5) + rotation) & 3) << 5; bh |= flags; - bh |= scenery->flags & 0x90; + bh |= scenery.flags & 0x90; flags = GAME_COMMAND_FLAG_APPLY; if (_trackDesignPlaceOperation == PTD_OPERATION_PLACE_TRACK_PREVIEW) @@ -1084,11 +1093,11 @@ static bool TrackDesignPlaceSceneryElement( * rct2: 0x006D0964 */ static int32_t track_design_place_all_scenery( - rct_td6_scenery_element* scenery_start, int32_t originX, int32_t originY, int32_t originZ) + const std::vector &sceneryList, int32_t originX, int32_t originY, int32_t originZ) { for (uint8_t mode = 0; mode <= 1; mode++) { - if (scenery_start->scenery_object.end_flag != 0xFF) + if (!sceneryList.empty()) { _trackDesignPlaceStateHasScenery = true; } @@ -1098,11 +1107,11 @@ static int32_t track_design_place_all_scenery( continue; } - for (rct_td6_scenery_element* scenery = scenery_start; scenery->scenery_object.end_flag != 0xFF; scenery++) + for (const auto& scenery : sceneryList) { uint8_t rotation = _currentTrackPieceDirection; TileCoordsXY tileCoords = { originX / 32, originY / 32 }; - TileCoordsXY offsets = { scenery->x, scenery->y }; + TileCoordsXY offsets = { scenery.x, scenery.y }; tileCoords += offsets.Rotate(rotation); CoordsXY mapCoord = { tileCoords.x * 32, tileCoords.y * 32 }; @@ -1117,7 +1126,7 @@ static int32_t track_design_place_all_scenery( return 1; } -static int32_t track_design_place_maze(rct_track_td6* td6, int16_t x, int16_t y, int16_t z, Ride* ride) +static int32_t track_design_place_maze(TrackDesign* td6, int16_t x, int16_t y, int16_t z, Ride* ride) { if (_trackDesignPlaceOperation == PTD_OPERATION_DRAW_OUTLINES) { @@ -1131,12 +1140,11 @@ static int32_t track_design_place_maze(rct_track_td6* td6, int16_t x, int16_t y, _trackDesignPlaceZ = 0; _trackDesignPlaceCost = 0; - rct_td6_maze_element* maze_element = td6->maze_elements; - for (; maze_element->all != 0; maze_element++) + for (const auto& maze_element : td6->maze_elements) { uint8_t rotation = _currentTrackPieceDirection & 3; - int16_t tmpX = maze_element->x * 32; - int16_t tmpY = maze_element->y * 32; + int16_t tmpX = maze_element.x * 32; + int16_t tmpY = maze_element.y * 32; rotate_map_coordinates(&tmpX, &tmpY, rotation); CoordsXY mapCoord = { tmpX, tmpY }; mapCoord.x += x; @@ -1156,11 +1164,11 @@ static int32_t track_design_place_maze(rct_track_td6* td6, int16_t x, int16_t y, uint8_t flags; money32 cost = 0; uint16_t maze_entry; - switch (maze_element->type) + switch (maze_element.type) { case MAZE_ELEMENT_TYPE_ENTRANCE: // entrance - rotation += maze_element->direction; + rotation += maze_element.direction; rotation &= 3; flags = GAME_COMMAND_FLAG_APPLY; @@ -1195,7 +1203,7 @@ static int32_t track_design_place_maze(rct_track_td6* td6, int16_t x, int16_t y, break; case MAZE_ELEMENT_TYPE_EXIT: // exit - rotation += maze_element->direction; + rotation += maze_element.direction; rotation &= 3; flags = GAME_COMMAND_FLAG_APPLY; @@ -1229,7 +1237,7 @@ static int32_t track_design_place_maze(rct_track_td6* td6, int16_t x, int16_t y, } break; default: - maze_entry = rol16(maze_element->maze_entry, rotation * 4); + maze_entry = rol16(maze_element.maze_entry, rotation * 4); if (_trackDesignPlaceOperation == PTD_OPERATION_PLACE_TRACK_PREVIEW) { @@ -1328,7 +1336,7 @@ static int32_t track_design_place_maze(rct_track_td6* td6, int16_t x, int16_t y, return 1; } -static bool track_design_place_ride(rct_track_td6* td6, int16_t x, int16_t y, int16_t z, Ride* ride) +static bool track_design_place_ride(TrackDesign* td6, int16_t x, int16_t y, int16_t z, Ride* ride) { const rct_preview_track** trackBlockArray = (ride_type_has_flag(td6->type, RIDE_TYPE_FLAG_HAS_TRACK)) ? TrackBlocks : FlatRideTrackBlocks; @@ -1350,10 +1358,9 @@ static bool track_design_place_ride(rct_track_td6* td6, int16_t x, int16_t y, in uint8_t rotation = _currentTrackPieceDirection; // Track elements - rct_td6_track_element* track = td6->track_elements; - for (; track->type != 0xFF; track++) + for (const auto& track : td6->track_elements) { - uint8_t trackType = track->type; + uint8_t trackType = track.type; if (trackType == TRACK_ELEM_INVERTED_90_DEG_UP_TO_FLAT_QUARTER_LOOP) { trackType = 0xFF; @@ -1392,16 +1399,16 @@ static bool track_design_place_ride(rct_track_td6* td6, int16_t x, int16_t y, in // di int16_t tempZ = z - trackCoordinates->z_begin; - uint32_t trackColour = (track->flags >> 4) & 0x3; - uint32_t brakeSpeed = (track->flags & 0x0F) * 2; - uint32_t seatRotation = track->flags & 0x0F; + uint32_t trackColour = (track.flags >> 4) & 0x3; + uint32_t brakeSpeed = (track.flags & 0x0F) * 2; + uint32_t seatRotation = track.flags & 0x0F; int32_t liftHillAndAlternativeState = 0; - if (track->flags & TRACK_ELEMENT_TYPE_FLAG_CHAIN_LIFT) + if (track.flags & TRACK_ELEMENT_TYPE_FLAG_CHAIN_LIFT) { liftHillAndAlternativeState |= 1; } - if (track->flags & TRACK_ELEMENT_FLAG_INVERTED) + if (track.flags & TRACK_ELEMENT_FLAG_INVERTED) { liftHillAndAlternativeState |= 2; } @@ -1504,12 +1511,11 @@ static bool track_design_place_ride(rct_track_td6* td6, int16_t x, int16_t y, in } // Entrance elements - rct_td6_entrance_element* entrance = td6->entrance_elements; - for (; entrance->z != -1; entrance++) + for (const auto& entrance:td6->entrance_elements) { rotation = _currentTrackPieceDirection & 3; - x = entrance->x; - y = entrance->y; + x = entrance.x; + y = entrance.y; rotate_map_coordinates(&x, &y, rotation); x += gTrackPreviewOrigin.x; y += gTrackPreviewOrigin.y; @@ -1526,9 +1532,9 @@ static bool track_design_place_ride(rct_track_td6* td6, int16_t x, int16_t y, in case PTD_OPERATION_PLACE_GHOST: case PTD_OPERATION_PLACE_TRACK_PREVIEW: { - rotation = (rotation + entrance->direction) & 3; + rotation = (rotation + entrance.direction) & 3; bool isExit = false; - if (entrance->direction & (1 << 7)) + if (entrance.direction & (1 << 7)) { isExit = true; } @@ -1541,7 +1547,7 @@ static bool track_design_place_ride(rct_track_td6* td6, int16_t x, int16_t y, in }; TileElement* tile_element = map_get_first_element_at(tile.x >> 5, tile.y >> 5); z = gTrackPreviewOrigin.z / 8; - z += (entrance->z == (int8_t)(uint8_t)0x80) ? -1 : entrance->z; + z += (entrance.z == (int8_t)(uint8_t)0x80) ? -1 : entrance.z; do { @@ -1591,7 +1597,7 @@ static bool track_design_place_ride(rct_track_td6* td6, int16_t x, int16_t y, in } else { - z = (entrance->z == (int8_t)(uint8_t)0x80) ? -1 : entrance->z; + z = (entrance.z == (int8_t)(uint8_t)0x80) ? -1 : entrance.z; z *= 8; z += gTrackPreviewOrigin.z; @@ -1634,7 +1640,7 @@ static bool track_design_place_ride(rct_track_td6* td6, int16_t x, int16_t y, in * rct2: 0x006D01B3 */ int32_t place_virtual_track( - rct_track_td6* td6, uint8_t ptdOperation, bool placeScenery, Ride* ride, int16_t x, int16_t y, int16_t z) + TrackDesign* td6, uint8_t ptdOperation, bool placeScenery, Ride* ride, int16_t x, int16_t y, int16_t z) { // Previously byte_F4414E was cleared here _trackDesignPlaceStatePlaceScenery = placeScenery; @@ -1668,10 +1674,10 @@ int32_t place_virtual_track( } // Scenery elements - rct_td6_scenery_element* scenery = td6->scenery_elements; - if (track_place_success && scenery != nullptr) + if (track_place_success) { - if (!track_design_place_all_scenery(scenery, gTrackPreviewOrigin.x, gTrackPreviewOrigin.y, gTrackPreviewOrigin.z)) + if (!track_design_place_all_scenery( + td6->scenery_elements, gTrackPreviewOrigin.x, gTrackPreviewOrigin.y, gTrackPreviewOrigin.z)) { return _trackDesignPlaceCost; } @@ -1701,7 +1707,7 @@ int32_t place_virtual_track( * ebx = ride_id * cost = edi */ -static bool track_design_place_preview(rct_track_td6* td6, money32* cost, Ride** outRide, uint8_t* flags) +static bool track_design_place_preview(TrackDesign* td6, money32* cost, Ride** outRide, uint8_t* flags) { *outRide = nullptr; *flags = 0; @@ -1815,7 +1821,7 @@ static money32 place_track_design(int16_t x, int16_t y, int16_t z, uint8_t flags } } - rct_track_td6* td6 = gActiveTrackDesign; + TrackDesign* td6 = gActiveTrackDesign; if (td6 == nullptr) { return MONEY32_UNDEFINED; @@ -1935,9 +1941,9 @@ static money32 place_track_design(int16_t x, int16_t y, int16_t z, uint8_t flags set_operating_setting_nested(ride->id, RideSetSetting::MinWaitingTime, td6->min_waiting_time, flags); set_operating_setting_nested(ride->id, RideSetSetting::MaxWaitingTime, td6->max_waiting_time, flags); set_operating_setting_nested(ride->id, RideSetSetting::Operation, td6->operation_setting, flags); - set_operating_setting_nested(ride->id, RideSetSetting::LiftHillSpeed, td6->lift_hill_speed_num_circuits & 0x1F, flags); + set_operating_setting_nested(ride->id, RideSetSetting::LiftHillSpeed, td6->lift_hill_speed & 0x1F, flags); - uint8_t num_circuits = td6->lift_hill_speed_num_circuits >> 5; + uint8_t num_circuits = td6->num_circuits; if (num_circuits == 0) { num_circuits = 1; @@ -1945,7 +1951,7 @@ static money32 place_track_design(int16_t x, int16_t y, int16_t z, uint8_t flags set_operating_setting_nested(ride->id, RideSetSetting::NumCircuits, num_circuits, flags); ride->SetToDefaultInspectionInterval(); ride->lifecycle_flags |= RIDE_LIFECYCLE_NOT_CUSTOM_DESIGN; - ride->colour_scheme_type = td6->version_and_colour_scheme & 3; + ride->colour_scheme_type = td6->colour_scheme; ride->entrance_style = td6->entrance_style; @@ -1963,7 +1969,7 @@ static money32 place_track_design(int16_t x, int16_t y, int16_t z, uint8_t flags ride->vehicle_colours[i].Ternary = td6->vehicle_additional_colour[i]; } - ride_set_name(ride, td6->name, flags); + ride_set_name(ride, td6->name.get(), flags); gCommandExpenditureType = RCT_EXPENDITURE_TYPE_RIDE_CONSTRUCTION; *outRideIndex = ride->id; @@ -2135,7 +2141,7 @@ void game_command_place_maze_design( * * rct2: 0x006D1EF0 */ -void track_design_draw_preview(rct_track_td6* td6, uint8_t* pixels) +void track_design_draw_preview(TrackDesign* td6, uint8_t* pixels) { // Make a copy of the map map_backup* mapBackup = track_design_preview_backup_map(); diff --git a/src/openrct2/ride/TrackDesign.h b/src/openrct2/ride/TrackDesign.h index 32982f9b9b..fc4d4b810c 100644 --- a/src/openrct2/ride/TrackDesign.h +++ b/src/openrct2/ride/TrackDesign.h @@ -164,6 +164,62 @@ assert_struct_size(rct_track_td6, 0xbf); #endif #pragma pack(pop) +/** + * Track design structure. + * size: 0x4E72B + */ +struct TrackDesign +{ + uint8_t type; + uint8_t vehicle_type; + money32 cost; + uint32_t flags; + uint8_t ride_mode; + uint8_t track_flags; + uint8_t colour_scheme; + rct_vehicle_colour vehicle_colours[RCT2_MAX_CARS_PER_TRAIN]; + uint8_t pad_48; + uint8_t entrance_style; + uint8_t total_air_time; + uint8_t depart_flags; + uint8_t number_of_trains; + uint8_t number_of_cars_per_train; + uint8_t min_waiting_time; + uint8_t max_waiting_time; + uint8_t operation_setting; + int8_t max_speed; + int8_t average_speed; + uint16_t ride_length; + uint8_t max_positive_vertical_g; + int8_t max_negative_vertical_g; + uint8_t max_lateral_g; + uint8_t inversions; + uint8_t holes; + uint8_t drops; + uint8_t highest_drop_height; + uint8_t excitement; + uint8_t intensity; + uint8_t nausea; + money16 upkeep_cost; + uint8_t track_spine_colour[RCT12_NUM_COLOUR_SCHEMES]; + uint8_t track_rail_colour[RCT12_NUM_COLOUR_SCHEMES]; + uint8_t track_support_colour[RCT12_NUM_COLOUR_SCHEMES]; + uint32_t flags2; + rct_object_entry vehicle_object; + uint8_t space_required_x; + uint8_t space_required_y; + uint8_t vehicle_additional_colour[RCT2_MAX_CARS_PER_TRAIN]; + uint8_t lift_hill_speed; + uint8_t num_circuits; + + std::vector maze_elements; + std::vector track_elements; + std::vector entrance_elements; + std::vector scenery_elements; + + std::unique_ptr name; +}; + // Only written to in RCT2, not used in OpenRCT2. All of these are elements that had to be invented in RCT1. enum : uint32_t { @@ -213,7 +269,7 @@ enum MAZE_ELEMENT_TYPE_EXIT = (1 << 7) }; -extern rct_track_td6* gActiveTrackDesign; +extern TrackDesign* gActiveTrackDesign; extern bool gTrackDesignSceneryToggle; extern LocationXYZ16 gTrackPreviewMin; extern LocationXYZ16 gTrackPreviewMax; @@ -224,13 +280,13 @@ extern bool byte_9D8150; extern bool gTrackDesignSaveMode; extern ride_id_t gTrackDesignSaveRideIndex; -rct_track_td6* track_design_open(const utf8* path); +std::unique_ptr track_design_open(const utf8* path); void track_design_dispose(rct_track_td6* td6); -void track_design_mirror(rct_track_td6* td6); +void track_design_mirror(TrackDesign* td6); int32_t place_virtual_track( - rct_track_td6* td6, uint8_t ptdOperation, bool placeScenery, Ride* ride, int16_t x, int16_t y, int16_t z); + TrackDesign* td6, uint8_t ptdOperation, bool placeScenery, Ride* ride, int16_t x, int16_t y, int16_t z); void game_command_place_track_design( int32_t* eax, int32_t* ebx, int32_t* ecx, int32_t* edx, int32_t* esi, int32_t* edi, int32_t* ebp); @@ -240,20 +296,20 @@ void game_command_place_maze_design( /////////////////////////////////////////////////////////////////////////////// // Track design preview /////////////////////////////////////////////////////////////////////////////// -void track_design_draw_preview(rct_track_td6* td6, uint8_t* pixels); +void track_design_draw_preview(TrackDesign* td6, uint8_t* pixels); /////////////////////////////////////////////////////////////////////////////// // Track design saving /////////////////////////////////////////////////////////////////////////////// -void track_design_save_init(); -void track_design_save_reset_scenery(); -bool track_design_save_contains_tile_element(const TileElement* tileElement); -void track_design_save_select_nearby_scenery(ride_id_t rideIndex); -void track_design_save_select_tile_element( - int32_t interactionType, int32_t x, int32_t y, TileElement* tileElement, bool collect); -bool track_design_save(ride_id_t rideIndex); -bool track_design_save_to_file(const utf8* path); - -bool track_design_are_entrance_and_exit_placed(); +// void track_design_save_init(); +// void track_design_save_reset_scenery(); +// bool track_design_save_contains_tile_element(const TileElement* tileElement); +// void track_design_save_select_nearby_scenery(ride_id_t rideIndex); +// void track_design_save_select_tile_element( +// int32_t interactionType, int32_t x, int32_t y, TileElement* tileElement, bool collect); +// bool track_design_save(ride_id_t rideIndex); +// bool track_design_save_to_file(const utf8* path); +// +// bool track_design_are_entrance_and_exit_placed(); #endif diff --git a/src/openrct2/ride/TrackDesignRepository.cpp b/src/openrct2/ride/TrackDesignRepository.cpp index 967cfe0f28..9f27ef5875 100644 --- a/src/openrct2/ride/TrackDesignRepository.cpp +++ b/src/openrct2/ride/TrackDesignRepository.cpp @@ -88,7 +88,6 @@ public: { item.Flags |= TRIF_READ_ONLY; } - track_design_dispose(td6); return std::make_tuple(true, item); } else diff --git a/src/openrct2/ride/TrackDesignSave.cpp b/src/openrct2/ride/TrackDesignSave.cpp index 909f5e245e..6eb1b17826 100644 --- a/src/openrct2/ride/TrackDesignSave.cpp +++ b/src/openrct2/ride/TrackDesignSave.cpp @@ -39,1292 +39,1293 @@ bool gTrackDesignSaveMode = false; ride_id_t gTrackDesignSaveRideIndex = RIDE_ID_NULL; -static size_t _trackSavedTileElementsCount; -static TileElement* _trackSavedTileElements[TRACK_MAX_SAVED_TILE_ELEMENTS]; - -static size_t _trackSavedTileElementsDescCount; -static rct_td6_scenery_element _trackSavedTileElementsDesc[TRACK_MAX_SAVED_TILE_ELEMENTS]; - -static rct_track_td6* _trackDesign; -static uint8_t _trackSaveDirection; - -static bool track_design_save_should_select_scenery_around(ride_id_t rideIndex, TileElement* tileElement); -static void track_design_save_select_nearby_scenery_for_tile(ride_id_t rideIndex, int32_t cx, int32_t cy); -static bool track_design_save_add_tile_element(int32_t interactionType, int32_t x, int32_t y, TileElement* tileElement); -static void track_design_save_remove_tile_element(int32_t interactionType, int32_t x, int32_t y, TileElement* tileElement); -static bool track_design_save_copy_scenery_to_td6(rct_track_td6* td6); -static rct_track_td6* track_design_save_to_td6(ride_id_t rideIndex); -static bool track_design_save_to_td6_for_maze(const Ride* ride, rct_track_td6* td6); -static bool track_design_save_to_td6_for_tracked_ride(const Ride* ride, rct_track_td6* td6); - -void track_design_save_init() -{ - _trackSavedTileElementsCount = 0; - _trackSavedTileElementsDescCount = 0; - - std::memset(_trackSavedTileElements, 0, sizeof(_trackSavedTileElements)); - std::memset(_trackSavedTileElementsDesc, 0, sizeof(_trackSavedTileElementsDesc)); -} - -/** - * - * rct2: 0x006D2B07 - */ -void track_design_save_select_tile_element( - int32_t interactionType, int32_t x, int32_t y, TileElement* tileElement, bool collect) -{ - if (track_design_save_contains_tile_element(tileElement)) - { - if (!collect) - { - track_design_save_remove_tile_element(interactionType, x, y, tileElement); - } - } - else - { - if (collect) - { - if (!track_design_save_add_tile_element(interactionType, x, y, tileElement)) - { - context_show_error( - STR_SAVE_TRACK_SCENERY_UNABLE_TO_SELECT_ADDITIONAL_ITEM_OF_SCENERY, - STR_SAVE_TRACK_SCENERY_TOO_MANY_ITEMS_SELECTED); - } - } - } -} - -/** - * - * rct2: 0x006D303D - */ -void track_design_save_select_nearby_scenery(ride_id_t rideIndex) -{ - TileElement* tileElement; - - for (int32_t y = 0; y < MAXIMUM_MAP_SIZE_TECHNICAL; y++) - { - for (int32_t x = 0; x < MAXIMUM_MAP_SIZE_TECHNICAL; x++) - { - tileElement = map_get_first_element_at(x, y); - do - { - if (track_design_save_should_select_scenery_around(rideIndex, tileElement)) - { - track_design_save_select_nearby_scenery_for_tile(rideIndex, x, y); - break; - } - } while (!(tileElement++)->IsLastForTile()); - } - } - gfx_invalidate_screen(); -} - -/** - * - * rct2: 0x006D3026 - */ -void track_design_save_reset_scenery() -{ - track_design_save_init(); - gfx_invalidate_screen(); -} - -static void track_design_save_callback(int32_t result, [[maybe_unused]] const utf8* path) -{ - free(_trackDesign->track_elements); - free(_trackDesign->entrance_elements); - free(_trackDesign->scenery_elements); - free(_trackDesign); - - if (result == MODAL_RESULT_OK) - { - track_repository_scan(); - } - gfx_invalidate_screen(); -} - -/** - * - * rct2: 0x006D2804, 0x006D264D - */ -bool track_design_save(ride_id_t rideIndex) -{ - auto ride = get_ride(rideIndex); - if (ride == nullptr || !(ride->lifecycle_flags & RIDE_LIFECYCLE_TESTED)) - { - context_show_error(STR_CANT_SAVE_TRACK_DESIGN, gGameCommandErrorText); - return false; - } - - if (!ride_has_ratings(ride)) - { - context_show_error(STR_CANT_SAVE_TRACK_DESIGN, gGameCommandErrorText); - return false; - } - - _trackDesign = track_design_save_to_td6(rideIndex); - if (_trackDesign == nullptr) - { - context_show_error(STR_CANT_SAVE_TRACK_DESIGN, gGameCommandErrorText); - return false; - } - - if (gTrackDesignSaveMode) - { - if (!track_design_save_copy_scenery_to_td6(_trackDesign)) - { - free(_trackDesign->track_elements); - free(_trackDesign->entrance_elements); - free(_trackDesign); - return false; - } - } - - auto trackName = ride->GetName(); - auto intent = Intent(WC_LOADSAVE); - intent.putExtra(INTENT_EXTRA_LOADSAVE_TYPE, LOADSAVETYPE_SAVE | LOADSAVETYPE_TRACK); - intent.putExtra(INTENT_EXTRA_PATH, trackName); - intent.putExtra(INTENT_EXTRA_CALLBACK, (void*)track_design_save_callback); - context_open_intent(&intent); - - return true; -} - -bool track_design_save_contains_tile_element(const TileElement* tileElement) -{ - for (size_t i = 0; i < _trackSavedTileElementsCount; i++) - { - if (_trackSavedTileElements[i] == tileElement) - { - return true; - } - } - return false; -} - -static int32_t tile_element_get_total_element_count(TileElement* tileElement) -{ - int32_t elementCount; - rct_scenery_entry* sceneryEntry; - rct_large_scenery_tile* tile; - - switch (tileElement->GetType()) - { - case TILE_ELEMENT_TYPE_PATH: - case TILE_ELEMENT_TYPE_SMALL_SCENERY: - case TILE_ELEMENT_TYPE_WALL: - return 1; - - case TILE_ELEMENT_TYPE_LARGE_SCENERY: - sceneryEntry = tileElement->AsLargeScenery()->GetEntry(); - tile = sceneryEntry->large_scenery.tiles; - elementCount = 0; - do - { - tile++; - elementCount++; - } while (tile->x_offset != (int16_t)(uint16_t)0xFFFF); - return elementCount; - - default: - return 0; - } -} - -/** - * - * rct2: 0x006D2ED2 - */ -static bool track_design_save_can_add_tile_element(TileElement* tileElement) -{ - size_t newElementCount = tile_element_get_total_element_count(tileElement); - if (newElementCount == 0) - { - return false; - } - - // Get number of spare elements left - size_t spareSavedElements = TRACK_MAX_SAVED_TILE_ELEMENTS - _trackSavedTileElementsCount; - if (newElementCount > spareSavedElements) - { - // No more spare saved elements left - return false; - } - - return true; -} - -/** - * - * rct2: 0x006D2F4C - */ -static void track_design_save_push_tile_element(int32_t x, int32_t y, TileElement* tileElement) -{ - if (_trackSavedTileElementsCount < TRACK_MAX_SAVED_TILE_ELEMENTS) - { - _trackSavedTileElements[_trackSavedTileElementsCount++] = tileElement; - map_invalidate_tile_full(x, y); - } -} - -/** - * - * rct2: 0x006D2FA7 - */ -static void track_design_save_push_tile_element_desc( - const rct_object_entry* entry, int32_t x, int32_t y, int32_t z, uint8_t flags, uint8_t primaryColour, - uint8_t secondaryColour) -{ - rct_td6_scenery_element* item = &_trackSavedTileElementsDesc[_trackSavedTileElementsDescCount++]; - item->scenery_object = *entry; - item->x = x / 32; - item->y = y / 32; - item->z = z; - item->flags = flags; - item->primary_colour = primaryColour; - item->secondary_colour = secondaryColour; -} - -static void track_design_save_add_scenery(int32_t x, int32_t y, TileElement* tileElement) -{ - SmallSceneryElement* sceneryElement = tileElement->AsSmallScenery(); - int32_t entryType = sceneryElement->GetEntryIndex(); - auto entry = object_entry_get_entry(OBJECT_TYPE_SMALL_SCENERY, entryType); - - uint8_t flags = 0; - flags |= tileElement->GetDirection(); - flags |= tileElement->AsSmallScenery()->GetSceneryQuadrant() << 2; - - uint8_t primaryColour = sceneryElement->GetPrimaryColour(); - uint8_t secondaryColour = sceneryElement->GetSecondaryColour(); - - track_design_save_push_tile_element(x, y, tileElement); - track_design_save_push_tile_element_desc(entry, x, y, tileElement->base_height, flags, primaryColour, secondaryColour); -} - -static void track_design_save_add_large_scenery(int32_t x, int32_t y, LargeSceneryElement* tileElement) -{ - rct_large_scenery_tile *sceneryTiles, *tile; - int32_t x0, y0, z0, z; - int32_t direction, sequence; - - int32_t entryType = tileElement->GetEntryIndex(); - auto entry = object_entry_get_entry(OBJECT_TYPE_LARGE_SCENERY, entryType); - sceneryTiles = get_large_scenery_entry(entryType)->large_scenery.tiles; - - z = tileElement->base_height; - direction = tileElement->GetDirection(); - sequence = tileElement->GetSequenceIndex(); - - if (!map_large_scenery_get_origin(x, y, z, direction, sequence, &x0, &y0, &z0, nullptr)) - { - return; - } - - // Iterate through each tile of the large scenery element - sequence = 0; - for (tile = sceneryTiles; tile->x_offset != -1; tile++, sequence++) - { - int16_t offsetX = tile->x_offset; - int16_t offsetY = tile->y_offset; - rotate_map_coordinates(&offsetX, &offsetY, direction); - - x = x0 + offsetX; - y = y0 + offsetY; - z = (z0 + tile->z_offset) / 8; - auto largeElement = map_get_large_scenery_segment(x, y, z, direction, sequence); - if (largeElement != nullptr) - { - if (sequence == 0) - { - uint8_t flags = largeElement->GetDirection(); - uint8_t primaryColour = largeElement->GetPrimaryColour(); - uint8_t secondaryColour = largeElement->GetSecondaryColour(); - - track_design_save_push_tile_element_desc(entry, x, y, z, flags, primaryColour, secondaryColour); - } - track_design_save_push_tile_element(x, y, reinterpret_cast(largeElement)); - } - } -} - -static void track_design_save_add_wall(int32_t x, int32_t y, TileElement* tileElement) -{ - int32_t entryType = tileElement->AsWall()->GetEntryIndex(); - auto entry = object_entry_get_entry(OBJECT_TYPE_WALLS, entryType); - - uint8_t flags = 0; - flags |= tileElement->GetDirection(); - flags |= tileElement->AsWall()->GetTertiaryColour() << 2; - - uint8_t secondaryColour = tileElement->AsWall()->GetSecondaryColour(); - uint8_t primaryColour = tileElement->AsWall()->GetPrimaryColour(); - - track_design_save_push_tile_element(x, y, tileElement); - track_design_save_push_tile_element_desc(entry, x, y, tileElement->base_height, flags, primaryColour, secondaryColour); -} - -static void track_design_save_add_footpath(int32_t x, int32_t y, TileElement* tileElement) -{ - int32_t entryType = tileElement->AsPath()->GetPathEntryIndex(); - auto entry = object_entry_get_entry(OBJECT_TYPE_PATHS, entryType); - - uint8_t flags = 0; - flags |= tileElement->AsPath()->GetEdges(); - flags |= (tileElement->AsPath()->GetSlopeDirection()) << 5; - if (tileElement->AsPath()->IsSloped()) - flags |= 0b00010000; - if (tileElement->AsPath()->IsQueue()) - flags |= 1 << 7; - - track_design_save_push_tile_element(x, y, tileElement); - track_design_save_push_tile_element_desc(entry, x, y, tileElement->base_height, flags, 0, 0); -} - -/** - * - * rct2: 0x006D2B3C - */ -static bool track_design_save_add_tile_element(int32_t interactionType, int32_t x, int32_t y, TileElement* tileElement) -{ - if (!track_design_save_can_add_tile_element(tileElement)) - { - return false; - } - - switch (interactionType) - { - case VIEWPORT_INTERACTION_ITEM_SCENERY: - track_design_save_add_scenery(x, y, tileElement); - return true; - case VIEWPORT_INTERACTION_ITEM_LARGE_SCENERY: - track_design_save_add_large_scenery(x, y, tileElement->AsLargeScenery()); - return true; - case VIEWPORT_INTERACTION_ITEM_WALL: - track_design_save_add_wall(x, y, tileElement); - return true; - case VIEWPORT_INTERACTION_ITEM_FOOTPATH: - track_design_save_add_footpath(x, y, tileElement); - return true; - default: - return false; - } -} - -/** - * - * rct2: 0x006D2F78 - */ -static void track_design_save_pop_tile_element(int32_t x, int32_t y, TileElement* tileElement) -{ - map_invalidate_tile_full(x, y); - - // Find index of map element to remove - size_t removeIndex = SIZE_MAX; - for (size_t i = 0; i < _trackSavedTileElementsCount; i++) - { - if (_trackSavedTileElements[i] == tileElement) - { - removeIndex = i; - } - } - - // Remove map element from list - if (removeIndex != SIZE_MAX) - { - size_t remainingNumItems = _trackSavedTileElementsCount - removeIndex - 1; - if (remainingNumItems > 0) - { - memmove( - &_trackSavedTileElements[removeIndex], &_trackSavedTileElements[removeIndex + 1], - remainingNumItems * sizeof(TileElement*)); - } - _trackSavedTileElementsCount--; - _trackSavedTileElements[_trackSavedTileElementsCount] = nullptr; - } -} - -/** - * - * rct2: 0x006D2FDD - */ -static void track_design_save_pop_tile_element_desc( - const rct_object_entry* entry, int32_t x, int32_t y, int32_t z, uint8_t flags) -{ - size_t removeIndex = SIZE_MAX; - for (size_t i = 0; i < _trackSavedTileElementsDescCount; i++) - { - rct_td6_scenery_element* item = &_trackSavedTileElementsDesc[i]; - if (item->x != x / 32) - continue; - if (item->y != y / 32) - continue; - if (item->z != z) - continue; - if (item->flags != flags) - continue; - if (!object_entry_compare(&item->scenery_object, entry)) - continue; - - removeIndex = i; - } - - if (removeIndex != SIZE_MAX) - { - size_t remainingNumItems = _trackSavedTileElementsDescCount - removeIndex - 1; - if (remainingNumItems > 0) - { - memmove( - &_trackSavedTileElementsDesc[removeIndex], &_trackSavedTileElementsDesc[removeIndex + 1], - remainingNumItems * sizeof(rct_td6_scenery_element)); - } - _trackSavedTileElementsDescCount--; - } -} - -static void track_design_save_remove_scenery(int32_t x, int32_t y, TileElement* tileElement) -{ - int32_t entryType = tileElement->AsSmallScenery()->GetEntryIndex(); - auto entry = object_entry_get_entry(OBJECT_TYPE_SMALL_SCENERY, entryType); - - uint8_t flags = 0; - flags |= tileElement->GetDirection(); - flags |= tileElement->AsSmallScenery()->GetSceneryQuadrant() << 2; - - track_design_save_pop_tile_element(x, y, tileElement); - track_design_save_pop_tile_element_desc(entry, x, y, tileElement->base_height, flags); -} - -static void track_design_save_remove_large_scenery(int32_t x, int32_t y, LargeSceneryElement* tileElement) -{ - rct_large_scenery_tile *sceneryTiles, *tile; - int32_t x0, y0, z0, z; - int32_t direction, sequence; - - int32_t entryType = tileElement->GetEntryIndex(); - auto entry = object_entry_get_entry(OBJECT_TYPE_LARGE_SCENERY, entryType); - sceneryTiles = get_large_scenery_entry(entryType)->large_scenery.tiles; - - z = tileElement->base_height; - direction = tileElement->GetDirection(); - sequence = tileElement->GetSequenceIndex(); - - if (!map_large_scenery_get_origin(x, y, z, direction, sequence, &x0, &y0, &z0, nullptr)) - { - return; - } - - // Iterate through each tile of the large scenery element - sequence = 0; - for (tile = sceneryTiles; tile->x_offset != -1; tile++, sequence++) - { - int16_t offsetX = tile->x_offset; - int16_t offsetY = tile->y_offset; - rotate_map_coordinates(&offsetX, &offsetY, direction); - - x = x0 + offsetX; - y = y0 + offsetY; - z = (z0 + tile->z_offset) / 8; - auto largeElement = map_get_large_scenery_segment(x, y, z, direction, sequence); - if (largeElement != nullptr) - { - if (sequence == 0) - { - uint8_t flags = largeElement->GetDirection(); - track_design_save_pop_tile_element_desc(entry, x, y, z, flags); - } - track_design_save_pop_tile_element(x, y, reinterpret_cast(largeElement)); - } - } -} - -static void track_design_save_remove_wall(int32_t x, int32_t y, TileElement* tileElement) -{ - int32_t entryType = tileElement->AsWall()->GetEntryIndex(); - auto entry = object_entry_get_entry(OBJECT_TYPE_WALLS, entryType); - - uint8_t flags = 0; - flags |= tileElement->GetDirection(); - flags |= tileElement->AsWall()->GetTertiaryColour() << 2; - - track_design_save_pop_tile_element(x, y, tileElement); - track_design_save_pop_tile_element_desc(entry, x, y, tileElement->base_height, flags); -} - -static void track_design_save_remove_footpath(int32_t x, int32_t y, TileElement* tileElement) -{ - int32_t entryType = tileElement->AsPath()->GetPathEntryIndex(); - auto entry = object_entry_get_entry(OBJECT_TYPE_PATHS, entryType); - - uint8_t flags = 0; - flags |= tileElement->AsPath()->GetEdges(); - if (tileElement->AsPath()->IsSloped()) - flags |= (1 << 4); - flags |= (tileElement->AsPath()->GetSlopeDirection()) << 5; - if (tileElement->AsPath()->IsQueue()) - flags |= (1 << 7); - - track_design_save_pop_tile_element(x, y, tileElement); - track_design_save_pop_tile_element_desc(entry, x, y, tileElement->base_height, flags); -} - -/** - * - * rct2: 0x006D2B3C - */ -static void track_design_save_remove_tile_element(int32_t interactionType, int32_t x, int32_t y, TileElement* tileElement) -{ - switch (interactionType) - { - case VIEWPORT_INTERACTION_ITEM_SCENERY: - track_design_save_remove_scenery(x, y, tileElement); - break; - case VIEWPORT_INTERACTION_ITEM_LARGE_SCENERY: - track_design_save_remove_large_scenery(x, y, tileElement->AsLargeScenery()); - break; - case VIEWPORT_INTERACTION_ITEM_WALL: - track_design_save_remove_wall(x, y, tileElement); - break; - case VIEWPORT_INTERACTION_ITEM_FOOTPATH: - track_design_save_remove_footpath(x, y, tileElement); - break; - } -} - -static bool track_design_save_should_select_scenery_around(ride_id_t rideIndex, TileElement* tileElement) -{ - switch (tileElement->GetType()) - { - case TILE_ELEMENT_TYPE_PATH: - if (tileElement->AsPath()->IsQueue() && tileElement->AsPath()->GetRideIndex() == rideIndex) - return true; - break; - case TILE_ELEMENT_TYPE_TRACK: - if (tileElement->AsTrack()->GetRideIndex() == rideIndex) - return true; - break; - case TILE_ELEMENT_TYPE_ENTRANCE: - // FIXME: This will always break and return false! - if (tileElement->AsEntrance()->GetEntranceType() != ENTRANCE_TYPE_RIDE_ENTRANCE) - break; - if (tileElement->AsEntrance()->GetEntranceType() != ENTRANCE_TYPE_RIDE_EXIT) - break; - if (tileElement->AsEntrance()->GetRideIndex() == rideIndex) - return true; - break; - } - return false; -} - -static void track_design_save_select_nearby_scenery_for_tile(ride_id_t rideIndex, int32_t cx, int32_t cy) -{ - TileElement* tileElement; - - for (int32_t y = cy - TRACK_NEARBY_SCENERY_DISTANCE; y <= cy + TRACK_NEARBY_SCENERY_DISTANCE; y++) - { - for (int32_t x = cx - TRACK_NEARBY_SCENERY_DISTANCE; x <= cx + TRACK_NEARBY_SCENERY_DISTANCE; x++) - { - tileElement = map_get_first_element_at(x, y); - do - { - int32_t interactionType = VIEWPORT_INTERACTION_ITEM_NONE; - switch (tileElement->GetType()) - { - case TILE_ELEMENT_TYPE_PATH: - if (!tileElement->AsPath()->IsQueue()) - interactionType = VIEWPORT_INTERACTION_ITEM_FOOTPATH; - else if (tileElement->AsPath()->GetRideIndex() == rideIndex) - interactionType = VIEWPORT_INTERACTION_ITEM_FOOTPATH; - break; - case TILE_ELEMENT_TYPE_SMALL_SCENERY: - interactionType = VIEWPORT_INTERACTION_ITEM_SCENERY; - break; - case TILE_ELEMENT_TYPE_WALL: - interactionType = VIEWPORT_INTERACTION_ITEM_WALL; - break; - case TILE_ELEMENT_TYPE_LARGE_SCENERY: - interactionType = VIEWPORT_INTERACTION_ITEM_LARGE_SCENERY; - break; - } - - if (interactionType != VIEWPORT_INTERACTION_ITEM_NONE) - { - if (!track_design_save_contains_tile_element(tileElement)) - { - track_design_save_add_tile_element(interactionType, x * 32, y * 32, tileElement); - } - } - } while (!(tileElement++)->IsLastForTile()); - } - } -} - -/* Based on rct2: 0x006D2897 */ -static bool track_design_save_copy_scenery_to_td6(rct_track_td6* td6) -{ - // Copy TD6 scenery elements to new memory and add end marker - size_t totalSceneryElementsSize = _trackSavedTileElementsDescCount * sizeof(rct_td6_scenery_element); - td6->scenery_elements = (rct_td6_scenery_element*)malloc(totalSceneryElementsSize + 1); - std::memcpy(td6->scenery_elements, _trackSavedTileElementsDesc, totalSceneryElementsSize); - *((uint8_t*)&td6->scenery_elements[_trackSavedTileElementsDescCount]) = 0xFF; - - // Run an element loop - for (size_t i = 0; i < _trackSavedTileElementsDescCount; i++) - { - rct_td6_scenery_element* scenery = &td6->scenery_elements[i]; - - switch (object_entry_get_type(&scenery->scenery_object)) - { - case OBJECT_TYPE_PATHS: - { - uint8_t slope = (scenery->flags & 0x60) >> 5; - slope -= _trackSaveDirection; - - scenery->flags &= 0x9F; - scenery->flags |= ((slope & 3) << 5); - - // Direction of connection on path - uint8_t direction = scenery->flags & 0xF; - // Rotate the direction by the track direction - direction = ((direction << 4) >> _trackSaveDirection); - - scenery->flags &= 0xF0; - scenery->flags |= (direction & 0xF) | (direction >> 4); - break; - } - case OBJECT_TYPE_WALLS: - { - uint8_t direction = scenery->flags & 3; - direction -= _trackSaveDirection; - - scenery->flags &= 0xFC; - scenery->flags |= (direction & 3); - break; - } - default: - { - uint8_t direction = scenery->flags & 3; - uint8_t quadrant = (scenery->flags & 0x0C) >> 2; - - direction -= _trackSaveDirection; - quadrant -= _trackSaveDirection; - - scenery->flags &= 0xF0; - scenery->flags |= (direction & 3) | ((quadrant & 3) << 2); - break; - } - } - - int16_t x = ((uint8_t)scenery->x) * 32 - gTrackPreviewOrigin.x; - int16_t y = ((uint8_t)scenery->y) * 32 - gTrackPreviewOrigin.y; - rotate_map_coordinates(&x, &y, (0 - _trackSaveDirection) & 3); - x /= 32; - y /= 32; - - if (x > 127 || y > 127 || x < -126 || y < -126) - { - context_show_error(STR_CANT_SAVE_TRACK_DESIGN, STR_TRACK_TOO_LARGE_OR_TOO_MUCH_SCENERY); - SafeFree(td6->scenery_elements); - return false; - } - - scenery->x = (int8_t)x; - scenery->y = (int8_t)y; - - int32_t z = scenery->z * 8 - gTrackPreviewOrigin.z; - z /= 8; - if (z > 127 || z < -126) - { - context_show_error(STR_CANT_SAVE_TRACK_DESIGN, STR_TRACK_TOO_LARGE_OR_TOO_MUCH_SCENERY); - SafeFree(td6->scenery_elements); - return false; - } - scenery->z = z; - } - - return true; -} - -/** - * - * rct2: 0x006CE44F - */ -static rct_track_td6* track_design_save_to_td6(ride_id_t rideIndex) -{ - auto ride = get_ride(rideIndex); - if (ride == nullptr) - return nullptr; - - auto td6 = (rct_track_td6*)calloc(1, sizeof(rct_track_td6)); - if (td6 == nullptr) - return nullptr; - - td6->type = ride->type; - auto object = object_entry_get_entry(OBJECT_TYPE_RIDE, ride->subtype); - - // Note we are only copying rct_object_entry in size and - // not the extended as we don't need the chunk size. - std::memcpy(&td6->vehicle_object, object, sizeof(rct_object_entry)); - - td6->ride_mode = ride->mode; - - td6->version_and_colour_scheme = (ride->colour_scheme_type & 3) | (1 << 3); // Version .TD6 - - for (int32_t i = 0; i < RCT12_MAX_VEHICLES_PER_RIDE; i++) - { - td6->vehicle_colours[i].body_colour = ride->vehicle_colours[i].Body; - td6->vehicle_colours[i].trim_colour = ride->vehicle_colours[i].Trim; - td6->vehicle_additional_colour[i] = ride->vehicle_colours[i].Ternary; - } - - for (int32_t i = 0; i < RCT12_NUM_COLOUR_SCHEMES; i++) - { - td6->track_spine_colour[i] = ride->track_colour[i].main; - td6->track_rail_colour[i] = ride->track_colour[i].additional; - td6->track_support_colour[i] = ride->track_colour[i].supports; - } - - td6->depart_flags = ride->depart_flags; - td6->number_of_trains = ride->num_vehicles; - td6->number_of_cars_per_train = ride->num_cars_per_train; - td6->min_waiting_time = ride->min_waiting_time; - td6->max_waiting_time = ride->max_waiting_time; - td6->operation_setting = ride->operation_option; - td6->lift_hill_speed_num_circuits = ride->lift_hill_speed | (ride->num_circuits << 5); - - td6->entrance_style = ride->entrance_style; - td6->max_speed = (int8_t)(ride->max_speed / 65536); - td6->average_speed = (int8_t)(ride->average_speed / 65536); - td6->ride_length = ride_get_total_length(ride) / 65536; - td6->max_positive_vertical_g = ride->max_positive_vertical_g / 32; - td6->max_negative_vertical_g = ride->max_negative_vertical_g / 32; - td6->max_lateral_g = ride->max_lateral_g / 32; - if (ride->type == RIDE_TYPE_MINI_GOLF) - td6->inversions = ride->holes & 0x1F; - else - td6->inversions = ride->inversions & 0x1F; - td6->inversions |= (ride->sheltered_eighths << 5); - td6->drops = ride->drops; - td6->highest_drop_height = ride->highest_drop_height; - - uint16_t total_air_time = (ride->total_air_time * 123) / 1024; - if (total_air_time > 255) - { - total_air_time = 0; - } - td6->total_air_time = (uint8_t)total_air_time; - - td6->excitement = ride->ratings.excitement / 10; - td6->intensity = ride->ratings.intensity / 10; - td6->nausea = ride->ratings.nausea / 10; - - td6->upkeep_cost = ride->upkeep_cost; - td6->flags = 0; - td6->flags2 = 0; - - bool result; - if (td6->type == RIDE_TYPE_MAZE) - { - result = track_design_save_to_td6_for_maze(ride, td6); - } - else - { - result = track_design_save_to_td6_for_tracked_ride(ride, td6); - } - - if (!result) - { - track_design_dispose(td6); - td6 = nullptr; - } - return td6; -} - -/** - * - * rct2: 0x006CEAAE - */ -static bool track_design_save_to_td6_for_maze(const Ride* ride, rct_track_td6* td6) -{ - TileElement* tileElement = nullptr; - bool mapFound = false; - int16_t startX = 0; - int16_t startY = 0; - for (startY = 0; startY < 8192; startY += 32) - { - for (startX = 0; startX < 8192; startX += 32) - { - tileElement = map_get_first_element_at(startX >> 5, startY >> 5); - do - { - if (tileElement->GetType() != TILE_ELEMENT_TYPE_TRACK) - continue; - if (tileElement->AsTrack()->GetRideIndex() == ride->id) - { - mapFound = true; - break; - } - } while (!(tileElement++)->IsLastForTile()); - if (mapFound) - { - break; - } - } - if (mapFound) - { - break; - } - } - - if (mapFound == 0) - { - gGameCommandErrorText = STR_TRACK_TOO_LARGE_OR_TOO_MUCH_SCENERY; - return false; - } - - gTrackPreviewOrigin = { startX, startY, (int16_t)(tileElement->base_height * 8) }; - - size_t numMazeElements = 0; - td6->maze_elements = (rct_td6_maze_element*)calloc(8192, sizeof(rct_td6_maze_element)); - rct_td6_maze_element* maze = td6->maze_elements; - - // x is defined here as we can start the search - // on tile start_x, start_y but then the next row - // must restart on 0 - for (int16_t y = startY, x = startX; y < 8192; y += 32) - { - for (; x < 8192; x += 32) - { - tileElement = map_get_first_element_at(x / 32, y / 32); - do - { - if (tileElement->GetType() != TILE_ELEMENT_TYPE_TRACK) - continue; - if (tileElement->AsTrack()->GetRideIndex() != ride->id) - continue; - - maze->maze_entry = tileElement->AsTrack()->GetMazeEntry(); - maze->x = (x - startX) / 32; - maze->y = (y - startY) / 32; - maze++; - numMazeElements++; - - if (numMazeElements >= 2000) - { - gGameCommandErrorText = STR_TRACK_TOO_LARGE_OR_TOO_MUCH_SCENERY; - SafeFree(td6->maze_elements); - return false; - } - } while (!(tileElement++)->IsLastForTile()); - } - x = 0; - } - - auto location = ride_get_entrance_location(ride, 0); - if (location.isNull()) - { - gGameCommandErrorText = STR_TRACK_TOO_LARGE_OR_TOO_MUCH_SCENERY; - SafeFree(td6->maze_elements); - return false; - } - - int16_t x = location.x * 32; - int16_t y = location.y * 32; - - tileElement = map_get_first_element_at(location.x, location.y); - do - { - if (tileElement->GetType() != TILE_ELEMENT_TYPE_ENTRANCE) - continue; - if (tileElement->AsEntrance()->GetEntranceType() != ENTRANCE_TYPE_RIDE_ENTRANCE) - continue; - if (tileElement->AsEntrance()->GetRideIndex() == ride->id) - break; - } while (!(tileElement++)->IsLastForTile()); - // Add something that stops this from walking off the end - - uint8_t entrance_direction = tileElement->GetDirection(); - maze->direction = entrance_direction; - maze->type = 8; - maze->x = (int8_t)((x - startX) / 32); - maze->y = (int8_t)((y - startY) / 32); - maze++; - numMazeElements++; - - location = ride_get_exit_location(ride, 0); - if (location.isNull()) - { - gGameCommandErrorText = STR_TRACK_TOO_LARGE_OR_TOO_MUCH_SCENERY; - SafeFree(td6->maze_elements); - return false; - } - - x = location.x * 32; - y = location.y * 32; - tileElement = map_get_first_element_at(location.x, location.y); - do - { - if (tileElement->GetType() != TILE_ELEMENT_TYPE_ENTRANCE) - continue; - if (tileElement->AsEntrance()->GetEntranceType() != ENTRANCE_TYPE_RIDE_EXIT) - continue; - if (tileElement->AsEntrance()->GetRideIndex() == ride->id) - break; - } while (!(tileElement++)->IsLastForTile()); - // Add something that stops this from walking off the end - - uint8_t exit_direction = tileElement->GetDirection(); - maze->direction = exit_direction; - maze->type = 0x80; - maze->x = (int8_t)((x - startX) / 32); - maze->y = (int8_t)((y - startY) / 32); - maze++; - maze->all = 0; - maze++; - numMazeElements++; - - // Write end marker - maze->all = 0; - maze++; - numMazeElements++; - - // Trim memory - td6->maze_elements = (rct_td6_maze_element*)realloc(td6->maze_elements, numMazeElements * sizeof(rct_td6_maze_element)); - - // Save global vars as they are still used by scenery - int16_t startZ = gTrackPreviewOrigin.z; - place_virtual_track(td6, PTD_OPERATION_DRAW_OUTLINES, true, GetOrAllocateRide(0), 4096, 4096, 0); - gTrackPreviewOrigin = { startX, startY, startZ }; - - gMapSelectFlags &= ~MAP_SELECT_FLAG_ENABLE_CONSTRUCT; - gMapSelectFlags &= ~MAP_SELECT_FLAG_ENABLE_ARROW; - gMapSelectFlags &= ~MAP_SELECT_FLAG_GREEN; - - td6->space_required_x = ((gTrackPreviewMax.x - gTrackPreviewMin.x) / 32) + 1; - td6->space_required_y = ((gTrackPreviewMax.y - gTrackPreviewMin.y) / 32) + 1; - return true; -} - -/** - * - * rct2: 0x006CE68D - */ -static bool track_design_save_to_td6_for_tracked_ride(const Ride* ride, rct_track_td6* td6) -{ - CoordsXYE trackElement; - if (!ride_try_get_origin_element(ride, &trackElement)) - { - gGameCommandErrorText = STR_TRACK_TOO_LARGE_OR_TOO_MUCH_SCENERY; - return false; - } - - ride_get_start_of_track(&trackElement); - - int32_t z = trackElement.element->base_height * 8; - uint8_t track_type = trackElement.element->AsTrack()->GetTrackType(); - uint8_t direction = trackElement.element->GetDirection(); - _trackSaveDirection = direction; - - if (sub_6C683D(&trackElement.x, &trackElement.y, &z, direction, track_type, 0, &trackElement.element, 0)) - { - gGameCommandErrorText = STR_TRACK_TOO_LARGE_OR_TOO_MUCH_SCENERY; - return 0; - } - - const rct_track_coordinates* trackCoordinates = &TrackCoordinates[trackElement.element->AsTrack()->GetTrackType()]; - // Used in the following loop to know when we have - // completed all of the elements and are back at the - // start. - TileElement* initialMap = trackElement.element; - - int16_t start_x = trackElement.x; - int16_t start_y = trackElement.y; - int16_t start_z = z + trackCoordinates->z_begin; - gTrackPreviewOrigin = { start_x, start_y, start_z }; - - size_t numTrackElements = 0; - td6->track_elements = (rct_td6_track_element*)calloc(TRACK_TD6_MAX_ELEMENTS, sizeof(rct_td6_track_element)); - rct_td6_track_element* track = td6->track_elements; - do - { - track->type = trackElement.element->AsTrack()->GetTrackType(); - if (track->type == TRACK_ELEM_255) - { - track->type = TRACK_ELEM_255_ALIAS; - } - - uint8_t flags; - if (track_element_has_speed_setting(track->type)) - { - flags = trackElement.element->AsTrack()->GetBrakeBoosterSpeed() >> 1; - } - else - { - flags = trackElement.element->AsTrack()->GetSeatRotation(); - } - - if (trackElement.element->AsTrack()->HasChain()) - flags |= (1 << 7); - flags |= trackElement.element->AsTrack()->GetColourScheme() << 4; - if (RideData4[ride->type].flags & RIDE_TYPE_FLAG4_HAS_ALTERNATIVE_TRACK_TYPE - && trackElement.element->AsTrack()->IsInverted()) - { - flags |= TRACK_ELEMENT_FLAG_INVERTED; - } - - track->flags = flags; - track++; - numTrackElements++; - - if (!track_block_get_next(&trackElement, &trackElement, nullptr, nullptr)) - { - break; - } - - z = trackElement.element->base_height * 8; - direction = trackElement.element->GetDirection(); - track_type = trackElement.element->AsTrack()->GetTrackType(); - - if (sub_6C683D(&trackElement.x, &trackElement.y, &z, direction, track_type, 0, &trackElement.element, 0)) - { - break; - } - - if (TRACK_TD6_MAX_ELEMENTS == numTrackElements) - { - free(td6->track_elements); - gGameCommandErrorText = STR_TRACK_TOO_LARGE_OR_TOO_MUCH_SCENERY; - return false; - } - } while (trackElement.element != initialMap); - - td6->track_elements = (rct_td6_track_element*)realloc( - td6->track_elements, numTrackElements * sizeof(rct_td6_track_element) + 1); - *((uint8_t*)&td6->track_elements[numTrackElements]) = 0xFF; - - size_t numEntranceElements = 0; - td6->entrance_elements = (rct_td6_entrance_element*)calloc(32, sizeof(rct_td6_entrance_element)); - rct_td6_entrance_element* entrance = td6->entrance_elements; - - // First entrances, second exits - for (int32_t i = 0; i < 2; i++) - { - for (int32_t station_index = 0; station_index < RCT12_MAX_STATIONS_PER_RIDE; station_index++) - { - z = ride->stations[station_index].Height; - - TileCoordsXYZD location; - if (i == 0) - { - location = ride_get_entrance_location(ride, station_index); - } - else - { - location = ride_get_exit_location(ride, station_index); - } - - if (location.isNull()) - { - continue; - } - - int16_t x = location.x * 32; - int16_t y = location.y * 32; - - TileElement* tile_element = map_get_first_element_at(x >> 5, y >> 5); - do - { - if (tile_element->GetType() != TILE_ELEMENT_TYPE_ENTRANCE) - continue; - if (tile_element->base_height == z) - break; - } while (!(tile_element++)->IsLastForTile()); - // Add something that stops this from walking off the end - - uint8_t entrance_direction = tile_element->GetDirection(); - entrance_direction -= _trackSaveDirection; - entrance_direction &= TILE_ELEMENT_DIRECTION_MASK; - entrance->direction = entrance_direction; - - x -= gTrackPreviewOrigin.x; - y -= gTrackPreviewOrigin.y; - - // Rotate entrance coordinates backwards to the correct direction - rotate_map_coordinates(&x, &y, (0 - _trackSaveDirection) & 3); - entrance->x = x; - entrance->y = y; - - z *= 8; - z -= gTrackPreviewOrigin.z; - z /= 8; - - if (z > 127 || z < -126) - { - gGameCommandErrorText = STR_TRACK_TOO_LARGE_OR_TOO_MUCH_SCENERY; - return 0; - } - - if (z == 0xFF) - { - z = 0x80; - } - - entrance->z = z; - - // If this is the exit version - if (i == 1) - { - entrance->direction |= (1 << 7); - } - entrance++; - numEntranceElements++; - } - } - td6->entrance_elements = (rct_td6_entrance_element*)realloc( - td6->entrance_elements, numEntranceElements * sizeof(rct_td6_entrance_element) + 1); - *((uint8_t*)&td6->entrance_elements[numEntranceElements]) = 0xFF; - - place_virtual_track(td6, PTD_OPERATION_DRAW_OUTLINES, true, GetOrAllocateRide(0), 4096, 4096, 0); - - // Resave global vars for scenery reasons. - gTrackPreviewOrigin = { start_x, start_y, start_z }; - - gMapSelectFlags &= ~MAP_SELECT_FLAG_ENABLE_CONSTRUCT; - gMapSelectFlags &= ~MAP_SELECT_FLAG_ENABLE_ARROW; - gMapSelectFlags &= ~MAP_SELECT_FLAG_GREEN; - - td6->space_required_x = ((gTrackPreviewMax.x - gTrackPreviewMin.x) / 32) + 1; - td6->space_required_y = ((gTrackPreviewMax.y - gTrackPreviewMin.y) / 32) + 1; - return true; -} - -static size_t track_design_get_maze_elements_count(rct_track_td6* td6) -{ - size_t count = 0; - rct_td6_maze_element* mazeElement = td6->maze_elements; - while (mazeElement->all != 0) - { - count++; - mazeElement++; - } - return count; -} - -static size_t track_design_get_track_elements_count(rct_track_td6* td6) -{ - size_t count = 0; - rct_td6_track_element* trackElement = td6->track_elements; - while (trackElement->type != 0xFF) - { - count++; - trackElement++; - } - return count; -} - -static size_t track_design_get_entrance_elements_count(rct_track_td6* td6) -{ - size_t count = 0; - rct_td6_entrance_element* entranceElement = td6->entrance_elements; - while (entranceElement->z != -1) - { - count++; - entranceElement++; - } - return count; -} - -static size_t track_design_get_scenery_elements_count(rct_track_td6* td6) -{ - size_t count = 0; - rct_td6_scenery_element* sceneryElement = td6->scenery_elements; - if (sceneryElement != nullptr) - { - while (sceneryElement->scenery_object.end_flag != 0xFF) - { - count++; - sceneryElement++; - } - } - return count; -} - -struct auto_buffer -{ - void* ptr; - size_t length; - size_t capacity; -}; - -static void auto_buffer_write(auto_buffer* buffer, const void* src, size_t len) -{ - size_t remainingSpace = buffer->capacity - buffer->length; - if (remainingSpace < len) - { - do - { - buffer->capacity = std::max(8, buffer->capacity * 2); - remainingSpace = buffer->capacity - buffer->length; - } while (remainingSpace < len); - - buffer->ptr = realloc(buffer->ptr, buffer->capacity); - } - std::memcpy((void*)((uintptr_t)buffer->ptr + buffer->length), src, len); - buffer->length += len; -} - -/** - * - * rct2: 0x006771DC but not really its branched from that - * quite far. - */ -bool track_design_save_to_file(const utf8* path) -{ - rct_track_td6* td6 = _trackDesign; - const rct_td6_maze_element EndMarkerForMaze = {}; - const uint8_t EndMarker = 0xFF; - - window_close_construction_windows(); - - // Create TD6 data buffer - auto_buffer td6Buffer = {}; - auto_buffer_write(&td6Buffer, td6, 0xA3); - if (td6->type == RIDE_TYPE_MAZE) - { - auto_buffer_write( - &td6Buffer, td6->maze_elements, track_design_get_maze_elements_count(td6) * sizeof(rct_td6_maze_element)); - auto_buffer_write(&td6Buffer, &EndMarkerForMaze, sizeof(EndMarkerForMaze)); - } - else - { - auto_buffer_write( - &td6Buffer, td6->track_elements, track_design_get_track_elements_count(td6) * sizeof(rct_td6_track_element)); - auto_buffer_write(&td6Buffer, &EndMarker, sizeof(EndMarker)); - auto_buffer_write( - &td6Buffer, td6->entrance_elements, - track_design_get_entrance_elements_count(td6) * sizeof(rct_td6_entrance_element)); - auto_buffer_write(&td6Buffer, &EndMarker, sizeof(EndMarker)); - } - auto_buffer_write( - &td6Buffer, td6->scenery_elements, track_design_get_scenery_elements_count(td6) * sizeof(rct_td6_scenery_element)); - auto_buffer_write(&td6Buffer, &EndMarker, sizeof(EndMarker)); - - // Encode TD6 data - uint8_t* encodedData = (uint8_t*)malloc(0x8000); - assert(td6Buffer.ptr != nullptr); - size_t encodedDataLength = sawyercoding_encode_td6((uint8_t*)td6Buffer.ptr, encodedData, td6Buffer.length); - - // Save encoded TD6 data to file - bool result; - log_verbose("saving track %s", path); - result = writeentirefile(path, encodedData, encodedDataLength); - if (!result) - { - log_error("Failed to save %s", path); - } - - free(encodedData); - free(td6Buffer.ptr); - return result; -} +// +//static size_t _trackSavedTileElementsCount; +//static TileElement* _trackSavedTileElements[TRACK_MAX_SAVED_TILE_ELEMENTS]; +// +//static size_t _trackSavedTileElementsDescCount; +//static rct_td6_scenery_element _trackSavedTileElementsDesc[TRACK_MAX_SAVED_TILE_ELEMENTS]; +// +//static rct_track_td6* _trackDesign; +//static uint8_t _trackSaveDirection; + +//static bool track_design_save_should_select_scenery_around(ride_id_t rideIndex, TileElement* tileElement); +//static void track_design_save_select_nearby_scenery_for_tile(ride_id_t rideIndex, int32_t cx, int32_t cy); +//static bool track_design_save_add_tile_element(int32_t interactionType, int32_t x, int32_t y, TileElement* tileElement); +//static void track_design_save_remove_tile_element(int32_t interactionType, int32_t x, int32_t y, TileElement* tileElement); +//static bool track_design_save_copy_scenery_to_td6(rct_track_td6* td6); +//static rct_track_td6* track_design_save_to_td6(ride_id_t rideIndex); +//static bool track_design_save_to_td6_for_maze(const Ride* ride, rct_track_td6* td6); +//static bool track_design_save_to_td6_for_tracked_ride(const Ride* ride, rct_track_td6* td6); +// +//void track_design_save_init() +//{ +// _trackSavedTileElementsCount = 0; +// _trackSavedTileElementsDescCount = 0; +// +// std::memset(_trackSavedTileElements, 0, sizeof(_trackSavedTileElements)); +// std::memset(_trackSavedTileElementsDesc, 0, sizeof(_trackSavedTileElementsDesc)); +//} +// +///** +// * +// * rct2: 0x006D2B07 +// */ +//void track_design_save_select_tile_element( +// int32_t interactionType, int32_t x, int32_t y, TileElement* tileElement, bool collect) +//{ +// if (track_design_save_contains_tile_element(tileElement)) +// { +// if (!collect) +// { +// track_design_save_remove_tile_element(interactionType, x, y, tileElement); +// } +// } +// else +// { +// if (collect) +// { +// if (!track_design_save_add_tile_element(interactionType, x, y, tileElement)) +// { +// context_show_error( +// STR_SAVE_TRACK_SCENERY_UNABLE_TO_SELECT_ADDITIONAL_ITEM_OF_SCENERY, +// STR_SAVE_TRACK_SCENERY_TOO_MANY_ITEMS_SELECTED); +// } +// } +// } +//} +// +///** +// * +// * rct2: 0x006D303D +// */ +//void track_design_save_select_nearby_scenery(ride_id_t rideIndex) +//{ +// TileElement* tileElement; +// +// for (int32_t y = 0; y < MAXIMUM_MAP_SIZE_TECHNICAL; y++) +// { +// for (int32_t x = 0; x < MAXIMUM_MAP_SIZE_TECHNICAL; x++) +// { +// tileElement = map_get_first_element_at(x, y); +// do +// { +// if (track_design_save_should_select_scenery_around(rideIndex, tileElement)) +// { +// track_design_save_select_nearby_scenery_for_tile(rideIndex, x, y); +// break; +// } +// } while (!(tileElement++)->IsLastForTile()); +// } +// } +// gfx_invalidate_screen(); +//} +// +///** +// * +// * rct2: 0x006D3026 +// */ +//void track_design_save_reset_scenery() +//{ +// track_design_save_init(); +// gfx_invalidate_screen(); +//} +// +//static void track_design_save_callback(int32_t result, [[maybe_unused]] const utf8* path) +//{ +// free(_trackDesign->track_elements); +// free(_trackDesign->entrance_elements); +// free(_trackDesign->scenery_elements); +// free(_trackDesign); +// +// if (result == MODAL_RESULT_OK) +// { +// track_repository_scan(); +// } +// gfx_invalidate_screen(); +//} +// +///** +// * +// * rct2: 0x006D2804, 0x006D264D +// */ +//bool track_design_save(ride_id_t rideIndex) +//{ +// auto ride = get_ride(rideIndex); +// if (ride == nullptr || !(ride->lifecycle_flags & RIDE_LIFECYCLE_TESTED)) +// { +// context_show_error(STR_CANT_SAVE_TRACK_DESIGN, gGameCommandErrorText); +// return false; +// } +// +// if (!ride_has_ratings(ride)) +// { +// context_show_error(STR_CANT_SAVE_TRACK_DESIGN, gGameCommandErrorText); +// return false; +// } +// +// _trackDesign = track_design_save_to_td6(rideIndex); +// if (_trackDesign == nullptr) +// { +// context_show_error(STR_CANT_SAVE_TRACK_DESIGN, gGameCommandErrorText); +// return false; +// } +// +// if (gTrackDesignSaveMode) +// { +// if (!track_design_save_copy_scenery_to_td6(_trackDesign)) +// { +// free(_trackDesign->track_elements); +// free(_trackDesign->entrance_elements); +// free(_trackDesign); +// return false; +// } +// } +// +// auto trackName = ride->GetName(); +// auto intent = Intent(WC_LOADSAVE); +// intent.putExtra(INTENT_EXTRA_LOADSAVE_TYPE, LOADSAVETYPE_SAVE | LOADSAVETYPE_TRACK); +// intent.putExtra(INTENT_EXTRA_PATH, trackName); +// intent.putExtra(INTENT_EXTRA_CALLBACK, (void*)track_design_save_callback); +// context_open_intent(&intent); +// +// return true; +//} +// +//bool track_design_save_contains_tile_element(const TileElement* tileElement) +//{ +// for (size_t i = 0; i < _trackSavedTileElementsCount; i++) +// { +// if (_trackSavedTileElements[i] == tileElement) +// { +// return true; +// } +// } +// return false; +//} +// +//static int32_t tile_element_get_total_element_count(TileElement* tileElement) +//{ +// int32_t elementCount; +// rct_scenery_entry* sceneryEntry; +// rct_large_scenery_tile* tile; +// +// switch (tileElement->GetType()) +// { +// case TILE_ELEMENT_TYPE_PATH: +// case TILE_ELEMENT_TYPE_SMALL_SCENERY: +// case TILE_ELEMENT_TYPE_WALL: +// return 1; +// +// case TILE_ELEMENT_TYPE_LARGE_SCENERY: +// sceneryEntry = tileElement->AsLargeScenery()->GetEntry(); +// tile = sceneryEntry->large_scenery.tiles; +// elementCount = 0; +// do +// { +// tile++; +// elementCount++; +// } while (tile->x_offset != (int16_t)(uint16_t)0xFFFF); +// return elementCount; +// +// default: +// return 0; +// } +//} +// +///** +// * +// * rct2: 0x006D2ED2 +// */ +//static bool track_design_save_can_add_tile_element(TileElement* tileElement) +//{ +// size_t newElementCount = tile_element_get_total_element_count(tileElement); +// if (newElementCount == 0) +// { +// return false; +// } +// +// // Get number of spare elements left +// size_t spareSavedElements = TRACK_MAX_SAVED_TILE_ELEMENTS - _trackSavedTileElementsCount; +// if (newElementCount > spareSavedElements) +// { +// // No more spare saved elements left +// return false; +// } +// +// return true; +//} +// +///** +// * +// * rct2: 0x006D2F4C +// */ +//static void track_design_save_push_tile_element(int32_t x, int32_t y, TileElement* tileElement) +//{ +// if (_trackSavedTileElementsCount < TRACK_MAX_SAVED_TILE_ELEMENTS) +// { +// _trackSavedTileElements[_trackSavedTileElementsCount++] = tileElement; +// map_invalidate_tile_full(x, y); +// } +//} +// +///** +// * +// * rct2: 0x006D2FA7 +// */ +//static void track_design_save_push_tile_element_desc( +// const rct_object_entry* entry, int32_t x, int32_t y, int32_t z, uint8_t flags, uint8_t primaryColour, +// uint8_t secondaryColour) +//{ +// rct_td6_scenery_element* item = &_trackSavedTileElementsDesc[_trackSavedTileElementsDescCount++]; +// item->scenery_object = *entry; +// item->x = x / 32; +// item->y = y / 32; +// item->z = z; +// item->flags = flags; +// item->primary_colour = primaryColour; +// item->secondary_colour = secondaryColour; +//} +// +//static void track_design_save_add_scenery(int32_t x, int32_t y, TileElement* tileElement) +//{ +// SmallSceneryElement* sceneryElement = tileElement->AsSmallScenery(); +// int32_t entryType = sceneryElement->GetEntryIndex(); +// auto entry = object_entry_get_entry(OBJECT_TYPE_SMALL_SCENERY, entryType); +// +// uint8_t flags = 0; +// flags |= tileElement->GetDirection(); +// flags |= tileElement->AsSmallScenery()->GetSceneryQuadrant() << 2; +// +// uint8_t primaryColour = sceneryElement->GetPrimaryColour(); +// uint8_t secondaryColour = sceneryElement->GetSecondaryColour(); +// +// track_design_save_push_tile_element(x, y, tileElement); +// track_design_save_push_tile_element_desc(entry, x, y, tileElement->base_height, flags, primaryColour, secondaryColour); +//} +// +//static void track_design_save_add_large_scenery(int32_t x, int32_t y, LargeSceneryElement* tileElement) +//{ +// rct_large_scenery_tile *sceneryTiles, *tile; +// int32_t x0, y0, z0, z; +// int32_t direction, sequence; +// +// int32_t entryType = tileElement->GetEntryIndex(); +// auto entry = object_entry_get_entry(OBJECT_TYPE_LARGE_SCENERY, entryType); +// sceneryTiles = get_large_scenery_entry(entryType)->large_scenery.tiles; +// +// z = tileElement->base_height; +// direction = tileElement->GetDirection(); +// sequence = tileElement->GetSequenceIndex(); +// +// if (!map_large_scenery_get_origin(x, y, z, direction, sequence, &x0, &y0, &z0, nullptr)) +// { +// return; +// } +// +// // Iterate through each tile of the large scenery element +// sequence = 0; +// for (tile = sceneryTiles; tile->x_offset != -1; tile++, sequence++) +// { +// int16_t offsetX = tile->x_offset; +// int16_t offsetY = tile->y_offset; +// rotate_map_coordinates(&offsetX, &offsetY, direction); +// +// x = x0 + offsetX; +// y = y0 + offsetY; +// z = (z0 + tile->z_offset) / 8; +// auto largeElement = map_get_large_scenery_segment(x, y, z, direction, sequence); +// if (largeElement != nullptr) +// { +// if (sequence == 0) +// { +// uint8_t flags = largeElement->GetDirection(); +// uint8_t primaryColour = largeElement->GetPrimaryColour(); +// uint8_t secondaryColour = largeElement->GetSecondaryColour(); +// +// track_design_save_push_tile_element_desc(entry, x, y, z, flags, primaryColour, secondaryColour); +// } +// track_design_save_push_tile_element(x, y, reinterpret_cast(largeElement)); +// } +// } +//} +// +//static void track_design_save_add_wall(int32_t x, int32_t y, TileElement* tileElement) +//{ +// int32_t entryType = tileElement->AsWall()->GetEntryIndex(); +// auto entry = object_entry_get_entry(OBJECT_TYPE_WALLS, entryType); +// +// uint8_t flags = 0; +// flags |= tileElement->GetDirection(); +// flags |= tileElement->AsWall()->GetTertiaryColour() << 2; +// +// uint8_t secondaryColour = tileElement->AsWall()->GetSecondaryColour(); +// uint8_t primaryColour = tileElement->AsWall()->GetPrimaryColour(); +// +// track_design_save_push_tile_element(x, y, tileElement); +// track_design_save_push_tile_element_desc(entry, x, y, tileElement->base_height, flags, primaryColour, secondaryColour); +//} +// +//static void track_design_save_add_footpath(int32_t x, int32_t y, TileElement* tileElement) +//{ +// int32_t entryType = tileElement->AsPath()->GetPathEntryIndex(); +// auto entry = object_entry_get_entry(OBJECT_TYPE_PATHS, entryType); +// +// uint8_t flags = 0; +// flags |= tileElement->AsPath()->GetEdges(); +// flags |= (tileElement->AsPath()->GetSlopeDirection()) << 5; +// if (tileElement->AsPath()->IsSloped()) +// flags |= 0b00010000; +// if (tileElement->AsPath()->IsQueue()) +// flags |= 1 << 7; +// +// track_design_save_push_tile_element(x, y, tileElement); +// track_design_save_push_tile_element_desc(entry, x, y, tileElement->base_height, flags, 0, 0); +//} +// +///** +// * +// * rct2: 0x006D2B3C +// */ +//static bool track_design_save_add_tile_element(int32_t interactionType, int32_t x, int32_t y, TileElement* tileElement) +//{ +// if (!track_design_save_can_add_tile_element(tileElement)) +// { +// return false; +// } +// +// switch (interactionType) +// { +// case VIEWPORT_INTERACTION_ITEM_SCENERY: +// track_design_save_add_scenery(x, y, tileElement); +// return true; +// case VIEWPORT_INTERACTION_ITEM_LARGE_SCENERY: +// track_design_save_add_large_scenery(x, y, tileElement->AsLargeScenery()); +// return true; +// case VIEWPORT_INTERACTION_ITEM_WALL: +// track_design_save_add_wall(x, y, tileElement); +// return true; +// case VIEWPORT_INTERACTION_ITEM_FOOTPATH: +// track_design_save_add_footpath(x, y, tileElement); +// return true; +// default: +// return false; +// } +//} +// +///** +// * +// * rct2: 0x006D2F78 +// */ +//static void track_design_save_pop_tile_element(int32_t x, int32_t y, TileElement* tileElement) +//{ +// map_invalidate_tile_full(x, y); +// +// // Find index of map element to remove +// size_t removeIndex = SIZE_MAX; +// for (size_t i = 0; i < _trackSavedTileElementsCount; i++) +// { +// if (_trackSavedTileElements[i] == tileElement) +// { +// removeIndex = i; +// } +// } +// +// // Remove map element from list +// if (removeIndex != SIZE_MAX) +// { +// size_t remainingNumItems = _trackSavedTileElementsCount - removeIndex - 1; +// if (remainingNumItems > 0) +// { +// memmove( +// &_trackSavedTileElements[removeIndex], &_trackSavedTileElements[removeIndex + 1], +// remainingNumItems * sizeof(TileElement*)); +// } +// _trackSavedTileElementsCount--; +// _trackSavedTileElements[_trackSavedTileElementsCount] = nullptr; +// } +//} +// +///** +// * +// * rct2: 0x006D2FDD +// */ +//static void track_design_save_pop_tile_element_desc( +// const rct_object_entry* entry, int32_t x, int32_t y, int32_t z, uint8_t flags) +//{ +// size_t removeIndex = SIZE_MAX; +// for (size_t i = 0; i < _trackSavedTileElementsDescCount; i++) +// { +// rct_td6_scenery_element* item = &_trackSavedTileElementsDesc[i]; +// if (item->x != x / 32) +// continue; +// if (item->y != y / 32) +// continue; +// if (item->z != z) +// continue; +// if (item->flags != flags) +// continue; +// if (!object_entry_compare(&item->scenery_object, entry)) +// continue; +// +// removeIndex = i; +// } +// +// if (removeIndex != SIZE_MAX) +// { +// size_t remainingNumItems = _trackSavedTileElementsDescCount - removeIndex - 1; +// if (remainingNumItems > 0) +// { +// memmove( +// &_trackSavedTileElementsDesc[removeIndex], &_trackSavedTileElementsDesc[removeIndex + 1], +// remainingNumItems * sizeof(rct_td6_scenery_element)); +// } +// _trackSavedTileElementsDescCount--; +// } +//} +// +//static void track_design_save_remove_scenery(int32_t x, int32_t y, TileElement* tileElement) +//{ +// int32_t entryType = tileElement->AsSmallScenery()->GetEntryIndex(); +// auto entry = object_entry_get_entry(OBJECT_TYPE_SMALL_SCENERY, entryType); +// +// uint8_t flags = 0; +// flags |= tileElement->GetDirection(); +// flags |= tileElement->AsSmallScenery()->GetSceneryQuadrant() << 2; +// +// track_design_save_pop_tile_element(x, y, tileElement); +// track_design_save_pop_tile_element_desc(entry, x, y, tileElement->base_height, flags); +//} +// +//static void track_design_save_remove_large_scenery(int32_t x, int32_t y, LargeSceneryElement* tileElement) +//{ +// rct_large_scenery_tile *sceneryTiles, *tile; +// int32_t x0, y0, z0, z; +// int32_t direction, sequence; +// +// int32_t entryType = tileElement->GetEntryIndex(); +// auto entry = object_entry_get_entry(OBJECT_TYPE_LARGE_SCENERY, entryType); +// sceneryTiles = get_large_scenery_entry(entryType)->large_scenery.tiles; +// +// z = tileElement->base_height; +// direction = tileElement->GetDirection(); +// sequence = tileElement->GetSequenceIndex(); +// +// if (!map_large_scenery_get_origin(x, y, z, direction, sequence, &x0, &y0, &z0, nullptr)) +// { +// return; +// } +// +// // Iterate through each tile of the large scenery element +// sequence = 0; +// for (tile = sceneryTiles; tile->x_offset != -1; tile++, sequence++) +// { +// int16_t offsetX = tile->x_offset; +// int16_t offsetY = tile->y_offset; +// rotate_map_coordinates(&offsetX, &offsetY, direction); +// +// x = x0 + offsetX; +// y = y0 + offsetY; +// z = (z0 + tile->z_offset) / 8; +// auto largeElement = map_get_large_scenery_segment(x, y, z, direction, sequence); +// if (largeElement != nullptr) +// { +// if (sequence == 0) +// { +// uint8_t flags = largeElement->GetDirection(); +// track_design_save_pop_tile_element_desc(entry, x, y, z, flags); +// } +// track_design_save_pop_tile_element(x, y, reinterpret_cast(largeElement)); +// } +// } +//} +// +//static void track_design_save_remove_wall(int32_t x, int32_t y, TileElement* tileElement) +//{ +// int32_t entryType = tileElement->AsWall()->GetEntryIndex(); +// auto entry = object_entry_get_entry(OBJECT_TYPE_WALLS, entryType); +// +// uint8_t flags = 0; +// flags |= tileElement->GetDirection(); +// flags |= tileElement->AsWall()->GetTertiaryColour() << 2; +// +// track_design_save_pop_tile_element(x, y, tileElement); +// track_design_save_pop_tile_element_desc(entry, x, y, tileElement->base_height, flags); +//} +// +//static void track_design_save_remove_footpath(int32_t x, int32_t y, TileElement* tileElement) +//{ +// int32_t entryType = tileElement->AsPath()->GetPathEntryIndex(); +// auto entry = object_entry_get_entry(OBJECT_TYPE_PATHS, entryType); +// +// uint8_t flags = 0; +// flags |= tileElement->AsPath()->GetEdges(); +// if (tileElement->AsPath()->IsSloped()) +// flags |= (1 << 4); +// flags |= (tileElement->AsPath()->GetSlopeDirection()) << 5; +// if (tileElement->AsPath()->IsQueue()) +// flags |= (1 << 7); +// +// track_design_save_pop_tile_element(x, y, tileElement); +// track_design_save_pop_tile_element_desc(entry, x, y, tileElement->base_height, flags); +//} +// +///** +// * +// * rct2: 0x006D2B3C +// */ +//static void track_design_save_remove_tile_element(int32_t interactionType, int32_t x, int32_t y, TileElement* tileElement) +//{ +// switch (interactionType) +// { +// case VIEWPORT_INTERACTION_ITEM_SCENERY: +// track_design_save_remove_scenery(x, y, tileElement); +// break; +// case VIEWPORT_INTERACTION_ITEM_LARGE_SCENERY: +// track_design_save_remove_large_scenery(x, y, tileElement->AsLargeScenery()); +// break; +// case VIEWPORT_INTERACTION_ITEM_WALL: +// track_design_save_remove_wall(x, y, tileElement); +// break; +// case VIEWPORT_INTERACTION_ITEM_FOOTPATH: +// track_design_save_remove_footpath(x, y, tileElement); +// break; +// } +//} +// +//static bool track_design_save_should_select_scenery_around(ride_id_t rideIndex, TileElement* tileElement) +//{ +// switch (tileElement->GetType()) +// { +// case TILE_ELEMENT_TYPE_PATH: +// if (tileElement->AsPath()->IsQueue() && tileElement->AsPath()->GetRideIndex() == rideIndex) +// return true; +// break; +// case TILE_ELEMENT_TYPE_TRACK: +// if (tileElement->AsTrack()->GetRideIndex() == rideIndex) +// return true; +// break; +// case TILE_ELEMENT_TYPE_ENTRANCE: +// // FIXME: This will always break and return false! +// if (tileElement->AsEntrance()->GetEntranceType() != ENTRANCE_TYPE_RIDE_ENTRANCE) +// break; +// if (tileElement->AsEntrance()->GetEntranceType() != ENTRANCE_TYPE_RIDE_EXIT) +// break; +// if (tileElement->AsEntrance()->GetRideIndex() == rideIndex) +// return true; +// break; +// } +// return false; +//} +// +//static void track_design_save_select_nearby_scenery_for_tile(ride_id_t rideIndex, int32_t cx, int32_t cy) +//{ +// TileElement* tileElement; +// +// for (int32_t y = cy - TRACK_NEARBY_SCENERY_DISTANCE; y <= cy + TRACK_NEARBY_SCENERY_DISTANCE; y++) +// { +// for (int32_t x = cx - TRACK_NEARBY_SCENERY_DISTANCE; x <= cx + TRACK_NEARBY_SCENERY_DISTANCE; x++) +// { +// tileElement = map_get_first_element_at(x, y); +// do +// { +// int32_t interactionType = VIEWPORT_INTERACTION_ITEM_NONE; +// switch (tileElement->GetType()) +// { +// case TILE_ELEMENT_TYPE_PATH: +// if (!tileElement->AsPath()->IsQueue()) +// interactionType = VIEWPORT_INTERACTION_ITEM_FOOTPATH; +// else if (tileElement->AsPath()->GetRideIndex() == rideIndex) +// interactionType = VIEWPORT_INTERACTION_ITEM_FOOTPATH; +// break; +// case TILE_ELEMENT_TYPE_SMALL_SCENERY: +// interactionType = VIEWPORT_INTERACTION_ITEM_SCENERY; +// break; +// case TILE_ELEMENT_TYPE_WALL: +// interactionType = VIEWPORT_INTERACTION_ITEM_WALL; +// break; +// case TILE_ELEMENT_TYPE_LARGE_SCENERY: +// interactionType = VIEWPORT_INTERACTION_ITEM_LARGE_SCENERY; +// break; +// } +// +// if (interactionType != VIEWPORT_INTERACTION_ITEM_NONE) +// { +// if (!track_design_save_contains_tile_element(tileElement)) +// { +// track_design_save_add_tile_element(interactionType, x * 32, y * 32, tileElement); +// } +// } +// } while (!(tileElement++)->IsLastForTile()); +// } +// } +//} +// +///* Based on rct2: 0x006D2897 */ +//static bool track_design_save_copy_scenery_to_td6(rct_track_td6* td6) +//{ +// // Copy TD6 scenery elements to new memory and add end marker +// size_t totalSceneryElementsSize = _trackSavedTileElementsDescCount * sizeof(rct_td6_scenery_element); +// td6->scenery_elements = (rct_td6_scenery_element*)malloc(totalSceneryElementsSize + 1); +// std::memcpy(td6->scenery_elements, _trackSavedTileElementsDesc, totalSceneryElementsSize); +// *((uint8_t*)&td6->scenery_elements[_trackSavedTileElementsDescCount]) = 0xFF; +// +// // Run an element loop +// for (size_t i = 0; i < _trackSavedTileElementsDescCount; i++) +// { +// rct_td6_scenery_element* scenery = &td6->scenery_elements[i]; +// +// switch (object_entry_get_type(&scenery->scenery_object)) +// { +// case OBJECT_TYPE_PATHS: +// { +// uint8_t slope = (scenery->flags & 0x60) >> 5; +// slope -= _trackSaveDirection; +// +// scenery->flags &= 0x9F; +// scenery->flags |= ((slope & 3) << 5); +// +// // Direction of connection on path +// uint8_t direction = scenery->flags & 0xF; +// // Rotate the direction by the track direction +// direction = ((direction << 4) >> _trackSaveDirection); +// +// scenery->flags &= 0xF0; +// scenery->flags |= (direction & 0xF) | (direction >> 4); +// break; +// } +// case OBJECT_TYPE_WALLS: +// { +// uint8_t direction = scenery->flags & 3; +// direction -= _trackSaveDirection; +// +// scenery->flags &= 0xFC; +// scenery->flags |= (direction & 3); +// break; +// } +// default: +// { +// uint8_t direction = scenery->flags & 3; +// uint8_t quadrant = (scenery->flags & 0x0C) >> 2; +// +// direction -= _trackSaveDirection; +// quadrant -= _trackSaveDirection; +// +// scenery->flags &= 0xF0; +// scenery->flags |= (direction & 3) | ((quadrant & 3) << 2); +// break; +// } +// } +// +// int16_t x = ((uint8_t)scenery->x) * 32 - gTrackPreviewOrigin.x; +// int16_t y = ((uint8_t)scenery->y) * 32 - gTrackPreviewOrigin.y; +// rotate_map_coordinates(&x, &y, (0 - _trackSaveDirection) & 3); +// x /= 32; +// y /= 32; +// +// if (x > 127 || y > 127 || x < -126 || y < -126) +// { +// context_show_error(STR_CANT_SAVE_TRACK_DESIGN, STR_TRACK_TOO_LARGE_OR_TOO_MUCH_SCENERY); +// SafeFree(td6->scenery_elements); +// return false; +// } +// +// scenery->x = (int8_t)x; +// scenery->y = (int8_t)y; +// +// int32_t z = scenery->z * 8 - gTrackPreviewOrigin.z; +// z /= 8; +// if (z > 127 || z < -126) +// { +// context_show_error(STR_CANT_SAVE_TRACK_DESIGN, STR_TRACK_TOO_LARGE_OR_TOO_MUCH_SCENERY); +// SafeFree(td6->scenery_elements); +// return false; +// } +// scenery->z = z; +// } +// +// return true; +//} +// +///** +// * +// * rct2: 0x006CE44F +// */ +//static TrackDesign* track_design_save_to_td6(ride_id_t rideIndex) +//{ +// auto ride = get_ride(rideIndex); +// if (ride == nullptr) +// return nullptr; +// +// auto td6 = (rct_track_td6*)calloc(1, sizeof(rct_track_td6)); +// if (td6 == nullptr) +// return nullptr; +// +// td6->type = ride->type; +// auto object = object_entry_get_entry(OBJECT_TYPE_RIDE, ride->subtype); +// +// // Note we are only copying rct_object_entry in size and +// // not the extended as we don't need the chunk size. +// std::memcpy(&td6->vehicle_object, object, sizeof(rct_object_entry)); +// +// td6->ride_mode = ride->mode; +// +// td6->version_and_colour_scheme = (ride->colour_scheme_type & 3) | (1 << 3); // Version .TD6 +// +// for (int32_t i = 0; i < RCT12_MAX_VEHICLES_PER_RIDE; i++) +// { +// td6->vehicle_colours[i].body_colour = ride->vehicle_colours[i].Body; +// td6->vehicle_colours[i].trim_colour = ride->vehicle_colours[i].Trim; +// td6->vehicle_additional_colour[i] = ride->vehicle_colours[i].Ternary; +// } +// +// for (int32_t i = 0; i < RCT12_NUM_COLOUR_SCHEMES; i++) +// { +// td6->track_spine_colour[i] = ride->track_colour[i].main; +// td6->track_rail_colour[i] = ride->track_colour[i].additional; +// td6->track_support_colour[i] = ride->track_colour[i].supports; +// } +// +// td6->depart_flags = ride->depart_flags; +// td6->number_of_trains = ride->num_vehicles; +// td6->number_of_cars_per_train = ride->num_cars_per_train; +// td6->min_waiting_time = ride->min_waiting_time; +// td6->max_waiting_time = ride->max_waiting_time; +// td6->operation_setting = ride->operation_option; +// td6->lift_hill_speed_num_circuits = ride->lift_hill_speed | (ride->num_circuits << 5); +// +// td6->entrance_style = ride->entrance_style; +// td6->max_speed = (int8_t)(ride->max_speed / 65536); +// td6->average_speed = (int8_t)(ride->average_speed / 65536); +// td6->ride_length = ride_get_total_length(ride) / 65536; +// td6->max_positive_vertical_g = ride->max_positive_vertical_g / 32; +// td6->max_negative_vertical_g = ride->max_negative_vertical_g / 32; +// td6->max_lateral_g = ride->max_lateral_g / 32; +// if (ride->type == RIDE_TYPE_MINI_GOLF) +// td6->inversions = ride->holes & 0x1F; +// else +// td6->inversions = ride->inversions & 0x1F; +// td6->inversions |= (ride->sheltered_eighths << 5); +// td6->drops = ride->drops; +// td6->highest_drop_height = ride->highest_drop_height; +// +// uint16_t total_air_time = (ride->total_air_time * 123) / 1024; +// if (total_air_time > 255) +// { +// total_air_time = 0; +// } +// td6->total_air_time = (uint8_t)total_air_time; +// +// td6->excitement = ride->ratings.excitement / 10; +// td6->intensity = ride->ratings.intensity / 10; +// td6->nausea = ride->ratings.nausea / 10; +// +// td6->upkeep_cost = ride->upkeep_cost; +// td6->flags = 0; +// td6->flags2 = 0; +// +// bool result; +// if (td6->type == RIDE_TYPE_MAZE) +// { +// result = track_design_save_to_td6_for_maze(ride, td6); +// } +// else +// { +// result = track_design_save_to_td6_for_tracked_ride(ride, td6); +// } +// +// if (!result) +// { +// //track_design_dispose(td6); +// td6 = nullptr; +// } +// return td6; +//} +// +///** +// * +// * rct2: 0x006CEAAE +// */ +//static bool track_design_save_to_td6_for_maze(const Ride* ride, TrackDesign* td6) +//{ +// TileElement* tileElement = nullptr; +// bool mapFound = false; +// int16_t startX = 0; +// int16_t startY = 0; +// for (startY = 0; startY < 8192; startY += 32) +// { +// for (startX = 0; startX < 8192; startX += 32) +// { +// tileElement = map_get_first_element_at(startX >> 5, startY >> 5); +// do +// { +// if (tileElement->GetType() != TILE_ELEMENT_TYPE_TRACK) +// continue; +// if (tileElement->AsTrack()->GetRideIndex() == ride->id) +// { +// mapFound = true; +// break; +// } +// } while (!(tileElement++)->IsLastForTile()); +// if (mapFound) +// { +// break; +// } +// } +// if (mapFound) +// { +// break; +// } +// } +// +// if (mapFound == 0) +// { +// gGameCommandErrorText = STR_TRACK_TOO_LARGE_OR_TOO_MUCH_SCENERY; +// return false; +// } +// +// gTrackPreviewOrigin = { startX, startY, (int16_t)(tileElement->base_height * 8) }; +// +// size_t numMazeElements = 0; +// td6->maze_elements = (rct_td6_maze_element*)calloc(8192, sizeof(rct_td6_maze_element)); +// rct_td6_maze_element* maze = td6->maze_elements; +// +// // x is defined here as we can start the search +// // on tile start_x, start_y but then the next row +// // must restart on 0 +// for (int16_t y = startY, x = startX; y < 8192; y += 32) +// { +// for (; x < 8192; x += 32) +// { +// tileElement = map_get_first_element_at(x / 32, y / 32); +// do +// { +// if (tileElement->GetType() != TILE_ELEMENT_TYPE_TRACK) +// continue; +// if (tileElement->AsTrack()->GetRideIndex() != ride->id) +// continue; +// +// maze->maze_entry = tileElement->AsTrack()->GetMazeEntry(); +// maze->x = (x - startX) / 32; +// maze->y = (y - startY) / 32; +// maze++; +// numMazeElements++; +// +// if (numMazeElements >= 2000) +// { +// gGameCommandErrorText = STR_TRACK_TOO_LARGE_OR_TOO_MUCH_SCENERY; +// SafeFree(td6->maze_elements); +// return false; +// } +// } while (!(tileElement++)->IsLastForTile()); +// } +// x = 0; +// } +// +// auto location = ride_get_entrance_location(ride, 0); +// if (location.isNull()) +// { +// gGameCommandErrorText = STR_TRACK_TOO_LARGE_OR_TOO_MUCH_SCENERY; +// SafeFree(td6->maze_elements); +// return false; +// } +// +// int16_t x = location.x * 32; +// int16_t y = location.y * 32; +// +// tileElement = map_get_first_element_at(location.x, location.y); +// do +// { +// if (tileElement->GetType() != TILE_ELEMENT_TYPE_ENTRANCE) +// continue; +// if (tileElement->AsEntrance()->GetEntranceType() != ENTRANCE_TYPE_RIDE_ENTRANCE) +// continue; +// if (tileElement->AsEntrance()->GetRideIndex() == ride->id) +// break; +// } while (!(tileElement++)->IsLastForTile()); +// // Add something that stops this from walking off the end +// +// uint8_t entrance_direction = tileElement->GetDirection(); +// maze->direction = entrance_direction; +// maze->type = 8; +// maze->x = (int8_t)((x - startX) / 32); +// maze->y = (int8_t)((y - startY) / 32); +// maze++; +// numMazeElements++; +// +// location = ride_get_exit_location(ride, 0); +// if (location.isNull()) +// { +// gGameCommandErrorText = STR_TRACK_TOO_LARGE_OR_TOO_MUCH_SCENERY; +// SafeFree(td6->maze_elements); +// return false; +// } +// +// x = location.x * 32; +// y = location.y * 32; +// tileElement = map_get_first_element_at(location.x, location.y); +// do +// { +// if (tileElement->GetType() != TILE_ELEMENT_TYPE_ENTRANCE) +// continue; +// if (tileElement->AsEntrance()->GetEntranceType() != ENTRANCE_TYPE_RIDE_EXIT) +// continue; +// if (tileElement->AsEntrance()->GetRideIndex() == ride->id) +// break; +// } while (!(tileElement++)->IsLastForTile()); +// // Add something that stops this from walking off the end +// +// uint8_t exit_direction = tileElement->GetDirection(); +// maze->direction = exit_direction; +// maze->type = 0x80; +// maze->x = (int8_t)((x - startX) / 32); +// maze->y = (int8_t)((y - startY) / 32); +// maze++; +// maze->all = 0; +// maze++; +// numMazeElements++; +// +// // Write end marker +// maze->all = 0; +// maze++; +// numMazeElements++; +// +// // Trim memory +// td6->maze_elements = (rct_td6_maze_element*)realloc(td6->maze_elements, numMazeElements * sizeof(rct_td6_maze_element)); +// +// // Save global vars as they are still used by scenery +// int16_t startZ = gTrackPreviewOrigin.z; +// place_virtual_track(td6, PTD_OPERATION_DRAW_OUTLINES, true, GetOrAllocateRide(0), 4096, 4096, 0); +// gTrackPreviewOrigin = { startX, startY, startZ }; +// +// gMapSelectFlags &= ~MAP_SELECT_FLAG_ENABLE_CONSTRUCT; +// gMapSelectFlags &= ~MAP_SELECT_FLAG_ENABLE_ARROW; +// gMapSelectFlags &= ~MAP_SELECT_FLAG_GREEN; +// +// td6->space_required_x = ((gTrackPreviewMax.x - gTrackPreviewMin.x) / 32) + 1; +// td6->space_required_y = ((gTrackPreviewMax.y - gTrackPreviewMin.y) / 32) + 1; +// return true; +//} +// +///** +// * +// * rct2: 0x006CE68D +// */ +//static bool track_design_save_to_td6_for_tracked_ride(const Ride* ride, TrackDesign* td6) +//{ +// CoordsXYE trackElement; +// if (!ride_try_get_origin_element(ride, &trackElement)) +// { +// gGameCommandErrorText = STR_TRACK_TOO_LARGE_OR_TOO_MUCH_SCENERY; +// return false; +// } +// +// ride_get_start_of_track(&trackElement); +// +// int32_t z = trackElement.element->base_height * 8; +// uint8_t track_type = trackElement.element->AsTrack()->GetTrackType(); +// uint8_t direction = trackElement.element->GetDirection(); +// _trackSaveDirection = direction; +// +// if (sub_6C683D(&trackElement.x, &trackElement.y, &z, direction, track_type, 0, &trackElement.element, 0)) +// { +// gGameCommandErrorText = STR_TRACK_TOO_LARGE_OR_TOO_MUCH_SCENERY; +// return 0; +// } +// +// const rct_track_coordinates* trackCoordinates = &TrackCoordinates[trackElement.element->AsTrack()->GetTrackType()]; +// // Used in the following loop to know when we have +// // completed all of the elements and are back at the +// // start. +// TileElement* initialMap = trackElement.element; +// +// int16_t start_x = trackElement.x; +// int16_t start_y = trackElement.y; +// int16_t start_z = z + trackCoordinates->z_begin; +// gTrackPreviewOrigin = { start_x, start_y, start_z }; +// +// size_t numTrackElements = 0; +// td6->track_elements = (rct_td6_track_element*)calloc(TRACK_TD6_MAX_ELEMENTS, sizeof(rct_td6_track_element)); +// rct_td6_track_element* track = td6->track_elements; +// do +// { +// track->type = trackElement.element->AsTrack()->GetTrackType(); +// if (track->type == TRACK_ELEM_255) +// { +// track->type = TRACK_ELEM_255_ALIAS; +// } +// +// uint8_t flags; +// if (track_element_has_speed_setting(track->type)) +// { +// flags = trackElement.element->AsTrack()->GetBrakeBoosterSpeed() >> 1; +// } +// else +// { +// flags = trackElement.element->AsTrack()->GetSeatRotation(); +// } +// +// if (trackElement.element->AsTrack()->HasChain()) +// flags |= (1 << 7); +// flags |= trackElement.element->AsTrack()->GetColourScheme() << 4; +// if (RideData4[ride->type].flags & RIDE_TYPE_FLAG4_HAS_ALTERNATIVE_TRACK_TYPE +// && trackElement.element->AsTrack()->IsInverted()) +// { +// flags |= TRACK_ELEMENT_FLAG_INVERTED; +// } +// +// track->flags = flags; +// track++; +// numTrackElements++; +// +// if (!track_block_get_next(&trackElement, &trackElement, nullptr, nullptr)) +// { +// break; +// } +// +// z = trackElement.element->base_height * 8; +// direction = trackElement.element->GetDirection(); +// track_type = trackElement.element->AsTrack()->GetTrackType(); +// +// if (sub_6C683D(&trackElement.x, &trackElement.y, &z, direction, track_type, 0, &trackElement.element, 0)) +// { +// break; +// } +// +// if (TRACK_TD6_MAX_ELEMENTS == numTrackElements) +// { +// free(td6->track_elements); +// gGameCommandErrorText = STR_TRACK_TOO_LARGE_OR_TOO_MUCH_SCENERY; +// return false; +// } +// } while (trackElement.element != initialMap); +// +// td6->track_elements = (rct_td6_track_element*)realloc( +// td6->track_elements, numTrackElements * sizeof(rct_td6_track_element) + 1); +// *((uint8_t*)&td6->track_elements[numTrackElements]) = 0xFF; +// +// size_t numEntranceElements = 0; +// td6->entrance_elements = (rct_td6_entrance_element*)calloc(32, sizeof(rct_td6_entrance_element)); +// rct_td6_entrance_element* entrance = td6->entrance_elements; +// +// // First entrances, second exits +// for (int32_t i = 0; i < 2; i++) +// { +// for (int32_t station_index = 0; station_index < RCT12_MAX_STATIONS_PER_RIDE; station_index++) +// { +// z = ride->stations[station_index].Height; +// +// TileCoordsXYZD location; +// if (i == 0) +// { +// location = ride_get_entrance_location(ride, station_index); +// } +// else +// { +// location = ride_get_exit_location(ride, station_index); +// } +// +// if (location.isNull()) +// { +// continue; +// } +// +// int16_t x = location.x * 32; +// int16_t y = location.y * 32; +// +// TileElement* tile_element = map_get_first_element_at(x >> 5, y >> 5); +// do +// { +// if (tile_element->GetType() != TILE_ELEMENT_TYPE_ENTRANCE) +// continue; +// if (tile_element->base_height == z) +// break; +// } while (!(tile_element++)->IsLastForTile()); +// // Add something that stops this from walking off the end +// +// uint8_t entrance_direction = tile_element->GetDirection(); +// entrance_direction -= _trackSaveDirection; +// entrance_direction &= TILE_ELEMENT_DIRECTION_MASK; +// entrance->direction = entrance_direction; +// +// x -= gTrackPreviewOrigin.x; +// y -= gTrackPreviewOrigin.y; +// +// // Rotate entrance coordinates backwards to the correct direction +// rotate_map_coordinates(&x, &y, (0 - _trackSaveDirection) & 3); +// entrance->x = x; +// entrance->y = y; +// +// z *= 8; +// z -= gTrackPreviewOrigin.z; +// z /= 8; +// +// if (z > 127 || z < -126) +// { +// gGameCommandErrorText = STR_TRACK_TOO_LARGE_OR_TOO_MUCH_SCENERY; +// return 0; +// } +// +// if (z == 0xFF) +// { +// z = 0x80; +// } +// +// entrance->z = z; +// +// // If this is the exit version +// if (i == 1) +// { +// entrance->direction |= (1 << 7); +// } +// entrance++; +// numEntranceElements++; +// } +// } +// td6->entrance_elements = (rct_td6_entrance_element*)realloc( +// td6->entrance_elements, numEntranceElements * sizeof(rct_td6_entrance_element) + 1); +// *((uint8_t*)&td6->entrance_elements[numEntranceElements]) = 0xFF; +// +// place_virtual_track(td6, PTD_OPERATION_DRAW_OUTLINES, true, GetOrAllocateRide(0), 4096, 4096, 0); +// +// // Resave global vars for scenery reasons. +// gTrackPreviewOrigin = { start_x, start_y, start_z }; +// +// gMapSelectFlags &= ~MAP_SELECT_FLAG_ENABLE_CONSTRUCT; +// gMapSelectFlags &= ~MAP_SELECT_FLAG_ENABLE_ARROW; +// gMapSelectFlags &= ~MAP_SELECT_FLAG_GREEN; +// +// td6->space_required_x = ((gTrackPreviewMax.x - gTrackPreviewMin.x) / 32) + 1; +// td6->space_required_y = ((gTrackPreviewMax.y - gTrackPreviewMin.y) / 32) + 1; +// return true; +//} +// +//static size_t track_design_get_maze_elements_count(rct_track_td6* td6) +//{ +// size_t count = 0; +// rct_td6_maze_element* mazeElement = td6->maze_elements; +// while (mazeElement->all != 0) +// { +// count++; +// mazeElement++; +// } +// return count; +//} +// +//static size_t track_design_get_track_elements_count(rct_track_td6* td6) +//{ +// size_t count = 0; +// rct_td6_track_element* trackElement = td6->track_elements; +// while (trackElement->type != 0xFF) +// { +// count++; +// trackElement++; +// } +// return count; +//} +// +//static size_t track_design_get_entrance_elements_count(rct_track_td6* td6) +//{ +// size_t count = 0; +// rct_td6_entrance_element* entranceElement = td6->entrance_elements; +// while (entranceElement->z != -1) +// { +// count++; +// entranceElement++; +// } +// return count; +//} +// +//static size_t track_design_get_scenery_elements_count(rct_track_td6* td6) +//{ +// size_t count = 0; +// rct_td6_scenery_element* sceneryElement = td6->scenery_elements; +// if (sceneryElement != nullptr) +// { +// while (sceneryElement->scenery_object.end_flag != 0xFF) +// { +// count++; +// sceneryElement++; +// } +// } +// return count; +//} +// +//struct auto_buffer +//{ +// void* ptr; +// size_t length; +// size_t capacity; +//}; +// +//static void auto_buffer_write(auto_buffer* buffer, const void* src, size_t len) +//{ +// size_t remainingSpace = buffer->capacity - buffer->length; +// if (remainingSpace < len) +// { +// do +// { +// buffer->capacity = std::max(8, buffer->capacity * 2); +// remainingSpace = buffer->capacity - buffer->length; +// } while (remainingSpace < len); +// +// buffer->ptr = realloc(buffer->ptr, buffer->capacity); +// } +// std::memcpy((void*)((uintptr_t)buffer->ptr + buffer->length), src, len); +// buffer->length += len; +//} +// +///** +// * +// * rct2: 0x006771DC but not really its branched from that +// * quite far. +// */ +//bool track_design_save_to_file(const utf8* path) +//{ +// rct_track_td6* td6 = _trackDesign; +// const rct_td6_maze_element EndMarkerForMaze = {}; +// const uint8_t EndMarker = 0xFF; +// +// window_close_construction_windows(); +// +// // Create TD6 data buffer +// auto_buffer td6Buffer = {}; +// auto_buffer_write(&td6Buffer, td6, 0xA3); +// if (td6->type == RIDE_TYPE_MAZE) +// { +// auto_buffer_write( +// &td6Buffer, td6->maze_elements, track_design_get_maze_elements_count(td6) * sizeof(rct_td6_maze_element)); +// auto_buffer_write(&td6Buffer, &EndMarkerForMaze, sizeof(EndMarkerForMaze)); +// } +// else +// { +// auto_buffer_write( +// &td6Buffer, td6->track_elements, track_design_get_track_elements_count(td6) * sizeof(rct_td6_track_element)); +// auto_buffer_write(&td6Buffer, &EndMarker, sizeof(EndMarker)); +// auto_buffer_write( +// &td6Buffer, td6->entrance_elements, +// track_design_get_entrance_elements_count(td6) * sizeof(rct_td6_entrance_element)); +// auto_buffer_write(&td6Buffer, &EndMarker, sizeof(EndMarker)); +// } +// auto_buffer_write( +// &td6Buffer, td6->scenery_elements, track_design_get_scenery_elements_count(td6) * sizeof(rct_td6_scenery_element)); +// auto_buffer_write(&td6Buffer, &EndMarker, sizeof(EndMarker)); +// +// // Encode TD6 data +// uint8_t* encodedData = (uint8_t*)malloc(0x8000); +// assert(td6Buffer.ptr != nullptr); +// size_t encodedDataLength = sawyercoding_encode_td6((uint8_t*)td6Buffer.ptr, encodedData, td6Buffer.length); +// +// // Save encoded TD6 data to file +// bool result; +// log_verbose("saving track %s", path); +// result = writeentirefile(path, encodedData, encodedDataLength); +// if (!result) +// { +// log_error("Failed to save %s", path); +// } +// +// free(encodedData); +// free(td6Buffer.ptr); +// return result; +//}