Compare commits

1 Commits

Author SHA1 Message Date
fb763cdf64 heload, a mess 2025-03-16 17:45:09 +02:00
16 changed files with 736 additions and 81 deletions

3
.gitmodules vendored
View File

@@ -1,6 +1,3 @@
[submodule "3rd/SDL"] [submodule "3rd/SDL"]
path = 3rd/SDL path = 3rd/SDL
url = https://github.com/libsdl-org/SDL.git url = https://github.com/libsdl-org/SDL.git
[submodule "hiload"]
path = hiload
url = git@git.kitemoonsche.me:schme/hiload.git

View File

@@ -1,14 +1,22 @@
cmake_minimum_required(VERSION 3.21) cmake_minimum_required(VERSION 3.21)
project(Hiisi) project(Hiisi)
# Required for hiload
set(CMAKE_EXPORT_COMPILE_COMMANDS ON) set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
set(CMAKE_CXX_STANDARD 17) set(CMAKE_CXX_STANDARD 17)
set(CMAKE_C)
set(CMAKE_C_STANDARD 11)
add_subdirectory(3rd/SDL EXCLUDE_FROM_ALL) add_subdirectory(3rd/SDL EXCLUDE_FROM_ALL)
add_subdirectory(hiload) add_subdirectory(heload)
add_subdirectory(hiisi) add_subdirectory(hiisi)
target_link_options(SDL3-shared PRIVATE -Wl,-z,nodelete)
add_executable(hiisi-run add_executable(hiisi-run
hiisi/main.cpp) hiisi/main.cpp)
target_link_libraries(hiisi-run hiisi-engine hiload) target_link_libraries(hiisi-run hiisi-engine heload)

View File

@@ -0,0 +1,43 @@
# clang-toolchain.cmake - Complete toolchain file for Clang
# Specify the compiler
set(CMAKE_C_COMPILER clang)
set(CMAKE_CXX_COMPILER clang++)
# Specify the target system
# set(CMAKE_SYSTEM_NAME Linux) # Change to appropriate system (Linux, Darwin, Windows, etc.)
# Specify LLVM binutils
set(CMAKE_AR llvm-ar CACHE FILEPATH "Archive command")
set(CMAKE_NM llvm-nm CACHE FILEPATH "Name mangling command")
set(CMAKE_RANLIB llvm-ranlib CACHE FILEPATH "Archive indexer command")
set(CMAKE_OBJDUMP llvm-objdump CACHE FILEPATH "Object dump command")
set(CMAKE_OBJCOPY llvm-objcopy CACHE FILEPATH "Object copy command")
set(CMAKE_STRIP llvm-strip CACHE FILEPATH "Strip command")
set(CMAKE_LINKER lld CACHE FILEPATH "Linker command")
# Use lld as the linker
# set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fuse-ld=lld")
# set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fuse-ld=lld")
# Set linker flags
# set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -fuse-ld=lld")
# set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -fuse-ld=lld")
# set(CMAKE_MODULE_LINKER_FLAGS "${CMAKE_MODULE_LINKER_FLAGS} -fuse-ld=lld")
# Use Clang's libc++ instead of GCC's libstdc++ (optional)
# Uncomment these lines if you want to use libc++
# set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -stdlib=libc++")
# set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -stdlib=libc++ -lc++abi")
# set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -stdlib=libc++ -lc++abi")
# Set compiler optimization flags (optional)
# set(CMAKE_C_FLAGS_RELEASE "-O3 -DNDEBUG")
# set(CMAKE_CXX_FLAGS_RELEASE "-O3 -DNDEBUG")
# set(CMAKE_C_FLAGS_DEBUG "-g -O0")
# set(CMAKE_CXX_FLAGS_DEBUG "-g -O0")
# Enable address sanitizer (optional)
# set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fsanitize=address -fno-omit-frame-pointer")
# set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=address -fno-omit-frame-pointer")
# set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -fsanitize=address")

55
heload/CMakeLists.txt Normal file
View File

