1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069 |
- /* MegaZeux
- *
- * Copyright (C) 2004 Gilead Kutnick <exophase@adelphia.net>
- * Copyright (C) 2008 Alistair John Strachan <alistair@devzero.co.uk>
- * Copyright (C) 2012 Alice Rowan <petrifiedrowan@gmail.com>
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License as
- * published by the Free Software Foundation; either version 2 of
- * the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
- */
- #include <sys/stat.h>
- #include <ctype.h>
- #include <time.h>
- // Must include after <time.h> (MSVC bug)
- #include "util.h"
- #ifndef _MSC_VER
- #include <unistd.h>
- #endif
- #include <stdint.h>
- #include <stdlib.h>
- #include <string.h>
- #include "const.h" // for MAX_PATH
- #include "error.h"
- #include "memcasecmp.h" // memtolower
- struct mzx_resource
- {
- const char *const base_name;
- char *path;
- /* So mzxrun requires fewer files even in CONFIG_EDITOR=1 build */
- boolean optional;
- };
- /* Using C99 initializers would be nicer here, but MSVC doesn't support
- * them and we try to allow compilation with that compiler.
- *
- * As a result, these must be in exactly the same order as the
- * enum resource_id enumeration defines them.
- */
- static struct mzx_resource mzx_res[] = {
- #define ASSETS "assets/"
- { CONFFILE, NULL, false },
- { ASSETS "default.chr", NULL, false },
- { ASSETS "edit.chr", NULL, false },
- { ASSETS "smzx.pal", NULL, false },
- #ifdef CONFIG_EDITOR
- { ASSETS "ascii.chr", NULL, true },
- { ASSETS "blank.chr", NULL, true },
- { ASSETS "smzx.chr", NULL, true },
- #endif
- #ifdef CONFIG_HELPSYS
- { ASSETS "help.fil", NULL, true },
- #endif
- #ifdef CONFIG_RENDER_GL_PROGRAM
- #define GLSL_SHADERS ASSETS "glsl/"
- #define GLSL_SCALERS GLSL_SHADERS "scalers/"
- { GLSL_SCALERS, NULL, false },
- { GLSL_SHADERS "scaler.vert", NULL, false },
- { GLSL_SCALERS "semisoft.frag", NULL, false },
- { GLSL_SHADERS "tilemap.vert", NULL, false },
- { GLSL_SHADERS "tilemap.frag", NULL, false },
- { GLSL_SHADERS "tilemap.smzx.frag", NULL, false },
- { GLSL_SHADERS "mouse.vert", NULL, false },
- { GLSL_SHADERS "mouse.frag", NULL, false },
- { GLSL_SHADERS "cursor.vert", NULL, false },
- { GLSL_SHADERS "cursor.frag", NULL, false },
- #endif
- #ifdef CONFIG_GAMECONTROLLERDB
- { ASSETS "gamecontrollerdb.txt", NULL, true },
- #endif
- };
- #ifdef CONFIG_CHECK_ALLOC
- static void out_of_memory_check(void *p, const char *file, int line)
- {
- char msgbuf[128];
- if(!p)
- {
- snprintf(msgbuf, sizeof(msgbuf), "Out of memory in %s:%d", file, line);
- msgbuf[sizeof(msgbuf)-1] = '\0';
- error(msgbuf, ERROR_T_FATAL, ERROR_OPT_EXIT|ERROR_OPT_NO_HELP, 0);
- }
- }
- void *check_calloc(size_t nmemb, size_t size, const char *file, int line)
- {
- void *result = calloc(nmemb, size);
- out_of_memory_check(result, file, line);
- return result;
- }
- void *check_malloc(size_t size, const char *file, int line)
- {
- void *result = malloc(size);
- out_of_memory_check(result, file, line);
- return result;
- }
- void *check_realloc(void *ptr, size_t size, const char *file, int line)
- {
- void *result = realloc(ptr, size);
- out_of_memory_check(result, file, line);
- return result;
- }
- #endif /* CONFIG_CHECK_ALLOC */
- int mzx_res_init(const char *argv0, boolean editor)
- {
- size_t i, bin_path_len = 0;
- struct stat file_info;
- char *full_path_base;
- char *full_path;
- char *bin_path;
- ssize_t g_ret;
- char *p_dir;
- int ret = 0;
- full_path_base = cmalloc(MAX_PATH);
- bin_path = cmalloc(MAX_PATH);
- p_dir = cmalloc(MAX_PATH);
- g_ret = get_path(argv0, bin_path, MAX_PATH);
- if(g_ret > 0)
- {
- // move and convert to absolute path style
- chdir(bin_path);
- getcwd(bin_path, MAX_PATH);
- bin_path_len = strlen(bin_path);
- // append the trailing '/'
- bin_path[bin_path_len++] = '/';
- bin_path[bin_path_len] = 0;
- }
- else
- {
- free(bin_path);
- bin_path = NULL;
- }
- /* Always try to use SHAREDIR/CONFDIR first, if possible. On some
- * platforms, such as Linux, this will be something other than
- * the current working directory (i.e. '.'). If this fails, try
- * to look up the resources relative to the binary's install
- * location, which should allow the MegaZeux binary to be more
- * "portable".
- */
- for(i = 0; i < END_RESOURCE_ID_T; i++)
- {
- size_t base_name_len = strlen(mzx_res[i].base_name);
- size_t p_dir_len;
- if(i == CONFIG_TXT)
- chdir(CONFDIR);
- else
- chdir(SHAREDIR);
- getcwd(p_dir, MAX_PATH);
- p_dir_len = strlen(p_dir);
- // if we can't add the path we should really fail hard
- if(p_dir_len + base_name_len + 1 >= MAX_PATH)
- continue;
- // append the trailing '/'
- p_dir[p_dir_len++] = '/';
- p_dir[p_dir_len] = 0;
- memcpy(full_path_base, p_dir, p_dir_len);
- memcpy(full_path_base + p_dir_len, mzx_res[i].base_name, base_name_len);
- full_path_base[p_dir_len + base_name_len] = 0;
- full_path = cmalloc(MAX_PATH);
- clean_path_slashes(full_path_base, full_path, MAX_PATH);
- // Attempt to load it from this new path
- if(!stat(full_path, &file_info))
- {
- mzx_res[i].path = full_path;
- continue;
- }
- free(full_path);
- // Try to locate the resource relative to the bin_path
- if(bin_path)
- {
- chdir(bin_path);
- if(!stat(mzx_res[i].base_name, &file_info))
- {
- mzx_res[i].path = cmalloc(bin_path_len + base_name_len + 1);
- memcpy(mzx_res[i].path, bin_path, bin_path_len);
- memcpy(mzx_res[i].path + bin_path_len,
- mzx_res[i].base_name, base_name_len);
- mzx_res[i].path[bin_path_len + base_name_len] = 0;
- }
- }
- }
- for(i = 0; i < END_RESOURCE_ID_T; i++)
- {
- /* Skip non-essential resources */
- if(!editor && mzx_res[i].optional)
- continue;
- #ifdef CONFIG_GAMECONTROLLERDB
- // FIXME stupid hack because "optional" doesn't really mean optional.
- if(i == GAMECONTROLLERDB_TXT)
- continue;
- #endif
- if(!mzx_res[i].path)
- {
- warn("Failed to locate critical resource '%s'.\n",
- mzx_res[i].base_name);
- ret = 1;
- }
- }
- free(p_dir);
- free(bin_path);
- free(full_path_base);
- return ret;
- }
- void mzx_res_free(void)
- {
- int i;
- for(i = 0; i < END_RESOURCE_ID_T; i++)
- if(mzx_res[i].path)
- free(mzx_res[i].path);
- }
- #ifdef USERCONFFILE
- #define COPY_BUFFER_SIZE 4096
- static unsigned char copy_buffer[COPY_BUFFER_SIZE];
- #endif
- char *mzx_res_get_by_id(enum resource_id id)
- {
- #ifdef USERCONFFILE
- static char userconfpath[MAX_PATH];
- if(id == CONFIG_TXT)
- {
- FILE *fp;
- // Special handling for CONFIG_TXT to allow for user
- // configuration files
- sprintf(userconfpath, "%s/%s", getenv("HOME"), USERCONFFILE);
- // Check if the file can be opened for reading
- fp = fopen_unsafe(userconfpath, "rb");
- if(fp)
- {
- fclose(fp);
- return (char *)userconfpath;
- }
- // Otherwise, try to open the file for writing
- fp = fopen_unsafe(userconfpath, "wb");
- if(fp)
- {
- FILE *original = fopen_unsafe(mzx_res[id].path, "rb");
- if(original)
- {
- size_t bytes_read;
- for(;;)
- {
- bytes_read = fread(copy_buffer, 1, COPY_BUFFER_SIZE, original);
- if(bytes_read)
- fwrite(copy_buffer, 1, bytes_read, fp);
- else
- break;
- }
- fclose(fp);
- fclose(original);
- return (char *)userconfpath;
- }
- fclose(fp);
- }
- // If that's no good, just read the normal config file
- }
- #endif /* USERCONFFILE */
- return mzx_res[id].path;
- }
- /**
- * Some platforms may not be able to display console output without extra work.
- * On these platforms redirect STDIO to files so the console output is easier
- * to read.
- */
- boolean redirect_stdio(const char *base_path, boolean require_conf)
- {
- char clean_path[MAX_PATH];
- char dest_path[MAX_PATH];
- FILE *fp_wr;
- uint64_t t;
- if(!base_path)
- return false;
- clean_path_slashes(base_path, clean_path, MAX_PATH);
- if(require_conf)
- {
- // If the config file is required, attempt to stat it.
- struct stat stat_info;
- join_path_names(dest_path, MAX_PATH, clean_path, "config.txt");
- if(stat(dest_path, &stat_info))
- return false;
- }
- // Test directory for write access.
- join_path_names(dest_path, MAX_PATH, clean_path, "stdout.txt");
- fp_wr = fopen_unsafe(dest_path, "w");
- if(fp_wr)
- {
- t = (uint64_t)time(NULL);
- // Redirect stdout to stdout.txt.
- fclose(fp_wr);
- fprintf(stdout, "Redirecting logs to '%s'...\n", dest_path);
- freopen(dest_path, "w", stdout);
- fprintf(stdout, "MegaZeux: Logging to '%s' (%" PRIu64 ")\n", dest_path, t);
- // Redirect stderr to stderr.txt.
- join_path_names(dest_path, MAX_PATH, clean_path, "stderr.txt");
- fprintf(stderr, "Redirecting logs to '%s'...\n", dest_path);
- freopen(dest_path, "w", stderr);
- fprintf(stderr, "MegaZeux: Logging to '%s' (%" PRIu64 ")\n", dest_path, t);
- return true;
- }
- return false;
- }
- // Get 2 bytes, little endian
- int fgetw(FILE *fp)
- {
- int a = fgetc(fp), b = fgetc(fp);
- if((a == EOF) || (b == EOF))
- return EOF;
- return (b << 8) | a;
- }
- // Get 4 bytes, little endian
- int fgetd(FILE *fp)
- {
- int a = fgetc(fp), b = fgetc(fp), c = fgetc(fp), d = fgetc(fp);
- if((a == EOF) || (b == EOF) || (c == EOF) || (d == EOF))
- return EOF;
- return (d << 24) | (c << 16) | (b << 8) | a;
- }
- // Put 2 bytes, little endian
- void fputw(int src, FILE *fp)
- {
- fputc(src & 0xFF, fp);
- fputc(src >> 8, fp);
- }
- // Put 4 bytes, little endian
- void fputd(int src, FILE *fp)
- {
- fputc(src & 0xFF, fp);
- fputc((src >> 8) & 0xFF, fp);
- fputc((src >> 16) & 0xFF, fp);
- fputc((src >> 24) & 0xFF, fp);
- }
- // Determine file size of an open FILE and rewind it
- long ftell_and_rewind(FILE *f)
- {
- long size;
- fseek(f, 0, SEEK_END);
- size = ftell(f);
- rewind(f);
- return size;
- }
- // Random function, returns an integer [0-range)
- static uint64_t rng_state;
- // Seed the RNG from system time on startup
- void rng_seed_init(void)
- {
- uint64_t seed = (((uint64_t)time(NULL)) << 32) | clock();
- rng_set_seed(seed);
- }
- uint64_t rng_get_seed(void)
- {
- return rng_state;
- }
- void rng_set_seed(uint64_t seed)
- {
- rng_state = seed;
- }
- // xorshift*
- // Implementation from https://en.wikipedia.org/wiki/Xorshift
- unsigned int Random(uint64_t range)
- {
- uint64_t x = rng_state;
- if(x == 0) x = 1;
- x ^= x >> 12; // a
- x ^= x << 25; // b
- x ^= x >> 27; // c
- rng_state = x;
- return ((x * 0x2545F4914F6CDD1D) >> 32) * range / 0xFFFFFFFF;
- }
- // FIXME: This function should probably die. It's unsafe.
- void add_ext(char *src, const char *ext)
- {
- size_t src_len = strlen(src);
- size_t ext_len = strlen(ext);
- if((src_len < ext_len) || (src[src_len - ext_len] != '.') ||
- strcasecmp(src + src_len - ext_len, ext))
- {
- strcat(src, ext);
- }
- }
- int get_ext_pos(const char *filename)
- {
- int filename_length = strlen(filename);
- int ext_pos;
- for(ext_pos = filename_length - 1; ext_pos >= 0; ext_pos--)
- if(filename[ext_pos] == '.')
- break;
- return ext_pos;
- }
- __utils_maybe_static ssize_t __get_path(const char *file_name, char *dest,
- unsigned int buf_len)
- {
- ssize_t c = (ssize_t)strlen(file_name) - 1;
- // no path, or it's too long to store
- if(c == -1 || c > (int)buf_len)
- {
- if(buf_len > 0)
- dest[0] = 0;
- return -1;
- }
- while((file_name[c] != '/') && (file_name[c] != '\\') && c)
- c--;
- if(c > 0)
- memcpy(dest, file_name, c);
- dest[c] = 0;
- return c;
- }
- ssize_t get_path(const char *file_name, char *dest, unsigned int buf_len)
- {
- return __get_path(file_name, dest, buf_len);
- }
- static int isslash(char n)
- {
- return n=='\\' || n=='/';
- }
- void clean_path_slashes(const char *src, char *dest, size_t buf_size)
- {
- unsigned int i = 0;
- unsigned int p = 0;
- size_t src_len = strlen(src);
- while((i < src_len) && (p < buf_size-1))
- {
- if(isslash(src[i]))
- {
- while(isslash(src[i]))
- i++;
- dest[p] = DIR_SEPARATOR_CHAR;
- p++;
- }
- else
- {
- dest[p] = src[i];
- i++;
- p++;
- }
- }
- dest[p] = '\0';
- if((p >= 2) && (dest[p-1] == DIR_SEPARATOR_CHAR) && (dest[p-2] != ':'))
- dest[p-1] = '\0';
- }
- void split_path_filename(const char *source,
- char *destpath, unsigned int dest_buffer_len,
- char *destfile, unsigned int file_buffer_len)
- {
- char temppath[MAX_PATH];
- struct stat path_info;
- int stat_res = stat(source, &path_info);
- // If the entirety of source is a directory
- if((stat_res >= 0) && S_ISDIR(path_info.st_mode))
- {
- if(dest_buffer_len)
- clean_path_slashes(source, destpath, dest_buffer_len);
- if(file_buffer_len)
- strcpy(destfile, "");
- }
- else
- // If source has a directory and a file
- if((source[0] != '\0') && get_path(source, temppath, MAX_PATH))
- {
- // get_path leaves off trailing /, add 1 to offset.
- if(dest_buffer_len)
- clean_path_slashes(temppath, destpath, dest_buffer_len);
- if(file_buffer_len)
- strncpy(destfile, &(source[strlen(temppath) + 1]), file_buffer_len);
- }
- // Source is just a file or blank.
- else
- {
- if(dest_buffer_len)
- strcpy(destpath, "");
- if(file_buffer_len)
- strncpy(destfile, source, file_buffer_len);
- }
- }
- void join_path_names(char* target, int max_len, const char* path1, const char* path2)
- {
- if(path1[strlen(path1)-1] == DIR_SEPARATOR_CHAR)
- snprintf(target, max_len, "%s%s", path1, path2);
- else
- snprintf(target, max_len, "%s%s%s", path1, DIR_SEPARATOR, path2);
- }
- int create_path_if_not_exists(const char *filename)
- {
- struct stat stat_info;
- char parent_directory[MAX_PATH];
- if(!get_path(filename, parent_directory, MAX_PATH))
- return 1;
- if(!stat(parent_directory, &stat_info))
- return 2;
- create_path_if_not_exists(parent_directory);
- if(mkdir(parent_directory, 0755))
- return 3;
- return 0;
- }
- // Navigate a path name.
- int change_dir_name(char *path_name, const char *dest)
- {
- struct stat stat_info;
- char path_temp[MAX_PATH];
- char path[MAX_PATH];
- const char *current;
- const char *next;
- const char *end;
- char current_char;
- size_t len;
- if(!dest || !dest[0])
- return -1;
- if(!path_name)
- return -1;
- current = dest;
- end = dest + strlen(dest);
- next = strchr(dest, ':');
- if(next)
- {
- /**
- * Destination starts with a Windows-style root directory.
- * Aside from Windows, these are often used by console SDKs (albeit with /
- * instead of \) to distinguish SD cards and the like.
- */
- if(next[1] != DIR_SEPARATOR_CHAR && next[1] != 0)
- return -1;
- snprintf(path, MAX_PATH, "%.*s" DIR_SEPARATOR, (int)(next - dest + 1),
- dest);
- if(stat(path, &stat_info) < 0)
- return -1;
- current = next + 1;
- if(current[0] == DIR_SEPARATOR_CHAR)
- current++;
- }
- else
- if(dest[0] == DIR_SEPARATOR_CHAR)
- {
- /**
- * Destination starts with a Unix-style root directory.
- * Aside from Unix-likes, these are also supported by console platforms.
- * Even Windows (back through XP at least) doesn't seem to mind them.
- */
- strcpy(path, DIR_SEPARATOR);
- current = dest + 1;
- }
- else
- {
- /**
- * Destination is relative--start from the current path. Make sure there's
- * a trailing separator.
- */
- if(path_name[strlen(path_name) - 1] != DIR_SEPARATOR_CHAR)
- snprintf(path, MAX_PATH, "%s" DIR_SEPARATOR, path_name);
- else
- strcpy(path, path_name);
- }
- current_char = current[0];
- len = strlen(path);
- // Apply directory fragments to the path.
- while(current_char != 0)
- {
- // Increment next to skip the separator so it will be copied over.
- next = strchr(current, DIR_SEPARATOR_CHAR);
- if(!next) next = end;
- else next++;
- // . does nothing, .. goes back one level
- if(current_char == '.')
- {
- if(current[1] == '.')
- {
- // Skip the rightmost separator (current level) and look for the
- // previous separator. If found, truncate the path to it.
- char *pos = path + len - 1;
- do
- {
- pos--;
- }
- while(pos >= path && *pos != DIR_SEPARATOR_CHAR);
- if(pos >= path)
- {
- pos[1] = 0;
- len = strlen(path);
- }
- }
- }
- else
- {
- snprintf(path + len, MAX_PATH - len, "%.*s", (int)(next - current),
- current);
- len = strlen(path);
- }
- current = next;
- current_char = current[0];
- }
- // This needs to be done before the stat for some platforms (e.g. 3DS)
- clean_path_slashes(path, path_temp, MAX_PATH);
- if(stat(path_temp, &stat_info) >= 0)
- {
- strcpy(path_name, path_temp);
- return 0;
- }
- return -1;
- }
- // Index must be an array of 256 ints
- void boyer_moore_index(const void *B, const size_t b_len,
- int index[256], boolean ignore_case)
- {
- char *b = (char *)B;
- int i;
- char *s = b;
- char *last = b + b_len - 1;
- for(i = 0; i < 256; i++)
- index[i] = b_len;
- if(!ignore_case)
- {
- for(s = b; s < last; s++)
- index[(int)*s] = last - s;
- }
- else
- {
- for(s = b; s < last; s++)
- index[memtolower((int)*s)] = last - s;
- // Duplicating the lowercase values over the uppercase values helps avoid
- // an extra tolower in the search function.
- memcpy(index + 'A', index + 'a', sizeof(int) * 26);
- }
- }
- // Search for substring B in haystack A. The index greatly increases the
- // search speed, especially for large needles. This is actually a reduced
- // Boyer-Moore search, as the original version uses two separate indexes.
- void *boyer_moore_search(const void *A, const size_t a_len,
- const void *B, const size_t b_len, const int index[256], boolean ignore_case)
- {
- const unsigned char *a = (const unsigned char *)A;
- const unsigned char *b = (const unsigned char *)B;
- size_t i = b_len - 1;
- size_t idx;
- int j;
- if(!ignore_case)
- {
- while(i < a_len)
- {
- j = b_len - 1;
- while(j >= 0 && a[i] == b[j])
- j--, i--;
- if(j == -1)
- return (void *)(a + i + 1);
- idx = index[(int)a[i]];
- i += MAX(b_len - j, idx);
- }
- }
- else
- {
- while(i < a_len)
- {
- j = b_len - 1;
- while(j >= 0 && memtolower((int)a[i]) == memtolower((int)b[j]))
- j--, i--;
- if(j == -1)
- return (void *)(a + i + 1);
- idx = index[(int)a[i]];
- i += MAX(b_len - j, idx);
- }
- }
- return NULL;
- }
- int mem_getc(const unsigned char **ptr)
- {
- int val = (*ptr)[0];
- *ptr += 1;
- return val;
- }
- int mem_getw(const unsigned char **ptr)
- {
- int val = (*ptr)[0] | ((*ptr)[1] << 8);
- *ptr += 2;
- return val;
- }
- int mem_getd(const unsigned char **ptr)
- {
- int val = (*ptr)[0] | ((*ptr)[1] << 8) | ((*ptr)[2] << 16) | ((int)((*ptr)[3]) << 24);
- *ptr += 4;
- return val;
- }
- void mem_putc(int src, unsigned char **ptr)
- {
- (*ptr)[0] = src;
- *ptr += 1;
- }
- void mem_putw(int src, unsigned char **ptr)
- {
- (*ptr)[0] = src & 0xFF;
- (*ptr)[1] = (src >> 8) & 0xFF;
- *ptr += 2;
- }
- void mem_putd(int src, unsigned char **ptr)
- {
- (*ptr)[0] = src & 0xFF;
- (*ptr)[1] = (src >> 8) & 0xFF;
- (*ptr)[2] = (src >> 16) & 0xFF;
- (*ptr)[3] = (src >> 24) & 0xFF;
- *ptr += 4;
- }
- // like fsafegets, except from memory.
- int memsafegets(char *dest, int size, char **src, char *end)
- {
- char *pos = dest;
- char *next = *src;
- char *stop = MIN(end, next+size);
- char ch;
- // Return 0 if this is the end of the memory block
- if(next == NULL)
- return 0;
- // Copy the memory until the end, the bound, or a newline
- while(next<stop && (ch = *next)!='\n')
- {
- *pos = ch;
- pos++;
- next++;
- }
- *pos = 0;
- // Place the counter where the next line begins
- if(next < end)
- {
- *src = next + 1;
- }
- // Mark that this is the end
- else
- {
- *src = NULL;
- }
- // Length at least 1 -- get rid of \r and \n
- if(pos > dest)
- if(pos[-1] == '\r' || pos[-1] == '\n')
- pos[-1] = 0;
- // Length at least 2 -- get rid of \r and \n
- if(pos - 1 > dest)
- if(pos[-2] == '\r' || pos[-2] == '\n')
- pos[-2] = 0;
- return 1;
- }
- #if defined(__WIN32__) && defined(__STRICT_ANSI__)
- /* On WIN32 with C99 defining __STRICT_ANSI__ these POSIX.1-2001 functions
- * are not available. The stricmp/strnicmp functions are available, but not
- * to C99 programs.
- *
- * Redefine them here; these are copied from glibc, and should be optimal.
- */
- int strcasecmp(const char *s1, const char *s2)
- {
- while(*s1 != '\0' && tolower(*s1) == tolower(*s2))
- {
- s1++;
- s2++;
- }
- return tolower(*(unsigned char *)s1) - tolower(*(unsigned char *)s2);
- }
- int strncasecmp(const char *s1, const char *s2, size_t n)
- {
- if(n == 0)
- return 0;
- while(n-- != 0 && tolower(*s1) == tolower(*s2))
- {
- if(n == 0 || *s1 == '\0' || *s2 == '\0')
- break;
- s1++;
- s2++;
- }
- return tolower(*(unsigned char *)s1) - tolower(*(unsigned char *)s2);
- }
- #endif // __WIN32__ && __STRICT_ANSI__
- #if defined(__WIN32__) || defined(__amigaos__)
- // strsep() stolen from glibc (GPL)
- char *strsep(char **stringp, const char *delim)
- {
- char *begin, *end;
- begin = *stringp;
- if(!begin)
- return NULL;
- if(delim[0] == '\0' || delim[1] == '\0')
- {
- char ch = delim[0];
- if(ch == '\0')
- end = NULL;
- else
- {
- if(*begin == ch)
- end = begin;
- else if(*begin == '\0')
- end = NULL;
- else
- end = strchr(begin + 1, ch);
- }
- }
- else
- end = strpbrk(begin, delim);
- if(end)
- {
- *end++ = '\0';
- *stringp = end;
- }
- else
- *stringp = NULL;
- return begin;
- }
- #endif // __WIN32__ || __amigaos__
- long dir_tell(struct mzx_dir *dir)
- {
- return dir->pos;
- }
- boolean dir_open(struct mzx_dir *dir, const char *path)
- {
- dir->d = opendir(path);
- if(!dir->d)
- return false;
- dir->entries = 0;
- while(readdir(dir->d) != NULL)
- dir->entries++;
- #if defined(CONFIG_PSP) || defined(CONFIG_3DS) || defined(CONFIG_SWITCH)
- strncpy(dir->path, path, PATH_BUF_LEN);
- dir->path[PATH_BUF_LEN - 1] = 0;
- closedir(dir->d);
- dir->d = opendir(path);
- #else
- rewinddir(dir->d);
- #endif
- dir->pos = 0;
- return true;
- }
- void dir_close(struct mzx_dir *dir)
- {
- if(dir->d)
- {
- closedir(dir->d);
- dir->d = NULL;
- dir->entries = 0;
- dir->pos = 0;
- }
- }
- void dir_seek(struct mzx_dir *dir, long offset)
- {
- long i;
- if(!dir->d)
- return;
- dir->pos = CLAMP(offset, 0L, dir->entries);
- #if defined(CONFIG_PSP) || defined(CONFIG_3DS) || defined(CONFIG_SWITCH)
- closedir(dir->d);
- dir->d = opendir(dir->path);
- if(!dir->d)
- return;
- #else
- rewinddir(dir->d);
- #endif
- for(i = 0; i < dir->pos; i++)
- readdir(dir->d);
- }
- boolean dir_get_next_entry(struct mzx_dir *dir, char *entry)
- {
- struct dirent *inode;
- if(!dir->d)
- return false;
- dir->pos = MIN(dir->pos + 1, dir->entries);
- inode = readdir(dir->d);
- if(!inode)
- {
- entry[0] = 0;
- return false;
- }
- snprintf(entry, PATH_BUF_LEN, "%s", inode->d_name);
- return true;
- }
- #if defined(__amigaos__)
- long __stack_chk_guard[8];
- void __stack_chk_fail(void)
- {
- warn("Stack overflow detected; terminated");
- exit(0);
- }
- #endif // __amigaos__
- #if defined(CONFIG_PSP) || defined(CONFIG_NDS)
- FILE *popen(const char *command, const char *type)
- {
- return NULL;
- }
- int pclose(FILE *stream)
- {
- return 0;
- }
- #endif // CONFIG_PSP || CONFIG_NDS
|