From 439f00caf4a853fe0283b257258b4a8ed499dc33 Mon Sep 17 00:00:00 2001 From: Charlie <66182434+asbott@users.noreply.github.com> Date: Wed, 3 Jul 2024 23:01:46 +0200 Subject: [PATCH] Merge --- build.bat | 2 +- build.c | 7 +- build_release.bat | 2 +- oogabooga/examples/minimal_game_loop.c | 2 +- oogabooga/examples/renderer_stress_test.c | 6 + oogabooga/gfx_interface.c | 6 + oogabooga/os_impl_windows.c | 178 +++++++++++++++++++++- oogabooga/os_interface.c | 16 +- oogabooga/path_utils.c | 32 ++++ oogabooga/tests.c | 35 +++++ 10 files changed, 279 insertions(+), 7 deletions(-) diff --git a/build.bat b/build.bat index 56c0d08..0636236 100644 --- a/build.bat +++ b/build.bat @@ -6,6 +6,6 @@ mkdir build pushd build -clang -g -o cgame.exe ../build.c -O0 -mavx -std=c11 -Wextra -Wno-incompatible-library-redeclaration -Wno-sign-compare -Wno-unused-parameter -Wno-builtin-requires-header -lgdi32 -luser32 -lwinmm -ld3d11 -ldxguid -ld3dcompiler +clang -g -o cgame.exe ../build.c -O0 -std=c11 -Wextra -Wno-incompatible-library-redeclaration -Wno-sign-compare -Wno-unused-parameter -Wno-builtin-requires-header -lgdi32 -luser32 -lwinmm -ld3d11 -ldxguid -ld3dcompiler popd \ No newline at end of file diff --git a/build.c b/build.c index 4497f79..4851453 100644 --- a/build.c +++ b/build.c @@ -3,7 +3,7 @@ /// // Build config stuff -#define RUN_TESTS 1 +#define RUN_TESTS 0 // This is only for people developing oogabooga! #define OOGABOOGA_DEV 1 @@ -29,6 +29,9 @@ typedef struct Context_Extra { // Ooga booga needs to be included AFTER configuration and BEFORE the program code #include "oogabooga/oogabooga.c" +// #Volatile tutorial below +// #Volatile tutorial below +// #Volatile tutorial below // // Comment & Uncomment these to swap projects (only include one at a time) @@ -44,4 +47,4 @@ typedef struct Context_Extra { // #include "entry_randygame.c" // This is where you swap in your own project! -// #include "entry_yourepicgamename.c" \ No newline at end of file +// #include "entry_yourepicgamename.c" diff --git a/build_release.bat b/build_release.bat index 034b63b..b627f50 100644 --- a/build_release.bat +++ b/build_release.bat @@ -7,7 +7,7 @@ pushd build mkdir release pushd release -clang -o cgame.exe ../../build.c -Ofast -std=c11 -Wextra -Wno-incompatible-library-redeclaration -Wno-sign-compare -Wno-unused-parameter -Wno-builtin-requires-header -lgdi32 -luser32 -lwinmm -ld3d11 -ldxguid -ld3dcompiler -mavx2 -mavx512f -msse4.1 -msse2 -finline-functions -ffast-math -fno-math-errno -funsafe-math-optimizations -freciprocal-math -ffinite-math-only -fassociative-math -fno-signed-zeros -fno-trapping-math -ftree-vectorize -fomit-frame-pointer -funroll-loops -fno-rtti -fno-exceptions +clang -o cgame.exe ../../build.c -Ofast -std=c11 -Wextra -Wno-incompatible-library-redeclaration -Wno-sign-compare -Wno-unused-parameter -Wno-builtin-requires-header -lgdi32 -luser32 -lwinmm -ld3d11 -ldxguid -ld3dcompiler -lshlwapi -mavx2 -mavx512f -msse4.1 -msse2 -finline-functions -ffast-math -fno-math-errno -funsafe-math-optimizations -freciprocal-math -ffinite-math-only -fassociative-math -fno-signed-zeros -fno-trapping-math -ftree-vectorize -fomit-frame-pointer -funroll-loops -fno-rtti -fno-exceptions popd popd \ No newline at end of file diff --git a/oogabooga/examples/minimal_game_loop.c b/oogabooga/examples/minimal_game_loop.c index f40e4e3..5ac743a 100644 --- a/oogabooga/examples/minimal_game_loop.c +++ b/oogabooga/examples/minimal_game_loop.c @@ -1,4 +1,4 @@ - +ยจ int entry(int argc, char **argv) { window.title = STR("Minimal Game Example"); diff --git a/oogabooga/examples/renderer_stress_test.c b/oogabooga/examples/renderer_stress_test.c index 0b81cd7..01b3faa 100644 --- a/oogabooga/examples/renderer_stress_test.c +++ b/oogabooga/examples/renderer_stress_test.c @@ -15,6 +15,8 @@ int entry(int argc, char **argv) { Gfx_Image *hammer_image = load_image_from_disk(STR("oogabooga/examples/hammer.png"), get_heap_allocator()); assert(hammer_image, "Failed loading hammer.png"); + Gfx_Font *font = load_font_From_disk( + seed_for_random = os_get_current_cycle_count(); const float64 fps_limit = 69000; @@ -100,6 +102,10 @@ int entry(int argc, char **argv) { draw_image(bush_image, v2(0.65, 0.65), v2(0.2*sin(now), 0.2*sin(now)), COLOR_WHITE); + draw_frame.font = STR(""); + + draw_text(); + gfx_update(); if (is_key_just_released('E')) { diff --git a/oogabooga/gfx_interface.c b/oogabooga/gfx_interface.c index 2bbd4a8..8ef7b94 100644 --- a/oogabooga/gfx_interface.c +++ b/oogabooga/gfx_interface.c @@ -36,3 +36,9 @@ typedef struct Gfx_Image { Allocator allocator; } Gfx_Image; +typedef struct Gfx_Font { + u32 width, height; + u8 *data; + Gfx_Handle gfx_handle; + Allocator allocator; +} Gfx_Font; diff --git a/oogabooga/os_impl_windows.c b/oogabooga/os_impl_windows.c index 207ef10..d743893 100644 --- a/oogabooga/os_impl_windows.c +++ b/oogabooga/os_impl_windows.c @@ -1,4 +1,5 @@ +#include #define VIRTUAL_MEMORY_BASE ((void*)0x0000690000000000ULL) @@ -553,8 +554,15 @@ void os_write_string_to_stdout(string s) { } u16 *win32_fixed_utf8_to_null_terminated_wide(string utf8, Allocator allocator) { + + if (utf8.count == 0) { + u16 *utf16_str = (u16 *)alloc(allocator, (1) * sizeof(u16)); + *utf16_str = 0; + return utf16_str; + } + u64 utf16_length = MultiByteToWideChar(CP_UTF8, 0, (LPCCH)utf8.data, (int)utf8.count, 0, 0); - + u16 *utf16_str = (u16 *)alloc(allocator, (utf16_length + 1) * sizeof(u16)); int result = MultiByteToWideChar(CP_UTF8, 0, (LPCCH)utf8.data, (int)utf8.count, utf16_str, utf16_length); @@ -573,6 +581,13 @@ u16 *temp_win32_fixed_utf8_to_null_terminated_wide(string utf8) { string win32_null_terminated_wide_to_fixed_utf8(const u16 *utf16, Allocator allocator) { u64 utf8_length = WideCharToMultiByte(CP_UTF8, 0, (LPCWCH)utf16, -1, 0, 0, 0, 0); + if (utf8_length == 0) { + string utf8; + utf8.count = 0; + utf8.data = 0; + return utf8; + } + u8 *utf8_str = (u8 *)alloc(allocator, utf8_length * sizeof(u8)); int result = WideCharToMultiByte(CP_UTF8, 0, (LPCWCH)utf16, -1, (LPSTR)utf8_str, (int)utf8_length, 0, 0); @@ -620,6 +635,79 @@ bool os_file_delete_s(string path) { return (bool)DeleteFileW(path_wide); } +bool os_make_directory_s(string path, bool recursive) { + wchar_t *wide_path = temp_win32_fixed_utf8_to_null_terminated_wide(path); + + // Convert forward slashes to backslashes + for (wchar_t *p = wide_path; *p; ++p) { + if (*p == L'/') { + *p = L'\\'; + } + } + + if (recursive) { + wchar_t *sep = wcschr(wide_path + 1, L'\\'); + while (sep) { + *sep = 0; + if (!CreateDirectoryW(wide_path, NULL) && GetLastError() != ERROR_ALREADY_EXISTS) { + return false; + } + *sep = L'\\'; + sep = wcschr(sep + 1, L'\\'); + } + } + + if (!CreateDirectoryW(wide_path, NULL) && GetLastError() != ERROR_ALREADY_EXISTS) { + return false; + } + + return true; +} +bool os_delete_directory_s(string path, bool recursive) { + wchar_t *wide_path = temp_win32_fixed_utf8_to_null_terminated_wide(path); + + if (recursive) { + WIN32_FIND_DATAW findFileData; + HANDLE hFind = INVALID_HANDLE_VALUE; + wchar_t search_path[MAX_PATH]; + wcscpy(search_path, wide_path); + wcscat(search_path, L"\\*"); + + hFind = FindFirstFileW(search_path, &findFileData); + if (hFind == INVALID_HANDLE_VALUE) { + return false; + } else { + do { + if (wcscmp(findFileData.cFileName, L".") != 0 && wcscmp(findFileData.cFileName, L"..") != 0) { + wchar_t child_path[MAX_PATH]; + wcscpy(child_path, wide_path); + wcscat(child_path, L"\\"); + wcscat(child_path, findFileData.cFileName); + + if (findFileData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) { + if (!os_delete_directory_s(temp_win32_null_terminated_wide_to_fixed_utf8(child_path), true)) { + FindClose(hFind); + return false; + } + } else { + if (!DeleteFileW(child_path)) { + FindClose(hFind); + return false; + } + } + } + } while (FindNextFileW(hFind, &findFileData) != 0); + FindClose(hFind); + } + } + + if (!RemoveDirectoryW(wide_path)) { + return false; + } + + return true; +} + bool os_file_write_string(File f, string s) { DWORD written; BOOL result = WriteFile(f, s.data, s.count, &written, NULL); @@ -717,6 +805,94 @@ bool os_is_directory_s(string path) { return (attributes & FILE_ATTRIBUTE_DIRECTORY); } +bool os_is_path_absolute(string path) { + // #Incomplete #Portability not sure this is very robust. + + if (path.count < 2) return false; + + if (path.data[1] == ':' && ((path.data[0] >= 'A' && path.data[0] <= 'Z') || (path.data[0] >= 'a' && path.data[0] <= 'z'))) { + return true; + } + + if (path.count > 1 && path.data[0] == '\\' && path.data[1] == '\\') { + return true; + } + + return false; +} + +bool os_get_absolute_path(string path, string *result, Allocator allocator) { + u16 buffer[MAX_PATH]; + u16 *path_wide = temp_win32_fixed_utf8_to_null_terminated_wide(path); + DWORD count = GetFullPathNameW(path_wide, MAX_PATH, buffer, 0); + + if (count == 0) { + return false; + } + + *result = win32_null_terminated_wide_to_fixed_utf8(buffer, allocator); + + return true; +} + +bool os_get_relative_path(string from, string to, string *result, Allocator allocator) { + + if (!os_is_path_absolute(from)) { + bool abs_ok = os_get_absolute_path(from, &from, temp); + if (!abs_ok) return false; + } + if (!os_is_path_absolute(to)) { + bool abs_ok = os_get_absolute_path(to, &to, temp); + if (!abs_ok) return false; + } + + u16 buffer[MAX_PATH]; + + u16 *from_wide = temp_win32_fixed_utf8_to_null_terminated_wide(from); + u16 *to_wide = temp_win32_fixed_utf8_to_null_terminated_wide(to); + + // #Speed is_file and is_directory potentially slow + DWORD attr_from = os_is_file(from) ? FILE_ATTRIBUTE_NORMAL : FILE_ATTRIBUTE_DIRECTORY; + DWORD attr_to = os_is_file(to) ? FILE_ATTRIBUTE_NORMAL : FILE_ATTRIBUTE_DIRECTORY; + + BOOL success = PathRelativePathToW(buffer, + from_wide, attr_from, + to_wide, attr_to); + if (!success) { + return false; + } + + u64 count = 0; + while (buffer[count] != 0) count += 1; + + *result = win32_null_terminated_wide_to_fixed_utf8(buffer, allocator); + + return true; +} + +bool os_do_paths_match(string a, string b) { + wchar_t *wide_path_a = temp_win32_fixed_utf8_to_null_terminated_wide(a); + wchar_t *wide_path_b = temp_win32_fixed_utf8_to_null_terminated_wide(b); + + wchar_t full_path_a[MAX_PATH]; + wchar_t full_path_b[MAX_PATH]; + + // Get the full path for both paths + if (!GetFullPathNameW(wide_path_a, MAX_PATH, full_path_a, NULL)) { + return false; + } + if (!GetFullPathNameW(wide_path_b, MAX_PATH, full_path_b, NULL)) { + return false; + } + + // Compare the full paths + if (wcscmp(full_path_a, full_path_b) == 0) { + return true; + } + + return false; +} + void fprints(File f, string fmt, ...) { va_list args; va_start(args, fmt); diff --git a/oogabooga/os_interface.c b/oogabooga/os_interface.c index c9e0d8e..9cd3fad 100644 --- a/oogabooga/os_interface.c +++ b/oogabooga/os_interface.c @@ -206,6 +206,7 @@ void os_file_close(File f); bool os_file_delete_s(string path); bool os_make_directory_s(string path, bool recursive); +bool os_delete_directory_s(string path, bool recursive); bool os_file_write_string(File f, string s); bool os_file_write_bytes(File f, void *buffer, u64 size_in_bytes); @@ -220,6 +221,13 @@ bool os_read_entire_file_s(string path, string *result, Allocator allocator); bool os_is_file_s(string path); bool os_is_directory_s(string path); +bool os_is_path_absolute(string path); + +bool os_get_absolute_path(string path, string *result, Allocator allocator); +bool os_get_relative_path(string from, string to, string *result, Allocator allocator); + +bool os_do_paths_match(string a, string b); + // It's a little unfortunate that we need to do this but I can't think of a better solution inline File os_file_open_f(const char *path, Os_Io_Open_Flags flags) {return os_file_open_s(STR(path), flags);} @@ -239,6 +247,11 @@ inline bool os_make_directory_f(const char *path, bool recursive) { return os_ma string: os_make_directory_s, \ default: os_make_directory_f \ )(__VA_ARGS__) +inline bool os_delete_directory_f(const char *path, bool recursive) { return os_delete_directory_s(STR(path), recursive); } +#define os_delete_directory(...) _Generic((FIRST_ARG(__VA_ARGS__)), \ + string: os_delete_directory_s, \ + default: os_delete_directory_f \ + )(__VA_ARGS__) inline bool os_write_entire_file_f(const char *path, string data) {return os_write_entire_file_s(STR(path), data);} #define os_write_entire_file(...) _Generic((FIRST_ARG(__VA_ARGS__)), \ @@ -263,7 +276,8 @@ inline bool os_is_directory_f(const char *path) {return os_is_directory_s(STR(pa string: os_is_directory_s, \ default: os_is_directory_f \ )(__VA_ARGS__) - + + void fprints(File f, string fmt, ...); void fprintf(File f, const char* fmt, ...); diff --git a/oogabooga/path_utils.c b/oogabooga/path_utils.c index 6a27674..2745990 100644 --- a/oogabooga/path_utils.c +++ b/oogabooga/path_utils.c @@ -19,4 +19,36 @@ string get_file_extension(string path) { } return ZERO(string); +} + +string get_file_name_including_extension(string file_path) { + if (file_path.count <= 0) return ZERO(string); + + s64 last_separator = -1; + for (s64 i = file_path.count - 1; i >= 0; i--) { + if (file_path.data[i] == '/' || file_path.data[i] == '\\' || file_path.data[i] == ':') { + last_separator = i; + break; + } + } + + string file_name = ZERO(string); + if (last_separator != -1 && last_separator < file_path.count - 1) { + file_name.data = file_path.data + last_separator + 1; + file_name.count = file_path.count - last_separator - 1; + } else { + file_name = file_path; // If no separator was found, assume entire path is a file name. + } + + return file_name; +} +string get_file_name_excluding_extension(string file_path) { + string file_name = get_file_name_including_extension(file_path); + + for (s64 i = file_name.count-1; i >= 1; i--) { + if (file_name.data[i] == '.') { + return string_view(file_name, 0, i); + } + } + return file_name; } \ No newline at end of file diff --git a/oogabooga/tests.c b/oogabooga/tests.c index 6c0b8c3..0e5fca8 100644 --- a/oogabooga/tests.c +++ b/oogabooga/tests.c @@ -531,6 +531,37 @@ void test_file_io() { assert(integers_read.count == integers_data.count, "Failed: big file read/write mismatch. Read was %d and written was %d", integers_read.count, integers_data.count); assert(strings_match(integers_data, integers_read), "Failed: big file read/write mismatch"); + assert(os_is_file("test.txt"), "Failed: test.txt not recognized as file"); + assert(os_is_file("test_bytes.txt"), "Failed: test_bytes.txt not recognized as file"); + assert(os_is_file("entire_test.txt"), "Failed: entire_test.txt not recognized as file"); + assert(os_is_file("balls.txt"), "Failed: balls.txt not recognized as file"); + assert(os_is_file("integers"), "Failed: integers not recognized as file"); + + bool dir_ok = os_make_directory("test_dir", false); + assert(dir_ok, "Failed: os_make_directory"); + + assert(os_is_directory("test_dir"), "Failed: os_is_directory"); + + string dir_abs; + bool abs_ok = os_get_absolute_path(STR("test_dir"), &dir_abs, heap); + assert(abs_ok, "Failed: os_get_absolute_path"); + assert(os_is_path_absolute(dir_abs), "Failed: os_is_path_absolute"); + assert(!os_is_path_absolute(STR("test_dir")), "Failed: os_is_path_absolute"); + string dir_rel; + bool rel_ok = os_get_relative_path(STR("."), dir_abs, &dir_rel, heap); + assert(rel_ok, "Failed: os_get_relative_path"); + assert(os_do_paths_match(dir_rel, STR("test_dir")), "Failed: Resolved relative path does not match. Expected '.\test_dir', got %s.", dir_rel); + + dir_ok = os_make_directory("test_dir1/test_dir2/test_dir3", true); + assert(dir_ok, "Failed: os_make_directory"); + dir_ok = os_make_directory("test_dir1/test_dir2/test_dir4", true); + assert(dir_ok, "Failed: os_make_directory"); + + assert(os_is_directory("test_dir1"), "Failed: os_is_directory"); + assert(os_is_directory("test_dir1/test_dir2"), "Failed: os_is_directory"); + assert(os_is_directory("test_dir1/test_dir2//test_dir3"), "Failed: os_is_directory"); + assert(os_is_directory("test_dir1/test_dir2//test_dir4"), "Failed: os_is_directory"); + // Clean up test files bool delete_ok = false; delete_ok = os_file_delete("test.txt"); @@ -543,6 +574,10 @@ void test_file_io() { assert(delete_ok, "Failed: could not delete balls.txt"); delete_ok = os_file_delete("integers"); assert(delete_ok, "Failed: could not delete integers"); + delete_ok = os_delete_directory("test_dir", false); + assert(delete_ok, "Failed: could not delete test_dir"); + delete_ok = os_delete_directory("test_dir1", true); + assert(delete_ok, "Failed: could not delete test_dir1 (recursive)"); } bool floats_roughly_match(float a, float b) { return fabs(a - b) < 0.01;