From 226a5691296bfb4f22d916348821b0eed6399a89 Mon Sep 17 00:00:00 2001 From: negativeExponent Date: Wed, 21 Apr 2021 11:05:01 +0800 Subject: [PATCH] Add libretro VFS and use VFS for windows target - Reverts the use of fopen_utf8 from previous commit and use "file_stream_transforms.h" instead: https://github.com/libretro/pcsx_rearmed/commit/270c6dd14f876b8a67929aa22abefc47f4588324#diff-2065d1414bac1198fb423574b4874d5bf92a2114a2f3e235c469d8fd6288dd10 - Enable the use of VFS for windows by default. - compile with USE_LIBRETRO_VFS=1 to enable vfs when compiling for other platforms (including android if needed) --- Makefile | 10 + Makefile.libretro | 2 + deps/libchdr/include/libchdr/coretypes.h | 14 +- frontend/libretro.c | 36 +- jni/Android.mk | 16 + libpcsxcore/cdriso.c | 33 +- libpcsxcore/psxmem.c | 8 +- libpcsxcore/sio.c | 14 +- libretro-common/compat/compat_posix_string.c | 104 ++ libretro-common/compat/compat_strcasestr.c | 58 + libretro-common/file/file_path.c | 1293 ++++++++++++++++ libretro-common/include/compat/posix_string.h | 60 + libretro-common/include/compat/strcasestr.h | 48 + libretro-common/include/file/file_path.h | 531 +++++++ libretro-common/include/memmap.h | 52 + libretro-common/include/retro_assert.h | 37 + libretro-common/include/retro_environment.h | 114 ++ libretro-common/include/retro_miscellaneous.h | 186 +++ libretro-common/include/streams/file_stream.h | 115 ++ .../include/streams/file_stream_transforms.h | 101 ++ libretro-common/include/string/stdstring.h | 198 +++ libretro-common/include/time/rtime.h | 48 + libretro-common/include/vfs/vfs.h | 111 ++ .../include/vfs/vfs_implementation.h | 76 + libretro-common/streams/file_stream.c | 662 ++++++++ .../streams/file_stream_transforms.c | 159 ++ libretro-common/string/stdstring.c | 416 ++++++ libretro-common/time/rtime.c | 81 + libretro-common/vfs/vfs_implementation.c | 1329 +++++++++++++++++ plugins/cdrcimg/cdrcimg.c | 10 +- 30 files changed, 5877 insertions(+), 45 deletions(-) create mode 100644 libretro-common/compat/compat_posix_string.c create mode 100644 libretro-common/compat/compat_strcasestr.c create mode 100644 libretro-common/file/file_path.c create mode 100644 libretro-common/include/compat/posix_string.h create mode 100644 libretro-common/include/compat/strcasestr.h create mode 100644 libretro-common/include/file/file_path.h create mode 100644 libretro-common/include/memmap.h create mode 100644 libretro-common/include/retro_assert.h create mode 100644 libretro-common/include/retro_environment.h create mode 100644 libretro-common/include/retro_miscellaneous.h create mode 100644 libretro-common/include/streams/file_stream.h create mode 100644 libretro-common/include/streams/file_stream_transforms.h create mode 100644 libretro-common/include/string/stdstring.h create mode 100644 libretro-common/include/time/rtime.h create mode 100644 libretro-common/include/vfs/vfs.h create mode 100644 libretro-common/include/vfs/vfs_implementation.h create mode 100644 libretro-common/streams/file_stream.c create mode 100644 libretro-common/streams/file_stream_transforms.c create mode 100644 libretro-common/string/stdstring.c create mode 100644 libretro-common/time/rtime.c create mode 100644 libretro-common/vfs/vfs_implementation.c diff --git a/Makefile b/Makefile index 3d2d94eb..3d718268 100644 --- a/Makefile +++ b/Makefile @@ -291,9 +291,19 @@ CFLAGS += `pkg-config --cflags glib-2.0 libosso dbus-1 hildon-fm-2` LDFLAGS += `pkg-config --libs glib-2.0 libosso dbus-1 hildon-fm-2` endif ifeq "$(PLATFORM)" "libretro" +ifeq "$(USE_LIBRETRO_VFS)" "1" +OBJS += libretro-common/compat/compat_posix_string.o OBJS += libretro-common/compat/fopen_utf8.o OBJS += libretro-common/encodings/compat_strl.o OBJS += libretro-common/encodings/encoding_utf.o +OBJS += libretro-common/file/file_path.o +OBJS += libretro-common/streams/file_stream.o +OBJS += libretro-common/streams/file_stream_transforms.o +OBJS += libretro-common/string/stdstring.o +OBJS += libretro-common/time/rtime.o +OBJS += libretro-common/vfs/vfs_implementation.o +CFLAGS += -DUSE_LIBRETRO_VFS +endif OBJS += frontend/libretro.o CFLAGS += -Ilibretro-common/include CFLAGS += -DFRONTEND_SUPPORTS_RGB565 diff --git a/Makefile.libretro b/Makefile.libretro index bd74dec5..1ecd3595 100644 --- a/Makefile.libretro +++ b/Makefile.libretro @@ -3,6 +3,7 @@ DEBUG ?= 0 WANT_ZLIB ?= 1 HAVE_CHD ?= 1 +USE_LIBRETRO_VFS ?= 0 # Dynarec options: lightrec, ari64 DYNAREC ?= lightrec @@ -451,6 +452,7 @@ else LIBPTHREAD := LIBDL := LIBM := + USE_LIBRETRO_VFS = 1 endif CFLAGS += $(fpic) diff --git a/deps/libchdr/include/libchdr/coretypes.h b/deps/libchdr/include/libchdr/coretypes.h index fc3f1d1f..ceffa6bd 100644 --- a/deps/libchdr/include/libchdr/coretypes.h +++ b/deps/libchdr/include/libchdr/coretypes.h @@ -20,13 +20,16 @@ typedef int32_t INT32; typedef int16_t INT16; typedef int8_t INT8; -#define core_file FILE -#ifdef HAVE_LIBRETRO -#include -#define core_fopen(file) fopen_utf8(file, "rb") +#ifdef USE_LIBRETRO_VFS +#define core_file RFILE +#define core_fopen(file) rfopen(file, "rb") +#define core_fseek rfseek +#define core_ftell rftell +#define core_fread(fc, buff, len) rfread(buff, 1, len, fc) +#define core_fclose rfclose #else +#define core_file FILE #define core_fopen(file) fopen(file, "rb") -#endif #if defined(__WIN32__) || defined(_WIN32) || defined(WIN32) || defined(__WIN64__) #define core_fseek _fseeki64 #define core_ftell _ftelli64 @@ -39,6 +42,7 @@ typedef int8_t INT8; #endif #define core_fread(fc, buff, len) fread(buff, 1, len, fc) #define core_fclose fclose +#endif static UINT64 core_fsize(core_file *f) { diff --git a/frontend/libretro.c b/frontend/libretro.c index 07601cbe..395bc13e 100644 --- a/frontend/libretro.c +++ b/frontend/libretro.c @@ -39,9 +39,12 @@ #include "revision.h" #include -#include #include "libretro_core_options.h" +#ifdef USE_LIBRETRO_VFS +#include +#endif + #ifdef _3DS #include "3ds/3ds_utils.h" #endif @@ -556,6 +559,10 @@ static const struct retro_controller_info ports[9] = /* libretro */ void retro_set_environment(retro_environment_t cb) { +#ifdef USE_LIBRETRO_VFS + struct retro_vfs_interface_info vfs_iface_info; +#endif + environ_cb = cb; if (cb(RETRO_ENVIRONMENT_GET_LOG_INTERFACE, &logging)) @@ -563,6 +570,13 @@ void retro_set_environment(retro_environment_t cb) environ_cb(RETRO_ENVIRONMENT_SET_CONTROLLER_INFO, (void*)ports); libretro_set_core_options(environ_cb); + +#ifdef USE_LIBRETRO_VFS + vfs_iface_info.required_interface_version = 1; + vfs_iface_info.iface = NULL; + if (environ_cb(RETRO_ENVIRONMENT_GET_VFS_INTERFACE, &vfs_iface_info)) + filestream_vfs_init(&vfs_iface_info); +#endif } void retro_set_video_refresh(retro_video_refresh_t cb) { video_cb = cb; } @@ -1098,11 +1112,11 @@ static bool read_m3u(const char *file) { char line[1024]; char name[PATH_MAX]; - FILE *f = fopen_utf8(file, "r"); - if (!f) + FILE *fp = fopen(file, "r"); + if (!fp) return false; - while (fgets(line, sizeof(line), f) && disk_count < sizeof(disks) / sizeof(disks[0])) + while (fgets(fp, line, sizeof(line)) && disk_count < sizeof(disks) / sizeof(disks[0])) { if (line[0] == '#') continue; @@ -1128,7 +1142,7 @@ static bool read_m3u(const char *file) } } - fclose(f); + fclose(fp); return (disk_count != 0); } @@ -2554,17 +2568,15 @@ void retro_run(void) static bool try_use_bios(const char *path) { - FILE *f; long size; const char *name; - - f = fopen_utf8(path, "rb"); - if (f == NULL) + FILE *fp = fopen(path, "rb"); + if (fp == NULL) return false; - fseek(f, 0, SEEK_END); - size = ftell(f); - fclose(f); + fseek(fp, 0, SEEK_END); + size = ftell(fp); + fclose(fp); if (size != 512 * 1024) return false; diff --git a/jni/Android.mk b/jni/Android.mk index 4e828f1f..40ebb1cb 100644 --- a/jni/Android.mk +++ b/jni/Android.mk @@ -5,6 +5,7 @@ $(shell cd "$(LOCAL_PATH)" && (diff -q ../frontend/revision.h_ ../frontend/revis $(shell cd "$(LOCAL_PATH)" && (rm ../frontend/revision.h_)) HAVE_CHD ?= 1 +USE_LIBRETRO_VFS ?= 0 ROOT_DIR := $(LOCAL_PATH)/.. CORE_DIR := $(ROOT_DIR)/libpcsxcore @@ -95,6 +96,21 @@ SOURCES_ASM := COREFLAGS := -ffast-math -funroll-loops -DHAVE_LIBRETRO -DNO_FRONTEND -DFRONTEND_SUPPORTS_RGB565 -DANDROID -DREARMED COREFLAGS += -DHAVE_CHD -D_7ZIP_ST +ifeq ($(USE_LIBRETRO_VFS),1) +SOURCES_C += \ + $(LIBRETRO_COMMON)/compat/compat_posix_string.c \ + $(LIBRETRO_COMMON)/compat/fopen_utf8.c \ + $(LIBRETRO_COMMON)/encodings/compat_strl.c \ + $(LIBRETRO_COMMON)/encodings/encoding_utf.c \ + $(LIBRETRO_COMMON)/file/file_path.c \ + $(LIBRETRO_COMMON)/streams/file_stream.c \ + $(LIBRETRO_COMMON)/streams/file_stream_transforms.c \ + $(LIBRETRO_COMMON)/string/stdstring.c \ + $(LIBRETRO_COMMON)/time/rtime.c \ + $(LIBRETRO_COMMON)/vfs/vfs_implementation.c +COREFLAGS += -DUSE_LIBRETRO_VFS +endif + HAVE_ARI64=0 HAVE_LIGHTREC=0 ifeq ($(TARGET_ARCH_ABI),armeabi-v7a) diff --git a/libpcsxcore/cdriso.c b/libpcsxcore/cdriso.c index c36c1961..fc1d0773 100644 --- a/libpcsxcore/cdriso.c +++ b/libpcsxcore/cdriso.c @@ -19,8 +19,6 @@ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * ***************************************************************************/ -#include - #include "psxcommon.h" #include "plugins.h" #include "cdrom.h" @@ -45,6 +43,13 @@ #include #endif +#ifdef USE_LIBRETRO_VFS +#include +#undef fseeko +#undef ftello +#define ftello rftell +#define fseeko rfseek +#endif #define OFF_T_MSB ((off_t)1 << (sizeof(off_t) * 8 - 1)) unsigned int cdrIsoMultidiskCount; @@ -336,16 +341,16 @@ static int parsetoc(const char *isofile) { return -1; } - if ((fi = fopen_utf8(tocname, "r")) == NULL) { + if ((fi = fopen(tocname, "r")) == NULL) { // try changing extension to .cue (to satisfy some stupid tutorials) strcpy(tocname + strlen(tocname) - 4, ".cue"); - if ((fi = fopen_utf8(tocname, "r")) == NULL) { + if ((fi = fopen(tocname, "r")) == NULL) { // if filename is image.toc.bin, try removing .bin (for Brasero) strcpy(tocname, isofile); t = strlen(tocname); if (t >= 8 && strcmp(tocname + t - 8, ".toc.bin") == 0) { tocname[t - 4] = '\0'; - if ((fi = fopen_utf8(tocname, "r")) == NULL) { + if ((fi = fopen(tocname, "r")) == NULL) { return -1; } } @@ -488,7 +493,7 @@ static int parsecue(const char *isofile) { return -1; } - if ((fi = fopen_utf8(cuename, "r")) == NULL) { + if ((fi = fopen(cuename, "r")) == NULL) { return -1; } @@ -592,7 +597,7 @@ static int parsecue(const char *isofile) { else tmp = tmpb; strncpy(incue_fname, tmp, incue_max_len); - ti[numtracks + 1].handle = fopen_utf8(filepath, "rb"); + ti[numtracks + 1].handle = fopen(filepath, "rb"); // update global offset if this is not first file in this .cue if (numtracks + 1 > 1) { @@ -613,7 +618,7 @@ static int parsecue(const char *isofile) { strncasecmp(isofile + strlen(isofile) - 4, ".cd", 3) == 0)) { // user selected .cue/.cdX as image file, use it's data track instead fclose(cdHandle); - cdHandle = fopen_utf8(filepath, "rb"); + cdHandle = fopen(filepath, "rb"); } } } @@ -647,7 +652,7 @@ static int parseccd(const char *isofile) { return -1; } - if ((fi = fopen_utf8(ccdname, "r")) == NULL) { + if ((fi = fopen(ccdname, "r")) == NULL) { return -1; } @@ -706,7 +711,7 @@ static int parsemds(const char *isofile) { return -1; } - if ((fi = fopen_utf8(mdsname, "rb")) == NULL) { + if ((fi = fopen(mdsname, "rb")) == NULL) { return -1; } @@ -1146,7 +1151,7 @@ static int opensubfile(const char *isoname) { return -1; } - subHandle = fopen_utf8(subname, "rb"); + subHandle = fopen(subname, "rb"); if (subHandle == NULL) { return -1; } @@ -1625,7 +1630,7 @@ static long CALLBACK ISOopen(void) { return 0; // it's already open } - cdHandle = fopen_utf8(GetIsoFile(), "rb"); + cdHandle = fopen(GetIsoFile(), "rb"); if (cdHandle == NULL) { SysPrintf(_("Could't open '%s' for reading: %s\n"), GetIsoFile(), strerror(errno)); @@ -1697,7 +1702,7 @@ static long CALLBACK ISOopen(void) { p = alt_bin_filename + strlen(alt_bin_filename) - 4; for (i = 0; i < sizeof(exts) / sizeof(exts[0]); i++) { strcpy(p, exts[i]); - tmpf = fopen_utf8(alt_bin_filename, "rb"); + tmpf = fopen(alt_bin_filename, "rb"); if (tmpf != NULL) break; } @@ -1733,7 +1738,7 @@ static long CALLBACK ISOopen(void) { // make sure we have another handle open for cdda if (numtracks > 1 && ti[1].handle == NULL) { - ti[1].handle = fopen_utf8(bin_filename, "rb"); + ti[1].handle = fopen(bin_filename, "rb"); } cdda_cur_sector = 0; cdda_file_offset = 0; diff --git a/libpcsxcore/psxmem.c b/libpcsxcore/psxmem.c index 53edfabc..70de76b7 100644 --- a/libpcsxcore/psxmem.c +++ b/libpcsxcore/psxmem.c @@ -23,8 +23,6 @@ // TODO: Implement caches & cycle penalty. -#include - #include "psxmem.h" #include "psxmem_map.h" #include "r3000a.h" @@ -33,6 +31,10 @@ #include "memmap.h" +#ifdef USE_LIBRETRO_VFS +#include +#endif + #ifndef MAP_ANONYMOUS #define MAP_ANONYMOUS MAP_ANON #endif @@ -219,7 +221,7 @@ void psxMemReset() { if (strcmp(Config.Bios, "HLE") != 0) { sprintf(bios, "%s/%s", Config.BiosDir, Config.Bios); - f = fopen_utf8(bios, "rb"); + f = fopen(bios, "rb"); if (f == NULL) { SysMessage(_("Could not open BIOS:\"%s\". Enabling HLE Bios!\n"), bios); diff --git a/libpcsxcore/sio.c b/libpcsxcore/sio.c index ae3e634a..4e269076 100644 --- a/libpcsxcore/sio.c +++ b/libpcsxcore/sio.c @@ -17,8 +17,6 @@ * 51 Franklin Street, Fifth Floor, Boston, MA 02111-1307 USA. * ***************************************************************************/ -#include - /* * SIO functions. */ @@ -26,6 +24,10 @@ #include "sio.h" #include +#ifdef USE_LIBRETRO_VFS +#include +#endif + // Status Flags #define TX_RDY 0x0001 #define RX_RDY 0x0002 @@ -438,11 +440,11 @@ void LoadMcd(int mcd, char *str) { if (*str == 0) return; - f = fopen_utf8(str, "rb"); + f = fopen(str, "rb"); if (f == NULL) { SysPrintf(_("The memory card %s doesn't exist - creating it\n"), str); CreateMcd(str); - f = fopen_utf8(str, "rb"); + f = fopen(str, "rb"); if (f != NULL) { struct stat buf; @@ -483,7 +485,7 @@ void SaveMcd(char *mcd, char *data, uint32_t adr, int size) { if (mcd == NULL || *mcd == 0 || strcmp(mcd, "none") == 0) return; - f = fopen_utf8(mcd, "r+b"); + f = fopen(mcd, "r+b"); if (f != NULL) { struct stat buf; @@ -520,7 +522,7 @@ void CreateMcd(char *mcd) { int s = MCD_SIZE; int i = 0, j; - f = fopen_utf8(mcd, "wb"); + f = fopen(mcd, "wb"); if (f == NULL) return; diff --git a/libretro-common/compat/compat_posix_string.c b/libretro-common/compat/compat_posix_string.c new file mode 100644 index 00000000..6a2f07ee --- /dev/null +++ b/libretro-common/compat/compat_posix_string.c @@ -0,0 +1,104 @@ +/* Copyright (C) 2010-2020 The RetroArch team + * + * --------------------------------------------------------------------------------------- + * The following license statement only applies to this file (compat_posix_string.c). + * --------------------------------------------------------------------------------------- + * + * 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. + */ + +#include + +#include + +#ifdef _WIN32 + +#undef strcasecmp +#undef strdup +#undef isblank +#undef strtok_r +#include +#include +#include +#include + +#include + +int retro_strcasecmp__(const char *a, const char *b) +{ + while (*a && *b) + { + int a_ = tolower(*a); + int b_ = tolower(*b); + + if (a_ != b_) + return a_ - b_; + + a++; + b++; + } + + return tolower(*a) - tolower(*b); +} + +char *retro_strdup__(const char *orig) +{ + size_t len = strlen(orig) + 1; + char *ret = (char*)malloc(len); + if (!ret) + return NULL; + + strlcpy(ret, orig, len); + return ret; +} + +int retro_isblank__(int c) +{ + return (c == ' ') || (c == '\t'); +} + +char *retro_strtok_r__(char *str, const char *delim, char **saveptr) +{ + char *first = NULL; + if (!saveptr || !delim) + return NULL; + + if (str) + *saveptr = str; + + do + { + char *ptr = NULL; + first = *saveptr; + while (*first && strchr(delim, *first)) + *first++ = '\0'; + + if (*first == '\0') + return NULL; + + ptr = first + 1; + + while (*ptr && !strchr(delim, *ptr)) + ptr++; + + *saveptr = ptr + (*ptr ? 1 : 0); + *ptr = '\0'; + } while (strlen(first) == 0); + + return first; +} + +#endif diff --git a/libretro-common/compat/compat_strcasestr.c b/libretro-common/compat/compat_strcasestr.c new file mode 100644 index 00000000..4129dab2 --- /dev/null +++ b/libretro-common/compat/compat_strcasestr.c @@ -0,0 +1,58 @@ +/* Copyright (C) 2010-2020 The RetroArch team + * + * --------------------------------------------------------------------------------------- + * The following license statement only applies to this file (compat_strcasestr.c). + * --------------------------------------------------------------------------------------- + * + * 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. + */ + +#include + +#include + +/* Pretty much strncasecmp. */ +static int casencmp(const char *a, const char *b, size_t n) +{ + size_t i; + + for (i = 0; i < n; i++) + { + int a_lower = tolower(a[i]); + int b_lower = tolower(b[i]); + if (a_lower != b_lower) + return a_lower - b_lower; + } + + return 0; +} + +char *strcasestr_retro__(const char *haystack, const char *needle) +{ + size_t i, search_off; + size_t hay_len = strlen(haystack); + size_t needle_len = strlen(needle); + + if (needle_len > hay_len) + return NULL; + + search_off = hay_len - needle_len; + for (i = 0; i <= search_off; i++) + if (!casencmp(haystack + i, needle, needle_len)) + return (char*)haystack + i; + + return NULL; +} diff --git a/libretro-common/file/file_path.c b/libretro-common/file/file_path.c new file mode 100644 index 00000000..3dd53c98 --- /dev/null +++ b/libretro-common/file/file_path.c @@ -0,0 +1,1293 @@ +/* Copyright (C) 2010-2020 The RetroArch team + * + * --------------------------------------------------------------------------------------- + * The following license statement only applies to this file (file_path.c). + * --------------------------------------------------------------------------------------- + * + * 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. + */ + +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include