GroveEngine/include/grove/platform/FileSystem.h
StillHammer 0540fbf526 fix: Resolve bgfx Frame 1 crash on Windows DLL + MinGW GCC 15 compatibility
- Add BGFX_CONFIG_MULTITHREADED=0 to fix TLS crash when bgfx runs from DLL
- Add -include stdint.h for MinGW GCC 15+ compatibility with bgfx third-party code
- Guard SDL2-dependent visual tests with if(SDL2_FOUND)
- Clean up debug logging in BgfxDevice::frame() and BgfxRendererModule::process()
- Re-enable all modules in test_full_stack_interactive.cpp
- Add grove::fs namespace for cross-platform filesystem operations
- Add InputModule C export for feedEvent across DLL boundary

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-30 11:03:06 +07:00

206 lines
4.8 KiB
C++

#pragma once
/**
* Platform-independent filesystem utilities
* Replaces std::filesystem to avoid MinGW static initialization crash
*/
#include <string>
#include <vector>
#include <fstream>
#ifdef _WIN32
#include <direct.h>
#include <io.h>
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#else
#include <sys/stat.h>
#include <sys/types.h>
#include <dirent.h>
#include <unistd.h>
#endif
namespace grove {
namespace fs {
/**
* Check if a file or directory exists
*/
inline bool exists(const std::string& path) {
#ifdef _WIN32
DWORD attrs = GetFileAttributesA(path.c_str());
return attrs != INVALID_FILE_ATTRIBUTES;
#else
struct stat st;
return stat(path.c_str(), &st) == 0;
#endif
}
/**
* Check if path is a directory
*/
inline bool isDirectory(const std::string& path) {
#ifdef _WIN32
DWORD attrs = GetFileAttributesA(path.c_str());
return (attrs != INVALID_FILE_ATTRIBUTES) && (attrs & FILE_ATTRIBUTE_DIRECTORY);
#else
struct stat st;
if (stat(path.c_str(), &st) != 0) return false;
return S_ISDIR(st.st_mode);
#endif
}
/**
* Check if path is a regular file
*/
inline bool isFile(const std::string& path) {
#ifdef _WIN32
DWORD attrs = GetFileAttributesA(path.c_str());
return (attrs != INVALID_FILE_ATTRIBUTES) && !(attrs & FILE_ATTRIBUTE_DIRECTORY);
#else
struct stat st;
if (stat(path.c_str(), &st) != 0) return false;
return S_ISFILE(st.st_mode);
#endif
}
/**
* Create a single directory
*/
inline bool createDirectory(const std::string& path) {
#ifdef _WIN32
return _mkdir(path.c_str()) == 0 || errno == EEXIST;
#else
return mkdir(path.c_str(), 0755) == 0 || errno == EEXIST;
#endif
}
/**
* Create directories recursively
*/
inline bool createDirectories(const std::string& path) {
std::string currentPath;
for (size_t i = 0; i < path.size(); ++i) {
char c = path[i];
currentPath += c;
if (c == '/' || c == '\\') {
if (!currentPath.empty() && currentPath != "/" && currentPath != "\\") {
createDirectory(currentPath);
}
}
}
if (!currentPath.empty()) {
return createDirectory(currentPath);
}
return true;
}
/**
* Get the parent path
*/
inline std::string parentPath(const std::string& path) {
size_t pos = path.find_last_of("/\\");
if (pos == std::string::npos) return "";
if (pos == 0) return path.substr(0, 1);
return path.substr(0, pos);
}
/**
* Get the filename from a path
*/
inline std::string filename(const std::string& path) {
size_t pos = path.find_last_of("/\\");
if (pos == std::string::npos) return path;
return path.substr(pos + 1);
}
/**
* Get the file extension (including the dot)
*/
inline std::string extension(const std::string& path) {
std::string name = filename(path);
size_t pos = name.find_last_of('.');
if (pos == std::string::npos) return "";
return name.substr(pos);
}
/**
* Get the filename without extension (stem)
*/
inline std::string stem(const std::string& path) {
std::string name = filename(path);
size_t pos = name.find_last_of('.');
if (pos == std::string::npos) return name;
return name.substr(0, pos);
}
/**
* Get file size
*/
inline size_t fileSize(const std::string& path) {
#ifdef _WIN32
WIN32_FILE_ATTRIBUTE_DATA fad;
if (!GetFileAttributesExA(path.c_str(), GetFileExInfoStandard, &fad)) {
return 0;
}
return (static_cast<size_t>(fad.nFileSizeHigh) << 32) | fad.nFileSizeLow;
#else
struct stat st;
if (stat(path.c_str(), &st) != 0) return 0;
return static_cast<size_t>(st.st_size);
#endif
}
/**
* List files in a directory
*/
inline std::vector<std::string> listDirectory(const std::string& path) {
std::vector<std::string> result;
#ifdef _WIN32
WIN32_FIND_DATAA fd;
std::string searchPath = path + "\\*";
HANDLE hFind = FindFirstFileA(searchPath.c_str(), &fd);
if (hFind != INVALID_HANDLE_VALUE) {
do {
std::string name = fd.cFileName;
if (name != "." && name != "..") {
result.push_back(name);
}
} while (FindNextFileA(hFind, &fd));
FindClose(hFind);
}
#else
DIR* dir = opendir(path.c_str());
if (dir) {
struct dirent* entry;
while ((entry = readdir(dir)) != nullptr) {
std::string name = entry->d_name;
if (name != "." && name != "..") {
result.push_back(name);
}
}
closedir(dir);
}
#endif
return result;
}
/**
* Copy a file
*/
inline bool copyFile(const std::string& from, const std::string& to) {
#ifdef _WIN32
return CopyFileA(from.c_str(), to.c_str(), FALSE) != 0;
#else
std::ifstream src(from, std::ios::binary);
std::ofstream dst(to, std::ios::binary);
if (!src || !dst) return false;
dst << src.rdbuf();
return true;
#endif
}
} // namespace fs
} // namespace grove