Skip to content

Commit 18dcb93

Browse files
committed
feat: OutputDevice build support for Linux
Note that this has minimal testing. Specifically, running UE4SS on Windows has been tested and still works, and using DynamicOutput on Linux has been tested and is working except FileDevice and NewFileDevice. Nothing else has been tested. The File dependency on Linux is not implemented.
1 parent 01e0a58 commit 18dcb93

10 files changed

Lines changed: 176 additions & 39 deletions

File tree

deps/first/DynamicOutput/include/DynamicOutput/Macros.hpp

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
#ifndef DYNAMIC_OUTPUT_MACROS_HPP
22
#define DYNAMIC_OUTPUT_MACROS_HPP
33

4+
#include <Helpers/String.hpp>
5+
46
#define ENABLE_OUTPUT_DEVICE_DEBUG_MODE 0
57

68
#define ASSERT_OUTPUT_DEVICE_IS_VALID(param_device) \
@@ -16,4 +18,14 @@
1618
"construct a Targets object and supply your own devices.") \
1719
}
1820

21+
#ifdef _WIN32
22+
#define RC_DEVICE_PRINT_FUNC(fmt_var, optional_arg, optional_prefix) \
23+
wprintf_s(STR("%s%hs%ls\033[0m"), optional_prefix, log_level_to_color(static_cast<Color::Color>(optional_arg)).c_str(), m_formatter(fmt_var).c_str());
24+
#elif __linux__
25+
#define RC_DEVICE_PRINT_FUNC(fmt_var, optional_arg, optional_prefix) \
26+
const auto as_utf8 = to_utf8_string(m_formatter(fmt_var)); \
27+
const auto color_as_utf8 = to_utf8_string(log_level_to_color(static_cast<Color::Color>(optional_arg))); \
28+
printf("%s%s%s\033[0m", optional_prefix, color_as_utf8.c_str(), as_utf8.c_str());
29+
#endif
30+
1931
#endif // DYNAMIC_OUTPUT_MACROS_HPP

deps/first/DynamicOutput/include/DynamicOutput/Output.hpp

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,30 @@ namespace RC::Output
3333

3434
auto RC_DYNOUT_API has_internal_error() -> bool;
3535

