Compare commits

..

10 Commits

Author SHA1 Message Date
fb763cdf64 heload, a mess 2025-03-16 17:45:09 +02:00
5d362d4237 add heload, mid commit 2025-03-13 21:21:14 +02:00
Kasper Sauramo
e84760fb8f make sdl unremovable 2025-03-13 17:28:48 +02:00
Kasper Sauramo
4a393035a2 add cache/ 2025-03-13 17:28:16 +02:00
2c90324d18 use shared sdl 2025-03-13 10:54:51 +02:00
ab18ed1d4a not quite yet working live reload 2025-03-13 00:52:35 +02:00
d2fcb9a24a move engine to shared lib 2025-03-13 00:09:05 +02:00
cf7e577b42 add build/ to gitignore 2025-03-11 23:12:28 +02:00
d7701d98b1 add gitignore 2025-03-11 23:09:59 +02:00
b4d3e20e88 add SDL 2025-03-11 23:02:25 +02:00
18 changed files with 882 additions and 41 deletions

48
.gitignore vendored
View File

@@ -1,3 +1,8 @@
# Hiisi
build/
compile_commands.json
.cache/
# ---> Emacs # ---> Emacs
# -*- mode: gitignore; -*- # -*- mode: gitignore; -*-
*~ *~
@@ -9,46 +14,6 @@ auto-save-list
tramp tramp
.\#* .\#*
# Org-mode
.org-id-locations
*_archive
# flymake-mode
*_flymake.*
# eshell files
/eshell/history
/eshell/lastdir
# elpa packages
/elpa/
# reftex files
*.rel
# AUCTeX auto folder
/auto/
# cask packages
.cask/
dist/
# Flycheck
flycheck_*.el
# server auth directory
/server/
# projectiles files
.projectile
# directory configuration
.dir-locals.el
# network security
/network-security.data
# ---> Linux # ---> Linux
*~ *~
@@ -64,3 +29,6 @@ flycheck_*.el
# .nfs files are created when an open file is removed but is still being accessed # .nfs files are created when an open file is removed but is still being accessed
.nfs* .nfs*
.idea
*.log
tmp/

3
.gitmodules vendored Normal file
View File

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

1
3rd/SDL Submodule

Submodule 3rd/SDL added at 9f32fafe21

22
CMakeLists.txt Normal file
View File

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

View File

@@ -1,3 +1,14 @@
# Hiisi # Hiisi
One more Hiisi-engine One more Hiisi-engine.
```bash
git clone --recurse-submodules -j8 git@git.kitemoonsche.me:schme/Hiisi.git
```
## Build
```bash
cmake -B build -S . -G Ninja
cmake --build build
```

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;
}

11
hiisi/CMakeLists.txt Normal file
View File

@@ -0,0 +1,11 @@
cmake_minimum_required(VERSION 3.21)
project(HiisiEngine)
add_library(hiisi-engine SHARED
hiisi.h
hiisi.cpp)
set_property(TARGET hiisi-engine PROPERTY POSITION_INDEPENDENT_CODE ON)
set_property(TARGET hiisi-engine PROPERTY LIBRARY_OUTPUT_PATH ${CMAKE_BINARY_DIR})
target_link_libraries(hiisi-engine SDL3::SDL3-shared)

114
hiisi/hiisi.cpp Normal file
View File

