Project Alice
Loading...
Searching...
No Matches
entry_point_win.cpp
Go to the documentation of this file.
1#include "system_state.hpp"
2#include "serialization.hpp"
3
4#ifndef UNICODE
5#define UNICODE
6#endif
7#define NOMINMAX
8#define WIN32_LEAN_AND_MEAN
9
10#include <Windows.h>
11#include <shellapi.h>
12#include "Objbase.h"
13#include "window.hpp"
14
15#pragma comment(lib, "Ole32.lib")
16#pragma comment(lib, "Shell32.lib")
17#pragma comment(lib, "icu.lib")
18
19static sys::state game_state; // too big for the stack
20static CRITICAL_SECTION guard_abort_handler;
21
23 static bool run_once = false;
24
25 EnterCriticalSection(&guard_abort_handler);
26 if(run_once == false) {
27 run_once = true;
28
29 STARTUPINFO si;
30 ZeroMemory(&si, sizeof(si));
31 si.cb = sizeof(si);
32 PROCESS_INFORMATION pi;
33 ZeroMemory(&pi, sizeof(pi));
34 // Start the child process.
35 if(CreateProcessW(
36 L"dbg_alice.exe", // Module name
37 NULL, // Command line
38 NULL, // Process handle not inheritable
39 NULL, // Thread handle not inheritable
40 FALSE, // Set handle inheritance to FALSE
41 0, // No creation flags
42 NULL, // Use parent's environment block
43 NULL, // Use parent's starting directory
44 &si, // Pointer to STARTUPINFO structure
45 &pi) == 0) {
46
47 // create process failed
48 LeaveCriticalSection(&guard_abort_handler);
49 return;
50
51 }
52 // Wait until child process exits.
53 WaitForSingleObject(pi.hProcess, INFINITE);
54 // Close process and thread handles.
55 CloseHandle(pi.hProcess);
56 CloseHandle(pi.hThread);
57 }
58 LeaveCriticalSection(&guard_abort_handler);
59}
60
61LONG WINAPI uef_wrapper(struct _EXCEPTION_POINTERS* lpTopLevelExceptionFilter) {
63 return EXCEPTION_CONTINUE_SEARCH;
64}
65
68}
70 const wchar_t* expression,
71 const wchar_t* function,
72 const wchar_t* file,
73 unsigned int line,
74 uintptr_t pReserved
75) {
77}
78
80 typedef BOOL(WINAPI* tGetPolicy)(LPDWORD lpFlags);
81 typedef BOOL(WINAPI* tSetPolicy)(DWORD dwFlags);
82 const DWORD EXCEPTION_SWALLOWING = 0x1;
83
84 HMODULE kernel32 = LoadLibraryA("kernel32.dll");
85 if(kernel32) {
86 tGetPolicy pGetPolicy = (tGetPolicy)GetProcAddress(kernel32, "GetProcessUserModeExceptionPolicy");
87 tSetPolicy pSetPolicy = (tSetPolicy)GetProcAddress(kernel32, "SetProcessUserModeExceptionPolicy");
88 if(pGetPolicy && pSetPolicy) {
89 DWORD dwFlags;
90 if(pGetPolicy(&dwFlags)) {
91 // Turn off the filter
92 pSetPolicy(dwFlags & ~EXCEPTION_SWALLOWING);
93 }
94 }
95 }
96 BOOL insanity = FALSE;
97 SetUserObjectInformationA(GetCurrentProcess(), UOI_TIMERPROC_EXCEPTION_SUPPRESSION, &insanity, sizeof(insanity));
98}
99
100int WINAPI wWinMain(HINSTANCE /*hInstance*/, HINSTANCE /*hPrevInstance*/, LPWSTR /*commandline*/, int /*nCmdShow*/
101) {
102
103#ifdef _DEBUG
104 HeapSetInformation(NULL, HeapEnableTerminationOnCorruption, NULL, 0);
105#endif
106
107 InitializeCriticalSection(&guard_abort_handler);
108
109 if(!IsDebuggerPresent()) {
111 _set_purecall_handler(generic_wrapper);
112 _set_invalid_parameter_handler(invalid_parameter_wrapper);
113 _set_abort_behavior(0, _WRITE_ABORT_MSG | _CALL_REPORTFAULT);
114 SetUnhandledExceptionFilter(uef_wrapper);
115 signal(SIGABRT, signal_abort_handler);
116 }
117
118 SetProcessDpiAwarenessContext(DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2);
119
120 if(SUCCEEDED(CoInitializeEx(NULL, COINIT_APARTMENTTHREADED))) {
121 // do everything here: create a window, read messages
122
123 int num_params = 0;
124 auto parsed_cmd = CommandLineToArgvW(GetCommandLineW(), &num_params);
125
126 int headless_speed = 6;
127 bool headless_repeat = false;
128 bool headless = false;
129
130 network::port_forwarder forwarding_apparatus;
131
132 if(num_params < 2) {
133#ifdef NDEBUG
134 auto msg = std::string("Start Alice.exe using the launcher");
136 return 0;
137#else
138
139 add_root(game_state.common_fs, NATIVE(".")); // for the moment this lets us find the shader files
140
141
142 if(!sys::try_read_scenario_and_save_file(game_state, NATIVE("development_test_file.bin"))) {
143 // scenario making functions
144 parsers::error_handler err{ "" };
145
147 simple_fs::add_root(fs_root, L".");
148
149 auto root = get_root(fs_root);
150 auto common = open_directory(root, NATIVE("common"));
151
152 parsers::bookmark_context bookmark_context;
153 if(auto f = open_file(common, NATIVE("bookmarks.txt")); f) {
154 auto bookmark_content = simple_fs::view_contents(*f);
155 err.file_name = "bookmarks.txt";
156 parsers::token_generator gen(bookmark_content.data, bookmark_content.data + bookmark_content.file_size);
157 parsers::parse_bookmark_file(gen, err, bookmark_context);
158 assert(!bookmark_context.bookmark_dates.empty());
159 } else {
160 err.accumulated_errors += "File common/bookmarks.txt could not be opened\n";
161 }
162
163 auto to_hex = [](uint64_t v) {
164 native_string ret;
165 constexpr native_char digits[] = NATIVE("0123456789ABCDEF");
166 do {
167 ret += digits[v & 0x0F];
168 v = v >> 4;
169 } while(v != 0);
170 return ret;
171 };
172
173 sys::checksum_key scenario_key;
174 for(uint32_t date_index = 0; date_index < uint32_t(bookmark_context.bookmark_dates.size()); date_index++) {
175 err.accumulated_errors.clear();
176 err.accumulated_warnings.clear();
177 //
178 auto inner_game_state = std::make_unique<sys::state>();
179 simple_fs::add_root(inner_game_state->common_fs, L".");
180
181 inner_game_state->load_scenario_data(err, bookmark_context.bookmark_dates[date_index].date_);
182 if(err.fatal)
183 break;
184 if(date_index == 0) {
186 int32_t append = 0;
187 auto time_stamp = uint64_t(std::time(0));
188 auto selected_scenario_file = native_string(NATIVE("development_test_file.bin"));
189 sys::write_scenario_file(*inner_game_state, selected_scenario_file, 0);
190 if(auto of = simple_fs::open_file(sdir, selected_scenario_file); of) {
191 auto content = view_contents(*of);
192 auto desc = sys::extract_mod_information(reinterpret_cast<uint8_t const*>(content.data), content.file_size);
193 }
194 scenario_key = inner_game_state->scenario_checksum;
195 } else {
196 inner_game_state->scenario_checksum = scenario_key;
197 sys::write_save_file(*inner_game_state, sys::save_type::bookmark, bookmark_context.bookmark_dates[date_index].name_);
198 }
199 }
200
201 if(!err.accumulated_errors.empty()) {
202 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;
204 simple_fs::write_file(pdir, NATIVE("scenario_errors.txt"), assembled_file.data(), uint32_t(assembled_file.length()));
205
206 auto fname = simple_fs::get_full_name(pdir) + NATIVE("\\scenario_errors.txt");
207 ShellExecuteW(
208 nullptr,
209 L"open",
210 fname.c_str(),
211 nullptr,
212 nullptr,
213 SW_NORMAL
214 );
215
216 std::abort();
217 }
218 if(!sys::try_read_scenario_and_save_file(game_state, NATIVE("development_test_file.bin")))
219 std::abort();
220 }
221
222 game_state.fill_unsaved_data();
223#endif
224 } else {
225 for(int i = 1; i < num_params; ++i) {
226 if(native_string(parsed_cmd[i]) == NATIVE("-host")) {
227 forwarding_apparatus.start_forwarding();
229 } else if(native_string(parsed_cmd[i]) == NATIVE("-join")) {
231 game_state.network_state.ip_address = "127.0.0.1";
232 if(i + 1 < num_params) {
233 game_state.network_state.ip_address = simple_fs::native_to_utf8(native_string(parsed_cmd[i + 1]));
234 i++;
235 }
236 } else if(native_string(parsed_cmd[i]) == NATIVE("-name")) {
237 if(i + 1 < num_params) {
238 std::string nickname = simple_fs::native_to_utf8(native_string(parsed_cmd[i + 1]));
239 memcpy(game_state.network_state.nickname.data, nickname.data(), std::min<size_t>(nickname.length(), 8));
240 i++;
241 }
242 } else if(native_string(parsed_cmd[i]) == NATIVE("-password")) {
243 if(i + 1 < num_params) {
244 auto str = simple_fs::native_to_utf8(native_string(parsed_cmd[i + 1]));
245 std::memset(game_state.network_state.password, '\0', sizeof(game_state.network_state.password));
246 std::memcpy(game_state.network_state.password, str.c_str(), std::min(sizeof(game_state.network_state.password), str.length()));
247 i++;
248 }
249 } else if(native_string(parsed_cmd[i]) == NATIVE("-v6")) {
250 game_state.network_state.as_v6 = true;
251 } else if(native_string(parsed_cmd[i]) == NATIVE("-v4")) {
252 game_state.network_state.as_v6 = false;
253 } else if(native_string(parsed_cmd[i]) == NATIVE("-headless")) {
254 headless = true;
255 } else if(native_string(parsed_cmd[i]) == NATIVE("-repeat")) {
256 headless_repeat = true;
257 } else if(native_string(parsed_cmd[i]) == NATIVE("-speed")) {
258 if(i + 1 < num_params) {
259 auto str = simple_fs::native_to_utf8(native_string(parsed_cmd[i + 1]));
260 headless_speed = std::atoi(str.c_str());
261 i++;
262 }
263 }
264 }
265
266 if(sys::try_read_scenario_and_save_file(game_state, parsed_cmd[1])) {
267 game_state.fill_unsaved_data();
268 } else {
269 auto msg = std::string("Scenario file ") + simple_fs::native_to_utf8(parsed_cmd[1]) + " could not be read";
271 return 0;
272 }
273
274 network::init(game_state);
275 }
276 LocalFree(parsed_cmd);
277
278 // scenario loading functions (would have to run these even when scenario is pre-built)
279 game_state.load_user_settings();
281
282 if(headless) {
283 game_state.actual_game_speed = headless_speed;
284 game_state.ui_pause.store(false, std::memory_order::release);
286 game_state.local_player_nation = dcon::nation_id{};
287 if(headless_repeat) {
288 std::thread update_thread([&]() { game_state.game_loop(); });
289 while(!game_state.quit_signaled.load(std::memory_order::acquire)) {
290 while(game_state.current_scene.game_in_progress) {
291 std::this_thread::sleep_for(std::chrono::milliseconds(15));
292 }
293 //Reload savefile
294 network::finish(game_state, true);
295 game_state.actual_game_speed = 0;
296 //
297 if(sys::try_read_scenario_and_save_file(game_state, parsed_cmd[1])) {
298 game_state.fill_unsaved_data();
299 } else {
300 auto msg = std::string("Scenario file ") + simple_fs::native_to_utf8(parsed_cmd[1]) + " could not be read";
302 return 0;
303 }
304 //
305 network::init(game_state);
307 game_state.actual_game_speed = headless_speed;
308 };
309 update_thread.join();
310 } else {
311 game_state.game_loop();
312 }
313 } else {
314 std::thread update_thread([&]() { game_state.game_loop(); });
315 // entire game runs during this line
317 game_state.quit_signaled.store(true, std::memory_order_release);
318 update_thread.join();
319 }
320
321 network::finish(game_state, true);
322 CoUninitialize();
323 }
324 return 0;
325}
#define assert(condition)
Definition: debug.h:74
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)
void switch_scene(sys::state &state, scene_id ui_scene)
Definition: game_scene.cpp:13
void finish(sys::state &state, bool notify_host)
Definition: network.cpp:1090
void init(sys::state &state)
Definition: network.cpp:497
void add_root(file_system &fs, native_string_view root_path)
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::string native_to_utf8(native_string_view data_in)
file_contents view_contents(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)
bool try_read_scenario_and_save_file(sys::state &state, native_string_view name)
void populate_definitions_map(sys::state &state)
void emit_error_message(std::string const &content, bool fatal)
Definition: window_nix.cpp:355
void create_window(sys::state &game_state, creation_parameters const &params)
Definition: window_nix.cpp:278
char native_char
#define NATIVE(X)
std::string native_string
uint uint32_t
ulong uint64_t
uchar uint8_t
std::string ip_address
Definition: network.hpp:80
sys::player_name nickname
Definition: network.hpp:73
uint8_t password[16]
Definition: network.hpp:90
std::vector< bookmark_definition > bookmark_dates
network_mode_type network_mode
user_settings_s user_settings
std::atomic< bool > ui_pause
std::atomic< int32_t > actual_game_speed
dcon::nation_id local_player_nation
simple_fs::file_system common_fs
game_scene::scene_properties current_scene
std::atomic< bool > quit_signaled
void load_user_settings()
void game_loop()
network::network_state network_state
void fill_unsaved_data()