From 1bc83fe904904e120ffcf80645b4b7a2efc00877 Mon Sep 17 00:00:00 2001 From: SeiRyu Date: Tue, 2 Jul 2024 06:43:12 -0700 Subject: [PATCH] Build: XCode16 beta2 compile --- Cores/BeetlePSX/BuildFlags.xcconfig | 4 +- Cores/Mednafen/Config.xcconfig | 2 +- Cores/Mu/PVMu.xcodeproj/project.pbxproj | 4 - Cores/Mupen64Plus-NX/Config.xcconfig | 18 +- Cores/PPSSPP/BuildFlags.xcconfig | 2 +- Cores/Play/BuildFlags.xcconfig | 2 +- Cores/Reicast/BuildFlags.xcconfig | 2 +- Cores/Stella/BuildFlags.xcconfig | 4 +- .../PVTGBDual.xcodeproj/project.pbxproj | 4 - Cores/emuThree/BuildFlags.xcconfig | 2 +- .../Importer/Services/GameImporter.swift | 11 +- .../PVLibRetro/PVLibRetro-Build.xcconfig | 2 +- PVSupport/PVSupport.xcodeproj/project.pbxproj | 115 +- PVSupport/Sources/PVLibRetro/PVLibRetroCore.m | 22 +- .../libretro-common/disk_control_interface.h | 257 ++++ .../retro/libretro-common/disk_index_file.h | 76 + .../retro/libretro-common/file/archive_file.c | 935 +++++------ .../libretro-common/file/archive_file_7z.c | 526 +++++++ .../libretro-common/file/archive_file_zlib.c | 599 ++++++-- .../retro/libretro-common/file/config_file.c | 1365 ++++++++++++----- .../file/config_file_userdata.c | 171 +++ .../retro/libretro-common/file/file_path.c | 1128 +++++++++++--- .../retro/libretro-common/file/file_path_io.c | 151 ++ .../libretro-common/file/nbio/nbio_intf.c | 108 ++ .../libretro-common/file/nbio/nbio_linux.c | 235 +++ .../libretro-common/file/nbio/nbio_orbis.c | 231 +++ .../libretro-common/file/nbio/nbio_stdio.c | 189 ++- .../libretro-common/file/nbio/nbio_unixmmap.c | 184 +++ .../file/nbio/nbio_windowsmmap.c | 237 +++ .../retro/libretro-common/file/retro_dirent.c | 206 +-- .../include/retro_miscellaneous.h | 5 +- .../Sources/retro/libretro-common/paths.h | 91 ++ .../libretro-common/retro_miscellaneous.h | 269 ++-- .../retro/libretro-common/retroarch_types.h | 348 +++++ .../libretro-common/streams/file_stream.c | 881 ++++++----- .../retro/libretro-common/string/stdstring.c | 467 +++++- .../retro/libretro-common/string/stdstring.h | 250 +++ .../retro/libretro-common/time/rtime.c | 81 + .../libretro-common/vfs/vfs_implementation.c | 1335 ++++++++++++++++ .../vfs/vfs_implementation_cdrom.c | 495 ++++++ .../vfs/vfs_implementation_uwp.cpp | 911 +++++++++++ Provenance.xcodeproj/project.pbxproj | 68 +- 42 files changed, 9906 insertions(+), 2087 deletions(-) create mode 100644 PVSupport/Sources/retro/libretro-common/disk_control_interface.h create mode 100644 PVSupport/Sources/retro/libretro-common/disk_index_file.h create mode 100644 PVSupport/Sources/retro/libretro-common/file/archive_file_7z.c create mode 100644 PVSupport/Sources/retro/libretro-common/file/config_file_userdata.c create mode 100644 PVSupport/Sources/retro/libretro-common/file/file_path_io.c create mode 100644 PVSupport/Sources/retro/libretro-common/file/nbio/nbio_intf.c create mode 100644 PVSupport/Sources/retro/libretro-common/file/nbio/nbio_linux.c create mode 100644 PVSupport/Sources/retro/libretro-common/file/nbio/nbio_orbis.c create mode 100644 PVSupport/Sources/retro/libretro-common/file/nbio/nbio_unixmmap.c create mode 100644 PVSupport/Sources/retro/libretro-common/file/nbio/nbio_windowsmmap.c create mode 100644 PVSupport/Sources/retro/libretro-common/paths.h create mode 100644 PVSupport/Sources/retro/libretro-common/retroarch_types.h create mode 100644 PVSupport/Sources/retro/libretro-common/string/stdstring.h create mode 100644 PVSupport/Sources/retro/libretro-common/time/rtime.c create mode 100644 PVSupport/Sources/retro/libretro-common/vfs/vfs_implementation.c create mode 100644 PVSupport/Sources/retro/libretro-common/vfs/vfs_implementation_cdrom.c create mode 100644 PVSupport/Sources/retro/libretro-common/vfs/vfs_implementation_uwp.cpp diff --git a/Cores/BeetlePSX/BuildFlags.xcconfig b/Cores/BeetlePSX/BuildFlags.xcconfig index 1616198363..f84b5e5aaf 100644 --- a/Cores/BeetlePSX/BuildFlags.xcconfig +++ b/Cores/BeetlePSX/BuildFlags.xcconfig @@ -18,12 +18,12 @@ GL_APIENTRYP=GLAPIENTRY* // EXCLUDED_SOURCE_FILE_NAMES[sdk=macosx*] = $(inherited) // Device -GCC_PREPROCESSOR_DEFINITIONS[sdk=iphoneos*] = $(inherited) TARGET_IPHONE=1 NEON=1 IOS=1 HAVE_OPENGLES3=1 +GCC_PREPROCESSOR_DEFINITIONS[sdk=iphoneos*] = $(inherited) TARGET_IPHONE=1 IOS=1 HAVE_OPENGLES3=1 //NEON=1 // TODO: Why does this result in some missing symbols? // HAVE_OPENGLES_3_1=1 -OTHER_CFLAGS[sdk=iphoneos*] = $(inherited) -mfpu=neon -D__VEC4_OPT -D__NEON_OPT +OTHER_CFLAGS[sdk=iphoneos*] = $(inherited) -D__VEC4_OPT // -D__NEON_OPT -mfpu=neon EXCLUDED_SOURCE_FILE_NAMES[sdk=iphoneos*] = $(inherited) // Simulator diff --git a/Cores/Mednafen/Config.xcconfig b/Cores/Mednafen/Config.xcconfig index 1a9df9e9ab..63537b43f2 100644 --- a/Cores/Mednafen/Config.xcconfig +++ b/Cores/Mednafen/Config.xcconfig @@ -20,7 +20,7 @@ GCC_PREPROCESSOR_DEFINITIONS = $(inherited) HAVE_LROUND HAVE_MKDIR HAVE_PTHREAD_ OTHER_CFLAGS = $(inherited) -funroll-loops -fPIC -Wall -Wno-sign-compare -Wno-unused-variable -Wno-unused-function -Wno-uninitialized -Wno-strict-aliasing -Wno-aggressive-loop-optimizations -fno-fast-math -fomit-frame-pointer -fsigned-char -Wshadow -Wempty-body -Wignored-qualifiers -Wvla -Wvariadic-macros -Wdisabled-optimization -DMEDNAFEN_VERSION=\"1.27.1\" -DPACKAGE=\"mednafen\" -DICONV_CONST= OTHER_CFLAGS[arch=armv7] = -mfpu=neon $(inherited) OTHER_CFLAGS[arch=armv7s] = -mfpu=neon $(inherited) -OTHER_CFLAGS[arch=arm64] = -mfpu=neon $(inherited) +//OTHER_CFLAGS[arch=arm64] = -mfpu=neon $(inherited) OTHER_CFLAGS[arch=x86_64] = $(inherited) // MacOS Catalyst diff --git a/Cores/Mu/PVMu.xcodeproj/project.pbxproj b/Cores/Mu/PVMu.xcodeproj/project.pbxproj index fa21669190..2fc388e210 100644 --- a/Cores/Mu/PVMu.xcodeproj/project.pbxproj +++ b/Cores/Mu/PVMu.xcodeproj/project.pbxproj @@ -1807,8 +1807,6 @@ MTL_ENABLE_DEBUG_INFO = NO; OTHER_CFLAGS = ( "$(inherited)", - "-mno-thumb", - "-mfpu=neon", "-fno-operator-names", "-fno-rtti", "-ffast-math", @@ -2099,8 +2097,6 @@ ONLY_ACTIVE_ARCH = YES; OTHER_CFLAGS = ( "$(inherited)", - "-mno-thumb", - "-mfpu=neon", "-fno-operator-names", "-fno-rtti", "-ffast-math", diff --git a/Cores/Mupen64Plus-NX/Config.xcconfig b/Cores/Mupen64Plus-NX/Config.xcconfig index 6ae30d888c..0a4530b6e8 100644 --- a/Cores/Mupen64Plus-NX/Config.xcconfig +++ b/Cores/Mupen64Plus-NX/Config.xcconfig @@ -12,7 +12,7 @@ OTHER_LDFLAGS= $(inherited) // -EGL < android // DEFINES by Arch's -GCC_PREPROCESSOR_DEFINITIONS[arch=*] = $(inherited) __LIBRETRO__ IOS MUPENPLUSAPI=1 TXFILTER_LIB=1 GL_SILENCE_DEPRECATION=1 VFP_HARD=1 NO_ASM MUPENPLUSAPI=1 TXFILTER_LIB=1 M64P_PLUGIN_PROTOTYPES=1 PROVENANCE GCC __LIBRETRO__=1 HAVE_OPENGLES=1 HAVE_OPENGLES3=1 GLES3=1 HAVE_ARM_NEON_ASM_OPTIMIZATIONS=1 HAVE_PARALLEL_RSP=1 PARALLEL_INTEGRATION=1 HAVE_THR_AL=1 HAVE_PARALLEL_RDP=0 PNG_ARM_NEON_OPT=0 JIT_ENABLED=1 HAVE_NEON=1 HAVE_THREADS=1 GCC=1 +GCC_PREPROCESSOR_DEFINITIONS[arch=*] = $(inherited) __LIBRETRO__ IOS MUPENPLUSAPI=1 TXFILTER_LIB=1 GL_SILENCE_DEPRECATION=1 VFP_HARD=1 NO_ASM MUPENPLUSAPI=1 TXFILTER_LIB=1 M64P_PLUGIN_PROTOTYPES=1 PROVENANCE GCC __LIBRETRO__=1 HAVE_OPENGLES=1 HAVE_OPENGLES3=1 GLES3=1 HAVE_PARALLEL_RSP=1 PARALLEL_INTEGRATION=1 HAVE_THR_AL=1 HAVE_PARALLEL_RDP=0 PNG_ARM_NEON_OPT=0 JIT_ENABLED=1 HAVE_THREADS=1 GCC=1 // HAVE_ARM_NEON_ASM_OPTIMIZATIONS=1 HAVE_NEON=1 // strdup=_strdup strlcpy=_strlcpy __unix__ GCC_PREPROCESSOR_DEFINITIONS[arch=arm64] = $(inherited) NEW_DYNAREC=4 DYNAREC=3 GCC_PREPROCESSOR_DEFINITIONS[arch=armv7s] = $(inherited) NEW_DYNAREC=3 @@ -21,27 +21,27 @@ GCC_PREPROCESSOR_DEFINITIONS[arch=x86_64] = $(inherited) NEW_DYNAREC=2 NO_ASM=0 // MacOS Catalyst GCC_PREPROCESSOR_DEFINITIONS[sdk=macosx*] = $(inherited) OS_MAC_OS_X=1 SDL_VIDEO_OPENGL=1 -OTHER_CFLAGS[sdk=macosx*] = $(inherited) -fno-strict-aliasing -fvisibility=hidden -DGCC -pthread -fPIC -D__unix__ -D__VEC4_OPT -D__NEON_OPT -DX86_ASM +OTHER_CFLAGS[sdk=macosx*] = $(inherited) -fno-strict-aliasing -fvisibility=hidden -DGCC -pthread -fPIC -D__unix__ -D__VEC4_OPT -DX86_ASM //-D__NEON_OPT EXCLUDED_SOURCE_FILE_NAMES[sdk=macosx*] = $(inherited) 3DMathNeon.cpp gSPNeon.cpp RSP_LoadMatrixNeon.cpp CRC_OPT_NEON.cpp // iPhone -GCC_PREPROCESSOR_DEFINITIONS[sdk=iphoneos*] = $(inherited) OS_IOS GLESX USE_GLES=1 NEON=1 SDL_VIDEO_OPENGL_ES2=1 -OTHER_CFLAGS[sdk=iphoneos*] = $(inherited) -DOS_IOS -mfpu=neon -D__VEC4_OPT -D__NEON_OPT +GCC_PREPROCESSOR_DEFINITIONS[sdk=iphoneos*] = $(inherited) OS_IOS GLESX USE_GLES=1 SDL_VIDEO_OPENGL_ES2=1 //NEON=1 +OTHER_CFLAGS[sdk=iphoneos*] = $(inherited) -DOS_IOS -D__VEC4_OPT // -D__NEON_OPT -mfpu=neon EXCLUDED_SOURCE_FILE_NAMES[sdk=iphoneos*] = $(inherited) 3DMath.cpp // Simulator -GCC_PREPROCESSOR_DEFINITIONS[sdk=iphonesimulator*] = $(inherited) OS_IOS GLESX USE_GLES=1 NEON=1 SDL_VIDEO_OPENGL_ES2=1 +GCC_PREPROCESSOR_DEFINITIONS[sdk=iphonesimulator*] = $(inherited) OS_IOS GLESX USE_GLES=1 //NEON=1 SDL_VIDEO_OPENGL_ES2=1 OTHER_CFLAGS[sdk=iphonesimulator*] = $(inherited) -DOS_IOS EXCLUDED_SOURCE_FILE_NAMES[sdk=iphonesimulator*] = $(inherited) sinc_resampler_neon.S s16_to_float_neon.c s16_to_float_neon.S float_to_s16_neon.c float_to_s16_neon.S 3DMathNeon.cpp gSPNeon.cpp // tvOS Device -GCC_PREPROCESSOR_DEFINITIONS[sdk=appletvos*] = $(inherited) OS_IOS GLESX USE_GLES=1 NEON=1 SDL_VIDEO_OPENGL_ES2=1 -OTHER_CFLAGS[sdk=appletvos*] = $(inherited) -DOS_IOS -mfpu=neon -D__VEC4_OPT +GCC_PREPROCESSOR_DEFINITIONS[sdk=appletvos*] = $(inherited) OS_IOS GLESX USE_GLES=1 SDL_VIDEO_OPENGL_ES2=1 //NEON=1 +OTHER_CFLAGS[sdk=appletvos*] = $(inherited) -DOS_IOS -D__VEC4_OPT // -mfpu=neon EXCLUDED_SOURCE_FILE_NAMES[sdk=appletvos*] = $(inherited) 3DMath.cpp // tvOS Simulator -GCC_PREPROCESSOR_DEFINITIONS[sdk=appletvsimulator*] = $(inherited) OS_IOS GLESX USE_GLES=1 NEON=1 SDL_VIDEO_OPENGL_ES2=1 -OTHER_CFLAGS[sdk=appletvsimulator*] = $(inherited) -DOS_IOS +GCC_PREPROCESSOR_DEFINITIONS[sdk=appletvsimulator*] = $(inherited) OS_IOS GLESX USE_GLES=1 SDL_VIDEO_OPENGL_ES2=1 // NEON=1 +OTHER_CFLAGS[sdk=appletvsimulator*] = $(inherited) -DOS_IOS diff --git a/Cores/PPSSPP/BuildFlags.xcconfig b/Cores/PPSSPP/BuildFlags.xcconfig index 04a567654c..98dec24150 100644 --- a/Cores/PPSSPP/BuildFlags.xcconfig +++ b/Cores/PPSSPP/BuildFlags.xcconfig @@ -18,7 +18,7 @@ //OTHER_CFLAGS[sdk=appletvos*] = $(inherited) -DDRC_SH2 -D_USE_CZ80 //OTHER_CFLAGS[sdk=appletvsimulator*] = $(inherited) -D_USE_CZ80 //OTHER_CFLAGS = $(inherited) -DXXH_VECTOR=XXH_SCALAR -DMASKED_PSP_MEMORY=TRUE -OTHER_CFLAGS = $(inherited) -mno-thumb -mfpu=neon -fno-operator-names -ffast-math -ftree-vectorize -fno-strict-aliasing -frename-registers -fpermissive -fno-operator-names -fobjc-arc -fsingle-precision-constant -fmodules -fcxx-modules -fomit-frame-pointer -fvisibility-inlines-hidden -fvisibility=hidden -flto -funsafe-math-optimizations -DXXH_VECTOR=XXH_SCALAR -DGLES_SILENCE_DEPRECATION=1 +OTHER_CFLAGS = $(inherited) -fno-operator-names -ffast-math -ftree-vectorize -fno-strict-aliasing -frename-registers -fpermissive -fno-operator-names -fobjc-arc -fsingle-precision-constant -fmodules -fcxx-modules -fomit-frame-pointer -fvisibility-inlines-hidden -fvisibility=hidden -flto -funsafe-math-optimizations -DXXH_VECTOR=XXH_SCALAR -DGLES_SILENCE_DEPRECATION=1 // Skip Simulator Build EXCLUDED_SOURCE_FILE_NAMES[sdk=iphonesimulator*][arch=*] = * INCLUDED_SOURCE_FILE_NAMES[sdk=iphonesimulator*][arch=*] = PVDebug.m diff --git a/Cores/Play/BuildFlags.xcconfig b/Cores/Play/BuildFlags.xcconfig index 9932c9aa13..060c37a081 100644 --- a/Cores/Play/BuildFlags.xcconfig +++ b/Cores/Play/BuildFlags.xcconfig @@ -24,7 +24,7 @@ OTHER_CFLAGS[arch=x86_64] = $(inherited) -DHAVE_SSE // MARK: ARM -OTHER_CFLAGS[arch=arm64] = $(inherited) -D__ARM_NEON__ -DHAVE_NEON -march=armv8-a+crc -mno-thumb -mfpu=neon +OTHER_CFLAGS[arch=arm64] = $(inherited) -march=armv8-a+crc // -D__ARM_NEON__ -DHAVE_NEON -mno-thumb -mfpu=neon // MARK: - Excluded Sources diff --git a/Cores/Reicast/BuildFlags.xcconfig b/Cores/Reicast/BuildFlags.xcconfig index 5a0ff07a0c..064feec82c 100644 --- a/Cores/Reicast/BuildFlags.xcconfig +++ b/Cores/Reicast/BuildFlags.xcconfig @@ -8,7 +8,7 @@ // All GCC_PREPROCESSOR_DEFINITIONS = $(inherited) REICAST_VERSION=15.0 TARGET_NO_WEBUI=1 TARGET_NO_REC=1 NO_ASM=1 -OTHER_CFLAGS = $(inherited) -mno-thumb -mfpu=neon -fno-operator-names -fno-rtti -ffast-math -ftree-vectorize -fno-strict-aliasing -frename-registers -fno-rtti -fpermissive -fno-operator-names -fsingle-precision-constant +OTHER_CFLAGS = $(inherited) -fno-operator-names -fno-rtti -ffast-math -ftree-vectorize -fno-strict-aliasing -frename-registers -fno-rtti -fpermissive -fno-operator-names -fsingle-precision-constant // MacOS Catalyst GCC_PREPROCESSOR_DEFINITIONS[sdk=macosx*] = $(inherited) TARGET_OSX=1 diff --git a/Cores/Stella/BuildFlags.xcconfig b/Cores/Stella/BuildFlags.xcconfig index 12f5f9c073..4f54090a78 100644 --- a/Cores/Stella/BuildFlags.xcconfig +++ b/Cores/Stella/BuildFlags.xcconfig @@ -21,8 +21,8 @@ CLANG_CXX_LANGUAGE_STANDARD = c++14 // Device -GCC_PREPROCESSOR_DEFINITIONS[sdk=iphoneos*] = $(inherited) TARGET_IPHONE=1 NEON=1 -OTHER_CFLAGS[sdk=iphoneos*] = $(inherited) -mfpu=neon -D__VEC4_OPT -D__NEON_OPT +GCC_PREPROCESSOR_DEFINITIONS[sdk=iphoneos*] = $(inherited) TARGET_IPHONE=1 // NEON=1 +OTHER_CFLAGS[sdk=iphoneos*] = $(inherited) -D__VEC4_OPT // -D__NEON_OPT -mfpu=neon EXCLUDED_SOURCE_FILE_NAMES[sdk=iphoneos*] = $(inherited) // Simulator diff --git a/Cores/TGBDual/PVTGBDual.xcodeproj/project.pbxproj b/Cores/TGBDual/PVTGBDual.xcodeproj/project.pbxproj index c62b267958..18a85a7d47 100644 --- a/Cores/TGBDual/PVTGBDual.xcodeproj/project.pbxproj +++ b/Cores/TGBDual/PVTGBDual.xcodeproj/project.pbxproj @@ -576,8 +576,6 @@ MTL_ENABLE_DEBUG_INFO = NO; OTHER_CFLAGS = ( "$(inherited)", - "-mno-thumb", - "-mfpu=neon", "-fno-operator-names", "-fno-rtti", "-ffast-math", @@ -779,8 +777,6 @@ ONLY_ACTIVE_ARCH = YES; OTHER_CFLAGS = ( "$(inherited)", - "-mno-thumb", - "-mfpu=neon", "-fno-operator-names", "-fno-rtti", "-ffast-math", diff --git a/Cores/emuThree/BuildFlags.xcconfig b/Cores/emuThree/BuildFlags.xcconfig index 95110c8d91..a672c835b6 100644 --- a/Cores/emuThree/BuildFlags.xcconfig +++ b/Cores/emuThree/BuildFlags.xcconfig @@ -3,7 +3,7 @@ // All GCC_PREPROCESSOR_DEFINITIONS = $(inherited) -OTHER_CFLAGS = $(inherited) -mno-thumb -mfpu=neon -ffast-math -ftree-vectorize -fno-strict-aliasing -frename-registers -fpermissive -fsingle-precision-constant -DTARGET_NO_NIXPROF +OTHER_CFLAGS = $(inherited) -ffast-math -ftree-vectorize -fno-strict-aliasing -frename-registers -fpermissive -fsingle-precision-constant -DTARGET_NO_NIXPROF OTHER_LDFLAGS = $(inherited) -ObjC -DIPHONEOS // Swift <--> C++ Interop diff --git a/PVLibrary/PVLibrary/Importer/Services/GameImporter.swift b/PVLibrary/PVLibrary/Importer/Services/GameImporter.swift index 639f5669b0..b13856bbec 100644 --- a/PVLibrary/PVLibrary/Importer/Services/GameImporter.swift +++ b/PVLibrary/PVLibrary/Importer/Services/GameImporter.swift @@ -1232,19 +1232,20 @@ public extension GameImporter { func releaseID(forCRCs crcs: Set) -> Int? { let roms = Table("ROMs") - let romID = Expression("romID") - let romHashCRC = Expression("romHashCRC") + let romID = Expression(value:"romID") + let romHashCRC = Expression(value:"romHashCRC") let query = roms.select(romID).filter(crcs.contains(romHashCRC)) do { let result = try sqldb.pluck(query) - let foundROMid = try result?.get(romID) - return foundROMid + if let foundROMid = try result?.get(romID) as? Int { + return foundROMid + } } catch { ELOG("Query error: \(error.localizedDescription)") - return nil } + return nil } enum DatabaseQueryError: Error { diff --git a/PVSupport/PVLibRetro/PVLibRetro-Build.xcconfig b/PVSupport/PVLibRetro/PVLibRetro-Build.xcconfig index 1dfa7c484f..1ed91b6b33 100644 --- a/PVSupport/PVLibRetro/PVLibRetro-Build.xcconfig +++ b/PVSupport/PVLibRetro/PVLibRetro-Build.xcconfig @@ -6,7 +6,7 @@ GCC_PREPROCESSOR_DEFINITIONS = $(inherited) __LIBRETRO__=1 HAVE_THREADS=1 HAVE_OPENGL=1 HAVE_OPENGLES=1 HAVE_OPENGLES2=1 HAVE_OPENGLES3=1 GLES=1 GLES2=1 GLES3=1 GLES31=1 // HAVE_PLAIN_DRM=1 OTHER_CFLAGS = $(inherited) -ObjC -OTHER_LDFLAGS = $(inherited) -ObjC -all_load +OTHER_LDFLAGS = $(inherited) -ObjC -Wl,-U,_content_get_crc -Wl,-U,_input_driver_keyboard_mapping_set_block -Wl,-U,_runloop_msg_queue_push -Wl,-U,_filestream_open -Wl,-U,_filestream_read -Wl,-U,_filestream_close -Wl,-U,_RARCH_LOG -Wl,-U,_string_to_lower -Wl,-U,_rtime_localtime // -all_load // Device GCC_PREPROCESSOR_DEFINITIONS[sdk=iphoneos*] = $(inherited) TARGET_IPHONE=1 diff --git a/PVSupport/PVSupport.xcodeproj/project.pbxproj b/PVSupport/PVSupport.xcodeproj/project.pbxproj index 93994024c8..dd1a1c1e68 100644 --- a/PVSupport/PVSupport.xcodeproj/project.pbxproj +++ b/PVSupport/PVSupport.xcodeproj/project.pbxproj @@ -8,19 +8,28 @@ /* Begin PBXBuildFile section */ 557DCAB1292A91E100045B02 /* ThemeOption.swift in Sources */ = {isa = PBXBuildFile; fileRef = 557DCAB0292A91E000045B02 /* ThemeOption.swift */; }; + 931C81012C36682B00969133 /* movie.c in Sources */ = {isa = PBXBuildFile; fileRef = B3225169286C6F2A001FDF42 /* movie.c */; }; + 931C81042C3668FB00969133 /* input_keyboard.c in Sources */ = {isa = PBXBuildFile; fileRef = B34DC54A28671EFF00B60497 /* input_keyboard.c */; }; + 931C81052C36693600969133 /* retro.m in Sources */ = {isa = PBXBuildFile; fileRef = B3225130286C4728001FDF42 /* retro.m */; }; + 934DCA1A2C363B6900908E3D /* performance_counters.c in Sources */ = {isa = PBXBuildFile; fileRef = B34DC3782867180300B60497 /* performance_counters.c */; }; + 934DCA1B2C363BC300908E3D /* dynamic_dummy.c in Sources */ = {isa = PBXBuildFile; fileRef = B34DC59528671EFF00B60497 /* dynamic_dummy.c */; }; + 934DCA1C2C363BD800908E3D /* dylib.c in Sources */ = {isa = PBXBuildFile; fileRef = B34DC6452867202D00B60497 /* dylib.c */; }; + 934DCA1E2C363C0700908E3D /* features_cpu.c in Sources */ = {isa = PBXBuildFile; fileRef = B34DC6F72867202D00B60497 /* features_cpu.c */; }; + 934DCA202C363C4D00908E3D /* file_path_str.c in Sources */ = {isa = PBXBuildFile; fileRef = B3225152286C6F28001FDF42 /* file_path_str.c */; }; + 934DCA212C363C7600908E3D /* nullinput.c in Sources */ = {isa = PBXBuildFile; fileRef = B34DC4ED28671EFF00B60497 /* nullinput.c */; }; + 93CBA79D2C36438200797E98 /* msg_hash.c in Sources */ = {isa = PBXBuildFile; fileRef = B322515C286C6F29001FDF42 /* msg_hash.c */; }; + 93CBA79E2C3643BB00797E98 /* msg_hash_us.c in Sources */ = {isa = PBXBuildFile; fileRef = B34DC4E628671EFF00B60497 /* msg_hash_us.c */; }; + 93CBA79F2C3643DD00797E98 /* rhash.c in Sources */ = {isa = PBXBuildFile; fileRef = B34DC6802867202D00B60497 /* rhash.c */; }; + 93CBA7AB2C364C1300797E98 /* file_path.c in Sources */ = {isa = PBXBuildFile; fileRef = B34DC6752867202D00B60497 /* file_path.c */; }; + 93CBA7B02C364DDC00797E98 /* encoding_utf.c in Sources */ = {isa = PBXBuildFile; fileRef = B34DC63B2867202D00B60497 /* encoding_utf.c */; }; + 93FC61622C376EEF00A43114 /* rthreads.c in Sources */ = {isa = PBXBuildFile; fileRef = B34DC7072867202D00B60497 /* rthreads.c */; }; B30681502895D407003E465F /* PVLibRetroCore+Cheats.m in Sources */ = {isa = PBXBuildFile; fileRef = B306814C2895D405003E465F /* PVLibRetroCore+Cheats.m */; }; B30681522895D407003E465F /* PVLibRetroCore+Video.m in Sources */ = {isa = PBXBuildFile; fileRef = B306814D2895D406003E465F /* PVLibRetroCore+Video.m */; }; B30681542895D407003E465F /* PVLibRetroCore+Audio.m in Sources */ = {isa = PBXBuildFile; fileRef = B306814E2895D406003E465F /* PVLibRetroCore+Audio.m */; }; B30681562895D407003E465F /* PVLibRetroCore+Options.m in Sources */ = {isa = PBXBuildFile; fileRef = B306814F2895D407003E465F /* PVLibRetroCore+Options.m */; }; B30F8013290516B900F21217 /* ScreenType.swift in Sources */ = {isa = PBXBuildFile; fileRef = B30F8012290516B900F21217 /* ScreenType.swift */; }; B3225132286C4728001FDF42 /* retro.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = B322512F286C4728001FDF42 /* retro.h */; }; - B32299422878CB3C00585AD8 /* input_driver.c in Sources */ = {isa = PBXBuildFile; fileRef = B34DC54928671EFF00B60497 /* input_driver.c */; }; - B32299462878CF2C00585AD8 /* rthreads.c in Sources */ = {isa = PBXBuildFile; fileRef = B34DC7072867202D00B60497 /* rthreads.c */; }; - B322994A2878CF6200585AD8 /* input_keyboard.c in Sources */ = {isa = PBXBuildFile; fileRef = B34DC54A28671EFF00B60497 /* input_keyboard.c */; }; - B32299512878D46F00585AD8 /* performance_counters.c in Sources */ = {isa = PBXBuildFile; fileRef = B34DC3782867180300B60497 /* performance_counters.c */; }; B32299672878DBE000585AD8 /* dynamic.h in Headers */ = {isa = PBXBuildFile; fileRef = B34DC36B286717AE00B60497 /* dynamic.h */; settings = {ATTRIBUTES = (Public, ); }; }; - B322996C28792B4000585AD8 /* movie.c in Sources */ = {isa = PBXBuildFile; fileRef = B3225169286C6F2A001FDF42 /* movie.c */; }; - B322996E28792BA000585AD8 /* nullinput.c in Sources */ = {isa = PBXBuildFile; fileRef = B34DC4ED28671EFF00B60497 /* nullinput.c */; }; B33FB2EB279BE0CC0013AAD8 /* CoreOptions+Serialization.swift in Sources */ = {isa = PBXBuildFile; fileRef = B33FB2EA279BE0CC0013AAD8 /* CoreOptions+Serialization.swift */; }; B33FB2EF279BE0F50013AAD8 /* CoreOptionValueDisplay.swift in Sources */ = {isa = PBXBuildFile; fileRef = B33FB2EE279BE0F50013AAD8 /* CoreOptionValueDisplay.swift */; }; B33FB2F3279BE1320013AAD8 /* CoreOptions+Protocols.swift in Sources */ = {isa = PBXBuildFile; fileRef = B33FB2F2279BE1320013AAD8 /* CoreOptions+Protocols.swift */; }; @@ -30,8 +39,6 @@ B33FB304279BE21C0013AAD8 /* CoreOptionRange.swift in Sources */ = {isa = PBXBuildFile; fileRef = B33FB303279BE21C0013AAD8 /* CoreOptionRange.swift */; }; B33FB308279BE2460013AAD8 /* CoreOptionValue.swift in Sources */ = {isa = PBXBuildFile; fileRef = B33FB307279BE2460013AAD8 /* CoreOptionValue.swift */; }; B33FB30D279BE2710013AAD8 /* CoreOptional.swift in Sources */ = {isa = PBXBuildFile; fileRef = B33FB30C279BE2710013AAD8 /* CoreOptional.swift */; }; - B342325B286EB4060033EFA5 /* dylib.c in Sources */ = {isa = PBXBuildFile; fileRef = B34DC6452867202D00B60497 /* dylib.c */; }; - B3423260286EB5CA0033EFA5 /* dynamic_dummy.c in Sources */ = {isa = PBXBuildFile; fileRef = B34DC59528671EFF00B60497 /* dynamic_dummy.c */; }; B3447E86218B7E4B00557ACE /* CABitOperations.h in Headers */ = {isa = PBXBuildFile; fileRef = B3447E7F218B7E4B00557ACE /* CABitOperations.h */; }; B3447E88218B7E4B00557ACE /* CAAudioTimeStamp.h in Headers */ = {isa = PBXBuildFile; fileRef = B3447E80218B7E4B00557ACE /* CAAudioTimeStamp.h */; settings = {ATTRIBUTES = (Private, ); }; }; B3447E8A218B7E4B00557ACE /* CARingBuffer.cpp in Sources */ = {isa = PBXBuildFile; fileRef = B3447E81218B7E4B00557ACE /* CARingBuffer.cpp */; }; @@ -62,7 +69,6 @@ B392849B286703F2003BAC21 /* PVSupport.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = B3C96ED81D62C5E7003F1E93 /* PVSupport.framework */; }; B39C29C727A0FADF0078D028 /* OERingBuffer.m in Sources */ = {isa = PBXBuildFile; fileRef = B3FA5D5A1D6B908300060D71 /* OERingBuffer.m */; }; B39C29CE27A10EE50078D028 /* OEGameAudio.m in Sources */ = {isa = PBXBuildFile; fileRef = 1ACEA69217F748F80031B1C9 /* OEGameAudio.m */; }; - B3A41BCF286E5E660054E9A5 /* retro.m in Sources */ = {isa = PBXBuildFile; fileRef = B3225130286C4728001FDF42 /* retro.m */; }; B3A4FB5A278FE45D00A65248 /* OEGeometry.h in Headers */ = {isa = PBXBuildFile; fileRef = B3A4FB59278FE45C00A65248 /* OEGeometry.h */; settings = {ATTRIBUTES = (Public, ); }; }; B3AB37B321880FA5009D9244 /* iCadeState.swift in Sources */ = {isa = PBXBuildFile; fileRef = B3AB378B2187F9A9009D9244 /* iCadeState.swift */; }; B3AB37BD218812BB009D9244 /* iCadeReaderView.swift in Sources */ = {isa = PBXBuildFile; fileRef = B3AB37B02187FC4B009D9244 /* iCadeReaderView.swift */; }; @@ -79,7 +85,6 @@ B3AF6FD821916168000FA7F9 /* GameController.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = B3AF6FD721916168000FA7F9 /* GameController.framework */; }; B3AF6FDA2191616C000FA7F9 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = B3AF6FD92191616C000FA7F9 /* Foundation.framework */; }; B3B1A740279021C4004D1EF2 /* AHAP in Resources */ = {isa = PBXBuildFile; fileRef = B3B1A73F279021C4004D1EF2 /* AHAP */; }; - B3B3D17428712EE1008A0E2F /* msg_hash.c in Sources */ = {isa = PBXBuildFile; fileRef = B322515C286C6F29001FDF42 /* msg_hash.c */; }; B3C96EC91D62C5E7003F1E93 /* OEGameAudio.h in Headers */ = {isa = PBXBuildFile; fileRef = 1ACEA69117F748F80031B1C9 /* OEGameAudio.h */; settings = {ATTRIBUTES = (Public, ); }; }; B3C96ECA1D62C5E7003F1E93 /* DebugUtils.h in Headers */ = {isa = PBXBuildFile; fileRef = 1A4E718C1A6C699F005CA80F /* DebugUtils.h */; settings = {ATTRIBUTES = (Public, ); }; }; B3C96ECC1D62C5E7003F1E93 /* NSObject+PVAbstractAdditions.h in Headers */ = {isa = PBXBuildFile; fileRef = 1ACEA6A017F74A5A0031B1C9 /* NSObject+PVAbstractAdditions.h */; settings = {ATTRIBUTES = (Public, ); }; }; @@ -175,6 +180,11 @@ 1ACEA6A017F74A5A0031B1C9 /* NSObject+PVAbstractAdditions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSObject+PVAbstractAdditions.h"; sourceTree = ""; }; 1ACEA6A117F74A5A0031B1C9 /* NSObject+PVAbstractAdditions.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSObject+PVAbstractAdditions.m"; sourceTree = ""; }; 557DCAB0292A91E000045B02 /* ThemeOption.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ThemeOption.swift; sourceTree = ""; }; + 931B1BDA2C367D3A00B78340 /* stdstring.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = stdstring.h; sourceTree = ""; }; + 93CBA7A52C364BCB00797E98 /* vfs_implementation.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = vfs_implementation.c; sourceTree = ""; }; + 93CBA7A72C364BCB00797E98 /* vfs_implementation_cdrom.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = vfs_implementation_cdrom.c; sourceTree = ""; }; + 93CBA7A82C364BCB00797E98 /* vfs_implementation_uwp.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = vfs_implementation_uwp.cpp; sourceTree = ""; }; + 93CBA7AD2C364DB100797E98 /* rtime.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = rtime.c; sourceTree = ""; }; B305EF99276B4F0A003AE510 /* GameKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = GameKit.framework; path = Platforms/WatchOS.platform/Developer/SDKs/WatchOS8.3.sdk/System/Library/Frameworks/GameKit.framework; sourceTree = DEVELOPER_DIR; }; B305EF9B276B4FDC003AE510 /* CoreGraphics.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreGraphics.framework; path = Platforms/WatchOS.platform/Developer/SDKs/WatchOS8.3.sdk/System/Library/Frameworks/CoreGraphics.framework; sourceTree = DEVELOPER_DIR; }; B306814C2895D405003E465F /* PVLibRetroCore+Cheats.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "PVLibRetroCore+Cheats.m"; sourceTree = ""; }; @@ -1171,6 +1181,24 @@ path = Audio; sourceTree = ""; }; + 93CBA7A92C364BCB00797E98 /* vfs */ = { + isa = PBXGroup; + children = ( + 93CBA7A52C364BCB00797E98 /* vfs_implementation.c */, + 93CBA7A72C364BCB00797E98 /* vfs_implementation_cdrom.c */, + 93CBA7A82C364BCB00797E98 /* vfs_implementation_uwp.cpp */, + ); + path = vfs; + sourceTree = ""; + }; + 93CBA7AE2C364DB100797E98 /* time */ = { + isa = PBXGroup; + children = ( + 93CBA7AD2C364DB100797E98 /* rtime.c */, + ); + path = time; + sourceTree = ""; + }; B30E94892793C77700871E57 /* PVSupport */ = { isa = PBXGroup; children = ( @@ -2388,6 +2416,8 @@ B34DC6392867202D00B60497 /* libretro-common */ = { isa = PBXGroup; children = ( + 93CBA7AE2C364DB100797E98 /* time */, + 93CBA7A92C364BCB00797E98 /* vfs */, B34DC63A2867202D00B60497 /* encodings */, B34DC63C2867202D00B60497 /* compat */, B34DC6442867202D00B60497 /* dynamic */, @@ -3005,6 +3035,7 @@ B34DC7172867202D00B60497 /* string */ = { isa = PBXGroup; children = ( + 931B1BDA2C367D3A00B78340 /* stdstring.h */, B34DC7182867202D00B60497 /* stdstring.c */, ); path = string; @@ -3485,16 +3516,7 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( - B32299462878CF2C00585AD8 /* rthreads.c in Sources */, - B342325B286EB4060033EFA5 /* dylib.c in Sources */, - B3B3D17428712EE1008A0E2F /* msg_hash.c in Sources */, - B322996C28792B4000585AD8 /* movie.c in Sources */, - B3A41BCF286E5E660054E9A5 /* retro.m in Sources */, - B32299422878CB3C00585AD8 /* input_driver.c in Sources */, - B3423260286EB5CA0033EFA5 /* dynamic_dummy.c in Sources */, - B322994A2878CF6200585AD8 /* input_keyboard.c in Sources */, - B322996E28792BA000585AD8 /* nullinput.c in Sources */, - B32299512878D46F00585AD8 /* performance_counters.c in Sources */, + 931C81052C36693600969133 /* retro.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -3518,6 +3540,19 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + 931C81042C3668FB00969133 /* input_keyboard.c in Sources */, + 931C81012C36682B00969133 /* movie.c in Sources */, + 93CBA7B02C364DDC00797E98 /* encoding_utf.c in Sources */, + 93CBA7AB2C364C1300797E98 /* file_path.c in Sources */, + 93CBA79F2C3643DD00797E98 /* rhash.c in Sources */, + 93CBA79E2C3643BB00797E98 /* msg_hash_us.c in Sources */, + 93CBA79D2C36438200797E98 /* msg_hash.c in Sources */, + 934DCA212C363C7600908E3D /* nullinput.c in Sources */, + 934DCA202C363C4D00908E3D /* file_path_str.c in Sources */, + 934DCA1E2C363C0700908E3D /* features_cpu.c in Sources */, + 934DCA1B2C363BC300908E3D /* dynamic_dummy.c in Sources */, + 934DCA1C2C363BD800908E3D /* dylib.c in Sources */, + 934DCA1A2C363B6900908E3D /* performance_counters.c in Sources */, B3D180A528964C40001207AF /* PVLibRetroCore+Saves.m in Sources */, B3D180A328964C40001207AF /* PVLibRetroCore+Controls.m in Sources */, B30681562895D407003E465F /* PVLibRetroCore+Options.m in Sources */, @@ -3533,6 +3568,7 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + 93FC61622C376EEF00A43114 /* rthreads.c in Sources */, B34AB57F2106DC6100C45F09 /* PVEmulatorCore.swift in Sources */, B3AB37EB21881B7A009D9244 /* PViCadeSteelSeriesController.swift in Sources */, B3CDEEBC21D4C3E6000C55F7 /* ArchiveSupport.swift in Sources */, @@ -3763,8 +3799,9 @@ ); MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; MTL_FAST_MATH = YES; + OTHER_LDFLAGS = $inherited; PRODUCT_NAME = retro; - RUN_CLANG_STATIC_ANALYZER = YES; + RUN_CLANG_STATIC_ANALYZER = NO; SKIP_INSTALL = YES; STRIP_INSTALLED_PRODUCT = NO; SUPPORTS_MACCATALYST = YES; @@ -3802,8 +3839,9 @@ ); MTL_ENABLE_DEBUG_INFO = NO; MTL_FAST_MATH = YES; + OTHER_LDFLAGS = $inherited; PRODUCT_NAME = retro; - RUN_CLANG_STATIC_ANALYZER = YES; + RUN_CLANG_STATIC_ANALYZER = NO; SKIP_INSTALL = YES; STRIP_INSTALLED_PRODUCT = NO; SUPPORTS_MACCATALYST = YES; @@ -3841,8 +3879,9 @@ ); MTL_ENABLE_DEBUG_INFO = NO; MTL_FAST_MATH = YES; + OTHER_LDFLAGS = $inherited; PRODUCT_NAME = retro; - RUN_CLANG_STATIC_ANALYZER = YES; + RUN_CLANG_STATIC_ANALYZER = NO; SKIP_INSTALL = YES; STRIP_INSTALLED_PRODUCT = NO; SUPPORTS_MACCATALYST = YES; @@ -3931,6 +3970,11 @@ GCC_NO_COMMON_BLOCKS = YES; GCC_PREFIX_HEADER = "Supporting Files/PVSupport-Prefix.pch"; GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + HEADER_SEARCH_PATHS = ( + "\"$(SRCROOT)/Sources/retro\"", + "\"$(SRCROOT)/Sources/retro/libretro-common/include\"", + "\"$(SRCROOT)/Sources/retro/libretro-common\"", + ); INFOPLIST_FILE = "Supporting Files/Info.plist"; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; IPHONEOS_DEPLOYMENT_TARGET = 13.0; @@ -4149,6 +4193,7 @@ }; B392849228670348003BAC21 /* Debug */ = { isa = XCBuildConfiguration; + baseConfigurationReference = B32299412878C5A000585AD8 /* PVLibRetro-Build.xcconfig */; buildSettings = { ALLOW_TARGET_PLATFORM_SPECIALIZATION = YES; CLANG_ANALYZER_NONNULL = YES; @@ -4182,7 +4227,6 @@ HEADER_SEARCH_PATHS = ( "\"$(SRCROOT)/Sources/retro/libretro-common/include\"", "\"$(SRCROOT)/Sources/retro\"", - "$(inherited)", ); INFOPLIST_KEY_NSHumanReadableCopyright = "Copyright © 2024 Provenance Emu. All rights reserved."; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; @@ -4194,6 +4238,8 @@ MARKETING_VERSION = 1.0; MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; MTL_FAST_MATH = YES; + OTHER_CFLAGS = "$(inherited)"; + OTHER_LDFLAGS = "$(inherited)"; PRODUCT_BUNDLE_IDENTIFIER = "org.provenance-emu.PVLibRetro"; PRODUCT_NAME = PVLibRetro; PROVISIONING_PROFILE_SPECIFIER = ""; @@ -4207,6 +4253,7 @@ SWIFT_OPTIMIZATION_LEVEL = "-Onone"; SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = "1,2,3,4,6,7"; + USER_HEADER_SEARCH_PATHS = "\"$(SRCROOT)/Sources/retro/libretro-common/include\""; VERSIONING_SYSTEM = "apple-generic"; VERSION_INFO_PREFIX = ""; }; @@ -4214,6 +4261,7 @@ }; B392849328670348003BAC21 /* Release */ = { isa = XCBuildConfiguration; + baseConfigurationReference = B32299412878C5A000585AD8 /* PVLibRetro-Build.xcconfig */; buildSettings = { ALLOW_TARGET_PLATFORM_SPECIALIZATION = YES; CLANG_ANALYZER_NONNULL = YES; @@ -4249,7 +4297,6 @@ HEADER_SEARCH_PATHS = ( "\"$(SRCROOT)/Sources/retro/libretro-common/include\"", "\"$(SRCROOT)/Sources/retro\"", - "$(inherited)", ); INFOPLIST_KEY_NSHumanReadableCopyright = "Copyright © 2024 Provenance Emu. All rights reserved."; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; @@ -4261,6 +4308,8 @@ MARKETING_VERSION = 1.0; MTL_ENABLE_DEBUG_INFO = NO; MTL_FAST_MATH = YES; + OTHER_CFLAGS = "$(inherited)"; + OTHER_LDFLAGS = "$(inherited)"; PRODUCT_BUNDLE_IDENTIFIER = "org.provenance-emu.PVLibRetro"; PRODUCT_NAME = PVLibRetro; PROVISIONING_PROFILE_SPECIFIER = ""; @@ -4272,6 +4321,7 @@ SWIFT_EMIT_LOC_STRINGS = YES; SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = "1,2,3,4,6,7"; + USER_HEADER_SEARCH_PATHS = "\"$(SRCROOT)/Sources/retro/libretro-common/include\""; VERSIONING_SYSTEM = "apple-generic"; VERSION_INFO_PREFIX = ""; }; @@ -4279,6 +4329,7 @@ }; B392849428670348003BAC21 /* Archive */ = { isa = XCBuildConfiguration; + baseConfigurationReference = B32299412878C5A000585AD8 /* PVLibRetro-Build.xcconfig */; buildSettings = { ALLOW_TARGET_PLATFORM_SPECIALIZATION = YES; CLANG_ANALYZER_NONNULL = YES; @@ -4314,7 +4365,6 @@ HEADER_SEARCH_PATHS = ( "\"$(SRCROOT)/Sources/retro/libretro-common/include\"", "\"$(SRCROOT)/Sources/retro\"", - "$(inherited)", ); INFOPLIST_KEY_NSHumanReadableCopyright = "Copyright © 2024 Provenance Emu. All rights reserved."; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; @@ -4326,6 +4376,8 @@ MARKETING_VERSION = 1.0; MTL_ENABLE_DEBUG_INFO = NO; MTL_FAST_MATH = YES; + OTHER_CFLAGS = "$(inherited)"; + OTHER_LDFLAGS = "$(inherited)"; PRODUCT_BUNDLE_IDENTIFIER = "org.provenance-emu.PVLibRetro"; PRODUCT_NAME = PVLibRetro; PROVISIONING_PROFILE_SPECIFIER = ""; @@ -4337,6 +4389,7 @@ SWIFT_EMIT_LOC_STRINGS = YES; SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = "1,2,3,4,6,7"; + USER_HEADER_SEARCH_PATHS = "\"$(SRCROOT)/Sources/retro/libretro-common/include\""; VERSIONING_SYSTEM = "apple-generic"; VERSION_INFO_PREFIX = ""; }; @@ -4364,6 +4417,11 @@ GCC_NO_COMMON_BLOCKS = YES; GCC_PREFIX_HEADER = "Supporting Files/PVSupport-Prefix.pch"; GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + HEADER_SEARCH_PATHS = ( + "\"$(SRCROOT)/Sources/retro\"", + "\"$(SRCROOT)/Sources/retro/libretro-common/include\"", + "\"$(SRCROOT)/Sources/retro/libretro-common\"", + ); INFOPLIST_FILE = "Supporting Files/Info.plist"; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; IPHONEOS_DEPLOYMENT_TARGET = 13.0; @@ -4417,6 +4475,11 @@ GCC_NO_COMMON_BLOCKS = YES; GCC_PREFIX_HEADER = "Supporting Files/PVSupport-Prefix.pch"; GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + HEADER_SEARCH_PATHS = ( + "\"$(SRCROOT)/Sources/retro\"", + "\"$(SRCROOT)/Sources/retro/libretro-common/include\"", + "\"$(SRCROOT)/Sources/retro/libretro-common\"", + ); INFOPLIST_FILE = "Supporting Files/Info.plist"; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; IPHONEOS_DEPLOYMENT_TARGET = 13.0; diff --git a/PVSupport/Sources/PVLibRetro/PVLibRetroCore.m b/PVSupport/Sources/PVLibRetro/PVLibRetroCore.m index 223e96e7a4..f64aca2082 100644 --- a/PVSupport/Sources/PVLibRetro/PVLibRetroCore.m +++ b/PVSupport/Sources/PVLibRetro/PVLibRetroCore.m @@ -100,9 +100,9 @@ @interface PVLibRetroCore () static bool runloop_overrides_active = false; static bool runloop_game_options_active = false; //static core_option_manager_t *runloop_core_options = NULL; -#ifdef HAVE_THREADS -static slock_t *_runloop_msg_queue_lock = NULL; -#endif +//#ifdef HAVE_THREADS +//static slock_t *_runloop_msg_queue_lock = NULL; +//#endif //static msg_queue_t *runloop_msg_queue = NULL; // MARK: - Config @@ -1096,10 +1096,10 @@ bool runloop_ctl(enum runloop_ctl_state state, void *data) { // runloop_msg_queue_unlock(); break; case RUNLOOP_CTL_MSG_QUEUE_FREE: -#ifdef HAVE_THREADS - slock_free(_runloop_msg_queue_lock); - _runloop_msg_queue_lock = NULL; -#endif +//#ifdef HAVE_THREADS +// slock_free(_runloop_msg_queue_lock); +// _runloop_msg_queue_lock = NULL; +//#endif break; case RUNLOOP_CTL_MSG_QUEUE_CLEAR: // msg_queue_clear(runloop_msg_queue); @@ -1122,10 +1122,10 @@ bool runloop_ctl(enum runloop_ctl_state state, void *data) { // runloop_msg_queue = msg_queue_new(8); // retro_assert(runloop_msg_queue); -#ifdef HAVE_THREADS - _runloop_msg_queue_lock = slock_new(); - retro_assert(_runloop_msg_queue_lock); -#endif +//#ifdef HAVE_THREADS +// _runloop_msg_queue_lock = slock_new(); +// retro_assert(_runloop_msg_queue_lock); +//#endif break; case RUNLOOP_CTL_TASK_INIT: { diff --git a/PVSupport/Sources/retro/libretro-common/disk_control_interface.h b/PVSupport/Sources/retro/libretro-common/disk_control_interface.h new file mode 100644 index 0000000000..641332f2c4 --- /dev/null +++ b/PVSupport/Sources/retro/libretro-common/disk_control_interface.h @@ -0,0 +1,257 @@ +/* Copyright (C) 2010-2020 The RetroArch team + * + * --------------------------------------------------------------------------------------- + * The following license statement only applies to this file (disk_control_interface.h). + * --------------------------------------------------------------------------------------- + * + * 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. + */ + +#ifndef __DISK_CONTROL_INTERFACE_H +#define __DISK_CONTROL_INTERFACE_H + +#include +#include + +#include + +#include "disk_index_file.h" + +RETRO_BEGIN_DECLS + +/* Holds all objects to operate the disk + * control interface */ +typedef struct +{ + struct retro_disk_control_ext_callback cb; /* ptr alignment */ + disk_index_file_t index_record; /* unsigned alignment */ + unsigned initial_num_images; + bool record_enabled; +} disk_control_interface_t; + +/*****************/ +/* Configuration */ +/*****************/ + +/** + * disk_control_set_callback: + * + * Set v0 disk interface callback functions + **/ +void disk_control_set_callback( + disk_control_interface_t *disk_control, + const struct retro_disk_control_callback *cb); + +/** + * disk_control_set_ext_callback: + * + * Set v1+ disk interface callback functions + **/ +void disk_control_set_ext_callback( + disk_control_interface_t *disk_control, + const struct retro_disk_control_ext_callback *cb); + +/**********/ +/* Status */ +/**********/ + +/** + * disk_control_enabled: + * + * Leaf function. + * + * @return true if core supports basic disk control functionality + * - set_eject_state + * - get_eject_state + * - get_image_index + * - set_image_index + * - get_num_images + **/ +bool disk_control_enabled( + disk_control_interface_t *disk_control); + +/** + * disk_control_append_enabled: + * + * Leaf function. + * + * @return true if core supports disk append functionality + * - replace_image_index + * - add_image_index + **/ +bool disk_control_append_enabled( + disk_control_interface_t *disk_control); + +/** + * disk_control_image_label_enabled: + * + * Leaf function. + * + * @return true if core supports image labels + * - get_image_label + **/ +bool disk_control_image_label_enabled( + disk_control_interface_t *disk_control); + +/** + * disk_control_initial_image_enabled: + * + * Leaf function. + * + * @return true if core supports setting initial disk index + * - set_initial_image + * - get_image_path + **/ +bool disk_control_initial_image_enabled( + disk_control_interface_t *disk_control); + +/***********/ +/* Getters */ +/***********/ + +/** + * disk_control_get_eject_state: + * + * @return true if disk is currently ejected + **/ +bool disk_control_get_eject_state( + disk_control_interface_t *disk_control); + +/** + * disk_control_get_num_images: + * + * @return number of disk images registered by the core + **/ +unsigned disk_control_get_num_images( + disk_control_interface_t *disk_control); + +/** + * disk_control_get_image_index: + * + * @return currently selected disk image index + **/ +unsigned disk_control_get_image_index( + disk_control_interface_t *disk_control); + +/** + * disk_control_get_image_label: + * + * Fetches core-provided disk image label + * (label is set to an empty string if core + * does not support image labels) + **/ +void disk_control_get_image_label( + disk_control_interface_t *disk_control, + unsigned index, char *label, size_t len); + +/***********/ +/* Setters */ +/***********/ + +/** + * disk_control_set_eject_state: + * + * Sets the eject state of the virtual disk tray + **/ +bool disk_control_set_eject_state( + disk_control_interface_t *disk_control, + bool eject, bool verbosity); + +/** + * disk_control_set_index: + * + * Sets currently selected disk index + * + * NOTE: Will fail if disk is not currently ejected + **/ +bool disk_control_set_index( + disk_control_interface_t *disk_control, + unsigned index, bool verbosity); + +/** + * disk_control_set_index_next: + * + * Increments selected disk index + **/ +bool disk_control_set_index_next( + disk_control_interface_t *disk_control, + bool verbosity); + +/** + * disk_control_set_index_prev: + * + * Decrements selected disk index + **/ +bool disk_control_set_index_prev( + disk_control_interface_t *disk_control, + bool verbosity); + +/** + * disk_control_append_image: + * + * Appends specified image file to disk image list + **/ +bool disk_control_append_image( + disk_control_interface_t *disk_control, + const char *image_path); + +/*****************************/ +/* 'Initial index' functions */ +/*****************************/ + +/** + * disk_control_set_initial_index: + * + * Attempts to set current core's initial disk index. + * > disk_control->record_enabled will be set to + * 'false' if core does not support initial + * index functionality + * > disk_control->index_record will be loaded + * from file (if an existing record is found) + * NOTE: Must be called immediately before + * loading content + **/ +bool disk_control_set_initial_index( + disk_control_interface_t *disk_control, + const char *content_path, + const char *dir_savefile); + +/** + * disk_control_verify_initial_index: + * + * Checks that initial index has been set correctly + * and provides user notification. + * > Sets disk_control->initial_num_images if + * if functionality is supported by core + * NOTE: Must be called immediately after + * loading content + **/ +bool disk_control_verify_initial_index( + disk_control_interface_t *disk_control, + bool verbosity); + +/** + * disk_control_save_image_index: + * + * Saves current disk index to file, if supported + * by current core + **/ +bool disk_control_save_image_index( + disk_control_interface_t *disk_control); + +RETRO_END_DECLS + +#endif diff --git a/PVSupport/Sources/retro/libretro-common/disk_index_file.h b/PVSupport/Sources/retro/libretro-common/disk_index_file.h new file mode 100644 index 0000000000..e09e25fc20 --- /dev/null +++ b/PVSupport/Sources/retro/libretro-common/disk_index_file.h @@ -0,0 +1,76 @@ +/* Copyright (C) 2010-2020 The RetroArch team + * + * --------------------------------------------------------------------------------------- + * The following license statement only applies to this file (disk_index_file.h). + * --------------------------------------------------------------------------------------- + * + * 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. + */ + +#ifndef __DISK_INDEX_FILE_H +#define __DISK_INDEX_FILE_H + +#include +#include +#include + +#include + +RETRO_BEGIN_DECLS + +/* Holds all parameters required for recording + * the last disk image selected via the disk + * control interface */ +typedef struct +{ + unsigned image_index; + char image_path[PATH_MAX_LENGTH]; + char file_path[PATH_MAX_LENGTH]; + bool modified; +} disk_index_file_t; + +/******************/ +/* Initialisation */ +/******************/ + +/* Initialises existing disk index record, loading + * current parameters if a record file exists. + * Returns false if arguments are invalid. */ +bool disk_index_file_init( + disk_index_file_t *disk_index_file, + const char *content_path, + const char *dir_savefile); + +/***********/ +/* Setters */ +/***********/ + +/* Sets image index and path */ +void disk_index_file_set( + disk_index_file_t *disk_index_file, + unsigned image_index, + const char *image_path); + +/**********/ +/* Saving */ +/**********/ + +/* Saves specified disk index file to disk */ +bool disk_index_file_save(disk_index_file_t *disk_index_file); + +RETRO_END_DECLS + +#endif diff --git a/PVSupport/Sources/retro/libretro-common/file/archive_file.c b/PVSupport/Sources/retro/libretro-common/file/archive_file.c index 4d25b5f649..57164a6a31 100644 --- a/PVSupport/Sources/retro/libretro-common/file/archive_file.c +++ b/PVSupport/Sources/retro/libretro-common/file/archive_file.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2010-2016 The RetroArch team +/* Copyright (C) 2010-2020 The RetroArch team * * --------------------------------------------------------------------------------------- * The following license statement only applies to this file (archive_file.c). @@ -20,180 +20,24 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ - #include #include #include -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif - -#ifdef HAVE_MMAP -#include -#include -#include - -#include -#include -#endif - #include #include #include #include -#include #include #include - -#ifndef CENTRAL_FILE_HEADER_SIGNATURE -#define CENTRAL_FILE_HEADER_SIGNATURE 0x02014b50 -#endif - -#ifndef END_OF_CENTRAL_DIR_SIGNATURE -#define END_OF_CENTRAL_DIR_SIGNATURE 0x06054b50 -#endif - -struct zip_extract_userdata -{ - char *zip_path; - char *first_extracted_file_path; - const char *extraction_directory; - size_t zip_path_size; - struct string_list *ext; - bool found_content; -}; - -enum file_archive_compression_mode -{ - ZLIB_MODE_UNCOMPRESSED = 0, - ZLIB_MODE_DEFLATE = 8 -}; - -typedef struct -{ -#ifdef HAVE_MMAP - int fd; -#endif - void *data; - size_t size; -} file_archive_file_data_t; +#include #ifdef HAVE_MMAP -/* Closes, unmaps and frees. */ -static void file_archive_free(void *handle) -{ - file_archive_file_data_t *data = (file_archive_file_data_t*)handle; - - if (!data) - return; - - if (data->data) - munmap(data->data, data->size); - if (data->fd >= 0) - close(data->fd); - free(data); -} - -static const uint8_t *file_archive_data(void *handle) -{ - file_archive_file_data_t *data = (file_archive_file_data_t*)handle; - if (!data) - return NULL; - return (const uint8_t*)data->data; -} - -static size_t file_archive_size(void *handle) -{ - file_archive_file_data_t *data = (file_archive_file_data_t*)handle; - if (!data) - return 0; - return data->size; -} - -static void *file_archive_open(const char *path) -{ - file_archive_file_data_t *data = (file_archive_file_data_t*)calloc(1, sizeof(*data)); - - if (!data) - return NULL; - - data->fd = open(path, O_RDONLY); - - /* Failed to open archive. */ - if (data->fd < 0) - goto error; - - data->size = path_get_size(path); - if (!data->size) - return data; - - data->data = mmap(NULL, data->size, PROT_READ, MAP_SHARED, data->fd, 0); - if (data->data == MAP_FAILED) - { - data->data = NULL; - - /* Failed to mmap() file */ - goto error; - } - - return data; - -error: - file_archive_free(data); - return NULL; -} -#else - -/* Closes, unmaps and frees. */ -static void file_archive_free(void *handle) -{ - file_archive_file_data_t *data = (file_archive_file_data_t*)handle; - if (!data) - return; - free(data->data); - free(data); -} - -static const uint8_t *file_archive_data(void *handle) -{ - file_archive_file_data_t *data = (file_archive_file_data_t*)handle; - if (!data) - return NULL; - return (const uint8_t*)data->data; -} - -static size_t file_archive_size(void *handle) -{ - file_archive_file_data_t *data = (file_archive_file_data_t*)handle; - if (!data) - return 0; - return data->size; -} - -static void *file_archive_open(const char *path) -{ - ssize_t ret = -1; - bool read_from_file = false; - file_archive_file_data_t *data = (file_archive_file_data_t*) - calloc(1, sizeof(*data)); - - if (!data) - return NULL; - - read_from_file = filestream_read_file(path, &data->data, &ret); - - /* Failed to open archive? */ - if (!read_from_file || ret < 0) - goto error; - - data->size = ret; - return data; - -error: - file_archive_free(data); - return NULL; -} +#include +#include +#include +#include +#include #endif static int file_archive_get_file_list_cb( @@ -204,256 +48,191 @@ static int file_archive_get_file_list_cb( uint32_t csize, uint32_t size, uint32_t checksum, - void *userdata) + struct archive_extract_userdata *userdata) { union string_list_elem_attr attr; - struct string_list *ext_list = NULL; - const char *file_ext = NULL; - struct string_list *list = (struct string_list*)userdata; - - (void)cdata; - (void)cmode; - (void)csize; - (void)size; - (void)checksum; - - memset(&attr, 0, sizeof(attr)); + attr.i = 0; if (valid_exts) - ext_list = string_split(valid_exts, "|"); - - if (ext_list) { + size_t path_len = strlen(path); /* Checks if this entry is a directory or a file. */ - char last_char = path[strlen(path)-1]; + char last_char = path[path_len - 1]; + struct string_list ext_list = {0}; /* Skip if directory. */ if (last_char == '/' || last_char == '\\' ) - goto error; + return 0; + + string_list_initialize(&ext_list); + if (string_split_noalloc(&ext_list, valid_exts, "|")) + { + const char *file_ext = path_get_extension(path); - file_ext = path_get_extension(path); + if (!file_ext) + { + string_list_deinitialize(&ext_list); + return 0; + } - if (!file_ext || - !string_list_find_elem_prefix(ext_list, ".", file_ext)) - goto error; + if (!string_list_find_elem_prefix(&ext_list, ".", file_ext)) + { + /* keep iterating */ + string_list_deinitialize(&ext_list); + return -1; + } - attr.i = RARCH_COMPRESSED_FILE_IN_ARCHIVE; - string_list_free(ext_list); + attr.i = RARCH_COMPRESSED_FILE_IN_ARCHIVE; + } + + string_list_deinitialize(&ext_list); } - return string_list_append(list, path, attr); - -error: - string_list_free(ext_list); - return 0; + return string_list_append(userdata->list, path, attr); } static int file_archive_extract_cb(const char *name, const char *valid_exts, const uint8_t *cdata, unsigned cmode, uint32_t csize, uint32_t size, - uint32_t checksum, void *userdata) + uint32_t checksum, struct archive_extract_userdata *userdata) { const char *ext = path_get_extension(name); - struct zip_extract_userdata *data = (struct zip_extract_userdata*)userdata; - /* Extract first content that matches our list. */ - if (ext && string_list_find_elem(data->ext, ext)) + /* Extract first file that matches our list. */ + if (ext && string_list_find_elem(userdata->ext, ext)) { - char new_path[PATH_MAX_LENGTH] = {0}; + char new_path[PATH_MAX_LENGTH]; + const char *delim; + + delim = path_get_archive_delim(userdata->archive_path); + + if (delim) + { + if (!string_is_equal_noncase(userdata->current_file_path, delim + 1)) + return 1; /* keep searching for the right file */ + } - if (data->extraction_directory) - fill_pathname_join(new_path, data->extraction_directory, + new_path[0] = '\0'; + if (userdata->extraction_directory) + fill_pathname_join(new_path, userdata->extraction_directory, path_basename(name), sizeof(new_path)); else - fill_pathname_resolve_relative(new_path, data->zip_path, + fill_pathname_resolve_relative(new_path, userdata->archive_path, path_basename(name), sizeof(new_path)); - data->first_extracted_file_path = strdup(new_path); - data->found_content = file_archive_perform_mode(new_path, - valid_exts, cdata, cmode, csize, size, - 0, NULL); - return 0; - } - - return 1; -} - -static uint32_t read_le(const uint8_t *data, unsigned size) -{ - unsigned i; - uint32_t val = 0; - - size *= 8; - for (i = 0; i < size; i += 8) - val |= (uint32_t)*data++ << i; - - return val; -} -static int file_archive_parse_file_iterate_step_internal( - file_archive_transfer_t *state, char *filename, - const uint8_t **cdata, - unsigned *cmode, uint32_t *size, uint32_t *csize, - uint32_t *checksum, unsigned *payback) -{ - uint32_t offset; - uint32_t namelength, extralength, commentlength, - offsetNL, offsetEL; - uint32_t signature = read_le(state->directory + 0, 4); + if (file_archive_perform_mode(new_path, + valid_exts, cdata, cmode, csize, size, + checksum, userdata)) + { + userdata->found_file = true; + userdata->first_extracted_file_path = strdup(new_path); + } - if (signature != CENTRAL_FILE_HEADER_SIGNATURE) return 0; - - *cmode = read_le(state->directory + 10, 2); - *checksum = read_le(state->directory + 16, 4); - *csize = read_le(state->directory + 20, 4); - *size = read_le(state->directory + 24, 4); - - namelength = read_le(state->directory + 28, 2); - extralength = read_le(state->directory + 30, 2); - commentlength = read_le(state->directory + 32, 2); - - if (namelength >= PATH_MAX_LENGTH) - return -1; - - memcpy(filename, state->directory + 46, namelength); - - offset = read_le(state->directory + 42, 4); - offsetNL = read_le(state->data + offset + 26, 2); - offsetEL = read_le(state->data + offset + 28, 2); - - *cdata = state->data + offset + 30 + offsetNL + offsetEL; - - *payback = 46 + namelength + extralength + commentlength; + } return 1; } -static int file_archive_parse_file_iterate_step(file_archive_transfer_t *state, - const char *valid_exts, void *userdata, file_archive_file_cb file_cb) +static int file_archive_parse_file_init(file_archive_transfer_t *state, + const char *file) { - const uint8_t *cdata = NULL; - uint32_t checksum = 0; - uint32_t size = 0; - uint32_t csize = 0; - unsigned cmode = 0; - unsigned payload = 0; - char filename[PATH_MAX_LENGTH] = {0}; - int ret = file_archive_parse_file_iterate_step_internal(state, filename, - &cdata, &cmode, &size, &csize, - &checksum, &payload); - - if (ret != 1) - return ret; - -#if 0 - VLOG(@"OFFSET: %u, CSIZE: %u, SIZE: %u.\n", offset + 30 + - offsetNL + offsetEL, csize, size); -#endif + char path[PATH_MAX_LENGTH]; + char *last = NULL; - if (!file_cb(filename, valid_exts, cdata, cmode, - csize, size, checksum, userdata)) - return 0; + path[0] = '\0'; - state->directory += payload; + strlcpy(path, file, sizeof(path)); - return 1; -} + last = (char*)path_get_archive_delim(path); -static int file_archive_parse_file_init(file_archive_transfer_t *state, - const char *file) -{ - state->backend = file_archive_get_default_file_backend(); + if (last) + *last = '\0'; + state->backend = file_archive_get_file_backend(path); if (!state->backend) return -1; - state->handle = file_archive_open(file); - if (!state->handle) - return -1; + state->archive_file = filestream_open(path, + RETRO_VFS_FILE_ACCESS_READ, + RETRO_VFS_FILE_ACCESS_HINT_NONE); - state->zip_size = file_archive_size(state->handle); - if (state->zip_size < 22) + /* Failed to open archive. */ + if (!state->archive_file) return -1; - state->data = file_archive_data(state->handle); - state->footer = state->data + state->zip_size - 22; + state->archive_size = filestream_get_size(state->archive_file); - for (;; state->footer--) +#ifdef HAVE_MMAP + if (state->archive_size <= (256*1024*1024)) { - if (state->footer <= state->data + 22) - return -1; - if (read_le(state->footer, 4) == END_OF_CENTRAL_DIR_SIGNATURE) + state->archive_mmap_fd = open(path, O_RDONLY); + if (state->archive_mmap_fd) { - unsigned comment_len = read_le(state->footer + 20, 2); - if (state->footer + 22 + comment_len == state->data + state->zip_size) - break; + state->archive_mmap_data = (uint8_t*)mmap(NULL, (size_t)state->archive_size, + PROT_READ, MAP_SHARED, state->archive_mmap_fd, 0); + + if (state->archive_mmap_data == (uint8_t*)MAP_FAILED) + { + close(state->archive_mmap_fd); + state->archive_mmap_fd = 0; + state->archive_mmap_data = NULL; + } } } +#endif - state->directory = state->data + read_le(state->footer + 16, 4); + state->step_current = 0; + state->step_total = 0; - return 0; + return state->backend->archive_parse_file_init(state, path); } /** * file_archive_decompress_data_to_file: * @path : filename path of archive. - * @valid_exts : Valid extensions of archive to be parsed. - * If NULL, allow all. - * @cdata : input data. - * @csize : size of input data. * @size : output file size * @checksum : CRC32 checksum from input data. * - * Decompress data to file. + * Write data to file. * * Returns: true (1) on success, otherwise false (0). **/ static int file_archive_decompress_data_to_file( + file_archive_transfer_t *transfer, file_archive_file_handle_t *handle, - int ret, const char *path, - const char *valid_exts, - const uint8_t *cdata, - uint32_t csize, uint32_t size, uint32_t checksum) { - if (handle) - { - handle->backend->stream_free(handle->stream); - free(handle->stream); - } - - if (!handle || ret == -1) - { - ret = 0; - goto end; - } - - handle->real_checksum = handle->backend->stream_crc_calculate( - 0, handle->data, size); + if (!handle) + return 0; #if 0 + handle->real_checksum = transfer->backend->stream_crc_calculate( + 0, handle->data, size); if (handle->real_checksum != checksum) { - /* File CRC difers from ZIP CRC. */ - printf("File CRC differs from ZIP CRC. File: 0x%x, ZIP: 0x%x.\n", + /* File CRC difers from archive CRC. */ + printf("File CRC differs from archive CRC. File: 0x%x, Archive: 0x%x.\n", (unsigned)handle->real_checksum, (unsigned)checksum); } #endif if (!filestream_write_file(path, handle->data, size)) - { - ret = false; - goto end; - } + return 0; -end: - if (handle && handle->data) - free(handle->data); - return ret; + return 1; +} + +void file_archive_parse_file_iterate_stop(file_archive_transfer_t *state) +{ + if (!state || !state->archive_file) + return; + + state->type = ARCHIVE_TRANSFER_DEINIT; + file_archive_parse_file_iterate(state, NULL, NULL, NULL, NULL, NULL); } int file_archive_parse_file_iterate( @@ -462,76 +241,114 @@ int file_archive_parse_file_iterate( const char *file, const char *valid_exts, file_archive_file_cb file_cb, - void *userdata) + struct archive_extract_userdata *userdata) { if (!state) return -1; switch (state->type) { - case ZLIB_TRANSFER_NONE: + case ARCHIVE_TRANSFER_NONE: break; - case ZLIB_TRANSFER_INIT: + case ARCHIVE_TRANSFER_INIT: if (file_archive_parse_file_init(state, file) == 0) - state->type = ZLIB_TRANSFER_ITERATE; + { + if (userdata) + { + userdata->transfer = state; + strlcpy(userdata->archive_path, file, + sizeof(userdata->archive_path)); + } + state->type = ARCHIVE_TRANSFER_ITERATE; + } else - state->type = ZLIB_TRANSFER_DEINIT_ERROR; + state->type = ARCHIVE_TRANSFER_DEINIT_ERROR; break; - case ZLIB_TRANSFER_ITERATE: + case ARCHIVE_TRANSFER_ITERATE: + if (state->backend) { - int ret = file_archive_parse_file_iterate_step(state, - valid_exts, userdata, file_cb); + int ret = state->backend->archive_parse_file_iterate_step( + state->context, valid_exts, userdata, file_cb); + + if (ret == 1) + state->step_current++; /* found another file */ if (ret != 1) - state->type = ZLIB_TRANSFER_DEINIT; + state->type = ARCHIVE_TRANSFER_DEINIT; if (ret == -1) - state->type = ZLIB_TRANSFER_DEINIT_ERROR; + state->type = ARCHIVE_TRANSFER_DEINIT_ERROR; + + /* early return to prevent deinit from never firing */ + return 0; } - break; - case ZLIB_TRANSFER_DEINIT_ERROR: + return -1; + case ARCHIVE_TRANSFER_DEINIT_ERROR: *returnerr = false; - case ZLIB_TRANSFER_DEINIT: - if (state->handle) - file_archive_free(state->handle); - state->handle = NULL; + case ARCHIVE_TRANSFER_DEINIT: + if (state->context) + { + if (state->backend->archive_parse_file_free) + state->backend->archive_parse_file_free(state->context); + state->context = NULL; + } + + if (state->archive_file) + { + filestream_close(state->archive_file); + state->archive_file = NULL; + } + +#ifdef HAVE_MMAP + if (state->archive_mmap_data) + { + munmap(state->archive_mmap_data, (size_t)state->archive_size); + close(state->archive_mmap_fd); + state->archive_mmap_fd = 0; + state->archive_mmap_data = NULL; + } +#endif + + if (userdata) + userdata->transfer = NULL; break; } - if (state->type == ZLIB_TRANSFER_DEINIT || - state->type == ZLIB_TRANSFER_DEINIT_ERROR) + if ( state->type == ARCHIVE_TRANSFER_DEINIT || + state->type == ARCHIVE_TRANSFER_DEINIT_ERROR) return -1; return 0; } -void file_archive_parse_file_iterate_stop(file_archive_transfer_t *state) -{ - if (!state || !state->handle) - return; - - state->type = ZLIB_TRANSFER_DEINIT; - file_archive_parse_file_iterate(state, NULL, NULL, NULL, NULL, NULL); -} - /** - * file_archive_parse_file: + * file_archive_walk: * @file : filename path of archive - * @valid_exts : Valid extensions of archive to be parsed. + * @valid_exts : Valid extensions of archive to be parsed. * If NULL, allow all. * @file_cb : file_cb function pointer * @userdata : userdata to pass to file_cb function pointer. * - * Low-level file parsing. Enumerates over all files and calls + * Low-level file parsing. Enumerates over all files and calls * file_cb with userdata. * * Returns: true (1) on success, otherwise false (0). **/ -static bool file_archive_parse_file(const char *file, const char *valid_exts, - file_archive_file_cb file_cb, void *userdata) +static bool file_archive_walk(const char *file, const char *valid_exts, + file_archive_file_cb file_cb, struct archive_extract_userdata *userdata) { - file_archive_transfer_t state = {0}; - bool returnerr = true; + file_archive_transfer_t state; + bool returnerr = true; - state.type = ZLIB_TRANSFER_INIT; + state.type = ARCHIVE_TRANSFER_INIT; + state.archive_file = NULL; +#ifdef HAVE_MMAP + state.archive_mmap_fd = 0; + state.archive_mmap_data = NULL; +#endif + state.archive_size = 0; + state.context = NULL; + state.step_total = 0; + state.step_current = 0; + state.backend = NULL; for (;;) { @@ -545,52 +362,53 @@ static bool file_archive_parse_file(const char *file, const char *valid_exts, int file_archive_parse_file_progress(file_archive_transfer_t *state) { - /* FIXME: this estimate is worse than before */ - ptrdiff_t delta = state->directory - state->data; - return delta * 100 / state->zip_size; + if (!state || state->step_total == 0) + return 0; + + return (int)((state->step_current * 100) / (state->step_total)); } /** - * file_archive_extract_first_content_file: - * @zip_path : filename path to ZIP archive. - * @zip_path_size : size of ZIP archive. - * @valid_exts : valid extensions for a content file. + * file_archive_extract_file: + * @archive_path : filename path to archive. + * @valid_exts : valid extensions for the file. * @extraction_directory : the directory to extract temporary - * unzipped content to. + * file to. * - * Extract first content file from archive. + * Extract file from archive. If no file inside the archive is + * specified, the first file found will be used. * * Returns : true (1) on success, otherwise false (0). **/ -bool file_archive_extract_first_content_file( - char *zip_path, - size_t zip_path_size, +bool file_archive_extract_file( + const char *archive_path, const char *valid_exts, const char *extraction_directory, char *out_path, size_t len) { - struct string_list *list = NULL; - bool ret = true; - struct zip_extract_userdata userdata = {0}; + struct archive_extract_userdata userdata; + bool ret = true; + struct string_list *list = string_split(valid_exts, "|"); + + userdata.archive_path[0] = '\0'; + userdata.current_file_path[0] = '\0'; + userdata.first_extracted_file_path = NULL; + userdata.extraction_directory = extraction_directory; + userdata.ext = list; + userdata.list = NULL; + userdata.found_file = false; + userdata.list_only = false; + userdata.crc = 0; + userdata.transfer = NULL; + userdata.dec = NULL; - /* We cannot unzip if the libretro - * implementation does not have any valid extensions. */ - if (!valid_exts) - return false; - - list = string_split(valid_exts, "|"); if (!list) { ret = false; goto end; } - userdata.zip_path = zip_path; - userdata.zip_path_size = zip_path_size; - userdata.extraction_directory = extraction_directory; - userdata.ext = list; - - if (!file_archive_parse_file(zip_path, valid_exts, + if (!file_archive_walk(archive_path, valid_exts, file_archive_extract_cb, &userdata)) { /* Parsing file archive failed. */ @@ -598,15 +416,15 @@ bool file_archive_extract_first_content_file( goto end; } - if (!userdata.found_content) + if (!userdata.found_file) { - /* Didn't find any content that matched valid extensions + /* Didn't find any file that matched valid extensions * for libretro implementation. */ ret = false; goto end; } - if (*userdata.first_extracted_file_path) + if (!string_is_empty(userdata.first_extracted_file_path)) strlcpy(out_path, userdata.first_extracted_file_path, len); end: @@ -617,6 +435,36 @@ bool file_archive_extract_first_content_file( return ret; } +/* Warning: 'list' must zero initialised before + * calling this function, otherwise memory leaks/ + * undefined behaviour will occur */ +bool file_archive_get_file_list_noalloc(struct string_list *list, + const char *path, + const char *valid_exts) +{ + struct archive_extract_userdata userdata; + + if (!list || !string_list_initialize(list)) + return false; + + strlcpy(userdata.archive_path, path, sizeof(userdata.archive_path)); + userdata.current_file_path[0] = '\0'; + userdata.first_extracted_file_path = NULL; + userdata.extraction_directory = NULL; + userdata.ext = NULL; + userdata.list = list; + userdata.found_file = false; + userdata.list_only = true; + userdata.crc = 0; + userdata.transfer = NULL; + userdata.dec = NULL; + + if (!file_archive_walk(path, valid_exts, + file_archive_get_file_list_cb, &userdata)) + return false; + return true; +} + /** * file_archive_get_file_list: * @path : filename path of archive @@ -626,66 +474,275 @@ bool file_archive_extract_first_content_file( struct string_list *file_archive_get_file_list(const char *path, const char *valid_exts) { + struct archive_extract_userdata userdata; + + strlcpy(userdata.archive_path, path, sizeof(userdata.archive_path)); + userdata.current_file_path[0] = '\0'; + userdata.first_extracted_file_path = NULL; + userdata.extraction_directory = NULL; + userdata.ext = NULL; + userdata.list = string_list_new(); + userdata.found_file = false; + userdata.list_only = true; + userdata.crc = 0; + userdata.transfer = NULL; + userdata.dec = NULL; + + if (!userdata.list) + return NULL; + if (!file_archive_walk(path, valid_exts, + file_archive_get_file_list_cb, &userdata)) + { + string_list_free(userdata.list); + return NULL; + } + return userdata.list; +} + +bool file_archive_perform_mode(const char *path, const char *valid_exts, + const uint8_t *cdata, unsigned cmode, uint32_t csize, uint32_t size, + uint32_t crc32, struct archive_extract_userdata *userdata) +{ + file_archive_file_handle_t handle; + int ret; + + if (!userdata->transfer || !userdata->transfer->backend) + return false; + + handle.data = NULL; + handle.real_checksum = 0; + + if (!userdata->transfer->backend->stream_decompress_data_to_file_init( + userdata->transfer->context, &handle, cdata, cmode, csize, size)) + return false; + + do + { + ret = userdata->transfer->backend->stream_decompress_data_to_file_iterate( + userdata->transfer->context, &handle); + }while (ret == 0); + + if (ret == -1 || !file_archive_decompress_data_to_file( + userdata->transfer, &handle, path, + size, crc32)) + return false; + + return true; +} + +/** + * file_archive_filename_split: + * @str : filename to turn into a string list + * + * Creates a new string list based on filename @path, delimited by a hash (#). + * + * Returns: new string list if successful, otherwise NULL. + */ +static struct string_list *file_archive_filename_split(const char *path) +{ + union string_list_elem_attr attr; struct string_list *list = string_list_new(); + const char *delim = path_get_archive_delim(path); - if (!list) - goto error; + attr.i = 0; + + if (delim) + { + /* add archive path to list first */ + if (!string_list_append_n(list, path, (unsigned)(delim - path), attr)) + goto error; - if (!file_archive_parse_file(path, valid_exts, - file_archive_get_file_list_cb, list)) - goto error; + /* now add the path within the archive */ + delim++; + + if (*delim) + { + if (!string_list_append(list, delim, attr)) + goto error; + } + } + else + if (!string_list_append(list, path, attr)) + goto error; return list; error: - if (list) - string_list_free(list); + string_list_free(list); return NULL; } -bool file_archive_perform_mode(const char *path, const char *valid_exts, - const uint8_t *cdata, unsigned cmode, uint32_t csize, uint32_t size, - uint32_t crc32, void *userdata) +/* Generic compressed file loader. + * Extracts to buf, unless optional_filename != 0 + * Then extracts to optional_filename and leaves buf alone. + */ +int file_archive_compressed_read( + const char * path, void **buf, + const char* optional_filename, int64_t *length) { - switch (cmode) + const struct + file_archive_file_backend *backend = NULL; + struct string_list *str_list = NULL; + + /* Safety check. + * If optional_filename and optional_filename + * exists, we simply return 0, + * hoping that optional_filename is the + * same as requested. + */ + if (optional_filename && path_is_valid(optional_filename)) { - case ZLIB_MODE_UNCOMPRESSED: - if (!filestream_write_file(path, cdata, size)) - goto error; - break; + *length = 0; + return 1; + } - case ZLIB_MODE_DEFLATE: - { - int ret = 0; - file_archive_file_handle_t handle = {0}; - handle.backend = file_archive_get_default_file_backend(); - - if (!handle.backend->stream_decompress_data_to_file_init(&handle, - cdata, csize, size)) - goto error; - - do{ - ret = handle.backend->stream_decompress_data_to_file_iterate( - handle.stream); - }while(ret == 0); - - if (!file_archive_decompress_data_to_file(&handle, - ret, path, valid_exts, - cdata, csize, size, crc32)) - goto error; - } - break; - default: - goto error; + str_list = file_archive_filename_split(path); + /* We assure that there is something after the '#' symbol. + * + * This error condition happens for example, when + * path = /path/to/file.7z, or + * path = /path/to/file.7z# + */ + if (str_list->size <= 1) + { + /* could not extract string and substring. */ + string_list_free(str_list); + *length = 0; + return 0; } - return true; + backend = file_archive_get_file_backend(str_list->elems[0].data); + *length = backend->compressed_file_read(str_list->elems[0].data, + str_list->elems[1].data, buf, optional_filename); -error: - return false; + string_list_free(str_list); + + if (*length != -1) + return 1; + + return 0; } -const struct file_archive_file_backend *file_archive_get_default_file_backend(void) +const struct file_archive_file_backend *file_archive_get_zlib_file_backend(void) { +#ifdef HAVE_ZLIB return &zlib_backend; +#else + return NULL; +#endif +} + +const struct file_archive_file_backend *file_archive_get_7z_file_backend(void) +{ +#ifdef HAVE_7ZIP + return &sevenzip_backend; +#else + return NULL; +#endif +} + +const struct file_archive_file_backend* file_archive_get_file_backend(const char *path) +{ +#if defined(HAVE_7ZIP) || defined(HAVE_ZLIB) + char newpath[PATH_MAX_LENGTH]; + const char *file_ext = NULL; + char *last = NULL; + + newpath[0] = '\0'; + + strlcpy(newpath, path, sizeof(newpath)); + + last = (char*)path_get_archive_delim(newpath); + + if (last) + *last = '\0'; + + file_ext = path_get_extension(newpath); + +#ifdef HAVE_7ZIP + if (string_is_equal_noncase(file_ext, "7z")) + return &sevenzip_backend; +#endif + +#ifdef HAVE_ZLIB + if ( string_is_equal_noncase(file_ext, "zip") + || string_is_equal_noncase(file_ext, "apk") + ) + return &zlib_backend; +#endif +#endif + + return NULL; +} + +/** + * file_archive_get_file_crc32: + * @path : filename path of archive + * + * Returns: CRC32 of the specified file in the archive, otherwise 0. + * If no path within the archive is specified, the first + * file found inside is used. + **/ +uint32_t file_archive_get_file_crc32(const char *path) +{ + file_archive_transfer_t state; + struct archive_extract_userdata userdata = {0}; + bool returnerr = false; + const char *archive_path = NULL; + bool contains_compressed = path_contains_compressed_file(path); + + if (contains_compressed) + { + archive_path = path_get_archive_delim(path); + + /* move pointer right after the delimiter to give us the path */ + if (archive_path) + archive_path += 1; + } + + state.type = ARCHIVE_TRANSFER_INIT; + state.archive_file = NULL; +#ifdef HAVE_MMAP + state.archive_mmap_fd = 0; + state.archive_mmap_data = NULL; +#endif + state.archive_size = 0; + state.context = NULL; + state.step_total = 0; + state.step_current = 0; + state.backend = NULL; + + /* Initialize and open archive first. + Sets next state type to ITERATE. */ + file_archive_parse_file_iterate(&state, + &returnerr, path, NULL, NULL, + &userdata); + + for (;;) + { + /* Now find the first file in the archive. */ + if (state.type == ARCHIVE_TRANSFER_ITERATE) + file_archive_parse_file_iterate(&state, + &returnerr, path, NULL, NULL, + &userdata); + + /* If no path specified within archive, stop after + * finding the first file. + */ + if (!contains_compressed) + break; + + /* Stop when the right file in the archive is found. */ + if (archive_path) + { + if (string_is_equal(userdata.current_file_path, archive_path)) + break; + } + else + break; + } + + file_archive_parse_file_iterate_stop(&state); + + return userdata.crc; } diff --git a/PVSupport/Sources/retro/libretro-common/file/archive_file_7z.c b/PVSupport/Sources/retro/libretro-common/file/archive_file_7z.c new file mode 100644 index 0000000000..c0aa7ad0ff --- /dev/null +++ b/PVSupport/Sources/retro/libretro-common/file/archive_file_7z.c @@ -0,0 +1,526 @@ +/* Copyright (C) 2010-2020 The RetroArch team + * + * --------------------------------------------------------------------------------------- + * The following license statement only applies to this file (archive_file_sevenzip.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 +#include <7zip/7z.h> +#include <7zip/7zCrc.h> +#include <7zip/7zFile.h> + +#define SEVENZIP_MAGIC "7z\xBC\xAF\x27\x1C" +#define SEVENZIP_MAGIC_LEN 6 +#define SEVENZIP_LOOKTOREAD_BUF_SIZE (1 << 14) + +/* Assume W-functions do not work below Win2K and Xbox platforms */ +#if defined(_WIN32_WINNT) && _WIN32_WINNT < 0x0500 || defined(_XBOX) +#ifndef LEGACY_WIN32 +#define LEGACY_WIN32 +#endif +#endif + +struct sevenzip_context_t +{ + uint8_t *output; + CFileInStream archiveStream; + CLookToRead2 lookStream; + ISzAlloc allocImp; + ISzAlloc allocTempImp; + CSzArEx db; + size_t temp_size; + uint32_t parse_index; + uint32_t decompress_index; + uint32_t packIndex; + uint32_t block_index; +}; + +static void *sevenzip_stream_alloc_impl(ISzAllocPtr p, size_t size) +{ + if (size == 0) + return 0; + return malloc(size); +} + +static void sevenzip_stream_free_impl(ISzAllocPtr p, void *address) +{ + (void)p; + + if (address) + free(address); +} + +static void *sevenzip_stream_alloc_tmp_impl(ISzAllocPtr p, size_t size) +{ + (void)p; + if (size == 0) + return 0; + return malloc(size); +} + +static void* sevenzip_stream_new(void) +{ + struct sevenzip_context_t *sevenzip_context = + (struct sevenzip_context_t*)calloc(1, sizeof(struct sevenzip_context_t)); + + /* These are the allocation routines - currently using + * the non-standard 7zip choices. */ + sevenzip_context->allocImp.Alloc = sevenzip_stream_alloc_impl; + sevenzip_context->allocImp.Free = sevenzip_stream_free_impl; + sevenzip_context->allocTempImp.Alloc = sevenzip_stream_alloc_tmp_impl; + sevenzip_context->allocTempImp.Free = sevenzip_stream_free_impl; + sevenzip_context->block_index = 0xFFFFFFFF; + sevenzip_context->output = NULL; + + sevenzip_context->lookStream.bufSize = SEVENZIP_LOOKTOREAD_BUF_SIZE * sizeof(Byte); + sevenzip_context->lookStream.buf = (Byte*)malloc(sevenzip_context->lookStream.bufSize); + + if (!sevenzip_context->lookStream.buf) + sevenzip_context->lookStream.bufSize = 0; + + return sevenzip_context; +} + +static void sevenzip_parse_file_free(void *context) +{ + struct sevenzip_context_t *sevenzip_context = (struct sevenzip_context_t*)context; + + if (!sevenzip_context) + return; + + if (sevenzip_context->output) + { + IAlloc_Free(&sevenzip_context->allocImp, sevenzip_context->output); + sevenzip_context->output = NULL; + } + + SzArEx_Free(&sevenzip_context->db, &sevenzip_context->allocImp); + File_Close(&sevenzip_context->archiveStream.file); + + if (sevenzip_context->lookStream.buf) + free(sevenzip_context->lookStream.buf); + + free(sevenzip_context); +} + +/* Extract the relative path (needle) from a 7z archive + * (path) and allocate a buf for it to write it in. + * If optional_outfile is set, extract to that instead + * and don't allocate buffer. + */ +static int64_t sevenzip_file_read( + const char *path, + const char *needle, void **buf, + const char *optional_outfile) +{ + CFileInStream archiveStream; + CLookToRead2 lookStream; + ISzAlloc allocImp; + ISzAlloc allocTempImp; + CSzArEx db; + uint8_t *output = 0; + int64_t outsize = -1; + + /*These are the allocation routines. + * Currently using the non-standard 7zip choices. */ + allocImp.Alloc = sevenzip_stream_alloc_impl; + allocImp.Free = sevenzip_stream_free_impl; + allocTempImp.Alloc = sevenzip_stream_alloc_tmp_impl; + allocTempImp.Free = sevenzip_stream_free_impl; + + lookStream.bufSize = SEVENZIP_LOOKTOREAD_BUF_SIZE * sizeof(Byte); + lookStream.buf = (Byte*)malloc(lookStream.bufSize); + + if (!lookStream.buf) + lookStream.bufSize = 0; + +#if defined(_WIN32) && defined(USE_WINDOWS_FILE) && !defined(LEGACY_WIN32) + if (!string_is_empty(path)) + { + wchar_t *pathW = utf8_to_utf16_string_alloc(path); + + if (pathW) + { + /* Could not open 7zip archive? */ + if (InFile_OpenW(&archiveStream.file, pathW)) + { + free(pathW); + return -1; + } + + free(pathW); + } + } +#else + /* Could not open 7zip archive? */ + if (InFile_Open(&archiveStream.file, path)) + return -1; +#endif + + FileInStream_CreateVTable(&archiveStream); + LookToRead2_CreateVTable(&lookStream, false); + lookStream.realStream = &archiveStream.vt; + LookToRead2_Init(&lookStream); + CrcGenerateTable(); + + memset(&db, 0, sizeof(db)); + + SzArEx_Init(&db); + + if (SzArEx_Open(&db, &lookStream.vt, &allocImp, &allocTempImp) == SZ_OK) + { + uint32_t i; + bool file_found = false; + uint16_t *temp = NULL; + size_t temp_size = 0; + uint32_t block_index = 0xFFFFFFFF; + SRes res = SZ_OK; + + for (i = 0; i < db.NumFiles; i++) + { + size_t len; + char infile[PATH_MAX_LENGTH]; + size_t offset = 0; + size_t outSizeProcessed = 0; + + /* We skip over everything which is not a directory. + * FIXME: Why continue then if IsDir is true?*/ + if (SzArEx_IsDir(&db, i)) + continue; + + len = SzArEx_GetFileNameUtf16(&db, i, NULL); + + if (len > temp_size) + { + if (temp) + free(temp); + temp_size = len; + temp = (uint16_t *)malloc(temp_size * sizeof(temp[0])); + + if (temp == 0) + { + res = SZ_ERROR_MEM; + break; + } + } + + SzArEx_GetFileNameUtf16(&db, i, temp); + res = SZ_ERROR_FAIL; + infile[0] = '\0'; + + if (temp) + res = utf16_to_char_string(temp, infile, sizeof(infile)) + ? SZ_OK : SZ_ERROR_FAIL; + + if (string_is_equal(infile, needle)) + { + size_t output_size = 0; + + /* C LZMA SDK does not support chunked extraction - see here: + * sourceforge.net/p/sevenzip/discussion/45798/thread/6fb59aaf/ + * */ + file_found = true; + res = SzArEx_Extract(&db, &lookStream.vt, i, &block_index, + &output, &output_size, &offset, &outSizeProcessed, + &allocImp, &allocTempImp); + + if (res != SZ_OK) + break; /* This goes to the error section. */ + + outsize = (int64_t)outSizeProcessed; + + if (optional_outfile) + { + const void *ptr = (const void*)(output + offset); + + if (!filestream_write_file(optional_outfile, ptr, outsize)) + { + res = SZ_OK; + file_found = true; + outsize = -1; + } + } + else + { + /*We could either use the 7Zip allocated buffer, + * or create our own and use it. + * We would however need to realloc anyways, because RetroArch + * expects a \0 at the end, therefore we allocate new, + * copy and free the old one. */ + *buf = malloc((size_t)(outsize + 1)); + ((char*)(*buf))[outsize] = '\0'; + memcpy(*buf,output + offset,outsize); + } + break; + } + } + + if (temp) + free(temp); + IAlloc_Free(&allocImp, output); + + if (!(file_found && res == SZ_OK)) + { + /* Error handling + * + * Failed to open compressed file inside 7zip archive. + */ + + outsize = -1; + } + } + + SzArEx_Free(&db, &allocImp); + File_Close(&archiveStream.file); + + if (lookStream.buf) + free(lookStream.buf); + + return outsize; +} + +static bool sevenzip_stream_decompress_data_to_file_init( + void *context, file_archive_file_handle_t *handle, + const uint8_t *cdata, unsigned cmode, uint32_t csize, uint32_t size) +{ + struct sevenzip_context_t *sevenzip_context = + (struct sevenzip_context_t*)context; + + if (!sevenzip_context) + return false; + + sevenzip_context->decompress_index = (uint32_t)(size_t)cdata; + + return true; +} + +static int sevenzip_stream_decompress_data_to_file_iterate( + void *context, file_archive_file_handle_t *handle) +{ + struct sevenzip_context_t *sevenzip_context = + (struct sevenzip_context_t*)context; + + SRes res = SZ_ERROR_FAIL; + size_t output_size = 0; + size_t offset = 0; + size_t outSizeProcessed = 0; + + res = SzArEx_Extract(&sevenzip_context->db, + &sevenzip_context->lookStream.vt, sevenzip_context->decompress_index, + &sevenzip_context->block_index, &sevenzip_context->output, + &output_size, &offset, &outSizeProcessed, + &sevenzip_context->allocImp, &sevenzip_context->allocTempImp); + + if (res != SZ_OK) + return 0; + + if (handle) + handle->data = sevenzip_context->output + offset; + + return 1; +} + +static int sevenzip_parse_file_init(file_archive_transfer_t *state, + const char *file) +{ + uint8_t magic_buf[SEVENZIP_MAGIC_LEN]; + struct sevenzip_context_t *sevenzip_context = NULL; + + if (state->archive_size < SEVENZIP_MAGIC_LEN) + goto error; + + filestream_seek(state->archive_file, 0, SEEK_SET); + if (filestream_read(state->archive_file, magic_buf, SEVENZIP_MAGIC_LEN) != SEVENZIP_MAGIC_LEN) + goto error; + + if (string_is_not_equal_fast(magic_buf, SEVENZIP_MAGIC, SEVENZIP_MAGIC_LEN)) + goto error; + + sevenzip_context = (struct sevenzip_context_t*)sevenzip_stream_new(); + state->context = sevenzip_context; + +#if defined(_WIN32) && defined(USE_WINDOWS_FILE) && !defined(LEGACY_WIN32) + if (!string_is_empty(file)) + { + wchar_t *fileW = utf8_to_utf16_string_alloc(file); + + if (fileW) + { + /* could not open 7zip archive? */ + if (InFile_OpenW(&sevenzip_context->archiveStream.file, fileW)) + { + free(fileW); + goto error; + } + + free(fileW); + } + } +#else + /* could not open 7zip archive? */ + if (InFile_Open(&sevenzip_context->archiveStream.file, file)) + goto error; +#endif + + FileInStream_CreateVTable(&sevenzip_context->archiveStream); + LookToRead2_CreateVTable(&sevenzip_context->lookStream, false); + sevenzip_context->lookStream.realStream = &sevenzip_context->archiveStream.vt; + LookToRead2_Init(&sevenzip_context->lookStream); + CrcGenerateTable(); + SzArEx_Init(&sevenzip_context->db); + + if (SzArEx_Open(&sevenzip_context->db, &sevenzip_context->lookStream.vt, + &sevenzip_context->allocImp, &sevenzip_context->allocTempImp) != SZ_OK) + goto error; + + state->step_total = sevenzip_context->db.NumFiles; + + return 0; + +error: + if (sevenzip_context) + sevenzip_parse_file_free(sevenzip_context); + return -1; +} + +static int sevenzip_parse_file_iterate_step_internal( + struct sevenzip_context_t *sevenzip_context, char *filename, + const uint8_t **cdata, unsigned *cmode, + uint32_t *size, uint32_t *csize, uint32_t *checksum, + unsigned *payback, struct archive_extract_userdata *userdata) +{ + if (sevenzip_context->parse_index < sevenzip_context->db.NumFiles) + { + size_t len = SzArEx_GetFileNameUtf16(&sevenzip_context->db, + sevenzip_context->parse_index, NULL); + uint64_t compressed_size = 0; + + if (sevenzip_context->packIndex < sevenzip_context->db.db.NumPackStreams) + { + compressed_size = sevenzip_context->db.db.PackPositions[sevenzip_context->packIndex + 1] - + sevenzip_context->db.db.PackPositions[sevenzip_context->packIndex]; + + sevenzip_context->packIndex++; + } + + if (len < PATH_MAX_LENGTH && + !SzArEx_IsDir(&sevenzip_context->db, sevenzip_context->parse_index)) + { + char infile[PATH_MAX_LENGTH]; + SRes res = SZ_ERROR_FAIL; + uint16_t *temp = (uint16_t*)malloc(len * sizeof(uint16_t)); + + if (!temp) + return -1; + + infile[0] = '\0'; + + SzArEx_GetFileNameUtf16(&sevenzip_context->db, sevenzip_context->parse_index, + temp); + + if (temp) + { + res = utf16_to_char_string(temp, infile, sizeof(infile)) + ? SZ_OK : SZ_ERROR_FAIL; + free(temp); + } + + if (res != SZ_OK) + return -1; + + strlcpy(filename, infile, PATH_MAX_LENGTH); + + *cmode = 0; /* unused for 7zip */ + *checksum = sevenzip_context->db.CRCs.Vals[sevenzip_context->parse_index]; + *size = (uint32_t)SzArEx_GetFileSize(&sevenzip_context->db, sevenzip_context->parse_index); + *csize = (uint32_t)compressed_size; + + *cdata = (uint8_t *)(size_t)sevenzip_context->parse_index; + } + } + else + return 0; + + *payback = 1; + + return 1; +} + +static int sevenzip_parse_file_iterate_step(void *context, + const char *valid_exts, + struct archive_extract_userdata *userdata, file_archive_file_cb file_cb) +{ + const uint8_t *cdata = NULL; + uint32_t checksum = 0; + uint32_t size = 0; + uint32_t csize = 0; + unsigned cmode = 0; + unsigned payload = 0; + struct sevenzip_context_t *sevenzip_context = (struct sevenzip_context_t*)context; + int ret; + + userdata->current_file_path[0] = '\0'; + + ret = sevenzip_parse_file_iterate_step_internal(sevenzip_context, + userdata->current_file_path, + &cdata, &cmode, &size, &csize, + &checksum, &payload, userdata); + + if (ret != 1) + return ret; + + userdata->crc = checksum; + + if (file_cb && !file_cb(userdata->current_file_path, valid_exts, + cdata, cmode, + csize, size, checksum, userdata)) + return 0; + + sevenzip_context->parse_index += payload; + + return 1; +} + +static uint32_t sevenzip_stream_crc32_calculate(uint32_t crc, + const uint8_t *data, size_t length) +{ + return encoding_crc32(crc, data, length); +} + +const struct file_archive_file_backend sevenzip_backend = { + sevenzip_parse_file_init, + sevenzip_parse_file_iterate_step, + sevenzip_parse_file_free, + sevenzip_stream_decompress_data_to_file_init, + sevenzip_stream_decompress_data_to_file_iterate, + sevenzip_stream_crc32_calculate, + sevenzip_file_read, + "7z" +}; diff --git a/PVSupport/Sources/retro/libretro-common/file/archive_file_zlib.c b/PVSupport/Sources/retro/libretro-common/file/archive_file_zlib.c index fd9fb281bc..4b9c4a94c8 100644 --- a/PVSupport/Sources/retro/libretro-common/file/archive_file_zlib.c +++ b/PVSupport/Sources/retro/libretro-common/file/archive_file_zlib.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2010-2016 The RetroArch team +/* Copyright (C) 2010-2020 The RetroArch team * * --------------------------------------------------------------------------------------- * The following license statement only applies to this file (archive_file_zlib.c). @@ -21,196 +21,551 @@ */ #include +#include -#include #include #include +#include +#include +#include +#include -static void *zlib_stream_new(void) -{ - return (z_stream*)calloc(1, sizeof(z_stream)); -} +/* Only for MAX_WBITS */ +#include -static void zlib_stream_free(void *data) -{ - z_stream *ret = (z_stream*)data; - if (ret) - inflateEnd(ret); -} +#ifndef CENTRAL_FILE_HEADER_SIGNATURE +#define CENTRAL_FILE_HEADER_SIGNATURE 0x02014b50 +#endif + +#ifndef END_OF_CENTRAL_DIR_SIGNATURE +#define END_OF_CENTRAL_DIR_SIGNATURE 0x06054b50 +#endif -static void zlib_stream_set(void *data, - uint32_t avail_in, - uint32_t avail_out, - const uint8_t *next_in, - uint8_t *next_out - ) +enum file_archive_compression_mode { - z_stream *stream = (z_stream*)data; + ZIP_MODE_STORED = 0, + ZIP_MODE_DEFLATED = 8 +}; - if (!stream) - return; +typedef struct +{ + struct file_archive_transfer *state; + uint8_t *directory; + uint8_t *directory_entry; + uint8_t *directory_end; + void *current_stream; + uint8_t *compressed_data; + uint8_t *decompressed_data; +} zip_context_t; + +static INLINE uint32_t read_le(const uint8_t *data, unsigned size) +{ + unsigned i; + uint32_t val = 0; - stream->avail_in = avail_in; - stream->avail_out = avail_out; + size *= 8; + for (i = 0; i < size; i += 8) + val |= (uint32_t)*data++ << i; - stream->next_in = (uint8_t*)next_in; - stream->next_out = next_out; + return val; } -static uint32_t zlib_stream_get_avail_in(void *data) +static void zip_context_free_stream( + zip_context_t *zip_context, bool keep_decompressed) { - z_stream *stream = (z_stream*)data; - - if (!stream) - return 0; - - return stream->avail_in; + if (zip_context->current_stream) + { + zlib_inflate_backend.stream_free(zip_context->current_stream); + zip_context->current_stream = NULL; + } + if (zip_context->compressed_data) + { +#ifdef HAVE_MMAP + if (!zip_context->state->archive_mmap_data) +#endif + { + free(zip_context->compressed_data); + zip_context->compressed_data = NULL; + } + } + if (zip_context->decompressed_data && !keep_decompressed) + { + free(zip_context->decompressed_data); + zip_context->decompressed_data = NULL; + } } -static uint32_t zlib_stream_get_avail_out(void *data) +static bool zlib_stream_decompress_data_to_file_init( + void *context, file_archive_file_handle_t *handle, + const uint8_t *cdata, unsigned cmode, uint32_t csize, uint32_t size) { - z_stream *stream = (z_stream*)data; - - if (!stream) - return 0; + zip_context_t *zip_context = (zip_context_t *)context; + struct file_archive_transfer *state = zip_context->state; + uint8_t local_header_buf[4]; + uint8_t *local_header; + uint32_t offsetNL, offsetEL; + int64_t offsetData; + + /* free previous data and stream if left unfinished */ + zip_context_free_stream(zip_context, false); + + /* seek past most of the local directory header */ +#ifdef HAVE_MMAP + if (state->archive_mmap_data) + { + local_header = state->archive_mmap_data + (size_t)cdata + 26; + } + else +#endif + { + filestream_seek(state->archive_file, (int64_t)(size_t)cdata + 26, RETRO_VFS_SEEK_POSITION_START); + if (filestream_read(state->archive_file, local_header_buf, 4) != 4) + goto error; + local_header = local_header_buf; + } + + offsetNL = read_le(local_header, 2); /* file name length */ + offsetEL = read_le(local_header + 2, 2); /* extra field length */ + offsetData = (int64_t)(size_t)cdata + 26 + 4 + offsetNL + offsetEL; + +#ifdef HAVE_MMAP + if (state->archive_mmap_data) + { + zip_context->compressed_data = state->archive_mmap_data + (size_t)offsetData; + } + else +#endif + { + /* allocate memory for the compressed data */ + zip_context->compressed_data = (uint8_t*)malloc(csize); + if (!zip_context->compressed_data) + goto error; + + /* skip over name and extra data */ + filestream_seek(state->archive_file, offsetData, RETRO_VFS_SEEK_POSITION_START); + if (filestream_read(state->archive_file, zip_context->compressed_data, csize) != csize) + goto error; + } + + switch (cmode) + { + case ZIP_MODE_STORED: + handle->data = zip_context->compressed_data; + return true; + + case ZIP_MODE_DEFLATED: + zip_context->current_stream = zlib_inflate_backend.stream_new(); + if (!zip_context->current_stream) + goto error; + + if (zlib_inflate_backend.define) + zlib_inflate_backend.define(zip_context->current_stream, "window_bits", (uint32_t)-MAX_WBITS); + + zip_context->decompressed_data = (uint8_t*)malloc(size); + + if (!zip_context->decompressed_data) + goto error; + + zlib_inflate_backend.set_in(zip_context->current_stream, + zip_context->compressed_data, csize); + zlib_inflate_backend.set_out(zip_context->current_stream, + zip_context->decompressed_data, size); + + return true; + } - return stream->avail_out; +error: + zip_context_free_stream(zip_context, false); + return false; } -static uint64_t zlib_stream_get_total_out(void *data) +static int zlib_stream_decompress_data_to_file_iterate( + void *context, file_archive_file_handle_t *handle) { - z_stream *stream = (z_stream*)data; + zip_context_t *zip_context = (zip_context_t *)context; + bool zstatus; + uint32_t rd, wn; + enum trans_stream_error terror; + + if (!zip_context->current_stream) + { + /* file was uncompressed or decompression finished before */ + return 1; + } - if (!stream) - return 0; + zstatus = zlib_inflate_backend.trans(zip_context->current_stream, false, &rd, &wn, &terror); - return stream->total_out; -} + if (zstatus && !terror) + { + /* successfully decompressed entire file */ + zip_context_free_stream(zip_context, true); + handle->data = zip_context->decompressed_data; + return 1; + } -static void zlib_stream_decrement_total_out(void *data, unsigned subtraction) -{ - z_stream *stream = (z_stream*)data; + if (!zstatus && terror != TRANS_STREAM_ERROR_BUFFER_FULL) + { + /* error during stream processing */ + zip_context_free_stream(zip_context, false); + return -1; + } - if (stream) - stream->total_out -= subtraction; + /* still more data to process */ + return 0; } -static void zlib_stream_compress_free(void *data) +static uint32_t zlib_stream_crc32_calculate(uint32_t crc, + const uint8_t *data, size_t length) { - z_stream *ret = (z_stream*)data; - if (ret) - deflateEnd(ret); + return encoding_crc32(crc, data, length); } -static int zlib_stream_compress_data_to_file(void *data) +static bool zip_file_decompressed_handle( + file_archive_transfer_t *transfer, + file_archive_file_handle_t* handle, + const uint8_t *cdata, unsigned cmode, uint32_t csize, + uint32_t size, uint32_t crc32) { - int zstatus; - z_stream *stream = (z_stream*)data; + int ret = 0; - if (!stream) - return -1; + transfer->backend = &zlib_backend; - zstatus = deflate(stream, Z_FINISH); + if (!transfer->backend->stream_decompress_data_to_file_init( + transfer->context, handle, cdata, cmode, csize, size)) + return false; - if (zstatus == Z_STREAM_END) - return 1; + do + { + ret = transfer->backend->stream_decompress_data_to_file_iterate( + transfer->context, handle); + }while (ret == 0); - return 0; -} +#if 0 + handle->real_checksum = transfer->backend->stream_crc_calculate(0, + handle->data, size); -static bool zlib_stream_decompress_init(void *data) -{ - z_stream *stream = (z_stream*)data; + if (handle->real_checksum != crc32) + { + if (handle->data) + free(handle->data); - if (!stream) - return false; - if (inflateInit(stream) != Z_OK) + handle->data = NULL; return false; + } +#endif + return true; } -static bool zlib_stream_decompress_data_to_file_init( - file_archive_file_handle_t *handle, - const uint8_t *cdata, uint32_t csize, uint32_t size) +typedef struct { - if (!handle) - return false; + char *opt_file; + char *needle; + void **buf; + size_t size; + bool found; +} decomp_state_t; + +/* Extract the relative path (needle) from a + * ZIP archive (path) and allocate a buffer for it to write it in. + * + * optional_outfile if not NULL will be used to extract the file to. + * buf will be 0 then. + */ - if (!(handle->stream = (z_stream*)zlib_stream_new())) - goto error; - - if (inflateInit2((z_streamp)handle->stream, -MAX_WBITS) != Z_OK) - goto error; +static int zip_file_decompressed( + const char *name, const char *valid_exts, + const uint8_t *cdata, unsigned cmode, + uint32_t csize, uint32_t size, + uint32_t crc32, struct archive_extract_userdata *userdata) +{ + decomp_state_t* decomp_state = (decomp_state_t*)userdata->cb_data; + char last_char = name[strlen(name) - 1]; + /* Ignore directories. */ + if (last_char == '/' || last_char == '\\') + return 1; - handle->data = (uint8_t*)malloc(size); + if (strstr(name, decomp_state->needle)) + { + file_archive_file_handle_t handle = {0}; + + if (zip_file_decompressed_handle(userdata->transfer, + &handle, cdata, cmode, csize, size, crc32)) + { + if (decomp_state->opt_file != 0) + { + /* Called in case core has need_fullpath enabled. */ + bool success = filestream_write_file(decomp_state->opt_file, handle.data, size); + + /* Note: Do not free handle.data here - this + * will be done when stream is deinitialised */ + handle.data = NULL; + + decomp_state->size = 0; + + if (!success) + return -1; + } + else + { + /* Called in case core has need_fullpath disabled. + * Will move decompressed content directly into + * RetroArch's ROM buffer. */ + zip_context_t *zip_context = (zip_context_t *)userdata->transfer->context; + + decomp_state->size = 0; + + /* Unlink data buffer from context (otherwise + * it will be freed when the stream is deinitialised) */ + if (handle.data == zip_context->compressed_data) + { + /* Well this is fun... + * If this is 'compressed' data (if the zip + * file was created with the '-0 store only' + * flag), and the origin file is mmapped, then + * the context compressed_data buffer cannot be + * reassigned (since it is not a traditional + * block of user-assigned memory). We have to + * create a copy of it instead... */ +#ifdef HAVE_MMAP + if (zip_context->state->archive_mmap_data) + { + uint8_t *temp_buf = (uint8_t*)malloc(csize); + + if (temp_buf) + { + memcpy(temp_buf, handle.data, csize); + *decomp_state->buf = temp_buf; + decomp_state->size = csize; + } + } + else +#endif + { + *decomp_state->buf = handle.data; + decomp_state->size = csize; + zip_context->compressed_data = NULL; + } + } + else if (handle.data == zip_context->decompressed_data) + { + *decomp_state->buf = handle.data; + decomp_state->size = size; + zip_context->decompressed_data = NULL; + } + + handle.data = NULL; + } + } + + decomp_state->found = true; + } + + return 1; +} - if (!handle->data) - goto error; +static int64_t zip_file_read( + const char *path, + const char *needle, void **buf, + const char *optional_outfile) +{ + file_archive_transfer_t state = {0}; + decomp_state_t decomp = {0}; + struct archive_extract_userdata userdata = {0}; + bool returnerr = true; + int ret = 0; + + if (needle) + decomp.needle = strdup(needle); + if (optional_outfile) + decomp.opt_file = strdup(optional_outfile); + + state.type = ARCHIVE_TRANSFER_INIT; + userdata.transfer = &state; + userdata.cb_data = &decomp; + decomp.buf = buf; + + do + { + ret = file_archive_parse_file_iterate(&state, &returnerr, path, + "", zip_file_decompressed, &userdata); + if (!returnerr) + break; + }while (ret == 0 && !decomp.found); + + file_archive_parse_file_iterate_stop(&state); + + if (decomp.opt_file) + free(decomp.opt_file); + if (decomp.needle) + free(decomp.needle); + + if (!decomp.found) + return -1; - zlib_stream_set(handle->stream, csize, size, - (const uint8_t*)cdata, handle->data); + return (int64_t)decomp.size; +} - return true; +static int zip_parse_file_init(file_archive_transfer_t *state, + const char *file) +{ + uint8_t footer_buf[1024]; + uint8_t *footer = footer_buf; + int64_t read_pos = state->archive_size; + int64_t read_block = MIN(read_pos, sizeof(footer_buf)); + int64_t directory_size, directory_offset; + zip_context_t *zip_context = NULL; + + /* Minimal ZIP file size is 22 bytes */ + if (read_block < 22) + return -1; -error: - zlib_stream_free(handle->stream); - free(handle->stream); - if (handle->data) - free(handle->data); + /* Find the end of central directory record by scanning + * the file from the end towards the beginning. + */ + for (;;) + { + if (--footer < footer_buf) + { + if (read_pos <= 0) + return -1; /* reached beginning of file */ + + /* Read 21 bytes of overlaps except on the first block. */ + if (read_pos == state->archive_size) + read_pos = read_pos - read_block; + else + read_pos = MAX(read_pos - read_block + 21, 0); + + /* Seek to read_pos and read read_block bytes. */ + filestream_seek(state->archive_file, read_pos, RETRO_VFS_SEEK_POSITION_START); + if (filestream_read(state->archive_file, footer_buf, read_block) != read_block) + return -1; + + footer = footer_buf + read_block - 22; + } + if (read_le(footer, 4) == END_OF_CENTRAL_DIR_SIGNATURE) + { + unsigned comment_len = read_le(footer + 20, 2); + if (read_pos + (footer - footer_buf) + 22 + comment_len == state->archive_size) + break; /* found it! */ + } + } + + /* Read directory info and do basic sanity checks. */ + directory_size = read_le(footer + 12, 4); + directory_offset = read_le(footer + 16, 4); + if (directory_size > state->archive_size + || directory_offset > state->archive_size) + return -1; - return false; + /* This is a ZIP file, allocate one block of memory for both the + * context and the entire directory, then read the directory. + */ + zip_context = (zip_context_t*)malloc(sizeof(zip_context_t) + (size_t)directory_size); + zip_context->state = state; + zip_context->directory = (uint8_t*)(zip_context + 1); + zip_context->directory_entry = zip_context->directory; + zip_context->directory_end = zip_context->directory + (size_t)directory_size; + zip_context->current_stream = NULL; + zip_context->compressed_data = NULL; + zip_context->decompressed_data = NULL; + + filestream_seek(state->archive_file, directory_offset, RETRO_VFS_SEEK_POSITION_START); + if (filestream_read(state->archive_file, zip_context->directory, directory_size) != directory_size) + { + free(zip_context); + return -1; + } + + state->context = zip_context; + state->step_total = read_le(footer + 10, 2); /* total entries */; + + return 0; } -static int zlib_stream_decompress_data_to_file_iterate(void *data) +static int zip_parse_file_iterate_step_internal( + zip_context_t * zip_context, char *filename, + const uint8_t **cdata, + unsigned *cmode, uint32_t *size, uint32_t *csize, + uint32_t *checksum, unsigned *payback) { - int zstatus; - z_stream *stream = (z_stream*)data; + uint8_t *entry = zip_context->directory_entry; + uint32_t signature, namelength, extralength, commentlength, offset; - if (!stream) - goto error; + if (entry < zip_context->directory || entry >= zip_context->directory_end) + return 0; - zstatus = inflate(stream, Z_NO_FLUSH); + signature = read_le(zip_context->directory_entry + 0, 4); - if (zstatus == Z_STREAM_END) - return 1; + if (signature != CENTRAL_FILE_HEADER_SIGNATURE) + return 0; - if (zstatus != Z_OK && zstatus != Z_BUF_ERROR) - goto error; + *cmode = read_le(zip_context->directory_entry + 10, 2); /* compression mode, 0 = store, 8 = deflate */ + *checksum = read_le(zip_context->directory_entry + 16, 4); /* CRC32 */ + *csize = read_le(zip_context->directory_entry + 20, 4); /* compressed size */ + *size = read_le(zip_context->directory_entry + 24, 4); /* uncompressed size */ - return 0; + namelength = read_le(zip_context->directory_entry + 28, 2); /* file name length */ + extralength = read_le(zip_context->directory_entry + 30, 2); /* extra field length */ + commentlength = read_le(zip_context->directory_entry + 32, 2); /* file comment length */ -error: - return -1; + if (namelength >= PATH_MAX_LENGTH) + return -1; + + memcpy(filename, zip_context->directory_entry + 46, namelength); /* file name */ + filename[namelength] = '\0'; + + offset = read_le(zip_context->directory_entry + 42, 4); /* relative offset of local file header */ + + *cdata = (uint8_t*)(size_t)offset; /* store file offset in data pointer */ + + *payback = 46 + namelength + extralength + commentlength; + + return 1; } -static void zlib_stream_compress_init(void *data, int level) +static int zip_parse_file_iterate_step(void *context, + const char *valid_exts, struct archive_extract_userdata *userdata, + file_archive_file_cb file_cb) { - z_stream *stream = (z_stream*)data; + zip_context_t *zip_context = (zip_context_t *)context; + const uint8_t *cdata = NULL; + uint32_t checksum = 0; + uint32_t size = 0; + uint32_t csize = 0; + unsigned cmode = 0; + unsigned payload = 0; + int ret = zip_parse_file_iterate_step_internal(zip_context, + userdata->current_file_path, &cdata, &cmode, &size, &csize, &checksum, &payload); + + if (ret != 1) + return ret; + + userdata->crc = checksum; + + if (file_cb && !file_cb(userdata->current_file_path, valid_exts, cdata, cmode, + csize, size, checksum, userdata)) + return 0; + + zip_context->directory_entry += payload; - if (stream) - deflateInit(stream, level); + return 1; } -static uint32_t zlib_stream_crc32_calculate(uint32_t crc, - const uint8_t *data, size_t length) +static void zip_parse_file_free(void *context) { - return crc32(crc, data, length); + zip_context_t *zip_context = (zip_context_t *)context; + zip_context_free_stream(zip_context, false); + free(zip_context); } const struct file_archive_file_backend zlib_backend = { - zlib_stream_new, - zlib_stream_free, - zlib_stream_set, - zlib_stream_get_avail_in, - zlib_stream_get_avail_out, - zlib_stream_get_total_out, - zlib_stream_decrement_total_out, - zlib_stream_decompress_init, + zip_parse_file_init, + zip_parse_file_iterate_step, + zip_parse_file_free, zlib_stream_decompress_data_to_file_init, zlib_stream_decompress_data_to_file_iterate, - zlib_stream_compress_init, - zlib_stream_compress_free, - zlib_stream_compress_data_to_file, zlib_stream_crc32_calculate, + zip_file_read, "zlib" }; diff --git a/PVSupport/Sources/retro/libretro-common/file/config_file.c b/PVSupport/Sources/retro/libretro-common/file/config_file.c index 9b049b0013..8660cfa810 100644 --- a/PVSupport/Sources/retro/libretro-common/file/config_file.c +++ b/PVSupport/Sources/retro/libretro-common/file/config_file.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2010-2015 The RetroArch team +/* Copyright (C) 2010-2020 The RetroArch team * * --------------------------------------------------------------------------------------- * The following license statement only applies to this file (config_file.c). @@ -26,140 +26,193 @@ #include #include -#if !defined(_WIN32) && !defined(__CELLOS_LV2__) && !defined(_XBOX) -#include /* PATH_MAX */ -#elif defined(_WIN32) && !defined(_XBOX) -#define WIN32_LEAN_AND_MEAN -#include -#elif defined(_XBOX) -#include +#ifdef ORBIS +#include +#include #endif - #include #include #include +#include #include #include #include -#include -#include #include -#include +#include +#include #define MAX_INCLUDE_DEPTH 16 -struct config_entry_list -{ - /* If we got this from an #include, - * do not allow overwrite. */ - bool readonly; - char *key; - char *value; - uint32_t key_hash; - - struct config_entry_list *next; -}; - struct config_include_list { char *path; struct config_include_list *next; }; -struct config_file -{ - char *path; - struct config_entry_list *entries; - struct config_entry_list *tail; - unsigned include_depth; +/* Forward declaration */ +static bool config_file_parse_line(config_file_t *conf, + struct config_entry_list *list, char *line, config_file_cb_t *cb); - struct config_include_list *includes; -}; +static int config_file_sort_compare_func(struct config_entry_list *a, + struct config_entry_list *b) +{ + if (a && b) + { + if (a->key) + { + if (b->key) + return strcasecmp(a->key, b->key); + return 1; + } + else if (b->key) + return -1; + } -static config_file_t *config_file_new_internal( - const char *path, unsigned depth); + return 0; +} -static char *getaline(FILE *file) +/* https://stackoverflow.com/questions/7685/merge-sort-a-linked-list */ +static struct config_entry_list* config_file_merge_sort_linked_list( + struct config_entry_list *list, int (*compare)( + struct config_entry_list *one,struct config_entry_list *two)) { - char* newline = (char*)malloc(9); - char* newline_tmp = NULL; - size_t cur_size = 8; - size_t idx = 0; - int in = getc(file); + struct config_entry_list + *right = list, + *temp = list, + *last = list, + *result = 0, + *next = 0, + *tail = 0; + + /* Trivial case. */ + if (!list || !list->next) + return list; + + /* Find halfway through the list (by running two pointers, + * one at twice the speed of the other). */ + while (temp && temp->next) + { + last = right; + right = right->next; + temp = temp->next->next; + } - if (!newline) - return NULL; + /* Break the list in two. (prev pointers are broken here, + * but we fix later) */ + last->next = 0; - while (in != EOF && in != '\n') + /* Recurse on the two smaller lists: */ + list = config_file_merge_sort_linked_list(list, compare); + right = config_file_merge_sort_linked_list(right, compare); + + /* Merge: */ + while (list || right) { - if (idx == cur_size) + /* Take from empty lists, or compare: */ + if (!right) { - cur_size *= 2; - newline_tmp = (char*)realloc(newline, cur_size + 1); - - if (!newline_tmp) - { - free(newline); - return NULL; - } - - newline = newline_tmp; + next = list; + list = list->next; + } + else if (!list) + { + next = right; + right = right->next; + } + else if (compare(list, right) < 0) + { + next = list; + list = list->next; } + else + { + next = right; + right = right->next; + } + + if (!result) + result = next; + else + tail->next = next; - newline[idx++] = in; - in = getc(file); + tail = next; } - newline[idx] = '\0'; - return newline; + + return result; } -static char *strip_comment(char *str) +/* Searches input string for a comment ('#') entry + * > If first character is '#', then entire line is + * a comment and may correspond to a directive + * (command action - e.g. include sub-config file). + * In this case, 'str' is set to NUL and the comment + * itself (everything after the '#' character) is + * returned + * > If a '#' character is found inside a string literal + * value, then it does not correspond to a comment and + * is ignored. In this case, 'str' is left untouched + * and NULL is returned + * > If a '#' character is found anywhere else, then the + * comment text is a suffix of the input string and + * has no programmatic value. In this case, the comment + * is removed from the end of 'str' and NULL is returned */ +static char *config_file_strip_comment(char *str) { - /* Remove everything after comment. - * Keep #s inside string literals. */ - char *string_end = str + strlen(str); - bool cut_comment = true; + /* Search for a comment (#) character */ + char *comment = strchr(str, '#'); - while (!string_is_empty(str)) + if (comment) { - char *comment = NULL; - char *literal = strchr(str, '\"'); - if (!literal) - literal = string_end; - comment = (char*)strchr(str, '#'); - if (!comment) - comment = string_end; + char *literal_start = NULL; - if (cut_comment && literal < comment) - { - cut_comment = false; - str = literal + 1; - } - else if (!cut_comment && literal) + /* Check whether entire line is a comment + * > First character == '#' */ + if (str == comment) { - cut_comment = true; - str = literal + 1; + /* Set 'str' to NUL and return comment + * for processing at a higher level */ + *str = '\0'; + return ++comment; } - else if (comment) + + /* Comment character occurs at an offset: + * Search for the start of a string literal value */ + literal_start = strchr(str, '\"'); + + /* Check whether string literal start occurs + * *before* the comment character */ + if (literal_start && (literal_start < comment)) { - *comment = '\0'; - str = comment; + /* Search for the end of the string literal + * value */ + char *literal_end = strchr(literal_start + 1, '\"'); + + /* Check whether string literal end occurs + * *after* the comment character + * > If this is the case, ignore the comment + * > Leave 'str' untouched and return NULL */ + if (literal_end && (literal_end > comment)) + return NULL; } - else - str = string_end; + + /* If we reach this point, then a comment + * exists outside of a string literal + * > Trim the entire comment from the end + * of 'str' */ + *comment = '\0'; } - return str; + return NULL; } -static char *extract_value(char *line, bool is_value) +static char *config_file_extract_value(char *line, bool is_value) { - char *save = NULL; - char *tok = NULL; + size_t idx = 0; + char *value = NULL; if (is_value) { - while (isspace((int)*line)) + while (ISSPACE((int)*line)) line++; /* If we don't have an equal sign here, @@ -170,82 +223,90 @@ static char *extract_value(char *line, bool is_value) line++; } - while (isspace((int)*line)) + while (ISSPACE((int)*line)) line++; - /* We have a full string. Read until next ". */ + /* Note: From this point on, an empty value + * string is valid - and in this case, strdup("") + * will be returned + * > If we instead return NULL, the the entry + * is ignored completely - which means we cannot + * track *changes* in entry value */ + + /* If first character is ("), we have a full string + * literal */ if (*line == '"') { + /* Skip to next character */ line++; - tok = strtok_r(line, "\"", &save); - if (!tok) - return NULL; - return strdup(tok); - } - else if (*line == '\0') /* Nothing */ - return NULL; - - /* We don't have that. Read until next space. */ - tok = strtok_r(line, " \n\t\f\r\v", &save); - if (tok) - return strdup(tok); - return NULL; -} - -static void add_include_list(config_file_t *conf, const char *path) -{ - struct config_include_list *head = conf->includes; - struct config_include_list *node = (struct config_include_list*)calloc(1, sizeof(*node)); - if (!node) - return; + /* If this a ("), then value string is empty */ + if (*line == '"') + return strdup(""); - node->path = strdup(path); + /* Find the next (") character */ + while (line[idx] && (line[idx] != '\"')) + idx++; - if (head) + line[idx] = '\0'; + value = line; + } + /* This is not a string literal - just read + * until the next space is found + * > Note: Skip this if line is empty */ + else if (*line != '\0') { - while (head->next) - head = head->next; + /* Find next space character */ + while (line[idx] && isgraph((int)line[idx])) + idx++; - head->next = node; + line[idx] = '\0'; + value = line; } - else - conf->includes = node; -} -static void set_list_readonly(struct config_entry_list *list) -{ - while (list) - { - list->readonly = true; - list = list->next; - } + if (value && *value) + return strdup(value); + + return strdup(""); } /* Move semantics? */ -static void add_child_list(config_file_t *parent, config_file_t *child) +static void config_file_add_child_list(config_file_t *parent, config_file_t *child) { + struct config_entry_list *list = child->entries; + bool merge_hash_map = false; + if (parent->entries) { struct config_entry_list *head = parent->entries; while (head->next) head = head->next; - set_list_readonly(child->entries); - head->next = child->entries; + /* set list readonly */ + while (list) + { + list->readonly = true; + list = list->next; + } + head->next = child->entries; + + merge_hash_map = true; } else { - set_list_readonly(child->entries); - parent->entries = child->entries; + /* set list readonly */ + while (list) + { + list->readonly = true; + list = list->next; + } + parent->entries = child->entries; } - child->entries = NULL; - /* Rebase tail. */ if (parent->entries) { - struct config_entry_list *head = + struct config_entry_list *head = (struct config_entry_list*)parent->entries; while (head->next) @@ -254,96 +315,305 @@ static void add_child_list(config_file_t *parent, config_file_t *child) } else parent->tail = NULL; -} -static void add_sub_conf(config_file_t *conf, char *line) -{ - char real_path[PATH_MAX_LENGTH] = {0}; - config_file_t *sub_conf = NULL; - char *path = extract_value(line, false); + /* Update hash map */ + if (merge_hash_map) + { + size_t i; + size_t cap; - if (!path) - return; + /* We are merging two lists - if any child entry + * (key) is not present in the parent list, add it + * to the parent hash map */ + for (i = 0, cap = RHMAP_CAP(child->entries_map); i != cap; i++) + { + uint32_t child_hash = RHMAP_KEY(child->entries_map, i); + const char *child_key = RHMAP_KEY_STR(child->entries_map, i); + + if (child_hash && + child_key && + !RHMAP_HAS_FULL(parent->entries_map, child_hash, child_key)) + { + struct config_entry_list *entry = child->entries_map[i]; + + if (entry) + RHMAP_SET_FULL(parent->entries_map, child_hash, child_key, entry); + } + } + + /* Child entries map is no longer required, + * so free it now */ + RHMAP_FREE(child->entries_map); + } + else + { + /* If parent list was originally empty, + * take map from child list */ + RHMAP_FREE(parent->entries_map); + parent->entries_map = child->entries_map; + child->entries_map = NULL; + } - add_include_list(conf, path); + child->entries = NULL; +} +static void config_file_get_realpath(char *s, size_t len, + char *path, const char *config_path) +{ #ifdef _WIN32 - fill_pathname_resolve_relative(real_path, conf->path, - path, sizeof(real_path)); + if (!string_is_empty(config_path)) + fill_pathname_resolve_relative(s, config_path, + path, len); #else -#ifndef __CELLOS_LV2__ +#if !defined(__PSL1GHT__) && !defined(__PS3__) if (*path == '~') { const char *home = getenv("HOME"); - strlcpy(real_path, home ? home : "", sizeof(real_path)); - strlcat(real_path, path + 1, sizeof(real_path)); + if (home) + { + strlcpy(s, home, len); + strlcat(s, path + 1, len); + } + else + strlcpy(s, path + 1, len); } else #endif - fill_pathname_resolve_relative(real_path, conf->path, - path, sizeof(real_path)); + if (!string_is_empty(config_path)) + fill_pathname_resolve_relative(s, config_path, path, len); #endif +} - sub_conf = (config_file_t*) - config_file_new_internal(real_path, conf->include_depth + 1); - if (!sub_conf) +static void config_file_add_sub_conf(config_file_t *conf, char *path, + char *real_path, size_t len, config_file_cb_t *cb) +{ + struct config_include_list *head = conf->includes; + struct config_include_list *node = (struct config_include_list*) + malloc(sizeof(*node)); + + if (node) { - free(path); - return; + node->next = NULL; + /* Add include list */ + node->path = strdup(path); + + if (head) + { + while (head->next) + head = head->next; + + head->next = node; + } + else + conf->includes = node; } - /* Pilfer internal list. */ - add_child_list(conf, sub_conf); - config_file_free(sub_conf); - free(path); + config_file_get_realpath(real_path, len, path, + conf->path); } -static bool parse_line(config_file_t *conf, - struct config_entry_list *list, char *line) +static int config_file_load_internal( + struct config_file *conf, + const char *path, unsigned depth, config_file_cb_t *cb) { - char *comment = NULL; - char *key_tmp = NULL; - size_t cur_size = 8; - size_t idx = 0; - char *key = (char*)malloc(9); + RFILE *file = NULL; + char *new_path = strdup(path); + if (!new_path) + return 1; - if (!key) - return false; + conf->path = new_path; + conf->include_depth = depth; + file = filestream_open(path, + RETRO_VFS_FILE_ACCESS_READ, + RETRO_VFS_FILE_ACCESS_HINT_NONE); - if (!line || !*line) + if (!file) { - free(key); - return false; + free(conf->path); + return 1; } - comment = strip_comment(line); - - /* Starting line with # and include includes config files. */ - if ((comment == line) && (conf->include_depth < MAX_INCLUDE_DEPTH)) + while (!filestream_eof(file)) { - comment++; - if (strstr(comment, "include ") == comment) + char *line = NULL; + struct config_entry_list *list = (struct config_entry_list*) + malloc(sizeof(*list)); + + if (!list) { - add_sub_conf(conf, comment + strlen("include ")); - free(key); - return false; + filestream_close(file); + return -1; } + + list->readonly = false; + list->key = NULL; + list->value = NULL; + list->next = NULL; + + line = filestream_getline(file); + + if (!line) + { + free(list); + continue; + } + + if ( + !string_is_empty(line) + && config_file_parse_line(conf, list, line, cb)) + { + if (conf->entries) + conf->tail->next = list; + else + conf->entries = list; + + conf->tail = list; + + if (list->key) + { + /* Only add entry to the map if an entry + * with the specified value does not + * already exist */ + uint32_t hash = rhmap_hash_string(list->key); + + if (!RHMAP_HAS_FULL(conf->entries_map, hash, list->key)) + { + RHMAP_SET_FULL(conf->entries_map, hash, list->key, list); + + if (cb && list->value) + cb->config_file_new_entry_cb(list->key, list->value); + } + } + } + + free(line); + + if (list != conf->tail) + free(list); } - else if (conf->include_depth >= MAX_INCLUDE_DEPTH) + + filestream_close(file); + + return 0; +} + +static bool config_file_parse_line(config_file_t *conf, + struct config_entry_list *list, char *line, config_file_cb_t *cb) +{ + size_t cur_size = 32; + size_t idx = 0; + char *key = NULL; + char *key_tmp = NULL; + /* Remove any comment text */ + char *comment = config_file_strip_comment(line); + + /* Check whether entire line is a comment */ + if (comment) { - fprintf(stderr, "!!! #include depth exceeded for config. Might be a cycle.\n"); + config_file_t sub_conf; + bool include_found = false; + bool reference_found = false; + char real_path[PATH_MAX_LENGTH]; + char *path = NULL; + char *include_line = NULL; + char *reference_line = NULL; + + include_found = string_starts_with_size(comment, "include ", + STRLEN_CONST("include ")); + reference_found = string_starts_with_size(comment, "reference ", + STRLEN_CONST("reference ")); + + /* All comments except those starting with the include or + * reference directive are ignored */ + if (!include_found && !reference_found) + return false; + + /* Starting a line with an 'include' directive + * appends a sub-config file */ + if (include_found) + { + include_line = comment + STRLEN_CONST("include "); + + if (string_is_empty(include_line)) + return false; + + path = config_file_extract_value(include_line, false); + + if (!path) + return false; + + if ( string_is_empty(path) + || conf->include_depth >= MAX_INCLUDE_DEPTH) + { + free(path); + return false; + } + + real_path[0] = '\0'; + config_file_add_sub_conf(conf, path, + real_path, sizeof(real_path), cb); + + config_file_initialize(&sub_conf); + + switch (config_file_load_internal(&sub_conf, real_path, + conf->include_depth + 1, cb)) + { + case 0: + /* Pilfer internal list. */ + config_file_add_child_list(conf, &sub_conf); + /* fall-through to deinitialize */ + case -1: + config_file_deinitialize(&sub_conf); + break; + case 1: + default: + break; + } + } + + /* Starting a line with an 'reference' directive + * sets the reference path */ + if (reference_found) + { + reference_line = comment + STRLEN_CONST("reference "); + + if (string_is_empty(reference_line)) + return false; + + path = config_file_extract_value(reference_line, false); + + if (!path) + return false; + + config_file_set_reference_path(conf, path); + + if (!path) + return false; + } + + free(path); + return true; } - /* Skips to first character. */ - while (isspace((int)*line)) + /* Skip to first non-space character */ + while (ISSPACE((int)*line)) line++; + /* Allocate storage for key */ + key = (char*)malloc(cur_size + 1); + if (!key) + return false; + + /* Copy line contents into key until we + * reach the next space character */ while (isgraph((int)*line)) { + /* If current key storage is too small, + * double its size */ if (idx == cur_size) { cur_size *= 2; - key_tmp = (char*)realloc(key, cur_size + 1); + key_tmp = (char*)realloc(key, cur_size + 1); if (!key_tmp) { @@ -356,11 +626,13 @@ static bool parse_line(config_file_t *conf, key[idx++] = *line++; } - key[idx] = '\0'; - list->key = key; - list->key_hash = djb2_calculate(key); + key[idx] = '\0'; - list->value = extract_value(line, true); + /* Add key and value entries to list */ + list->key = key; + list->value = config_file_extract_value(line, true); + + /* An entry without a value is invalid */ if (!list->value) { list->key = NULL; @@ -371,86 +643,97 @@ static bool parse_line(config_file_t *conf, return true; } -static config_file_t *config_file_new_internal( - const char *path, unsigned depth) +static int config_file_from_string_internal( + struct config_file *conf, + char *from_string, + const char *path) { - FILE *file = NULL; - struct config_file *conf = (struct config_file*)calloc(1, sizeof(*conf)); - if (!conf) - return NULL; - - if (!path || !*path) - return conf; + char *lines = from_string; + char *save_ptr = NULL; + char *line = NULL; - if (path_is_directory(path)) - goto error; + if (!string_is_empty(path)) + conf->path = strdup(path); + if (string_is_empty(lines)) + return 0; - conf->path = strdup(path); - if (!conf->path) - goto error; + /* Get first line of config file */ + line = strtok_r(lines, "\n", &save_ptr); - conf->include_depth = depth; - file = fopen(path, "r"); - - if (!file) - { - free(conf->path); - goto error; - } - - while (!feof(file)) + while (line) { struct config_entry_list *list = (struct config_entry_list*) - calloc(1, sizeof(*list)); - char *line = NULL; + malloc(sizeof(*list)); if (!list) - { - config_file_free(conf); - fclose(file); - return NULL; - } - - line = getaline(file); + return -1; - if (!line) - { - free(list); - continue; - } + list->readonly = false; + list->key = NULL; + list->value = NULL; + list->next = NULL; - if (parse_line(conf, list, line)) + /* Parse current line */ + if ( + !string_is_empty(line) + && config_file_parse_line(conf, list, line, NULL)) { if (conf->entries) conf->tail->next = list; else - conf->entries = list; + conf->entries = list; - conf->tail = list; - } + conf->tail = list; - free(line); + if (list->key) + { + /* Only add entry to the map if an entry + * with the specified value does not + * already exist */ + uint32_t hash = rhmap_hash_string(list->key); + if (!RHMAP_HAS_FULL(conf->entries_map, hash, list->key)) + RHMAP_SET_FULL(conf->entries_map, hash, list->key, list); + } + } if (list != conf->tail) free(list); + + /* Get next line of config file */ + line = strtok_r(NULL, "\n", &save_ptr); } + + return 0; +} - fclose(file); +void config_file_set_reference_path(config_file_t *conf, char *path) +{ + /* It is expected that the conf has it's path already set */ + + char short_path[PATH_MAX_LENGTH]; + + short_path[0] = '\0'; - return conf; + if (!conf) + return; -error: - free(conf); + if (conf->reference) + { + free(conf->reference); + conf->reference = NULL; + } - return NULL; + fill_pathname_abbreviated_or_relative(short_path, conf->path, path, sizeof(short_path)); + + conf->reference = strdup(short_path); } -void config_file_free(config_file_t *conf) +bool config_file_deinitialize(config_file_t *conf) { struct config_include_list *inc_tmp = NULL; struct config_entry_list *tmp = NULL; if (!conf) - return; + return false; tmp = conf->entries; while (tmp) @@ -475,23 +758,56 @@ void config_file_free(config_file_t *conf) while (inc_tmp) { struct config_include_list *hold = NULL; - free(inc_tmp->path); - hold = (struct config_include_list*)inc_tmp; + if (inc_tmp->path) + free(inc_tmp->path); + hold = (struct config_include_list*)inc_tmp; inc_tmp = inc_tmp->next; - free(hold); + if (hold) + free(hold); } + if (conf->reference) + free(conf->reference); + if (conf->path) free(conf->path); + + RHMAP_FREE(conf->entries_map); + + return true; +} + +void config_file_free(config_file_t *conf) +{ + if (!config_file_deinitialize(conf)) + return; free(conf); } bool config_append_file(config_file_t *conf, const char *path) { - config_file_t *new_conf = config_file_new(path); + config_file_t *new_conf = config_file_new_from_path_to_string(path); + size_t i; + size_t cap; + if (!new_conf) return false; + /* Update hash map */ + for (i = 0, cap = RHMAP_CAP(new_conf->entries_map); i != cap; i++) + { + uint32_t new_hash = RHMAP_KEY(new_conf->entries_map, i); + const char *new_key = RHMAP_KEY_STR(new_conf->entries_map, i); + + if (new_hash && new_key) + { + struct config_entry_list *entry = new_conf->entries_map[i]; + + if (entry) + RHMAP_SET_FULL(conf->entries_map, new_hash, new_key, entry); + } + } + if (new_conf->tail) { new_conf->tail->next = conf->entries; @@ -503,134 +819,205 @@ bool config_append_file(config_file_t *conf, const char *path) return true; } - -config_file_t *config_file_new_from_string(const char *from_string) +config_file_t *config_file_new_from_string(char *from_string, + const char *path) { - size_t i; - struct string_list *lines = NULL; - struct config_file *conf = (struct config_file*)calloc(1, sizeof(*conf)); + struct config_file *conf = config_file_new_alloc(); + if (!conf) return NULL; - - if (!from_string) - return conf; - - conf->path = NULL; - conf->include_depth = 0; - - lines = string_split(from_string, "\n"); - if (!lines) - return conf; - - for (i = 0; i < lines->size; i++) + if (config_file_from_string_internal(conf, from_string, path) == -1) { - struct config_entry_list *list = (struct config_entry_list*) - calloc(1, sizeof(*list)); - char* line = lines->elems[i].data; + config_file_free(conf); + return NULL; + } + return conf; +} - if (!list) - { - string_list_free(lines); - config_file_free(conf); - return NULL; - } +config_file_t *config_file_new_from_path_to_string(const char *path) +{ + int64_t length = 0; + uint8_t *ret_buf = NULL; + config_file_t *conf = NULL; - if (line) + if (path_is_valid(path)) + { + if (filestream_read_file(path, (void**)&ret_buf, &length)) { - if (parse_line(conf, list, line)) - { - if (conf->entries) - conf->tail->next = list; - else - conf->entries = list; - - conf->tail = list; - } + /* Note: 'ret_buf' is not used outside this + * function - we do not care that it will be + * modified by config_file_new_from_string() */ + if (length >= 0) + conf = config_file_new_from_string((char*)ret_buf, path); + + if ((void*)ret_buf) + free((void*)ret_buf); } - - if (list != conf->tail) - free(list); } - string_list_free(lines); + return conf; +} +config_file_t *config_file_new_with_callback( + const char *path, config_file_cb_t *cb) +{ + int ret = 0; + struct config_file *conf = config_file_new_alloc(); + if (!path || !*path) + return conf; + ret = config_file_load_internal(conf, path, 0, cb); + if (ret == -1) + { + config_file_free(conf); + return NULL; + } + else if (ret == 1) + { + free(conf); + return NULL; + } return conf; } config_file_t *config_file_new(const char *path) { - return config_file_new_internal(path, 0); + int ret = 0; + struct config_file *conf = config_file_new_alloc(); + if (!path || !*path) + return conf; + ret = config_file_load_internal(conf, path, 0, NULL); + if (ret == -1) + { + config_file_free(conf); + return NULL; + } + else if (ret == 1) + { + free(conf); + return NULL; + } + return conf; +} + +void config_file_initialize(struct config_file *conf) +{ + if (!conf) + return; + + conf->path = NULL; + conf->entries_map = NULL; + conf->entries = NULL; + conf->tail = NULL; + conf->last = NULL; + conf->reference = NULL; + conf->includes = NULL; + conf->include_depth = 0; + conf->guaranteed_no_duplicates = false; + conf->modified = false; } +config_file_t *config_file_new_alloc(void) +{ + struct config_file *conf = (struct config_file*)malloc(sizeof(*conf)); + if (!conf) + return NULL; + config_file_initialize(conf); + return conf; +} -static struct config_entry_list *config_get_entry(const config_file_t *conf, +static struct config_entry_list *config_get_entry_internal( + const config_file_t *conf, const char *key, struct config_entry_list **prev) { - struct config_entry_list *entry; - struct config_entry_list *previous = NULL; + struct config_entry_list *entry = NULL; + struct config_entry_list *previous = prev ? *prev : NULL; - uint32_t hash = djb2_calculate(key); + entry = RHMAP_GET_STR(conf->entries_map, key); - if (prev) - previous = *prev; + if (entry) + return entry; - for (entry = conf->entries; entry; entry = entry->next) + if (prev) { - if (hash == entry->key_hash && !strcmp(key, entry->key)) - return entry; - - previous = entry; - } + for (entry = conf->entries; entry; entry = entry->next) + previous = entry; - if (prev) *prev = previous; + } return NULL; } +struct config_entry_list *config_get_entry( + const config_file_t *conf, const char *key) +{ + return RHMAP_GET_STR(conf->entries_map, key); +} + bool config_get_double(config_file_t *conf, const char *key, double *in) { - const struct config_entry_list *entry = config_get_entry(conf, key, NULL); + const struct config_entry_list *entry = config_get_entry(conf, key); - if (entry) - *in = strtod(entry->value, NULL); + if (!entry) + return false; - return entry != NULL; + *in = strtod(entry->value, NULL); + return true; } bool config_get_float(config_file_t *conf, const char *key, float *in) { - const struct config_entry_list *entry = config_get_entry(conf, key, NULL); + const struct config_entry_list *entry = config_get_entry(conf, key); - if (entry) - { - /* strtof() is C99/POSIX. Just use the more portable kind. */ - *in = (float)strtod(entry->value, NULL); - } + if (!entry) + return false; - return entry != NULL; + /* strtof() is C99/POSIX. Just use the more portable kind. */ + *in = (float)strtod(entry->value, NULL); + return true; } bool config_get_int(config_file_t *conf, const char *key, int *in) { - const struct config_entry_list *entry = config_get_entry(conf, key, NULL); + const struct config_entry_list *entry = config_get_entry(conf, key); errno = 0; if (entry) { - int val = strtol(entry->value, NULL, 0); + int val = (int)strtol(entry->value, NULL, 0); if (errno == 0) + { *in = val; + return true; + } } - return entry != NULL && errno == 0; + return false; +} + +bool config_get_size_t(config_file_t *conf, const char *key, size_t *in) +{ + const struct config_entry_list *entry = config_get_entry(conf, key); + errno = 0; + + if (entry) + { + size_t val = 0; + if (sscanf(entry->value, "%" PRI_SIZET, &val) == 1) + { + *in = val; + return true; + } + } + + return false; } #if defined(__STDC_VERSION__) && __STDC_VERSION__>=199901L bool config_get_uint64(config_file_t *conf, const char *key, uint64_t *in) { - const struct config_entry_list *entry = config_get_entry(conf, key, NULL); + const struct config_entry_list *entry = config_get_entry(conf, key); errno = 0; if (entry) @@ -638,48 +1025,56 @@ bool config_get_uint64(config_file_t *conf, const char *key, uint64_t *in) uint64_t val = strtoull(entry->value, NULL, 0); if (errno == 0) + { *in = val; + return true; + } } - - return entry != NULL && errno == 0; + return false; } #endif bool config_get_uint(config_file_t *conf, const char *key, unsigned *in) { - const struct config_entry_list *entry = config_get_entry(conf, key, NULL); + const struct config_entry_list *entry = config_get_entry(conf, key); errno = 0; if (entry) { - unsigned val = strtoul(entry->value, NULL, 0); + unsigned val = (unsigned)strtoul(entry->value, NULL, 0); if (errno == 0) + { *in = val; + return true; + } } - return entry != NULL && errno == 0; + return false; } bool config_get_hex(config_file_t *conf, const char *key, unsigned *in) { - const struct config_entry_list *entry = config_get_entry(conf, key, NULL); + const struct config_entry_list *entry = config_get_entry(conf, key); errno = 0; if (entry) { - unsigned val = strtoul(entry->value, NULL, 16); + unsigned val = (unsigned)strtoul(entry->value, NULL, 16); if (errno == 0) + { *in = val; + return true; + } } - return entry != NULL && errno == 0; + return false; } bool config_get_char(config_file_t *conf, const char *key, char *in) { - const struct config_entry_list *entry = config_get_entry(conf, key, NULL); + const struct config_entry_list *entry = config_get_entry(conf, key); if (entry) { @@ -687,122 +1082,187 @@ bool config_get_char(config_file_t *conf, const char *key, char *in) return false; *in = *entry->value; + return true; } - return entry != NULL; + return false; } bool config_get_string(config_file_t *conf, const char *key, char **str) { - const struct config_entry_list *entry = config_get_entry(conf, key, NULL); + const struct config_entry_list *entry = config_get_entry(conf, key); - if (entry) - *str = strdup(entry->value); + if (!entry || !entry->value) + return false; - return entry != NULL; + *str = strdup(entry->value); + return true; } bool config_get_config_path(config_file_t *conf, char *s, size_t len) { if (!conf) return false; - return strlcpy(s, conf->path, len); } bool config_get_array(config_file_t *conf, const char *key, char *buf, size_t size) { - const struct config_entry_list *entry = config_get_entry(conf, key, NULL); - + const struct config_entry_list *entry = config_get_entry(conf, key); if (entry) return strlcpy(buf, entry->value, size) < size; - - return entry != NULL; + return false; } bool config_get_path(config_file_t *conf, const char *key, char *buf, size_t size) { -#if defined(RARCH_CONSOLE) - return config_get_array(conf, key, buf, size); +#if defined(RARCH_CONSOLE) || !defined(RARCH_INTERNAL) + if (config_get_array(conf, key, buf, size)) + return true; #else - const struct config_entry_list *entry = config_get_entry(conf, key, NULL); + const struct config_entry_list *entry = config_get_entry(conf, key); if (entry) + { fill_pathname_expand_special(buf, entry->value, size); - - return entry != NULL; + return true; + } #endif + return false; } bool config_get_bool(config_file_t *conf, const char *key, bool *in) { - const struct config_entry_list *entry = config_get_entry(conf, key, NULL); + const struct config_entry_list *entry = config_get_entry(conf, key); - if (entry) - { - if (strcasecmp(entry->value, "true") == 0) - *in = true; - else if (strcasecmp(entry->value, "1") == 0) - *in = true; - else if (strcasecmp(entry->value, "false") == 0) - *in = false; - else if (strcasecmp(entry->value, "0") == 0) - *in = false; - else - return false; - } + if (!entry) + return false; - return entry != NULL; + if ( + ( + entry->value[0] == '1' + && entry->value[1] == '\0' + ) + || string_is_equal(entry->value, "true") + ) + *in = true; + else if ( + ( + entry->value[0] == '0' + && entry->value[1] == '\0' + ) + || string_is_equal(entry->value, "false") + ) + *in = false; + else + return false; + + return true; } void config_set_string(config_file_t *conf, const char *key, const char *val) { - struct config_entry_list *last = conf->entries; - struct config_entry_list *entry = config_get_entry(conf, key, &last); + struct config_entry_list *last = NULL; + struct config_entry_list *entry = NULL; - if (entry && !entry->readonly) - { - free(entry->value); - entry->value = strdup(val); + if (!conf || !key || !val) return; + + last = conf->entries; + + if (conf->guaranteed_no_duplicates) + { + if (conf->last) + last = conf->last; } + else + { + entry = config_get_entry_internal( + conf, key, &last); + if (entry) + { + /* An entry corresponding to 'key' already exists + * > Check whether value is currently set */ + if (entry->value) + { + /* Do nothing if value is unchanged */ + if (string_is_equal(entry->value, val)) + return; - if (!val) return; + /* Value is to be updated + * > Free existing */ + free(entry->value); + } - entry = (struct config_entry_list*)calloc(1, sizeof(*entry)); - if (!entry) return; + /* Update value + * > Note that once a value is set, it + * is no longer considered 'read only' */ + entry->value = strdup(val); + entry->readonly = false; + conf->modified = true; + return; + } + } + + /* Entry corresponding to 'key' does not exist + * > Create new entry */ + entry = (struct config_entry_list*)malloc(sizeof(*entry)); + if (!entry) + return; - entry->key = strdup(key); - entry->value = strdup(val); + entry->readonly = false; + entry->key = strdup(key); + entry->value = strdup(val); + entry->next = NULL; + conf->modified = true; if (last) - last->next = entry; + last->next = entry; else conf->entries = entry; + + conf->last = entry; + + RHMAP_SET_STR(conf->entries_map, entry->key, entry); } void config_unset(config_file_t *conf, const char *key) { - struct config_entry_list *last = conf->entries; - struct config_entry_list *entry = config_get_entry(conf, key, &last); + struct config_entry_list *last = NULL; + struct config_entry_list *entry = NULL; + + if (!conf || !key) + return; + + last = conf->entries; + entry = config_get_entry_internal(conf, key, &last); if (!entry) return; - entry->key = NULL; - entry->value = NULL; - free(entry->key); - free(entry->value); + (void)RHMAP_DEL_STR(conf->entries_map, entry->key); + + if (entry->key) + free(entry->key); + + if (entry->value) + free(entry->value); + + entry->key = NULL; + entry->value = NULL; + conf->modified = true; } void config_set_path(config_file_t *conf, const char *entry, const char *val) { -#if defined(RARCH_CONSOLE) +#if defined(RARCH_CONSOLE) || !defined(RARCH_INTERNAL) config_set_string(conf, entry, val); #else - char buf[PATH_MAX_LENGTH] = {0}; + char buf[PATH_MAX_LENGTH]; + + buf[0] = '\0'; fill_pathname_abbreviate_special(buf, val, sizeof(buf)); config_set_string(conf, entry, buf); #endif @@ -810,7 +1270,7 @@ void config_set_path(config_file_t *conf, const char *entry, const char *val) void config_set_double(config_file_t *conf, const char *key, double val) { - char buf[128] = {0}; + char buf[320]; #ifdef __cplusplus snprintf(buf, sizeof(buf), "%f", (float)val); #elif defined(__STDC_VERSION__) && __STDC_VERSION__>=199901L @@ -823,39 +1283,42 @@ void config_set_double(config_file_t *conf, const char *key, double val) void config_set_float(config_file_t *conf, const char *key, float val) { - char buf[128] = {0}; + char buf[64]; snprintf(buf, sizeof(buf), "%f", val); config_set_string(conf, key, buf); } void config_set_int(config_file_t *conf, const char *key, int val) { - char buf[128] = {0}; + char buf[16]; snprintf(buf, sizeof(buf), "%d", val); config_set_string(conf, key, buf); } +void config_set_uint(config_file_t *conf, const char *key, unsigned int val) +{ + char buf[16]; + snprintf(buf, sizeof(buf), "%u", val); + config_set_string(conf, key, buf); +} + void config_set_hex(config_file_t *conf, const char *key, unsigned val) { - char buf[128] = {0}; + char buf[16]; snprintf(buf, sizeof(buf), "%x", val); config_set_string(conf, key, buf); } void config_set_uint64(config_file_t *conf, const char *key, uint64_t val) { - char buf[128] = {0}; -#ifdef _WIN32 - snprintf(buf, sizeof(buf), "%I64u", val); -#else - snprintf(buf, sizeof(buf), "%llu", (long long unsigned)val); -#endif + char buf[32]; + snprintf(buf, sizeof(buf), "%" PRIu64, val); config_set_string(conf, key, buf); } void config_set_char(config_file_t *conf, const char *key, char val) { - char buf[2] = {0}; + char buf[2]; snprintf(buf, sizeof(buf), "%c", val); config_set_string(conf, key, buf); } @@ -865,60 +1328,142 @@ void config_set_bool(config_file_t *conf, const char *key, bool val) config_set_string(conf, key, val ? "true" : "false"); } -bool config_file_write(config_file_t *conf, const char *path) +bool config_file_write(config_file_t *conf, const char *path, bool sort) { - FILE *file; + if (!conf) + return false; + + if (!conf->modified) + return true; - if (path) + if (!string_is_empty(path)) { - file = fopen(path, "w"); +#ifdef ORBIS + int fd = orbisOpen(path,O_RDWR|O_CREAT,0644); + if (fd < 0) + return false; + config_file_dump_orbis(conf,fd); + orbisClose(fd); +#else + void* buf = NULL; + FILE *file = (FILE*)fopen_utf8(path, "wb"); if (!file) return false; - } - else - file = stdout; - config_file_dump(conf, file); + /* TODO: this is only useful for a few platforms, find which and add ifdef */ +#if !defined(PSP) + buf = calloc(1, 0x4000); + setvbuf(file, (char*)buf, _IOFBF, 0x4000); +#endif - if (path) - fclose(file); + config_file_dump(conf, file, sort); + + if (file != stdout) + fclose(file); + if (buf) + free(buf); +#endif + + /* Only update modified flag if config file + * is actually written to disk */ + conf->modified = false; + } + else + config_file_dump(conf, stdout, sort); return true; } -void config_file_dump(config_file_t *conf, FILE *file) +#ifdef ORBIS +void config_file_dump_orbis(config_file_t *conf, int fd) { struct config_entry_list *list = NULL; struct config_include_list *includes = conf->includes; - - while (includes) + + if (conf->reference) { - fprintf(file, "#include \"%s\"\n", includes->path); - includes = includes->next; + pathname_make_slashes_portable(conf->reference); + fprintf(file, "#reference \"%s\"\n", conf->reference); } - list = (struct config_entry_list*)conf->entries; + + list = config_file_merge_sort_linked_list( + (struct config_entry_list*)conf->entries, + config_file_sort_compare_func); + conf->entries = list; while (list) { if (!list->readonly && list->key) - fprintf(file, "%s = \"%s\"\n", list->key, list->value); + { + char newlist[256]; + snprintf(newlist, sizeof(newlist), + "%s = %s\n", list->key, list->value); + orbisWrite(fd, newlist, strlen(newlist)); + } list = list->next; } + + /* Config files are read from the top down - if + * duplicate entries are found then the topmost + * one in the list takes precedence. This means + * '#include' directives must go *after* individual + * config entries, otherwise they will override + * any custom-set values */ + while (includes) + { + char cad[256]; + snprintf(cad, sizeof(cad), + "#include %s\n", includes->path); + orbisWrite(fd, cad, strlen(cad)); + includes = includes->next; + } } +#endif -bool config_entry_exists(config_file_t *conf, const char *entry) +void config_file_dump(config_file_t *conf, FILE *file, bool sort) { - struct config_entry_list *list = conf->entries; + struct config_entry_list *list = NULL; + struct config_include_list *includes = conf->includes; + + if (conf->reference) + { + pathname_make_slashes_portable(conf->reference); + fprintf(file, "#reference \"%s\"\n", conf->reference); + } + + if (sort) + list = config_file_merge_sort_linked_list( + (struct config_entry_list*)conf->entries, + config_file_sort_compare_func); + else + list = (struct config_entry_list*)conf->entries; + + conf->entries = list; while (list) { - if (!strcmp(entry, list->key)) - return true; + if (!list->readonly && list->key) + fprintf(file, "%s = \"%s\"\n", list->key, list->value); list = list->next; } - return false; + /* Config files are read from the top down - if + * duplicate entries are found then the topmost + * one in the list takes precedence. This means + * '#include' directives must go *after* individual + * config entries, otherwise they will override + * any custom-set values */ + while (includes) + { + fprintf(file, "#include \"%s\"\n", includes->path); + includes = includes->next; + } +} + +bool config_entry_exists(config_file_t *conf, const char *entry) +{ + return (bool)RHMAP_HAS_STR(conf->entries_map, entry); } bool config_get_entry_list_head(config_file_t *conf, @@ -948,3 +1493,13 @@ bool config_get_entry_list_next(struct config_file_entry *entry) return true; } +bool config_file_exists(const char *path) +{ + config_file_t conf; + config_file_initialize(&conf); + if (config_file_load_internal(&conf, path, 0, NULL) == 1) + return false; + + config_file_deinitialize(&conf); + return true; +} diff --git a/PVSupport/Sources/retro/libretro-common/file/config_file_userdata.c b/PVSupport/Sources/retro/libretro-common/file/config_file_userdata.c new file mode 100644 index 0000000000..799ce5f2a4 --- /dev/null +++ b/PVSupport/Sources/retro/libretro-common/file/config_file_userdata.c @@ -0,0 +1,171 @@ +/* Copyright (C) 2010-2020 The RetroArch team + * + * --------------------------------------------------------------------------------------- + * The following license statement only applies to this file (config_file_userdata.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 + +int config_userdata_get_float(void *userdata, const char *key_str, + float *value, float default_value) +{ + bool got; + char key[2][256]; + struct config_file_userdata *usr = (struct config_file_userdata*)userdata; + + fill_pathname_join_delim(key[0], usr->prefix[0], key_str, '_', sizeof(key[0])); + fill_pathname_join_delim(key[1], usr->prefix[1], key_str, '_', sizeof(key[1])); + + got = config_get_float (usr->conf, key[0], value); + got = got || config_get_float(usr->conf, key[1], value); + + if (!got) + *value = default_value; + return got; +} + +int config_userdata_get_int(void *userdata, const char *key_str, + int *value, int default_value) +{ + bool got; + char key[2][256]; + struct config_file_userdata *usr = (struct config_file_userdata*)userdata; + + fill_pathname_join_delim(key[0], usr->prefix[0], key_str, '_', sizeof(key[0])); + fill_pathname_join_delim(key[1], usr->prefix[1], key_str, '_', sizeof(key[1])); + + got = config_get_int (usr->conf, key[0], value); + got = got || config_get_int(usr->conf, key[1], value); + + if (!got) + *value = default_value; + return got; +} + +int config_userdata_get_hex(void *userdata, const char *key_str, + unsigned *value, unsigned default_value) +{ + bool got; + char key[2][256]; + struct config_file_userdata *usr = (struct config_file_userdata*)userdata; + + fill_pathname_join_delim(key[0], usr->prefix[0], key_str, '_', sizeof(key[0])); + fill_pathname_join_delim(key[1], usr->prefix[1], key_str, '_', sizeof(key[1])); + + got = config_get_hex(usr->conf, key[0], value); + got = got || config_get_hex(usr->conf, key[1], value); + + if (!got) + *value = default_value; + return got; +} + +int config_userdata_get_float_array(void *userdata, const char *key_str, + float **values, unsigned *out_num_values, + const float *default_values, unsigned num_default_values) +{ + char key[2][256]; + struct config_file_userdata *usr = (struct config_file_userdata*)userdata; + char *str = NULL; + + fill_pathname_join_delim(key[0], usr->prefix[0], key_str, '_', sizeof(key[0])); + fill_pathname_join_delim(key[1], usr->prefix[1], key_str, '_', sizeof(key[1])); + + if ( config_get_string(usr->conf, key[0], &str) || + config_get_string(usr->conf, key[1], &str)) + { + unsigned i; + struct string_list list = {0}; + string_list_initialize(&list); + string_split_noalloc(&list, str, " "); + *values = (float*)calloc(list.size, sizeof(float)); + for (i = 0; i < list.size; i++) + (*values)[i] = (float)strtod(list.elems[i].data, NULL); + *out_num_values = (unsigned)list.size; + string_list_deinitialize(&list); + free(str); + return true; + } + + *values = (float*)calloc(num_default_values, sizeof(float)); + memcpy(*values, default_values, sizeof(float) * num_default_values); + *out_num_values = num_default_values; + return false; +} + +int config_userdata_get_int_array(void *userdata, const char *key_str, + int **values, unsigned *out_num_values, + const int *default_values, unsigned num_default_values) +{ + char key[2][256]; + struct config_file_userdata *usr = (struct config_file_userdata*)userdata; + char *str = NULL; + fill_pathname_join_delim(key[0], usr->prefix[0], key_str, '_', sizeof(key[0])); + fill_pathname_join_delim(key[1], usr->prefix[1], key_str, '_', sizeof(key[1])); + + if ( config_get_string(usr->conf, key[0], &str) || + config_get_string(usr->conf, key[1], &str)) + { + unsigned i; + struct string_list list = {0}; + string_list_initialize(&list); + string_split_noalloc(&list, str, " "); + *values = (int*)calloc(list.size, sizeof(int)); + for (i = 0; i < list.size; i++) + (*values)[i] = (int)strtod(list.elems[i].data, NULL); + *out_num_values = (unsigned)list.size; + string_list_deinitialize(&list); + free(str); + return true; + } + + *values = (int*)calloc(num_default_values, sizeof(int)); + memcpy(*values, default_values, sizeof(int) * num_default_values); + *out_num_values = num_default_values; + return false; +} + +int config_userdata_get_string(void *userdata, const char *key_str, + char **output, const char *default_output) +{ + char key[2][256]; + struct config_file_userdata *usr = (struct config_file_userdata*)userdata; + char *str = NULL; + fill_pathname_join_delim(key[0], usr->prefix[0], key_str, '_', sizeof(key[0])); + fill_pathname_join_delim(key[1], usr->prefix[1], key_str, '_', sizeof(key[1])); + + if ( config_get_string(usr->conf, key[0], &str) || + config_get_string(usr->conf, key[1], &str)) + { + *output = str; + return true; + } + + *output = strdup(default_output); + return false; +} + +void config_userdata_free(void *ptr) +{ + if (ptr) + free(ptr); +} diff --git a/PVSupport/Sources/retro/libretro-common/file/file_path.c b/PVSupport/Sources/retro/libretro-common/file/file_path.c index 2f2a7e772d..f3e32b9bf1 100644 --- a/PVSupport/Sources/retro/libretro-common/file/file_path.c +++ b/PVSupport/Sources/retro/libretro-common/file/file_path.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2010-2016 The RetroArch team +/* Copyright (C) 2010-2020 The RetroArch team * * --------------------------------------------------------------------------------------- * The following license statement only applies to this file (file_path.c). @@ -20,68 +20,124 @@ * 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 +#include +#include