diff --git a/src/openrct2/drawing/drawing.h b/src/openrct2/drawing/drawing.h index 30318873c7..e7bfa39022 100644 --- a/src/openrct2/drawing/drawing.h +++ b/src/openrct2/drawing/drawing.h @@ -270,7 +270,6 @@ extern sint32 gPickupPeepX; extern sint32 gPickupPeepY; extern rct_g1_element *g1Elements; -extern rct_gx g2; extern rct_drawpixelinfo gScreenDPI; extern rct_drawpixelinfo gWindowDPI; diff --git a/src/openrct2/drawing/sprite.cpp b/src/openrct2/drawing/sprite.cpp index f9d6c4cc9e..1a27f690dc 100644 --- a/src/openrct2/drawing/sprite.cpp +++ b/src/openrct2/drawing/sprite.cpp @@ -14,67 +14,79 @@ *****************************************************************************/ #pragma endregion +#include #include "../common.h" -#include "../config.h" +#include "../core/FileStream.hpp" +#include "../core/Memory.hpp" +#include "../core/Util.hpp" #include "../OpenRCT2.h" -#include "../rct2/addresses.h" #include "../sprites.h" -#include "../util/util.h" -#include "drawing.h" -void *_g1Buffer = NULL; - -rct_gx g2; -rct_gx csg; - -#ifdef NO_RCT2 - rct_g1_element *g1Elements = NULL; -#else - rct_g1_element *g1Elements = RCT2_ADDRESS(RCT2_ADDRESS_G1_ELEMENTS, rct_g1_element); -#endif - -static const uint32 fadeSprites[] = { - SPR_NONE, - SPR_FADE_1, - SPR_FADE_2, - SPR_FADE_3, - SPR_FADE_4, - SPR_FADE_5, - SPR_FADE_6, - SPR_FADE_7, -}; - -static void read_and_convert_gxdat(SDL_RWops *file, size_t count, rct_g1_element *elements) +extern "C" { - rct_g1_element_32bit *g1Elements32 = calloc(count, sizeof(rct_g1_element_32bit)); - SDL_RWread(file, g1Elements32, count * sizeof(rct_g1_element_32bit), 1); - for (size_t i = 0; i < count; i++) { - /* Double cast to silence compiler warning about casting to - * pointer from integer of mismatched length. - */ - elements[i].offset = (uint8*)(uintptr_t)g1Elements32[i].offset; - elements[i].width = g1Elements32[i].width; - elements[i].height = g1Elements32[i].height; - elements[i].x_offset = g1Elements32[i].x_offset; - elements[i].y_offset = g1Elements32[i].y_offset; - elements[i].flags = g1Elements32[i].flags; - elements[i].zoomed_offset = g1Elements32[i].zoomed_offset; - } - free(g1Elements32); + #include "../config.h" + #include "../rct2/addresses.h" + #include "../util/util.h" + #include "drawing.h" } -/** - * - * rct2: 0x00678998 - */ -bool gfx_load_g1() +extern "C" { - log_verbose("loading g1 graphics"); + static void * _g1Buffer = nullptr; + static rct_gx _g2; + static rct_gx _csg; + + #ifdef NO_RCT2 + rct_g1_element * g1Elements = nullptr; + #else + rct_g1_element * g1Elements = RCT2_ADDRESS(RCT2_ADDRESS_G1_ELEMENTS, rct_g1_element); + #endif + + static const uint32 FadeSprites[] = + { + (uint32)SPR_NONE, + SPR_FADE_1, + SPR_FADE_2, + SPR_FADE_3, + SPR_FADE_4, + SPR_FADE_5, + SPR_FADE_6, + SPR_FADE_7, + }; + + static void read_and_convert_gxdat(IStream * stream, size_t count, rct_g1_element *elements) + { + auto g1Elements32 = stream->ReadArray(count); + for (size_t i = 0; i < count; i++) + { + const auto src = &g1Elements32[i]; + + /* Double cast to silence compiler warning about casting to + * pointer from integer of mismatched length. + */ + elements[i].offset = (uint8*)(uintptr_t)src->offset; + elements[i].width = src->width; + elements[i].height = src->height; + elements[i].x_offset = src->x_offset; + elements[i].y_offset = src->y_offset; + elements[i].flags = src->flags; + elements[i].zoomed_offset = src->zoomed_offset; + } + Memory::Free(g1Elements32); + } + + /** + * + * rct2: 0x00678998 + */ + bool gfx_load_g1() + { + log_verbose("gfx_load_g1()"); + try + { + auto fs = FileStream(get_file_path(PATH_ID_G1), FILE_MODE_OPEN); + rct_g1_header header = fs.ReadValue(); - SDL_RWops *file = SDL_RWFromFile(get_file_path(PATH_ID_G1), "rb"); - if (file != NULL) { - rct_g1_header header; - if (SDL_RWread(file, &header, 8, 1) == 1) { /* number of elements is stored in g1.dat, but because the entry * headers are static, this can't be variable until made into a * dynamic array. @@ -83,545 +95,541 @@ bool gfx_load_g1() // Read element headers #ifdef NO_RCT2 - g1Elements = calloc(324206, sizeof(rct_g1_element)); + g1Elements = Memory::AllocateArray(324206); #endif - - read_and_convert_gxdat(file, header.num_entries, g1Elements); + read_and_convert_gxdat(&fs, header.num_entries, g1Elements); // Read element data - _g1Buffer = malloc(header.total_size); - SDL_RWread(file, _g1Buffer, header.total_size, 1); - - SDL_RWclose(file); + _g1Buffer = fs.ReadArray(header.total_size); // Fix entry data offsets for (uint32 i = 0; i < header.num_entries; i++) + { g1Elements[i].offset += (uintptr_t)_g1Buffer; - + } return true; } - SDL_RWclose(file); + catch (const Exception &) + { + log_fatal("Unable to load g1 graphics"); + if (!gOpenRCT2Headless) + { + platform_show_messagebox("Unable to load g1.dat. Your RollerCoaster Tycoon 2 path may be incorrectly set."); + } + return false; + } } - log_fatal("Unable to load g1 graphics"); - if (!gOpenRCT2Headless) { - platform_show_messagebox("Unable to load g1.dat. Your RollerCoaster Tycoon 2 path may be incorrectly set."); + void gfx_unload_g1() + { + SafeFree(_g1Buffer); + #ifdef NO_RCT2 + SafeFree(g1Elements); + #endif } - return false; -} -void gfx_unload_g1() -{ - SafeFree(_g1Buffer); -#ifdef NO_RCT2 - SafeFree(g1Elements); -#endif -} + void gfx_unload_g2() + { + SafeFree(_g2.elements); + SafeFree(_g2.data); + } -void gfx_unload_g2() -{ - SafeFree(g2.elements); -} + void gfx_unload_csg() + { + SafeFree(_csg.elements); + SafeFree(_csg.data); + } -void gfx_unload_csg() -{ - SafeFree(csg.elements); -} + bool gfx_load_g2() + { + log_verbose("gfx_load_g2()"); -bool gfx_load_g2() -{ - log_verbose("loading g2 graphics"); + char path[MAX_PATH]; - char path[MAX_PATH]; + platform_get_openrct_data_path(path, sizeof(path)); + safe_strcat_path(path, "g2.dat", MAX_PATH); + try + { + auto fs = FileStream(path, FILE_MODE_OPEN); + _g2.header = fs.ReadValue(); - platform_get_openrct_data_path(path, sizeof(path)); - safe_strcat_path(path, "g2.dat", MAX_PATH); - SDL_RWops *file = SDL_RWFromFile(path, "rb"); - if (file != NULL) { - if (SDL_RWread(file, &g2.header, 8, 1) == 1) { // Read element headers - g2.elements = malloc(g2.header.num_entries * sizeof(rct_g1_element)); - - read_and_convert_gxdat(file, g2.header.num_entries, g2.elements); + _g2.elements = Memory::AllocateArray(_g2.header.num_entries); + read_and_convert_gxdat(&fs, _g2.header.num_entries, _g2.elements); // Read element data - g2.data = malloc(g2.header.total_size); - SDL_RWread(file, g2.data, g2.header.total_size, 1); - - SDL_RWclose(file); + _g2.data = fs.ReadArray(_g2.header.total_size); // Fix entry data offsets - for (uint32 i = 0; i < g2.header.num_entries; i++) - g2.elements[i].offset += (uintptr_t)g2.data; - + for (uint32 i = 0; i < _g2.header.num_entries; i++) + { + _g2.elements[i].offset += (uintptr_t)_g2.data; + } return true; } - SDL_RWclose(file); - } - - log_fatal("Unable to load g2 graphics"); - if (!gOpenRCT2Headless) { - platform_show_messagebox("Unable to load g2.dat"); - } - return false; -} - -bool gfx_load_csg() -{ - if (str_is_null_or_empty(gConfigGeneral.rct1_path)) { - return false; - } - - bool success = false; - log_verbose("loading csg graphics"); - - char pathHeader[MAX_PATH]; - safe_strcpy(pathHeader, gConfigGeneral.rct1_path, sizeof(pathHeader)); - safe_strcat_path(pathHeader, "Data", sizeof(pathHeader)); - safe_strcat_path(pathHeader, "csg1i.dat", sizeof(pathHeader)); - - char pathData[MAX_PATH]; - safe_strcpy(pathData, gConfigGeneral.rct1_path, sizeof(pathData)); - safe_strcat_path(pathData, "Data", sizeof(pathData)); - safe_strcat_path(pathData, "csg1.1", sizeof(pathData)); - - SDL_RWops * fileHeader = SDL_RWFromFile(pathHeader, "rb"); - SDL_RWops * fileData = SDL_RWFromFile(pathData, "rb"); - if (fileHeader != NULL && fileData != NULL) { - SDL_RWseek(fileHeader, 0, RW_SEEK_END); - SDL_RWseek(fileData, 0, RW_SEEK_END); - size_t fileHeaderSize = SDL_RWtell(fileHeader); - size_t fileDataSize = SDL_RWtell(fileData); - SDL_RWseek(fileHeader, 0, RW_SEEK_SET); - SDL_RWseek(fileData, 0, RW_SEEK_SET); - - csg.header.num_entries = (uint32)(fileHeaderSize / sizeof(rct_g1_element_32bit)); - csg.header.total_size = (uint32)fileDataSize; - - // Read element headers - csg.elements = malloc(csg.header.num_entries * sizeof(rct_g1_element)); - read_and_convert_gxdat(fileHeader, csg.header.num_entries, csg.elements); - - // Read element data - csg.data = malloc(csg.header.total_size); - SDL_RWread(fileData, csg.data, csg.header.total_size, 1); - - // Fix entry data offsets - for (uint32 i = 0; i < csg.header.num_entries; i++) { - csg.elements[i].offset += (uintptr_t)csg.data; - // RCT1 used zoomed offsets that counted from the beginning of the file, rather than from the current sprite. - csg.elements[i].zoomed_offset = i - (SPR_CSG_BEGIN + csg.elements[i].zoomed_offset); - } - - success = true; - } - - if (fileHeader != NULL) { - SDL_RWclose(fileHeader); - } - if (fileData != NULL) { - SDL_RWclose(fileData); - } - - if (success) { - return true; - } else { - log_error("Unable to load csg graphics"); - return false; - } -} - -/** - * This function looks like it initialises the 0x009E3CE4 array which references sprites used for background / palette mixing or - * something. Further investigation is needed. - */ -void sub_68371D() -{ - for (sint32 i = 0; i < countof(fadeSprites); i++) { - const uint32 spriteId = fadeSprites[i]; - if (spriteId == SPR_NONE) { - unk_9E3CE4[i] = NULL; - } else { - unk_9E3CE4[i] = g1Elements[fadeSprites[i]].offset; - } - } -} - -/** - * Copies a sprite onto the buffer. There is no compression used on the sprite - * image. - * rct2: 0x0067A690 - */ -static void FASTCALL gfx_bmp_sprite_to_buffer(uint8* palette_pointer, uint8* unknown_pointer, uint8* source_pointer, uint8* dest_pointer, rct_g1_element* source_image, rct_drawpixelinfo *dest_dpi, sint32 height, sint32 width, sint32 image_type){ - uint16 zoom_level = dest_dpi->zoom_level; - uint8 zoom_amount = 1 << zoom_level; - uint32 dest_line_width = (dest_dpi->width / zoom_amount) + dest_dpi->pitch; - uint32 source_line_width = source_image->width * zoom_amount; - - // Image uses the palette pointer to remap the colours of the image - if (image_type & IMAGE_TYPE_REMAP){ - assert(palette_pointer != NULL); - - //image with remaps - for (; height > 0; height -= zoom_amount){ - uint8* next_source_pointer = source_pointer + source_line_width; - uint8* next_dest_pointer = dest_pointer + dest_line_width; - for (sint32 no_pixels = width; no_pixels > 0; no_pixels -= zoom_amount, source_pointer += zoom_amount, dest_pointer++){ - uint8 pixel = *source_pointer; - pixel = palette_pointer[pixel]; - if (pixel){ - *dest_pointer = pixel; - } + catch (const Exception &) + { + log_fatal("Unable to load g2 graphics"); + if (!gOpenRCT2Headless) + { + platform_show_messagebox("Unable to load g2.dat"); } - - source_pointer = next_source_pointer; - dest_pointer = next_dest_pointer; } - return; + return false; } - //Image is Transparent. It only uses source pointer for - //telling if it needs to be drawn not for colour. Colour provided - //by the palette pointer. - if (image_type & IMAGE_TYPE_TRANSPARENT){//Not tested - assert(palette_pointer != NULL); - for (; height > 0; height -= zoom_amount){ - uint8* next_source_pointer = source_pointer + source_line_width; - uint8* next_dest_pointer = dest_pointer + dest_line_width; + bool gfx_load_csg() + { + log_verbose("gfx_load_csg()"); - for (sint32 no_pixels = width; no_pixels > 0; no_pixels -= zoom_amount, source_pointer += zoom_amount, dest_pointer++){ - uint8 pixel = *source_pointer; - if (pixel){ - pixel = *dest_pointer; + if (str_is_null_or_empty(gConfigGeneral.rct1_path)) + { + log_verbose(" unable to load CSG, RCT1 path not set"); + return false; + } + + char pathHeader[MAX_PATH]; + safe_strcpy(pathHeader, gConfigGeneral.rct1_path, sizeof(pathHeader)); + safe_strcat_path(pathHeader, "Data", sizeof(pathHeader)); + safe_strcat_path(pathHeader, "csg1i.dat", sizeof(pathHeader)); + + char pathData[MAX_PATH]; + safe_strcpy(pathData, gConfigGeneral.rct1_path, sizeof(pathData)); + safe_strcat_path(pathData, "Data", sizeof(pathData)); + safe_strcat_path(pathData, "csg1.1", sizeof(pathData)); + + try + { + auto fileHeader = FileStream(pathHeader, FILE_MODE_OPEN); + auto fileData = FileStream(pathData, FILE_MODE_OPEN); + size_t fileHeaderSize = fileHeader.GetLength(); + size_t fileDataSize = fileData.GetLength(); + + _csg.header.num_entries = (uint32)(fileHeaderSize / sizeof(rct_g1_element_32bit)); + _csg.header.total_size = (uint32)fileDataSize; + + // Read element headers + _csg.elements = Memory::AllocateArray(_csg.header.num_entries); + read_and_convert_gxdat(&fileHeader, _csg.header.num_entries, _csg.elements); + + // Read element data + _csg.data = fileData.ReadArray(_csg.header.total_size); + + // Fix entry data offsets + for (uint32 i = 0; i < _csg.header.num_entries; i++) + { + _csg.elements[i].offset += (uintptr_t)_csg.data; + // RCT1 used zoomed offsets that counted from the beginning of the file, rather than from the current sprite. + _csg.elements[i].zoomed_offset = i - (SPR_CSG_BEGIN + _csg.elements[i].zoomed_offset); + } + return true; + } + catch (const Exception &) + { + log_error("Unable to load csg graphics"); + return false; + } + } + + /** + * This function looks like it initialises the 0x009E3CE4 array which references sprites used for background / palette mixing or + * something. Further investigation is needed. + */ + void sub_68371D() + { + for (size_t i = 0; i < Util::CountOf(FadeSprites); i++) + { + const uint32 spriteId = FadeSprites[i]; + if (spriteId == (uint32)SPR_NONE) + { + unk_9E3CE4[i] = nullptr; + } + else + { + unk_9E3CE4[i] = g1Elements[FadeSprites[i]].offset; + } + } + } + + /** + * Copies a sprite onto the buffer. There is no compression used on the sprite + * image. + * rct2: 0x0067A690 + */ + static void FASTCALL gfx_bmp_sprite_to_buffer(uint8* palette_pointer, uint8* unknown_pointer, uint8* source_pointer, uint8* dest_pointer, rct_g1_element* source_image, rct_drawpixelinfo *dest_dpi, sint32 height, sint32 width, sint32 image_type) + { + uint16 zoom_level = dest_dpi->zoom_level; + uint8 zoom_amount = 1 << zoom_level; + uint32 dest_line_width = (dest_dpi->width / zoom_amount) + dest_dpi->pitch; + uint32 source_line_width = source_image->width * zoom_amount; + + // Image uses the palette pointer to remap the colours of the image + if (image_type & IMAGE_TYPE_REMAP){ + assert(palette_pointer != nullptr); + + //image with remaps + for (; height > 0; height -= zoom_amount){ + uint8* next_source_pointer = source_pointer + source_line_width; + uint8* next_dest_pointer = dest_pointer + dest_line_width; + for (sint32 no_pixels = width; no_pixels > 0; no_pixels -= zoom_amount, source_pointer += zoom_amount, dest_pointer++){ + uint8 pixel = *source_pointer; pixel = palette_pointer[pixel]; - *dest_pointer = pixel; + if (pixel){ + *dest_pointer = pixel; + } } + + source_pointer = next_source_pointer; + dest_pointer = next_dest_pointer; } - - source_pointer = next_source_pointer; - dest_pointer = next_dest_pointer; + return; } - return; - } - //Basic bitmap no fancy stuff - if (!(source_image->flags & G1_FLAG_BMP)){//Not tested + //Image is Transparent. It only uses source pointer for + //telling if it needs to be drawn not for colour. Colour provided + //by the palette pointer. + if (image_type & IMAGE_TYPE_TRANSPARENT){//Not tested + assert(palette_pointer != nullptr); + for (; height > 0; height -= zoom_amount){ + uint8* next_source_pointer = source_pointer + source_line_width; + uint8* next_dest_pointer = dest_pointer + dest_line_width; + + for (sint32 no_pixels = width; no_pixels > 0; no_pixels -= zoom_amount, source_pointer += zoom_amount, dest_pointer++){ + uint8 pixel = *source_pointer; + if (pixel){ + pixel = *dest_pointer; + pixel = palette_pointer[pixel]; + *dest_pointer = pixel; + } + } + + source_pointer = next_source_pointer; + dest_pointer = next_dest_pointer; + } + return; + } + + //Basic bitmap no fancy stuff + if (!(source_image->flags & G1_FLAG_BMP)){//Not tested + for (; height > 0; height -= zoom_amount){ + uint8* next_source_pointer = source_pointer + source_line_width; + uint8* next_dest_pointer = dest_pointer + dest_line_width; + + for (sint32 no_pixels = width; no_pixels > 0; no_pixels -= zoom_amount, dest_pointer++, source_pointer += zoom_amount){ + *dest_pointer = *source_pointer; + } + + dest_pointer = next_dest_pointer; + source_pointer = next_source_pointer; + } + return; + } + + //Basic bitmap with no draw pixels for (; height > 0; height -= zoom_amount){ uint8* next_source_pointer = source_pointer + source_line_width; uint8* next_dest_pointer = dest_pointer + dest_line_width; for (sint32 no_pixels = width; no_pixels > 0; no_pixels -= zoom_amount, dest_pointer++, source_pointer += zoom_amount){ - *dest_pointer = *source_pointer; + uint8 pixel = *source_pointer; + if (pixel){ + *dest_pointer = pixel; + } } - dest_pointer = next_dest_pointer; source_pointer = next_source_pointer; } return; } - //Basic bitmap with no draw pixels - for (; height > 0; height -= zoom_amount){ - uint8* next_source_pointer = source_pointer + source_line_width; - uint8* next_dest_pointer = dest_pointer + dest_line_width; + uint8* FASTCALL gfx_draw_sprite_get_palette(sint32 image_id, uint32 tertiary_colour) { + sint32 image_type = (image_id & 0xE0000000); + if (image_type == 0) + return nullptr; - for (sint32 no_pixels = width; no_pixels > 0; no_pixels -= zoom_amount, dest_pointer++, source_pointer += zoom_amount){ - uint8 pixel = *source_pointer; - if (pixel){ - *dest_pointer = pixel; - } - } - dest_pointer = next_dest_pointer; - source_pointer = next_source_pointer; - } - return; -} - -uint8* FASTCALL gfx_draw_sprite_get_palette(sint32 image_id, uint32 tertiary_colour) { - sint32 image_type = (image_id & 0xE0000000); - if (image_type == 0) - return NULL; - - if (!(image_type & IMAGE_TYPE_REMAP_2_PLUS)) { - uint8 palette_ref = (image_id >> 19) & 0xFF; - if (!(image_type & IMAGE_TYPE_TRANSPARENT)) { - palette_ref &= 0x7F; - } - - uint16 palette_offset = palette_to_g1_offset[palette_ref]; - return g1Elements[palette_offset].offset; - } - else { - uint8* palette_pointer = gPeepPalette; - - uint32 primary_offset = palette_to_g1_offset[(image_id >> 19) & 0x1F]; - uint32 secondary_offset = palette_to_g1_offset[(image_id >> 24) & 0x1F]; - - if (!(image_type & IMAGE_TYPE_REMAP)) { - palette_pointer = gOtherPalette; -#if defined(DEBUG_LEVEL_2) && DEBUG_LEVEL_2 - assert(tertiary_colour < PALETTE_TO_G1_OFFSET_COUNT); -#endif // DEBUG_LEVEL_2 - uint32 tertiary_offset = palette_to_g1_offset[tertiary_colour]; - rct_g1_element* tertiary_palette = &g1Elements[tertiary_offset]; - memcpy(palette_pointer + 0x2E, &tertiary_palette->offset[0xF3], 12); - } - rct_g1_element* primary_palette = &g1Elements[primary_offset]; - rct_g1_element* secondary_palette = &g1Elements[secondary_offset]; - - memcpy(palette_pointer + 0xF3, &primary_palette->offset[0xF3], 12); - memcpy(palette_pointer + 0xCA, &secondary_palette->offset[0xF3], 12); - - return palette_pointer; - } -} - -/** - * - * rct2: 0x0067A28E - * image_id (ebx) - * image_id as below - * 0b_111X_XXXX_XXXX_XXXX_XXXX_XXXX_XXXX_XXXX image_type - * 0b_XXX1_11XX_XXXX_XXXX_XXXX_XXXX_XXXX_XXXX image_sub_type (unknown pointer) - * 0b_XXX1_1111_XXXX_XXXX_XXXX_XXXX_XXXX_XXXX secondary_colour - * 0b_XXXX_XXXX_1111_1XXX_XXXX_XXXX_XXXX_XXXX primary_colour - * 0b_XXXX_X111_1111_1XXX_XXXX_XXXX_XXXX_XXXX palette_ref - * 0b_XXXX_XXXX_XXXX_X111_1111_1111_1111_1111 image_id (offset to g1) - * x (cx) - * y (dx) - * dpi (esi) - * tertiary_colour (ebp) - */ -void FASTCALL gfx_draw_sprite_software(rct_drawpixelinfo *dpi, sint32 image_id, sint32 x, sint32 y, uint32 tertiary_colour) -{ - uint8* palette_pointer = gfx_draw_sprite_get_palette(image_id, tertiary_colour); - if (image_id & IMAGE_TYPE_REMAP_2_PLUS) { - image_id |= IMAGE_TYPE_REMAP; - } - - - gfx_draw_sprite_palette_set_software(dpi, image_id, x, y, palette_pointer, NULL); -} - -/* -* rct: 0x0067A46E -* image_id (ebx) and also (0x00EDF81C) -* palette_pointer (0x9ABDA4) -* unknown_pointer (0x9E3CDC) -* dpi (edi) -* x (cx) -* y (dx) -*/ -void FASTCALL gfx_draw_sprite_palette_set_software(rct_drawpixelinfo *dpi, sint32 image_id, sint32 x, sint32 y, uint8* palette_pointer, uint8* unknown_pointer) -{ - sint32 image_element = image_id & 0x7FFFF; - sint32 image_type = image_id & 0xE0000000; - - rct_g1_element *g1_source = gfx_get_g1_element(image_element); - - if (dpi->zoom_level != 0 && (g1_source->flags & G1_FLAG_HAS_ZOOM_SPRITE)) { - rct_drawpixelinfo zoomed_dpi = { - .bits = dpi->bits, - .x = dpi->x >> 1, - .y = dpi->y >> 1, - .height = dpi->height>>1, - .width = dpi->width>>1, - .pitch = dpi->pitch, - .zoom_level = dpi->zoom_level - 1 - }; - gfx_draw_sprite_palette_set_software(&zoomed_dpi, image_type | (image_element - g1_source->zoomed_offset), x >> 1, y >> 1, palette_pointer, unknown_pointer); - return; - } - - if (dpi->zoom_level != 0 && (g1_source->flags & G1_FLAG_NO_ZOOM_DRAW)) { - return; - } - - //Its used super often so we will define it to a separate variable. - sint32 zoom_level = dpi->zoom_level; - sint32 zoom_mask = 0xFFFFFFFF << zoom_level; - - if (zoom_level && g1_source->flags & G1_FLAG_RLE_COMPRESSION){ - x -= ~zoom_mask; - y -= ~zoom_mask; - } - - //This will be the height of the drawn image - sint32 height = g1_source->height; - //This is the start y coordinate on the destination - sint16 dest_start_y = y + g1_source->y_offset; - - // For whatever reason the RLE version does not use - // the zoom mask on the y coordinate but does on x. - if (g1_source->flags & G1_FLAG_RLE_COMPRESSION){ - dest_start_y -= dpi->y; - } - else{ - dest_start_y = (dest_start_y&zoom_mask) - dpi->y; - } - //This is the start y coordinate on the source - sint32 source_start_y = 0; - - if (dest_start_y < 0){ - //If the destination y is negative reduce the height of the - //image as we will cut off the bottom - height += dest_start_y; - //If the image is no longer visible nothing to draw - if (height <= 0){ - return; - } - //The source image will start a further up the image - source_start_y -= dest_start_y; - //The destination start is now reset to 0 - dest_start_y = 0; - } - else{ - if (g1_source->flags & G1_FLAG_RLE_COMPRESSION && zoom_level){ - source_start_y -= dest_start_y & ~zoom_mask; - height += dest_start_y & ~zoom_mask; - } - } - - sint32 dest_end_y = dest_start_y + height; - - if (dest_end_y > dpi->height){ - //If the destination y is outside of the drawing - //image reduce the height of the image - height -= dest_end_y - dpi->height; - } - //If the image no longer has anything to draw - if (height <= 0)return; - - dest_start_y >>= zoom_level; - - //This will be the width of the drawn image - sint32 width = g1_source->width; - //This is the source start x coordinate - sint32 source_start_x = 0; - //This is the destination start x coordinate - sint16 dest_start_x = ((x + g1_source->x_offset + ~zoom_mask)&zoom_mask) - dpi->x; - - if (dest_start_x < 0){ - //If the destination is negative reduce the width - //image will cut off the side - width += dest_start_x; - //If there is no image to draw - if (width <= 0){ - return; - } - //The source start will also need to cut off the side - source_start_x -= dest_start_x; - //Reset the destination to 0 - dest_start_x = 0; - } - else{ - if (g1_source->flags & G1_FLAG_RLE_COMPRESSION && zoom_level){ - source_start_x -= dest_start_x & ~zoom_mask; - } - } - - sint32 dest_end_x = dest_start_x + width; - - if (dest_end_x > dpi->width){ - //If the destination x is outside of the drawing area - //reduce the image width. - width -= dest_end_x - dpi->width; - //If there is no image to draw. - if (width <= 0)return; - } - - dest_start_x >>= zoom_level; - - uint8* dest_pointer = (uint8*)dpi->bits; - //Move the pointer to the start point of the destination - dest_pointer += ((dpi->width >> zoom_level) + dpi->pitch) * dest_start_y + dest_start_x; - - if (g1_source->flags & G1_FLAG_RLE_COMPRESSION){ - //We have to use a different method to move the source pointer for - //rle encoded sprites so that will be handled within this function - gfx_rle_sprite_to_buffer(g1_source->offset, dest_pointer, palette_pointer, dpi, image_type, source_start_y, height, source_start_x, width); - return; - } - uint8* source_pointer = g1_source->offset; - //Move the pointer to the start point of the source - source_pointer += g1_source->width*source_start_y + source_start_x; - - if (!(g1_source->flags & G1_FLAG_1)) { - gfx_bmp_sprite_to_buffer(palette_pointer, unknown_pointer, source_pointer, dest_pointer, g1_source, dpi, height, width, image_type); - } - return; -} - -/** - * Draws the given colour image masked out by the given mask image. This can currently only cope with bitmap formatted mask and - * colour images. Presumably the original game never used RLE images for masking. Colour 0 represents transparent. - * - * rct2: 0x00681DE2 - */ -void FASTCALL gfx_draw_sprite_raw_masked_software(rct_drawpixelinfo *dpi, sint32 x, sint32 y, sint32 maskImage, sint32 colourImage) -{ - sint32 left, top, right, bottom, width, height; - rct_g1_element *imgMask = &g1Elements[maskImage & 0x7FFFF]; - rct_g1_element *imgColour = &g1Elements[colourImage & 0x7FFFF]; - - assert(imgMask->flags & G1_FLAG_BMP); - assert(imgColour->flags & G1_FLAG_BMP); - - if (dpi->zoom_level != 0) { - // TODO implement other zoom levels (probably not used though) - assert(false); - return; - } - - width = min(imgMask->width, imgColour->width); - height = min(imgMask->height, imgColour->height); - - x += imgMask->x_offset; - y += imgMask->y_offset; - - left = max(dpi->x, x); - top = max(dpi->y, y); - right = min(dpi->x + dpi->width, x + width); - bottom = min(dpi->y + dpi->height, y + height); - - width = right - left; - height = bottom - top; - if (width < 0 || height < 0) - return; - - sint32 skipX = left - x; - sint32 skipY = top - y; - - uint8 *maskSrc = imgMask->offset + (skipY * imgMask->width) + skipX; - uint8 *colourSrc = imgColour->offset + (skipY * imgColour->width) + skipX; - uint8 *dst = dpi->bits + (left - dpi->x) + ((top - dpi->y) * (dpi->width + dpi->pitch)); - - sint32 maskWrap = imgMask->width - width; - sint32 colourWrap = imgColour->width - width; - sint32 dstWrap = ((dpi->width + dpi->pitch) - width); - for (sint32 yy = top; yy < bottom; yy++) { - for (sint32 xx = left; xx < right; xx++) { - uint8 colour = (*colourSrc) & (*maskSrc); - if (colour != 0) { - *dst = colour; + if (!(image_type & IMAGE_TYPE_REMAP_2_PLUS)) { + uint8 palette_ref = (image_id >> 19) & 0xFF; + if (!(image_type & IMAGE_TYPE_TRANSPARENT)) { + palette_ref &= 0x7F; } - maskSrc++; - colourSrc++; - dst++; + uint16 palette_offset = palette_to_g1_offset[palette_ref]; + return g1Elements[palette_offset].offset; } - maskSrc += maskWrap; - colourSrc += colourWrap; - dst += dstWrap; - } -} + else { + uint8* palette_pointer = gPeepPalette; -rct_g1_element *gfx_get_g1_element(sint32 image_id) { - if (image_id < SPR_G2_BEGIN) { - return &g1Elements[image_id]; + uint32 primary_offset = palette_to_g1_offset[(image_id >> 19) & 0x1F]; + uint32 secondary_offset = palette_to_g1_offset[(image_id >> 24) & 0x1F]; + + if (!(image_type & IMAGE_TYPE_REMAP)) { + palette_pointer = gOtherPalette; + #if defined(DEBUG_LEVEL_2) && DEBUG_LEVEL_2 + assert(tertiary_colour < PALETTE_TO_G1_OFFSET_COUNT); + #endif // DEBUG_LEVEL_2 + uint32 tertiary_offset = palette_to_g1_offset[tertiary_colour]; + rct_g1_element* tertiary_palette = &g1Elements[tertiary_offset]; + memcpy(palette_pointer + 0x2E, &tertiary_palette->offset[0xF3], 12); + } + rct_g1_element* primary_palette = &g1Elements[primary_offset]; + rct_g1_element* secondary_palette = &g1Elements[secondary_offset]; + + memcpy(palette_pointer + 0xF3, &primary_palette->offset[0xF3], 12); + memcpy(palette_pointer + 0xCA, &secondary_palette->offset[0xF3], 12); + + return palette_pointer; + } } - if (image_id < SPR_CSG_BEGIN) { - return &g2.elements[image_id - SPR_G2_BEGIN]; + + /** + * + * rct2: 0x0067A28E + * image_id (ebx) + * image_id as below + * 0b_111X_XXXX_XXXX_XXXX_XXXX_XXXX_XXXX_XXXX image_type + * 0b_XXX1_11XX_XXXX_XXXX_XXXX_XXXX_XXXX_XXXX image_sub_type (unknown pointer) + * 0b_XXX1_1111_XXXX_XXXX_XXXX_XXXX_XXXX_XXXX secondary_colour + * 0b_XXXX_XXXX_1111_1XXX_XXXX_XXXX_XXXX_XXXX primary_colour + * 0b_XXXX_X111_1111_1XXX_XXXX_XXXX_XXXX_XXXX palette_ref + * 0b_XXXX_XXXX_XXXX_X111_1111_1111_1111_1111 image_id (offset to g1) + * x (cx) + * y (dx) + * dpi (esi) + * tertiary_colour (ebp) + */ + void FASTCALL gfx_draw_sprite_software(rct_drawpixelinfo *dpi, sint32 image_id, sint32 x, sint32 y, uint32 tertiary_colour) + { + uint8* palette_pointer = gfx_draw_sprite_get_palette(image_id, tertiary_colour); + if (image_id & IMAGE_TYPE_REMAP_2_PLUS) { + image_id |= IMAGE_TYPE_REMAP; + } + + + gfx_draw_sprite_palette_set_software(dpi, image_id, x, y, palette_pointer, nullptr); } - return &csg.elements[image_id - SPR_CSG_BEGIN]; + + /* + * rct: 0x0067A46E + * image_id (ebx) and also (0x00EDF81C) + * palette_pointer (0x9ABDA4) + * unknown_pointer (0x9E3CDC) + * dpi (edi) + * x (cx) + * y (dx) + */ + void FASTCALL gfx_draw_sprite_palette_set_software(rct_drawpixelinfo *dpi, sint32 image_id, sint32 x, sint32 y, uint8* palette_pointer, uint8* unknown_pointer) + { + sint32 image_element = image_id & 0x7FFFF; + sint32 image_type = image_id & 0xE0000000; + + rct_g1_element *g1_source = gfx_get_g1_element(image_element); + + if (dpi->zoom_level != 0 && (g1_source->flags & G1_FLAG_HAS_ZOOM_SPRITE)) { + rct_drawpixelinfo zoomed_dpi; + zoomed_dpi.bits = dpi->bits; + zoomed_dpi.x = dpi->x >> 1; + zoomed_dpi.y = dpi->y >> 1; + zoomed_dpi.height = dpi->height >> 1; + zoomed_dpi.width = dpi->width >> 1; + zoomed_dpi.pitch = dpi->pitch; + zoomed_dpi.zoom_level = dpi->zoom_level - 1; + gfx_draw_sprite_palette_set_software(&zoomed_dpi, image_type | (image_element - g1_source->zoomed_offset), x >> 1, y >> 1, palette_pointer, unknown_pointer); + return; + } + + if (dpi->zoom_level != 0 && (g1_source->flags & G1_FLAG_NO_ZOOM_DRAW)) { + return; + } + + //Its used super often so we will define it to a separate variable. + sint32 zoom_level = dpi->zoom_level; + sint32 zoom_mask = 0xFFFFFFFF << zoom_level; + + if (zoom_level && g1_source->flags & G1_FLAG_RLE_COMPRESSION){ + x -= ~zoom_mask; + y -= ~zoom_mask; + } + + //This will be the height of the drawn image + sint32 height = g1_source->height; + //This is the start y coordinate on the destination + sint16 dest_start_y = y + g1_source->y_offset; + + // For whatever reason the RLE version does not use + // the zoom mask on the y coordinate but does on x. + if (g1_source->flags & G1_FLAG_RLE_COMPRESSION){ + dest_start_y -= dpi->y; + } + else{ + dest_start_y = (dest_start_y&zoom_mask) - dpi->y; + } + //This is the start y coordinate on the source + sint32 source_start_y = 0; + + if (dest_start_y < 0){ + //If the destination y is negative reduce the height of the + //image as we will cut off the bottom + height += dest_start_y; + //If the image is no longer visible nothing to draw + if (height <= 0){ + return; + } + //The source image will start a further up the image + source_start_y -= dest_start_y; + //The destination start is now reset to 0 + dest_start_y = 0; + } + else{ + if (g1_source->flags & G1_FLAG_RLE_COMPRESSION && zoom_level){ + source_start_y -= dest_start_y & ~zoom_mask; + height += dest_start_y & ~zoom_mask; + } + } + + sint32 dest_end_y = dest_start_y + height; + + if (dest_end_y > dpi->height){ + //If the destination y is outside of the drawing + //image reduce the height of the image + height -= dest_end_y - dpi->height; + } + //If the image no longer has anything to draw + if (height <= 0)return; + + dest_start_y >>= zoom_level; + + //This will be the width of the drawn image + sint32 width = g1_source->width; + //This is the source start x coordinate + sint32 source_start_x = 0; + //This is the destination start x coordinate + sint16 dest_start_x = ((x + g1_source->x_offset + ~zoom_mask)&zoom_mask) - dpi->x; + + if (dest_start_x < 0){ + //If the destination is negative reduce the width + //image will cut off the side + width += dest_start_x; + //If there is no image to draw + if (width <= 0){ + return; + } + //The source start will also need to cut off the side + source_start_x -= dest_start_x; + //Reset the destination to 0 + dest_start_x = 0; + } + else{ + if (g1_source->flags & G1_FLAG_RLE_COMPRESSION && zoom_level){ + source_start_x -= dest_start_x & ~zoom_mask; + } + } + + sint32 dest_end_x = dest_start_x + width; + + if (dest_end_x > dpi->width){ + //If the destination x is outside of the drawing area + //reduce the image width. + width -= dest_end_x - dpi->width; + //If there is no image to draw. + if (width <= 0)return; + } + + dest_start_x >>= zoom_level; + + uint8* dest_pointer = (uint8*)dpi->bits; + //Move the pointer to the start point of the destination + dest_pointer += ((dpi->width >> zoom_level) + dpi->pitch) * dest_start_y + dest_start_x; + + if (g1_source->flags & G1_FLAG_RLE_COMPRESSION){ + //We have to use a different method to move the source pointer for + //rle encoded sprites so that will be handled within this function + gfx_rle_sprite_to_buffer(g1_source->offset, dest_pointer, palette_pointer, dpi, image_type, source_start_y, height, source_start_x, width); + return; + } + uint8* source_pointer = g1_source->offset; + //Move the pointer to the start point of the source + source_pointer += g1_source->width*source_start_y + source_start_x; + + if (!(g1_source->flags & G1_FLAG_1)) { + gfx_bmp_sprite_to_buffer(palette_pointer, unknown_pointer, source_pointer, dest_pointer, g1_source, dpi, height, width, image_type); + } + } + + /** + * Draws the given colour image masked out by the given mask image. This can currently only cope with bitmap formatted mask and + * colour images. Presumably the original game never used RLE images for masking. Colour 0 represents transparent. + * + * rct2: 0x00681DE2 + */ + void FASTCALL gfx_draw_sprite_raw_masked_software(rct_drawpixelinfo *dpi, sint32 x, sint32 y, sint32 maskImage, sint32 colourImage) + { + sint32 left, top, right, bottom, width, height; + rct_g1_element *imgMask = &g1Elements[maskImage & 0x7FFFF]; + rct_g1_element *imgColour = &g1Elements[colourImage & 0x7FFFF]; + + assert(imgMask->flags & G1_FLAG_BMP); + assert(imgColour->flags & G1_FLAG_BMP); + + if (dpi->zoom_level != 0) { + // TODO implement other zoom levels (probably not used though) + assert(false); + return; + } + + width = Math::Min(imgMask->width, imgColour->width); + height = Math::Min(imgMask->height, imgColour->height); + + x += imgMask->x_offset; + y += imgMask->y_offset; + + left = Math::Max(dpi->x, x); + top = Math::Max(dpi->y, y); + right = Math::Min(dpi->x + dpi->width, x + width); + bottom = Math::Min(dpi->y + dpi->height, y + height); + + width = right - left; + height = bottom - top; + if (width < 0 || height < 0) + return; + + sint32 skipX = left - x; + sint32 skipY = top - y; + + uint8 *maskSrc = imgMask->offset + (skipY * imgMask->width) + skipX; + uint8 *colourSrc = imgColour->offset + (skipY * imgColour->width) + skipX; + uint8 *dst = dpi->bits + (left - dpi->x) + ((top - dpi->y) * (dpi->width + dpi->pitch)); + + sint32 maskWrap = imgMask->width - width; + sint32 colourWrap = imgColour->width - width; + sint32 dstWrap = ((dpi->width + dpi->pitch) - width); + for (sint32 yy = top; yy < bottom; yy++) { + for (sint32 xx = left; xx < right; xx++) { + uint8 colour = (*colourSrc) & (*maskSrc); + if (colour != 0) { + *dst = colour; + } + + maskSrc++; + colourSrc++; + dst++; + } + maskSrc += maskWrap; + colourSrc += colourWrap; + dst += dstWrap; + } + } + + rct_g1_element * gfx_get_g1_element(sint32 image_id) + { + if (image_id < SPR_G2_BEGIN) + { + return &g1Elements[image_id]; + } + if (image_id < SPR_CSG_BEGIN) + { + return &_g2.elements[image_id - SPR_G2_BEGIN]; + } + return &_csg.elements[image_id - SPR_CSG_BEGIN]; + } + } diff --git a/src/openrct2/platform/linux.c b/src/openrct2/platform/linux.c index 90eff32baf..f49193d39b 100644 --- a/src/openrct2/platform/linux.c +++ b/src/openrct2/platform/linux.c @@ -495,7 +495,7 @@ utf8 *platform_open_directory_browser(utf8 *title) { return return_value; } -void platform_show_messagebox(char *message) { +void platform_show_messagebox(const char * message) { size_t size; dialog_type dtype; char cmd[MAX_PATH]; diff --git a/src/openrct2/platform/macos.m b/src/openrct2/platform/macos.m index 890fb2bdd5..77b67b8808 100644 --- a/src/openrct2/platform/macos.m +++ b/src/openrct2/platform/macos.m @@ -94,7 +94,7 @@ void platform_posix_sub_resolve_openrct_data_path(utf8 *out, size_t size) { } } -void platform_show_messagebox(char *message) +void platform_show_messagebox(const char * message) { @autoreleasepool { diff --git a/src/openrct2/platform/platform.h b/src/openrct2/platform/platform.h index e3c96fb41f..29a81740b7 100644 --- a/src/openrct2/platform/platform.h +++ b/src/openrct2/platform/platform.h @@ -193,7 +193,7 @@ void platform_resolve_openrct_data_path(); void platform_get_openrct_data_path(utf8 *outPath, size_t outSize); void platform_get_user_directory(utf8 *outPath, const utf8 *subDirectory, size_t outSize); utf8* platform_get_username(); -void platform_show_messagebox(utf8 *message); +void platform_show_messagebox(const utf8 * message); bool platform_open_common_file_dialog(utf8 *outFilename, file_dialog_desc *desc, size_t outSize); utf8 *platform_open_directory_browser(utf8 *title); uint8 platform_get_locale_currency(); diff --git a/src/openrct2/platform/windows.c b/src/openrct2/platform/windows.c index 01b499b42c..298134d82c 100644 --- a/src/openrct2/platform/windows.c +++ b/src/openrct2/platform/windows.c @@ -590,7 +590,7 @@ void platform_get_user_directory(utf8 *outPath, const utf8 *subDirectory, size_t } } -void platform_show_messagebox(utf8 *message) +void platform_show_messagebox(const utf8 * message) { MessageBoxA(windows_get_window_handle(), message, "OpenRCT2", MB_OK); }