36+
inline auto log_level_to_color(Color::Color color) -> std::string
37+
{
38+
switch (color)
39+
{
40+
case Color::Default:
41+
case Color::NoColor:
42+
return "\033[0;0m";
43+
case Color::Cyan:
44+
return "\033[1;36m";
45+
case Color::Yellow:
46+
return "\033[1;33m";
47+
case Color::Red:
48+
return "\033[1;31m";
49+
case Color::Green:
50+
return "\033[1;32m";
51+
case Color::Blue:
52+
return "\033[1;94m";
53+
case Color::Purple:
54+
return "\033[1;35m";
55+
}
56+
57+
return "\033[0;0m";
58+
}
59+
3660
template <typename DeviceType>
3761
auto get_device_internal(OutputDevicesContainerType& device_container) -> DeviceType&
3862
{
@@ -169,7 +193,7 @@ namespace RC::Output
169193
ASSERT_OUTPUT_DEVICE_IS_VALID(device)
170194
if (device->has_optional_arg())
171195
{
172-
device->receive_with_optional_arg(fmt::vformat(content, fmt_args...), RC_STD_MAKE_FORMAT_ARGS(static_cast<int32_t>(optional_arg)));
196+
device->receive_with_optional_arg(fmt::vformat(content, fmt_args...), static_cast<int32_t>(optional_arg));
173197
}
174198
else
175199
{

deps/first/DynamicOutput/include/DynamicOutput/TestDevice.hpp

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,12 @@
44
#include <DynamicOutput/Macros.hpp>
55
#include <DynamicOutput/OutputDevice.hpp>
66

7+
#if _WIN32
8+
#define RC_TEST_DEVICE_PRINT printf_s
9+
#elif __linux__
10+
#define RC_TEST_DEVICE_PRINT printf
11+
#endif
12+
713
namespace RC::Output
814
{
915
class TestDevice : public OutputDevice
@@ -49,23 +55,23 @@ namespace RC::Output
4955
switch (typed_optional_arg)
5056
{
5157
case OptionalArgTest::ValueDefault:
52-
printf_s("Optional Arg: ValueDefault - ");
58+
RC_TEST_DEVICE_PRINT("Optional Arg: ValueDefault - ");
5359
break;
5460
case OptionalArgTest::ValueOne:
55-
printf_s("Optional Arg: ValueOne - ");
61+
RC_TEST_DEVICE_PRINT("Optional Arg: ValueOne - ");
5662
break;
5763
case OptionalArgTest::ValueTwo:
58-
printf_s("Optional Arg: ValueTwo - ");
64+
RC_TEST_DEVICE_PRINT("Optional Arg: ValueTwo - ");
5965
break;
6066
case OptionalArgTest::ValueThree:
61-
printf_s("Optional Arg: ValueThree - ");
67+
RC_TEST_DEVICE_PRINT("Optional Arg: ValueThree - ");
6268
break;
6369
}
6470

6571
#if ENABLE_OUTPUT_DEVICE_DEBUG_MODE
66-
printf_s("TestDevice received: %S", fmt.c_str());
72+
RC_DEVICE_PRINT_FUNC(fmt, optional_arg, "TestDevice received: ");
6773
#else
68-
printf_s("%S", FromCharTypePtr<wchar_t>(fmt.data()));
74+
RC_DEVICE_PRINT_FUNC(fmt, optional_arg, "");
6975
#endif
7076
}
7177
};

deps/first/DynamicOutput/src/DebugConsoleDevice.cpp

Lines changed: 6 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -4,51 +4,31 @@
44
#include <DynamicOutput/DebugConsoleDevice.hpp>
55
#include <DynamicOutput/Output.hpp>
66

7+
#ifdef _WIN32
78
#define NOMINMAX
89
#include <Windows.h>
910
#ifdef TEXT
1011
#undef TEXT
1112
#endif
13+
#endif
1214

1315
namespace RC::Output
1416
{
15-
static auto log_level_to_color(Color::Color color) -> std::string
16-
{
17-
switch (color)
18-
{
19-
case Color::Default:
20-
case Color::NoColor:
21-
return "\033[0;0m";
22-
case Color::Cyan:
23-
return "\033[1;36m";
24-
case Color::Yellow:
25-
return "\033[1;33m";
26-
case Color::Red:
27-
return "\033[1;31m";
28-
case Color::Green:
29-
return "\033[1;32m";
30-
case Color::Blue:
31-
return "\033[1;94m";
32-
case Color::Purple:
33-
return "\033[1;35m";
34-
}
35-
36-
return "\033[0;0m";
37-
}
38-
3917
auto DebugConsoleDevice::set_windows_console_out_mode_if_needed() const -> void
4018
{
4119
if (m_windows_console_mode_set)
4220
{
4321
return;
4422
}
23+
#ifdef _WIN32
4524
HANDLE current_console_out_handle = GetStdHandle(STD_OUTPUT_HANDLE);
4625
if (current_console_out_handle != INVALID_HANDLE_VALUE)
4726
{
4827
DWORD current_console_out_mode = 0;
4928
GetConsoleMode(current_console_out_handle, &current_console_out_mode);
5029
SetConsoleMode(current_console_out_handle, current_console_out_mode | ENABLE_VIRTUAL_TERMINAL_PROCESSING);
5130
}
31+
#endif
5232
m_windows_console_mode_set = true;
5333
}
5434

@@ -67,9 +47,9 @@ namespace RC::Output
6747
set_windows_console_out_mode_if_needed();
6848

6949
#if ENABLE_OUTPUT_DEVICE_DEBUG_MODE
70-
wprintf_s(STR("DebugConsoleDevice received: %ls"), m_formatter(fmt).c_str());
50+
RC_DEVICE_PRINT_FUNC(fmt, optional_arg, "DebugConsoleDevice received: ")
7151
#else
72-
wprintf_s(STR("%hs%ls\033[0m"), log_level_to_color(static_cast<Color::Color>(optional_arg)).c_str(), m_formatter(fmt).c_str());
52+
RC_DEVICE_PRINT_FUNC(fmt, optional_arg, "")
7353
#endif
7454
}
7555
} // namespace RC::Output

deps/first/File/include/File/FileDef.hpp

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,14 +5,18 @@ namespace RC::File
55
#ifndef RC_DETECTED_OS
66
#ifdef _WIN32
77
#define RC_DETECTED_OS _WIN32
8+
#elif __linux__
9+
#define RC_DETECTED_OS __linux__
810
#else
911
static_assert(false, "Could not setup the 'Handle' typedef because a supported OS was not detected.");
1012
#endif
1113
#endif
1214