@@ -0,0 +1,55 @@
cmake_minimum_required(VERSION 3.21)
project(Heload)
# I just like to have this with my tooling
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
set(CMAKE_C_STANDARD 11)
# heload library
# ##############
add_library(heload SHARED
src/heload.c
src/symbols.c
)
set_property(TARGET heload PROPERTY POSITION_INDEPENDENT_CODE ON)
target_link_libraries(heload dl)
# Specify the public headers location
target_include_directories(heload PUBLIC
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include> # During build
$<INSTALL_INTERFACE:include> # When installed
)
install(TARGETS heload
EXPORT heloadTargets
ARCHIVE DESTINATION lib
LIBRARY DESTINATION lib
RUNTIME DESTINATION bin
)
# Install header files
install(DIRECTORY include/ DESTINATION include)
# Export the library for find_package()
install(EXPORT heloadTargets
FILE heloadConfig.cmake
DESTINATION lib/cmake/heload
)
export(TARGETS heload FILE heloadConfig.cmake)
# auditor libraries
# ###############
add_library(auditor-x86_64 SHARED
src/auditor/auditor-x86_64.c
)
install(TARGETS auditor-x86_64
EXPORT auditor-x86_64Targets
ARCHIVE DESTINATION lib
LIBRARY DESTINATION lib
RUNTIME DESTINATION bin
)

View File

@@ -0,0 +1,25 @@
#ifndef HELOAD_H_
#define HELOAD_H_
#ifdef __cplusplus
extern "C" {
#endif
// Return codes for reload_module
typedef enum {
RELOAD_SUCCESS = 0,
RELOAD_NOT_FOUND,
RELOAD_CLOSE_ERROR,
RELOAD_OPEN_ERROR
} ReloadResult;
int he_init();
void he_deinit();
void he_print_module_infos();
ReloadResult he_reload_module(const char *module_name);
#ifdef __cplusplus
}
#endif
#endif // HELOAD_H_

View File

@@ -0,0 +1,22 @@
#ifndef SYMBOLS_H_
#define SYMBOLS_H_
#include <link.h>
#include <stddef.h>
#include <stdint.h>
typedef struct {
char **names;
void **addresses;
size_t count;
size_t capacity;
} SymbolInfos;
typedef enum { CREATE_SUCCESS = 0, CREATE_FAILED } CreateResult;
struct dl_phdr_info;
CreateResult he_create_symbol_info(SymbolInfos *, struct dl_phdr_info *);
void he_free_symbol_info(SymbolInfos *);
#endif // SYMBOLS_H_

View File

@@ -0,0 +1,81 @@
#define _GNU_SOURCE
#include <link.h>
#include <stdio.h>
unsigned int la_version(unsigned int version) {
printf("la_version(): version = %u; LAV_CURRENT = %u\n", version,
LAV_CURRENT);
return LAV_CURRENT;
}
char *la_objsearch(const char *name, uintptr_t *cookie, unsigned int flag) {
printf("la_objsearch(): name = %s; cookie = %p", name, cookie);
printf("; flag = %s\n", (flag == LA_SER_ORIG) ? "LA_SER_ORIG"
: (flag == LA_SER_LIBPATH) ? "LA_SER_LIBPATH"
: (flag == LA_SER_RUNPATH) ? "LA_SER_RUNPATH"
: (flag == LA_SER_DEFAULT) ? "LA_SER_DEFAULT"
: (flag == LA_SER_CONFIG) ? "LA_SER_CONFIG"
: (flag == LA_SER_SECURE) ? "LA_SER_SECURE"
: "???");
return (char *)name;
}
void la_activity(uintptr_t *cookie, unsigned int flag) {
printf("la_activity(): cookie = %p; flag = %s\n", cookie,
(flag == LA_ACT_CONSISTENT) ? "LA_ACT_CONSISTENT"
: (flag == LA_ACT_ADD) ? "LA_ACT_ADD"
: (flag == LA_ACT_DELETE) ? "LA_ACT_DELETE"
: "???");
}
unsigned int la_objopen(struct link_map *map, Lmid_t lmid, uintptr_t *cookie) {
printf("la_objopen(): loading \"%s\"; lmid = %s; cookie=%p\n", map->l_name,
(lmid == LM_ID_BASE) ? "LM_ID_BASE"
: (lmid == LM_ID_NEWLM) ? "LM_ID_NEWLM"
: "???",
cookie);
return LA_FLG_BINDTO | LA_FLG_BINDFROM;
}
unsigned int la_objclose(uintptr_t *cookie) {
printf("la_objclose(): %p\n", cookie);
return 0;
}
void la_preinit(uintptr_t *cookie) { printf("la_preinit(): %p\n", cookie); }
uintptr_t la_symbind32(Elf32_Sym *sym, unsigned int ndx, uintptr_t *refcook,
uintptr_t *defcook, unsigned int *flags,
const char *symname) {
printf("la_symbind64(): symname = %s; sym->st_value = %p\n", symname,
sym->st_value);
printf(" ndx = %u; flags = %#x", ndx, *flags);
printf("; refcook = %p; defcook = %p\n", refcook, defcook);
return sym->st_value;
}
uintptr_t la_symbind64(Elf64_Sym *sym, unsigned int ndx, uintptr_t *refcook,
uintptr_t *defcook, unsigned int *flags,
const char *symname) {
printf("la_symbind64(): symname = %s; sym->st_value = %p\n", symname,
(void*)sym->st_value);
printf(" ndx = %u; flags = %#x", ndx, *flags);
printf("; refcook = %p; defcook = %p\n", refcook, defcook);
return sym->st_value;
}
Elf64_Addr la_i86_gnu_pltenter(Elf64_Sym *sym, unsigned int ndx,
uintptr_t *refcook, uintptr_t *defcook,
La_x86_64_regs *regs, unsigned int *flags,
const char *symname, long *framesizep) {
printf("la_x86_64_gnu_pltenter(): %s (%p)\n", symname, (void*)sym->st_value);
return sym->st_value;
}

