#pragma once // cUI_Button2 (vtable 0x895308, 0xbc bytes, constructor 0x72a180, plain ret). // Windows set their buttons up as cUI_Options 0x7184e0 does: images // (+0x54..+0x87), sounds (0x72acf0 -> +0x90..+0x9f), caption (0x72ad60 (text // id, font 1) -> +0xa4 / +0xa0), caption offset (0x72ad40 -> +0xb0), name // (0x732090). #include #include #include #include "game/ui/tabs.h" #include "game/ui/text.h" namespace game { constexpr size_t kButtonSize = 0xbc; constexpr size_t kButtonLook = 0x54; constexpr size_t kButtonLookEnd = 0x88; constexpr size_t kButtonSounds = 0x90; constexpr size_t kButtonSoundsEnd = 0xa0; constexpr size_t kButtonCaptionOffset = 0xb0; // x, y (int16) constexpr uint32_t kButtonFlags = 5; // 0x731e70, as 0x7184e0 passes // Control vtable entries: vf10 handles an event (ret 4), vf14 renders (ret 4). constexpr size_t kVfHandleEvent = 0x10 / 4; constexpr size_t kVfRender = 0x14 / 4; // A new button with `model`'s look and sounds at `rect` (relative to the // parent it is added to, or the screen frame without one), captioned with text // id `caption`. inline uint8_t* NewButtonLike(const uint8_t* model, const UiRect& rect, uint32_t caption, const char* name) { void* memory = GameNew(kButtonSize); if (!memory) { return nullptr; } using ConstructFn = uint8_t*(__fastcall*)(void*, void*); uint8_t* button = reinterpret_cast(0x0072a180)(memory, nullptr); using FlagsFn = void(__fastcall*)(void*, void*, uint32_t); reinterpret_cast(0x00731e70)(button, nullptr, kButtonFlags); std::copy(model + kButtonLook, model + kButtonLookEnd, button + kButtonLook); std::copy(model + kButtonSounds, model + kButtonSoundsEnd, button + kButtonSounds); std::copy(model + kButtonCaptionOffset, model + kButtonCaptionOffset + 4, button + kButtonCaptionOffset); *reinterpret_cast(button + kControlRect) = rect; using CaptionFn = void(__fastcall*)(void*, void*, uint32_t, uint32_t); reinterpret_cast(0x0072ad60)(button, nullptr, caption, 1); using NameFn = void(__fastcall*)(void*, void*, const char*); reinterpret_cast(0x00732090)(button, nullptr, name); return button; } inline int ControlHandleEvent(void* control, void* event) { using Fn = int(__fastcall*)(void*, void*, void*); return reinterpret_cast((*static_cast(control))[kVfHandleEvent])(control, nullptr, event); } inline int ControlRender(void* control, void* device) { using Fn = int(__fastcall*)(void*, void*, void*); return reinterpret_cast((*static_cast(control))[kVfRender])(control, nullptr, device); } inline bool Inside(const UiRect& rect, int x, int y) { return x >= rect.x && x < rect.x + rect.w && y >= rect.y && y < rect.y + rect.h; } } // namespace game