symbolic_unreal/
context.rs

1//! Unreal Engine 4 crash context information
2#![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/// RuntimeProperties context element.
14///
15/// [Source](https://github.com/EpicGames/UnrealEngine/blob/b70f31f6645d764bcb55829228918a6e3b571e0b/Engine/Source/Runtime/Core/Private/GenericPlatform/GenericPlatformCrashContext.cpp#L274)
16#[derive(Clone, Debug, Default, PartialEq, Eq)]
17#[cfg_attr(feature = "serde", derive(serde::Serialize))]
18pub struct Unreal4ContextRuntimeProperties {
19    /// CrashGUID
20    #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
21    pub crash_guid: Option<String>,
22    /// ProcessId
23    #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
24    pub process_id: Option<u32>,
25    /// IsInternalBuild
26    #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
27    pub is_internal_build: Option<bool>,
28    /// IsSourceDistribution
29    #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
30    pub is_source_distribution: Option<bool>,
31    /// IsAssert
32    #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
33    pub is_assert: Option<bool>,
34    /// IsEnsure
35    #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
36    pub is_ensure: Option<bool>,
37    /// CrashType
38    #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
39    pub crash_type: Option<String>,
40    /// SecondsSinceStart
41    #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
42    pub seconds_since_start: Option<u32>,
43    /// GameName
44    #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
45    pub game_name: Option<String>,
46    /// ExecutableName
47    #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
48    pub executable_name: Option<String>,
49    /// BuildConfiguration
50    #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
51    pub build_configuration: Option<String>,
52    /// PlatformName
53    #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
54    pub platform_name: Option<String>,
55    /// EngineMode
56    #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
57    pub engine_mode: Option<String>,
58    /// EngineVersion
59    #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
60    pub engine_version: Option<String>,
61    /// LanguageLCID
62    #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
63    pub language_lcid: Option<i32>,
64    /// AppDefaultLocale
65    #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
66    pub app_default_locate: Option<String>,
67    /// BuildVersion
68    #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
69    pub build_version: Option<String>,
70    /// IsUE4Release
71    #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
72    pub is_ue4_release: Option<bool>,
73    /// UserName
74    #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
75    pub username: Option<String>,
76    /// BaseDir
77    #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
78    pub base_dir: Option<String>,
79    /// RootDir
80    #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
81    pub root_dir: Option<String>,
82    /// MachineId
83    #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
84    pub machine_id: Option<String>,
85    /// LoginId
86    #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
87    pub login_id: Option<String>,
88    /// EpicAccountId
89    #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
90    pub epic_account_id: Option<String>,
91    /// CallStack
92    /// [Source](https://github.com/EpicGames/UnrealEngine/blob/b70f31f6645d764bcb55829228918a6e3b571e0b/Engine/Source/Runtime/Core/Private/GenericPlatform/GenericPlatformCrashContext.cpp#L326-L327)
93    #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
94    pub legacy_call_stack: Option<String>,
95    /// PCallStack
96    // [Sopurce](https://github.com/EpicGames/UnrealEngine/blob/b70f31f6645d764bcb55829228918a6e3b571e0b/Engine/Source/Runtime/Core/Private/GenericPlatform/GenericPlatformCrashContext.cpp#L329-L330)
97    #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
98    pub portable_call_stack: Option<String>,
99    /// UserDescription
100    #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
101    pub user_description: Option<String>,
102    /// ErrorMessage
103    #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
104    pub error_message: Option<String>,
105    /// CrashReporterMessage
106    #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
107    pub crash_reporter_message: Option<String>,
108    /// Misc.NumberOfCores
109    #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
110    pub misc_number_of_cores: Option<u32>,
111    /// Misc.NumberOfCoresIncludingHyperthreads
112    #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
113    pub misc_number_of_cores_inc_hyperthread: Option<u32>,
114    /// Misc.Is64bitOperatingSystem
115    #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
116    pub misc_is_64bit: Option<bool>,
117    /// Misc.CPUVendor
118    #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
119    pub misc_cpu_vendor: Option<String>,
120    /// Misc.CPUBrand
121    #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
122    pub misc_cpu_brand: Option<String>,
123    /// Misc.PrimaryGPUBrand
124    #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
125    pub misc_primary_gpu_brand: Option<String>,
126    /// Misc.OSVersionMajor
127    #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
128    pub misc_os_version_major: Option<String>,
129    /// Misc.OSVersionMinor
130    #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
131    pub misc_os_version_minor: Option<String>,
132    /// GameStateName
133    #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
134    pub game_state_name: Option<String>,
135    /// MemoryStats.TotalPhysical
136    #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
137    pub memory_stats_total_physical: Option<u64>,
138    /// MemoryStats.TotalVirtual
139    #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
140    pub memory_stats_total_virtual: Option<u64>,
141    /// MemoryStats.PageSize
142    #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
143    pub memory_stats_page_size: Option<u64>,
144    /// MemoryStats.TotalPhysicalGB
145    #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
146    pub memory_stats_total_phsysical_gb: Option<u32>,
147    /// TimeOfCrash
148    #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
149    pub time_of_crash: Option<u64>,
150    /// bAllowToBeContacted
151    #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
152    pub allowed_to_be_contacted: Option<bool>,
153    /// CrashReportClientVersion
154    #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
155    pub crash_reporter_client_version: Option<String>,
156    /// Modules
157    #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
158    pub modules: Option<String>,
159    /// Custom attributes
160    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            // We don't expect an XML with namespace here
182            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/// Platform specific properties.
264///
265/// [Source](https://github.com/EpicGames/UnrealEngine/blob/b70f31f6645d764bcb55829228918a6e3b571e0b/Engine/Source/Runtime/Core/Private/GenericPlatform/GenericPlatformCrashContext.cpp#L451-L455)
266/// [Windows](https://github.com/EpicGames/UnrealEngine/blob/b70f31f6645d764bcb55829228918a6e3b571e0b/Engine/Source/Runtime/Core/Private/Windows/WindowsPlatformCrashContext.cpp#L39-L44)
267#[derive(Clone, Debug, Default, PartialEq, Eq)]
268#[cfg_attr(feature = "serde", derive(serde::Serialize))]
269pub struct Unreal4ContextPlatformProperties {
270    /// Whether the crash happened on a Windows device.
271    pub is_windows: Option<bool>,
272    /// Platform-specific UE4 Core value.
273    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/// The context data found in the context xml file.
305///
306/// [Source](https://github.com/EpicGames/UnrealEngine/blob/b70f31f6645d764bcb55829228918a6e3b571e0b/Engine/Source/Runtime/Core/Private/GenericPlatform/GenericPlatformCrashContext.cpp)
307#[derive(Clone, Debug, Default, PartialEq, Eq)]
308#[cfg_attr(feature = "serde", derive(serde::Serialize))]
309pub struct Unreal4Context {
310    /// RuntimeProperties context element.
311    #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
312    pub runtime_properties: Option<Unreal4ContextRuntimeProperties>,
313
314    /// Platform specific properties.
315    #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
316    pub platform_properties: Option<Unreal4ContextPlatformProperties>,
317
318    /// Engine data.
319    #[cfg_attr(
320        feature = "serde",
321        serde(default, skip_serializing_if = "BTreeMap::is_empty")
322    )]
323    pub engine_data: BTreeMap<String, String>,
324
325    /// Game data.
326    #[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    /// Parses the unreal context XML file.
342    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>{&quot;release&quot;:"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);