View File

@@ -0,0 +1,26 @@
#ifndef AUDITOR_H_
#define AUDITOR_H_
#define _GNU_SOURCE
#include <link.h>
#include <stdint.h>
unsigned int la_version(unsigned int version);
char *la_objsearch(const char *name, uintptr_t *cookie, unsigned int flag);
void la_activity(uintptr_t *cookie, unsigned int flag);
unsigned int la_objopen(struct link_map *map, Lmid_t lmid, uintptr_t *cookie);
unsigned int la_objclose(uintptr_t *cookie);
void la_preinit(uintptr_t *cookie);
uintptr_t la_symbind32(Elf32_Sym *sym, unsigned int ndx, uintptr_t *refcook,
uintptr_t *defcook, unsigned int *flags,
const char *symname);
uintptr_t la_symbind64(Elf64_Sym *sym, unsigned int ndx, uintptr_t *refcook,
uintptr_t *defcook, unsigned int *flags,
const char *symname);
/* Elf32_Addr la_i86_gnu_pltenter(Elf32_Sym *sym, unsigned int ndx, */
/* uintptr_t *refcook, uintptr_t *defcook, */
/* La_i86_regs *regs, unsigned int *flags, */
/* const char *symname, long *framesizep); */
#endif // AUDITOR_H_

266
heload/src/heload.c Normal file
View File

