Decompile widget drawing

This commit is contained in:
Marijn van der Werf 2018-02-16 07:19:37 +01:00
parent 1e7a6e60ec
commit 8f8ab7ca13
17 changed files with 1157 additions and 83 deletions

View File

@ -0,0 +1,24 @@
#include "../interop/interop.hpp"
#include "colours.h"
#include <cassert>
using namespace openloco::interop;
namespace openloco::colour
{
loco_global<uint8_t[32][8], 0x01136BA0> _colour_map_a;
loco_global<uint8_t[32][8], 0x01136C98> _colour_map_b;
uint8_t get_shade(colour_t colour, uint8_t shade)
{
assert(colour <= 31);
if (shade < 8)
{
return _colour_map_a[colour][shade];
}
return _colour_map_b[colour][shade - 8];
}
}

View File

@ -52,5 +52,7 @@ namespace openloco
{
return c & ~translucent_flag;
}
uint8_t get_shade(colour_t colour, uint8_t shade);
}
}

View File

@ -139,6 +139,33 @@ namespace openloco::gfx
clear(dpi, fill);
}
// 0x004957C4
int16_t clip_string(int16_t width, char* string)
{
registers regs;
regs.di = width;
regs.esi = (int32_t)string;
call(0x004957C4, regs);
return regs.cx;
}
void draw_string(
drawpixelinfo_t& dpi,
int16_t x,
int16_t y,
uint8_t colour,
const char* string)
{
registers regs;
regs.cx = x;
regs.dx = y;
regs.al = colour;
regs.edi = (int32_t)&dpi;
regs.esi = (int32_t)string;
call(0x00451025, regs);
}
// 0x00494B3F
// al: colour
// bx: string id
@ -192,6 +219,26 @@ namespace openloco::gfx
call(0x00494BBF, regs);
}
void draw_string_centred_clipped(
drawpixelinfo_t& dpi,
int16_t x,
int16_t y,
int16_t width,
uint8_t colour,
string_id stringId,
const char* 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);
}
// 0x004474BA
static void draw_rect_impl(gfx::drawpixelinfo_t* dpi, int16_t left, int16_t top, int16_t right, int16_t bottom, uint32_t color)
{
@ -205,12 +252,30 @@ namespace openloco::gfx
call(0x004474BA, regs);
}
void fill_rect(gfx::drawpixelinfo_t* dpi, int16_t left, int16_t top, int16_t right, int16_t bottom, uint32_t color)
{
draw_rect_impl(dpi, left, top, right, bottom, color);
}
void draw_rect(gfx::drawpixelinfo_t* dpi, int16_t x, int16_t y, uint16_t dx, uint16_t dy, uint32_t color)
{
// This makes the function signature more like a drawing application
draw_rect_impl(dpi, x, y, x + dx - 1, y + dy - 1, color);
}
void fill_rect_inset(gfx::drawpixelinfo_t* dpi, int16_t left, int16_t top, int16_t right, int16_t bottom, uint32_t color, uint8_t flags)
{
registers regs;
regs.ax = left;
regs.bx = right;
regs.cx = top;
regs.dx = bottom;
regs.ebp = color;
regs.edi = (uint32_t)dpi;
regs.si = flags;
call(0x004C58C7, regs);
}
// 0x004CD406
void invalidate_screen()
{
@ -227,4 +292,47 @@ namespace openloco::gfx
regs.bp = bottom;
call(0x004C5C69, regs);
}
void draw_image(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);
}
loco_global<uint8_t*, 0x0050B860> _50B860;
loco_global<uint32_t, 0x00E04324> _E04324;
void draw_image_solid(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;
_50B860 = palette;
_E04324 = 0x20000000;
registers regs;
regs.cx = x;
regs.dx = y;
regs.ebx = image;
regs.edi = (uint32_t)dpi;
call(0x00448D90, regs);
}
bool clip_drawpixelinfo(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;
}
}

View File

@ -69,6 +69,16 @@ namespace openloco::gfx
void load_g1();
void clear(drawpixelinfo_t& dpi, uint32_t fill);
void clear_single(drawpixelinfo_t& dpi, uint8_t paletteId);
int16_t clip_string(int16_t width, char* string);
void draw_string(
drawpixelinfo_t& dpi,
int16_t x,
int16_t y,
uint8_t colour,
const char* string);
void draw_string_494B3F(
drawpixelinfo_t& dpi,
int16_t x,
@ -84,7 +94,22 @@ namespace openloco::gfx
uint8_t colour,
string_id stringId,
const void* args);
void draw_string_centred_clipped(
drawpixelinfo_t& dpi,
int16_t x,
int16_t y,
int16_t width,
uint8_t colour,
string_id stringId,
const char* args);
void fill_rect(gfx::drawpixelinfo_t* dpi, int16_t left, int16_t top, int16_t right, int16_t bottom, uint32_t color);
void draw_rect(gfx::drawpixelinfo_t* dpi, int16_t x, int16_t y, uint16_t dx, uint16_t dy, uint32_t color);
void fill_rect_inset(gfx::drawpixelinfo_t* dpi, int16_t left, int16_t top, int16_t right, int16_t bottom, uint32_t color, uint8_t flags);
void draw_image(gfx::drawpixelinfo_t* dpi, int16_t x, int16_t y, uint32_t image);
void draw_image_solid(gfx::drawpixelinfo_t* dpi, int16_t x, int16_t y, uint32_t image, uint8_t palette_index);
void invalidate_screen();
void set_dirty_blocks(int32_t left, int32_t top, int32_t right, int32_t bottom);
bool clip_drawpixelinfo(gfx::drawpixelinfo_t** dst, gfx::drawpixelinfo_t* src, int16_t x, int16_t y, int16_t width, int16_t height);
}

View File

