ona/source/app.cpp

283 lines
6.8 KiB
C++
Raw Normal View History

2023-02-19 17:45:40 +01:00
module;
#include <SDL2/SDL.h>
2023-02-18 04:34:40 +01:00
export module app;
import core;
2023-02-19 17:45:40 +01:00
import core.files;
2023-02-18 04:34:40 +01:00
import core.image;
2023-02-19 17:47:16 +01:00
import core.math;
2023-02-18 04:34:40 +01:00
2023-02-19 17:45:40 +01:00
import oar;
2023-02-18 04:34:40 +01:00
export namespace app {
2023-02-19 17:45:40 +01:00
struct directory : public core::fs {
using core::fs::access_result;
2023-02-18 04:34:40 +01:00
2023-02-19 17:45:40 +01:00
struct rules {
bool can_read;
2023-02-18 04:34:40 +01:00
2023-02-19 17:45:40 +01:00
bool can_write;
};
2023-02-18 04:34:40 +01:00
2023-02-19 17:45:40 +01:00
directory() : path_buffer{0} {}
2023-02-18 04:34:40 +01:00
2023-02-19 17:45:40 +01:00
access_result read_file(core::path const & file_path,
core::callable<void(core::readable const &)> const & then) {
2023-02-18 04:34:40 +01:00
2023-02-19 17:45:40 +01:00
if (this->prefix_length == 0) return access_result::not_found;
2023-02-18 04:34:40 +01:00
2023-02-19 17:45:40 +01:00
if (!this->access_rules.can_read) return access_result::access_denied;
2023-02-18 04:34:40 +01:00
2023-02-19 17:45:40 +01:00
::SDL_RWops * rw_ops = this->open_rw(file_path, {.can_read = true});
2023-02-18 04:34:40 +01:00
2023-02-19 17:45:40 +01:00
if (rw_ops == nullptr) return access_result::not_found;
then([rw_ops](core::slice<uint8_t> const & buffer) -> size_t {
return ::SDL_RWread(rw_ops, buffer.pointer, sizeof(uint8_t), buffer.length);
});
2023-02-18 04:34:40 +01:00
2023-02-19 17:45:40 +01:00
::SDL_RWclose(rw_ops);
return access_result::ok;
2023-02-18 04:34:40 +01:00
}
2023-02-19 17:45:40 +01:00
void target(core::slice<char const> const & directory_path, rules const & access_rules) {
this->access_rules = access_rules;
this->prefix_length = core::min(directory_path.length, path_max - 1);
2023-02-18 04:34:40 +01:00
2023-02-19 17:45:40 +01:00
{
core::slice const path_buffer_slice{this->path_buffer};
2023-02-18 04:34:40 +01:00
2023-02-19 17:45:40 +01:00
core::copy(path_buffer_slice.sliced(0, this->prefix_length),
directory_path.sliced(0, this->prefix_length).as_bytes());
2023-02-18 04:34:40 +01:00
2023-02-19 17:45:40 +01:00
core::zero(path_buffer_slice.sliced(this->prefix_length, path_max - this->prefix_length));
}
2023-02-18 04:34:40 +01:00
}
2023-02-19 17:45:40 +01:00
access_result write_file(core::path const & file_path,
core::callable<void(core::writable const &)> const & then) {
if (this->prefix_length == 0) return access_result::not_found;
if (!this->access_rules.can_write) return access_result::access_denied;
::SDL_RWops * rw_ops = this->open_rw(file_path, {.can_write = true});
if (rw_ops == nullptr) return access_result::not_found;
then([rw_ops](core::slice<uint8_t const> const & buffer) -> size_t {
return ::SDL_RWwrite(rw_ops, buffer.pointer, sizeof(uint8_t), buffer.length);
});
::SDL_RWclose(rw_ops);
return access_result::ok;
2023-02-18 04:34:40 +01:00
}
2023-02-19 17:45:40 +01:00
private:
static constexpr core::usize path_max = 4096;
rules access_rules;
core::usize prefix_length;
core::u8 path_buffer[path_max];
::SDL_RWops * open_rw(core::path const & file_path, rules const & file_rules) {
core::u8 * const path_begin = this->path_buffer + this->prefix_length;
core::slice const path_remaining =
{path_begin, path_begin + (path_max - this->prefix_length) - 1};
if (path_remaining.length < file_path.byte_size()) return nullptr;
core::copy(path_remaining, core::slice{file_path.begin(), file_path.end()}.as_bytes());
return ::SDL_RWFromFile(reinterpret_cast<char const *>(this->path_buffer), "r");
}
2023-02-18 04:34:40 +01:00
};
enum class log_level {
notice,
warning,
error,
};
struct system {
2023-02-19 17:45:40 +01:00
system(core::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 {
core::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) {
core::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);
}
}
core::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;
}
core::fs & res_fs() {
return this->res_archive;
}
void log(app::log_level level, core::slice<char const> const & message) {
core::i32 const length = static_cast<core::i32>(
core::min(message.length, static_cast<size_t>(core::i32_max)));
::SDL_LogMessage(SDL_LOG_CATEGORY_APPLICATION,
SDL_LOG_PRIORITY_INFO, "%.*s", length, message.pointer);
}
core::allocator & thread_safe_allocator() {
return this->allocator;
}
core::fs & user_fs() {
return this->user_directory;
}
2023-02-18 04:34:40 +01:00
2023-02-19 17:45:40 +01:00
private:
::SDL_Event sdl_event;
2023-02-18 04:34:40 +01:00
2023-02-19 17:45:40 +01:00
struct : public core::allocator {
core::u8 * reallocate(core::u8 * maybe_allocation, core::usize requested_size) override {
return reinterpret_cast<core::u8 *>(::SDL_malloc(requested_size));
}
void deallocate(void * allocation) override {
::SDL_free(allocation);
}
} allocator;
2023-02-18 04:34:40 +01:00
2023-02-19 17:45:40 +01:00
oar::archive res_archive;
directory cwd_directory;
directory user_directory;
2023-02-18 04:34:40 +01:00
};
struct graphics {
2023-02-19 17:45:40 +01:00
enum class [[nodiscard]] show_result {
ok,
2023-02-18 04:34:40 +01:00
out_of_memory,
};
struct canvas {
core::color background_color;
};
2023-02-19 17:45:40 +01:00
graphics(core::path const & title) {
this->retitle(title);
}
2023-02-18 04:34:40 +01:00
2023-02-19 17:45:40 +01:00
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());
2023-02-18 04:34:40 +01:00
2023-02-19 17:45:40 +01:00
SDL_RenderClear(this->sdl_renderer);
}
}
2023-02-18 04:34:40 +01:00
2023-02-19 17:45:40 +01:00
void retitle(core::path const & title) {
this->title = title;
if (this->sdl_window != nullptr)
::SDL_SetWindowTitle(this->sdl_window, this->title.begin());
}
show_result show(core::u16 physical_width, core::u16 physical_height) {
if (this->sdl_window == nullptr) {
constexpr int sdl_windowpos = SDL_WINDOWPOS_UNDEFINED;
constexpr core::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 core::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;
}
void present() {
if (this->sdl_renderer != nullptr) {
::SDL_RenderPresent(this->sdl_renderer);
}
}
private:
core::path title;
::SDL_Window * sdl_window = nullptr;
::SDL_Renderer * sdl_renderer = nullptr;
2023-02-18 04:34:40 +01:00
};
2023-02-19 17:45:40 +01:00
using graphical_runnable = core::callable<int(system &, graphics &)>;
int display(core::path const & title, graphical_runnable const & run) {
system app_system{title};
graphics app_graphics{title};
return run(app_system, app_graphics);
}
2023-02-18 04:34:40 +01:00
}