/***************************************************************************** * Copyright (c) 2014 Ted John * OpenRCT2, an open source clone of Roller Coaster Tycoon 2. * * This file is part of OpenRCT2. * * OpenRCT2 is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * You should have received a copy of the GNU General Public License * along with this program. If not, see . *****************************************************************************/ #include "addresses.h" #include "config.h" #include "localisation/localisation.h" #include "object.h" #include "platform/platform.h" #include "ride/ride.h" #include "util/sawyercoding.h" #include "drawing/drawing.h" #include "world/footpath.h" #include "world/water.h" #include "world/entrance.h" #include "world/scenery.h" #include "scenario.h" #include "rct1.h" char gTempObjectLoadName[9] = { 0 }; int object_load_entry(const utf8 *path, rct_object_entry *outEntry) { SDL_RWops *file; file = SDL_RWFromFile(path, "rb"); if (file == NULL) return 0; if (SDL_RWread(file, outEntry, sizeof(rct_object_entry), 1) != 1) { SDL_RWclose(file); return 0; } SDL_RWclose(file); return 1; } int object_load_file(int groupIndex, const rct_object_entry *entry, int* chunkSize, const rct_object_entry *installedObject) { uint8 objectType; rct_object_entry openedEntry; char path[MAX_PATH]; SDL_RWops* rw; substitute_path(path, RCT2_ADDRESS(RCT2_ADDRESS_OBJECT_DATA_PATH, char), (char*)installedObject + 16); log_verbose("loading object, %s", path); rw = SDL_RWFromFile(path, "rb"); if (rw == NULL) return 0; SDL_RWread(rw, &openedEntry, sizeof(rct_object_entry), 1); if (!object_entry_compare(&openedEntry, entry)) { SDL_RWclose(rw); return 0; } // Get chunk size uint8 *installedObject_pointer = (uint8*)installedObject + 16; // Skip file name while (*installedObject_pointer++); // Read chunk size *chunkSize = *((uint32*)installedObject_pointer); uint8 *chunk; if (*chunkSize == 0xFFFFFFFF) { chunk = (uint8*)malloc(0x600000); *chunkSize = sawyercoding_read_chunk(rw, chunk); chunk = realloc(chunk, *chunkSize); } else { chunk = (uint8*)malloc(*chunkSize); *chunkSize = sawyercoding_read_chunk(rw, chunk); } SDL_RWclose(rw); int calculatedChecksum=object_calculate_checksum(&openedEntry, chunk, *chunkSize); // Calculate and check checksum if (calculatedChecksum != openedEntry.checksum) { char buffer[100]; sprintf(buffer, "Object Load failed due to checksum failure: calculated checksum %d, object says %d.", calculatedChecksum, (int)openedEntry.checksum); log_error(buffer); RCT2_GLOBAL(0x00F42BD9, uint8) = 2; free(chunk); return 0; } objectType = openedEntry.flags & 0x0F; if (!object_test(objectType, chunk)) { log_error("Object Load failed due to paint failure."); RCT2_GLOBAL(0x00F42BD9, uint8) = 3; free(chunk); return 0; } if (RCT2_GLOBAL(RCT2_ADDRESS_TOTAL_NO_IMAGES, uint32) >= 0x4726E){ log_error("Object Load failed due to too many images loaded."); RCT2_GLOBAL(0x00F42BD9, uint8) = 4; free(chunk); return 0; } uint8** chunk_list = object_entry_groups[objectType].chunks; if (groupIndex == -1) { for (groupIndex = 0; chunk_list[groupIndex] != (uint8*)-1; groupIndex++) { if (groupIndex + 1 >= object_entry_group_counts[objectType]) { log_error("Object Load failed due to too many objects of a certain type."); RCT2_GLOBAL(0x00F42BD9, uint8) = 5; free(chunk); return 0; } } } chunk_list[groupIndex] = chunk; rct_object_entry_extended* extended_entry = &object_entry_groups[objectType].entries[groupIndex]; memcpy(extended_entry, &openedEntry, sizeof(rct_object_entry)); extended_entry->chunk_size = *chunkSize; gLastLoadedObjectChunkData = chunk; if (RCT2_GLOBAL(0x9ADAFD, uint8) != 0) { object_load(objectType, chunk, groupIndex); } return 1; } /** * * rct2: 0x006A985D */ int object_load_chunk(int groupIndex, rct_object_entry *entry, int* chunkSize) { // Alow chunkSize to be null int tempChunkSize; if (chunkSize == NULL) chunkSize = &tempChunkSize; RCT2_GLOBAL(0xF42B64, uint32) = groupIndex; if (gInstalledObjectsCount == 0) { RCT2_GLOBAL(0xF42BD9, uint8) = 0; log_error("Object Load failed due to no items installed check."); return 1; } rct_object_entry *installedObject = object_list_find(entry); if (installedObject == NULL) { log_error("object not installed"); return 0; } if (object_load_file(groupIndex, entry, chunkSize, installedObject)) return 1; return 0; } /** * * rct2: 0x006a9f42 * ebx: file * ebp: entry */ int write_object_file(SDL_RWops *rw, rct_object_entry* entry) { uint8 entryGroupIndex = 0, type = 0; uint8* chunk = 0; if (!find_object_in_entry_group(entry, &type, &entryGroupIndex))return 0; chunk = object_entry_groups[type].chunks[entryGroupIndex]; object_unload(type, chunk); rct_object_entry_extended* installed_entry = &object_entry_groups[type].entries[entryGroupIndex]; uint8* dst_buffer = (uint8*)malloc(0x600000); memcpy(dst_buffer, installed_entry, sizeof(rct_object_entry)); uint32 size_dst = sizeof(rct_object_entry); sawyercoding_chunk_header chunkHeader; // Encoding type (not used anymore) RCT2_GLOBAL(0x9E3CBD, uint8) = object_entry_group_encoding[type]; chunkHeader.encoding = object_entry_group_encoding[type]; chunkHeader.length = installed_entry->chunk_size; size_dst += sawyercoding_write_chunk_buffer(dst_buffer + sizeof(rct_object_entry), chunk, chunkHeader); SDL_RWwrite(rw, dst_buffer, 1, size_dst); free(dst_buffer); return 1; } /** * * rct2: 0x006AA2B7 */ int object_load_packed(SDL_RWops* rw) { object_unload_all(); rct_object_entry entry; SDL_RWread(rw, &entry, 16, 1); uint8* chunk = (uint8*)malloc(0x600000); uint32 chunkSize = sawyercoding_read_chunk(rw, chunk); chunk = realloc(chunk, chunkSize); if (chunk == NULL){ log_error("Failed to allocate memory for packed object."); return 0; } if (object_calculate_checksum(&entry, chunk, chunkSize) != entry.checksum){ log_error("Checksum mismatch from packed object: %.8s", entry.name); free(chunk); return 0; } int type = entry.flags & 0x0F; if (!object_test(type, chunk)) { log_error("Packed object failed paint test."); free(chunk); return 0; } if (RCT2_GLOBAL(RCT2_ADDRESS_TOTAL_NO_IMAGES, uint32) >= 0x4726E){ log_error("Packed object has too many images."); free(chunk); return 0; } int entryGroupIndex = 0; for (; entryGroupIndex < object_entry_group_counts[type]; entryGroupIndex++){ if (object_entry_groups[type].chunks[entryGroupIndex] == (uint8*)-1){ break; } } if (entryGroupIndex == object_entry_group_counts[type]){ // This should never occur. Objects are not loaded before installing a // packed object. So there is only one object loaded at this point. log_error("Too many objects of the same type loaded."); free(chunk); return 0; } // Copy the entry into the relevant entry group. object_entry_groups[type].chunks[entryGroupIndex] = chunk; rct_object_entry_extended* extended_entry = &object_entry_groups[type].entries[entryGroupIndex]; memcpy(extended_entry, &entry, sizeof(rct_object_entry)); extended_entry->chunk_size = chunkSize; // Ensure the entry does not already exist. rct_object_entry *installedObject = gInstalledObjects; if (gInstalledObjectsCount){ for (uint32 i = 0; i < gInstalledObjectsCount; ++i){ if (object_entry_compare(&entry, installedObject)){ object_unload_all(); return 0; } installedObject = object_get_next(installedObject); } } // Convert the entry name to a upper case path name char path[MAX_PATH]; char objectPath[9] = { 0 }; for (int i = 0; i < 8; ++i){ if (entry.name[i] != ' ') objectPath[i] = toupper(entry.name[i]); else objectPath[i] = '\0'; } substitute_path(path, RCT2_ADDRESS(RCT2_ADDRESS_OBJECT_DATA_PATH, char), objectPath); // Require pointer to start of filename char* last_char = path + strlen(path); strcat(path, ".DAT"); // Check that file does not exist // Adjust filename if it does. for (; platform_file_exists(path);){ for (char* curr_char = last_char - 1;; --curr_char){ if (*curr_char == '\\'){ substitute_path(path, RCT2_ADDRESS(RCT2_ADDRESS_OBJECT_DATA_PATH, char), "00000000.DAT"); char* last_char = path + strlen(path); break; } if (*curr_char < '0') *curr_char = '0'; else if (*curr_char == '9') *curr_char = 'A'; else if (*curr_char == 'Z') *curr_char = '0'; else (*curr_char)++; if (*curr_char != '0') break; } } // Actually write the object to the file SDL_RWops* rw_out = SDL_RWFromFile(path, "wb"); if (rw_out != NULL){ uint8 result = write_object_file(rw_out, &entry); SDL_RWclose(rw_out); object_unload_all(); return result; } object_unload_all(); return 0; } /** * * rct2: 0x006A9CAF */ void object_unload_chunk(rct_object_entry *entry) { uint8 object_type, object_index; if (!find_object_in_entry_group(entry, &object_type, &object_index)){ return; } uint8* chunk = object_entry_groups[object_type].chunks[object_index]; object_unload(object_type, chunk); free(chunk); object_entry_groups[object_type].chunks[object_index] = (uint8*)-1; } int object_entry_compare(const rct_object_entry *a, const rct_object_entry *b) { // If an official object don't bother checking checksum if ((a->flags & 0xF0) || (b->flags & 0xF0)) { if ((a->flags & 0x0F) != (b->flags & 0x0F)) return 0; int match = memcmp(a->name, b->name, 8); if (match) return 0; } else { if (a->flags != b->flags) return 0; int match = memcmp(a->name, b->name, 8); if (match) return 0; if (a->checksum != b->checksum) return 0; } return 1; } int object_calculate_checksum(const rct_object_entry *entry, const uint8 *data, int dataLength) { const uint8 *entryBytePtr = (uint8*)entry; uint32 checksum = 0xF369A75B; checksum ^= entryBytePtr[0]; checksum = rol32(checksum, 11); for (int i = 4; i < 12; i++) { checksum ^= entryBytePtr[i]; checksum = rol32(checksum, 11); } for (int i = 0; i < dataLength; i++) { checksum ^= data[i]; checksum = rol32(checksum, 11); } return (int)checksum; } /** * * rct2: 0x006A9ED1 */ int object_chunk_load_image_directory(uint8_t** chunk) { int image_start_no = RCT2_GLOBAL(RCT2_ADDRESS_TOTAL_NO_IMAGES, uint32_t); // First dword of chunk is no_images int no_images = *((uint32_t*)(*chunk)); *chunk += 4; // Second dword of chunk is length of image data int length_of_data = *((uint32_t*)(*chunk)); *chunk += 4; RCT2_GLOBAL(RCT2_ADDRESS_TOTAL_NO_IMAGES, uint32_t) = no_images + image_start_no; rct_g1_element* g1_dest = &g1Elements[image_start_no]; // After length of data is the start of all g1 element structs rct_g1_element* g1_source = (rct_g1_element*)(*chunk); // After the g1 element structs is the actual images. uint8* image_offset = no_images * sizeof(rct_g1_element) + (uint8*)g1_source; for (int i = 0; i < no_images; ++i){ *g1_dest = *g1_source++; g1_dest->offset += (uint32)image_offset; g1_dest++; } *chunk = ((uint8*)g1_source) + length_of_data; return image_start_no; } typedef bool (*object_load_func)(void *objectEntry, uint32 entryIndex); typedef void (*object_unload_func)(void *objectEntry); typedef bool (*object_test_func)(void *objectEntry); typedef void (*object_paint_func)(void *objectEntry, rct_drawpixelinfo *dpi, sint32 x, sint32 y); typedef rct_string_id (*object_desc_func)(void *objectEntry); /** * Represents addresses for virtual object functions. */ typedef struct { object_load_func load; object_unload_func unload; object_test_func test; object_paint_func paint; object_desc_func desc; } object_type_vtable; /////////////////////////////////////////////////////////////////////////////// // Ride (rct2: 0x006E6E2A) /////////////////////////////////////////////////////////////////////////////// static bool object_type_ride_load(void *objectEntry, uint32 entryIndex) { rct_ride_type *rideEntry = (rct_ride_type*)objectEntry; // After rideEntry is 3 string tables uint8 *extendedEntryData = (uint8*)((size_t)objectEntry + sizeof(rct_ride_type)); rideEntry->name = object_get_localised_text(&extendedEntryData, OBJECT_TYPE_RIDE, entryIndex, 0); rideEntry->description = object_get_localised_text(&extendedEntryData, OBJECT_TYPE_RIDE, entryIndex, 1); //TODO: Move to its own function when ride construction window is merged. if (gConfigInterface.select_by_track_type) { rideEntry->enabledTrackPieces = 0xFFFFFFFFFFFFFFFF; } object_get_localised_text(&extendedEntryData, OBJECT_TYPE_RIDE, entryIndex, 2); rideEntry->vehicle_preset_list = (vehicle_colour_preset_list*)extendedEntryData; // If Unknown struct size is 0xFF then there are 32 3 byte structures uint8 unknown_size = *extendedEntryData++; if (unknown_size != 0xFF) { extendedEntryData += unknown_size * 3; } else { extendedEntryData += 0x60; } sint8 *peep_loading_positions = (sint8*)extendedEntryData; // Peep loading positions variable size // 4 different vehicle subtypes are available for (int i = 0; i < 4; i++){ uint16 no_peep_positions = *extendedEntryData++; // If no_peep_positions is 0xFF then no_peep_positions is a word if (no_peep_positions == 0xFF) { no_peep_positions = *((uint16*)extendedEntryData); extendedEntryData += 2; } extendedEntryData += no_peep_positions; } int images_offset = object_chunk_load_image_directory(&extendedEntryData); rideEntry->images_offset = images_offset; int cur_vehicle_images_offset = images_offset + 3; for (int i = 0; i < 4; i++) { rct_ride_type_vehicle* vehicleEntry = &rideEntry->vehicles[i]; if (vehicleEntry->sprite_flags & VEHICLE_SPRITE_FLAG_FLAT) { int al = 1; if (vehicleEntry->flags_b & VEHICLE_ENTRY_FLAG_B_SWINGING) { al = 13; if ((vehicleEntry->flags_b & (VEHICLE_ENTRY_FLAG_B_5 | VEHICLE_ENTRY_FLAG_B_11)) != (VEHICLE_ENTRY_FLAG_B_5 | VEHICLE_ENTRY_FLAG_B_11)) { al = 7; if (!(vehicleEntry->flags_b & VEHICLE_ENTRY_FLAG_B_5)) { if (!(vehicleEntry->flags_b & VEHICLE_ENTRY_FLAG_B_11)) { al = 5; if (vehicleEntry->flags_b & VEHICLE_ENTRY_FLAG_B_9) { al = 3; } } } } } vehicleEntry->var_03 = al; // 0x6DE90B al = 0x20; if (!(vehicleEntry->flags_a & VEHICLE_ENTRY_FLAG_A_14)) { al = 1; if (vehicleEntry->flags_b & VEHICLE_ENTRY_FLAG_B_7) { if (vehicleEntry->var_11 != 6) { al = 2; if (!(vehicleEntry->flags_a & VEHICLE_ENTRY_FLAG_A_7)) { al = 4; } } } } if (vehicleEntry->flags_a & VEHICLE_ENTRY_FLAG_A_12) { al = vehicleEntry->special_frames; } vehicleEntry->var_02 = al; // 0x6DE946 vehicleEntry->var_16 = vehicleEntry->var_02 * vehicleEntry->var_03; vehicleEntry->base_image_id = cur_vehicle_images_offset; int image_index = vehicleEntry->base_image_id; if (vehicleEntry->car_visual != VEHICLE_VISUAL_RIVER_RAPIDS) { int b = vehicleEntry->var_16 * 32; if (vehicleEntry->flags_a & VEHICLE_ENTRY_FLAG_A_11) b /= 2; if (vehicleEntry->sprite_flags & VEHICLE_SPRITE_FLAG_15) b /= 8; image_index += b; // Incline 25 if (vehicleEntry->sprite_flags & VEHICLE_SPRITE_FLAG_GENTLE_SLOPES) { vehicleEntry->var_20 = image_index; b = vehicleEntry->var_16 * 72; if (vehicleEntry->flags_a & VEHICLE_ENTRY_FLAG_A_14) b = vehicleEntry->var_16 * 16; image_index += b; } // Incline 60 if (vehicleEntry->sprite_flags & VEHICLE_SPRITE_FLAG_STEEP_SLOPES) { vehicleEntry->var_24 = image_index; b = vehicleEntry->var_16 * 80; image_index += b; } // Verticle if (vehicleEntry->sprite_flags & VEHICLE_SPRITE_FLAG_VERTICAL_SLOPES) { vehicleEntry->var_28 = image_index; b = vehicleEntry->var_16 * 116; image_index += b; } // Unknown if (vehicleEntry->sprite_flags & VEHICLE_SPRITE_FLAG_DIAGONAL_SLOPES) { vehicleEntry->var_2C = image_index; b = vehicleEntry->var_16 * 24; image_index += b; } // Bank if (vehicleEntry->sprite_flags & VEHICLE_SPRITE_FLAG_FLAT_BANKED) { vehicleEntry->var_30 = image_index; b = vehicleEntry->var_16 * 80; image_index += b; } if (vehicleEntry->sprite_flags & VEHICLE_SPRITE_FLAG_INLINE_TWISTS) { vehicleEntry->var_34 = image_index; b = vehicleEntry->var_16 * 40; image_index += b; } // Track half? Up/Down if (vehicleEntry->sprite_flags & VEHICLE_SPRITE_FLAG_FLAT_TO_GENTLE_SLOPE_BANKED_TRANSITIONS) { vehicleEntry->var_38 = image_index; b = vehicleEntry->var_16 * 128; image_index += b; } // Unknown if (vehicleEntry->sprite_flags & VEHICLE_SPRITE_FLAG_DIAGONAL_GENTLE_SLOPE_BANKED_TRANSITIONS) { vehicleEntry->var_3C = image_index; b = vehicleEntry->var_16 * 16; image_index += b; } // Unknown if (vehicleEntry->sprite_flags & VEHICLE_SPRITE_FLAG_GENTLE_SLOPE_BANKED_TRANSITIONS) { vehicleEntry->var_40 = image_index; b = vehicleEntry->var_16 * 16; image_index += b; } if (vehicleEntry->sprite_flags & VEHICLE_SPRITE_FLAG_GENTLE_SLOPE_BANKED_TURNS) { vehicleEntry->var_44 = image_index; b = vehicleEntry->var_16 * 128; image_index += b; } if (vehicleEntry->sprite_flags & VEHICLE_SPRITE_FLAG_FLAT_TO_GENTLE_SLOPE_WHILE_BANKED_TRANSITIONS) { vehicleEntry->var_48 = image_index; b = vehicleEntry->var_16 * 16; image_index += b; } if (vehicleEntry->sprite_flags & VEHICLE_SPRITE_FLAG_CORKSCREWS) { vehicleEntry->var_4C = image_index; b = vehicleEntry->var_16 * 80; image_index += b; } // Unknown if (vehicleEntry->sprite_flags & VEHICLE_SPRITE_FLAG_RESTRAINT_ANIMATION) { vehicleEntry->var_1C = image_index; b = vehicleEntry->var_16 * 12; image_index += b; } if (vehicleEntry->sprite_flags & VEHICLE_SPRITE_FLAG_14) { // Same offset as above??? vehicleEntry->var_4C = image_index; b = vehicleEntry->var_16 * 32; image_index += b; } } else { image_index += vehicleEntry->var_16 * 36; } // No vehicle images vehicleEntry->no_vehicle_images = image_index - cur_vehicle_images_offset; // Move the offset over this vehicles images. Including peeps cur_vehicle_images_offset = image_index + vehicleEntry->no_seating_rows * vehicleEntry->no_vehicle_images; // 0x6DEB0D if (!(vehicleEntry->flags_a & VEHICLE_ENTRY_FLAG_A_10)) { int num_images = cur_vehicle_images_offset - vehicleEntry->base_image_id; if (vehicleEntry->flags_a & VEHICLE_ENTRY_FLAG_A_13) { num_images *= 2; } set_vehicle_type_image_max_sizes(vehicleEntry, num_images); } sint8 no_positions = *peep_loading_positions++; if (no_positions == -1) { // The no_positions is 16 bit skip over peep_loading_positions += 2; } vehicleEntry->peep_loading_positions = peep_loading_positions; } } // 0x6DEB71 if (RCT2_GLOBAL(0x9ADAFD, uint8) == 0) { for (int i = 0; i < 3; i++) { int dl = rideEntry->ride_type[i]; if (dl == 0xFF) { continue; } uint8 *typeToRideEntryIndexMap = RCT2_ADDRESS(0x009E32F8, uint8); while (dl >= 0) { if (*typeToRideEntryIndexMap++ == 0xFF) { dl--; } } typeToRideEntryIndexMap--; uint8 previous_entry = entryIndex; while (typeToRideEntryIndexMap < RCT2_ADDRESS(0x9E34E4, uint8)){ uint8 backup_entry = *typeToRideEntryIndexMap; *typeToRideEntryIndexMap++ = previous_entry; previous_entry = backup_entry; } } } // 0x6DEBAA if (RCT2_GLOBAL(0x9ADAF4, sint32) != 0xFFFFFFFF) { *RCT2_GLOBAL(0x9ADAF4, uint16*) = 0; } int di = rideEntry->ride_type[0] | (rideEntry->ride_type[1] << 8) | (rideEntry->ride_type[2] << 16); if ((rideEntry->flags & RIDE_ENTRY_FLAG_SEPARATE_RIDE_NAME) && !rideTypeShouldLoseSeparateFlag(rideEntry)) { di |= 0x1000000; } RCT2_GLOBAL(0xF433DD, uint32) = di; return true; } static void object_type_ride_unload(void *objectEntry) { rct_ride_type *rideEntry = (rct_ride_type*)objectEntry; rideEntry->name = 0; rideEntry->description = 0; rideEntry->images_offset = 0; for (int i = 0; i < 4; i++) { rct_ride_type_vehicle* rideVehicleEntry = &rideEntry->vehicles[i]; rideVehicleEntry->base_image_id = 0; rideVehicleEntry->var_1C = 0; rideVehicleEntry->var_20 = 0; rideVehicleEntry->var_24 = 0; rideVehicleEntry->var_28 = 0; rideVehicleEntry->var_2C = 0; rideVehicleEntry->var_30 = 0; rideVehicleEntry->var_34 = 0; rideVehicleEntry->var_38 = 0; rideVehicleEntry->var_3C = 0; rideVehicleEntry->var_40 = 0; rideVehicleEntry->var_44 = 0; rideVehicleEntry->var_48 = 0; rideVehicleEntry->var_4C = 0; rideVehicleEntry->no_vehicle_images = 0; rideVehicleEntry->var_16 = 0; if (!(rideVehicleEntry->flags_a & VEHICLE_ENTRY_FLAG_A_10)) { rideVehicleEntry->sprite_width = 0; rideVehicleEntry->sprite_height_negative = 0; rideVehicleEntry->sprite_height_positive = 0; } rideVehicleEntry->var_02 = 0; rideVehicleEntry->var_03 = 0; rideVehicleEntry->peep_loading_positions = 0; } rideEntry->vehicle_preset_list = NULL; } static bool object_type_ride_test(void *objectEntry) { rct_ride_type* rideEntry = (rct_ride_type*)objectEntry; if (rideEntry->excitement_multipler > 75) return false; if (rideEntry->intensity_multipler > 75) return false; if (rideEntry->nausea_multipler > 75) return false; return true; } static void object_type_ride_paint(void *objectEntry, rct_drawpixelinfo *dpi, sint32 x, sint32 y) { rct_ride_type *rideEntry = (rct_ride_type*)objectEntry; int imageId = rideEntry->images_offset; if (rideEntry->ride_type[0] == 0xFF) { imageId++; if (rideEntry->ride_type[1] == 0xFF) { imageId++; } } gfx_draw_sprite(dpi, imageId, x - 56, y - 56, 0); } static rct_string_id object_type_ride_desc(void *objectEntry) { rct_ride_type *rideEntry = (rct_ride_type*)objectEntry; // Get description rct_string_id stringId = rideEntry->description; if (!(rideEntry->flags & RIDE_ENTRY_FLAG_SEPARATE_RIDE_NAME) || rideTypeShouldLoseSeparateFlag(rideEntry)) { uint8 rideType = rideEntry->ride_type[0]; if (rideType == 0xFF) { rideType = rideEntry->ride_type[1]; if (rideType == 0xFF) { rideType = rideEntry->ride_type[2]; } } stringId = 512 + rideType; } return stringId; } static const object_type_vtable object_type_ride_vtable[] = { object_type_ride_load, object_type_ride_unload, object_type_ride_test, object_type_ride_paint, object_type_ride_desc }; /////////////////////////////////////////////////////////////////////////////// // Small Scenery (rct2: 0x006E3466) /////////////////////////////////////////////////////////////////////////////// static bool object_type_small_scenery_load(void *objectEntry, uint32 entryIndex) { rct_scenery_entry* sceneryEntry = (rct_scenery_entry*)objectEntry; uint8 *extendedEntryData = (uint8*)((size_t)objectEntry + (size_t)0x1C); sceneryEntry->name = object_get_localised_text(&extendedEntryData, OBJECT_TYPE_SMALL_SCENERY, entryIndex, 0); sceneryEntry->small_scenery.scenery_tab_id = 0xFF; if (*extendedEntryData != 0xFF) { uint8 entry_type, entry_index; if (find_object_in_entry_group((rct_object_entry*)extendedEntryData, &entry_type, &entry_index)) { sceneryEntry->small_scenery.scenery_tab_id = entry_index; } } extendedEntryData += sizeof(rct_object_entry); if (sceneryEntry->small_scenery.flags & SMALL_SCENERY_FLAG16){ sceneryEntry->small_scenery.var_10 = (uint32)extendedEntryData; while (*++extendedEntryData != 0xFF); extendedEntryData++; } sceneryEntry->image = object_chunk_load_image_directory(&extendedEntryData); if (RCT2_GLOBAL(0x9ADAF4, uint32) != 0xFFFFFFFF) { *RCT2_GLOBAL(0x9ADAF4, uint16*) = 0; } return true; } static void object_type_small_scenery_unload(void *objectEntry) { rct_scenery_entry *sceneryEntry = (rct_scenery_entry*)objectEntry; sceneryEntry->name = 0; sceneryEntry->image = 0; sceneryEntry->small_scenery.var_10 = 0; sceneryEntry->small_scenery.scenery_tab_id = 0; } static bool object_type_small_scenery_test(void *objectEntry) { rct_scenery_entry *sceneryEntry = (rct_scenery_entry*)objectEntry; if (sceneryEntry->small_scenery.price <= 0) return false; if (sceneryEntry->small_scenery.removal_price > 0) return true; // Make sure you don't make a profit when placing then removing. if (-sceneryEntry->small_scenery.removal_price > sceneryEntry->small_scenery.price) return false; return true; } static void object_type_small_scenery_paint(void *objectEntry, rct_drawpixelinfo *dpi, sint32 x, sint32 y) { rct_scenery_entry* sceneryEntry = (rct_scenery_entry*)objectEntry; rct_drawpixelinfo clipDPI; if (!clip_drawpixelinfo(&clipDPI, dpi, x - 56, y - 56, 112, 112)) { return; } int imageId = sceneryEntry->image; if (sceneryEntry->small_scenery.flags & SMALL_SCENERY_FLAG_HAS_PRIMARY_COLOUR) { imageId |= 0x20D00000; if (sceneryEntry->small_scenery.flags & SMALL_SCENERY_FLAG_HAS_SECONDARY_COLOUR) { imageId |= 0x92000000; } } x = 56; y = sceneryEntry->small_scenery.height / 4 + 78; if (sceneryEntry->small_scenery.flags & SMALL_SCENERY_FLAG_FULL_TILE) { if (sceneryEntry->small_scenery.flags & SMALL_SCENERY_FLAG_VOFFSET_CENTRE) { y -= 12; } } gfx_draw_sprite(&clipDPI, imageId, x, y, 0); if (sceneryEntry->small_scenery.flags & SMALL_SCENERY_FLAG10) { imageId = sceneryEntry->image + 0x44500004; if (sceneryEntry->small_scenery.flags & SMALL_SCENERY_FLAG_HAS_SECONDARY_COLOUR) { imageId |= 0x92000000; } gfx_draw_sprite(&clipDPI, imageId, x, y, 0); } if (sceneryEntry->small_scenery.flags & SMALL_SCENERY_FLAG8) { imageId = sceneryEntry->image + 4; if (sceneryEntry->small_scenery.flags & SMALL_SCENERY_FLAG_HAS_SECONDARY_COLOUR) { imageId |= 0x92000000; } gfx_draw_sprite(&clipDPI, imageId, x, y, 0); } } static rct_string_id object_type_small_scenery_desc(void *objectEntry) { return STR_NONE; } static const object_type_vtable object_type_small_scenery_vtable[] = { object_type_small_scenery_load, object_type_small_scenery_unload, object_type_small_scenery_test, object_type_small_scenery_paint, object_type_small_scenery_desc }; /////////////////////////////////////////////////////////////////////////////// // Large Scenery (rct2: 0x006B92A7) /////////////////////////////////////////////////////////////////////////////// static bool object_type_large_scenery_load(void *objectEntry, uint32 entryIndex) { rct_scenery_entry* sceneryEntry = (rct_scenery_entry*)objectEntry; uint8 *extendedEntryData = (uint8*)((size_t)objectEntry + (size_t)0x1A); sceneryEntry->name = object_get_localised_text(&extendedEntryData, OBJECT_TYPE_LARGE_SCENERY, entryIndex, 0); sceneryEntry->large_scenery.scenery_tab_id = 0xFF; if (*extendedEntryData != 0xFF) { uint8 entry_type, entry_index; if (find_object_in_entry_group((rct_object_entry*)extendedEntryData, &entry_type, &entry_index)) { sceneryEntry->large_scenery.scenery_tab_id = entry_index; } } extendedEntryData += sizeof(rct_object_entry); if (sceneryEntry->large_scenery.flags & (1 << 2)) { sceneryEntry->large_scenery.var_12 = (uint32)extendedEntryData; extendedEntryData += 1038; } sceneryEntry->large_scenery.tiles = (rct_large_scenery_tile*)extendedEntryData; // skip over large scenery tiles while (*((uint16*)extendedEntryData) != 0xFFFF){ extendedEntryData += sizeof(rct_large_scenery_tile); } extendedEntryData += 2; int imageId = object_chunk_load_image_directory(&extendedEntryData); if (sceneryEntry->large_scenery.flags & (1 << 2)){ sceneryEntry->large_scenery.var_16 = imageId; uint8* edx = (uint8*)sceneryEntry->large_scenery.var_12; if (!(edx[0xC] & 1)) { imageId += edx[0xD] * 4; } else{ imageId += edx[0xD] * 2; } } sceneryEntry->image = imageId; if (RCT2_GLOBAL(0x9ADAF4, uint32) != 0xFFFFFFFF) { *RCT2_GLOBAL(0x9ADAF4, uint16*) = 0; } return true; } static void object_type_large_scenery_unload(void *objectEntry) { rct_scenery_entry *sceneryEntry = (rct_scenery_entry*)objectEntry; sceneryEntry->name = 0; sceneryEntry->image = 0; sceneryEntry->large_scenery.tiles = 0; sceneryEntry->large_scenery.scenery_tab_id = 0; sceneryEntry->large_scenery.var_12 = 0; sceneryEntry->large_scenery.var_16 = 0; } static bool object_type_large_scenery_test(void *objectEntry) { rct_scenery_entry *sceneryEntry = (rct_scenery_entry*)objectEntry; if (sceneryEntry->large_scenery.price <= 0) return false; if (sceneryEntry->large_scenery.removal_price > 0) return true; // Make sure you don't make a profit when placing then removing. if (-sceneryEntry->large_scenery.removal_price > sceneryEntry->large_scenery.price) return false; return true; } static void object_type_large_scenery_paint(void *objectEntry, rct_drawpixelinfo *dpi, sint32 x, sint32 y) { rct_scenery_entry* sceneryEntry = (rct_scenery_entry*)objectEntry; int imageId = sceneryEntry->image | 0xB2D00000; gfx_draw_sprite(dpi, imageId, x, y - 39, 0); } static rct_string_id object_type_large_scenery_desc(void *objectEntry) { return STR_NONE; } static const object_type_vtable object_type_large_scenery_vtable[] = { object_type_large_scenery_load, object_type_large_scenery_unload, object_type_large_scenery_test, object_type_large_scenery_paint, object_type_large_scenery_desc }; /////////////////////////////////////////////////////////////////////////////// // Wall (rct2: 0x006E5A25) /////////////////////////////////////////////////////////////////////////////// static bool object_type_wall_load(void *objectEntry, uint32 entryIndex) { rct_scenery_entry* sceneryEntry = (rct_scenery_entry*)objectEntry; uint8 *extendedEntryData = (uint8*)((size_t)objectEntry + (size_t)0x0E); sceneryEntry->name = object_get_localised_text(&extendedEntryData, OBJECT_TYPE_WALLS, entryIndex, 0); sceneryEntry->wall.scenery_tab_id = 0xFF; if (*extendedEntryData != 0xFF){ uint8 entry_type, entry_index; if (find_object_in_entry_group((rct_object_entry*)extendedEntryData, &entry_type, &entry_index)) { sceneryEntry->wall.scenery_tab_id = entry_index; } } extendedEntryData += sizeof(rct_object_entry); sceneryEntry->image = object_chunk_load_image_directory(&extendedEntryData); if (RCT2_GLOBAL(0x9ADAF4, uint32) != 0xFFFFFFFF) { *RCT2_GLOBAL(0x9ADAF4, uint16*) = 0; } return true; } static void object_type_wall_unload(void *objectEntry) { rct_scenery_entry *sceneryEntry = (rct_scenery_entry*)objectEntry; sceneryEntry->name = 0; sceneryEntry->image = 0; sceneryEntry->wall.scenery_tab_id = 0; } static bool object_type_wall_test(void *objectEntry) { rct_scenery_entry *sceneryEntry = (rct_scenery_entry*)objectEntry; if (sceneryEntry->wall.price <= 0) return false; return true; } static void object_type_wall_paint(void *objectEntry, rct_drawpixelinfo *dpi, sint32 x, sint32 y) { rct_scenery_entry* sceneryEntry = (rct_scenery_entry*)objectEntry; rct_drawpixelinfo clipDPI; if (!clip_drawpixelinfo(&clipDPI, dpi, x - 56, y - 56, 112, 112)) { return; } int imageId = sceneryEntry->image; imageId |= 0x20D00000; if (sceneryEntry->wall.flags & WALL_SCENERY_HAS_SECONDARY_COLOUR) { imageId |= 0x92000000; } x = 70; y = sceneryEntry->wall.height * 2 + 72; gfx_draw_sprite(&clipDPI, imageId, x, y, 0); if (sceneryEntry->wall.flags & WALL_SCENERY_FLAG2){ imageId = sceneryEntry->image + 0x44500006; gfx_draw_sprite(&clipDPI, imageId, x, y, 0); } else if (sceneryEntry->wall.flags & WALL_SCENERY_FLAG5){ imageId++; gfx_draw_sprite(&clipDPI, imageId, x, y, 0); } } static rct_string_id object_type_wall_desc(void *objectEntry) { return STR_NONE; } static const object_type_vtable object_type_wall_vtable[] = { object_type_wall_load, object_type_wall_unload, object_type_wall_test, object_type_wall_paint, object_type_wall_desc }; /////////////////////////////////////////////////////////////////////////////// // Banner (rct2: 0x006BA84E) /////////////////////////////////////////////////////////////////////////////// static bool object_type_banner_load(void *objectEntry, uint32 entryIndex) { rct_scenery_entry* sceneryEntry = (rct_scenery_entry*)objectEntry; uint8 *extendedEntryData = (uint8*)((size_t)objectEntry + (size_t)0x0C); sceneryEntry->name = object_get_localised_text(&extendedEntryData, OBJECT_TYPE_BANNERS, entryIndex, 0); sceneryEntry->banner.scenery_tab_id = 0xFF; if (*extendedEntryData != 0xFF){ uint8 entry_type, entry_index; if (find_object_in_entry_group((rct_object_entry*)extendedEntryData, &entry_type, &entry_index)){ sceneryEntry->banner.scenery_tab_id = entry_index; } } extendedEntryData += sizeof(rct_object_entry); sceneryEntry->image = object_chunk_load_image_directory(&extendedEntryData); if (RCT2_GLOBAL(0x9ADAF4, uint32) != 0xFFFFFFFF) { *RCT2_GLOBAL(0x9ADAF4, uint16*) = 0; } return true; } static void object_type_banner_unload(void *objectEntry) { rct_scenery_entry *sceneryEntry = (rct_scenery_entry*)objectEntry; sceneryEntry->name = 0; sceneryEntry->image = 0; sceneryEntry->banner.scenery_tab_id = 0; } static bool object_type_banner_test(void *objectEntry) { rct_scenery_entry *sceneryEntry = (rct_scenery_entry*)objectEntry; if (sceneryEntry->banner.price <= 0) return false; return true; } static void object_type_banner_paint(void *objectEntry, rct_drawpixelinfo *dpi, sint32 x, sint32 y) { rct_scenery_entry* sceneryEntry = (rct_scenery_entry*)objectEntry; int imageId = sceneryEntry->image | 0x20D00000; gfx_draw_sprite(dpi, imageId, x, y, 0); gfx_draw_sprite(dpi, imageId + 1, x, y, 0); } static rct_string_id object_type_banner_desc(void *objectEntry) { return STR_NONE; } static const object_type_vtable object_type_banner_vtable[] = { object_type_banner_load, object_type_banner_unload, object_type_banner_test, object_type_banner_paint, object_type_banner_desc }; /////////////////////////////////////////////////////////////////////////////// // Path (rct2: 0x006A8621) /////////////////////////////////////////////////////////////////////////////// static bool object_type_path_load(void *objectEntry, uint32 entryIndex) { rct_path_type *pathEntry = (rct_path_type*)objectEntry; uint8 *extendedEntryData = (uint8*)((size_t)objectEntry + (size_t)0x0E); pathEntry->string_idx = object_get_localised_text(&extendedEntryData, OBJECT_TYPE_PATHS, entryIndex, 0); int imageId = object_chunk_load_image_directory(&extendedEntryData); pathEntry->image = imageId; pathEntry->bridge_image = imageId + 109; if (RCT2_GLOBAL(0x9ADAF4, uint32) != 0xFFFFFFFF) { *RCT2_GLOBAL(0x9ADAF4, uint16*) = 0; } RCT2_GLOBAL(RCT2_ADDRESS_SELECTED_PATH_ID, sint16) = 0; // Set the default path for when opening footpath window for (int i = 0; i < object_entry_group_counts[OBJECT_TYPE_PATHS]; i++) { rct_path_type *pathEntry2 = (rct_path_type*)object_entry_groups[OBJECT_TYPE_PATHS].chunks[i]; if (pathEntry2 == (rct_path_type*)-1) { continue; } if (!(pathEntry2->flags & 4)) { RCT2_GLOBAL(RCT2_ADDRESS_SELECTED_PATH_ID, sint16) = i; break; } RCT2_GLOBAL(RCT2_ADDRESS_SELECTED_PATH_ID, sint16) = i; } return true; } static void object_type_path_unload(void *objectEntry) { rct_path_type *pathEntry = (rct_path_type*)objectEntry; pathEntry->string_idx = 0; pathEntry->image = 0; pathEntry->bridge_image = 0; } static bool object_type_path_test(void *objectEntry) { rct_path_type *pathEntry = (rct_path_type*)objectEntry; if (pathEntry->var_0A >= 2) return false; return true; } static void object_type_path_paint(void *objectEntry, rct_drawpixelinfo *dpi, sint32 x, sint32 y) { rct_path_type *pathEntry = (rct_path_type*)objectEntry; gfx_draw_sprite(dpi, pathEntry->image + 71, x - 49, y - 17, 0); gfx_draw_sprite(dpi, pathEntry->image + 72, x + 4, y - 17, 0); } static rct_string_id object_type_path_desc(void *objectEntry) { return STR_NONE; } static const object_type_vtable object_type_path_vtable[] = { object_type_path_load, object_type_path_unload, object_type_path_test, object_type_path_paint, object_type_path_desc }; /////////////////////////////////////////////////////////////////////////////// // Path Item (rct2: 0x006A86E2) /////////////////////////////////////////////////////////////////////////////// static bool object_type_path_bit_load(void *objectEntry, uint32 entryIndex) { rct_scenery_entry* sceneryEntry = (rct_scenery_entry*)objectEntry; uint8 *extendedEntryData = (uint8*)((size_t)objectEntry + (size_t)0x0E); sceneryEntry->name = object_get_localised_text(&extendedEntryData, OBJECT_TYPE_PATH_BITS, entryIndex, 0); sceneryEntry->path_bit.scenery_tab_id = 0xFF; if (*extendedEntryData != 0xFF) { uint8 entry_type, entry_index; if (find_object_in_entry_group((rct_object_entry*)extendedEntryData, &entry_type, &entry_index)){ sceneryEntry->path_bit.scenery_tab_id = entry_index; } } extendedEntryData += sizeof(rct_object_entry); sceneryEntry->image = object_chunk_load_image_directory(&extendedEntryData); if (RCT2_GLOBAL(0x9ADAF4, uint32) != 0xFFFFFFFF) { *RCT2_GLOBAL(0x9ADAF4, uint16*) = 0; } return true; } static void object_type_path_bit_unload(void *objectEntry) { rct_scenery_entry *sceneryEntry = (rct_scenery_entry*)objectEntry; sceneryEntry->name = 0; sceneryEntry->image = 0; sceneryEntry->path_bit.scenery_tab_id = 0; } static bool object_type_path_bit_test(void *objectEntry) { rct_scenery_entry *sceneryEntry = (rct_scenery_entry*)objectEntry; if (sceneryEntry->path_bit.price <= 0) return false; return true; } static void object_type_path_bit_paint(void *objectEntry, rct_drawpixelinfo *dpi, sint32 x, sint32 y) { rct_scenery_entry* sceneryEntry = (rct_scenery_entry*)objectEntry; gfx_draw_sprite(dpi, sceneryEntry->image, x - 22, y - 24, 0); } static rct_string_id object_type_path_bit_desc(void *objectEntry) { return STR_NONE; } static const object_type_vtable object_type_path_bit_vtable[] = { object_type_path_bit_load, object_type_path_bit_unload, object_type_path_bit_test, object_type_path_bit_paint, object_type_path_bit_desc }; /////////////////////////////////////////////////////////////////////////////// // Scenery Set (rct2: 0x006B93AA) /////////////////////////////////////////////////////////////////////////////// static bool object_type_scenery_set_load(void *objectEntry, uint32 entryIndex) { rct_scenery_set_entry *scenerySetEntry = (rct_scenery_set_entry*)objectEntry; uint8 *extendedEntryData = (uint8*)((size_t)objectEntry + sizeof(rct_scenery_set_entry)); scenerySetEntry->name = object_get_localised_text(&extendedEntryData, OBJECT_TYPE_SCENERY_SETS, entryIndex, 0); rct_object_entry *entryObjects = NULL; uint8 *eax = RCT2_GLOBAL(0x9ADAF4, uint8*); if ((uint32)eax != 0xFFFFFFFF){ *((uint16*)eax) = 0; entryObjects = (rct_object_entry*)(eax + 2); } scenerySetEntry->entry_count = 0; scenerySetEntry->var_107 = 0; for (; *extendedEntryData != 0xFF; extendedEntryData += sizeof(rct_object_entry)) { scenerySetEntry->var_107++; if (entryObjects != NULL){ memcpy(entryObjects, extendedEntryData, sizeof(rct_object_entry)); entryObjects++; (*(eax + 1))++; } uint8 entry_type; uint8 entry_index = 0; if (!find_object_in_entry_group((rct_object_entry*)extendedEntryData, &entry_type, &entry_index)) continue; uint16 scenery_entry = entry_index; switch (entry_type){ case OBJECT_TYPE_SMALL_SCENERY: break; case OBJECT_TYPE_LARGE_SCENERY: scenery_entry |= 0x300; break; case OBJECT_TYPE_WALLS: scenery_entry |= 0x200; break; case OBJECT_TYPE_PATH_BITS: scenery_entry |= 0x100; break; default: scenery_entry |= 0x400; break; } scenerySetEntry->scenery_entries[scenerySetEntry->entry_count++] = scenery_entry; } extendedEntryData++; scenerySetEntry->image = object_chunk_load_image_directory(&extendedEntryData); return true; } static void object_type_scenery_set_unload(void *objectEntry) { rct_scenery_set_entry *scenerySetEntry = (rct_scenery_set_entry*)objectEntry; scenerySetEntry->name = 0; scenerySetEntry->image = 0; scenerySetEntry->entry_count = 0; scenerySetEntry->var_107 = 0; memset(scenerySetEntry->scenery_entries, 0, 256); } static bool object_type_scenery_set_test(void *objectEntry) { return true; } static void object_type_scenery_set_paint(void *objectEntry, rct_drawpixelinfo *dpi, sint32 x, sint32 y) { rct_scenery_set_entry *scenerySetEntry = (rct_scenery_set_entry*)objectEntry; int imageId = scenerySetEntry->image + 0x20600001; gfx_draw_sprite(dpi, imageId, x - 15, y - 14, 0); } static rct_string_id object_type_scenery_set_desc(void *objectEntry) { return STR_NONE; } static const object_type_vtable object_type_scenery_set_vtable[] = { object_type_scenery_set_load, object_type_scenery_set_unload, object_type_scenery_set_test, object_type_scenery_set_paint, object_type_scenery_set_desc }; /////////////////////////////////////////////////////////////////////////////// // Park Entrance (rct2: 0x00666E42) /////////////////////////////////////////////////////////////////////////////// bool object_type_park_entrance_load(void *objectEntry, uint32 entryIndex) { rct_entrance_type *entranceType = (rct_entrance_type*)objectEntry; uint8 *extendedEntryData = (uint8*)((size_t)objectEntry + sizeof(rct_entrance_type)); entranceType->string_idx = object_get_localised_text(&extendedEntryData, OBJECT_TYPE_PARK_ENTRANCE, entryIndex, 0); entranceType->image_id = object_chunk_load_image_directory(&extendedEntryData); if (RCT2_GLOBAL(0x9ADAF4, uint32) != 0xFFFFFFFF) { *RCT2_GLOBAL(0x9ADAF4, uint16*) = 0; } return true; } static void object_type_park_entrance_unload(void *objectEntry) { rct_entrance_type *entranceType = (rct_entrance_type*)objectEntry; entranceType->string_idx = 0; entranceType->image_id = 0; } static bool object_type_park_entrance_test(void *objectEntry) { return true; } static void object_type_park_entrance_paint(void *objectEntry, rct_drawpixelinfo *dpi, sint32 x, sint32 y) { rct_entrance_type *entranceType = (rct_entrance_type*)objectEntry; rct_drawpixelinfo clipDPI; if (!clip_drawpixelinfo(&clipDPI, dpi, x - 56, y - 56, 112, 112)) { return; } int imageId = entranceType->image_id; gfx_draw_sprite(&clipDPI, imageId + 1, 24, 68, 0); gfx_draw_sprite(&clipDPI, imageId, 56, 84, 0); gfx_draw_sprite(&clipDPI, imageId + 2, 88, 100, 0); } static rct_string_id object_type_park_entrance_desc(void *objectEntry) { return STR_NONE; } static const object_type_vtable object_type_park_entrance_vtable[] = { object_type_park_entrance_load, object_type_park_entrance_unload, object_type_park_entrance_test, object_type_park_entrance_paint, object_type_park_entrance_desc }; /////////////////////////////////////////////////////////////////////////////// // Water (rct2: 0x006E6E2A) /////////////////////////////////////////////////////////////////////////////// static bool object_type_water_load(void *objectEntry, uint32 entryIndex) { rct_water_type *waterEntry = (rct_water_type*)objectEntry; uint8 *pStringTable = (uint8*)((size_t)objectEntry + sizeof(rct_water_type)); waterEntry->string_idx = object_get_localised_text(&pStringTable, OBJECT_TYPE_WATER, entryIndex, 0); int imageId = object_chunk_load_image_directory(&pStringTable); waterEntry->image_id = imageId; waterEntry->var_06 = imageId + 1; waterEntry->var_0A = imageId + 4; if (RCT2_GLOBAL(0x009ADAF4, uint32) != 0xFFFFFFFF) { *RCT2_GLOBAL(0x009ADAF4, uint16*) = 0; } if (RCT2_GLOBAL(0x009ADAFD, uint8) == 0) { load_palette(); gfx_invalidate_screen(); } return true; } static void object_type_water_unload(void *objectEntry) { rct_water_type *waterEntry = (rct_water_type*)objectEntry; waterEntry->string_idx = 0; waterEntry->image_id = 0; waterEntry->var_06 = 0; waterEntry->var_0A = 0; } static bool object_type_water_test(void *objectEntry) { return true; } static void object_type_water_paint(void *objectEntry, rct_drawpixelinfo *dpi, sint32 x, sint32 y) { // Write (no image) gfx_draw_string_centred(dpi, 3326, x, y, 0, NULL); } static rct_string_id object_type_water_desc(void *objectEntry) { return STR_NONE; } static const object_type_vtable object_type_water_vtable[] = { object_type_water_load, object_type_water_unload, object_type_water_test, object_type_water_paint, object_type_water_desc }; /////////////////////////////////////////////////////////////////////////////// // Stex (rct2: 0x0066B355) /////////////////////////////////////////////////////////////////////////////// static bool object_type_stex_load(void *objectEntry, uint32 entryIndex) { rct_stex_entry *stexEntry = (rct_stex_entry*)objectEntry; uint8 *stringTable = (uint8*)((size_t)objectEntry + (size_t)0x08); stexEntry->scenario_name = object_get_localised_text(&stringTable, OBJECT_TYPE_SCENARIO_TEXT, entryIndex, 0); stexEntry->park_name = object_get_localised_text(&stringTable, OBJECT_TYPE_SCENARIO_TEXT, entryIndex, 1); stexEntry->details = object_get_localised_text(&stringTable, OBJECT_TYPE_SCENARIO_TEXT, entryIndex, 2); if (RCT2_GLOBAL(0x9ADAF4, int) != -1) { RCT2_GLOBAL(0x9ADAF4, uint16*)[0] = 0; } return true; } static void object_type_stex_unload(void *objectEntry) { rct_stex_entry *stexEntry = (rct_stex_entry*)objectEntry; stexEntry->scenario_name = 0; stexEntry->park_name = 0; stexEntry->details = 0; } static bool object_type_stex_test(void *objectEntry) { return true; } static void object_type_stex_paint(void *objectEntry, rct_drawpixelinfo *dpi, sint32 x, sint32 y) { // Write (no image) gfx_draw_string_centred(dpi, 3326, x, y, 0, NULL); } static rct_string_id object_type_stex_desc(void *objectEntry) { rct_stex_entry *stexEntry = (rct_stex_entry*)objectEntry; return stexEntry->details; } static const object_type_vtable object_type_stex_vtable[] = { object_type_stex_load, object_type_stex_unload, object_type_stex_test, object_type_stex_paint, object_type_stex_desc }; /////////////////////////////////////////////////////////////////////////////// static const object_type_vtable * const object_type_vtables[] = { object_type_ride_vtable, object_type_small_scenery_vtable, object_type_large_scenery_vtable, object_type_wall_vtable, object_type_banner_vtable, object_type_path_vtable, object_type_path_bit_vtable, object_type_scenery_set_vtable, object_type_park_entrance_vtable, object_type_water_vtable, object_type_stex_vtable }; bool object_load(int type, void *objectEntry, uint32 entryIndex) { assert(type >= OBJECT_TYPE_RIDE && type <= OBJECT_TYPE_SCENARIO_TEXT); const object_type_vtable *vtable = object_type_vtables[type]; return vtable->load(objectEntry, entryIndex) ? 0 : 1; } void object_unload(int type, void *objectEntry) { assert(type >= OBJECT_TYPE_RIDE && type <= OBJECT_TYPE_SCENARIO_TEXT); const object_type_vtable *vtable = object_type_vtables[type]; vtable->unload(objectEntry); } bool object_test(int type, void *objectEntry) { assert(type >= OBJECT_TYPE_RIDE && type <= OBJECT_TYPE_SCENARIO_TEXT); const object_type_vtable *vtable = object_type_vtables[type]; return vtable->test(objectEntry); } void object_paint(int type, void *objectEntry, rct_drawpixelinfo *dpi, sint32 x, sint32 y) { assert(type >= OBJECT_TYPE_RIDE && type <= OBJECT_TYPE_SCENARIO_TEXT); const object_type_vtable *vtable = object_type_vtables[type]; vtable->paint(objectEntry, dpi, x, y); } rct_string_id object_desc(int type, void *objectEntry) { assert(type >= OBJECT_TYPE_RIDE && type <= OBJECT_TYPE_SCENARIO_TEXT); const object_type_vtable *vtable = object_type_vtables[type]; return vtable->desc(objectEntry); } /** * * rct2: 0x006A9428 */ int object_get_scenario_text(rct_object_entry *entry) { rct_object_entry *installedObject = gInstalledObjects; installedObject = object_list_find(entry); if (installedObject == NULL){ log_error("Object not found: %.8s", entry->name); RCT2_GLOBAL(0x00F42BD9, uint8) = 0; return 0; } char path[MAX_PATH]; char *objectPath = (char*)installedObject + 16; substitute_path(path, RCT2_ADDRESS(RCT2_ADDRESS_OBJECT_DATA_PATH, char), objectPath); rct_object_entry openedEntry; SDL_RWops* rw = SDL_RWFromFile(path, "rb"); if (rw != NULL) { SDL_RWread(rw, &openedEntry, sizeof(rct_object_entry), 1); if (object_entry_compare(&openedEntry, entry)) { // Skip over the object entry char *pos = (char*)installedObject + sizeof(rct_object_entry); // Skip file name while (*pos++); // Read chunk int chunkSize = *((uint32*)pos); uint8 *chunk; if (chunkSize == 0xFFFFFFFF) { chunk = (uint8*)malloc(0x600000); chunkSize = sawyercoding_read_chunk(rw, chunk); chunk = realloc(chunk, chunkSize); } else { chunk = (uint8*)malloc(chunkSize); sawyercoding_read_chunk(rw, chunk); } SDL_RWclose(rw); // Calculate and check checksum if (object_calculate_checksum(&openedEntry, chunk, chunkSize) != openedEntry.checksum) { log_error("Opened object failed calculated checksum."); RCT2_GLOBAL(0x00F42BD9, uint8) = 2; free(chunk); return 0; } if (!object_test(openedEntry.flags & 0x0F, chunk)) { // This is impossible for STEX entries to fail. log_error("Opened object failed paint test."); RCT2_GLOBAL(0x00F42BD9, uint8) = 3; free(chunk); return 0; } // Save the real total images. int total_no_images = RCT2_GLOBAL(RCT2_ADDRESS_TOTAL_NO_IMAGES, uint32); // This is being changed to force the images to be loaded into a different // image id. RCT2_GLOBAL(RCT2_ADDRESS_TOTAL_NO_IMAGES, uint32) = 0x726E; RCT2_GLOBAL(RCT2_ADDRESS_SCENARIO_TEXT_TEMP_CHUNK, uint32) = (int)chunk; // Not used anywhere. RCT2_GLOBAL(RCT2_ADDRESS_SCENARIO_TEXT_TEMP_OBJECT, rct_object_entry) = openedEntry; // Tell text to be loaded into a different address RCT2_GLOBAL(0x009ADAFC, uint8) = 255; memcpy(gTempObjectLoadName, openedEntry.name, 8); // Not used?? RCT2_GLOBAL(0x009ADAFD, uint8) = 1; object_load(openedEntry.flags & 0x0F, chunk, 0); // Tell text to be loaded into normal address RCT2_GLOBAL(0x009ADAFC, uint8) = 0; // Not used?? RCT2_GLOBAL(0x009ADAFD, uint8) = 0; RCT2_GLOBAL(RCT2_ADDRESS_TOTAL_NO_IMAGES, uint32) = total_no_images; return 1; } log_error("Opened object didn't match."); SDL_RWclose(rw); return 0; } log_error("File failed to open."); RCT2_GLOBAL(0x00F42BD9, uint8) = 0; return 0; } /** * * rct2: 0x006A982D */ void object_free_scenario_text() { if (RCT2_GLOBAL(RCT2_ADDRESS_SCENARIO_TEXT_TEMP_CHUNK, void*) != NULL) { free(RCT2_GLOBAL(RCT2_ADDRESS_SCENARIO_TEXT_TEMP_CHUNK, void*)); RCT2_GLOBAL(RCT2_ADDRESS_SCENARIO_TEXT_TEMP_CHUNK, void*) = NULL; } } int object_get_length(rct_object_entry *entry) { return (int)object_get_next(entry) - (int)entry; } rct_object_entry *object_get_next(rct_object_entry *entry) { uint8 *pos = (uint8*)entry; // Skip sizeof(rct_object_entry) pos += 16; // Skip filename while (*pos++); // Skip no of images pos += 4; // Skip name while (*pos++); // Skip size of chunk pos += 4; // Skip required objects pos += *pos * 16 + 1; // Skip theme objects pos += *pos * 16 + 1; // Skip pos += 4; return (rct_object_entry*)pos; } char *object_get_name(rct_object_entry *entry) { uint8 *pos = (uint8*)entry; // Skip sizeof(rct_object_entry) pos += 16; // Skip filename while (*pos++); // Skip no of images pos += 4; return (char *)pos; }