mirror of
https://github.com/Relintai/sfw.git
synced 2024-11-08 07:52:09 +01:00
Took the DirAccess and FileAccess implementations from pandemonium. Also kept a few helper methods from the old api. Removed tinydir.
This commit is contained in:
parent
9ee7156890
commit
42d9e1be66
@ -1,831 +0,0 @@
|
|||||||
/*
|
|
||||||
Copyright (c) 2013-2019, 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__
|
|
||||||
#else
|
|
||||||
# define _TINYDIR_FUNC static inline
|
|
||||||
#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))
|
|
||||||
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
|
|
@ -2,144 +2,811 @@
|
|||||||
//--STRIP
|
//--STRIP
|
||||||
#include "dir_access.h"
|
#include "dir_access.h"
|
||||||
|
|
||||||
#include "3rd_tinydir.h"
|
|
||||||
#include <cstdio>
|
#include <cstdio>
|
||||||
//--STRIP
|
//--STRIP
|
||||||
|
|
||||||
Error DirAccess::open_dir(const String &path, bool skip_specials) {
|
/*************************************************************************/
|
||||||
if (_dir_open) {
|
/* dir_access.cpp */
|
||||||
return ERR_CANT_ACQUIRE_RESOURCE;
|
/* From https://github.com/Relintai/pandemonium_engine (MIT) */
|
||||||
}
|
/*************************************************************************/
|
||||||
|
|
||||||
|
//--STRIP
|
||||||
|
#include "dir_access.h"
|
||||||
|
|
||||||
|
#include "core/list.h"
|
||||||
|
|
||||||
|
#include "core/file_access.h"
|
||||||
|
#include "core/memory.h"
|
||||||
|
#include "core/local_vector.h"
|
||||||
|
//--STRIP
|
||||||
|
|
||||||
|
#if defined(_WIN64) || defined(_WIN32)
|
||||||
|
#else
|
||||||
|
|
||||||
|
#include <dirent.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <sys/statvfs.h>
|
||||||
|
|
||||||
|
#ifdef HAVE_MNTENT
|
||||||
|
#include <mntent.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined(_WIN64) || defined(_WIN32)
|
||||||
|
#else
|
||||||
|
|
||||||
|
Error DirAccess::list_dir_begin(bool skip_specials) {
|
||||||
|
list_dir_end(); //close any previous dir opening!
|
||||||
|
|
||||||
_skip_specials = skip_specials;
|
_skip_specials = skip_specials;
|
||||||
|
|
||||||
if (tinydir_open(_dir, path.utf8().get_data()) == -1) {
|
//char real_current_dir_name[2048]; //is this enough?!
|
||||||
return FAILED;
|
//getcwd(real_current_dir_name,2048);
|
||||||
|
//chdir(current_path.utf8().get_data());
|
||||||
|
dir_stream = opendir(current_dir.utf8().get_data());
|
||||||
|
//chdir(real_current_dir_name);
|
||||||
|
if (!dir_stream) {
|
||||||
|
return ERR_CANT_OPEN; //error!
|
||||||
}
|
}
|
||||||
|
|
||||||
_dir_open = true;
|
|
||||||
|
|
||||||
return OK;
|
return OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
Error DirAccess::open_dir(const char *path, bool skip_specials) {
|
bool DirAccess::file_exists(String p_file) {
|
||||||
if (_dir_open) {
|
GLOBAL_LOCK_FUNCTION
|
||||||
return ERR_CANT_ACQUIRE_RESOURCE;
|
|
||||||
|
if (p_file.is_rel_path()) {
|
||||||
|
p_file = current_dir.plus_file(p_file);
|
||||||
}
|
}
|
||||||
|
|
||||||
_skip_specials = skip_specials;
|
struct stat flags;
|
||||||
|
bool success = (stat(p_file.utf8().get_data(), &flags) == 0);
|
||||||
|
|
||||||
if (tinydir_open(_dir, path) == -1) {
|
if (success && S_ISDIR(flags.st_mode)) {
|
||||||
return FAILED;
|
success = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
_dir_open = true;
|
return success;
|
||||||
|
|
||||||
return OK;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void DirAccess::close_dir() {
|
bool DirAccess::dir_exists(String p_dir) {
|
||||||
if (!_dir_open) {
|
GLOBAL_LOCK_FUNCTION
|
||||||
return;
|
|
||||||
|
if (p_dir.is_rel_path()) {
|
||||||
|
p_dir = get_current_dir().plus_file(p_dir);
|
||||||
}
|
}
|
||||||
|
|
||||||
tinydir_close(_dir);
|
struct stat flags;
|
||||||
|
bool success = (stat(p_dir.utf8().get_data(), &flags) == 0);
|
||||||
|
|
||||||
_dir_open = false;
|
return (success && S_ISDIR(flags.st_mode));
|
||||||
}
|
}
|
||||||
|
|
||||||
bool DirAccess::has_next() {
|
uint64_t DirAccess::get_modified_time(String p_file) {
|
||||||
if (!_dir) {
|
if (p_file.is_rel_path()) {
|
||||||
|
p_file = current_dir.plus_file(p_file);
|
||||||
|
}
|
||||||
|
|
||||||
|
struct stat flags;
|
||||||
|
bool success = (stat(p_file.utf8().get_data(), &flags) == 0);
|
||||||
|
|
||||||
|
if (success) {
|
||||||
|
return flags.st_mtime;
|
||||||
|
} else {
|
||||||
|
ERR_FAIL_V(0);
|
||||||
|
};
|
||||||
|
return 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
String DirAccess::get_next() {
|
||||||
|
if (!dir_stream) {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
dirent *entry = readdir(dir_stream);
|
||||||
|
|
||||||
|
if (entry == nullptr) {
|
||||||
|
list_dir_end();
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
String fname = fix_unicode_name(entry->d_name);
|
||||||
|
|
||||||
|
// Look at d_type to determine if the entry is a directory, unless
|
||||||
|
// its type is unknown (the file system does not support it) or if
|
||||||
|
// the type is a link, in that case we want to resolve the link to
|
||||||
|
// known if it points to a directory. stat() will resolve the link
|
||||||
|
// for us.
|
||||||
|
if (entry->d_type == DT_UNKNOWN || entry->d_type == DT_LNK) {
|
||||||
|
String f = current_dir.plus_file(fname);
|
||||||
|
|
||||||
|
struct stat flags;
|
||||||
|
if (stat(f.utf8().get_data(), &flags) == 0) {
|
||||||
|
_cisdir = S_ISDIR(flags.st_mode);
|
||||||
|
} else {
|
||||||
|
_cisdir = false;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
_cisdir = (entry->d_type == DT_DIR);
|
||||||
|
}
|
||||||
|
|
||||||
|
_cishidden = is_hidden(fname);
|
||||||
|
|
||||||
|
_cisspecial = is_special(fname);
|
||||||
|
|
||||||
|
if (_skip_specials && _cisspecial) {
|
||||||
|
// Should only happen 2 times max
|
||||||
|
return get_next();
|
||||||
|
}
|
||||||
|
|
||||||
|
return fname;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool DirAccess::current_is_dir() const {
|
||||||
|
return _cisdir;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool DirAccess::current_is_file() const {
|
||||||
|
return !_cisdir;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool DirAccess::current_is_special_dir() const {
|
||||||
|
return _cisspecial;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool DirAccess::current_is_hidden() const {
|
||||||
|
return _cishidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
void DirAccess::list_dir_end() {
|
||||||
|
if (dir_stream) {
|
||||||
|
closedir(dir_stream);
|
||||||
|
}
|
||||||
|
dir_stream = nullptr;
|
||||||
|
_cisdir = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
#if defined(HAVE_MNTENT) && defined(X11_ENABLED)
|
||||||
|
static bool _filter_drive(struct mntent *mnt) {
|
||||||
|
// Ignore devices that don't point to /dev
|
||||||
|
if (strncmp(mnt->mnt_fsname, "/dev", 4) != 0) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return _dir->has_next;
|
// Accept devices mounted at common locations
|
||||||
}
|
if (strncmp(mnt->mnt_dir, "/media", 6) == 0 ||
|
||||||
bool DirAccess::read() {
|
strncmp(mnt->mnt_dir, "/mnt", 4) == 0 ||
|
||||||
_read_file_result = tinydir_readfile(_dir, _file);
|
strncmp(mnt->mnt_dir, "/home", 5) == 0 ||
|
||||||
|
strncmp(mnt->mnt_dir, "/run/media", 10) == 0) {
|
||||||
return _read_file_result != -1;
|
|
||||||
}
|
|
||||||
bool DirAccess::next() {
|
|
||||||
if (!_dir->has_next) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool rres = read();
|
|
||||||
while (!rres && _dir->has_next) {
|
|
||||||
tinydir_next(_dir);
|
|
||||||
rres = read();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!rres) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (_dir->has_next) {
|
|
||||||
tinydir_next(_dir);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (_skip_specials && current_is_dir() && current_is_special_dir()) {
|
|
||||||
return next();
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool DirAccess::current_is_ok() {
|
|
||||||
return _read_file_result == 01;
|
|
||||||
}
|
|
||||||
String DirAccess::current_get_name() {
|
|
||||||
return String(_file->name);
|
|
||||||
}
|
|
||||||
String DirAccess::current_get_path() {
|
|
||||||
return String(_file->path);
|
|
||||||
}
|
|
||||||
String DirAccess::current_get_extension() {
|
|
||||||
return String(_file->extension);
|
|
||||||
}
|
|
||||||
const char *DirAccess::current_get_name_cstr() {
|
|
||||||
return _file->name;
|
|
||||||
}
|
|
||||||
const char *DirAccess::current_get_path_cstr() {
|
|
||||||
return _file->path;
|
|
||||||
}
|
|
||||||
const char *DirAccess::current_get_extension_cstr() {
|
|
||||||
return _file->extension;
|
|
||||||
}
|
|
||||||
bool DirAccess::current_is_file() {
|
|
||||||
return !_file->is_dir;
|
|
||||||
}
|
|
||||||
bool DirAccess::current_is_dir() {
|
|
||||||
return _file->is_dir;
|
|
||||||
}
|
|
||||||
bool DirAccess::current_is_special_dir() {
|
|
||||||
if ((_file->name[0] == '.' && _file->name[1] == '\0') || (_file->name[0] == '.' && _file->name[1] == '.')) {
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Ignore everything else
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
bool DirAccess::is_dir_open() {
|
static void _get_drives(List<String> *list) {
|
||||||
return _dir_open;
|
list->push_back("/");
|
||||||
|
|
||||||
|
#if defined(HAVE_MNTENT) && defined(X11_ENABLED)
|
||||||
|
// Check /etc/mtab for the list of mounted partitions
|
||||||
|
FILE *mtab = setmntent("/etc/mtab", "r");
|
||||||
|
if (mtab) {
|
||||||
|
struct mntent mnt;
|
||||||
|
char strings[4096];
|
||||||
|
|
||||||
|
while (getmntent_r(mtab, &mnt, strings, sizeof(strings))) {
|
||||||
|
if (mnt.mnt_dir != nullptr && _filter_drive(&mnt)) {
|
||||||
|
// Avoid duplicates
|
||||||
|
if (!list->find(mnt.mnt_dir)) {
|
||||||
|
list->push_back(mnt.mnt_dir);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
endmntent(mtab);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Add $HOME
|
||||||
|
const char *home = getenv("HOME");
|
||||||
|
if (home) {
|
||||||
|
// Only add if it's not a duplicate
|
||||||
|
if (!list->find(home)) {
|
||||||
|
list->push_back(home);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check $HOME/.config/gtk-3.0/bookmarks
|
||||||
|
char path[1024];
|
||||||
|
snprintf(path, 1024, "%s/.config/gtk-3.0/bookmarks", home);
|
||||||
|
FILE *fd = fopen(path, "r");
|
||||||
|
if (fd) {
|
||||||
|
char string[1024];
|
||||||
|
while (fgets(string, 1024, fd)) {
|
||||||
|
// Parse only file:// links
|
||||||
|
if (strncmp(string, "file://", 7) == 0) {
|
||||||
|
// Strip any unwanted edges on the strings and push_back if it's not a duplicate
|
||||||
|
String fpath = String(string + 7).strip_edges().split_spaces()[0].percent_decode();
|
||||||
|
if (!list->find(fpath)) {
|
||||||
|
list->push_back(fpath);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fclose(fd);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
list->sort();
|
||||||
}
|
}
|
||||||
bool DirAccess::is_dir_closed() {
|
|
||||||
return !_dir_open;
|
int DirAccess::get_drive_count() {
|
||||||
|
List<String> list;
|
||||||
|
_get_drives(&list);
|
||||||
|
|
||||||
|
return list.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
String DirAccess::get_drive(int p_drive) {
|
||||||
|
List<String> list;
|
||||||
|
_get_drives(&list);
|
||||||
|
|
||||||
|
ERR_FAIL_INDEX_V(p_drive, list.size(), "");
|
||||||
|
|
||||||
|
return list[p_drive];
|
||||||
|
}
|
||||||
|
|
||||||
|
int DirAccess::get_current_drive() {
|
||||||
|
int drive = 0;
|
||||||
|
int max_length = -1;
|
||||||
|
const String path = get_current_dir().to_lower();
|
||||||
|
for (int i = 0; i < get_drive_count(); i++) {
|
||||||
|
const String d = get_drive(i).to_lower();
|
||||||
|
if (max_length < d.length() && path.begins_with(d)) {
|
||||||
|
max_length = d.length();
|
||||||
|
drive = i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return drive;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool DirAccess::drives_are_shortcuts() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
Error DirAccess::make_dir(String p_dir) {
|
||||||
|
GLOBAL_LOCK_FUNCTION
|
||||||
|
|
||||||
|
if (p_dir.is_rel_path()) {
|
||||||
|
p_dir = get_current_dir().plus_file(p_dir);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool success = (mkdir(p_dir.utf8().get_data(), S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH) == 0);
|
||||||
|
int err = errno;
|
||||||
|
|
||||||
|
if (success) {
|
||||||
|
return OK;
|
||||||
|
};
|
||||||
|
|
||||||
|
if (err == EEXIST) {
|
||||||
|
return ERR_ALREADY_EXISTS;
|
||||||
|
};
|
||||||
|
|
||||||
|
return ERR_CANT_CREATE;
|
||||||
|
}
|
||||||
|
|
||||||
|
Error DirAccess::change_dir(String p_dir) {
|
||||||
|
GLOBAL_LOCK_FUNCTION
|
||||||
|
|
||||||
|
// prev_dir is the directory we are changing out of
|
||||||
|
String prev_dir;
|
||||||
|
char real_current_dir_name[2048];
|
||||||
|
ERR_FAIL_COND_V(getcwd(real_current_dir_name, 2048) == nullptr, ERR_BUG);
|
||||||
|
if (prev_dir.parse_utf8(real_current_dir_name)) {
|
||||||
|
prev_dir = real_current_dir_name; //no utf8, maybe latin?
|
||||||
|
}
|
||||||
|
|
||||||
|
// try_dir is the directory we are trying to change into
|
||||||
|
String try_dir = "";
|
||||||
|
if (p_dir.is_rel_path()) {
|
||||||
|
String next_dir = current_dir.plus_file(p_dir);
|
||||||
|
next_dir = next_dir.simplify_path();
|
||||||
|
try_dir = next_dir;
|
||||||
|
} else {
|
||||||
|
try_dir = p_dir;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool worked = (chdir(try_dir.utf8().get_data()) == 0); // we can only give this utf8
|
||||||
|
if (!worked) {
|
||||||
|
return ERR_INVALID_PARAMETER;
|
||||||
|
}
|
||||||
|
|
||||||
|
String base;
|
||||||
|
if (base != String() && !try_dir.begins_with(base)) {
|
||||||
|
ERR_FAIL_COND_V(getcwd(real_current_dir_name, 2048) == nullptr, ERR_BUG);
|
||||||
|
String new_dir;
|
||||||
|
new_dir.parse_utf8(real_current_dir_name);
|
||||||
|
|
||||||
|
if (!new_dir.begins_with(base)) {
|
||||||
|
try_dir = current_dir; //revert
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// the directory exists, so set current_dir to try_dir
|
||||||
|
current_dir = try_dir;
|
||||||
|
ERR_FAIL_COND_V(chdir(prev_dir.utf8().get_data()) != 0, ERR_BUG);
|
||||||
|
return OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
String DirAccess::get_current_dir() {
|
||||||
|
String base;
|
||||||
|
if (base != "") {
|
||||||
|
String bd = current_dir.replace_first(base, "");
|
||||||
|
if (bd.begins_with("/")) {
|
||||||
|
return bd.substr(1, bd.length());
|
||||||
|
} else {
|
||||||
|
return bd;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return current_dir;
|
||||||
|
}
|
||||||
|
|
||||||
|
Error DirAccess::rename(String p_path, String p_new_path) {
|
||||||
|
if (p_path.is_rel_path()) {
|
||||||
|
p_path = get_current_dir().plus_file(p_path);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (p_new_path.is_rel_path()) {
|
||||||
|
p_new_path = get_current_dir().plus_file(p_new_path);
|
||||||
|
}
|
||||||
|
|
||||||
|
return ::rename(p_path.utf8().get_data(), p_new_path.utf8().get_data()) == 0 ? OK : FAILED;
|
||||||
|
}
|
||||||
|
|
||||||
|
Error DirAccess::remove(String p_path) {
|
||||||
|
if (p_path.is_rel_path()) {
|
||||||
|
p_path = get_current_dir().plus_file(p_path);
|
||||||
|
}
|
||||||
|
|
||||||
|
struct stat flags;
|
||||||
|
if ((stat(p_path.utf8().get_data(), &flags) != 0)) {
|
||||||
|
return FAILED;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (S_ISDIR(flags.st_mode)) {
|
||||||
|
return ::rmdir(p_path.utf8().get_data()) == 0 ? OK : FAILED;
|
||||||
|
} else {
|
||||||
|
return ::unlink(p_path.utf8().get_data()) == 0 ? OK : FAILED;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool DirAccess::is_link(String p_file) {
|
||||||
|
if (p_file.is_rel_path()) {
|
||||||
|
p_file = get_current_dir().plus_file(p_file);
|
||||||
|
}
|
||||||
|
|
||||||
|
struct stat flags;
|
||||||
|
if ((lstat(p_file.utf8().get_data(), &flags) != 0))
|
||||||
|
return FAILED;
|
||||||
|
|
||||||
|
return S_ISLNK(flags.st_mode);
|
||||||
|
}
|
||||||
|
|
||||||
|
String DirAccess::read_link(String p_file) {
|
||||||
|
if (p_file.is_rel_path()) {
|
||||||
|
p_file = get_current_dir().plus_file(p_file);
|
||||||
|
}
|
||||||
|
|
||||||
|
char buf[256];
|
||||||
|
memset(buf, 0, 256);
|
||||||
|
ssize_t len = readlink(p_file.utf8().get_data(), buf, sizeof(buf));
|
||||||
|
String link;
|
||||||
|
if (len > 0) {
|
||||||
|
link.parse_utf8(buf, len);
|
||||||
|
}
|
||||||
|
return link;
|
||||||
|
}
|
||||||
|
|
||||||
|
Error DirAccess::create_link(String p_source, String p_target) {
|
||||||
|
if (p_target.is_rel_path())
|
||||||
|
p_target = get_current_dir().plus_file(p_target);
|
||||||
|
|
||||||
|
if (symlink(p_source.utf8().get_data(), p_target.utf8().get_data()) == 0) {
|
||||||
|
return OK;
|
||||||
|
} else {
|
||||||
|
return FAILED;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
uint64_t DirAccess::get_space_left() {
|
||||||
|
#ifndef NO_STATVFS
|
||||||
|
struct statvfs vfs;
|
||||||
|
if (statvfs(current_dir.utf8().get_data(), &vfs) != 0) {
|
||||||
|
return 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
return (uint64_t)vfs.f_bavail * (uint64_t)vfs.f_frsize;
|
||||||
|
#else
|
||||||
|
// FIXME: Implement this.
|
||||||
|
return 0;
|
||||||
|
#endif
|
||||||
|
};
|
||||||
|
|
||||||
|
String DirAccess::get_filesystem_type() const {
|
||||||
|
return ""; //TODO this should be implemented
|
||||||
|
}
|
||||||
|
|
||||||
|
bool DirAccess::is_hidden(const String &p_name) {
|
||||||
|
return p_name != "." && p_name != ".." && p_name.begins_with(".");
|
||||||
}
|
}
|
||||||
|
|
||||||
DirAccess::DirAccess() {
|
DirAccess::DirAccess() {
|
||||||
_skip_specials = true;
|
dir_stream = NULL;
|
||||||
_read_file_result = 0;
|
_cisdir = false;
|
||||||
_dir_open = false;
|
|
||||||
_dir = memnew(tinydir_dir);
|
next_is_dir = false;
|
||||||
_file = memnew(tinydir_file);
|
_skip_specials = false;
|
||||||
}
|
|
||||||
DirAccess::~DirAccess() {
|
_cishidden = false;
|
||||||
if (is_dir_open()) {
|
_cisspecial = false;
|
||||||
close_dir();
|
|
||||||
|
/* determine drive count */
|
||||||
|
|
||||||
|
// set current directory to an absolute path of the current directory
|
||||||
|
char real_current_dir_name[2048];
|
||||||
|
ERR_FAIL_COND(getcwd(real_current_dir_name, 2048) == nullptr);
|
||||||
|
if (current_dir.parse_utf8(real_current_dir_name)) {
|
||||||
|
current_dir = real_current_dir_name;
|
||||||
}
|
}
|
||||||
|
|
||||||
memdelete(_dir);
|
change_dir(current_dir);
|
||||||
memdelete(_file);
|
}
|
||||||
|
|
||||||
|
DirAccess::~DirAccess() {
|
||||||
|
list_dir_end();
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/*
|
||||||
|
int DirAccess::get_current_drive() {
|
||||||
|
String path = get_current_dir().to_lower();
|
||||||
|
for (int i = 0; i < get_drive_count(); i++) {
|
||||||
|
String d = get_drive(i).to_lower();
|
||||||
|
if (path.begins_with(d)) {
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool DirAccess::drives_are_shortcuts() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
String DirAccess::get_current_dir_without_drive() {
|
||||||
|
return get_current_dir();
|
||||||
|
}
|
||||||
|
|
||||||
|
static Error _erase_recursive(DirAccess *da) {
|
||||||
|
List<String> dirs;
|
||||||
|
List<String> files;
|
||||||
|
|
||||||
|
da->list_dir_begin();
|
||||||
|
String n = da->get_next();
|
||||||
|
while (n != String()) {
|
||||||
|
if (n != "." && n != "..") {
|
||||||
|
if (da->current_is_dir()) {
|
||||||
|
dirs.push_back(n);
|
||||||
|
} else {
|
||||||
|
files.push_back(n);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
n = da->get_next();
|
||||||
|
}
|
||||||
|
|
||||||
|
da->list_dir_end();
|
||||||
|
|
||||||
|
for (List<String>::Element *E = dirs.front(); E; E = E->next()) {
|
||||||
|
Error err = da->change_dir(E->get());
|
||||||
|
if (err == OK) {
|
||||||
|
err = _erase_recursive(da);
|
||||||
|
if (err) {
|
||||||
|
da->change_dir("..");
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
err = da->change_dir("..");
|
||||||
|
if (err) {
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
err = da->remove(da->get_current_dir().plus_file(E->get()));
|
||||||
|
if (err) {
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (List<String>::Element *E = files.front(); E; E = E->next()) {
|
||||||
|
Error err = da->remove(da->get_current_dir().plus_file(E->get()));
|
||||||
|
if (err) {
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
Error DirAccess::erase_contents_recursive() {
|
||||||
|
return _erase_recursive(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
Error DirAccess::make_dir_recursive(String p_dir) {
|
||||||
|
if (p_dir.length() < 1) {
|
||||||
|
return OK;
|
||||||
|
};
|
||||||
|
|
||||||
|
String full_dir;
|
||||||
|
|
||||||
|
if (p_dir.is_rel_path()) {
|
||||||
|
//append current
|
||||||
|
full_dir = get_current_dir().plus_file(p_dir);
|
||||||
|
|
||||||
|
} else {
|
||||||
|
full_dir = p_dir;
|
||||||
|
}
|
||||||
|
|
||||||
|
full_dir = full_dir.replace("\\", "/");
|
||||||
|
|
||||||
|
String base;
|
||||||
|
|
||||||
|
if (full_dir.begins_with("res://")) {
|
||||||
|
base = "res://";
|
||||||
|
} else if (full_dir.begins_with("user://")) {
|
||||||
|
base = "user://";
|
||||||
|
} else if (full_dir.is_network_share_path()) {
|
||||||
|
int pos = full_dir.find("/", 2);
|
||||||
|
ERR_FAIL_COND_V(pos < 0, ERR_INVALID_PARAMETER);
|
||||||
|
pos = full_dir.find("/", pos + 1);
|
||||||
|
ERR_FAIL_COND_V(pos < 0, ERR_INVALID_PARAMETER);
|
||||||
|
base = full_dir.substr(0, pos + 1);
|
||||||
|
} else if (full_dir.begins_with("/")) {
|
||||||
|
base = "/";
|
||||||
|
} else if (full_dir.find(":/") != -1) {
|
||||||
|
base = full_dir.substr(0, full_dir.find(":/") + 2);
|
||||||
|
} else {
|
||||||
|
ERR_FAIL_V(ERR_INVALID_PARAMETER);
|
||||||
|
}
|
||||||
|
|
||||||
|
full_dir = full_dir.replace_first(base, "").simplify_path();
|
||||||
|
|
||||||
|
Vector<String> subdirs = full_dir.split("/");
|
||||||
|
|
||||||
|
String curpath = base;
|
||||||
|
for (int i = 0; i < subdirs.size(); i++) {
|
||||||
|
curpath = curpath.plus_file(subdirs[i]);
|
||||||
|
Error err = make_dir(curpath);
|
||||||
|
if (err != OK && err != ERR_ALREADY_EXISTS) {
|
||||||
|
ERR_FAIL_V_MSG(err, "Could not create directory: " + curpath);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
DirAccess *DirAccess::create_for_path(const String &p_path) {
|
||||||
|
DirAccess *d = memnew(DirAccess());
|
||||||
|
d->open(p_path);
|
||||||
|
return d;
|
||||||
|
}
|
||||||
|
DirAccess *DirAccess::create() {
|
||||||
|
return memnew(DirAccess());
|
||||||
|
}
|
||||||
|
|
||||||
|
Error DirAccess::open(const String &p_path) {
|
||||||
|
return change_dir(p_path);
|
||||||
|
}
|
||||||
|
|
||||||
|
String DirAccess::get_full_path(const String &p_path) {
|
||||||
|
DirAccess d;
|
||||||
|
|
||||||
|
d.change_dir(p_path);
|
||||||
|
String full = d.get_current_dir();
|
||||||
|
|
||||||
|
return full;
|
||||||
|
}
|
||||||
|
|
||||||
|
Error DirAccess::copy(String p_from, String p_to, int p_chmod_flags) {
|
||||||
|
//printf("copy %s -> %s\n",p_from.ascii().get_data(),p_to.ascii().get_data());
|
||||||
|
Error err;
|
||||||
|
FileAccess *fsrc = FileAccess::open(p_from, FileAccess::READ, &err);
|
||||||
|
|
||||||
|
if (err) {
|
||||||
|
ERR_PRINT("Failed to open " + p_from);
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
FileAccess *fdst = FileAccess::open(p_to, FileAccess::WRITE, &err);
|
||||||
|
if (err) {
|
||||||
|
fsrc->close();
|
||||||
|
memdelete(fsrc);
|
||||||
|
ERR_PRINT("Failed to open " + p_to);
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
const size_t copy_buffer_limit = 65536; // 64 KB
|
||||||
|
|
||||||
|
fsrc->seek_end(0);
|
||||||
|
uint64_t size = fsrc->get_position();
|
||||||
|
fsrc->seek(0);
|
||||||
|
err = OK;
|
||||||
|
size_t buffer_size = MIN(size * sizeof(uint8_t), copy_buffer_limit);
|
||||||
|
LocalVector<uint8_t> buffer;
|
||||||
|
buffer.resize(buffer_size);
|
||||||
|
while (size > 0) {
|
||||||
|
if (fsrc->get_error() != OK) {
|
||||||
|
err = fsrc->get_error();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (fdst->get_error() != OK) {
|
||||||
|
err = fdst->get_error();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
int bytes_read = fsrc->get_buffer(buffer.ptr(), buffer_size);
|
||||||
|
if (bytes_read <= 0) {
|
||||||
|
err = FAILED;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
fdst->store_buffer(buffer.ptr(), bytes_read);
|
||||||
|
|
||||||
|
size -= bytes_read;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (err == OK && p_chmod_flags != -1) {
|
||||||
|
fdst->close();
|
||||||
|
err = FileAccess::set_unix_permissions(p_to, p_chmod_flags);
|
||||||
|
// If running on a platform with no chmod support (i.e., Windows), don't fail
|
||||||
|
if (err == ERR_UNAVAILABLE) {
|
||||||
|
err = OK;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
memdelete(fsrc);
|
||||||
|
memdelete(fdst);
|
||||||
|
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Changes dir for the current scope, returning back to the original dir
|
||||||
|
// when scope exits
|
||||||
|
class DirChanger {
|
||||||
|
DirAccess *da;
|
||||||
|
String original_dir;
|
||||||
|
|
||||||
|
public:
|
||||||
|
DirChanger(DirAccess *p_da, String p_dir) :
|
||||||
|
da(p_da),
|
||||||
|
original_dir(p_da->get_current_dir()) {
|
||||||
|
p_da->change_dir(p_dir);
|
||||||
|
}
|
||||||
|
|
||||||
|
~DirChanger() {
|
||||||
|
da->change_dir(original_dir);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
Error DirAccess::_copy_dir(DirAccess *p_target_da, String p_to, int p_chmod_flags, bool p_copy_links) {
|
||||||
|
List<String> dirs;
|
||||||
|
|
||||||
|
String curdir = get_current_dir();
|
||||||
|
list_dir_begin();
|
||||||
|
String n = get_next();
|
||||||
|
while (n != String()) {
|
||||||
|
if (n != "." && n != "..") {
|
||||||
|
if (p_copy_links && is_link(get_current_dir().plus_file(n))) {
|
||||||
|
create_link(read_link(get_current_dir().plus_file(n)), p_to + n);
|
||||||
|
} else if (current_is_dir()) {
|
||||||
|
dirs.push_back(n);
|
||||||
|
} else {
|
||||||
|
const String &rel_path = n;
|
||||||
|
if (!n.is_rel_path()) {
|
||||||
|
list_dir_end();
|
||||||
|
return ERR_BUG;
|
||||||
|
}
|
||||||
|
Error err = copy(get_current_dir().plus_file(n), p_to + rel_path, p_chmod_flags);
|
||||||
|
if (err) {
|
||||||
|
list_dir_end();
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
n = get_next();
|
||||||
|
}
|
||||||
|
|
||||||
|
list_dir_end();
|
||||||
|
|
||||||
|
for (List<String>::Element *E = dirs.front(); E; E = E->next()) {
|
||||||
|
String rel_path = E->get();
|
||||||
|
String target_dir = p_to + rel_path;
|
||||||
|
if (!p_target_da->dir_exists(target_dir)) {
|
||||||
|
Error err = p_target_da->make_dir(target_dir);
|
||||||
|
ERR_FAIL_COND_V_MSG(err != OK, err, "Cannot create directory '" + target_dir + "'.");
|
||||||
|
}
|
||||||
|
|
||||||
|
Error err = change_dir(E->get());
|
||||||
|
ERR_FAIL_COND_V_MSG(err != OK, err, "Cannot change current directory to '" + E->get() + "'.");
|
||||||
|
|
||||||
|
err = _copy_dir(p_target_da, p_to + rel_path + "/", p_chmod_flags, p_copy_links);
|
||||||
|
if (err) {
|
||||||
|
change_dir("..");
|
||||||
|
ERR_FAIL_V_MSG(err, "Failed to copy recursively.");
|
||||||
|
}
|
||||||
|
err = change_dir("..");
|
||||||
|
ERR_FAIL_COND_V_MSG(err != OK, err, "Failed to go back.");
|
||||||
|
}
|
||||||
|
|
||||||
|
return OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
Error DirAccess::copy_dir(String p_from, String p_to, int p_chmod_flags, bool p_copy_links) {
|
||||||
|
ERR_FAIL_COND_V_MSG(!dir_exists(p_from), ERR_FILE_NOT_FOUND, "Source directory doesn't exist.");
|
||||||
|
|
||||||
|
DirAccess *target_da = DirAccess::create_for_path(p_to);
|
||||||
|
ERR_FAIL_COND_V_MSG(!target_da, ERR_CANT_CREATE, "Cannot create DirAccess for path '" + p_to + "'.");
|
||||||
|
|
||||||
|
if (!target_da->dir_exists(p_to)) {
|
||||||
|
Error err = target_da->make_dir_recursive(p_to);
|
||||||
|
if (err) {
|
||||||
|
memdelete(target_da);
|
||||||
|
}
|
||||||
|
ERR_FAIL_COND_V_MSG(err != OK, err, "Cannot create directory '" + p_to + "'.");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!p_to.ends_with("/")) {
|
||||||
|
p_to = p_to + "/";
|
||||||
|
}
|
||||||
|
|
||||||
|
DirChanger dir_changer(this, p_from);
|
||||||
|
Error err = _copy_dir(target_da, p_to, p_chmod_flags, p_copy_links);
|
||||||
|
memdelete(target_da);
|
||||||
|
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool DirAccess::exists(String p_dir) {
|
||||||
|
DirAccess *da = DirAccess::create_for_path(p_dir);
|
||||||
|
bool valid = da->change_dir(p_dir) == OK;
|
||||||
|
memdelete(da);
|
||||||
|
return valid;
|
||||||
|
}
|
||||||
|
|
||||||
|
String DirAccess::get_filesystem_abspath_for(String p_path) {
|
||||||
|
DirAccess d;
|
||||||
|
|
||||||
|
d.change_dir(p_path);
|
||||||
|
String full = d.get_current_dir();
|
||||||
|
|
||||||
|
return full;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool DirAccess::is_special(const String &p_path) {
|
||||||
|
if (p_path.size() > 2) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return p_path == "." || p_path == "..";
|
||||||
}
|
}
|
||||||
|
@ -2,48 +2,122 @@
|
|||||||
#ifndef DIR_ACCESS_H
|
#ifndef DIR_ACCESS_H
|
||||||
#define DIR_ACCESS_H
|
#define DIR_ACCESS_H
|
||||||
|
|
||||||
|
/*************************************************************************/
|
||||||
|
/* dir_access.h */
|
||||||
|
/* From https://github.com/Relintai/pandemonium_engine (MIT) */
|
||||||
|
/*************************************************************************/
|
||||||
|
|
||||||
//--STRIP
|
//--STRIP
|
||||||
#include "core/error_list.h"
|
#include "core/error_list.h"
|
||||||
#include "core/ustring.h"
|
#include "core/ustring.h"
|
||||||
//--STRIP
|
//--STRIP
|
||||||
|
|
||||||
struct tinydir_file;
|
#if defined(_WIN64) || defined(_WIN32)
|
||||||
struct tinydir_dir;
|
#else
|
||||||
|
struct __dirstream;
|
||||||
|
typedef struct __dirstream DIR;
|
||||||
|
#endif
|
||||||
|
|
||||||
class DirAccess {
|
class DirAccess {
|
||||||
|
Error _copy_dir(DirAccess *p_target_da, String p_to, int p_chmod_flags, bool p_copy_links);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
Error open_dir(const String &path, bool skip_specials = true);
|
virtual Error list_dir_begin(bool skip_specials = false); ///< This starts dir listing
|
||||||
Error open_dir(const char *path, bool skip_specials = true);
|
virtual String get_next();
|
||||||
void close_dir();
|
virtual bool current_is_dir() const;
|
||||||
|
virtual bool current_is_hidden() const;
|
||||||
|
virtual bool current_is_file() const;
|
||||||
|
virtual bool current_is_special_dir() const;
|
||||||
|
|
||||||
bool has_next();
|
virtual void list_dir_end(); ///<
|
||||||
bool read();
|
|
||||||
bool next();
|
|
||||||
|
|
||||||
bool current_is_ok();
|
virtual int get_drive_count();
|
||||||
String current_get_name();
|
virtual String get_drive(int p_drive);
|
||||||
String current_get_path();
|
virtual int get_current_drive();
|
||||||
String current_get_extension();
|
virtual bool drives_are_shortcuts();
|
||||||
const char *current_get_name_cstr();
|
|
||||||
const char *current_get_path_cstr();
|
|
||||||
const char *current_get_extension_cstr();
|
|
||||||
bool current_is_file();
|
|
||||||
bool current_is_dir();
|
|
||||||
bool current_is_special_dir();
|
|
||||||
|
|
||||||
bool is_dir_open();
|
virtual Error change_dir(String p_dir); ///< can be relative or absolute, return false on success
|
||||||
bool is_dir_closed();
|
virtual String get_current_dir(); ///< return current dir location
|
||||||
|
virtual String get_current_dir_without_drive();
|
||||||
|
virtual Error make_dir(String p_dir);
|
||||||
|
virtual Error make_dir_recursive(String p_dir);
|
||||||
|
virtual Error erase_contents_recursive(); //super dangerous, use with care!
|
||||||
|
|
||||||
|
virtual bool file_exists(String p_file);
|
||||||
|
virtual bool dir_exists(String p_dir);
|
||||||
|
static bool exists(String p_dir);
|
||||||
|
virtual uint64_t get_space_left();
|
||||||
|
|
||||||
|
Error copy_dir(String p_from, String p_to, int p_chmod_flags = -1, bool p_copy_links = false);
|
||||||
|
virtual Error copy(String p_from, String p_to, int p_chmod_flags = -1);
|
||||||
|
virtual Error rename(String p_from, String p_to);
|
||||||
|
virtual Error remove(String p_name);
|
||||||
|
|
||||||
|
virtual bool is_link(String p_file);
|
||||||
|
virtual String read_link(String p_file);
|
||||||
|
virtual Error create_link(String p_source, String p_target);
|
||||||
|
|
||||||
|
virtual uint64_t get_modified_time(String p_file);
|
||||||
|
|
||||||
|
virtual String get_filesystem_type() const;
|
||||||
|
static String get_full_path(const String &p_path);
|
||||||
|
|
||||||
|
static DirAccess *create_for_path(const String &p_path);
|
||||||
|
static DirAccess *create();
|
||||||
|
|
||||||
|
Error open(const String &p_path);
|
||||||
|
|
||||||
|
static String get_filesystem_abspath_for(String p_path);
|
||||||
|
|
||||||
|
static bool is_special(const String &p_path);
|
||||||
|
|
||||||
DirAccess();
|
DirAccess();
|
||||||
virtual ~DirAccess();
|
virtual ~DirAccess();
|
||||||
|
|
||||||
private:
|
protected:
|
||||||
bool _skip_specials;
|
#if defined(_WIN64) || defined(_WIN32)
|
||||||
int _read_file_result;
|
#else
|
||||||
tinydir_dir *_dir;
|
String current_dir;
|
||||||
tinydir_file *_file;
|
virtual String fix_unicode_name(const char *p_name) const { return String::utf8(p_name); }
|
||||||
|
virtual bool is_hidden(const String &p_name);
|
||||||
|
#endif
|
||||||
|
|
||||||
bool _dir_open;
|
bool next_is_dir;
|
||||||
|
bool _skip_specials;
|
||||||
|
|
||||||
|
#if defined(_WIN64) || defined(_WIN32)
|
||||||
|
#else
|
||||||
|
DIR *dir_stream;
|
||||||
|
|
||||||
|
bool _cisdir;
|
||||||
|
bool _cishidden;
|
||||||
|
bool _cisspecial;
|
||||||
|
#endif
|
||||||
|
};
|
||||||
|
|
||||||
|
struct DirAccessRef {
|
||||||
|
DirAccess *f;
|
||||||
|
|
||||||
|
_FORCE_INLINE_ bool is_null() const { return f == nullptr; }
|
||||||
|
_FORCE_INLINE_ bool is_valid() const { return f != nullptr; }
|
||||||
|
|
||||||
|
_FORCE_INLINE_ operator bool() const { return f != nullptr; }
|
||||||
|
_FORCE_INLINE_ operator DirAccess *() { return f; }
|
||||||
|
|
||||||
|
_FORCE_INLINE_ DirAccess *operator->() {
|
||||||
|
return f;
|
||||||
|
}
|
||||||
|
|
||||||
|
DirAccessRef(DirAccess *fa) { f = fa; }
|
||||||
|
DirAccessRef(DirAccessRef &&other) {
|
||||||
|
f = other.f;
|
||||||
|
other.f = nullptr;
|
||||||
|
}
|
||||||
|
~DirAccessRef() {
|
||||||
|
if (f) {
|
||||||
|
memdelete(f);
|
||||||
|
}
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
@ -1,98 +1,843 @@
|
|||||||
|
|
||||||
|
/*************************************************************************/
|
||||||
|
/* file_access.cpp */
|
||||||
|
/*************************************************************************/
|
||||||
|
/* This file is part of: */
|
||||||
|
/* PANDEMONIUM ENGINE */
|
||||||
|
/* https://github.com/Relintai/pandemonium_engine */
|
||||||
|
/*************************************************************************/
|
||||||
|
/* Copyright (c) 2022-present Péter Magyar. */
|
||||||
|
/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
|
||||||
|
/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
|
||||||
|
/* */
|
||||||
|
/* Permission is hereby granted, free of charge, to any person obtaining */
|
||||||
|
/* a copy of this software and associated documentation files (the */
|
||||||
|
/* "Software"), to deal in the Software without restriction, including */
|
||||||
|
/* without limitation the rights to use, copy, modify, merge, publish, */
|
||||||
|
/* distribute, sublicense, and/or sell copies of the Software, and to */
|
||||||
|
/* permit persons to whom the Software is furnished to do so, subject to */
|
||||||
|
/* the following conditions: */
|
||||||
|
/* */
|
||||||
|
/* The above copyright notice and this permission notice shall be */
|
||||||
|
/* included in all copies or substantial portions of the Software. */
|
||||||
|
/* */
|
||||||
|
/* 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 OR COPYRIGHT HOLDERS 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. */
|
||||||
|
/*************************************************************************/
|
||||||
|
|
||||||
//--STRIP
|
//--STRIP
|
||||||
#include "file_access.h"
|
#include "file_access.h"
|
||||||
|
|
||||||
|
#include "core/marshalls.h"
|
||||||
|
|
||||||
#include <cstdio>
|
#include <cstdio>
|
||||||
//--STRIP
|
//--STRIP
|
||||||
|
|
||||||
|
#if defined(_WIN64) || defined(_WIN32)
|
||||||
|
#else
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
|
||||||
String FileAccess::read_file(const String &path) {
|
#include <errno.h>
|
||||||
FILE *f = fopen(path.utf8().get_data(), "r");
|
|
||||||
|
|
||||||
ERR_FAIL_COND_V_MSG(!f, String(), "Error opening file! " + path);
|
#include <unistd.h>
|
||||||
|
|
||||||
fseek(f, 0, SEEK_END);
|
#ifdef MSVC
|
||||||
long fsize = ftell(f);
|
#define S_ISREG(m) ((m)&_S_IFREG)
|
||||||
fseek(f, 0, SEEK_SET); /* same as rewind(f); */
|
#include <io.h>
|
||||||
|
#endif
|
||||||
|
#ifndef S_ISREG
|
||||||
|
#define S_ISREG(m) ((m)&S_IFREG)
|
||||||
|
#endif
|
||||||
|
|
||||||
CharString cs;
|
#ifndef NO_FCNTL
|
||||||
cs.resize(fsize + 1); // +1 for the null terminator
|
#include <fcntl.h>
|
||||||
|
#else
|
||||||
|
#include <sys/ioctl.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
fread(cs.ptrw(), 1, fsize, f);
|
#endif
|
||||||
fclose(f);
|
|
||||||
|
|
||||||
return String::utf8(cs.ptr());
|
#if defined(_WIN64) || defined(_WIN32)
|
||||||
|
#else
|
||||||
|
|
||||||
|
void FileAccess::check_errors() const {
|
||||||
|
ERR_FAIL_COND_MSG(!f, "File must be opened before use.");
|
||||||
|
|
||||||
|
if (feof(f)) {
|
||||||
|
last_error = ERR_FILE_EOF;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Vector<uint8_t> FileAccess::read_file_bin(const String &path) {
|
Error FileAccess::_open(const String &p_path, int p_mode_flags) {
|
||||||
FILE *f = fopen(path.utf8().get_data(), "rb");
|
if (f) {
|
||||||
|
fclose(f);
|
||||||
|
}
|
||||||
|
f = nullptr;
|
||||||
|
|
||||||
Vector<uint8_t> fd;
|
path_src = p_path;
|
||||||
|
path = fix_path(p_path);
|
||||||
|
//printf("opening %s, %i\n", path.utf8().get_data(), Memory::get_static_mem_usage());
|
||||||
|
|
||||||
ERR_FAIL_COND_V_MSG(!f, fd, "Error opening file! " + path);
|
ERR_FAIL_COND_V_MSG(f, ERR_ALREADY_IN_USE, "File is already in use.");
|
||||||
|
const char *mode_string;
|
||||||
|
|
||||||
fseek(f, 0, SEEK_END);
|
if (p_mode_flags == READ) {
|
||||||
long fsize = ftell(f);
|
mode_string = "rb";
|
||||||
fseek(f, 0, SEEK_SET); /* same as rewind(f); */
|
} else if (p_mode_flags == WRITE) {
|
||||||
|
mode_string = "wb";
|
||||||
fd.resize(fsize);
|
} else if (p_mode_flags == READ_WRITE) {
|
||||||
|
mode_string = "rb+";
|
||||||
fread(fd.ptrw(), 1, fsize, f);
|
} else if (p_mode_flags == WRITE_READ) {
|
||||||
fclose(f);
|
mode_string = "wb+";
|
||||||
|
} else {
|
||||||
return fd;
|
return ERR_INVALID_PARAMETER;
|
||||||
}
|
|
||||||
|
|
||||||
Error FileAccess::read_file_into_bin(const String &path, Vector<uint8_t> *data) {
|
|
||||||
if (!data) {
|
|
||||||
return ERR_PARAMETER_RANGE_ERROR;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
FILE *f = fopen(path.utf8().get_data(), "rb");
|
/* pretty much every implementation that uses fopen as primary
|
||||||
|
backend (unix-compatible mostly) supports utf8 encoding */
|
||||||
|
|
||||||
if (!f) {
|
//printf("opening %s as %s\n", p_path.utf8().get_data(), path.utf8().get_data());
|
||||||
return ERR_FILE_CANT_OPEN;
|
struct stat st;
|
||||||
|
int err = stat(path.utf8().get_data(), &st);
|
||||||
|
if (!err) {
|
||||||
|
switch (st.st_mode & S_IFMT) {
|
||||||
|
case S_IFLNK:
|
||||||
|
case S_IFREG:
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
return ERR_FILE_CANT_OPEN;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fseek(f, 0, SEEK_END);
|
if (is_backup_save_enabled() && (p_mode_flags & WRITE) && !(p_mode_flags & READ)) {
|
||||||
long fsize = ftell(f);
|
save_path = path;
|
||||||
fseek(f, 0, SEEK_SET); /* same as rewind(f); */
|
path = path + ".tmp";
|
||||||
|
}
|
||||||
|
|
||||||
data->resize(fsize);
|
f = fopen(path.utf8().get_data(), mode_string);
|
||||||
|
|
||||||
fread(data->ptrw(), 1, fsize, f);
|
if (f == nullptr) {
|
||||||
fclose(f);
|
switch (errno) {
|
||||||
|
case ENOENT: {
|
||||||
|
last_error = ERR_FILE_NOT_FOUND;
|
||||||
|
} break;
|
||||||
|
default: {
|
||||||
|
last_error = ERR_FILE_CANT_OPEN;
|
||||||
|
} break;
|
||||||
|
}
|
||||||
|
return last_error;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set close on exec to avoid leaking it to subprocesses.
|
||||||
|
int fd = fileno(f);
|
||||||
|
|
||||||
|
if (fd != -1) {
|
||||||
|
#if defined(NO_FCNTL)
|
||||||
|
unsigned long par = 0;
|
||||||
|
ioctl(fd, FIOCLEX, &par);
|
||||||
|
#else
|
||||||
|
int opts = fcntl(fd, F_GETFD);
|
||||||
|
fcntl(fd, F_SETFD, opts | FD_CLOEXEC);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
last_error = OK;
|
||||||
|
flags = p_mode_flags;
|
||||||
return OK;
|
return OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
Error FileAccess::write_file(const String &path, const String &str) {
|
void FileAccess::close() {
|
||||||
FILE *f = fopen(path.utf8().get_data(), "w");
|
|
||||||
|
|
||||||
if (!f) {
|
if (!f) {
|
||||||
return ERR_FILE_CANT_OPEN;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
fwrite(str.utf8().ptr(), sizeof(char), str.size(), f);
|
|
||||||
fclose(f);
|
fclose(f);
|
||||||
|
f = nullptr;
|
||||||
|
|
||||||
return OK;
|
if (close_notification_func) {
|
||||||
}
|
close_notification_func(path, flags);
|
||||||
|
|
||||||
Error FileAccess::write_file_bin(const String &path, const Vector<uint8_t> &data) {
|
|
||||||
FILE *f = fopen(path.utf8().get_data(), "wb");
|
|
||||||
|
|
||||||
if (!f) {
|
|
||||||
return ERR_FILE_CANT_OPEN;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fwrite(data.ptr(), sizeof(uint8_t), data.size(), f);
|
if (save_path != "") {
|
||||||
fclose(f);
|
int rename_error = rename((save_path + ".tmp").utf8().get_data(), save_path.utf8().get_data());
|
||||||
|
|
||||||
return OK;
|
if (rename_error && close_fail_notify) {
|
||||||
|
close_fail_notify(save_path);
|
||||||
|
}
|
||||||
|
|
||||||
|
save_path = "";
|
||||||
|
ERR_FAIL_COND(rename_error != 0);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
FileAccess::FileAccess() {
|
bool FileAccess::is_open() const {
|
||||||
|
return (f != nullptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
String FileAccess::get_path() const {
|
||||||
|
return path_src;
|
||||||
|
}
|
||||||
|
|
||||||
|
String FileAccess::get_path_absolute() const {
|
||||||
|
return path;
|
||||||
|
}
|
||||||
|
|
||||||
|
void FileAccess::seek(uint64_t p_position) {
|
||||||
|
ERR_FAIL_COND_MSG(!f, "File must be opened before use.");
|
||||||
|
|
||||||
|
last_error = OK;
|
||||||
|
if (fseeko(f, p_position, SEEK_SET)) {
|
||||||
|
check_errors();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void FileAccess::seek_end(int64_t p_position) {
|
||||||
|
ERR_FAIL_COND_MSG(!f, "File must be opened before use.");
|
||||||
|
|
||||||
|
if (fseeko(f, p_position, SEEK_END)) {
|
||||||
|
check_errors();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
uint64_t FileAccess::get_position() const {
|
||||||
|
ERR_FAIL_COND_V_MSG(!f, 0, "File must be opened before use.");
|
||||||
|
|
||||||
|
int64_t pos = ftello(f);
|
||||||
|
if (pos < 0) {
|
||||||
|
check_errors();
|
||||||
|
ERR_FAIL_V(0);
|
||||||
|
}
|
||||||
|
return pos;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint64_t FileAccess::get_len() const {
|
||||||
|
ERR_FAIL_COND_V_MSG(!f, 0, "File must be opened before use.");
|
||||||
|
|
||||||
|
int64_t pos = ftello(f);
|
||||||
|
ERR_FAIL_COND_V(pos < 0, 0);
|
||||||
|
ERR_FAIL_COND_V(fseeko(f, 0, SEEK_END), 0);
|
||||||
|
int64_t size = ftello(f);
|
||||||
|
ERR_FAIL_COND_V(size < 0, 0);
|
||||||
|
ERR_FAIL_COND_V(fseeko(f, pos, SEEK_SET), 0);
|
||||||
|
|
||||||
|
return size;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool FileAccess::eof_reached() const {
|
||||||
|
return last_error == ERR_FILE_EOF;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t FileAccess::get_8() const {
|
||||||
|
ERR_FAIL_COND_V_MSG(!f, 0, "File must be opened before use.");
|
||||||
|
uint8_t b;
|
||||||
|
if (fread(&b, 1, 1, f) == 0) {
|
||||||
|
check_errors();
|
||||||
|
b = '\0';
|
||||||
|
}
|
||||||
|
return b;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint64_t FileAccess::get_buffer(uint8_t *p_dst, uint64_t p_length) const {
|
||||||
|
ERR_FAIL_COND_V(!p_dst && p_length > 0, -1);
|
||||||
|
ERR_FAIL_COND_V_MSG(!f, -1, "File must be opened before use.");
|
||||||
|
|
||||||
|
uint64_t read = fread(p_dst, 1, p_length, f);
|
||||||
|
check_errors();
|
||||||
|
return read;
|
||||||
|
};
|
||||||
|
|
||||||
|
Error FileAccess::get_error() const {
|
||||||
|
return last_error;
|
||||||
|
}
|
||||||
|
|
||||||
|
void FileAccess::flush() {
|
||||||
|
ERR_FAIL_COND_MSG(!f, "File must be opened before use.");
|
||||||
|
fflush(f);
|
||||||
|
}
|
||||||
|
|
||||||
|
void FileAccess::store_8(uint8_t p_dest) {
|
||||||
|
ERR_FAIL_COND_MSG(!f, "File must be opened before use.");
|
||||||
|
ERR_FAIL_COND(fwrite(&p_dest, 1, 1, f) != 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
void FileAccess::store_buffer(const uint8_t *p_src, uint64_t p_length) {
|
||||||
|
ERR_FAIL_COND_MSG(!f, "File must be opened before use.");
|
||||||
|
ERR_FAIL_COND(!p_src && p_length > 0);
|
||||||
|
ERR_FAIL_COND(fwrite(p_src, 1, p_length, f) != p_length);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool FileAccess::file_exists(const String &p_path) {
|
||||||
|
int err;
|
||||||
|
struct stat st;
|
||||||
|
String filename = fix_path(p_path);
|
||||||
|
|
||||||
|
// Does the name exist at all?
|
||||||
|
err = stat(filename.utf8().get_data(), &st);
|
||||||
|
if (err) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
#if defined(_WIN64) || defined(_WIN32)
|
||||||
|
if (_access(filename.utf8().get_data(), 4) == -1) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
// See if we have access to the file
|
||||||
|
if (access(filename.utf8().get_data(), F_OK)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// See if this is a regular file
|
||||||
|
switch (st.st_mode & S_IFMT) {
|
||||||
|
case S_IFLNK:
|
||||||
|
case S_IFREG:
|
||||||
|
return true;
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
uint64_t FileAccess::_get_modified_time(const String &p_file) {
|
||||||
|
String file = fix_path(p_file);
|
||||||
|
struct stat flags;
|
||||||
|
int err = stat(file.utf8().get_data(), &flags);
|
||||||
|
|
||||||
|
if (!err) {
|
||||||
|
return flags.st_mtime;
|
||||||
|
} else {
|
||||||
|
LOG_TRACE("Failed to get modified time for: " + p_file + "");
|
||||||
|
return 0;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t FileAccess::_get_unix_permissions(const String &p_file) {
|
||||||
|
String file = fix_path(p_file);
|
||||||
|
struct stat flags;
|
||||||
|
int err = stat(file.utf8().get_data(), &flags);
|
||||||
|
|
||||||
|
if (!err) {
|
||||||
|
return flags.st_mode & 0x7FF; //only permissions
|
||||||
|
} else {
|
||||||
|
ERR_FAIL_V_MSG(0, "Failed to get unix permissions for: " + p_file + ".");
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
Error FileAccess::_set_unix_permissions(const String &p_file, uint32_t p_permissions) {
|
||||||
|
String file = fix_path(p_file);
|
||||||
|
|
||||||
|
int err = chmod(file.utf8().get_data(), p_permissions);
|
||||||
|
if (!err) {
|
||||||
|
return OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
return FAILED;
|
||||||
|
}
|
||||||
|
|
||||||
|
FileCloseNotificationFunc FileAccess::close_notification_func = nullptr;
|
||||||
|
|
||||||
|
FileAccess::FileAccess() :
|
||||||
|
f(nullptr),
|
||||||
|
flags(0),
|
||||||
|
last_error(OK) {
|
||||||
|
endian_swap = false;
|
||||||
|
real_is_double = false;
|
||||||
|
}
|
||||||
|
|
||||||
FileAccess::~FileAccess() {
|
FileAccess::~FileAccess() {
|
||||||
|
close();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
FileAccess::FileCloseFailNotify FileAccess::close_fail_notify = nullptr;
|
||||||
|
|
||||||
|
bool FileAccess::backup_save = false;
|
||||||
|
|
||||||
|
FileAccess *FileAccess::create() {
|
||||||
|
return memnew(FileAccess());
|
||||||
|
}
|
||||||
|
|
||||||
|
bool FileAccess::exists(const String &p_name) {
|
||||||
|
FileAccess *f = open(p_name, READ);
|
||||||
|
if (!f) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
memdelete(f);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
Error FileAccess::reopen(const String &p_path, int p_mode_flags) {
|
||||||
|
return _open(p_path, p_mode_flags);
|
||||||
|
};
|
||||||
|
|
||||||
|
FileAccess *FileAccess::open(const String &p_path, int p_mode_flags, Error *r_error) {
|
||||||
|
//try packed data first
|
||||||
|
|
||||||
|
FileAccess *ret = nullptr;
|
||||||
|
|
||||||
|
ret = create();
|
||||||
|
Error err = ret->_open(p_path, p_mode_flags);
|
||||||
|
|
||||||
|
if (r_error) {
|
||||||
|
*r_error = err;
|
||||||
|
}
|
||||||
|
if (err != OK) {
|
||||||
|
memdelete(ret);
|
||||||
|
ret = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
String FileAccess::fix_path(const String &p_path) const {
|
||||||
|
//helper used by file accesses that use a single filesystem
|
||||||
|
|
||||||
|
String r_path = p_path.replace("\\", "/");
|
||||||
|
|
||||||
|
return r_path;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* these are all implemented for ease of porting, then can later be optimized */
|
||||||
|
|
||||||
|
uint16_t FileAccess::get_16() const {
|
||||||
|
uint16_t res;
|
||||||
|
uint8_t a, b;
|
||||||
|
|
||||||
|
a = get_8();
|
||||||
|
b = get_8();
|
||||||
|
|
||||||
|
if (endian_swap) {
|
||||||
|
SWAP(a, b);
|
||||||
|
}
|
||||||
|
|
||||||
|
res = b;
|
||||||
|
res <<= 8;
|
||||||
|
res |= a;
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
uint32_t FileAccess::get_32() const {
|
||||||
|
uint32_t res;
|
||||||
|
uint16_t a, b;
|
||||||
|
|
||||||
|
a = get_16();
|
||||||
|
b = get_16();
|
||||||
|
|
||||||
|
if (endian_swap) {
|
||||||
|
SWAP(a, b);
|
||||||
|
}
|
||||||
|
|
||||||
|
res = b;
|
||||||
|
res <<= 16;
|
||||||
|
res |= a;
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
uint64_t FileAccess::get_64() const {
|
||||||
|
uint64_t res;
|
||||||
|
uint32_t a, b;
|
||||||
|
|
||||||
|
a = get_32();
|
||||||
|
b = get_32();
|
||||||
|
|
||||||
|
if (endian_swap) {
|
||||||
|
SWAP(a, b);
|
||||||
|
}
|
||||||
|
|
||||||
|
res = b;
|
||||||
|
res <<= 32;
|
||||||
|
res |= a;
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
float FileAccess::get_float() const {
|
||||||
|
MarshallFloat m;
|
||||||
|
m.i = get_32();
|
||||||
|
return m.f;
|
||||||
|
};
|
||||||
|
|
||||||
|
real_t FileAccess::get_real() const {
|
||||||
|
if (real_is_double) {
|
||||||
|
return get_double();
|
||||||
|
} else {
|
||||||
|
return get_float();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
double FileAccess::get_double() const {
|
||||||
|
MarshallDouble m;
|
||||||
|
m.l = get_64();
|
||||||
|
return m.d;
|
||||||
|
};
|
||||||
|
|
||||||
|
String FileAccess::get_token() const {
|
||||||
|
CharString token;
|
||||||
|
|
||||||
|
CharType c = get_8();
|
||||||
|
|
||||||
|
while (!eof_reached()) {
|
||||||
|
if (c <= ' ') {
|
||||||
|
if (token.length()) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
token += c;
|
||||||
|
}
|
||||||
|
c = get_8();
|
||||||
|
}
|
||||||
|
|
||||||
|
return String::utf8(token.get_data());
|
||||||
|
}
|
||||||
|
|
||||||
|
class CharBuffer {
|
||||||
|
Vector<char> vector;
|
||||||
|
char stack_buffer[256];
|
||||||
|
|
||||||
|
char *buffer;
|
||||||
|
int capacity;
|
||||||
|
int written;
|
||||||
|
|
||||||
|
bool grow() {
|
||||||
|
if (vector.resize(next_power_of_2(1 + written)) != OK) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (buffer == stack_buffer) { // first chunk?
|
||||||
|
|
||||||
|
for (int i = 0; i < written; i++) {
|
||||||
|
vector.write[i] = stack_buffer[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
buffer = vector.ptrw();
|
||||||
|
capacity = vector.size();
|
||||||
|
ERR_FAIL_COND_V(written >= capacity, false);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
_FORCE_INLINE_ CharBuffer() :
|
||||||
|
buffer(stack_buffer),
|
||||||
|
capacity(sizeof(stack_buffer) / sizeof(char)),
|
||||||
|
written(0) {
|
||||||
|
}
|
||||||
|
|
||||||
|
_FORCE_INLINE_ void push_back(char c) {
|
||||||
|
if (written >= capacity) {
|
||||||
|
ERR_FAIL_COND(!grow());
|
||||||
|
}
|
||||||
|
|
||||||
|
buffer[written++] = c;
|
||||||
|
}
|
||||||
|
|
||||||
|
_FORCE_INLINE_ const char *get_data() const {
|
||||||
|
return buffer;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
String FileAccess::get_line() const {
|
||||||
|
CharBuffer line;
|
||||||
|
|
||||||
|
CharType c = get_8();
|
||||||
|
|
||||||
|
while (!eof_reached()) {
|
||||||
|
if (c == '\n' || c == '\0') {
|
||||||
|
line.push_back(0);
|
||||||
|
return String::utf8(line.get_data());
|
||||||
|
} else if (c != '\r') {
|
||||||
|
line.push_back(c);
|
||||||
|
}
|
||||||
|
|
||||||
|
c = get_8();
|
||||||
|
}
|
||||||
|
line.push_back(0);
|
||||||
|
return String::utf8(line.get_data());
|
||||||
|
}
|
||||||
|
|
||||||
|
Vector<String> FileAccess::get_csv_line(const String &p_delim) const {
|
||||||
|
ERR_FAIL_COND_V_MSG(p_delim.length() != 1, Vector<String>(), "Only single character delimiters are supported to parse CSV lines.");
|
||||||
|
ERR_FAIL_COND_V_MSG(p_delim[0] == '"', Vector<String>(), "The double quotation mark character (\") is not supported as a delimiter for CSV lines.");
|
||||||
|
|
||||||
|
String line;
|
||||||
|
|
||||||
|
// CSV can support entries with line breaks as long as they are enclosed
|
||||||
|
// in double quotes. So our "line" might be more than a single line in the
|
||||||
|
// text file.
|
||||||
|
int qc = 0;
|
||||||
|
do {
|
||||||
|
if (eof_reached()) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
line += get_line() + "\n";
|
||||||
|
qc = 0;
|
||||||
|
for (int i = 0; i < line.length(); i++) {
|
||||||
|
if (line[i] == '"') {
|
||||||
|
qc++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} while (qc % 2);
|
||||||
|
|
||||||
|
// Remove the extraneous newline we've added above.
|
||||||
|
line = line.substr(0, line.length() - 1);
|
||||||
|
|
||||||
|
Vector<String> strings;
|
||||||
|
|
||||||
|
bool in_quote = false;
|
||||||
|
String current;
|
||||||
|
for (int i = 0; i < line.length(); i++) {
|
||||||
|
CharType c = line[i];
|
||||||
|
// A delimiter ends the current entry, unless it's in a quoted string.
|
||||||
|
if (!in_quote && c == p_delim[0]) {
|
||||||
|
strings.push_back(current);
|
||||||
|
current = String();
|
||||||
|
} else if (c == '"') {
|
||||||
|
// Doubled quotes are escapes for intentional quotes in the string.
|
||||||
|
if (line[i + 1] == '"' && in_quote) {
|
||||||
|
current += '"';
|
||||||
|
i++;
|
||||||
|
} else {
|
||||||
|
in_quote = !in_quote;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
current += c;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
strings.push_back(current);
|
||||||
|
|
||||||
|
return strings;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
uint64_t FileAccess::get_buffer(uint8_t *p_dst, uint64_t p_length) const {
|
||||||
|
ERR_FAIL_COND_V(!p_dst && p_length > 0, -1);
|
||||||
|
|
||||||
|
uint64_t i = 0;
|
||||||
|
for (i = 0; i < p_length && !eof_reached(); i++) {
|
||||||
|
p_dst[i] = get_8();
|
||||||
|
}
|
||||||
|
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
String FileAccess::get_as_utf8_string(bool p_skip_cr) const {
|
||||||
|
Vector<uint8_t> sourcef;
|
||||||
|
uint64_t len = get_len();
|
||||||
|
sourcef.resize(len + 1);
|
||||||
|
|
||||||
|
uint8_t *w = sourcef.ptrw();
|
||||||
|
uint64_t r = get_buffer(w, len);
|
||||||
|
ERR_FAIL_COND_V(r != len, String());
|
||||||
|
w[len] = 0;
|
||||||
|
|
||||||
|
String s;
|
||||||
|
if (s.parse_utf8((const char *)w, -1, p_skip_cr)) {
|
||||||
|
return String();
|
||||||
|
}
|
||||||
|
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
|
||||||
|
void FileAccess::store_16(uint16_t p_dest) {
|
||||||
|
uint8_t a, b;
|
||||||
|
|
||||||
|
a = p_dest & 0xFF;
|
||||||
|
b = p_dest >> 8;
|
||||||
|
|
||||||
|
if (endian_swap) {
|
||||||
|
SWAP(a, b);
|
||||||
|
}
|
||||||
|
|
||||||
|
store_8(a);
|
||||||
|
store_8(b);
|
||||||
|
}
|
||||||
|
void FileAccess::store_32(uint32_t p_dest) {
|
||||||
|
uint16_t a, b;
|
||||||
|
|
||||||
|
a = p_dest & 0xFFFF;
|
||||||
|
b = p_dest >> 16;
|
||||||
|
|
||||||
|
if (endian_swap) {
|
||||||
|
SWAP(a, b);
|
||||||
|
}
|
||||||
|
|
||||||
|
store_16(a);
|
||||||
|
store_16(b);
|
||||||
|
}
|
||||||
|
void FileAccess::store_64(uint64_t p_dest) {
|
||||||
|
uint32_t a, b;
|
||||||
|
|
||||||
|
a = p_dest & 0xFFFFFFFF;
|
||||||
|
b = p_dest >> 32;
|
||||||
|
|
||||||
|
if (endian_swap) {
|
||||||
|
SWAP(a, b);
|
||||||
|
}
|
||||||
|
|
||||||
|
store_32(a);
|
||||||
|
store_32(b);
|
||||||
|
}
|
||||||
|
|
||||||
|
void FileAccess::store_real(real_t p_real) {
|
||||||
|
if (sizeof(real_t) == 4) {
|
||||||
|
store_float(p_real);
|
||||||
|
} else {
|
||||||
|
store_double(p_real);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void FileAccess::store_float(float p_dest) {
|
||||||
|
MarshallFloat m;
|
||||||
|
m.f = p_dest;
|
||||||
|
store_32(m.i);
|
||||||
|
};
|
||||||
|
|
||||||
|
void FileAccess::store_double(double p_dest) {
|
||||||
|
MarshallDouble m;
|
||||||
|
m.d = p_dest;
|
||||||
|
store_64(m.l);
|
||||||
|
};
|
||||||
|
|
||||||
|
uint64_t FileAccess::get_modified_time(const String &p_file) {
|
||||||
|
FileAccess *fa = create();
|
||||||
|
ERR_FAIL_COND_V_MSG(!fa, 0, "Cannot create FileAccess for path '" + p_file + "'.");
|
||||||
|
|
||||||
|
uint64_t mt = fa->_get_modified_time(p_file);
|
||||||
|
memdelete(fa);
|
||||||
|
return mt;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t FileAccess::get_unix_permissions(const String &p_file) {
|
||||||
|
FileAccess *fa = create();
|
||||||
|
ERR_FAIL_COND_V_MSG(!fa, 0, "Cannot create FileAccess for path '" + p_file + "'.");
|
||||||
|
|
||||||
|
uint32_t mt = fa->_get_unix_permissions(p_file);
|
||||||
|
memdelete(fa);
|
||||||
|
return mt;
|
||||||
|
}
|
||||||
|
|
||||||
|
Error FileAccess::set_unix_permissions(const String &p_file, uint32_t p_permissions) {
|
||||||
|
FileAccess *fa = create();
|
||||||
|
ERR_FAIL_COND_V_MSG(!fa, ERR_CANT_CREATE, "Cannot create FileAccess for path '" + p_file + "'.");
|
||||||
|
|
||||||
|
Error err = fa->_set_unix_permissions(p_file, p_permissions);
|
||||||
|
memdelete(fa);
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
void FileAccess::store_string(const String &p_string) {
|
||||||
|
if (p_string.length() == 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
CharString cs = p_string.utf8();
|
||||||
|
store_buffer((uint8_t *)&cs[0], cs.length());
|
||||||
|
}
|
||||||
|
|
||||||
|
void FileAccess::store_pascal_string(const String &p_string) {
|
||||||
|
CharString cs = p_string.utf8();
|
||||||
|
store_32(cs.length());
|
||||||
|
store_buffer((uint8_t *)&cs[0], cs.length());
|
||||||
|
};
|
||||||
|
|
||||||
|
String FileAccess::get_pascal_string() {
|
||||||
|
uint32_t sl = get_32();
|
||||||
|
CharString cs;
|
||||||
|
cs.resize(sl + 1);
|
||||||
|
get_buffer((uint8_t *)cs.ptr(), sl);
|
||||||
|
cs[sl] = 0;
|
||||||
|
|
||||||
|
String ret;
|
||||||
|
ret.parse_utf8(cs.ptr());
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
};
|
||||||
|
|
||||||
|
void FileAccess::store_line(const String &p_line) {
|
||||||
|
store_string(p_line);
|
||||||
|
store_8('\n');
|
||||||
|
}
|
||||||
|
|
||||||
|
void FileAccess::store_csv_line(const Vector<String> &p_values, const String &p_delim) {
|
||||||
|
ERR_FAIL_COND(p_delim.length() != 1);
|
||||||
|
|
||||||
|
String line = "";
|
||||||
|
int size = p_values.size();
|
||||||
|
for (int i = 0; i < size; ++i) {
|
||||||
|
String value = p_values[i];
|
||||||
|
|
||||||
|
if (value.find("\"") != -1 || value.find(p_delim) != -1 || value.find("\n") != -1) {
|
||||||
|
value = "\"" + value.replace("\"", "\"\"") + "\"";
|
||||||
|
}
|
||||||
|
if (i < size - 1) {
|
||||||
|
value += p_delim;
|
||||||
|
}
|
||||||
|
|
||||||
|
line += value;
|
||||||
|
}
|
||||||
|
|
||||||
|
store_line(line);
|
||||||
|
}
|
||||||
|
|
||||||
|
void FileAccess::store_buffer_vec(const Vector<uint8_t> &data) {
|
||||||
|
store_buffer(data.ptr(), data.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
void FileAccess::store_buffer(const uint8_t *p_src, uint64_t p_length) {
|
||||||
|
ERR_FAIL_COND(!p_src && p_length > 0);
|
||||||
|
for (uint64_t i = 0; i < p_length; i++) {
|
||||||
|
store_8(p_src[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
Vector<uint8_t> FileAccess::get_file_as_array(const String &p_path, Error *r_error) {
|
||||||
|
FileAccess *f = FileAccess::open(p_path, READ, r_error);
|
||||||
|
if (!f) {
|
||||||
|
if (r_error) { // if error requested, do not throw error
|
||||||
|
return Vector<uint8_t>();
|
||||||
|
}
|
||||||
|
ERR_FAIL_V_MSG(Vector<uint8_t>(), "Can't open file from path '" + String(p_path) + "'.");
|
||||||
|
}
|
||||||
|
Vector<uint8_t> data;
|
||||||
|
data.resize(f->get_len());
|
||||||
|
f->get_buffer(data.ptrw(), data.size());
|
||||||
|
memdelete(f);
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
String FileAccess::get_file_as_string(const String &p_path, Error *r_error) {
|
||||||
|
Error err;
|
||||||
|
Vector<uint8_t> array = get_file_as_array(p_path, &err);
|
||||||
|
if (r_error) {
|
||||||
|
*r_error = err;
|
||||||
|
}
|
||||||
|
if (err != OK) {
|
||||||
|
if (r_error) {
|
||||||
|
return String();
|
||||||
|
}
|
||||||
|
ERR_FAIL_V_MSG(String(), "Can't get file as string from path '" + String(p_path) + "'.");
|
||||||
|
}
|
||||||
|
|
||||||
|
String ret;
|
||||||
|
ret.parse_utf8((const char *)array.ptr(), array.size());
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
FileAccess::FileAccess() {
|
||||||
|
endian_swap = false;
|
||||||
|
real_is_double = false;
|
||||||
|
};
|
||||||
|
*/
|
||||||
|
@ -2,27 +2,202 @@
|
|||||||
#ifndef FILE_ACCESS_H
|
#ifndef FILE_ACCESS_H
|
||||||
#define FILE_ACCESS_H
|
#define FILE_ACCESS_H
|
||||||
|
|
||||||
|
/*************************************************************************/
|
||||||
|
/* file_access.h */
|
||||||
|
/*************************************************************************/
|
||||||
|
/* This file is part of: */
|
||||||
|
/* PANDEMONIUM ENGINE */
|
||||||
|
/* https://github.com/Relintai/pandemonium_engine */
|
||||||
|
/*************************************************************************/
|
||||||
|
/* Copyright (c) 2022-present Péter Magyar. */
|
||||||
|
/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
|
||||||
|
/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
|
||||||
|
/* */
|
||||||
|
/* Permission is hereby granted, free of charge, to any person obtaining */
|
||||||
|
/* a copy of this software and associated documentation files (the */
|
||||||
|
/* "Software"), to deal in the Software without restriction, including */
|
||||||
|
/* without limitation the rights to use, copy, modify, merge, publish, */
|
||||||
|
/* distribute, sublicense, and/or sell copies of the Software, and to */
|
||||||
|
/* permit persons to whom the Software is furnished to do so, subject to */
|
||||||
|
/* the following conditions: */
|
||||||
|
/* */
|
||||||
|
/* The above copyright notice and this permission notice shall be */
|
||||||
|
/* included in all copies or substantial portions of the Software. */
|
||||||
|
/* */
|
||||||
|
/* 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 OR COPYRIGHT HOLDERS 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. */
|
||||||
|
/*************************************************************************/
|
||||||
|
|
||||||
//--STRIP
|
//--STRIP
|
||||||
#include "core/error_list.h"
|
#include "core/error_list.h"
|
||||||
|
#include "core/math_defs.h"
|
||||||
#include "core/ustring.h"
|
#include "core/ustring.h"
|
||||||
//--STRIP
|
//--STRIP
|
||||||
|
|
||||||
|
#if defined(_WIN64) || defined(_WIN32)
|
||||||
|
#else
|
||||||
|
struct _IO_FILE;
|
||||||
|
typedef struct _IO_FILE FILE;
|
||||||
|
|
||||||
|
typedef void (*FileCloseNotificationFunc)(const String &p_file, int p_flags);
|
||||||
|
#endif
|
||||||
|
|
||||||
class FileAccess {
|
class FileAccess {
|
||||||
public:
|
public:
|
||||||
//TODO should probably have some simple buffered open / close / write / read api.
|
typedef void (*FileCloseFailNotify)(const String &);
|
||||||
|
|
||||||
String read_file(const String &path);
|
bool endian_swap;
|
||||||
|
bool real_is_double;
|
||||||
|
|
||||||
Vector<uint8_t> read_file_bin(const String &path);
|
virtual uint32_t _get_unix_permissions(const String &p_file);
|
||||||
Error read_file_into_bin(const String &path, Vector<uint8_t> *data);
|
virtual Error _set_unix_permissions(const String &p_file, uint32_t p_permissions);
|
||||||
|
|
||||||
Error write_file(const String &path, const String &str);
|
protected:
|
||||||
Error write_file_bin(const String &path, const Vector<uint8_t> &data);
|
String fix_path(const String &p_path) const;
|
||||||
|
virtual Error _open(const String &p_path, int p_mode_flags); ///< open a file
|
||||||
|
virtual uint64_t _get_modified_time(const String &p_file);
|
||||||
|
|
||||||
|
static FileCloseFailNotify close_fail_notify;
|
||||||
|
|
||||||
|
private:
|
||||||
|
static bool backup_save;
|
||||||
|
|
||||||
|
public:
|
||||||
|
static void set_file_close_fail_notify_callback(FileCloseFailNotify p_cbk) { close_fail_notify = p_cbk; }
|
||||||
|
|
||||||
|
enum ModeFlags {
|
||||||
|
READ = 1,
|
||||||
|
WRITE = 2,
|
||||||
|
READ_WRITE = 3,
|
||||||
|
WRITE_READ = 7,
|
||||||
|
};
|
||||||
|
|
||||||
|
virtual void close(); ///< close a file
|
||||||
|
virtual bool is_open() const; ///< true when file is open
|
||||||
|
|
||||||
|
virtual String get_path() const; /// returns the path for the current open file
|
||||||
|
virtual String get_path_absolute() const; /// returns the absolute path for the current open file
|
||||||
|
|
||||||
|
virtual void seek(uint64_t p_position); ///< seek to a given position
|
||||||
|
virtual void seek_end(int64_t p_position); ///< seek from the end of file with negative offset
|
||||||
|
virtual uint64_t get_position() const; ///< get position in the file
|
||||||
|
virtual uint64_t get_len() const; ///< get size of the file
|
||||||
|
|
||||||
|
virtual bool eof_reached() const; ///< reading passed EOF
|
||||||
|
|
||||||
|
virtual uint8_t get_8() const; ///< get a byte
|
||||||
|
virtual uint16_t get_16() const; ///< get 16 bits uint
|
||||||
|
virtual uint32_t get_32() const; ///< get 32 bits uint
|
||||||
|
virtual uint64_t get_64() const; ///< get 64 bits uint
|
||||||
|
|
||||||
|
virtual float get_float() const;
|
||||||
|
virtual double get_double() const;
|
||||||
|
virtual real_t get_real() const;
|
||||||
|
|
||||||
|
virtual uint64_t get_buffer(uint8_t *p_dst, uint64_t p_length) const; ///< get an array of bytes
|
||||||
|
virtual String get_line() const;
|
||||||
|
virtual String get_token() const;
|
||||||
|
virtual Vector<String> get_csv_line(const String &p_delim = ",") const;
|
||||||
|
virtual String get_as_utf8_string(bool p_skip_cr = true) const; // Skip CR by default for compat.
|
||||||
|
|
||||||
|
/**< use this for files WRITTEN in _big_ endian machines (ie, amiga/mac)
|
||||||
|
* It's not about the current CPU type but file formats.
|
||||||
|
* this flags get reset to false (little endian) on each open
|
||||||
|
*/
|
||||||
|
|
||||||
|
virtual void set_endian_swap(bool p_swap) { endian_swap = p_swap; }
|
||||||
|
inline bool get_endian_swap() const { return endian_swap; }
|
||||||
|
|
||||||
|
virtual Error get_error() const; ///< get last error
|
||||||
|
|
||||||
|
virtual void flush();
|
||||||
|
virtual void store_8(uint8_t p_dest); ///< store a byte
|
||||||
|
virtual void store_16(uint16_t p_dest); ///< store 16 bits uint
|
||||||
|
virtual void store_32(uint32_t p_dest); ///< store 32 bits uint
|
||||||
|
virtual void store_64(uint64_t p_dest); ///< store 64 bits uint
|
||||||
|
|
||||||
|
virtual void store_float(float p_dest);
|
||||||
|
virtual void store_double(double p_dest);
|
||||||
|
virtual void store_real(real_t p_real);
|
||||||
|
|
||||||
|
virtual void store_string(const String &p_string);
|
||||||
|
virtual void store_line(const String &p_line);
|
||||||
|
virtual void store_csv_line(const Vector<String> &p_values, const String &p_delim = ",");
|
||||||
|
|
||||||
|
virtual void store_pascal_string(const String &p_string);
|
||||||
|
virtual String get_pascal_string();
|
||||||
|
|
||||||
|
void store_buffer_vec(const Vector<uint8_t> &data); ///< store an array of bytes
|
||||||
|
virtual void store_buffer(const uint8_t *p_src, uint64_t p_length); ///< store an array of bytes
|
||||||
|
|
||||||
|
virtual bool file_exists(const String &p_name); ///< return true if a file exists
|
||||||
|
|
||||||
|
virtual Error reopen(const String &p_path, int p_mode_flags); ///< does not change the AccessType
|
||||||
|
|
||||||
|
static FileAccess *create(); /// Create a file access (for the current platform) this is the only portable way of accessing files.
|
||||||
|
static FileAccess *open(const String &p_path, int p_mode_flags, Error *r_error = nullptr); /// Create a file access (for the current platform) this is the only portable way of accessing files.
|
||||||
|
static bool exists(const String &p_name); ///< return true if a file exists
|
||||||
|
static uint64_t get_modified_time(const String &p_file);
|
||||||
|
static uint32_t get_unix_permissions(const String &p_file);
|
||||||
|
static Error set_unix_permissions(const String &p_file, uint32_t p_permissions);
|
||||||
|
|
||||||
|
static void set_backup_save(bool p_enable) { backup_save = p_enable; };
|
||||||
|
static bool is_backup_save_enabled() { return backup_save; };
|
||||||
|
|
||||||
|
static Vector<uint8_t> get_file_as_array(const String &p_path, Error *r_error = nullptr);
|
||||||
|
static String get_file_as_string(const String &p_path, Error *r_error = nullptr);
|
||||||
|
|
||||||
FileAccess();
|
FileAccess();
|
||||||
virtual ~FileAccess();
|
virtual ~FileAccess();
|
||||||
|
|
||||||
private:
|
#if defined(_WIN64) || defined(_WIN32)
|
||||||
|
#else
|
||||||
|
static FileCloseNotificationFunc close_notification_func;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
protected:
|
||||||
|
#if defined(_WIN64) || defined(_WIN32)
|
||||||
|
#else
|
||||||
|
void check_errors() const;
|
||||||
|
|
||||||
|
FILE *f;
|
||||||
|
int flags;
|
||||||
|
|
||||||
|
mutable Error last_error;
|
||||||
|
String save_path;
|
||||||
|
String path;
|
||||||
|
String path_src;
|
||||||
|
#endif
|
||||||
|
};
|
||||||
|
|
||||||
|
struct FileAccessRef {
|
||||||
|
FileAccess *f;
|
||||||
|
|
||||||
|
_FORCE_INLINE_ bool is_null() const { return f == nullptr; }
|
||||||
|
_FORCE_INLINE_ bool is_valid() const { return f != nullptr; }
|
||||||
|
|
||||||
|
_FORCE_INLINE_ operator bool() const { return f != nullptr; }
|
||||||
|
_FORCE_INLINE_ operator FileAccess *() { return f; }
|
||||||
|
|
||||||
|
_FORCE_INLINE_ FileAccess *operator->() {
|
||||||
|
return f;
|
||||||
|
}
|
||||||
|
|
||||||
|
FileAccessRef(FileAccess *fa) { f = fa; }
|
||||||
|
FileAccessRef(FileAccessRef &&other) {
|
||||||
|
f = other.f;
|
||||||
|
other.f = nullptr;
|
||||||
|
}
|
||||||
|
~FileAccessRef() {
|
||||||
|
if (f) {
|
||||||
|
memdelete(f);
|
||||||
|
}
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
142
sfw/core/marshalls.h
Normal file
142
sfw/core/marshalls.h
Normal file
@ -0,0 +1,142 @@
|
|||||||
|
#ifndef MARSHALLS_H
|
||||||
|
#define MARSHALLS_H
|
||||||
|
|
||||||
|
/*************************************************************************/
|
||||||
|
/* marshalls.h */
|
||||||
|
/* From https://github.com/Relintai/pandemonium_engine (MIT) */
|
||||||
|
/*************************************************************************/
|
||||||
|
|
||||||
|
#include "core/int_types.h"
|
||||||
|
#include "core/math_defs.h"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Miscellaneous helpers for marshalling data types, and encoding
|
||||||
|
* in an endian independent way
|
||||||
|
*/
|
||||||
|
|
||||||
|
union MarshallFloat {
|
||||||
|
uint32_t i; ///< int
|
||||||
|
float f; ///< float
|
||||||
|
};
|
||||||
|
|
||||||
|
union MarshallDouble {
|
||||||
|
uint64_t l; ///< long long
|
||||||
|
double d; ///< double
|
||||||
|
};
|
||||||
|
|
||||||
|
static inline unsigned int encode_uint16(uint16_t p_uint, uint8_t *p_arr) {
|
||||||
|
for (int i = 0; i < 2; i++) {
|
||||||
|
*p_arr = p_uint & 0xFF;
|
||||||
|
p_arr++;
|
||||||
|
p_uint >>= 8;
|
||||||
|
}
|
||||||
|
|
||||||
|
return sizeof(uint16_t);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline unsigned int encode_uint32(uint32_t p_uint, uint8_t *p_arr) {
|
||||||
|
for (int i = 0; i < 4; i++) {
|
||||||
|
*p_arr = p_uint & 0xFF;
|
||||||
|
p_arr++;
|
||||||
|
p_uint >>= 8;
|
||||||
|
}
|
||||||
|
|
||||||
|
return sizeof(uint32_t);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline unsigned int encode_float(float p_float, uint8_t *p_arr) {
|
||||||
|
MarshallFloat mf;
|
||||||
|
mf.f = p_float;
|
||||||
|
encode_uint32(mf.i, p_arr);
|
||||||
|
|
||||||
|
return sizeof(uint32_t);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline unsigned int encode_uint64(uint64_t p_uint, uint8_t *p_arr) {
|
||||||
|
for (int i = 0; i < 8; i++) {
|
||||||
|
*p_arr = p_uint & 0xFF;
|
||||||
|
p_arr++;
|
||||||
|
p_uint >>= 8;
|
||||||
|
}
|
||||||
|
|
||||||
|
return sizeof(uint64_t);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline unsigned int encode_double(double p_double, uint8_t *p_arr) {
|
||||||
|
MarshallDouble md;
|
||||||
|
md.d = p_double;
|
||||||
|
encode_uint64(md.l, p_arr);
|
||||||
|
|
||||||
|
return sizeof(uint64_t);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline int encode_cstring(const char *p_string, uint8_t *p_data) {
|
||||||
|
int len = 0;
|
||||||
|
|
||||||
|
while (*p_string) {
|
||||||
|
if (p_data) {
|
||||||
|
*p_data = (uint8_t)*p_string;
|
||||||
|
p_data++;
|
||||||
|
}
|
||||||
|
p_string++;
|
||||||
|
len++;
|
||||||
|
};
|
||||||
|
|
||||||
|
if (p_data) {
|
||||||
|
*p_data = 0;
|
||||||
|
}
|
||||||
|
return len + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline uint16_t decode_uint16(const uint8_t *p_arr) {
|
||||||
|
uint16_t u = 0;
|
||||||
|
|
||||||
|
for (int i = 0; i < 2; i++) {
|
||||||
|
uint16_t b = *p_arr;
|
||||||
|
b <<= (i * 8);
|
||||||
|
u |= b;
|
||||||
|
p_arr++;
|
||||||
|
}
|
||||||
|
|
||||||
|
return u;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline uint32_t decode_uint32(const uint8_t *p_arr) {
|
||||||
|
uint32_t u = 0;
|
||||||
|
|
||||||
|
for (int i = 0; i < 4; i++) {
|
||||||
|
uint32_t b = *p_arr;
|
||||||
|
b <<= (i * 8);
|
||||||
|
u |= b;
|
||||||
|
p_arr++;
|
||||||
|
}
|
||||||
|
|
||||||
|
return u;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline float decode_float(const uint8_t *p_arr) {
|
||||||
|
MarshallFloat mf;
|
||||||
|
mf.i = decode_uint32(p_arr);
|
||||||
|
return mf.f;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline uint64_t decode_uint64(const uint8_t *p_arr) {
|
||||||
|
uint64_t u = 0;
|
||||||
|
|
||||||
|
for (int i = 0; i < 8; i++) {
|
||||||
|
uint64_t b = (*p_arr) & 0xFF;
|
||||||
|
b <<= (i * 8);
|
||||||
|
u |= b;
|
||||||
|
p_arr++;
|
||||||
|
}
|
||||||
|
|
||||||
|
return u;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline double decode_double(const uint8_t *p_arr) {
|
||||||
|
MarshallDouble md;
|
||||||
|
md.l = decode_uint64(p_arr);
|
||||||
|
return md.d;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
@ -188,8 +188,6 @@
|
|||||||
//--STRIP
|
//--STRIP
|
||||||
{{FILE:sfw/core/file_access.cpp}}
|
{{FILE:sfw/core/file_access.cpp}}
|
||||||
|
|
||||||
{{FILE:sfw/core/3rd_tinydir.h}}
|
|
||||||
|
|
||||||
//--STRIP
|
//--STRIP
|
||||||
//#include "dir_access.h"
|
//#include "dir_access.h"
|
||||||
//#include "3rd_tinydir.h"
|
//#include "3rd_tinydir.h"
|
||||||
|
@ -36,6 +36,11 @@
|
|||||||
//--STRIP
|
//--STRIP
|
||||||
{{FILE:sfw/core/typedefs.h}}
|
{{FILE:sfw/core/typedefs.h}}
|
||||||
|
|
||||||
|
//--STRIP
|
||||||
|
//#include "core/int_types.h"
|
||||||
|
//#include "core/math_defs.h"
|
||||||
|
//--STRIP
|
||||||
|
{{FILE:sfw/core/marshalls.h}}
|
||||||
|
|
||||||
//--STRIP
|
//--STRIP
|
||||||
//#include "core/int_types.h"
|
//#include "core/int_types.h"
|
||||||
|
@ -360,8 +360,6 @@
|
|||||||
//--STRIP
|
//--STRIP
|
||||||
{{FILE:sfw/core/file_access.cpp}}
|
{{FILE:sfw/core/file_access.cpp}}
|
||||||
|
|
||||||
{{FILE:sfw/core/3rd_tinydir.h}}
|
|
||||||
|
|
||||||
//--STRIP
|
//--STRIP
|
||||||
//#include "dir_access.h"
|
//#include "dir_access.h"
|
||||||
//#include "3rd_tinydir.h"
|
//#include "3rd_tinydir.h"
|
||||||
|
@ -36,6 +36,11 @@
|
|||||||
//--STRIP
|
//--STRIP
|
||||||
{{FILE:sfw/core/typedefs.h}}
|
{{FILE:sfw/core/typedefs.h}}
|
||||||
|
|
||||||
|
//--STRIP
|
||||||
|
//#include "core/int_types.h"
|
||||||
|
//#include "core/math_defs.h"
|
||||||
|
//--STRIP
|
||||||
|
{{FILE:sfw/core/marshalls.h}}
|
||||||
|
|
||||||
//--STRIP
|
//--STRIP
|
||||||
//#include "core/int_types.h"
|
//#include "core/int_types.h"
|
||||||
|
Loading…
Reference in New Issue
Block a user