#include "common.h" #include "crossplatform.h" // Codes compatible with Windows and Linux #ifndef _WIN32 // For internal use // wMilliseconds is not needed void tmToSystemTime(const tm *tm, SYSTEMTIME *out) { out->wYear = tm->tm_year + 1900; out->wMonth = tm->tm_mon + 1; out->wDayOfWeek = tm->tm_wday; out->wDay = tm->tm_mday; out->wHour = tm->tm_hour; out->wMinute = tm->tm_min; out->wSecond = tm->tm_sec; } void GetLocalTime_CP(SYSTEMTIME *out) { time_t timestamp = time(nil); tm *localTm = localtime(×tamp); tmToSystemTime(localTm, out); } #endif // Compatible with Linux/POSIX and MinGW on Windows #ifndef _WIN32 HANDLE FindFirstFile(const char* pathname, WIN32_FIND_DATA* firstfile) { char newpathname[64]; snprintf(newpathname, sizeof(newpathname), "E:/data/gta3/%s", pathname); char* path = strtok(newpathname, "\\*"); strncpy(firstfile->folder, path, sizeof(firstfile->folder)); // Both w/ extension and w/o extension is ok if (strlen(path) + 2 != strlen(pathname)) strncpy(firstfile->extension, strtok(NULL, "\\*"), sizeof(firstfile->extension)); else strncpy(firstfile->extension, "", sizeof(firstfile->extension)); HANDLE d; if ((d = (HANDLE)opendir(path)) == NULL || !FindNextFile(d, firstfile)) return NULL; return d; } bool FindNextFile(HANDLE d, WIN32_FIND_DATA* finddata) { dirent *file; static struct stat fileStats; static char path[PATH_MAX], relativepath[NAME_MAX + sizeof(finddata->folder) + 1]; int extensionLen = strlen(finddata->extension); while ((file = readdir((DIR*)d)) != NULL) { if ((extensionLen == 0 || strncmp(&file->d_name[strlen(file->d_name) - extensionLen], finddata->extension, extensionLen) == 0)) { sprintf(relativepath, "%s/%s", finddata->folder, file->d_name); // realpath(relativepath, path); strcpy(path, relativepath); stat(path, &fileStats); strncpy(finddata->cFileName, file->d_name, sizeof(finddata->cFileName)); finddata->ftLastWriteTime = fileStats.st_mtime; return true; } } return false; } void GetDateFormat(int unused1, int unused2, SYSTEMTIME* in, int unused3, char* out, int size) { tm linuxTime; linuxTime.tm_year = in->wYear - 1900; linuxTime.tm_mon = in->wMonth - 1; linuxTime.tm_wday = in->wDayOfWeek; linuxTime.tm_mday = in->wDay; linuxTime.tm_hour = in->wHour; linuxTime.tm_min = in->wMinute; linuxTime.tm_sec = in->wSecond; strftime(out, size, nl_langinfo(D_FMT), &linuxTime); } void FileTimeToSystemTime(time_t* writeTime, SYSTEMTIME* out) { tm *ptm = gmtime(writeTime); tmToSystemTime(ptm, out); } #endif // Because wchar length differs between platforms. wchar* AllocUnicode(const char* src) { wchar *dst = (wchar*)malloc(strlen(src)*2 + 2); wchar *i = dst; while((*i++ = (unsigned char)*src++) != '\0'); return dst; } // Funcs/features from Windows that we need on other platforms #ifndef _WIN32 char *strupr(char *s) { char* tmp = s; for (;*tmp;++tmp) { *tmp = toupper((unsigned char) *tmp); } return s; } char *strlwr(char *s) { char* tmp = s; for (;*tmp;++tmp) { *tmp = tolower((unsigned char) *tmp); } return s; } char *trim(char *s) { char *ptr; if (!s) return NULL; // handle NULL string if (!*s) return s; // handle empty string for (ptr = s + strlen(s) - 1; (ptr >= s) && isspace(*ptr); --ptr); ptr[1] = '\0'; return s; } FILE* _fcaseopen(char const* filename, char const* mode) { FILE* result; char* real = casepath(filename); if (!real) result = fopen(filename, mode); else { result = fopen(real, mode); free(real); } return result; } int _caserename(const char *old_filename, const char *new_filename) { int result; char *real_old = casepath(old_filename); char *real_new = casepath(new_filename); // hack so we don't even try to rename it to new_filename if it already exists if (!real_new) { free(real_old); return -1; } if (!real_old) result = rename(old_filename, real_new); else result = rename(real_old, real_new); free(real_old); free(real_new); return result; } // Case-insensitivity on linux (from https://github.com/OneSadCookie/fcaseopen) // Returned string should freed manually (if exists) char* casepath(char const* path, bool checkPathFirst) { size_t l = strlen(path); char data_path[512]; getcwd(data_path, sizeof(data_path)); char* out = (char*)malloc(l + strlen(data_path) + 2); if (strncmp(path, "E:", 2) == 0 || strncmp(path, "F:", 2) == 0) { strcpy(out, path); } else { sprintf(out, "%s/%s", data_path, path); } l = strlen(out); for (int i = l-1; i >= 0; i--) { if (out[i] == '\\' || out[i] == '/' || out[i] == ' ') out[i] = '\0'; else break; } return out; } #endif #ifdef __SWITCH__ /* Taken from glibc */ char *realpath(const char *name, char *resolved) { char *rpath, *dest = NULL; const char *start, *end, *rpath_limit; long int path_max; /* As per Single Unix Specification V2 we must return an error if either parameter is a null pointer. We extend this to allow the RESOLVED parameter to be NULL in case the we are expected to allocate the room for the return value. */ if (!name) return NULL; /* As per Single Unix Specification V2 we must return an error if the name argument points to an empty string. */ if (name[0] == '\0') return NULL; #ifdef PATH_MAX path_max = PATH_MAX; #else path_max = pathconf(name, _PC_PATH_MAX); if (path_max <= 0) path_max = 1024; #endif if (!resolved) { rpath = (char*)malloc(path_max); if (!rpath) return NULL; } else rpath = resolved; rpath_limit = rpath + path_max; if (name[0] != '/') { if (!getcwd(rpath, path_max)) { rpath[0] = '\0'; goto error; } dest = (char*)memchr(rpath, '\0', path_max); } else { rpath[0] = '/'; dest = rpath + 1; } for (start = end = name; *start; start = end) { /* Skip sequence of multiple path-separators. */ while (*start == '/') ++start; /* Find end of path component. */ for (end = start; *end && *end != '/'; ++end) /* Nothing. */; if (end - start == 0) break; else if (end - start == 1 && start[0] == '.') /* nothing */; else if (end - start == 2 && start[0] == '.' && start[1] == '.') { /* Back up to previous component, ignore if at root already. */ if (dest > rpath + 1) while ((--dest)[-1] != '/') ; } else { size_t new_size; if (dest[-1] != '/') *dest++ = '/'; if (dest + (end - start) >= rpath_limit) { ptrdiff_t dest_offset = dest - rpath; char *new_rpath; if (resolved) { if (dest > rpath + 1) dest--; *dest = '\0'; goto error; } new_size = rpath_limit - rpath; if (end - start + 1 > path_max) new_size += end - start + 1; else new_size += path_max; new_rpath = (char *)realloc(rpath, new_size); if (!new_rpath) goto error; rpath = new_rpath; rpath_limit = rpath + new_size; dest = rpath + dest_offset; } dest = (char*)memcpy(dest, start, end - start); *dest = '\0'; } } if (dest > rpath + 1 && dest[-1] == '/') --dest; *dest = '\0'; return rpath; error: if (!resolved) free(rpath); return NULL; } ssize_t readlink (const char * __path, char * __buf, size_t __buflen) { errno = ENOSYS; return -1; } #endif