diff --git a/source/app.cpp b/source/app.cpp index 84a3f56..edc539d 100644 --- a/source/app.cpp +++ b/source/app.cpp @@ -1,61 +1,103 @@ +module; + +#include + export module app; import core; +import core.files; import core.image; import core.lalgebra; +import oar; + export namespace app { - struct path { - static constexpr core::usize max = 0xff; + struct directory : public core::fs { + using core::fs::access_result; - static constexpr char seperator = '/'; + struct rules { + bool can_read; - core::u8 buffer[max + 1]; + bool can_write; + }; - static constexpr path empty() { - path empty_path = {0}; + directory() : path_buffer{0} {} - empty_path.buffer[max] = max; + access_result read_file(core::path const & file_path, + core::callable const & then) { - return empty_path; + if (this->prefix_length == 0) return access_result::not_found; + + if (!this->access_rules.can_read) return access_result::access_denied; + + ::SDL_RWops * rw_ops = this->open_rw(file_path, {.can_read = true}); + + if (rw_ops == nullptr) return access_result::not_found; + + then([rw_ops](core::slice const & buffer) -> size_t { + return ::SDL_RWread(rw_ops, buffer.pointer, sizeof(uint8_t), buffer.length); + }); + + ::SDL_RWclose(rw_ops); + + return access_result::ok; } - core::slice as_slice() const { - return {reinterpret_cast(this->buffer), this->size()}; - } + void target(core::slice const & directory_path, rules const & access_rules) { + this->access_rules = access_rules; + this->prefix_length = core::min(directory_path.length, path_max - 1); - constexpr core::usize size() const { - return max - this->buffer[max]; - } + { + core::slice const path_buffer_slice{this->path_buffer}; - core::i16 compare(path const & that) { - return 0; - } + core::copy(path_buffer_slice.sliced(0, this->prefix_length), + directory_path.sliced(0, this->prefix_length).as_bytes()); - bool equals(path const & that) const { - return core::equals(this->as_slice().as_bytes(), that.as_slice().as_bytes()); - } - - constexpr path joined(core::slice const & text) const { - if (text.length > this->buffer[max]) return empty(); - - path joined_path = *this; - - for (char const c : text) { - joined_path.buffer[joined_path.size()] = c; - joined_path.buffer[max] -= 1; + core::zero(path_buffer_slice.sliced(this->prefix_length, path_max - this->prefix_length)); } - - return joined_path; } - core::u64 hash() { - return 0; - } - }; + access_result write_file(core::path const & file_path, + core::callable const & then) { - struct file_store { - virtual void read_file(app::path const & file_path, core::callable const & then) = 0; + 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 const & buffer) -> size_t { + return ::SDL_RWwrite(rw_ops, buffer.pointer, sizeof(uint8_t), buffer.length); + }); + + ::SDL_RWclose(rw_ops); + + return access_result::ok; + } + + 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(this->path_buffer), "r"); + } }; enum class log_level { @@ -65,18 +107,103 @@ export namespace app { }; struct system { - virtual bool poll() = 0; + system(core::path const & title) : res_archive{} { + constexpr directory::rules read_only_rules = {.can_read = true}; - virtual file_store & bundle() = 0; + { + char * const path = ::SDL_GetBasePath(); - virtual void log(log_level level, core::slice const & message) = 0; + if (path == nullptr) { + this->cwd_directory.target("./", read_only_rules); + } else { + core::usize path_length = 0; - virtual core::allocator & thread_safe_allocator() = 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 const & message) { + core::i32 const length = static_cast( + core::min(message.length, static_cast(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; + } + + private: + ::SDL_Event sdl_event; + + struct : public core::allocator { + core::u8 * reallocate(core::u8 * maybe_allocation, core::usize requested_size) override { + return reinterpret_cast(::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 show_error { - none, + enum class [[nodiscard]] show_result { + ok, out_of_memory, }; @@ -84,12 +211,72 @@ export namespace app { core::color background_color; }; - virtual void render(canvas & source_canvas) = 0; + graphics(core::path const & title) { + this->retitle(title); + } - virtual void present() = 0; + 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()); - virtual show_error show(core::u16 physical_width, core::u16 physical_height) = 0; + SDL_RenderClear(this->sdl_renderer); + } + } - virtual void retitle(core::slice const & updated_title) = 0; + 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(physical_width), static_cast(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; }; + + using graphical_runnable = core::callable; + + int display(core::path const & title, graphical_runnable const & run) { + system app_system{title}; + graphics app_graphics{title}; + + return run(app_system, app_graphics); + } } diff --git a/source/app/sdl.cpp b/source/app/sdl.cpp deleted file mode 100644 index c332c6b..0000000 --- a/source/app/sdl.cpp +++ /dev/null @@ -1,167 +0,0 @@ -module; - -#include - -export module app.sdl; - -import app; - -import core; -import core.image; -import core.sequence; -import core.lalgebra; - -struct bundled_file_store : public app::file_store { - void read_file(app::path const & file_path, core::callable const & then) override { - constexpr core::slice path_prefix = "./"; - constexpr core::usize path_max = 512; - - if ((file_path.size() + path_prefix.length) > path_max) core::unreachable(); - - core::stack path_buffer{&core::null_allocator()}; - - if (path_buffer.append("./") != core::append_result::ok) core::unreachable(); - - // File path is guaranteed to be null-terminated. - if (path_buffer.append(file_path.as_slice()) != core::append_result::ok) - core::unreachable(); - - SDL_RWops * rw_ops = ::SDL_RWFromFile(path_buffer.begin(), "r"); - - if (rw_ops == nullptr) return; - - then([rw_ops](core::slice const & buffer) -> size_t { - return ::SDL_RWread(rw_ops, buffer.pointer, sizeof(uint8_t), buffer.length); - }); - - ::SDL_RWclose(rw_ops); - } -}; - -struct sdl_allocator : public core::allocator { - core::u8 * reallocate(core::u8 * maybe_allocation, core::usize requested_size) override { - return reinterpret_cast(::SDL_malloc(requested_size)); - } - - void deallocate(void * allocation) override { - ::SDL_free(allocation); - } -}; - -struct sdl_system : public app::system { - private: - ::SDL_Event sdl_event; - - sdl_allocator allocator; - - bundled_file_store bundled_store; - - public: - sdl_system() : - sdl_event{0}, - allocator{} {} - - bool poll() override { - while (::SDL_PollEvent(&this->sdl_event) != 0) { - switch (this->sdl_event.type) { - case SDL_QUIT: return false; - } - } - - return true; - } - - app::file_store & bundle() override { - return this->bundled_store; - } - - void log(app::log_level level, core::slice const & message) override { - core::i32 const length = static_cast( - core::min(message.length, static_cast(core::i32_max))); - - ::SDL_LogMessage(SDL_LOG_CATEGORY_APPLICATION, - SDL_LOG_PRIORITY_INFO, "%.*s", length, message.pointer); - } - - core::allocator & thread_safe_allocator() override { - return this->allocator; - } -}; - -struct sdl_graphics : public app::graphics { - static constexpr core::usize title_maximum = 128; - - core::u32 title_length; - - char title_buffer[title_maximum]; - - ::SDL_Window * sdl_window = nullptr; - - ::SDL_Renderer * sdl_renderer = nullptr; - - sdl_graphics(core::slice const & title) { - this->retitle(title); - } - - void render(canvas & source_canvas) override { - 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(core::slice const & title) override { - this->title_length = core::min(title.length, title_maximum - 1); - - for (core::usize i = 0; i < this->title_length; i += 1) title_buffer[i] = title[i]; - - for (core::usize i = this->title_length; i < title_maximum; i += 1) title_buffer[i] = 0; - - if (this->sdl_window != nullptr) ::SDL_SetWindowTitle(this->sdl_window, title_buffer); - } - - app::graphics::show_error show(core::u16 physical_width, core::u16 physical_height) override { - if (this->sdl_window == nullptr) { - constexpr int sdl_windowpos = SDL_WINDOWPOS_UNDEFINED; - constexpr core::u32 sdl_windowflags = 0; - - this->sdl_window = ::SDL_CreateWindow(title_buffer, sdl_windowpos, sdl_windowpos, - static_cast(physical_width), static_cast(physical_height), - sdl_windowflags); - - if (this->sdl_window == nullptr) return show_error::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_error::out_of_memory; - } - - return show_error::none; - } - - void present() override { - if (this->sdl_renderer != nullptr) { - ::SDL_RenderPresent(this->sdl_renderer); - } - } -}; - -export namespace app { - int display(core::slice const & title, - core::callable const & run) { - - sdl_system system; - sdl_graphics graphics(title); - - return run(system, graphics); - } -} diff --git a/source/runtime.cpp b/source/runtime.cpp index a933521..bed15e1 100644 --- a/source/runtime.cpp +++ b/source/runtime.cpp @@ -1,9 +1,9 @@ export module runtime; import app; -import app.sdl; import core; +import core.files; import core.lalgebra; import core.sequence; @@ -12,13 +12,14 @@ import kym.environment; extern "C" int main(int argc, char const * const * argv) { return app::display("Ona Runtime", [](app::system & system, app::graphics & graphics) -> int { - constexpr app::path config_path = app::path::empty().joined("config.kym"); - bool is_config_loaded = false; + constexpr core::path config_path{"config.kym"}; + bool is_config_loaded{false}; - system.bundle().read_file(config_path, [&](core::readable const & file) { - kym::vm vm{&system.thread_safe_allocator(), [&system](core::slice const & error_message) { - system.log(app::log_level::error, error_message); - }}; + if ((system.res_fs().read_file(config_path, [&](core::readable const & file) { + kym::vm vm{&system.thread_safe_allocator(), + [&system](core::slice const & error_message) { + system.log(app::log_level::error, error_message); + }}; if (!vm.init({ .datastack_size = 64, @@ -30,24 +31,25 @@ extern "C" int main(int argc, char const * const * argv) { } core::stack script_source{&system.thread_safe_allocator()}; - core::u8 stream_buffer[1024] = {}; + core::u8 stream_buffer[1024]{0}; if (!core::stream(core::sequence_writer{&script_source}, file, stream_buffer).is_ok()) return; vm.with_object(vm.compile(core::slice{script_source.begin(), script_source.end()}.as_chars()), [&](kym::bound_object & script) { vm.with_object(script.call({}), [&](kym::bound_object & config) { - core::u16 const width = config.get_field("width").as_u16().value_or(0); + core::u16 const width{config.get_field("width").as_u16().value_or(0)}; if (width == 0) return system.log(app::log_level::error, "failed to decode `width` property of config"); - core::u16 const height = config.get_field("height").as_u16().value_or(0); + core::u16 const height{config.get_field("height").as_u16().value_or(0)}; if (height == 0) return system.log(app::log_level::error, "failed to decode `height` property of config"); - graphics.show(width, height); + if (graphics.show(width, height) != app::graphics::show_result::ok) + return system.log(app::log_level::error, "failed to initialize window"); vm.with_object(config.get_field("title"), [&](kym::bound_object & title) { core::stack title_buffer{&system.thread_safe_allocator()}; @@ -63,13 +65,7 @@ extern "C" int main(int argc, char const * const * argv) { }); }); }); - }); - - if (!is_config_loaded) { - system.log(app::log_level::error, "failed to load config"); - - return core::u8_max; - } + }) != core::fs::access_result::ok) || (!is_config_loaded)) return core::u8_max; // app::canvas canvas_2d();