diff --git a/Makefile b/Makefile index f3d99d85..5cbe89b4 100644 --- a/Makefile +++ b/Makefile @@ -557,6 +557,9 @@ BACKEND_LDFLAG0S := SDL1_USED := 0 SDL2_USED := 0 +# suppress warnings +BACKEND_CFLAGS += -Wno-format-truncation + # for now, it's either SDL+GL or DXGI+DirectX, so choose based on WAPI ifeq ($(WINDOW_API),DXGI) DXBITS := `cat $(ENDIAN_BITWIDTH) | tr ' ' '\n' | tail -1` diff --git a/mods/character-movesets.lua b/mods/character-movesets.lua index 2f797dde..11d0e987 100644 --- a/mods/character-movesets.lua +++ b/mods/character-movesets.lua @@ -1,3 +1,6 @@ +-- name: Character Movesets +-- incompatible: moveset +-- description: Gives each character unique abilities and stats. gStateExtras = {} for i=0,(MAX_PLAYERS-1) do diff --git a/mods/extended-moveset.lua b/mods/extended-moveset.lua index f387bf98..d4e2787c 100644 --- a/mods/extended-moveset.lua +++ b/mods/extended-moveset.lua @@ -1,3 +1,7 @@ +-- name: Extended Moveset +-- incompatible: moveset +-- description: Adds various new moves from games like Sunshine and Odyssey without replacing any existing ones.\n\nOriginal author: TheGag96 + ------------------------ -- initialize actions -- ------------------------ diff --git a/src/pc/djui/djui_panel_host_mods.c b/src/pc/djui/djui_panel_host_mods.c index 3ed69109..e540e5d9 100644 --- a/src/pc/djui/djui_panel_host_mods.c +++ b/src/pc/djui/djui_panel_host_mods.c @@ -7,18 +7,122 @@ #include "pc/cheats.h" #include "pc/mod_list.h" +static struct DjuiFlowLayout* sModPanelBody = NULL; +static struct DjuiThreePanel* sDescriptionPanel = NULL; +static struct DjuiText* sTooltip = NULL; + +static void djui_panel_host_mods_description_create() { + f32 bodyHeight = 600; + + struct DjuiThreePanel* panel = djui_three_panel_create(&gDjuiRoot->base, 64, bodyHeight, 0); + djui_base_set_alignment(&panel->base, DJUI_HALIGN_RIGHT, DJUI_VALIGN_CENTER); + djui_base_set_size_type(&panel->base, DJUI_SVT_ABSOLUTE, DJUI_SVT_RELATIVE); + djui_base_set_size(&panel->base, DJUI_DEFAULT_PANEL_WIDTH, 1.0f); + djui_base_set_color(&panel->base, 0, 0, 0, 240); + djui_base_set_border_color(&panel->base, 0, 0, 0, 200); + djui_base_set_border_width(&panel->base, 8); + djui_base_set_padding(&panel->base, 16, 16, 16, 16); + { + struct DjuiFlowLayout* body = djui_flow_layout_create(&panel->base); + djui_base_set_alignment(&body->base, DJUI_HALIGN_CENTER, DJUI_VALIGN_CENTER); + djui_base_set_size_type(&body->base, DJUI_SVT_RELATIVE, DJUI_SVT_RELATIVE); + djui_base_set_size(&body->base, 1.0f, 1.0f); + djui_base_set_color(&body->base, 0, 0, 0, 0); + djui_flow_layout_set_margin(body, 16); + djui_flow_layout_set_flow_direction(body, DJUI_FLOW_DIR_DOWN); + + struct DjuiText* description = djui_text_create(&panel->base, ""); + djui_base_set_size_type(&description->base, DJUI_SVT_RELATIVE, DJUI_SVT_RELATIVE); + djui_base_set_size(&description->base, 1.0f, 1.0f); + djui_base_set_color(&description->base, 222, 222, 222, 255); + djui_text_set_alignment(description, DJUI_HALIGN_LEFT, DJUI_VALIGN_CENTER); + sTooltip = description; + } + sDescriptionPanel = panel; +} + +static void djui_mod_checkbox_set_style(struct DjuiBase* base) { + struct DjuiCheckbox* checkbox = (struct DjuiCheckbox*)base; + int val = base->enabled ? 200 : 100; + djui_base_set_border_color(&checkbox->rect->base, 173, 173, 173, 255); + djui_base_set_color(&checkbox->rect->base, 0, 0, 0, 0); + djui_base_set_color(&checkbox->text->base, val, val, val, 255); + djui_base_set_color(&checkbox->rectValue->base, val, val, val, 255); +} + +static void djui_mod_checkbox_on_hover(struct DjuiBase* base) { + struct DjuiCheckbox* checkbox = (struct DjuiCheckbox*)base; + djui_base_set_border_color(&checkbox->rect->base, 0, 120, 215, 255); + djui_base_set_color(&checkbox->text->base, 229, 241, 251, 255); + djui_base_set_color(&checkbox->rectValue->base, 229, 241, 251, 255); + + char* description = ""; + if (base->tag >= 0 && base->tag < gModTableLocal.entryCount) { + char* d = gModTableLocal.entries[base->tag].description; + if (d != NULL) { + description = gModTableLocal.entries[base->tag].description; + } + } + djui_text_set_text(sTooltip, description); +} + +static void djui_mod_checkbox_on_click(UNUSED struct DjuiBase* base) { + mod_list_update_selectable(); + + u16 index = 0; + struct DjuiBaseChild* node = sModPanelBody->base.child; + while (node != NULL) { + if (index >= gModTableLocal.entryCount) { break; } + struct ModListEntry* entry = &gModTableLocal.entries[index]; + + djui_base_set_enabled(node->base, entry->selectable); + djui_mod_checkbox_set_style(node->base); + + // iterate + index++; + node = node->next; + } +} + +static void djui_mod_checkbox_on_hover_end(struct DjuiBase* base) { + struct DjuiCheckbox* checkbox = (struct DjuiCheckbox*)base; + djui_base_set_border_color(&checkbox->rect->base, 173, 173, 173, 255); + djui_base_set_color(&checkbox->rect->base, 0, 0, 0, 0); + djui_base_set_color(&checkbox->text->base, 200, 200, 200, 255); + djui_base_set_color(&checkbox->rectValue->base, 200, 200, 200, 255); + djui_text_set_text(sTooltip, ""); +} + +static void djui_panel_host_mods_destroy(struct DjuiBase* base) { + struct DjuiThreePanel* threePanel = (struct DjuiThreePanel*)base; + free(threePanel); + + if (sDescriptionPanel != NULL) { + djui_base_destroy(&sDescriptionPanel->base); + sDescriptionPanel = NULL; + } +} + void djui_panel_host_mods_create(struct DjuiBase* caller) { f32 bodyHeight = 32 * gModTableLocal.entryCount + 64 * 1 + 16 * (gModTableLocal.entryCount + 1); + mod_list_update_selectable(); + struct DjuiBase* defaultBase = NULL; struct DjuiThreePanel* panel = djui_panel_menu_create(bodyHeight, "\\#ff0800\\M\\#1be700\\O\\#00b3ff\\D\\#ffef00\\S"); struct DjuiFlowLayout* body = (struct DjuiFlowLayout*)djui_three_panel_get_body(panel); + sModPanelBody = body; { for (int i = 0; i < gModTableLocal.entryCount; i++) { struct ModListEntry* entry = &gModTableLocal.entries[i]; - struct DjuiCheckbox* checkbox = djui_checkbox_create(&body->base, entry->name, &entry->enabled); + struct DjuiCheckbox* checkbox = djui_checkbox_create(&body->base, entry->displayName ? entry->displayName : entry->name, &entry->enabled); + checkbox->base.tag = i; djui_base_set_size_type(&checkbox->base, DJUI_SVT_RELATIVE, DJUI_SVT_ABSOLUTE); djui_base_set_size(&checkbox->base, 1.0f, 32); + djui_base_set_enabled(&checkbox->base, entry->selectable); + djui_mod_checkbox_set_style(&checkbox->base); + djui_interactable_hook_hover(&checkbox->base, djui_mod_checkbox_on_hover, djui_mod_checkbox_on_hover_end); + djui_interactable_hook_click(&checkbox->base, djui_mod_checkbox_on_click); } struct DjuiButton* button1 = djui_button_create(&body->base, "Back"); @@ -29,5 +133,8 @@ void djui_panel_host_mods_create(struct DjuiBase* caller) { defaultBase = &button1->base; } + panel->base.destroy = djui_panel_host_mods_destroy; + djui_panel_add(caller, &panel->base, defaultBase); + djui_panel_host_mods_description_create(); } diff --git a/src/pc/mod_list.c b/src/pc/mod_list.c index b9d855a5..cf466f58 100644 --- a/src/pc/mod_list.c +++ b/src/pc/mod_list.c @@ -3,6 +3,7 @@ #include #include "mod_list.h" #include "pc/fs/fs.h" +#include "pc/utils/misc.h" #include "pc/debuglog.h" #define MAX_SESSION_CHARS 7 @@ -20,7 +21,6 @@ static bool acceptable_file(char* string) { return (string != NULL && !strcmp(string, ".lua")); } - static void mod_list_delete_tmp(void) { struct dirent* dir; DIR* d = opendir(sTmpPath); @@ -86,7 +86,48 @@ void mod_list_add_tmp(u16 index, u16 remoteIndex, char* name, size_t size) { entry->remoteIndex = remoteIndex; entry->complete = false; entry->enabled = true; + entry->selectable = false; +} +static char* extract_lua_field(char* fieldName, char* buffer) { + size_t length = strlen(fieldName); + if (strncmp(fieldName, buffer, length) == 0) { + char* s = &buffer[length]; + while (*s == ' ' || *s == '\t') { s++; } + return s; + } + return NULL; +} + +static void extract_lua_fields(struct ModListEntry* entry) { + FILE* f = entry->fp; + char buffer[512] = { 0 }; + + entry->displayName = NULL; + entry->incompatible = NULL; + entry->description = NULL; + + while (!feof(f)) { + file_get_line(buffer, 512, f); + + // no longer in header + if (buffer[0] != '-' || buffer[1] != '-') { + return; + } + + // extract the field + char* extracted = NULL; + if (entry->displayName == NULL && (extracted = extract_lua_field("-- name:", buffer))) { + entry->displayName = calloc(33, sizeof(char)); + snprintf(entry->displayName, 32, "%s", extracted); + } else if (entry->incompatible == NULL && (extracted = extract_lua_field("-- incompatible:", buffer))) { + entry->incompatible = calloc(257, sizeof(char)); + snprintf(entry->incompatible, 256, "%s", extracted); + } else if (entry->description == NULL && (extracted = extract_lua_field("-- description:", buffer))) { + entry->description = calloc(513, sizeof(char)); + snprintf(entry->description, 512, "%s", extracted); + } + } } static void mod_list_add_local(u16 index, const char* path, char* name) { @@ -101,6 +142,8 @@ static void mod_list_add_local(u16 index, const char* path, char* name) { snprintf(entry->path, PATH_MAX - 1, "%s/%s", path, name); entry->fp = fopen(entry->path, "rb"); + extract_lua_fields(entry); + fseek(entry->fp, 0, SEEK_END); entry->size = ftell(entry->fp); table->totalSize += entry->size; @@ -108,6 +151,7 @@ static void mod_list_add_local(u16 index, const char* path, char* name) { entry->complete = true; entry->enabled = false; + entry->selectable = true; } void mod_table_clear(struct ModTable* table) { @@ -117,16 +161,34 @@ void mod_table_clear(struct ModTable* table) { free(entry->name); entry->name = NULL; } + + if (entry->displayName != NULL) { + free(entry->displayName); + entry->displayName = NULL; + } + + if (entry->incompatible != NULL) { + free(entry->incompatible); + entry->incompatible = NULL; + } + + if (entry->description != NULL) { + free(entry->description); + entry->description = NULL; + } + if (entry->fp != NULL) { fclose(entry->fp); entry->fp = NULL; } entry->size = 0; } + if (table->entries != NULL) { free(table->entries); table->entries = NULL; } + table->entryCount = 0; table->totalSize = 0; } @@ -137,6 +199,52 @@ void mod_list_alloc(struct ModTable* table, u16 count) { table->entries = (struct ModListEntry*)calloc(count, sizeof(struct ModListEntry)); } +static bool mod_list_incompatible_match(struct ModListEntry* a, struct ModListEntry* b) { + if (a->incompatible == NULL || b->incompatible == NULL) { + return false; + } + + char* ai = a->incompatible; + char* bi = b->incompatible; + char* atoken = NULL; + char* btoken = NULL; + + while ((atoken = strtok(ai, " "))) { + while((btoken = strtok(bi, " "))) { + if (!strcmp(atoken, btoken)) { + return true; + } + } + } + + return false; +} + +void mod_list_update_selectable(void) { + // reset selectable value + for (int i = 0; i < gModTableLocal.entryCount; i++) { + struct ModListEntry* entry = &gModTableLocal.entries[i]; + entry->selectable = true; + } + + // figure out which ones to deselect + for (int i = 0; i < gModTableLocal.entryCount; i++) { + struct ModListEntry* entry = &gModTableLocal.entries[i]; + if (entry->enabled) { continue; } + + for (int j = 0; j < gModTableLocal.entryCount; j++) { + if (j == i) { continue; } + struct ModListEntry* entry2 = &gModTableLocal.entries[j]; + if (!entry2->enabled) { continue; } + + if (mod_list_incompatible_match(entry, entry2)) { + entry->selectable = false; + break; + } + } + } +} + static void mod_list_load_local(const char* path) { if (!fs_sys_dir_exists(path)) { return; } struct ModTable* table = &gModTableLocal; @@ -188,6 +296,8 @@ void mod_list_init(void) { mod_table_clear(&gModTableLocal); mod_list_load_local(userModPath); mod_list_load_local(MOD_PATH); + + mod_list_update_selectable(); } void mod_list_shutdown(void) { diff --git a/src/pc/mod_list.h b/src/pc/mod_list.h index 5203d388..b3928235 100644 --- a/src/pc/mod_list.h +++ b/src/pc/mod_list.h @@ -17,9 +17,13 @@ struct ModListEntry { size_t size; u64 curOffset; u16 remoteIndex; + char* displayName; + char* incompatible; + char* description; bool tmp; bool complete; bool enabled; + bool selectable; }; #pragma pack(1) @@ -37,6 +41,8 @@ void mod_list_add_tmp(u16 index, u16 remoteIndex, char* name, size_t size); void mod_table_clear(struct ModTable* table); void mod_list_alloc(struct ModTable* table, u16 count); +void mod_list_update_selectable(void); + void mod_list_init(void); void mod_list_shutdown(void); diff --git a/src/pc/network/discord/activity.c b/src/pc/network/discord/activity.c index e99597d9..5de54edc 100644 --- a/src/pc/network/discord/activity.c +++ b/src/pc/network/discord/activity.c @@ -15,7 +15,7 @@ static void on_activity_update_callback(UNUSED void* data, enum EDiscordResult r } static void on_activity_join_callback(UNUSED void* data, enum EDiscordResult result, struct DiscordLobby* lobby) { - LOGFILE_INFO(LFT_DISCORD, "> on_activity_join_callback returned %d, lobby %lld, owner %lld", result, lobby->id, lobby->owner_id); + LOGFILE_INFO(LFT_DISCORD, "> on_activity_join_callback returned %d, lobby %ld, owner %ld", result, lobby->id, lobby->owner_id); DISCORD_REQUIRE(result); if (gNetworkType != NT_NONE) { LOGFILE_ERROR(LFT_DISCORD, "Joined lobby when already connected somewhere!"); @@ -25,7 +25,7 @@ static void on_activity_join_callback(UNUSED void* data, enum EDiscordResult res network_init(NT_CLIENT); gCurActivity.type = DiscordActivityType_Playing; - snprintf(gCurActivity.party.id, 128, "%lld", lobby->id); + snprintf(gCurActivity.party.id, 128, "%ld", lobby->id); gCurActivity.party.size.current_size = 2; gCurActivity.party.size.max_size = lobby->capacity; @@ -57,7 +57,7 @@ static void on_activity_join_request_callback(UNUSED void* data, enum EDiscordRe } static void on_activity_join_request(UNUSED void* data, struct DiscordUser* user) { - LOGFILE_INFO(LFT_DISCORD, "> on_activity_join_request from %lld", user->id); + LOGFILE_INFO(LFT_DISCORD, "> on_activity_join_request from %ld", user->id); //app.activities->send_request_reply(app.activities, user->id, DiscordActivityJoinRequestReply_Yes, NULL, on_activity_join_request_callback); } diff --git a/src/pc/network/discord/discord_network.c b/src/pc/network/discord/discord_network.c index c7765018..095da3b3 100644 --- a/src/pc/network/discord/discord_network.c +++ b/src/pc/network/discord/discord_network.c @@ -48,14 +48,14 @@ void ns_discord_save_id(u8 localId, s64 networkId) { assert(localId > 0); assert(localId < MAX_PLAYERS); gNetworkUserIds[localId] = (networkId == 0) ? gNetworkUserIds[0] : networkId; - LOGFILE_INFO(LFT_DISCORD, "saved user id %d == %lld", localId, gNetworkUserIds[localId]); + LOGFILE_INFO(LFT_DISCORD, "saved user id %d == %ld", localId, gNetworkUserIds[localId]); } void ns_discord_clear_id(u8 localId) { if (localId == 0) { return; } assert(localId < MAX_PLAYERS); gNetworkUserIds[localId] = 0; - LOGFILE_INFO(LFT_DISCORD, "cleared user id %d == %lld", localId, gNetworkUserIds[localId]); + LOGFILE_INFO(LFT_DISCORD, "cleared user id %d == %ld", localId, gNetworkUserIds[localId]); } void discord_network_init(int64_t lobbyId) { @@ -68,5 +68,5 @@ void discord_network_shutdown(void) { app.lobbies->flush_network(app.lobbies); if (gCurLobbyId == 0) { return; } app.lobbies->disconnect_network(app.lobbies, gCurLobbyId); - LOGFILE_INFO(LFT_DISCORD, "shutdown network, lobby = %lld", gCurLobbyId); + LOGFILE_INFO(LFT_DISCORD, "shutdown network, lobby = %ld", gCurLobbyId); } \ No newline at end of file diff --git a/src/pc/network/discord/lobby.c b/src/pc/network/discord/lobby.c index d9d378da..b9fa2c07 100644 --- a/src/pc/network/discord/lobby.c +++ b/src/pc/network/discord/lobby.c @@ -35,20 +35,20 @@ static void on_lobby_create_callback(UNUSED void* data, enum EDiscordResult resu } DISCORD_REQUIRE(result); - LOGFILE_INFO(LFT_DISCORD, "Lobby id: %lld", lobby->id); + LOGFILE_INFO(LFT_DISCORD, "Lobby id: %ld", lobby->id); LOGFILE_INFO(LFT_DISCORD, "Lobby type: %u", lobby->type); - LOGFILE_INFO(LFT_DISCORD, "Lobby owner id: %lld", lobby->owner_id); + LOGFILE_INFO(LFT_DISCORD, "Lobby owner id: %ld", lobby->owner_id); LOGFILE_INFO(LFT_DISCORD, "Lobby secret: %s", lobby->secret); LOGFILE_INFO(LFT_DISCORD, "Lobby capacity: %u", lobby->capacity); LOGFILE_INFO(LFT_DISCORD, "Lobby locked: %d", lobby->locked); gCurActivity.type = DiscordActivityType_Playing; - snprintf(gCurActivity.party.id, 128, "%lld", lobby->id); + snprintf(gCurActivity.party.id, 128, "%ld", lobby->id); gCurActivity.party.size.current_size = 1; gCurActivity.party.size.max_size = MAX_PLAYERS; char secretJoin[128] = ""; - snprintf(secretJoin, 128, "%lld:%s", lobby->id, lobby->secret); + snprintf(secretJoin, 128, "%ld:%s", lobby->id, lobby->secret); strcpy(gCurActivity.secrets.join, secretJoin); isHosting = true; @@ -59,21 +59,21 @@ static void on_lobby_create_callback(UNUSED void* data, enum EDiscordResult resu } static void on_lobby_update(UNUSED void* data, int64_t lobbyId) { - LOGFILE_INFO(LFT_DISCORD, "> on_lobby_update id: %lld", lobbyId); + LOGFILE_INFO(LFT_DISCORD, "> on_lobby_update id: %ld", lobbyId); } static void on_member_connect(UNUSED void* data, int64_t lobbyId, int64_t userId) { - LOGFILE_INFO(LFT_DISCORD, "> on_member_connect lobby: %lld, user: %lld", lobbyId, userId); + LOGFILE_INFO(LFT_DISCORD, "> on_member_connect lobby: %ld, user: %ld", lobbyId, userId); gCurActivity.party.size.current_size++; discord_activity_update(true); } static void on_member_update(UNUSED void* data, int64_t lobbyId, int64_t userId) { - LOGFILE_INFO(LFT_DISCORD, "> on_member_update lobby: %lld, user: %lld", lobbyId, userId); + LOGFILE_INFO(LFT_DISCORD, "> on_member_update lobby: %ld, user: %ld", lobbyId, userId); } static void on_member_disconnect(UNUSED void* data, int64_t lobbyId, int64_t userId) { - LOGFILE_INFO(LFT_DISCORD, "> on_member_disconnect lobby: %lld, user: %lld", lobbyId, userId); + LOGFILE_INFO(LFT_DISCORD, "> on_member_disconnect lobby: %ld, user: %ld", lobbyId, userId); u8 localIndex = discord_user_id_to_local_index(userId); if (localIndex != UNKNOWN_LOCAL_INDEX && gNetworkPlayers[localIndex].connected) { network_player_disconnected(gNetworkPlayers[localIndex].globalIndex); @@ -108,7 +108,7 @@ void discord_lobby_leave(void) { app.lobbies->disconnect_lobby(app.lobbies, gCurLobbyId, NULL, on_lobby_leave_callback); } - LOGFILE_INFO(LFT_DISCORD, "left lobby %lld", gCurLobbyId); + LOGFILE_INFO(LFT_DISCORD, "left lobby %ld", gCurLobbyId); isHosting = false; gCurLobbyId = 0; diff --git a/src/pc/network/socket/socket.c b/src/pc/network/socket/socket.c index b6c2c934..36b26e65 100644 --- a/src/pc/network/socket/socket.c +++ b/src/pc/network/socket/socket.c @@ -36,7 +36,7 @@ static int socket_send(SOCKET socket, struct sockaddr_in* addr, u8* buffer, u16 static int socket_receive(SOCKET socket, struct sockaddr_in* rxAddr, u8* buffer, u16 bufferLength, u16* receiveLength, u8* localIndex) { *receiveLength = 0; - int rxAddrSize = sizeof(struct sockaddr_in); + unsigned int rxAddrSize = sizeof(struct sockaddr_in); int rc = recvfrom(socket, (char*)buffer, bufferLength, 0, (struct sockaddr*)rxAddr, &rxAddrSize); for (int i = 1; i < MAX_PLAYERS; i++) { diff --git a/src/pc/network/socket/socket_linux.c b/src/pc/network/socket/socket_linux.c index f608e5c9..5ab75ad0 100644 --- a/src/pc/network/socket/socket_linux.c +++ b/src/pc/network/socket/socket_linux.c @@ -13,7 +13,7 @@ SOCKET socket_initialize(void) { // set non-blocking mode int rc = fcntl(sock, F_SETFL, fcntl(sock, F_GETFL, 0) | O_NONBLOCK); - if (rc == INVALID_SOCKET) { + if (rc == (int)INVALID_SOCKET) { LOG_ERROR("fcntl failed with error: %d", rc); return INVALID_SOCKET; } @@ -24,7 +24,7 @@ SOCKET socket_initialize(void) { void socket_shutdown(SOCKET socket) { if (socket == INVALID_SOCKET) { return; } int rc = closesocket(socket); - if (rc == SOCKET_ERROR) { + if (rc == (int)SOCKET_ERROR) { LOG_ERROR("closesocket failed with error %d\n", SOCKET_LAST_ERROR); } } diff --git a/src/pc/utils/misc.c b/src/pc/utils/misc.c index 1163848c..b01000bc 100644 --- a/src/pc/utils/misc.c +++ b/src/pc/utils/misc.c @@ -77,4 +77,39 @@ f64 clock_elapsed_f64(void) { u32 clock_elapsed_ticks(void) { return (clock_elapsed_ns() * 3 / 100000000); -} \ No newline at end of file +} + +void file_get_line(char* buffer, size_t maxLength, FILE* fp) { + char* initial = buffer; + + char c = fgetc(fp); + while (!feof(fp) && c != '\n') { + // make sure it's printable + if (c < ' ' || c > '~') { goto next_get; } + + // parse new line escape code + if (c == '\\') { + c = fgetc(fp); + if (feof(fp)) { break; } + if (c == 'n') { + if ((size_t)(buffer - initial) < (maxLength - 1)) { + *buffer++ = '\n'; + } + goto next_get; + } + } + + // found new line + if (c == '\n') { break; } + + // append to buffer + if ((size_t)(buffer - initial) < (maxLength - 1)) { + *buffer++ = c; + } + +next_get: + c = fgetc(fp); + } + + *buffer = '\0'; +} diff --git a/src/pc/utils/misc.h b/src/pc/utils/misc.h index ff86e699..52677fea 100644 --- a/src/pc/utils/misc.h +++ b/src/pc/utils/misc.h @@ -1,10 +1,13 @@ #ifndef UTILS_MISC_H #define UTILS_MISC_H +#include + float smoothstep(float edge0, float edge1, float x); void update_all_mario_stars(void); f32 clock_elapsed(void); f64 clock_elapsed_f64(void); u32 clock_elapsed_ticks(void); +void file_get_line(char* buffer, size_t maxLength, FILE* fp); #endif \ No newline at end of file