create new track index file format at startup

This commit is contained in:
Ted John 2016-04-27 00:15:48 +01:00
parent fc74e5045e
commit 5ad8fb272e
6 changed files with 344 additions and 4 deletions

View File

@ -181,6 +181,7 @@
<ClCompile Include="src\ride\thrill\twist.c" />
<ClCompile Include="src\ride\track.c" />
<ClCompile Include="src\ride\track_data.c" />
<ClCompile Include="src\ride\track_list.c" />
<ClCompile Include="src\ride\track_paint.c" />
<ClCompile Include="src\ride\transport\chairlift.c" />
<ClCompile Include="src\ride\transport\lift.c" />

View File

@ -527,8 +527,8 @@ enum{
};
enum{
RCT1_RIDE_MODE_REVERSE_INCLINE_LAUNCHED_SHUTTLE = 2,
RCT1_RIDE_MODE_POWERED_LAUNCH = 3,
};
enum {

View File

@ -158,9 +158,7 @@ int rct2_init()
object_list_load();
scenario_load_list();
ride_list_item item = { 253, 0 };
track_load_list(item);
track_design_index_create();
font_sprite_initialise_characters();
if (!gOpenRCT2Headless) {

View File

@ -5695,3 +5695,187 @@ bool track_element_is_covered(int trackElementType)
return false;
}
}
static bool track_design_open_from_buffer(rct_track_td6 *td6, uint8 *src, size_t srcLength);
bool track_design_open(rct_track_td6 *td6, const utf8 *path)
{
SDL_RWops *file = SDL_RWFromFile(path, "rb");
if (file != NULL) {
// Read whole file into a buffer
size_t bufferLength = (size_t)SDL_RWsize(file);
uint8 *buffer = (uint8*)malloc(bufferLength);
if (buffer == NULL) {
log_error("Unable to allocate memory for track design file.");
SDL_RWclose(file);
return false;
}
SDL_RWread(file, buffer, bufferLength, 1);
SDL_RWclose(file);
if (!sawyercoding_validate_track_checksum(buffer, bufferLength)) {
log_error("Track checksum failed.");
free(buffer);
return false;
}
// Decode the track data
uint8 *decoded = malloc(0x10000);
size_t decodedLength = sawyercoding_decode_td6(buffer, decoded, bufferLength);
free(buffer);
decoded = realloc(decoded, decodedLength);
if (decoded == NULL) {
log_error("failed to realloc");
} else {
track_design_open_from_buffer(td6, decoded, decodedLength);
free(decoded);
return true;
}
}
return false;
}
static bool track_design_open_from_buffer(rct_track_td6 *td6, uint8 *src, size_t srcLength)
{
uint8 *readPtr = src;
// Clear top of track_design as this is not loaded from the td4 files
memset(&td6->track_spine_colour, 0, 67);
// Read start of track_design
copy(td6, &readPtr, 32);
uint8 version = td6->version_and_colour_scheme >> 2;
if (version > 2) {
log_error("Unsupported track design.");
return false;
}
// 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.");
return false;
}
copy(elementData, &readPtr, 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_maze_element* maze_element = (rct_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_track_element* track_element = (rct_track_element*)elementData;
while (track_element->type != 255) {
track_element++;
}
memset(((uint8*)track_element) + 1, 255, final_track_element_location - (uint8*)track_element);
}
// 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);
}
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);
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);
td6->elements = NULL;
return false;
}
// 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 *vehicle_object;
if (td6->type == RIDE_TYPE_MAZE) {
vehicle_object = RCT2_ADDRESS(0x0097F66C, rct_object_entry);
} else {
int vehicle_type = td6->vehicle_type;
if (vehicle_type == RCT1_VEHICLE_TYPE_INVERTED_COASTER_TRAIN &&
td6->type == RIDE_TYPE_INVERTED_ROLLER_COASTER
) {
vehicle_type = RCT1_VEHICLE_TYPE_4_ACROSS_INVERTED_COASTER_TRAIN;
}
vehicle_object = &RCT2_ADDRESS(0x0097F0DC, rct_object_entry)[vehicle_type];
}
memcpy(&td6->vehicle_object, vehicle_object, 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,
RCT2_GLOBAL(RCT2_ADDRESS_RIDE_FLAGS + 5 + (td6->type * 8), uint8)
);
return true;
}

View File