@ -19,7 +19,7 @@ using namespace openloco::interop;
namespace openloco::gui
{
loco_global<openloco::ui::widget[64], 0x00509c20> _mainWindowWidgets;
loco_global<openloco::ui::widget_t[64], 0x00509c20> _mainWindowWidgets;
// 0x00438A6C
void init()
@ -60,7 +60,7 @@ namespace openloco::gui
92,
ui::window_flags::flag_1 | ui::window_flags::flag_4 | ui::window_flags::flag_5 | ui::window_flags::flag_6,
(void*)0x004f9ec8);
window->widgets = (ui::widget*)0x00509df4;
window->widgets = (ui::widget_t*)0x00509df4;
window->enabled_widgets = (1 << 0) | (1 << 1) | (1 << 2) | (1 << 3) | (1 << 4) | (1 << 5);
windowmgr::init_scroll_widgets(window);
@ -77,7 +77,7 @@ namespace openloco::gui
28,
ui::window_flags::flag_1 | ui::window_flags::flag_4 | ui::window_flags::flag_5 | ui::window_flags::flag_6,
(void*)0x004f9f3c);
window->widgets = (ui::widget*)0x00509e58;
window->widgets = (ui::widget_t*)0x00509e58;
window->enabled_widgets = (1 << 0);
windowmgr::init_scroll_widgets(window);
@ -93,7 +93,7 @@ namespace openloco::gui
170,
ui::window_flags::flag_1,
(void*)0x004f9fb0);
window->widgets = (ui::widget*)0x00509e6c;
window->widgets = (ui::widget_t*)0x00509e6c;
window->enabled_widgets = (1 << 0);
windowmgr::init_scroll_widgets(window);
@ -115,7 +115,7 @@ namespace openloco::gui
28,
ui::window_flags::flag_1 | ui::window_flags::flag_4 | ui::window_flags::flag_5,
(void*)0x4fa180);
window->widgets = (ui::widget*)0x509c34;
window->widgets = (ui::widget_t*)0x509c34;
window->enabled_widgets = (1 << 0) | (1 << 1) | (1 << 2) | (1 << 3) | (1 << 4) | (1 << 5) | (1 << 6) | (1 << 7) | (1 << 8) | (1 << 9) | (1 << 10) | (1 << 11) | (1 << 12);
windowmgr::init_scroll_widgets(window);
@ -136,7 +136,7 @@ namespace openloco::gui
27,
ui::window_flags::flag_1 | ui::window_flags::flag_4 | ui::window_flags::flag_5,
(void*)0x4fa024);
window->widgets = (ui::widget*)0x509d08;
window->widgets = (ui::widget_t*)0x509d08;
window->enabled_widgets = (1 << 2) | (1 << 3) | (1 << 4);
window->var_854 = 0;
windowmgr::init_scroll_widgets(window);
@ -155,7 +155,7 @@ namespace openloco::gui
27,
ui::window_flags::flag_1 | ui::window_flags::flag_4 | ui::window_flags::flag_5,
(void*)0x4fa098);
window->widgets = (ui::widget*)0x509d5c;
window->widgets = (ui::widget_t*)0x509d5c;
window->enabled_widgets = (1 << 2) | (1 << 3) | (1 << 4) | (1 << 5) | (1 << 6) | (1 << 7);
window->var_854 = 0;
window->var_856 = 0;
@ -178,7 +178,7 @@ namespace openloco::gui
27,
ui::window_flags::flag_1 | ui::window_flags::flag_4 | ui::window_flags::flag_5,
(void*)0x4fa10c);
window->widgets = (ui::widget*)0x509de0;
window->widgets = (ui::widget_t*)0x509de0;
windowmgr::init_scroll_widgets(window);
if (skin != nullptr)

View File

@ -654,6 +654,15 @@ void openloco::interop::register_hooks()
return 0;
});
register_hook(
0x004CA4DF,
[](registers& regs) FORCE_ALIGN_ARG_POINTER -> uint8_t {
auto window = (ui::window*)regs.esi;
auto dpi = (gfx::drawpixelinfo_t*)regs.edi;
window->draw(dpi);
return 0;
});
ui::tooltip::register_hooks();
register_hook(

View File

@ -11,4 +11,13 @@ namespace openloco::stringmgr
{
return _strings[id];
}
void format_string(char* buffer, string_id id, void* args)
{
registers regs;
regs.eax = id;
regs.edi = (uint32_t)buffer;
regs.ecx = (uint32_t)args;
call(0x004958C6, regs);
}
}

View File

@ -15,4 +15,5 @@ namespace openloco
namespace openloco::stringmgr
{
const char* get_string(string_id id);
void format_string(char* buffer, string_id id, void* args);
}

View File

@ -48,12 +48,14 @@
</ClCompile>
<ClCompile Include="viewportmgr.cpp" />
<ClCompile Include="window.cpp" />
<ClCompile Include="widget.cpp" />
<ClCompile Include="intro.cpp" />
<ClCompile Include="objects\objectmgr.cpp" />
<ClCompile Include="scenariomgr.cpp" />
<ClCompile Include="tutorial.cpp" />
<ClCompile Include="windowmgr.cpp" />
<ClCompile Include="graphics\gfx.cpp" />
<ClCompile Include="graphics\colour.cpp" />
<ClCompile Include="ui.cpp" />
<ClCompile Include="progressbar.cpp" />
<ClCompile Include="interop\hook.cpp" />

604
src/openloco/widget.cpp Normal file
View File

