This commit is contained in:
Charlie 2024-07-03 23:01:46 +02:00
parent 6134c6e982
commit 439f00caf4
10 changed files with 279 additions and 7 deletions

View file

@ -6,6 +6,6 @@ mkdir build
pushd 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 popd

View file

@ -3,7 +3,7 @@
/// ///
// Build config stuff // Build config stuff
#define RUN_TESTS 1 #define RUN_TESTS 0
// This is only for people developing oogabooga! // This is only for people developing oogabooga!
#define OOGABOOGA_DEV 1 #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 // Ooga booga needs to be included AFTER configuration and BEFORE the program code
#include "oogabooga/oogabooga.c" #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) // Comment & Uncomment these to swap projects (only include one at a time)
@ -44,4 +47,4 @@ typedef struct Context_Extra {
// #include "entry_randygame.c" // #include "entry_randygame.c"
// This is where you swap in your own project! // This is where you swap in your own project!
// #include "entry_yourepicgamename.c" // #include "entry_yourepicgamename.c"

View file

@ -7,7 +7,7 @@ pushd build
mkdir release mkdir release
pushd 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
popd popd

View file

@ -1,4 +1,4 @@
¨
int entry(int argc, char **argv) { int entry(int argc, char **argv) {
window.title = STR("Minimal Game Example"); window.title = STR("Minimal Game Example");

View file

@ -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()); Gfx_Image *hammer_image = load_image_from_disk(STR("oogabooga/examples/hammer.png"), get_heap_allocator());
assert(hammer_image, "Failed loading hammer.png"); assert(hammer_image, "Failed loading hammer.png");
Gfx_Font *font = load_font_From_disk(
seed_for_random = os_get_current_cycle_count(); seed_for_random = os_get_current_cycle_count();
const float64 fps_limit = 69000; 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_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(); gfx_update();
if (is_key_just_released('E')) { if (is_key_just_released('E')) {

View file

@ -36,3 +36,9 @@ typedef struct Gfx_Image {
Allocator allocator; Allocator allocator;
} Gfx_Image; } Gfx_Image;
typedef struct Gfx_Font {
u32 width, height;
u8 *data;
Gfx_Handle gfx_handle;
Allocator allocator;
} Gfx_Font;

View file

@ -1,4 +1,5 @@
#include <Shlwapi.h>
#define VIRTUAL_MEMORY_BASE ((void*)0x0000690000000000ULL) #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) { 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); 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)); 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); 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) { 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); 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)); 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); 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); 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) { bool os_file_write_string(File f, string s) {
DWORD written; DWORD written;
BOOL result = WriteFile(f, s.data, s.count, &written, NULL); 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); 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, ...) { void fprints(File f, string fmt, ...) {
va_list args; va_list args;
va_start(args, fmt); va_start(args, fmt);

View file

@ -206,6 +206,7 @@ void os_file_close(File f);
bool os_file_delete_s(string path); bool os_file_delete_s(string path);
bool os_make_directory_s(string path, bool recursive); 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_string(File f, string s);
bool os_file_write_bytes(File f, void *buffer, u64 size_in_bytes); 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_file_s(string path);
bool os_is_directory_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 // 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);} 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, \ string: os_make_directory_s, \
default: os_make_directory_f \ default: os_make_directory_f \
)(__VA_ARGS__) )(__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);} 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__)), \ #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, \ string: os_is_directory_s, \
default: os_is_directory_f \ default: os_is_directory_f \
)(__VA_ARGS__) )(__VA_ARGS__)
void fprints(File f, string fmt, ...); void fprints(File f, string fmt, ...);
void fprintf(File f, const char* fmt, ...); void fprintf(File f, const char* fmt, ...);

View file

@ -19,4 +19,36 @@ string get_file_extension(string path) {
} }
return ZERO(string); 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;
} }

View file

@ -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(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(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 // Clean up test files
bool delete_ok = false; bool delete_ok = false;
delete_ok = os_file_delete("test.txt"); delete_ok = os_file_delete("test.txt");
@ -543,6 +574,10 @@ void test_file_io() {
assert(delete_ok, "Failed: could not delete balls.txt"); assert(delete_ok, "Failed: could not delete balls.txt");
delete_ok = os_file_delete("integers"); delete_ok = os_file_delete("integers");
assert(delete_ok, "Failed: could not 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) { bool floats_roughly_match(float a, float b) {
return fabs(a - b) < 0.01; return fabs(a - b) < 0.01;