--- /dev/null
+/*
+Copyright (c) 2013-2021, tinydir authors:
+- Cong Xu
+- Lautis Sun
+- Baudouin Feildel
+- Andargor <andargor@yahoo.com>
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+1. Redistributions of source code must retain the above copyright notice, this
+ list of conditions and the following disclaimer.
+2. Redistributions in binary form must reproduce the above copyright notice,
+ this list of conditions and the following disclaimer in the documentation
+ and/or other materials provided with the distribution.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
+ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+#ifndef TINYDIR_H
+#define TINYDIR_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#if ((defined _UNICODE) && !(defined UNICODE))
+#define UNICODE
+#endif
+
+#if ((defined UNICODE) && !(defined _UNICODE))
+#define _UNICODE
+#endif
+
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+#ifdef _MSC_VER
+# ifndef WIN32_LEAN_AND_MEAN
+# define WIN32_LEAN_AND_MEAN
+# endif
+# include <windows.h>
+# include <tchar.h>
+# pragma warning(push)
+# pragma warning (disable : 4996)
+#else
+# include <dirent.h>
+# include <libgen.h>
+# include <sys/stat.h>
+# include <stddef.h>
+#endif
+#ifdef __MINGW32__
+# include <tchar.h>
+#endif
+
+
+/* types */
+
+/* Windows UNICODE wide character support */
+#if defined _MSC_VER || defined __MINGW32__
+# define _tinydir_char_t TCHAR
+# define TINYDIR_STRING(s) _TEXT(s)
+# define _tinydir_strlen _tcslen
+# define _tinydir_strcpy _tcscpy
+# define _tinydir_strcat _tcscat
+# define _tinydir_strcmp _tcscmp
+# define _tinydir_strrchr _tcsrchr
+# define _tinydir_strncmp _tcsncmp
+#else
+# define _tinydir_char_t char
+# define TINYDIR_STRING(s) s
+# define _tinydir_strlen strlen
+# define _tinydir_strcpy strcpy
+# define _tinydir_strcat strcat
+# define _tinydir_strcmp strcmp
+# define _tinydir_strrchr strrchr
+# define _tinydir_strncmp strncmp
+#endif
+
+#if (defined _MSC_VER || defined __MINGW32__)
+# include <windows.h>
+# define _TINYDIR_PATH_MAX MAX_PATH
+#elif defined __linux__
+# include <limits.h>
+# ifdef PATH_MAX
+# define _TINYDIR_PATH_MAX PATH_MAX
+# endif
+#elif defined(__unix__) || (defined(__APPLE__) && defined(__MACH__))
+# include <sys/param.h>
+# if defined(BSD)
+# include <limits.h>
+# ifdef PATH_MAX
+# define _TINYDIR_PATH_MAX PATH_MAX
+# endif
+# endif
+#endif
+
+#ifndef _TINYDIR_PATH_MAX
+#define _TINYDIR_PATH_MAX 4096
+#endif
+
+#ifdef _MSC_VER
+/* extra chars for the "\\*" mask */
+# define _TINYDIR_PATH_EXTRA 2
+#else
+# define _TINYDIR_PATH_EXTRA 0
+#endif
+
+#define _TINYDIR_FILENAME_MAX 256
+
+#if (defined _MSC_VER || defined __MINGW32__)
+#define _TINYDIR_DRIVE_MAX 3
+#endif
+
+#ifdef _MSC_VER
+# define _TINYDIR_FUNC static __inline
+#elif !defined __STDC_VERSION__ || __STDC_VERSION__ < 199901L
+# define _TINYDIR_FUNC static __inline__
+#elif defined(__cplusplus)
+# define _TINYDIR_FUNC static inline
+#elif defined(__GNUC__)
+/* Suppress unused function warning */
+# define _TINYDIR_FUNC __attribute__((unused)) static
+#else
+# define _TINYDIR_FUNC static
+#endif
+
+/* readdir_r usage; define TINYDIR_USE_READDIR_R to use it (if supported) */
+#ifdef TINYDIR_USE_READDIR_R
+
+/* readdir_r is a POSIX-only function, and may not be available under various
+ * environments/settings, e.g. MinGW. Use readdir fallback */
+#if _POSIX_C_SOURCE >= 1 || _XOPEN_SOURCE || _BSD_SOURCE || _SVID_SOURCE ||\
+ _POSIX_SOURCE
+# define _TINYDIR_HAS_READDIR_R
+#endif
+#if _POSIX_C_SOURCE >= 200112L
+# define _TINYDIR_HAS_FPATHCONF
+# include <unistd.h>
+#endif
+#if _BSD_SOURCE || _SVID_SOURCE || \
+ (_POSIX_C_SOURCE >= 200809L || _XOPEN_SOURCE >= 700)
+# define _TINYDIR_HAS_DIRFD
+# include <sys/types.h>
+#endif
+#if defined _TINYDIR_HAS_FPATHCONF && defined _TINYDIR_HAS_DIRFD &&\
+ defined _PC_NAME_MAX
+# define _TINYDIR_USE_FPATHCONF
+#endif
+#if defined __MINGW32__ || !defined _TINYDIR_HAS_READDIR_R ||\
+ !(defined _TINYDIR_USE_FPATHCONF || defined NAME_MAX)
+# define _TINYDIR_USE_READDIR
+#endif
+
+/* Use readdir by default */
+#else
+# define _TINYDIR_USE_READDIR
+#endif
+
+/* MINGW32 has two versions of dirent, ASCII and UNICODE*/
+#ifndef _MSC_VER
+#if (defined __MINGW32__) && (defined _UNICODE)
+#define _TINYDIR_DIR _WDIR
+#define _tinydir_dirent _wdirent
+#define _tinydir_opendir _wopendir
+#define _tinydir_readdir _wreaddir
+#define _tinydir_closedir _wclosedir
+#else
+#define _TINYDIR_DIR DIR
+#define _tinydir_dirent dirent
+#define _tinydir_opendir opendir
+#define _tinydir_readdir readdir
+#define _tinydir_closedir closedir
+#endif
+#endif
+
+/* Allow user to use a custom allocator by defining _TINYDIR_MALLOC and _TINYDIR_FREE. */
+#if defined(_TINYDIR_MALLOC) && defined(_TINYDIR_FREE)
+#elif !defined(_TINYDIR_MALLOC) && !defined(_TINYDIR_FREE)
+#else
+#error "Either define both alloc and free or none of them!"
+#endif
+
+#if !defined(_TINYDIR_MALLOC)
+ #define _TINYDIR_MALLOC(_size) malloc(_size)
+ #define _TINYDIR_FREE(_ptr) free(_ptr)
+#endif /* !defined(_TINYDIR_MALLOC) */
+
+typedef struct tinydir_file
+{
+ _tinydir_char_t path[_TINYDIR_PATH_MAX];
+ _tinydir_char_t name[_TINYDIR_FILENAME_MAX];
+ _tinydir_char_t *extension;
+ int is_dir;
+ int is_reg;
+
+#ifndef _MSC_VER
+#ifdef __MINGW32__
+ struct _stat _s;
+#else
+ struct stat _s;
+#endif
+#endif
+} tinydir_file;
+
+typedef struct tinydir_dir
+{
+ _tinydir_char_t path[_TINYDIR_PATH_MAX];
+ int has_next;
+ size_t n_files;
+
+ tinydir_file *_files;
+#ifdef _MSC_VER
+ HANDLE _h;
+ WIN32_FIND_DATA _f;
+#else
+ _TINYDIR_DIR *_d;
+ struct _tinydir_dirent *_e;
+#ifndef _TINYDIR_USE_READDIR
+ struct _tinydir_dirent *_ep;
+#endif
+#endif
+} tinydir_dir;
+
+
+/* declarations */
+
+_TINYDIR_FUNC
+int tinydir_open(tinydir_dir *dir, const _tinydir_char_t *path);
+_TINYDIR_FUNC
+int tinydir_open_sorted(tinydir_dir *dir, const _tinydir_char_t *path);
+_TINYDIR_FUNC
+void tinydir_close(tinydir_dir *dir);
+
+_TINYDIR_FUNC
+int tinydir_next(tinydir_dir *dir);
+_TINYDIR_FUNC
+int tinydir_readfile(const tinydir_dir *dir, tinydir_file *file);
+_TINYDIR_FUNC
+int tinydir_readfile_n(const tinydir_dir *dir, tinydir_file *file, size_t i);
+_TINYDIR_FUNC
+int tinydir_open_subdir_n(tinydir_dir *dir, size_t i);
+
+_TINYDIR_FUNC
+int tinydir_file_open(tinydir_file *file, const _tinydir_char_t *path);
+_TINYDIR_FUNC
+void _tinydir_get_ext(tinydir_file *file);
+_TINYDIR_FUNC
+int _tinydir_file_cmp(const void *a, const void *b);
+#ifndef _MSC_VER
+#ifndef _TINYDIR_USE_READDIR
+_TINYDIR_FUNC
+size_t _tinydir_dirent_buf_size(_TINYDIR_DIR *dirp);
+#endif
+#endif
+
+
+/* definitions*/
+
+_TINYDIR_FUNC
+int tinydir_open(tinydir_dir *dir, const _tinydir_char_t *path)
+{
+#ifndef _MSC_VER
+#ifndef _TINYDIR_USE_READDIR
+ int error;
+ int size; /* using int size */
+#endif
+#else
+ _tinydir_char_t path_buf[_TINYDIR_PATH_MAX];
+#endif
+ _tinydir_char_t *pathp;
+
+ if (dir == NULL || path == NULL || _tinydir_strlen(path) == 0)
+ {
+ errno = EINVAL;
+ return -1;
+ }
+ if (_tinydir_strlen(path) + _TINYDIR_PATH_EXTRA >= _TINYDIR_PATH_MAX)
+ {
+ errno = ENAMETOOLONG;
+ return -1;
+ }
+
+ /* initialise dir */
+ dir->_files = NULL;
+#ifdef _MSC_VER
+ dir->_h = INVALID_HANDLE_VALUE;
+#else
+ dir->_d = NULL;
+#ifndef _TINYDIR_USE_READDIR
+ dir->_ep = NULL;
+#endif
+#endif
+ tinydir_close(dir);
+
+ _tinydir_strcpy(dir->path, path);
+ /* Remove trailing slashes */
+ pathp = &dir->path[_tinydir_strlen(dir->path) - 1];
+ while (pathp != dir->path && (*pathp == TINYDIR_STRING('\\') || *pathp == TINYDIR_STRING('/')))
+ {
+ *pathp = TINYDIR_STRING('\0');
+ pathp++;
+ }
+#ifdef _MSC_VER
+ _tinydir_strcpy(path_buf, dir->path);
+ _tinydir_strcat(path_buf, TINYDIR_STRING("\\*"));
+#if (defined WINAPI_FAMILY) && (WINAPI_FAMILY != WINAPI_FAMILY_DESKTOP_APP)
+ dir->_h = FindFirstFileEx(path_buf, FindExInfoStandard, &dir->_f, FindExSearchNameMatch, NULL, 0);
+#else
+ dir->_h = FindFirstFile(path_buf, &dir->_f);
+#endif
+ if (dir->_h == INVALID_HANDLE_VALUE)
+ {
+ errno = ENOENT;
+#else
+ dir->_d = _tinydir_opendir(path);
+ if (dir->_d == NULL)
+ {
+#endif
+ goto bail;
+ }
+
+ /* read first file */
+ dir->has_next = 1;
+#ifndef _MSC_VER
+#ifdef _TINYDIR_USE_READDIR
+ dir->_e = _tinydir_readdir(dir->_d);
+#else
+ /* allocate dirent buffer for readdir_r */
+ size = _tinydir_dirent_buf_size(dir->_d); /* conversion to int */
+ if (size == -1) return -1;
+ dir->_ep = (struct _tinydir_dirent*)_TINYDIR_MALLOC(size);
+ if (dir->_ep == NULL) return -1;
+
+ error = readdir_r(dir->_d, dir->_ep, &dir->_e);
+ if (error != 0) return -1;
+#endif
+ if (dir->_e == NULL)
+ {
+ dir->has_next = 0;
+ }
+#endif
+
+ return 0;
+
+bail:
+ tinydir_close(dir);
+ return -1;
+}
+
+_TINYDIR_FUNC
+int tinydir_open_sorted(tinydir_dir *dir, const _tinydir_char_t *path)
+{
+ /* Count the number of files first, to pre-allocate the files array */
+ size_t n_files = 0;
+ if (tinydir_open(dir, path) == -1)
+ {
+ return -1;
+ }
+ while (dir->has_next)
+ {
+ n_files++;
+ if (tinydir_next(dir) == -1)
+ {
+ goto bail;
+ }
+ }
+ tinydir_close(dir);
+
+ if (n_files == 0 || tinydir_open(dir, path) == -1)
+ {
+ return -1;
+ }
+
+ dir->n_files = 0;
+ dir->_files = (tinydir_file *)_TINYDIR_MALLOC(sizeof *dir->_files * n_files);
+ if (dir->_files == NULL)
+ {
+ goto bail;
+ }
+ while (dir->has_next)
+ {
+ tinydir_file *p_file;
+ dir->n_files++;
+
+ p_file = &dir->_files[dir->n_files - 1];
+ if (tinydir_readfile(dir, p_file) == -1)
+ {
+ goto bail;
+ }
+
+ if (tinydir_next(dir) == -1)
+ {
+ goto bail;
+ }
+
+ /* Just in case the number of files has changed between the first and
+ second reads, terminate without writing into unallocated memory */
+ if (dir->n_files == n_files)
+ {
+ break;
+ }
+ }
+
+ qsort(dir->_files, dir->n_files, sizeof(tinydir_file), _tinydir_file_cmp);
+
+ return 0;
+
+bail:
+ tinydir_close(dir);
+ return -1;
+}
+
+_TINYDIR_FUNC
+void tinydir_close(tinydir_dir *dir)
+{
+ if (dir == NULL)
+ {
+ return;
+ }
+
+ memset(dir->path, 0, sizeof(dir->path));
+ dir->has_next = 0;
+ dir->n_files = 0;
+ _TINYDIR_FREE(dir->_files);
+ dir->_files = NULL;
+#ifdef _MSC_VER
+ if (dir->_h != INVALID_HANDLE_VALUE)
+ {
+ FindClose(dir->_h);
+ }
+ dir->_h = INVALID_HANDLE_VALUE;
+#else
+ if (dir->_d)
+ {
+ _tinydir_closedir(dir->_d);
+ }
+ dir->_d = NULL;
+ dir->_e = NULL;
+#ifndef _TINYDIR_USE_READDIR
+ _TINYDIR_FREE(dir->_ep);
+ dir->_ep = NULL;
+#endif
+#endif
+}
+
+_TINYDIR_FUNC
+int tinydir_next(tinydir_dir *dir)
+{
+ if (dir == NULL)
+ {
+ errno = EINVAL;
+ return -1;
+ }
+ if (!dir->has_next)
+ {
+ errno = ENOENT;
+ return -1;
+ }
+
+#ifdef _MSC_VER
+ if (FindNextFile(dir->_h, &dir->_f) == 0)
+#else
+#ifdef _TINYDIR_USE_READDIR
+ dir->_e = _tinydir_readdir(dir->_d);
+#else
+ if (dir->_ep == NULL)
+ {
+ return -1;
+ }
+ if (readdir_r(dir->_d, dir->_ep, &dir->_e) != 0)
+ {
+ return -1;
+ }
+#endif
+ if (dir->_e == NULL)
+#endif
+ {
+ dir->has_next = 0;
+#ifdef _MSC_VER
+ if (GetLastError() != ERROR_SUCCESS &&
+ GetLastError() != ERROR_NO_MORE_FILES)
+ {
+ tinydir_close(dir);
+ errno = EIO;
+ return -1;
+ }
+#endif
+ }
+
+ return 0;
+}
+
+_TINYDIR_FUNC
+int tinydir_readfile(const tinydir_dir *dir, tinydir_file *file)
+{
+ const _tinydir_char_t *filename;
+ if (dir == NULL || file == NULL)
+ {
+ errno = EINVAL;
+ return -1;
+ }
+#ifdef _MSC_VER
+ if (dir->_h == INVALID_HANDLE_VALUE)
+#else
+ if (dir->_e == NULL)
+#endif
+ {
+ errno = ENOENT;
+ return -1;
+ }
+ filename =
+#ifdef _MSC_VER
+ dir->_f.cFileName;
+#else
+ dir->_e->d_name;
+#endif
+ if (_tinydir_strlen(dir->path) +
+ _tinydir_strlen(filename) + 1 + _TINYDIR_PATH_EXTRA >=
+ _TINYDIR_PATH_MAX)
+ {
+ /* the path for the file will be too long */
+ errno = ENAMETOOLONG;
+ return -1;
+ }
+ if (_tinydir_strlen(filename) >= _TINYDIR_FILENAME_MAX)
+ {
+ errno = ENAMETOOLONG;
+ return -1;
+ }
+
+ _tinydir_strcpy(file->path, dir->path);
+ if (_tinydir_strcmp(dir->path, TINYDIR_STRING("/")) != 0)
+ _tinydir_strcat(file->path, TINYDIR_STRING("/"));
+ _tinydir_strcpy(file->name, filename);
+ _tinydir_strcat(file->path, filename);
+#ifndef _MSC_VER
+#ifdef __MINGW32__
+ if (_tstat(
+#elif (defined _BSD_SOURCE) || (defined _DEFAULT_SOURCE) \
+ || ((defined _XOPEN_SOURCE) && (_XOPEN_SOURCE >= 500)) \
+ || ((defined _POSIX_C_SOURCE) && (_POSIX_C_SOURCE >= 200112L)) \
+ || ((defined __APPLE__) && (defined __MACH__)) \
+ || (defined BSD)
+ if (lstat(
+#else
+ if (stat(
+#endif
+ file->path, &file->_s) == -1)
+ {
+ return -1;
+ }
+#endif
+ _tinydir_get_ext(file);
+
+ file->is_dir =
+#ifdef _MSC_VER
+ !!(dir->_f.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY);
+#else
+ S_ISDIR(file->_s.st_mode);
+#endif
+ file->is_reg =
+#ifdef _MSC_VER
+ !!(dir->_f.dwFileAttributes & FILE_ATTRIBUTE_NORMAL) ||
+ (
+ !(dir->_f.dwFileAttributes & FILE_ATTRIBUTE_DEVICE) &&
+ !(dir->_f.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) &&
+ !(dir->_f.dwFileAttributes & FILE_ATTRIBUTE_ENCRYPTED) &&
+#ifdef FILE_ATTRIBUTE_INTEGRITY_STREAM
+ !(dir->_f.dwFileAttributes & FILE_ATTRIBUTE_INTEGRITY_STREAM) &&
+#endif
+#ifdef FILE_ATTRIBUTE_NO_SCRUB_DATA
+ !(dir->_f.dwFileAttributes & FILE_ATTRIBUTE_NO_SCRUB_DATA) &&
+#endif
+ !(dir->_f.dwFileAttributes & FILE_ATTRIBUTE_OFFLINE) &&
+ !(dir->_f.dwFileAttributes & FILE_ATTRIBUTE_TEMPORARY));
+#else
+ S_ISREG(file->_s.st_mode);
+#endif
+
+ return 0;
+}
+
+_TINYDIR_FUNC
+int tinydir_readfile_n(const tinydir_dir *dir, tinydir_file *file, size_t i)
+{
+ if (dir == NULL || file == NULL)
+ {
+ errno = EINVAL;
+ return -1;
+ }
+ if (i >= dir->n_files)
+ {
+ errno = ENOENT;
+ return -1;
+ }
+
+ memcpy(file, &dir->_files[i], sizeof(tinydir_file));
+ _tinydir_get_ext(file);
+
+ return 0;
+}
+
+_TINYDIR_FUNC
+int tinydir_open_subdir_n(tinydir_dir *dir, size_t i)
+{
+ _tinydir_char_t path[_TINYDIR_PATH_MAX];
+ if (dir == NULL)
+ {
+ errno = EINVAL;
+ return -1;
+ }
+ if (i >= dir->n_files || !dir->_files[i].is_dir)
+ {
+ errno = ENOENT;
+ return -1;
+ }
+
+ _tinydir_strcpy(path, dir->_files[i].path);
+ tinydir_close(dir);
+ if (tinydir_open_sorted(dir, path) == -1)
+ {
+ return -1;
+ }
+
+ return 0;
+}
+
+/* Open a single file given its path */
+_TINYDIR_FUNC
+int tinydir_file_open(tinydir_file *file, const _tinydir_char_t *path)
+{
+ tinydir_dir dir;
+ int result = 0;
+ int found = 0;
+ _tinydir_char_t dir_name_buf[_TINYDIR_PATH_MAX];
+ _tinydir_char_t file_name_buf[_TINYDIR_FILENAME_MAX];
+ _tinydir_char_t *dir_name;
+ _tinydir_char_t *base_name;
+#if (defined _MSC_VER || defined __MINGW32__)
+ _tinydir_char_t drive_buf[_TINYDIR_PATH_MAX];
+ _tinydir_char_t ext_buf[_TINYDIR_FILENAME_MAX];
+#endif
+
+ if (file == NULL || path == NULL || _tinydir_strlen(path) == 0)
+ {
+ errno = EINVAL;
+ return -1;
+ }
+ if (_tinydir_strlen(path) + _TINYDIR_PATH_EXTRA >= _TINYDIR_PATH_MAX)
+ {
+ errno = ENAMETOOLONG;
+ return -1;
+ }
+
+ /* Get the parent path */
+#if (defined _MSC_VER || defined __MINGW32__)
+#if ((defined _MSC_VER) && (_MSC_VER >= 1400))
+ errno = _tsplitpath_s(
+ path,
+ drive_buf, _TINYDIR_DRIVE_MAX,
+ dir_name_buf, _TINYDIR_FILENAME_MAX,
+ file_name_buf, _TINYDIR_FILENAME_MAX,
+ ext_buf, _TINYDIR_FILENAME_MAX);
+#else
+ _tsplitpath(
+ path,
+ drive_buf,
+ dir_name_buf,
+ file_name_buf,
+ ext_buf);
+#endif
+
+ if (errno)
+ {
+ return -1;
+ }
+
+/* _splitpath_s not work fine with only filename and widechar support */
+#ifdef _UNICODE
+ if (drive_buf[0] == L'\xFEFE')
+ drive_buf[0] = '\0';
+ if (dir_name_buf[0] == L'\xFEFE')
+ dir_name_buf[0] = '\0';
+#endif
+
+ /* Emulate the behavior of dirname by returning "." for dir name if it's
+ empty */
+ if (drive_buf[0] == '\0' && dir_name_buf[0] == '\0')
+ {
+ _tinydir_strcpy(dir_name_buf, TINYDIR_STRING("."));
+ }
+ /* Concatenate the drive letter and dir name to form full dir name */
+ _tinydir_strcat(drive_buf, dir_name_buf);
+ dir_name = drive_buf;
+ /* Concatenate the file name and extension to form base name */
+ _tinydir_strcat(file_name_buf, ext_buf);
+ base_name = file_name_buf;
+#else
+ _tinydir_strcpy(dir_name_buf, path);
+ dir_name = dirname(dir_name_buf);
+ _tinydir_strcpy(file_name_buf, path);
+ base_name = basename(file_name_buf);
+#endif
+
+ /* Special case: if the path is a root dir, open the parent dir as the file */
+#if (defined _MSC_VER || defined __MINGW32__)
+ if (_tinydir_strlen(base_name) == 0)
+#else
+ if ((_tinydir_strcmp(base_name, TINYDIR_STRING("/"))) == 0)
+#endif
+ {
+ memset(file, 0, sizeof * file);
+ file->is_dir = 1;
+ file->is_reg = 0;
+ _tinydir_strcpy(file->path, dir_name);
+ file->extension = file->path + _tinydir_strlen(file->path);
+ return 0;
+ }
+
+ /* Open the parent directory */
+ if (tinydir_open(&dir, dir_name) == -1)
+ {
+ return -1;
+ }
+
+ /* Read through the parent directory and look for the file */
+ while (dir.has_next)
+ {
+ if (tinydir_readfile(&dir, file) == -1)
+ {
+ result = -1;
+ goto bail;
+ }
+ if (_tinydir_strcmp(file->name, base_name) == 0)
+ {
+ /* File found */
+ found = 1;
+ break;
+ }
+ tinydir_next(&dir);
+ }
+ if (!found)
+ {
+ result = -1;
+ errno = ENOENT;
+ }
+
+bail:
+ tinydir_close(&dir);
+ return result;
+}
+
+_TINYDIR_FUNC
+void _tinydir_get_ext(tinydir_file *file)
+{
+ _tinydir_char_t *period = _tinydir_strrchr(file->name, TINYDIR_STRING('.'));
+ if (period == NULL)
+ {
+ file->extension = &(file->name[_tinydir_strlen(file->name)]);
+ }
+ else
+ {
+ file->extension = period + 1;
+ }
+}
+
+_TINYDIR_FUNC
+int _tinydir_file_cmp(const void *a, const void *b)
+{
+ const tinydir_file *fa = (const tinydir_file *)a;
+ const tinydir_file *fb = (const tinydir_file *)b;
+ if (fa->is_dir != fb->is_dir)
+ {
+ return -(fa->is_dir - fb->is_dir);
+ }
+ return _tinydir_strncmp(fa->name, fb->name, _TINYDIR_FILENAME_MAX);
+}
+
+#ifndef _MSC_VER
+#ifndef _TINYDIR_USE_READDIR
+/*
+The following authored by Ben Hutchings <ben@decadent.org.uk>
+from https://womble.decadent.org.uk/readdir_r-advisory.html
+*/
+/* Calculate the required buffer size (in bytes) for directory *
+* entries read from the given directory handle. Return -1 if this *
+* this cannot be done. *
+* *
+* This code does not trust values of NAME_MAX that are less than *
+* 255, since some systems (including at least HP-UX) incorrectly *
+* define it to be a smaller value. */
+_TINYDIR_FUNC
+size_t _tinydir_dirent_buf_size(_TINYDIR_DIR *dirp)
+{
+ long name_max;
+ size_t name_end;
+ /* parameter may be unused */
+ (void)dirp;
+
+#if defined _TINYDIR_USE_FPATHCONF
+ name_max = fpathconf(dirfd(dirp), _PC_NAME_MAX);
+ if (name_max == -1)
+#if defined(NAME_MAX)
+ name_max = (NAME_MAX > 255) ? NAME_MAX : 255;
+#else
+ return (size_t)(-1);
+#endif
+#elif defined(NAME_MAX)
+ name_max = (NAME_MAX > 255) ? NAME_MAX : 255;
+#else
+#error "buffer size for readdir_r cannot be determined"
+#endif
+ name_end = (size_t)offsetof(struct _tinydir_dirent, d_name) + name_max + 1;
+ return (name_end > sizeof(struct _tinydir_dirent) ?
+ name_end : sizeof(struct _tinydir_dirent));
+}
+#endif
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+# if defined (_MSC_VER)
+# pragma warning(pop)
+# endif
+
+#endif
--- /dev/null
+/*
+ ------------------------------------------------------------------------------
+ Licensing information can be found at the end of the file.
+ ------------------------------------------------------------------------------
+
+ cute_files.h - v1.0
+
+ To create implementation (the function definitions)
+ #define CUTE_FILES_IMPLEMENTATION
+ in *one* C/CPP file (translation unit) that includes this file
+
+ Summary:
+ Utility header for traversing directories to apply a function on each found file.
+ Recursively finds sub-directories. Can also be used to iterate over files in a
+ folder manually. All operations done in a cross-platform manner (thx posix!).
+
+ This header does no dynamic memory allocation, and performs internally safe string
+ copies as necessary. Strings for paths, file names and file extensions are all
+ capped, and intended to use primarily the C run-time stack memory. Feel free to
+ modify the defines in this file to adjust string size limitations.
+
+ Read the header for specifics on each function.
+
+ Here's an example to print all files in a folder:
+ cf_dir_t dir;
+ cf_dir_open(&dir, "a");
+
+ while (dir.has_next)
+ {
+ cf_file_t file;
+ cf_read_file(&dir, &file);
+ printf("%s\n", file.name);
+ cf_dir_next(&dir);
+ }
+
+ cf_dir_close(&dir);
+*/
+
+#if !defined(CUTE_FILES_H)
+
+#define CUTE_FILES_WINDOWS 1
+#define CUTE_FILES_MAC 2
+#define CUTE_FILES_UNIX 3
+
+#if defined(_WIN32)
+ #define CUTE_FILES_PLATFORM CUTE_FILES_WINDOWS
+ #if !defined(_CRT_SECURE_NO_WARNINGS)
+ #define _CRT_SECURE_NO_WARNINGS
+ #endif
+#elif defined(__APPLE__)
+ #define CUTE_FILES_PLATFORM CUTE_FILES_MAC
+#else
+ #define CUTE_FILES_PLATFORM CUTE_FILES_UNIX
+#endif
+
+#include <string.h> // strerror, strncpy
+
+// change to 0 to compile out any debug checks
+#define CUTE_FILES_DEBUG_CHECKS 1
+
+#if CUTE_FILES_DEBUG_CHECKS
+
+ #include <stdio.h> // printf
+ #include <assert.h> // assert
+ #include <errno.h>
+ #define CUTE_FILES_ASSERT assert
+
+#else
+
+ #define CUTE_FILES_ASSERT(...)
+
+#endif // CUTE_FILES_DEBUG_CHECKS
+
+#define CUTE_FILES_MAX_PATH 1024
+#define CUTE_FILES_MAX_FILENAME 256
+#define CUTE_FILES_MAX_EXT 32
+
+struct cf_file_t;
+struct cf_dir_t;
+struct cf_time_t;
+typedef struct cf_file_t cf_file_t;
+typedef struct cf_dir_t cf_dir_t;
+typedef struct cf_time_t cf_time_t;
+typedef void (cf_callback_t)(cf_file_t* file, void* udata);
+
+// Stores the file extension in cf_file_t::ext, and returns a pointer to
+// cf_file_t::ext
+const char* cf_get_ext(cf_file_t* file);
+
+// Applies a function (cb) to all files in a directory. Will recursively visit
+// all subdirectories. Useful for asset management, file searching, indexing, etc.
+void cf_traverse(const char* path, cf_callback_t* cb, void* udata);
+
+// Fills out a cf_file_t struct with file information. Does not actually open the
+// file contents, and instead performs more lightweight OS-specific calls.
+int cf_read_file(cf_dir_t* dir, cf_file_t* file);
+
+// Once a cf_dir_t is opened, this function can be used to grab another file
+// from the operating system.
+void cf_dir_next(cf_dir_t* dir);
+
+// Performs lightweight OS-specific call to close internal handle.
+void cf_dir_close(cf_dir_t* dir);
+
+// Performs lightweight OS-specific call to open a file handle on a directory.
+int cf_dir_open(cf_dir_t* dir, const char* path);
+
+// Compares file last write times. -1 if file at path_a was modified earlier than path_b.
+// 0 if they are equal. 1 if file at path_b was modified earlier than path_a.
+int cf_compare_file_times_by_path(const char* path_a, const char* path_b);
+
+// Retrieves time file was last modified, returns 0 upon failure
+int cf_get_file_time(const char* path, cf_time_t* time);
+
+// Compares file last write times. -1 if time_a was modified earlier than path_b.
+// 0 if they are equal. 1 if time_b was modified earlier than path_a.
+int cf_compare_file_times(cf_time_t* time_a, cf_time_t* time_b);
+
+// Returns 1 of file exists, otherwise returns 0.
+int cf_file_exists(const char* path);
+
+// Returns 1 if the file's extension matches the string in ext
+// Returns 0 otherwise
+int cf_match_ext(cf_file_t* file, const char* ext);
+
+// Prints detected errors to stdout
+void cf_do_unit_tests();
+
+#if CUTE_FILES_PLATFORM == CUTE_FILES_WINDOWS
+
+#if !defined _CRT_SECURE_NO_WARNINGS
+#define _CRT_SECURE_NO_WARNINGS
+#endif
+#include <windows.h>
+
+ struct cf_file_t
+ {
+ char path[CUTE_FILES_MAX_PATH];
+ char name[CUTE_FILES_MAX_FILENAME];
+ char ext[CUTE_FILES_MAX_EXT];
+ int is_dir;
+ int is_reg;
+ size_t size;
+ };
+
+ struct cf_dir_t
+ {
+ char path[CUTE_FILES_MAX_PATH];
+ int has_next;
+ HANDLE handle;
+ WIN32_FIND_DATAA fdata;
+ };
+
+ struct cf_time_t
+ {
+ FILETIME time;
+ };
+
+#elif CUTE_FILES_PLATFORM == CUTE_FILES_MAC || CUTE_FILES_PLATFORM == CUTE_FILES_UNIX
+
+ #include <sys/stat.h>
+ #include <dirent.h>
+ #include <unistd.h>
+ #include <time.h>
+
+ struct cf_file_t
+ {
+ char path[CUTE_FILES_MAX_PATH];
+ char name[CUTE_FILES_MAX_FILENAME];
+ char ext[CUTE_FILES_MAX_EXT];
+ int is_dir;
+ int is_reg;
+ int size;
+ struct stat info;
+ };
+
+ struct cf_dir_t
+ {
+ char path[CUTE_FILES_MAX_PATH];
+ int has_next;
+ DIR* dir;
+ struct dirent* entry;
+ };
+
+ struct cf_time_t
+ {
+ time_t time;
+ };
+
+#endif
+
+#define CUTE_FILES_H
+#endif
+
+#ifdef CUTE_FILES_IMPLEMENTATION
+#ifndef CUTE_FILES_IMPLEMENTATION_ONCE
+#define CUTE_FILES_IMPLEMENTATION_ONCE
+
+#define cf_safe_strcpy(dst, src, n, max) cf_safe_strcpy_internal(dst, src, n, max, __FILE__, __LINE__)
+static int cf_safe_strcpy_internal(char* dst, const char* src, int n, int max, const char* file, int line)
+{
+ int c;
+ const char* original = src;
+
+ do
+ {
+ if (n >= max)
+ {
+ if (!CUTE_FILES_DEBUG_CHECKS) break;
+ printf("ERROR: String \"%s\" too long to copy on line %d in file %s (max length of %d).\n"
+ , original
+ , line
+ , file
+ , max);
+ CUTE_FILES_ASSERT(0);
+ }
+
+ c = *src++;
+ dst[n] = c;
+ ++n;
+ } while (c);
+
+ return n;
+}
+
+const char* cf_get_ext(cf_file_t* file)
+{
+ char* name = file->name;
+ char* period = NULL;
+ while (*name++) if (*name == '.') period = name;
+ if (period) cf_safe_strcpy(file->ext, period, 0, CUTE_FILES_MAX_EXT);
+ else file->ext[0] = 0;
+ return file->ext;
+}
+
+void cf_traverse(const char* path, cf_callback_t* cb, void* udata)
+{
+ cf_dir_t dir;
+ cf_dir_open(&dir, path);
+
+ while (dir.has_next)
+ {
+ cf_file_t file;
+ int res = cf_read_file(&dir, &file);
+
+ if (res == 0) {
+ cf_dir_next(&dir);
+ continue;
+ }
+
+ if (file.is_dir && file.name[0] != '.')
+ {
+ char path2[CUTE_FILES_MAX_PATH];
+ int n = cf_safe_strcpy(path2, path, 0, CUTE_FILES_MAX_PATH);
+ n = cf_safe_strcpy(path2, "/", n - 1, CUTE_FILES_MAX_PATH);
+ cf_safe_strcpy(path2, file.name, n -1, CUTE_FILES_MAX_PATH);
+ cf_traverse(path2, cb, udata);
+ }
+
+ if (file.is_reg) cb(&file, udata);
+ cf_dir_next(&dir);
+ }
+
+ cf_dir_close(&dir);
+}
+
+int cf_match_ext(cf_file_t* file, const char* ext)
+{
+ return !strcmp(file->ext, ext);
+}
+
+#if CUTE_FILES_PLATFORM == CUTE_FILES_WINDOWS
+
+ int cf_read_file(cf_dir_t* dir, cf_file_t* file)
+ {
+ CUTE_FILES_ASSERT(dir->handle != INVALID_HANDLE_VALUE);
+
+ int n = 0;
+ char* fpath = file->path;
+ char* dpath = dir->path;
+
+ n = cf_safe_strcpy(fpath, dpath, 0, CUTE_FILES_MAX_PATH);
+ n = cf_safe_strcpy(fpath, "/", n - 1, CUTE_FILES_MAX_PATH);
+
+ char* dname = dir->fdata.cFileName;
+ char* fname = file->name;
+
+ cf_safe_strcpy(fname, dname, 0, CUTE_FILES_MAX_FILENAME);
+ cf_safe_strcpy(fpath, fname, n - 1, CUTE_FILES_MAX_PATH);
+
+ size_t max_dword = MAXDWORD;
+ file->size = ((size_t)dir->fdata.nFileSizeHigh * (max_dword + 1)) + (size_t)dir->fdata.nFileSizeLow;
+ cf_get_ext(file);
+
+ file->is_dir = !!(dir->fdata.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY);
+ file->is_reg = !!(dir->fdata.dwFileAttributes & FILE_ATTRIBUTE_NORMAL) ||
+ !(dir->fdata.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY);
+
+ return 1;
+ }
+
+ void cf_dir_next(cf_dir_t* dir)
+ {
+ CUTE_FILES_ASSERT(dir->has_next);
+
+ if (!FindNextFileA(dir->handle, &dir->fdata))
+ {
+ dir->has_next = 0;
+ DWORD err = GetLastError();
+ CUTE_FILES_ASSERT(err == ERROR_SUCCESS || err == ERROR_NO_MORE_FILES);
+ }
+ }
+
+ void cf_dir_close(cf_dir_t* dir)
+ {
+ dir->path[0] = 0;
+ dir->has_next = 0;
+ if (dir->handle != INVALID_HANDLE_VALUE) FindClose(dir->handle);
+ }
+
+ int cf_dir_open(cf_dir_t* dir, const char* path)
+ {
+ int n = cf_safe_strcpy(dir->path, path, 0, CUTE_FILES_MAX_PATH);
+ n = cf_safe_strcpy(dir->path, "\\*", n - 1, CUTE_FILES_MAX_PATH);
+ dir->handle = FindFirstFileA(dir->path, &dir->fdata);
+ dir->path[n - 3] = 0;
+
+ if (dir->handle == INVALID_HANDLE_VALUE)
+ {
+ printf("ERROR: Failed to open directory (%s): %s.\n", path, strerror(errno));
+ cf_dir_close(dir);
+ CUTE_FILES_ASSERT(0);
+ return 0;
+ }
+
+ dir->has_next = 1;
+
+ return 1;
+ }
+
+ int cf_compare_file_times_by_path(const char* path_a, const char* path_b)
+ {
+ FILETIME time_a = { 0 };
+ FILETIME time_b = { 0 };
+ WIN32_FILE_ATTRIBUTE_DATA data;
+
+ if (GetFileAttributesExA(path_a, GetFileExInfoStandard, &data)) time_a = data.ftLastWriteTime;
+ if (GetFileAttributesExA(path_b, GetFileExInfoStandard, &data)) time_b = data.ftLastWriteTime;
+ return CompareFileTime(&time_a, &time_b);
+ }
+
+ int cf_get_file_time(const char* path, cf_time_t* time)
+ {
+ FILETIME initialized_to_zero = { 0 };
+ time->time = initialized_to_zero;
+ WIN32_FILE_ATTRIBUTE_DATA data;
+ if (GetFileAttributesExA(path, GetFileExInfoStandard, &data))
+ {
+ time->time = data.ftLastWriteTime;
+ return 1;
+ }
+ return 0;
+ }
+
+ int cf_compare_file_times(cf_time_t* time_a, cf_time_t* time_b)
+ {
+ return CompareFileTime(&time_a->time, &time_b->time);
+ }
+
+ int cf_file_exists(const char* path)
+ {
+ WIN32_FILE_ATTRIBUTE_DATA unused;
+ return GetFileAttributesExA(path, GetFileExInfoStandard, &unused);
+ }
+
+#elif CUTE_FILES_PLATFORM == CUTE_FILES_MAC || CUTE_FILES_PLATFORM == CUTE_FILES_UNIX
+
+ int cf_read_file(cf_dir_t* dir, cf_file_t* file)
+ {
+ CUTE_FILES_ASSERT(dir->entry);
+
+ int n = 0;
+ char* fpath = file->path;
+ char* dpath = dir->path;
+
+ n = cf_safe_strcpy(fpath, dpath, 0, CUTE_FILES_MAX_PATH);
+ n = cf_safe_strcpy(fpath, "/", n - 1, CUTE_FILES_MAX_PATH);
+
+ char* dname = dir->entry->d_name;
+ char* fname = file->name;
+
+ cf_safe_strcpy(fname, dname, 0, CUTE_FILES_MAX_FILENAME);
+ cf_safe_strcpy(fpath, fname, n - 1, CUTE_FILES_MAX_PATH);
+
+ if (stat(file->path, &file->info))
+ return 0;
+
+ file->size = file->info.st_size;
+ cf_get_ext(file);
+
+ file->is_dir = S_ISDIR(file->info.st_mode);
+ file->is_reg = S_ISREG(file->info.st_mode);
+
+ return 1;
+ }
+
+ void cf_dir_next(cf_dir_t* dir)
+ {
+ CUTE_FILES_ASSERT(dir->has_next);
+ dir->entry = readdir(dir->dir);
+ dir->has_next = dir->entry ? 1 : 0;
+ }
+
+ void cf_dir_close(cf_dir_t* dir)
+ {
+ dir->path[0] = 0;
+ if (dir->dir) closedir(dir->dir);
+ dir->dir = 0;
+ dir->has_next = 0;
+ dir->entry = 0;
+ }
+
+ int cf_dir_open(cf_dir_t* dir, const char* path)
+ {
+ cf_safe_strcpy(dir->path, path, 0, CUTE_FILES_MAX_PATH);
+ dir->dir = opendir(path);
+
+ if (!dir->dir)
+ {
+ printf("ERROR: Failed to open directory (%s): %s.\n", path, strerror(errno));
+ cf_dir_close(dir);
+ CUTE_FILES_ASSERT(0);
+ return 0;
+ }
+
+ dir->has_next = 1;
+ dir->entry = readdir(dir->dir);
+ if (!dir->dir) dir->has_next = 0;
+
+ return 1;
+ }
+
+ // Warning : untested code! (let me know if it breaks)
+ int cf_compare_file_times_by_path(const char* path_a, const char* path_b)
+ {
+ time_t time_a;
+ time_t time_b;
+ struct stat info;
+ if (stat(path_a, &info)) return 0;
+ time_a = info.st_mtime;
+ if (stat(path_b, &info)) return 0;
+ time_b = info.st_mtime;
+ return (int)difftime(time_a, time_b);
+ }
+
+ // Warning : untested code! (let me know if it breaks)
+ int cf_get_file_time(const char* path, cf_time_t* time)
+ {
+ struct stat info;
+ if (stat(path, &info)) return 0;
+ time->time = info.st_mtime;
+ return 1;
+ }
+
+ // Warning : untested code! (let me know if it breaks)
+ int cf_compare_file_times(cf_time_t* time_a, cf_time_t* time_b)
+ {
+ return (int)difftime(time_a->time, time_b->time);
+ }
+
+ // Warning : untested code! (let me know if it breaks)
+ int cf_file_exists(const char* path)
+ {
+ return access(path, F_OK) != -1;
+ }
+
+#endif // CUTE_FILES_PLATFORM
+
+#endif // CUTE_FILES_IMPLEMENTATION_ONCE
+#endif // CUTE_FILES_IMPLEMENTATION
+
+/*
+ ------------------------------------------------------------------------------
+ This software is available under 2 licenses - you may choose the one you like.
+ ------------------------------------------------------------------------------
+ ALTERNATIVE A - zlib license
+ Copyright (c) 2017 Randy Gaul http://www.randygaul.net
+ This software is provided 'as-is', without any express or implied warranty.
+ In no event will the authors be held liable for any damages arising from
+ the use of this software.
+ Permission is granted to anyone to use this software for any purpose,
+ including commercial applications, and to alter it and redistribute it
+ freely, subject to the following restrictions:
+ 1. The origin of this software must not be misrepresented; you must not
+ claim that you wrote the original software. If you use this software
+ in a product, an acknowledgment in the product documentation would be
+ appreciated but is not required.
+ 2. Altered source versions must be plainly marked as such, and must not
+ be misrepresented as being the original software.
+ 3. This notice may not be removed or altered from any source distribution.
+ ------------------------------------------------------------------------------
+ ALTERNATIVE B - Public Domain (www.unlicense.org)
+ This is free and unencumbered software released into the public domain.
+ Anyone is free to copy, modify, publish, use, compile, sell, or distribute this
+ software, either in source code form or as a compiled binary, for any purpose,
+ commercial or non-commercial, and by any means.
+ In jurisdictions that recognize copyright laws, the author or authors of this
+ software dedicate any and all copyright interest in the software to the public
+ domain. We make this dedication for the benefit of the public at large and to
+ the detriment of our heirs and successors. We intend this dedication to be an
+ overt act of relinquishment in perpetuity of all present and future rights to
+ this software under copyright law.
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ ------------------------------------------------------------------------------
+*/
#define PLAYBACK_LEFT 2
#define PLAYBACK_RIGHT 4
-#define L (PLAYBACK_LEFT | PLAYBACK_MONO)
-#define C (PLAYBACK_LEFT | PLAYBACK_RIGHT | PLAYBACK_MONO)
-#define R (PLAYBACK_RIGHT | PLAYBACK_MONO)
+#define SBVL (PLAYBACK_LEFT | PLAYBACK_MONO)
+#define SBVC (PLAYBACK_LEFT | PLAYBACK_RIGHT | PLAYBACK_MONO)
+#define SBVR (PLAYBACK_RIGHT | PLAYBACK_MONO)
static int8 channel_position[7][6] =
{
{ 0 },
- { C },
- { L, R },
- { L, C, R },
- { L, R, L, R },
- { L, C, R, L, R },
- { L, C, R, L, R, C },
+ { SBVC },
+ { SBVL, SBVR },
+ { SBVL, SBVC, SBVR },
+ { SBVL, SBVR, SBVL, SBVR },
+ { SBVL, SBVC, SBVR, SBVL, SBVR },
+ { SBVL, SBVC, SBVR, SBVL, SBVR, SBVC },
};
samples;
float refresh_rate;
- v2f mouse,
+ double mouse_pos[2];
+ v2f
+ mouse_delta,
mouse_wheel;
/* Runtime */
int gamepad_ready;
const char *gamepad_name;
int gamepad_id;
+ int gamepad_use_trackpad_look;
}
VG_STATIC vg = { .time_rate = 1.0 };
void vg_mouse_callback( GLFWwindow* ptrW, double xpos, double ypos )
{
- vg.mouse[0] = xpos;
- vg.mouse[1] = ypos;
+ vg.mouse_delta[0] += xpos - vg.mouse_pos[0];
+ vg.mouse_delta[1] += ypos - vg.mouse_pos[1];
+
+ vg.mouse_pos[0] = xpos;
+ vg.mouse_pos[1] = ypos;
}
void vg_scroll_callback( GLFWwindow* ptrW, double xoffset, double yoffset )
glfwSetCharCallback( vg.window, console_proc_wchar );
glfwSetKeyCallback( vg.window, console_proc_key );
- glfwSetInputMode( vg.window, GLFW_CURSOR, GLFW_CURSOR_HIDDEN );
+ glfwSetInputMode( vg.window, GLFW_CURSOR, GLFW_CURSOR_DISABLED );
+
+ if( glfwRawMouseMotionSupported() )
+ glfwSetInputMode( vg.window, GLFW_RAW_MOUSE_MOTION, GLFW_TRUE );
if( !gladLoadGLLoader((GLADloadproc)glfwGetProcAddress) )
{
if( glfwWindowShouldClose( vg.window ) )
break;
- v2_copy( (v2f){ 0.0f, 0.0f }, vg.mouse_wheel );
+ v2_zero( vg.mouse_wheel );
+ v2_zero( vg.mouse_delta );
+
glfwPollEvents();
vg.time_real_last = vg.time_real;
vg.engine_stage = k_engine_stage_ui;
{
ui_begin( vg.window_x, vg.window_y );
- ui_set_mouse( vg.mouse[0], vg.mouse[1],
- vg_get_button_state( "primary" ) );
+
+ /* TODO */
+ ui_set_mouse( vg.mouse_pos[0], vg.mouse_pos[1], 0 );
vg_profile_drawn(
(struct vg_profile *[]){&vg_prof_update,&vg_prof_render}, 2,
#include "dr_soft/miniaudio.h"
-
#include "vg/vg.h"
#include "vg/vg_stdint.h"
#include "vg/vg_platform.h"
#ifndef __clang__
#pragma GCC push_options
#pragma GCC optimize ("O3")
+ #pragma GCC diagnostic push
+ #pragma GCC diagnostic ignored "-Wdeprecated-declarations"
#endif
#endif
-#pragma GCC diagnostic push
-#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
-
#define STB_VORBIS_MAX_CHANNELS 2
#include "stb/stb_vorbis.h"
-#pragma GCC diagnostic pop
-
#ifdef __GNUC__
#ifndef __clang__
#pragma GCC pop_options
+ #pragma GCC diagnostic pop
#endif
#endif
k_input_type_unknown,
k_input_type_keyboard_key,
+ k_input_type_mouse_button, /* ? TODO */
k_input_type_gamepad_axis,
k_input_type_gamepad_button
}
keyboard_positive,
keyboard_negative;
+ int gamepad_inverted;
float value;
}
axis;
vg_named_inputs[ 32 ];
VG_STATIC u32 vg_named_input_count = 0;
+VG_STATIC void vg_create_unnamed_input( struct input_binding *bind,
+ enum input_type type )
+{
+ memset( bind, 0, sizeof(struct input_binding) );
+
+ bind->name = "API DEFINED";
+ bind->save_this = 0;
+ bind->type = type;
+
+ bind->axis.gamepad_axis = -1;
+ bind->axis.keyboard_positive = -1;
+ bind->axis.keyboard_negative = -1;
+ bind->button.gamepad_id = -1;
+ bind->button.keyboard_id = -1;
+}
+
VG_STATIC struct input_binding *vg_create_named_input( const char *name,
enum input_type type )
{
bind->save_this = 0;
bind->type = type;
+ bind->axis.gamepad_axis = -1;
+ bind->axis.keyboard_positive = -1;
+ bind->axis.keyboard_negative = -1;
+ bind->button.gamepad_id = -1;
+ bind->button.keyboard_id = -1;
+
return bind;
}
VG_STATIC struct input_binding *vg_get_named_input( const char *name )
{
+ if( name[0] == '+' || name[0] == '-' )
+ name ++;
+
for( u32 i=0; i<vg_named_input_count; i++ )
{
struct input_binding *bind = &vg_named_inputs[i];
{k_input_type_keyboard_key, "down", GLFW_KEY_DOWN},
{k_input_type_keyboard_key, "shift", GLFW_KEY_LEFT_SHIFT},
{k_input_type_keyboard_key, "control", GLFW_KEY_LEFT_CONTROL},
+ {k_input_type_keyboard_key, "\2enter", GLFW_KEY_ENTER},
+ {k_input_type_keyboard_key, "\2escape", GLFW_KEY_ESCAPE },
{k_input_type_gamepad_axis, "gp-lt", GLFW_GAMEPAD_AXIS_LEFT_TRIGGER},
{k_input_type_gamepad_axis, "gp-rt", GLFW_GAMEPAD_AXIS_RIGHT_TRIGGER},
{k_input_type_gamepad_button, "gp-dpad-down", GLFW_GAMEPAD_BUTTON_DPAD_DOWN},
{k_input_type_gamepad_button, "gp-dpad-left", GLFW_GAMEPAD_BUTTON_DPAD_LEFT},
{k_input_type_gamepad_button, "gp-dpad-right", GLFW_GAMEPAD_BUTTON_DPAD_RIGHT},
- {k_input_type_gamepad_button, "gp-dpad-up", GLFW_GAMEPAD_BUTTON_DPAD_UP}
+ {k_input_type_gamepad_button, "gp-dpad-up", GLFW_GAMEPAD_BUTTON_DPAD_UP},
+ {k_input_type_gamepad_button, "\2gp-menu", GLFW_GAMEPAD_BUTTON_BACK}
};
VG_STATIC const char *vg_input_to_str( u32 input, enum input_type input_type )
VG_STATIC void vg_print_binding_info( struct input_binding *bind )
{
vg_info( " name: %s\n", bind->name );
- vg_info( " type: %s\n", (const char *[]){"button","axis"}[ bind->type ] );
+ vg_info( " type: %s\n", (const char *[]){"button","axis","axis[0-1]"}
+ [ bind->type ] );
vg_info( " save this? %d\n", bind->save_this );
if( (bind->type == k_input_type_axis) ||
}
}
+VG_STATIC void vg_apply_bind_str( struct input_binding *bind,
+ const char *mod,
+ const char *str )
+{
+ int axis_mod = 0;
+ char modch = ' ';
+ if( (mod[0] == '-') || (mod[0] == '+') )
+ {
+ axis_mod = 1;
+ modch = mod[0];
+ mod ++;
+ }
+
+ int invert = 0;
+ if( (str[0] == '-' ) )
+ {
+ invert = 1;
+ str ++;
+ }
+
+ u32 id;
+ enum input_type type = vg_str_to_input( str, &id );
+
+ if( bind->type == k_input_type_button )
+ {
+ if( axis_mod )
+ {
+ vg_error( "Cannot use axis modifiers on button input!\n" );
+ return;
+ }
+
+ if( invert )
+ {
+ vg_error( "Cannot invert button input!\n" );
+ return;
+ }
+
+ if( type == k_input_type_keyboard_key )
+ bind->button.keyboard_id = id;
+ else if( type == k_input_type_gamepad_button )
+ bind->button.gamepad_id = id;
+ else
+ {
+ vg_error( "Unknown button or key '%s'\n", str );
+ return;
+ }
+ }
+ else if( (bind->type == k_input_type_axis ) ||
+ (bind->type == k_input_type_axis_norm))
+ {
+ if( axis_mod )
+ {
+ if( type == k_input_type_keyboard_key )
+ {
+ if( invert )
+ {
+ vg_error( "Cannot invert a keyboard key!\n" );
+ return;
+ }
+
+ if( modch == '+' )
+ bind->axis.keyboard_positive = id;
+ else
+ bind->axis.keyboard_negative = id;
+ }
+ else
+ {
+ vg_error( "You can only bind keyboard keys to +- axises\n" );
+ return;
+ }
+ }
+ else
+ {
+ if( type == k_input_type_gamepad_axis )
+ {
+ bind->axis.gamepad_inverted = invert;
+ bind->axis.gamepad_axis = id;
+ }
+ else
+ {
+ vg_error( "You can only bind gamepad axises to this\n" );
+ return;
+ }
+ }
+ }
+}
+
/*
* bind x jump
* bind a -horizontal
* bind d +horizontal
+ * bind -gp-ls-h horizontal
*/
VG_STATIC int vg_rebind_input_cmd( int argc, const char *argv[] )
return 0;
}
- if( strlen(argv[0]) == 0 )
- return 0;
-
- int axis_mod = 0;
- if( (argv[0][0] == '-') || (argv[0][0] == '+') )
- axis_mod = 1;
-
- struct input_binding *bind = vg_get_named_input( argv[0]+axis_mod );
+ const char *str_bind_name = argv[0];
+ struct input_binding *bind = vg_get_named_input( str_bind_name );
if( !bind )
{
- vg_error( "There is no named input called %s\n", argv[0]+axis_mod );
+ vg_error( "There is no bind with that name '%s'\n", str_bind_name );
return 0;
}
if( argc == 2 )
{
- u32 id;
- enum input_type type = vg_str_to_input( argv[1], &id );
-
- if( bind->type == k_input_type_button )
- {
- if( axis_mod )
- {
- vg_error( "Cannot use axis modifiers on button input!\n" );
- return 0;
- }
+ const char *str_input_id = argv[1];
- if( type == k_input_type_keyboard_key )
- bind->button.keyboard_id = id;
- else if( type == k_input_type_gamepad_button )
- bind->button.gamepad_id = id;
- else
- {
- vg_error( "Unknown button or key '%s'\n", argv[1] );
- return 0;
- }
- }
- else if( (bind->type == k_input_type_axis ) ||
- (bind->type == k_input_type_axis_norm))
- {
- if( axis_mod )
- {
- if( type == k_input_type_keyboard_key )
- {
- if( argv[0][0] == '+' )
- bind->axis.keyboard_positive = id;
- else
- bind->axis.keyboard_negative = id;
- }
- else
- {
- vg_error( "You can only bind keyboard keys to +- axises\n" );
- return 0;
- }
- }
- else
- {
- if( type == k_input_type_gamepad_axis )
- bind->axis.gamepad_axis = id;
- else
- {
- vg_error( "You can only bind gamepad axises to this\n" );
- return 0;
- }
- }
- }
+ vg_apply_bind_str( bind, str_bind_name, str_input_id );
+ return 0;
}
return 0;
VG_STATIC void vg_input_update( u32 num, struct input_binding *binds )
{
+ if( vg_console.enabled )
+ {
+ for( i32 i=0; i<num; i++ )
+ {
+ struct input_binding *bind = &binds[i];
+
+ if( bind->type == k_input_type_button )
+ {
+ bind->button.prev = bind->button.value;
+ bind->button.value = 0;
+ }
+ }
+
+ return;
+ }
+
for( i32 i=0; i<num; i++ )
{
struct input_binding *bind = &binds[i];
keyboard_value -= 1.0f;
if( bind->axis.gamepad_axis != -1 )
+ {
gamepad_value = vg.gamepad.axes[ bind->axis.gamepad_axis ];
+ if( bind->axis.gamepad_inverted )
+ gamepad_value *= -1.0f;
+ }
if( fabsf(gamepad_value) <= 0.01f )
gamepad_value = 0.0f;
}
}
+VG_STATIC int vg_console_enabled(void);
VG_STATIC int vg_input_button_down( struct input_binding *bind )
{
if( bind->button.value && !bind->button.prev )
return 1;
return 0;
}
+#if 0
VG_STATIC float vg_get_axis( const char *axis )
{
return 0.0f;
}
-VG_STATIC int vg_console_enabled(void);
VG_STATIC void vg_get_button_states( const char *name, int *cur, int *prev )
{
if(vg_get_button( button )) return k_button_state_pressed;
return k_button_state_none;
}
+#endif
void vg_update_inputs(void)
{
vg.gamepad_name = glfwGetGamepadName( id );
vg_success( "Gamepad mapping registered: %s\n", vg.gamepad_name );
+ /* This path usually only gets chosen when starting outside of steam */
+ if( !strcmp(vg.gamepad_name, "Steam Controller") )
+ vg.gamepad_use_trackpad_look = 1;
+
vg.gamepad_ready = 1;
vg.gamepad_id = id;
break;
static inline void v2_normalize( v2f a )
{
- v2_muls( a, 1.f / v2_length( a ), a );
+ v2_muls( a, 1.0f / v2_length( a ), a );
+}
+
+static void v2_normalize_clamp( v2f a )
+{
+ float l2 = v2_length2( a );
+ if( l2 > 1.0f )
+ v2_muls( a, 1.0f/sqrtf(l2), a );
}
static inline void v2_floor( v2f a, v2f b )
#pragma pack(pop)
typedef void ISteamUtils;
+typedef void ISteamInput;
+
ISteamUtils *SteamAPI_SteamUtils_v010(void);
ISteamUtils *SteamAPI_SteamUtils(void)
{
ISteamUtils *self, void( *fn_print )(int, const char *) );
+ISteamInput *SteamAPI_SteamInput_v006(void);
+ISteamInput *SteamAPI_SteamInput(void)
+{
+ return SteamAPI_SteamInput_v006();
+}
+
+typedef u64 InputHandle_t;
+
+typedef enum ESteamInputType ESteamInputType;
+enum ESteamInputType
+{
+ k_ESteamInputType_Unknown,
+ k_ESteamInputType_SteamController,
+ k_ESteamInputType_XBox360Controller,
+ k_ESteamInputType_XBoxOneController,
+ k_ESteamInputType_GenericGamepad,
+ k_ESteamInputType_PS4Controller,
+ k_ESteamInputType_AppleMFiController,
+ k_ESteamInputType_AndroidController,
+ k_ESteamInputType_SwitchJoyConPair,
+ k_ESteamInputType_SwitchJoyConSingle,
+ k_ESteamInputType_SwitchProController,
+ k_ESteamInputType_MobileTouch,
+ k_ESteamInputType_PS3Controller,
+ k_ESteamInputType_PS5Controller,
+ k_ESteamInputType_SteamDeckController, // Added in SDK 153
+ k_ESteamInputType_Count,
+ k_ESteamInputType_MaximumPossibleValue = 255,
+};
+
+int SteamAPI_ISteamInput_Init( ISteamInput* self, int bExplicitlyCallRunFrame );
+int SteamAPI_ISteamInput_Shutdown( ISteamInput* self );
+InputHandle_t SteamAPI_ISteamInput_GetControllerForGamepadIndex(
+ ISteamInput* self, int nIndex );
+ESteamInputType SteamAPI_ISteamInput_GetInputTypeForHandle(
+ ISteamInput* self, InputHandle_t inputHandle );
+void SteamAPI_ISteamInput_RunFrame( ISteamInput* self, int bReservedValue );
+
+
#endif /* VG_STEAM_UTILS_H */