1#![warn(missing_docs)]
3
4use elementtree::{Element, QName};
5
6use std::collections::BTreeMap;
7
8#[cfg(test)]
9use similar_asserts::assert_eq;
10
11use crate::error::Unreal4Error;
12
13#[derive(Clone, Debug, Default, PartialEq, Eq)]
17#[cfg_attr(feature = "serde", derive(serde::Serialize))]
18pub struct Unreal4ContextRuntimeProperties {
19 #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
21 pub crash_guid: Option<String>,
22 #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
24 pub process_id: Option<u32>,
25 #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
27 pub is_internal_build: Option<bool>,
28 #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
30 pub is_source_distribution: Option<bool>,
31 #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
33 pub is_assert: Option<bool>,
34 #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
36 pub is_ensure: Option<bool>,
37 #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
39 pub crash_type: Option<String>,
40 #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
42 pub seconds_since_start: Option<u32>,
43 #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
45 pub game_name: Option<String>,
46 #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
48 pub executable_name: Option<String>,
49 #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
51 pub build_configuration: Option<String>,
52 #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
54 pub platform_name: Option<String>,
55 #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
57 pub engine_mode: Option<String>,
58 #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
60 pub engine_version: Option<String>,
61 #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
63 pub language_lcid: Option<i32>,
64 #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
66 pub app_default_locate: Option<String>,
67 #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
69 pub build_version: Option<String>,
70 #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
72 pub is_ue4_release: Option<bool>,
73 #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
75 pub username: Option<String>,
76 #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
78 pub base_dir: Option<String>,
79 #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
81 pub root_dir: Option<String>,
82 #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
84 pub machine_id: Option<String>,
85 #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
87 pub login_id: Option<String>,
88 #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
90 pub epic_account_id: Option<String>,
91 #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
94 pub legacy_call_stack: Option<String>,
95 #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
98 pub portable_call_stack: Option<String>,
99 #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
101 pub user_description: Option<String>,
102 #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
104 pub error_message: Option<String>,
105 #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
107 pub crash_reporter_message: Option<String>,
108 #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
110 pub misc_number_of_cores: Option<u32>,
111 #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
113 pub misc_number_of_cores_inc_hyperthread: Option<u32>,
114 #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
116 pub misc_is_64bit: Option<bool>,
117 #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
119 pub misc_cpu_vendor: Option<String>,
120 #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
122 pub misc_cpu_brand: Option<String>,
123 #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
125 pub misc_primary_gpu_brand: Option<String>,
126 #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
128 pub misc_os_version_major: Option<String>,
129 #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
131 pub misc_os_version_minor: Option<String>,
132 #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
134 pub game_state_name: Option<String>,
135 #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
137 pub memory_stats_total_physical: Option<u64>,
138 #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
140 pub memory_stats_total_virtual: Option<u64>,
141 #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
143 pub memory_stats_page_size: Option<u64>,
144 #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
146 pub memory_stats_total_phsysical_gb: Option<u32>,
147 #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
149 pub time_of_crash: Option<u64>,
150 #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
152 pub allowed_to_be_contacted: Option<bool>,
153 #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
155 pub crash_reporter_client_version: Option<String>,
156 #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
158 pub modules: Option<String>,
159 pub custom: BTreeMap<String, String>,
161}
162
163impl Unreal4ContextRuntimeProperties {
164 fn from_xml(root: &Element) -> Option<Self> {
165 let list = root.find("RuntimeProperties")?;
166
167 let mut rv = Unreal4ContextRuntimeProperties::default();
168
169 fn get_text_or_none(elm: &Element) -> Option<String> {
170 let text = elm.text();
171 if !text.is_empty() {
172 Some(text.to_string())
173 } else {
174 None
175 }
176 }
177
178 for child in list.children() {
179 let tag = child.tag();
180
181 if tag.ns().is_some() {
183 continue;
184 }
185 match tag.name() {
186 "CrashGUID" => rv.crash_guid = get_text_or_none(child),
187 "ProcessId" => rv.process_id = child.text().parse::<u32>().ok(),
188 "IsInternalBuild" => rv.is_internal_build = child.text().parse::<bool>().ok(),
189 "IsSourceDistribution" => {
190 rv.is_source_distribution = child.text().parse::<bool>().ok()
191 }
192 "IsAssert" => rv.is_assert = child.text().parse::<bool>().ok(),
193 "IsEnsure" => rv.is_ensure = child.text().parse::<bool>().ok(),
194 "CrashType" => rv.crash_type = get_text_or_none(child),
195 "SecondsSinceStart" => rv.seconds_since_start = child.text().parse::<u32>().ok(),
196 "GameName" => rv.game_name = get_text_or_none(child),
197 "ExecutableName" => rv.executable_name = get_text_or_none(child),
198 "BuildConfiguration" => rv.build_configuration = get_text_or_none(child),
199 "PlatformName" => rv.platform_name = get_text_or_none(child),
200 "EngineMode" => rv.engine_mode = get_text_or_none(child),
201 "EngineVersion" => rv.engine_version = get_text_or_none(child),
202 "LanguageLCID" => rv.language_lcid = child.text().parse::<i32>().ok(),
203 "AppDefaultLocale" => rv.app_default_locate = get_text_or_none(child),
204 "BuildVersion" => rv.build_version = get_text_or_none(child),
205 "IsUE4Release" => rv.is_ue4_release = child.text().parse::<bool>().ok(),
206 "UserName" => rv.username = get_text_or_none(child),
207 "BaseDir" => rv.base_dir = get_text_or_none(child),
208 "RootDir" => rv.root_dir = get_text_or_none(child),
209 "MachineId" => rv.machine_id = get_text_or_none(child),
210 "LoginId" => rv.login_id = get_text_or_none(child),
211 "EpicAccountId" => rv.epic_account_id = get_text_or_none(child),
212 "CallStack" => rv.legacy_call_stack = get_text_or_none(child),
213 "PCallStack" => rv.portable_call_stack = get_text_or_none(child),
214 "UserDescription" => rv.user_description = get_text_or_none(child),
215 "ErrorMessage" => rv.error_message = get_text_or_none(child),
216 "CrashReporterMessage" => rv.crash_reporter_message = get_text_or_none(child),
217 "Misc.NumberOfCores" => rv.misc_number_of_cores = child.text().parse::<u32>().ok(),
218 "Misc.NumberOfCoresIncludingHyperthreads" => {
219 rv.misc_number_of_cores_inc_hyperthread = child.text().parse::<u32>().ok()
220 }
221 "Misc.Is64bitOperatingSystem" => {
222 rv.misc_is_64bit = child.text().parse::<bool>().ok()
223 }
224 "Misc.CPUVendor" => rv.misc_cpu_vendor = get_text_or_none(child),
225 "Misc.CPUBrand" => rv.misc_cpu_brand = get_text_or_none(child),
226 "Misc.PrimaryGPUBrand" => rv.misc_primary_gpu_brand = get_text_or_none(child),
227 "Misc.OSVersionMajor" => rv.misc_os_version_major = get_text_or_none(child),
228 "Misc.OSVersionMinor" => rv.misc_os_version_minor = get_text_or_none(child),
229 "GameStateName" => rv.game_state_name = get_text_or_none(child),
230 "MemoryStats.TotalPhysical" => {
231 rv.memory_stats_total_physical = child.text().parse::<u64>().ok()
232 }
233 "MemoryStats.TotalVirtual" => {
234 rv.memory_stats_total_virtual = child.text().parse::<u64>().ok()
235 }
236 "MemoryStats.PageSize" => {
237 rv.memory_stats_page_size = child.text().parse::<u64>().ok()
238 }
239 "MemoryStats.TotalPhysicalGB" => {
240 rv.memory_stats_total_phsysical_gb = child.text().parse::<u32>().ok()
241 }
242 "TimeOfCrash" => rv.time_of_crash = child.text().parse::<u64>().ok(),
243 "bAllowToBeContacted" => {
244 rv.allowed_to_be_contacted = child.text().parse::<bool>().ok()
245 }
246 "CrashReportClientVersion" => {
247 rv.crash_reporter_client_version = get_text_or_none(child)
248 }
249 "Modules" => rv.modules = get_text_or_none(child),
250 _ => {
251 rv.custom.insert(
252 tag.name().to_string(),
253 get_text_or_none(child).unwrap_or_default(),
254 );
255 }
256 }
257 }
258
259 Some(rv)
260 }
261}
262
263#[derive(Clone, Debug, Default, PartialEq, Eq)]
268#[cfg_attr(feature = "serde", derive(serde::Serialize))]
269pub struct Unreal4ContextPlatformProperties {
270 pub is_windows: Option<bool>,
272 pub callback_result: Option<i32>,
274}
275
276impl Unreal4ContextPlatformProperties {
277 fn from_xml(root: &Element) -> Option<Self> {
278 let list = root.find("PlatformProperties")?;
279
280 let mut rv = Unreal4ContextPlatformProperties::default();
281
282 for child in list.children() {
283 if child.tag() == &QName::from("PlatformIsRunningWindows") {
284 match child.text().parse::<u32>() {
285 Ok(1) => rv.is_windows = Some(true),
286 Ok(0) => rv.is_windows = Some(false),
287 Ok(_) => {}
288 Err(_) => {}
289 }
290 match child.text().parse::<bool>() {
291 Ok(true) => rv.is_windows = Some(true),
292 Ok(false) => rv.is_windows = Some(false),
293 Err(_) => {}
294 }
295 } else if child.tag() == &QName::from("PlatformCallbackResult") {
296 rv.callback_result = child.text().parse::<i32>().ok();
297 }
298 }
299
300 Some(rv)
301 }
302}
303
304#[derive(Clone, Debug, Default, PartialEq, Eq)]
308#[cfg_attr(feature = "serde", derive(serde::Serialize))]
309pub struct Unreal4Context {
310 #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
312 pub runtime_properties: Option<Unreal4ContextRuntimeProperties>,
313
314 #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
316 pub platform_properties: Option<Unreal4ContextPlatformProperties>,
317
318 #[cfg_attr(
320 feature = "serde",
321 serde(default, skip_serializing_if = "BTreeMap::is_empty")
322 )]
323 pub engine_data: BTreeMap<String, String>,
324
325 #[cfg_attr(
327 feature = "serde",
328 serde(default, skip_serializing_if = "BTreeMap::is_empty")
329 )]
330 pub game_data: BTreeMap<String, String>,
331}
332
333fn load_data_bag(element: &Element) -> BTreeMap<String, String> {
334 element
335 .children()
336 .map(|child| (child.tag().name().to_string(), child.text().to_string()))
337 .collect()
338}
339
340impl Unreal4Context {
341 pub fn parse(data: &[u8]) -> Result<Self, Unreal4Error> {
343 let root = Element::from_reader(data)?;
344
345 Ok(Unreal4Context {
346 runtime_properties: Unreal4ContextRuntimeProperties::from_xml(&root),
347 platform_properties: Unreal4ContextPlatformProperties::from_xml(&root),
348 engine_data: root
349 .find("EngineData")
350 .map_or_else(Default::default, load_data_bag),
351 game_data: root
352 .find("GameData")
353 .map_or_else(Default::default, load_data_bag),
354 })
355 }
356}
357
358#[allow(dead_code)]
359const ONLY_ROOT_NODE: &str = r#"<?xml version="1.0" encoding="UTF-8"?>
360<FGenericCrashContext>
361</FGenericCrashContext>
362"#;
363
364#[allow(dead_code)]
365const ONLY_ROOT_AND_CHILD_NODES: &str = r#"<?xml version="1.0" encoding="UTF-8"?>
366<FGenericCrashContext>
367 <RuntimeProperties>
368 </RuntimeProperties>
369 <PlatformProperties>
370 </PlatformProperties>
371</FGenericCrashContext>
372"#;
373
374#[allow(dead_code)]
375const ROOT_WITH_GAME_AND_ENGINE_DATA: &str = r#"<?xml version="1.0" encoding="UTF-8"?>
376<FGenericCrashContext>
377 <RuntimeProperties>
378 </RuntimeProperties>
379 <PlatformProperties>
380 </PlatformProperties>
381 <EngineData>
382 <RHI.IsGPUOverclocked>false</RHI.IsGPUOverclocked>
383 </EngineData>
384 <GameData>
385 <sentry>{"release":"foo.bar.baz@1.0.0"}</sentry>
386 </GameData>
387</FGenericCrashContext>
388"#;
389
390#[test]
391fn test_get_runtime_properties_missing_element() {
392 let root = Element::from_reader(ONLY_ROOT_NODE.as_bytes()).unwrap();
393 assert!(Unreal4ContextRuntimeProperties::from_xml(&root).is_none());
394}
395
396#[test]
397fn test_get_platform_properties_missing_element() {
398 let root = Element::from_reader(ONLY_ROOT_NODE.as_bytes()).unwrap();
399 assert!(Unreal4ContextPlatformProperties::from_xml(&root).is_none());
400}
401
402#[test]
403fn test_get_runtime_properties_no_children() {
404 let root = Element::from_reader(ONLY_ROOT_AND_CHILD_NODES.as_bytes()).unwrap();
405 let actual = Unreal4ContextRuntimeProperties::from_xml(&root).expect("default struct");
406 assert_eq!(Unreal4ContextRuntimeProperties::default(), actual)
407}
408
409#[test]
410fn test_get_game_and_engine_data() {
411 let actual =
412 Unreal4Context::parse(ROOT_WITH_GAME_AND_ENGINE_DATA.as_bytes()).expect("default struct");
413 assert_eq!(
414 actual
415 .engine_data
416 .get("RHI.IsGPUOverclocked")
417 .map(|x| x.as_str()),
418 Some("false")
419 );
420 assert_eq!(
421 actual.game_data.get("sentry").map(|x| x.as_str()),
422 Some(r#"{"release":"foo.bar.baz@1.0.0"}"#)
423 );
424}
425
426#[test]
427fn test_get_platform_properties_no_children() {
428 let root = Element::from_reader(ONLY_ROOT_AND_CHILD_NODES.as_bytes()).unwrap();
429 let actual = Unreal4ContextPlatformProperties::from_xml(&root).expect("default struct");
430 assert_eq!(Unreal4ContextPlatformProperties::default(), actual)
431}
432
433macro_rules! test_unreal_contect {
434 ($xml_parent:expr, $func_name:expr, $name:ident, $xml_elm:expr, $expect:expr, $(,)*) => {
435 #[cfg(test)]
436 mod $name {
437 use super::*;
438
439 #[test]
440 fn test_some() {
441 #[rustfmt::skip]
442 let xml = concat!("<FGenericCrashContext><", $xml_parent, "><", $xml_elm, ">", $expect, "</", $xml_elm, "></", $xml_parent, "></FGenericCrashContext>");
443 let root = Element::from_reader(xml.as_bytes()).unwrap();
444 let runtime_properties = $func_name(&root).expect("RuntimeProperties exists");
445 similar_asserts::assert_eq!(
446 $expect,
447 runtime_properties.$name.expect("missing property value")
448 );
449 }
450
451 #[test]
452 fn test_none() {
453 #[rustfmt::skip]
454 let xml = concat!("<FGenericCrashContext><", $xml_parent, "><", $xml_elm, "></", $xml_elm, "></", $xml_parent, "></FGenericCrashContext>");
455 let root = Element::from_reader(xml.as_bytes()).unwrap();
456 let runtime_properties = $func_name(&root).expect("RuntimeProperties exists");
457 assert!(runtime_properties.$name.is_none());
458 }
459 }
460 };
461}
462
463macro_rules! test_unreal_runtime_properties {
464 ($name:ident, $xml_elm:expr, $expect:expr $(,)*) => {
465 test_unreal_contect!(
466 "RuntimeProperties",
467 Unreal4ContextRuntimeProperties::from_xml,
468 $name,
469 $xml_elm,
470 $expect,
471 );
472 };
473}
474
475macro_rules! test_unreal_platform_properties {
476 ($name:ident, $xml_elm:expr, $expect:expr $(,)*) => {
477 test_unreal_contect!(
478 "PlatformProperties",
479 Unreal4ContextPlatformProperties::from_xml,
480 $name,
481 $xml_elm,
482 $expect,
483 );
484 };
485}
486
487test_unreal_runtime_properties!(
488 crash_guid,
489 "CrashGUID",
490 "UE4CC-Windows-379993BB42BD8FBED67986857D8844B5_0000",
491);
492
493test_unreal_runtime_properties!(process_id, "ProcessId", 2576);
494test_unreal_runtime_properties!(is_internal_build, "IsInternalBuild", true);
495test_unreal_runtime_properties!(is_source_distribution, "IsSourceDistribution", true);
496test_unreal_runtime_properties!(is_ensure, "IsEnsure", true);
497test_unreal_runtime_properties!(is_assert, "IsAssert", true);
498test_unreal_runtime_properties!(crash_type, "CrashType", "Crash");
499test_unreal_runtime_properties!(seconds_since_start, "SecondsSinceStart", 4);
500test_unreal_runtime_properties!(game_name, "GameName", "UE4-YetAnother");
501test_unreal_runtime_properties!(executable_name, "ExecutableName", "YetAnother");
502test_unreal_runtime_properties!(build_configuration, "BuildConfiguration", "Development");
503test_unreal_runtime_properties!(platform_name, "PlatformName", "WindowsNoEditor");
504test_unreal_runtime_properties!(engine_mode, "EngineMode", "Game");
505test_unreal_runtime_properties!(engine_version, "EngineVersion", "Game");
506test_unreal_runtime_properties!(language_lcid, "LanguageLCID", 1033);
507test_unreal_runtime_properties!(app_default_locate, "AppDefaultLocale", "en-US");
508test_unreal_runtime_properties!(
509 build_version,
510 "BuildVersion",
511 "++UE4+Release-4.20-CL-4369336"
512);
513test_unreal_runtime_properties!(is_ue4_release, "IsUE4Release", true);
514test_unreal_runtime_properties!(username, "UserName", "bruno");
515test_unreal_runtime_properties!(
516 base_dir,
517 "BaseDir",
518 "//Mac/Home/Desktop/WindowsNoEditor/YetAnother/Binaries/Win64/"
519);
520test_unreal_runtime_properties!(root_dir, "RootDir", "/Mac/Home/Desktop/WindowsNoEditor/");
521test_unreal_runtime_properties!(machine_id, "MachineId", "9776D4844CC893F55395DBBEFB0EB6D7");
522test_unreal_runtime_properties!(login_id, "LoginId", "9776d4844cc893f55395dbbefb0eb6d7");
523test_unreal_runtime_properties!(epic_account_id, "EpicAccountId", "epic acc id");
524test_unreal_runtime_properties!(
525 legacy_call_stack,
526 "CallStack",
527 r"YetAnother!AActor::IsPendingKillPending()
528YetAnother!__scrt_common_main_seh() [f:\dd\vctools\crt\vcstartup\src\startup\exe_common.inl:288]
529kernel32
530ntdll"
531);
532test_unreal_runtime_properties!(portable_call_stack, "PCallStack", "YetAnother 0x0000000025ca0000 + 703394 YetAnother 0x0000000025ca0000 + 281f2ee YetAnother 0x0000000025ca0000 + 2a26dd3 YetAnother 0x0000000025ca0000 + 2a4f984 YetAnother 0x0000000025ca0000 + 355e77e YetAnother 0x0000000025ca0000 + 3576186 YetAnother 0x0000000025ca0000 + 8acc56 YetAnother 0x0000000025ca0000 + 8acf00 YetAnother 0x0000000025ca0000 + 35c121d YetAnother 0x0000000025ca0000 + 35cfb58 YetAnother 0x0000000025ca0000 + 2eb082f YetAnother 0x0000000025ca0000 + 2eb984f YetAnother 0x0000000025ca0000 + 2d1cd39 YetAnother 0x0000000025ca0000 + 325258 YetAnother 0x0000000025ca0000 + 334e4c YetAnother 0x0000000025ca0000 + 334eaa YetAnother 0x0000000025ca0000 + 3429e6 YetAnother 0x0000000025ca0000 + 44e73c6 KERNEL32 0x000000000fd40000 + 13034 ntdll 0x0000000010060000 + 71471");
533test_unreal_runtime_properties!(
534 error_message,
535 "ErrorMessage",
536 "Access violation - code c0000005 (first/second chance not available)"
537);
538test_unreal_runtime_properties!(crash_reporter_message, "CrashReporterMessage", "message");
539test_unreal_runtime_properties!(misc_number_of_cores, "Misc.NumberOfCores", 6);
540test_unreal_runtime_properties!(
541 misc_number_of_cores_inc_hyperthread,
542 "Misc.NumberOfCoresIncludingHyperthreads",
543 6
544);
545test_unreal_runtime_properties!(misc_is_64bit, "Misc.Is64bitOperatingSystem", true);
546test_unreal_runtime_properties!(misc_cpu_vendor, "Misc.CPUVendor", "GenuineIntel");
547test_unreal_runtime_properties!(
548 misc_cpu_brand,
549 "Misc.CPUBrand",
550 "Intel(R) Core(TM) i7-7920HQ CPU @ 3.10GHz"
551);
552test_unreal_runtime_properties!(
553 misc_primary_gpu_brand,
554 "Misc.PrimaryGPUBrand",
555 "Parallels Display Adapter (WDDM)"
556);
557test_unreal_runtime_properties!(misc_os_version_major, "Misc.OSVersionMajor", "Windows 10");
558test_unreal_runtime_properties!(
559 misc_os_version_minor,
560 "Misc.OSVersionMinor",
561 "some minor version"
562);
563test_unreal_runtime_properties!(game_state_name, "GameStateName", "game state");
564test_unreal_runtime_properties!(
565 memory_stats_total_physical,
566 "MemoryStats.TotalPhysical",
567 6_896_832_512,
568);
569test_unreal_runtime_properties!(
570 memory_stats_total_virtual,
571 "MemoryStats.TotalVirtual",
572 140_737_488_224_256,
573);
574test_unreal_runtime_properties!(memory_stats_page_size, "MemoryStats.PageSize", 4096);
575test_unreal_runtime_properties!(
576 memory_stats_total_phsysical_gb,
577 "MemoryStats.TotalPhysicalGB",
578 7
579);
580test_unreal_runtime_properties!(time_of_crash, "TimeOfCrash", 636_783_195_289_630_000,);
581test_unreal_runtime_properties!(allowed_to_be_contacted, "bAllowToBeContacted", true);
582test_unreal_runtime_properties!(
583 crash_reporter_client_version,
584 "CrashReportClientVersion",
585 "1.0",
586);
587test_unreal_runtime_properties!(
588 modules,
589 "Modules",
590 r"\\Mac\Home\Desktop\WindowsNoEditor\YetAnother\Binaries\Win64\YetAnother.exe
591\\Mac\Home\Desktop\WindowsNoEditor\Engine\Binaries\ThirdParty\Vorbis\Win64\VS2015\libvorbis_64.dll"
592);
593
594test_unreal_platform_properties!(is_windows, "PlatformIsRunningWindows", true);
595test_unreal_platform_properties!(callback_result, "PlatformCallbackResult", 123);