@@ -0,0 +1,266 @@
// Required for dlinfo and other GNU specific linker code
#define _GNU_SOURCE
#include "heload/heload.h"
#include "heload/symbols.h"
#include <assert.h>
#include <dlfcn.h>
#include <link.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef struct {
char **names; // Array of library names
void **handles; // Array of library handles
SymbolInfos *symbols; // Symbol info for modules
size_t count; // Number of libraries
size_t capacity; // Allocated capacity
} ModuleInfos;
static ModuleInfos *module_infos = 0;
// Callback function for dl_iterate_phdr
static int gather_module_infos_callback(struct dl_phdr_info *info, size_t size,
void *data) {
ModuleInfos *infos = (ModuleInfos *)data;
// Resize arrays if needed
if (infos->count >= infos->capacity) {
infos->capacity *= 2;
char **new_names = realloc(infos->names, infos->capacity * sizeof(char *));
void **new_handles =
realloc(infos->handles, infos->capacity * sizeof(void *));
SymbolInfos *new_symbols =
realloc(infos->symbols, infos->capacity * sizeof(SymbolInfos));
if (!new_names || !new_handles || !new_symbols) {
return 1; // Stop iteration on error
}
infos->names = new_names;
infos->handles = new_handles;
infos->symbols = new_symbols;
}
// Store the module name
infos->names[infos->count] = strdup(info->dlpi_name);
if (!infos->names[infos->count]) {
return 1; // Stop iteration on error
}
// Try to get the handle
infos->handles[infos->count] =
dlopen(info->dlpi_name, RTLD_LAZY | RTLD_NOLOAD);
if (he_create_symbol_info(&infos->symbols[infos->count], info) != CREATE_SUCCESS) {
fprintf(stderr, "Failed to create symbol info for %s\n", infos->names[infos->count]);
}
infos->count++;
return 0; // Continue iteration
}
static void free_module_infos(ModuleInfos *modules) {
if (!modules)
return;
for (size_t i = 0; i < modules->count; i++) {
if (modules->names[i])
free(modules->names[i]);
he_free_symbol_info(&modules->symbols[i]);
}
free(modules->names);
free(modules->handles);
free(modules->symbols);
free(modules);
}
static ModuleInfos *gather_shared_libraries(void) {
ModuleInfos *result = NULL;
// Allocate the result structure
result = calloc(1, sizeof(ModuleInfos));
if (!result) {
return NULL;
}
// Initial capacity
result->capacity = 16;
result->names = calloc(result->capacity, sizeof(char *));
result->handles = calloc(result->capacity, sizeof(void *));
result->symbols = calloc(result->capacity, sizeof(SymbolInfos));
if (!result->names || !result->handles || !result->symbols) {
if (result->names)
free(result->names);
if (result->handles)
free(result->handles);
if (result->symbols)
free(result->symbols);
free(result);
return NULL;
}
// Iterate over all shared objects
if (dl_iterate_phdr(gather_module_infos_callback, result) != 0) {
// Error occurred in callback
free_module_infos(result);
return NULL;
}
return result;
}
int he_init() {
assert(!module_infos);
ModuleInfos *infos = gather_shared_libraries();
if (!infos) {
fprintf(stderr, "Failed to gather module infos.\n");
return 1;
}
if (module_infos) {
free_module_infos(module_infos);
}
module_infos = infos;
return 0;
}
void he_deinit() { free_module_infos(module_infos); }
/**
* Reloads a shared library module
*
* @param modules The ModuleInfos structure containing loaded modules
* @param filename The name of the module to reload
* @param updated_handle Pointer to store the new handle (can be NULL)
* @return ReloadResult indicating success or failure
*/
static ReloadResult reload_module(ModuleInfos *modules, const char *filename,
void **updated_handle) {
if (!modules || !filename) {
return RELOAD_NOT_FOUND;
}
// Find the module by filename
int found = 0;
size_t index = 0;
for (size_t i = 0; i < modules->count; i++) {
// Check if this is the module we're looking for
if (modules->names[i] && strcmp(modules->names[i], filename) == 0) {
found = 1;
index = i;
break;
}
// Also check if the filename is at the end of the path
if (modules->names[i]) {
const char *basename = strrchr(modules->names[i], '/');
if (basename && strcmp(basename + 1, filename) == 0) {
found = 1;
index = i;
break;
}
}
}
if (!found) {
return RELOAD_NOT_FOUND;
}
// Save the full path
char *fullpath = strdup(modules->names[index]);
if (!fullpath) {
return RELOAD_OPEN_ERROR;
}
// Close the old handle
if (modules->handles[index]) {
if (dlclose(modules->handles[index]) != 0) {
free(fullpath);
return RELOAD_CLOSE_ERROR;
}
}
// Open the module again with RTLD_NOW
void *new_handle = dlopen(fullpath, RTLD_NOW);
if (!new_handle) {
fprintf(stderr, "Error reloading module: %s\n", dlerror());
free(fullpath);
return RELOAD_OPEN_ERROR;
}
// Update the handle in our structure
modules->handles[index] = new_handle;
// If the caller wants the new handle, provide it
if (updated_handle) {
*updated_handle = new_handle;
}
free(fullpath);
return RELOAD_SUCCESS;
}
/**
* Helper function to print the result of a module reload
*/
static void print_reload_result(ReloadResult result, const char *filename) {
switch (result) {
case RELOAD_SUCCESS:
printf("Successfully reloaded module: %s\n", filename);
break;
case RELOAD_NOT_FOUND:
printf("Module not found: %s\n", filename);
break;
case RELOAD_CLOSE_ERROR:
printf("Error closing module: %s\n", filename);
break;
case RELOAD_OPEN_ERROR:
printf("Error reopening module: %s\n", filename);
break;
default:
printf("Unknown error reloading module: %s\n", filename);
}
}
ReloadResult he_reload_module(const char *module_name) {
assert(module_infos);
void *new_handle = NULL;
ReloadResult result = reload_module(module_infos, module_name, &new_handle);
print_reload_result(result, module_name);
return result;
}
void he_print_module_infos() {
assert(module_infos);
const ModuleInfos *modules = module_infos;
if (!modules) {
printf("No module information available.\n");
return;
}
printf("Found %zu loaded modules:\n\n", modules->count);
for (size_t i = 0; i < modules->count; i++) {
// Get handle information where possible
Dl_info info = {0};
int has_info = 0;
printf("%s: %p\n", modules->names[i], modules->handles[i]);
if (modules->symbols) {
for (int j = 0; j < modules->symbols->count; j++) {
const void *addr = modules->symbols->addresses[j];
const char *name = modules->symbols->names[j];
printf(" %p: %s\n", addr, name);
}
}
printf("\n");
}
}