@ -183,6 +183,8 @@ typedef struct {
uint8 space_required_y; // 0x81
uint8 vehicle_additional_colour[32]; // 0x82
uint8 lift_hill_speed_num_circuits; // 0xA2 0bCCCL_LLLL
void *elements; // 0xA3 (data starts here in file)
size_t elementsSize;
} rct_track_td6;
typedef struct{
@ -637,4 +639,7 @@ int track_get_actual_bank_3(rct_vehicle *vehicle, rct_map_element *mapElement);
bool track_element_is_station(rct_map_element *trackElement);
bool track_element_is_covered(int trackElementType);
bool track_design_open(rct_track_td6 *td6, const utf8 *path);
void track_design_index_create();
#endif

152
src/ride/track_list.c Normal file
View File

@ -0,0 +1,152 @@
#include "../common.h"
#include "../config.h"
#include "../util/util.h"
#include "track.h"
typedef struct {
uint8 ride_type;
char ride_entry[9];
utf8 path[MAX_PATH];
} td_index_item;
static void track_design_index_scan();
static int track_design_index_item_compare(const void *a, const void *b);
static void track_design_index_include(const utf8 *directory);
static void track_design_add_file(const utf8 *path);
static void track_design_add(const td_index_item *item);
static void track_design_index_dispose();
static void track_design_index_get_path(utf8 * buffer, size_t bufferLength);
static const uint32 TrackIndexMagicNumber = 0x58444954;
static const uint16 TrackIndexVersion = 0;
static td_index_item *_tdIndex = NULL;
static size_t _tdIndexSize = 0;
static size_t _tdIndexCapacity = 0;
void track_design_index_create()
{
track_design_index_dispose();
log_verbose("saving track list index (tracks.idx)");
utf8 path[MAX_PATH];
track_design_index_get_path(path, sizeof(path));
SDL_RWops *file = SDL_RWFromFile(path, "wb");
if (file != NULL) {
track_design_index_scan();
SDL_RWwrite(file, &TrackIndexMagicNumber, 4, 1);
SDL_RWwrite(file, &TrackIndexVersion, 4, 1);
SDL_RWwrite(file, &_tdIndexSize, 4, 1);
SDL_RWwrite(file, _tdIndex, sizeof(td_index_item), _tdIndexSize);
SDL_RWclose(file);
track_design_index_dispose();
}
}
static void track_design_index_scan()
{
utf8 directory[MAX_PATH];
// Get track directory from RCT2
safe_strcpy(directory, gConfigGeneral.game_path, sizeof(directory));
safe_strcat_path(directory, "Tracks", sizeof(directory));
track_design_index_include(directory);
// Get track directory from user directory
platform_get_user_directory(directory, "tracks");
track_design_index_include(directory);
// Sort items by ride type then by filename
qsort(_tdIndex, _tdIndexSize, sizeof(td_index_item), track_design_index_item_compare);
}
static int track_design_index_item_compare(const void *a, const void *b)
{
const td_index_item *tdA = (const td_index_item*)a;
const td_index_item *tdB = (const td_index_item*)b;
if (tdA->ride_type != tdB->ride_type) {
return tdA->ride_type - tdB->ride_type;
}
const utf8 *tdAName = path_get_filename(tdA->path);
const utf8 *tdBName = path_get_filename(tdB->path);
return strcmp(tdAName, tdBName);
}
static void track_design_index_include(const utf8 *directory)
{
int handle;
file_info fileInfo;
// Scenarios in this directory
utf8 pattern[MAX_PATH];
safe_strcpy(pattern, directory, sizeof(pattern));
safe_strcat_path(pattern, "*.td6", sizeof(pattern));
handle = platform_enumerate_files_begin(pattern);
while (platform_enumerate_files_next(handle, &fileInfo)) {
utf8 path[MAX_PATH];
safe_strcpy(path, directory, sizeof(pattern));
safe_strcat_path(path, fileInfo.path, sizeof(pattern));
track_design_add_file(path);
}
platform_enumerate_files_end(handle);
// Include sub-directories
utf8 subDirectory[MAX_PATH];
handle = platform_enumerate_directories_begin(directory);
while (platform_enumerate_directories_next(handle, subDirectory)) {
utf8 path[MAX_PATH];
safe_strcpy(path, directory, sizeof(pattern));
safe_strcat_path(path, subDirectory, sizeof(pattern));
track_design_index_include(path);
}
platform_enumerate_directories_end(handle);
}
static void track_design_add_file(const utf8 *path)
{
rct_track_td6 td6;
if (track_design_open(&td6, path)) {
td_index_item tdIndexItem = { 0 };
safe_strcpy(tdIndexItem.path, path, sizeof(tdIndexItem.path));
memcpy(tdIndexItem.ride_entry, td6.vehicle_object.name, 8);
tdIndexItem.ride_type = td6.type;
track_design_add(&tdIndexItem);
free(td6.elements);
}
}
static void track_design_add(const td_index_item *item)
{
size_t nextIndex = _tdIndexSize;
if (nextIndex >= _tdIndexCapacity) {
_tdIndexCapacity = max(128, _tdIndexCapacity * 2);
_tdIndex = realloc(_tdIndex, _tdIndexCapacity * sizeof(td_index_item));
if (_tdIndex == NULL) {
log_fatal("Unable to allocate more memory.");
exit(-1);
}
}
_tdIndex[nextIndex] = *item;
_tdIndexSize++;
}
static void track_design_index_dispose()
{
free(_tdIndex);
_tdIndexSize = 0;
_tdIndexCapacity = 0;
}
static void track_design_index_get_path(utf8 * buffer, size_t bufferLength)
{
platform_get_user_directory(buffer, NULL);
safe_strcat(buffer, "tracks.idx", bufferLength);
}