Fix TD4 loading / memory violation

Instead of doing these hacks to load bits of TD4 into a TD6, load TD4 data directly into its own struct and then copy field by field to TD6. Simplify TD6 loading.
This commit is contained in:
Ted John 2016-10-14 23:58:41 +01:00
parent 8f8677aa83
commit 98c71dcf5b
4 changed files with 255 additions and 174 deletions

View File

@ -346,51 +346,59 @@ typedef struct rct1_s4 {
} rct1_s4;
assert_struct_size(rct1_s4, 0x1F850C);
/**
* Track design structure.
* size: 0x2006
*/
typedef struct rct_track_td4 {
uint8 type; // 0x00
uint8 vehicle_type; // 0x01
uint32 special_track_flags; // 0x02
uint8 operating_mode; // 0x06
uint8 vehicle_colour_version; // 0x07 Vehicle colour type in first two bits, Version in bits 3,4
colour_t body_trim_colour[24]; // 0x08
colour_t track_spine_colour_rct1; // 0x20
colour_t track_rail_colour_rct1; // 0x21
colour_t track_support_colour_rct1; // 0x22
uint8 departure_control_flags; // 0x23
uint8 vehicle_type;
uint32 flags; // 0x02
uint8 mode; // 0x06
uint8 version_and_colour_scheme; // 0x07 0b0000_VVCC
rct_vehicle_colour vehicle_colours[12]; // 0x08
uint8 track_spine_colour_v0; // 0x20
uint8 track_rail_colour_v0; // 0x21
uint8 track_support_colour_v0; // 0x22
uint8 depart_flags; // 0x23
uint8 number_of_trains; // 0x24
uint8 cars_per_train; // 0x25
uint8 min_wait_time; // 0x26
uint8 max_wait_time; // 0x27
uint8 speed; // 0x28
uint8 max_speed; // 0x29
uint8 average_speed; // 0x2A
uint8 number_of_cars_per_train; // 0x25
uint8 min_waiting_time; // 0x26
uint8 max_waiting_time; // 0x27
union {
uint8 operation_setting;
uint8 launch_speed;
uint8 num_laps;
uint8 max_people;
};
sint8 max_speed; // 0x29
sint8 average_speed; // 0x2A
uint16 ride_length; // 0x2B
uint8 max_positive_vertical_g; // 0x2D
uint8 max_negitive_vertical_g; // 0x2E
sint8 max_negative_vertical_g; // 0x2C
uint8 max_lateral_g; // 0x2F
union {
uint8 inversions; // 0x30
uint8 holes; // 0x30
uint8 num_inversions; // 0x30
uint8 num_holes; // 0x30
};
uint8 drops; // 0x31
uint8 num_drops; // 0x31
uint8 highest_drop_height; // 0x32
uint8 excitement; // 0x33
uint8 intensity; // 0x34
uint8 nausea; // 0x35
uint8 pad_36[2];
union{
uint16 start_track_data_original; // 0x38
colour_t track_spine_colour[4]; // 0x38
};
colour_t track_rail_colour[4]; // 0x3C
union{
colour_t track_support_colour[4]; // 0x40
uint8 wall_type[4]; // 0x40
};
uint8 pad_41[0x83];
uint16 start_track_data_AA_CF; // 0xC4
} rct_track_td4; // Information based off RCTTechDepot
assert_struct_size(rct_track_td4, 0xC9);
money16 upkeep_cost; // 0x36
// Added Attractions / Loopy Landscapes only
uint8 track_spine_colour[4]; // 0x38
uint8 track_rail_colour[4]; // 0x3C
uint8 track_support_colour[4]; // 0x40
uint8 flags2; // 0x44
uint8 var_45[0x7F]; // 0x45
void *elements; // 0xC4 (data starts here in file, 38 for original RCT1)
size_t elementsSize;
} rct_track_td4;
#pragma pack(pop)
enum {

View File

@ -64,13 +64,9 @@ static map_backup *track_design_preview_backup_map();
static void track_design_preview_restore_map(map_backup *backup);
static void track_design_preview_clear_map();
static bool td4_track_has_boosters(rct_track_td6* track_design, uint8* track_elements);
static void copy(void *dst, uint8 **src, int length)
{
memcpy(dst, *src, length);
*src += length;
}
static void td6_reset_trailing_elements(rct_track_td6 * td6);
static void td6_set_element_helper_pointers(rct_track_td6 * td6);
static bool td4_track_has_boosters(rct_track_td4 * td4);
rct_track_td6 *track_design_open(const utf8 *path)
{
@ -119,147 +115,224 @@ rct_track_td6 *track_design_open(const utf8 *path)
return NULL;
}
static rct_track_td6 *track_design_open_from_buffer(uint8 *src, size_t srcLength)
static rct_track_td6 * track_design_open_from_td4(uint8 *src, size_t srcLength)
{
rct_track_td6 *td6 = calloc(1, sizeof(rct_track_td6));
uint8 *readPtr = src;
rct_track_td4 * td4 = calloc(1, sizeof(rct_track_td4));
if (td4 == NULL) {
log_error("Unable to allocate memory for TD4 data.");
SafeFree(td4);
return NULL;
}
// Read start of track_design
copy(td6, &readPtr, 32);
uint8 version = td6->version_and_colour_scheme >> 2;
if (version > 2) {
uint8 version = (src[7] >> 2) & 2;
if (version == 0) {
memcpy(td4, src, 0x38);
td4->elementsSize = srcLength - 0x38;
td4->elements = malloc(td4->elementsSize);
if (td4->elements == NULL) {
log_error("Unable to allocate memory for TD4 element data.");
SafeFree(td4);
return NULL;
}
memcpy(td4->elements, src + 0x38, td4->elementsSize);
} else if (version == 1) {
memcpy(td4, src, 0xC4);
td4->elementsSize = srcLength - 0xC4;
td4->elements = malloc(td4->elementsSize);
if (td4->elements == NULL) {
log_error("Unable to allocate memory for TD4 element data.");
SafeFree(td4);
return NULL;
}
memcpy(td4->elements, src + 0xC4, td4->elementsSize);
} else {
log_error("Unsupported track design.");
free(td6);
SafeFree(td4);
return NULL;
}
// In TD6 there are 32 sets of two byte vehicle colour specifiers
// In TD4 there are 12 sets so the remaining 20 need to be read
if (version == 2) {
copy(&td6->vehicle_colours[12], &readPtr, 40);
}
copy(&td6->pad_48, &readPtr, 24);
// In TD4 (version AA/CF) and TD6 both start actual track data at 0xA3
if (version > 0) {
copy(&td6->track_spine_colour, &readPtr, version == 1 ? 140 : 67);
}
// Read the actual track data to memory directly after the passed in TD6 struct
size_t elementDataLength = srcLength - (readPtr - src);
uint8 *elementData = malloc(elementDataLength);
if (elementData == NULL) {
log_error("Unable to allocate memory for TD6 element data.");
free(td6);
rct_track_td6 * td6 = calloc(1, sizeof(rct_track_td6));
if (td6 == NULL) {
log_error("Unable to allocate memory for TD6 data.");
SafeFree(td4);
return NULL;
}
copy(elementData, &readPtr, (int)elementDataLength);
td6->elements = elementData;
td6->elementsSize = elementDataLength;
uint8 *final_track_element_location = elementData + elementDataLength;
// TD4 files require some extra work to be recognised as TD6.
if (version < 2) {
// Set any element passed the tracks to 0xFF
if (td6->type == RIDE_TYPE_MAZE) {
rct_td6_maze_element* maze_element = (rct_td6_maze_element*)elementData;
while (maze_element->all != 0) {
maze_element++;
}
maze_element++;
memset(maze_element, 255, final_track_element_location - (uint8*)maze_element);
} else {
rct_td6_track_element* track_element = (rct_td6_track_element*)elementData;
while (track_element->type != 255) {
track_element++;
}
memset(((uint8*)track_element) + 1, 255, final_track_element_location - (uint8*)track_element);
// Convert RCT1 ride type to RCT2 ride type
uint8 rct1RideType = td4->type;
if (td4->type == RCT1_RIDE_TYPE_WOODEN_ROLLER_COASTER) {
td6->type = RIDE_TYPE_WOODEN_ROLLER_COASTER;
td6->ride_mode = td4->mode;
} else if (td4->type == RCT1_RIDE_TYPE_STEEL_CORKSCREW_ROLLER_COASTER) {
if (td4->vehicle_type == RCT1_VEHICLE_TYPE_HYPERCOASTER_TRAIN &&
td4->mode == RCT1_RIDE_MODE_REVERSE_INCLINE_LAUNCHED_SHUTTLE
) {
td6->ride_mode = RIDE_MODE_CONTINUOUS_CIRCUIT;
}
} else {
td6->type = td4->type;
td6->ride_mode = td4->mode;
}
// Convert the colours from RCT1 to RCT2
for (int i = 0; i < 32; i++) {
rct_vehicle_colour *vehicleColour = &td6->vehicle_colours[i];
vehicleColour->body_colour = rct1_get_colour(vehicleColour->body_colour);
vehicleColour->trim_colour = rct1_get_colour(vehicleColour->trim_colour);
// All TD4s that use powered launch use the type that doesn't pass the station.
if (td4->mode == RCT1_RIDE_MODE_POWERED_LAUNCH) {
td6->ride_mode = RIDE_MODE_POWERED_LAUNCH;
}
// Convert RCT1 vehicle type to RCT2 vehicle type
rct_object_entry vehicleObject = { 0x80, { " " }, 0 };
if (td4->type == RIDE_TYPE_MAZE) {
const char * name = rct1_get_ride_type_object(td6->type);
assert(name != NULL);
memcpy(vehicleObject.name, name, min(strlen(name), 8));
} else {
const char * name = rct1_get_vehicle_object(td6->vehicle_type);
assert(name != NULL);
memcpy(vehicleObject.name, name, min(strlen(name), 8));
}
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 (int i = 0; i < 12; i++) {
td6->vehicle_colours[i].body_colour = rct1_get_colour(td4->vehicle_colours[i].body_colour);
td6->vehicle_colours[i].trim_colour = rct1_get_colour(td4->vehicle_colours[i].trim_colour);
// RCT1 river rapids always had black seats
if (td4->type == RCT1_RIDE_TYPE_RIVER_RAPIDS) {
td6->vehicle_colours[i].trim_colour = COLOUR_BLACK;
}
}
// Set remaining vehicles to same colour as first vehicle
for (int i = 12; i < 32; i++) {
td6->vehicle_colours[i] = td6->vehicle_colours[0];
}
// Set additional colour to trim colour for all vehicles
for (int i = 0; i < 32; i++) {
td6->vehicle_additional_colour[i] = td6->vehicle_colours[i].trim_colour;
}
td6->track_spine_colour_rct1 = rct1_get_colour(td6->track_spine_colour_rct1);
td6->track_rail_colour_rct1 = rct1_get_colour(td6->track_rail_colour_rct1);
td6->track_support_colour_rct1 = rct1_get_colour(td6->track_support_colour_rct1);
// Track colours
if (version == 0) {
for (int i = 0; i < 4; i++) {
td6->track_spine_colour[i] = rct1_get_colour(td4->track_spine_colour_v0);
td6->track_rail_colour[i] = rct1_get_colour(td4->track_rail_colour_v0);
td6->track_support_colour[i] = rct1_get_colour(td4->track_support_colour_v0);
}
} else {
for (int i = 0; i < 4; i++) {
td6->track_spine_colour[i] = rct1_get_colour(td6->track_spine_colour[i]);
td6->track_rail_colour[i] = rct1_get_colour(td6->track_rail_colour[i]);
td6->track_support_colour[i] = rct1_get_colour(td6->track_support_colour[i]);
}
// Highest drop height is 1bit = 3/4 a meter in TD6
// Highest drop height is 1bit = 1/3 a meter in TD4
// Not sure if this is correct??
td6->highest_drop_height >>= 1;
// If it has boosters then sadly track has to be discarded.
if (td4_track_has_boosters(td6, elementData)) {
log_error("Track design contains RCT1 boosters which are not yet supported.");
free(td6->elements);
free(td6);
return NULL;
}
// Convert RCT1 ride type to RCT2 ride type
uint8 rct1RideType = td6->type;
if (rct1RideType == RCT1_RIDE_TYPE_WOODEN_ROLLER_COASTER) {
td6->type = RIDE_TYPE_WOODEN_ROLLER_COASTER;
} else if (rct1RideType == RCT1_RIDE_TYPE_STEEL_CORKSCREW_ROLLER_COASTER) {
if (td6->vehicle_type == RCT1_VEHICLE_TYPE_HYPERCOASTER_TRAIN) {
if (td6->ride_mode == RCT1_RIDE_MODE_REVERSE_INCLINE_LAUNCHED_SHUTTLE) {
td6->ride_mode = RIDE_MODE_CONTINUOUS_CIRCUIT;
}
}
}
// All TD4s that use powered launch use the type that doesn't pass the station.
if (td6->ride_mode == RCT1_RIDE_MODE_POWERED_LAUNCH) {
td6->ride_mode = RIDE_MODE_POWERED_LAUNCH;
}
// Convert RCT1 vehicle type to RCT2 vehicle type
rct_object_entry vehicleObject = { 0x80, { " " }, 0 };
if (td6->type == RIDE_TYPE_MAZE) {
const char * name = rct1_get_ride_type_object(td6->type);
assert(name != NULL);
memcpy(vehicleObject.name, name, min(strlen(name), 8));
} else {
const char * name = rct1_get_vehicle_object(td6->vehicle_type);
assert(name != NULL);
memcpy(vehicleObject.name, name, min(strlen(name), 8));
}
memcpy(&td6->vehicle_object, &vehicleObject, sizeof(rct_object_entry));
// Further vehicle colour fixes
for (int i = 0; i < 32; i++) {
td6->vehicle_additional_colour[i] = td6->vehicle_colours[i].trim_colour;
// RCT1 river rapids always had black seats.
if (rct1RideType == RCT1_RIDE_TYPE_RIVER_RAPIDS) {
td6->vehicle_colours[i].trim_colour = COLOUR_BLACK;
}
}
td6->space_required_x = 255;
td6->space_required_y = 255;
td6->lift_hill_speed_num_circuits = 5;
}
td6->var_50 = min(
td6->var_50,
RideProperties[td6->type].max_value
);
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 = 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;
}
// Set the element helper pointers
td6->space_required_x = 255;
td6->space_required_y = 255;
td6->lift_hill_speed_num_circuits = 5;
// If it has boosters then sadly track has to be discarded.
if (td4_track_has_boosters(td4)) {
log_error("Track design contains RCT1 boosters which are not yet supported.");
SafeFree(td4->elements);
SafeFree(td4);
SafeFree(td6);
return NULL;
}
// Move elements across
td6->elements = td4->elements;
td6->elementsSize = td4->elementsSize;
td6_reset_trailing_elements(td6);
td6_set_element_helper_pointers(td6);
SafeFree(td4);
return td6;
}
static rct_track_td6 *track_design_open_from_buffer(uint8 * src, size_t srcLength)
{
uint8 version = (src[7] >> 2) & 2;
if (version == 0 || version == 1) {
return track_design_open_from_td4(src, srcLength);
} else if (version != 2) {
log_error("Unsupported track design.");
return NULL;
}
rct_track_td6 * td6 = calloc(1, sizeof(rct_track_td6));
if (td6 == NULL) {
log_error("Unable to allocate memory for TD6 data.");
return NULL;
}
memcpy(td6, src, 0xA3);
td6->elementsSize = srcLength - 0xA3;
td6->elements = malloc(td6->elementsSize);
if (td6->elements == NULL) {
free(td6);
log_error("Unable to allocate memory for TD6 element data.");
return NULL;
}
memcpy(td6->elements, src + 0xA3, td6->elementsSize);
// Cap operation setting
td6->operation_setting = min(td6->operation_setting, RideProperties[td6->type].max_value);
td6_set_element_helper_pointers(td6);
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++;
}
mazeElement++;
lastElement = mazeElement;
} else {
rct_td6_track_element * trackElement = (rct_td6_track_element *)td6->elements;
while (trackElement->type != 0xFF) {
trackElement++;
}
trackElement++;
lastElement = trackElement;
}
size_t trailingSize = td6->elementsSize - (size_t)((uintptr_t)lastElement - (uintptr_t)td6->elements);
memset(lastElement, 0xFF, trailingSize);
}
static void td6_set_element_helper_pointers(rct_track_td6 * td6)
{
uintptr_t entranceElementsStart;
uintptr_t sceneryElementsStart;
if (td6->type == RIDE_TYPE_MAZE) {
@ -267,26 +340,24 @@ static rct_track_td6 *track_design_open_from_buffer(uint8 *src, size_t srcLength
td6->maze_elements = (rct_td6_maze_element*)td6->elements;
rct_td6_maze_element *maze = td6->maze_elements;
for (; maze->all != 0; maze++) { }
for (; maze->all != 0; maze++) {}
sceneryElementsStart = (uintptr_t)(++maze);
} else {
td6->maze_elements = NULL;
td6->track_elements = (rct_td6_track_element*)td6->elements;
rct_td6_track_element *track = td6->track_elements;
for (; track->type != 0xFF; track++) { }
for (; track->type != 0xFF; track++) {}
entranceElementsStart = (uintptr_t)track + 1;
rct_td6_entrance_element *entranceElement = (rct_td6_entrance_element*)entranceElementsStart;
td6->entrance_elements = entranceElement;
for (; entranceElement->z != -1; entranceElement++) { }
for (; entranceElement->z != -1; entranceElement++) {}
sceneryElementsStart = (uintptr_t)entranceElement + 1;
}
rct_td6_scenery_element *sceneryElement = (rct_td6_scenery_element*)sceneryElementsStart;
td6->scenery_elements = sceneryElement;
return td6;
}
/**
@ -294,12 +365,14 @@ static rct_track_td6 *track_design_open_from_buffer(uint8 *src, size_t srcLength
* rct2: 0x00677530
* Returns true if it has booster track elements
*/
static bool td4_track_has_boosters(rct_track_td6* track_design, uint8* track_elements)
static bool td4_track_has_boosters(rct_track_td4 * td4)
{
if (track_design->type != RCT1_RIDE_TYPE_HEDGE_MAZE) {
rct_td6_track_element *track = (rct_td6_track_element*)track_elements;
for (; track->type != 0xFF; track++) {
if (track->type == RCT1_TRACK_ELEM_BOOSTER) {
if (td4->type != RCT1_RIDE_TYPE_HEDGE_MAZE) {
rct_td6_track_element * track = (rct_td6_track_element *)td4->elements;
size_t numElements = td4->elementsSize / sizeof(rct_td6_track_element);
for (size_t i = 0; i < numElements; i++) {
if (track[i].type == 0xFF) break;
if (track[i].type == RCT1_TRACK_ELEM_BOOSTER) {
return true;
}
}
@ -1455,7 +1528,7 @@ static money32 place_track_design(sint16 x, sint16 y, sint16 z, uint8 flags, uin
game_do_command(0, GAME_COMMAND_FLAG_APPLY | (td6->depart_flags << 8), 0, rideIndex | (1 << 8), GAME_COMMAND_SET_RIDE_SETTING, 0, 0);
game_do_command(0, GAME_COMMAND_FLAG_APPLY | (td6->min_waiting_time << 8), 0, rideIndex | (2 << 8), GAME_COMMAND_SET_RIDE_SETTING, 0, 0);
game_do_command(0, GAME_COMMAND_FLAG_APPLY | (td6->max_waiting_time << 8), 0, rideIndex | (3 << 8), GAME_COMMAND_SET_RIDE_SETTING, 0, 0);
game_do_command(0, GAME_COMMAND_FLAG_APPLY | (td6->var_50 << 8), 0, rideIndex | (4 << 8), GAME_COMMAND_SET_RIDE_SETTING, 0, 0);
game_do_command(0, GAME_COMMAND_FLAG_APPLY | (td6->operation_setting << 8), 0, rideIndex | (4 << 8), GAME_COMMAND_SET_RIDE_SETTING, 0, 0);
game_do_command(0, GAME_COMMAND_FLAG_APPLY | ((td6->lift_hill_speed_num_circuits & 0x1F) << 8), 0, rideIndex | (8 << 8), GAME_COMMAND_SET_RIDE_SETTING, 0, 0);
uint8 num_circuits = td6->lift_hill_speed_num_circuits >> 5;

View File

@ -110,7 +110,7 @@ typedef struct rct_track_td6 {
uint8 number_of_cars_per_train; // 0x4D
uint8 min_waiting_time; // 0x4E
uint8 max_waiting_time; // 0x4F
uint8 var_50;
uint8 operation_setting;
sint8 max_speed; // 0x51
sint8 average_speed; // 0x52
uint16 ride_length; // 0x53

View File

@ -744,7 +744,7 @@ static rct_track_td6 *track_design_save_to_td6(uint8 rideIndex)
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->var_50 = ride->operation_option;
td6->operation_setting = ride->operation_option;
td6->lift_hill_speed_num_circuits =
ride->lift_hill_speed |
(ride->num_circuits << 5);