115
heload/src/symbols.c Normal file
View File

@@ -0,0 +1,115 @@
#define _GNU_SOURCE
#include "heload/symbols.h"
#include <dlfcn.h>
#include <elf.h>
#include <link.h>
#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
/**
* Gathers and populates symbols, given a dynamic module info
*
* Will clear and free the given SymbolInfo struct. Allocates enough memory to
* hold found symbols.
*/
CreateResult he_create_symbol_info(SymbolInfos *symbols,
struct dl_phdr_info *info) {
if (!symbols)
return CREATE_FAILED;
he_free_symbol_info(symbols);
for (int i = 0; i < info->dlpi_phnum; i++) {
const ElfW(Phdr) *phdr = &info->dlpi_phdr[i];
// Look for the dynamic segment
if (phdr->p_type != PT_DYNAMIC)
continue;
printf("Dynamic Header:\n");
printf("p_type: %u\n", phdr->p_type);
printf("p_flags: %u\n", phdr->p_flags);
printf("p_offset: %#06x\n", phdr->p_offset);
printf("p_vaddr: %#06x\n", phdr->p_vaddr);
printf("p_paddr: %#06x\n", phdr->p_paddr);
printf("p_filesz: %zu\n", phdr->p_filesz);
printf("p_memsz: %zu\n", phdr->p_memsz);
printf("p_align: %zu\n", phdr->p_align);
const ElfW(Dyn) *dyn = (const ElfW(Dyn) *)(info->dlpi_addr + phdr->p_vaddr);
const char *strtab = NULL;
const ElfW(Sym) *symtab = NULL;
size_t symtab_size = 0;
size_t strtab_size = 0;
// Parse the dynamic table
for (; dyn->d_tag != DT_NULL; dyn++) {
if (dyn->d_tag == DT_STRTAB) {
strtab = (const char *)(dyn->d_un.d_ptr);
} else if (dyn->d_tag == DT_STRSZ) {
strtab_size = dyn->d_un.d_val;
} else if (dyn->d_tag == DT_SYMTAB) {
symtab = (const ElfW(Sym) *)(dyn->d_un.d_ptr);
} else if (dyn->d_tag == DT_SYMENT) {
symtab_size = dyn->d_un.d_val;
}
}
// Ensure we found the symbol and string tables
if (!strtab || !symtab || strtab_size == 0 || symtab_size == 0) {
fprintf(stderr, "Failed to find symbol or string table in %s\n",
info->dlpi_name);
return CREATE_FAILED;
}
symbols->capacity = symtab_size / sizeof(ElfW(Sym));
symbols->names = calloc(symbols->capacity, sizeof(char *));
if (!symbols->names) {
fprintf(stderr, "Failed to allocate memory for symbol names.\n");
return CREATE_FAILED;
}
symbols->addresses = calloc(symbols->capacity, sizeof(void *));
if (!symbols->addresses) {
fprintf(stderr, "Failed to allocate memory for symbol addresses.\n");
return CREATE_FAILED;
}
// Iterate over the symbol table
for (const ElfW(Sym) *sym = symtab;
(const char *)sym < (const char *)symtab + symtab_size; sym++) {
if (ELF64_ST_TYPE(sym->st_info) == STT_FUNC ||
ELF64_ST_TYPE(sym->st_info) == STT_OBJECT) {
const char *name = strdup(&strtab[sym->st_name]);
void *address = (void *)(info->dlpi_addr + sym->st_value);
// Store the symbol information in the struct of arrays
if (symbols->count < symbols->capacity) {
symbols->names[symbols->count] = (char *)name;
symbols->addresses[symbols->count] = address;
symbols->count++;
} else {
fprintf(stderr, "Symbol table capacity exceeded!\n");
return CREATE_FAILED;
}
}
}
}
return CREATE_SUCCESS;
}
void he_free_symbol_info(SymbolInfos *symbols) {
for (size_t i = 0; i < symbols->count; i++) {
free(symbols->names[i]);
}
free(symbols->names);
free(symbols->addresses);
symbols->count = 0;
symbols->capacity = 0;
}