@ -0,0 +1,604 @@
#include "widget.h"
#include "graphics/colours.h"
#include "input.h"
#include "interop/interop.hpp"
#include "window.h"
#include <cassert>
using namespace openloco::interop;
namespace openloco::ui::widget
{
static loco_global<int32_t, 0x112C876> _currentFontSpriteBase;
static loco_global<char[512], 0x0112CC04> stringFormatBuffer;
static loco_global<char[1], 0x112C826> _commonFormatArgs;
static loco_global<char[2], 0x005045F8> _strCheckmark;
void draw_11_c(gfx::drawpixelinfo_t* dpi, window* window, widget_t* widget, uint8_t colour, bool disabled, int16_t x, int16_t y, string_id string);
void draw_14(gfx::drawpixelinfo_t* dpi, widget_t* widget, uint8_t colour, bool disabled, int16_t x, int16_t y, string_id string);
static void sub_4CF3EB(const gfx::drawpixelinfo_t* dpi, const window* window, const widget_t* widget, int16_t x, int16_t y, uint8_t colour, int16_t width)
{
registers regs;
regs.eax = colour;
regs.bx = width;
regs.cx = x;
regs.dx = y;
regs.esi = (uint32_t)window;
regs.edi = (uint32_t)dpi;
regs.ebp = (uint32_t)widget;
call(0x004CF3EB, regs);
}
// 0x004CAB8E
static void draw_resize_handle(gfx::drawpixelinfo_t* dpi, window* window, widget_t* widget, uint8_t colour)
{
if (!(window->flags & window_flags::resizable))
{
return;
}
if (window->min_height == window->max_height || window->min_width == window->max_width)
{
return;
}
int16_t x = widget->right + window->x - 18;
int16_t y = widget->bottom + window->y - 18;
uint32_t image = 0x20000000 | 2305 | (colour << 19);
gfx::draw_image(dpi, x, y, image);
}
void sub_4CADE8(gfx::drawpixelinfo_t* dpi, const window* window, const widget_t* widget, uint8_t colour, bool enabled, bool disabled, bool activated)
{
int16_t x = widget->left + window->x;
int16_t y = widget->top + window->y;
uint32_t image = widget->image;
if (widget->type == widget_type::wt_6 || widget->type == widget_type::wt_7 || widget->type == widget_type::wt_8 || widget->type == widget_type::wt_4)
{
if (activated)
{
// TODO: remove image addition
image++;
}
}
if (disabled)
{
if (image & (1 << 31))
{
return;
}
image &= 0x7FFFF;
uint8_t c;
if (colour & openloco::colour::translucent_flag)
{
c = openloco::colour::get_shade(colour & 0x7F, 4);
gfx::draw_image_solid(dpi, x + 1, y + 1, image, c);
c = openloco::colour::get_shade(colour & 0x7F, 2);
gfx::draw_image_solid(dpi, x, y, image, c);
}
else
{
c = openloco::colour::get_shade(colour & 0x7F, 6);
gfx::draw_image_solid(dpi, x + 1, y + 1, image, c);
c = openloco::colour::get_shade(colour & 0x7F, 4);
gfx::draw_image_solid(dpi, x, y, image, c);
}
return;
}
if (image & (1 << 31))
{
// 0x4CAE5F
assert(false);
}
if (image & (1 << 30))
{
image |= colour << 19;
}
gfx::draw_image(dpi, x, y, image);
}
// 0x004CAB58
void draw_1(gfx::drawpixelinfo_t* dpi, window* window, widget_t* widget, uint16_t flags, uint8_t colour)
{
gfx::fill_rect_inset(dpi, window->x + widget->left, window->y + widget->top, window->x + widget->right, window->y + widget->bottom, colour, flags);
draw_resize_handle(dpi, window, widget, colour);
}
// 0x004CAAB9
void draw_2(gfx::drawpixelinfo_t* dpi, window* window, widget_t* widget, uint16_t flags, uint8_t colour)
{
gfx::drawpixelinfo_t* clipped = nullptr;
if (gfx::clip_drawpixelinfo(&clipped, dpi, widget->left + window->x, widget->top + window->y, widget->right - widget->left, 41))
{
uint32_t image;
if (window->flags & window_flags::flag_11)
{
image = 0x20000000 | 2322 | ((colour & 0x7F) << 19);
}
else
{
image = 0x20000000 | 2323 | ((colour & 0x7F) << 19);
}
gfx::draw_image(clipped, 0, 0, image);
}
uint8_t shade;
if (window->flags & window_flags::flag_11)
{
shade = openloco::colour::get_shade(colour, 3);
}
else
{
shade = openloco::colour::get_shade(colour, 1);
}
gfx::fill_rect(
dpi,
window->x + widget->right,
window->y + widget->top,
window->x + widget->right,
window->y + widget->top + 40,
shade);
draw_resize_handle(dpi, window, widget, colour);
}
void draw_3(gfx::drawpixelinfo_t* dpi, window* window, widget_t* widget, uint16_t flags, uint8_t colour, bool enabled, bool disabled, bool activated)
{
int16_t top, left, bottom, right;
top = window->y + widget->top;
left = window->x + widget->left;
right = window->x + widget->right;
bottom = window->y + widget->bottom;
if (activated)
{
flags |= 0x20;
}
if (widget->content == -2)
{
flags |= 0x10;
gfx::fill_rect_inset(dpi, left, top, right, bottom, colour, flags);
return;
}
if (window->flags & window_flags::flag_6)
{
gfx::fill_rect(dpi, left, top, right, bottom, 0x2000000 | 52);
}
gfx::fill_rect_inset(dpi, left, top, right, bottom, colour, flags);
if (widget->content == -1)
{
return;
}
sub_4CADE8(dpi, window, widget, colour, enabled, disabled, activated);
}
// 0x004CABFE
void draw_5(gfx::drawpixelinfo_t* dpi, window* window, widget_t* widget, uint16_t flags, uint8_t colour, bool enabled, bool disabled, bool activated)
{
if (widget->content == -1)
{
return;
}
if (!disabled)
{
widget::sub_4CADE8(dpi, window, widget, colour, enabled, disabled, activated);
return;
}
if (widget->type == widget_type::wt_8)
{
return;
}
if (widget->type != widget_type::wt_7)
{
widget::sub_4CADE8(dpi, window, widget, colour, enabled, disabled, activated);
return;
}
// TODO: Remove image addition
uint32_t image = widget->image + 2;
if (image & (1 << 30))
{
image |= colour << 19;
}
gfx::draw_image(dpi, window->x + widget->left, window->y + widget->top, image);
}
// 0x004CACD4
void draw_9(gfx::drawpixelinfo_t* dpi, window* window, widget_t* widget, uint16_t flags, uint8_t colour, bool enabled, bool disabled, bool activated, bool hovered)
{
if (!disabled && hovered)
{
// TODO: Fix mixed windows
widget::draw_3(dpi, window, widget, flags, colour, enabled, disabled, activated);
return;
}
int l = widget->left + window->x;
int t = widget->top + window->y;
int r = widget->right + window->x;
int b = widget->bottom + window->y;
if (activated)
{
flags |= 0x20;
if (widget->content == -2)
{
// 0x004CABE8
flags |= 0x10;
gfx::fill_rect_inset(dpi, l, t, r, b, colour, flags);
return;
}
gfx::fill_rect_inset(dpi, l, t, r, b, colour, flags);
}
if (widget->content == -1)
{
return;
}
widget::sub_4CADE8(dpi, window, widget, colour, enabled, disabled, activated);
}
// 0x004CAC5F
void draw_10(gfx::drawpixelinfo_t* dpi, window* window, widget_t* widget, uint16_t flags, uint8_t colour, bool enabled, bool disabled, bool activated, bool hovered)
{
if (widget->content == -1)
{
return;
}
uint32_t image = widget->image;
if (enabled)
{
// TODO: Remove image addition
image += 2;
if (!activated)
{
image -= 1;
if (!hovered)
{
image -= 1;
}
}
}
if (image & (1 << 30))
{
image |= colour << 19;
}
gfx::draw_image(dpi, window->x + widget->left, window->y + widget->top, image);
}
// 0x004CB164
void draw_11_a(gfx::drawpixelinfo_t* dpi, window* window, widget_t* widget, uint16_t flags, uint8_t colour, bool enabled, bool disabled, bool activated)
{
int left = window->x + widget->left;
int right = window->x + widget->right;
int top = window->y + widget->top;
int bottom = window->y + widget->bottom;
if (activated)
{
flags |= 0x20;
}
gfx::fill_rect_inset(dpi, left, top, right, bottom, colour, flags);
}
// 0x004CB1BE
void draw_13(gfx::drawpixelinfo_t* dpi, window* window, widget_t* widget, uint16_t flags, uint8_t colour, bool enabled, bool disabled, bool activated)
{
if (widget->content == -1)
{
return;
}
int16_t x = window->x + widget->left;
int16_t y = window->y + std::max<int16_t>(widget->top, (widget->top + widget->bottom) / 2 - 5);
string_id string = widget->text;
// TODO: Refactor out widget_t type check
if (widget->type == widget_type::wt_12)
{
if (activated)
{
// TODO: Remove string addition
string++;
}
}
if (widget->type == widget_type::wt_14)
{
draw_14(dpi, widget, colour, disabled, x, y, string);
}
else
{
draw_11_c(dpi, window, widget, colour, disabled, x, y, string);
}
}
// 0x004CB21D
void draw_11_c(gfx::drawpixelinfo_t* dpi, window* window, widget_t* widget, uint8_t colour, bool disabled, int16_t x, int16_t y, string_id string)
{
colour &= 0x7F;
if (disabled)
{
colour |= 0x40;
}
int16_t centreX = window->x + (widget->left + widget->right + 1) / 2 - 1;
int16_t width = widget->right - widget->left - 2;
gfx::draw_string_centred_clipped(*dpi, centreX, y, width, colour, string, _commonFormatArgs);
}
// 0x004CB263
void draw_14(gfx::drawpixelinfo_t* dpi, widget_t* widget, uint8_t colour, bool disabled, int16_t x, int16_t y, string_id string)
{
x = x + 1;
colour &= 0x7F;
if (disabled)
{
colour |= 0x40;
}
int width = widget->right - widget->left - 2;
gfx::draw_string_494BBF(*dpi, x, y, width, colour, string, _commonFormatArgs);
}
// 0x4CB2D6
void draw_15(gfx::drawpixelinfo_t* dpi, window* window, widget_t* widget, uint16_t flags, uint8_t colour, bool disabled)
{
if (widget->content < 0)
{
// FIXME: Probably wrong check, but I couldn't think of another meaning
return;
}
uint8_t c = 0xFD;
if (disabled)
{
c = colour | 0x40;
}
draw_string_494B3F(*dpi, window->x + widget->left + 1, window->y + widget->top, c, widget->text, _commonFormatArgs);
}
// 0x4CB29C
void draw_17(gfx::drawpixelinfo_t* dpi, window* window, widget_t* widget, uint16_t flags, uint8_t colour)
{
gfx::fill_rect_inset(dpi, window->x + widget->left, window->y + widget->top, window->x + widget->right, window->y + widget->bottom, colour, flags | 0x60);
}
// 0x004CA6AE
void draw_22_caption(gfx::drawpixelinfo_t* dpi, window* window, widget_t* widget, uint16_t flags, uint8_t colour)
{
int left = window->x + widget->left;
int right = window->x + widget->right;
int top = window->y + widget->top;
int bottom = window->y + widget->bottom;
gfx::fill_rect_inset(dpi, left, top, right, bottom, colour, flags | 0x60);
gfx::fill_rect(dpi, left + 1, top + 1, right - 1, bottom - 1, 0x2000000 | 46);
int16_t width = widget->right - widget->left - 4 - 10;
int16_t y = widget->top + window->y + 1;
int16_t x = widget->left + window->x + 2 + (width / 2);
gfx::draw_string_centred_clipped(*dpi, x, y, width, colour::white | 0x20, widget->text, _commonFormatArgs);
}
// 0x004CA750
void draw_23_caption(gfx::drawpixelinfo_t* dpi, window* window, widget_t* widget, uint16_t flags, uint8_t colour)
{
stringFormatBuffer[0] = (char)144;
stringmgr::format_string(&stringFormatBuffer[1], widget->text, _commonFormatArgs);
int16_t width = widget->right - widget->left - 4 - 14;
int16_t x = widget->left + window->x + 2 + (width / 2);
_currentFontSpriteBase = 224;
width = gfx::clip_string(width - 8, stringFormatBuffer);
x -= width / 2;
int16_t y = window->y + widget->top + 1;
sub_4CF3EB(dpi, window, widget, x, y, colour, width);
gfx::draw_string(*dpi, x, y, colour::black, stringFormatBuffer);
}
// 0x004CA7F6
void draw_24_caption(gfx::drawpixelinfo_t* dpi, window* window, widget_t* widget, uint16_t flags, uint8_t colour)
{
stringFormatBuffer[0] = (char)13;
stringmgr::format_string(&stringFormatBuffer[1], widget->text, _commonFormatArgs);
int16_t x = widget->left + window->x + 2;
int16_t width = widget->right - widget->left - 4 - 14;
x = x + (width / 2);
_currentFontSpriteBase = 224;
int16_t stringWidth = gfx::clip_string(width - 8, stringFormatBuffer);
x -= (stringWidth - 1) / 2;
gfx::draw_string(*dpi, x, window->y + widget->top + 1, 0x20, stringFormatBuffer);
}
// 0x004CA88B
void draw_25_caption(gfx::drawpixelinfo_t* dpi, window* window, widget_t* widget, uint16_t flags, uint8_t colour)
{
stringFormatBuffer[0] = (char)146;
stringmgr::format_string(&stringFormatBuffer[1], widget->text, _commonFormatArgs);
int16_t x = widget->left + window->x + 2;
int16_t width = widget->right - widget->left - 4 - 14;
x = x + (width / 2);
_currentFontSpriteBase = 224;
int16_t stringWidth = gfx::clip_string(width - 8, stringFormatBuffer);
x -= (stringWidth - 1) / 2;
gfx::draw_string(*dpi, x, window->y + widget->top + 1, 0x20, stringFormatBuffer);
}
// 0x004CB31C
void draw_26(gfx::drawpixelinfo_t* dpi, window* window, widget_t* widget, uint16_t flags, uint8_t colour, bool enabled, bool disabled, bool activated, bool hovered, int16_t scrollview_index)
{
uint16_t left = window->x + widget->left;
uint16_t top = window->y + widget->top;
uint16_t right = window->x + widget->right;
uint16_t bottom = window->y + widget->bottom;
gfx::fill_rect_inset(dpi, left, top, right, bottom, colour, flags | 0x60);
left++;
top++;
right--;
bottom--;
ui::scroll_area_t* scroll_area = &window->scroll_areas[scrollview_index];
_currentFontSpriteBase = 224;
if (scroll_area->flags & (1 << 0))
{
// draw horizontal scrollbar
bottom -= 11;
}
if (scroll_area->flags & (1 << 4))
{
// draw vertical scrollbar
right -= 11;
}
gfx::drawpixelinfo_t cropped = *dpi;
bottom++;
right++;
if (left > cropped.x)
{
int offset = left - cropped.x;
cropped.width -= offset;
cropped.x = left;
cropped.pitch += offset;
cropped.bits += offset;
}
int16_t bp = cropped.x + cropped.width - right;
if (bp > 0)
{
cropped.width -= bp;
cropped.pitch += bp;
}
if (top > cropped.y)
{
int offset = top - cropped.y;
cropped.height -= offset;
cropped.y = top;
int aex = (cropped.pitch + cropped.width) * offset;
cropped.bits += aex;
}
bp = cropped.y + cropped.height - bottom;
if (bp > 0)
{
cropped.height -= bp;
}
if (cropped.width > 0 && cropped.height > 0)
{
cropped.x -= left - scroll_area->h_left;
cropped.y -= top - scroll_area->v_top;
{
registers regs;
regs.ax = scrollview_index;
regs.esi = (int32_t)window;
regs.edi = (int32_t)&cropped;
call(window->event_handlers->event_28, regs);
}
}
}
// 0x004CB00B
void draw_27_checkbox(gfx::drawpixelinfo_t* dpi, window* window, widget_t* widget, uint16_t flags, uint8_t colour, bool enabled, bool disabled, bool activated)
{
if (enabled)
{
gfx::fill_rect_inset(
dpi,
window->x + widget->left,
window->y + widget->top,
window->x + widget->left + 9,
window->y + widget->bottom - 1,
colour,
flags | 0x60);
}
if (activated)
{
_currentFontSpriteBase = 224;
gfx::draw_string(*dpi, window->x + widget->left, window->y + widget->top, colour & 0x7F, _strCheckmark);
}
}
// 0x004CB080
void draw_27_label(gfx::drawpixelinfo_t* dpi, window* window, widget_t* widget, uint16_t flags, uint8_t colour, bool disabled)
{
if (widget->content == -1)
{
return;
}
colour &= 0x7F;
if (disabled)
{
colour |= 0x40;
}
gfx::draw_string_494B3F(*dpi, window->x + widget->left + 14, window->y + widget->top, colour, widget->text, _commonFormatArgs);
}
// 0x004CA679
void draw_29(gfx::drawpixelinfo_t* dpi, window* window, widget_t* widget)
{
int left = window->x + widget->left;
int right = window->x + widget->right;
int top = window->y + widget->top;
int bottom = window->y + widget->bottom;
gfx::fill_rect(dpi, left, top, right, bottom, colour::get_shade(colour::black, 5));
}
}

