More C++ Refactors

This commit is contained in:
TheBrokenRail 2024-11-17 22:48:25 -05:00
parent 58a6706cf9
commit 57503d6a31
63 changed files with 830 additions and 1006 deletions

View File

@ -62,6 +62,13 @@ string(CONCAT COMPILE_FLAGS_SETUP
# Skip RPath # Skip RPath
"set(CMAKE_SKIP_BUILD_RPATH TRUE)" "set(CMAKE_SKIP_BUILD_RPATH TRUE)"
) )
if(BUILD_ARM_COMPONENTS)
string(CONCAT COMPILE_FLAGS_SETUP
"${COMPILE_FLAGS_SETUP}\n"
# Disable C++11 String ABI
"add_compile_definitions(_GLIBCXX_USE_CXX11_ABI=0)"
)
endif()
cmake_language(EVAL CODE "${COMPILE_FLAGS_SETUP}") cmake_language(EVAL CODE "${COMPILE_FLAGS_SETUP}")
# Fast Math # Fast Math
@ -79,7 +86,7 @@ if(CMAKE_C_COMPILER_ID STREQUAL "GNU")
endif() endif()
endif() endif()
# Buld Dependencies # Build Dependencies
add_subdirectory(dependencies) add_subdirectory(dependencies)
# Build libreborn # Build libreborn
@ -117,10 +124,10 @@ endif()
if(BUILD_ARM_COMPONENTS) if(BUILD_ARM_COMPONENTS)
install(EXPORT sdk DESTINATION "${MCPI_SDK_DIR}" FILE "sdk-targets.cmake" EXPORT_LINK_INTERFACE_LIBRARIES) install(EXPORT sdk DESTINATION "${MCPI_SDK_DIR}" FILE "sdk-targets.cmake" EXPORT_LINK_INTERFACE_LIBRARIES)
file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/sdk.cmake" file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/sdk.cmake"
# Sanity Check
"${ARM_SANITY_CHECK}\n"
# Compile Flags # Compile Flags
"${COMPILE_FLAGS_SETUP}\n" "${COMPILE_FLAGS_SETUP}\n"
# Snaity Check
"${ARM_SANITY_CHECK}\n"
# Log # Log
"message(STATUS \"Using Reborn SDK v${MCPI_VERSION}\")\n" "message(STATUS \"Using Reborn SDK v${MCPI_VERSION}\")\n"
# Include Targets # Include Targets

View File

