Spellforce-Spell-framework
Loading...
Searching...
No Matches
sf_ui_wrappers.c
Go to the documentation of this file.
1#include <windows.h>
2#include <stdio.h>
3#include <stdlib.h>
4#include <string.h>
5
6#include "sf_ui_wrappers.h"
7#include "sf_wrappers.h"
8#include "sf_hooks.h"
9
10#include "hooks/sf_menu_hook.h"
15
16void wrap_text(const char *input, char *output, size_t max_width)
17{
18 size_t input_len = strlen(input);
19 size_t current_line_width = 0;
20 size_t output_index = 0;
21
22 for (size_t i = 0; i < input_len; ++i)
23 {
24 output[output_index++] = input[i];
25 current_line_width++;
26
27 if (current_line_width >= max_width)
28 {
29 size_t break_point = output_index;
30 while (break_point > 0 && !isspace((unsigned char)output[break_point - 1]))
31 {
32 break_point--;
33 }
34
35 if (break_point > 0)
36 {
37 output[break_point - 1] = '\n';
38 current_line_width = output_index - break_point;
39 }
40 else
41 {
42 output[output_index++] = '\n';
43 current_line_width = 0;
44 }
45 }
46 }
47
48 output[output_index] = '\0';
49}
50
51
56void __thiscall SFStringConstructor(SF_String *_this)
57{
58 _this->raw_data = nullptr;
59 _this->char_data = nullptr;
60 _this->str_length = 0x0;
61 _this->str_length_char = 0x0;
62}
63
70SF_String * __thiscall SFStringConstructor_char(SF_String *_this, const char *char_string)
71{
73 uint32_t length = 0;
74 if (char_string != 0)
75 {
76 //not sure here, might need to use internal implementation?
77 length = strlen(char_string);
78 }
79 uiAPI.SFStringSetLength(_this, length);
80 if (length != 0)
81 {
82 mbstowcs(_this->raw_data, char_string, length);
83 }
84 _this->str_length = length;
85 return _this;
86}
87
94void __thiscall updateLabelText(CMnuLabel *label, const char *text)
95{
96 if (!label || !text)
97 {
98 return;
99 }
100
101 SF_String string_obj;
102 SF_String *sf_string = g_create_sf_string(&string_obj, text);
103 uiAPI.menuLabelSetString(label, sf_string);
104 g_destroy_sf_string(sf_string);
105}
106
107
111
115static bool is_mod_list_shown = false;
116static bool does_mod_list_exist = false;
117
121#define MOD_LIST_ROWS 11
122
123#define MOD_ROW_X 108
124#define MOD_ROW_Y_START 40
125#define MOD_ROW_PITCH 44
126#define MOD_ROW_WIDTH 227
127#define MOD_ROW_HEIGHT 36
128#define MOD_NAV_Y 540
129
130#define MOD_INFO_PANEL_WIDTH 443
131#define MOD_INFO_TITLE_Y 34
132#define MOD_INFO_TITLE_H 34
133#define MOD_INFO_AUTHOR_Y 62
134#define MOD_INFO_AUTHOR_H 28
135#define MOD_INFO_TYPE_Y 98
136#define MOD_INFO_TYPE_H 28
137#define MOD_INFO_DESC_Y 136
138#define MOD_INFO_DESC_H 248
139#define MOD_INFO_ERROR_Y 396
140#define MOD_INFO_ERROR_H 200
141
149#define MOD_GLYPH_WIDTH 8
150
152#define MOD_TITLE_RGB 0.85f, 0.64f, 0.12f
153#define MOD_AUTHOR_RGB 0.75f, 0.75f, 0.75f
154#define MOD_TYPE_RGB 0.45f, 0.72f, 0.95f
155#define MOD_ERROR_RGB 1.0f, 0.0f, 0.0f
156
157static CMnuSmpButton *s_mod_buttons[MOD_LIST_ROWS];
158
160static int s_mod_row_target[MOD_LIST_ROWS];
161
162
167{
168 mod_info_page = nullptr;
169 mod_container = nullptr;
170 mod_list = nullptr;
171 mod_list_title = nullptr;
172 left_nav = nullptr;
173 right_nav = nullptr;
174
175 mod_struct.title_label = nullptr;
176 mod_struct.author_label = nullptr;
177 mod_struct.desc_label = nullptr;
178 mod_struct.page_label = nullptr;
179 mod_struct.error_label = nullptr;
180 mod_struct.type_label = nullptr;
181 mod_struct.toggle = 0;
182 mod_struct.index = 0;
183
184 for (int row = 0; row < MOD_LIST_ROWS; row++)
185 {
186 s_mod_buttons[row] = nullptr;
187 s_mod_row_target[row] = -1;
188 }
189 is_mod_list_shown = false;
190 does_mod_list_exist = false;
191}
192
193
195{
196 return (int)get_registered_mods().size();
197}
198
199int calculate_total_pages(int total_unique_mods, int mods_per_page)
200{
201 int total_pages = (total_unique_mods + mods_per_page - 1) / mods_per_page;
202 return (total_pages == 0) ? 1 : total_pages;
203}
204
205int normalize_page_index(int page, int total_pages)
206{
207 return (page + total_pages) % total_pages;
208}
209
210void prepare_mod_title(SFMod *parent_mod, char *mod_title, size_t buffer_size)
211{
212 snprintf(mod_title, buffer_size, "%s - %s",
213 parent_mod->mod_id,
214 parent_mod->mod_version);
215}
216
217void prepare_mod_author(SFMod *parent_mod, char *mod_author, size_t buffer_size)
218{
219 snprintf(mod_author, buffer_size, "by %s",
220 (parent_mod->mod_author[0] != '\0') ? parent_mod->mod_author : "Unknown");
221}
222
223#define MOD_INFO_WRAP 50
224
225void prepare_mod_description(SFMod *parent_mod, char *mod_description, size_t description_buffer_size,
226 char *wrapped_description, size_t wrapped_buffer_size)
227{
228 snprintf(mod_description, description_buffer_size, "%s",
229 parent_mod->mod_description);
230
231 wrap_text(mod_description, wrapped_description, MOD_INFO_WRAP);
232}
233
234void prepare_mod_page_info(int page, int total_pages, char *mod_page_info, size_t buffer_size)
235{
236 snprintf(mod_page_info, buffer_size, "(%d / %d)",
237 (page + 1), total_pages);
238}
239
240void prepare_mod_error_info(SFMod *parent_mod, char *mod_error_info, size_t error_buffer_size,
241 char *wrapped_error_info, size_t wrapped_buffer_size)
242{
243 if (parent_mod->mod_errors[0] != 0)
244 {
245 snprintf(mod_error_info, error_buffer_size, "%s",
246 parent_mod->mod_errors);
247 wrap_text(mod_error_info, wrapped_error_info, MOD_INFO_WRAP);
248 }
249 else
250 {
251 snprintf(mod_error_info, error_buffer_size, " ");
252 snprintf(wrapped_error_info, wrapped_buffer_size, " ");
253 }
254}
255
256
258static const RegisteredMod *get_mod_at(int index)
259{
260 if (index < 0)
261 {
262 return nullptr;
263 }
264
265 int current = 0;
266 for (const RegisteredMod &entry : get_registered_mods())
267 {
268 if (current == index)
269 {
270 return &entry;
271 }
272 current++;
273 }
274 return nullptr;
275}
276
280void set_centred_label_text(CMnuLabel *label, const char *text, int panel_width,
281 int y, int height)
282{
283 if (label == nullptr || text == nullptr)
284 {
285 return;
286 }
287
288 g_set_label_flags(label, 0xa); //just centers position
289 uiAPI.updateLabelText(label, text);
290}
291
293void apply_label_colour(CMnuLabel *label, float red, float green, float blue)
294{
295 if (label == nullptr)
296 {
297 return;
298 }
299
300 uiAPI.setLabelColour(label, red, green, blue, '\0'); //normal
301 uiAPI.setLabelColour(label, red, green, blue, '\x01'); //hover
302}
303
305static void set_button_label(CMnuSmpButton *button, const char *text)
306{
307 if (button == nullptr)
308 {
309 return;
310 }
311
312 SF_String label;
313 SF_String *label_string = g_create_sf_string(&label, text);
314 uiAPI.CMnuBaseSetName((CMnuBase *)button, label_string);
315 uiAPI.setButtonName(button, label_string);
316 g_destroy_sf_string(label_string);
317}
318
324void show_mod_details(int mod_index)
325{
326 const RegisteredMod *entry = get_mod_at(mod_index);
327 if (entry == nullptr)
328 {
329 return;
330 }
331
332 SFMod *mod = entry->mod;
333
334 char mod_title[512] = {0};
335 prepare_mod_title(mod, mod_title, sizeof(mod_title));
336
337 char mod_author[256] = {0};
338 prepare_mod_author(mod, mod_author, sizeof(mod_author));
339
340 char mod_description[512] = {0};
341 char wrapped_description[1024] = {0};
342 prepare_mod_description(mod, mod_description, sizeof(mod_description),
343 wrapped_description, sizeof(wrapped_description));
344
345 char mod_error_info[512] = {0};
346 char wrapped_error_info[1024] = {0};
347 prepare_mod_error_info(mod, mod_error_info, sizeof(mod_error_info),
348 wrapped_error_info, sizeof(wrapped_error_info));
349
351 set_centred_label_text(mod_struct.author_label, mod_author,
357}
358
363{
364 int total_mods = calculate_total_unique_mods();
365 int total_pages = calculate_total_pages(total_mods, MOD_LIST_ROWS);
366
367 page = normalize_page_index(page, total_pages);
368 mod_struct.index = page;
369
370 const int first_mod = page * MOD_LIST_ROWS;
371
372 for (int row = 0; row < MOD_LIST_ROWS; row++)
373 {
374 int mod_index = first_mod + row;
375 const RegisteredMod *entry = (mod_index < total_mods) ? get_mod_at(mod_index) : nullptr;
376
377 if (entry != nullptr)
378 {
379 s_mod_row_target[row] = mod_index;
380 set_button_label(s_mod_buttons[row], entry->mod->mod_id);
381 }
382 else
383 {
384 s_mod_row_target[row] = -1;
385 set_button_label(s_mod_buttons[row], "");
386 }
387 }
388
389 char mod_page_info[48] = {0};
390 prepare_mod_page_info(page, total_pages, mod_page_info, sizeof(mod_page_info));
391 if (mod_struct.page_label != nullptr)
392 {
393 uiAPI.updateLabelText(mod_struct.page_label, mod_page_info);
394 }
395}
396
398void __thiscall on_mod_selected(CMnuSmpButton *button)
399{
400 if (!does_mod_list_exist)
401 {
402 return;
403 }
404
405 for (int row = 0; row < MOD_LIST_ROWS; row++)
406 {
407 if (s_mod_buttons[row] == button)
408 {
409 if (s_mod_row_target[row] >= 0)
410 {
411 show_mod_details(s_mod_row_target[row]);
412 }
413 return;
414 }
415 }
416}
417
421static void navigate_page(int delta)
422{
423 if (!does_mod_list_exist)
424 {
425 return;
426 }
427
428 refresh_mod_list_page(mod_struct.index + delta);
429}
430
431static void navigate_page_left(CMnuSmpButton *button)
432{
433 navigate_page(-1);
434}
435
436static void navigate_page_right(CMnuSmpButton *button)
437{
438 navigate_page(1);
439}
440
443{
444 char row_default[128] = "ui_mainmenu_button_default.msh";
445 char row_pressed[128] = "ui_mainmenu_button_pressed.msh";
446 char row_disabled[128] = "ui_mainmenu_button_disabled.msh";
447 char row_load[1] = "";
448 char row_label[1] = "";
449
450 for (int row = 0; row < MOD_LIST_ROWS; row++)
451 {
452 s_mod_row_target[row] = -1;
453 s_mod_buttons[row] = uiAPI.attachNewButton(
454 list_panel, row_default, row_pressed, row_load, row_disabled,
455 row_label, 7,
458 32 + row,
459 (uint32_t) &on_mod_selected);
460 }
461
462 char btn_disabled[128] = "ui_btn_togglearrow_right_disabled.msh";
463 char btn_pressed[128] = "ui_btn_togglearrow_right_pressed.msh";
464 char btn_load[1] = "";
465 char btn_default[128] = "ui_btn_togglearrow_right_default.msh";
466 char btn_label[1] = "";
467
468 right_nav = uiAPI.attachNewButton(list_panel, btn_default, btn_pressed, btn_load,
469 btn_disabled, btn_label, 7, (443 - 96), MOD_NAV_Y,
470 48, 48, 0, (uint32_t) &navigate_page_right);
471
472 char btn_disabled_left[128] = "ui_btn_togglearrow_left_disabled.msh";
473 char btn_pressed_left[128] = "ui_btn_togglearrow_left_pressed.msh";
474 char btn_default_left[128] = "ui_btn_togglearrow_left_default.msh";
475
476 left_nav = uiAPI.attachNewButton(list_panel, btn_default_left, btn_pressed_left,
477 btn_load, btn_disabled_left, btn_label, 7, 48, MOD_NAV_Y,
478 48, 48, 1, (uint32_t) &navigate_page_left);
479
480 char page_placeholder[8] = "";
481 mod_struct.page_label = uiAPI.attachLabel(nullptr, list_panel, page_placeholder,
482 6, 190, MOD_NAV_Y + 14, 80, 32);
483 uiAPI.setMenuID(mod_struct.page_label, 0x6);
484}
485
490{
491 char placeholder[2] = " ";
492
493 mod_struct.title_label = uiAPI.attachLabel(nullptr, info_panel, placeholder,
494 4, 0, MOD_INFO_TITLE_Y,
496 uiAPI.setMenuID(mod_struct.title_label, 0x6);
497 g_set_label_flags(mod_struct.title_label, 0xa);
499
500 mod_struct.author_label = uiAPI.attachLabel(nullptr, info_panel, placeholder,
501 8, 0, MOD_INFO_AUTHOR_Y,
503 uiAPI.setMenuID(mod_struct.author_label, 0x6);
505
506 mod_struct.type_label = uiAPI.attachLabel(nullptr, info_panel, placeholder,
507 10, 0, MOD_INFO_TYPE_Y,
509 uiAPI.setMenuID(mod_struct.type_label, 0x6);
511
512 mod_struct.desc_label = uiAPI.attachLabel(nullptr, info_panel, placeholder,
513 11, 0, MOD_INFO_DESC_Y,
515
516 uiAPI.setMenuID(mod_struct.desc_label, 0x6);
517
518 mod_struct.error_label = uiAPI.attachLabel(nullptr, info_panel, placeholder,
519 11, 0, MOD_INFO_ERROR_Y,
521 uiAPI.setMenuID(mod_struct.error_label, 0x6);
523}
524
525void __fastcall close_mod_list_callback(CMnuSmpButton *button, int32_t *cui_menu_ptr_maybe)
526{
527 CMnuContainer *parent_container =
529
530 if (parent_container != nullptr)
531 {
532 uiAPI.setContainerVisible(parent_container, false, false);
533 }
534 is_mod_list_shown = false;
535}
536
538 uint16_t x,
539 uint16_t y,
540 uint16_t width,
541 uint16_t height,
542 const char *background_msb,
543 const char *border_msb,
544 float alpha
545 )
546{
547 CMnuContainer *container;
548 SF_String s_bg, s_border;
549 SF_String *p_bg, *p_border;
550
551 container = (CMnuContainer *)uiAPI.newOperator(0x340);
552 if (!container)
553 {
554 log_error("Failed to allocate CMnuContainer");
555 return NULL;
556 }
557
558 uiAPI.initializeMenuContainer(container);
559
560 p_bg = g_create_sf_string(&s_bg, (char *)background_msb);
561 p_border = g_create_sf_string(&s_border, (char *)border_msb);
562
563 uiAPI.setupMenuContainerData(
564 container,
565 x,
566 y,
567 width,
568 height,
569 p_bg,
570 p_border
571 );
572
574 g_destroy_sf_string(p_border);
575 void *CMnuVisControl = (void *) uiAPI.getVisualControl(container);
576 uiAPI.setBaseAlpha(CMnuVisControl, alpha);
577
578 return container;
579}
580
582{
583 char close_btn_default[128] = "ui_btn_nav_back_default.msh";
584 char close_btn_pressed[128] = "ui_btn_nav_back_pressed.msh";
585 char close_btn_disabled[128] = "ui_btn_nav_back_disabled.msh";
586 char close_btn_load[1] = "";
587 char close_btn_label[1] = "";
588
589 uiAPI.attachNewButton(
590 mod_list,
591 close_btn_default,
592 close_btn_pressed,
593 close_btn_load,
594 close_btn_disabled,
595 close_btn_label,
596 7,
597 28,
598 700,
599 48,
600 48,
601 2,
602 (uint32_t) &close_mod_list_callback
603 );
604}
605
606void __thiscall show_mod_list(CMnuSmpButton *button)
607{
609 if(!does_mod_list_exist)
610 {
611 is_mod_list_shown = true;
612 mod_info_page = uiAPI.createContainer(
613 0, 0, 1024, 768,
614 "ui_bgr_landscape_bg.msb",
615 "", 0.99f
616 );
617
618 mod_container = uiAPI.createContainer(
619 11,6,1008,757,
620 "ui_bgr_pregame_border_transparency.msb",
621 "ui_bgr_pregame_border.msb", 0.5f
622 );
623
624 mod_list = uiAPI.createContainer(
625 59, 50, 443, 619,
626 "ui_bgr_pregame_border_right_transparency.msb",
627 "ui_bgr_pregame_border_right.msb", 0.5f
628 );
629
630 CMnuContainer *mod_list_info = uiAPI.createContainer(
631 502, 50, 443, 619,
632 "ui_bgr_pregame_border_right_transparency.msb",
633 "ui_bgr_pregame_border_right.msb", 0.5f
634 );
635
636 if (!mod_info_page || !mod_container || !mod_list || !mod_list_info)
637 {
638 log_error("Unable to create Mod Menu, a container allocation failed");
639
640 if (mod_info_page)
641 {
642 uiAPI.destroyContainer(mod_info_page);
643 }
644 if (mod_container)
645 {
646 uiAPI.destroyContainer(mod_container);
647 }
648 if (mod_list)
649 {
650 uiAPI.destroyContainer(mod_list);
651 }
652 if (mod_list_info)
653 {
654 uiAPI.destroyContainer(mod_list_info);
655 }
656
658 return;
659 }
660
661 uiAPI.containerAddControl(parent, (CMnuBase *)mod_info_page, '\x01', '\x01', 0);
662
663 uiAPI.containerAddControl(mod_info_page, (CMnuBase *)mod_container, '\x01', '\x01', 0);
664
665 uiAPI.containerAddControl(mod_container, (CMnuBase *)mod_list, '\x01', '\x01', 0);
666 uiAPI.containerAddControl(mod_container, (CMnuBase *)mod_list_info, '\x01', '\x01', 0);
667
669
671 build_mod_info_panel(mod_list_info);
674
675 char sfsf_mod_info[32] = "Mod Information";
676 mod_list_title = uiAPI.attachLabel(nullptr, mod_container, sfsf_mod_info, 6, 468, 16, 128, 16);
677
678 uiAPI.setMenuID(mod_list_title, 0x6);
679 uiAPI.setLabelColour(mod_list_title, 0.85, 0.64, 0.12, '\0');
680 uiAPI.setLabelColour(mod_list_title, 0.85, 0.64, 0.12, '\x01');
681
682 does_mod_list_exist = true;
683 }
684 else if (mod_info_page != nullptr)
685 {
686 // Same menu session, so the screen *should* still be attached, so we can toggle visibility.
687 is_mod_list_shown = !is_mod_list_shown;
688 uiAPI.setContainerVisible(mod_info_page, is_mod_list_shown, 0);
689 }
690}
691
693 char *button_mesh_default,
694 char *button_mesh_pressed,
695 char *button_initial_load_mesh,
696 char *button_mesh_disabled, char *label_char,
697 uint8_t font_index, uint16_t x_pos,
698 uint16_t y_pos, uint16_t width,
699 uint16_t height, int button_index,
700 uint32_t callback_func_ptr)
701{
702 SF_String m_mesh_string_default;
703 SF_String m_mesh_string_pressed;
704 SF_String m_button_initial_load_mesh;
705 SF_String m_mesh_string_disabled;
706
707 SF_String m_label_string;
708 CMnuSmpButton *new_button;
709 //void *new_btn_operation;
710
711 SF_FontStruct *fonts = uiAPI.getFonts();
712 SF_String *label_string = g_create_sf_string(&m_label_string, label_char);
713
714 // Default
715 SF_String *mesh_string_default = g_create_sf_string(&m_mesh_string_default,
716 button_mesh_default);
717
718 // Pressed
719 SF_String *mesh_string_pressed = g_create_sf_string(&m_mesh_string_pressed,
720 button_mesh_pressed);
721
722 // Highlight
723 SF_String *init_load_mesh = g_create_sf_string(&m_button_initial_load_mesh,
724 button_initial_load_mesh);
725
726 // Disabled
727 SF_String *mesh_string_disabled =
728 g_create_sf_string(&m_mesh_string_disabled, button_mesh_disabled);
729
730 // 0x3b0 seems to corralate to CUiStartMenu, but is directly cast to be a type of CUiFrameStats
731 new_button = (CMnuSmpButton *)uiAPI.newOperator(0x428); // 0x368, 0x3b0 and 0x708 are all valid. (I suspect that they're creating objects that have CMnuLabel as a Parent Class).
732
733 if (font_index > 32)
734 {
735 log_error("Invalid font index 0~32, defaulting to font 6");
736 font_index = 6;
737 }
738
739 new_button = uiAPI.initializeSmpButton(new_button);
740 SF_Font *selected_font = uiAPI.getFont(fonts, font_index);
741
742 uiAPI.CMnuBaseSetName((CMnuBase *)new_button, label_string);
743
744 uiAPI.createButton(new_button,x_pos,y_pos,width,height,mesh_string_default,
745 init_load_mesh,mesh_string_pressed,mesh_string_disabled);
746
747 uiAPI.setFont(new_button, selected_font);
748
749 uiAPI.setButtonIndex(new_button, button_index);
750
751 uiAPI.setMenuButtonFlag(new_button, '\x01');
752
753 uiAPI.setButtonName(new_button, label_string);
754
755 CUtlCallback2 callback;
756 callback.vtable_ptr = *(uint32_t *)(ASI::AddrOf(0x7F9C64));
757 callback.param_ptr = (uint32_t) parent;
758 callback.callback_func = callback_func_ptr;
759
760 uint32_t param1, param2, param3;
761
762 uiAPI.attachCallback(&callback, &param1, &param2, &param3);
763
764 new_button->CMnuBase_data.param_1_callback = param1;
765 new_button->CMnuBase_data.param_2_callback = param2;
766 new_button->CMnuBase_data.param_3_callback = param3;
767
768 uiAPI.vfunction16AttachCallback(new_button, '\x01');
769
770 uiAPI.containerAddControl(parent, (CMnuBase *)new_button, '\x01', '\x01', 0);
771
772 g_destroy_sf_string(mesh_string_default);
773 g_destroy_sf_string(mesh_string_pressed);
774 g_destroy_sf_string(init_load_mesh);
775 g_destroy_sf_string(mesh_string_disabled);
776 g_destroy_sf_string(label_string);
777
778 return new_button;
779}
780
781SFMod *createModInfo(const char *mod_id,const char *mod_version,
782 const char *mod_author, const char *mod_description)
783{
784 SFMod *mod = (SFMod *)malloc(sizeof(SFMod));
785
786 if (mod == nullptr)
787 {
788 log_error("Unable to allocate SFMod for [%s]", (mod_id != nullptr) ? mod_id : "<unnamed>");
789 return nullptr;
790 }
791
792 clear_mod_errors(mod);
793
794 strncpy(mod->mod_id, mod_id, sizeof(mod->mod_id) - 1);
795 mod->mod_id[sizeof(mod->mod_id) - 1] = '\0';
796
797 strncpy(mod->mod_version, mod_version, sizeof(mod->mod_version) - 1);
798 mod->mod_version[sizeof(mod->mod_version) - 1] = '\0';
799
800 strncpy(mod->mod_description, mod_description, sizeof(mod->mod_description) - 1);
801 mod->mod_description[sizeof(mod->mod_description) - 1] = '\0';
802
803 strncpy(mod->mod_author, mod_author, sizeof(mod->mod_author) - 1);
804 mod->mod_author[sizeof(mod->mod_author) - 1] = '\0';
805
806 return mod;
807}
808
809/*
810 void attachVideo(CAppMenu *CAppMenu_ptr, CMnuContainer *parent,
811 char *video_loc_and_name_chars)
812 {
813 SF_CUiVideo *video_ptr = (SF_CUiVideo *) uiAPI.newOperator(0x348);
814 SF_String m_video_name_string;
815 SF_String *video_name_string = g_create_sf_string(&m_video_name_string,
816 video_loc_and_name_chars);
817 // TODO Cleanup: Not Sequence, just normal Video, haven't renamed it from previous investigations.
818 video_ptr =(SF_CUiVideo *) cuiVideoSequence_constructor(video_ptr,
819 video_name_string);
820
821 char controller_mark_chars[64];
822 sprintf(controller_mark_chars, "<Cont>CreditsVideoController");
823 SF_String m_controller_mark_string;
824 SF_String *controller_mark_string =
825 g_create_sf_string(&m_controller_mark_string, controller_mark_chars);
826 CMnuBase_setname((CMnuBase *)video_ptr, controller_mark_string);
827
828 //void *_CMnuScreen_ptr, CMnuBase* base, char flag
829 //CMnuScreen_attach_control(parent, CAppMenu_ptr->CAppMenu_data.CMnuBase_ptr, '\x01');
830 }
831 */
832
833CMnuLabel * __thiscall attachMeshedLabel(CMnuLabel *new_label,
834 CMnuContainer *parent,
835 char *mesh_char,
836 char *label_char,
837 uint8_t font_index,
838 uint16_t x_pos, uint16_t y_pos,
839 uint16_t width, uint16_t height)
840{
841 SF_String m_mesh_string;
842 SF_String m_label_string;
843
844 SF_FontStruct *fonts = uiAPI.getFonts();
845 SF_String *label_string = g_create_sf_string(&m_label_string, label_char);
846 SF_String *mesh_string = g_create_sf_string(&m_mesh_string, mesh_char);
847
848 new_label = (CMnuLabel *)uiAPI.newOperator(0x3b0);
849
850 if (font_index > 32)
851 {
852 log_error("Invalid font index 0~32, defaulting to font 6");
853 font_index = 6;
854 }
855
856 SF_Font *selected_font = uiAPI.getFont(fonts, font_index);
857
858 uiAPI.menuLabelConstructor(new_label);
859
860 g_set_label_flags(new_label, 7);
861
862 uiAPI.initMenuElement(new_label, x_pos, y_pos, width, height, mesh_string);
863
864 uiAPI.menuLabelSetFont(new_label, selected_font);
865
866 uiAPI.containerAddControl(parent, (CMnuBase *) new_label, '\x01', '\x01', 0);
867
868 uiAPI.menuLabelSetString(new_label, label_string);
869
870 g_destroy_sf_string(label_string);
871 g_destroy_sf_string(mesh_string);
872
873 return new_label;
874}
875
876CMnuLabel * __thiscall attachLabel(CMnuLabel *label_ptr,
877 CMnuContainer *parent,
878 char *label_chars, uint8_t font_index,
879 uint16_t x_pos, uint16_t y_pos,
880 uint16_t width, uint16_t height)
881{
882 char empty[] = "";
883 return attachMeshedLabel(label_ptr, parent, empty, label_chars,
884 font_index, x_pos, y_pos, width, height);
885}
const char * get_mod_type_label(ModType type)
Tag rendered next to a mod in the list. Empty for external mods.
void clear_mod_errors(SFMod *mod)
Empties a mod's error buffer.
const std::list< RegisteredMod > & get_registered_mods()
The mods known to the framework, grouped framework -> core -> external.
UiFunctions uiAPI
Definition sf_hooks.c:50
SFSF_ModlistStruct mod_struct
set_label_flags_ptr g_set_label_flags
void log_error(const char *format,...)
SF_String_ctor_ptr g_create_sf_string
Definition sf_wrappers.c:29
SF_String_dtor_ptr g_destroy_sf_string
Definition sf_wrappers.c:30
int AddrOf(int offset)
returns "real" virtual address of given memory offset
Definition sf_asi.h:135
#define MOD_INFO_TYPE_Y
#define MOD_TITLE_RGB
#define MOD_INFO_TITLE_Y
void prepare_mod_title(SFMod *parent_mod, char *mod_title, size_t buffer_size)
CMnuLabel *__thiscall attachMeshedLabel(CMnuLabel *new_label, CMnuContainer *parent, char *mesh_char, char *label_char, uint8_t font_index, uint16_t x_pos, uint16_t y_pos, uint16_t width, uint16_t height)
CMnuSmpButton * left_nav
#define MOD_LIST_ROWS
Number of mod rows the left panel can show at once.
#define MOD_INFO_ERROR_Y
void prepare_mod_error_info(SFMod *parent_mod, char *mod_error_info, size_t error_buffer_size, char *wrapped_error_info, size_t wrapped_buffer_size)
#define MOD_INFO_AUTHOR_Y
int normalize_page_index(int page, int total_pages)
int calculate_total_pages(int total_unique_mods, int mods_per_page)
void reset_mod_list_screen()
Drops every cached mod list pointer, this will ensure we don't crash after a map load.
CMnuContainer * mod_info_page
void __thiscall updateLabelText(CMnuLabel *label, const char *text)
#define MOD_INFO_ERROR_H
CMnuContainer * mod_container
void show_mod_details(int mod_index)
Renders one mod into the right hand info panel.
void __thiscall show_mod_list(CMnuSmpButton *button)
void wrap_text(const char *input, char *output, size_t max_width)
Wraps text to a character width, inserting newlines.
void apply_label_colour(CMnuLabel *label, float red, float green, float blue)
Sets both colour slots of a label. (normal and on hover).
#define MOD_ROW_PITCH
#define MOD_TYPE_RGB
void __fastcall close_mod_list_callback(CMnuSmpButton *button, int32_t *cui_menu_ptr_maybe)
void __thiscall on_mod_selected(CMnuSmpButton *button)
Left panel row callback.
#define MOD_INFO_DESC_H
#define MOD_ROW_WIDTH
#define MOD_INFO_TYPE_H
#define MOD_ERROR_RGB
void prepare_mod_description(SFMod *parent_mod, char *mod_description, size_t description_buffer_size, char *wrapped_description, size_t wrapped_buffer_size)
void prepare_mod_author(SFMod *parent_mod, char *mod_author, size_t buffer_size)
#define MOD_AUTHOR_RGB
void set_centred_label_text(CMnuLabel *label, const char *text, int panel_width, int y, int height)
Repositions a label so its text block sits centred, then sets the text.
#define MOD_ROW_Y_START
void build_mod_info_panel(CMnuContainer *info_panel)
Creates the right hand panel's labels.
#define MOD_INFO_AUTHOR_H
#define MOD_NAV_Y
SF_String *__thiscall SFStringConstructor_char(SF_String *_this, const char *char_string)
Constructs an SF_String from a null-terminated char string. Moved here from sf_vanilla_fix_hook....
CMnuContainer *__thiscall createContainer(uint16_t x, uint16_t y, uint16_t width, uint16_t height, const char *background_msb, const char *border_msb, float alpha)
#define MOD_INFO_WRAP
CMnuLabel *__thiscall attachLabel(CMnuLabel *label_ptr, CMnuContainer *parent, char *label_chars, uint8_t font_index, uint16_t x_pos, uint16_t y_pos, uint16_t width, uint16_t height)
CMnuSmpButton *__thiscall attachNewButton(CMnuContainer *parent, char *button_mesh_default, char *button_mesh_pressed, char *button_initial_load_mesh, char *button_mesh_disabled, char *label_char, uint8_t font_index, uint16_t x_pos, uint16_t y_pos, uint16_t width, uint16_t height, int button_index, uint32_t callback_func_ptr)
CMnuLabel * mod_list_title
void build_mod_list_panel(CMnuContainer *list_panel)
Creates the clickable mod rows, the paging arrows and the counter.
void __thiscall SFStringConstructor(SF_String *_this)
Zero-initializes an SF_String (no allocation). Moved here from sf_vanilla_fix_hook....
CMnuSmpButton * right_nav
int calculate_total_unique_mods()
SFMod * createModInfo(const char *mod_id, const char *mod_version, const char *mod_author, const char *mod_description)
void refresh_mod_list_page(int page)
Points the visible rows at the mods on the given page.
#define MOD_ROW_HEIGHT
#define MOD_INFO_DESC_Y
#define MOD_ROW_X
void prepare_mod_page_info(int page, int total_pages, char *mod_page_info, size_t buffer_size)
#define MOD_INFO_PANEL_WIDTH
CMnuContainer * mod_list
void add_close_button(CMnuContainer *mod_list)
#define MOD_INFO_TITLE_H
CMnuBase_data CMnuBase_data
A mod as shown in the in-game mod list.
char mod_description[128]
char mod_version[24]
char mod_errors[256]
char mod_author[128]