/***************************************************************************** * 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 = rct2_malloc(0x600000); *chunkSize = sawyercoding_read_chunk(rw, chunk); chunk = rct2_realloc(chunk, *chunkSize); } else { chunk = rct2_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; rct2_free(chunk); return 0; } objectType = openedEntry.flags & 0x0F; if (object_paint(objectType, 2, 0, objectType, 0, (int)chunk, 0, 0)) { log_error("Object Load failed due to paint failure."); RCT2_GLOBAL(0x00F42BD9, uint8) = 3; rct2_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; rct2_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; rct2_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; RCT2_GLOBAL(RCT2_ADDRESS_CURR_OBJECT_CHUNK_POINTER, uint8*) = chunk; if (RCT2_GLOBAL(0x9ADAFD, uint8) != 0) object_paint(objectType, 0, groupIndex, objectType, 0, (int)chunk, 0, 0); return 1; } /** * * rct2: 0x006A985D */ int object_load(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 (RCT2_GLOBAL(RCT2_ADDRESS_OBJECT_LIST_NO_ITEMS, uint32) == 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_paint(type, 1, entryGroupIndex, type, 0, (int)chunk, 0, 0); rct_object_entry_extended* installed_entry = &object_entry_groups[type].entries[entryGroupIndex]; uint8* dst_buffer = 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 = rct2_malloc(0x600000); uint32 chunkSize = sawyercoding_read_chunk(rw, chunk); chunk = rct2_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); rct2_free(chunk); return 0; } int type = entry.flags & 0x0F; if (object_paint(type, 2, 0, type, 0, (int)chunk, 0, 0)) { log_error("Packed object failed paint test."); rct2_free(chunk); return 0; } if (RCT2_GLOBAL(RCT2_ADDRESS_TOTAL_NO_IMAGES, uint32) >= 0x4726E){ log_error("Packed object has too many images."); rct2_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."); rct2_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 = RCT2_GLOBAL(RCT2_ADDRESS_INSTALLED_OBJECT_LIST, rct_object_entry*); if (RCT2_GLOBAL(RCT2_ADDRESS_OBJECT_LIST_NO_ITEMS, uint32)){ for (uint32 i = 0; i < RCT2_GLOBAL(RCT2_ADDRESS_OBJECT_LIST_NO_ITEMS, uint32); ++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(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_paint(object_type, 1, 0, 0, 0, (int)chunk, 0, 0); rct2_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; } /** * * rct2: 0x006DE83E */ int paint_ride_entry(int flags, int ebx, int ecx, int edx, rct_drawpixelinfo* dpi, int esi, int ebp) { if ((flags & 0xFF) == 0){ // esi rct_ride_type* ride_type = (rct_ride_type*)esi; // After ride_type is 3 string tables uint8_t* chunk = (uint8*)(ride_type + 1); ride_type->name = object_get_localised_text(&chunk, ecx, ebx, 0); ride_type->description = object_get_localised_text(&chunk, ecx, ebx, 1); //TODO: Move to its own function when ride construction window is merged. if(gConfigInterface.select_by_track_type) { ride_type->enabledTrackPieces = 0xFFFFFFFFFFFFFFFF; } object_get_localised_text(&chunk, ecx, ebx, 2); ride_type->vehicle_preset_list = (vehicle_colour_preset_list*)chunk; // If Unknown struct size is 0xFF then there are 32 3 byte structures uint8 unknown_size = *chunk++; if (unknown_size != 0xFF) { chunk += unknown_size * 3; } else { chunk += 0x60; } sint8* peep_loading_positions = (sint8 *)chunk; // Peep loading positions variable size // 4 different vehicle subtypes are available for (int i = 0; i < 4; ++i){ uint16 no_peep_positions = *chunk++; // If no_peep_positions is 0xFF then no_peep_positions is a word if (no_peep_positions == 0xFF) { no_peep_positions = *((uint16_t*)chunk); chunk += 2; } chunk += no_peep_positions; } int images_offset = object_chunk_load_image_directory(&chunk); ride_type->images_offset = images_offset; int cur_vehicle_images_offset = images_offset + 3; for (int i = 0; i < 4; ++i){ rct_ride_type_vehicle* rideVehicleEntry = &ride_type->vehicles[i]; if (rideVehicleEntry->sprite_flags & VEHICLE_SPRITE_FLAG_FLAT) { int al = 1; if (rideVehicleEntry->var_14 & 2) { al = 13; if ((rideVehicleEntry->var_14 & 0x820) != 0x820) { al = 7; if (!(rideVehicleEntry->var_14 & 0x20)) { if (!(rideVehicleEntry->var_14 & 0x800)) { al = 5; if (rideVehicleEntry->var_14 & 0x200) { al = 3; } } } } } rideVehicleEntry->var_03 = al; // 0x6DE90B al = 0x20; if (!(rideVehicleEntry->var_12 & 0x4000)) { al = 1; if (rideVehicleEntry->var_14 & 0x80) { if (rideVehicleEntry->var_11 != 6) { al = 2; if (!(rideVehicleEntry->var_12 & 0x80)) al = 4; } } } if (rideVehicleEntry->var_12 & 0x1000) al = rideVehicleEntry->special_frames; rideVehicleEntry->var_02 = al; // 0x6DE946 rideVehicleEntry->var_16 = rideVehicleEntry->var_02 * rideVehicleEntry->var_03; rideVehicleEntry->base_image_id = cur_vehicle_images_offset; int image_index = rideVehicleEntry->base_image_id; if (rideVehicleEntry->car_visual != VEHICLE_VISUAL_RIVER_RAPIDS) { int b = rideVehicleEntry->var_16 * 32; if (rideVehicleEntry->var_12 & 0x800) b /= 2; if (rideVehicleEntry->sprite_flags & VEHICLE_SPRITE_FLAG_15) b /= 8; image_index += b; // Incline 25 if (rideVehicleEntry->sprite_flags & VEHICLE_SPRITE_FLAG_GENTLE_SLOPES) { rideVehicleEntry->var_20 = image_index; b = rideVehicleEntry->var_16 * 72; if (rideVehicleEntry->var_12 & 0x4000) b = rideVehicleEntry->var_16 * 16; image_index += b; } // Incline 60 if (rideVehicleEntry->sprite_flags & VEHICLE_SPRITE_FLAG_STEEP_SLOPES) { rideVehicleEntry->var_24 = image_index; b = rideVehicleEntry->var_16 * 80; image_index += b; } // Verticle if (rideVehicleEntry->sprite_flags & VEHICLE_SPRITE_FLAG_VERTICAL_SLOPES) { rideVehicleEntry->var_28 = image_index; b = rideVehicleEntry->var_16 * 116; image_index += b; } // Unknown if (rideVehicleEntry->sprite_flags & VEHICLE_SPRITE_FLAG_DIAGONAL_SLOPES) { rideVehicleEntry->var_2C = image_index; b = rideVehicleEntry->var_16 * 24; image_index += b; } // Bank if (rideVehicleEntry->sprite_flags & VEHICLE_SPRITE_FLAG_FLAT_BANKED) { rideVehicleEntry->var_30 = image_index; b = rideVehicleEntry->var_16 * 80; image_index += b; } if (rideVehicleEntry->sprite_flags & VEHICLE_SPRITE_FLAG_INLINE_TWISTS) { rideVehicleEntry->var_34 = image_index; b = rideVehicleEntry->var_16 * 40; image_index += b; } // Track half? Up/Down if (rideVehicleEntry->sprite_flags & VEHICLE_SPRITE_FLAG_FLAT_TO_GENTLE_SLOPE_BANKED_TRANSITIONS) { rideVehicleEntry->var_38 = image_index; b = rideVehicleEntry->var_16 * 128; image_index += b; } // Unknown if (rideVehicleEntry->sprite_flags & VEHICLE_SPRITE_FLAG_DIAGONAL_GENTLE_SLOPE_BANKED_TRANSITIONS) { rideVehicleEntry->var_3C = image_index; b = rideVehicleEntry->var_16 * 16; image_index += b; } // Unknown if (rideVehicleEntry->sprite_flags & VEHICLE_SPRITE_FLAG_GENTLE_SLOPE_BANKED_TRANSITIONS) { rideVehicleEntry->var_40 = image_index; b = rideVehicleEntry->var_16 * 16; image_index += b; } if (rideVehicleEntry->sprite_flags & VEHICLE_SPRITE_FLAG_GENTLE_SLOPE_BANKED_TURNS) { rideVehicleEntry->var_44 = image_index; b = rideVehicleEntry->var_16 * 128; image_index += b; } if (rideVehicleEntry->sprite_flags & VEHICLE_SPRITE_FLAG_FLAT_TO_GENTLE_SLOPE_WHILE_BANKED_TRANSITIONS) { rideVehicleEntry->var_48 = image_index; b = rideVehicleEntry->var_16 * 16; image_index += b; } if (rideVehicleEntry->sprite_flags & VEHICLE_SPRITE_FLAG_CORKSCREWS) { rideVehicleEntry->var_4C = image_index; b = rideVehicleEntry->var_16 * 80; image_index += b; } // Unknown if (rideVehicleEntry->sprite_flags & VEHICLE_SPRITE_FLAG_RESTRAINT_ANIMATION) { rideVehicleEntry->var_1C = image_index; b = rideVehicleEntry->var_16 * 12; image_index += b; } if (rideVehicleEntry->sprite_flags & VEHICLE_SPRITE_FLAG_14) { // Same offset as above??? rideVehicleEntry->var_4C = image_index; b = rideVehicleEntry->var_16 * 32; image_index += b; } } else{ image_index += rideVehicleEntry->var_16 * 36; } // No vehicle images rideVehicleEntry->no_vehicle_images = image_index - cur_vehicle_images_offset; // Move the offset over this vehicles images. Including peeps cur_vehicle_images_offset = image_index + rideVehicleEntry->no_seating_rows * rideVehicleEntry->no_vehicle_images; // 0x6DEB0D if (!(rideVehicleEntry->var_12 & 0x400)){ int num_images = cur_vehicle_images_offset - rideVehicleEntry->base_image_id; if (rideVehicleEntry->var_12 & 0x2000){ num_images *= 2; } set_vehicle_type_image_max_sizes(rideVehicleEntry, num_images); } sint8 no_positions = *peep_loading_positions++; if (no_positions == -1) { // The no_positions is 16 bit skip over peep_loading_positions += 2; } rideVehicleEntry->peep_loading_positions = peep_loading_positions; } } // 0x6DEB71 if (RCT2_GLOBAL(0x9ADAFD, uint8_t) == 0) { for (int i = 0; i < 3; ++i){ int dl = ride_type->ride_type[i]; if (dl == 0xFF)continue; uint8 *typeToRideEntryIndexMap = RCT2_ADDRESS(0x009E32F8, uint8); while (dl >= 0){ if (*typeToRideEntryIndexMap++ == 0xFF)dl--; } typeToRideEntryIndexMap--; uint8 previous_entry = ebx; 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 = ride_type->ride_type[0] | (ride_type->ride_type[1] << 8) | (ride_type->ride_type[2] << 16); if ((ride_type->flags & RIDE_ENTRY_FLAG_SEPARATE_RIDE_NAME) && !rideTypeShouldLoseSeparateFlag(ride_type)) di |= 0x1000000; RCT2_GLOBAL(0xF433DD, uint32) = di; return 0;// flags; } else if ((flags & 0xFF) == 1){ // Object Unload rct_ride_type* ride_type = (rct_ride_type*)esi; ride_type->name = 0; ride_type->description = 0; ride_type->images_offset = 0; for (int i = 0; i < 4; ++i){ rct_ride_type_vehicle* rideVehicleEntry = &ride_type->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->var_12 & 0x400)){ 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; } ride_type->vehicle_preset_list = NULL; return flags; } else if ((flags & 0xFF) == 2){ rct_ride_type* ride_type = (rct_ride_type*)esi; if (ride_type->excitement_multipler > 75) return 1; if (ride_type->intensity_multipler > 75) return 1; if (ride_type->nausea_multipler > 75) return 1; return 0; } else if ((flags & 0xFF) == 3){ // Paint object picture and description rct_ride_type* ride_type = (rct_ride_type*)ebp; int x = ecx, y = edx; if (!((flags >> 8) & 0xFF)) { int image_id = ride_type->images_offset; if (ride_type->ride_type[0] == 0xFF) { image_id++; if (ride_type->ride_type[1] == 0xFF) image_id++; } gfx_draw_sprite(dpi, image_id, x - 56, y - 56, ebp); return flags; } else { rct_window* w = (rct_window*)esi; int width = w->x + w->width - x - 4; int format_args = ride_type->description; if (!(ride_type->flags & RIDE_ENTRY_FLAG_SEPARATE_RIDE_NAME) || rideTypeShouldLoseSeparateFlag(ride_type)) { format_args = ride_type->ride_type[0]; if ((format_args & 0xFF) == 0xFF) { format_args = ride_type->ride_type[1]; if ((format_args & 0xFF) == 0xFF) format_args = ride_type->ride_type[2]; } format_args += 0x200; } RCT2_GLOBAL(RCT2_ADDRESS_COMMON_FORMAT_ARGS, short) = format_args; gfx_draw_string_left_wrapped(dpi, (void*)RCT2_ADDRESS_COMMON_FORMAT_ARGS, x, y + 5, width, 1191, 0); return flags; } } return flags; } /** * * rct2: 0x006E3466 */ int paint_small_scenery(int flags, int ebx, int ecx, int edx, rct_drawpixelinfo* dpi, int esi, int ebp) { if ((flags & 0xFF) == 0){ // Object Load rct_scenery_entry* scenery_type = (rct_scenery_entry*)esi; uint8* chunk = (uint8*)(esi + 0x1C); scenery_type->name = object_get_localised_text(&chunk, ecx, ebx, 0); scenery_type->small_scenery.scenery_tab_id = 0xFF; if (*chunk != 0xFF){ uint8 entry_type, entry_index; if (find_object_in_entry_group((rct_object_entry*)chunk, &entry_type, &entry_index)){ scenery_type->small_scenery.scenery_tab_id = entry_index; } } chunk += sizeof(rct_object_entry); if (scenery_type->small_scenery.flags & SMALL_SCENERY_FLAG16){ scenery_type->small_scenery.var_10 = (uint32)chunk; while (*++chunk != 0xFF); chunk++; } scenery_type->image = object_chunk_load_image_directory(&chunk); if (RCT2_GLOBAL(0x9ADAF4, uint32) != 0xFFFFFFFF) *RCT2_GLOBAL(0x9ADAF4, uint16*) = 0; } else if ((flags & 0xFF) == 1){ // Object Unload rct_scenery_entry* scenery_type = (rct_scenery_entry*)esi; scenery_type->name = 0; scenery_type->image = 0; scenery_type->small_scenery.var_10 = 0; scenery_type->small_scenery.scenery_tab_id = 0; } else if ((flags & 0xFF) == 2){ rct_scenery_entry* scenery_type = (rct_scenery_entry*)esi; if (scenery_type->small_scenery.price <= 0)return 1; if (scenery_type->small_scenery.removal_price > 0) return 0; // Make sure you don't make a profit when placing then removing. if (-scenery_type->small_scenery.removal_price > scenery_type->small_scenery.price)return 1; return 0; } else if ((flags & 0xFF) == 3){ int x = ecx, y = edx; if (!((flags >> 8) & 0xFF)) { rct_scenery_entry* scenery_type = (rct_scenery_entry*)ebp; dpi = clip_drawpixelinfo(dpi, x - 56, 112, y - 56, 112); if (dpi == NULL) return flags; int image_id = scenery_type->image; if (scenery_type->small_scenery.flags & SMALL_SCENERY_FLAG_HAS_PRIMARY_COLOUR){ image_id |= 0x20D00000; if (scenery_type->small_scenery.flags & SMALL_SCENERY_FLAG_HAS_SECONDARY_COLOUR) image_id |= 0x92000000; } x = 56; y = scenery_type->small_scenery.height / 4 + 78; if (scenery_type->small_scenery.flags & SMALL_SCENERY_FLAG_FULL_TILE){ if (scenery_type->small_scenery.flags & SMALL_SCENERY_FLAG_VOFFSET_CENTRE){ y -= 12; } } gfx_draw_sprite(dpi, image_id, x, y, 0); if (scenery_type->small_scenery.flags & SMALL_SCENERY_FLAG10){ image_id = scenery_type->image + 0x44500004; if (scenery_type->small_scenery.flags & SMALL_SCENERY_FLAG_HAS_SECONDARY_COLOUR) image_id |= 0x92000000; gfx_draw_sprite(dpi, image_id, x, y, 0); } if (scenery_type->small_scenery.flags & SMALL_SCENERY_FLAG8){ image_id = scenery_type->image + 4; if (scenery_type->small_scenery.flags & SMALL_SCENERY_FLAG_HAS_SECONDARY_COLOUR) image_id |= 0x92000000; gfx_draw_sprite(dpi, image_id, x, y, 0); } rct2_free(dpi); } } return flags; } /** * * rct2: 0x006B92A7 */ int paint_large_scenery(int flags, int ebx, int ecx, int edx, rct_drawpixelinfo* dpi, int esi, int ebp) { if ((flags & 0xFF) == 0){ // Object Load rct_scenery_entry* scenery_type = (rct_scenery_entry*)esi; uint8* chunk = (uint8*)(esi + 0x1A); scenery_type->name = object_get_localised_text(&chunk, ecx, ebx, 0); scenery_type->large_scenery.scenery_tab_id = 0xFF; if (*chunk != 0xFF){ uint8 entry_type, entry_index; if (find_object_in_entry_group((rct_object_entry*)chunk, &entry_type, &entry_index)){ scenery_type->large_scenery.scenery_tab_id = entry_index; } } chunk += sizeof(rct_object_entry); if (scenery_type->large_scenery.flags & (1<<2)){ scenery_type->large_scenery.var_12 = (uint32)chunk; chunk += 1038; } scenery_type->large_scenery.tiles = (rct_large_scenery_tile*)chunk; // skip over large scenery tiles while (*((uint16*)chunk) != 0xFFFF){ chunk += sizeof(rct_large_scenery_tile); } chunk += 2; int image_id = object_chunk_load_image_directory(&chunk); if (scenery_type->large_scenery.flags & (1 << 2)){ scenery_type->large_scenery.var_16 = image_id; uint8* edx = (uint8*)scenery_type->large_scenery.var_12; if (!(edx[0xC] & 1)){ image_id += edx[0xD] * 4; } else{ image_id += edx[0xD] * 2; } } scenery_type->image = image_id; if (RCT2_GLOBAL(0x9ADAF4, uint32) != 0xFFFFFFFF) *RCT2_GLOBAL(0x9ADAF4, uint16*) = 0; } else if ((flags & 0xFF) == 1){ // Object Unload rct_scenery_entry* scenery_type = (rct_scenery_entry*)esi; scenery_type->name = 0; scenery_type->image = 0; scenery_type->large_scenery.tiles = 0; scenery_type->large_scenery.scenery_tab_id = 0; scenery_type->large_scenery.var_12 = 0; scenery_type->large_scenery.var_16 = 0; } else if ((flags & 0xFF) == 2){ rct_scenery_entry* scenery_type = (rct_scenery_entry*)esi; if (scenery_type->large_scenery.price <= 0)return 1; if (scenery_type->large_scenery.removal_price > 0) return 0; // Make sure you don't make a profit when placing then removing. if (-scenery_type->large_scenery.removal_price > scenery_type->large_scenery.price)return 1; return 0; } else if ((flags & 0xFF) == 3){ int x = ecx, y = edx; if (!((flags >> 8) & 0xFF)) { rct_scenery_entry* scenery_type = (rct_scenery_entry*)ebp; int image_id = scenery_type->image | 0xB2D00000; gfx_draw_sprite(dpi, image_id, x, y - 39, 0); } } return flags; } /** * * rct2: 0x006E5A25 */ int paint_wall(int flags, int ebx, int ecx, int edx, rct_drawpixelinfo* dpi, int esi, int ebp) { if ((flags & 0xFF) == 0){ // Object Load rct_scenery_entry* scenery_type = (rct_scenery_entry*)esi; uint8* chunk = (uint8*)(esi + 0xE); scenery_type->name = object_get_localised_text(&chunk, ecx, ebx, 0); scenery_type->wall.scenery_tab_id = 0xFF; if (*chunk != 0xFF){ uint8 entry_type, entry_index; if (find_object_in_entry_group((rct_object_entry*)chunk, &entry_type, &entry_index)){ scenery_type->wall.scenery_tab_id = entry_index; } } chunk += sizeof(rct_object_entry); scenery_type->image = object_chunk_load_image_directory(&chunk); if (RCT2_GLOBAL(0x9ADAF4, uint32) != 0xFFFFFFFF) *RCT2_GLOBAL(0x9ADAF4, uint16*) = 0; } else if ((flags & 0xFF) == 1){ // Object Unload rct_scenery_entry* scenery_type = (rct_scenery_entry*)esi; scenery_type->name = 0; scenery_type->image = 0; scenery_type->wall.scenery_tab_id = 0; } else if ((flags & 0xFF) == 2){ rct_scenery_entry* scenery_type = (rct_scenery_entry*)esi; if (scenery_type->wall.price <= 0)return 1; return 0; } else if ((flags & 0xFF) == 3){ int x = ecx, y = edx; if (!((flags >> 8) & 0xFF)) { rct_scenery_entry* scenery_type = (rct_scenery_entry*)ebp; dpi = clip_drawpixelinfo(dpi, x - 56, 112, y - 56, 112); if (dpi == NULL) return flags; int image_id = scenery_type->image; image_id |= 0x20D00000; if (scenery_type->wall.flags & WALL_SCENERY_HAS_SECONDARY_COLOUR) image_id |= 0x92000000; x = 70; y = scenery_type->wall.height * 2 + 72; gfx_draw_sprite(dpi, image_id, x, y, 0); if (scenery_type->wall.flags & WALL_SCENERY_FLAG2){ image_id = scenery_type->image + 0x44500006; gfx_draw_sprite(dpi, image_id, x, y, 0); } else if (scenery_type->wall.flags & WALL_SCENERY_FLAG5){ image_id++; gfx_draw_sprite(dpi, image_id, x, y, 0); } rct2_free(dpi); } } return flags; } /** * * rct2: 0x006BA84E */ int paint_banner(int flags, int ebx, int ecx, int edx, rct_drawpixelinfo* dpi, int esi, int ebp) { if ((flags & 0xFF) == 0){ // Object Load rct_scenery_entry* scenery_type = (rct_scenery_entry*)esi; uint8* chunk = (uint8*)(esi + 0xC); scenery_type->name = object_get_localised_text(&chunk, ecx, ebx, 0); scenery_type->banner.scenery_tab_id = 0xFF; if (*chunk != 0xFF){ uint8 entry_type, entry_index; if (find_object_in_entry_group((rct_object_entry*)chunk, &entry_type, &entry_index)){ scenery_type->banner.scenery_tab_id = entry_index; } } chunk += sizeof(rct_object_entry); scenery_type->image = object_chunk_load_image_directory(&chunk); if (RCT2_GLOBAL(0x9ADAF4, uint32) != 0xFFFFFFFF) *RCT2_GLOBAL(0x9ADAF4, uint16*) = 0; } else if ((flags & 0xFF) == 1){ // Object Unload rct_scenery_entry* scenery_type = (rct_scenery_entry*)esi; scenery_type->name = 0; scenery_type->image = 0; scenery_type->banner.scenery_tab_id = 0; } else if ((flags & 0xFF) == 2){ rct_scenery_entry* scenery_type = (rct_scenery_entry*)esi; if (scenery_type->banner.price <= 0)return 1; return 0; } else if ((flags & 0xFF) == 3){ int x = ecx, y = edx; if (!((flags >> 8) & 0xFF)) { rct_scenery_entry* scenery_type = (rct_scenery_entry*)ebp; int image_id = scenery_type->image; image_id |= 0x20D00000; gfx_draw_sprite(dpi, image_id, x, y, 0); gfx_draw_sprite(dpi, image_id + 1, x, y, 0); } } return flags; } //rct2: 0x006A8621 int paint_path_entry(int flags, int ebx, int ecx, int edx, rct_drawpixelinfo* dpi, int esi, int ebp) { if ((flags & 0xFF) == 0){ // Object Load rct_path_type* path_type = (rct_path_type*)esi; // String table starts after path entry // Note there are 2 spare bytes after // the path entry. uint8* chunk = (uint8*)(esi + 0xE); // Only 1 string table for paths path_type->string_idx = object_get_localised_text(&chunk, ecx, ebx, 0); int image_id = object_chunk_load_image_directory(&chunk); path_type->image = image_id; path_type->bridge_image = image_id + 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* path_type_entry = (rct_path_type*)object_entry_groups[OBJECT_TYPE_PATHS].chunks[i]; if ((uint32)path_type_entry == 0xFFFFFFFF) continue; if (!(path_type_entry->flags & 4)) { RCT2_GLOBAL(RCT2_ADDRESS_SELECTED_PATH_ID, sint16) = i; break; } RCT2_GLOBAL(RCT2_ADDRESS_SELECTED_PATH_ID, sint16) = i; } } else if ((flags & 0xFF) == 1){ // Object Unload rct_path_type* path_type = (rct_path_type*)esi; path_type->string_idx = 0; path_type->image = 0; path_type->bridge_image = 0; } else if ((flags & 0xFF) == 2){ rct_path_type* path_type = (rct_path_type*)esi; if (path_type->var_0A >= 2) return 1;//actually the carry bit should be set (stc) else return 0; } else if ((flags & 0xFF) == 3){ rct_path_type* path_type = (rct_path_type*)ebp; if (!((flags >> 8) & 0xFF)) { //Draws preview for scenario editor! gfx_draw_sprite(dpi, path_type->image + 71, ecx - 49, edx - 17, ebp); gfx_draw_sprite(dpi, path_type->image + 72, ecx + 4, edx - 17, ebp); } } return flags; } /** * * rct2: 0x006A86E2 */ int paint_path_bit(int flags, int ebx, int ecx, int edx, rct_drawpixelinfo* dpi, int esi, int ebp) { if ((flags & 0xFF) == 0){ // Object Load rct_scenery_entry* scenery_type = (rct_scenery_entry*)esi; uint8* chunk = (uint8*)(esi + 0xE); scenery_type->name = object_get_localised_text(&chunk, ecx, ebx, 0); scenery_type->path_bit.scenery_tab_id = 0xFF; if (*chunk != 0xFF){ uint8 entry_type, entry_index; if (find_object_in_entry_group((rct_object_entry*)chunk, &entry_type, &entry_index)){ scenery_type->path_bit.scenery_tab_id = entry_index; } } chunk += sizeof(rct_object_entry); scenery_type->image = object_chunk_load_image_directory(&chunk); if (RCT2_GLOBAL(0x9ADAF4, uint32) != 0xFFFFFFFF) *RCT2_GLOBAL(0x9ADAF4, uint16*) = 0; } else if ((flags & 0xFF) == 1){ // Object Unload rct_scenery_entry* scenery_type = (rct_scenery_entry*)esi; scenery_type->name = 0; scenery_type->image = 0; scenery_type->path_bit.scenery_tab_id = 0; } else if ((flags & 0xFF) == 2){ rct_scenery_entry* scenery_type = (rct_scenery_entry*)esi; if (scenery_type->path_bit.price <= 0)return 1; return 0; } else if ((flags & 0xFF) == 3){ int x = ecx, y = edx; if (!((flags >> 8) & 0xFF)) { rct_scenery_entry* scenery_type = (rct_scenery_entry*)ebp; int image_id = scenery_type->image; x -= 22; y -= 24; gfx_draw_sprite(dpi, image_id, x, y, 0); } } return flags; } /** * * rct2: 0x006B93AA */ int paint_scenery_set(int flags, int ebx, int ecx, int edx, rct_drawpixelinfo* dpi, int esi, int ebp) { if ((flags & 0xFF) == 0){ // Object Load rct_scenery_set_entry* scenery_set = (rct_scenery_set_entry*)esi; uint8* chunk = (uint8*)(esi + sizeof(rct_scenery_set_entry)); scenery_set->name = object_get_localised_text(&chunk, ecx, ebx, 0); rct_object_entry* entry_objects = NULL; uint8* eax = RCT2_GLOBAL(0x9ADAF4, uint8*); if ((uint32)eax != 0xFFFFFFFF){ *((uint16*)eax) = 0; entry_objects = (rct_object_entry*)(eax + 2); } scenery_set->entry_count = 0; scenery_set->var_107 = 0; for (; *chunk != 0xFF; chunk += sizeof(rct_object_entry)){ scenery_set->var_107++; if (entry_objects != NULL){ memcpy(entry_objects, chunk, sizeof(rct_object_entry)); entry_objects++; (*(eax + 1))++; } uint8 entry_type; uint8 entry_index = 0; if (!find_object_in_entry_group((rct_object_entry*)chunk, &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; } scenery_set->scenery_entries[scenery_set->entry_count++] = scenery_entry; } chunk++; scenery_set->image = object_chunk_load_image_directory(&chunk); } else if ((flags & 0xFF) == 1){ // Object Unload rct_scenery_set_entry* scenery_set = (rct_scenery_set_entry*)esi; scenery_set->name = 0; scenery_set->image = 0; scenery_set->entry_count = 0; scenery_set->var_107 = 0; memset(scenery_set->scenery_entries, 0, 256); } else if ((flags & 0xFF) == 2){ return 0; } else if ((flags & 0xFF) == 3){ int x = ecx, y = edx; rct_scenery_set_entry* scenery_set = (rct_scenery_set_entry*)ebp; if (!((flags >> 8) & 0xFF)) { int image_id = scenery_set->image; image_id += 0x20600001; gfx_draw_sprite(dpi, image_id, x - 15, y - 14, 0); } else{ RCT2_GLOBAL(0x13CE952, uint16) = scenery_set->var_107; gfx_draw_string_left(dpi, 3167, RCT2_ADDRESS(0x13CE952, void), 0, x, y); } } return flags; } //rct2: 0x00666E42 int paint_park_entrance_entry(int flags, int ebx, int ecx, int edx, rct_drawpixelinfo* dpi, int esi, int ebp) { if ((flags & 0xFF) == 0){ // Object Load rct_entrance_type* entrance_type = (rct_entrance_type*)esi; uint8* pStringTable = (uint8*)(esi + sizeof(rct_entrance_type)); entrance_type->string_idx = object_get_localised_text(&pStringTable, ecx, ebx, 0); entrance_type->image_id = object_chunk_load_image_directory(&pStringTable); if (RCT2_GLOBAL(0x9ADAF4, uint32) != 0xFFFFFFFF) *RCT2_GLOBAL(0x9ADAF4, uint16*) = 0; } else if ((flags & 0xFF) == 1){ // Object Unload rct_entrance_type* entrance_type = (rct_entrance_type*)esi; entrance_type->string_idx = 0; entrance_type->image_id = 0; } else if ((flags & 0xFF) == 2){ return 0; } else if ((flags & 0xFF) == 3){ int x = ecx, y = edx; if (!((flags >> 8) & 0xFF)) { rct_entrance_type* entrance_type = (rct_entrance_type*)ebp; dpi = clip_drawpixelinfo(dpi, x - 56, 112, y - 56, 112); if (dpi == NULL) return flags; int image_id = entrance_type->image_id; gfx_draw_sprite(dpi, image_id + 1, 24, 68, ebp); gfx_draw_sprite(dpi, image_id, 56, 84, ebp); gfx_draw_sprite(dpi, image_id + 2, 88, 100, ebp); rct2_free(dpi); } } return flags; } //rct2: 0x006E6E2A int paint_water_entry(int flags, int ebx, int ecx, int edx, rct_drawpixelinfo* dpi, int esi, int ebp) { if ((flags & 0xFF) == 0){ // Object Load rct_water_type* water_type = (rct_water_type*)esi; uint8_t* pStringTable = (uint8_t*)(esi + sizeof(rct_water_type)); water_type->string_idx = object_get_localised_text(&pStringTable, ecx, ebx, 0); int image_id = object_chunk_load_image_directory(&pStringTable); water_type->image_id = image_id; water_type->var_06 = image_id + 1; water_type->var_0A = image_id + 4; if (RCT2_GLOBAL(0x9ADAF4, uint32) != 0xFFFFFFFF) *RCT2_GLOBAL(0x9ADAF4, uint16*) = 0; if (RCT2_GLOBAL(0x9ADAFD, uint8_t) == 0) { load_palette(); gfx_invalidate_screen(); } } else if ((flags & 0xFF) == 1){ // Object Unload rct_water_type* water_type = (rct_water_type*)esi; water_type->string_idx = 0; water_type->image_id = 0; water_type->var_06 = 0; water_type->var_0A = 0; } else if ((flags & 0xFF) == 2){ return 0; } else if ((flags & 0xFF) == 3){ if (!((flags >> 8) & 0xFF)) gfx_draw_string_centred(dpi, 3326, ecx, edx, 0, (void*)esi); } return flags; } //rct2: 0x0066B355 int paint_stex_entry(int flags, int ebx, int ecx, int edx, rct_drawpixelinfo* dpi, int esi, int ebp) { if ((flags & 0xFF) == 0){ // Object Load rct_stex_entry* stex_entry = (rct_stex_entry*)esi; uint8_t* pStringTable = (uint8_t*)(esi + 8); stex_entry->scenario_name = object_get_localised_text(&pStringTable, ecx, ebx, 0); stex_entry->park_name = object_get_localised_text(&pStringTable, ecx, ebx, 1); stex_entry->details = object_get_localised_text(&pStringTable, ecx, ebx, 2); if (RCT2_GLOBAL(0x9ADAF4, int) != -1) RCT2_GLOBAL(0x9ADAF4, uint16_t*)[0] = 0; } else if ((flags & 0xFF) == 1){ // Object Unload rct_stex_entry* stex_entry = (rct_stex_entry*)esi; stex_entry->scenario_name = 0; stex_entry->park_name = 0; stex_entry->details = 0; } else if ((flags & 0xFF) == 2){ return 0; } else if ((flags & 0xFF) == 3){ int x = ecx, y = edx; rct_stex_entry* stex_entry = (rct_stex_entry*)ebp; rct_window* w = (rct_window*)esi; if (!((flags >> 8) & 0xFF)) { gfx_draw_string_centred(dpi, 0xCFE, x, y, 0, NULL); } else{ RCT2_GLOBAL(RCT2_ADDRESS_COMMON_FORMAT_ARGS, short) = stex_entry->details; int width = w->x + w->width - 4 - x; gfx_draw_string_left_wrapped(dpi, (void*)RCT2_ADDRESS_COMMON_FORMAT_ARGS, x, y, width, 3168, 0); } } return flags; } int object_paint(int type, int eax, int ebx, int ecx, int edx, int esi, int edi, int ebp) { switch (type) { case OBJECT_TYPE_RIDE: return paint_ride_entry(eax, ebx, ecx, edx, (rct_drawpixelinfo*)edi, esi, ebp); case OBJECT_TYPE_SMALL_SCENERY: return paint_small_scenery(eax, ebx, ecx, edx, (rct_drawpixelinfo*)edi, esi, ebp); case OBJECT_TYPE_LARGE_SCENERY: return paint_large_scenery(eax, ebx, ecx, edx, (rct_drawpixelinfo*)edi, esi, ebp); case OBJECT_TYPE_WALLS: return paint_wall(eax, ebx, ecx, edx, (rct_drawpixelinfo*)edi, esi, ebp); case OBJECT_TYPE_BANNERS: return paint_banner(eax, ebx, ecx, edx, (rct_drawpixelinfo*)edi, esi, ebp); case OBJECT_TYPE_PATHS: return paint_path_entry(eax, ebx, ecx, edx, (rct_drawpixelinfo*)edi, esi, ebp); case OBJECT_TYPE_PATH_BITS: return paint_path_bit(eax, ebx, ecx, edx, (rct_drawpixelinfo*)edi, esi, ebp); case OBJECT_TYPE_SCENERY_SETS: return paint_scenery_set(eax, ebx, ecx, edx, (rct_drawpixelinfo*)edi, esi, ebp); case OBJECT_TYPE_PARK_ENTRANCE: return paint_park_entrance_entry(eax, ebx, ecx, edx, (rct_drawpixelinfo*)edi, esi, ebp); case OBJECT_TYPE_WATER: return paint_water_entry(eax, ebx, ecx, edx, (rct_drawpixelinfo*)edi, esi, ebp); case OBJECT_TYPE_SCENARIO_TEXT: return paint_stex_entry(eax, ebx, ecx, edx, (rct_drawpixelinfo*)edi, esi, ebp); default: assert(false); return 0; } } /** * * rct2: 0x006A9428 */ int object_get_scenario_text(rct_object_entry *entry) { rct_object_entry *installedObject = RCT2_GLOBAL(RCT2_ADDRESS_INSTALLED_OBJECT_LIST, rct_object_entry*); 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 = malloc(0x600000); chunkSize = sawyercoding_read_chunk(rw, chunk); chunk = realloc(chunk, chunkSize); } else { chunk = 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_paint(openedEntry.flags & 0x0F, 2, 0, 0, 0, (int)chunk, 0, 0)) { // 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_paint(openedEntry.flags & 0x0F, 0, 0, 0, 0, (int)chunk, 0, 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; }