13-
#if RC_DETECTED_OS == _WIN32
1415
#ifndef RC_OS_FILE_TYPE_INCLUDE_FILE
16+
#if RC_DETECTED_OS == _WIN32
1517
#define RC_OS_FILE_TYPE_INCLUDE_FILE <File/FileType/WinFile.hpp>
18+
#elif RC_DETECTED_OS == __linux__
19+
#define RC_OS_FILE_TYPE_INCLUDE_FILE <File/FileType/LinuxFile.hpp>
1620
#else
1721
static_assert(false, "Could not setup the 'RC_OS_FILE_TYPE_INCLUDE_FILE' macro because a supported OS was not detected.");
1822
#endif
Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
#pragma once
2+
3+
#ifdef __linux__
4+
5+
#include <filesystem>
6+
#include <format>
7+
8+
#include <File/Common.hpp>
9+
#include <File/FileType/FileBase.hpp>
10+
#include <File/Macros.hpp>
11+
12+
// NOTE: This file is effectively a stub.
13+
// The LinuxFile class has not been implemented!
14+
// File operations through the Output::* system doesn't work, for example FileDevice and NewFileDevice.
15+
16+
namespace RC::File
17+
{
18+
class LinuxFile : public FileInterface<LinuxFile>
19+
{
20+
private:
21+
using HANDLE = void*;
22+
23+
struct IdentifyingProperties
24+
{
25+
unsigned long volume_serial_number{};
26+
unsigned long file_index_low{};
27+
unsigned long file_index_high{};
28+
unsigned long creation_time_low{};
29+
unsigned long creation_time_high{};
30+
unsigned long last_write_time_high{};
31+
unsigned long last_write_time_low{};
32+
unsigned long file_size_low{};
33+
unsigned long file_size_high{};
34+
};
35+
36+
private:
37+
HANDLE m_file{};
38+
HANDLE m_map_handle{};
39+
uint8_t* m_memory_map{};
40+
OpenProperties m_open_properties{};
41+
std::filesystem::path m_file_path_and_name{};
42+
std::filesystem::path m_serialization_file_path_and_name{};
43+
IdentifyingProperties m_identifying_properties{};
44+
constexpr static inline size_t cache_size = 0x500;
45+
unsigned char m_cache[cache_size]{};
46+
size_t m_offset_to_next_serialized_item{};
47+
bool m_has_cache_in_memory{};
48+
bool m_has_cached_identifying_properties{};
49+
bool m_is_file_open{};
50+
51+
public:
52+
~LinuxFile() override = default;
53+
54+
private:
55+
auto static create_all_directories(const std::filesystem::path& file_name_and_path) -> void;
56+
57+
private:
58+
auto close_file() -> void;
59+
60+
public:
61+
[[nodiscard]] auto is_file_open() const -> bool;
62+
63+
public:
64+
RC_FILE_API auto set_file(HANDLE new_file) -> void;
65+
RC_FILE_API auto set_is_file_open(bool new_is_open) -> void;
66+
RC_FILE_API auto get_file() -> HANDLE;
67+
RC_FILE_API auto serialization_file_exists() -> bool;
68+
69+
// File Interface -> START
70+
RC_FILE_API auto is_valid() noexcept -> bool override;
71+
RC_FILE_API auto invalidate_file() noexcept -> void override;
72+
RC_FILE_API auto static delete_file(const std::filesystem::path&) -> void;
73+
RC_FILE_API auto delete_file() -> void override;
74+
RC_FILE_API auto get_raw_handle() noexcept -> void* override;
75+
[[nodiscard]] RC_FILE_API auto get_file_path() const noexcept -> const std::filesystem::path& override;
76+
RC_FILE_API auto set_serialization_output_file(const std::filesystem::path& output_file) noexcept -> void override;
77+
RC_FILE_API auto serialize_identifying_properties() -> void override;
78+
RC_FILE_API auto deserialize_identifying_properties() -> void override;
79+
RC_FILE_API auto is_deserialized_and_live_equal() -> bool override;
80+
RC_FILE_API auto invalidate_serialization() -> void override;
81+
RC_FILE_API auto serialize_item(const GenericItemData& data, bool is_internal_item = false) -> void override;
82+
RC_FILE_API auto get_serialized_item(size_t data_size, bool is_internal_item = false) -> void* override;
83+
RC_FILE_API auto close_current_file() -> void override;
84+
RC_FILE_API auto write_string_to_file(StringViewType string_to_write) -> void override;
85+
RC_FILE_API auto is_same_as(LinuxFile& other_file) -> bool override;
86+
[[nodiscard]] RC_FILE_API auto read_all() const -> StringType override;
87+
[[nodiscard]] RC_FILE_API auto memory_map() -> std::span<uint8_t> override;
88+
[[nodiscard]] RC_FILE_API auto static open_file(const std::filesystem::path& file_name_and_path, const OpenProperties& open_properties) -> LinuxFile;
89+
// File Interface -> END
90+
};
91+
92+
// This file is automatically included ONLY if Windows is detected
93+
// Therefore, it's not necessary to do any checks here
94+
template <ImplementsFileInterface UnderlyingAbstraction>
95+
class HandleTemplate;
96+
using Handle = HandleTemplate<LinuxFile>;
97+
} // namespace RC::File
98+
99+
#endif // ifdef __linux__