37
src/openloco/widget.h Normal file
View File

@ -0,0 +1,37 @@
#pragma once
#include "graphics/gfx.h"
#include "localisation/stringmgr.h"
#include "window.h"
#include <cstdint>
namespace openloco::ui::widget
{
void sub_4CADE8(gfx::drawpixelinfo_t* dpi, const window* window, const widget_t* widget, uint8_t colour, bool enabled, bool disabled, bool activated);
void draw_1(gfx::drawpixelinfo_t* dpi, window* window, widget_t* widget, uint16_t flags, uint8_t colour);
void draw_2(gfx::drawpixelinfo_t* dpi, window* window, widget_t* widget, uint16_t flags, uint8_t colour);
void draw_3(gfx::drawpixelinfo_t* dpi, window* window, widget_t* widget, uint16_t flags, uint8_t colour, bool enabled, bool disabled, bool activated);
void draw_5(gfx::drawpixelinfo_t* dpi, window* window, widget_t* widget, uint16_t flags, uint8_t colour, bool enabled, bool disabled, bool activated);
void draw_9(gfx::drawpixelinfo_t* dpi, window* window, widget_t* widget, uint16_t flags, uint8_t colour, bool enabled, bool disabled, bool activated, bool hovered);
void draw_10(gfx::drawpixelinfo_t* dpi, window* window, widget_t* widget, uint16_t flags, uint8_t colour, bool enabled, bool disabled, bool activated, bool hovered);
void draw_11_a(gfx::drawpixelinfo_t* dpi, window* window, widget_t* widget, uint16_t flags, uint8_t colour, bool enabled, bool disabled, bool activated);
void draw_13(gfx::drawpixelinfo_t* dpi, window* window, widget_t* widget, uint16_t flags, uint8_t colour, bool enabled, bool disabled, bool activated);
void draw_15(gfx::drawpixelinfo_t* dpi, window* window, widget_t* widget, uint16_t flags, uint8_t colour, bool disabled);
void draw_17(gfx::drawpixelinfo_t* dpi, window* window, widget_t* widget, uint16_t flags, uint8_t colour);
void draw_22_caption(gfx::drawpixelinfo_t* dpi, window* window, widget_t* widget, uint16_t flags, uint8_t colour);
void draw_23_caption(gfx::drawpixelinfo_t* dpi, window* window, widget_t* widget, uint16_t flags, uint8_t colour);
void draw_24_caption(gfx::drawpixelinfo_t* dpi, window* window, widget_t* widget, uint16_t flags, uint8_t colour);
void draw_25_caption(gfx::drawpixelinfo_t* dpi, window* window, widget_t* widget, uint16_t flags, uint8_t colour);
void draw_26(gfx::drawpixelinfo_t* dpi, window* window, widget_t* widget, uint16_t flags, uint8_t colour, bool enabled, bool disabled, bool activated, bool hovered, int16_t scrollview_index);
void draw_27_checkbox(gfx::drawpixelinfo_t* dpi, window* window, widget_t* widget, uint16_t flags, uint8_t colour, bool enabled, bool disabled, bool activated);
void draw_27_label(gfx::drawpixelinfo_t* dpi, window* window, widget_t* widget, uint16_t flags, uint8_t colour, bool disabled);
void draw_29(gfx::drawpixelinfo_t* dpi, window* window, widget_t* widget);
}

