pavone@495: #include pavone@495: #include pavone@495: #include pavone@495: #include pavone@768: #include pavone@805: #include pavone@495: pavone@496: #include pavone@496: #include pavone@496: #include pavone@955: #include pavone@496: pavone@879: #ifdef __ANDROID__ pavone@879: #include pavone@879: #define info_puts(msg) __android_log_write(ANDROID_LOG_INFO, "BlastEm", msg) pavone@879: #define warning_puts(msg) __android_log_write(ANDROID_LOG_WARN, "BlastEm", msg) pavone@879: #define fatal_puts(msg) __android_log_write(ANDROID_LOG_FATAL, "BlastEm", msg) pavone@879: pavone@879: #define info_printf(msg, args) __android_log_vprint(ANDROID_LOG_INFO, "BlastEm", msg, args) pavone@879: #define warning_printf(msg, args) __android_log_vprint(ANDROID_LOG_WARN, "BlastEm", msg, args) pavone@879: #define fatal_printf(msg, args) __android_log_vprint(ANDROID_LOG_FATAL, "BlastEm", msg, args) pavone@879: #else pavone@884: #define info_puts(msg) fputs(msg, stdout); pavone@884: #define warning_puts(msg) fputs(msg, stderr); pavone@884: #define fatal_puts(msg) fputs(msg, stderr); pavone@879: pavone@883: #define info_printf(msg, args) vprintf(msg, args) pavone@883: #define warning_printf(msg, args) vfprintf(stderr, msg, args) pavone@883: #define fatal_printf(msg, args) vfprintf(stderr, msg, args) pavone@879: #endif pavone@879: pavone@792: #include "blastem.h" //for headless global pavone@792: #include "render.h" //for render_errorbox pavone@866: #include "util.h" pavone@792: pavone@876: char * alloc_concat(char const * first, char const * second) pavone@495: { pavone@495: int flen = strlen(first); pavone@495: int slen = strlen(second); pavone@495: char * ret = malloc(flen + slen + 1); pavone@495: memcpy(ret, first, flen); pavone@495: memcpy(ret+flen, second, slen+1); pavone@495: return ret; pavone@495: } pavone@495: pavone@876: char * alloc_concat_m(int num_parts, char const ** parts) pavone@495: { pavone@495: int total = 0; pavone@495: for (int i = 0; i < num_parts; i++) { pavone@495: total += strlen(parts[i]); pavone@495: } pavone@495: char * ret = malloc(total + 1); pavone@495: *ret = 0; pavone@495: for (int i = 0; i < num_parts; i++) { pavone@495: strcat(ret, parts[i]); pavone@495: } pavone@495: return ret; pavone@495: } pavone@495: pavone@495: long file_size(FILE * f) pavone@495: { pavone@495: fseek(f, 0, SEEK_END); pavone@495: long fsize = ftell(f); pavone@495: fseek(f, 0, SEEK_SET); pavone@495: return fsize; pavone@495: } pavone@495: pavone@495: char * strip_ws(char * text) pavone@495: { pavone@495: while (*text && (!isprint(*text) || isblank(*text))) pavone@495: { pavone@495: text++; pavone@495: } pavone@495: char * ret = text; pavone@495: text = ret + strlen(ret) - 1; pavone@495: while (text > ret && (!isprint(*text) || isblank(*text))) pavone@495: { pavone@495: *text = 0; pavone@495: text--; pavone@495: } pavone@495: return ret; pavone@495: } pavone@495: pavone@495: char * split_keyval(char * text) pavone@495: { pavone@495: while (*text && !isblank(*text)) pavone@495: { pavone@495: text++; pavone@495: } pavone@495: if (!*text) { pavone@495: return text; pavone@495: } pavone@495: *text = 0; pavone@495: return text+1; pavone@495: } pavone@496: pavone@1008: char is_path_sep(char c) pavone@1008: { pavone@1008: #ifdef _WIN32 pavone@1008: if (c == '\\') { pavone@1008: return 1; pavone@1008: } pavone@1008: #endif pavone@1008: return c == '/'; pavone@1008: } pavone@1008: pavone@1008: char is_absolute_path(char *path) pavone@1008: { pavone@1008: #ifdef _WIN32 pavone@1008: if (path[1] == ':' && is_path_sep(path[2]) && isalpha(path[0])) { pavone@1008: return 1; pavone@1008: } pavone@1008: #endif pavone@1008: return is_path_sep(path[0]); pavone@1008: } pavone@1008: pavone@955: char * basename_no_extension(char *path) pavone@955: { pavone@955: char *lastdot = NULL; pavone@955: char *lastslash = NULL; pavone@955: char *cur; pavone@955: for (cur = path; *cur; cur++) pavone@955: { pavone@955: if (*cur == '.') { pavone@955: lastdot = cur; pavone@1008: } else if (is_path_sep(*cur)) { pavone@955: lastslash = cur + 1; pavone@955: } pavone@955: } pavone@955: if (!lastdot) { pavone@955: lastdot = cur; pavone@955: } pavone@955: if (!lastslash) { pavone@955: lastslash = path; pavone@955: } pavone@955: char *barename = malloc(lastdot-lastslash+1); pavone@955: memcpy(barename, lastslash, lastdot-lastslash); pavone@955: barename[lastdot-lastslash] = 0; pavone@955: pavone@955: return barename; pavone@955: } pavone@955: pavone@768: uint32_t nearest_pow2(uint32_t val) pavone@768: { pavone@768: uint32_t ret = 1; pavone@768: while (ret < val) pavone@768: { pavone@768: ret = ret << 1; pavone@768: } pavone@768: return ret; pavone@768: } pavone@768: pavone@496: static char * exe_str; pavone@496: pavone@496: void set_exe_str(char * str) pavone@496: { pavone@496: exe_str = str; pavone@496: } pavone@496: pavone@792: void fatal_error(char *format, ...) pavone@792: { pavone@792: va_list args; pavone@792: va_start(args, format); pavone@792: if (!headless) { pavone@792: //take a guess at the final size pavone@792: size_t size = strlen(format) * 2; pavone@792: char *buf = malloc(size); pavone@792: size_t actual = vsnprintf(buf, size, format, args); pavone@792: if (actual >= size) { pavone@792: actual++; pavone@792: free(buf); pavone@792: buf = malloc(actual); pavone@792: va_end(args); pavone@792: va_start(args, format); pavone@792: vsnprintf(buf, actual, format, args); pavone@792: } pavone@879: fatal_puts(buf); pavone@792: render_errorbox("Fatal Error", buf); pavone@792: free(buf); pavone@792: } else { pavone@879: fatal_printf(format, args); pavone@792: } pavone@792: va_end(args); pavone@792: exit(1); pavone@792: } pavone@792: pavone@794: void warning(char *format, ...) pavone@794: { pavone@794: va_list args; pavone@794: va_start(args, format); pavone@794: #ifndef _WIN32 pavone@794: if (headless || (isatty(STDERR_FILENO) && isatty(STDIN_FILENO))) { pavone@879: warning_printf(format, args); pavone@794: } else { pavone@794: #endif pavone@794: size_t size = strlen(format) * 2; pavone@794: char *buf = malloc(size); pavone@794: size_t actual = vsnprintf(buf, size, format, args); pavone@794: if (actual >= size) { pavone@794: actual++; pavone@794: free(buf); pavone@794: buf = malloc(actual); pavone@794: va_end(args); pavone@794: va_start(args, format); pavone@794: vsnprintf(buf, actual, format, args); pavone@794: } pavone@879: warning_puts(buf); pavone@794: render_infobox("BlastEm Info", buf); pavone@794: free(buf); pavone@794: #ifndef _WIN32 pavone@794: } pavone@794: #endif pavone@794: va_end(args); pavone@794: } pavone@794: pavone@792: void info_message(char *format, ...) pavone@792: { pavone@792: va_list args; pavone@792: va_start(args, format); pavone@792: #ifndef _WIN32 pavone@792: if (headless || (isatty(STDOUT_FILENO) && isatty(STDIN_FILENO))) { pavone@879: info_printf(format, args); pavone@792: } else { pavone@792: #endif pavone@792: size_t size = strlen(format) * 2; pavone@792: char *buf = malloc(size); pavone@792: size_t actual = vsnprintf(buf, size, format, args); pavone@792: if (actual >= size) { pavone@792: actual++; pavone@792: free(buf); pavone@792: buf = malloc(actual); pavone@792: va_end(args); pavone@792: va_start(args, format); pavone@792: vsnprintf(buf, actual, format, args); pavone@792: } pavone@879: info_puts(buf); pavone@792: render_infobox("BlastEm Info", buf); pavone@792: free(buf); pavone@792: #ifndef _WIN32 pavone@792: } pavone@792: #endif pavone@792: va_end(args); pavone@792: } pavone@792: pavone@742: #ifdef _WIN32 heuripedes@795: #include heuripedes@795: #include pavone@742: pavone@742: char * get_home_dir() pavone@742: { pavone@742: static char path[MAX_PATH]; pavone@742: SHGetFolderPathA(NULL, CSIDL_PROFILE, NULL, 0, path); pavone@742: return path; pavone@742: } pavone@742: pavone@742: char * get_exe_dir() pavone@742: { pavone@742: static char path[MAX_PATH]; pavone@742: HMODULE module = GetModuleHandleA(NULL); pavone@742: GetModuleFileNameA(module, path, MAX_PATH); pavone@742: pavone@742: int pathsize = strlen(path); pavone@742: for(char * cur = path + pathsize - 1; cur != path; cur--) pavone@742: { pavone@742: if (*cur == '\\') { pavone@742: *cur = 0; pavone@742: break; pavone@742: } pavone@742: } pavone@742: return path; pavone@742: } pavone@742: pavone@972: dir_entry *get_dir_list(char *path, size_t *numret) pavone@972: { pavone@972: HANDLE dir; pavone@972: WIN32_FIND_DATA file; pavone@972: char *pattern = alloc_concat(path, "/*.*"); pavone@972: dir = FindFirstFile(pattern, &file); pavone@972: free(pattern); pavone@972: if (dir == INVALID_HANDLE_VALUE) { pavone@972: if (numret) { pavone@972: *numret = 0; pavone@972: } pavone@972: return NULL; pavone@972: } pavone@972: pavone@972: size_t storage = 64; pavone@972: dir_entry *ret = malloc(sizeof(dir_entry) * storage); pavone@972: size_t pos = 0; pavone@972: pavone@972: do { pavone@972: if (pos == storage) { pavone@972: storage = storage * 2; pavone@972: ret = realloc(ret, sizeof(dir_entry) * storage); pavone@972: } pavone@972: ret[pos].name = strdup(file.cFileName); pavone@972: ret[pos++].is_dir = (file.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0; pavone@972: } while (FindNextFile(dir, &file)); pavone@972: pavone@972: FindClose(dir); pavone@972: if (numret) { pavone@972: *numret = pos; pavone@972: } pavone@972: return ret; pavone@972: } pavone@972: pavone@972: time_t get_modification_time(char *path) pavone@972: { pavone@974: HANDLE results; pavone@974: WIN32_FIND_DATA file; pavone@974: results = FindFirstFile(path, &file); pavone@974: if (results == INVALID_HANDLE_VALUE) { pavone@972: return 0; pavone@972: } pavone@974: FindClose(results); pavone@974: uint64_t wintime = ((uint64_t)file.ftLastWriteTime.dwHighDateTime) << 32 | file.ftLastWriteTime.dwLowDateTime; pavone@974: //convert to seconds pavone@974: wintime /= 10000000; pavone@974: //adjust for difference between Windows and Unix Epoch pavone@974: wintime -= 11644473600LL; pavone@974: return (time_t)wintime; pavone@972: } pavone@972: pavone@972: int ensure_dir_exists(char *path) pavone@972: { pavone@972: if (CreateDirectory(path, NULL)) { pavone@972: return 1; pavone@972: } pavone@972: if (GetLastError() == ERROR_ALREADY_EXISTS) { pavone@972: return 1; pavone@972: } pavone@972: if (GetLastError() != ERROR_PATH_NOT_FOUND) { pavone@972: warning("CreateDirectory failed with unexpected error code %X\n", GetLastError()); pavone@972: return 0; pavone@972: } pavone@972: char *parent = strdup(path); pavone@1008: //Windows technically supports both native and Unix-style path separators pavone@1008: //so search for both pavone@1008: char *sep = strrchr(parent, '\\'); pavone@1008: char *osep = strrchr(parent, '/'); pavone@1008: if (osep && (!sep || osep < sep)) { pavone@1008: sep = osep; pavone@1008: } pavone@972: if (!sep || sep == parent) { pavone@972: //relative path, but for some reason we failed pavone@972: return 0; pavone@972: } pavone@972: *sep = 0; pavone@972: if (!ensure_dir_exists(parent)) { pavone@972: free(parent); pavone@972: return 0; pavone@972: } pavone@972: free(parent); pavone@972: return CreateDirectory(path, NULL); pavone@972: } pavone@972: pavone@742: #else pavone@742: pavone@742: char * get_home_dir() pavone@742: { pavone@742: return getenv("HOME"); pavone@742: } pavone@742: pavone@496: char * readlink_alloc(char * path) pavone@496: { pavone@496: char * linktext = NULL; pavone@496: ssize_t linksize = 512; pavone@496: ssize_t cursize = 0; pavone@496: do { pavone@496: if (linksize > cursize) { pavone@496: cursize = linksize; pavone@496: if (linktext) { pavone@496: free(linktext); pavone@496: } pavone@496: } pavone@496: linktext = malloc(cursize); pavone@496: linksize = readlink(path, linktext, cursize-1); pavone@496: if (linksize == -1) { pavone@496: perror("readlink"); pavone@496: free(linktext); pavone@559: return NULL; pavone@496: } pavone@549: } while ((linksize+1) > cursize); pavone@549: linktext[linksize] = 0; pavone@496: return linktext; pavone@496: } pavone@496: pavone@496: char * get_exe_dir() pavone@496: { pavone@496: static char * exe_dir; pavone@496: if (!exe_dir) { pavone@762: char * cur; pavone@763: #ifdef HAS_PROC pavone@496: char * linktext = readlink_alloc("/proc/self/exe"); pavone@496: if (!linktext) { pavone@496: goto fallback; pavone@496: } pavone@496: int linksize = strlen(linktext); pavone@496: for(cur = linktext + linksize - 1; cur != linktext; cur--) pavone@496: { pavone@1008: if (is_path_sep(*cur)) { pavone@496: *cur = 0; pavone@496: break; pavone@496: } pavone@496: } pavone@496: if (cur == linktext) { pavone@496: free(linktext); pavone@496: fallback: pavone@762: #endif pavone@496: if (!exe_str) { pavone@496: fputs("/proc/self/exe is not available and set_exe_str was not called!", stderr); pavone@496: } pavone@496: int pathsize = strlen(exe_str); pavone@496: for(cur = exe_str + pathsize - 1; cur != exe_str; cur--) pavone@496: { pavone@1008: if (is_path_sep(*cur)) { pavone@496: exe_dir = malloc(cur-exe_str+1); pavone@496: memcpy(exe_dir, exe_str, cur-exe_str); pavone@496: exe_dir[cur-exe_str] = 0; pavone@496: break; pavone@496: } pavone@496: } pavone@763: #ifdef HAS_PROC pavone@496: } else { pavone@496: exe_dir = linktext; pavone@496: } pavone@762: #endif pavone@496: } pavone@496: return exe_dir; pavone@496: } pavone@866: #include pavone@866: pavone@866: dir_entry *get_dir_list(char *path, size_t *numret) pavone@866: { pavone@866: DIR *d = opendir(path); pavone@866: if (!d) { pavone@866: if (numret) { pavone@866: *numret = 0; pavone@866: } pavone@866: return NULL; pavone@866: } pavone@866: size_t storage = 64; pavone@866: dir_entry *ret = malloc(sizeof(dir_entry) * storage); pavone@866: size_t pos = 0; pavone@866: struct dirent* entry; pavone@866: while (entry = readdir(d)) pavone@866: { pavone@866: if (entry->d_type != DT_REG && entry->d_type != DT_LNK && entry->d_type != DT_DIR) { pavone@866: continue; pavone@866: } pavone@866: if (pos == storage) { pavone@866: storage = storage * 2; pavone@866: ret = realloc(ret, sizeof(dir_entry) * storage); pavone@866: } pavone@866: ret[pos].name = strdup(entry->d_name); pavone@866: ret[pos++].is_dir = entry->d_type == DT_DIR; pavone@866: } pavone@866: if (numret) { pavone@866: *numret = pos; pavone@866: } pavone@866: return ret; pavone@866: } pavone@866: pavone@957: time_t get_modification_time(char *path) pavone@957: { pavone@957: struct stat st; pavone@957: if (stat(path, &st)) { pavone@957: return 0; pavone@957: } pavone@1021: #ifdef __APPLE__ pavone@1021: return st.st_mtimespec.tv_sec; pavone@1021: #else pavone@957: return st.st_mtim.tv_sec; pavone@1021: #endif pavone@957: } pavone@957: pavone@955: int ensure_dir_exists(char *path) pavone@955: { pavone@955: struct stat st; pavone@955: if (stat(path, &st)) { pavone@955: if (errno == ENOENT) { pavone@955: char *parent = strdup(path); pavone@955: char *sep = strrchr(parent, '/'); pavone@955: if (sep && sep != parent) { pavone@955: *sep = 0; pavone@955: if (!ensure_dir_exists(parent)) { pavone@955: free(parent); pavone@955: return 0; pavone@955: } pavone@955: free(parent); pavone@955: } pavone@955: return mkdir(path, 0777) == 0; pavone@955: } else { pavone@955: char buf[80]; pavone@955: strerror_r(errno, buf, sizeof(buf)); pavone@955: warning("stat failed with error: %s", buf); pavone@955: return 0; pavone@955: } pavone@955: } pavone@955: return S_ISDIR(st.st_mode); pavone@955: } pavone@955: pavone@741: #endif pavone@875: pavone@972: void free_dir_list(dir_entry *list, size_t numentries) pavone@972: { pavone@972: for (size_t i = 0; i < numentries; i++) pavone@972: { pavone@972: free(list[i].name); pavone@972: } pavone@972: free(list); pavone@972: } pavone@972: pavone@875: #ifdef __ANDROID__ pavone@875: pavone@875: #include pavone@875: char *read_bundled_file(char *name, long *sizeret) pavone@875: { pavone@876: SDL_RWops *rw = SDL_RWFromFile(name, "rb"); pavone@875: if (!rw) { pavone@875: if (sizeret) { pavone@875: *sizeret = -1; pavone@875: } pavone@875: return NULL; pavone@875: } pavone@875: pavone@875: long fsize = rw->size(rw); pavone@875: if (sizeret) { pavone@875: *sizeret = fsize; pavone@875: } pavone@875: char *ret; pavone@875: if (fsize) { pavone@875: ret = malloc(fsize); pavone@875: if (SDL_RWread(rw, ret, 1, fsize) != fsize) { pavone@875: free(ret); pavone@875: ret = NULL; pavone@875: } pavone@875: } else { pavone@875: ret = NULL; pavone@875: } pavone@875: SDL_RWclose(rw); pavone@875: return ret; pavone@875: } pavone@875: pavone@876: char const *get_config_dir() pavone@875: { pavone@875: return SDL_AndroidGetInternalStoragePath(); pavone@875: } pavone@875: pavone@955: char const *get_save_dir() pavone@955: { pavone@955: return SDL_AndroidGetInternalStoragePath(); pavone@955: } pavone@955: pavone@875: #else pavone@875: pavone@875: char *read_bundled_file(char *name, long *sizeret) pavone@875: { pavone@875: char *exe_dir = get_exe_dir(); pavone@875: if (!exe_dir) { pavone@875: if (sizeret) { pavone@875: *sizeret = -1; pavone@875: } pavone@875: return NULL; pavone@875: } pavone@1008: char const *pieces[] = {exe_dir, PATH_SEP, name}; pavone@875: char *path = alloc_concat_m(3, pieces); pavone@875: FILE *f = fopen(path, "rb"); pavone@875: free(path); pavone@875: if (!f) { pavone@875: if (sizeret) { pavone@875: *sizeret = -1; pavone@875: } pavone@875: return NULL; pavone@875: } pavone@875: pavone@875: long fsize = file_size(f); pavone@875: if (sizeret) { pavone@875: *sizeret = fsize; pavone@875: } pavone@875: char *ret; pavone@875: if (fsize) { pavone@875: //reserve an extra byte in case caller wants pavone@875: //to null terminate the data pavone@875: ret = malloc(fsize+1); pavone@875: if (fread(ret, 1, fsize, f) != fsize) { pavone@875: free(ret); pavone@875: ret = NULL; pavone@875: } pavone@875: } else { pavone@875: ret = NULL; pavone@875: } pavone@875: return ret; pavone@875: } pavone@875: pavone@876: char const *get_config_dir() pavone@875: { pavone@875: static char* confdir; pavone@875: if (!confdir) { pavone@875: char *homedir = get_home_dir(); pavone@875: if (homedir) { pavone@875: confdir = alloc_concat(homedir, "/.config/blastem"); pavone@875: } pavone@875: } pavone@875: return confdir; pavone@875: } pavone@875: pavone@955: char const *get_save_dir() pavone@955: { pavone@955: static char* savedir; pavone@955: if (!savedir) { pavone@955: char *homedir = get_home_dir(); pavone@955: if (homedir) { pavone@955: savedir = alloc_concat(homedir, "/.local/share/blastem"); pavone@955: } pavone@955: } pavone@955: return savedir; pavone@955: } pavone@955: pavone@875: #endif