deps/first/File/include/File/FileType/WinFile.hpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
#pragma once
22

3+
#ifdef _WIN32
4+
35
#include <filesystem>
46
#include <format>
57

@@ -89,3 +91,5 @@ namespace RC::File
8991
class HandleTemplate;
9092
using Handle = HandleTemplate<WinFile>;
9193
} // namespace RC::File
94+
95+
#endif // ifdef _WIN32

deps/first/File/src/FileType/WinFile.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
#ifdef _WIN32
12
#include <fstream>
23

34
#include <File/File.hpp>
@@ -623,3 +624,5 @@ namespace RC::File
623624
return file;
624625
}
625626
} // namespace RC::File
627+
628+
#endif // ifdef _WIN32

deps/first/Helpers/src/SysError.cpp

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,9 @@
1212
#include "Helpers/SysError.hpp"
1313
#include "Helpers/String.hpp"
1414

15+
#include <fmt/core.h>
16+
#include <fmt/xchar.h>
17+
1518
namespace RC
1619
{
1720
SysError::SysError(const int error_code, const std::error_category &category)
@@ -28,7 +31,7 @@ namespace RC
2831

2932
auto SysError::assign(unsigned long error_code) -> void
3033
{
31-
m_error_text.assign(std::format(L"[0x{:x}] {}", error_code, format_error(static_cast<int>(error_code))));
34+
m_error_text.assign(fmt::format(STR("[0x{:x}] {}"), error_code, format_error(static_cast<int>(error_code))));
3235
}
3336

3437
#ifdef _WIN32
@@ -53,20 +56,20 @@ namespace RC
5356

5457
auto SysError::category() const -> StringType
5558
{
56-
return to_wstring(m_error_category->name());
59+
return ensure_str(m_error_category->name());
5760
}
5861

5962
auto SysError::format_error(const int error_code) const -> StringType
6063
{
6164
const std::error_category& error_category = *m_error_category;
6265
const std::error_code ec(error_code, error_category);
6366
// remove new line(s) and tabs
64-
auto result = std::regex_replace(to_wstring(std::system_error(ec).what()), std::wregex(STR("(\t|\r?\n)")), STR(" "));
67+
auto result = std::regex_replace(to_wstring(std::system_error(ec).what()), std::wregex(L"(\t|\r?\n)"), L" ");
6568
// right trim
6669
result.erase(std::ranges::find_if(std::ranges::reverse_view(result), [](const CharType c) -> bool {
6770
return !std::isspace<CharType>(c, std::locale::classic());
6871
}).base(), result.end());
6972

70-
return result;
73+
return ensure_str(result);
7174
}
7275
}

deps/first/String/include/String/StringType.hpp

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,9 @@
99
// This is a debug flag to force the use of u16string for testing purposes
1010
// char16_t and wchar_t are two different types, so we need to force the use of one of them
1111
// to ensure we have covered all the cases.
12-
// #define FORCE_U16
12+
#ifdef __linux__
13+
#define FORCE_U16
14+
#endif
1315

1416
namespace RC
1517
{

0 commit comments

Comments
 (0)