@@ -0,0 +1,114 @@
#include "hiisi.h"
#define SDL_MAIN_USE_CALLBACKS 1 /* use the callbacks instead of main() */
#include <SDL3/SDL.h>
#define WINDOW_WIDTH 1280
#define WINDOW_HEIGHT 720
namespace hiisi {
/* This function runs once at startup. */
int init(EngineData *state, int argc, char *argv[])
{
SDL_SetAppMetadata("Hiisi Engine", "0.0.1", "me.kitemoonsche.hiisi-engine");
SDL_Window *window = 0;
SDL_Renderer *renderer = 0;
if (!SDL_Init(SDL_INIT_VIDEO)) {
SDL_Log("Couldn't initialize SDL: %s", SDL_GetError());
return SDL_APP_FAILURE;
}
if (!SDL_CreateWindowAndRenderer("Hiisi Engine", WINDOW_WIDTH, WINDOW_HEIGHT, 0, &window, &renderer)) {
SDL_Log("Couldn't create window/renderer: %s", SDL_GetError());
return SDL_APP_FAILURE;
}
state->window = window;
state->renderer = renderer;
return SDL_APP_CONTINUE; /* carry on with the program! */
}
/* This function runs when a new event (mouse input, keypresses, etc) occurs. */
int event(EngineData* state)
{
SDL_Event event;
while (SDL_PollEvent(&event)) {
if (event.type == SDL_EVENT_QUIT) {
return 0; /* end the program, reporting success to the OS. */
}
}
return 1; /* carry on with the program! */
}
/* This function runs once per frame, and is the heart of the program. */
int iterate(EngineData *state)
{
SDL_Renderer *renderer = (SDL_Renderer*)state->renderer;
SDL_FRect rects[16];
const Uint64 now = SDL_GetTicks();
int i;
/* we'll have the rectangles grow and shrink over a few seconds. */
const float direction = ((now % 2000) >= 1000) ? 1.0f : -1.0f;
const float scale = ((float) (((int) (now % 1000)) - 500) / 50.0f) * direction;
/* as you can see from this, rendering draws over whatever was drawn before it. */
SDL_SetRenderDrawColor(renderer, 0, 0, 0, SDL_ALPHA_OPAQUE); /* black, full alpha */
SDL_RenderClear(renderer); /* start with a blank canvas. */
/* Rectangles are comprised of set of X and Y coordinates, plus width and
height. (0, 0) is the top left of the window, and larger numbers go
down and to the right. This isn't how geometry works, but this is
pretty standard in 2D graphics. */
/* Let's draw a single rectangle (square, really). */
rects[0].x = rects[0].y = 10;
rects[0].w = rects[0].h = 100 + (100 * scale);
SDL_SetRenderDrawColor(renderer, 250, 0, 255, SDL_ALPHA_OPAQUE); /* red, full alpha */
SDL_RenderRect(renderer, &rects[0]);
/* Now let's draw several rectangles with one function call. */
for (i = 0; i < 3; i++) {
const float size = (i+1) * 500.0f;
rects[i].w = rects[i].h = size + (size * scale);
rects[i].x = (WINDOW_WIDTH - rects[i].w) / 2; /* center it. */
rects[i].y = (WINDOW_HEIGHT - rects[i].h) / 2; /* center it. */
}
SDL_SetRenderDrawColor(renderer, 255, 255, 0, SDL_ALPHA_OPAQUE); /* green, full alpha */
SDL_RenderRects(renderer, rects, 3); /* draw three rectangles at once */
/* those were rectangle _outlines_, really. You can also draw _filled_ rectangles! */
rects[0].x = 400;
rects[0].y = 50;
rects[0].w = 100 + (100 * scale);
rects[0].h = 50 + (50 * scale);
SDL_SetRenderDrawColor(renderer, 50, 25, 255, SDL_ALPHA_OPAQUE); /* blue, full alpha */
SDL_RenderFillRect(renderer, &rects[0]);
/* ...and also fill a bunch of rectangles at once... */
for (i = 0; i < SDL_arraysize(rects); i++) {
const float w = (float) (WINDOW_WIDTH / SDL_arraysize(rects));
const float h = i * 8.0f;
rects[i].x = i * w;
rects[i].y = WINDOW_HEIGHT - h;
rects[i].w = w;
rects[i].h = h;
}
SDL_SetRenderDrawColor(renderer, 25, 255, 255, SDL_ALPHA_OPAQUE); /* white, full alpha */
SDL_RenderFillRects(renderer, rects, SDL_arraysize(rects));
SDL_RenderPresent(renderer); /* put it all on the screen! */
return SDL_APP_CONTINUE; /* carry on with the program! */
}
/* This function runs once at shutdown. */
void quit(EngineData *state)
{
/* SDL will clean up the window/renderer for us. */
}
}

17
hiisi/hiisi.h Normal file
View File

@@ -0,0 +1,17 @@
#ifndef HIISI_H_
#define HIISI_H_
namespace hiisi {
struct EngineData {
void *window = 0;
void *renderer = 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_

50
hiisi/main.cpp Normal file
View File

@@ -0,0 +1,50 @@
#include "hiisi.h"
#include "heload/heload.h"
#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <dlfcn.h>
#include <link.h>
#include <signal.h>
#include <atomic>
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) {
signal(SIGUSR1, reload_signal_handler);
if (he_init() != 0) {
fprintf(stderr, "Failed to initialize heload. Exiting.\n");
exit(EXIT_FAILURE);
}
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();
return 0;
}

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