diff --git a/source/app.cpp b/source/app.cpp index fc82c67..da5c475 100644 --- a/source/app.cpp +++ b/source/app.cpp @@ -12,270 +12,213 @@ import coral.math; import oar; -namespace app { - struct directory : public coral::fs { - struct rules { - bool can_read; +using native_path = coral::fixed_buffer<4096>; - bool can_write; - }; - - virtual rules access_rules() const = 0; - - virtual coral::slice native_path() const = 0; +struct native_file : public coral::file_reader, public coral::file_writer { + enum class open_mode { + read_only, + overwrite, }; - struct file : public coral::file_reader, public coral::file_writer { - enum class open_mode { - read_only, - overwrite, - }; - - enum class [[nodiscard]] close_result { - ok, - io_unavailable, - }; - - enum class [[nodiscard]] open_result { - ok, - io_unavailable, - access_denied, - not_found, - }; - - file(directory * file_directory) : rw_ops{nullptr} { - this->file_directory = file_directory; - } - - close_result close() { - if (::SDL_RWclose(this->rw_ops) != 0) return close_result::io_unavailable; - - this->rw_ops = nullptr; - - return close_result::ok; - } - - bool is_open() const { - return this->rw_ops != nullptr; - } - - open_result open(coral::path const & file_path, open_mode mode) { - if (this->is_open()) switch (this->close()) { - case close_result::ok: break; - case close_result::io_unavailable: return open_result::io_unavailable; - default: coral::unreachable(); - } - - // Windows system path max is something like 512 bytes (depending on the version) and - // on Linux it's 4096. - coral::fixed_buffer<4096> path_buffer{0}; - - if (!path_buffer.write(this->file_directory->native_path().as_bytes()).is_ok()) - // What kind of directory path is being used that would be longer than 4096 bytes? - return open_result::not_found; - - if (!path_buffer.write(file_path.as_slice().as_bytes()).is_ok()) - // This happening is still implausible but slightly more reasonable? - return open_result::not_found; - - // No room for zero terminator. - if (path_buffer.is_full()) return open_result::not_found; - - switch (mode) { - case open_mode::read_only: { - if (!this->file_directory->access_rules().can_read) - return open_result::access_denied; - - this->rw_ops = ::SDL_RWFromFile( - reinterpret_cast(path_buffer.begin()), "rb"); - - break; - } - - case open_mode::overwrite: { - if (!this->file_directory->access_rules().can_write) - return open_result::access_denied; - - this->rw_ops = ::SDL_RWFromFile( - reinterpret_cast(path_buffer.begin()), "wb"); - - break; - } - - default: coral::unreachable(); - } - - if (this->rw_ops == nullptr) return open_result::not_found; - - return open_result::ok; - } - - coral::expected read(coral::slice const & data) override { - if (!this->is_open()) return coral::io_error::unavailable; - - coral::usize const data_read{::SDL_RWread(this->rw_ops, data.pointer, sizeof(uint8_t), data.length)}; - - if ((data_read == 0) && (::SDL_GetError() != nullptr)) return coral::io_error::unavailable; - - return data_read; - } - - coral::expected seek(coral::u64 offset) override { - if (!this->is_open()) return coral::io_error::unavailable; - - // TODO: Fix cast. - coral::i64 const byte_position{ - ::SDL_RWseek(this->rw_ops, static_cast(offset), RW_SEEK_SET)}; - - if (byte_position == -1) return coral::io_error::unavailable; - - return static_cast(byte_position); - } - - coral::expected tell() override { - if (!this->is_open()) return coral::io_error::unavailable; - - coral::i64 const byte_position{::SDL_RWseek(this->rw_ops, 0, RW_SEEK_SET)}; - - if (byte_position == -1) return coral::io_error::unavailable; - - return static_cast(byte_position); - } - - coral::expected write(coral::slice const & data) override { - if (!this->is_open()) return coral::io_error::unavailable; - - coral::usize const data_written{::SDL_RWwrite(this->rw_ops, data.pointer, sizeof(uint8_t), data.length)}; - - if ((data_written == 0) && (::SDL_GetError() != nullptr)) return coral::io_error::unavailable; - - return data_written; - } - - private: - directory * file_directory; - - ::SDL_RWops * rw_ops; + enum class [[nodiscard]] close_result { + ok, + io_unavailable, }; -} -struct base_directory : public app::directory { - base_directory() : directory_path{} { - char * const path{::SDL_GetBasePath()}; + enum class [[nodiscard]] open_result { + ok, + io_unavailable, + access_denied, + not_found, + }; - if (path == nullptr) return; + native_file() = default; - coral::usize path_length{0}; + close_result close() { + if (SDL_RWclose(this->rw_ops) != 0) return close_result::io_unavailable; - while (path[path_length] != 0) path_length += 1; + this->rw_ops = nullptr; - if (path_length == 0) { - ::SDL_free(path); + return close_result::ok; + } - return; + bool is_open() const { + return this->rw_ops != nullptr; + } + + open_result open(native_path const & file_path, open_mode mode) { + if (this->is_open()) switch (this->close()) { + case close_result::ok: break; + case close_result::io_unavailable: return open_result::io_unavailable; + default: coral::unreachable(); } - this->directory_path = {path, path_length}; + // No room for zero terminator. + if (file_path.is_full()) return open_result::not_found; + + switch (mode) { + case open_mode::read_only: { + this->rw_ops = SDL_RWFromFile(file_path.as_slice().as_chars().begin(), "rb"); + + break; + } + + case open_mode::overwrite: { + this->rw_ops = SDL_RWFromFile(file_path.as_slice().as_chars().begin(), "wb"); + + break; + } + + default: coral::unreachable(); + } + + if (this->rw_ops == nullptr) return open_result::not_found; + + return open_result::ok; } - ~base_directory() override { - ::SDL_free(this->directory_path.begin()); + coral::expected read(coral::slice const & data) override { + if (!this->is_open()) return coral::io_error::unavailable; + + coral::usize const data_read{SDL_RWread(this->rw_ops, data.pointer, sizeof(uint8_t), data.length)}; + + if ((data_read == 0) && (SDL_GetError() != nullptr)) return coral::io_error::unavailable; + + return data_read; } - rules access_rules() const override { - return { - .can_read = true, - .can_write = false, - }; + coral::expected seek(coral::u64 offset) override { + if (!this->is_open()) return coral::io_error::unavailable; + + // TODO: Fix cast. + coral::i64 const byte_position{ + SDL_RWseek(this->rw_ops, static_cast(offset), RW_SEEK_SET)}; + + if (byte_position == -1) return coral::io_error::unavailable; + + return static_cast(byte_position); } - coral::slice native_path() const override { - return this->directory_path; + coral::expected tell() override { + if (!this->is_open()) return coral::io_error::unavailable; + + coral::i64 const byte_position{SDL_RWseek(this->rw_ops, 0, RW_SEEK_SET)}; + + if (byte_position == -1) return coral::io_error::unavailable; + + return static_cast(byte_position); } - void read_file(coral::path const & file_path, coral::callable const & then) override { - if (this->directory_path.length == 0) return; + coral::expected write(coral::slice const & data) override { + if (!this->is_open()) return coral::io_error::unavailable; - app::file file{this}; + coral::usize const data_written{SDL_RWwrite(this->rw_ops, data.pointer, sizeof(uint8_t), data.length)}; - if (file.open(file_path, app::file::open_mode::read_only) != app::file::open_result::ok) - return; + if ((data_written == 0) && (SDL_GetError() != nullptr)) return coral::io_error::unavailable; - then(file); - - if (file.close() != app::file::close_result::ok) return; + return data_written; } - void write_file(coral::path const & file_path, coral::callable const & then) override { - // Directory is read-only. - } - - protected: - coral::slice directory_path; + private: + SDL_RWops * rw_ops{nullptr}; }; -struct user_directory : public app::directory { - user_directory(coral::path const & title) : directory_path{} { - char * const path{::SDL_GetPrefPath("ona", title.begin())}; +struct sandboxed_fs : public coral::fs { + sandboxed_fs() { + char * const path{SDL_GetBasePath()}; if (path == nullptr) return; - coral::usize path_length{0}; + for (coral::usize index = 0; path[index] != 0; index += 1) + this->sandbox_path.put(path[index]); - while (path[path_length] != 0) path_length += 1; + SDL_free(path); - if (path_length == 0) { - ::SDL_free(path); - - return; - } - - this->directory_path = {path, path_length}; + this->access_rules.can_read = true; } - ~user_directory() override { - ::SDL_free(this->directory_path.begin()); + sandboxed_fs(coral::path const & organization_name, coral::path const & app_name) { + char * const path{SDL_GetPrefPath(organization_name.begin(), app_name.begin())}; + + if (path == nullptr) return; + + for (coral::usize index = 0; path[index] != 0; index += 1) + this->sandbox_path.put(path[index]); + + SDL_free(path); + + this->access_rules.can_read = true; } - rules access_rules() const override { - return { - .can_read = true, - .can_write = false, - }; - } - - coral::slice native_path() const override { - return this->directory_path; + access_rules query_access() override { + return this->access_rules; } void read_file(coral::path const & file_path, coral::callable const & then) override { - if (this->directory_path.length == 0) return; + if (!this->access_rules.can_read) return; - app::file file{this}; + native_path sandbox_file_path{0}; + { + coral::expected const written = sandbox_file_path.write(this->sandbox_path.as_slice()); - if (file.open(file_path, app::file::open_mode::read_only) != app::file::open_result::ok) - return; + if (!written.is_ok() || (written.value() != this->sandbox_path.count())) return; + } + + { + coral::expected const written = + sandbox_file_path.write(file_path.as_slice().as_bytes()); + + if (!written.is_ok() || (written.value() != this->sandbox_path.count())) return; + } + + native_file file; + + if (file.open(sandbox_file_path, native_file::open_mode::read_only) != + native_file::open_result::ok) return; then(file); - if (file.close() != app::file::close_result::ok) return; + if (file.close() != native_file::close_result::ok) + // Error orphaned file handle! + return; } void write_file(coral::path const & file_path, coral::callable const & then) override { - if (this->directory_path.length == 0) return; + if (!this->access_rules.can_write) return; - app::file file{this}; + native_path sandbox_file_path{0}; + { + coral::expected const written = sandbox_file_path.write(this->sandbox_path.as_slice()); - if (file.open(file_path, app::file::open_mode::overwrite) != app::file::open_result::ok) - return; + if (!written.is_ok() || (written.value() != this->sandbox_path.count())) return; + } + + { + coral::expected const written = + sandbox_file_path.write(file_path.as_slice().as_bytes()); + + if (!written.is_ok() || (written.value() != this->sandbox_path.count())) return; + } + + native_file file; + + if (file.open(sandbox_file_path, native_file::open_mode::overwrite) != + native_file::open_result::ok) return; then(file); - if (file.close() != app::file::close_result::ok) return; + if (file.close() != native_file::close_result::ok) + // Error orphaned file handle! + return; } - protected: - coral::slice directory_path; + private: + native_path sandbox_path{0}; + + access_rules access_rules{ + .can_read = false, + .can_write = false, + }; }; export namespace app { @@ -285,17 +228,27 @@ export namespace app { error, }; - struct system { - system(coral::path const & title) : user_directory{title}, - res_archive{&base_directory, res_archive_path} {} + struct client { + coral::fs & base() { + return this->base_sandbox; + } - app::directory & base_dir() { - return this->base_directory; + void display(coral::u16 screen_width, coral::u16 screen_height) { + SDL_SetWindowSize(this->window, screen_width, screen_height); + SDL_ShowWindow(this->window); + } + + void log(log_level level, coral::slice const & message) { + coral::i32 const length{static_cast( + coral::min(message.length, static_cast(coral::i32_max)))}; + + SDL_LogMessage(SDL_LOG_CATEGORY_APPLICATION, + SDL_LOG_PRIORITY_INFO, "%.*s", length, message.pointer); } bool poll() { - while (::SDL_PollEvent(&this->sdl_event) != 0) { - switch (this->sdl_event.type) { + while (SDL_PollEvent(&this->event) != 0) { + switch (this->event.type) { case SDL_QUIT: return false; } } @@ -303,124 +256,62 @@ export namespace app { return true; } - coral::fs & res_fs() { - return this->res_archive; + coral::fs & resources() { + return this->resources_archive; } - void log(app::log_level level, coral::slice const & message) { - coral::i32 const length{static_cast( - coral::min(message.length, static_cast(coral::i32_max)))}; + static int run(coral::path const & title, coral::callable const & start) { + constexpr int windowpos {SDL_WINDOWPOS_UNDEFINED}; + constexpr coral::u32 windowflags {SDL_WINDOW_HIDDEN}; + constexpr int window_width {640}; + constexpr int window_height {480}; - ::SDL_LogMessage(SDL_LOG_CATEGORY_APPLICATION, - SDL_LOG_PRIORITY_INFO, "%.*s", length, message.pointer); + SDL_Window * const window {SDL_CreateWindow( + title.begin(), windowpos, windowpos, window_width, window_height, windowflags)}; + + if (window == nullptr) return 0xff; + + struct : public coral::allocator { + coral::u8 * reallocate(coral::u8 * allocation, coral::usize requested_size) override { + return reinterpret_cast(SDL_realloc(allocation, requested_size)); + } + + void deallocate(void * allocation) override { + SDL_free(allocation); + } + } allocator; + + client app_client {&allocator, window, title}; + + return start(app_client); } coral::allocator & thread_safe_allocator() { - return this->allocator; + return *this->allocator; } - app::directory & user_dir() { - return this->user_directory; + coral::fs & user() { + return this->user_sandbox; } private: - static constexpr coral::path res_archive_path{"base.oar"}; + client(coral::allocator * allocator, SDL_Window * window, + coral::path const & title) : user_sandbox{"ona", title} { - ::SDL_Event sdl_event; + this->allocator = allocator; + this->window = window; + } - struct : public coral::allocator { - coral::u8 * reallocate(coral::u8 * maybe_allocation, coral::usize requested_size) override { - return reinterpret_cast(::SDL_malloc(requested_size)); - } + coral::allocator * allocator; - void deallocate(void * allocation) override { - ::SDL_free(allocation); - } - } allocator; + SDL_Window * window; - struct base_directory base_directory; + SDL_Event event; - struct user_directory user_directory; + sandboxed_fs base_sandbox; - oar::archive res_archive; + sandboxed_fs user_sandbox; + + oar::archive resources_archive{&base_sandbox, "base.oar"}; }; - - 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(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 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 display(coral::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/oar.cpp b/source/oar.cpp index 332fbb3..9dc338d 100644 --- a/source/oar.cpp +++ b/source/oar.cpp @@ -171,6 +171,13 @@ export namespace oar { this->archive_path = archive_path; } + access_rules query_access() override { + return { + .can_read = true, + .can_write = false, + }; + } + void read_file(coral::path const & file_path, coral::callable const & then) override { diff --git a/source/runtime.cpp b/source/runtime.cpp index 3a7a422..b7b98aa 100644 --- a/source/runtime.cpp +++ b/source/runtime.cpp @@ -9,12 +9,12 @@ import coral.math; import coral.sequence; extern "C" int main(int argc, char const * const * argv) { - return app::display("Ona Runtime", [](app::system & system, app::graphics & graphics) -> int { + return app::client::run("Ona Runtime", [](app::client & client) -> int { constexpr coral::path config_path{"config.kym"}; bool is_config_loaded{false}; - system.res_fs().read_file(config_path, [&](coral::reader & file) { - coral::allocator * const allocator{&system.thread_safe_allocator()}; + client.resources().read_file(config_path, [&](coral::reader & file) { + coral::allocator * const allocator{&client.thread_safe_allocator()}; coral::stack script_source{allocator}; { @@ -24,13 +24,13 @@ extern "C" int main(int argc, char const * const * argv) { if (!coral::stream(script_writer, file, stream_buffer).is_ok()) return; } - system.log(app::log_level::notice, script_source.as_slice().as_chars()); + client.log(app::log_level::notice, script_source.as_slice().as_chars()); is_config_loaded = true; }); if (!is_config_loaded) { - coral::stack error_message{&system.thread_safe_allocator()}; + coral::stack error_message{&client.thread_safe_allocator()}; { coral::contiguous_writer error_writer{&error_message}; @@ -44,11 +44,10 @@ extern "C" int main(int argc, char const * const * argv) { return coral::u8_max; } - // app::canvas canvas_2d(); + client.display(1280, 800); + + while (client.poll()) { - while (system.poll()) { - // canvas_2d.render(graphics); - graphics.present(); } return 0;