View File

@ -1,53 +1,251 @@
#include "window.h"
#include "console.h"
#include "graphics/colours.h"
#include "input.h"
#include "interop/interop.hpp"
#include "widget.h"
#include <cassert>
#include <cinttypes>
using namespace openloco;
using namespace openloco::interop;
using namespace openloco::ui;
template<typename T>
static bool is_interop_event(T e)
namespace openloco::ui
{
return (uint32_t)e < 0x004D7000;
}
// 0x004CA4BD
void window::invalidate()
{
registers regs;
regs.esi = (int32_t)this;
call(0x004CA4BD, regs);
}
void window::sub_4CA17F()
{
registers regs;
regs.esi = (int32_t)this;
call(0x004CA17F, regs);
}
bool window::call_tooltip(int16_t widget_index)
{
registers regs;
regs.ax = widget_index;
regs.esi = (int32_t)this;
call((int32_t)this->event_handlers->tooltip, regs);
return regs.ax != (int16_t)string_ids::null;
}
void window::call_prepare_draw()
{
if (event_handlers->prepare_draw != nullptr)
template<typename T>
static bool is_interop_event(T e)
{
if (is_interop_event(event_handlers->prepare_draw))
return (uint32_t)e < 0x004D7000;
}
// 0x004CA4BD
void window::invalidate()
{
registers regs;
regs.esi = (int32_t)this;
call(0x004CA4BD, regs);
}
void window::sub_4CA17F()
{
registers regs;
regs.esi = (int32_t)this;
call(0x004CA17F, regs);
}
bool window::call_tooltip(int16_t widget_index)
{
registers regs;
regs.ax = widget_index;
regs.esi = (int32_t)this;
call((int32_t)this->event_handlers->tooltip, regs);
return regs.ax != (int16_t)string_ids::null;
}
void window::call_prepare_draw()
{
if (event_handlers->prepare_draw != nullptr)
{
registers regs;
regs.esi = (int32_t)this;
call((int32_t)this->event_handlers->prepare_draw, regs);
if (is_interop_event(event_handlers->prepare_draw))
{
registers regs;
regs.esi = (int32_t)this;
call((int32_t)this->event_handlers->prepare_draw, regs);
}
else
{
event_handlers->prepare_draw(this);
}
}
else
}
// 0x004CA4DF
void window::draw(gfx::drawpixelinfo_t* dpi)
{
if ((this->flags & window_flags::flag_4) && !(this->flags & window_flags::flag_5))
{
event_handlers->prepare_draw(this);
gfx::fill_rect(dpi, this->x, this->y, this->x + this->width - 1, this->y + this->height - 1, 0x2000000 | 52);
}
uint64_t pressed_widget = 0;
if (input::state() == input::input_state::dropdown_active || input::state() == input::input_state::widget_pressed)
{
if (this->type == addr<0x0052336F, window_type>() && this->number == addr<0x00523370, uint16_t>())
{
if (input::has_flag((input::input_flags)(1 << 0)))
{
pressed_widget = 1u << addr<0x00523372, uint32_t>();
}
}
}
uint64_t tool_widget = 0;
if (this->type == addr<0x00523392, window_type>() && this->number == addr<0x00523390, uint16_t>())
{
tool_widget = 1u << addr<0x00523394, uint32_t>();
}
uint64_t hovered_widget = 0;
if (this->type == addr<0x005233A8, window_type>() && this->number == addr<0x005233AA, uint16_t>())
{
hovered_widget = 1u << addr<0x005233AC, uint16_t>();
}
int scrollviewIndex = 0;
for (int widgetIndex = 0; widgetIndex < 64; widgetIndex++)
{
auto widget = &this->widgets[widgetIndex];
auto type = (widget_type)widget->type;
if (type == widget_type::end)
{
break;
}
if ((this->flags & window_flags::flag_5) == 0)
{
// Check if widget is outside the draw region
if (this->x + widget->left >= dpi->x + dpi->width && this->x + widget->right < dpi->x)
{
if (this->y + widget->top >= dpi->y + dpi->height && this->y + widget->bottom < dpi->y)
{
continue;
}
}
}
uint16_t flags = 0;
if (widget->colour == 0 && this->flags & window_flags::flag_11)
{
flags = 0x80;
}
uint8_t colour = this->colours[widget->colour];
bool enabled = (this->enabled_widgets & (1 << widgetIndex)) != 0;
bool disabled = (this->disabled_widgets & (1 << widgetIndex)) != 0;
bool activated = (this->activated_widgets & (1 << widgetIndex)) != 0;
activated |= (pressed_widget & (1 << widgetIndex)) != 0;
activated |= (tool_widget & (1 << widgetIndex)) != 0;
bool hovered = (hovered_widget & (1 << widgetIndex)) != 0;
switch (type)
{
case widget_type::none:
case widget_type::end:
break;
case widget_type::panel:
widget::draw_1(dpi, this, widget, flags, colour);
break;
case widget_type::frame:
widget::draw_2(dpi, this, widget, flags, colour);
break;
case widget_type::wt_3:
widget::draw_3(dpi, this, widget, flags, colour, enabled, disabled, activated);
break;
case widget_type::wt_4:
assert(false); // Unused
break;
case widget_type::wt_5:
case widget_type::wt_6:
case widget_type::wt_7:
case widget_type::wt_8:
widget::draw_5(dpi, this, widget, flags, colour, enabled, disabled, activated);
break;
case widget_type::wt_9:
widget::draw_9(dpi, this, widget, flags, colour, enabled, disabled, activated, hovered);
break;
case widget_type::wt_10:
widget::draw_10(dpi, this, widget, flags, colour, enabled, disabled, activated, hovered);
break;
case widget_type::wt_11:
case widget_type::wt_12:
case widget_type::wt_14:
if (type == widget_type::wt_12)
{
assert(false); // Unused
}
widget::draw_11_a(dpi, this, widget, flags, colour, enabled, disabled, activated);
widget::draw_13(dpi, this, widget, flags, colour, enabled, disabled, activated);
break;
case widget_type::wt_13:
widget::draw_13(dpi, this, widget, flags, colour, enabled, disabled, activated);
break;
case widget_type::wt_15:
case widget_type::wt_16:
assert(false); // Unused
break;
case widget_type::wt_17:
case widget_type::wt_18:
case widget_type::wt_19:
widget::draw_17(dpi, this, widget, flags, colour);
widget::draw_15(dpi, this, widget, flags, colour, disabled);
break;
case widget_type::wt_20:
case widget_type::wt_21:
assert(false); // Unused
break;
case widget_type::caption_22:
widget::draw_22_caption(dpi, this, widget, flags, colour);
break;
case widget_type::caption_23:
widget::draw_23_caption(dpi, this, widget, flags, colour);
break;
case widget_type::caption_24:
widget::draw_24_caption(dpi, this, widget, flags, colour);
break;
case widget_type::caption_25:
widget::draw_25_caption(dpi, this, widget, flags, colour);
break;
case widget_type::scrollview:
widget::draw_26(dpi, this, widget, flags, colour, enabled, disabled, activated, hovered, scrollviewIndex);
scrollviewIndex++;
break;
case widget_type::checkbox:
widget::draw_27_checkbox(dpi, this, widget, flags, colour, enabled, disabled, activated);
widget::draw_27_label(dpi, this, widget, flags, colour, disabled);
break;
case widget_type::wt_28:
assert(false); // Unused
widget::draw_27_label(dpi, this, widget, flags, colour, disabled);
break;
case widget_type::wt_29:
assert(false); // Unused
widget::draw_29(dpi, this, widget);
break;
}
}
if (this->flags & window_flags::white_border_mask)
{
gfx::fill_rect_inset(
dpi,
this->x,
this->y,
this->x + this->width - 1,
this->y + this->height - 1,
colour::white,
0x10);
}
}
}

