#include "Utils.hpp" #ifdef _WIN32 # include #endif #ifdef _WIN32 # define BRUSSEL_MODE_STRING(string) L##string #else # define BRUSSEL_MODE_STRING(string) string #endif #if _WIN32 using FopenModeString = const wchar_t*; #else using FopenModeString = const char*; #endif static FopenModeString GetModeString(Utils::IoMode mode, bool binary) { using namespace Utils; if (binary) { switch (mode) { case Read: return BRUSSEL_MODE_STRING("rb"); case WriteTruncate: return BRUSSEL_MODE_STRING("wb"); case WriteAppend: return BRUSSEL_MODE_STRING("ab"); } } else { switch (mode) { case Read: return BRUSSEL_MODE_STRING("r"); case WriteTruncate: return BRUSSEL_MODE_STRING("w"); case WriteAppend: return BRUSSEL_MODE_STRING("a"); } } return nullptr; } FILE* Utils::OpenCstdioFile(const std::filesystem::path& path, IoMode mode, bool binary) { #ifdef _WIN32 // std::filesystem::path::c_str() returns `const wchar_t*` under Windows, because NT uses UTF-16 natively // NOTE: _wfopen() only affects the type of path parameter, otherwise the file stream created is identical to the one by fopen() return _wfopen(path.c_str(), ::GetModeString(mode, binary)); #else return fopen(path.c_str(), ::GetModeString(mode, binary)); #endif } FILE* Utils::OpenCstdioFile(const char* path, IoMode mode, bool binary) { #ifdef _WIN32 // On Windows, fopen() accepts ANSI codepage encoded path, convert our UTF-8 string to UTF-16 to ensure that no matter what the locale is, the path continues to work WCHAR platformPath[MAX_PATH]; if (MultiByteToWideChar(CP_UTF8, 0, path, -1, platformPath, MAX_PATH) == 0) { return nullptr; } return _wfopen(platformPath, ::GetModeString(mode, binary)); #else return fopen(path, ::GetModeString(mode, binary)); #endif } bool Utils::InRangeInclusive(int n, int lower, int upper) { if (lower > upper) { std::swap(lower, upper); } return n >= lower && n <= upper; } bool Utils::LineContains(glm::ivec2 p1, glm::ivec2 p2, glm::ivec2 candidate) { bool verticalLine = p1.x == p2.x && InRangeInclusive(candidate.x, p1.x, p2.x); bool horizontalLine = p1.y == p2.y && InRangeInclusive(candidate.y, p1.y, p2.y); return verticalLine && horizontalLine; } bool Utils::IsColinear(glm::ivec2 p1, glm::ivec2 p2) { return p1.x == p2.x || p1.y == p2.y; }