-
Notifications
You must be signed in to change notification settings - Fork 98
make linux case-insensitive when loading files #4531
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull request overview
This PR adds a Unix-like (non-Windows) case-insensitive file lookup path so the engine can find/open/delete files even when the requested filename casing doesn’t match what’s on disk (helpful when running content authored for case-insensitive filesystems).
Changes:
- Added a non-Windows helper that scans a directory and matches filenames via
strcasecmp(). - Updated
LbFileExists,LbFileOpen,LbFileLength, andLbFileDeleteto use case-insensitive resolution when the exact path doesn’t exist. - Simplified
create_directory_for_file()platform handling to treat all non-Windows platforms as POSIXmkdir(path, mode).
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| char dir_path[PATH_MAX]; | ||
| size_t dir_len = 0; |
Copilot
AI
Feb 9, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
PATH_MAX is used for dir_path/actual_fname stack buffers, but PATH_MAX is not guaranteed to be defined by <limits.h> on all Unix-like targets. Consider adding a local fallback guard (#ifndef PATH_MAX ...) like the one in src/custom_sprites.c to avoid build failures on platforms where it’s missing.
| // Split fname into directory and filename | ||
| const char *last_slash = strrchr(fname, '/'); | ||
| const char *filename; | ||
| char dir_path[PATH_MAX]; | ||
| size_t dir_len = 0; | ||
|
|
||
| if (last_slash != NULL) { | ||
| dir_len = last_slash - fname; | ||
| if (dir_len >= sizeof(dir_path)) { | ||
| return 0; | ||
| } | ||
| if (dir_len > 0) { | ||
| memcpy(dir_path, fname, dir_len); | ||
| dir_path[dir_len] = '\0'; | ||
| } else { | ||
| strcpy(dir_path, "/"); | ||
| } | ||
| filename = last_slash + 1; | ||
| } else { | ||
| strcpy(dir_path, "."); | ||
| filename = fname; | ||
| } | ||
|
|
||
| DIR *dir = opendir(dir_path); | ||
| if (dir == NULL) { | ||
| return 0; | ||
| } |
Copilot
AI
Feb 9, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
find_case_insensitive_file() only case-normalizes the final path component. If the directory portion of fname has case mismatches (e.g. Data/Levels/file.dat vs data/levels/file.dat), opendir(dir_path) will fail and the lookup won’t be case-insensitive for the full path. If the intent of this PR is “case-insensitive when loading files” for full paths, consider resolving each path component case-insensitively while walking the directory tree.
| // Try to find the file case-insensitively on Unix-like systems | ||
| if (access(fname, F_OK) != 0 && find_case_insensitive_file(fname, actual_fname, sizeof(actual_fname))) { | ||
| open_fname = actual_fname; | ||
| } | ||
| #endif | ||
|
|
||
| if ( !LbFileExists(fname) ) |
Copilot
AI
Feb 9, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In LbFileOpen(), the non-Windows path resolution does access() + find_case_insensitive_file(), but then the code still calls LbFileExists(fname), which will repeat access() and potentially repeat the directory scan. Consider restructuring to avoid the double lookup (e.g., base existence checks on open_fname, or have a single helper that both resolves and reports existence).
| // Try to find the file case-insensitively on Unix-like systems | |
| if (access(fname, F_OK) != 0 && find_case_insensitive_file(fname, actual_fname, sizeof(actual_fname))) { | |
| open_fname = actual_fname; | |
| } | |
| #endif | |
| if ( !LbFileExists(fname) ) | |
| int access_rc = access(fname, F_OK); | |
| // Try to find the file case-insensitively on Unix-like systems | |
| if (access_rc != 0 && find_case_insensitive_file(fname, actual_fname, sizeof(actual_fname))) { | |
| open_fname = actual_fname; | |
| } | |
| #endif | |
| #if !defined(_WIN32) | |
| int file_exists = (access_rc == 0) || (open_fname != fname); | |
| #else | |
| int file_exists = LbFileExists(fname); | |
| #endif | |
| if ( !file_exists ) |
| static int find_case_insensitive_file(const char *fname, char *actual_fname, size_t buflen) | ||
| { | ||
| // Split fname into directory and filename | ||
| const char *last_slash = strrchr(fname, '/'); | ||
| const char *filename; | ||
| char dir_path[PATH_MAX]; | ||
| size_t dir_len = 0; | ||
|
|
||
| if (last_slash != NULL) { |
Copilot
AI
Feb 9, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The new find_case_insensitive_file() helper uses 4-space indentation, while the surrounding code in this file predominantly uses 2-space indentation. Aligning the indentation/style here will keep formatting consistent and reduce diff noise in future edits.
| const char *open_fname = fname; | ||
| #if !defined(_WIN32) | ||
| char actual_fname[PATH_MAX]; | ||
| // Try to find the file case-insensitively on Unix-like systems | ||
| if (access(fname, F_OK) != 0 && find_case_insensitive_file(fname, actual_fname, sizeof(actual_fname))) { | ||
| open_fname = actual_fname; | ||
| } | ||
| #endif | ||
| TbFileHandle handle = fopen(open_fname, "rb"); |
Copilot
AI
Feb 9, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The case-insensitive path resolution logic (access(...) != 0 && find_case_insensitive_file(...)) is duplicated in LbFileOpen, LbFileLength, and LbFileDelete. Consider factoring this into a small internal helper to prevent behavior from drifting between call sites (and to centralize any future improvements, like handling directory-component case mismatches).
No description provided.