View File

@ -7,37 +7,88 @@
namespace openloco::ui
{
enum class window_type : uint8_t;
enum class widget_type : uint8_t;
struct window;
#pragma pack(push, 1)
struct widget
struct widget_t
{
uint8_t type; // 0x00
uint8_t colour; // 0x01
int16_t left; // 0x02
int16_t right; // 0x04
int16_t top; // 0x06
int16_t bottom; // 0x08
widget_type type; // 0x00
uint8_t colour; // 0x01
int16_t left; // 0x02
int16_t right; // 0x04
int16_t top; // 0x06
int16_t bottom; // 0x08
union
{
uint32_t image;
string_id text;
uint32_t content;
int32_t content;
};
string_id tooltip; // 0x0E
};
enum class widget_type : uint8_t
{
none = 0,
panel = 1,
frame = 2,
wt_3,
wt_4,
wt_5,
wt_6,
wt_7,
wt_8,
wt_9,
wt_10,
wt_11,
wt_12,
wt_13,
wt_14,
wt_15,
wt_16,
wt_17,
wt_18,
wt_19,
wt_20,
wt_21,
caption_22,
caption_23,
caption_24,
caption_25,
scrollview = 26,
checkbox = 27,
wt_28,
wt_29,
end = 30,
};
struct scroll_area_t
{
uint16_t flags; // 0x00
uint16_t h_left; // 0x02
uint16_t h_right; // 0x04
uint16_t h_thumb_left; // 0x06
uint16_t h_thumb_right; // 0x08
uint16_t v_top; // 0x0A
uint16_t v_bottom; // 0x0C
uint16_t v_thumb_top; // 0x0E
uint16_t v_thumb_bottom; // 0x10
};
namespace window_flags
{
constexpr uint16_t flag_0 = 1 << 0;
constexpr uint16_t flag_1 = 1 << 1;
constexpr uint16_t flag_4 = 1 << 4;
constexpr uint16_t flag_5 = 1 << 5;
constexpr uint16_t flag_6 = 1 << 6;
constexpr uint16_t flag_7 = 1 << 7;
constexpr uint16_t flag_9 = 1 << 9;
constexpr uint16_t flag_12 = 1 << 12;
constexpr uint32_t flag_0 = 1 << 0;
constexpr uint32_t flag_1 = 1 << 1;
constexpr uint32_t flag_4 = 1 << 4;
constexpr uint32_t flag_5 = 1 << 5;
constexpr uint32_t flag_6 = 1 << 6;
constexpr uint32_t flag_7 = 1 << 7;
constexpr uint32_t resizable = 1 << 9;
constexpr uint32_t flag_11 = 1 << 11;
constexpr uint32_t flag_12 = 1 << 12;
constexpr uint32_t white_border_mask = (1 << 17) | (1 << 18);
}
struct window_event_list
@ -114,20 +165,23 @@ namespace openloco::ui
window_event_list* event_handlers; // 0x00
ui::viewport* viewport; // 0x04
uint8_t pad_08[0x04]; // 0x08
uint32_t enabled_widgets; // 0x0C
uint8_t pad_10[0x2C - 0x10];
widget* widgets; // 0x2C
uint16_t x; // 0x30
uint16_t y; // 0x32
uint16_t width; // 0x34
uint16_t height; // 0x36
uint16_t min_width; // 0x38
uint16_t max_width; // 0x3a
uint16_t min_height; // 0x3c
uint16_t max_height; // 0x3e
uint16_t number;
uint32_t flags;
uint8_t pad_46[0x83E - 0x46];
uint64_t enabled_widgets; // 0x0C
uint64_t disabled_widgets; // 0x14
uint64_t activated_widgets; // 0x1C
uint8_t pad_24[0x2C - 0x24];
widget_t* widgets; // 0x2C
uint16_t x; // 0x30
uint16_t y; // 0x32
uint16_t width; // 0x34
uint16_t height; // 0x36
uint16_t min_width; // 0x38
uint16_t max_width; // 0x3a
uint16_t min_height; // 0x3c
uint16_t max_height; // 0x3e
uint16_t number; // 0x40
uint32_t flags; // 0x42
scroll_area_t scroll_areas[3]; // 0x46
uint8_t pad_7C[0x83E - 0x7C];
uint16_t var_83E;
uint8_t pad_840[0x846 - 0x840];
uint16_t var_846;
@ -154,6 +208,7 @@ namespace openloco::ui
void invalidate();
void sub_4CA17F();
void draw(openloco::gfx::drawpixelinfo_t* dpi);
bool call_tooltip(int16_t widget_index); // 23
void call_prepare_draw(); // 26

View File

@ -85,10 +85,10 @@ namespace openloco::ui::windows
utility::strcpy_safe(_text_input_buffer, baseName.c_str());
sub_446A93();
auto window = windowmgr::create_window_centred(window_type::prompt_browse, 500, 380, ui::window_flags::flag_1 | ui::window_flags::flag_9 | ui::window_flags::flag_12, (void*)0x004FB308);
auto window = windowmgr::create_window_centred(window_type::prompt_browse, 500, 380, ui::window_flags::flag_1 | ui::window_flags::resizable | ui::window_flags::flag_12, (void*)0x004FB308);
if (window != nullptr)
{
window->widgets = (widget*)0x0050AD58;
window->widgets = (widget_t*)0x0050AD58;
window->enabled_widgets = (1 << 2) | (1 << 4) | (1 << 6);
window->sub_4CA17F();
addr<0x01136FA2, int16_t>() = -1;

View File

@ -39,7 +39,7 @@ namespace openloco::ui::windows
auto window = windowmgr::create_window_centred(window_type::prompt_ok_cancel, 280, 92, ui::window_flags::flag_12 | ui::window_flags::flag_1, (void*)0x004FB37C);
if (window != nullptr)
{
window->widgets = (widget*)0x0050AE00;
window->widgets = (widget_t*)0x0050AE00;
window->enabled_widgets = (1 << 2) | (1 << 3) | (1 << 4);
window->sub_4CA17F();
window->colours[0] = colour::translucent(colour::salmon_pink);

View File

@ -9,8 +9,8 @@ using namespace openloco::interop;
namespace openloco::ui::windows
{
static widget widgets[] = {
{ 0x1E, 0, 0, 0, 0, 0, { 0 }, 0 }
static widget_t widgets[] = {
{ widget_type::end, 0, 0, 0, 0, 0, { 0 }, 0 }
};
static ui::window_event_list _events;

View File

@ -19,7 +19,7 @@ namespace openloco::ui::tooltip
static loco_global<int16_t, 0x00523384> _523384;
static loco_global<uint16_t, 0x0052338C> _tooltipNotShownTicks;
static loco_global<ui::widget[1], 0x005234CC> _widgets;
static loco_global<ui::widget_t[1], 0x005234CC> _widgets;
static loco_global<int32_t, 0x112C876> gCurrentFontSpriteBase;
static loco_global<char[512], 0x0112CC04> byte_112CC04;