OpenLoco/src/openloco/graphics/gfx.cpp

1137 lines
32 KiB
C++

#include "gfx.h"
#include "../Console.h"
#include "../Environment.h"
#include "../Input.h"
#include "../Ui.h"
#include "../drawing/SoftwareDrawingEngine.h"
#include "../interop/interop.hpp"
#include "../localisation/languagefiles.h"
#include "../ui/WindowManager.h"
#include "../utility/stream.hpp"
#include "colours.h"
#include "image_ids.h"
#include <algorithm>
#include <cassert>
#include <fstream>
#include <iostream>
#include <memory>
using namespace openloco::interop;
using namespace openloco::utility;
using namespace openloco::drawing;
using namespace openloco::ui;
namespace openloco::gfx
{
namespace g1_expected_count
{
constexpr uint32_t disc = 0x101A; // And GOG
constexpr uint32_t steam = 0x0F38;
}
namespace text_draw_flags
{
constexpr uint8_t inset = (1ULL << 0);
constexpr uint8_t outline = (1ULL << 1);
constexpr uint8_t dark = (1ULL << 2);
constexpr uint8_t extra_dark = (1ULL << 3);
}
constexpr uint32_t g1_count_objects = 0x40000;
constexpr uint32_t g1_count_temporary = 0x1000;
static loco_global<drawpixelinfo_t, 0x0050B884> _screen_dpi;
static loco_global<drawpixelinfo_t, 0x005233B8> _windowDPI;
static loco_global<g1_element[g1_expected_count::disc + g1_count_temporary + g1_count_objects], 0x9E2424> _g1Elements;
static std::unique_ptr<std::byte[]> _g1Buffer;
static loco_global<uint16_t, 0x112C824> _currentFontFlags;
static loco_global<int16_t, 0x112C876> _currentFontSpriteBase;
static loco_global<uint8_t[224 * 4], 0x112C884> _characterWidths;
static loco_global<uint8_t[4], 0x1136594> _windowColours;
static palette_index_t _textColours[8] = { 0 };
drawpixelinfo_t& screenDpi()
{
return _screen_dpi;
}
static std::vector<g1_element> convertElements(const std::vector<g1_element32_t>& elements32)
{
auto elements = std::vector<g1_element>();
elements.reserve(elements32.size());
std::transform(
elements32.begin(),
elements32.end(),
std::back_inserter(elements),
[](g1_element32_t src) { return g1_element(src); });
return elements;
}
// 0x0044733C
void loadG1()
{
auto g1Path = environment::getPath(environment::path_id::g1);
std::ifstream stream(g1Path, std::ios::in | std::ios::binary);
if (!stream)
{
throw std::runtime_error("Opening g1 file failed.");
}
g1_header_t header;
if (!readData(stream, header))
{
throw std::runtime_error("Reading g1 file header failed.");
}
if (header.num_entries != g1_expected_count::disc)
{
std::cout << "G1 element count doesn't match expected value: ";
std::cout << "Expected " << g1_expected_count::disc << "; Got " << header.num_entries << std::endl;
if (header.num_entries == g1_expected_count::steam)
{
std::cout << "Got Steam G1.DAT variant, will fix elements automatically." << std::endl;
}
}
// Read element headers
auto elements32 = std::vector<g1_element32_t>(header.num_entries);
if (!readData(stream, elements32.data(), header.num_entries))
{
throw std::runtime_error("Reading g1 element headers failed.");
}
auto elements = convertElements(elements32);
// Read element data
auto elementData = std::make_unique<std::byte[]>(header.total_size);
if (!readData(stream, elementData.get(), header.total_size))
{
throw std::runtime_error("Reading g1 elements failed.");
}
stream.close();
// The steam G1.DAT is missing two localised tutorial icons, and a smaller font variant
// This code copies the closest variants into their place, and moves other elements accordingly
if (header.num_entries == g1_expected_count::steam)
{
elements.resize(g1_expected_count::disc);
// Extra two tutorial images
std::copy_n(&elements[3549], header.num_entries - 3549, &elements[3551]);
std::copy_n(&elements[3551], 1, &elements[3549]);
std::copy_n(&elements[3551], 1, &elements[3550]);
// Extra font variant
std::copy_n(&elements[1788], 223, &elements[3898]);
}
// Adjust memory offsets
for (auto& element : elements)
{
element.offset += (uintptr_t)elementData.get();
}
_g1Buffer = std::move(elementData);
std::copy(elements.begin(), elements.end(), _g1Elements.get());
}
// 0x00447485
// edi: dpi
// ebp: fill
void clear(drawpixelinfo_t& dpi, uint32_t fill)
{
int32_t w = dpi.width / (1 << dpi.zoom_level);
int32_t h = dpi.height / (1 << dpi.zoom_level);
uint8_t* ptr = dpi.bits;
for (int32_t y = 0; y < h; y++)
{
std::fill_n(ptr, w, fill);
ptr += w + dpi.pitch;
}
}
void clearSingle(drawpixelinfo_t& dpi, uint8_t paletteId)
{
auto fill = (paletteId << 24) | (paletteId << 16) | (paletteId << 8) | paletteId;
clear(dpi, fill);
}
// 0x004957C4
int16_t clipString(int16_t width, char* string)
{
registers regs;
regs.di = width;
regs.esi = (int32_t)string;
call(0x004957C4, regs);
return regs.cx;
}
/**
* 0x00495685
*
* @param buffer @<esi>
* @return width @<cx>
*/
uint16_t getStringWidth(const char* buffer)
{
uint16_t width = 0;
const uint8_t* str = reinterpret_cast<const uint8_t*>(buffer);
int16_t fontSpriteBase = _currentFontSpriteBase;
while (*str != (uint8_t)0)
{
const uint8_t chr = *str;
str++;
if (chr >= 32)
{
width += _characterWidths[chr - 32 + fontSpriteBase];
continue;
}
switch (chr)
{
case control_codes::move_x:
width = *str;
str++;
break;
case control_codes::adjust_palette:
case 3:
case 4:
str++;
break;
case control_codes::newline:
case control_codes::newline_smaller:
continue;
case control_codes::font_small:
fontSpriteBase = font::small;
break;
case control_codes::font_large:
fontSpriteBase = font::large;
break;
case control_codes::font_bold:
fontSpriteBase = font::medium_bold;
break;
case control_codes::font_regular:
fontSpriteBase = font::medium_normal;
break;
case control_codes::outline:
case control_codes::outline_off:
case control_codes::window_colour_1:
case control_codes::window_colour_2:
case control_codes::window_colour_3:
case 0x10:
break;
case control_codes::inline_sprite_str:
{
const uint32_t image = reinterpret_cast<const uint32_t*>(str)[0];
const uint32_t imageId = image & 0x7FFFF;
str += 4;
width += _g1Elements[imageId].width;
break;
}
default:
if (chr <= 0x16)
{
str += 2;
}
else
{
str += 4;
}
break;
}
}
return width;
}
static void setTextColours(palette_index_t pal1, palette_index_t pal2, palette_index_t pal3)
{
if ((_currentFontFlags & text_draw_flags::inset) != 0)
return;
_textColours[1] = pal1;
_textColours[2] = palette_index::transparent;
_textColours[3] = palette_index::transparent;
if ((_currentFontFlags & text_draw_flags::outline) != 0)
{
_textColours[2] = pal2;
_textColours[3] = pal3;
}
}
static void setTextColour(int colour)
{
auto el = &_g1Elements[image_ids::text_palette];
setTextColours(el->offset[colour * 4 + 0], el->offset[colour * 4 + 1], el->offset[colour * 4 + 2]);
}
// 0x00451189
static gfx::point_t loopNewline(drawpixelinfo_t* context, gfx::point_t origin, uint8_t* str)
{
gfx::point_t pos = origin;
while (true)
{
// When offscreen in y dimension don't draw text
// In original this check only performed if pos.y updated instead of every loop
bool offscreen = true;
if (pos.y + 19 > context->y)
{
if (context->y + context->height > pos.y)
{
offscreen = false;
}
}
uint8_t chr = *str;
str++;
switch (chr)
{
case '\0':
return pos;
case control_codes::adjust_palette:
// This control character does not appear in the localisation files
assert(false);
str++;
break;
case control_codes::newline_smaller:
pos.x = origin.x;
if (_currentFontSpriteBase == font::medium_normal || _currentFontSpriteBase == font::medium_bold)
{
pos.y += 5;
}
else if (_currentFontSpriteBase == font::small)
{
pos.y += 3;
}
else if (_currentFontSpriteBase == font::large)
{
pos.y += 9;
}
break;
case control_codes::newline:
pos.x = origin.x;
if (_currentFontSpriteBase == font::medium_normal || _currentFontSpriteBase == font::medium_bold)
{
pos.y += 10;
}
else if (_currentFontSpriteBase == font::small)
{
pos.y += 6;
}
else if (_currentFontSpriteBase == font::large)
{
pos.y += 18;
}
break;
case control_codes::move_x:
{
uint8_t offset = *str;
str++;
pos.x = origin.x + offset;
break;
}
case control_codes::newline_x_y:
{
uint8_t offset = *str;
str++;
pos.x = origin.x + offset;
offset = *str;
str++;
pos.y = origin.y + offset;
break;
}
case control_codes::font_small:
_currentFontSpriteBase = font::small;
break;
case control_codes::font_large:
_currentFontSpriteBase = font::large;
break;
case control_codes::font_regular:
_currentFontSpriteBase = font::medium_normal;
break;
case control_codes::font_bold:
_currentFontSpriteBase = font::medium_bold;
break;
case control_codes::outline:
_currentFontFlags = _currentFontFlags | text_draw_flags::outline;
break;
case control_codes::outline_off:
_currentFontFlags = _currentFontFlags & ~text_draw_flags::outline;
break;
case control_codes::window_colour_1:
{
int hue = _windowColours[0];
setTextColours(colour::getShade(hue, 7), palette_index::index_0A, palette_index::index_0A);
break;
}
case control_codes::window_colour_2:
{
int hue = _windowColours[1];
setTextColours(colour::getShade(hue, 9), palette_index::index_0A, palette_index::index_0A);
break;
}
case control_codes::window_colour_3:
{
int hue = _windowColours[2];
setTextColours(colour::getShade(hue, 9), palette_index::index_0A, palette_index::index_0A);
break;
}
case control_codes::inline_sprite_str:
{
uint32_t image = ((uint32_t*)str)[0];
uint32_t imageId = image & 0x7FFFF;
str += 4;
if ((_currentFontFlags & text_draw_flags::inset) != 0)
{
gfx::drawImageSolid(context, pos.x, pos.y, imageId, _textColours[3]);
gfx::drawImageSolid(context, pos.x + 1, pos.y + 1, imageId, _textColours[1]);
}
else
{
gfx::drawImage(context, pos.x, pos.y, image);
}
pos.x += _g1Elements[imageId].width;
break;
}
case control_codes::colour_black:
setTextColour(0);
break;
case control_codes::colour_grey:
setTextColour(1);
break;
case control_codes::colour_white:
setTextColour(2);
break;
case control_codes::colour_red:
setTextColour(3);
break;
case control_codes::colour_green:
setTextColour(4);
break;
case control_codes::colour_yellow:
setTextColour(5);
break;
case control_codes::colour_topaz:
setTextColour(6);
break;
case control_codes::colour_celadon:
setTextColour(7);
break;
case control_codes::colour_babyblue:
setTextColour(8);
break;
case control_codes::colour_palelavender:
setTextColour(9);
break;
case control_codes::colour_palegold:
setTextColour(10);
break;
case control_codes::colour_lightpink:
setTextColour(11);
break;
case control_codes::colour_pearlaqua:
setTextColour(12);
break;
case control_codes::colour_palesilver:
setTextColour(13);
break;
default:
if (!offscreen)
{
// When offscreen in the y dimension there is no requirement to keep pos.x correct
if (chr >= 32)
{
gfx::drawImagePaletteSet(context, pos.x, pos.y, 1116 + chr - 32 + _currentFontSpriteBase, _textColours);
pos.x += _characterWidths[chr - 32 + _currentFontSpriteBase];
}
else
{
// Unhandled control code
assert(false);
}
}
break;
}
}
return pos;
}
/**
* 0x00451025
*
* @param x @<cx>
* @param y @<dx>
* @param colour @<al>
* @param context @<edi>
* @param text @<esi>
*/
gfx::point_t drawString(drawpixelinfo_t* context, int16_t x, int16_t y, uint8_t colour, void* str)
{
// 0x00E04348, 0x00E0434A
gfx::point_t origin = { x, y };
if (colour == format_flags::fe)
{
return loopNewline(context, origin, (uint8_t*)str);
}
if (colour == format_flags::fd)
{
_currentFontFlags = 0;
setTextColour(0);
return loopNewline(context, origin, (uint8_t*)str);
}
if (x >= context->x + context->width)
return origin;
if (x < context->x - 1280)
return origin;
if (y >= context->y + context->height)
return origin;
if (y < context->y - 90)
return origin;
if (colour == format_flags::ff)
{
return loopNewline(context, origin, (uint8_t*)str);
}
_currentFontFlags = 0;
if (_currentFontSpriteBase == font::m1)
{
_currentFontSpriteBase = font::medium_bold;
_currentFontFlags = _currentFontFlags | text_draw_flags::dark;
}
else if (_currentFontSpriteBase == font::m2)
{
_currentFontSpriteBase = font::medium_bold;
_currentFontFlags = _currentFontFlags | text_draw_flags::dark;
_currentFontFlags = _currentFontFlags | text_draw_flags::extra_dark;
}
_textColours[0] = palette_index::transparent;
_textColours[1] = colour::getShade(colour::dark_purple, 5);
_textColours[2] = colour::getShade(colour::bright_pink, 5);
_textColours[3] = colour::getShade(colour::light_blue, 5);
if (colour & format_flags::textflag_5)
{
colour &= ~format_flags::textflag_5;
_currentFontFlags = _currentFontFlags | text_draw_flags::outline;
}
if (colour & format_flags::textflag_6)
{
colour &= ~format_flags::textflag_6;
_currentFontFlags = _currentFontFlags | text_draw_flags::inset;
}
if ((_currentFontFlags & text_draw_flags::inset) != 0)
{
if ((_currentFontFlags & text_draw_flags::dark) != 0 && (_currentFontFlags & text_draw_flags::extra_dark) != 0)
{
_textColours[1] = colour::getShade(colour, 2);
_textColours[2] = palette_index::transparent;
_textColours[3] = colour::getShade(colour, 4);
}
else if ((_currentFontFlags & text_draw_flags::dark) != 0)
{
_textColours[1] = colour::getShade(colour, 3);
_textColours[2] = palette_index::transparent;
_textColours[3] = colour::getShade(colour, 5);
}
else
{
_textColours[1] = colour::getShade(colour, 4);
_textColours[2] = palette_index::transparent;
_textColours[3] = colour::getShade(colour, 6);
}
}
else
{
setTextColours(colour::getShade(colour, 9), palette_index::index_0A, palette_index::index_0A);
}
return loopNewline(context, origin, (uint8_t*)str);
}
// 0x00495224
// al: colour
// bp: width
// bx: string id
// cx: x
// dx: y
// esi: args
// edi: dpi
int16_t drawString_495224(
drawpixelinfo_t& dpi,
int16_t x,
int16_t y,
int16_t width,
uint8_t colour,
string_id stringId,
const void* args)
{
registers regs;
regs.al = colour;
regs.bx = stringId;
regs.bp = width;
regs.cx = x;
regs.dx = y;
regs.esi = (int32_t)args;
regs.edi = (int32_t)&dpi;
call(0x00495224, regs);
return regs.dx;
}
// 0x00494B3F
// al: colour
// bx: string id
// cx: x
// dx: y
// esi: args
// edi: dpi
void drawString_494B3F(
drawpixelinfo_t& dpi,
int16_t x,
int16_t y,
uint8_t colour,
string_id stringId,
const void* args)
{
registers regs;
regs.al = colour;
regs.bx = stringId;
regs.cx = x;
regs.dx = y;
regs.esi = (int32_t)args;
regs.edi = (int32_t)&dpi;
call(0x00494B3F, regs);
}
/**
*
* @param dpi @<edi>
* @param origin {x @<cx>, y @<dx>}
* @param colour @<al>
* @param stringId @<bx>
* @param args @<edi>
*/
void drawString_494B3F(
drawpixelinfo_t& dpi,
point_t* origin,
uint8_t colour,
string_id stringId,
const void* args)
{
registers regs;
regs.al = colour;
regs.bx = stringId;
regs.cx = origin->x;
regs.dx = origin->y;
regs.esi = (int32_t)args;
regs.edi = (int32_t)&dpi;
call(0x00494B3F, regs);
origin->x = regs.cx;
origin->y = regs.dx;
}
// 0x00494BBF
// al: colour
// bx: string id
// cx: x
// dx: y
// esi: args
// edi: dpi
// bp: width
void drawString_494BBF(
drawpixelinfo_t& dpi,
int16_t x,
int16_t y,
int16_t width,
uint8_t colour,
string_id stringId,
const void* args)
{
registers regs;
regs.al = colour;
regs.bx = stringId;
regs.cx = x;
regs.dx = y;
regs.esi = (int32_t)args;
regs.edi = (int32_t)&dpi;
regs.bp = width;
call(0x00494BBF, regs);
}
// 0x00494C78
// al: colour
// bx: string id
// cx: x
// dx: y
// esi: args
// edi: dpi
void drawString_494C78(
drawpixelinfo_t& dpi,
int16_t x,
int16_t y,
uint8_t colour,
string_id stringId,
const void* args)
{
registers regs;
regs.al = colour;
regs.bx = stringId;
regs.cx = x;
regs.dx = y;
regs.esi = (int32_t)args;
regs.edi = (int32_t)&dpi;
call(0x00494C78, regs);
}
// 0x00494CB2
// al: colour
// bx: string id
// cx: x
// dx: y
// esi: args
// edi: dpi
void drawStringUnderline(
drawpixelinfo_t& dpi,
int16_t x,
int16_t y,
uint8_t colour,
string_id stringId,
const void* args)
{
registers regs;
regs.al = colour;
regs.bx = stringId;
regs.cx = x;
regs.dx = y;
regs.esi = (int32_t)args;
regs.edi = (int32_t)&dpi;
call(0x00494CB2, regs);
}
// 0x00494D78
// al: colour
// bx: string id
// cx: x
// dx: y
// esi: args
// edi: dpi
void drawStringLeftUnderline(
drawpixelinfo_t& dpi,
int16_t x,
int16_t y,
uint8_t colour,
string_id stringId,
const void* args)
{
registers regs;
regs.al = colour;
regs.bx = stringId;
regs.cx = x;
regs.dx = y;
regs.esi = (int32_t)args;
regs.edi = (int32_t)&dpi;
call(0x00494D78, regs);
}
// 0x00494DE8
// al: colour
// bx: string id
// cx: x
// dx: y
// esi: args
// edi: dpi
void drawStringCentred(
drawpixelinfo_t& dpi,
int16_t x,
int16_t y,
uint8_t colour,
string_id stringId,
const void* args)
{
registers regs;
regs.al = colour;
regs.bx = stringId;
regs.cx = x;
regs.dx = y;
regs.esi = (int32_t)args;
regs.edi = (int32_t)&dpi;
call(0x00494DE8, regs);
}
// 0x00494C36
// al: colour
// bx: string id
// bp: width
// cx: x
// dx: y
// esi: args
// edi: dpi
void drawStringCentredClipped(
drawpixelinfo_t& dpi,
int16_t x,
int16_t y,
int16_t width,
uint8_t colour,
string_id stringId,
const void* args)
{
registers regs;
regs.edi = (int32_t)&dpi;
regs.esi = (int32_t)args;
regs.ebx = stringId;
regs.cx = x;
regs.dx = y;
regs.al = colour;
regs.bp = width;
call(0x00494C36, regs);
}
/**
* 0x00494ECF
*
* @param context @<edi>
* @param origin {x @<cx>, y @<dx>}
* @param width @<bp>
* @param colour @<al>
* @param stringId @<bx>
* @param args @<esi>
*/
void drawStringCentredWrapped(
drawpixelinfo_t* context,
point_t* origin,
uint16_t width,
uint8_t colour,
string_id stringId,
const void* args)
{
registers regs;
regs.edi = (uintptr_t)context;
regs.esi = (uintptr_t)args;
regs.cx = origin->x;
regs.dx = origin->y;
regs.bp = width;
regs.al = colour;
regs.bx = stringId;
call(0x00494ECF, regs);
origin->x = regs.cx;
origin->y = regs.dx;
}
// 0x00494E33
// al: colour
// bx: string id
// bp: width
// cx: x
// dx: y
// esi: args
// edi: dpi
void drawStringCentredRaw(
drawpixelinfo_t& dpi,
int16_t x,
int16_t y,
int16_t width,
uint8_t colour,
const void* args)
{
registers regs;
regs.edi = (int32_t)&dpi;
regs.esi = (int32_t)args;
regs.cx = x;
regs.dx = y;
regs.al = colour;
regs.bp = width;
call(0x00494E33, regs);
}
// 0x00495715
// @param buffer @<esi>
// @return width @<cx>
uint16_t getStringWidthNewLined(const char* buffer)
{
registers regs;
regs.esi = (uintptr_t)buffer;
call(0x00495715, regs);
return regs.cx;
}
std::pair<uint16_t, uint16_t> wrapString(const char* buffer, uint16_t stringWidth)
{
// gfx_wrap_string
registers regs;
regs.esi = (uintptr_t)buffer;
regs.di = stringWidth;
call(0x00495301, regs);
return std::make_pair(regs.cx, regs.di);
}
// 0x004474BA
static void drawRectImpl(gfx::drawpixelinfo_t* dpi, int16_t left, int16_t top, int16_t right, int16_t bottom, uint32_t colour)
{
registers regs;
regs.ax = left;
regs.bx = right;
regs.cx = top;
regs.dx = bottom;
regs.ebp = colour;
regs.edi = (uint32_t)dpi;
call(0x004474BA, regs);
}
void fillRect(gfx::drawpixelinfo_t* dpi, int16_t left, int16_t top, int16_t right, int16_t bottom, uint32_t colour)
{
drawRectImpl(dpi, left, top, right, bottom, colour);
}
void drawRect(gfx::drawpixelinfo_t* dpi, int16_t x, int16_t y, uint16_t dx, uint16_t dy, uint32_t colour)
{
// This makes the function signature more like a drawing application
drawRectImpl(dpi, x, y, x + dx - 1, y + dy - 1, colour);
}
void fillRectInset(gfx::drawpixelinfo_t* dpi, int16_t left, int16_t top, int16_t right, int16_t bottom, uint32_t colour, uint8_t flags)
{
registers regs;
regs.ax = left;
regs.bx = right;
regs.cx = top;
regs.dx = bottom;
regs.ebp = colour;
regs.edi = (uint32_t)dpi;
regs.si = flags;
call(0x004C58C7, regs);
}
void drawRectInset(gfx::drawpixelinfo_t* dpi, int16_t x, int16_t y, uint16_t dx, uint16_t dy, uint32_t colour, uint8_t flags)
{
// This makes the function signature more like a drawing application
fillRectInset(dpi, x, y, x + dx - 1, y + dy - 1, colour, flags);
}
// 0x00452DA4
void drawLine(gfx::drawpixelinfo_t* dpi, int16_t left, int16_t top, int16_t right, int16_t bottom, uint32_t colour)
{
registers regs;
regs.ax = left;
regs.bx = top;
regs.cx = right;
regs.dx = bottom;
regs.ebp = colour;
regs.edi = (uint32_t)dpi;
call(0x00452DA4, regs);
}
// 0x004CD406
void invalidateScreen()
{
setDirtyBlocks(0, 0, ui::width(), ui::height());
}
drawing::SoftwareDrawingEngine* engine;
/**
* 0x004C5C69
*
* @param left @<ax>
* @param top @<bx>
* @param right @<dx>
* @param bottom @<bp>
*/
void setDirtyBlocks(int32_t left, int32_t top, int32_t right, int32_t bottom)
{
if (engine == nullptr)
engine = new drawing::SoftwareDrawingEngine();
engine->setDirtyBlocks(left, top, right, bottom);
}
// 0x004C5CFA
void drawDirtyBlocks()
{
if (engine == nullptr)
engine = new drawing::SoftwareDrawingEngine();
engine->drawDirtyBlocks();
}
loco_global<char[512], 0x0112CC04> byte_112CC04;
loco_global<char[512], 0x0112CE04> byte_112CE04;
// 0x004CF63B
void render()
{
if (engine == nullptr)
engine = new drawing::SoftwareDrawingEngine();
char backup1[512] = { 0 };
char backup2[512] = { 0 };
std::memcpy(backup1, byte_112CC04, 512);
std::memcpy(backup2, byte_112CE04, 512);
if (ui::dirtyBlocksInitialised())
{
engine->drawDirtyBlocks();
}
if (input::hasFlag(input::input_flags::flag5))
{
call(0x004072EC); // NOP on _NO_LOCO_WIN32_
}
else
{
ui::processMessages();
}
if (addr<0x005252AC, uint32_t>() != 0)
{
// sub_4058F5();
}
std::memcpy(byte_112CC04, backup1, 512);
std::memcpy(byte_112CE04, backup2, 512);
}
void redrawScreenRect(Rect rect)
{
engine->drawRect(rect);
}
/**
* 0x004C5DD5
* rct2: window_draw_all
*
* @param left @<ax>
* @param top @<bx>
* @param right @<dx>
* @param bottom @<bp>
*/
void redrawScreenRect(int16_t left, int16_t top, int16_t right, int16_t bottom)
{
redrawScreenRect(Rect::fromLTRB(left, top, right, bottom));
}
void drawImage(gfx::drawpixelinfo_t* dpi, int16_t x, int16_t y, uint32_t image)
{
registers regs;
regs.cx = x;
regs.dx = y;
regs.ebx = image;
regs.edi = (uint32_t)dpi;
call(0x00448C79, regs);
}
uint32_t recolour(uint32_t image)
{
return (1 << 29) | image;
}
uint32_t recolour(uint32_t image, uint8_t colour)
{
return (1 << 29) | (colour << 19) | image;
}
loco_global<uint8_t*, 0x0050B860> _50B860;
loco_global<uint32_t, 0x00E04324> _E04324;
void drawImageSolid(gfx::drawpixelinfo_t* dpi, int16_t x, int16_t y, uint32_t image, uint8_t palette_index)
{
uint8_t palette[256];
memset(palette, palette_index, 256);
palette[0] = 0;
drawImagePaletteSet(dpi, x, y, image, palette);
}
void drawImagePaletteSet(gfx::drawpixelinfo_t* dpi, int16_t x, int16_t y, uint32_t image, uint8_t* palette)
{
_50B860 = palette;
_E04324 = 0x20000000;
registers regs;
regs.cx = x;
regs.dx = y;
regs.ebx = image;
regs.edi = (uint32_t)dpi;
call(0x00448D90, regs);
}
bool clipDrawpixelinfo(gfx::drawpixelinfo_t** dst, gfx::drawpixelinfo_t* src, int16_t x, int16_t y, int16_t width, int16_t height)
{
registers regs;
regs.ax = x;
regs.bx = width;
regs.edi = (int32_t)src;
regs.dx = height;
regs.cx = y;
call(0x4cec50, regs);
*dst = (gfx::drawpixelinfo_t*)regs.edi;
return *dst != nullptr;
}
g1_element* getG1Element(uint32_t id)
{
if (id < _g1Elements.size())
{
return &_g1Elements[id];
}
return nullptr;
}
}