View File

@@ -5,8 +5,6 @@ add_library(hiisi-engine SHARED
hiisi.h hiisi.h
hiisi.cpp) hiisi.cpp)
target_compile_options(hiisi-engine PRIVATE "-Wall" "-Wextra" "-Wpedantic")
set_property(TARGET hiisi-engine PROPERTY POSITION_INDEPENDENT_CODE ON) set_property(TARGET hiisi-engine PROPERTY POSITION_INDEPENDENT_CODE ON)
set_property(TARGET hiisi-engine PROPERTY LIBRARY_OUTPUT_PATH ${CMAKE_BINARY_DIR}) set_property(TARGET hiisi-engine PROPERTY LIBRARY_OUTPUT_PATH ${CMAKE_BINARY_DIR})

View File

@@ -6,11 +6,10 @@
#define WINDOW_WIDTH 1280 #define WINDOW_WIDTH 1280
#define WINDOW_HEIGHT 720 #define WINDOW_HEIGHT 720
namespace hiisi {
/* This function runs once at startup. */ /* This function runs once at startup. */
int HiisiEngine::init(int argc, char *argv[]) { int init(EngineData *state, int argc, char *argv[])
(void)argc; {
(void)argv;
SDL_SetAppMetadata("Hiisi Engine", "0.0.1", "me.kitemoonsche.hiisi-engine"); SDL_SetAppMetadata("Hiisi Engine", "0.0.1", "me.kitemoonsche.hiisi-engine");
SDL_Window *window = 0; SDL_Window *window = 0;
@@ -21,20 +20,20 @@ int HiisiEngine::init(int argc, char *argv[]) {
return SDL_APP_FAILURE; return SDL_APP_FAILURE;
} }
if (!SDL_CreateWindowAndRenderer("Hiisi Engine", WINDOW_WIDTH, if (!SDL_CreateWindowAndRenderer("Hiisi Engine", WINDOW_WIDTH, WINDOW_HEIGHT, 0, &window, &renderer)) {
WINDOW_HEIGHT, 0, &window, &renderer)) {
SDL_Log("Couldn't create window/renderer: %s", SDL_GetError()); SDL_Log("Couldn't create window/renderer: %s", SDL_GetError());
return SDL_APP_FAILURE; return SDL_APP_FAILURE;
} }
_window = window; state->window = window;
_renderer = renderer; state->renderer = renderer;
return SDL_APP_CONTINUE; /* carry on with the program! */ return SDL_APP_CONTINUE; /* carry on with the program! */
} }
/* This function runs when a new event (mouse input, keypresses, etc) occurs. */ /* This function runs when a new event (mouse input, keypresses, etc) occurs. */
int HiisiEngine::event() { int event(EngineData* state)
{
SDL_Event event; SDL_Event event;
while (SDL_PollEvent(&event)) { while (SDL_PollEvent(&event)) {
if (event.type == SDL_EVENT_QUIT) { if (event.type == SDL_EVENT_QUIT) {
@@ -45,21 +44,19 @@ int HiisiEngine::event() {
} }
/* This function runs once per frame, and is the heart of the program. */ /* This function runs once per frame, and is the heart of the program. */
int HiisiEngine::iterate() { int iterate(EngineData *state)
SDL_Renderer *renderer = (SDL_Renderer *)_renderer; {
SDL_Renderer *renderer = (SDL_Renderer*)state->renderer;
SDL_FRect rects[16]; SDL_FRect rects[16];
const Uint64 now = SDL_GetTicks(); const Uint64 now = SDL_GetTicks();
size_t i; int i;
/* we'll have the rectangles grow and shrink over a few seconds. */ /* we'll have the rectangles grow and shrink over a few seconds. */
const float direction = ((now % 2000) >= 1000) ? 1.0f : -1.0f; const float direction = ((now % 2000) >= 1000) ? 1.0f : -1.0f;
const float scale = const float scale = ((float) (((int) (now % 1000)) - 500) / 50.0f) * direction;
((float)(((int)(now % 1000)) - 500) / 500.0f) * direction;
/* as you can see from this, rendering draws over whatever was drawn before /* as you can see from this, rendering draws over whatever was drawn before it. */
* it. */ SDL_SetRenderDrawColor(renderer, 0, 0, 0, SDL_ALPHA_OPAQUE); /* black, full alpha */
SDL_SetRenderDrawColor(renderer, 0, 0, 0,
SDL_ALPHA_OPAQUE); /* black, full alpha */
SDL_RenderClear(renderer); /* start with a blank canvas. */ SDL_RenderClear(renderer); /* start with a blank canvas. */
/* Rectangles are comprised of set of X and Y coordinates, plus width and /* Rectangles are comprised of set of X and Y coordinates, plus width and
@@ -68,10 +65,9 @@ int HiisiEngine::iterate() {
pretty standard in 2D graphics. */ pretty standard in 2D graphics. */
/* Let's draw a single rectangle (square, really). */ /* Let's draw a single rectangle (square, really). */
rects[0].x = rects[0].y = 100; rects[0].x = rects[0].y = 10;
rects[0].w = rects[0].h = 100 + (100 * scale); rects[0].w = rects[0].h = 100 + (100 * scale);
SDL_SetRenderDrawColor(renderer, 250, 0, 255, SDL_SetRenderDrawColor(renderer, 250, 0, 255, SDL_ALPHA_OPAQUE); /* red, full alpha */
SDL_ALPHA_OPAQUE); /* red, full alpha */
SDL_RenderRect(renderer, &rects[0]); SDL_RenderRect(renderer, &rects[0]);
/* Now let's draw several rectangles with one function call. */ /* Now let's draw several rectangles with one function call. */
@@ -81,20 +77,18 @@ int HiisiEngine::iterate() {
rects[i].x = (WINDOW_WIDTH - rects[i].w) / 2; /* center it. */ rects[i].x = (WINDOW_WIDTH - rects[i].w) / 2; /* center it. */
rects[i].y = (WINDOW_HEIGHT - rects[i].h) / 2; /* center it. */ rects[i].y = (WINDOW_HEIGHT - rects[i].h) / 2; /* center it. */
} }
SDL_SetRenderDrawColor(renderer, 255, 255, 0, SDL_SetRenderDrawColor(renderer, 255, 255, 0, SDL_ALPHA_OPAQUE); /* green, full alpha */
SDL_ALPHA_OPAQUE); /* green, full alpha */
SDL_RenderRects(renderer, rects, 3); /* draw three rectangles at once */ SDL_RenderRects(renderer, rects, 3); /* draw three rectangles at once */
/* those were rectangle _outlines_, really. You can also draw _filled_ /* those were rectangle _outlines_, really. You can also draw _filled_ rectangles! */
* rectangles! */
rects[0].x = 400; rects[0].x = 400;
rects[0].y = 50; rects[0].y = 50;
rects[0].w = 100 + (100 * scale); rects[0].w = 100 + (100 * scale);
rects[0].h = 50 + (50 * scale); rects[0].h = 50 + (50 * scale);
SDL_SetRenderDrawColor(renderer, 50, 25, 255, SDL_SetRenderDrawColor(renderer, 50, 25, 255, SDL_ALPHA_OPAQUE); /* blue, full alpha */
SDL_ALPHA_OPAQUE); /* blue, full alpha */
SDL_RenderFillRect(renderer, &rects[0]); SDL_RenderFillRect(renderer, &rects[0]);
/* ...and also fill a bunch of rectangles at once... */ /* ...and also fill a bunch of rectangles at once... */
for (i = 0; i < SDL_arraysize(rects); i++) { for (i = 0; i < SDL_arraysize(rects); i++) {
const float w = (float) (WINDOW_WIDTH / SDL_arraysize(rects)); const float w = (float) (WINDOW_WIDTH / SDL_arraysize(rects));
@@ -104,8 +98,7 @@ int HiisiEngine::iterate() {
rects[i].w = w; rects[i].w = w;
rects[i].h = h; rects[i].h = h;
} }
SDL_SetRenderDrawColor(renderer, 25, 255, 255, SDL_SetRenderDrawColor(renderer, 25, 255, 255, SDL_ALPHA_OPAQUE); /* white, full alpha */
SDL_ALPHA_OPAQUE); /* white, full alpha */
SDL_RenderFillRects(renderer, rects, SDL_arraysize(rects)); SDL_RenderFillRects(renderer, rects, SDL_arraysize(rects));
SDL_RenderPresent(renderer); /* put it all on the screen! */ SDL_RenderPresent(renderer); /* put it all on the screen! */
@@ -114,5 +107,8 @@ int HiisiEngine::iterate() {
} }
/* This function runs once at shutdown. */ /* This function runs once at shutdown. */
void HiisiEngine::quit() { /* SDL will clean up the window/renderer for us. */ void quit(EngineData *state)
{
/* SDL will clean up the window/renderer for us. */
}
} }

View File

@@ -1,17 +1,17 @@
#ifndef HIISI_H_ #ifndef HIISI_H_
#define HIISI_H_ #define HIISI_H_
class HiisiEngine { namespace hiisi {
public:
int init(int argc, char *argv[]);
int event();
int iterate();
void quit();
private: struct EngineData {
void *_window = 0; void *window = 0;
void *_renderer = 0; void *renderer = 0;
void *_module = 0;
}; };
int init(EngineData *state, int argc, char *argv[]);
int event(EngineData *state);
int iterate(EngineData *state);
void quit(EngineData *state);
} // namespace hiisi
#endif // HIISI_H_ #endif // HIISI_H_

View File

@@ -1,37 +1,50 @@
#include "hiisi.h" #include "hiisi.h"
#include "heload/heload.h"
#include "hiload.h"
#include <assert.h> #include <assert.h>
#include <atomic> #include <stdio.h>
#include <stdlib.h>
#include <dlfcn.h> #include <dlfcn.h>
#include <link.h> #include <link.h>
#include <signal.h> #include <signal.h>
#include <stdio.h> #include <atomic>
#include <stdlib.h>
std::atomic<bool> reload_requested(false);
void reload_signal_handler(int) {
reload_requested = true;
}
void reload_modules() {
if (he_reload_module("libhiisi-engine.so") != 0) {
fprintf(stderr, "Failed to reload libhiisi-engine.so. Exiting.\n");
exit(EXIT_FAILURE);
}
}
int main(int argc, char **argv) { int main(int argc, char **argv) {
/* Hiload hotreloader is compiled as a shared library, these are the signal(SIGUSR1, reload_signal_handler);
* only required changes for the code to successfully reload */
// Init hiload if (he_init() != 0) {
const char *himodules[] = {"", "libhiisi-engine.so"}; fprintf(stderr, "Failed to initialize heload. Exiting.\n");
hi_init(2, himodules); exit(EXIT_FAILURE);
HiisiEngine hiisi;
hiisi.init(argc, argv);
while (hiisi.event()) {
hiisi.iterate();
// Reload changes
hi_reload();
} }
hiisi.quit(); he_print_module_infos();
hiisi::EngineData state;
hiisi::init(&state, argc, argv);
while (hiisi::event(&state)) {
hiisi::iterate(&state);
if (reload_requested) {
reload_modules();
reload_requested = false;
}
}
he_deinit();
// Deinit hiload
hi_deinit();
return 0; return 0;
} }

1
hiload

Submodule hiload deleted from c7ff4f8813

11
live-reloader.sh Executable file
View File

@@ -0,0 +1,11 @@
#!/usr/bin/env bash
echo "Watching changes in hiisi..."
inotifywait --monitor -e modify build/hiisi/ |
while read -r _ action file; do
sleep 0.1
echo "$(date +%H:%M:%S): Hiisi changed!"
echo "$action on $file"
HIISI_PID=$(pgrep -x hiisi-run)
kill -USR1 "${HIISI_PID}" && echo "Signal sent!" || echo "Signal send failed!"
done