Project Alice
Loading...
Searching...
No Matches
launcher_main.cpp
Go to the documentation of this file.
1#define ALICE_NO_ENTRY_POINT 1
2
3#include "common_types.cpp"
4#include "system_state.cpp"
5#ifndef INCREMENTAL
6#include "parsers.cpp"
7#include "text.cpp"
9#include "fonts.cpp"
10#include "texture.cpp"
11#include "date_interface.cpp"
12#include "serialization.cpp"
13#include "nations.cpp"
14#include "culture.cpp"
15#include "military.cpp"
16#include "modifiers.cpp"
17#include "province.cpp"
18#include "triggers.cpp"
19#include "effects.cpp"
20#include "economy.cpp"
21#include "demographics.cpp"
22#include "bmfont.cpp"
23#include "rebels.cpp"
24#include "politics.cpp"
25#include "events.cpp"
26#include "gui_graphics.cpp"
28#include "widgets/table.cpp"
33#include "gui_leader_select.cpp"
37#include "gui_budget_window.cpp"
39#include "gui_error_window.cpp"
40#include "game_scene.cpp"
41#include "commands.cpp"
42#include "network.cpp"
44#include "notifications.cpp"
45#include "map_tooltip.cpp"
46#include "unit_tooltip.cpp"
47#include "ai.cpp"
48#include "fif_triggers.cpp"
49#include "map_modes.cpp"
50#include "platform_specific.cpp"
51#include "opengl_wrapper.cpp"
52#include "prng.cpp"
53#include "blake2.cpp"
54#include "zstd.cpp"
55#include "pcp.cpp"
56#endif
57#include "gui_element_types.cpp"
58#include "gui_main_menu.cpp"
59#include "gui_console.cpp"
60#include "gui_event.cpp"
62
63#ifndef UNICODE
64#define UNICODE
65#endif
66#include <Windowsx.h>
67#include <shellapi.h>
68#include "Objbase.h"
69#ifndef GLEW_STATIC
70#define GLEW_STATIC
71#endif
72#include "glew.h"
73#include "wglew.h"
74#include <cassert>
75#include "resource.h"
76#pragma comment(lib, "Ole32.lib")
77#pragma comment(lib, "Shell32.lib")
78#pragma comment(lib, "icu.lib")
79#include "fonts.hpp"
80#include "texture.hpp"
81#include "text.hpp"
82#include "prng.hpp"
83#include "system_state.hpp"
84#include "serialization.hpp"
85#include "network.hpp"
86#include "simple_fs.hpp"
87
88namespace launcher {
89
90static float scaling_factor = 1.0f;
91static float dpi = 96.0f;
92constexpr inline float base_width = 880.0f;
93constexpr inline float base_height = 540.0f;
94
95constexpr inline float caption_width = 837.0f;
96constexpr inline float caption_height = 44.0f;
97
98static int32_t mouse_x = 0;
99static int32_t mouse_y = 0;
100
101static std::string ip_addr = "127.0.0.1";
102static std::string password = "";
103static std::string player_name = "AnonAnon";
104
105enum class string_index : uint8_t {
108 working,
113 password,
114 nickname,
118 host,
119 join,
120 mod_list,
121 count
122};
123
124//english
125static std::string_view en_localised_strings[uint8_t(string_index::count)] = {
126 "Create scenario",
127 "Recreate scenario",
128 "Working...",
129 "Create a new scenario",
130 "for the selected mods",
131 "No scenario found",
132 "IP Address",
133 "Password",
134 "Nickname",
135 "Singleplayer",
136 "Multiplayer",
137 "Start game",
138 "Host",
139 "Join",
140 "Mod list"
141};
142//turkish
143static std::string_view tr_localised_strings[uint8_t(string_index::count)] = {
144 "Senaryo oluştur",
145 "Senaryoyu yeniden oluştur",
146 "Çalışma...",
147 "Seçilen modlar için yeni",
148 "bir senaryo oluşturun",
149 "Senaryo bulunamadı",
150 "IP adresi",
151 "Şifre",
152 "Takma ad",
153 "Tek oyunculu",
154 "Çok Oyunculu",
155 "Oyunu başlatmak",
156 "Ev sahibi",
157 "Katılmak",
158 "Mod listesi"
159};
160//albanian
161static std::string_view sq_localised_strings[uint8_t(string_index::count)] = {
162 "Krijo skenar",
163 "Rikrijo skenar",
164 "Punon...",
165 "Krijo një skenar të ri",
166 "për modalitetet e zgjedhura",
167 "Nuk u gjet asnjë skenar",
168 "Adresa IP",
169 "Fjalëkalimi",
170 "Pseudonimi",
171 "Lojtar i vetëm",
172 "Shumë lojtarë",
173 "Fillo lojen",
174 "Mikpritës",
175 "Bashkohu",
176 "Lista e modës"
177};
178//spanish
179static std::string_view es_localised_strings[uint8_t(string_index::count)] = {
180 "Crear escenario",
181 "Recrear escenario",
182 "Trabajando...",
183 "Crea un nuevo escenario",
184 "para los mods seleccionados",
185 "No se encontro el escenario",
186 "Dirección IP",
187 "Contraseña",
188 "Alias",
189 "Un jugador",
190 "Multijugador",
191 "Empezar juego",
192 "Hostear",
193 "Unirse",
194 "Lista de mods"
195};
196//italian
197static std::string_view it_localised_strings[uint8_t(string_index::count)] = {
198 "Crea esenario",
199 "Ricreare esenario",
200 "Lavorando...",
201 "Crea un nuovo esenario",
202 "per i mod selezionati",
203 "Scenario non trovato",
204 "Indirizzo IP",
205 "Password",
206 "Alias",
207 "Singolo",
208 "Multigiocatore",
209 "Inizia il gioco",
210 "Ospite",
211 "Partecipare",
212 "Elenco delle mods"
213};
214//french
215static std::string_view fr_localised_strings[uint8_t(string_index::count)] = {
216 "Creer un scènario",
217 "Recrèer le scènario",
218 "Fonctionnement...",
219 "Creer un nouveau scènario",
220 "pour les mods sèlectionnès",
221 "Scènario introuvable",
222 "Addresse IP",
223 "Passe",
224 "Alias",
225 "Solo",
226 "Multijoueur",
227 "Dèmarrer jeu",
228 "Hõte",
229 "Rejoindre",
230 "Liste des modifications"
231};
232//portuguese
233static std::string_view po_localised_strings[uint8_t(string_index::count)] = {
234 "Criar cenário",
235 "Recriar cenário",
236 "Trabalhando...",
237 "Crie un novo cenário para",
238 "os mods selecionados",
239 "Cenário não encontrado",
240 "Endereço IP",
241 "Senha",
242 "Alias",
243 "Unjogador",
244 "Multijogador",
245 "Começar o jogo",
246 "Hospedar",
247 "Junte-se",
248 "Lista de modificaçães"
249};
250//deutsch
251static std::string_view de_localised_strings[uint8_t(string_index::count)] = {
252 "Szenario erstellen",
253 "Szenario neu erstellen",
254 "Arbeitet...",
255 "Neues Szenario für die",
256 "ausgewählten mods erstellen",
257 "Szenario nicht gefunden",
258 "IP-Adresse",
259 "Passwort",
260 "Alias",
261 "Einzelspieler",
262 "Mehrspieler",
263 "Spiel starten",
264 "Hosten",
265 "Teilnehmen",
266 "Liste der Modifikationen"
267};
268//swedish
269static std::string_view sv_localised_strings[uint8_t(string_index::count)] = {
270 "Skapa scenario",
271 "Återskapa scenario",
272 "Arbetssått...",
273 "Skepa ett nytt scenario",
274 "för de valda mods",
275 "Scenario hittades inte",
276 "IP-adress",
277 "Lösenord",
278 "Alias",
279 "Einselspalet",
280 "Merspalet",
281 "Starta spelet",
282 "Gå med",
283 "Vara värd",
284 "Lista åver åndriggar"
285};
286//chinese
287static std::string_view zh_localised_strings[uint8_t(string_index::count)] = {
288 "创建方案",
289 "重新创建方案",
290 "工作中……",
291 "为所选模组",
292 "创建新方案",
293 "未找到方案",
294 "IP 地址",
295 "密码",
296 "昵称",
297 "单人游戏",
298 "多人游戏",
299 "开始游戏",
300 "主机",
301 "加入",
302 "模组列表"
303};
304//arabic
305static std::string_view ar_localised_strings[uint8_t(string_index::count)] = {
306 "إنشاء السيناريو",
307 "إعادة إنشاء السيناريو",
308 "عمل...",
309 "إنشاء سيناريو جديد",
310 "للوضع المحدد",
311 "لم يتم العثور على المشهد",
312 "عنوان IP",
313 "كلمة المرور",
314 "كنية",
315 "لاعب واحد",
316 "متعددة اللاعبين",
317 "بدء اللعبة",
318 "يستضيف",
319 "ينضم",
320 "قائمة وزارة الدفاع",
321};
322//norwegian
323static std::string_view no_localised_strings[uint8_t(string_index::count)] = {
324 "Lag scenario",
325 "Gjenskape scenario",
326 "Arbeider...",
327 "Lag et nytt scenario",
328 "for de valgte modsene",
329 "Ingen scenarioer funnet",
330 "IP adresse",
331 "Passord",
332 "Kallenavn",
333 "Enkeltspiller",
334 "Flerspiller",
335 "Start spill",
336 "Vert",
337 "Bli med",
338 "Mod liste",
339};
340//romanian
341static std::string_view ro_localised_strings[uint8_t(string_index::count)] = {
342 "Creați script",
343 "Scenariu de recenzie",
344 "Lucru...",
345 "Creați un nou script",
346 "pentru moduri selectate",
347 "Nu a fost găsit niciun script",
348 "adresa IP",
349 "Parola",
350 "Poreclă",
351 "Un singur jucator",
352 "Jucători multipli",
353 "Începeți jocul",
354 "Gazdă",
355 "A te alatura",
356 "Lista de moduri"
357};
358//russian
359static std::string_view ru_localised_strings[uint8_t(string_index::count)] = {
360 "Создать сценарий",
361 "Воссоздать сценарий",
362 "Работающий...",
363 "Создать новый сценарий",
364 "Для выбранного мода",
365 "Сцена не найдена",
366 "айпи адрес",
367 "Пароль",
368 "Псевдоним",
369 "Один игрок",
370 "Мультиплеер",
371 "Начать игру",
372 "Хозяин",
373 "Присоединиться",
374 "Список модов",
375};
376//polish
377static std::string_view pl_localised_strings[uint8_t(string_index::count)] = {
378 "Stwórz scenariusz",
379 "Odtwórz scenariusz",
380 "Pracujący...",
381 "Utwórz nowy scenariusz",
382 "dla wybranych modów",
383 "Nie znaleziono scenariusza",
384 "Adres IP",
385 "Hasło",
386 "Przezwisko",
387 "Jeden gracz",
388 "Tryb wieloosobowy",
389 "Rozpocząć grę",
390 "Gospodarz",
391 "Dołączyć",
392 "Lista modów"
393};
394//bulgarian
395static std::string_view bg_localised_strings[uint8_t(string_index::count)] = {
396 "Създайте сценарий",
397 "Пресъздайте сценарий",
398 "Работи...",
399 "Създайте нов сценарий",
400 "за избраните модове",
401 "Няма намерен сценарий",
402 "IP адрес",
403 "Парола",
404 "Псевдоним",
405 "Един играч",
406 "Мултиплейър",
407 "Започни игра",
408 "Домакин",
409 "Присъединяване",
410 "Мод списък",
411};
412//catalan
413static std::string_view ca_localised_strings[uint8_t(string_index::count)] = {
414 "Crea un escenari",
415 "Recrea l'escenari",
416 "Treball...",
417 "Creeu un nou escenari",
418 "per als mods seleccionats",
419 "No s'ha trobat cap escenari",
420 "Adreça IP",
421 "Contrasenya",
422 "Pseudònim",
423 "Sol jugador",
424 "Multijugador",
425 "Començar el joc",
426 "Amfitrió",
427 "Uneix - te",
428 "Llista de modificacions",
429};
430//czech
431static std::string_view cs_localised_strings[uint8_t(string_index::count)] = {
432 "Vytvořte scénář",
433 "Znovu vytvořit scénář",
434 "Pracovní...",
435 "Vytvořte nový scénář",
436 "pro vybrané mody",
437 "Nebyl nalezen žádný scénář",
438 "IP adresa",
439 "Heslo",
440 "Přezdívka",
441 "Pro jednoho hráče",
442 "Pro více hráčů",
443 "Začít hru",
444 "Hostitel",
445 "Připojit",
446 "Seznam modů"
447};
448//danish
449static std::string_view da_localised_strings[uint8_t(string_index::count)] = {
450 "Opret scenarie",
451 "Genskab scenariet",
452 "Arbejder...",
453 "Opret et nyt scenarie",
454 "for de valgte mods",
455 "Intet scenarie fundet",
456 "IP - adresse",
457 "Adgangskode",
458 "Kaldenavn",
459 "En spiller",
460 "Flere spillere",
461 "Start Spil",
462 "Vært",
463 "Tilslutte",
464 "Mod liste"
465};
466//greek
467static std::string_view el_localised_strings[uint8_t(string_index::count)] = {
468 "Δημιουργία σεναρίου",
469 "Αναδημιουργήστε το σενάριο",
470 "Εργαζόμενος...",
471 "Δημιουργήστε ένα νέο σενάριο",
472 "για τα επιλεγμένα mods",
473 "Δεν βρέθηκε κανένα σενάριο",
474 "Διεύθυνση IP",
475 "Κωδικός πρόσβασης",
476 "Παρατσούκλι",
477 "Μονος παιχτης",
478 "Λειτουργία για πολλούς παίκτες",
479 "Ξεκίνα το παιχνίδι",
480 "Πλήθος",
481 "Συμμετοχή",
482 "Λίστα mod"
483};
484//finnish
485static std::string_view fi_localised_strings[uint8_t(string_index::count)] = {
486 "Luo skenaario",
487 "Luo skenaario uudelleen",
488 "Työskentelee...",
489 "Luo uusi skenaario",
490 "valituille modeille",
491 "Skenaariota ei löytynyt",
492 "IP - osoite",
493 "Salasana",
494 "Nimimerkki",
495 "Yksinpeli",
496 "Moninpeli",
497 "Aloita peli",
498 "Isäntä",
499 "Liittyä seuraan",
500 "Mod lista",
501};
502//hebrew
503static std::string_view he_localised_strings[uint8_t(string_index::count)] = {
504 "צור תרחיש",
505 "ליצור מחדש תרחיש",
506 "עובד...",
507 "צור תרחיש חדש עבור",
508 "המצבים שנבחרו",
509 "לא נמצא תרחיש",
510 "כתובת פרוטוקול אינטרנט",
511 "סיסמה",
512 "כינוי",
513 "שחקן יחיד",
514 "רב משתתפים",
515 "התחל משחק",
516 "מנחה",
517 "לְהִצְטַרֵף",
518 "רשימת השינויים במשחק"
519};
520//hungarian
521static std::string_view hu_localised_strings[uint8_t(string_index::count)] = {
522 "Forgatókönyv létrehozása",
523 "Forgatókönyv újbóli létrehozása",
524 "Dolgozó...",
525 "Hozzon létre egy új forgatókönyvet",
526 "a kiválasztott modokhoz",
527 "Nem található forgatókönyv",
528 "IP - cím",
529 "Jelszó",
530 "Becenév",
531 "Egyjátékos",
532 "Többjátékos",
533 "Játék kezdése",
534 "Házigazda",
535 "Csatlakozik",
536 "Mod lista"
537};
538//dutch
539static std::string_view nl_localised_strings[uint8_t(string_index::count)] = {
540 "Scenario maken",
541 "Scenario opnieuw maken",
542 "Werken...",
543 "Maak een nieuw scenario",
544 "voor de geselecteerde mods",
545 "Geen scenario gevonden",
546 "IP adres",
547 "Wachtwoord",
548 "Bijnaam",
549 "Een speler",
550 "Meerdere spelers",
551 "Start het spel",
552 "Gastheer",
553 "Meedoen",
554 "Mod - lijst"
555};
556//lithuanian
557static std::string_view lt_localised_strings[uint8_t(string_index::count)] = {
558 "Sukurti scenarijų",
559 "Atkurti scenarijų",
560 "Dirba...",
561 "Sukurkite naują pasirinktų",
562 "modifikacijų scenarijų",
563 "Scenarijus nerastas",
564 "IP adresas",
565 "Slaptažodis",
566 "Slapyvardis",
567 "Vieno žaidėjo",
568 "Kelių žaidėjų",
569 "Pradėti žaidimą",
570 "Šeimininkas",
571 "Prisijunk",
572 "Modifikacijų sąrašas"
573};
574//latvian
575static std::string_view lv_localised_strings[uint8_t(string_index::count)] = {
576 "Izveidojiet scenāriju",
577 "Atkārtoti izveidojiet scenāriju",
578 "Strādā...",
579 "Izveidojiet jaunu scenāriju",
580 "atlasītajiem modiem",
581 "Nav atrasts neviens scenārijs",
582 "IP adrese",
583 "Parole",
584 "Segvārds",
585 "Viens spēlētājs",
586 "Vairāku spēlētāju spēle",
587 "Sākt spēli",
588 "Uzņēmēja",
589 "Pievienojieties",
590 "Modu saraksts"
591};
592//estonian
593static std::string_view et_localised_strings[uint8_t(string_index::count)] = {
594 "Loo stsenaarium",
595 "Loo stsenaarium uuesti",
596 "Töötab...",
597 "Looge valitud modifikatsioonide",
598 "jaoks uus stsenaarium",
599 "Stsenaariumi ei leitud",
600 "IP - aadress",
601 "Parool",
602 "Hüüdnimi",
603 "Üksik mängija",
604 "Mitmikmäng",
605 "Alusta mängu",
606 "Host",
607 "Liitu",
608 "Modifikatsioonide loend"
609};
610//hindi
611static std::string_view hi_localised_strings[uint8_t(string_index::count)] = {
612 "परिदृश्य बनाएँ",
613 "परिदृश्य फिर से बनाएँ",
614 "कार्य कर रहा है...",
615 "चयनित मॉड के लिए",
616 "एक नया परिदृश्य बनाएँ",
617 "कोई परिदृश्य नहीं मिला",
618 "आईपी पता",
619 "पासवर्ड",
620 "उपनाम",
621 "एकल खिलाड़ी",
622 "मल्टीप्लेयर",
623 "खेल शुरू करें",
624 "होस्ट",
625 "जॉइन करें",
626 "मॉड सूची"
627};
628//vietnamese
629static std::string_view vi_localised_strings[uint8_t(string_index::count)] = {
630 "Tạo kịch bản",
631 "Kịch bản tái tạo",
632 "Đang làm việc...",
633 "Tạo một kịch bản mới cho",
634 "các mod đã chọn",
635 "Không tìm thấy kịch bản",
636 "Địa chỉ IP",
637 "Mật khẩu",
638 "Tên nick",
639 "Người chơi đơn",
640 "Nhiều người chơi",
641 "Bắt đầu trò chơi",
642 "Chủ nhà",
643 "Tham gia",
644 "Danh sách mod"
645};
646//armenian
647static std::string_view hy_localised_strings[uint8_t(string_index::count)] = {
648 "Ստեղծեք սցենար",
649 "Վերստեղծեք սցենարը",
650 "Աշխատանքային ...",
651 "Ստեղծեք նոր սցենար",
652 "ընտրված ռեժիմների համար",
653 "Ոչ մի սցենար չի գտնվել",
654 "IP հասցե",
655 "Գաղտնաբառ",
656 "Մականուն",
657 "Միայնակ խաղացող",
658 "Բազմապատկիչ",
659 "Սկսել խաղը",
660 "Հյուրընկալող",
661 "Միանալ",
662 "Mod ուցակ"
663};
664//ukrainian
665static std::string_view uk_localised_strings[uint8_t(string_index::count)] = {
666 "Створити сценарій",
667 "Сценарій огляду",
668 "Працює ...",
669 "Створіть новий сценарій",
670 "для вибраних мод",
671 "Не знайдено сценарію",
672 "IP - адреса",
673 "Пароль",
674 "Прізвище",
675 "Один гравець",
676 "Мультиплеєр",
677 "Почніть гру",
678 "Господар",
679 "З'єднувати",
680 "Список мод"
681};
682static std::string_view* localised_strings = &en_localised_strings[0];
683
684static HWND m_hwnd = nullptr;
685
687 int32_t x = 0;
688 int32_t y = 0;
689 int32_t width = 0;
690 int32_t height = 0;
691};
692
693constexpr inline int32_t ui_obj_close = 0;
694constexpr inline int32_t ui_obj_list_left = 1;
695constexpr inline int32_t ui_obj_list_right = 2;
696constexpr inline int32_t ui_obj_create_scenario = 3;
697constexpr inline int32_t ui_obj_play_game = 4;
698constexpr inline int32_t ui_obj_host_game = 5;
699constexpr inline int32_t ui_obj_join_game = 6;
700constexpr inline int32_t ui_obj_ip_addr = 7;
701constexpr inline int32_t ui_obj_password = 8;
702constexpr inline int32_t ui_obj_player_name = 9;
703
704constexpr inline int32_t ui_list_count = 14;
705
706constexpr inline int32_t ui_list_first = 10;
707constexpr inline int32_t ui_list_checkbox = 0;
708constexpr inline int32_t ui_list_move_up = 1;
709constexpr inline int32_t ui_list_move_down = 2;
710constexpr inline int32_t ui_list_end = ui_list_first + ui_list_count * 3;
711
712constexpr inline int32_t ui_row_height = 32;
713
714constexpr inline float list_text_right_align = 420.0f;
715
716static int32_t obj_under_mouse = -1;
717
718static bool game_dir_not_found = false;
719
720constexpr inline ui_active_rect ui_rects[] = {
721 ui_active_rect{ 880 - 31, 0 , 31, 31}, // close
722 ui_active_rect{ 30, 208, 21, 93}, // left
723 ui_active_rect{ 515, 208, 21, 93}, // right
724 ui_active_rect{ 555, 48, 286, 33 }, // create scenario
725 ui_active_rect{ 555, 48 + 156 * 1, 286, 33 }, // play game
726 ui_active_rect{ 555, 48 + 156 * 2 + 36 * 0, 138, 33 }, // host game
727 ui_active_rect{ 703, 48 + 156 * 2 + 36 * 0, 138, 33 }, // join game
728 ui_active_rect{ 555, 54 + 156 * 2 + 36 * 2, 200, 23 }, // ip address textbox
729 ui_active_rect{ 555, 54 + 156 * 2 + 36 * 3 + 12, 200, 23 }, // password textbox
730 ui_active_rect{ 765, 54 + 156 * 2 + 36 * 2, 76, 23 }, // player name textbox
731
732 ui_active_rect{ 60 + 6, 75 + 32 * 0 + 4, 24, 24 },
733 ui_active_rect{ 60 + 383, 75 + 32 * 0 + 4, 24, 24 },
734 ui_active_rect{ 60 + 412, 75 + 32 * 0 + 4, 24, 24 },
735 ui_active_rect{ 60 + 6, 75 + 32 * 1 + 4, 24, 24 },
736 ui_active_rect{ 60 + 383, 75 + 32 * 1 + 4, 24, 24 },
737 ui_active_rect{ 60 + 412, 75 + 32 * 1 + 4, 24, 24 },
738 ui_active_rect{ 60 + 6, 75 + 32 * 2 + 4, 24, 24 },
739 ui_active_rect{ 60 + 383, 75 + 32 * 2 + 4, 24, 24 },
740 ui_active_rect{ 60 + 412, 75 + 32 * 2 + 4, 24, 24 },
741 ui_active_rect{ 60 + 6, 75 + 32 * 3 + 4, 24, 24 },
742 ui_active_rect{ 60 + 383, 75 + 32 * 3 + 4, 24, 24 },
743 ui_active_rect{ 60 + 412, 75 + 32 * 3 + 4, 24, 24 },
744 ui_active_rect{ 60 + 6, 75 + 32 * 4 + 4, 24, 24 },
745 ui_active_rect{ 60 + 383, 75 + 32 * 4 + 4, 24, 24 },
746 ui_active_rect{ 60 + 412, 75 + 32 * 4 + 4, 24, 24 },
747 ui_active_rect{ 60 + 6, 75 + 32 * 5 + 4, 24, 24 },
748 ui_active_rect{ 60 + 383, 75 + 32 * 5 + 4, 24, 24 },
749 ui_active_rect{ 60 + 412, 75 + 32 * 5 + 4, 24, 24 },
750 ui_active_rect{ 60 + 6, 75 + 32 * 6 + 4, 24, 24 },
751 ui_active_rect{ 60 + 383, 75 + 32 * 6 + 4, 24, 24 },
752 ui_active_rect{ 60 + 412, 75 + 32 * 6 + 4, 24, 24 },
753 ui_active_rect{ 60 + 6, 75 + 32 * 7 + 4, 24, 24 },
754 ui_active_rect{ 60 + 383, 75 + 32 * 7 + 4, 24, 24 },
755 ui_active_rect{ 60 + 412, 75 + 32 * 7 + 4, 24, 24 },
756 ui_active_rect{ 60 + 6, 75 + 32 * 8 + 4, 24, 24 },
757 ui_active_rect{ 60 + 383, 75 + 32 * 8 + 4, 24, 24 },
758 ui_active_rect{ 60 + 412, 75 + 32 * 8 + 4, 24, 24 },
759 ui_active_rect{ 60 + 6, 75 + 32 * 9 + 4, 24, 24 },
760 ui_active_rect{ 60 + 383, 75 + 32 * 9 + 4, 24, 24 },
761 ui_active_rect{ 60 + 412, 75 + 32 * 9 + 4, 24, 24 },
762 ui_active_rect{ 60 + 6, 75 + 32 * 10 + 4, 24, 24 },
763 ui_active_rect{ 60 + 383, 75 + 32 * 10 + 4, 24, 24 },
764 ui_active_rect{ 60 + 412, 75 + 32 * 10 + 4, 24, 24 },
765 ui_active_rect{ 60 + 6, 75 + 32 * 11 + 4, 24, 24 },
766 ui_active_rect{ 60 + 383, 75 + 32 * 11 + 4, 24, 24 },
767 ui_active_rect{ 60 + 412, 75 + 32 * 11 + 4, 24, 24 },
768 ui_active_rect{ 60 + 6, 75 + 32 * 12 + 4, 24, 24 },
769 ui_active_rect{ 60 + 383, 75 + 32 * 12 + 4, 24, 24 },
770 ui_active_rect{ 60 + 412, 75 + 32 * 12 + 4, 24, 24 },
771 ui_active_rect{ 60 + 6, 75 + 32 * 13 + 4, 24, 24 },
772 ui_active_rect{ 60 + 383, 75 + 32 * 13 + 4, 24, 24 },
773 ui_active_rect{ 60 + 412, 75 + 32 * 13 + 4, 24, 24 },
774};
775
776static std::vector<parsers::mod_file> mod_list;
777
781};
782
783static std::vector<scenario_file> scenario_files;
784static native_string selected_scenario_file;
785static uint32_t max_scenario_count = 0;
786static std::atomic<bool> file_is_ready = true;
787
788static int32_t frame_in_list = 0;
789
790static HDC opengl_window_dc = nullptr;
791static void* opengl_context = nullptr;
792
794 PIXELFORMATDESCRIPTOR pfd;
795 ZeroMemory(&pfd, sizeof(pfd));
796 pfd.nSize = sizeof(pfd);
797 pfd.nVersion = 1;
798 pfd.dwFlags = PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER;
799 pfd.iPixelType = PFD_TYPE_RGBA;
800 pfd.cColorBits = 32;
801 pfd.cDepthBits = 24;
802 pfd.cStencilBits = 8;
803 pfd.iLayerType = PFD_MAIN_PLANE;
804
805 HDC window_dc = opengl_window_dc;
806
807 int const pixel_format = ChoosePixelFormat(window_dc, &pfd);
808 SetPixelFormat(window_dc, pixel_format, &pfd);
809
810 auto handle_to_ogl_dc = wglCreateContext(window_dc);
811 wglMakeCurrent(window_dc, handle_to_ogl_dc);
812
813 if(glewInit() != 0) {
814 window::emit_error_message("GLEW failed to initialize", true);
815 }
816
817 if(!wglewIsSupported("WGL_ARB_create_context")) {
818 window::emit_error_message("WGL_ARB_create_context not supported", true);
819 }
820
821 // Explicitly request for OpenGL 3.1
822 static const int attribs_3_1[] = {
823 WGL_CONTEXT_MAJOR_VERSION_ARB, 3,
824 WGL_CONTEXT_MINOR_VERSION_ARB, 1,
825 WGL_CONTEXT_FLAGS_ARB, 0,
826 WGL_CONTEXT_PROFILE_MASK_ARB,
827 WGL_CONTEXT_CORE_PROFILE_BIT_ARB,
828 0
829 };
830 opengl_context = wglCreateContextAttribsARB(window_dc, nullptr, attribs_3_1);
831 if(opengl_context == nullptr) {
832 window::emit_error_message("Unable to create WGL context", true);
833 }
834
835 wglMakeCurrent(window_dc, HGLRC(opengl_context));
836 wglDeleteContext(handle_to_ogl_dc);
837
838 if(wglewIsSupported("WGL_EXT_swap_control_tear") == 1) {
839 wglSwapIntervalEXT(-1);
840 } else if(wglewIsSupported("WGL_EXT_swap_control") == 1) {
841 wglSwapIntervalEXT(1);
842 } else {
843 window::emit_error_message("WGL_EXT_swap_control_tear and WGL_EXT_swap_control not supported", true);
844 }
845}
846
848 wglMakeCurrent(opengl_window_dc, nullptr);
849 wglDeleteContext(HGLRC(opengl_context));
850 opengl_context = nullptr;
851}
852
853bool update_under_mouse() { // return if the selected object (if any) has changed
854 for(int32_t i = 0; i < ui_list_end; ++i) {
855 if(mouse_x >= ui_rects[i].x && mouse_x < ui_rects[i].x + ui_rects[i].width
856 && mouse_y >= ui_rects[i].y && mouse_y < ui_rects[i].y + ui_rects[i].height) {
857
858 if(obj_under_mouse != i) {
859 obj_under_mouse = i;
860 return true;
861 } else {
862 return false;
863 }
864 }
865 }
866
867 if(obj_under_mouse != -1) {
868 obj_under_mouse = -1;
869 return true;
870 } else {
871 return false;
872 }
873}
874
876 for(int32_t i = 0; i < int32_t(mod_list.size()); ++i) {
877 if(mod_list[i].mod_selected) {
878 if(std::find(mod_list[i].dependent_mods.begin(), mod_list[i].dependent_mods.end(), mod.name_) != mod_list[i].dependent_mods.end()) {
879 mod_list[i].mod_selected = false;
880 recursively_remove_from_list(mod_list[i]);
881 }
882 }
883 }
884}
886 for(auto& dep : mod.dependent_mods) {
887 for(int32_t i = 0; i < int32_t(mod_list.size()); ++i) {
888 if(!mod_list[i].mod_selected && mod_list[i].name_ == dep) {
889 mod_list[i].mod_selected = true;
890 recursively_add_to_list(mod_list[i]);
891 }
892 }
893 }
894}
895
896bool transitively_depends_on_internal(parsers::mod_file const& moda, parsers::mod_file const& modb, std::vector<bool>& seen_indices) {
897 for(auto& dep : moda.dependent_mods) {
898 if(dep == modb.name_)
899 return true;
900
901 for(int32_t i = 0; i < int32_t(mod_list.size()); ++i) {
902 if(seen_indices[i] == false && mod_list[i].name_ == dep) {
903 seen_indices[i] = true;
904 if(transitively_depends_on_internal(mod_list[i], modb, seen_indices))
905 return true;
906 }
907 }
908 }
909 return false;
910}
911
913 std::vector<bool> seen_indices;
914 seen_indices.resize(mod_list.size());
915
916 return transitively_depends_on_internal(moda, modb, seen_indices);
917}
918
920 std::stable_sort(mod_list.begin(), mod_list.end(), [&](parsers::mod_file const& a, parsers::mod_file const& b) {
921 if(a.mod_selected && b.mod_selected) {
922 return transitively_depends_on(b, a);
923 } else if(a.mod_selected && !b.mod_selected) {
924 return true;
925 } else if(!a.mod_selected && b.mod_selected) {
926 return false;
927 } else {
928 return a.name_ < b.name_;
929 }
930 });
931}
932
933bool nth_item_can_move_up(int32_t n) {
934 if(n == 0)
935 return false;
936 if(transitively_depends_on(mod_list[n], mod_list[n - 1]))
937 return false;
938
939 return true;
940}
941bool nth_item_can_move_down(int32_t n) {
942 if(n >= int32_t(mod_list.size()) - 1)
943 return false;
944 if(mod_list[n + 1].mod_selected == false)
945 return false;
946 if(transitively_depends_on(mod_list[n + 1], mod_list[n]))
947 return false;
948
949 return true;
950}
951
954 simple_fs::add_root(dummy, NATIVE("."));
955
956 for(int32_t i = 0; i < int32_t(mod_list.size()); ++i) {
957 if(mod_list[i].mod_selected == false)
958 break;
959
960 mod_list[i].add_to_file_system(dummy);
961 }
962
963 return simple_fs::extract_state(dummy);
964}
965
968 auto len = std::min<size_t>(launcher::player_name.length(), sizeof(p.data));
969 std::memcpy(p.data, launcher::player_name.c_str(), len);
970
971 auto settings_location = simple_fs::get_or_create_settings_directory();
972 simple_fs::write_file(settings_location, NATIVE("player_name.dat"), (const char*)&p, sizeof(p));
973}
974
976 native_string ret;
977 constexpr native_char digits[] = NATIVE("0123456789ABCDEF");
978 do {
979 ret += digits[v & 0x0F];
980 v = v >> 4;
981 } while(v != 0);
982
983 return ret;
984}
985
987 file_is_ready.store(false, std::memory_order::memory_order_seq_cst);
988 auto path = produce_mod_path();
989 std::thread file_maker([path]() {
991 simple_fs::restore_state(fs_root, path);
993 auto root = get_root(fs_root);
994 auto common = open_directory(root, NATIVE("common"));
995 parsers::bookmark_context bookmark_context;
996 if(auto f = open_file(common, NATIVE("bookmarks.txt")); f) {
997 auto bookmark_content = simple_fs::view_contents(*f);
998 err.file_name = "bookmarks.txt";
999 parsers::token_generator gen(bookmark_content.data, bookmark_content.data + bookmark_content.file_size);
1000 parsers::parse_bookmark_file(gen, err, bookmark_context);
1001 assert(!bookmark_context.bookmark_dates.empty());
1002 } else {
1003 err.accumulated_errors += "File common/bookmarks.txt could not be opened\n";
1004 }
1005
1006 sys::checksum_key scenario_key;
1007
1008 for(uint32_t date_index = 0; date_index < uint32_t(bookmark_context.bookmark_dates.size()); date_index++) {
1009 err.accumulated_errors.clear();
1010 err.accumulated_warnings.clear();
1011 //
1012 auto game_state = std::make_unique<sys::state>();
1013 simple_fs::restore_state(game_state->common_fs, path);
1014 game_state->load_scenario_data(err, bookmark_context.bookmark_dates[date_index].date_);
1015 if(err.fatal)
1016 break;
1017 if(date_index == 0) {
1019 int32_t append = 0;
1020 auto time_stamp = uint64_t(std::time(0));
1021 auto base_name = to_hex(time_stamp);
1022 while(simple_fs::peek_file(sdir, base_name + NATIVE("-") + std::to_wstring(append) + NATIVE(".bin"))) {
1023 ++append;
1024 }
1025 ++max_scenario_count;
1026 selected_scenario_file = base_name + NATIVE("-") + std::to_wstring(append) + NATIVE(".bin");
1027 sys::write_scenario_file(*game_state, selected_scenario_file, max_scenario_count);
1028 if(auto of = simple_fs::open_file(sdir, selected_scenario_file); of) {
1029 auto content = view_contents(*of);
1030 auto desc = sys::extract_mod_information(reinterpret_cast<uint8_t const*>(content.data), content.file_size);
1031 if(desc.count != 0) {
1032 scenario_files.push_back(scenario_file{ selected_scenario_file , desc });
1033 }
1034 }
1035 std::sort(scenario_files.begin(), scenario_files.end(), [](scenario_file const& a, scenario_file const& b) {
1036 return a.ident.count > b.ident.count;
1037 });
1038 scenario_key = game_state->scenario_checksum;
1039 } else {
1040#ifndef NDEBUG
1041 sys::write_scenario_file(*game_state, std::to_wstring(date_index) + NATIVE(".bin"), 0);
1042#endif
1043 game_state->scenario_checksum = scenario_key;
1044 sys::write_save_file(*game_state, sys::save_type::bookmark, bookmark_context.bookmark_dates[date_index].name_);
1045 }
1046 }
1047
1048 if(!err.accumulated_errors.empty() || !err.accumulated_warnings.empty()) {
1049 auto assembled_file = std::string("You can still play the mod, but it might be unstable\r\nThe following problems were encountered while creating the scenario:\r\n\r\nErrors:\r\n") + err.accumulated_errors + "\r\n\r\nWarnings:\r\n" + err.accumulated_warnings;
1051 simple_fs::write_file(pdir, NATIVE("scenario_errors.txt"), assembled_file.data(), uint32_t(assembled_file.length()));
1052
1053 if(!err.accumulated_errors.empty()) {
1054 auto fname = simple_fs::get_full_name(pdir) + NATIVE("\\scenario_errors.txt");
1055 ShellExecuteW(
1056 nullptr,
1057 L"open",
1058 fname.c_str(),
1059 nullptr,
1060 nullptr,
1061 SW_NORMAL
1062 );
1063 }
1064 }
1065 file_is_ready.store(true, std::memory_order::memory_order_release);
1066 InvalidateRect((HWND)(m_hwnd), nullptr, FALSE);
1067 });
1068
1069 file_maker.detach();
1070}
1071
1073 if(!file_is_ready.load(std::memory_order::memory_order_acquire))
1074 return;
1075
1076 file_is_ready.store(false, std::memory_order::memory_order_seq_cst);
1077 selected_scenario_file = NATIVE("");
1078 auto mod_path = produce_mod_path();
1079
1080 for(auto& f : scenario_files) {
1081 if(f.ident.mod_path == mod_path) {
1082 selected_scenario_file = f.file_name;
1083 break;
1084 }
1085 }
1086 file_is_ready.store(true, std::memory_order::memory_order_release);
1087}
1088
1090 if(obj_under_mouse == -1)
1091 return;
1092
1093 switch(obj_under_mouse) {
1094 case ui_obj_close:
1095 PostMessageW(m_hwnd, WM_CLOSE, 0, 0);
1096 return;
1097 case ui_obj_list_left:
1098 if(frame_in_list > 0) {
1099 --frame_in_list;
1100 InvalidateRect((HWND)(m_hwnd), nullptr, FALSE);
1101 }
1102 return;
1103 case ui_obj_list_right:
1104 if((frame_in_list + 1) * ui_list_count < int32_t(mod_list.size())) {
1105 ++frame_in_list;
1106 InvalidateRect((HWND)(m_hwnd), nullptr, FALSE);
1107 }
1108 return;
1110 if(file_is_ready.load(std::memory_order::memory_order_acquire)) {
1111 make_mod_file();
1112 InvalidateRect((HWND)(m_hwnd), nullptr, FALSE);
1113 }
1114 return;
1115 case ui_obj_play_game:
1116 if(file_is_ready.load(std::memory_order::memory_order_acquire) && !selected_scenario_file.empty()) {
1117 if(IsProcessorFeaturePresent(PF_AVX512F_INSTRUCTIONS_AVAILABLE)) {
1118 native_string temp_command_line = native_string(NATIVE("Alice512.exe ")) + selected_scenario_file;
1119
1120 STARTUPINFO si;
1121 ZeroMemory(&si, sizeof(si));
1122 si.cb = sizeof(si);
1123 PROCESS_INFORMATION pi;
1124 ZeroMemory(&pi, sizeof(pi));
1125 // Start the child process.
1126 if(CreateProcessW(
1127 nullptr, // Module name
1128 const_cast<wchar_t*>(temp_command_line.c_str()), // Command line
1129 nullptr, // Process handle not inheritable
1130 nullptr, // Thread handle not inheritable
1131 FALSE, // Set handle inheritance to FALSE
1132 0, // No creation flags
1133 nullptr, // Use parent's environment block
1134 nullptr, // Use parent's starting directory
1135 &si, // Pointer to STARTUPINFO structure
1136 &pi) != 0) {
1137
1138 CloseHandle(pi.hProcess);
1139 CloseHandle(pi.hThread);
1140
1141 return; // exit -- don't try starting avx2
1142 }
1143 }
1144 if(!IsProcessorFeaturePresent(PF_AVX2_INSTRUCTIONS_AVAILABLE)) {
1145 native_string temp_command_line = native_string(NATIVE("AliceSSE.exe ")) + selected_scenario_file;
1146
1147 STARTUPINFO si;
1148 ZeroMemory(&si, sizeof(si));
1149 si.cb = sizeof(si);
1150 PROCESS_INFORMATION pi;
1151 ZeroMemory(&pi, sizeof(pi));
1152 // Start the child process.
1153 if(CreateProcessW(
1154 nullptr, // Module name
1155 const_cast<wchar_t*>(temp_command_line.c_str()), // Command line
1156 nullptr, // Process handle not inheritable
1157 nullptr, // Thread handle not inheritable
1158 FALSE, // Set handle inheritance to FALSE
1159 0, // No creation flags
1160 nullptr, // Use parent's environment block
1161 nullptr, // Use parent's starting directory
1162 &si, // Pointer to STARTUPINFO structure
1163 &pi) != 0) {
1164
1165 CloseHandle(pi.hProcess);
1166 CloseHandle(pi.hThread);
1167
1168 return; // exit -- don't try starting avx2
1169 }
1170 }
1171 { // normal case (avx2)
1172 native_string temp_command_line = native_string(NATIVE("Alice.exe ")) + selected_scenario_file;
1173
1174 STARTUPINFO si;
1175 ZeroMemory(&si, sizeof(si));
1176 si.cb = sizeof(si);
1177 PROCESS_INFORMATION pi;
1178 ZeroMemory(&pi, sizeof(pi));
1179 // Start the child process.
1180 if(CreateProcessW(
1181 nullptr, // Module name
1182 const_cast<wchar_t*>(temp_command_line.c_str()), // Command line
1183 nullptr, // Process handle not inheritable
1184 nullptr, // Thread handle not inheritable
1185 FALSE, // Set handle inheritance to FALSE
1186 0, // No creation flags
1187 nullptr, // Use parent's environment block
1188 nullptr, // Use parent's starting directory
1189 &si, // Pointer to STARTUPINFO structure
1190 &pi) != 0) {
1191
1192 CloseHandle(pi.hProcess);
1193 CloseHandle(pi.hThread);
1194 }
1195 return;
1196 }
1197 }
1198 case ui_obj_host_game:
1199 case ui_obj_join_game:
1200 if(file_is_ready.load(std::memory_order::memory_order_acquire) && !selected_scenario_file.empty()) {
1201 native_string temp_command_line = native_string(NATIVE("AliceSSE.exe ")) + selected_scenario_file;
1202 if(obj_under_mouse == ui_obj_host_game) {
1203 temp_command_line += NATIVE(" -host");
1204 temp_command_line += NATIVE(" -name ");
1205 temp_command_line += simple_fs::utf8_to_native(player_name);
1206 } else if(obj_under_mouse == ui_obj_join_game) {
1207 temp_command_line += NATIVE(" -join");
1208 temp_command_line += NATIVE(" ");
1209 temp_command_line += simple_fs::utf8_to_native(ip_addr);
1210 temp_command_line += NATIVE(" -name ");
1211 temp_command_line += simple_fs::utf8_to_native(player_name);
1212
1213 // IPv6 address
1214 if(!ip_addr.empty() && ::strchr(ip_addr.c_str(), ':') != nullptr) {
1215 temp_command_line += NATIVE(" -v6");
1216 }
1217 }
1218
1219 if(!password.empty()) {
1220 temp_command_line += NATIVE(" -password ");
1221 temp_command_line += simple_fs::utf8_to_native(password);
1222 }
1223
1224 STARTUPINFO si;
1225 ZeroMemory(&si, sizeof(si));
1226 si.cb = sizeof(si);
1227 PROCESS_INFORMATION pi;
1228 ZeroMemory(&pi, sizeof(pi));
1229 // Start the child process.
1230 if(CreateProcessW(
1231 nullptr, // Module name
1232 const_cast<wchar_t*>(temp_command_line.c_str()), // Command line
1233 nullptr, // Process handle not inheritable
1234 nullptr, // Thread handle not inheritable
1235 FALSE, // Set handle inheritance to FALSE
1236 0, // No creation flags
1237 nullptr, // Use parent's environment block
1238 nullptr, // Use parent's starting directory
1239 &si, // Pointer to STARTUPINFO structure
1240 &pi) != 0) {
1241
1242 CloseHandle(pi.hProcess);
1243 CloseHandle(pi.hThread);
1244 }
1245
1246
1247 // ready to launch
1248 }
1249 return;
1250 case ui_obj_ip_addr:
1251 return;
1252 case ui_obj_password:
1253 return;
1254 case ui_obj_player_name:
1255 return;
1256 default:
1257 break;
1258 }
1259
1260 if(!file_is_ready.load(std::memory_order::memory_order_acquire))
1261 return;
1262
1263 int32_t list_position = (obj_under_mouse - ui_list_first) / 3;
1264 int32_t sub_obj = (obj_under_mouse - ui_list_first) - list_position * 3;
1265
1266 switch(sub_obj) {
1267 case ui_list_checkbox:
1268 {
1269 int32_t list_offset = launcher::frame_in_list * launcher::ui_list_count + list_position;
1270 if(list_offset < int32_t(launcher::mod_list.size())) {
1271 launcher::mod_list[list_offset].mod_selected = !launcher::mod_list[list_offset].mod_selected;
1272 if(!launcher::mod_list[list_offset].mod_selected) {
1273 recursively_remove_from_list(launcher::mod_list[list_offset]);
1274 } else {
1275 recursively_add_to_list(launcher::mod_list[list_offset]);
1276 }
1279 InvalidateRect((HWND)(m_hwnd), nullptr, FALSE);
1280 }
1281 return;
1282 }
1283 case ui_list_move_up:
1284 {
1285 int32_t list_offset = launcher::frame_in_list * launcher::ui_list_count + list_position;
1286 if(launcher::mod_list[list_offset].mod_selected && nth_item_can_move_up(list_offset)) {
1287 std::swap(launcher::mod_list[list_offset], launcher::mod_list[list_offset - 1]);
1289 InvalidateRect((HWND)(m_hwnd), nullptr, FALSE);
1290 }
1291 return;
1292 }
1293 case ui_list_move_down:
1294 {
1295 int32_t list_offset = launcher::frame_in_list * launcher::ui_list_count + list_position;
1296 if(launcher::mod_list[list_offset].mod_selected && nth_item_can_move_down(list_offset)) {
1297 std::swap(launcher::mod_list[list_offset], launcher::mod_list[list_offset + 1]);
1299 InvalidateRect((HWND)(m_hwnd), nullptr, FALSE);
1300 }
1301 return;
1302 }
1303 default:
1304 break;
1305 }
1306}
1307
1308GLint compile_shader(std::string_view source, GLenum type) {
1309 GLuint return_value = glCreateShader(type);
1310
1311 if(return_value == 0) {
1312 MessageBoxW(m_hwnd, L"shader creation failed", L"OpenGL error", MB_OK);
1313 }
1314
1315 std::string s_source(source);
1316 GLchar const* texts[] = {
1317 "#version 140\r\n",
1318 "#extension GL_ARB_explicit_uniform_location : enable\r\n",
1319 "#extension GL_ARB_explicit_attrib_location : enable\r\n",
1320 "#extension GL_ARB_shader_subroutine : enable\r\n",
1321 "#define M_PI 3.1415926535897932384626433832795\r\n",
1322 "#define PI 3.1415926535897932384626433832795\r\n",
1323 s_source.c_str()
1324 };
1325 glShaderSource(return_value, 7, texts, nullptr);
1326 glCompileShader(return_value);
1327
1328 GLint result;
1329 glGetShaderiv(return_value, GL_COMPILE_STATUS, &result);
1330 if(result == GL_FALSE) {
1331 GLint log_length = 0;
1332 glGetShaderiv(return_value, GL_INFO_LOG_LENGTH, &log_length);
1333
1334 auto log = std::unique_ptr<char[]>(new char[static_cast<size_t>(log_length)]);
1335 GLsizei written = 0;
1336 glGetShaderInfoLog(return_value, log_length, &written, log.get());
1337 auto error = std::string("Shader failed to compile:\n") + log.get();
1338 MessageBoxA(m_hwnd, error.c_str(), "OpenGL error", MB_OK);
1339 }
1340 return return_value;
1341}
1342
1343GLuint create_program(std::string_view vertex_shader, std::string_view fragment_shader) {
1344 GLuint return_value = glCreateProgram();
1345 if(return_value == 0) {
1346 MessageBoxW(m_hwnd, L"program creation failed", L"OpenGL error", MB_OK);
1347 }
1348
1349 auto v_shader = compile_shader(vertex_shader, GL_VERTEX_SHADER);
1350 auto f_shader = compile_shader(fragment_shader, GL_FRAGMENT_SHADER);
1351
1352 glAttachShader(return_value, v_shader);
1353 glAttachShader(return_value, f_shader);
1354 glLinkProgram(return_value);
1355
1356 GLint result;
1357 glGetProgramiv(return_value, GL_LINK_STATUS, &result);
1358 if(result == GL_FALSE) {
1359 GLint logLen;
1360 glGetProgramiv(return_value, GL_INFO_LOG_LENGTH, &logLen);
1361
1362 char* log = new char[static_cast<size_t>(logLen)];
1363 GLsizei written;
1364 glGetProgramInfoLog(return_value, logLen, &written, log);
1365 auto err = std::string("Program failed to link:\n") + log;
1366 MessageBoxA(m_hwnd, err.c_str(), "OpenGL error", MB_OK);
1367 }
1368
1369 glDeleteShader(v_shader);
1370 glDeleteShader(f_shader);
1371
1372 return return_value;
1373}
1374
1375static GLfloat global_square_data[] = { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 0.0f, 1.0f, 0.0f };
1376static GLfloat global_square_right_data[] = { 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f };
1377static GLfloat global_square_left_data[] = { 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f, 1.0f, 0.0f, 1.0f, 1.0f, 0.0f, 1.0f, 1.0f };
1378static GLfloat global_square_flipped_data[] = { 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f, 1.0f, 1.0f, 0.0f, 1.0f, 0.0f, 1.0f, 1.0f };
1379static GLfloat global_square_right_flipped_data[] = { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 1.0f, 0.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 0.0f, 0.0f, 1.0f };
1380static GLfloat global_square_left_flipped_data[] = { 0.0f, 0.0f, 1.0f, 1.0f, 0.0f, 1.0f, 0.0f, 1.0f, 1.0f, 1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f };
1381
1382static GLuint ui_shader_program = 0;
1383
1384void load_shaders() {
1386 simple_fs::add_root(fs, NATIVE("."));
1387 auto root = get_root(fs);
1388
1389 std::string_view fx_str =
1390 "in vec2 tex_coord;\n"
1391 "out vec4 frag_color;\n"
1392 "uniform sampler2D texture_sampler;\n"
1393 "uniform vec4 d_rect;\n"
1394 "uniform float border_size;\n"
1395 "uniform vec3 inner_color;\n"
1396 "uniform vec4 subrect;\n"
1397 "uniform uvec2 subroutines_index;\n"
1398 "vec4 color_filter(vec2 tc) {\n"
1399 "\tvec4 color_in = texture(texture_sampler, tc);\n"
1400 "\tfloat sm_val = smoothstep(0.5 - border_size / 2.0, 0.5 + border_size / 2.0, color_in.r);\n"
1401 "\treturn vec4(inner_color, sm_val);\n"
1402 "}\n"
1403 "vec4 no_filter(vec2 tc) {\n"
1404 "\treturn texture(texture_sampler, tc);\n"
1405 "}\n"
1406 "vec4 disabled_color(vec4 color_in) {\n"
1407 "\tfloat amount = (color_in.r + color_in.g + color_in.b) / 4.0;\n"
1408 "\treturn vec4(amount, amount, amount, color_in.a);\n"
1409 "}\n"
1410 "vec4 interactable_color(vec4 color_in) {\n"
1411 "\treturn vec4(color_in.r + 0.1, color_in.g + 0.1, color_in.b + 0.1, color_in.a);\n"
1412 "}\n"
1413 "vec4 interactable_disabled_color(vec4 color_in) {\n"
1414 "\tfloat amount = (color_in.r + color_in.g + color_in.b) / 4.0;\n"
1415 "\treturn vec4(amount + 0.1, amount + 0.1, amount + 0.1, color_in.a);\n"
1416 "}\n"
1417 "vec4 tint_color(vec4 color_in) {\n"
1418 "\treturn vec4(color_in.r * inner_color.r, color_in.g * inner_color.g, color_in.b * inner_color.b, color_in.a);\n"
1419 "}\n"
1420 "vec4 enabled_color(vec4 color_in) {\n"
1421 "\treturn color_in;\n"
1422 "}\n"
1423 "vec4 alt_tint_color(vec4 color_in) {\n"
1424 "\treturn vec4(color_in.r * subrect.r, color_in.g * subrect.g, color_in.b * subrect.b, color_in.a);\n"
1425 "}\n"
1426 "vec4 font_function(vec2 tc) {\n"
1427 "\treturn int(subroutines_index.y) == 1 ? color_filter(tc) : no_filter(tc);\n"
1428 "}\n"
1429 "vec4 coloring_function(vec4 tc) {\n"
1430 "\tswitch(int(subroutines_index.x)) {\n"
1431 "\tcase 3: return disabled_color(tc);\n"
1432 "\tcase 4: return enabled_color(tc);\n"
1433 "\tcase 12: return tint_color(tc);\n"
1434 "\tcase 13: return interactable_color(tc);\n"
1435 "\tcase 14: return interactable_disabled_color(tc);\n"
1436 "\tcase 16: return alt_tint_color(tc);\n"
1437 "\tdefault: break;\n"
1438 "\t}\n"
1439 "\treturn tc;\n"
1440 "}\n"
1441 "void main() {\n"
1442 "\tfrag_color = coloring_function(font_function(tex_coord));\n"
1443 "}";
1444 std::string_view vx_str =
1445 "layout (location = 0) in vec2 vertex_position;\n"
1446 "layout (location = 1) in vec2 v_tex_coord;\n"
1447 "out vec2 tex_coord;\n"
1448 "uniform float screen_width;\n"
1449 "uniform float screen_height;\n"
1450 "uniform vec4 d_rect;\n"
1451 "void main() {\n"
1452 "\tgl_Position = vec4(\n"
1453 "\t\t-1.0 + (2.0 * ((vertex_position.x * d_rect.z) + d_rect.x) / screen_width),\n"
1454 "\t\t 1.0 - (2.0 * ((vertex_position.y * d_rect.w) + d_rect.y) / screen_height),\n"
1455 "\t\t0.0, 1.0);\n"
1456 "\ttex_coord = v_tex_coord;\n"
1457 "}";
1458
1459 ui_shader_program = create_program(vx_str, fx_str);
1460}
1461
1462static GLuint global_square_vao = 0;
1463static GLuint global_square_buffer = 0;
1464static GLuint global_square_right_buffer = 0;
1465static GLuint global_square_left_buffer = 0;
1466static GLuint global_square_flipped_buffer = 0;
1467static GLuint global_square_right_flipped_buffer = 0;
1468static GLuint global_square_left_flipped_buffer = 0;
1469
1470static GLuint sub_square_buffers[64] = { 0 };
1471
1472void load_global_squares() {
1473 glGenBuffers(1, &global_square_buffer);
1474
1475 // Populate the position buffer
1476 glBindBuffer(GL_ARRAY_BUFFER, global_square_buffer);
1477 glBufferData(GL_ARRAY_BUFFER, sizeof(GLfloat) * 16, global_square_data, GL_STATIC_DRAW);
1478
1479 glGenVertexArrays(1, &global_square_vao);
1480 glBindVertexArray(global_square_vao);
1481 glEnableVertexAttribArray(0); // position
1482 glEnableVertexAttribArray(1); // texture coordinates
1483
1484 glBindVertexBuffer(0, global_square_buffer, 0, sizeof(GLfloat) * 4);
1485
1486 glVertexAttribFormat(0, 2, GL_FLOAT, GL_FALSE, 0); // position
1487 glVertexAttribFormat(1, 2, GL_FLOAT, GL_FALSE, sizeof(GLfloat) * 2); // texture coordinates
1488 glVertexAttribBinding(0, 0); // position -> to array zero
1489 glVertexAttribBinding(1, 0); // texture coordinates -> to array zero
1490
1491 glGenBuffers(1, &global_square_left_buffer);
1492 glBindBuffer(GL_ARRAY_BUFFER, global_square_left_buffer);
1493 glBufferData(GL_ARRAY_BUFFER, sizeof(GLfloat) * 16, global_square_left_data, GL_STATIC_DRAW);
1494
1495 glGenBuffers(1, &global_square_right_buffer);
1496 glBindBuffer(GL_ARRAY_BUFFER, global_square_right_buffer);
1497 glBufferData(GL_ARRAY_BUFFER, sizeof(GLfloat) * 16, global_square_right_data, GL_STATIC_DRAW);
1498
1499 glGenBuffers(1, &global_square_right_flipped_buffer);
1500 glBindBuffer(GL_ARRAY_BUFFER, global_square_right_flipped_buffer);
1501 glBufferData(GL_ARRAY_BUFFER, sizeof(GLfloat) * 16, global_square_right_flipped_data, GL_STATIC_DRAW);
1502
1503 glGenBuffers(1, &global_square_left_flipped_buffer);
1504 glBindBuffer(GL_ARRAY_BUFFER, global_square_left_flipped_buffer);
1505 glBufferData(GL_ARRAY_BUFFER, sizeof(GLfloat) * 16, global_square_left_flipped_data, GL_STATIC_DRAW);
1506
1507 glGenBuffers(1, &global_square_flipped_buffer);
1508 glBindBuffer(GL_ARRAY_BUFFER, global_square_flipped_buffer);
1509 glBufferData(GL_ARRAY_BUFFER, sizeof(GLfloat) * 16, global_square_flipped_data, GL_STATIC_DRAW);
1510
1511 glGenBuffers(64, sub_square_buffers);
1512 for(uint32_t i = 0; i < 64; ++i) {
1513 glBindBuffer(GL_ARRAY_BUFFER, sub_square_buffers[i]);
1514
1515 float const cell_x = static_cast<float>(i & 7) / 8.0f;
1516 float const cell_y = static_cast<float>((i >> 3) & 7) / 8.0f;
1517
1518 GLfloat global_sub_square_data[] = { 0.0f, 0.0f, cell_x, cell_y, 0.0f, 1.0f, cell_x, cell_y + 1.0f / 8.0f, 1.0f, 1.0f,
1519 cell_x + 1.0f / 8.0f, cell_y + 1.0f / 8.0f, 1.0f, 0.0f, cell_x + 1.0f / 8.0f, cell_y };
1520
1521 glBufferData(GL_ARRAY_BUFFER, sizeof(GLfloat) * 16, global_sub_square_data, GL_STATIC_DRAW);
1522 }
1523}
1524
1525
1526namespace ogl {
1527namespace parameters {
1528
1529inline constexpr GLuint screen_width = 0;
1530inline constexpr GLuint screen_height = 1;
1531inline constexpr GLuint drawing_rectangle = 2;
1532
1533inline constexpr GLuint border_size = 6;
1534inline constexpr GLuint inner_color = 7;
1535inline constexpr GLuint subrect = 10;
1536
1537inline constexpr GLuint enabled = 4;
1538inline constexpr GLuint disabled = 3;
1539inline constexpr GLuint border_filter = 0;
1540inline constexpr GLuint filter = 1;
1541inline constexpr GLuint no_filter = 2;
1542inline constexpr GLuint sub_sprite = 5;
1543inline constexpr GLuint use_mask = 6;
1544inline constexpr GLuint progress_bar = 7;
1545inline constexpr GLuint frame_stretch = 8;
1546inline constexpr GLuint piechart = 9;
1547inline constexpr GLuint barchart = 10;
1548inline constexpr GLuint linegraph = 11;
1549inline constexpr GLuint tint = 12;
1550inline constexpr GLuint interactable = 13;
1551inline constexpr GLuint interactable_disabled = 14;
1552inline constexpr GLuint subsprite_b = 15;
1553inline constexpr GLuint atlas_index = 18;
1554
1555} // namespace parameters
1556
1559};
1560
1561struct color3f {
1562 float r = 0.0f;
1563 float g = 0.0f;
1564 float b = 0.0f;
1565};
1566
1568 switch(e) {
1570 return parameters::disabled;
1575 default:
1577 return parameters::enabled;
1578 }
1579}
1580
1582 switch(r) {
1584 if(!flipped)
1585 glBindVertexBuffer(0, global_square_buffer, 0, sizeof(GLfloat) * 4);
1586 else
1587 glBindVertexBuffer(0, global_square_flipped_buffer, 0, sizeof(GLfloat) * 4);
1588 break;
1590 if(!flipped)
1591 glBindVertexBuffer(0, global_square_left_buffer, 0, sizeof(GLfloat) * 4);
1592 else
1593 glBindVertexBuffer(0, global_square_left_flipped_buffer, 0, sizeof(GLfloat) * 4);
1594 break;
1596 if(!flipped)
1597 glBindVertexBuffer(0, global_square_right_buffer, 0, sizeof(GLfloat) * 4);
1598 else
1599 glBindVertexBuffer(0, global_square_right_flipped_buffer, 0, sizeof(GLfloat) * 4);
1600 break;
1601 }
1602}
1603
1604void render_textured_rect(color_modification enabled, int32_t ix, int32_t iy, int32_t iwidth, int32_t iheight, GLuint texture_handle, ui::rotation r, bool flipped) {
1605 float x = float(ix);
1606 float y = float(iy);
1607 float width = float(iwidth);
1608 float height = float(iheight);
1609
1610 glBindVertexArray(global_square_vao);
1611
1612 bind_vertices_by_rotation(r, flipped);
1613
1614 glUniform4f(glGetUniformLocation(ui_shader_program, "d_rect"), x, y, width, height);
1615 // glUniform4f(glGetUniformLocation(ui_shader_program, "d_rect"), 0, 0, width, height);
1616
1617 glActiveTexture(GL_TEXTURE0);
1618 glBindTexture(GL_TEXTURE_2D, texture_handle);
1619
1620 GLuint subroutines[2] = { map_color_modification_to_index(enabled), parameters::no_filter };
1621 glUniform2ui(glGetUniformLocation(ui_shader_program, "subroutines_index"), subroutines[0], subroutines[1]);
1622 //glUniformSubroutinesuiv(GL_FRAGMENT_SHADER, 2, subroutines); // must set all subroutines in one call
1623
1624 glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
1625}
1626
1627void internal_text_render(std::string_view str, float baseline_x, float baseline_y, float size, ::text::font& f) {
1628 hb_buffer_clear_contents(f.hb_buf);
1629 hb_buffer_add_utf8(f.hb_buf, str.data(), int(str.size()), 0, int(str.size()));
1630 hb_buffer_guess_segment_properties(f.hb_buf);
1631 hb_shape(f.hb_font_face, f.hb_buf, NULL, 0);
1632 unsigned int glyph_count = 0;
1633 hb_glyph_info_t* glyph_info = hb_buffer_get_glyph_infos(f.hb_buf, &glyph_count);
1634 hb_glyph_position_t* glyph_pos = hb_buffer_get_glyph_positions(f.hb_buf, &glyph_count);
1635 float x = baseline_x;
1636 for(unsigned int i = 0; i < glyph_count; i++) {
1637 f.make_glyph(glyph_info[i].codepoint);
1638 }
1639 for(unsigned int i = 0; i < glyph_count; i++) {
1640 hb_codepoint_t glyphid = glyph_info[i].codepoint;
1641 auto gso = f.glyph_positions[glyphid];
1642 float x_advance = float(glyph_pos[i].x_advance) / (float((1 << 6) * text::magnification_factor));
1643 float x_offset = float(glyph_pos[i].x_offset) / (float((1 << 6) * text::magnification_factor)) + float(gso.x);
1644 float y_offset = float(gso.y) - float(glyph_pos[i].y_offset) / (float((1 << 6) * text::magnification_factor));
1645 if(glyphid != FT_Get_Char_Index(f.font_face, ' ')) {
1646 glBindVertexBuffer(0, sub_square_buffers[f.glyph_positions[glyphid].texture_slot & 63], 0, sizeof(GLfloat) * 4);
1647 glActiveTexture(GL_TEXTURE0);
1648 glBindTexture(GL_TEXTURE_2D, f.textures[f.glyph_positions[glyphid].texture_slot >> 6]);
1649 glUniform4f(glGetUniformLocation(ui_shader_program, "d_rect"), x + x_offset * size / 64.f, baseline_y + y_offset * size / 64.f, size, size);
1650 glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
1651 }
1652 x += x_advance * size / 64.f;
1653 }
1654}
1655
1656void render_new_text(std::string_view sv, color_modification enabled, float x, float y, float size, color3f const& c, ::text::font& f) {
1657 glUniform3f(glGetUniformLocation(ui_shader_program, "inner_color"), c.r, c.g, c.b);
1658 glUniform1f(glGetUniformLocation(ui_shader_program, "border_size"), 0.08f * 16.0f / size);
1659
1660 GLuint subroutines[2] = { map_color_modification_to_index(enabled), parameters::filter };
1661 glUniform2ui(glGetUniformLocation(ui_shader_program, "subroutines_index"), subroutines[0], subroutines[1]);
1662 //glUniformSubroutinesuiv(GL_FRAGMENT_SHADER, 2, subroutines); // must set all subroutines in one call
1663 internal_text_render(sv, x, y + size, size, f);
1664}
1665
1666} // launcher::ogl
1667
1668static ::text::font_manager font_collection; //keep static because it uninits FT lib on destructor
1669static ::text::font fonts[2];
1670
1671static ::ogl::texture bg_tex;
1672static ::ogl::texture left_tex;
1673static ::ogl::texture right_tex;
1674static ::ogl::texture close_tex;
1675static ::ogl::texture big_button_tex;
1676static ::ogl::texture empty_check_tex;
1677static ::ogl::texture check_tex;
1678static ::ogl::texture up_tex;
1679static ::ogl::texture down_tex;
1680static ::ogl::texture line_bg_tex;
1681static ::ogl::texture big_l_button_tex;
1682static ::ogl::texture big_r_button_tex;
1683static ::ogl::texture warning_tex;
1684
1685float base_text_extent(char const* codepoints, uint32_t count, int32_t size, text::font& f) {
1686 hb_buffer_clear_contents(f.hb_buf);
1687 hb_buffer_add_utf8(f.hb_buf, codepoints, int(count), 0, int(count));
1688 hb_buffer_guess_segment_properties(f.hb_buf);
1689 hb_shape(f.hb_font_face, f.hb_buf, NULL, 0);
1690 unsigned int glyph_count = 0;
1691 hb_glyph_info_t* glyph_info = hb_buffer_get_glyph_infos(f.hb_buf, &glyph_count);
1692 hb_glyph_position_t* glyph_pos = hb_buffer_get_glyph_positions(f.hb_buf, &glyph_count);
1693 float x = 0.0f;
1694 for(unsigned int i = 0; i < glyph_count; i++) {
1695 f.make_glyph(glyph_info[i].codepoint);
1696 }
1697 for(unsigned int i = 0; i < glyph_count; i++) {
1698 hb_codepoint_t glyphid = glyph_info[i].codepoint;
1699 auto gso = f.glyph_positions[glyphid];
1700 float x_advance = float(glyph_pos[i].x_advance) / (float((1 << 6) * text::magnification_factor));
1701 float x_offset = float(glyph_pos[i].x_offset) / (float((1 << 6) * text::magnification_factor)) + float(gso.x);
1702 float y_offset = float(gso.y) - float(glyph_pos[i].y_offset) / (float((1 << 6) * text::magnification_factor));
1703 x += x_advance * size / 64.f;
1704 }
1705 return x;
1706}
1707
1708void render() {
1709 if(!opengl_context)
1710 return;
1711
1712 glClearColor(0.5f, 0.5f, 0.5f, 1.0f);
1713 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
1714
1715 glUseProgram(ui_shader_program);
1716 glUniform1i(glGetUniformLocation(ui_shader_program, "texture_sampler"), 0);
1717 glUniform1f(glGetUniformLocation(ui_shader_program, "screen_width"), float(base_width));
1718 glUniform1f(glGetUniformLocation(ui_shader_program, "screen_height"), float(base_height));
1719 glEnable(GL_BLEND);
1720 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
1721
1722 glViewport(0, 0, int32_t(base_width * scaling_factor), int32_t(base_height * scaling_factor));
1723 glDepthRange(-1.0f, 1.0f);
1724
1726
1727 launcher::ogl::render_new_text("Project Alice", launcher::ogl::color_modification::none, 83, 5, 26, launcher::ogl::color3f{ 255.0f / 255.0f, 230.0f / 255.0f, 153.0f / 255.0f }, fonts[1]);
1728
1732 ui_rects[ui_obj_close].width,
1733 ui_rects[ui_obj_close].height,
1734 close_tex.get_texture_handle(), ui::rotation::upright, false);
1735
1736 if(int32_t(mod_list.size()) > ui_list_count) {
1737 if(frame_in_list > 0) {
1742 ui_rects[ui_obj_list_left].height,
1743 left_tex.get_texture_handle(), ui::rotation::upright, false);
1744 } else {
1749 ui_rects[ui_obj_list_left].height,
1750 left_tex.get_texture_handle(), ui::rotation::upright, false);
1751 }
1752
1753 if((frame_in_list + 1) * ui_list_count < int32_t(mod_list.size())) {
1759 right_tex.get_texture_handle(), ui::rotation::upright, false);
1760 } else {
1766 right_tex.get_texture_handle(), ui::rotation::upright, false);
1767 }
1768 }
1769
1770 if(file_is_ready.load(std::memory_order::memory_order_acquire)) {
1776 big_button_tex.get_texture_handle(), ui::rotation::upright, false);
1777 if(game_dir_not_found) {
1781 44,
1782 33,
1783 warning_tex.get_texture_handle(), ui::rotation::upright, false);
1784 }
1785
1786 if(selected_scenario_file.empty()) {
1787 auto sv = launcher::localised_strings[uint8_t(launcher::string_index::create_scenario)];
1788 float x_pos = ui_rects[ui_obj_create_scenario].x + ui_rects[ui_obj_create_scenario].width / 2 - base_text_extent(sv.data(), uint32_t(sv.size()), 22, fonts[1]) / 2.0f;
1789 launcher::ogl::render_new_text(sv.data(), launcher::ogl::color_modification::none, x_pos, ui_rects[ui_obj_create_scenario].y + 2.f, 22.0f, launcher::ogl::color3f{ 50.0f / 255.0f, 50.0f / 255.0f, 50.0f / 255.0f }, fonts[1]);
1790 } else {
1791 auto sv = launcher::localised_strings[uint8_t(launcher::string_index::recreate_scenario)];
1792 float x_pos = ui_rects[ui_obj_create_scenario].x + ui_rects[ui_obj_create_scenario].width / 2 - base_text_extent(sv.data(), uint32_t(sv.size()), 22, fonts[1]) / 2.0f;
1793 launcher::ogl::render_new_text(sv.data(), launcher::ogl::color_modification::none, x_pos, ui_rects[ui_obj_create_scenario].y + 2.f, 22.0f, launcher::ogl::color3f{ 50.0f / 255.0f, 50.0f / 255.0f, 50.0f / 255.0f }, fonts[1]);
1794 }
1795 } else {
1801 big_button_tex.get_texture_handle(), ui::rotation::upright, false);
1802 if(game_dir_not_found) {
1806 44,
1807 33,
1808 warning_tex.get_texture_handle(), ui::rotation::upright, false);
1809 }
1810 auto sv = launcher::localised_strings[uint8_t(launcher::string_index::working)];
1811 float x_pos = ui_rects[ui_obj_create_scenario].x + ui_rects[ui_obj_create_scenario].width / 2 - base_text_extent(sv.data(), uint32_t(sv.size()), 22, fonts[1]) / 2.0f;
1812 launcher::ogl::render_new_text(sv.data(), launcher::ogl::color_modification::none, x_pos, 50.0f, 22.0f, launcher::ogl::color3f{ 50.0f / 255.0f, 50.0f / 255.0f, 50.0f / 255.0f }, fonts[1]);
1813 }
1814
1815 {
1816 // Create a new scenario file for the selected mods
1817 auto sv = launcher::localised_strings[uint8_t(launcher::string_index::create_a_new_scenario)];
1818 auto xoffset = 830.0f - base_text_extent(sv.data(), uint32_t(sv.size()), 14, fonts[0]);
1819 launcher::ogl::render_new_text(sv.data(), launcher::ogl::color_modification::none, xoffset, 94.0f + 0 * 18.0f, 14.0f, launcher::ogl::color3f{ 255.0f / 255.0f, 230.0f / 255.0f, 153.0f / 255.0f }, fonts[0]);
1820 sv = launcher::localised_strings[uint8_t(launcher::string_index::for_the_selected_mods)];
1821 xoffset = 830.0f - base_text_extent(sv.data(), uint32_t(sv.size()), 14, fonts[0]);
1822 launcher::ogl::render_new_text(sv.data(), launcher::ogl::color_modification::none, xoffset, 94.0f + 1 * 18.0f, 14.0f, launcher::ogl::color3f{ 255.0f / 255.0f, 230.0f / 255.0f, 153.0f / 255.0f }, fonts[0]);
1823 }
1824
1825 if(file_is_ready.load(std::memory_order::memory_order_acquire) && !selected_scenario_file.empty()) {
1830 ui_rects[ui_obj_play_game].height,
1831 big_button_tex.get_texture_handle(), ui::rotation::upright, false);
1836 ui_rects[ui_obj_host_game].height,
1837 big_l_button_tex.get_texture_handle(), ui::rotation::upright, false);
1842 ui_rects[ui_obj_join_game].height,
1843 big_r_button_tex.get_texture_handle(), ui::rotation::upright, false);
1844 } else {
1849 ui_rects[ui_obj_play_game].height,
1850 big_button_tex.get_texture_handle(), ui::rotation::upright, false);
1855 ui_rects[ui_obj_host_game].height,
1856 big_l_button_tex.get_texture_handle(), ui::rotation::upright, false);
1861 ui_rects[ui_obj_join_game].height,
1862 big_r_button_tex.get_texture_handle(), ui::rotation::upright, false);
1863
1864 /*830, 250*/
1865 // No scenario file found
1866
1867 auto sv = launcher::localised_strings[uint8_t(launcher::string_index::no_scenario_found)];
1868 auto xoffset = 830.0f - base_text_extent(sv.data(), uint32_t(sv.size()), 14, fonts[0]);
1869 launcher::ogl::render_new_text(sv.data(), launcher::ogl::color_modification::none, xoffset, ui_rects[ui_obj_play_game].y + 48.f, 14.0f, launcher::ogl::color3f{ 255.0f / 255.0f, 230.0f / 255.0f, 153.0f / 255.0f }, fonts[0]);
1870 }
1871
1872 auto sv = launcher::localised_strings[uint8_t(launcher::string_index::ip_address)];
1873 launcher::ogl::render_new_text(sv.data(), launcher::ogl::color_modification::none, ui_rects[ui_obj_ip_addr].x + ui_rects[ui_obj_ip_addr].width - base_text_extent(sv.data(), uint32_t(sv.size()), 14, fonts[0]), ui_rects[ui_obj_ip_addr].y - 21.f, 14.0f, launcher::ogl::color3f{ 255.0f / 255.0f, 230.0f / 255.0f, 153.0f / 255.0f }, fonts[0]);
1877 ui_rects[ui_obj_ip_addr].width,
1878 ui_rects[ui_obj_ip_addr].height,
1879 line_bg_tex.get_texture_handle(), ui::rotation::upright, false);
1880
1881 sv = launcher::localised_strings[uint8_t(launcher::string_index::password)];
1882 launcher::ogl::render_new_text(sv.data(), launcher::ogl::color_modification::none, ui_rects[ui_obj_password].x + ui_rects[ui_obj_password].width - base_text_extent(sv.data(), uint32_t(sv.size()), 14, fonts[0]), ui_rects[ui_obj_password].y - 21.f, 14.0f, launcher::ogl::color3f{ 255.0f / 255.0f, 230.0f / 255.0f, 153.0f / 255.0f }, fonts[0]);
1887 ui_rects[ui_obj_password].height,
1888 line_bg_tex.get_texture_handle(), ui::rotation::upright, false);
1889
1890 sv = launcher::localised_strings[uint8_t(launcher::string_index::nickname)];
1891 launcher::ogl::render_new_text(sv.data(), launcher::ogl::color_modification::none, ui_rects[ui_obj_player_name].x + ui_rects[ui_obj_player_name].width - base_text_extent(sv.data(), uint32_t(sv.size()), 14, fonts[0]), ui_rects[ui_obj_player_name].y - 21.f, 14.0f, launcher::ogl::color3f{ 255.0f / 255.0f, 230.0f / 255.0f, 153.0f / 255.0f }, fonts[0]);
1897 line_bg_tex.get_texture_handle(), ui::rotation::upright, false);
1898
1899 sv = launcher::localised_strings[uint8_t(launcher::string_index::singleplayer)];
1900 launcher::ogl::render_new_text(sv.data(), launcher::ogl::color_modification::none, ui_rects[ui_obj_play_game].x + ui_rects[ui_obj_play_game].width - base_text_extent(sv.data(), uint32_t(sv.size()), 22, fonts[1]), ui_rects[ui_obj_play_game].y - 32.f, 22.0f, launcher::ogl::color3f{ 255.0f / 255.0f, 230.0f / 255.0f, 153.0f / 255.0f }, fonts[1]);
1901
1902 sv = launcher::localised_strings[uint8_t(launcher::string_index::start_game)];
1903 float sg_x_pos = ui_rects[ui_obj_play_game].x + ui_rects[ui_obj_play_game].width / 2 - base_text_extent(sv.data(), uint32_t(sv.size()), 22, fonts[1]) / 2.0f;
1904 launcher::ogl::render_new_text(sv.data(), launcher::ogl::color_modification::none, sg_x_pos, ui_rects[ui_obj_play_game].y + 2.f, 22.0f, launcher::ogl::color3f{ 50.0f / 255.0f, 50.0f / 255.0f, 50.0f / 255.0f }, fonts[1]);
1905
1906 sv = launcher::localised_strings[uint8_t(launcher::string_index::multiplayer)];
1907 launcher::ogl::render_new_text(sv.data(), launcher::ogl::color_modification::none, ui_rects[ui_obj_join_game].x + ui_rects[ui_obj_join_game].width - base_text_extent(sv.data(), uint32_t(sv.size()), 22, fonts[1]), ui_rects[ui_obj_host_game].y - 32.f, 22.0f, launcher::ogl::color3f{ 255.0f / 255.0f, 230.0f / 255.0f, 153.0f / 255.0f }, fonts[1]);
1908
1909 // Join and host game buttons
1910 sv = launcher::localised_strings[uint8_t(launcher::string_index::host)];
1911 float hg_x_pos = ui_rects[ui_obj_host_game].x + ui_rects[ui_obj_host_game].width / 2 - base_text_extent(sv.data(), uint32_t(sv.size()), 22, fonts[1]) / 2.0f;
1912 launcher::ogl::render_new_text(sv.data(), launcher::ogl::color_modification::none, hg_x_pos, ui_rects[ui_obj_host_game].y + 2.f, 22.0f, launcher::ogl::color3f{ 50.0f / 255.0f, 50.0f / 255.0f, 50.0f / 255.0f }, fonts[1]);
1913 sv = launcher::localised_strings[uint8_t(launcher::string_index::join)];
1914 float jg_x_pos = ui_rects[ui_obj_join_game].x + ui_rects[ui_obj_join_game].width / 2 - base_text_extent(sv.data(), uint32_t(sv.size()), 22, fonts[1]) / 2.0f;
1915 launcher::ogl::render_new_text(sv.data(), launcher::ogl::color_modification::none, jg_x_pos, ui_rects[ui_obj_join_game].y + 2.f, 22.0f, launcher::ogl::color3f{ 50.0f / 255.0f, 50.0f / 255.0f, 50.0f / 255.0f }, fonts[1]);
1916
1917 // Text fields
1918 float ia_x_pos = ui_rects[ui_obj_ip_addr].x + 6.f;// ui_rects[ui_obj_ip_addr].width - base_text_extent(ip_addr.c_str(), uint32_t(ip_addr.length()), 14, fonts[0]) - 4.f;
1919 launcher::ogl::render_new_text(ip_addr.c_str(), launcher::ogl::color_modification::none, ia_x_pos, ui_rects[ui_obj_ip_addr].y + 3.f, 14.0f, launcher::ogl::color3f{ 255.0f, 255.0f, 255.0f }, fonts[0]);
1920 float ps_x_pos = ui_rects[ui_obj_password].x + 6.f;
1921 launcher::ogl::render_new_text(password.c_str(), launcher::ogl::color_modification::none, ia_x_pos, ui_rects[ui_obj_password].y + 3.f, 14.0f, launcher::ogl::color3f{ 255.0f, 255.0f, 255.0f }, fonts[0]);
1922 float pn_x_pos = ui_rects[ui_obj_player_name].x + 6.f;// ui_rects[ui_obj_player_name].width - base_text_extent(player_name.c_str(), uint32_t(player_name.length()), 14, fonts[0]) - 4.f;
1923 launcher::ogl::render_new_text(player_name.c_str(), launcher::ogl::color_modification::none, pn_x_pos, ui_rects[ui_obj_player_name].y + 3.f, 14.0f, launcher::ogl::color3f{ 255.0f, 255.0f, 255.0f }, fonts[0]);
1924
1925 sv = launcher::localised_strings[uint8_t(launcher::string_index::mod_list)];
1926 auto ml_xoffset = list_text_right_align - base_text_extent(sv.data(), uint32_t(sv.size()), 24, fonts[1]);
1927 launcher::ogl::render_new_text(sv.data(), launcher::ogl::color_modification::none, ml_xoffset, 45.0f, 24.0f, launcher::ogl::color3f{ 255.0f / 255.0f, 230.0f / 255.0f, 153.0f / 255.0f }, fonts[1]);
1928
1929 int32_t list_offset = launcher::frame_in_list * launcher::ui_list_count;
1930
1931 for(int32_t i = 0; i < ui_list_count && list_offset + i < int32_t(mod_list.size()); ++i) {
1932 auto& mod_ref = mod_list[list_offset + i];
1933
1934 if(i % 2 == 1) {
1937 60,
1938 75 + ui_row_height * i,
1939 440,
1941 launcher::line_bg_tex.get_texture_handle(), ui::rotation::upright, false);
1942 }
1943
1944 if(mod_ref.mod_selected) {
1948 ui_rects[ui_list_first + 3 * i + ui_list_checkbox].width,
1949 ui_rects[ui_list_first + 3 * i + ui_list_checkbox].height,
1950 check_tex.get_texture_handle(), ui::rotation::upright, false);
1951
1952 if(!nth_item_can_move_up(list_offset + i)) {
1956 ui_rects[ui_list_first + 3 * i + ui_list_move_up].width,
1957 ui_rects[ui_list_first + 3 * i + ui_list_move_up].height,
1958 up_tex.get_texture_handle(), ui::rotation::upright, false);
1959 } else {
1963 ui_rects[ui_list_first + 3 * i + ui_list_move_up].width,
1964 ui_rects[ui_list_first + 3 * i + ui_list_move_up].height,
1965 up_tex.get_texture_handle(), ui::rotation::upright, false);
1966 }
1967 if(!nth_item_can_move_down(list_offset + i)) {
1971 ui_rects[ui_list_first + 3 * i + ui_list_move_down].width,
1972 ui_rects[ui_list_first + 3 * i + ui_list_move_down].height,
1973 down_tex.get_texture_handle(), ui::rotation::upright, false);
1974 } else {
1978 ui_rects[ui_list_first + 3 * i + ui_list_move_down].width,
1979 ui_rects[ui_list_first + 3 * i + ui_list_move_down].height,
1980 down_tex.get_texture_handle(), ui::rotation::upright, false);
1981 }
1982 } else {
1986 ui_rects[ui_list_first + 3 * i + ui_list_checkbox].width,
1987 ui_rects[ui_list_first + 3 * i + ui_list_checkbox].height,
1988 empty_check_tex.get_texture_handle(), ui::rotation::upright, false);
1989 }
1990
1991 auto xoffset = list_text_right_align - base_text_extent(mod_ref.name_.data(), uint32_t(mod_ref.name_.length()), 14, fonts[0]);
1992
1993 launcher::ogl::render_new_text(mod_ref.name_.data(), launcher::ogl::color_modification::none, xoffset, 75.0f + 7.0f + i * ui_row_height, 14.0f, launcher::ogl::color3f{ 255.0f / 255.0f, 230.0f / 255.0f, 153.0f / 255.0f }, fonts[0]);
1994 }
1995
1996 SwapBuffers(opengl_window_dc);
1997}
1998
1999bool is_low_surrogate(uint16_t char_code) noexcept {
2000 return char_code >= 0xDC00 && char_code <= 0xDFFF;
2001}
2002bool is_high_surrogate(uint16_t char_code) noexcept {
2003 return char_code >= 0xD800 && char_code <= 0xDBFF;
2004}
2005
2007 if(c <= 127)
2008 return char(c);
2010 return 0;
2011 char char_out = 0;
2012 WideCharToMultiByte(1250, 0, &c, 1, &char_out, 1, nullptr, nullptr);
2013 return char_out;
2014}
2015
2016LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) {
2017 if(message == WM_CREATE) {
2018 opengl_window_dc = GetDC(hwnd);
2019
2020 create_opengl_context();
2021
2022 glProvokingVertex(GL_FIRST_VERTEX_CONVENTION);
2023 glEnable(GL_LINE_SMOOTH);
2024
2025 load_shaders(); // create shaders
2026 load_global_squares(); // create various squares to drive the shaders with
2027
2029 simple_fs::add_root(fs, NATIVE("."));
2030 auto root = get_root(fs);
2031
2032 uint8_t font_set_load = 0;
2033 LANGID lang = GetUserDefaultUILanguage();
2034 //lang = 0x0004;
2035 switch(lang & 0xff) {
2036 //case 0x0001:
2037 // localised_strings = &ar_localised_strings[0];
2038 // font_set_load = 2;
2039 // break;
2040 case 0x0002:
2041 localised_strings = &bg_localised_strings[0];
2042 font_set_load = 3;
2043 break;
2044 case 0x0003:
2045 localised_strings = &ca_localised_strings[0];
2046 break;
2047 case 0x0004:
2048 localised_strings = &zh_localised_strings[0];
2049 font_set_load = 1;
2050 break;
2051 case 0x0005:
2052 localised_strings = &cs_localised_strings[0];
2053 break;
2054 case 0x0006:
2055 localised_strings = &da_localised_strings[0];
2056 break;
2057 case 0x0007:
2058 localised_strings = &de_localised_strings[0];
2059 break;
2060 case 0x0008:
2061 localised_strings = &el_localised_strings[0];
2062 break;
2063 case 0x0009:
2064 localised_strings = &en_localised_strings[0];
2065 break;
2066 case 0x000A:
2067 localised_strings = &es_localised_strings[0];
2068 break;
2069 case 0x000B:
2070 localised_strings = &fi_localised_strings[0];
2071 break;
2072 case 0x000C:
2073 localised_strings = &fr_localised_strings[0];
2074 break;
2075 //case 0x000D:
2076 // localised_strings = &he_localised_strings[0];
2077 // break;
2078 case 0x000E:
2079 localised_strings = &hu_localised_strings[0];
2080 break;
2081 case 0x000F:
2082 //localised_strings = &is_localised_strings[0];
2083 break;
2084 case 0x0010:
2085 localised_strings = &it_localised_strings[0];
2086 break;
2087 case 0x0011:
2088 //localised_strings = &ja_localised_strings[0];
2089 break;
2090 case 0x0012:
2091 //localised_strings = &ko_localised_strings[0];
2092 break;
2093 case 0x0016:
2094 localised_strings = &po_localised_strings[0];
2095 break;
2096 case 0x0013:
2097 localised_strings = &nl_localised_strings[0];
2098 break;
2099 case 0x0014:
2100 localised_strings = &no_localised_strings[0];
2101 break;
2102 case 0x0015:
2103 localised_strings = &pl_localised_strings[0];
2104 break;
2105 case 0x0018:
2106 localised_strings = &ro_localised_strings[0];
2107 break;
2108 case 0x0019:
2109 localised_strings = &ru_localised_strings[0];
2110 font_set_load = 3;
2111 break;
2112 case 0x001C:
2113 localised_strings = &sq_localised_strings[0];
2114 break;
2115 case 0x001D:
2116 localised_strings = &sv_localised_strings[0];
2117 break;
2118 case 0x001F:
2119 localised_strings = &tr_localised_strings[0];
2120 break;
2121 case 0x0022:
2122 localised_strings = &uk_localised_strings[0];
2123 font_set_load = 3;
2124 break;
2125 case 0x0025:
2126 localised_strings = &et_localised_strings[0];
2127 break;
2128 case 0x0026:
2129 localised_strings = &lv_localised_strings[0];
2130 break;
2131 case 0x0027:
2132 localised_strings = &lt_localised_strings[0];
2133 break;
2134 case 0x002A:
2135 localised_strings = &vi_localised_strings[0];
2136 break;
2137 case 0x002B:
2138 localised_strings = &hy_localised_strings[0];
2139 break;
2140 case 0x0039:
2141 localised_strings = &hi_localised_strings[0];
2142 break;
2143 default:
2144 break;
2145 }
2146 if(font_set_load == 0) {
2147 auto font_a = simple_fs::open_file(root, NATIVE("assets/fonts/LibreCaslonText-Regular.ttf"));
2148 if(font_a) {
2149 auto file_content = simple_fs::view_contents(*font_a);
2150 font_collection.load_font(fonts[0], file_content.data, file_content.file_size);
2151 }
2152 auto font_b = simple_fs::open_file(root, NATIVE("assets/fonts/LibreCaslonText-Italic.ttf"));
2153 if(font_b) {
2154 auto file_content = simple_fs::view_contents(*font_b);
2155 font_collection.load_font(fonts[1], file_content.data, file_content.file_size);
2156 }
2157 } else if(font_set_load == 1) { //chinese
2158 auto font_a = simple_fs::open_file(root, NATIVE("assets/fonts/STZHONGS.TTF"));
2159 if(font_a) {
2160 auto file_content = simple_fs::view_contents(*font_a);
2161 font_collection.load_font(fonts[0], file_content.data, file_content.file_size);
2162 }
2163 auto font_b = simple_fs::open_file(root, NATIVE("assets/fonts/STZHONGS.TTF"));
2164 if(font_b) {
2165 auto file_content = simple_fs::view_contents(*font_b);
2166 font_collection.load_font(fonts[1], file_content.data, file_content.file_size);
2167 }
2168 } else if(font_set_load == 2) { //arabic
2169 auto font_a = simple_fs::open_file(root, NATIVE("assets/fonts/NotoNaskhArabic-Bold.ttf"));
2170 if(font_a) {
2171 auto file_content = simple_fs::view_contents(*font_a);
2172 font_collection.load_font(fonts[0], file_content.data, file_content.file_size);
2173 }
2174 auto font_b = simple_fs::open_file(root, NATIVE("assets/fonts/NotoNaskhArabic-Regular.ttf"));
2175 if(font_b) {
2176 auto file_content = simple_fs::view_contents(*font_b);
2177 font_collection.load_font(fonts[1], file_content.data, file_content.file_size);
2178 }
2179 } else if(font_set_load == 3) { //cyrillic
2180 auto font_a = simple_fs::open_file(root, NATIVE("assets/fonts/NotoSerif-Regular.ttf"));
2181 if(font_a) {
2182 auto file_content = simple_fs::view_contents(*font_a);
2183 font_collection.load_font(fonts[0], file_content.data, file_content.file_size);
2184 }
2185 auto font_b = simple_fs::open_file(root, NATIVE("assets/fonts/NotoSerif-Regular.ttf"));
2186 if(font_b) {
2187 auto file_content = simple_fs::view_contents(*font_b);
2188 font_collection.load_font(fonts[1], file_content.data, file_content.file_size);
2189 }
2190 }
2191
2192 ::ogl::load_file_and_return_handle(NATIVE("assets/launcher_bg.png"), fs, bg_tex, false);
2193 ::ogl::load_file_and_return_handle(NATIVE("assets/launcher_left.png"), fs, left_tex, false);
2194 ::ogl::load_file_and_return_handle(NATIVE("assets/launcher_right.png"), fs, right_tex, false);
2195 ::ogl::load_file_and_return_handle(NATIVE("assets/launcher_close.png"), fs, close_tex, false);
2196 ::ogl::load_file_and_return_handle(NATIVE("assets/launcher_big_button.png"), fs, big_button_tex, false);
2197 ::ogl::load_file_and_return_handle(NATIVE("assets/launcher_big_left.png"), fs, big_l_button_tex, false);
2198 ::ogl::load_file_and_return_handle(NATIVE("assets/launcher_big_right.png"), fs, big_r_button_tex, false);
2199 ::ogl::load_file_and_return_handle(NATIVE("assets/launcher_no_check.png"), fs, empty_check_tex, false);
2200 ::ogl::load_file_and_return_handle(NATIVE("assets/launcher_check.png"), fs, check_tex, false);
2201 ::ogl::load_file_and_return_handle(NATIVE("assets/launcher_up.png"), fs, up_tex, false);
2202 ::ogl::load_file_and_return_handle(NATIVE("assets/launcher_down.png"), fs, down_tex, false);
2203 ::ogl::load_file_and_return_handle(NATIVE("assets/launcher_line_bg.png"), fs, line_bg_tex, false);
2204 ::ogl::load_file_and_return_handle(NATIVE("assets/launcher_warning.png"), fs, warning_tex, false);
2205
2206 game_dir_not_found = false;
2207 {
2208 auto f = simple_fs::peek_file(root, NATIVE("v2game.exe"));
2209 if(!f) {
2210 f = simple_fs::peek_file(root, NATIVE("victoria2.exe"));
2211 if(!f) {
2212 game_dir_not_found = true;
2213 }
2214 }
2215 }
2216
2217 auto mod_dir = simple_fs::open_directory(root, NATIVE("mod"));
2218 auto mod_files = simple_fs::list_files(mod_dir, NATIVE(".mod"));
2219
2220 parsers::error_handler err("");
2221 for(auto& f : mod_files) {
2222 auto of = simple_fs::open_file(f);
2223 if(of) {
2224 auto content = view_contents(*of);
2225 parsers::token_generator gen(content.data, content.data + content.file_size);
2226 mod_list.push_back(parsers::parse_mod_file(gen, err, parsers::mod_file_context{}));
2227 }
2228 }
2229
2231 auto s_files = simple_fs::list_files(sdir, NATIVE(".bin"));
2232 for(auto& f : s_files) {
2233 auto of = simple_fs::open_file(f);
2234 if(of) {
2235 auto content = view_contents(*of);
2236 auto desc = sys::extract_mod_information(reinterpret_cast<uint8_t const*>(content.data), content.file_size);
2237 if(desc.count != 0) {
2238 max_scenario_count = std::max(desc.count, max_scenario_count);
2239 scenario_files.push_back(scenario_file{ simple_fs::get_file_name(f) , desc });
2240 }
2241 }
2242 }
2243
2244 std::sort(scenario_files.begin(), scenario_files.end(), [](scenario_file const& a, scenario_file const& b) {
2245 return a.ident.count > b.ident.count;
2246 });
2247
2249
2250 return 1;
2251 } else {
2252 switch(message) {
2253 case WM_DPICHANGED:
2254 {
2255 dpi = float(HIWORD(wParam));
2256 auto lprcNewScale = reinterpret_cast<RECT*>(lParam);
2257
2258
2259 scaling_factor = float(lprcNewScale->right - lprcNewScale->left) / base_width;
2260
2261 SetWindowPos(hwnd, nullptr, lprcNewScale->left, lprcNewScale->top,
2262 lprcNewScale->right - lprcNewScale->left, lprcNewScale->bottom - lprcNewScale->top,
2263 SWP_NOZORDER | SWP_NOACTIVATE);
2264
2265 InvalidateRect((HWND)(m_hwnd), nullptr, FALSE);
2266
2267 break;
2268 }
2269 case WM_NCMOUSEMOVE:
2270 {
2271 RECT rcWindow;
2272 GetWindowRect(hwnd, &rcWindow);
2273 auto x = GET_X_LPARAM(lParam);
2274 auto y = GET_Y_LPARAM(lParam);
2275
2276 POINTS adj{ SHORT(x - rcWindow.left), SHORT(y - rcWindow.top) };
2277 memcpy(&lParam, &adj, sizeof(LPARAM));
2278
2279 mouse_x = int32_t(float(GET_X_LPARAM(lParam)) / scaling_factor);
2280 mouse_y = int32_t(float(GET_Y_LPARAM(lParam)) / scaling_factor);
2281 if(update_under_mouse()) {
2282 InvalidateRect((HWND)(m_hwnd), nullptr, FALSE);
2283 }
2284 return 0;
2285 }
2286 case WM_MOUSEMOVE:
2287 {
2288 mouse_x = int32_t(float(GET_X_LPARAM(lParam)) / scaling_factor);
2289 mouse_y = int32_t(float(GET_Y_LPARAM(lParam)) / scaling_factor);
2290 if(update_under_mouse()) {
2291 InvalidateRect((HWND)(m_hwnd), nullptr, FALSE);
2292 }
2293 return 0;
2294 }
2295 case WM_LBUTTONDOWN:
2296 {
2297 mouse_click();
2298 return 0;
2299 }
2300 case WM_NCCALCSIZE:
2301 if(wParam == TRUE)
2302 return 0;
2303 break;
2304 case WM_NCHITTEST:
2305 {
2306 POINT ptMouse = { GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam) };
2307 RECT rcWindow;
2308 GetWindowRect(hwnd, &rcWindow);
2309
2310 if(ptMouse.x <= int32_t(rcWindow.left + caption_width * scaling_factor)
2311 && ptMouse.y <= int32_t(rcWindow.top + caption_height * scaling_factor)) {
2312
2313 return HTCAPTION;
2314 } else {
2315 return HTCLIENT;
2316 }
2317 }
2318 case WM_PAINT:
2319 case WM_DISPLAYCHANGE:
2320 {
2321 PAINTSTRUCT ps;
2322 BeginPaint(hwnd, &ps);
2323
2324 render();
2325
2326 EndPaint(hwnd, &ps);
2327 return 0;
2328 }
2329 case WM_DESTROY:
2330 PostQuitMessage(0);
2331 return 1;
2332 case WM_KEYDOWN:
2333 if(GetKeyState(VK_CONTROL) & 0x8000) {
2334 if(wParam == L'v' || wParam == L'V') {
2335 if(!IsClipboardFormatAvailable(CF_TEXT))
2336 return 0;
2337 if(!OpenClipboard(m_hwnd))
2338 return 0;
2339
2340 auto hglb = GetClipboardData(CF_TEXT);
2341 if(hglb != nullptr) {
2342 auto lptstr = GlobalLock(hglb);
2343 if(lptstr != nullptr) {
2344 std::string cb_data((char*)lptstr);
2345 while(cb_data.length() > 0 && isspace(cb_data.back())) {
2346 cb_data.pop_back();
2347 }
2348 ip_addr = cb_data;
2349 GlobalUnlock(hglb);
2350 }
2351 }
2352 CloseClipboard();
2353 }
2354 }
2355 return 0;
2356 case WM_CHAR:
2357 {
2358 if(GetKeyState(VK_CONTROL) & 0x8000) {
2359
2360 } else {
2361 char turned_into = process_utf16_to_win1250(wchar_t(wParam));
2362 if(turned_into) {
2363 if(obj_under_mouse == ui_obj_ip_addr) {
2364 if(turned_into == '\b') {
2365 if(!ip_addr.empty())
2366 ip_addr.pop_back();
2367 } else if(turned_into >= 32 && turned_into != '\t' && turned_into != ' ' && ip_addr.size() < 46) {
2368 ip_addr.push_back(turned_into);
2369 }
2370 } else if(obj_under_mouse == ui_obj_player_name) {
2371 if(turned_into == '\b') {
2372 if(!player_name.empty()) {
2373 player_name.pop_back();
2375 }
2376 } else if(turned_into >= 32 && turned_into != '\t' && turned_into != ' ' && player_name.size() < 24) {
2377 player_name.push_back(turned_into);
2379 }
2380 } else if(obj_under_mouse == ui_obj_password) {
2381 if(turned_into == '\b') {
2382 if(!password.empty())
2383 password.pop_back();
2384 } else if(turned_into >= 32 && turned_into != '\t' && turned_into != ' ' && password.size() < 16) {
2385 password.push_back(turned_into);
2386 }
2387 }
2388 }
2389 }
2390 InvalidateRect((HWND)(m_hwnd), nullptr, FALSE);
2391 return 0;
2392 }
2393 default:
2394 break;
2395
2396 }
2397 return DefWindowProc(hwnd, message, wParam, lParam);
2398 }
2399}
2400
2401} // end launcher namespace
2402
2403static CRITICAL_SECTION guard_abort_handler;
2404
2406 static bool run_once = false;
2407
2408 EnterCriticalSection(&guard_abort_handler);
2409 if(run_once == false) {
2410 run_once = true;
2411
2412 STARTUPINFO si;
2413 ZeroMemory(&si, sizeof(si));
2414 si.cb = sizeof(si);
2415 PROCESS_INFORMATION pi;
2416 ZeroMemory(&pi, sizeof(pi));
2417 // Start the child process.
2418 if(CreateProcessW(
2419 L"dbg_alice.exe", // Module name
2420 NULL, // Command line
2421 NULL, // Process handle not inheritable
2422 NULL, // Thread handle not inheritable
2423 FALSE, // Set handle inheritance to FALSE
2424 0, // No creation flags
2425 NULL, // Use parent's environment block
2426 NULL, // Use parent's starting directory
2427 &si, // Pointer to STARTUPINFO structure
2428 &pi) == 0) {
2429
2430 // create process failed
2431 LeaveCriticalSection(&guard_abort_handler);
2432 return;
2433
2434 }
2435 // Wait until child process exits.
2436 WaitForSingleObject(pi.hProcess, INFINITE);
2437 // Close process and thread handles.
2438 CloseHandle(pi.hProcess);
2439 CloseHandle(pi.hThread);
2440 }
2441 LeaveCriticalSection(&guard_abort_handler);
2442}
2443
2444LONG WINAPI uef_wrapper(struct _EXCEPTION_POINTERS* lpTopLevelExceptionFilter) {
2446 return EXCEPTION_CONTINUE_SEARCH;
2447}
2448
2451}
2453 const wchar_t* expression,
2454 const wchar_t* function,
2455 const wchar_t* file,
2456 unsigned int line,
2457 uintptr_t pReserved
2458) {
2460}
2461
2463 typedef BOOL(WINAPI* tGetPolicy)(LPDWORD lpFlags);
2464 typedef BOOL(WINAPI* tSetPolicy)(DWORD dwFlags);
2465 const DWORD EXCEPTION_SWALLOWING = 0x1;
2466
2467 HMODULE kernel32 = LoadLibraryA("kernel32.dll");
2468 tGetPolicy pGetPolicy = (tGetPolicy)GetProcAddress(kernel32, "GetProcessUserModeExceptionPolicy");
2469 tSetPolicy pSetPolicy = (tSetPolicy)GetProcAddress(kernel32, "SetProcessUserModeExceptionPolicy");
2470 if(pGetPolicy && pSetPolicy) {
2471 DWORD dwFlags;
2472 if(pGetPolicy(&dwFlags)) {
2473 // Turn off the filter
2474 pSetPolicy(dwFlags & ~EXCEPTION_SWALLOWING);
2475 }
2476 }
2477 BOOL insanity = FALSE;
2478 SetUserObjectInformationA(GetCurrentProcess(), UOI_TIMERPROC_EXCEPTION_SUPPRESSION, &insanity, sizeof(insanity));
2479}
2480
2481int WINAPI wWinMain(
2482 HINSTANCE /*hInstance*/,
2483 HINSTANCE /*hPrevInstance*/,
2484 LPWSTR /*lpCmdLine*/,
2485 int /*nCmdShow*/
2486) {
2487#ifdef _DEBUG
2488 HeapSetInformation(NULL, HeapEnableTerminationOnCorruption, NULL, 0);
2489#endif
2490
2491 InitializeCriticalSection(&guard_abort_handler);
2492
2493 if(!IsDebuggerPresent()) {
2495 _set_purecall_handler(generic_wrapper);
2496 _set_invalid_parameter_handler(invalid_parameter_wrapper);
2497 _set_abort_behavior(0, _WRITE_ABORT_MSG | _CALL_REPORTFAULT);
2498 SetUnhandledExceptionFilter(uef_wrapper);
2499 signal(SIGABRT, signal_abort_handler);
2500 }
2501
2502 SetProcessDpiAwarenessContext(DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2);
2503
2504 if(!SUCCEEDED(CoInitializeEx(NULL, COINIT_APARTMENTTHREADED)))
2505 return 0;
2506
2507 WNDCLASSEX wcex = { };
2508 wcex.cbSize = UINT(sizeof(WNDCLASSEX));
2509 wcex.style = CS_OWNDC;
2510 wcex.lpfnWndProc = launcher::WndProc;
2511 wcex.cbClsExtra = 0;
2512 wcex.cbWndExtra = sizeof(LONG_PTR);
2513 wcex.hInstance = GetModuleHandle(nullptr);
2514 wcex.hIcon = (HICON)LoadImage(GetModuleHandleW(nullptr), MAKEINTRESOURCE(IDI_ICON1), IMAGE_ICON, GetSystemMetrics(SM_CXICON), GetSystemMetrics(SM_CYICON), 0);
2515 wcex.hbrBackground = NULL;
2516 wcex.lpszMenuName = NULL;
2517 wcex.hCursor = LoadCursor(NULL, IDC_ARROW);
2518 wcex.lpszClassName = NATIVE("alice_launcher_class");
2519
2520 if(RegisterClassEx(&wcex) == 0) {
2521 window::emit_error_message("Unable to register window class", true);
2522 }
2523
2524 // Use by default the name of the computer
2525 char username[256 + 1];
2526 DWORD username_len = 256 + 1;
2527 GetComputerNameA(username, &username_len);
2528
2529 // Load from user settings
2530 auto settings_location = simple_fs::get_or_create_settings_directory();
2531 if(auto player_name_file = simple_fs::open_file(settings_location, NATIVE("player_name.dat")); player_name_file) {
2532 auto contents = simple_fs::view_contents(*player_name_file);
2533 const sys::player_name *p = (const sys::player_name*)contents.data;
2534 if(contents.file_size >= sizeof(*p)) {
2535 launcher::player_name = std::string(p->data);
2536 }
2537 } else {
2538 srand(time(NULL));
2539 launcher::player_name = std::to_string(int32_t(rand()));
2540 }
2541
2542 launcher::m_hwnd = CreateWindowEx(
2543 0,
2544 L"alice_launcher_class",
2545 L"Launch Project Alice",
2546 WS_CLIPCHILDREN | WS_CLIPSIBLINGS | WS_BORDER,
2547 CW_USEDEFAULT,
2548 CW_USEDEFAULT,
2549 0,
2550 0,
2551 NULL,
2552 NULL,
2553 GetModuleHandle(nullptr),
2554 nullptr
2555 );
2556
2557 if(launcher::m_hwnd) {
2558
2559 launcher::dpi = float(GetDpiForWindow((HWND)(launcher::m_hwnd)));
2560
2561 auto monitor_handle = MonitorFromWindow((HWND)(launcher::m_hwnd), MONITOR_DEFAULTTOPRIMARY);
2562 MONITORINFO mi;
2563 mi.cbSize = sizeof(mi);
2564 GetMonitorInfo(monitor_handle, &mi);
2565
2566 auto vert_space = mi.rcWork.bottom - mi.rcWork.top;
2567 float rough_scale = float(vert_space) / 1080.0f;
2568 if(rough_scale >= 1.0f) {
2569 launcher::scaling_factor = std::round(rough_scale);
2570 } else {
2571 launcher::scaling_factor = std::round(rough_scale * 4.0f) / 4.0f;
2572 }
2573
2574 SetWindowPos(
2575 (HWND)(launcher::m_hwnd),
2576 NULL,
2577 NULL,
2578 NULL,
2579 static_cast<int>(launcher::scaling_factor * launcher::base_width),
2580 static_cast<int>(launcher::scaling_factor * launcher::base_height),
2581 SWP_NOMOVE | SWP_FRAMECHANGED);
2582
2583
2584 ShowWindow((HWND)(launcher::m_hwnd), SW_SHOWNORMAL);
2585 UpdateWindow((HWND)(launcher::m_hwnd));
2586 }
2587
2588 MSG msg;
2589 while(GetMessage(&msg, NULL, 0, 0)) {
2590 TranslateMessage(&msg);
2591 DispatchMessage(&msg);
2592 }
2593
2594 CoUninitialize();
2595
2596 return 0;
2597}
GLuint get_texture_handle() const
Definition: texture.cpp:366
std::string accumulated_errors
Definition: parsers.hpp:62
std::string accumulated_warnings
Definition: parsers.hpp:63
std::string file_name
Definition: parsers.hpp:61
void load_font(font &fnt, char const *file_data, uint32_t file_size)
Definition: fonts.cpp:438
hb_font_t * hb_font_face
Definition: fonts.hpp:110
ankerl::unordered_dense::map< char32_t, glyph_sub_offset > glyph_positions
Definition: fonts.hpp:117
FT_Face font_face
Definition: fonts.hpp:109
hb_buffer_t * hb_buf
Definition: fonts.hpp:111
void make_glyph(char32_t ch_in)
Definition: fonts.cpp:478
std::vector< uint32_t > textures
Definition: fonts.hpp:118
#define assert(condition)
Definition: debug.h:74
native_string produce_mod_path()
void enforce_list_order()
bool transitively_depends_on(parsers::mod_file const &moda, parsers::mod_file const &modb)
void find_scenario_file()
native_string to_hex(uint64_t v)
void EnableCrashingOnCrashes()
void signal_abort_handler(int)
LONG WINAPI uef_wrapper(struct _EXCEPTION_POINTERS *lpTopLevelExceptionFilter)
void generic_wrapper()
void invalid_parameter_wrapper(const wchar_t *expression, const wchar_t *function, const wchar_t *file, unsigned int line, uintptr_t pReserved)
int WINAPI wWinMain(HINSTANCE, HINSTANCE, LPWSTR, int)
constexpr GLuint atlas_index
void render_new_text(std::string_view sv, color_modification enabled, float x, float y, float size, color3f const &c, ::text::font &f)
void render_textured_rect(color_modification enabled, int32_t ix, int32_t iy, int32_t iwidth, int32_t iheight, GLuint texture_handle, ui::rotation r, bool flipped)
constexpr float base_height
constexpr int32_t ui_obj_list_right
constexpr int32_t ui_list_move_down
constexpr int32_t ui_list_first
constexpr float caption_height
void shutdown_opengl()
constexpr int32_t ui_obj_password
constexpr float list_text_right_align
void save_playername()
constexpr int32_t ui_list_move_up
constexpr int32_t ui_obj_ip_addr
constexpr int32_t ui_row_height
constexpr int32_t ui_obj_close
bool transitively_depends_on(parsers::mod_file const &moda, parsers::mod_file const &modb)
void recursively_remove_from_list(parsers::mod_file &mod)
void recursively_add_to_list(parsers::mod_file &mod)
char process_utf16_to_win1250(wchar_t c)
constexpr int32_t ui_list_checkbox
constexpr int32_t ui_list_count
constexpr float caption_width
bool nth_item_can_move_up(int32_t n)
bool nth_item_can_move_down(int32_t n)
bool is_high_surrogate(uint16_t char_code) noexcept
bool update_under_mouse()
float base_text_extent(char const *codepoints, uint32_t count, int32_t size, text::font &f)
void enforce_list_order()
bool is_low_surrogate(uint16_t char_code) noexcept
constexpr ui_active_rect ui_rects[]
LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
constexpr int32_t ui_obj_host_game
constexpr int32_t ui_obj_create_scenario
constexpr int32_t ui_obj_list_left
bool transitively_depends_on_internal(parsers::mod_file const &moda, parsers::mod_file const &modb, std::vector< bool > &seen_indices)
constexpr int32_t ui_obj_player_name
void create_opengl_context()
constexpr int32_t ui_list_end
constexpr float base_width
void make_mod_file()
constexpr int32_t ui_obj_play_game
void mouse_click()
constexpr int32_t ui_obj_join_game
constexpr GLuint piechart
constexpr GLuint tint
constexpr GLuint interactable
constexpr GLuint frame_stretch
constexpr GLuint no_filter
constexpr GLuint progress_bar
constexpr GLuint use_mask
constexpr GLuint screen_width
constexpr GLuint subsprite_b
constexpr GLuint enabled
constexpr GLuint filter
constexpr GLuint barchart
constexpr GLuint inner_color
constexpr GLuint disabled
constexpr GLuint screen_height
constexpr GLuint drawing_rectangle
constexpr GLuint border_size
constexpr GLuint linegraph
constexpr GLuint subrect
constexpr GLuint interactable_disabled
constexpr GLuint border_filter
constexpr GLuint sub_sprite
Definition: color.hpp:6
void internal_text_render(sys::state &state, text::stored_glyphs const &txt, float x, float baseline_y, float size, text::font &f)
void render_new_text(sys::state &state, text::stored_glyphs const &txt, color_modification enabled, float x, float y, float size, color3f const &c, text::font &f)
auto map_color_modification_to_index(color_modification e)
void bind_vertices_by_rotation(sys::state const &state, ui::rotation r, bool flipped, bool rtl)
GLuint load_file_and_return_handle(native_string const &native_name, simple_fs::file_system const &fs, texture &asset_texture, bool keep_data)
Definition: texture.cpp:371
void render_textured_rect(sys::state const &state, color_modification enabled, float x, float y, float width, float height, GLuint texture_handle, ui::rotation r, bool flipped, bool rtl)
std::vector< unopened_file > list_files(directory const &dir, native_char const *extension)
void add_root(file_system &fs, native_string_view root_path)
directory open_directory(directory const &dir, native_string_view directory_name)
native_string utf8_to_native(std::string_view data_in)
native_string extract_state(file_system const &fs)
void restore_state(file_system &fs, native_string_view data)
void write_file(directory const &dir, native_string_view file_name, char const *file_data, uint32_t file_size)
directory get_or_create_settings_directory()
native_string get_full_name(directory const &f)
std::optional< file > open_file(directory const &dir, native_string_view file_name)
std::optional< unopened_file > peek_file(directory const &dir, native_string_view file_name)
file_contents view_contents(file const &f)
native_string get_file_name(unopened_file const &f)
directory get_or_create_scenario_directory()
mod_identifier extract_mod_information(uint8_t const *ptr_in, uint64_t file_size)
@ bookmark
Definition: constants.hpp:568
void write_scenario_file(sys::state &state, native_string_view name, uint32_t count)
void write_save_file(sys::state &state, save_type type, std::string const &name)
constexpr int magnification_factor
Definition: fonts.hpp:17
void emit_error_message(std::string const &content, bool fatal)
Definition: window_nix.cpp:355
char native_char
#define NATIVE(X)
std::string native_string
uint uint32_t
ulong uint64_t
uchar uint8_t
#define IDI_ICON1
Definition: resource.h:5
struct HWND__ * HWND
Definition: sound_win.hpp:8
sys::mod_identifier ident
std::vector< bookmark_definition > bookmark_dates
std::vector< std::string > dependent_mods