@ -2,12 +2,15 @@ project(launcher)
# Launcher # Launcher
add_executable(launcher add_executable(launcher
src/bootstrap.cpp src/bootstrap/bootstrap.cpp
src/patchelf.cpp src/bootstrap/mods.cpp
src/util.cpp src/bootstrap/assets.cpp
src/crash-report.cpp src/bootstrap/patchelf.cpp
src/sdk.cpp src/bootstrap/debug.cpp
src/mods.cpp src/util/util.cpp
src/util/sdk.cpp
src/logger/logger.cpp
src/logger/crash-report.cpp
src/options/parser.cpp src/options/parser.cpp
src/main.cpp src/main.cpp
src/client/configuration.cpp src/client/configuration.cpp
@ -19,6 +22,8 @@ target_link_libraries(launcher reborn-util LIB_LIEF trampoline-headers)
# RPath # RPath
set_target_properties(launcher PROPERTIES INSTALL_RPATH "$ORIGIN/lib/native") set_target_properties(launcher PROPERTIES INSTALL_RPATH "$ORIGIN/lib/native")
target_link_options(launcher PRIVATE "LINKER:--disable-new-dtags") target_link_options(launcher PRIVATE "LINKER:--disable-new-dtags")
# Files
target_compile_definitions(launcher PRIVATE _FILE_OFFSET_BITS=64)
# Install # Install
install(TARGETS launcher DESTINATION "${MCPI_INSTALL_DIR}") install(TARGETS launcher DESTINATION "${MCPI_INSTALL_DIR}")

View File

@ -1,190 +0,0 @@
#define _FILE_OFFSET_BITS 64
#include <string>
#include <vector>
#include <libreborn/libreborn.h>
#include "util.h"
#include "bootstrap.h"
#include "patchelf.h"
#define MCPI_BINARY "minecraft-pi"
#define REQUIRED_PAGE_SIZE 4096
// Debug Information
static void run_debug_command(const char *const command[], const char *prefix) {
int status = 0;
char *output = run_command(command, &status, nullptr);
if (output != nullptr) {
// Remove Newline
size_t length = strlen(output);
if (length > 0 && output[length - 1] == '\n') {
output[length - 1] = '\0';
}
// Print
DEBUG("%s: %s", prefix, output);
free(output);
}
if (!is_exit_status_success(status)) {
ERR("Unable To Gather Debug Information");
}
}
static void print_debug_information() {
// System Information
const char *const command[] = {"uname", "-a", nullptr};
run_debug_command(command, "System Information");
// Version
DEBUG("Reborn Version: v%s", MCPI_VERSION);
// Architecture
const char *arch =
#ifdef __x86_64__
"AMD64"
#elif defined(__aarch64__)
"ARM64"
#elif defined(__arm__)
"ARM32"
#else
"Unknown"
#endif
;
DEBUG("Reborn Target Architecture: %s", arch);
}
// Bootstrap
void bootstrap(const options_t &options) {
// Debug Information
print_debug_information();
// Check Page Size
long page_size = sysconf(_SC_PAGESIZE);
if (page_size != REQUIRED_PAGE_SIZE) {
CONDITIONAL_ERR(!options.skip_pagesize_check, "Invalid page size! A page size of %ld bytes is required, but the system size is %ld bytes.", (long) REQUIRED_PAGE_SIZE, page_size);
}
// Get Binary Directory
const std::string binary_directory = get_binary_directory();
DEBUG("Binary Directory: %s", binary_directory.c_str());
// Copy SDK
if (!reborn_is_server()) {
copy_sdk(binary_directory, true);
}
// Set MCPI_REBORN_ASSETS_PATH
{
std::string assets_path = safe_realpath("/proc/self/exe");
chop_last_component(assets_path);
assets_path += "/data";
set_and_print_env(_MCPI_REBORN_ASSETS_PATH_ENV, assets_path.c_str());
}
// Resolve Binary Path & Set MCPI_DIRECTORY
std::string original_game_binary;
std::string game_binary;
{
// Log
DEBUG("Resolving File Paths...");
// Resolve Full Binary Path
const std::string full_path = binary_directory + ("/" MCPI_BINARY);
original_game_binary = safe_realpath(full_path);
const char *custom_binary = getenv(MCPI_BINARY_ENV);
if (custom_binary != nullptr) {
game_binary = safe_realpath(custom_binary);
} else {
game_binary = original_game_binary;
}
}
// Configure Preloaded Objects
std::vector<std::string> mcpi_ld_preload;
{
// Log
DEBUG("Locating Mods...");
// ARM Components
mcpi_ld_preload = bootstrap_mods(binary_directory);
}
// Configure Library Search Path
std::vector<std::string> mcpi_ld_path;
{
// Log
DEBUG("Setting Linker Search Paths...");
// Library Search Path For ARM Components
{
// Add ARM Library Directory
mcpi_ld_path.push_back("lib/arm");
// Add ARM Sysroot Libraries (Ensure Priority) (Ignore On Actual ARM System)
#ifdef MCPI_USE_PREBUILT_ARMHF_TOOLCHAIN
mcpi_ld_path.push_back("sysroot/lib");
mcpi_ld_path.push_back("sysroot/lib/arm-linux-gnueabihf");
mcpi_ld_path.push_back("sysroot/usr/lib");
mcpi_ld_path.push_back("sysroot/usr/lib/arm-linux-gnueabihf");
#endif
// Fix Paths
for (std::string &path : mcpi_ld_path) {
path = binary_directory + '/' + path;
}
}
}
// Fix MCPI Dependencies
char new_mcpi_exe_path[] = MCPI_PATCHED_DIR "/XXXXXX";
{
// Log
DEBUG("Patching ELF...");
// Find Linker
std::string linker = "/lib/ld-linux-armhf.so.3";
#ifdef MCPI_USE_PREBUILT_ARMHF_TOOLCHAIN
// Use ARM Sysroot Linker
linker = binary_directory + "/sysroot" + linker;
#endif
// Patch
patch_mcpi_elf_dependencies(game_binary, new_mcpi_exe_path, linker, mcpi_ld_path, mcpi_ld_preload);
// Verify
if (!starts_with(new_mcpi_exe_path, MCPI_PATCHED_DIR)) {
IMPOSSIBLE();
}
}
// Set MCPI_VANILLA_ASSETS_PATH
{
std::string assets_path = original_game_binary;
chop_last_component(assets_path);
assets_path += "/data";
set_and_print_env(_MCPI_VANILLA_ASSETS_PATH_ENV, assets_path.c_str());
}
// Start Game
INFO("Starting Game...");
// Arguments
std::vector<std::string> args;
// Use Extra If Needed
#ifdef MCPI_BUILD_RUNTIME
args.push_back("runtime");
#endif
// Specify MCPI Binary
args.push_back(new_mcpi_exe_path);
// Run
const char *new_argv[args.size() + 1];
for (std::vector<std::string>::size_type i = 0; i < args.size(); i++) {
new_argv[i] = args[i].c_str();
}
new_argv[args.size()] = nullptr;
safe_execvpe(new_argv, environ);
}

View File

@ -1,10 +0,0 @@
#pragma once
#include <string>
#include <vector>
#include "options/parser.h"
void bootstrap(const options_t &options);
void copy_sdk(const std::string &binary_directory, bool log_with_debug);
std::vector<std::string> bootstrap_mods(const std::string &binary_directory);

View File

@ -0,0 +1,15 @@
#include <libreborn/libreborn.h>
#include "bootstrap.h"
#include "../util/util.h"
// Setup Asset Paths
static void setup_path(const char *env_name, std::string assets_path) {
chop_last_component(assets_path);
assets_path += "/data";
set_and_print_env(env_name, assets_path.c_str());
}
void bootstrap_assets(const std::string &original_game_binary) {
setup_path(_MCPI_REBORN_ASSETS_PATH_ENV, safe_realpath("/proc/self/exe"));
setup_path(_MCPI_VANILLA_ASSETS_PATH_ENV, original_game_binary);
}

View File

@ -0,0 +1,75 @@
#include <string>
#include <vector>
#include <libreborn/libreborn.h>
#include "../util/util.h"
#include "bootstrap.h"
#define MCPI_BINARY "minecraft-pi"
#define REQUIRED_PAGE_SIZE 4096
// Bootstrap
void bootstrap(const options_t &options) {
// Debug Information
print_debug_information();
// Check Page Size
const long page_size = sysconf(_SC_PAGESIZE);
if (page_size != REQUIRED_PAGE_SIZE) {
CONDITIONAL_ERR(!options.skip_pagesize_check, "Invalid page size! A page size of %ld bytes is required, but the system size is %ld bytes.", (long) REQUIRED_PAGE_SIZE, page_size);
}
// Get Binary Directory
const std::string binary_directory = get_binary_directory();
DEBUG("Binary Directory: %s", binary_directory.c_str());
// Copy SDK
if (!reborn_is_server()) {
copy_sdk(binary_directory, true);
}
// Resolve Binary Path
DEBUG("Resolving File Paths...");
std::string original_game_binary = binary_directory + ("/" MCPI_BINARY);
original_game_binary = safe_realpath(original_game_binary);
const char *custom_binary = getenv(MCPI_BINARY_ENV);
const std::string game_binary = custom_binary ? safe_realpath(custom_binary) : original_game_binary;
// Configure Preloaded Objects
DEBUG("Locating Mods...");
const std::vector<std::string> mcpi_ld_preload = bootstrap_mods(binary_directory);
// Configure Library Search Path
DEBUG("Setting Linker Search Paths...");
const std::vector<std::string> mcpi_ld_path = get_ld_path(binary_directory);
// Assets
DEBUG("Finding Assets...");
bootstrap_assets(original_game_binary);
// Patch Binary
char new_mcpi_exe_path[] = MCPI_PATCHED_DIR "/XXXXXX";
DEBUG("Patching ELF...");
patch_mcpi_elf_dependencies(game_binary, new_mcpi_exe_path, get_new_linker(binary_directory), mcpi_ld_path, mcpi_ld_preload);
// Start Game
INFO("Starting Game...");
// Arguments
const std::vector<std::string> args {
#ifdef MCPI_BUILD_RUNTIME
"runtime",
#endif
new_mcpi_exe_path
};
// Run
const char *new_argv[args.size() + 1];
for (std::vector<std::string>::size_type i = 0; i < args.size(); i++) {
new_argv[i] = args[i].c_str();
}
new_argv[args.size()] = nullptr;
safe_execvpe(new_argv, environ);
}

View File

@ -0,0 +1,20 @@
#pragma once
#include <string>
#include <vector>
#include "../options/parser.h"
#define MCPI_PATCHED_DIR "/tmp/.minecraft-pi-patched"
void bootstrap(const options_t &options);
// Debugging
void print_debug_information();
// Mods
std::vector<std::string> bootstrap_mods(const std::string &binary_directory);
// Assets
void bootstrap_assets(const std::string &original_game_binary);
// ELF
std::string get_new_linker(const std::string &binary_directory);
std::vector<std::string> get_ld_path(const std::string &binary_directory);
void patch_mcpi_elf_dependencies(const std::string &original_path, char *new_path, const std::string &interpreter, const std::vector<std::string> &rpath, const std::vector<std::string> &mods);

View File

@ -0,0 +1,43 @@
#include <libreborn/libreborn.h>
#include "bootstrap.h"
// Debug Information
static void run_debug_command(const char *const command[], const char *prefix) {
int status = 0;
const std::vector<unsigned char> *output = run_command(command, &status);
if (!is_exit_status_success(status)) {
ERR("Unable To Gather Debug Information");
}
std::string output_str = (const char *) output->data();
delete output;
// Trim
const std::string::size_type length = output_str.length();
if (length > 0 && output_str[length - 1] == '\n') {
output_str.pop_back();
}
// Print
DEBUG("%s: %s", prefix, output_str.c_str());
}
void print_debug_information() {
// System Information
constexpr const char *const command[] = {"uname", "-a", nullptr};
run_debug_command(command, "System Information");
// Version
DEBUG("Reborn Version: v%s", MCPI_VERSION);
// Architecture
const char *arch =
#ifdef __x86_64__
"AMD64"
#elif defined(__aarch64__)
"ARM64"
#elif defined(__arm__)
"ARM32"
#else
"Unknown"
#endif
;
DEBUG("Reborn Target Architecture: %s", arch);
}

View File

@ -9,7 +9,7 @@
#include <libreborn/libreborn.h> #include <libreborn/libreborn.h>
#include "patchelf.h" #include "bootstrap.h"
// Duplicate MCPI Executable Into /tmp // Duplicate MCPI Executable Into /tmp
static void duplicate_mcpi_executable(char *new_path) { static void duplicate_mcpi_executable(char *new_path) {
@ -43,12 +43,10 @@ void patch_mcpi_elf_dependencies(const std::string &original_path, char *new_pat
duplicate_mcpi_executable(new_path); duplicate_mcpi_executable(new_path);
// Load Binary // Load Binary
std::unique_ptr<LIEF::ELF::Binary> binary = LIEF::ELF::Parser::parse(original_path); const std::unique_ptr<LIEF::ELF::Binary> binary = LIEF::ELF::Parser::parse(original_path);
// Set Interpreter // Set Interpreter
if (!interpreter.empty()) {
binary->interpreter(interpreter); binary->interpreter(interpreter);
}
// Remove Existing Needed Libraries // Remove Existing Needed Libraries
std::vector<std::string> to_remove; std::vector<std::string> to_remove;
@ -96,3 +94,34 @@ void patch_mcpi_elf_dependencies(const std::string &original_path, char *new_pat
ERR("Unable To Set File Permissions: %s: %s", new_path, strerror(errno)); ERR("Unable To Set File Permissions: %s: %s", new_path, strerror(errno));
} }
} }
// Linker
std::string get_new_linker(const std::string &binary_directory) {
std::string linker = "/lib/ld-linux-armhf.so.3";
#ifdef MCPI_USE_PREBUILT_ARMHF_TOOLCHAIN
linker = binary_directory + "/sysroot" + linker;
#else
(void) binary_directory;
#endif
return linker;
}
std::vector<std::string> get_ld_path(const std::string &binary_directory) {
std::vector<std::string> mcpi_ld_path = {
// ARM Sysroot
#ifdef MCPI_USE_PREBUILT_ARMHF_TOOLCHAIN
"sysroot/lib",
"sysroot/lib/arm-linux-gnueabihf",
"sysroot/usr/lib",
"sysroot/usr/lib/arm-linux-gnueabihf",
#endif
// Libraries
"lib/arm"
};
// Fix Paths
for (std::string &path : mcpi_ld_path) {
path.insert(0, 1, '/');
path.insert(0, binary_directory);
}
// Return
return mcpi_ld_path;
}

View File

@ -7,11 +7,11 @@
#define CACHE_VERSION 0 #define CACHE_VERSION 0
// Load Cache // Load Cache
typedef struct { struct launcher_cache {
std::string username; std::string username;
std::string render_distance; std::string render_distance;
std::unordered_map<std::string, bool> feature_flags; std::unordered_map<std::string, bool> feature_flags;
} launcher_cache; };
extern launcher_cache empty_cache; extern launcher_cache empty_cache;
launcher_cache load_cache(); launcher_cache load_cache();

View File

@ -9,7 +9,7 @@
#include <libreborn/libreborn.h> #include <libreborn/libreborn.h>
#include "../util.h" #include "../util/util.h"
#include "configuration.h" #include "configuration.h"
#include "cache.h" #include "cache.h"
@ -83,18 +83,16 @@ static void run_command_and_set_env(const char *env_name, const char *command[])
reborn_check_display(); reborn_check_display();
// Run // Run
int return_code; int return_code;
char *output = run_command(command, &return_code, nullptr); const std::vector<unsigned char> *output = run_command(command, &return_code);
if (output != nullptr) { std::string output_str = (const char *) output->data();
delete output;
// Trim // Trim
const size_t length = strlen(output); const std::string::size_type length = output_str.length();
if (output[length - 1] == '\n') { if (length > 0 && output_str[length - 1] == '\n') {
output[length - 1] = '\0'; output_str.pop_back();
} }
// Set // Set
set_and_print_env(env_name, output); set_and_print_env(env_name, output_str.c_str());
// Free
free(output);
}
// Check Return Code // Check Return Code
if (!is_exit_status_success(return_code)) { if (!is_exit_status_success(return_code)) {
// Launch Interrupted // Launch Interrupted
@ -161,7 +159,7 @@ void configure_client(const options_t &options) {
if (options.use_default) { if (options.use_default) {
// Use Default Feature Flags // Use Default Feature Flags
set_env_if_unset(MCPI_FEATURE_FLAGS_ENV, [&cache]() { set_env_if_unset(MCPI_FEATURE_FLAGS_ENV, [&cache]() {
std::string feature_flags = ""; std::string feature_flags;
load_available_feature_flags([&feature_flags, &cache](const std::string &flag) { load_available_feature_flags([&feature_flags, &cache](const std::string &flag) {
bool value; bool value;
// Strip Default Value // Strip Default Value

View File

@ -1,259 +0,0 @@
#include <unistd.h>
#include <cstring>
#include <cerrno>
#include <cstdlib>
#include <cstdio>
#include <cstdint>
#include <csignal>
#include <poll.h>
#include <sys/ioctl.h>
#include <sys/stat.h>
#include <sys/prctl.h>
#include <ctime>
#include <string>
#include <libreborn/libreborn.h>
#include "crash-report.h"
// Show Crash Report Dialog
#define DIALOG_TITLE "Crash Report"
#define CRASH_REPORT_DIALOG_WIDTH "640"
#define CRASH_REPORT_DIALOG_HEIGHT "480"
static void show_report(const char *log_filename) {
// Fork
pid_t pid = fork();
if (pid == 0) {
// Child
setsid();
ALLOC_CHECK(freopen("/dev/null", "w", stdout));
ALLOC_CHECK(freopen("/dev/null", "w", stderr));
ALLOC_CHECK(freopen("/dev/null", "r", stdin));
const char *command[] = {
"zenity",
"--title", DIALOG_TITLE,
"--name", MCPI_APP_ID,
"--width", CRASH_REPORT_DIALOG_WIDTH,
"--height", CRASH_REPORT_DIALOG_HEIGHT,
"--text-info",
"--text", MCPI_APP_TITLE " has crashed!\n\nNeed help? Consider asking on the <a href=\"" MCPI_DISCORD_INVITE "\">Discord server</a>! <i>If you believe this is a problem with " MCPI_APP_TITLE " itself, please upload this crash report to the #bugs Discord channel.</i>",
"--filename", log_filename,
"--no-wrap",
"--font", "Monospace",
"--save-filename", MCPI_VARIANT_NAME "-crash-report.log",
"--ok-label", "Exit",
NULL
};
safe_execvpe(command, (const char *const *) environ);
}
}
// Exit Handler
static pid_t child_pid = -1;
static void exit_handler(__attribute__((unused)) int signal) {
// Murder
kill(child_pid, SIGTERM);
}
// Log File
static std::string log_filename;
static int log_fd;
static void setup_log_file() {
// Get Log Directory
const std::string home = std::string(getenv(_MCPI_HOME_ENV)) + get_home_subdirectory_for_game_data();
ensure_directory(home.c_str());
const std::string logs = home + "/logs";
ensure_directory(logs.c_str());
// Get Timestamp
time_t raw_time;
time(&raw_time);
const tm *time_info = localtime(&raw_time);
char time[512];
strftime(time, 512, "%Y-%m-%d", time_info);
// Get Log Filename
std::string file;
int num = 1;
do {
file = std::string(time) + '-' + std::to_string(num) + ".log";
log_filename = logs + '/' + file;
num++;
} while (access(log_filename.c_str(), F_OK) != -1);
// Create latest.log Symlink
const std::string latest_log = logs + "/latest.log";
unlink(latest_log.c_str());
if (symlink(file.c_str(), latest_log.c_str()) != 0) {
WARN("Unable To Create Latest Log Symlink: %s", strerror(errno));
}
// Create File
log_fd = open(log_filename.c_str(), O_RDWR | O_CREAT, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
if (log_fd == -1) {
ERR("Unable To Create Log File: %s", strerror(errno));
}
reborn_set_log(log_fd);
}
// Setup
#define PIPE_READ 0
#define PIPE_WRITE 1
#define BUFFER_SIZE 1024
static void safe_write(int fd, const void *buf, size_t size) {
const ssize_t bytes_written = write(fd, buf, size);
if (bytes_written < 0) {
ERR("Unable To Write Data: %s", strerror(errno));
}
}
void setup_crash_report() {
// Setup Logging
setup_log_file();
// Store Output
int output_pipe[2];
safe_pipe2(output_pipe, 0);
int error_pipe[2];
safe_pipe2(error_pipe, 0);
int input_pipe[2];
safe_pipe2(input_pipe, 0);
// Fork
pid_t ret = fork();
if (ret == -1) {
ERR("Unable To Fork: %s", strerror(errno));
} else if (ret == 0) {
// Child Process
// Pipe stdio
dup2(output_pipe[PIPE_WRITE], STDOUT_FILENO);
close(output_pipe[PIPE_READ]);
close(output_pipe[PIPE_WRITE]);
dup2(error_pipe[PIPE_WRITE], STDERR_FILENO);
close(error_pipe[PIPE_READ]);
close(error_pipe[PIPE_WRITE]);
dup2(input_pipe[PIPE_READ], STDIN_FILENO);
close(input_pipe[PIPE_READ]);
close(input_pipe[PIPE_WRITE]);
// Create New Process Group
setpgid(0, 0);
// Kill Child If Parent Exits First
prctl(PR_SET_PDEATHSIG, SIGKILL);
// Continue Execution
} else {
// Install Signal Handlers
child_pid = ret;
struct sigaction act_sigint = {};
act_sigint.sa_flags = SA_RESTART;
act_sigint.sa_handler = &exit_handler;
sigaction(SIGINT, &act_sigint, nullptr);
struct sigaction act_sigterm = {};
act_sigterm.sa_flags = SA_RESTART;
act_sigterm.sa_handler = &exit_handler;
sigaction(SIGTERM, &act_sigterm, nullptr);
// Close Unneeded File Descriptors
close(output_pipe[PIPE_WRITE]);
close(error_pipe[PIPE_WRITE]);
close(input_pipe[PIPE_READ]);
// Set Debug Tag
reborn_debug_tag = "(Crash Reporter) ";
// Setup Polling
const int number_fds = 3;
pollfd poll_fds[number_fds];
poll_fds[0].fd = output_pipe[PIPE_READ];
poll_fds[1].fd = error_pipe[PIPE_READ];
poll_fds[2].fd = STDIN_FILENO;
for (pollfd &poll_fd : poll_fds) {
poll_fd.events = POLLIN;
}
// Poll Data
int status;
while (waitpid(ret, &status, WNOHANG) != ret) {
const int poll_ret = poll(poll_fds, number_fds, -1);
if (poll_ret == -1) {
if (errno == EINTR) {
continue;
} else {
ERR("Unable To Poll Data: %s", strerror(errno));
}
}
// Handle Data
for (pollfd &poll_fd : poll_fds) {
if (poll_fd.revents != 0) {
if (poll_fd.revents & POLLIN) {
char buf[BUFFER_SIZE];
if (poll_fd.fd == STDIN_FILENO) {
// Data Available From stdin
int bytes_available;
if (ioctl(fileno(stdin), FIONREAD, &bytes_available) == -1) {
bytes_available = 0;
}
// Read
const ssize_t bytes_read = read(poll_fd.fd, buf, BUFFER_SIZE);
if (bytes_read == -1) {
ERR("Unable To Read Input: %s", strerror(errno));
}
// Write To Child
safe_write(input_pipe[PIPE_WRITE], buf, bytes_read);
} else {
// Data Available From Child's stdout/stderr
const ssize_t bytes_read = read(poll_fd.fd, buf, BUFFER_SIZE);
if (bytes_read == -1) {
ERR("Unable To Read Log Data: %s", strerror(errno));
}
// Print To Terminal
safe_write(poll_fd.fd == output_pipe[PIPE_READ] ? STDOUT_FILENO : STDERR_FILENO, buf, bytes_read);
// Write To log
safe_write(reborn_get_log_fd(), buf, bytes_read);
}
} else {
// File Descriptor No Longer Accessible
poll_fd.events = 0;
}
}
}
}
// Close Pipes
close(output_pipe[PIPE_READ]);
close(error_pipe[PIPE_READ]);
close(input_pipe[PIPE_WRITE]);
// Check If Is Crash
const bool is_crash = !is_exit_status_success(status);
// Log Exit Code To log If Crash
if (is_crash) {
// Create Exit Code Log Line
char *exit_status = nullptr;
get_exit_status_string(status, &exit_status);
const std::string exit_code_line = "[CRASH]: Terminated" + std::string(exit_status) + '\n';
free(exit_status);
// Print Exit Code Log Line
safe_write(STDERR_FILENO, exit_code_line.c_str(), strlen(exit_code_line.c_str()));
// Write Exit Code Log Line
safe_write(reborn_get_log_fd(), exit_code_line.c_str(), strlen(exit_code_line.c_str()));
}
// Close Log File
close(log_fd);
unsetenv(_MCPI_LOG_FD_ENV);
// Show Crash Log
if (is_crash && !reborn_is_headless()) {
show_report(log_filename.c_str());
}
// Exit
exit(WIFEXITED(status) ? WEXITSTATUS(status) : EXIT_FAILURE);
}
}

View File

@ -1,11 +0,0 @@
#pragma once
#ifdef __cplusplus
extern "C" {
#endif
void setup_crash_report();
#ifdef __cplusplus
}
#endif

View File

@ -0,0 +1,35 @@
#include <libreborn/libreborn.h>
#include "logger.h"
// Show Crash Report Dialog
#define DIALOG_TITLE "Crash Report"
#define CRASH_REPORT_DIALOG_WIDTH "640"
#define CRASH_REPORT_DIALOG_HEIGHT "480"
void show_report(const char *log_filename) {
// Fork
pid_t pid = fork();
if (pid == 0) {
// Child
setsid();
ALLOC_CHECK(freopen("/dev/null", "w", stdout));
ALLOC_CHECK(freopen("/dev/null", "w", stderr));
ALLOC_CHECK(freopen("/dev/null", "r", stdin));
const char *command[] = {
"zenity",
"--title", DIALOG_TITLE,
"--name", MCPI_APP_ID,
"--width", CRASH_REPORT_DIALOG_WIDTH,
"--height", CRASH_REPORT_DIALOG_HEIGHT,
"--text-info",
"--text", MCPI_APP_TITLE " has crashed!\n\nNeed help? Consider asking on the <a href=\"" MCPI_DISCORD_INVITE "\">Discord server</a>! <i>If you believe this is a problem with " MCPI_APP_TITLE " itself, please upload this crash report to the #bugs Discord channel.</i>",
"--filename", log_filename,
"--no-wrap",
"--font", "Monospace",
"--save-filename", MCPI_VARIANT_NAME "-crash-report.log",
"--ok-label", "Exit",
nullptr
};
safe_execvpe(command, (const char *const *) environ);
}
}

View File

@ -0,0 +1,142 @@
#include <unistd.h>
#include <cstring>
#include <cerrno>
#include <cstdlib>
#include <cstdio>
#include <cstdint>
#include <csignal>
#include <poll.h>
#include <sys/ioctl.h>
#include <sys/stat.h>
#include <ctime>
#include <string>
#include <fcntl.h>
#include <libreborn/libreborn.h>
#include "logger.h"
// Exit Handler
static pid_t child_pid = -1;
static void exit_handler(__attribute__((unused)) int signal) {
// Murder
kill(child_pid, SIGTERM);
}
// Log File
static std::string log_filename;
static int log_fd;
static void setup_log_file() {
// Get Log Directory
const std::string home = std::string(getenv(_MCPI_HOME_ENV)) + get_home_subdirectory_for_game_data();
ensure_directory(home.c_str());
const std::string logs = home + "/logs";
ensure_directory(logs.c_str());
// Get Timestamp
time_t raw_time;
time(&raw_time);
const tm *time_info = localtime(&raw_time);
char time[512];
strftime(time, 512, "%Y-%m-%d", time_info);
// Get Log Filename
std::string file;
int num = 1;
do {
file = std::string(time) + '-' + std::to_string(num) + ".log";
log_filename = logs + '/' + file;
num++;
} while (access(log_filename.c_str(), F_OK) != -1);
// Create latest.log Symlink
const std::string latest_log = logs + "/latest.log";
unlink(latest_log.c_str());
if (symlink(file.c_str(), latest_log.c_str()) != 0) {
WARN("Unable To Create Latest Log Symlink: %s", strerror(errno));
}
// Create File
log_fd = open(log_filename.c_str(), O_RDWR | O_CREAT, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
if (log_fd == -1) {
ERR("Unable To Create Log File: %s", strerror(errno));
}
reborn_set_log(log_fd);
}
// Setup
void setup_logger() {
// Setup Logging
setup_log_file();
// Fork
std::optional<Process> child = fork_with_stdio();
if (!child) {
// Child Process
// Create New Process Group
setpgid(0, 0);
// Continue Execution
} else {
// Set Debug Tag
reborn_debug_tag = "(Logger) ";
DEBUG("Writing To: %s", log_filename.c_str());
// Install Signal Handlers
child_pid = child->pid;
struct sigaction act_sigint = {};
act_sigint.sa_flags = SA_RESTART;
act_sigint.sa_handler = &exit_handler;
sigaction(SIGINT, &act_sigint, nullptr);
struct sigaction act_sigterm = {};
act_sigterm.sa_flags = SA_RESTART;
act_sigterm.sa_handler = &exit_handler;
sigaction(SIGTERM, &act_sigterm, nullptr);
// Poll
poll_fds({child->fds[0], child->fds[1], STDIN_FILENO}, [&child](const int i, const size_t size, unsigned char *buf) {
if (i == 0 || i == 1) {
// stdout/stderr
// Print To Terminal
safe_write(i == 0 ? STDOUT_FILENO : STDERR_FILENO, buf, size);
// Write To log
safe_write(reborn_get_log_fd(), buf, size);
} else {
// stdin
// Write To Child
safe_write(child->fds[2], buf, size);
}
});
// Get Exit Status
const int status = child->close();
const bool is_crash = !is_exit_status_success(status);
// Log Exit Code To log If Crash
if (is_crash) {
// Create Exit Code Log Line
const std::string exit_status = get_exit_status_string(status);
const std::string exit_code_line = "[CRASH]: Terminated" + exit_status + '\n';
// Print Exit Code Log Line
safe_write(STDERR_FILENO, exit_code_line.c_str(), exit_code_line.size());
// Write Exit Code Log Line
safe_write(reborn_get_log_fd(), exit_code_line.c_str(), exit_code_line.size());
}
// Close Log File
close(log_fd);
unsetenv(_MCPI_LOG_FD_ENV);
// Show Crash Log
if (is_crash && !reborn_is_headless()) {
show_report(log_filename.c_str());
}
// Exit
exit(WIFEXITED(status) ? WEXITSTATUS(status) : EXIT_FAILURE);
}
}

View File

@ -0,0 +1,4 @@
#pragma once
void setup_logger();
void show_report(const char *log_filename);

View File

@ -1,11 +1,10 @@
#include <cstdlib> #include <cstdlib>
#include <libreborn/libreborn.h> #include <libreborn/libreborn.h>
#include <sys/stat.h>
#include "bootstrap.h" #include "bootstrap/bootstrap.h"
#include "options/parser.h" #include "options/parser.h"
#include "crash-report.h" #include "logger/logger.h"
#include "util.h" #include "util/util.h"
#include "client/configuration.h" #include "client/configuration.h"
// Bind Options To Environmental Variable // Bind Options To Environmental Variable
@ -45,7 +44,8 @@ static void setup_environment(const options_t &options) {
} }
// Setup MCPI_HOME // Setup MCPI_HOME
if (const char *custom_profile_directory = getenv(MCPI_PROFILE_DIRECTORY_ENV); custom_profile_directory != nullptr) { const char *custom_profile_directory = getenv(MCPI_PROFILE_DIRECTORY_ENV);
if (custom_profile_directory != nullptr) {
// Custom Directory // Custom Directory
custom_profile_directory = realpath(custom_profile_directory, nullptr); custom_profile_directory = realpath(custom_profile_directory, nullptr);
ALLOC_CHECK(custom_profile_directory); ALLOC_CHECK(custom_profile_directory);
@ -86,8 +86,8 @@ static void start_game(const options_t &options) {
setvbuf(stdout, nullptr, _IONBF, 0); setvbuf(stdout, nullptr, _IONBF, 0);
// Setup Crash Reporting // Setup Crash Reporting
if (!options.disable_crash_report) { if (!options.disable_logger) {
setup_crash_report(); setup_logger();
} }
// Configure Client Options // Configure Client Options

View File

@ -1,6 +1,6 @@
OPTION(debug, "debug", 'd', "Enable Debug Logging") OPTION(debug, "debug", 'd', "Enable Debug Logging")
OPTION(copy_sdk, "copy-sdk", -2, "Extract Modding SDK And Exit") OPTION(copy_sdk, "copy-sdk", -2, "Extract Modding SDK And Exit")
OPTION(disable_crash_report, "disable-crash-report", -1, "Disable Crash Report Dialog") OPTION(disable_logger, "disable-logger", -1, "Disable Logger (And Crash Report Dialog)")
OPTION(use_default, "default", -3, "Skip Client-Mode Configuration Dialogs") OPTION(use_default, "default", -3, "Skip Client-Mode Configuration Dialogs")
OPTION(no_cache, "no-cache", -4, "Disable Client-Mode Configuration Cache") OPTION(no_cache, "no-cache", -4, "Disable Client-Mode Configuration Cache")
OPTION(wipe_cache, "wipe-cache", -5, "Wipe Cached Client-Mode Configuration And Exit") OPTION(wipe_cache, "wipe-cache", -5, "Wipe Cached Client-Mode Configuration And Exit")

View File

@ -1,8 +0,0 @@
#pragma once
#include <string>
#include <vector>
#define MCPI_PATCHED_DIR "/tmp/.minecraft-pi-patched"
void patch_mcpi_elf_dependencies(const std::string &original_path, char *new_path, const std::string &interpreter, const std::vector<std::string> &rpath, const std::vector<std::string> &mods);

View File

@ -1,6 +1,6 @@
#include <libreborn/libreborn.h> #include <libreborn/libreborn.h>
#include "bootstrap.h" #include "../bootstrap/bootstrap.h"
#include "util.h" #include "util.h"
// Log // Log

View File

@ -5,10 +5,8 @@
// Simpler Version Of run_command() // Simpler Version Of run_command()
void run_simple_command(const char *const command[], const char *error) { void run_simple_command(const char *const command[], const char *error) {
int status = 0; int status = 0;
char *output = run_command(command, &status, nullptr); const std::vector<unsigned char> *output = run_command(command, &status);
if (output != nullptr) { delete output;
free(output);
}
if (!is_exit_status_success(status)) { if (!is_exit_status_success(status)) {
ERR("%s", error); ERR("%s", error);
} }

View File

@ -7,3 +7,5 @@ void run_simple_command(const char *const command[], const char *error);
void chop_last_component(std::string &str); void chop_last_component(std::string &str);
std::string safe_realpath(const std::string &path); std::string safe_realpath(const std::string &path);
std::string get_binary_directory(); std::string get_binary_directory();
void copy_sdk(const std::string &binary_directory, bool log_with_debug);

View File

@ -6,12 +6,12 @@ configure_file(include/libreborn/config.h.in "${CMAKE_CURRENT_BINARY_DIR}/includ
# Util # Util
add_library(reborn-util SHARED add_library(reborn-util SHARED
src/util/exec.c src/util/exec.cpp
src/util/string.c src/util/string.cpp
src/util/util.c src/util/util.cpp
src/util/log.c src/util/log.cpp
src/util/cp437.cpp src/util/cp437.cpp
src/util/env.c src/util/env.cpp
) )
target_include_directories( target_include_directories(
reborn-util reborn-util

View File

@ -1,16 +1,11 @@
#pragma once #pragma once
#ifdef __cplusplus
extern "C" {
#endif
#define ENV(name, ...) extern const char *const name##_ENV; #define ENV(name, ...) extern const char *const name##_ENV;
#include "env-list.h" #include "env-list.h"
#undef ENV #undef ENV
int is_env_var_internal(const char *env); bool is_env_var_internal(const char *env);
void clear_internal_env_vars(); void clear_internal_env_vars();
#ifdef __cplusplus // Set Environmental Variable
} void set_and_print_env(const char *name, const char *value);
#endif

View File

@ -1,23 +1,21 @@
#pragma once #pragma once
#include <unistd.h> #include <string>
#include <stdint.h> #include <optional>
#include <errno.h> #include <array>
#include <string.h> #include <vector>
#include <sys/wait.h> #include <functional>
#include <fcntl.h>
#include <signal.h>
#include "log.h" // fork() With I/O
#include "string.h" struct Process {
#include "util.h" static constexpr int fd_count = 3;
Process(pid_t pid_, std::array<int, fd_count> fds_);
#ifdef __cplusplus [[nodiscard]] int close() const;
extern "C" { const pid_t pid;
#endif const std::array<int, fd_count> fds;
};
// Set Environmental Variable std::optional<Process> fork_with_stdio();
void set_and_print_env(const char *name, const char *value); void poll_fds(const std::vector<int> &fds, const std::function<void(int, size_t, unsigned char *)> &on_data);
// Safe execvpe() // Safe execvpe()
__attribute__((noreturn)) void safe_execvpe(const char *const argv[], const char *const envp[]); __attribute__((noreturn)) void safe_execvpe(const char *const argv[], const char *const envp[]);
@ -26,12 +24,8 @@ __attribute__((noreturn)) void safe_execvpe(const char *const argv[], const char
#define CHILD_PROCESS_TAG "(Child Process) " #define CHILD_PROCESS_TAG "(Child Process) "
// Run Command And Get Output // Run Command And Get Output
char *run_command(const char *const command[], int *exit_status, size_t *output_size); std::vector<unsigned char> *run_command(const char *const command[], int *exit_status);
#define is_exit_status_success(status) (WIFEXITED(status) && WEXITSTATUS(status) == 0) #define is_exit_status_success(status) (WIFEXITED(status) && WEXITSTATUS(status) == 0)
// Get Exit Status String // Get Exit Status String
void get_exit_status_string(int status, char **out); std::string get_exit_status_string(int status);
#ifdef __cplusplus
}
#endif

View File

@ -1,11 +1,7 @@
#pragma once #pragma once
#include <stdio.h> #include <cstdio>
#include <stdlib.h> #include <cstdlib>
#ifdef __cplusplus
extern "C" {
#endif
// Log File // Log File
int reborn_get_log_fd(); int reborn_get_log_fd();
@ -29,7 +25,3 @@ int reborn_get_debug_fd();
WARN(__VA_ARGS__); \ WARN(__VA_ARGS__); \
} \ } \
} }
#ifdef __cplusplus
}
#endif

View File

@ -1,11 +1,10 @@
#pragma once #pragma once
// Patching Functions
#if defined(REBORN_HAS_PATCH_CODE) && defined(__cplusplus)
#include <string> #include <string>
// Patching Functions
#if defined(REBORN_HAS_PATCH_CODE)
// Init // Init
void reborn_init_patch(); void reborn_init_patch();

View File

@ -1,19 +1,10 @@
#pragma once #pragma once
#ifdef __cplusplus #include <string>
extern "C" {
#endif
// Sanitize String // Sanitize String
void sanitize_string(char *str, int max_length, int allow_newlines); void sanitize_string(std::string &str, int max_length, bool allow_newlines);
// CP437 // CP437
char *to_cp437(const char *input); std::string to_cp437(const std::string &input);
char *from_cp437(const char *input); std::string from_cp437(const std::string &input);
// Starts With
int starts_with(const char *str, const char *prefix);
#ifdef __cplusplus
}
#endif

View File

@ -1,16 +1,17 @@
#pragma once #pragma once
#include <unistd.h> #include <unistd.h>
#include <string.h> #include <cstring>
#include <errno.h> #include <cerrno>
#include <dlfcn.h> #include <dlfcn.h>
#include <array>
#include "log.h" #include "log.h"
// Check Memory Allocation // Check Memory Allocation
#define ALLOC_CHECK(obj) \ #define ALLOC_CHECK(obj) \
{ \ { \
if (obj == NULL) { \ if ((obj) == nullptr) { \
ERR("Memory Allocation Failed"); \ ERR("Memory Allocation Failed"); \
} \ } \
} }
@ -41,23 +42,18 @@
} \ } \
return func; \ return func; \
} }
#ifdef __cplusplus
#define hooked_function_setup extern "C"
#else
#define hooked_function_setup
#endif
#define HOOK(name, return_type, args) \ #define HOOK(name, return_type, args) \
EXTERNAL_FUNC(name, return_type, args) \ EXTERNAL_FUNC(name, return_type, args) \
hooked_function_setup __attribute__((__used__)) return_type name args extern "C" __attribute__((__used__)) return_type name args
#ifdef __cplusplus
extern "C" {
#endif
// Safe Version Of pipe() // Safe Version Of pipe()
void safe_pipe2(int pipefd[2], int flags); struct Pipe {
Pipe();
const int read;
const int write;
};
// Check If Two Percentages Are Different Enough To Be Logged // Check If Two Percentages Are Different Enough To Be Logged
int is_progress_difference_significant(int32_t new_val, int32_t old_val); bool is_progress_difference_significant(int32_t new_val, int32_t old_val);
// Lock File // Lock File
int lock_file(const char *file); int lock_file(const char *file);
@ -65,8 +61,8 @@ void unlock_file(const char *file, int fd);
// Access Configuration At Runtime // Access Configuration At Runtime
const char *reborn_get_version(); const char *reborn_get_version();
int reborn_is_headless(); bool reborn_is_headless();
int reborn_is_server(); bool reborn_is_server();
// Check $DISPLAY // Check $DISPLAY
void reborn_check_display(); void reborn_check_display();
@ -77,6 +73,5 @@ const char *get_home_subdirectory_for_game_data();
// Make Sure Directory Exists // Make Sure Directory Exists
void ensure_directory(const char *path); void ensure_directory(const char *path);
#ifdef __cplusplus // Safe write()
} void safe_write(int fd, const void *buf, size_t size);
#endif

View File

@ -1,5 +1,4 @@
#include <cstdint> #include <cstdint>
#include <cstdlib>
#include "utf8.h" #include "utf8.h"
@ -48,22 +47,22 @@ static uint32_t *get_cp437_characters_codepoint_map() {
} }
return map; return map;
} }
char *to_cp437(const char *input) { std::string to_cp437(const std::string &input) {
// Convert To UTF-32 For Easier Parsing // Convert To UTF-32 For Easier Parsing
std::u32string utf32_str = to_utf32(input); const std::u32string utf32_str = to_utf32(input);
// Allocate String // Allocate String
std::string cp437_str; std::string cp437_str;
// Handle Characters // Handle Characters
for (size_t i = 0; i < utf32_str.length(); i++) { for (size_t i = 0; i < utf32_str.length(); i++) {
uint32_t codepoint = utf32_str[i]; const uint32_t codepoint = utf32_str[i];
bool valid = false; bool valid = false;
for (int j = 0; j < CP437_CHARACTERS; j++) { for (int j = 0; j < CP437_CHARACTERS; j++) {
uint32_t test_codepoint = get_cp437_characters_codepoint_map()[j]; const uint32_t test_codepoint = get_cp437_characters_codepoint_map()[j];
if (codepoint == test_codepoint) { if (codepoint == test_codepoint) {
valid = true; valid = true;
cp437_str += j; cp437_str += char(j);
break; break;
} }
} }
@ -73,19 +72,18 @@ char *to_cp437(const char *input) {
} }
// Return // Return
return strdup(cp437_str.c_str()); return cp437_str;
} }
char *from_cp437(const char *raw_input) { std::string from_cp437(const std::string &input) {
// Convert To UTF-32 For Easier Parsing // Convert To UTF-32 For Easier Parsing
std::string input = raw_input;
std::u32string utf32_str; std::u32string utf32_str;
// Handle Characters // Handle Characters
for (size_t i = 0; i < input.length(); i++) { for (size_t i = 0; i < input.length(); i++) {
unsigned char c = (unsigned char) input[i]; const unsigned char c = (unsigned char) input[i];
utf32_str += get_cp437_characters_codepoint_map()[(uint32_t) c]; utf32_str += char32_t(get_cp437_characters_codepoint_map()[(uint32_t) c]);
} }
// Convert To UTF-8 // Convert To UTF-8
return strdup(to_utf8(utf32_str).c_str()); return to_utf8(utf32_str);
} }

View File

@ -1,20 +0,0 @@
#include <libreborn/env.h>
#include <libreborn/exec.h>
// Define Constants
#define ENV(name, ...) const char *const name##_ENV = #name;
#include <libreborn/env-list.h>
#undef ENV
// Clear Internal Variables
int is_env_var_internal(const char *env) {
return env[0] == '_';
}
void clear_internal_env_vars() {
#define ENV(name, ...) \
if (is_env_var_internal(name##_ENV)) { \
set_and_print_env(name##_ENV, NULL); \
}
#include <libreborn/env-list.h>
#undef ENV
}

View File

@ -0,0 +1,38 @@
#include <libreborn/env.h>
#include <libreborn/exec.h>
#include <libreborn/log.h>
// Define Constants
#define ENV(name, ...) const char *const name##_ENV = #name;
#include <libreborn/env-list.h>
#undef ENV
// Clear Internal Variables
bool is_env_var_internal(const char *env) {
return env[0] == '_';
}
void clear_internal_env_vars() {
DEBUG("Clearing Internal Environmental Variables...");
#define ENV(name, ...) \
if (is_env_var_internal(name##_ENV)) { \
set_and_print_env(name##_ENV, nullptr); \
}
#include <libreborn/env-list.h>
#undef ENV
}
// Set Environmental Variable
static void setenv_safe(const char *name, const char *value) {
if (value != nullptr) {
setenv(name, value, 1);
} else {
unsetenv(name);
}
}
void set_and_print_env(const char *name, const char *value) {
// Set The Value
setenv_safe(name, value);
// Print New Value
DEBUG("Set %s = %s", name, value != NULL ? value : "(unset)");
}

View File

@ -1,151 +0,0 @@
#include <pthread.h>
#include <sys/prctl.h>
#include <libreborn/exec.h>
// Set Environmental Variable
static void setenv_safe(const char *name, const char *value) {
if (value != NULL) {
setenv(name, value, 1);
} else {
unsetenv(name);
}
}
void set_and_print_env(const char *name, const char *value) {
// Set The Value
setenv_safe(name, value);
// Print New Value
DEBUG("Set %s = %s", name, value != NULL ? value : "(unset)");
}
// Safe execvpe()
__attribute__((noreturn)) void safe_execvpe(const char *const argv[], const char *const envp[]) {
// Log
DEBUG("Running Command:");
for (int i = 0; argv[i] != NULL; i++) {
DEBUG(" %s", argv[i]);
}
// Run
int ret = execvpe(argv[0], (char *const *) argv, (char *const *) envp);
if (ret == -1) {
ERR("Unable To Execute Program: %s: %s", argv[0], strerror(errno));
} else {
IMPOSSIBLE();
}
}
// Run Command And Get Output
char *run_command(const char *const command[], int *exit_status, size_t *output_size) {
// Store Output
int output_pipe[2];
safe_pipe2(output_pipe, 0);
// Run
pid_t ret = fork();
if (ret == -1) {
ERR("Unable To Run Command: %s", strerror(errno));
} else if (ret == 0) {
// Child Process
// Set Debug Tag
reborn_debug_tag = CHILD_PROCESS_TAG;
// Pipe stdout
dup2(output_pipe[1], STDOUT_FILENO);
close(output_pipe[0]);
close(output_pipe[1]);
// Setup stderr
dup2(reborn_get_debug_fd(), STDERR_FILENO);
// Kill On Parent Death
prctl(PR_SET_PDEATHSIG, SIGKILL);
// Run
safe_execvpe(command, (const char *const *) environ);
} else {
// Read stdout
close(output_pipe[1]);
#define BUFFER_SIZE 1024
size_t size = BUFFER_SIZE;
char *output = malloc(size);
char buf[BUFFER_SIZE];
size_t position = 0;
ssize_t bytes_read = 0;
while ((bytes_read = read(output_pipe[0], buf, BUFFER_SIZE)) > 0) {
// Grow Output If Needed
size_t needed_size = position + bytes_read;
if (needed_size > size) {
// More Memory Needed
size_t new_size = size;
while (new_size < needed_size) {
new_size += BUFFER_SIZE;
}
char *new_output = realloc(output, new_size);
if (new_output == NULL) {
ERR("Unable To Grow Output Buffer");
} else {
output = new_output;
size = new_size;
}
}
// Copy Data To Output
memcpy(output + position, buf, bytes_read);
position += bytes_read;
}
close(output_pipe[0]);
// Add NULL-Terminator To Output
size_t needed_size = position + 1;
if (needed_size > size) {
// More Memory Needed
size_t new_size = size + 1;
char *new_output = realloc(output, new_size);
if (new_output == NULL) {
ERR("Unable To Grow Output Buffer (For NULL-Terminator)");
} else {
output = new_output;
}
}
output[position++] = '\0';
// Return Output Size
if (output_size != NULL) {
*output_size = position;
}
// Get Return Code
int status;
waitpid(ret, &status, 0);
if (exit_status != NULL) {
*exit_status = status;
}
// Return
return output;
}
}
// Set obj To NULL On asprintf() Failure
#define safe_asprintf(obj, ...) \
{ \
if (asprintf(obj, __VA_ARGS__) == -1) { \
*obj = NULL; \
} \
ALLOC_CHECK(*obj); \
}
// Get Exit Status String
void get_exit_status_string(const int status, char **out) {
if (out != NULL) {
*out = NULL;
if (WIFEXITED(status)) {
safe_asprintf(out, ": Exit Code: %i", WEXITSTATUS(status));
} else if (WIFSIGNALED(status)) {
safe_asprintf(out, ": Signal: %i%s", WTERMSIG(status), WCOREDUMP(status) ? " (Core Dumped)" : "");
} else {
safe_asprintf(out, ": Terminated");
}
}
}

176
libreborn/src/util/exec.cpp Normal file
View File

@ -0,0 +1,176 @@
#include <pthread.h>
#include <sys/prctl.h>
#include <cerrno>
#include <sys/wait.h>
#include <unistd.h>
#include <sys/poll.h>
#include <libreborn/util.h>
#include <libreborn/log.h>
#include <libreborn/exec.h>
// Fork
Process::Process(const pid_t pid_, const std::array<int, 3> fds_): pid(pid_), fds(fds_) {}
int Process::close() const {
for (const int fd : fds) {
::close(fd);
}
int status;
waitpid(pid, &status, 0);
return status;
}
#define PIPE_READ 0
#define PIPE_WRITE 1
std::optional<Process> fork_with_stdio() {
// Store Output
const std::array<Pipe, Process::fd_count> pipes = {};
// Fork
const pid_t ret = fork();
if (ret == -1) {
ERR("Unable To Fork: %s", strerror(errno));
} else if (ret == 0) {
// Child Process
// Redirect stdio To Pipes
dup2(pipes[0].write, STDOUT_FILENO);
dup2(pipes[1].write, STDERR_FILENO);
dup2(pipes[2].read, STDIN_FILENO);
for (const Pipe &pipe : pipes) {
close(pipe.write);
close(pipe.read);
}
// Kill Child If Parent Exits First
prctl(PR_SET_PDEATHSIG, SIGKILL);
// Continue Execution
return {};
} else {
// Parent Process
// Close Unneeded File Descriptors
close(pipes[0].write);
close(pipes[1].write);
close(pipes[2].read);
// Return
return Process(ret, {pipes[0].read, pipes[1].read, pipes[2].write});
}
}
#define BUFFER_SIZE 1024
void poll_fds(const std::vector<int> &fds, const std::function<void(int, size_t, unsigned char *)> &on_data) {
// Track Open FDs
int open_fds = int(fds.size());
// Setup Polling
pollfd *poll_fds = new pollfd[fds.size()];
for (std::vector<int>::size_type i = 0; i < fds.size(); i++) {
const int fd = fds[i];
poll_fds[i].fd = fd;
poll_fds[i].events = POLLIN;
if (fd == STDIN_FILENO) {
// Don't Wait For stdin To Close
open_fds--;
}
}
// Poll
while (open_fds > 0) {
const int poll_ret = poll(poll_fds, fds.size(), -1);
if (poll_ret == -1) {
if (errno == EINTR) {
continue;
} else {
ERR("Unable To Poll Data: %s", strerror(errno));
}
}
// Handle Data
for (std::vector<int>::size_type i = 0; i < fds.size(); i++) {
pollfd &poll_fd = poll_fds[i];
if (poll_fd.revents != 0 && poll_fd.events != 0) {
if (poll_fd.revents & POLLIN) {
// Data Available From Child's stdout/stderr
unsigned char buf[BUFFER_SIZE];
const ssize_t bytes_read = read(poll_fd.fd, buf, BUFFER_SIZE);
if (bytes_read == -1) {
ERR("Unable To Read Data: %s", strerror(errno));
}
// Callback
on_data(int(i), size_t(bytes_read), buf);
} else {
// File Descriptor No Longer Accessible
poll_fd.events = 0;
open_fds--;
}
}
}
}
// Cleanup
delete[] poll_fds;
}
// Safe execvpe()
__attribute__((noreturn)) void safe_execvpe(const char *const argv[], const char *const envp[]) {
// Log
DEBUG("Running Command:");
for (int i = 0; argv[i] != nullptr; i++) {
DEBUG(" %s", argv[i]);
}
// Run
int ret = execvpe(argv[0], (char *const *) argv, (char *const *) envp);
if (ret == -1) {
ERR("Unable To Execute Program: %s: %s", argv[0], strerror(errno));
} else {
IMPOSSIBLE();
}
}
// Run Command And Get Output
std::vector<unsigned char> *run_command(const char *const command[], int *exit_status) {
// Run
const std::optional<Process> child = fork_with_stdio();
if (!child) {
// Child Process
reborn_debug_tag = CHILD_PROCESS_TAG;
// Run
safe_execvpe(command, (const char *const *) environ);
} else {
// Read stdout
std::vector<unsigned char> *output = new std::vector<unsigned char>;
poll_fds({child->fds[0], child->fds[1]}, [&output](const int i, const size_t size, unsigned char *buf) {
if (i == 0) {
// stdout
output->insert(output->end(), buf, buf + size);
} else {
// stderr
safe_write(reborn_get_debug_fd(), buf, size);
}
});
// Get Exit Status
const int status = child->close();
if (exit_status != nullptr) {
*exit_status = status;
}
// Add NULL-Terminator To Output
output->push_back(0);
// Return
return output;
}
}
// Get Exit Status String
std::string get_exit_status_string(const int status) {
if (WIFEXITED(status)) {
return ": Exit Code: " + std::to_string(WEXITSTATUS(status));
} else if (WIFSIGNALED(status)) {
return ": Signal: " + std::to_string(WTERMSIG(status)) + (WCOREDUMP(status) ? " (Core Dumped)" : "");
} else {
return "";
}
}

View File

@ -1,15 +1,24 @@
#include <unistd.h> #include <unistd.h>
#include <fcntl.h> #include <fcntl.h>
#include <errno.h> #include <string>
#include <string.h>
#include <libreborn/log.h> #include <libreborn/log.h>
#include <libreborn/exec.h>
#include <libreborn/env.h> #include <libreborn/env.h>
// Debug Tag // Debug Tag
const char *reborn_debug_tag = ""; const char *reborn_debug_tag = "";
// /dev/null FD
static int null_fd = -1;
static void setup_null_fd() {
if (null_fd == -1) {
null_fd = open("/dev/null", O_WRONLY | O_APPEND);
}
}
__attribute__((destructor)) static void close_null_fd() {
close(null_fd);
}
// Log File // Log File
static int log_fd = -1; static int log_fd = -1;
int reborn_get_log_fd() { int reborn_get_log_fd() {
@ -18,10 +27,11 @@ int reborn_get_log_fd() {
} }
// Open Log File // Open Log File
const char *fd_str = getenv(_MCPI_LOG_FD_ENV); const char *fd_str = getenv(_MCPI_LOG_FD_ENV);
log_fd = fd_str ? atoi(fd_str) : open("/dev/null", O_WRONLY | O_APPEND); if (fd_str) {
// Check FD log_fd = std::stoi(fd_str);
if (log_fd < 0) { } else {
ERR("Unable To Open Log: %s", strerror(errno)); setup_null_fd();
log_fd = null_fd;
} }
// Return // Return
return reborn_get_log_fd(); return reborn_get_log_fd();
@ -29,14 +39,12 @@ int reborn_get_log_fd() {
void reborn_set_log(const int fd) { void reborn_set_log(const int fd) {
// Set Variable // Set Variable
log_fd = -1; log_fd = -1;
char buf[128]; set_and_print_env(_MCPI_LOG_FD_ENV, std::to_string(fd).c_str());
sprintf(buf, "%i", fd);
set_and_print_env(_MCPI_LOG_FD_ENV, buf);
} }
// Debug Logging // Debug Logging
static int should_print_debug_to_stderr() { static bool should_print_debug_to_stderr() {
return getenv(MCPI_DEBUG_ENV) != NULL; return getenv(MCPI_DEBUG_ENV) != nullptr;
} }
int reborn_get_debug_fd() { int reborn_get_debug_fd() {
return should_print_debug_to_stderr() ? STDERR_FILENO : reborn_get_log_fd(); return should_print_debug_to_stderr() ? STDERR_FILENO : reborn_get_log_fd();

View File

@ -1,14 +1,12 @@
#include <libreborn/string.h> #include <libreborn/string.h>
#include <string.h>
// Sanitize String // Sanitize String
void sanitize_string(char *str, const int max_length, const int allow_newlines) { void sanitize_string(std::string &str, const int max_length, const bool allow_newlines) {
// Store Message Length // Store Message Length
size_t length = strlen(str); size_t length = str.size();
// Truncate Message // Truncate Message
if (max_length >= 0 && length > ((size_t) max_length)) { if (max_length >= 0 && length > ((size_t) max_length)) {
str[max_length] = '\0'; str = str.substr(0, max_length);
length = max_length; length = max_length;
} }
// Loop Through Message // Loop Through Message
@ -21,8 +19,3 @@ void sanitize_string(char *str, const int max_length, const int allow_newlines)
} }
} }
} }
// Starts With
int starts_with(const char *str, const char *prefix) {
return strncmp(prefix, str, strlen(prefix)) == 0;
}

View File

@ -7,31 +7,34 @@
#include <libreborn/env.h> #include <libreborn/env.h>
// Safe Version Of pipe() // Safe Version Of pipe()
void safe_pipe2(int pipefd[2], int flags) { Pipe::Pipe(): read(-1), write(-1) {
if (pipe2(pipefd, flags) != 0) { int out[2];
if (pipe(out) != 0) {
ERR("Unable To Create Pipe: %s", strerror(errno)); ERR("Unable To Create Pipe: %s", strerror(errno));
} }
const_cast<int &>(read) = out[0];
const_cast<int &>(write) = out[1];
} }
// Check If Two Percentages Are Different Enough To Be Logged // Check If Two Percentages Are Different Enough To Be Logged
#define SIGNIFICANT_PROGRESS 5 #define SIGNIFICANT_PROGRESS 5
int is_progress_difference_significant(int32_t new_val, int32_t old_val) { bool is_progress_difference_significant(const int32_t new_val, const int32_t old_val) {
if (new_val != old_val) { if (new_val != old_val) {
if (new_val == -1 || old_val == -1) { if (new_val == -1 || old_val == -1) {
return 1; return true;
} else if (new_val == 0 || new_val == 100) { } else if (new_val == 0 || new_val == 100) {
return 1; return true;
} else { } else {
return new_val - old_val >= SIGNIFICANT_PROGRESS; return new_val - old_val >= SIGNIFICANT_PROGRESS;
} }
} else { } else {
return 0; return false;
} }
} }
// Lock File // Lock File
int lock_file(const char *file) { int lock_file(const char *file) {
int fd = open(file, O_WRONLY | O_CREAT, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH); const int fd = open(file, O_WRONLY | O_CREAT, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH);
if (fd == -1) { if (fd == -1) {
ERR("Unable To Open Lock File: %s: %s", file, strerror(errno)); ERR("Unable To Open Lock File: %s: %s", file, strerror(errno));
} }
@ -51,25 +54,25 @@ void unlock_file(const char *file, const int fd) {
const char *reborn_get_version() { const char *reborn_get_version() {
return MCPI_VERSION; return MCPI_VERSION;
} }
int reborn_is_headless() { bool reborn_is_headless() {
static int ret; static bool ret;
static int is_set = 0; static bool is_set = false;
if (!is_set) { if (!is_set) {
ret = reborn_is_server(); ret = reborn_is_server();
if (getenv(_MCPI_FORCE_HEADLESS_ENV)) { if (getenv(_MCPI_FORCE_HEADLESS_ENV)) {
ret = 1; ret = true;
} else if (getenv(_MCPI_FORCE_NON_HEADLESS_ENV)) { } else if (getenv(_MCPI_FORCE_NON_HEADLESS_ENV)) {
ret = 0; ret = false;
} }
is_set = 1; is_set = true;
} }
return ret; return ret;
} }
int reborn_is_server() { bool reborn_is_server() {
static int ret; static int ret;
static int is_set = 0; static int is_set = 0;
if (!is_set) { if (!is_set) {
ret = getenv(_MCPI_SERVER_MODE_ENV) != NULL; ret = getenv(_MCPI_SERVER_MODE_ENV) != nullptr;
is_set = 1; is_set = 1;
} }
return ret; return ret;
@ -84,7 +87,7 @@ void reborn_check_display() {
// Home Subdirectory // Home Subdirectory
const char *get_home_subdirectory_for_game_data() { const char *get_home_subdirectory_for_game_data() {
if (getenv(MCPI_PROFILE_DIRECTORY_ENV) != NULL) { if (getenv(MCPI_PROFILE_DIRECTORY_ENV) != nullptr) {
// No Subdirectory When Using Custom Profile Directory // No Subdirectory When Using Custom Profile Directory
return ""; return "";
} else if (!reborn_is_server()) { } else if (!reborn_is_server()) {
@ -112,3 +115,11 @@ void ensure_directory(const char *path) {
ERR("Not A Directory: %s", path); ERR("Not A Directory: %s", path);
} }
} }
// Write To FD
void safe_write(const int fd, const void *buf, const size_t size) {
const ssize_t bytes_written = write(fd, buf, size);
if (bytes_written < 0) {
ERR("Unable To Write Data: %s", strerror(errno));
}
}

View File

@ -9,7 +9,7 @@ set(CORE_SRC
src/events.cpp src/events.cpp
src/offscreen.cpp src/offscreen.cpp
src/audio/api.cpp src/audio/api.cpp
src/audio/engine.c src/audio/engine.cpp
src/audio/file.cpp src/audio/file.cpp
) )

View File

@ -44,7 +44,7 @@ void _media_audio_delete_sources() {
} }
// Update Listener // Update Listener
void media_audio_update(float volume, float x, float y, float z, float yaw) { void media_audio_update(const float volume, const float x, const float y, const float z, const float yaw) {
// Check // Check
if (_media_audio_is_loaded()) { if (_media_audio_is_loaded()) {
// Update Listener Volume // Update Listener Volume
@ -56,8 +56,8 @@ void media_audio_update(float volume, float x, float y, float z, float yaw) {
AL_ERROR_CHECK(); AL_ERROR_CHECK();
// Update Listener Orientation // Update Listener Orientation
float radian_yaw = yaw * (M_PI / 180); const float radian_yaw = float(yaw * (M_PI / 180));
ALfloat orientation[] = {-sinf(radian_yaw), 0.0f, cosf(radian_yaw), 0.0f, 1.0f, 0.0f}; const ALfloat orientation[] = {-sinf(radian_yaw), 0.0f, cosf(radian_yaw), 0.0f, 1.0f, 0.0f};
alListenerfv(AL_ORIENTATION, orientation); alListenerfv(AL_ORIENTATION, orientation);
AL_ERROR_CHECK(); AL_ERROR_CHECK();
@ -101,11 +101,11 @@ void media_audio_play(const char *source, const char *name, float x, float y, fl
// Check // Check
if (_media_audio_is_loaded()) { if (_media_audio_is_loaded()) {
// Load Sound // Load Sound
ALuint buffer = _media_audio_get_buffer(source, name); const ALuint buffer = _media_audio_get_buffer(source, name);
if (volume > 0.0f && buffer) { if (volume > 0.0f && buffer) {
// Get Source // Get Source
ALuint al_source; ALuint al_source;
if (idle_sources.size() > 0) { if (!idle_sources.empty()) {
// Use Idle Source // Use Idle Source
al_source = idle_sources.back(); al_source = idle_sources.back();
idle_sources.pop_back(); idle_sources.pop_back();
@ -114,7 +114,7 @@ void media_audio_play(const char *source, const char *name, float x, float y, fl
alGenSources(1, &al_source); alGenSources(1, &al_source);
// Special Out-Of-Memory Handling // Special Out-Of-Memory Handling
{ {
ALenum err = alGetError(); const ALenum err = alGetError();
if (err == AL_OUT_OF_MEMORY) { if (err == AL_OUT_OF_MEMORY) {
return; return;
} else { } else {

View File

@ -1,11 +1,3 @@
#pragma once #pragma once
#ifdef __cplusplus
extern "C" {
#endif
__attribute__((visibility("internal"))) void _media_audio_delete_sources(); __attribute__((visibility("internal"))) void _media_audio_delete_sources();
#ifdef __cplusplus
}
#endif

View File

@ -9,8 +9,8 @@
#include "api.h" #include "api.h"
// Store Device // Store Device
static ALCdevice *device = NULL; static ALCdevice *device = nullptr;
static ALCcontext *context = NULL; static ALCcontext *context = nullptr;
// Store State // Store State
static int is_loaded = 0; static int is_loaded = 0;
@ -21,14 +21,14 @@ int _media_audio_is_loaded() {
// Init // Init
void _media_audio_init() { void _media_audio_init() {
// Open Device // Open Device
device = alcOpenDevice(NULL); device = alcOpenDevice(nullptr);
if (!device) { if (!device) {
WARN("Unable To Load Audio Engine"); WARN("Unable To Load Audio Engine");
return; return;
} }
// Create Context // Create Context
context = alcCreateContext(device, NULL); context = alcCreateContext(device, nullptr);
ALCenum err = alcGetError(device); ALCenum err = alcGetError(device);
if (err != ALC_NO_ERROR) { if (err != ALC_NO_ERROR) {
ERR("Unable To Open Audio Context: %s", alcGetString(device, err)); ERR("Unable To Open Audio Context: %s", alcGetString(device, err));
@ -63,7 +63,7 @@ void _media_audio_cleanup() {
_media_audio_delete_buffers(); _media_audio_delete_buffers();
// Deselect Context // Deselect Context
alcMakeContextCurrent(NULL); alcMakeContextCurrent(nullptr);
ALCenum err = alcGetError(device); ALCenum err = alcGetError(device);
if (err != ALC_NO_ERROR) { if (err != ALC_NO_ERROR) {
ERR("Unable To Deselect Audio Context: %s", alcGetString(device, err)); ERR("Unable To Deselect Audio Context: %s", alcGetString(device, err));

View File

@ -2,14 +2,6 @@
#include <AL/al.h> #include <AL/al.h>
#ifdef __cplusplus
extern "C" {
#endif
__attribute__((visibility("internal"))) void _media_audio_init(); __attribute__((visibility("internal"))) void _media_audio_init();
__attribute__((visibility("internal"))) void _media_audio_cleanup(); __attribute__((visibility("internal"))) void _media_audio_cleanup();
__attribute__((visibility("internal"))) int _media_audio_is_loaded(); __attribute__((visibility("internal"))) int _media_audio_is_loaded();
#ifdef __cplusplus
}
#endif

View File

@ -14,13 +14,13 @@
// Load Symbol From ELF File // Load Symbol From ELF File
static void load_symbol(const char *source, const char *name, std::function<void(const unsigned char *, uint32_t)> callback) { static void load_symbol(const char *source, const char *name, std::function<void(const unsigned char *, uint32_t)> callback) {
static std::unordered_map<std::string, std::unique_ptr<LIEF::ELF::Binary>> sources = {}; static std::unordered_map<std::string, std::unique_ptr<LIEF::ELF::Binary>> sources = {};
std::string cpp_source = source; const std::string cpp_source = source;
if (sources.count(cpp_source) == 0) { if (!sources.contains(cpp_source)) {
sources[cpp_source] = LIEF::ELF::Parser::parse(source); sources[cpp_source] = LIEF::ELF::Parser::parse(source);
} }
std::unique_ptr<LIEF::ELF::Binary> &binary = sources[cpp_source]; const std::unique_ptr<LIEF::ELF::Binary> &binary = sources[cpp_source];
const LIEF::ELF::Symbol *symbol = binary->get_dynamic_symbol(name); const LIEF::ELF::Symbol *symbol = binary->get_dynamic_symbol(name);
if (symbol != NULL) { if (symbol != nullptr) {
LIEF::span<const uint8_t> data = binary->get_content_from_virtual_address(symbol->value(), symbol->size(), LIEF::Binary::VA_TYPES::VA); LIEF::span<const uint8_t> data = binary->get_content_from_virtual_address(symbol->value(), symbol->size(), LIEF::Binary::VA_TYPES::VA);
callback(data.data(), data.size()); callback(data.data(), data.size());
} else { } else {
@ -73,8 +73,8 @@ static ALuint load_sound(const char *source, const char *name) {
} }
// Load Data // Load Data
int remaining_size = size - sizeof (audio_metadata); const int remaining_size = size - sizeof (audio_metadata);
int data_size = meta->channels * meta->frames * meta->frame_size; const int data_size = meta->channels * meta->frames * meta->frame_size;
if (remaining_size < data_size) { if (remaining_size < data_size) {
WARN("Symbol Too Small To Contain Specified Audio Data: %s", name); WARN("Symbol Too Small To Contain Specified Audio Data: %s", name);
return; return;

View File

@ -2,13 +2,5 @@
#include <AL/al.h> #include <AL/al.h>
#ifdef __cplusplus
extern "C" {
#endif
__attribute__((visibility("internal"))) ALuint _media_audio_get_buffer(const char *source, const char *name); __attribute__((visibility("internal"))) ALuint _media_audio_get_buffer(const char *source, const char *name);
__attribute__((visibility("internal"))) void _media_audio_delete_buffers(); __attribute__((visibility("internal"))) void _media_audio_delete_buffers();
#ifdef __cplusplus
}
#endif

View File

@ -164,15 +164,13 @@ static void codepoint_to_utf8(unsigned char *const buffer, const unsigned int co
static void glfw_char(__attribute__((unused)) GLFWwindow *window, const unsigned int codepoint) { static void glfw_char(__attribute__((unused)) GLFWwindow *window, const unsigned int codepoint) {
// Convert // Convert
size_t str_size = 4 /* Maximum UTF-8 character size */ + 1 /* NULL-terminator */; size_t str_size = 4 /* Maximum UTF-8 character size */ + 1 /* NULL-terminator */;
char str[str_size] = {}; unsigned char str[str_size] = {};
codepoint_to_utf8((unsigned char *) str, codepoint); codepoint_to_utf8(str, codepoint);
char *cp437_str = to_cp437(str); std::string cp437_str = to_cp437((const char *) str);
// Send Event // Send Event
for (int i = 0; cp437_str[i] != '\0'; i++) { for (const char x : cp437_str) {
character_event(cp437_str[i]); character_event(x);
} }
// Free
free(cp437_str);
} }
// Convert Screen Coordinates To Pixels // Convert Screen Coordinates To Pixels

View File

@ -1,5 +1,7 @@
#pragma once #pragma once
#include <string>
extern "C" { extern "C" {
char *override_get_path(const char *filename); std::string override_get_path(std::string filename);
} }

View File

@ -26,9 +26,8 @@ std::string chat_send_api_command(const Minecraft *minecraft, const std::string
// Send API Chat Command // Send API Chat Command
static void send_api_chat_command(const Minecraft *minecraft, const char *str) { static void send_api_chat_command(const Minecraft *minecraft, const char *str) {
char *utf_str = from_cp437(str); const std::string utf_str = from_cp437(str);
const std::string command = std::string("chat.post(") + utf_str + ")\n"; const std::string command = std::string("chat.post(") + utf_str + ")\n";
free(utf_str);
chat_send_api_command(minecraft, command); chat_send_api_command(minecraft, command);
} }
@ -38,19 +37,13 @@ std::string _chat_get_prefix(const char *username) {
} }
void chat_send_message_to_clients(ServerSideNetworkHandler *server_side_network_handler, const char *username, const char *message) { void chat_send_message_to_clients(ServerSideNetworkHandler *server_side_network_handler, const char *username, const char *message) {
std::string full_message = _chat_get_prefix(username) + message; std::string full_message = _chat_get_prefix(username) + message;
char *raw_str = strdup(full_message.c_str()); sanitize_string(full_message, MAX_CHAT_MESSAGE_LENGTH, false);
ALLOC_CHECK(raw_str);
sanitize_string(raw_str, MAX_CHAT_MESSAGE_LENGTH, 0);
full_message = raw_str;
free(raw_str);
server_side_network_handler->displayGameMessage(full_message); server_side_network_handler->displayGameMessage(full_message);
} }
// Handle Chat packet Send // Handle Chat packet Send
void chat_handle_packet_send(const Minecraft *minecraft, ChatPacket *packet) { void chat_handle_packet_send(const Minecraft *minecraft, ChatPacket *packet) {
// Convert To CP-437 // Convert To CP-437
char *cp437_str = to_cp437(packet->message.c_str()); packet->message = to_cp437(packet->message);
packet->message = cp437_str;
free(cp437_str);
// Send // Send
RakNetInstance *rak_net_instance = minecraft->rak_net_instance; RakNetInstance *rak_net_instance = minecraft->rak_net_instance;
if (rak_net_instance->isServer()) { if (rak_net_instance->isServer()) {

View File

@ -1,10 +1,13 @@
#include "game-mode-internal.h" #include <cstdint>
#include <mods/init/init.h> #include <mods/init/init.h>
#include <mods/feature/feature.h> #include <mods/feature/feature.h>
#include <libreborn/libreborn.h> #include <libreborn/libreborn.h>
#include <symbols/minecraft.h> #include <symbols/minecraft.h>
#include "game-mode-internal.h"
static int is_survival = -1; static int is_survival = -1;
// Patch Game Mode // Patch Game Mode

View File

@ -1,3 +1,5 @@
#include <cstdint>
#include <libreborn/libreborn.h> #include <libreborn/libreborn.h>
#include <symbols/minecraft.h> #include <symbols/minecraft.h>

View File

@ -10,10 +10,8 @@
// Print Chat To Log // Print Chat To Log
static void Gui_addMessage_injection(Gui_addMessage_t original, Gui *gui, const std::string &text) { static void Gui_addMessage_injection(Gui_addMessage_t original, Gui *gui, const std::string &text) {
// Sanitize Message // Sanitize Message
char *new_message = strdup(text.c_str()); std::string new_message = text;
ALLOC_CHECK(new_message); sanitize_string(new_message, -1, true);
sanitize_string(new_message, -1, 1);
const std::string cpp_str = new_message;
// Process Message // Process Message
static bool recursing = false; static bool recursing = false;
@ -22,22 +20,18 @@ static void Gui_addMessage_injection(Gui_addMessage_t original, Gui *gui, const
recursing = true; recursing = true;
// Print Log Message // Print Log Message
char *safe_message = from_cp437(new_message); std::string safe_message = from_cp437(new_message);
fprintf(stderr, "[CHAT]: %s\n", safe_message); fprintf(stderr, "[CHAT]: %s\n", safe_message.c_str());
free(safe_message);
// Call Original Method // Call Original Method
original(gui, cpp_str); original(gui, new_message);
// End Recursing // End Recursing
recursing = false; recursing = false;
} else { } else {
// Call Original Method // Call Original Method
original(gui, cpp_str); original(gui, new_message);
} }
// Free
free(new_message);
} }
// Print Progress Reports // Print Progress Reports

View File

@ -34,13 +34,10 @@ static void LoginPacket_read_injection(LoginPacket_read_t original, LoginPacket
const RakNet_RakString_SharedString *shared_string = rak_string->sharedString; const RakNet_RakString_SharedString *shared_string = rak_string->sharedString;
const char *c_str = shared_string->c_str; const char *c_str = shared_string->c_str;
// Sanitize // Sanitize
char *new_username = strdup(c_str); std::string new_username = c_str;
ALLOC_CHECK(new_username);
sanitize_string(new_username, MAX_USERNAME_LENGTH, 0); sanitize_string(new_username, MAX_USERNAME_LENGTH, 0);
// Set New Username // Set New Username
rak_string->Assign(new_username); rak_string->Assign(new_username.c_str());
// Free
free(new_username);
} }
// Fix RakNet::RakString Security Bug // Fix RakNet::RakString Security Bug

View File

@ -161,10 +161,8 @@ static void position_info(Font *font, const int width, const int height) {
void open_url(const std::string &url) { void open_url(const std::string &url) {
int return_code; int return_code;
const char *command[] = {"xdg-open", url.c_str(), nullptr}; const char *command[] = {"xdg-open", url.c_str(), nullptr};
char *output = run_command(command, &return_code, nullptr); const std::vector<unsigned char> *output = run_command(command, &return_code);
if (output != nullptr) { delete output;
free(output);
}
if (!is_exit_status_success(return_code)) { if (!is_exit_status_success(return_code)) {
WARN("Unable To Open URL: %s", url.c_str()); WARN("Unable To Open URL: %s", url.c_str());
} }

View File

@ -44,10 +44,6 @@ static const char *get_username() {
} }
return username; return username;
} }
static char *safe_username = nullptr;
__attribute__((destructor)) static void _free_safe_username() {
free(safe_username);
}
static int render_distance; static int render_distance;
// Configure Options // Configure Options
@ -162,8 +158,9 @@ void init_options() {
if (strcmp(Strings::default_username, "StevePi") != 0) { if (strcmp(Strings::default_username, "StevePi") != 0) {
ERR("Default Username Is Invalid"); ERR("Default Username Is Invalid");
} }
safe_username = to_cp437(username); std::string *safe_username = new std::string;
patch_address((void *) &Strings::default_username, (void *) safe_username); *safe_username = to_cp437(username);
patch_address((void *) &Strings::default_username, (void *) safe_username->c_str());
// Disable Autojump By Default // Disable Autojump By Default
if (feature_has("Disable Autojump By Default", server_disabled)) { if (feature_has("Disable Autojump By Default", server_disabled)) {
@ -210,7 +207,7 @@ void init_options() {
// Replace String // Replace String
patch_address((void *) &Strings::feedback_vibration_options_txt_name, (void *) "gfx_ao"); patch_address((void *) &Strings::feedback_vibration_options_txt_name, (void *) "gfx_ao");
// Loading // Loading
const unsigned char offset = (unsigned char) offsetof(Options, ambient_occlusion); constexpr unsigned char offset = (unsigned char) offsetof(Options, ambient_occlusion);
unsigned char gfx_ao_loading_patch[4] = {offset, 0x10, 0x84, 0xe2}; // "add r1, r4, #OFFSET" unsigned char gfx_ao_loading_patch[4] = {offset, 0x10, 0x84, 0xe2}; // "add r1, r4, #OFFSET"
patch((void *) 0x193b8, gfx_ao_loading_patch); patch((void *) 0x193b8, gfx_ao_loading_patch);
// Saving // Saving
@ -223,7 +220,7 @@ void init_options() {
// Replace String // Replace String
patch_address((void *) &Strings::gfx_lowquality_options_txt_name, (void *) "gfx_anaglyph"); patch_address((void *) &Strings::gfx_lowquality_options_txt_name, (void *) "gfx_anaglyph");
// Loading // Loading
const unsigned char offset = (unsigned char) offsetof(Options, anaglyph_3d); constexpr unsigned char offset = (unsigned char) offsetof(Options, anaglyph_3d);
unsigned char gfx_anaglyph_loading_patch[4] = {offset, 0x10, 0x84, 0xe2}; // "add r1, r4, #OFFSET" unsigned char gfx_anaglyph_loading_patch[4] = {offset, 0x10, 0x84, 0xe2}; // "add r1, r4, #OFFSET"
patch((void *) 0x19400, gfx_anaglyph_loading_patch); patch((void *) 0x19400, gfx_anaglyph_loading_patch);
// Disable Loading Side Effects // Disable Loading Side Effects

View File

@ -1,4 +1,5 @@
#include <string> #include <string>
#include <cstdint>
#include <libreborn/libreborn.h> #include <libreborn/libreborn.h>
#include <symbols/minecraft.h> #include <symbols/minecraft.h>

View File

@ -12,18 +12,18 @@
#include <mods/home/home.h> #include <mods/home/home.h>
#include <mods/init/init.h> #include <mods/init/init.h>
// Hook access // Hook Functions
HOOK(access, int, (const char *pathname, int mode)) { #define HOOK_OPEN(name, return_type, mode_type) \
char *new_path = override_get_path(pathname); HOOK(name, return_type, (const char *filename, mode_type mode)) { \
// Open File const std::string new_path = override_get_path(filename); \
const int ret = real_access()(new_path != nullptr ? new_path : pathname, mode); /* Open File */ \
// Free Data return_type ret = real_##name()(!new_path.empty() ? new_path.c_str() : filename, mode); \
if (new_path != nullptr) { /* Return */ \
free(new_path); return ret; \
} }
// Return HOOK_OPEN(fopen, FILE *, const char *)
return ret; HOOK_OPEN(fopen64, FILE *, const char *)
} HOOK_OPEN(access, int, int)
// Get Override Folder // Get Override Folder
static std::string get_override_directory() { static std::string get_override_directory() {
@ -32,9 +32,9 @@ static std::string get_override_directory() {
} }
// Get Override Path For File (If It Exists) // Get Override Path For File (If It Exists)
char *override_get_path(const char *filename) { std::string override_get_path(std::string filename) {
// Custom Skin // Custom Skin
if (starts_with(filename, "data/images/$")) { if (filename.starts_with("data/images/$")) {
// Fallback Texture // Fallback Texture
filename = "data/images/mob/char.png"; filename = "data/images/mob/char.png";
} }
@ -42,12 +42,12 @@ char *override_get_path(const char *filename) {
// Get Asset Override Path // Get Asset Override Path
const std::string overrides = get_override_directory(); const std::string overrides = get_override_directory();
// Data Prefiix // Data Prefix
const std::string data_prefix = "data/"; const std::string data_prefix = "data/";
int data_prefix_length = data_prefix.length(); const int data_prefix_length = data_prefix.length();
// Folders To Check // Folders To Check
std::string asset_folders[] = { const std::string asset_folders[] = {
overrides, overrides,
getenv(_MCPI_REBORN_ASSETS_PATH_ENV), getenv(_MCPI_REBORN_ASSETS_PATH_ENV),
getenv(_MCPI_VANILLA_ASSETS_PATH_ENV), getenv(_MCPI_VANILLA_ASSETS_PATH_ENV),
@ -72,39 +72,7 @@ char *override_get_path(const char *filename) {
} }
// Return // Return
if (new_path.empty()) { return new_path;
return nullptr;
} else {
char *ret = strdup(new_path.c_str());
ALLOC_CHECK(ret);
return ret;
}
}
// Hook fopen
HOOK(fopen, FILE *, (const char *filename, const char *mode)) {
char *new_path = override_get_path(filename);
// Open File
FILE *file = real_fopen()(new_path != nullptr ? new_path : filename, mode);
// Free Data
if (new_path != nullptr) {
free(new_path);
}
// Return File
return file;
}
// Hook fopen64
HOOK(fopen64, FILE *, (const char *filename, const char *mode)) {
char *new_path = override_get_path(filename);
// Open File
FILE *file = real_fopen64()(new_path != nullptr ? new_path : filename, mode);
// Free Data
if (new_path != nullptr) {
free(new_path);
}
// Return File
return file;
} }
// Init // Init

View File

@ -57,9 +57,7 @@ static ServerPropertyTypes &get_property_types() {
// Get World Name // Get World Name
static std::string get_world_name() { static std::string get_world_name() {
const std::string name = get_server_properties().get_string(get_property_types().world_name); const std::string name = get_server_properties().get_string(get_property_types().world_name);
char *safe_name_c = to_cp437(name.c_str()); std::string safe_name = to_cp437(name);
std::string safe_name = safe_name_c;
free(safe_name_c);
return safe_name; return safe_name;
} }
@ -116,11 +114,9 @@ static std::vector<Player *> get_players_in_level(Level *level) {
return level->players; return level->players;
} }
// Get Player's Username // Get Player's Username
static std::string get_player_username(Player *player) { static std::string get_player_username(const Player *player) {
const std::string *username = &player->username; const std::string *username = &player->username;
char *safe_username_c = from_cp437(username->c_str()); std::string safe_username = from_cp437(*username);
std::string safe_username = safe_username_c;
free(safe_username_c);
return safe_username; return safe_username;
} }
// Get Level From Minecraft // Get Level From Minecraft
@ -307,12 +303,9 @@ std::vector<ServerCommand> *server_get_commands(Minecraft *minecraft, ServerSide
.callback = [server_side_network_handler](const std::string &cmd) { .callback = [server_side_network_handler](const std::string &cmd) {
// Format Message // Format Message
const std::string message = "[Server] " + cmd; const std::string message = "[Server] " + cmd;
char *safe_message = to_cp437(message.c_str()); std::string cpp_string = to_cp437(message);
std::string cpp_string = safe_message;
// Post Message To Chat // Post Message To Chat
server_side_network_handler->displayGameMessage(cpp_string); server_side_network_handler->displayGameMessage(cpp_string);
// Free
free(safe_message);
} }
}); });
// List Players // List Players

View File

@ -1,3 +1,5 @@
#include <cstdint>
#include <symbols/minecraft.h> #include <symbols/minecraft.h>
#include <libreborn/libreborn.h> #include <libreborn/libreborn.h>

View File

@ -19,15 +19,14 @@
// Loading Pending Skins // Loading Pending Skins
struct pending_skin { struct pending_skin {
int32_t texture_id; int32_t texture_id;
char *data; const std::vector<unsigned char> *data;
int size;
}; };
static std::vector<pending_skin> &get_pending_skins() { static std::vector<pending_skin> &get_pending_skins() {
static std::vector<pending_skin> pending_skins; static std::vector<pending_skin> pending_skins;
return pending_skins; return pending_skins;
} }
static pthread_mutex_t pending_skins_lock = PTHREAD_MUTEX_INITIALIZER; static pthread_mutex_t pending_skins_lock = PTHREAD_MUTEX_INITIALIZER;
static void load_pending_skins(Minecraft *minecraft) { static void load_pending_skins(const Minecraft *minecraft) {
// Lock // Lock
pthread_mutex_lock(&pending_skins_lock); pthread_mutex_lock(&pending_skins_lock);
@ -35,7 +34,7 @@ static void load_pending_skins(Minecraft *minecraft) {
for (const pending_skin &skin : get_pending_skins()) { for (const pending_skin &skin : get_pending_skins()) {
// Read PNG Info // Read PNG Info
int width = 0, height = 0, channels = 0; int width = 0, height = 0, channels = 0;
stbi_uc *img = stbi_load_from_memory((unsigned char *) skin.data, skin.size, &width, &height, &channels, STBI_rgb_alpha); stbi_uc *img = stbi_load_from_memory(skin.data->data(), skin.data->size(), &width, &height, &channels, STBI_rgb_alpha);
if (width != SKIN_WIDTH || height != SKIN_HEIGHT) { if (width != SKIN_WIDTH || height != SKIN_HEIGHT) {
continue; continue;
} }
@ -54,7 +53,7 @@ static void load_pending_skins(Minecraft *minecraft) {
// Free // Free
for (const pending_skin &skin : get_pending_skins()) { for (const pending_skin &skin : get_pending_skins()) {
free(skin.data); delete skin.data;
} }
// Clear // Clear
@ -87,11 +86,10 @@ static void *loader_thread(void *user_data) {
const std::string url = get_skin_server() + '/' + data->name + ".png"; const std::string url = get_skin_server() + '/' + data->name + ".png";
int return_code; int return_code;
const char *command[] = {"wget", "-O", "-", url.c_str(), nullptr}; const char *command[] = {"wget", "-O", "-", url.c_str(), nullptr};
size_t output_size = 0; const std::vector<unsigned char> *output = run_command(command, &return_code);
char *output = run_command(command, &return_code, &output_size);
// Check Success // Check Success
if (output != nullptr && is_exit_status_success(return_code)) { if (is_exit_status_success(return_code)) {
// Success // Success
DEBUG("Downloaded Skin: %s", data->name.c_str()); DEBUG("Downloaded Skin: %s", data->name.c_str());
@ -99,14 +97,12 @@ static void *loader_thread(void *user_data) {
pending_skin skin = {}; pending_skin skin = {};
skin.texture_id = data->texture_id; skin.texture_id = data->texture_id;
skin.data = output; skin.data = output;
skin.size = (int) output_size;
pthread_mutex_lock(&pending_skins_lock); pthread_mutex_lock(&pending_skins_lock);
get_pending_skins().push_back(skin); get_pending_skins().push_back(skin);
pthread_mutex_unlock(&pending_skins_lock); pthread_mutex_unlock(&pending_skins_lock);
} else { } else {
// Failure // Failure
WARN("Failed To Download Skin: %s", data->name.c_str()); WARN("Failed To Download Skin: %s", data->name.c_str());
free(output);
} }
// Free // Free
@ -120,7 +116,7 @@ static int32_t Textures_assignTexture_injection(Textures_assignTexture_t origina
const int32_t id = original(textures, name, data); const int32_t id = original(textures, name, data);
// Load Skin // Load Skin
if (starts_with(name.c_str(), "$")) { if (name.starts_with("$")) {
loader_data *user_data = new loader_data; loader_data *user_data = new loader_data;
user_data->name = name.substr(1); user_data->name = name.substr(1);
DEBUG("Loading Skin: %s", user_data->name.c_str()); DEBUG("Loading Skin: %s", user_data->name.c_str());

View File

@ -24,31 +24,21 @@ std::string _sound_get_source_file() {
// Resolve // Resolve
// Get Path // Get Path
char *path = strdup(SOURCE_FILE_BASE); const std::string path = SOURCE_FILE_BASE;
ALLOC_CHECK(path); const std::string full_path = override_get_path(path);
// Handle Overrides
char *overridden_full_path = override_get_path(path);
if (overridden_full_path != nullptr) {
free(path);
path = overridden_full_path;
}
// Check If Sound Exists // Check If Sound Exists
if (access(path, F_OK) == -1) { if (access(full_path.c_str(), F_OK) == -1) {
// Fail // Fail
WARN("Audio Source File Doesn't Exist: " SOURCE_FILE_BASE); WARN("Audio Source File Doesn't Exist: %s", path.c_str());
source.assign(""); source = "";
info_sound_data_state = "Missing"; info_sound_data_state = "Missing";
} else { } else {
// Set // Set
source.assign(path); source = full_path;
info_sound_data_state = "Loaded"; info_sound_data_state = "Loaded";
} }
// Free
free(path);
// Mark As Loaded // Mark As Loaded
source_loaded = true; source_loaded = true;

View File

@ -1,4 +1,5 @@
#include <cmath> #include <cmath>
#include <cstdint>
#include <libreborn/libreborn.h> #include <libreborn/libreborn.h>
#include <mods/extend/extend.h> #include <mods/extend/extend.h>

View File

@ -1,3 +1,5 @@
#include <cstdint>
#include <libreborn/libreborn.h> #include <libreborn/libreborn.h>
#include <mods/feature/feature.h> #include <mods/feature/feature.h>

View File

@ -253,9 +253,6 @@ target_include_directories(
"$<INSTALL_INTERFACE:${MCPI_SDK_INCLUDE_DIR}/symbols>" "$<INSTALL_INTERFACE:${MCPI_SDK_INCLUDE_DIR}/symbols>"
) )
# Disable C++11 String ABI
target_compile_definitions(symbols PUBLIC -D_GLIBCXX_USE_CXX11_ABI=0)
# Install # Install
install(TARGETS symbols DESTINATION "${MCPI_LIB_DIR}") install(TARGETS symbols DESTINATION "${MCPI_LIB_DIR}")
# SDK # SDK