util.c 23 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069
  1. /* MegaZeux
  2. *
  3. * Copyright (C) 2004 Gilead Kutnick <exophase@adelphia.net>
  4. * Copyright (C) 2008 Alistair John Strachan <alistair@devzero.co.uk>
  5. * Copyright (C) 2012 Alice Rowan <petrifiedrowan@gmail.com>
  6. *
  7. * This program is free software; you can redistribute it and/or
  8. * modify it under the terms of the GNU General Public License as
  9. * published by the Free Software Foundation; either version 2 of
  10. * the License, or (at your option) any later version.
  11. *
  12. * This program is distributed in the hope that it will be useful,
  13. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  14. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  15. * General Public License for more details.
  16. *
  17. * You should have received a copy of the GNU General Public License
  18. * along with this program; if not, write to the Free Software
  19. * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
  20. */
  21. #include <sys/stat.h>
  22. #include <ctype.h>
  23. #include <time.h>
  24. // Must include after <time.h> (MSVC bug)
  25. #include "util.h"
  26. #ifndef _MSC_VER
  27. #include <unistd.h>
  28. #endif
  29. #include <stdint.h>
  30. #include <stdlib.h>
  31. #include <string.h>
  32. #include "const.h" // for MAX_PATH
  33. #include "error.h"
  34. #include "memcasecmp.h" // memtolower
  35. struct mzx_resource
  36. {
  37. const char *const base_name;
  38. char *path;
  39. /* So mzxrun requires fewer files even in CONFIG_EDITOR=1 build */
  40. boolean optional;
  41. };
  42. /* Using C99 initializers would be nicer here, but MSVC doesn't support
  43. * them and we try to allow compilation with that compiler.
  44. *
  45. * As a result, these must be in exactly the same order as the
  46. * enum resource_id enumeration defines them.
  47. */
  48. static struct mzx_resource mzx_res[] = {
  49. #define ASSETS "assets/"
  50. { CONFFILE, NULL, false },
  51. { ASSETS "default.chr", NULL, false },
  52. { ASSETS "edit.chr", NULL, false },
  53. { ASSETS "smzx.pal", NULL, false },
  54. #ifdef CONFIG_EDITOR
  55. { ASSETS "ascii.chr", NULL, true },
  56. { ASSETS "blank.chr", NULL, true },
  57. { ASSETS "smzx.chr", NULL, true },
  58. #endif
  59. #ifdef CONFIG_HELPSYS
  60. { ASSETS "help.fil", NULL, true },
  61. #endif
  62. #ifdef CONFIG_RENDER_GL_PROGRAM
  63. #define GLSL_SHADERS ASSETS "glsl/"
  64. #define GLSL_SCALERS GLSL_SHADERS "scalers/"
  65. { GLSL_SCALERS, NULL, false },
  66. { GLSL_SHADERS "scaler.vert", NULL, false },
  67. { GLSL_SCALERS "semisoft.frag", NULL, false },
  68. { GLSL_SHADERS "tilemap.vert", NULL, false },
  69. { GLSL_SHADERS "tilemap.frag", NULL, false },
  70. { GLSL_SHADERS "tilemap.smzx.frag", NULL, false },
  71. { GLSL_SHADERS "mouse.vert", NULL, false },
  72. { GLSL_SHADERS "mouse.frag", NULL, false },
  73. { GLSL_SHADERS "cursor.vert", NULL, false },
  74. { GLSL_SHADERS "cursor.frag", NULL, false },
  75. #endif
  76. #ifdef CONFIG_GAMECONTROLLERDB
  77. { ASSETS "gamecontrollerdb.txt", NULL, true },
  78. #endif
  79. };
  80. #ifdef CONFIG_CHECK_ALLOC
  81. static void out_of_memory_check(void *p, const char *file, int line)
  82. {
  83. char msgbuf[128];
  84. if(!p)
  85. {
  86. snprintf(msgbuf, sizeof(msgbuf), "Out of memory in %s:%d", file, line);
  87. msgbuf[sizeof(msgbuf)-1] = '\0';
  88. error(msgbuf, ERROR_T_FATAL, ERROR_OPT_EXIT|ERROR_OPT_NO_HELP, 0);
  89. }
  90. }
  91. void *check_calloc(size_t nmemb, size_t size, const char *file, int line)
  92. {
  93. void *result = calloc(nmemb, size);
  94. out_of_memory_check(result, file, line);
  95. return result;
  96. }
  97. void *check_malloc(size_t size, const char *file, int line)
  98. {
  99. void *result = malloc(size);
  100. out_of_memory_check(result, file, line);
  101. return result;
  102. }
  103. void *check_realloc(void *ptr, size_t size, const char *file, int line)
  104. {
  105. void *result = realloc(ptr, size);
  106. out_of_memory_check(result, file, line);
  107. return result;
  108. }
  109. #endif /* CONFIG_CHECK_ALLOC */
  110. int mzx_res_init(const char *argv0, boolean editor)
  111. {
  112. size_t i, bin_path_len = 0;
  113. struct stat file_info;
  114. char *full_path_base;
  115. char *full_path;
  116. char *bin_path;
  117. ssize_t g_ret;
  118. char *p_dir;
  119. int ret = 0;
  120. full_path_base = cmalloc(MAX_PATH);
  121. bin_path = cmalloc(MAX_PATH);
  122. p_dir = cmalloc(MAX_PATH);
  123. g_ret = get_path(argv0, bin_path, MAX_PATH);
  124. if(g_ret > 0)
  125. {
  126. // move and convert to absolute path style
  127. chdir(bin_path);
  128. getcwd(bin_path, MAX_PATH);
  129. bin_path_len = strlen(bin_path);
  130. // append the trailing '/'
  131. bin_path[bin_path_len++] = '/';
  132. bin_path[bin_path_len] = 0;
  133. }
  134. else
  135. {
  136. free(bin_path);
  137. bin_path = NULL;
  138. }
  139. /* Always try to use SHAREDIR/CONFDIR first, if possible. On some
  140. * platforms, such as Linux, this will be something other than
  141. * the current working directory (i.e. '.'). If this fails, try
  142. * to look up the resources relative to the binary's install
  143. * location, which should allow the MegaZeux binary to be more
  144. * "portable".
  145. */
  146. for(i = 0; i < END_RESOURCE_ID_T; i++)
  147. {
  148. size_t base_name_len = strlen(mzx_res[i].base_name);
  149. size_t p_dir_len;
  150. if(i == CONFIG_TXT)
  151. chdir(CONFDIR);
  152. else
  153. chdir(SHAREDIR);
  154. getcwd(p_dir, MAX_PATH);
  155. p_dir_len = strlen(p_dir);
  156. // if we can't add the path we should really fail hard
  157. if(p_dir_len + base_name_len + 1 >= MAX_PATH)
  158. continue;
  159. // append the trailing '/'
  160. p_dir[p_dir_len++] = '/';
  161. p_dir[p_dir_len] = 0;
  162. memcpy(full_path_base, p_dir, p_dir_len);
  163. memcpy(full_path_base + p_dir_len, mzx_res[i].base_name, base_name_len);
  164. full_path_base[p_dir_len + base_name_len] = 0;
  165. full_path = cmalloc(MAX_PATH);
  166. clean_path_slashes(full_path_base, full_path, MAX_PATH);
  167. // Attempt to load it from this new path
  168. if(!stat(full_path, &file_info))
  169. {
  170. mzx_res[i].path = full_path;
  171. continue;
  172. }
  173. free(full_path);
  174. // Try to locate the resource relative to the bin_path
  175. if(bin_path)
  176. {
  177. chdir(bin_path);
  178. if(!stat(mzx_res[i].base_name, &file_info))
  179. {
  180. mzx_res[i].path = cmalloc(bin_path_len + base_name_len + 1);
  181. memcpy(mzx_res[i].path, bin_path, bin_path_len);
  182. memcpy(mzx_res[i].path + bin_path_len,
  183. mzx_res[i].base_name, base_name_len);
  184. mzx_res[i].path[bin_path_len + base_name_len] = 0;
  185. }
  186. }
  187. }
  188. for(i = 0; i < END_RESOURCE_ID_T; i++)
  189. {
  190. /* Skip non-essential resources */
  191. if(!editor && mzx_res[i].optional)
  192. continue;
  193. #ifdef CONFIG_GAMECONTROLLERDB
  194. // FIXME stupid hack because "optional" doesn't really mean optional.
  195. if(i == GAMECONTROLLERDB_TXT)
  196. continue;
  197. #endif
  198. if(!mzx_res[i].path)
  199. {
  200. warn("Failed to locate critical resource '%s'.\n",
  201. mzx_res[i].base_name);
  202. ret = 1;
  203. }
  204. }
  205. free(p_dir);
  206. free(bin_path);
  207. free(full_path_base);
  208. return ret;
  209. }
  210. void mzx_res_free(void)
  211. {
  212. int i;
  213. for(i = 0; i < END_RESOURCE_ID_T; i++)
  214. if(mzx_res[i].path)
  215. free(mzx_res[i].path);
  216. }
  217. #ifdef USERCONFFILE
  218. #define COPY_BUFFER_SIZE 4096
  219. static unsigned char copy_buffer[COPY_BUFFER_SIZE];
  220. #endif
  221. char *mzx_res_get_by_id(enum resource_id id)
  222. {
  223. #ifdef USERCONFFILE
  224. static char userconfpath[MAX_PATH];
  225. if(id == CONFIG_TXT)
  226. {
  227. FILE *fp;
  228. // Special handling for CONFIG_TXT to allow for user
  229. // configuration files
  230. sprintf(userconfpath, "%s/%s", getenv("HOME"), USERCONFFILE);
  231. // Check if the file can be opened for reading
  232. fp = fopen_unsafe(userconfpath, "rb");
  233. if(fp)
  234. {
  235. fclose(fp);
  236. return (char *)userconfpath;
  237. }
  238. // Otherwise, try to open the file for writing
  239. fp = fopen_unsafe(userconfpath, "wb");
  240. if(fp)
  241. {
  242. FILE *original = fopen_unsafe(mzx_res[id].path, "rb");
  243. if(original)
  244. {
  245. size_t bytes_read;
  246. for(;;)
  247. {
  248. bytes_read = fread(copy_buffer, 1, COPY_BUFFER_SIZE, original);
  249. if(bytes_read)
  250. fwrite(copy_buffer, 1, bytes_read, fp);
  251. else
  252. break;
  253. }
  254. fclose(fp);
  255. fclose(original);
  256. return (char *)userconfpath;
  257. }
  258. fclose(fp);
  259. }
  260. // If that's no good, just read the normal config file
  261. }
  262. #endif /* USERCONFFILE */
  263. return mzx_res[id].path;
  264. }
  265. /**
  266. * Some platforms may not be able to display console output without extra work.
  267. * On these platforms redirect STDIO to files so the console output is easier
  268. * to read.
  269. */
  270. boolean redirect_stdio(const char *base_path, boolean require_conf)
  271. {
  272. char clean_path[MAX_PATH];
  273. char dest_path[MAX_PATH];
  274. FILE *fp_wr;
  275. uint64_t t;
  276. if(!base_path)
  277. return false;
  278. clean_path_slashes(base_path, clean_path, MAX_PATH);
  279. if(require_conf)
  280. {
  281. // If the config file is required, attempt to stat it.
  282. struct stat stat_info;
  283. join_path_names(dest_path, MAX_PATH, clean_path, "config.txt");
  284. if(stat(dest_path, &stat_info))
  285. return false;
  286. }
  287. // Test directory for write access.
  288. join_path_names(dest_path, MAX_PATH, clean_path, "stdout.txt");
  289. fp_wr = fopen_unsafe(dest_path, "w");
  290. if(fp_wr)
  291. {
  292. t = (uint64_t)time(NULL);
  293. // Redirect stdout to stdout.txt.
  294. fclose(fp_wr);
  295. fprintf(stdout, "Redirecting logs to '%s'...\n", dest_path);
  296. freopen(dest_path, "w", stdout);
  297. fprintf(stdout, "MegaZeux: Logging to '%s' (%" PRIu64 ")\n", dest_path, t);
  298. // Redirect stderr to stderr.txt.
  299. join_path_names(dest_path, MAX_PATH, clean_path, "stderr.txt");
  300. fprintf(stderr, "Redirecting logs to '%s'...\n", dest_path);
  301. freopen(dest_path, "w", stderr);
  302. fprintf(stderr, "MegaZeux: Logging to '%s' (%" PRIu64 ")\n", dest_path, t);
  303. return true;
  304. }
  305. return false;
  306. }
  307. // Get 2 bytes, little endian
  308. int fgetw(FILE *fp)
  309. {
  310. int a = fgetc(fp), b = fgetc(fp);
  311. if((a == EOF) || (b == EOF))
  312. return EOF;
  313. return (b << 8) | a;
  314. }
  315. // Get 4 bytes, little endian
  316. int fgetd(FILE *fp)
  317. {
  318. int a = fgetc(fp), b = fgetc(fp), c = fgetc(fp), d = fgetc(fp);
  319. if((a == EOF) || (b == EOF) || (c == EOF) || (d == EOF))
  320. return EOF;
  321. return (d << 24) | (c << 16) | (b << 8) | a;
  322. }
  323. // Put 2 bytes, little endian
  324. void fputw(int src, FILE *fp)
  325. {
  326. fputc(src & 0xFF, fp);
  327. fputc(src >> 8, fp);
  328. }
  329. // Put 4 bytes, little endian
  330. void fputd(int src, FILE *fp)
  331. {
  332. fputc(src & 0xFF, fp);
  333. fputc((src >> 8) & 0xFF, fp);
  334. fputc((src >> 16) & 0xFF, fp);
  335. fputc((src >> 24) & 0xFF, fp);
  336. }
  337. // Determine file size of an open FILE and rewind it
  338. long ftell_and_rewind(FILE *f)
  339. {
  340. long size;
  341. fseek(f, 0, SEEK_END);
  342. size = ftell(f);
  343. rewind(f);
  344. return size;
  345. }
  346. // Random function, returns an integer [0-range)
  347. static uint64_t rng_state;
  348. // Seed the RNG from system time on startup
  349. void rng_seed_init(void)
  350. {
  351. uint64_t seed = (((uint64_t)time(NULL)) << 32) | clock();
  352. rng_set_seed(seed);
  353. }
  354. uint64_t rng_get_seed(void)
  355. {
  356. return rng_state;
  357. }
  358. void rng_set_seed(uint64_t seed)
  359. {
  360. rng_state = seed;
  361. }
  362. // xorshift*
  363. // Implementation from https://en.wikipedia.org/wiki/Xorshift
  364. unsigned int Random(uint64_t range)
  365. {
  366. uint64_t x = rng_state;
  367. if(x == 0) x = 1;
  368. x ^= x >> 12; // a
  369. x ^= x << 25; // b
  370. x ^= x >> 27; // c
  371. rng_state = x;
  372. return ((x * 0x2545F4914F6CDD1D) >> 32) * range / 0xFFFFFFFF;
  373. }
  374. // FIXME: This function should probably die. It's unsafe.
  375. void add_ext(char *src, const char *ext)
  376. {
  377. size_t src_len = strlen(src);
  378. size_t ext_len = strlen(ext);
  379. if((src_len < ext_len) || (src[src_len - ext_len] != '.') ||
  380. strcasecmp(src + src_len - ext_len, ext))
  381. {
  382. strcat(src, ext);
  383. }
  384. }
  385. int get_ext_pos(const char *filename)
  386. {
  387. int filename_length = strlen(filename);
  388. int ext_pos;
  389. for(ext_pos = filename_length - 1; ext_pos >= 0; ext_pos--)
  390. if(filename[ext_pos] == '.')
  391. break;
  392. return ext_pos;
  393. }
  394. __utils_maybe_static ssize_t __get_path(const char *file_name, char *dest,
  395. unsigned int buf_len)
  396. {
  397. ssize_t c = (ssize_t)strlen(file_name) - 1;
  398. // no path, or it's too long to store
  399. if(c == -1 || c > (int)buf_len)
  400. {
  401. if(buf_len > 0)
  402. dest[0] = 0;
  403. return -1;
  404. }
  405. while((file_name[c] != '/') && (file_name[c] != '\\') && c)
  406. c--;
  407. if(c > 0)
  408. memcpy(dest, file_name, c);
  409. dest[c] = 0;
  410. return c;
  411. }
  412. ssize_t get_path(const char *file_name, char *dest, unsigned int buf_len)
  413. {
  414. return __get_path(file_name, dest, buf_len);
  415. }
  416. static int isslash(char n)
  417. {
  418. return n=='\\' || n=='/';
  419. }
  420. void clean_path_slashes(const char *src, char *dest, size_t buf_size)
  421. {
  422. unsigned int i = 0;
  423. unsigned int p = 0;
  424. size_t src_len = strlen(src);
  425. while((i < src_len) && (p < buf_size-1))
  426. {
  427. if(isslash(src[i]))
  428. {
  429. while(isslash(src[i]))
  430. i++;
  431. dest[p] = DIR_SEPARATOR_CHAR;
  432. p++;
  433. }
  434. else
  435. {
  436. dest[p] = src[i];
  437. i++;
  438. p++;
  439. }
  440. }
  441. dest[p] = '\0';
  442. if((p >= 2) && (dest[p-1] == DIR_SEPARATOR_CHAR) && (dest[p-2] != ':'))
  443. dest[p-1] = '\0';
  444. }
  445. void split_path_filename(const char *source,
  446. char *destpath, unsigned int dest_buffer_len,
  447. char *destfile, unsigned int file_buffer_len)
  448. {
  449. char temppath[MAX_PATH];
  450. struct stat path_info;
  451. int stat_res = stat(source, &path_info);
  452. // If the entirety of source is a directory
  453. if((stat_res >= 0) && S_ISDIR(path_info.st_mode))
  454. {
  455. if(dest_buffer_len)
  456. clean_path_slashes(source, destpath, dest_buffer_len);
  457. if(file_buffer_len)
  458. strcpy(destfile, "");
  459. }
  460. else
  461. // If source has a directory and a file
  462. if((source[0] != '\0') && get_path(source, temppath, MAX_PATH))
  463. {
  464. // get_path leaves off trailing /, add 1 to offset.
  465. if(dest_buffer_len)
  466. clean_path_slashes(temppath, destpath, dest_buffer_len);
  467. if(file_buffer_len)
  468. strncpy(destfile, &(source[strlen(temppath) + 1]), file_buffer_len);
  469. }
  470. // Source is just a file or blank.
  471. else
  472. {
  473. if(dest_buffer_len)
  474. strcpy(destpath, "");
  475. if(file_buffer_len)
  476. strncpy(destfile, source, file_buffer_len);
  477. }
  478. }
  479. void join_path_names(char* target, int max_len, const char* path1, const char* path2)
  480. {
  481. if(path1[strlen(path1)-1] == DIR_SEPARATOR_CHAR)
  482. snprintf(target, max_len, "%s%s", path1, path2);
  483. else
  484. snprintf(target, max_len, "%s%s%s", path1, DIR_SEPARATOR, path2);
  485. }
  486. int create_path_if_not_exists(const char *filename)
  487. {
  488. struct stat stat_info;
  489. char parent_directory[MAX_PATH];
  490. if(!get_path(filename, parent_directory, MAX_PATH))
  491. return 1;
  492. if(!stat(parent_directory, &stat_info))
  493. return 2;
  494. create_path_if_not_exists(parent_directory);
  495. if(mkdir(parent_directory, 0755))
  496. return 3;
  497. return 0;
  498. }
  499. // Navigate a path name.
  500. int change_dir_name(char *path_name, const char *dest)
  501. {
  502. struct stat stat_info;
  503. char path_temp[MAX_PATH];
  504. char path[MAX_PATH];
  505. const char *current;
  506. const char *next;
  507. const char *end;
  508. char current_char;
  509. size_t len;
  510. if(!dest || !dest[0])
  511. return -1;
  512. if(!path_name)
  513. return -1;
  514. current = dest;
  515. end = dest + strlen(dest);
  516. next = strchr(dest, ':');
  517. if(next)
  518. {
  519. /**
  520. * Destination starts with a Windows-style root directory.
  521. * Aside from Windows, these are often used by console SDKs (albeit with /
  522. * instead of \) to distinguish SD cards and the like.
  523. */
  524. if(next[1] != DIR_SEPARATOR_CHAR && next[1] != 0)
  525. return -1;
  526. snprintf(path, MAX_PATH, "%.*s" DIR_SEPARATOR, (int)(next - dest + 1),
  527. dest);
  528. if(stat(path, &stat_info) < 0)
  529. return -1;
  530. current = next + 1;
  531. if(current[0] == DIR_SEPARATOR_CHAR)
  532. current++;
  533. }
  534. else
  535. if(dest[0] == DIR_SEPARATOR_CHAR)
  536. {
  537. /**
  538. * Destination starts with a Unix-style root directory.
  539. * Aside from Unix-likes, these are also supported by console platforms.
  540. * Even Windows (back through XP at least) doesn't seem to mind them.
  541. */
  542. strcpy(path, DIR_SEPARATOR);
  543. current = dest + 1;
  544. }
  545. else
  546. {
  547. /**
  548. * Destination is relative--start from the current path. Make sure there's
  549. * a trailing separator.
  550. */
  551. if(path_name[strlen(path_name) - 1] != DIR_SEPARATOR_CHAR)
  552. snprintf(path, MAX_PATH, "%s" DIR_SEPARATOR, path_name);
  553. else
  554. strcpy(path, path_name);
  555. }
  556. current_char = current[0];
  557. len = strlen(path);
  558. // Apply directory fragments to the path.
  559. while(current_char != 0)
  560. {
  561. // Increment next to skip the separator so it will be copied over.
  562. next = strchr(current, DIR_SEPARATOR_CHAR);
  563. if(!next) next = end;
  564. else next++;
  565. // . does nothing, .. goes back one level
  566. if(current_char == '.')
  567. {
  568. if(current[1] == '.')
  569. {
  570. // Skip the rightmost separator (current level) and look for the
  571. // previous separator. If found, truncate the path to it.
  572. char *pos = path + len - 1;
  573. do
  574. {
  575. pos--;
  576. }
  577. while(pos >= path && *pos != DIR_SEPARATOR_CHAR);
  578. if(pos >= path)
  579. {
  580. pos[1] = 0;
  581. len = strlen(path);
  582. }
  583. }
  584. }
  585. else
  586. {
  587. snprintf(path + len, MAX_PATH - len, "%.*s", (int)(next - current),
  588. current);
  589. len = strlen(path);
  590. }
  591. current = next;
  592. current_char = current[0];
  593. }
  594. // This needs to be done before the stat for some platforms (e.g. 3DS)
  595. clean_path_slashes(path, path_temp, MAX_PATH);
  596. if(stat(path_temp, &stat_info) >= 0)
  597. {
  598. strcpy(path_name, path_temp);
  599. return 0;
  600. }
  601. return -1;
  602. }
  603. // Index must be an array of 256 ints
  604. void boyer_moore_index(const void *B, const size_t b_len,
  605. int index[256], boolean ignore_case)
  606. {
  607. char *b = (char *)B;
  608. int i;
  609. char *s = b;
  610. char *last = b + b_len - 1;
  611. for(i = 0; i < 256; i++)
  612. index[i] = b_len;
  613. if(!ignore_case)
  614. {
  615. for(s = b; s < last; s++)
  616. index[(int)*s] = last - s;
  617. }
  618. else
  619. {
  620. for(s = b; s < last; s++)
  621. index[memtolower((int)*s)] = last - s;
  622. // Duplicating the lowercase values over the uppercase values helps avoid
  623. // an extra tolower in the search function.
  624. memcpy(index + 'A', index + 'a', sizeof(int) * 26);
  625. }
  626. }
  627. // Search for substring B in haystack A. The index greatly increases the
  628. // search speed, especially for large needles. This is actually a reduced
  629. // Boyer-Moore search, as the original version uses two separate indexes.
  630. void *boyer_moore_search(const void *A, const size_t a_len,
  631. const void *B, const size_t b_len, const int index[256], boolean ignore_case)
  632. {
  633. const unsigned char *a = (const unsigned char *)A;
  634. const unsigned char *b = (const unsigned char *)B;
  635. size_t i = b_len - 1;
  636. size_t idx;
  637. int j;
  638. if(!ignore_case)
  639. {
  640. while(i < a_len)
  641. {
  642. j = b_len - 1;
  643. while(j >= 0 && a[i] == b[j])
  644. j--, i--;
  645. if(j == -1)
  646. return (void *)(a + i + 1);
  647. idx = index[(int)a[i]];
  648. i += MAX(b_len - j, idx);
  649. }
  650. }
  651. else
  652. {
  653. while(i < a_len)
  654. {
  655. j = b_len - 1;
  656. while(j >= 0 && memtolower((int)a[i]) == memtolower((int)b[j]))
  657. j--, i--;
  658. if(j == -1)
  659. return (void *)(a + i + 1);
  660. idx = index[(int)a[i]];
  661. i += MAX(b_len - j, idx);
  662. }
  663. }
  664. return NULL;
  665. }
  666. int mem_getc(const unsigned char **ptr)
  667. {
  668. int val = (*ptr)[0];
  669. *ptr += 1;
  670. return val;
  671. }
  672. int mem_getw(const unsigned char **ptr)
  673. {
  674. int val = (*ptr)[0] | ((*ptr)[1] << 8);
  675. *ptr += 2;
  676. return val;
  677. }
  678. int mem_getd(const unsigned char **ptr)
  679. {
  680. int val = (*ptr)[0] | ((*ptr)[1] << 8) | ((*ptr)[2] << 16) | ((int)((*ptr)[3]) << 24);
  681. *ptr += 4;
  682. return val;
  683. }
  684. void mem_putc(int src, unsigned char **ptr)
  685. {
  686. (*ptr)[0] = src;
  687. *ptr += 1;
  688. }
  689. void mem_putw(int src, unsigned char **ptr)
  690. {
  691. (*ptr)[0] = src & 0xFF;
  692. (*ptr)[1] = (src >> 8) & 0xFF;
  693. *ptr += 2;
  694. }
  695. void mem_putd(int src, unsigned char **ptr)
  696. {
  697. (*ptr)[0] = src & 0xFF;
  698. (*ptr)[1] = (src >> 8) & 0xFF;
  699. (*ptr)[2] = (src >> 16) & 0xFF;
  700. (*ptr)[3] = (src >> 24) & 0xFF;
  701. *ptr += 4;
  702. }
  703. // like fsafegets, except from memory.
  704. int memsafegets(char *dest, int size, char **src, char *end)
  705. {
  706. char *pos = dest;
  707. char *next = *src;
  708. char *stop = MIN(end, next+size);
  709. char ch;
  710. // Return 0 if this is the end of the memory block
  711. if(next == NULL)
  712. return 0;
  713. // Copy the memory until the end, the bound, or a newline
  714. while(next<stop && (ch = *next)!='\n')
  715. {
  716. *pos = ch;
  717. pos++;
  718. next++;
  719. }
  720. *pos = 0;
  721. // Place the counter where the next line begins
  722. if(next < end)
  723. {
  724. *src = next + 1;
  725. }
  726. // Mark that this is the end
  727. else
  728. {
  729. *src = NULL;
  730. }
  731. // Length at least 1 -- get rid of \r and \n
  732. if(pos > dest)
  733. if(pos[-1] == '\r' || pos[-1] == '\n')
  734. pos[-1] = 0;
  735. // Length at least 2 -- get rid of \r and \n
  736. if(pos - 1 > dest)
  737. if(pos[-2] == '\r' || pos[-2] == '\n')
  738. pos[-2] = 0;
  739. return 1;
  740. }
  741. #if defined(__WIN32__) && defined(__STRICT_ANSI__)
  742. /* On WIN32 with C99 defining __STRICT_ANSI__ these POSIX.1-2001 functions
  743. * are not available. The stricmp/strnicmp functions are available, but not
  744. * to C99 programs.
  745. *
  746. * Redefine them here; these are copied from glibc, and should be optimal.
  747. */
  748. int strcasecmp(const char *s1, const char *s2)
  749. {
  750. while(*s1 != '\0' && tolower(*s1) == tolower(*s2))
  751. {
  752. s1++;
  753. s2++;
  754. }
  755. return tolower(*(unsigned char *)s1) - tolower(*(unsigned char *)s2);
  756. }
  757. int strncasecmp(const char *s1, const char *s2, size_t n)
  758. {
  759. if(n == 0)
  760. return 0;
  761. while(n-- != 0 && tolower(*s1) == tolower(*s2))
  762. {
  763. if(n == 0 || *s1 == '\0' || *s2 == '\0')
  764. break;
  765. s1++;
  766. s2++;
  767. }
  768. return tolower(*(unsigned char *)s1) - tolower(*(unsigned char *)s2);
  769. }
  770. #endif // __WIN32__ && __STRICT_ANSI__
  771. #if defined(__WIN32__) || defined(__amigaos__)
  772. // strsep() stolen from glibc (GPL)
  773. char *strsep(char **stringp, const char *delim)
  774. {
  775. char *begin, *end;
  776. begin = *stringp;
  777. if(!begin)
  778. return NULL;
  779. if(delim[0] == '\0' || delim[1] == '\0')
  780. {
  781. char ch = delim[0];
  782. if(ch == '\0')
  783. end = NULL;
  784. else
  785. {
  786. if(*begin == ch)
  787. end = begin;
  788. else if(*begin == '\0')
  789. end = NULL;
  790. else
  791. end = strchr(begin + 1, ch);
  792. }
  793. }
  794. else
  795. end = strpbrk(begin, delim);
  796. if(end)
  797. {
  798. *end++ = '\0';
  799. *stringp = end;
  800. }
  801. else
  802. *stringp = NULL;
  803. return begin;
  804. }
  805. #endif // __WIN32__ || __amigaos__
  806. long dir_tell(struct mzx_dir *dir)
  807. {
  808. return dir->pos;
  809. }
  810. boolean dir_open(struct mzx_dir *dir, const char *path)
  811. {
  812. dir->d = opendir(path);
  813. if(!dir->d)
  814. return false;
  815. dir->entries = 0;
  816. while(readdir(dir->d) != NULL)
  817. dir->entries++;
  818. #if defined(CONFIG_PSP) || defined(CONFIG_3DS) || defined(CONFIG_SWITCH)
  819. strncpy(dir->path, path, PATH_BUF_LEN);
  820. dir->path[PATH_BUF_LEN - 1] = 0;
  821. closedir(dir->d);
  822. dir->d = opendir(path);
  823. #else
  824. rewinddir(dir->d);
  825. #endif
  826. dir->pos = 0;
  827. return true;
  828. }
  829. void dir_close(struct mzx_dir *dir)
  830. {
  831. if(dir->d)
  832. {
  833. closedir(dir->d);
  834. dir->d = NULL;
  835. dir->entries = 0;
  836. dir->pos = 0;
  837. }
  838. }
  839. void dir_seek(struct mzx_dir *dir, long offset)
  840. {
  841. long i;
  842. if(!dir->d)
  843. return;
  844. dir->pos = CLAMP(offset, 0L, dir->entries);
  845. #if defined(CONFIG_PSP) || defined(CONFIG_3DS) || defined(CONFIG_SWITCH)
  846. closedir(dir->d);
  847. dir->d = opendir(dir->path);
  848. if(!dir->d)
  849. return;
  850. #else
  851. rewinddir(dir->d);
  852. #endif
  853. for(i = 0; i < dir->pos; i++)
  854. readdir(dir->d);
  855. }
  856. boolean dir_get_next_entry(struct mzx_dir *dir, char *entry)
  857. {
  858. struct dirent *inode;
  859. if(!dir->d)
  860. return false;
  861. dir->pos = MIN(dir->pos + 1, dir->entries);
  862. inode = readdir(dir->d);
  863. if(!inode)
  864. {
  865. entry[0] = 0;
  866. return false;
  867. }
  868. snprintf(entry, PATH_BUF_LEN, "%s", inode->d_name);
  869. return true;
  870. }
  871. #if defined(__amigaos__)
  872. long __stack_chk_guard[8];
  873. void __stack_chk_fail(void)
  874. {
  875. warn("Stack overflow detected; terminated");
  876. exit(0);
  877. }
  878. #endif // __amigaos__
  879. #if defined(CONFIG_PSP) || defined(CONFIG_NDS)
  880. FILE *popen(const char *command, const char *type)
  881. {
  882. return NULL;
  883. }
  884. int pclose(FILE *stream)
  885. {
  886. return 0;
  887. }
  888. #endif // CONFIG_PSP || CONFIG_NDS