More C++ Refactors
This commit is contained in:
parent
58a6706cf9
commit
57503d6a31
@ -62,6 +62,13 @@ string(CONCAT COMPILE_FLAGS_SETUP
|
||||
# Skip RPath
|
||||
"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}")
|
||||
|
||||
# Fast Math
|
||||
@ -79,7 +86,7 @@ if(CMAKE_C_COMPILER_ID STREQUAL "GNU")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
# Buld Dependencies
|
||||
# Build Dependencies
|
||||
add_subdirectory(dependencies)
|
||||
|
||||
# Build libreborn
|
||||
@ -117,10 +124,10 @@ endif()
|
||||
if(BUILD_ARM_COMPONENTS)
|
||||
install(EXPORT sdk DESTINATION "${MCPI_SDK_DIR}" FILE "sdk-targets.cmake" EXPORT_LINK_INTERFACE_LIBRARIES)
|
||||
file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/sdk.cmake"
|
||||
# Sanity Check
|
||||
"${ARM_SANITY_CHECK}\n"
|
||||
# Compile Flags
|
||||
"${COMPILE_FLAGS_SETUP}\n"
|
||||
# Snaity Check
|
||||
"${ARM_SANITY_CHECK}\n"
|
||||
# Log
|
||||
"message(STATUS \"Using Reborn SDK v${MCPI_VERSION}\")\n"
|
||||
# Include Targets
|
||||
|
@ -2,12 +2,15 @@ project(launcher)
|
||||
|
||||
# Launcher
|
||||
add_executable(launcher
|
||||
src/bootstrap.cpp
|
||||
src/patchelf.cpp
|
||||
src/util.cpp
|
||||
src/crash-report.cpp
|
||||
src/sdk.cpp
|
||||
src/mods.cpp
|
||||
src/bootstrap/bootstrap.cpp
|
||||
src/bootstrap/mods.cpp
|
||||
src/bootstrap/assets.cpp
|
||||
src/bootstrap/patchelf.cpp
|
||||
src/bootstrap/debug.cpp
|
||||
src/util/util.cpp
|
||||
src/util/sdk.cpp
|
||||
src/logger/logger.cpp
|
||||
src/logger/crash-report.cpp
|
||||
src/options/parser.cpp
|
||||
src/main.cpp
|
||||
src/client/configuration.cpp
|
||||
@ -19,6 +22,8 @@ target_link_libraries(launcher reborn-util LIB_LIEF trampoline-headers)
|
||||
# RPath
|
||||
set_target_properties(launcher PROPERTIES INSTALL_RPATH "$ORIGIN/lib/native")
|
||||
target_link_options(launcher PRIVATE "LINKER:--disable-new-dtags")
|
||||
# Files
|
||||
target_compile_definitions(launcher PRIVATE _FILE_OFFSET_BITS=64)
|
||||
|
||||
# Install
|
||||
install(TARGETS launcher DESTINATION "${MCPI_INSTALL_DIR}")
|
||||
|
@ -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);
|
||||
}
|
@ -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);
|
15
launcher/src/bootstrap/assets.cpp
Normal file
15
launcher/src/bootstrap/assets.cpp
Normal 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);
|
||||
}
|
75
launcher/src/bootstrap/bootstrap.cpp
Normal file
75
launcher/src/bootstrap/bootstrap.cpp
Normal 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);
|
||||
}
|
20
launcher/src/bootstrap/bootstrap.h
Normal file
20
launcher/src/bootstrap/bootstrap.h
Normal 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);
|
43
launcher/src/bootstrap/debug.cpp
Normal file
43
launcher/src/bootstrap/debug.cpp
Normal 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);
|
||||
}
|
@ -9,7 +9,7 @@
|
||||
|
||||
#include <libreborn/libreborn.h>
|
||||
|
||||
#include "patchelf.h"
|
||||
#include "bootstrap.h"
|
||||
|
||||
// Duplicate MCPI Executable Into /tmp
|
||||
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);
|
||||
|
||||
// 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
|
||||
if (!interpreter.empty()) {
|
||||
binary->interpreter(interpreter);
|
||||
}
|
||||
binary->interpreter(interpreter);
|
||||
|
||||
// Remove Existing Needed Libraries
|
||||
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));
|
||||
}
|
||||
}
|
||||
|
||||
// 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;
|
||||
}
|
@ -7,11 +7,11 @@
|
||||
#define CACHE_VERSION 0
|
||||
|
||||
// Load Cache
|
||||
typedef struct {
|
||||
struct launcher_cache {
|
||||
std::string username;
|
||||
std::string render_distance;
|
||||
std::unordered_map<std::string, bool> feature_flags;
|
||||
} launcher_cache;
|
||||
};
|
||||
extern launcher_cache empty_cache;
|
||||
launcher_cache load_cache();
|
||||
|
||||
|
@ -9,7 +9,7 @@
|
||||
|
||||
#include <libreborn/libreborn.h>
|
||||
|
||||
#include "../util.h"
|
||||
#include "../util/util.h"
|
||||
#include "configuration.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();
|
||||
// Run
|
||||
int return_code;
|
||||
char *output = run_command(command, &return_code, nullptr);
|
||||
if (output != nullptr) {
|
||||
// Trim
|
||||
const size_t length = strlen(output);
|
||||
if (output[length - 1] == '\n') {
|
||||
output[length - 1] = '\0';
|
||||
}
|
||||
// Set
|
||||
set_and_print_env(env_name, output);
|
||||
// Free
|
||||
free(output);
|
||||
const std::vector<unsigned char> *output = run_command(command, &return_code);
|
||||
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();
|
||||
}
|
||||
// Set
|
||||
set_and_print_env(env_name, output_str.c_str());
|
||||
// Check Return Code
|
||||
if (!is_exit_status_success(return_code)) {
|
||||
// Launch Interrupted
|
||||
@ -161,7 +159,7 @@ void configure_client(const options_t &options) {
|
||||
if (options.use_default) {
|
||||
// Use Default Feature Flags
|
||||
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) {
|
||||
bool value;
|
||||
// Strip Default Value
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
@ -1,11 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
void setup_crash_report();
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
35
launcher/src/logger/crash-report.cpp
Normal file
35
launcher/src/logger/crash-report.cpp
Normal 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);
|
||||
}
|
||||
}
|
142
launcher/src/logger/logger.cpp
Normal file
142
launcher/src/logger/logger.cpp
Normal 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);
|
||||
}
|
||||
}
|
4
launcher/src/logger/logger.h
Normal file
4
launcher/src/logger/logger.h
Normal file
@ -0,0 +1,4 @@
|
||||
#pragma once
|
||||
|
||||
void setup_logger();
|
||||
void show_report(const char *log_filename);
|
@ -1,11 +1,10 @@
|
||||
#include <cstdlib>
|
||||
#include <libreborn/libreborn.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
#include "bootstrap.h"
|
||||
#include "bootstrap/bootstrap.h"
|
||||
#include "options/parser.h"
|
||||
#include "crash-report.h"
|
||||
#include "util.h"
|
||||
#include "logger/logger.h"
|
||||
#include "util/util.h"
|
||||
#include "client/configuration.h"
|
||||
|
||||
// Bind Options To Environmental Variable
|
||||
@ -45,7 +44,8 @@ static void setup_environment(const options_t &options) {
|
||||
}
|
||||
|
||||
// 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_profile_directory = realpath(custom_profile_directory, nullptr);
|
||||
ALLOC_CHECK(custom_profile_directory);
|
||||
@ -86,8 +86,8 @@ static void start_game(const options_t &options) {
|
||||
setvbuf(stdout, nullptr, _IONBF, 0);
|
||||
|
||||
// Setup Crash Reporting
|
||||
if (!options.disable_crash_report) {
|
||||
setup_crash_report();
|
||||
if (!options.disable_logger) {
|
||||
setup_logger();
|
||||
}
|
||||
|
||||
// Configure Client Options
|
||||
|
@ -1,6 +1,6 @@
|
||||
OPTION(debug, "debug", 'd', "Enable Debug Logging")
|
||||
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(no_cache, "no-cache", -4, "Disable Client-Mode Configuration Cache")
|
||||
OPTION(wipe_cache, "wipe-cache", -5, "Wipe Cached Client-Mode Configuration And Exit")
|
||||
|
@ -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);
|
@ -1,6 +1,6 @@
|
||||
#include <libreborn/libreborn.h>
|
||||
|
||||
#include "bootstrap.h"
|
||||
#include "../bootstrap/bootstrap.h"
|
||||
#include "util.h"
|
||||
|
||||
// Log
|
@ -5,10 +5,8 @@
|
||||
// Simpler Version Of run_command()
|
||||
void run_simple_command(const char *const command[], const char *error) {
|
||||
int status = 0;
|
||||
char *output = run_command(command, &status, nullptr);
|
||||
if (output != nullptr) {
|
||||
free(output);
|
||||
}
|
||||
const std::vector<unsigned char> *output = run_command(command, &status);
|
||||
delete output;
|
||||
if (!is_exit_status_success(status)) {
|
||||
ERR("%s", error);
|
||||
}
|
@ -7,3 +7,5 @@ void run_simple_command(const char *const command[], const char *error);
|
||||
void chop_last_component(std::string &str);
|
||||
std::string safe_realpath(const std::string &path);
|
||||
std::string get_binary_directory();
|
||||
|
||||
void copy_sdk(const std::string &binary_directory, bool log_with_debug);
|
@ -6,12 +6,12 @@ configure_file(include/libreborn/config.h.in "${CMAKE_CURRENT_BINARY_DIR}/includ
|
||||
|
||||
# Util
|
||||
add_library(reborn-util SHARED
|
||||
src/util/exec.c
|
||||
src/util/string.c
|
||||
src/util/util.c
|
||||
src/util/log.c
|
||||
src/util/exec.cpp
|
||||
src/util/string.cpp
|
||||
src/util/util.cpp
|
||||
src/util/log.cpp
|
||||
src/util/cp437.cpp
|
||||
src/util/env.c
|
||||
src/util/env.cpp
|
||||
)
|
||||
target_include_directories(
|
||||
reborn-util
|
||||
|
@ -1,16 +1,11 @@
|
||||
#pragma once
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#define ENV(name, ...) extern const char *const name##_ENV;
|
||||
#include "env-list.h"
|
||||
#undef ENV
|
||||
|
||||
int is_env_var_internal(const char *env);
|
||||
bool is_env_var_internal(const char *env);
|
||||
void clear_internal_env_vars();
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
// Set Environmental Variable
|
||||
void set_and_print_env(const char *name, const char *value);
|
@ -1,23 +1,21 @@
|
||||
#pragma once
|
||||
|
||||
#include <unistd.h>
|
||||
#include <stdint.h>
|
||||
#include <errno.h>
|
||||
#include <string.h>
|
||||
#include <sys/wait.h>
|
||||
#include <fcntl.h>
|
||||
#include <signal.h>
|
||||
#include <string>
|
||||
#include <optional>
|
||||
#include <array>
|
||||
#include <vector>
|
||||
#include <functional>
|
||||
|
||||
#include "log.h"
|
||||
#include "string.h"
|
||||
#include "util.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
// Set Environmental Variable
|
||||
void set_and_print_env(const char *name, const char *value);
|
||||
// fork() With I/O
|
||||
struct Process {
|
||||
static constexpr int fd_count = 3;
|
||||
Process(pid_t pid_, std::array<int, fd_count> fds_);
|
||||
[[nodiscard]] int close() const;
|
||||
const pid_t pid;
|
||||
const std::array<int, fd_count> fds;
|
||||
};
|
||||
std::optional<Process> fork_with_stdio();
|
||||
void poll_fds(const std::vector<int> &fds, const std::function<void(int, size_t, unsigned char *)> &on_data);
|
||||
|
||||
// Safe execvpe()
|
||||
__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) "
|
||||
|
||||
// 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)
|
||||
|
||||
// Get Exit Status String
|
||||
void get_exit_status_string(int status, char **out);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
std::string get_exit_status_string(int status);
|
@ -1,11 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
|
||||
// Log File
|
||||
int reborn_get_log_fd();
|
||||
@ -29,7 +25,3 @@ int reborn_get_debug_fd();
|
||||
WARN(__VA_ARGS__); \
|
||||
} \
|
||||
}
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
@ -1,11 +1,10 @@
|
||||
#pragma once
|
||||
|
||||
// Patching Functions
|
||||
|
||||
#if defined(REBORN_HAS_PATCH_CODE) && defined(__cplusplus)
|
||||
|
||||
#include <string>
|
||||
|
||||
// Patching Functions
|
||||
#if defined(REBORN_HAS_PATCH_CODE)
|
||||
|
||||
// Init
|
||||
void reborn_init_patch();
|
||||
|
||||
|
@ -1,19 +1,10 @@
|
||||
#pragma once
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
#include <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
|
||||
char *to_cp437(const char *input);
|
||||
char *from_cp437(const char *input);
|
||||
|
||||
// Starts With
|
||||
int starts_with(const char *str, const char *prefix);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
std::string to_cp437(const std::string &input);
|
||||
std::string from_cp437(const std::string &input);
|
@ -1,16 +1,17 @@
|
||||
#pragma once
|
||||
|
||||
#include <unistd.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include <cstring>
|
||||
#include <cerrno>
|
||||
#include <dlfcn.h>
|
||||
#include <array>
|
||||
|
||||
#include "log.h"
|
||||
|
||||
// Check Memory Allocation
|
||||
#define ALLOC_CHECK(obj) \
|
||||
{ \
|
||||
if (obj == NULL) { \
|
||||
if ((obj) == nullptr) { \
|
||||
ERR("Memory Allocation Failed"); \
|
||||
} \
|
||||
}
|
||||
@ -41,23 +42,18 @@
|
||||
} \
|
||||
return func; \
|
||||
}
|
||||
#ifdef __cplusplus
|
||||
#define hooked_function_setup extern "C"
|
||||
#else
|
||||
#define hooked_function_setup
|
||||
#endif
|
||||
#define HOOK(name, return_type, args) \
|
||||
EXTERNAL_FUNC(name, return_type, args) \
|
||||
hooked_function_setup __attribute__((__used__)) return_type name args
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
extern "C" __attribute__((__used__)) return_type name args
|
||||
|
||||
// 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
|
||||
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
|
||||
int lock_file(const char *file);
|
||||
@ -65,8 +61,8 @@ void unlock_file(const char *file, int fd);
|
||||
|
||||
// Access Configuration At Runtime
|
||||
const char *reborn_get_version();
|
||||
int reborn_is_headless();
|
||||
int reborn_is_server();
|
||||
bool reborn_is_headless();
|
||||
bool reborn_is_server();
|
||||
|
||||
// Check $DISPLAY
|
||||
void reborn_check_display();
|
||||
@ -77,6 +73,5 @@ const char *get_home_subdirectory_for_game_data();
|
||||
// Make Sure Directory Exists
|
||||
void ensure_directory(const char *path);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
// Safe write()
|
||||
void safe_write(int fd, const void *buf, size_t size);
|
@ -1,5 +1,4 @@
|
||||
#include <cstdint>
|
||||
#include <cstdlib>
|
||||
|
||||
#include "utf8.h"
|
||||
|
||||
@ -48,22 +47,22 @@ static uint32_t *get_cp437_characters_codepoint_map() {
|
||||
}
|
||||
return map;
|
||||
}
|
||||
char *to_cp437(const char *input) {
|
||||
std::string to_cp437(const std::string &input) {
|
||||
// Convert To UTF-32 For Easier Parsing
|
||||
std::u32string utf32_str = to_utf32(input);
|
||||
const std::u32string utf32_str = to_utf32(input);
|
||||
|
||||
// Allocate String
|
||||
std::string cp437_str;
|
||||
|
||||
// Handle Characters
|
||||
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;
|
||||
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) {
|
||||
valid = true;
|
||||
cp437_str += j;
|
||||
cp437_str += char(j);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -73,19 +72,18 @@ char *to_cp437(const char *input) {
|
||||
}
|
||||
|
||||
// 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
|
||||
std::string input = raw_input;
|
||||
std::u32string utf32_str;
|
||||
|
||||
// Handle Characters
|
||||
for (size_t i = 0; i < input.length(); i++) {
|
||||
unsigned char c = (unsigned char) input[i];
|
||||
utf32_str += get_cp437_characters_codepoint_map()[(uint32_t) c];
|
||||
const unsigned char c = (unsigned char) input[i];
|
||||
utf32_str += char32_t(get_cp437_characters_codepoint_map()[(uint32_t) c]);
|
||||
}
|
||||
|
||||
// Convert To UTF-8
|
||||
return strdup(to_utf8(utf32_str).c_str());
|
||||
return to_utf8(utf32_str);
|
||||
}
|
||||
|
@ -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
|
||||
}
|
38
libreborn/src/util/env.cpp
Normal file
38
libreborn/src/util/env.cpp
Normal 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)");
|
||||
}
|
@ -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
176
libreborn/src/util/exec.cpp
Normal 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 "";
|
||||
}
|
||||
}
|
@ -1,15 +1,24 @@
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
#include <errno.h>
|
||||
#include <string.h>
|
||||
#include <string>
|
||||
|
||||
#include <libreborn/log.h>
|
||||
#include <libreborn/exec.h>
|
||||
#include <libreborn/env.h>
|
||||
|
||||
// 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
|
||||
static int log_fd = -1;
|
||||
int reborn_get_log_fd() {
|
||||
@ -18,10 +27,11 @@ int reborn_get_log_fd() {
|
||||
}
|
||||
// Open Log File
|
||||
const char *fd_str = getenv(_MCPI_LOG_FD_ENV);
|
||||
log_fd = fd_str ? atoi(fd_str) : open("/dev/null", O_WRONLY | O_APPEND);
|
||||
// Check FD
|
||||
if (log_fd < 0) {
|
||||
ERR("Unable To Open Log: %s", strerror(errno));
|
||||
if (fd_str) {
|
||||
log_fd = std::stoi(fd_str);
|
||||
} else {
|
||||
setup_null_fd();
|
||||
log_fd = null_fd;
|
||||
}
|
||||
// Return
|
||||
return reborn_get_log_fd();
|
||||
@ -29,14 +39,12 @@ int reborn_get_log_fd() {
|
||||
void reborn_set_log(const int fd) {
|
||||
// Set Variable
|
||||
log_fd = -1;
|
||||
char buf[128];
|
||||
sprintf(buf, "%i", fd);
|
||||
set_and_print_env(_MCPI_LOG_FD_ENV, buf);
|
||||
set_and_print_env(_MCPI_LOG_FD_ENV, std::to_string(fd).c_str());
|
||||
}
|
||||
|
||||
// Debug Logging
|
||||
static int should_print_debug_to_stderr() {
|
||||
return getenv(MCPI_DEBUG_ENV) != NULL;
|
||||
static bool should_print_debug_to_stderr() {
|
||||
return getenv(MCPI_DEBUG_ENV) != nullptr;
|
||||
}
|
||||
int reborn_get_debug_fd() {
|
||||
return should_print_debug_to_stderr() ? STDERR_FILENO : reborn_get_log_fd();
|
@ -1,14 +1,12 @@
|
||||
#include <libreborn/string.h>
|
||||
|
||||
#include <string.h>
|
||||
|
||||
// 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
|
||||
size_t length = strlen(str);
|
||||
size_t length = str.size();
|
||||
// Truncate Message
|
||||
if (max_length >= 0 && length > ((size_t) max_length)) {
|
||||
str[max_length] = '\0';
|
||||
str = str.substr(0, max_length);
|
||||
length = max_length;
|
||||
}
|
||||
// Loop Through Message
|
||||
@ -20,9 +18,4 @@ 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;
|
||||
}
|
||||
}
|
@ -7,31 +7,34 @@
|
||||
#include <libreborn/env.h>
|
||||
|
||||
// Safe Version Of pipe()
|
||||
void safe_pipe2(int pipefd[2], int flags) {
|
||||
if (pipe2(pipefd, flags) != 0) {
|
||||
Pipe::Pipe(): read(-1), write(-1) {
|
||||
int out[2];
|
||||
if (pipe(out) != 0) {
|
||||
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
|
||||
#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 == -1 || old_val == -1) {
|
||||
return 1;
|
||||
return true;
|
||||
} else if (new_val == 0 || new_val == 100) {
|
||||
return 1;
|
||||
return true;
|
||||
} else {
|
||||
return new_val - old_val >= SIGNIFICANT_PROGRESS;
|
||||
}
|
||||
} else {
|
||||
return 0;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Lock 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) {
|
||||
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() {
|
||||
return MCPI_VERSION;
|
||||
}
|
||||
int reborn_is_headless() {
|
||||
static int ret;
|
||||
static int is_set = 0;
|
||||
bool reborn_is_headless() {
|
||||
static bool ret;
|
||||
static bool is_set = false;
|
||||
if (!is_set) {
|
||||
ret = reborn_is_server();
|
||||
if (getenv(_MCPI_FORCE_HEADLESS_ENV)) {
|
||||
ret = 1;
|
||||
ret = true;
|
||||
} else if (getenv(_MCPI_FORCE_NON_HEADLESS_ENV)) {
|
||||
ret = 0;
|
||||
ret = false;
|
||||
}
|
||||
is_set = 1;
|
||||
is_set = true;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
int reborn_is_server() {
|
||||
bool reborn_is_server() {
|
||||
static int ret;
|
||||
static int is_set = 0;
|
||||
if (!is_set) {
|
||||
ret = getenv(_MCPI_SERVER_MODE_ENV) != NULL;
|
||||
ret = getenv(_MCPI_SERVER_MODE_ENV) != nullptr;
|
||||
is_set = 1;
|
||||
}
|
||||
return ret;
|
||||
@ -84,7 +87,7 @@ void reborn_check_display() {
|
||||
|
||||
// Home Subdirectory
|
||||
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
|
||||
return "";
|
||||
} else if (!reborn_is_server()) {
|
||||
@ -111,4 +114,12 @@ void ensure_directory(const char *path) {
|
||||
if (!is_dir) {
|
||||
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));
|
||||
}
|
||||
}
|
@ -9,7 +9,7 @@ set(CORE_SRC
|
||||
src/events.cpp
|
||||
src/offscreen.cpp
|
||||
src/audio/api.cpp
|
||||
src/audio/engine.c
|
||||
src/audio/engine.cpp
|
||||
src/audio/file.cpp
|
||||
)
|
||||
|
||||
|
@ -44,7 +44,7 @@ void _media_audio_delete_sources() {
|
||||
}
|
||||
|
||||
// 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
|
||||
if (_media_audio_is_loaded()) {
|
||||
// Update Listener Volume
|
||||
@ -56,8 +56,8 @@ void media_audio_update(float volume, float x, float y, float z, float yaw) {
|
||||
AL_ERROR_CHECK();
|
||||
|
||||
// Update Listener Orientation
|
||||
float radian_yaw = yaw * (M_PI / 180);
|
||||
ALfloat orientation[] = {-sinf(radian_yaw), 0.0f, cosf(radian_yaw), 0.0f, 1.0f, 0.0f};
|
||||
const float radian_yaw = float(yaw * (M_PI / 180));
|
||||
const ALfloat orientation[] = {-sinf(radian_yaw), 0.0f, cosf(radian_yaw), 0.0f, 1.0f, 0.0f};
|
||||
alListenerfv(AL_ORIENTATION, orientation);
|
||||
AL_ERROR_CHECK();
|
||||
|
||||
@ -101,11 +101,11 @@ void media_audio_play(const char *source, const char *name, float x, float y, fl
|
||||
// Check
|
||||
if (_media_audio_is_loaded()) {
|
||||
// Load Sound
|
||||
ALuint buffer = _media_audio_get_buffer(source, name);
|
||||
const ALuint buffer = _media_audio_get_buffer(source, name);
|
||||
if (volume > 0.0f && buffer) {
|
||||
// Get Source
|
||||
ALuint al_source;
|
||||
if (idle_sources.size() > 0) {
|
||||
if (!idle_sources.empty()) {
|
||||
// Use Idle Source
|
||||
al_source = idle_sources.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);
|
||||
// Special Out-Of-Memory Handling
|
||||
{
|
||||
ALenum err = alGetError();
|
||||
const ALenum err = alGetError();
|
||||
if (err == AL_OUT_OF_MEMORY) {
|
||||
return;
|
||||
} else {
|
||||
|
@ -1,11 +1,3 @@
|
||||
#pragma once
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
__attribute__((visibility("internal"))) void _media_audio_delete_sources();
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
@ -9,8 +9,8 @@
|
||||
#include "api.h"
|
||||
|
||||
// Store Device
|
||||
static ALCdevice *device = NULL;
|
||||
static ALCcontext *context = NULL;
|
||||
static ALCdevice *device = nullptr;
|
||||
static ALCcontext *context = nullptr;
|
||||
|
||||
// Store State
|
||||
static int is_loaded = 0;
|
||||
@ -21,14 +21,14 @@ int _media_audio_is_loaded() {
|
||||
// Init
|
||||
void _media_audio_init() {
|
||||
// Open Device
|
||||
device = alcOpenDevice(NULL);
|
||||
device = alcOpenDevice(nullptr);
|
||||
if (!device) {
|
||||
WARN("Unable To Load Audio Engine");
|
||||
return;
|
||||
}
|
||||
|
||||
// Create Context
|
||||
context = alcCreateContext(device, NULL);
|
||||
context = alcCreateContext(device, nullptr);
|
||||
ALCenum err = alcGetError(device);
|
||||
if (err != ALC_NO_ERROR) {
|
||||
ERR("Unable To Open Audio Context: %s", alcGetString(device, err));
|
||||
@ -63,7 +63,7 @@ void _media_audio_cleanup() {
|
||||
_media_audio_delete_buffers();
|
||||
|
||||
// Deselect Context
|
||||
alcMakeContextCurrent(NULL);
|
||||
alcMakeContextCurrent(nullptr);
|
||||
ALCenum err = alcGetError(device);
|
||||
if (err != ALC_NO_ERROR) {
|
||||
ERR("Unable To Deselect Audio Context: %s", alcGetString(device, err));
|
@ -2,14 +2,6 @@
|
||||
|
||||
#include <AL/al.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
__attribute__((visibility("internal"))) void _media_audio_init();
|
||||
__attribute__((visibility("internal"))) void _media_audio_cleanup();
|
||||
__attribute__((visibility("internal"))) int _media_audio_is_loaded();
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
__attribute__((visibility("internal"))) int _media_audio_is_loaded();
|
@ -14,13 +14,13 @@
|
||||
// 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 std::unordered_map<std::string, std::unique_ptr<LIEF::ELF::Binary>> sources = {};
|
||||
std::string cpp_source = source;
|
||||
if (sources.count(cpp_source) == 0) {
|
||||
const std::string cpp_source = source;
|
||||
if (!sources.contains(cpp_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);
|
||||
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);
|
||||
callback(data.data(), data.size());
|
||||
} else {
|
||||
@ -73,8 +73,8 @@ static ALuint load_sound(const char *source, const char *name) {
|
||||
}
|
||||
|
||||
// Load Data
|
||||
int remaining_size = size - sizeof (audio_metadata);
|
||||
int data_size = meta->channels * meta->frames * meta->frame_size;
|
||||
const int remaining_size = size - sizeof (audio_metadata);
|
||||
const int data_size = meta->channels * meta->frames * meta->frame_size;
|
||||
if (remaining_size < data_size) {
|
||||
WARN("Symbol Too Small To Contain Specified Audio Data: %s", name);
|
||||
return;
|
||||
|
@ -2,13 +2,5 @@
|
||||
|
||||
#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"))) void _media_audio_delete_buffers();
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
@ -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) {
|
||||
// Convert
|
||||
size_t str_size = 4 /* Maximum UTF-8 character size */ + 1 /* NULL-terminator */;
|
||||
char str[str_size] = {};
|
||||
codepoint_to_utf8((unsigned char *) str, codepoint);
|
||||
char *cp437_str = to_cp437(str);
|
||||
unsigned char str[str_size] = {};
|
||||
codepoint_to_utf8(str, codepoint);
|
||||
std::string cp437_str = to_cp437((const char *) str);
|
||||
// Send Event
|
||||
for (int i = 0; cp437_str[i] != '\0'; i++) {
|
||||
character_event(cp437_str[i]);
|
||||
for (const char x : cp437_str) {
|
||||
character_event(x);
|
||||
}
|
||||
// Free
|
||||
free(cp437_str);
|
||||
}
|
||||
|
||||
// Convert Screen Coordinates To Pixels
|
||||
|
@ -1,5 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
|
||||
extern "C" {
|
||||
char *override_get_path(const char *filename);
|
||||
std::string override_get_path(std::string filename);
|
||||
}
|
@ -26,9 +26,8 @@ std::string chat_send_api_command(const Minecraft *minecraft, const std::string
|
||||
|
||||
// Send API Chat Command
|
||||
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";
|
||||
free(utf_str);
|
||||
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) {
|
||||
std::string full_message = _chat_get_prefix(username) + message;
|
||||
char *raw_str = strdup(full_message.c_str());
|
||||
ALLOC_CHECK(raw_str);
|
||||
sanitize_string(raw_str, MAX_CHAT_MESSAGE_LENGTH, 0);
|
||||
full_message = raw_str;
|
||||
free(raw_str);
|
||||
sanitize_string(full_message, MAX_CHAT_MESSAGE_LENGTH, false);
|
||||
server_side_network_handler->displayGameMessage(full_message);
|
||||
}
|
||||
// Handle Chat packet Send
|
||||
void chat_handle_packet_send(const Minecraft *minecraft, ChatPacket *packet) {
|
||||
// Convert To CP-437
|
||||
char *cp437_str = to_cp437(packet->message.c_str());
|
||||
packet->message = cp437_str;
|
||||
free(cp437_str);
|
||||
packet->message = to_cp437(packet->message);
|
||||
// Send
|
||||
RakNetInstance *rak_net_instance = minecraft->rak_net_instance;
|
||||
if (rak_net_instance->isServer()) {
|
||||
|
@ -1,10 +1,13 @@
|
||||
#include "game-mode-internal.h"
|
||||
#include <cstdint>
|
||||
|
||||
#include <mods/init/init.h>
|
||||
#include <mods/feature/feature.h>
|
||||
|
||||
#include <libreborn/libreborn.h>
|
||||
#include <symbols/minecraft.h>
|
||||
|
||||
#include "game-mode-internal.h"
|
||||
|
||||
static int is_survival = -1;
|
||||
|
||||
// Patch Game Mode
|
||||
|
@ -1,3 +1,5 @@
|
||||
#include <cstdint>
|
||||
|
||||
#include <libreborn/libreborn.h>
|
||||
#include <symbols/minecraft.h>
|
||||
|
||||
|
@ -10,10 +10,8 @@
|
||||
// Print Chat To Log
|
||||
static void Gui_addMessage_injection(Gui_addMessage_t original, Gui *gui, const std::string &text) {
|
||||
// Sanitize Message
|
||||
char *new_message = strdup(text.c_str());
|
||||
ALLOC_CHECK(new_message);
|
||||
sanitize_string(new_message, -1, 1);
|
||||
const std::string cpp_str = new_message;
|
||||
std::string new_message = text;
|
||||
sanitize_string(new_message, -1, true);
|
||||
|
||||
// Process Message
|
||||
static bool recursing = false;
|
||||
@ -22,22 +20,18 @@ static void Gui_addMessage_injection(Gui_addMessage_t original, Gui *gui, const
|
||||
recursing = true;
|
||||
|
||||
// Print Log Message
|
||||
char *safe_message = from_cp437(new_message);
|
||||
fprintf(stderr, "[CHAT]: %s\n", safe_message);
|
||||
free(safe_message);
|
||||
std::string safe_message = from_cp437(new_message);
|
||||
fprintf(stderr, "[CHAT]: %s\n", safe_message.c_str());
|
||||
|
||||
// Call Original Method
|
||||
original(gui, cpp_str);
|
||||
original(gui, new_message);
|
||||
|
||||
// End Recursing
|
||||
recursing = false;
|
||||
} else {
|
||||
// Call Original Method
|
||||
original(gui, cpp_str);
|
||||
original(gui, new_message);
|
||||
}
|
||||
|
||||
// Free
|
||||
free(new_message);
|
||||
}
|
||||
|
||||
// Print Progress Reports
|
||||
|
@ -34,13 +34,10 @@ static void LoginPacket_read_injection(LoginPacket_read_t original, LoginPacket
|
||||
const RakNet_RakString_SharedString *shared_string = rak_string->sharedString;
|
||||
const char *c_str = shared_string->c_str;
|
||||
// Sanitize
|
||||
char *new_username = strdup(c_str);
|
||||
ALLOC_CHECK(new_username);
|
||||
std::string new_username = c_str;
|
||||
sanitize_string(new_username, MAX_USERNAME_LENGTH, 0);
|
||||
// Set New Username
|
||||
rak_string->Assign(new_username);
|
||||
// Free
|
||||
free(new_username);
|
||||
rak_string->Assign(new_username.c_str());
|
||||
}
|
||||
|
||||
// Fix RakNet::RakString Security Bug
|
||||
|
@ -161,10 +161,8 @@ static void position_info(Font *font, const int width, const int height) {
|
||||
void open_url(const std::string &url) {
|
||||
int return_code;
|
||||
const char *command[] = {"xdg-open", url.c_str(), nullptr};
|
||||
char *output = run_command(command, &return_code, nullptr);
|
||||
if (output != nullptr) {
|
||||
free(output);
|
||||
}
|
||||
const std::vector<unsigned char> *output = run_command(command, &return_code);
|
||||
delete output;
|
||||
if (!is_exit_status_success(return_code)) {
|
||||
WARN("Unable To Open URL: %s", url.c_str());
|
||||
}
|
||||
|
@ -44,10 +44,6 @@ static const char *get_username() {
|
||||
}
|
||||
return username;
|
||||
}
|
||||
static char *safe_username = nullptr;
|
||||
__attribute__((destructor)) static void _free_safe_username() {
|
||||
free(safe_username);
|
||||
}
|
||||
|
||||
static int render_distance;
|
||||
// Configure Options
|
||||
@ -162,8 +158,9 @@ void init_options() {
|
||||
if (strcmp(Strings::default_username, "StevePi") != 0) {
|
||||
ERR("Default Username Is Invalid");
|
||||
}
|
||||
safe_username = to_cp437(username);
|
||||
patch_address((void *) &Strings::default_username, (void *) safe_username);
|
||||
std::string *safe_username = new std::string;
|
||||
*safe_username = to_cp437(username);
|
||||
patch_address((void *) &Strings::default_username, (void *) safe_username->c_str());
|
||||
|
||||
// Disable Autojump By Default
|
||||
if (feature_has("Disable Autojump By Default", server_disabled)) {
|
||||
@ -210,7 +207,7 @@ void init_options() {
|
||||
// Replace String
|
||||
patch_address((void *) &Strings::feedback_vibration_options_txt_name, (void *) "gfx_ao");
|
||||
// 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"
|
||||
patch((void *) 0x193b8, gfx_ao_loading_patch);
|
||||
// Saving
|
||||
@ -223,7 +220,7 @@ void init_options() {
|
||||
// Replace String
|
||||
patch_address((void *) &Strings::gfx_lowquality_options_txt_name, (void *) "gfx_anaglyph");
|
||||
// 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"
|
||||
patch((void *) 0x19400, gfx_anaglyph_loading_patch);
|
||||
// Disable Loading Side Effects
|
||||
|
@ -1,4 +1,5 @@
|
||||
#include <string>
|
||||
#include <cstdint>
|
||||
|
||||
#include <libreborn/libreborn.h>
|
||||
#include <symbols/minecraft.h>
|
||||
|
@ -12,18 +12,18 @@
|
||||
#include <mods/home/home.h>
|
||||
#include <mods/init/init.h>
|
||||
|
||||
// Hook access
|
||||
HOOK(access, int, (const char *pathname, int mode)) {
|
||||
char *new_path = override_get_path(pathname);
|
||||
// Open File
|
||||
const int ret = real_access()(new_path != nullptr ? new_path : pathname, mode);
|
||||
// Free Data
|
||||
if (new_path != nullptr) {
|
||||
free(new_path);
|
||||
// Hook Functions
|
||||
#define HOOK_OPEN(name, return_type, mode_type) \
|
||||
HOOK(name, return_type, (const char *filename, mode_type mode)) { \
|
||||
const std::string new_path = override_get_path(filename); \
|
||||
/* Open File */ \
|
||||
return_type ret = real_##name()(!new_path.empty() ? new_path.c_str() : filename, mode); \
|
||||
/* Return */ \
|
||||
return ret; \
|
||||
}
|
||||
// Return
|
||||
return ret;
|
||||
}
|
||||
HOOK_OPEN(fopen, FILE *, const char *)
|
||||
HOOK_OPEN(fopen64, FILE *, const char *)
|
||||
HOOK_OPEN(access, int, int)
|
||||
|
||||
// Get Override Folder
|
||||
static std::string get_override_directory() {
|
||||
@ -32,9 +32,9 @@ static std::string get_override_directory() {
|
||||
}
|
||||
|
||||
// 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
|
||||
if (starts_with(filename, "data/images/$")) {
|
||||
if (filename.starts_with("data/images/$")) {
|
||||
// Fallback Texture
|
||||
filename = "data/images/mob/char.png";
|
||||
}
|
||||
@ -42,12 +42,12 @@ char *override_get_path(const char *filename) {
|
||||
// Get Asset Override Path
|
||||
const std::string overrides = get_override_directory();
|
||||
|
||||
// Data Prefiix
|
||||
// Data Prefix
|
||||
const std::string data_prefix = "data/";
|
||||
int data_prefix_length = data_prefix.length();
|
||||
const int data_prefix_length = data_prefix.length();
|
||||
|
||||
// Folders To Check
|
||||
std::string asset_folders[] = {
|
||||
const std::string asset_folders[] = {
|
||||
overrides,
|
||||
getenv(_MCPI_REBORN_ASSETS_PATH_ENV),
|
||||
getenv(_MCPI_VANILLA_ASSETS_PATH_ENV),
|
||||
@ -72,39 +72,7 @@ char *override_get_path(const char *filename) {
|
||||
}
|
||||
|
||||
// Return
|
||||
if (new_path.empty()) {
|
||||
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;
|
||||
return new_path;
|
||||
}
|
||||
|
||||
// Init
|
||||
|
@ -57,9 +57,7 @@ static ServerPropertyTypes &get_property_types() {
|
||||
// Get World Name
|
||||
static std::string get_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 = safe_name_c;
|
||||
free(safe_name_c);
|
||||
std::string safe_name = to_cp437(name);
|
||||
return safe_name;
|
||||
}
|
||||
|
||||
@ -116,11 +114,9 @@ static std::vector<Player *> get_players_in_level(Level *level) {
|
||||
return level->players;
|
||||
}
|
||||
// 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;
|
||||
char *safe_username_c = from_cp437(username->c_str());
|
||||
std::string safe_username = safe_username_c;
|
||||
free(safe_username_c);
|
||||
std::string safe_username = from_cp437(*username);
|
||||
return safe_username;
|
||||
}
|
||||
// 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) {
|
||||
// Format Message
|
||||
const std::string message = "[Server] " + cmd;
|
||||
char *safe_message = to_cp437(message.c_str());
|
||||
std::string cpp_string = safe_message;
|
||||
std::string cpp_string = to_cp437(message);
|
||||
// Post Message To Chat
|
||||
server_side_network_handler->displayGameMessage(cpp_string);
|
||||
// Free
|
||||
free(safe_message);
|
||||
}
|
||||
});
|
||||
// List Players
|
||||
|
@ -1,3 +1,5 @@
|
||||
#include <cstdint>
|
||||
|
||||
#include <symbols/minecraft.h>
|
||||
#include <libreborn/libreborn.h>
|
||||
|
||||
|
@ -19,15 +19,14 @@
|
||||
// Loading Pending Skins
|
||||
struct pending_skin {
|
||||
int32_t texture_id;
|
||||
char *data;
|
||||
int size;
|
||||
const std::vector<unsigned char> *data;
|
||||
};
|
||||
static std::vector<pending_skin> &get_pending_skins() {
|
||||
static std::vector<pending_skin> pending_skins;
|
||||
return pending_skins;
|
||||
}
|
||||
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
|
||||
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()) {
|
||||
// Read PNG Info
|
||||
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) {
|
||||
continue;
|
||||
}
|
||||
@ -54,7 +53,7 @@ static void load_pending_skins(Minecraft *minecraft) {
|
||||
|
||||
// Free
|
||||
for (const pending_skin &skin : get_pending_skins()) {
|
||||
free(skin.data);
|
||||
delete skin.data;
|
||||
}
|
||||
|
||||
// Clear
|
||||
@ -87,11 +86,10 @@ static void *loader_thread(void *user_data) {
|
||||
const std::string url = get_skin_server() + '/' + data->name + ".png";
|
||||
int return_code;
|
||||
const char *command[] = {"wget", "-O", "-", url.c_str(), nullptr};
|
||||
size_t output_size = 0;
|
||||
char *output = run_command(command, &return_code, &output_size);
|
||||
const std::vector<unsigned char> *output = run_command(command, &return_code);
|
||||
|
||||
// Check Success
|
||||
if (output != nullptr && is_exit_status_success(return_code)) {
|
||||
if (is_exit_status_success(return_code)) {
|
||||
// Success
|
||||
DEBUG("Downloaded Skin: %s", data->name.c_str());
|
||||
|
||||
@ -99,14 +97,12 @@ static void *loader_thread(void *user_data) {
|
||||
pending_skin skin = {};
|
||||
skin.texture_id = data->texture_id;
|
||||
skin.data = output;
|
||||
skin.size = (int) output_size;
|
||||
pthread_mutex_lock(&pending_skins_lock);
|
||||
get_pending_skins().push_back(skin);
|
||||
pthread_mutex_unlock(&pending_skins_lock);
|
||||
} else {
|
||||
// Failure
|
||||
WARN("Failed To Download Skin: %s", data->name.c_str());
|
||||
free(output);
|
||||
}
|
||||
|
||||
// Free
|
||||
@ -120,7 +116,7 @@ static int32_t Textures_assignTexture_injection(Textures_assignTexture_t origina
|
||||
const int32_t id = original(textures, name, data);
|
||||
|
||||
// Load Skin
|
||||
if (starts_with(name.c_str(), "$")) {
|
||||
if (name.starts_with("$")) {
|
||||
loader_data *user_data = new loader_data;
|
||||
user_data->name = name.substr(1);
|
||||
DEBUG("Loading Skin: %s", user_data->name.c_str());
|
||||
|
@ -24,31 +24,21 @@ std::string _sound_get_source_file() {
|
||||
// Resolve
|
||||
|
||||
// Get Path
|
||||
char *path = strdup(SOURCE_FILE_BASE);
|
||||
ALLOC_CHECK(path);
|
||||
|
||||
// Handle Overrides
|
||||
char *overridden_full_path = override_get_path(path);
|
||||
if (overridden_full_path != nullptr) {
|
||||
free(path);
|
||||
path = overridden_full_path;
|
||||
}
|
||||
const std::string path = SOURCE_FILE_BASE;
|
||||
const std::string full_path = override_get_path(path);
|
||||
|
||||
// Check If Sound Exists
|
||||
if (access(path, F_OK) == -1) {
|
||||
if (access(full_path.c_str(), F_OK) == -1) {
|
||||
// Fail
|
||||
WARN("Audio Source File Doesn't Exist: " SOURCE_FILE_BASE);
|
||||
source.assign("");
|
||||
WARN("Audio Source File Doesn't Exist: %s", path.c_str());
|
||||
source = "";
|
||||
info_sound_data_state = "Missing";
|
||||
} else {
|
||||
// Set
|
||||
source.assign(path);
|
||||
source = full_path;
|
||||
info_sound_data_state = "Loaded";
|
||||
}
|
||||
|
||||
// Free
|
||||
free(path);
|
||||
|
||||
// Mark As Loaded
|
||||
source_loaded = true;
|
||||
|
||||
|
@ -1,4 +1,5 @@
|
||||
#include <cmath>
|
||||
#include <cstdint>
|
||||
|
||||
#include <libreborn/libreborn.h>
|
||||
#include <mods/extend/extend.h>
|
||||
|
@ -1,3 +1,5 @@
|
||||
#include <cstdint>
|
||||
|
||||
#include <libreborn/libreborn.h>
|
||||
|
||||
#include <mods/feature/feature.h>
|
||||
|
@ -253,9 +253,6 @@ target_include_directories(
|
||||
"$<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(TARGETS symbols DESTINATION "${MCPI_LIB_DIR}")
|
||||
# SDK
|
||||
|
Loading…
Reference in New Issue
Block a user