/* Linux compatibility shims for MSVC-only CRT calls used throughout the original Windows codebase (fopen_s, _strdup, _stricmp, sprintf_s, ...), plus a path resolver: the shipped STB/AIP/quest data files embed Windows-style paths ("3DDATA\AI\FOO.AIP") with arbitrary case, which don't resolve on a case-sensitive Linux filesystem. rose_fopen() rewrites backslashes to '/' and, if that still doesn't exist, resolves each path component case-insensitively against the real directory contents before opening the file. */ #ifndef __ROSE_COMPAT_H__ #define __ROSE_COMPAT_H__ #ifndef _WIN32 #include #include #include #include #include #include #include #include #include #include #include #define __cdecl typedef int errno_t; typedef int INT; typedef unsigned int UINT; typedef long LONG; typedef unsigned long ULONG; inline FILE* rose_fopen(const char* path, const char* mode) { char norm[1024]; size_t n = 0; for (; path[n] != 0 && n < sizeof(norm) - 1; ++n) norm[n] = (path[n] == '\\') ? '/' : path[n]; norm[n] = 0; struct stat st; if (stat(norm, &st) == 0) return fopen(norm, mode); char resolved[1024]; size_t rlen = 0; resolved[0] = 0; const char* p = norm; if (*p == '/') { resolved[0] = '/'; resolved[1] = 0; rlen = 1; ++p; } while (*p) { const char* slash = strchr(p, '/'); size_t len = slash ? (size_t)(slash - p) : strlen(p); char component[256]; if (len >= sizeof(component)) len = sizeof(component) - 1; memcpy(component, p, len); component[len] = 0; char candidate[1024]; snprintf(candidate, sizeof(candidate), "%s%s", resolved, component); if (stat(candidate, &st) != 0) { char dirbuf[1024]; const char* dirpath = "."; if (rlen > 0) { size_t copylen = (rlen > 1 && resolved[rlen - 1] == '/') ? rlen - 1 : rlen; snprintf(dirbuf, sizeof(dirbuf), "%.*s", (int)copylen, resolved); dirpath = dirbuf[0] ? dirbuf : "/"; } DIR* d = opendir(dirpath); if (d) { struct dirent* de; while ((de = readdir(d)) != NULL) { if (strcasecmp(de->d_name, component) == 0) { snprintf(candidate, sizeof(candidate), "%s/%s", dirpath, de->d_name); break; } } closedir(d); } } rlen = snprintf(resolved, sizeof(resolved), "%s/", candidate); p = slash ? slash + 1 : p + len; } if (rlen > 0 && resolved[rlen - 1] == '/') resolved[rlen - 1] = 0; return fopen(resolved, mode); } inline errno_t rose_fopen_s(FILE** fh, const char* path, const char* mode) { *fh = rose_fopen(path, mode); return (*fh != NULL) ? 0 : 1; } #define fopen_s(pfh, path, mode) rose_fopen_s(pfh, path, mode) #define _strdup strdup #define _stricmp strcasecmp #define _strcmpi strcasecmp #define sprintf_s(buf, ...) sprintf(buf, __VA_ARGS__) #define vsprintf_s(buf, fmt, ap) vsprintf(buf, fmt, ap) inline errno_t rose_ctime_s(char* buf, size_t bufsz, const time_t* t) { return (ctime_r(t, buf) != NULL) ? 0 : 1; } #define ctime_s(buf, bufsz, t) rose_ctime_s(buf, bufsz, t) inline errno_t rose_strncpy_s(char* dst, const char* src, size_t count) { strncpy(dst, src, count); dst[count] = 0; return 0; } #define strncpy_s(dst, src, count) rose_strncpy_s(dst, src, count) // Resolves a literal IPv4 address ("10.0.0.5") *or* a hostname (docker // compose service name, e.g. "charserver") to a network-order IPv4 address, // so inter-server links (server2server config values) work with either. inline unsigned long rose_inet_addr(const char* host) { struct in_addr addr; if (inet_pton(AF_INET, host, &addr) == 1) return addr.s_addr; struct addrinfo hints; memset(&hints, 0, sizeof(hints)); hints.ai_family = AF_INET; hints.ai_socktype = SOCK_STREAM; struct addrinfo* res = NULL; if (getaddrinfo(host, NULL, &hints, &res) != 0 || res == NULL) return INADDR_NONE; unsigned long result = ((struct sockaddr_in*)res->ai_addr)->sin_addr.s_addr; freeaddrinfo(res); return result; } struct SYSTEMTIME { unsigned short wYear; unsigned short wMonth; unsigned short wDayOfWeek; unsigned short wDay; unsigned short wHour; unsigned short wMinute; unsigned short wSecond; unsigned short wMilliseconds; }; inline void GetLocalTime(SYSTEMTIME* out) { time_t rawtime = time(NULL); struct tm ltm; localtime_r(&rawtime, <m); out->wYear = (unsigned short)(ltm.tm_year + 1900); out->wMonth = (unsigned short)(ltm.tm_mon + 1); out->wDayOfWeek = (unsigned short)ltm.tm_wday; out->wDay = (unsigned short)ltm.tm_mday; out->wHour = (unsigned short)ltm.tm_hour; out->wMinute = (unsigned short)ltm.tm_min; out->wSecond = (unsigned short)ltm.tm_sec; out->wMilliseconds = 0; } #endif // !_WIN32 #endif // __ROSE_COMPAT_H__