277 lines
6.6 KiB
C++
277 lines
6.6 KiB
C++
module;
|
|
|
|
#include <SDL2/SDL.h>
|
|
|
|
export module app;
|
|
|
|
import coral;
|
|
import coral.files;
|
|
import coral.image;
|
|
import coral.math;
|
|
|
|
import oar;
|
|
|
|
export namespace app {
|
|
struct directory : public coral::fs {
|
|
struct rules {
|
|
bool can_read;
|
|
|
|
bool can_write;
|
|
};
|
|
|
|
directory() : path_buffer{0} {}
|
|
|
|
void read_file(coral::path const & file_path,
|
|
coral::callable<void(coral::readable const &)> const & then) {
|
|
|
|
if (this->prefix_length == 0) return;
|
|
|
|
if (!this->access_rules.can_read) return;
|
|
|
|
::SDL_RWops * rw_ops{this->open_rw(file_path, {.can_read = true})};
|
|
|
|
if (rw_ops == nullptr) return;
|
|
|
|
then([rw_ops](coral::slice<uint8_t> const & buffer) -> size_t {
|
|
return ::SDL_RWread(rw_ops, buffer.pointer, sizeof(uint8_t), buffer.length);
|
|
});
|
|
|
|
::SDL_RWclose(rw_ops);
|
|
}
|
|
|
|
void target(coral::slice<char const> const & directory_path, rules const & access_rules) {
|
|
this->access_rules = access_rules;
|
|
this->prefix_length = coral::min(directory_path.length, path_max - 1);
|
|
|
|
{
|
|
coral::slice const path_buffer_slice{this->path_buffer};
|
|
|
|
coral::copy(path_buffer_slice.sliced(0, this->prefix_length),
|
|
directory_path.sliced(0, this->prefix_length).as_bytes());
|
|
|
|
coral::zero(path_buffer_slice.sliced(this->prefix_length, path_max - this->prefix_length));
|
|
}
|
|
}
|
|
|
|
void write_file(coral::path const & file_path,
|
|
coral::callable<void(coral::writable const &)> const & then) {
|
|
|
|
if (this->prefix_length == 0) return;
|
|
|
|
if (!this->access_rules.can_write) return;
|
|
|
|
::SDL_RWops * rw_ops{this->open_rw(file_path, {.can_write = true})};
|
|
|
|
if (rw_ops == nullptr) return;
|
|
|
|
then([rw_ops](coral::slice<uint8_t const> const & buffer) -> size_t {
|
|
return ::SDL_RWwrite(rw_ops, buffer.pointer, sizeof(uint8_t), buffer.length);
|
|
});
|
|
|
|
::SDL_RWclose(rw_ops);
|
|
}
|
|
|
|
private:
|
|
static constexpr coral::usize path_max = 4096;
|
|
|
|
rules access_rules;
|
|
|
|
coral::usize prefix_length;
|
|
|
|
coral::u8 path_buffer[path_max];
|
|
|
|
::SDL_RWops * open_rw(coral::path const & file_path, rules const & file_rules) {
|
|
coral::u8 * const path_begin{this->path_buffer + this->prefix_length};
|
|
|
|
coral::slice const path_remaining =
|
|
{path_begin, path_begin + (path_max - this->prefix_length) - 1};
|
|
|
|
if (path_remaining.length < file_path.byte_size()) return nullptr;
|
|
|
|
coral::copy(path_remaining, coral::slice{file_path.begin(), file_path.end()}.as_bytes());
|
|
|
|
return ::SDL_RWFromFile(reinterpret_cast<char const *>(this->path_buffer), "r");
|
|
}
|
|
};
|
|
|
|
enum class log_level {
|
|
notice,
|
|
warning,
|
|
error,
|
|
};
|
|
|
|
struct system {
|
|
system(coral::path const & title) : res_archive{} {
|
|
constexpr directory::rules read_only_rules = {.can_read = true};
|
|
|
|
{
|
|
char * const path{::SDL_GetBasePath()};
|
|
|
|
if (path == nullptr) {
|
|
this->cwd_directory.target("./", read_only_rules);
|
|
} else {
|
|
coral::usize path_length{0};
|
|
|
|
while (path[path_length] != 0) path_length += 1;
|
|
|
|
if (path_length == 0) {
|
|
this->cwd_directory.target("./", read_only_rules);
|
|
} else {
|
|
this->cwd_directory.target({path, path_length}, read_only_rules);
|
|
}
|
|
}
|
|
|
|
::SDL_free(path);
|
|
}
|
|
|
|
{
|
|
char * const path{::SDL_GetPrefPath("ona", title.begin())};
|
|
|
|
if (path != nullptr) {
|
|
coral::usize path_length{0};
|
|
|
|
while (path[path_length] != 0) path_length += 1;
|
|
|
|
if (path_length != 0) this->cwd_directory.target({path, path_length}, {
|
|
.can_read = true,
|
|
});
|
|
}
|
|
|
|
::SDL_free(path);
|
|
}
|
|
}
|
|
|
|
coral::fs & cwd_fs() {
|
|
return this->cwd_directory;
|
|
}
|
|
|
|
bool poll() {
|
|
while (::SDL_PollEvent(&this->sdl_event) != 0) {
|
|
switch (this->sdl_event.type) {
|
|
case SDL_QUIT: return false;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
coral::fs & res_fs() {
|
|
return this->res_archive;
|
|
}
|
|
|
|
void log(app::log_level level, coral::slice<char const> const & message) {
|
|
coral::i32 const length{static_cast<coral::i32>(
|
|
coral::min(message.length, static_cast<size_t>(coral::i32_max)))};
|
|
|
|
::SDL_LogMessage(SDL_LOG_CATEGORY_APPLICATION,
|
|
SDL_LOG_PRIORITY_INFO, "%.*s", length, message.pointer);
|
|
}
|
|
|
|
coral::allocator & thread_safe_allocator() {
|
|
return this->allocator;
|
|
}
|
|
|
|
coral::fs & user_fs() {
|
|
return this->user_directory;
|
|
}
|
|
|
|
private:
|
|
::SDL_Event sdl_event;
|
|
|
|
struct : public coral::allocator {
|
|
coral::u8 * reallocate(coral::u8 * maybe_allocation, coral::usize requested_size) override {
|
|
return reinterpret_cast<coral::u8 *>(::SDL_malloc(requested_size));
|
|
}
|
|
|
|
void deallocate(void * allocation) override {
|
|
::SDL_free(allocation);
|
|
}
|
|
} allocator;
|
|
|
|
oar::archive res_archive;
|
|
|
|
directory cwd_directory;
|
|
|
|
directory user_directory;
|
|
};
|
|
|
|
struct graphics {
|
|
enum class [[nodiscard]] show_result {
|
|
ok,
|
|
out_of_memory,
|
|
};
|
|
|
|
struct canvas {
|
|
coral::color background_color;
|
|
};
|
|
|
|
graphics(coral::path const & title) {
|
|
this->retitle(title);
|
|
}
|
|
|
|
void present() {
|
|
if (this->sdl_renderer != nullptr) {
|
|
::SDL_RenderPresent(this->sdl_renderer);
|
|
}
|
|
}
|
|
|
|
void render(canvas & source_canvas) {
|
|
if (this->sdl_renderer != nullptr) {
|
|
SDL_SetRenderDrawColor(this->sdl_renderer, source_canvas.background_color.to_r8(),
|
|
source_canvas.background_color.to_g8(), source_canvas.background_color.to_b8(),
|
|
source_canvas.background_color.to_a8());
|
|
|
|
SDL_RenderClear(this->sdl_renderer);
|
|
}
|
|
}
|
|
|
|
void retitle(coral::path const & title) {
|
|
this->title = title;
|
|
|
|
if (this->sdl_window != nullptr)
|
|
::SDL_SetWindowTitle(this->sdl_window, this->title.begin());
|
|
}
|
|
|
|
show_result show(coral::u16 physical_width, coral::u16 physical_height) {
|
|
if (this->sdl_window == nullptr) {
|
|
constexpr int sdl_windowpos = SDL_WINDOWPOS_UNDEFINED;
|
|
constexpr coral::u32 sdl_windowflags = 0;
|
|
|
|
this->sdl_window = ::SDL_CreateWindow(this->title.begin(), sdl_windowpos,
|
|
sdl_windowpos, static_cast<int>(physical_width), static_cast<int>(physical_height),
|
|
sdl_windowflags);
|
|
|
|
if (this->sdl_window == nullptr) return show_result::out_of_memory;
|
|
} else {
|
|
::SDL_ShowWindow(this->sdl_window);
|
|
}
|
|
|
|
if (this->sdl_renderer == nullptr) {
|
|
constexpr coral::u32 sdl_rendererflags = 0;
|
|
|
|
this->sdl_renderer = ::SDL_CreateRenderer(this->sdl_window, -1, sdl_rendererflags);
|
|
|
|
if (this->sdl_renderer == nullptr) return show_result::out_of_memory;
|
|
}
|
|
|
|
return show_result::ok;
|
|
}
|
|
|
|
private:
|
|
coral::path title;
|
|
|
|
::SDL_Window * sdl_window = nullptr;
|
|
|
|
::SDL_Renderer * sdl_renderer = nullptr;
|
|
};
|
|
|
|
using graphical_runnable = coral::callable<int(system &, graphics &)>;
|
|
|
|
int display(coral::path const & title, graphical_runnable const & run) {
|
|
system app_system{title};
|
|
graphics app_graphics{title};
|
|
|
|
return run(app_system, app_graphics);
|
|
}
|
|
}
|