├── .github ├── FUNDING.yml └── workflows │ └── main.yml ├── .gitignore ├── CMakeLists.txt ├── LICENSE.txt ├── README.txt ├── docs ├── CHANGELOG.txt ├── CREDITS.txt ├── Doxyfile ├── INSTALL.txt ├── README-API-documentation.txt └── TODO.txt ├── extras ├── README-CSharp.txt ├── abs-file.h ├── buildbot-checker.sh ├── buildbot-emscripten.sh ├── buildbot-os2.sh ├── buildbot-raspberrypi.sh ├── casefolding.txt ├── globbing.c ├── globbing.h ├── ignorecase.c ├── ignorecase.h ├── makecasefoldhashtable.pl ├── physfs.pc.in ├── physfshttpd.c ├── physfsrwops.c ├── physfsrwops.h ├── physfsunpack.c ├── selfextract.c └── uninstall.sh ├── src ├── physfs.c ├── physfs.h ├── physfs_archiver_7z.c ├── physfs_archiver_dir.c ├── physfs_archiver_grp.c ├── physfs_archiver_hog.c ├── physfs_archiver_iso9660.c ├── physfs_archiver_mvl.c ├── physfs_archiver_qpak.c ├── physfs_archiver_slb.c ├── physfs_archiver_unpacked.c ├── physfs_archiver_vdf.c ├── physfs_archiver_wad.c ├── physfs_archiver_zip.c ├── physfs_byteorder.c ├── physfs_casefolding.h ├── physfs_internal.h ├── physfs_lzmasdk.h ├── physfs_miniz.h ├── physfs_platform_android.c ├── physfs_platform_apple.m ├── physfs_platform_haiku.cpp ├── physfs_platform_os2.c ├── physfs_platform_posix.c ├── physfs_platform_qnx.c ├── physfs_platform_unix.c ├── physfs_platform_windows.c ├── physfs_platform_winrt.cpp ├── physfs_platforms.h └── physfs_unicode.c └── test └── test_physfs.c /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | github: [icculus] 2 | patreon: icculus 3 | -------------------------------------------------------------------------------- /.github/workflows/main.yml: -------------------------------------------------------------------------------- 1 | name: Build 2 | 3 | on: [push, pull_request] 4 | 5 | jobs: 6 | Build: 7 | name: ${{ matrix.platform.name }} 8 | runs-on: ${{ matrix.platform.os }} 9 | strategy: 10 | matrix: 11 | platform: # !!! FIXME: figure out an efficient way to get SDL2 on the Windows/Mac bots. 12 | - { name: Linux, os: ubuntu-20.04, flags: -GNinja } 13 | - { name: Windows, os: windows-latest } 14 | - { name: MacOS, os: macos-latest } 15 | steps: 16 | - name: Setup Linux dependencies 17 | if: runner.os == 'Linux' 18 | run: | 19 | sudo apt-get update 20 | sudo apt-get install cmake ninja-build 21 | - name: Get PhysicsFS sources 22 | uses: actions/checkout@v2 23 | - name: Configure CMake 24 | run: cmake -B build ${{ matrix.platform.flags }} 25 | - name: Build 26 | run: cmake --build build/ 27 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | cmake-build 2 | 3 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # PhysicsFS; a portable, flexible file i/o abstraction. 2 | # 3 | # Please see the file LICENSE.txt in the source's root directory. 4 | 5 | # The CMake project file is meant to get this compiling on all sorts of 6 | # platforms quickly, and serve as the way Unix platforms and Linux distros 7 | # package up official builds, but you don't _need_ to use this; we have 8 | # built PhysicsFS to (hopefully) be able to drop into your project and 9 | # compile, using preprocessor checks for platform-specific bits instead of 10 | # testing in here. 11 | 12 | cmake_minimum_required(VERSION 2.8.12) 13 | 14 | project(PhysicsFS) 15 | set(PHYSFS_VERSION 3.1.0) 16 | 17 | # Increment this if/when we break backwards compatibility. 18 | set(PHYSFS_SOVERSION 1) 19 | 20 | # I hate that they define "WIN32" ... we're about to move to Win64...I hope! 21 | if(WIN32 AND NOT WINDOWS) 22 | set(WINDOWS TRUE) 23 | endif() 24 | 25 | include_directories(./src) 26 | 27 | if(APPLE) 28 | set(OTHER_LDFLAGS ${OTHER_LDFLAGS} "-framework IOKit -framework Foundation") 29 | set(PHYSFS_M_SRCS src/physfs_platform_apple.m) 30 | endif() 31 | 32 | if(CMAKE_COMPILER_IS_GNUCC) 33 | # Don't use -rpath. 34 | set(CMAKE_SKIP_RPATH ON CACHE BOOL "Skip RPATH" FORCE) 35 | endif() 36 | 37 | if(CMAKE_C_COMPILER_ID STREQUAL "SunPro") 38 | add_definitions(-erroff=E_EMPTY_TRANSLATION_UNIT) 39 | add_definitions(-xldscope=hidden) 40 | endif() 41 | 42 | if(HAIKU) 43 | # We add this explicitly, since we don't want CMake to think this 44 | # is a C++ project unless we're on Haiku. 45 | set(PHYSFS_CPP_SRCS src/physfs_platform_haiku.cpp) 46 | find_library(BE_LIBRARY be) 47 | find_library(ROOT_LIBRARY root) 48 | set(OPTIONAL_LIBRARY_LIBS ${OPTIONAL_LIBRARY_LIBS} ${BE_LIBRARY} ${ROOT_LIBRARY}) 49 | endif() 50 | 51 | if(CMAKE_SYSTEM_NAME STREQUAL "WindowsPhone" OR CMAKE_SYSTEM_NAME STREQUAL "WindowsStore") 52 | set(WINRT TRUE) 53 | endif() 54 | 55 | if(WINRT) 56 | set(PHYSFS_CPP_SRCS src/physfs_platform_winrt.cpp) 57 | endif() 58 | 59 | if(UNIX AND NOT WINDOWS AND NOT APPLE) # (MingW and such might be UNIX _and_ WINDOWS!) 60 | find_library(PTHREAD_LIBRARY pthread) 61 | if(PTHREAD_LIBRARY) 62 | set(OPTIONAL_LIBRARY_LIBS ${OPTIONAL_LIBRARY_LIBS} ${PTHREAD_LIBRARY}) 63 | endif() 64 | endif() 65 | 66 | # Almost everything is "compiled" here, but things that don't apply to the 67 | # build are #ifdef'd out. This is to make it easy to embed PhysicsFS into 68 | # another project or bring up a new build system: just compile all the source 69 | # code and #define the things you want. 70 | set(PHYSFS_SRCS 71 | src/physfs.c 72 | src/physfs_byteorder.c 73 | src/physfs_unicode.c 74 | src/physfs_platform_posix.c 75 | src/physfs_platform_unix.c 76 | src/physfs_platform_windows.c 77 | src/physfs_platform_os2.c 78 | src/physfs_platform_qnx.c 79 | src/physfs_platform_android.c 80 | src/physfs_archiver_dir.c 81 | src/physfs_archiver_unpacked.c 82 | src/physfs_archiver_grp.c 83 | src/physfs_archiver_hog.c 84 | src/physfs_archiver_7z.c 85 | src/physfs_archiver_mvl.c 86 | src/physfs_archiver_qpak.c 87 | src/physfs_archiver_wad.c 88 | src/physfs_archiver_zip.c 89 | src/physfs_archiver_slb.c 90 | src/physfs_archiver_iso9660.c 91 | src/physfs_archiver_vdf.c 92 | ${PHYSFS_CPP_SRCS} 93 | ${PHYSFS_M_SRCS} 94 | ) 95 | 96 | 97 | # Archivers ... 98 | # These are (mostly) on by default now, so these options are only useful for 99 | # disabling them. 100 | 101 | option(PHYSFS_ARCHIVE_ZIP "Enable ZIP support" TRUE) 102 | if(NOT PHYSFS_ARCHIVE_ZIP) 103 | add_definitions(-DPHYSFS_SUPPORTS_ZIP=0) 104 | endif() 105 | 106 | option(PHYSFS_ARCHIVE_7Z "Enable 7zip support" TRUE) 107 | if(NOT PHYSFS_ARCHIVE_7Z) 108 | add_definitions(-DPHYSFS_SUPPORTS_7Z=0) 109 | endif() 110 | 111 | option(PHYSFS_ARCHIVE_GRP "Enable Build Engine GRP support" TRUE) 112 | if(NOT PHYSFS_ARCHIVE_GRP) 113 | add_definitions(-DPHYSFS_SUPPORTS_GRP=0) 114 | endif() 115 | 116 | option(PHYSFS_ARCHIVE_WAD "Enable Doom WAD support" TRUE) 117 | if(NOT PHYSFS_ARCHIVE_WAD) 118 | add_definitions(-DPHYSFS_SUPPORTS_WAD=0) 119 | endif() 120 | 121 | option(PHYSFS_ARCHIVE_HOG "Enable Descent I/II HOG support" TRUE) 122 | if(NOT PHYSFS_ARCHIVE_HOG) 123 | add_definitions(-DPHYSFS_SUPPORTS_HOG=0) 124 | endif() 125 | 126 | option(PHYSFS_ARCHIVE_MVL "Enable Descent I/II MVL support" TRUE) 127 | if(NOT PHYSFS_ARCHIVE_MVL) 128 | add_definitions(-DPHYSFS_SUPPORTS_MVL=0) 129 | endif() 130 | 131 | option(PHYSFS_ARCHIVE_QPAK "Enable Quake I/II QPAK support" TRUE) 132 | if(NOT PHYSFS_ARCHIVE_QPAK) 133 | add_definitions(-DPHYSFS_SUPPORTS_QPAK=0) 134 | endif() 135 | 136 | option(PHYSFS_ARCHIVE_SLB "Enable I-War / Independence War SLB support" TRUE) 137 | if(NOT PHYSFS_ARCHIVE_SLB) 138 | add_definitions(-DPHYSFS_SUPPORTS_SLB=0) 139 | endif() 140 | 141 | option(PHYSFS_ARCHIVE_ISO9660 "Enable ISO9660 support" TRUE) 142 | if(NOT PHYSFS_ARCHIVE_ISO9660) 143 | add_definitions(-DPHYSFS_SUPPORTS_ISO9660=0) 144 | endif() 145 | 146 | option(PHYSFS_ARCHIVE_VDF "Enable Gothic I/II VDF archive support" TRUE) 147 | if(NOT PHYSFS_ARCHIVE_VDF) 148 | add_definitions(-DPHYSFS_SUPPORTS_VDF=0) 149 | endif() 150 | 151 | 152 | option(PHYSFS_BUILD_STATIC "Build static library" TRUE) 153 | if(PHYSFS_BUILD_STATIC) 154 | add_library(physfs-static STATIC ${PHYSFS_SRCS}) 155 | # Don't rename this on Windows, since DLLs will also produce an import 156 | # library named "physfs.lib" which would conflict; Unix tend to like the 157 | # same library name with a different extension for static libs, but 158 | # Windows can just have a separate name. 159 | if(NOT MSVC) 160 | set_target_properties(physfs-static PROPERTIES OUTPUT_NAME "physfs") 161 | endif() 162 | if(WINRT) 163 | # Ignore LNK4264 warnings; we don't author any WinRT components, just consume them, so we're okay in a static library. 164 | set_target_properties(physfs-static PROPERTIES VS_WINRT_COMPONENT True) 165 | set_target_properties(physfs-static PROPERTIES STATIC_LIBRARY_FLAGS "/ignore:4264") 166 | endif() 167 | 168 | set(PHYSFS_LIB_TARGET physfs-static) 169 | set(PHYSFS_INSTALL_TARGETS ${PHYSFS_INSTALL_TARGETS} ";physfs-static") 170 | endif() 171 | 172 | option(PHYSFS_BUILD_SHARED "Build shared library" TRUE) 173 | if(PHYSFS_BUILD_SHARED) 174 | add_library(physfs SHARED ${PHYSFS_SRCS}) 175 | set_target_properties(physfs PROPERTIES MACOSX_RPATH 1) 176 | set_target_properties(physfs PROPERTIES VERSION ${PHYSFS_VERSION}) 177 | set_target_properties(physfs PROPERTIES SOVERSION ${PHYSFS_SOVERSION}) 178 | if(WINRT) 179 | set_target_properties(physfs PROPERTIES VS_WINRT_COMPONENT True) 180 | endif() 181 | target_link_libraries(physfs ${OPTIONAL_LIBRARY_LIBS} ${OTHER_LDFLAGS}) 182 | set(PHYSFS_LIB_TARGET physfs) 183 | set(PHYSFS_INSTALL_TARGETS ${PHYSFS_INSTALL_TARGETS} ";physfs") 184 | endif() 185 | 186 | if(NOT PHYSFS_BUILD_SHARED AND NOT PHYSFS_BUILD_STATIC) 187 | message(FATAL "Both shared and static libraries are disabled!") 188 | endif() 189 | 190 | # CMake FAQ says I need this... 191 | if(PHYSFS_BUILD_SHARED AND PHYSFS_BUILD_STATIC AND NOT WINDOWS) 192 | set_target_properties(physfs PROPERTIES CLEAN_DIRECT_OUTPUT 1) 193 | set_target_properties(physfs-static PROPERTIES CLEAN_DIRECT_OUTPUT 1) 194 | endif() 195 | 196 | option(PHYSFS_BUILD_TEST "Build stdio test program." TRUE) 197 | mark_as_advanced(PHYSFS_BUILD_TEST) 198 | if(PHYSFS_BUILD_TEST) 199 | find_path(READLINE_H readline/readline.h) 200 | find_path(HISTORY_H readline/history.h) 201 | if(READLINE_H AND HISTORY_H) 202 | find_library(CURSES_LIBRARY NAMES curses ncurses) 203 | set(CMAKE_REQUIRED_LIBRARIES ${CURSES_LIBRARY}) 204 | find_library(READLINE_LIBRARY readline) 205 | if(READLINE_LIBRARY) 206 | set(HAVE_SYSTEM_READLINE TRUE) 207 | set(TEST_PHYSFS_LIBS ${TEST_PHYSFS_LIBS} ${READLINE_LIBRARY} ${CURSES_LIBRARY}) 208 | include_directories(SYSTEM ${READLINE_H} ${HISTORY_H}) 209 | add_definitions(-DPHYSFS_HAVE_READLINE=1) 210 | endif() 211 | endif() 212 | add_executable(test_physfs test/test_physfs.c) 213 | target_link_libraries(test_physfs ${PHYSFS_LIB_TARGET} ${TEST_PHYSFS_LIBS} ${OTHER_LDFLAGS}) 214 | set(PHYSFS_INSTALL_TARGETS ${PHYSFS_INSTALL_TARGETS} ";test_physfs") 215 | endif() 216 | 217 | install(TARGETS ${PHYSFS_INSTALL_TARGETS} EXPORT PhysFSExport 218 | RUNTIME DESTINATION bin 219 | LIBRARY DESTINATION lib${LIB_SUFFIX} 220 | ARCHIVE DESTINATION lib${LIB_SUFFIX} 221 | INCLUDES DESTINATION include) 222 | install(FILES src/physfs.h DESTINATION include) 223 | 224 | install(EXPORT PhysFSExport 225 | DESTINATION "lib${LIB_SUFFIX}/cmake/PhysFS" 226 | FILE PhysFSConfig.cmake 227 | ) 228 | 229 | 230 | find_package(Doxygen) 231 | if(DOXYGEN_FOUND) 232 | set(PHYSFS_OUTPUT_DOXYFILE "${CMAKE_CURRENT_BINARY_DIR}/Doxyfile") 233 | configure_file( 234 | "${CMAKE_CURRENT_SOURCE_DIR}/docs/Doxyfile" 235 | "${PHYSFS_OUTPUT_DOXYFILE}" 236 | COPYONLY 237 | ) 238 | file(APPEND "${PHYSFS_OUTPUT_DOXYFILE}" "\n\n# Below auto-generated by cmake...\n\n") 239 | file(APPEND "${PHYSFS_OUTPUT_DOXYFILE}" "PROJECT_NUMBER = \"${PHYSFS_VERSION}\"\n") 240 | file(APPEND "${PHYSFS_OUTPUT_DOXYFILE}" "OUTPUT_DIRECTORY = \"${CMAKE_CURRENT_BINARY_DIR}/docs\"\n") 241 | file(APPEND "${PHYSFS_OUTPUT_DOXYFILE}" "\n# End auto-generated section.\n\n") 242 | 243 | set(PHYSFS_TARGETNAME_DOCS "docs" CACHE STRING "Name of 'docs' build target") 244 | add_custom_target( 245 | ${PHYSFS_TARGETNAME_DOCS} 246 | ${DOXYGEN_EXECUTABLE} "${PHYSFS_OUTPUT_DOXYFILE}" 247 | WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}" 248 | COMMENT "Building documentation in 'docs' directory..." 249 | ) 250 | else() 251 | message(STATUS "Doxygen not found. You won't be able to build documentation.") 252 | endif() 253 | 254 | if(UNIX) 255 | set(PHYSFS_TARBALL "${CMAKE_CURRENT_SOURCE_DIR}/../physfs-${PHYSFS_VERSION}.tar.gz") 256 | 257 | set(PHYSFS_TARGETNAME_DIST "dist" CACHE STRING "Name of 'dist' build target") 258 | add_custom_target( 259 | ${PHYSFS_TARGETNAME_DIST} 260 | git archive --prefix="physfs-${PHYSFS_VERSION}/" --output="${PHYSFS_TARBALL}" HEAD 261 | WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}" 262 | COMMENT "Building source tarball '${PHYSFS_TARBALL}'..." 263 | ) 264 | 265 | set(PHYSFS_TARGETNAME_UNINSTALL "uninstall" CACHE STRING "Name of 'uninstall' build target") 266 | add_custom_target( 267 | ${PHYSFS_TARGETNAME_UNINSTALL} 268 | "${CMAKE_CURRENT_SOURCE_DIR}/extras/uninstall.sh" 269 | WORKING_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}" 270 | COMMENT "Uninstall the project..." 271 | ) 272 | endif() 273 | 274 | if(NOT MSVC) 275 | configure_file( 276 | "extras/physfs.pc.in" 277 | "extras/physfs.pc" 278 | @ONLY 279 | ) 280 | install( 281 | FILES "${CMAKE_CURRENT_BINARY_DIR}/extras/physfs.pc" 282 | DESTINATION "lib${LIB_SUFFIX}/pkgconfig" 283 | ) 284 | endif() 285 | 286 | macro(message_bool_option _NAME _VALUE) 287 | if(${_VALUE}) 288 | message(STATUS " ${_NAME}: enabled") 289 | else() 290 | message(STATUS " ${_NAME}: disabled") 291 | endif() 292 | endmacro() 293 | 294 | message(STATUS "PhysicsFS will build with the following options:") 295 | message_bool_option("ZIP support" PHYSFS_ARCHIVE_ZIP) 296 | message_bool_option("7zip support" PHYSFS_ARCHIVE_7Z) 297 | message_bool_option("GRP support" PHYSFS_ARCHIVE_GRP) 298 | message_bool_option("WAD support" PHYSFS_ARCHIVE_WAD) 299 | message_bool_option("HOG support" PHYSFS_ARCHIVE_HOG) 300 | message_bool_option("MVL support" PHYSFS_ARCHIVE_MVL) 301 | message_bool_option("QPAK support" PHYSFS_ARCHIVE_QPAK) 302 | message_bool_option("SLB support" PHYSFS_ARCHIVE_SLB) 303 | message_bool_option("VDF support" PHYSFS_ARCHIVE_VDF) 304 | message_bool_option("ISO9660 support" PHYSFS_ARCHIVE_ISO9660) 305 | message_bool_option("Build static library" PHYSFS_BUILD_STATIC) 306 | message_bool_option("Build shared library" PHYSFS_BUILD_SHARED) 307 | message_bool_option("Build stdio test program" PHYSFS_BUILD_TEST) 308 | if(PHYSFS_BUILD_TEST) 309 | message_bool_option(" Use readline in test program" HAVE_SYSTEM_READLINE) 310 | endif() 311 | 312 | # end of CMakeLists.txt ... 313 | 314 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) 2001-2021 Ryan C. Gordon and others. 2 | 3 | This software is provided 'as-is', without any express or implied 4 | warranty. In no event will the authors be held liable for any damages 5 | arising from the use of this software. 6 | 7 | Permission is granted to anyone to use this software for any purpose, 8 | including commercial applications, and to alter it and redistribute it 9 | freely, subject to the following restrictions: 10 | 11 | 1. The origin of this software must not be misrepresented; you must not 12 | claim that you wrote the original software. If you use this software 13 | in a product, an acknowledgment in the product documentation would be 14 | appreciated but is not required. 15 | 2. Altered source versions must be plainly marked as such, and must not be 16 | misrepresented as being the original software. 17 | 3. This notice may not be removed or altered from any source distribution. 18 | -------------------------------------------------------------------------------- /README.txt: -------------------------------------------------------------------------------- 1 | 2 | PhysicsFS; a portable, flexible file i/o abstraction. 3 | 4 | https://icculus.org/physfs/ 5 | 6 | Please see the docs directory for documentation. 7 | 8 | Please see LICENSE.txt for licensing information. 9 | 10 | -------------------------------------------------------------------------------- /docs/CHANGELOG.txt: -------------------------------------------------------------------------------- 1 | 2 | The changelog is no longer maintained by hand. It made sense to have a single 3 | timeline when we were using CVS, but modern revision control tools make this 4 | redundant, at best. 5 | 6 | If you want a list of changes, updated in real time, just point your web 7 | browser here: 8 | 9 | https://github.com/icculus/physfs/commits/ 10 | 11 | 12 | -------------------------------------------------------------------------------- /docs/CREDITS.txt: -------------------------------------------------------------------------------- 1 | Maintainer and general codemonkey: 2 | Ryan C. Gordon 3 | 4 | Tons of win32 help: 5 | Adam Gates 6 | 7 | More win32 hacking: 8 | Gregory S. Read 9 | 10 | Fixes for missing current working directories, 11 | PHYSFS_setSaneConfig() improvements, 12 | other bugfixes: 13 | David Hedbor 14 | 15 | Darwin support: 16 | Patrick Stein 17 | 18 | configure fixes, 19 | RPM specfile: 20 | Edward Rudd 21 | 22 | GetLastModTime API, 23 | other stuff: 24 | John R. Hall 25 | 26 | Various support, fixes and suggestions: 27 | Alexander Pipelka 28 | 29 | Russian translation, 30 | QPAK archiver: 31 | Ed Sinjiashvili 32 | 33 | French translation: 34 | Stéphane Peter 35 | 36 | Debian package support: 37 | Colin Bayer 38 | 39 | "abs-file.h" in "extras" dir: 40 | Adam D. Moss 41 | 42 | WinCE port and other Win32 patches: 43 | Corona688 44 | 45 | German translation: 46 | Michael Renner 47 | 48 | Apple Project Builder support, 49 | Mac OS X improvements: 50 | Eric Wing 51 | 52 | iPhone support: 53 | Christian Gmeiner 54 | 55 | WinRT support: 56 | Martin Ahrnbom 57 | 58 | HOG archiver, 59 | MVL archiver: 60 | Bradley Bell 61 | 62 | MIX archiver: 63 | Sebastian Steinhauer 64 | 65 | Bug fixes: 66 | Tolga Dalman 67 | 68 | Initial PHYSFS_mount() work: 69 | Philip D. Bober 70 | 71 | Brazillian Portuguese translation: 72 | Danny Angelo Carminati Grein 73 | 74 | Spanish translation: 75 | Pedro J. Pérez 76 | 77 | MacOS Classic fixes, 78 | MPW support, 79 | bug fixes: 80 | Chris Taylor 81 | 82 | Mingw support, 83 | General bug fixes: 84 | Matze Braun 85 | 86 | Haiku support: 87 | scott mc 88 | 89 | Bug fixes: 90 | Jörg Walter 91 | 92 | Bug fixes: 93 | Olivier Boudeville 94 | 95 | Bug fixes: 96 | Henk Boom 97 | 98 | Build system fixes: 99 | Marc Kleine-Budde 100 | 101 | Windows .rc file, 102 | 7zip/lzma archiver: 103 | Dennis Schridde 104 | 105 | OS/2 updates: 106 | Dave Yeo 107 | 108 | Bug fixes: 109 | Patrice Mandin 110 | 111 | PHYSFS_stat() API: 112 | Christoph Nelles 113 | Indy Sams 114 | 115 | ISO9660 archiver: 116 | Christoph Nelles 117 | 118 | Bug fixes: 119 | Steven Fuller 120 | 121 | Bug fixes: 122 | Tolga Dalman 123 | 124 | Bug fixes: 125 | Frank Becker 126 | 127 | Bug fixes: 128 | Norfanin 129 | 130 | Bug fixes: 131 | Evgeny Podjachev 132 | 133 | Haiku fixes: 134 | Chris Roberts 135 | 136 | SLB archiver: 137 | Aleksi Nurmi 138 | 139 | Bug fixes: 140 | Dmitry Marakasov 141 | 142 | Bug fixes: 143 | Andreas Karlsson 144 | 145 | Bug fixes: 146 | Michael Bacon 147 | 148 | Bug fixes: 149 | Xian Nox 150 | 151 | Bug fixes: 152 | Reto Schneider 153 | 154 | pkg-config support: 155 | Jonas Kulla 156 | 157 | Bug fixes, 158 | VDF archiver: 159 | Francesco Bertolaccini 160 | 161 | CMake fixes: 162 | Tobias Markus 163 | 164 | Bug fixes, 165 | Rémi Verschelde 166 | 167 | Bug fixes: 168 | Rob Loach 169 | 170 | Other stuff: 171 | Your name here! Patches go to icculus@icculus.org ... 172 | 173 | /* end of CREDITS.txt ... */ 174 | 175 | -------------------------------------------------------------------------------- /docs/INSTALL.txt: -------------------------------------------------------------------------------- 1 | 2 | The latest PhysicsFS information and releases can be found at: 3 | https://icculus.org/physfs/ 4 | 5 | Building is (ahem) very easy. 6 | 7 | 8 | ALL PLATFORMS: 9 | 10 | Please read the text file LICENSE.txt in the root of the source tree. 11 | The license is extremely liberal, even to closed-source, commercial 12 | applications. 13 | 14 | If you've got Doxygen (http://www.doxygen.org/) installed, you can run it 15 | without any command line arguments in the root of the source tree to generate 16 | the API reference (or build the "docs" target from your build system). This 17 | is optional. You can browse the API docs online here: 18 | 19 | https://icculus.org/physfs/docs/ 20 | 21 | 22 | 23 | BUILD IT WITH YOUR OWN PROGRAM: 24 | 25 | If you don't care about formal packaging: just add everything in the "src" 26 | directory to whatever you use to build your program and compile it along with 27 | everything else, and you're done. It should compile with any reasonable 28 | ANSI C compiler, should build cleanly even with excessive compiler warnings 29 | enabled, needs no extra configuration, and allows static linking. 30 | WinRT and Haiku need C++ compilers for their system APIs, but if you aren't on 31 | these platforms and don't have a C++ compiler, don't build the .cpp files. 32 | Likewise: Apple platforms (macOS, iOS, etc) need an Objective-C compiler, but 33 | if you aren't on these platforms and don't have a Objective-C compiler, don't 34 | build the .m file. Everything you need is in the .c sources. 35 | 36 | If this all worked for your specific project, you can stop reading now. 37 | 38 | 39 | 40 | Unix: 41 | 42 | You will need CMake (https://www.cmake.org/) 2.4 or later installed. 43 | 44 | Make a directory, wherever you like. This will be your build directory. 45 | 46 | Chdir to your build directory. Run "cmake /where/i/unpacked/physfs" to 47 | generate Makefiles. You can then run "ccmake ." and customize the build, 48 | but the defaults are probably okay. You can have CMake generate KDevelop 49 | or Ninja project files or whatever, if you prefer these. 50 | 51 | Run "make". PhysicsFS will now build. 52 | 53 | As root, run "make install". 54 | If you get sick of the library, run "make uninstall" as root 55 | and it will remove all traces of the library from the system paths. 56 | 57 | Once you are satisfied, you can delete the build directory. 58 | 59 | Primary Unix development is done with GNU/Linux, but PhysicsFS is known to 60 | work out of the box with several flavors of Unix. It it doesn't work, patches 61 | to get it running can be sent to icculus@icculus.org. 62 | 63 | 64 | Windows: 65 | 66 | If building with Cygwin, mingw32, MSYS, or something else that uses the GNU 67 | toolchain, follow the Unix instructions, above. 68 | 69 | If you want to use Visual Studio, nmake, or the Platform SDK, you will need 70 | CMake (https://www.cmake.org/) 2.4 or later installed. Point CMake at the 71 | CMakeLists.txt file in the root of the source directory and hit the 72 | "Configure" button. After telling it what type of compiler you are targeting 73 | (Borland, Visual Studio, etc), CMake will process for while and then give you 74 | a list of options you can change (what archivers you want to support, etc). 75 | If you aren't sure, the defaults are probably fine. Hit the "Configure" 76 | button again, then "OK" once configuration has completed with options that 77 | match your liking. Now project files for your favorite programming 78 | environment will be generated for you in the directory you specified. 79 | Go there and use them to build PhysicsFS. 80 | 81 | PhysicsFS will only link directly against system libraries that have existed 82 | since Windows NT 3.51. If there's a newer API we want to use, we try to 83 | dynamically load it at runtime and fallback to a reasonable behaviour when 84 | we can't find it. Note that Windows 98 and later _should_ 85 | work if you use the Microsoft Layer for Unicode (UNICOWS.DLL) to provide 86 | some missing system APIs, but this is no longer tested as of PhysicsFS 2.1.0. 87 | PhysicsFS 2.0.x is known to work with Windows 95 without UNICOWS.DLL. 88 | 89 | PhysicsFS works on 32-bit and 64-bit Windows. There is no 16-bit Windows 90 | support at all. Windows RT is covered below. 91 | 92 | 93 | Windows RT: 94 | 95 | Windows RT (Windows Phone, Windows Store, UWP) 8.0 and later are supported. 96 | Make sure you include both physfs_platform_windows.c _and_ 97 | physfs_platform_winrt.cpp in your build, and that the C++ file has 98 | "Consume Windows Runtime Extension" set to "Yes" in its Visual Studio 99 | properties (from the command line, you want to compile this file with the 100 | "/ZW" compiler switch). CMake can, in theory, generate a project file for 101 | WinRT if you pick a recent Visual Studio target, choose manual cross-compile 102 | options, and set the system name to "WindowsPhone" or "WindowsStore" and the 103 | correct OS version (8.0 or later). 104 | 105 | 106 | PocketPC/WindowsCE: 107 | 108 | Support for PocketPC was removed in PhysicsFS 2.1.0. This was known to work 109 | in the 1.0 releases, but wasn't tested in 2.0 and later. PhysicsFS should 110 | work on modern Windows Phones (see "Windows RT" section). 111 | 112 | 113 | macOS: 114 | 115 | You will need CMake (https://www.cmake.org/) 2.4 or later installed. 116 | 117 | You can either generate a Unix makefile with CMake, or generate an Xcode 118 | project, whichever makes you more comfortable. 119 | 120 | PowerPC and Intel Macs should both be supported. 121 | 122 | 123 | MAC OS 8/9 ("Mac OS Classic"): 124 | 125 | Classic Mac OS support has been dropped in PhysicsFS 2.0. Apple hasn't updated 126 | pre-OSX versions in more than a decade at this point, none of the hardware 127 | they've shipped will boot it for almost as many years, and finding 128 | developer tools for it is becoming almost impossible. As the switch to Intel 129 | hardware has removed the "Classic" emulation environment, it was time to 130 | remove support from PhysicsFS. That being said, the PhysicsFS 1.0 branch can 131 | still target back to Mac OS 8.5, so you can use that if you need support for 132 | this legacy OS. We still very much support modern macOS, though: see above. 133 | 134 | 135 | Emscripten: 136 | 137 | Use the "Unix" instructions, above. You can install the Emscripten SDK and use 138 | the extras/buildbot-emscripten.sh script to automate this for you. 139 | 140 | 141 | BeOS, Zeta, YellowTab: 142 | 143 | BeOS support was dropped in PhysicsFS 2.1.0. Consider installing Haiku, which 144 | we still support. 145 | 146 | 147 | Haiku: 148 | 149 | Use the "Unix" instructions, above. 150 | 151 | 152 | OS/2: 153 | 154 | OS/2 is known to work with OpenWatcom and GCC-based compilers. I couldn't get 155 | an OS/2 port of CMake to generate OpenWatcom project files (although it should 156 | be able to do that in theory), it should be able to do Unix Makefiles with 157 | GCC. It might be easier to just compile PhysicsFS along with the rest of 158 | your project on this platform. 159 | 160 | 161 | 162 | OTHER PLATFORMS: 163 | 164 | Many Unix-like platforms might "just work" with CMake. Some of these platforms 165 | are known to have worked at one time, but have not been heavily tested, if 166 | tested at all. PhysicsFS is, as far as we know, 64-bit and byteorder clean, 167 | and is known to compile on several compilers across many platforms. To 168 | implement a new platform or archiver, please read the heavily-commented 169 | physfs_internal.h and look at the physfs_platform_* and physfs_archiver_* 170 | source files for examples. 171 | 172 | --ryan. (icculus@icculus.org) 173 | 174 | -------------------------------------------------------------------------------- /docs/README-API-documentation.txt: -------------------------------------------------------------------------------- 1 | The API documentation is readable in a few ways: 2 | 3 | - Read physfs.h; it's _heavily_ documented and the primary source of reference 4 | documentation for the library. 5 | - Run Doxygen over the header, which produces nicer-to-browse documentation in 6 | HTML, LaTeX, manpage, etc formats. This is done for you if Doxygen is 7 | installed and you build the "docs" target in whatever project files CMake 8 | generated for you. 9 | - Too much trouble? We generated the HTML reference for you, online here: 10 | 11 | https://icculus.org/physfs/docs/ 12 | 13 | - We would love well-written tutorials for the latest version of PhysicsFS! 14 | If you write one, we would love to list it here. Drop me a line about it: 15 | icculus@icculus.org ... Thanks! 16 | 17 | --ryan. 18 | 19 | -------------------------------------------------------------------------------- /docs/TODO.txt: -------------------------------------------------------------------------------- 1 | Stuff that needs to be done and wishlist: 2 | 3 | These are in no particular order. 4 | Some might be dupes, some might be done already, some might be bad ideas. 5 | 6 | 7 | From https://icculus.org/pipermail/physfs/2009-March/000698.html ... 8 | 9 | - Write support for various archives. I haven't decided how to do this yet, 10 | but I'd like to. 11 | - Add an API to expose a file's extended attributes to the application? 12 | - Deprecate PHYSFS_setSaneConfig(). It really should have been in the extras 13 | directory. 14 | - Clean up the sources to match my ever-changing coding style. :) 15 | 16 | 17 | 18 | From https://icculus.org/pipermail/physfs/2010-January/000826.html ... 19 | 20 | - Lua bindings 21 | 22 | 23 | From https://icculus.org/pipermail/physfs/2010-January/000833.html ... 24 | 25 | - SWIG bindings 26 | 27 | 28 | 29 | From old TODO.txt... 30 | 31 | - Other archivers: perhaps tar(.gz|.bz2), RPM, ARJ, etc. These are less 32 | important, since streaming archives aren't of much value to games (which 33 | is why zipfiles are king: random access), but it could have uses for, say, 34 | an installer/updater. 35 | - Do symlinks in zip archiver work when they point to dirs? 36 | - Enable more warnings? 37 | - Use __cdecl in physfs.h? 38 | - Look for FIXMEs (many marked with "!!!" in comments). 39 | - fscanf and fprintf support in extras dir. 40 | - Sanity check byte order at runtime. 41 | - Memory locking? 42 | - General code audit. 43 | - Multiple write dirs with mount points? 44 | 45 | 46 | Other stuff I thought of... 47 | - moar asserts! 48 | - constify! 49 | - Does iPhone work? 50 | - Fix CMake vs Doxygen. 51 | - Doxygen replacement? (manpages suck.) 52 | - Fix coding standards to match. 53 | - See if we can ditch some #include lines... 54 | - LZMA support in zip archiver? 55 | - bzip2 support in zip archiver? 56 | - Reduce the BAIL and GOTO macro use. A lot of these don't add anything. 57 | - Change the term "search path" to something less confusing. 58 | 59 | Probably other stuff. Requests and recommendations are welcome. 60 | 61 | // end of TODO.txt ... 62 | 63 | -------------------------------------------------------------------------------- /extras/README-CSharp.txt: -------------------------------------------------------------------------------- 1 | There used to be C# bindings in this directory, but they have been 2 | unmaintained for many years. 3 | 4 | Instead, there is a more modern PhysicsFS wrapper for .NET available. 5 | 6 | You can find it at https://github.com/frabert/SharpPhysFS 7 | 8 | Thanks to Francesco Bertolaccini for his efforts on that project! 9 | 10 | --ryan. 11 | 12 | -------------------------------------------------------------------------------- /extras/abs-file.h: -------------------------------------------------------------------------------- 1 | /* 2 | * stdio/physfs abstraction layer 2003-04-02 3 | * 4 | * Adam D. Moss 5 | * 6 | * These wrapper macros and functions are designed to allow a program 7 | * to perform file I/O with identical semantics and syntax regardless 8 | * of whether PhysicsFS is being used or not. 9 | */ 10 | #ifndef _ABS_FILE_H 11 | #define _ABS_FILE_H 12 | /* 13 | PLEASE NOTE: This license applies to abs-file.h ONLY (to make it clear that 14 | you may embed this wrapper code within commercial software); PhysicsFS itself 15 | is (at the time of writing) released under a different license with 16 | additional restrictions. 17 | 18 | Copyright (C) 2002-2003 Adam D. Moss (the "Author"). All Rights Reserved. 19 | 20 | Permission is hereby granted, free of charge, to any person obtaining a copy 21 | of this software and associated documentation files (the "Software"), to deal 22 | in the Software without restriction, including without limitation the rights 23 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 24 | copies of the Software, and to permit persons to whom the Software is fur- 25 | nished to do so, subject to the following conditions: 26 | 27 | The above copyright notice and this permission notice shall be included in 28 | all copies or substantial portions of the Software. 29 | 30 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 31 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FIT- 32 | NESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 33 | AUTHOR BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 34 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CON- 35 | NECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 36 | 37 | Except as contained in this notice, the name of the Author of the 38 | Software shall not be used in advertising or otherwise to promote the sale, 39 | use or other dealings in this Software without prior written authorization 40 | from the Author. 41 | */ 42 | 43 | #include 44 | #include 45 | 46 | /* 47 | * API: 48 | * 49 | * Macro/function use like stdio equivalent... 50 | * -------------- ---------------------------- 51 | * MY_FILETYPE FILE 52 | * MY_OPEN_FOR_READ fopen(..., "rb") 53 | * MY_READ fread(...) 54 | * MY_CLOSE fclose(...) 55 | * MY_GETC fgetc(...) 56 | * MY_GETS fgets(...) 57 | * MY_ATEOF feof(...) 58 | * MY_TELL ftell(...) 59 | * MY_SEEK fseek(..., SEEK_SET) 60 | * MY_REWIND rewind(...) 61 | * MY_SETBUFFER (not a standard for stdio, does nothing there) 62 | */ 63 | 64 | /* 65 | * Important DEFINEs: 66 | * It is important to define these consistantly across the various 67 | * compilation modules of your program if you wish to exchange file 68 | * handles between them. 69 | * 70 | * USE_PHYSFS: Define USE_PHYSFS if PhysicsFS is being used; note that if 71 | * you do intend to use PhysicsFS then you will still need to initialize 72 | * PhysicsFS yourself and set up its search-paths. 73 | * 74 | * Optional DEFINEs: 75 | * 76 | * PHYSFS_DEFAULT_READ_BUFFER : If set then abs-file.h sets the 77 | * PhysicsFS buffer size to this value whenever you open a file. You 78 | * may over-ride this on a per-filehandle basis by using the 79 | * MY_SETBUFFER() macro (which simply does nothing when not using 80 | * PhysicsFS). If you have not defined this value explicitly then 81 | * abs-file.h will default to the same default buffer size as used by 82 | * stdio if it can be determined, or 8192 bytes otherwise. 83 | */ 84 | #ifndef PHYSFS_DEFAULT_READ_BUFFER 85 | #ifdef BUFSIZ 86 | #define PHYSFS_DEFAULT_READ_BUFFER BUFSIZ 87 | #else 88 | #define PHYSFS_DEFAULT_READ_BUFFER 8192 89 | #endif 90 | #endif 91 | 92 | #ifdef USE_PHYSFS 93 | 94 | #include 95 | #define MY_FILETYPE PHYSFS_File 96 | #define MY_SETBUFFER(fp,size) PHYSFS_setBuffer(fp,size) 97 | #define MY_READ(p,s,n,fp) PHYSFS_read(fp,p,s,n) 98 | #if PHYSFS_DEFAULT_READ_BUFFER 99 | static MY_FILETYPE* MY_OPEN_FOR_READ(const char *const filename) 100 | { 101 | MY_FILETYPE *const file = PHYSFS_openRead(filename); 102 | if (file) { 103 | MY_SETBUFFER(file, PHYSFS_DEFAULT_READ_BUFFER); 104 | } 105 | return file; 106 | } 107 | #else 108 | #define MY_OPEN_FOR_READ(fn) PHYSFS_openRead(fn) 109 | #endif 110 | static int MY_GETC(MY_FILETYPE *const fp) { 111 | unsigned char c; 112 | /*if (PHYSFS_eof(fp)) { 113 | return EOF; 114 | } 115 | MY_READ(&c, 1, 1, fp);*/ 116 | if (MY_READ(&c, 1, 1, fp) != 1) { 117 | return EOF; 118 | } 119 | return c; 120 | } 121 | static char * MY_GETS(char * const str, const int size, 122 | MY_FILETYPE *const fp) { 123 | int i = 0; 124 | int c; 125 | do { 126 | if (i == size-1) { 127 | break; 128 | } 129 | c = MY_GETC(fp); 130 | if (c == EOF) { 131 | break; 132 | } 133 | str[i++] = c; 134 | } while (c != '\0' && 135 | c != -1 && 136 | c != '\n'); 137 | str[i] = '\0'; 138 | if (i == 0) { 139 | return NULL; 140 | } 141 | return str; 142 | } 143 | #define MY_CLOSE(fp) PHYSFS_close(fp) 144 | #define MY_ATEOF(fp) PHYSFS_eof(fp) 145 | #define MY_TELL(fp) PHYSFS_tell(fp) 146 | #define MY_SEEK(fp,o) PHYSFS_seek(fp,o) 147 | #define MY_REWIND(fp) MY_SEEK(fp,0) 148 | 149 | #else 150 | 151 | #define MY_FILETYPE FILE 152 | #define MY_READ(p,s,n,fp) fread(p,s,n,fp) 153 | #define MY_OPEN_FOR_READ(n) fopen(n, "rb") 154 | #define MY_GETC(fp) fgetc(fp) 155 | #define MY_GETS(str,size,fp) fgets(str,size,fp) 156 | #define MY_CLOSE(fp) fclose(fp) 157 | #define MY_ATEOF(fp) feof(fp) 158 | #define MY_TELL(fp) ftell(fp) 159 | #define MY_SEEK(fp,o) fseek(fp,o, SEEK_SET) 160 | #define MY_REWIND(fp) rewind(fp) 161 | /*static void MY_SETBUFFER(const MY_FILETYPE *const file, const int num) { }*/ 162 | #define MY_SETBUFFER(fp,size) 163 | #endif 164 | 165 | #endif 166 | -------------------------------------------------------------------------------- /extras/buildbot-checker.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # This is a script used by some Buildbot workers to push the project 4 | # through Clang's static analyzer and prepare the output to be uploaded 5 | # back to the buildmaster. You might find it useful too. 6 | 7 | # Install Clang (you already have it on Mac OS X, apt-get install clang 8 | # on Ubuntu, etc), Make sure "scan-build" is in your $PATH. 9 | 10 | FINALDIR="$1" 11 | 12 | set -x 13 | set -e 14 | 15 | cd `dirname "$0"` 16 | cd .. 17 | 18 | rm -rf checker-buildbot analysis 19 | if [ ! -z "$FINALDIR" ]; then 20 | rm -rf "$FINALDIR" 21 | fi 22 | 23 | mkdir checker-buildbot 24 | cd checker-buildbot 25 | 26 | # We turn off deprecated declarations, because we don't care about these warnings during static analysis. 27 | # The -Wno-liblto is new since our checker-279 upgrade, I think; checker otherwise warns "libLTO.dylib relative to clang installed dir not found" 28 | 29 | # You might want to do this for CMake-backed builds instead... 30 | scan-build -o analysis cmake -G Ninja -Wno-dev -DPHYSFS_BUILD_SHARED=False -DCMAKE_BUILD_TYPE=Debug -DCMAKE_C_FLAGS="-Wno-deprecated-declarations" -DCMAKE_EXE_LINKER_FLAGS="-Wno-liblto" .. 31 | 32 | rm -rf analysis 33 | scan-build -o analysis cmake --build . --config Debug 34 | 35 | if [ `ls -A analysis |wc -l` == 0 ] ; then 36 | mkdir analysis/zarro 37 | echo 'Zarro boogsStatic analysis: no issues to report.' >analysis/zarro/index.html 38 | fi 39 | 40 | mv analysis/* ../analysis 41 | rmdir analysis # Make sure this is empty. 42 | cd .. 43 | chmod -R a+r analysis 44 | chmod -R go-w analysis 45 | find analysis -type d -exec chmod a+x {} \; 46 | if [ -x /usr/bin/xattr ]; then find analysis -exec /usr/bin/xattr -d com.apple.quarantine {} \; 2>/dev/null ; fi 47 | 48 | if [ ! -z "$FINALDIR" ]; then 49 | mv analysis "$FINALDIR" 50 | else 51 | FINALDIR=analysis 52 | fi 53 | 54 | rm -rf checker-buildbot 55 | 56 | echo "Done. Final output is in '$FINALDIR' ..." 57 | 58 | # end of checker-buildbot.sh ... 59 | 60 | -------------------------------------------------------------------------------- /extras/buildbot-emscripten.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | if [ -z "$SDKDIR" ]; then 4 | SDKDIR="/emsdk" 5 | fi 6 | 7 | ENVSCRIPT="$SDKDIR/emsdk_env.sh" 8 | if [ ! -f "$ENVSCRIPT" ]; then 9 | echo "ERROR: This script expects the Emscripten SDK to be in '$SDKDIR'." 1>&2 10 | echo "ERROR: Set the \$SDKDIR environment variable to override this." 1>&2 11 | exit 1 12 | fi 13 | 14 | TARBALL="$1" 15 | if [ -z $1 ]; then 16 | TARBALL=physfs-emscripten.tar.xz 17 | fi 18 | 19 | cd `dirname "$0"` 20 | cd .. 21 | PHYSFSBASE=`pwd` 22 | 23 | echo "Setting up Emscripten SDK environment..." 24 | source "$ENVSCRIPT" 25 | 26 | echo "Setting up..." 27 | cd "$PHYSFSBASE" 28 | rm -rf buildbot 29 | mkdir buildbot 30 | cd buildbot 31 | 32 | echo "Configuring..." 33 | emcmake cmake -G "Ninja" -DPHYSFS_BUILD_SHARED=False -DCMAKE_BUILD_TYPE=MinSizeRel .. || exit $? 34 | 35 | echo "Building..." 36 | emmake cmake --build . --config MinSizeRel || exit $? 37 | 38 | set -e 39 | rm -rf "$TARBALL" physfs-emscripten 40 | mkdir -p physfs-emscripten 41 | echo "Archiving to '$TARBALL' ..." 42 | cp ../src/physfs.h libphysfs.a physfs-emscripten 43 | chmod -R a+r physfs-emscripten 44 | chmod a+x physfs-emscripten 45 | chmod -R go-w physfs-emscripten 46 | tar -cJvvf "$TARBALL" physfs-emscripten 47 | echo "Done." 48 | 49 | exit 0 50 | 51 | # end of emscripten-buildbot.sh ... 52 | 53 | -------------------------------------------------------------------------------- /extras/buildbot-os2.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # This is used by the buildbot to cross-compile for OS/2 from Linux, using 4 | # OpenWatcom. In an ideal world, we wouldn't need this, but we need a few 5 | # things to do this "properly" ... 6 | # 7 | # - OS/2 running as a VMware guest on the build machine 8 | # - Buildbot-worker running on that OS/2 guest 9 | # - CMake for OS/2 that... 10 | # - ... has Open Watcom compiler support and... 11 | # - ... a Watcom WMake project generator. 12 | # 13 | # Some of these things are doable (there is a CMake port for OS/2, you can 14 | # use GCC with it), but since OpenWatcom will just target OS/2 on Linux just 15 | # like it could on OS/2, it's easier and more efficient to just have a 16 | # buildbot script compile it here. 17 | # 18 | # Note that we just blast all the C files through the wcc386 compiler and 19 | # skip CMake entirely. We should fix this at some point. 20 | 21 | set -e 22 | 23 | ZIPFILE="$1" 24 | if [ -z $ZIPFILE ]; then 25 | ZIPFILE=physfs-os2.zip 26 | fi 27 | 28 | export WATCOM="/usr/local/share/watcom" 29 | export PATH="$PATH:$WATCOM/binl" 30 | 31 | CFLAGS="-i=\"$WATCOM/h;$WATCOM/h/os2;../src\" -wx -d0 -otexan -6r -zq -bt=os2 -fo=.obj -mf" 32 | WLIBFLAGS="-b -c -n -q -p=512" 33 | 34 | cd `dirname "$0"` 35 | cd .. 36 | mkdir -p buildbot 37 | cd buildbot 38 | 39 | rm -f test_physfs.obj 40 | 41 | OKAY="1" 42 | for src in ../src/*.c ; do 43 | echo wcc386 $src $CFLAGS 44 | wcc386 $src $CFLAGS || OKAY="0" 45 | done 46 | 47 | if [ "$OKAY" == "1" ]; then 48 | echo wlib $WLIBFLAGS physfs.lib *.obj 49 | wlib $WLIBFLAGS physfs.lib *.obj || OKAY="0" 50 | fi 51 | 52 | echo wcc386 ../test/test_physfs.c $CFLAGS 53 | wcc386 ../test/test_physfs.c $CFLAGS || OKAY="0" 54 | 55 | if [ "$OKAY" == "1" ]; then 56 | LDFLAGS="name test_physfs d all sys os2v2 op m libr physfs op q op symf FIL test_physfs.obj" 57 | echo wlink $LDFLAGS 58 | wlink $LDFLAGS || OKAY="0" 59 | fi 60 | 61 | if [ "$OKAY" == "1" ]; then 62 | F=`file test_physfs.exe` 63 | echo "$F" 64 | if [ "$F" != 'test_physfs.exe: MS-DOS executable, LX for OS/2 (console) i80386' ]; then 65 | echo 1>&2 "ERROR: final binary doesn't appear to be OS/2 executable." 66 | OKAY=0 67 | fi 68 | fi 69 | 70 | if [ "$OKAY" == "1" ]; then 71 | echo 1>&2 "Build succeeded." 72 | set -e 73 | rm -rf "$ZIPFILE" physfs-os2 74 | mkdir -p physfs-os2 75 | echo "Zipping to '$ZIPFILE' ..." 76 | cp ../src/physfs.h physfs.lib physfs-os2 77 | chmod -R a+r physfs-os2 78 | chmod a+x physfs-os2 79 | chmod -R go-w physfs-os2 80 | zip -9r "$ZIPFILE" physfs-os2 81 | echo "Done." 82 | exit 0 83 | else 84 | echo 1>&2 "Build failed." 85 | exit 1 86 | fi 87 | 88 | -------------------------------------------------------------------------------- /extras/buildbot-raspberrypi.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # This is the script physfs-buildbot.icculus.org uses to cross-compile 4 | # PhysicsFS from x86 Linux to Raspberry Pi. This script was originally from 5 | # Simple Directmedia Layer ( https://www.libsdl.org/ ). 6 | 7 | # The final tarball can be unpacked in the root directory of a RPi, 8 | # so the PhysicsFS install lands in /usr/local. Run ldconfig, and then 9 | # you should be able to build and run PhysicsFS-based software on your 10 | # Pi. Standard configure scripts should be able to find PhysicsFS and 11 | # build against it. 12 | 13 | TARBALL="$1" 14 | if [ -z $1 ]; then 15 | TARBALL=physfs-raspberrypi.tar.xz 16 | fi 17 | 18 | BUILDBOTDIR="buildbot" 19 | PARENTDIR="$PWD" 20 | 21 | set -e 22 | set -x 23 | rm -f $TARBALL 24 | rm -rf $BUILDBOTDIR 25 | mkdir -p $BUILDBOTDIR 26 | pushd $BUILDBOTDIR 27 | 28 | # the '-G "Ninja"' can be '-G "Unix Makefiles"' if you prefer to use GNU Make. 29 | SYSROOT="/opt/rpi-sysroot" 30 | cmake -G "Ninja" \ 31 | -DCMAKE_C_COMPILER="/opt/rpi-tools/arm-bcm2708/gcc-linaro-arm-linux-gnueabihf-raspbian/bin/arm-linux-gnueabihf-gcc" \ 32 | -DCMAKE_BUILD_TYPE=MinSizeRel \ 33 | -DCMAKE_SYSROOT="$SYSROOT" \ 34 | -DCMAKE_FIND_ROOT_PATH="$SYSROOT" \ 35 | -DCMAKE_SYSTEM_NAME="Linux" \ 36 | -DCMAKE_SYSTEM_VERSION=1 \ 37 | -DCMAKE_FIND_ROOT_PATH_MODE_PROGRAM=NEVER \ 38 | -DCMAKE_FIND_ROOT_PATH_MODE_LIBRARY=ONLY \ 39 | -DCMAKE_FIND_ROOT_PATH_MODE_INCLUDE=ONLY \ 40 | .. 41 | 42 | cmake --build . --config MinSizeRel 43 | 44 | rm -rf "$TARBALL" physfs-raspberrypi 45 | mkdir -p physfs-raspberrypi 46 | echo "Archiving to '$TARBALL' ..." 47 | cp -a ../src/physfs.h libphysfs.a libphysfs.so* physfs-raspberrypi 48 | chmod -R a+r physfs-raspberrypi 49 | chmod a+x physfs-raspberrypi physfs-raspberrypi/*.so* 50 | chmod -R go-w physfs-raspberrypi 51 | tar -cJvvf "$TARBALL" physfs-raspberrypi 52 | 53 | set +x 54 | echo "Done." 55 | 56 | 57 | -------------------------------------------------------------------------------- /extras/globbing.c: -------------------------------------------------------------------------------- 1 | /** \file globbing.c */ 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | #include "globbing.h" 10 | 11 | /** 12 | * Please see globbing.h for details. 13 | * 14 | * License: this code is public domain. I make no warranty that it is useful, 15 | * correct, harmless, or environmentally safe. 16 | * 17 | * This particular file may be used however you like, including copying it 18 | * verbatim into a closed-source project, exploiting it commercially, and 19 | * removing any trace of my name from the source (although I hope you won't 20 | * do that). I welcome enhancements and corrections to this file, but I do 21 | * not require you to send me patches if you make changes. This code has 22 | * NO WARRANTY. 23 | * 24 | * Unless otherwise stated, the rest of PhysicsFS falls under the zlib license. 25 | * Please see the file LICENSE.txt in the source's root directory. 26 | * 27 | * \author Ryan C. Gordon. 28 | */ 29 | 30 | 31 | static int matchesPattern(const char *fname, const char *wildcard, 32 | int caseSensitive) 33 | { 34 | char x, y; 35 | const char *fnameptr = fname; 36 | const char *wildptr = wildcard; 37 | 38 | while ((*wildptr) && (*fnameptr)) 39 | { 40 | y = *wildptr; 41 | if (y == '*') 42 | { 43 | do 44 | { 45 | wildptr++; /* skip multiple '*' in a row... */ 46 | } while (*wildptr == '*'); 47 | 48 | y = (caseSensitive) ? *wildptr : (char) tolower(*wildptr); 49 | 50 | while (1) 51 | { 52 | x = (caseSensitive) ? *fnameptr : (char) tolower(*fnameptr); 53 | if ((!x) || (x == y)) 54 | break; 55 | else 56 | fnameptr++; 57 | } /* while */ 58 | } /* if */ 59 | 60 | else if (y == '?') 61 | { 62 | wildptr++; 63 | fnameptr++; 64 | } /* else if */ 65 | 66 | else 67 | { 68 | if (caseSensitive) 69 | x = *fnameptr; 70 | else 71 | { 72 | x = tolower(*fnameptr); 73 | y = tolower(y); 74 | } /* if */ 75 | 76 | wildptr++; 77 | fnameptr++; 78 | 79 | if (x != y) 80 | return 0; 81 | } /* else */ 82 | } /* while */ 83 | 84 | while (*wildptr == '*') 85 | wildptr++; 86 | 87 | return (*fnameptr == *wildptr); 88 | } /* matchesPattern */ 89 | 90 | typedef struct 91 | { 92 | const PHYSFS_Allocator *allocator; 93 | const char *wildcard; 94 | int caseSensitive; 95 | PHYSFS_EnumFilesCallback callback; 96 | void *origData; 97 | } WildcardCallbackData; 98 | 99 | 100 | /* 101 | * This callback sits between the enumerator and the enduser callback, 102 | * filtering out files that don't match the wildcard pattern. 103 | */ 104 | static void wildcardCallback(void *_d, const char *origdir, const char *fname) 105 | { 106 | const WildcardCallbackData *data = (const WildcardCallbackData *) _d; 107 | if (matchesPattern(fname, data->wildcard, data->caseSensitive)) 108 | data->callback(data->origData, origdir, fname); 109 | } /* wildcardCallback */ 110 | 111 | 112 | void PHYSFSEXT_enumerateFilesCallbackWildcard(const char *dir, 113 | const char *wildcard, 114 | int caseSensitive, 115 | PHYSFS_EnumFilesCallback c, 116 | void *d) 117 | { 118 | WildcardCallbackData data; 119 | data.allocator = PHYSFS_getAllocator(); 120 | data.wildcard = wildcard; 121 | data.caseSensitive = caseSensitive; 122 | data.callback = c; 123 | data.origData = d; 124 | PHYSFS_enumerateFilesCallback(dir, wildcardCallback, &data); 125 | } /* PHYSFSEXT_enumerateFilesCallbackWildcard */ 126 | 127 | 128 | void PHYSFSEXT_freeEnumeration(char **list) 129 | { 130 | const PHYSFS_Allocator *allocator = PHYSFS_getAllocator(); 131 | int i; 132 | if (list != NULL) 133 | { 134 | for (i = 0; list[i] != NULL; i++) 135 | allocator->Free(list[i]); 136 | allocator->Free(list); 137 | } /* if */ 138 | } /* PHYSFSEXT_freeEnumeration */ 139 | 140 | 141 | char **PHYSFSEXT_enumerateFilesWildcard(const char *dir, const char *wildcard, 142 | int caseSensitive) 143 | { 144 | const PHYSFS_Allocator *allocator = PHYSFS_getAllocator(); 145 | char **list = PHYSFS_enumerateFiles(dir); 146 | char **retval = NULL; 147 | int totalmatches = 0; 148 | int matches = 0; 149 | char **i; 150 | 151 | for (i = list; *i != NULL; i++) 152 | { 153 | #if 0 154 | printf("matchesPattern: '%s' vs '%s' (%s) ... %s\n", *i, wildcard, 155 | caseSensitive ? "case" : "nocase", 156 | matchesPattern(*i, wildcard, caseSensitive) ? "true" : "false"); 157 | #endif 158 | if (matchesPattern(*i, wildcard, caseSensitive)) 159 | totalmatches++; 160 | } /* for */ 161 | 162 | retval = (char **) allocator->Malloc(sizeof (char *) * (totalmatches+1)); 163 | if (retval != NULL) 164 | { 165 | for (i = list; ((matches < totalmatches) && (*i != NULL)); i++) 166 | { 167 | if (matchesPattern(*i, wildcard, caseSensitive)) 168 | { 169 | retval[matches] = (char *) allocator->Malloc(strlen(*i) + 1); 170 | if (retval[matches] == NULL) 171 | { 172 | while (matches--) 173 | allocator->Free(retval[matches]); 174 | allocator->Free(retval); 175 | retval = NULL; 176 | break; 177 | } /* if */ 178 | strcpy(retval[matches], *i); 179 | matches++; 180 | } /* if */ 181 | } /* for */ 182 | 183 | if (retval != NULL) 184 | { 185 | assert(totalmatches == matches); 186 | retval[matches] = NULL; 187 | } /* if */ 188 | } /* if */ 189 | 190 | PHYSFS_freeList(list); 191 | return retval; 192 | } /* PHYSFSEXT_enumerateFilesWildcard */ 193 | 194 | 195 | #ifdef TEST_PHYSFSEXT_ENUMERATEFILESWILDCARD 196 | int main(int argc, char **argv) 197 | { 198 | int rc; 199 | char **flist; 200 | char **i; 201 | 202 | if (argc != 3) 203 | { 204 | printf("USAGE: %s \n" 205 | " where is 1 or 0.\n", argv[0]); 206 | return 1; 207 | } /* if */ 208 | 209 | if (!PHYSFS_init(argv[0])) 210 | { 211 | fprintf(stderr, "PHYSFS_init(): %s\n", PHYSFS_getErrorByCode(PHYSFS_getLastErrorCode())); 212 | return 1; 213 | } /* if */ 214 | 215 | if (!PHYSFS_addToSearchPath(".", 1)) 216 | { 217 | fprintf(stderr, "PHYSFS_addToSearchPath(): %s\n", PHYSFS_getErrorByCode(PHYSFS_getLastErrorCode())); 218 | PHYSFS_deinit(); 219 | return 1; 220 | } /* if */ 221 | 222 | flist = PHYSFSEXT_enumerateFilesWildcard("/", argv[1], atoi(argv[2])); 223 | rc = 0; 224 | for (i = flist; *i; i++) 225 | { 226 | printf("%s\n", *i); 227 | rc++; 228 | } /* for */ 229 | printf("\n total %d files.\n\n", rc); 230 | 231 | PHYSFSEXT_freeEnumeration(flist); 232 | PHYSFS_deinit(); 233 | 234 | return 0; 235 | } /* main */ 236 | #endif 237 | 238 | /* end of globbing.c ... */ 239 | 240 | -------------------------------------------------------------------------------- /extras/globbing.h: -------------------------------------------------------------------------------- 1 | #ifndef INCL_PHYSFSEXT_GLOBBING_H 2 | #define INCL_PHYSFSEXT_GLOBBING_H 3 | 4 | /** \file globbing.h */ 5 | 6 | #include "physfs.h" 7 | 8 | /** 9 | * \mainpage PhysicsFS globbing 10 | * 11 | * This is an extension to PhysicsFS to let you search for files with basic 12 | * wildcard matching, regardless of what sort of filesystem or archive they 13 | * reside in. It does this by enumerating directories as needed and manually 14 | * locating matching entries. 15 | * 16 | * Usage: Set up PhysicsFS as you normally would, then use 17 | * PHYSFSEXT_enumerateFilesWildcard() when enumerating files. This is just 18 | * like PHYSFS_enumerateFiles(), but it returns a subset that matches your 19 | * wildcard pattern. You must call PHYSFSEXT_freeEnumeration() on the results, 20 | * just PHYSFS_enumerateFiles() would do with PHYSFS_freeList(). 21 | * 22 | * License: this code is public domain. I make no warranty that it is useful, 23 | * correct, harmless, or environmentally safe. 24 | * 25 | * This particular file may be used however you like, including copying it 26 | * verbatim into a closed-source project, exploiting it commercially, and 27 | * removing any trace of my name from the source (although I hope you won't 28 | * do that). I welcome enhancements and corrections to this file, but I do 29 | * not require you to send me patches if you make changes. This code has 30 | * NO WARRANTY. 31 | * 32 | * Unless otherwise stated, the rest of PhysicsFS falls under the zlib license. 33 | * Please see LICENSE.txt in the source's "docs" directory. 34 | * 35 | * \author Ryan C. Gordon. 36 | */ 37 | 38 | #ifdef __cplusplus 39 | extern "C" { 40 | #endif 41 | 42 | /** 43 | * \fn char **PHYSFS_enumerateFilesWildcard(const char *dir, const char *wildcard, int caseSensitive) 44 | * \brief Get a file listing of a search path's directory, filtered with a wildcard pattern. 45 | * 46 | * Matching directories are interpolated. That is, if "C:\mydir" is in the 47 | * search path and contains a directory "savegames" that contains "x.sav", 48 | * "y.Sav", and "z.txt", and there is also a "C:\userdir" in the search path 49 | * that has a "savegames" subdirectory with "w.sav", then the following code: 50 | * 51 | * \code 52 | * char **rc = PHYSFS_enumerateFilesWildcard("savegames", "*.sav", 0); 53 | * char **i; 54 | * 55 | * for (i = rc; *i != NULL; i++) 56 | * printf(" * We've got [%s].\n", *i); 57 | * 58 | * PHYSFS_freeList(rc); 59 | * \endcode 60 | * 61 | * ...will print: 62 | * 63 | * \verbatim 64 | * We've got [x.sav]. 65 | * We've got [y.Sav]. 66 | * We've got [w.sav].\endverbatim 67 | * 68 | * Feel free to sort the list however you like. We only promise there will 69 | * be no duplicates, but not what order the final list will come back in. 70 | * 71 | * Wildcard strings can use the '*' and '?' characters, currently. 72 | * Matches can be case-insensitive if you pass a zero for argument 3. 73 | * 74 | * Don't forget to call PHYSFSEXT_freeEnumerator() with the return value from 75 | * this function when you are done with it. As we use PhysicsFS's allocator 76 | * for this list, you must free it before calling PHYSFS_deinit(). 77 | * Do not use PHYSFS_freeList() on the returned value! 78 | * 79 | * \param dir directory in platform-independent notation to enumerate. 80 | * \param wildcard Wildcard pattern to use for filtering. 81 | * \param caseSensitive Zero for case-insensitive matching, 82 | * non-zero for case-sensitive. 83 | * \return Null-terminated array of null-terminated strings. 84 | * 85 | * \sa PHYSFSEXT_freeEnumeration 86 | */ 87 | PHYSFS_DECL char **PHYSFSEXT_enumerateFilesWildcard(const char *dir, 88 | const char *wildcard, 89 | int caseSensitive); 90 | 91 | /** 92 | * \fn void PHYSFSEXT_freeEnumeration(char **list) 93 | * \brief Free data returned by PHYSFSEXT_enumerateFilesWildcard 94 | * 95 | * Conceptually, this works like PHYSFS_freeList(), but is used with data 96 | * returned by PHYSFSEXT_enumerateFilesWildcard() only. Be sure to call this 97 | * on any returned data from that function before 98 | * 99 | * \param list Pointer previously returned by 100 | * PHYSFSEXT_enumerateFilesWildcard(). It is safe to pass a 101 | * NULL here. 102 | * 103 | * \sa PHYSFSEXT_enumerateFilesWildcard 104 | */ 105 | PHYSFS_DECL void PHYSFSEXT_freeEnumeration(char **list); 106 | 107 | 108 | /** 109 | * \fn void PHYSFSEXT_enumerateFilesCallbackWildcard(const char *dir, const char *wildcard, int caseSensitive, PHYSFS_EnumFilesCallback c, void *d); 110 | * \brief Get a file listing of a search path's directory, filtered with a wildcard pattern, using an application-defined callback. 111 | * 112 | * This function is equivalent to PHYSFSEXT_enumerateFilesWildcard(). It 113 | * reports file listings, filtered by a wildcard pattern. 114 | * 115 | * Unlike PHYSFS_enumerateFiles(), this function does not return an array. 116 | * Rather, it calls a function specified by the application once per 117 | * element of the search path: 118 | * 119 | * \code 120 | * 121 | * static void printDir(void *data, const char *origdir, const char *fname) 122 | * { 123 | * printf(" * We've got [%s] in [%s].\n", fname, origdir); 124 | * } 125 | * 126 | * // ... 127 | * PHYSFS_enumerateFilesCallbackWildcard("savegames","*.sav",0,printDir,NULL); 128 | * \endcode 129 | * 130 | * Items sent to the callback are not guaranteed to be in any order whatsoever. 131 | * There is no sorting done at this level, and if you need that, you should 132 | * probably use PHYSFS_enumerateFilesWildcard() instead, which guarantees 133 | * alphabetical sorting. This form reports whatever is discovered in each 134 | * archive before moving on to the next. Even within one archive, we can't 135 | * guarantee what order it will discover data. Any sorting you find in 136 | * these callbacks is just pure luck. Do not rely on it. As this walks 137 | * the entire list of archives, you may receive duplicate filenames. 138 | * 139 | * Wildcard strings can use the '*' and '?' characters, currently. 140 | * Matches can be case-insensitive if you pass a zero for argument 3. 141 | * 142 | * \param dir Directory, in platform-independent notation, to enumerate. 143 | * \param wildcard Wildcard pattern to use for filtering. 144 | * \param caseSensitive Zero for case-insensitive matching, 145 | * non-zero for case-sensitive. 146 | * \param c Callback function to notify about search path elements. 147 | * \param d Application-defined data passed to callback. Can be NULL. 148 | * 149 | * \sa PHYSFS_EnumFilesCallback 150 | * \sa PHYSFS_enumerateFiles 151 | */ 152 | PHYSFS_DECL void PHYSFSEXT_enumerateFilesCallbackWildcard(const char *dir, 153 | const char *wildcard, 154 | int caseSensitive, 155 | PHYSFS_EnumFilesCallback c, 156 | void *d); 157 | 158 | #ifdef __cplusplus 159 | } 160 | #endif 161 | 162 | #endif /* include-once blocker. */ 163 | 164 | /* end of globbing.h ... */ 165 | 166 | -------------------------------------------------------------------------------- /extras/ignorecase.c: -------------------------------------------------------------------------------- 1 | /** \file ignorecase.c */ 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #include "physfs.h" 9 | #include "ignorecase.h" 10 | 11 | /** 12 | * Please see ignorecase.h for details. 13 | * 14 | * License: this code is public domain. I make no warranty that it is useful, 15 | * correct, harmless, or environmentally safe. 16 | * 17 | * This particular file may be used however you like, including copying it 18 | * verbatim into a closed-source project, exploiting it commercially, and 19 | * removing any trace of my name from the source (although I hope you won't 20 | * do that). I welcome enhancements and corrections to this file, but I do 21 | * not require you to send me patches if you make changes. This code has 22 | * NO WARRANTY. 23 | * 24 | * Unless otherwise stated, the rest of PhysicsFS falls under the zlib license. 25 | * Please see LICENSE.txt in the root of the source tree. 26 | * 27 | * \author Ryan C. Gordon. 28 | */ 29 | 30 | static int locateOneElement(char *buf) 31 | { 32 | char *ptr; 33 | char **rc; 34 | char **i; 35 | 36 | if (PHYSFS_exists(buf)) 37 | return 1; /* quick rejection: exists in current case. */ 38 | 39 | ptr = strrchr(buf, '/'); /* find entry at end of path. */ 40 | if (ptr == NULL) 41 | { 42 | rc = PHYSFS_enumerateFiles("/"); 43 | ptr = buf; 44 | } /* if */ 45 | else 46 | { 47 | *ptr = '\0'; 48 | rc = PHYSFS_enumerateFiles(buf); 49 | *ptr = '/'; 50 | ptr++; /* point past dirsep to entry itself. */ 51 | } /* else */ 52 | 53 | if (rc != NULL) 54 | { 55 | for (i = rc; *i != NULL; i++) 56 | { 57 | if (PHYSFS_utf8stricmp(*i, ptr) == 0) 58 | { 59 | strcpy(ptr, *i); /* found a match. Overwrite with this case. */ 60 | PHYSFS_freeList(rc); 61 | return 1; 62 | } /* if */ 63 | } /* for */ 64 | 65 | PHYSFS_freeList(rc); 66 | } /* if */ 67 | 68 | /* no match at all... */ 69 | return 0; 70 | } /* locateOneElement */ 71 | 72 | 73 | int PHYSFSEXT_locateCorrectCase(char *buf) 74 | { 75 | int rc; 76 | char *ptr; 77 | 78 | while (*buf == '/') /* skip any '/' at start of string... */ 79 | buf++; 80 | 81 | ptr = buf; 82 | if (*ptr == '\0') 83 | return 0; /* Uh...I guess that's success. */ 84 | 85 | while ( (ptr = strchr(ptr + 1, '/')) != NULL ) 86 | { 87 | *ptr = '\0'; /* block this path section off */ 88 | rc = locateOneElement(buf); 89 | *ptr = '/'; /* restore path separator */ 90 | if (!rc) 91 | return -2; /* missing element in path. */ 92 | } /* while */ 93 | 94 | /* check final element... */ 95 | return locateOneElement(buf) ? 0 : -1; 96 | } /* PHYSFSEXT_locateCorrectCase */ 97 | 98 | 99 | #ifdef TEST_PHYSFSEXT_LOCATECORRECTCASE 100 | int main(int argc, char **argv) 101 | { 102 | int rc; 103 | char buf[128]; 104 | PHYSFS_File *f; 105 | 106 | if (!PHYSFS_init(argv[0])) 107 | { 108 | fprintf(stderr, "PHYSFS_init(): %s\n", PHYSFS_getErrorByCode(PHYSFS_getLastErrorCode())); 109 | return 1; 110 | } /* if */ 111 | 112 | if (!PHYSFS_addToSearchPath(".", 1)) 113 | { 114 | fprintf(stderr, "PHYSFS_addToSearchPath(): %s\n", PHYSFS_getErrorByCode(PHYSFS_getLastErrorCode())); 115 | PHYSFS_deinit(); 116 | return 1; 117 | } /* if */ 118 | 119 | if (!PHYSFS_setWriteDir(".")) 120 | { 121 | fprintf(stderr, "PHYSFS_setWriteDir(): %s\n", PHYSFS_getErrorByCode(PHYSFS_getLastErrorCode())); 122 | PHYSFS_deinit(); 123 | return 1; 124 | } /* if */ 125 | 126 | if (!PHYSFS_mkdir("/a/b/c")) 127 | { 128 | fprintf(stderr, "PHYSFS_mkdir(): %s\n", PHYSFS_getErrorByCode(PHYSFS_getLastErrorCode())); 129 | PHYSFS_deinit(); 130 | return 1; 131 | } /* if */ 132 | 133 | if (!PHYSFS_mkdir("/a/b/C")) 134 | { 135 | fprintf(stderr, "PHYSFS_mkdir(): %s\n", PHYSFS_getErrorByCode(PHYSFS_getLastErrorCode())); 136 | PHYSFS_deinit(); 137 | return 1; 138 | } /* if */ 139 | 140 | f = PHYSFS_openWrite("/a/b/c/x.txt"); 141 | PHYSFS_close(f); 142 | if (f == NULL) 143 | { 144 | fprintf(stderr, "PHYSFS_openWrite(): %s\n", PHYSFS_getErrorByCode(PHYSFS_getLastErrorCode())); 145 | PHYSFS_deinit(); 146 | return 1; 147 | } /* if */ 148 | 149 | f = PHYSFS_openWrite("/a/b/C/X.txt"); 150 | PHYSFS_close(f); 151 | if (f == NULL) 152 | { 153 | fprintf(stderr, "PHYSFS_openWrite(): %s\n", PHYSFS_getErrorByCode(PHYSFS_getLastErrorCode())); 154 | PHYSFS_deinit(); 155 | return 1; 156 | } /* if */ 157 | 158 | strcpy(buf, "/a/b/c/x.txt"); 159 | rc = PHYSFSEXT_locateCorrectCase(buf); 160 | if ((rc != 0) || (strcmp(buf, "/a/b/c/x.txt") != 0)) 161 | printf("test 1 failed\n"); 162 | 163 | strcpy(buf, "/a/B/c/x.txt"); 164 | rc = PHYSFSEXT_locateCorrectCase(buf); 165 | if ((rc != 0) || (strcmp(buf, "/a/b/c/x.txt") != 0)) 166 | printf("test 2 failed\n"); 167 | 168 | strcpy(buf, "/a/b/C/x.txt"); 169 | rc = PHYSFSEXT_locateCorrectCase(buf); 170 | if ((rc != 0) || (strcmp(buf, "/a/b/C/X.txt") != 0)) 171 | printf("test 3 failed\n"); 172 | 173 | strcpy(buf, "/a/b/c/X.txt"); 174 | rc = PHYSFSEXT_locateCorrectCase(buf); 175 | if ((rc != 0) || (strcmp(buf, "/a/b/c/x.txt") != 0)) 176 | printf("test 4 failed\n"); 177 | 178 | strcpy(buf, "/a/b/c/z.txt"); 179 | rc = PHYSFSEXT_locateCorrectCase(buf); 180 | if ((rc != -1) || (strcmp(buf, "/a/b/c/z.txt") != 0)) 181 | printf("test 5 failed\n"); 182 | 183 | strcpy(buf, "/A/B/Z/z.txt"); 184 | rc = PHYSFSEXT_locateCorrectCase(buf); 185 | if ((rc != -2) || (strcmp(buf, "/a/b/Z/z.txt") != 0)) 186 | printf("test 6 failed\n"); 187 | 188 | printf("Testing completed.\n"); 189 | printf(" If no errors were reported, you're good to go.\n"); 190 | 191 | PHYSFS_delete("/a/b/c/x.txt"); 192 | PHYSFS_delete("/a/b/C/X.txt"); 193 | PHYSFS_delete("/a/b/c"); 194 | PHYSFS_delete("/a/b/C"); 195 | PHYSFS_delete("/a/b"); 196 | PHYSFS_delete("/a"); 197 | PHYSFS_deinit(); 198 | return 0; 199 | } /* main */ 200 | #endif 201 | 202 | /* end of ignorecase.c ... */ 203 | 204 | -------------------------------------------------------------------------------- /extras/ignorecase.h: -------------------------------------------------------------------------------- 1 | #ifndef INCL_PHYSFSEXT_IGNORECASE_H 2 | #define INCL_PHYSFSEXT_IGNORECASE_H 3 | 4 | /** \file ignorecase.h */ 5 | 6 | /** 7 | * \mainpage PhysicsFS ignorecase 8 | * 9 | * This is an extension to PhysicsFS to let you handle files in a 10 | * case-insensitive manner, regardless of what sort of filesystem or 11 | * archive they reside in. It does this by enumerating directories as 12 | * needed and manually locating matching entries. 13 | * 14 | * Please note that this brings with it some caveats: 15 | * - On filesystems that are case-insensitive to start with, such as those 16 | * used on Windows or MacOS, you are adding extra overhead. 17 | * - On filesystems that are case-sensitive, you might select the wrong dir 18 | * or file (which brings security considerations and potential bugs). This 19 | * code favours exact case matches, but you will lose access to otherwise 20 | * duplicate filenames, or you might go down a wrong directory tree, etc. 21 | * In practice, this is rarely a problem, but you need to be aware of it. 22 | * - This doesn't do _anything_ with the write directory; you're on your 23 | * own for opening the right files for writing. You can sort of get around 24 | * this by adding your write directory to the search path, but then the 25 | * interpolated directory tree can screw you up even more. 26 | * 27 | * This code should be considered an aid for legacy code. New development 28 | * shouldn't do things that require this aid in the first place. :) 29 | * 30 | * Usage: Set up PhysicsFS as you normally would, then use 31 | * PHYSFSEXT_locateCorrectCase() to get a "correct" pathname to pass to 32 | * functions like PHYSFS_openRead(), etc. 33 | * 34 | * License: this code is public domain. I make no warranty that it is useful, 35 | * correct, harmless, or environmentally safe. 36 | * 37 | * This particular file may be used however you like, including copying it 38 | * verbatim into a closed-source project, exploiting it commercially, and 39 | * removing any trace of my name from the source (although I hope you won't 40 | * do that). I welcome enhancements and corrections to this file, but I do 41 | * not require you to send me patches if you make changes. This code has 42 | * NO WARRANTY. 43 | * 44 | * Unless otherwise stated, the rest of PhysicsFS falls under the zlib license. 45 | * Please see LICENSE.txt in the root of the source tree. 46 | * 47 | * \author Ryan C. Gordon. 48 | */ 49 | 50 | #ifdef __cplusplus 51 | extern "C" { 52 | #endif 53 | 54 | /** 55 | * \fn int PHYSFSEXT_locateCorrectCase(char *buf) 56 | * \brief Find an existing filename with matching case. 57 | * 58 | * This function will look for a path/filename that matches the passed in 59 | * buffer. Each element of the buffer's path is checked for a 60 | * case-insensitive match. The buffer must specify a null-terminated string 61 | * in platform-independent notation. 62 | * 63 | * Please note results may be skewed differently depending on whether symlinks 64 | * are enabled or not. 65 | * 66 | * Each element of the buffer is overwritten with the actual case of an 67 | * existing match. If there is no match, the search aborts and reports an 68 | * error. Exact matches are favored over case-insensitive matches. 69 | * 70 | * THIS IS RISKY. Please do not use this function for anything but legacy code. 71 | * 72 | * \param buf Buffer with null-terminated string of path/file to locate. 73 | * This buffer will be modified by this function. 74 | * \return zero if match was found, -1 if the final element (the file itself) 75 | * is missing, -2 if one of the parent directories is missing. 76 | */ 77 | int PHYSFSEXT_locateCorrectCase(char *buf); 78 | 79 | #ifdef __cplusplus 80 | } 81 | #endif 82 | 83 | #endif /* include-once blocker. */ 84 | 85 | /* end of ignorecase.h ... */ 86 | 87 | -------------------------------------------------------------------------------- /extras/makecasefoldhashtable.pl: -------------------------------------------------------------------------------- 1 | #!/usr/bin/perl -w 2 | 3 | use warnings; 4 | use strict; 5 | 6 | my $HASHBUCKETS1_16 = 256; 7 | my $HASHBUCKETS1_32 = 16; 8 | my $HASHBUCKETS2_16 = 16; 9 | my $HASHBUCKETS3_16 = 4; 10 | 11 | print <<__EOF__; 12 | /* 13 | * This file is part of PhysicsFS (https://icculus.org/physfs/) 14 | * 15 | * This data generated by physfs/extras/makecasefoldhashtable.pl ... 16 | * Do not manually edit this file! 17 | * 18 | * Please see the file LICENSE.txt in the source's root directory. 19 | */ 20 | 21 | #ifndef _INCLUDE_PHYSFS_CASEFOLDING_H_ 22 | #define _INCLUDE_PHYSFS_CASEFOLDING_H_ 23 | 24 | #ifndef __PHYSICSFS_INTERNAL__ 25 | #error Do not include this header from your applications. 26 | #endif 27 | 28 | /* We build three simple hashmaps here: one that maps Unicode codepoints to 29 | a one, two, or three lowercase codepoints. To retrieve this info: look at 30 | case_fold_hashX, where X is 1, 2, or 3. Most foldable codepoints fold to one, 31 | a few dozen fold to two, and a handful fold to three. If the codepoint isn't 32 | in any of these hashes, it doesn't fold (no separate upper and lowercase). 33 | 34 | Almost all these codepoints fit into 16 bits, so we hash them as such to save 35 | memory. If a codepoint is > 0xFFFF, we have separate hashes for them, 36 | since there are (currently) only about 120 of them and (currently) all of them 37 | map to a single lowercase codepoint. */ 38 | 39 | typedef struct CaseFoldMapping1_32 40 | { 41 | PHYSFS_uint32 from; 42 | PHYSFS_uint32 to0; 43 | } CaseFoldMapping1_32; 44 | 45 | typedef struct CaseFoldMapping1_16 46 | { 47 | PHYSFS_uint16 from; 48 | PHYSFS_uint16 to0; 49 | } CaseFoldMapping1_16; 50 | 51 | typedef struct CaseFoldMapping2_16 52 | { 53 | PHYSFS_uint16 from; 54 | PHYSFS_uint16 to0; 55 | PHYSFS_uint16 to1; 56 | } CaseFoldMapping2_16; 57 | 58 | typedef struct CaseFoldMapping3_16 59 | { 60 | PHYSFS_uint16 from; 61 | PHYSFS_uint16 to0; 62 | PHYSFS_uint16 to1; 63 | PHYSFS_uint16 to2; 64 | } CaseFoldMapping3_16; 65 | 66 | typedef struct CaseFoldHashBucket1_16 67 | { 68 | const CaseFoldMapping1_16 *list; 69 | const PHYSFS_uint8 count; 70 | } CaseFoldHashBucket1_16; 71 | 72 | typedef struct CaseFoldHashBucket1_32 73 | { 74 | const CaseFoldMapping1_32 *list; 75 | const PHYSFS_uint8 count; 76 | } CaseFoldHashBucket1_32; 77 | 78 | typedef struct CaseFoldHashBucket2_16 79 | { 80 | const CaseFoldMapping2_16 *list; 81 | const PHYSFS_uint8 count; 82 | } CaseFoldHashBucket2_16; 83 | 84 | typedef struct CaseFoldHashBucket3_16 85 | { 86 | const CaseFoldMapping3_16 *list; 87 | const PHYSFS_uint8 count; 88 | } CaseFoldHashBucket3_16; 89 | 90 | __EOF__ 91 | 92 | 93 | my @foldPairs1_16; 94 | my @foldPairs2_16; 95 | my @foldPairs3_16; 96 | my @foldPairs1_32; 97 | 98 | for (my $i = 0; $i < $HASHBUCKETS1_16; $i++) { 99 | $foldPairs1_16[$i] = ''; 100 | } 101 | 102 | for (my $i = 0; $i < $HASHBUCKETS1_32; $i++) { 103 | $foldPairs1_32[$i] = ''; 104 | } 105 | 106 | for (my $i = 0; $i < $HASHBUCKETS2_16; $i++) { 107 | $foldPairs2_16[$i] = ''; 108 | } 109 | 110 | for (my $i = 0; $i < $HASHBUCKETS3_16; $i++) { 111 | $foldPairs3_16[$i] = ''; 112 | } 113 | 114 | open(FH,'<','casefolding.txt') or die("failed to open casefolding.txt: $!\n"); 115 | while () { 116 | chomp; 117 | # strip comments from textfile... 118 | s/\#.*\Z//; 119 | 120 | # strip whitespace... 121 | s/\A\s+//; 122 | s/\s+\Z//; 123 | 124 | next if not /\A([a-fA-F0-9]+)\;\s*(.)\;\s*(.+)\;/; 125 | my ($code, $status, $mapping) = ($1, $2, $3); 126 | 127 | my $hexxed = hex($code); 128 | #print("// code '$code' status '$status' mapping '$mapping'\n"); 129 | 130 | if (($status eq 'C') or ($status eq 'F')) { 131 | my ($map1, $map2, $map3) = (undef, undef, undef); 132 | $map1 = $1 if $mapping =~ s/\A([a-fA-F0-9]+)(\s*|\Z)//; 133 | $map2 = $1 if $mapping =~ s/\A([a-fA-F0-9]+)(\s*|\Z)//; 134 | $map3 = $1 if $mapping =~ s/\A([a-fA-F0-9]+)(\s*|\Z)//; 135 | die("mapping space too small for '$code'\n") if ($mapping ne ''); 136 | die("problem parsing mapping for '$code'\n") if (not defined($map1)); 137 | 138 | if ($hexxed < 128) { 139 | # Just ignore these, we'll handle the low-ASCII ones ourselves. 140 | } elsif ($hexxed > 0xFFFF) { 141 | # We just need to add the 32-bit 2 and/or 3 codepoint maps if this die()'s here. 142 | die("Uhoh, a codepoint > 0xFFFF that folds to multiple codepoints! Fixme.") if defined($map2); 143 | my $hashed = (($hexxed ^ ($hexxed >> 8)) & ($HASHBUCKETS1_32-1)); 144 | #print("// hexxed '$hexxed' hashed1 '$hashed'\n"); 145 | $foldPairs1_32[$hashed] .= " { 0x$code, 0x$map1 },\n"; 146 | } elsif (not defined($map2)) { 147 | my $hashed = (($hexxed ^ ($hexxed >> 8)) & ($HASHBUCKETS1_16-1)); 148 | #print("// hexxed '$hexxed' hashed1 '$hashed'\n"); 149 | $foldPairs1_16[$hashed] .= " { 0x$code, 0x$map1 },\n"; 150 | } elsif (not defined($map3)) { 151 | my $hashed = (($hexxed ^ ($hexxed >> 8)) & ($HASHBUCKETS2_16-1)); 152 | #print("// hexxed '$hexxed' hashed2 '$hashed'\n"); 153 | $foldPairs2_16[$hashed] .= " { 0x$code, 0x$map1, 0x$map2 },\n"; 154 | } else { 155 | my $hashed = (($hexxed ^ ($hexxed >> 8)) & ($HASHBUCKETS3_16-1)); 156 | #print("// hexxed '$hexxed' hashed3 '$hashed'\n"); 157 | $foldPairs3_16[$hashed] .= " { 0x$code, 0x$map1, 0x$map2, 0x$map3 },\n"; 158 | } 159 | } 160 | } 161 | close(FH); 162 | 163 | for (my $i = 0; $i < $HASHBUCKETS1_16; $i++) { 164 | $foldPairs1_16[$i] =~ s/,\n\Z//; 165 | my $str = $foldPairs1_16[$i]; 166 | next if $str eq ''; 167 | my $num = '000' . $i; 168 | $num =~ s/\A.*?(\d\d\d)\Z/$1/; 169 | my $sym = "case_fold1_16_${num}"; 170 | print("static const CaseFoldMapping1_16 ${sym}[] = {\n$str\n};\n\n"); 171 | } 172 | 173 | for (my $i = 0; $i < $HASHBUCKETS1_32; $i++) { 174 | $foldPairs1_32[$i] =~ s/,\n\Z//; 175 | my $str = $foldPairs1_32[$i]; 176 | next if $str eq ''; 177 | my $num = '000' . $i; 178 | $num =~ s/\A.*?(\d\d\d)\Z/$1/; 179 | my $sym = "case_fold1_32_${num}"; 180 | print("static const CaseFoldMapping1_32 ${sym}[] = {\n$str\n};\n\n"); 181 | } 182 | 183 | for (my $i = 0; $i < $HASHBUCKETS2_16; $i++) { 184 | $foldPairs2_16[$i] =~ s/,\n\Z//; 185 | my $str = $foldPairs2_16[$i]; 186 | next if $str eq ''; 187 | my $num = '000' . $i; 188 | $num =~ s/\A.*?(\d\d\d)\Z/$1/; 189 | my $sym = "case_fold2_16_${num}"; 190 | print("static const CaseFoldMapping2_16 ${sym}[] = {\n$str\n};\n\n"); 191 | } 192 | 193 | for (my $i = 0; $i < $HASHBUCKETS3_16; $i++) { 194 | $foldPairs3_16[$i] =~ s/,\n\Z//; 195 | my $str = $foldPairs3_16[$i]; 196 | next if $str eq ''; 197 | my $num = '000' . $i; 198 | $num =~ s/\A.*?(\d\d\d)\Z/$1/; 199 | my $sym = "case_fold3_16_${num}"; 200 | print("static const CaseFoldMapping3_16 ${sym}[] = {\n$str\n};\n\n"); 201 | } 202 | 203 | print("static const CaseFoldHashBucket1_16 case_fold_hash1_16[] = {\n"); 204 | 205 | for (my $i = 0; $i < $HASHBUCKETS1_16; $i++) { 206 | my $str = $foldPairs1_16[$i]; 207 | if ($str eq '') { 208 | print(" { NULL, 0 },\n"); 209 | } else { 210 | my $num = '000' . $i; 211 | $num =~ s/\A.*?(\d\d\d)\Z/$1/; 212 | my $sym = "case_fold1_16_${num}"; 213 | print(" { $sym, __PHYSFS_ARRAYLEN($sym) },\n"); 214 | } 215 | } 216 | print("};\n\n"); 217 | 218 | 219 | print("static const CaseFoldHashBucket1_32 case_fold_hash1_32[] = {\n"); 220 | 221 | for (my $i = 0; $i < $HASHBUCKETS1_32; $i++) { 222 | my $str = $foldPairs1_32[$i]; 223 | if ($str eq '') { 224 | print(" { NULL, 0 },\n"); 225 | } else { 226 | my $num = '000' . $i; 227 | $num =~ s/\A.*?(\d\d\d)\Z/$1/; 228 | my $sym = "case_fold1_32_${num}"; 229 | print(" { $sym, __PHYSFS_ARRAYLEN($sym) },\n"); 230 | } 231 | } 232 | print("};\n\n"); 233 | 234 | 235 | print("static const CaseFoldHashBucket2_16 case_fold_hash2_16[] = {\n"); 236 | 237 | for (my $i = 0; $i < $HASHBUCKETS2_16; $i++) { 238 | my $str = $foldPairs2_16[$i]; 239 | if ($str eq '') { 240 | print(" { NULL, 0 },\n"); 241 | } else { 242 | my $num = '000' . $i; 243 | $num =~ s/\A.*?(\d\d\d)\Z/$1/; 244 | my $sym = "case_fold2_16_${num}"; 245 | print(" { $sym, __PHYSFS_ARRAYLEN($sym) },\n"); 246 | } 247 | } 248 | print("};\n\n"); 249 | 250 | print("static const CaseFoldHashBucket3_16 case_fold_hash3_16[] = {\n"); 251 | 252 | for (my $i = 0; $i < $HASHBUCKETS3_16; $i++) { 253 | my $str = $foldPairs3_16[$i]; 254 | if ($str eq '') { 255 | print(" { NULL, 0 },\n"); 256 | } else { 257 | my $num = '000' . $i; 258 | $num =~ s/\A.*?(\d\d\d)\Z/$1/; 259 | my $sym = "case_fold3_16_${num}"; 260 | print(" { $sym, __PHYSFS_ARRAYLEN($sym) },\n"); 261 | } 262 | } 263 | print("};\n\n"); 264 | 265 | print <<__EOF__; 266 | 267 | #endif /* _INCLUDE_PHYSFS_CASEFOLDING_H_ */ 268 | 269 | /* end of physfs_casefolding.h ... */ 270 | 271 | __EOF__ 272 | 273 | exit 0; 274 | 275 | # end of makecashfoldhashtable.pl ... 276 | 277 | -------------------------------------------------------------------------------- /extras/physfs.pc.in: -------------------------------------------------------------------------------- 1 | prefix=@CMAKE_INSTALL_PREFIX@ 2 | exec_prefix=${prefix} 3 | libdir=${exec_prefix}/lib 4 | includedir=${prefix}/include 5 | 6 | Name: PhysicsFS 7 | Description: PhysicsFS is a library to provide abstract access to various archives. 8 | URL: https://icculus.org/physfs/ 9 | Version: @PHYSFS_VERSION@ 10 | Libs: -L${libdir} -lphysfs 11 | Cflags: -I${includedir} 12 | -------------------------------------------------------------------------------- /extras/physfshttpd.c: -------------------------------------------------------------------------------- 1 | /* 2 | * This is a quick and dirty HTTP server that uses PhysicsFS to retrieve 3 | * files. It is not robust at all, probably buggy, and definitely poorly 4 | * designed. It's just meant to show that it can be done. 5 | * 6 | * Basically, you compile this code, and run it: 7 | * ./physfshttpd archive1.zip archive2.zip /path/to/a/real/dir etc... 8 | * 9 | * The files are appended in order to the PhysicsFS search path, and when 10 | * a client request comes in, it looks for the file in said search path. 11 | * 12 | * My goal was to make this work in less than 300 lines of C, so again, it's 13 | * not to be used for any serious purpose. Patches to make this application 14 | * suck less will be readily and gratefully accepted. 15 | * 16 | * Command line I used to build this on Linux: 17 | * gcc -Wall -Werror -g -o bin/physfshttpd extras/physfshttpd.c -lphysfs 18 | * 19 | * License: this code is public domain. I make no warranty that it is useful, 20 | * correct, harmless, or environmentally safe. 21 | * 22 | * This particular file may be used however you like, including copying it 23 | * verbatim into a closed-source project, exploiting it commercially, and 24 | * removing any trace of my name from the source (although I hope you won't 25 | * do that). I welcome enhancements and corrections to this file, but I do 26 | * not require you to send me patches if you make changes. This code has 27 | * NO WARRANTY. 28 | * 29 | * Unless otherwise stated, the rest of PhysicsFS falls under the zlib license. 30 | * Please see LICENSE.txt in the root of the source tree. 31 | * 32 | * This file was written by Ryan C. Gordon. (icculus@icculus.org). 33 | */ 34 | 35 | #include 36 | #include 37 | #include 38 | #include 39 | #include 40 | #include 41 | #include 42 | #include 43 | #include 44 | #include 45 | #include 46 | 47 | #ifndef LACKING_SIGNALS 48 | #include 49 | #endif 50 | 51 | #ifndef LACKING_PROTOENT 52 | #include 53 | #endif 54 | 55 | #include "physfs.h" 56 | 57 | 58 | #define DEFAULT_PORTNUM 8080 59 | 60 | typedef struct 61 | { 62 | int sock; 63 | struct sockaddr *addr; 64 | socklen_t addrlen; 65 | } http_args; 66 | 67 | 68 | #define txt404 \ 69 | "HTTP/1.0 404 Not Found\n" \ 70 | "Connection: close\n" \ 71 | "Content-Type: text/html; charset=utf-8\n" \ 72 | "\n" \ 73 | "404 Not Found\n" \ 74 | "Can't find '%s'.\n\n" \ 75 | 76 | #define txt200 \ 77 | "HTTP/1.0 200 OK\n" \ 78 | "Connection: close\n" \ 79 | "Content-Type: %s\n" \ 80 | "\n" 81 | 82 | static const char *lastError(void) 83 | { 84 | return PHYSFS_getErrorByCode(PHYSFS_getLastErrorCode()); 85 | } /* lastError */ 86 | 87 | static int writeAll(const char *ipstr, const int sock, void *buf, const size_t len) 88 | { 89 | if (write(sock, buf, len) != len) 90 | { 91 | printf("%s: Write error to socket.\n", ipstr); 92 | return 0; 93 | } /* if */ 94 | 95 | return 1; 96 | } /* writeAll */ 97 | 98 | static int writeString(const char *ipstr, const int sock, const char *fmt, ...) 99 | { 100 | /* none of this is robust against large strings or HTML escaping. */ 101 | char buffer[1024]; 102 | int len; 103 | va_list ap; 104 | va_start(ap, fmt); 105 | len = vsnprintf(buffer, sizeof (buffer), fmt, ap); 106 | va_end(ap); 107 | if (len < 0) 108 | { 109 | printf("uhoh, vsnprintf() failed!\n"); 110 | return 0; 111 | } /* if */ 112 | 113 | return writeAll(ipstr, sock, buffer, len); 114 | } /* writeString */ 115 | 116 | 117 | static void feed_file_http(const char *ipstr, int sock, const char *fname) 118 | { 119 | PHYSFS_File *in = PHYSFS_openRead(fname); 120 | 121 | if (in == NULL) 122 | { 123 | printf("%s: Can't open [%s]: %s.\n", ipstr, fname, lastError()); 124 | writeString(ipstr, sock, txt404, fname); 125 | return; 126 | } /* if */ 127 | 128 | /* !!! FIXME: mimetype */ 129 | if (writeString(ipstr, sock, txt200, "text/plain; charset=utf-8")) 130 | { 131 | do 132 | { 133 | char buffer[1024]; 134 | PHYSFS_sint64 br = PHYSFS_readBytes(in, buffer, sizeof (buffer)); 135 | if (br == -1) 136 | { 137 | printf("%s: Read error: %s.\n", ipstr, lastError()); 138 | break; 139 | } /* if */ 140 | 141 | else if (!writeAll(ipstr, sock, buffer, (size_t) br)) 142 | { 143 | break; 144 | } /* else if */ 145 | } while (!PHYSFS_eof(in)); 146 | } /* if */ 147 | 148 | PHYSFS_close(in); 149 | } /* feed_file_http */ 150 | 151 | 152 | static void feed_dirlist_http(const char *ipstr, int sock, 153 | const char *dname, char **list) 154 | { 155 | int i; 156 | 157 | if (!writeString(ipstr, sock, txt200, "text/html; charset=utf-8")) 158 | return; 159 | 160 | else if (!writeString(ipstr, sock, 161 | "Directory %s" 162 | "

Directory %s

    \n", 163 | dname, dname)) 164 | return; 165 | 166 | if (strcmp(dname, "/") == 0) 167 | dname = ""; 168 | 169 | for (i = 0; list[i]; i++) 170 | { 171 | const char *fname = list[i]; 172 | if (!writeString(ipstr, sock, 173 | "
  • %s
  • \n", dname, fname, fname)) 174 | break; 175 | } /* for */ 176 | 177 | writeString(ipstr, sock, "
\n"); 178 | } /* feed_dirlist_http */ 179 | 180 | static void feed_dir_http(const char *ipstr, int sock, const char *dname) 181 | { 182 | char **list = PHYSFS_enumerateFiles(dname); 183 | if (list == NULL) 184 | { 185 | printf("%s: Can't enumerate directory [%s]: %s.\n", 186 | ipstr, dname, lastError()); 187 | writeString(ipstr, sock, txt404, dname); 188 | return; 189 | } /* if */ 190 | 191 | feed_dirlist_http(ipstr, sock, dname, list); 192 | PHYSFS_freeList(list); 193 | } /* feed_dir_http */ 194 | 195 | static void feed_http_request(const char *ipstr, int sock, const char *fname) 196 | { 197 | PHYSFS_Stat statbuf; 198 | 199 | printf("%s: requested [%s].\n", ipstr, fname); 200 | 201 | if (!PHYSFS_stat(fname, &statbuf)) 202 | { 203 | printf("%s: Can't stat [%s]: %s.\n", ipstr, fname, lastError()); 204 | writeString(ipstr, sock, txt404, fname); 205 | return; 206 | } /* if */ 207 | 208 | if (statbuf.filetype == PHYSFS_FILETYPE_DIRECTORY) 209 | feed_dir_http(ipstr, sock, fname); 210 | else 211 | feed_file_http(ipstr, sock, fname); 212 | } /* feed_http_request */ 213 | 214 | 215 | static void *do_http(void *_args) 216 | { 217 | http_args *args = (http_args *) _args; 218 | char ipstr[128]; 219 | char buffer[512]; 220 | char *ptr; 221 | strncpy(ipstr, inet_ntoa(((struct sockaddr_in *) args->addr)->sin_addr), 222 | sizeof (ipstr)); 223 | ipstr[sizeof (ipstr) - 1] = '\0'; 224 | 225 | printf("%s: connected.\n", ipstr); 226 | read(args->sock, buffer, sizeof (buffer)); 227 | buffer[sizeof (buffer) - 1] = '\0'; 228 | ptr = strchr(buffer, '\n'); 229 | if (!ptr) 230 | printf("%s: potentially bogus request.\n", ipstr); 231 | else 232 | { 233 | *ptr = '\0'; 234 | ptr = strchr(buffer, '\r'); 235 | if (ptr != NULL) 236 | *ptr = '\0'; 237 | 238 | if ((toupper(buffer[0]) == 'G') && 239 | (toupper(buffer[1]) == 'E') && 240 | (toupper(buffer[2]) == 'T') && 241 | (toupper(buffer[3]) == ' ') && 242 | (toupper(buffer[4]) == '/')) 243 | { 244 | ptr = strchr(buffer + 5, ' '); 245 | if (ptr != NULL) 246 | *ptr = '\0'; 247 | feed_http_request(ipstr, args->sock, buffer + 4); 248 | } /* if */ 249 | } /* else */ 250 | 251 | /* !!! FIXME: Time the transfer. */ 252 | printf("%s: closing connection.\n", ipstr); 253 | close(args->sock); 254 | free(args->addr); 255 | free(args); 256 | return NULL; 257 | } /* do_http */ 258 | 259 | 260 | static void serve_http_request(int sock, struct sockaddr *addr, 261 | socklen_t addrlen) 262 | { 263 | http_args *args = (http_args *) malloc(sizeof (http_args)); 264 | if (args == NULL) 265 | { 266 | printf("out of memory.\n"); 267 | return; 268 | } /* if */ 269 | args->addr = (struct sockaddr *) malloc(addrlen); 270 | if (args->addr == NULL) 271 | { 272 | free(args); 273 | printf("out of memory.\n"); 274 | return; 275 | } /* if */ 276 | 277 | args->sock = sock; 278 | args->addrlen = addrlen; 279 | memcpy(args->addr, addr, addrlen); 280 | 281 | /* !!! FIXME: optionally spin a thread... */ 282 | do_http((void *) args); 283 | } /* server_http_request */ 284 | 285 | 286 | static int create_listen_socket(short portnum) 287 | { 288 | int retval = -1; 289 | int protocol = 0; /* pray this is right. */ 290 | 291 | #ifndef LACKING_PROTOENT 292 | struct protoent *prot; 293 | setprotoent(0); 294 | prot = getprotobyname("tcp"); 295 | if (prot != NULL) 296 | protocol = prot->p_proto; 297 | #endif 298 | 299 | retval = socket(PF_INET, SOCK_STREAM, protocol); 300 | if (retval >= 0) 301 | { 302 | struct sockaddr_in addr; 303 | addr.sin_family = AF_INET; 304 | addr.sin_port = htons(portnum); 305 | addr.sin_addr.s_addr = INADDR_ANY; 306 | if ((bind(retval, (struct sockaddr *) &addr, (socklen_t) sizeof (addr)) == -1) || 307 | (listen(retval, 5) == -1)) 308 | { 309 | close(retval); 310 | retval = -1; 311 | } /* if */ 312 | } /* if */ 313 | 314 | return retval; 315 | } /* create_listen_socket */ 316 | 317 | 318 | static int listensocket = -1; 319 | 320 | void at_exit_cleanup(void) 321 | { 322 | /* 323 | * !!! FIXME: If thread support, signal threads to terminate and 324 | * !!! FIXME: wait for them to clean up. 325 | */ 326 | 327 | if (listensocket >= 0) 328 | close(listensocket); 329 | 330 | if (!PHYSFS_deinit()) 331 | printf("PHYSFS_deinit() failed: %s\n", lastError()); 332 | } /* at_exit_cleanup */ 333 | 334 | 335 | int main(int argc, char **argv) 336 | { 337 | int i; 338 | int portnum = DEFAULT_PORTNUM; 339 | 340 | setbuf(stdout, NULL); 341 | setbuf(stderr, NULL); 342 | 343 | #ifndef LACKING_SIGNALS 344 | /* I'm not sure if this qualifies as a cheap trick... */ 345 | signal(SIGTERM, exit); 346 | signal(SIGINT, exit); 347 | signal(SIGFPE, exit); 348 | signal(SIGSEGV, exit); 349 | signal(SIGPIPE, exit); 350 | signal(SIGILL, exit); 351 | #endif 352 | 353 | if (argc == 1) 354 | { 355 | printf("USAGE: %s [archive2 [... archiveN]]\n", argv[0]); 356 | return 42; 357 | } /* if */ 358 | 359 | if (!PHYSFS_init(argv[0])) 360 | { 361 | printf("PHYSFS_init() failed: %s\n", lastError()); 362 | return 42; 363 | } /* if */ 364 | 365 | /* normally, this is bad practice, but oh well. */ 366 | atexit(at_exit_cleanup); 367 | 368 | for (i = 1; i < argc; i++) 369 | { 370 | if (!PHYSFS_mount(argv[i], NULL, 1)) 371 | printf(" WARNING: failed to add [%s] to search path.\n", argv[i]); 372 | } /* else */ 373 | 374 | listensocket = create_listen_socket(portnum); 375 | if (listensocket < 0) 376 | { 377 | printf("listen socket failed to create.\n"); 378 | return 42; 379 | } /* if */ 380 | 381 | while (1) /* infinite loop for now. */ 382 | { 383 | struct sockaddr addr; 384 | socklen_t len; 385 | int s = accept(listensocket, &addr, &len); 386 | if (s < 0) 387 | { 388 | printf("accept() failed: %s\n", strerror(errno)); 389 | close(listensocket); 390 | return 42; 391 | } /* if */ 392 | 393 | serve_http_request(s, &addr, len); 394 | } /* while */ 395 | 396 | return 0; 397 | } /* main */ 398 | 399 | /* end of physfshttpd.c ... */ 400 | 401 | -------------------------------------------------------------------------------- /extras/physfsrwops.c: -------------------------------------------------------------------------------- 1 | /* 2 | * This code provides a glue layer between PhysicsFS and Simple Directmedia 3 | * Layer's (SDL) RWops i/o abstraction. 4 | * 5 | * License: this code is public domain. I make no warranty that it is useful, 6 | * correct, harmless, or environmentally safe. 7 | * 8 | * This particular file may be used however you like, including copying it 9 | * verbatim into a closed-source project, exploiting it commercially, and 10 | * removing any trace of my name from the source (although I hope you won't 11 | * do that). I welcome enhancements and corrections to this file, but I do 12 | * not require you to send me patches if you make changes. This code has 13 | * NO WARRANTY. 14 | * 15 | * Unless otherwise stated, the rest of PhysicsFS falls under the zlib license. 16 | * Please see LICENSE.txt in the root of the source tree. 17 | * 18 | * SDL 1.2 falls under the LGPL license. SDL 1.3+ is zlib, like PhysicsFS. 19 | * You can get SDL at https://www.libsdl.org/ 20 | * 21 | * This file was written by Ryan C. Gordon. (icculus@icculus.org). 22 | */ 23 | 24 | #include /* used for SEEK_SET, SEEK_CUR, SEEK_END ... */ 25 | #include "physfsrwops.h" 26 | 27 | /* SDL's RWOPS interface changed a little in SDL 2.0... */ 28 | #if defined(SDL_VERSION_ATLEAST) 29 | #if SDL_VERSION_ATLEAST(2, 0, 0) 30 | #define TARGET_SDL2 1 31 | #endif 32 | #endif 33 | 34 | #if !TARGET_SDL2 35 | #ifndef RW_SEEK_SET 36 | #define RW_SEEK_SET SEEK_SET 37 | #endif 38 | #ifndef RW_SEEK_CUR 39 | #define RW_SEEK_CUR SEEK_CUR 40 | #endif 41 | #ifndef RW_SEEK_END 42 | #define RW_SEEK_END SEEK_END 43 | #endif 44 | #endif 45 | 46 | #if TARGET_SDL2 47 | static Sint64 SDLCALL physfsrwops_size(struct SDL_RWops *rw) 48 | { 49 | PHYSFS_File *handle = (PHYSFS_File *) rw->hidden.unknown.data1; 50 | return (Sint64) PHYSFS_fileLength(handle); 51 | } /* physfsrwops_size */ 52 | #endif 53 | 54 | 55 | #if TARGET_SDL2 56 | static Sint64 SDLCALL physfsrwops_seek(struct SDL_RWops *rw, Sint64 offset, int whence) 57 | #else 58 | static int physfsrwops_seek(SDL_RWops *rw, int offset, int whence) 59 | #endif 60 | { 61 | PHYSFS_File *handle = (PHYSFS_File *) rw->hidden.unknown.data1; 62 | PHYSFS_sint64 pos = 0; 63 | 64 | if (whence == RW_SEEK_SET) 65 | pos = (PHYSFS_sint64) offset; 66 | 67 | else if (whence == RW_SEEK_CUR) 68 | { 69 | const PHYSFS_sint64 current = PHYSFS_tell(handle); 70 | if (current == -1) 71 | { 72 | SDL_SetError("Can't find position in file: %s", 73 | PHYSFS_getErrorByCode(PHYSFS_getLastErrorCode())); 74 | return -1; 75 | } /* if */ 76 | 77 | if (offset == 0) /* this is a "tell" call. We're done. */ 78 | { 79 | #if TARGET_SDL2 80 | return (Sint64) current; 81 | #else 82 | return (int) current; 83 | #endif 84 | } /* if */ 85 | 86 | pos = current + ((PHYSFS_sint64) offset); 87 | } /* else if */ 88 | 89 | else if (whence == RW_SEEK_END) 90 | { 91 | const PHYSFS_sint64 len = PHYSFS_fileLength(handle); 92 | if (len == -1) 93 | { 94 | SDL_SetError("Can't find end of file: %s", PHYSFS_getErrorByCode(PHYSFS_getLastErrorCode())); 95 | return -1; 96 | } /* if */ 97 | 98 | pos = len + ((PHYSFS_sint64) offset); 99 | } /* else if */ 100 | 101 | else 102 | { 103 | SDL_SetError("Invalid 'whence' parameter."); 104 | return -1; 105 | } /* else */ 106 | 107 | if ( pos < 0 ) 108 | { 109 | SDL_SetError("Attempt to seek past start of file."); 110 | return -1; 111 | } /* if */ 112 | 113 | if (!PHYSFS_seek(handle, (PHYSFS_uint64) pos)) 114 | { 115 | SDL_SetError("PhysicsFS error: %s", PHYSFS_getErrorByCode(PHYSFS_getLastErrorCode())); 116 | return -1; 117 | } /* if */ 118 | 119 | #if TARGET_SDL2 120 | return (Sint64) pos; 121 | #else 122 | return (int) pos; 123 | #endif 124 | } /* physfsrwops_seek */ 125 | 126 | 127 | #if TARGET_SDL2 128 | static size_t SDLCALL physfsrwops_read(struct SDL_RWops *rw, void *ptr, 129 | size_t size, size_t maxnum) 130 | #else 131 | static int physfsrwops_read(SDL_RWops *rw, void *ptr, int size, int maxnum) 132 | #endif 133 | { 134 | PHYSFS_File *handle = (PHYSFS_File *) rw->hidden.unknown.data1; 135 | const PHYSFS_uint64 readlen = (PHYSFS_uint64) (maxnum * size); 136 | const PHYSFS_sint64 rc = PHYSFS_readBytes(handle, ptr, readlen); 137 | if (rc != ((PHYSFS_sint64) readlen)) 138 | { 139 | if (!PHYSFS_eof(handle)) /* not EOF? Must be an error. */ 140 | { 141 | SDL_SetError("PhysicsFS error: %s", PHYSFS_getErrorByCode(PHYSFS_getLastErrorCode())); 142 | 143 | #if TARGET_SDL2 144 | return 0; 145 | #else 146 | return -1; 147 | #endif 148 | } /* if */ 149 | } /* if */ 150 | 151 | #if TARGET_SDL2 152 | return (size_t) rc / size; 153 | #else 154 | return (int) rc / size; 155 | #endif 156 | } /* physfsrwops_read */ 157 | 158 | 159 | #if TARGET_SDL2 160 | static size_t SDLCALL physfsrwops_write(struct SDL_RWops *rw, const void *ptr, 161 | size_t size, size_t num) 162 | #else 163 | static int physfsrwops_write(SDL_RWops *rw, const void *ptr, int size, int num) 164 | #endif 165 | { 166 | PHYSFS_File *handle = (PHYSFS_File *) rw->hidden.unknown.data1; 167 | const PHYSFS_uint64 writelen = (PHYSFS_uint64) (num * size); 168 | const PHYSFS_sint64 rc = PHYSFS_writeBytes(handle, ptr, writelen); 169 | if (rc != ((PHYSFS_sint64) writelen)) 170 | SDL_SetError("PhysicsFS error: %s", PHYSFS_getErrorByCode(PHYSFS_getLastErrorCode())); 171 | 172 | #if TARGET_SDL2 173 | return (size_t) rc; 174 | #else 175 | return (int) rc; 176 | #endif 177 | } /* physfsrwops_write */ 178 | 179 | 180 | static int physfsrwops_close(SDL_RWops *rw) 181 | { 182 | PHYSFS_File *handle = (PHYSFS_File *) rw->hidden.unknown.data1; 183 | if (!PHYSFS_close(handle)) 184 | { 185 | SDL_SetError("PhysicsFS error: %s", PHYSFS_getErrorByCode(PHYSFS_getLastErrorCode())); 186 | return -1; 187 | } /* if */ 188 | 189 | SDL_FreeRW(rw); 190 | return 0; 191 | } /* physfsrwops_close */ 192 | 193 | 194 | static SDL_RWops *create_rwops(PHYSFS_File *handle) 195 | { 196 | SDL_RWops *retval = NULL; 197 | 198 | if (handle == NULL) 199 | SDL_SetError("PhysicsFS error: %s", PHYSFS_getErrorByCode(PHYSFS_getLastErrorCode())); 200 | else 201 | { 202 | retval = SDL_AllocRW(); 203 | if (retval != NULL) 204 | { 205 | #if TARGET_SDL2 206 | retval->size = physfsrwops_size; 207 | #endif 208 | retval->seek = physfsrwops_seek; 209 | retval->read = physfsrwops_read; 210 | retval->write = physfsrwops_write; 211 | retval->close = physfsrwops_close; 212 | retval->hidden.unknown.data1 = handle; 213 | } /* if */ 214 | } /* else */ 215 | 216 | return retval; 217 | } /* create_rwops */ 218 | 219 | 220 | SDL_RWops *PHYSFSRWOPS_makeRWops(PHYSFS_File *handle) 221 | { 222 | SDL_RWops *retval = NULL; 223 | if (handle == NULL) 224 | SDL_SetError("NULL pointer passed to PHYSFSRWOPS_makeRWops()."); 225 | else 226 | retval = create_rwops(handle); 227 | 228 | return retval; 229 | } /* PHYSFSRWOPS_makeRWops */ 230 | 231 | 232 | SDL_RWops *PHYSFSRWOPS_openRead(const char *fname) 233 | { 234 | return create_rwops(PHYSFS_openRead(fname)); 235 | } /* PHYSFSRWOPS_openRead */ 236 | 237 | 238 | SDL_RWops *PHYSFSRWOPS_openWrite(const char *fname) 239 | { 240 | return create_rwops(PHYSFS_openWrite(fname)); 241 | } /* PHYSFSRWOPS_openWrite */ 242 | 243 | 244 | SDL_RWops *PHYSFSRWOPS_openAppend(const char *fname) 245 | { 246 | return create_rwops(PHYSFS_openAppend(fname)); 247 | } /* PHYSFSRWOPS_openAppend */ 248 | 249 | 250 | /* end of physfsrwops.c ... */ 251 | 252 | -------------------------------------------------------------------------------- /extras/physfsrwops.h: -------------------------------------------------------------------------------- 1 | /* 2 | * This code provides a glue layer between PhysicsFS and Simple Directmedia 3 | * Layer's (SDL) RWops i/o abstraction. 4 | * 5 | * License: this code is public domain. I make no warranty that it is useful, 6 | * correct, harmless, or environmentally safe. 7 | * 8 | * This particular file may be used however you like, including copying it 9 | * verbatim into a closed-source project, exploiting it commercially, and 10 | * removing any trace of my name from the source (although I hope you won't 11 | * do that). I welcome enhancements and corrections to this file, but I do 12 | * not require you to send me patches if you make changes. This code has 13 | * NO WARRANTY. 14 | * 15 | * Unless otherwise stated, the rest of PhysicsFS falls under the zlib license. 16 | * Please see LICENSE.txt in the root of the source tree. 17 | * 18 | * SDL 1.2 falls under the LGPL license. SDL 1.3+ is zlib, like PhysicsFS. 19 | * You can get SDL at https://www.libsdl.org/ 20 | * 21 | * This file was written by Ryan C. Gordon. (icculus@icculus.org). 22 | */ 23 | 24 | #ifndef _INCLUDE_PHYSFSRWOPS_H_ 25 | #define _INCLUDE_PHYSFSRWOPS_H_ 26 | 27 | #include "physfs.h" 28 | #include "SDL.h" 29 | 30 | #ifdef __cplusplus 31 | extern "C" { 32 | #endif 33 | 34 | /** 35 | * Open a platform-independent filename for reading, and make it accessible 36 | * via an SDL_RWops structure. The file will be closed in PhysicsFS when the 37 | * RWops is closed. PhysicsFS should be configured to your liking before 38 | * opening files through this method. 39 | * 40 | * @param filename File to open in platform-independent notation. 41 | * @return A valid SDL_RWops structure on success, NULL on error. Specifics 42 | * of the error can be gleaned from PHYSFS_getLastError(). 43 | */ 44 | PHYSFS_DECL SDL_RWops *PHYSFSRWOPS_openRead(const char *fname); 45 | 46 | /** 47 | * Open a platform-independent filename for writing, and make it accessible 48 | * via an SDL_RWops structure. The file will be closed in PhysicsFS when the 49 | * RWops is closed. PhysicsFS should be configured to your liking before 50 | * opening files through this method. 51 | * 52 | * @param filename File to open in platform-independent notation. 53 | * @return A valid SDL_RWops structure on success, NULL on error. Specifics 54 | * of the error can be gleaned from PHYSFS_getLastError(). 55 | */ 56 | PHYSFS_DECL SDL_RWops *PHYSFSRWOPS_openWrite(const char *fname); 57 | 58 | /** 59 | * Open a platform-independent filename for appending, and make it accessible 60 | * via an SDL_RWops structure. The file will be closed in PhysicsFS when the 61 | * RWops is closed. PhysicsFS should be configured to your liking before 62 | * opening files through this method. 63 | * 64 | * @param filename File to open in platform-independent notation. 65 | * @return A valid SDL_RWops structure on success, NULL on error. Specifics 66 | * of the error can be gleaned from PHYSFS_getLastError(). 67 | */ 68 | PHYSFS_DECL SDL_RWops *PHYSFSRWOPS_openAppend(const char *fname); 69 | 70 | /** 71 | * Make a SDL_RWops from an existing PhysicsFS file handle. You should 72 | * dispose of any references to the handle after successful creation of 73 | * the RWops. The actual PhysicsFS handle will be destroyed when the 74 | * RWops is closed. 75 | * 76 | * @param handle a valid PhysicsFS file handle. 77 | * @return A valid SDL_RWops structure on success, NULL on error. Specifics 78 | * of the error can be gleaned from PHYSFS_getLastError(). 79 | */ 80 | PHYSFS_DECL SDL_RWops *PHYSFSRWOPS_makeRWops(PHYSFS_File *handle); 81 | 82 | #ifdef __cplusplus 83 | } 84 | #endif 85 | 86 | #endif /* include-once blocker */ 87 | 88 | /* end of physfsrwops.h ... */ 89 | 90 | -------------------------------------------------------------------------------- /extras/physfsunpack.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | #include "physfs.h" 7 | 8 | 9 | static int failure = 0; 10 | 11 | static void modTimeToStr(PHYSFS_sint64 modtime, char *modstr, size_t strsize) 12 | { 13 | const char *str = "unknown modtime"; 14 | if (modtime != -1) 15 | { 16 | time_t t = (time_t) modtime; 17 | str = ctime(&t); 18 | } /* if */ 19 | 20 | strncpy(modstr, str, strsize); 21 | modstr[strsize-1] = '\0'; 22 | strsize = strlen(modstr); 23 | while ((modstr[strsize-1] == '\n') || (modstr[strsize-1] == '\r')) 24 | modstr[--strsize] = '\0'; 25 | } /* modTimeToStr */ 26 | 27 | 28 | static void fail(const char *what, const char *why) 29 | { 30 | if (why == NULL) 31 | why = PHYSFS_getErrorByCode(PHYSFS_getLastErrorCode()); 32 | fprintf(stderr, "%s failed: %s\n", what, why); 33 | failure = 1; 34 | } /* fail */ 35 | 36 | 37 | static void dumpFile(const char *fname) 38 | { 39 | const int origfailure = failure; 40 | PHYSFS_File *out = NULL; 41 | PHYSFS_File *in = NULL; 42 | 43 | failure = 0; 44 | 45 | if ((in = PHYSFS_openRead(fname)) == NULL) 46 | fail("\nPHYSFS_openRead", NULL); 47 | else if ((out = PHYSFS_openWrite(fname)) == NULL) 48 | fail("\nPHYSFS_openWrite", NULL); 49 | else 50 | { 51 | char modstr[64]; 52 | PHYSFS_sint64 size = PHYSFS_fileLength(in); 53 | 54 | printf("("); 55 | if (size == -1) 56 | printf("?"); 57 | else 58 | printf("%lld", (long long) size); 59 | printf(" bytes"); 60 | 61 | modTimeToStr(PHYSFS_getLastModTime(fname), modstr, sizeof (modstr)); 62 | printf(", %s)\n", modstr); 63 | 64 | while ( (!failure) && (!PHYSFS_eof(in)) ) 65 | { 66 | static char buf[64 * 1024]; 67 | PHYSFS_sint64 br = PHYSFS_read(in, buf, 1, sizeof (buf)); 68 | if (br == -1) 69 | fail("PHYSFS_read", NULL); 70 | else 71 | { 72 | PHYSFS_sint64 bw = PHYSFS_write(out, buf, 1, br); 73 | if (bw != br) 74 | fail("PHYSFS_write", NULL); 75 | else 76 | size -= bw; 77 | } /* else */ 78 | } /* while */ 79 | 80 | if ((!failure) && (size != 0)) 81 | fail("PHYSFS_eof", "BUG! eof != PHYSFS_fileLength bytes!"); 82 | } /* else */ 83 | 84 | if (in != NULL) 85 | PHYSFS_close(in); 86 | 87 | if (out != NULL) 88 | { 89 | if (!PHYSFS_close(out)) 90 | fail("PHYSFS_close", NULL); 91 | } /* if */ 92 | 93 | if (failure) 94 | PHYSFS_delete(fname); 95 | else 96 | failure = origfailure; 97 | } /* dumpFile */ 98 | 99 | 100 | static void unpackCallback(void *_depth, const char *origdir, const char *str) 101 | { 102 | int depth = *((int *) _depth); 103 | const int len = strlen(origdir) + strlen(str) + 2; 104 | char *fname = (char *) malloc(len); 105 | if (fname == NULL) 106 | fail("malloc", "Out of memory!"); 107 | else 108 | { 109 | if (strcmp(origdir, "/") == 0) 110 | origdir = ""; 111 | 112 | snprintf(fname, len, "%s/%s", origdir, str); 113 | 114 | printf("%s ", fname); 115 | if (PHYSFS_isDirectory(fname)) 116 | { 117 | depth++; 118 | printf("(directory)\n"); 119 | if (!PHYSFS_mkdir(fname)) 120 | fail("PHYSFS_mkdir", NULL); 121 | else 122 | PHYSFS_enumerateFilesCallback(fname, unpackCallback, &depth); 123 | } /* if */ 124 | 125 | else if (PHYSFS_isSymbolicLink(fname)) 126 | { 127 | printf("(symlink)\n"); 128 | /* !!! FIXME: ? if (!symlink(fname, */ 129 | } /* else if */ 130 | 131 | else /* ...file. */ 132 | { 133 | dumpFile(fname); 134 | } /* else */ 135 | 136 | free(fname); 137 | } /* else */ 138 | } /* unpackCallback */ 139 | 140 | 141 | int main(int argc, char **argv) 142 | { 143 | int zero = 0; 144 | 145 | if (argc != 3) 146 | { 147 | fprintf(stderr, "USAGE: %s \n", argv[0]); 148 | return 1; 149 | } /* if */ 150 | 151 | if (!PHYSFS_init(argv[0])) 152 | { 153 | fprintf(stderr, "PHYSFS_init() failed: %s\n", PHYSFS_getErrorByCode(PHYSFS_getLastErrorCode())); 154 | return 2; 155 | } /* if */ 156 | 157 | if (!PHYSFS_setWriteDir(argv[2])) 158 | { 159 | fprintf(stderr, "PHYSFS_setWriteDir('%s') failed: %s\n", 160 | argv[2], PHYSFS_getErrorByCode(PHYSFS_getLastErrorCode())); 161 | return 3; 162 | } /* if */ 163 | 164 | if (!PHYSFS_mount(argv[1], NULL, 1)) 165 | { 166 | fprintf(stderr, "PHYSFS_mount('%s') failed: %s\n", 167 | argv[1], PHYSFS_getErrorByCode(PHYSFS_getLastErrorCode())); 168 | return 4; 169 | } /* if */ 170 | 171 | PHYSFS_permitSymbolicLinks(1); 172 | PHYSFS_enumerateFilesCallback("/", unpackCallback, &zero); 173 | PHYSFS_deinit(); 174 | if (failure) 175 | return 5; 176 | 177 | return 0; 178 | } /* main */ 179 | 180 | /* end of physfsunpack.c ... */ 181 | 182 | -------------------------------------------------------------------------------- /extras/selfextract.c: -------------------------------------------------------------------------------- 1 | /* 2 | * This code shows how to read a zipfile included in an app's binary. 3 | * 4 | * License: this code is public domain. I make no warranty that it is useful, 5 | * correct, harmless, or environmentally safe. 6 | * 7 | * This particular file may be used however you like, including copying it 8 | * verbatim into a closed-source project, exploiting it commercially, and 9 | * removing any trace of my name from the source (although I hope you won't 10 | * do that). I welcome enhancements and corrections to this file, but I do 11 | * not require you to send me patches if you make changes. This code has 12 | * NO WARRANTY. 13 | * 14 | * Unless otherwise stated, the rest of PhysicsFS falls under the zlib license. 15 | * Please see LICENSE.txt in the root of the source tree. 16 | * 17 | * This file was written by Ryan C. Gordon. (icculus@icculus.org). 18 | */ 19 | 20 | /* 21 | * Compile this program and then attach a .zip file to the end of the 22 | * compiled binary. 23 | * 24 | * On Linux, something like this will build the final binary: 25 | * gcc -o selfextract.tmp selfextract.c -lphysfs && \ 26 | * cat selfextract.tmp myzipfile.zip >> selfextract && \ 27 | * chmod a+x selfextract && \ 28 | * rm -f selfextract.tmp 29 | * 30 | * This may not work on all platforms, and it probably only works with 31 | * .zip files, since they are designed to be appended to another file. 32 | */ 33 | 34 | #include 35 | #include "physfs.h" 36 | 37 | int main(int argc, char **argv) 38 | { 39 | int rc = 0; 40 | 41 | if (!PHYSFS_init(argv[0])) 42 | { 43 | printf("PHYSFS_init() failed: %s\n", PHYSFS_getErrorByCode(PHYSFS_getLastErrorCode())); 44 | return 42; 45 | } /* if */ 46 | 47 | rc = PHYSFS_addToSearchPath(argv[0], 0); 48 | if (!rc) 49 | { 50 | printf("Couldn't find self-extract data: %s\n", PHYSFS_getErrorByCode(PHYSFS_getLastErrorCode())); 51 | printf("This might mean you didn't append a zipfile to the binary.\n"); 52 | return 42; 53 | } /* if */ 54 | 55 | char **files = PHYSFS_enumerateFiles("/"); 56 | char **i; 57 | for (i = files; *i != NULL; i++) 58 | { 59 | const char *dirorfile = PHYSFS_isDirectory(*i) ? "Directory" : "File"; 60 | printf(" * %s '%s' is in root of attached data.\n", dirorfile, *i); 61 | } /* for */ 62 | PHYSFS_freeList(files); 63 | 64 | return 0; 65 | } /* main */ 66 | 67 | -------------------------------------------------------------------------------- /extras/uninstall.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | if [ ! -f "./install_manifest.txt" ]; then 4 | echo "ERROR: This needs to be run from your CMake build directory after installing." 1>&2 5 | exit 1 6 | fi 7 | 8 | xargs rm -vf < install_manifest.txt 9 | exit 0 10 | 11 | -------------------------------------------------------------------------------- /src/physfs_archiver_7z.c: -------------------------------------------------------------------------------- 1 | /* 2 | * 7zip support routines for PhysicsFS. 3 | * 4 | * Please see the file LICENSE.txt in the source's root directory. 5 | * 6 | * This file was written by Ryan C. Gordon. 7 | */ 8 | 9 | #define __PHYSICSFS_INTERNAL__ 10 | #include "physfs_internal.h" 11 | 12 | #if PHYSFS_SUPPORTS_7Z 13 | 14 | #include "physfs_lzmasdk.h" 15 | 16 | typedef struct 17 | { 18 | ISeekInStream seekStream; /* lzma sdk i/o interface (lower level). */ 19 | PHYSFS_Io *io; /* physfs i/o interface for this archive. */ 20 | CLookToRead lookStream; /* lzma sdk i/o interface (higher level). */ 21 | } SZIPLookToRead; 22 | 23 | /* One SZIPentry is kept for each file in an open 7zip archive. */ 24 | typedef struct 25 | { 26 | __PHYSFS_DirTreeEntry tree; /* manages directory tree */ 27 | PHYSFS_uint32 dbidx; /* index into lzma sdk database */ 28 | } SZIPentry; 29 | 30 | /* One SZIPinfo is kept for each open 7zip archive. */ 31 | typedef struct 32 | { 33 | __PHYSFS_DirTree tree; /* manages directory tree. */ 34 | PHYSFS_Io *io; /* physfs i/o interface for this archive. */ 35 | CSzArEx db; /* lzma sdk archive database object. */ 36 | } SZIPinfo; 37 | 38 | 39 | static PHYSFS_ErrorCode szipErrorCode(const SRes rc) 40 | { 41 | switch (rc) 42 | { 43 | case SZ_OK: return PHYSFS_ERR_OK; 44 | case SZ_ERROR_DATA: return PHYSFS_ERR_CORRUPT; 45 | case SZ_ERROR_MEM: return PHYSFS_ERR_OUT_OF_MEMORY; 46 | case SZ_ERROR_CRC: return PHYSFS_ERR_CORRUPT; 47 | case SZ_ERROR_UNSUPPORTED: return PHYSFS_ERR_UNSUPPORTED; 48 | case SZ_ERROR_INPUT_EOF: return PHYSFS_ERR_CORRUPT; 49 | case SZ_ERROR_OUTPUT_EOF: return PHYSFS_ERR_IO; 50 | case SZ_ERROR_READ: return PHYSFS_ERR_IO; 51 | case SZ_ERROR_WRITE: return PHYSFS_ERR_IO; 52 | case SZ_ERROR_ARCHIVE: return PHYSFS_ERR_CORRUPT; 53 | case SZ_ERROR_NO_ARCHIVE: return PHYSFS_ERR_UNSUPPORTED; 54 | default: break; 55 | } /* switch */ 56 | 57 | return PHYSFS_ERR_OTHER_ERROR; 58 | } /* szipErrorCode */ 59 | 60 | 61 | /* LZMA SDK's ISzAlloc interface ... */ 62 | 63 | static void *SZIP_ISzAlloc_Alloc(void *p, size_t size) 64 | { 65 | return allocator.Malloc(size ? size : 1); 66 | } /* SZIP_ISzAlloc_Alloc */ 67 | 68 | static void SZIP_ISzAlloc_Free(void *p, void *address) 69 | { 70 | if (address) 71 | allocator.Free(address); 72 | } /* SZIP_ISzAlloc_Free */ 73 | 74 | static ISzAlloc SZIP_SzAlloc = { 75 | SZIP_ISzAlloc_Alloc, SZIP_ISzAlloc_Free 76 | }; 77 | 78 | 79 | /* we implement ISeekInStream, and then wrap that in LZMA SDK's CLookToRead, 80 | which implements the higher-level ILookInStream on top of that, handling 81 | buffering and such for us. */ 82 | 83 | /* LZMA SDK's ISeekInStream interface ... */ 84 | 85 | static SRes SZIP_ISeekInStream_Read(void *p, void *buf, size_t *size) 86 | { 87 | SZIPLookToRead *stream = (SZIPLookToRead *) p; 88 | PHYSFS_Io *io = stream->io; 89 | const PHYSFS_uint64 len = (PHYSFS_uint64) *size; 90 | const PHYSFS_sint64 rc = (len == 0) ? 0 : io->read(io, buf, len); 91 | 92 | if (rc < 0) 93 | { 94 | *size = 0; 95 | return SZ_ERROR_READ; 96 | } /* if */ 97 | 98 | *size = (size_t) rc; 99 | return SZ_OK; 100 | } /* SZIP_ISeekInStream_Read */ 101 | 102 | static SRes SZIP_ISeekInStream_Seek(void *p, Int64 *pos, ESzSeek origin) 103 | { 104 | SZIPLookToRead *stream = (SZIPLookToRead *) p; 105 | PHYSFS_Io *io = stream->io; 106 | PHYSFS_sint64 base; 107 | PHYSFS_uint64 newpos; 108 | 109 | switch (origin) 110 | { 111 | case SZ_SEEK_SET: 112 | base = 0; 113 | break; 114 | 115 | case SZ_SEEK_CUR: 116 | base = io->tell(io); 117 | break; 118 | 119 | case SZ_SEEK_END: 120 | base = io->length(io); 121 | break; 122 | 123 | default: 124 | return SZ_ERROR_FAIL; 125 | } /* switch */ 126 | 127 | if (base < 0) 128 | return SZ_ERROR_FAIL; 129 | else if ((*pos < 0) && (((Int64) base) < -*pos)) 130 | return SZ_ERROR_FAIL; 131 | 132 | newpos = (PHYSFS_uint64) (((Int64) base) + *pos); 133 | if (!io->seek(io, newpos)) 134 | return SZ_ERROR_FAIL; 135 | 136 | *pos = (Int64) newpos; 137 | return SZ_OK; 138 | } /* SZIP_ISeekInStream_Seek */ 139 | 140 | 141 | static void szipInitStream(SZIPLookToRead *stream, PHYSFS_Io *io) 142 | { 143 | stream->seekStream.Read = SZIP_ISeekInStream_Read; 144 | stream->seekStream.Seek = SZIP_ISeekInStream_Seek; 145 | 146 | stream->io = io; 147 | 148 | /* !!! FIXME: can we use lookahead? Is there value to it? */ 149 | LookToRead_Init(&stream->lookStream); 150 | LookToRead_CreateVTable(&stream->lookStream, False); 151 | stream->lookStream.realStream = &stream->seekStream; 152 | } /* szipInitStream */ 153 | 154 | 155 | /* Do this in a separate function so we can smallAlloc without looping. */ 156 | static int szipLoadEntry(SZIPinfo *info, const PHYSFS_uint32 idx) 157 | { 158 | const size_t utf16len = SzArEx_GetFileNameUtf16(&info->db, idx, NULL); 159 | const size_t utf16buflen = utf16len * 2; 160 | PHYSFS_uint16 *utf16 = (PHYSFS_uint16 *) __PHYSFS_smallAlloc(utf16buflen); 161 | const size_t utf8buflen = utf16len * 4; 162 | char *utf8 = (char *) __PHYSFS_smallAlloc(utf8buflen); 163 | int retval = 0; 164 | 165 | if (utf16 && utf8) 166 | { 167 | const int isdir = SzArEx_IsDir(&info->db, idx) != 0; 168 | SZIPentry *entry; 169 | SzArEx_GetFileNameUtf16(&info->db, idx, (UInt16 *) utf16); 170 | PHYSFS_utf8FromUtf16(utf16, utf8, utf8buflen); 171 | entry = (SZIPentry*) __PHYSFS_DirTreeAdd(&info->tree, utf8, isdir); 172 | retval = (entry != NULL); 173 | if (retval) 174 | entry->dbidx = idx; 175 | } /* if */ 176 | 177 | __PHYSFS_smallFree(utf8); 178 | __PHYSFS_smallFree(utf16); 179 | 180 | return retval; 181 | } /* szipLoadEntry */ 182 | 183 | 184 | static int szipLoadEntries(SZIPinfo *info) 185 | { 186 | int retval = 0; 187 | 188 | if (__PHYSFS_DirTreeInit(&info->tree, sizeof (SZIPentry))) 189 | { 190 | const PHYSFS_uint32 count = info->db.NumFiles; 191 | PHYSFS_uint32 i; 192 | for (i = 0; i < count; i++) 193 | BAIL_IF_ERRPASS(!szipLoadEntry(info, i), 0); 194 | retval = 1; 195 | } /* if */ 196 | 197 | return retval; 198 | } /* szipLoadEntries */ 199 | 200 | 201 | static void SZIP_closeArchive(void *opaque) 202 | { 203 | SZIPinfo *info = (SZIPinfo *) opaque; 204 | if (info) 205 | { 206 | if (info->io) 207 | info->io->destroy(info->io); 208 | SzArEx_Free(&info->db, &SZIP_SzAlloc); 209 | __PHYSFS_DirTreeDeinit(&info->tree); 210 | allocator.Free(info); 211 | } /* if */ 212 | } /* SZIP_closeArchive */ 213 | 214 | 215 | static void *SZIP_openArchive(PHYSFS_Io *io, const char *name, 216 | int forWriting, int *claimed) 217 | { 218 | static const PHYSFS_uint8 wantedsig[] = { '7','z',0xBC,0xAF,0x27,0x1C }; 219 | SZIPLookToRead stream; 220 | ISzAlloc *alloc = &SZIP_SzAlloc; 221 | SZIPinfo *info = NULL; 222 | SRes rc; 223 | PHYSFS_uint8 sig[6]; 224 | PHYSFS_sint64 pos; 225 | 226 | BAIL_IF(forWriting, PHYSFS_ERR_READ_ONLY, NULL); 227 | pos = io->tell(io); 228 | BAIL_IF_ERRPASS(pos == -1, NULL); 229 | BAIL_IF_ERRPASS(io->read(io, sig, 6) != 6, NULL); 230 | *claimed = (memcmp(sig, wantedsig, 6) == 0); 231 | BAIL_IF_ERRPASS(!io->seek(io, pos), NULL); 232 | 233 | info = (SZIPinfo *) allocator.Malloc(sizeof (SZIPinfo)); 234 | BAIL_IF(!info, PHYSFS_ERR_OUT_OF_MEMORY, NULL); 235 | memset(info, '\0', sizeof (*info)); 236 | 237 | SzArEx_Init(&info->db); 238 | 239 | info->io = io; 240 | 241 | szipInitStream(&stream, io); 242 | rc = SzArEx_Open(&info->db, &stream.lookStream.s, alloc, alloc); 243 | GOTO_IF(rc != SZ_OK, szipErrorCode(rc), failed); 244 | 245 | GOTO_IF_ERRPASS(!szipLoadEntries(info), failed); 246 | 247 | return info; 248 | 249 | failed: 250 | info->io = NULL; /* don't let cleanup destroy the PHYSFS_Io. */ 251 | SZIP_closeArchive(info); 252 | return NULL; 253 | } /* SZIP_openArchive */ 254 | 255 | 256 | static PHYSFS_Io *SZIP_openRead(void *opaque, const char *path) 257 | { 258 | /* !!! FIXME: the current lzma sdk C API only allows you to decompress 259 | !!! FIXME: the entire file at once, which isn't ideal. Fix this in the 260 | !!! FIXME: SDK and then convert this all to a streaming interface. */ 261 | 262 | SZIPinfo *info = (SZIPinfo *) opaque; 263 | SZIPentry *entry = (SZIPentry *) __PHYSFS_DirTreeFind(&info->tree, path); 264 | ISzAlloc *alloc = &SZIP_SzAlloc; 265 | SZIPLookToRead stream; 266 | PHYSFS_Io *retval = NULL; 267 | PHYSFS_Io *io = NULL; 268 | UInt32 blockIndex = 0xFFFFFFFF; 269 | Byte *outBuffer = NULL; 270 | size_t outBufferSize = 0; 271 | size_t offset = 0; 272 | size_t outSizeProcessed = 0; 273 | void *buf = NULL; 274 | SRes rc; 275 | 276 | BAIL_IF_ERRPASS(!entry, NULL); 277 | BAIL_IF(entry->tree.isdir, PHYSFS_ERR_NOT_A_FILE, NULL); 278 | 279 | io = info->io->duplicate(info->io); 280 | GOTO_IF_ERRPASS(!io, SZIP_openRead_failed); 281 | 282 | szipInitStream(&stream, io); 283 | 284 | rc = SzArEx_Extract(&info->db, &stream.lookStream.s, entry->dbidx, 285 | &blockIndex, &outBuffer, &outBufferSize, &offset, 286 | &outSizeProcessed, alloc, alloc); 287 | GOTO_IF(rc != SZ_OK, szipErrorCode(rc), SZIP_openRead_failed); 288 | GOTO_IF(outBuffer == NULL, PHYSFS_ERR_OUT_OF_MEMORY, SZIP_openRead_failed); 289 | 290 | io->destroy(io); 291 | io = NULL; 292 | 293 | buf = allocator.Malloc(outSizeProcessed ? outSizeProcessed : 1); 294 | GOTO_IF(buf == NULL, PHYSFS_ERR_OUT_OF_MEMORY, SZIP_openRead_failed); 295 | 296 | if (outSizeProcessed > 0) 297 | memcpy(buf, outBuffer + offset, outSizeProcessed); 298 | 299 | alloc->Free(alloc, outBuffer); 300 | outBuffer = NULL; 301 | 302 | retval = __PHYSFS_createMemoryIo(buf, outSizeProcessed, allocator.Free); 303 | GOTO_IF_ERRPASS(!retval, SZIP_openRead_failed); 304 | 305 | return retval; 306 | 307 | SZIP_openRead_failed: 308 | if (io != NULL) 309 | io->destroy(io); 310 | 311 | if (buf) 312 | allocator.Free(buf); 313 | 314 | if (outBuffer) 315 | alloc->Free(alloc, outBuffer); 316 | 317 | return NULL; 318 | } /* SZIP_openRead */ 319 | 320 | 321 | static PHYSFS_Io *SZIP_openWrite(void *opaque, const char *filename) 322 | { 323 | BAIL(PHYSFS_ERR_READ_ONLY, NULL); 324 | } /* SZIP_openWrite */ 325 | 326 | 327 | static PHYSFS_Io *SZIP_openAppend(void *opaque, const char *filename) 328 | { 329 | BAIL(PHYSFS_ERR_READ_ONLY, NULL); 330 | } /* SZIP_openAppend */ 331 | 332 | 333 | static int SZIP_remove(void *opaque, const char *name) 334 | { 335 | BAIL(PHYSFS_ERR_READ_ONLY, 0); 336 | } /* SZIP_remove */ 337 | 338 | 339 | static int SZIP_mkdir(void *opaque, const char *name) 340 | { 341 | BAIL(PHYSFS_ERR_READ_ONLY, 0); 342 | } /* SZIP_mkdir */ 343 | 344 | 345 | static inline PHYSFS_uint64 lzmasdkTimeToPhysfsTime(const CNtfsFileTime *t) 346 | { 347 | const PHYSFS_uint64 winEpochToUnixEpoch = __PHYSFS_UI64(0x019DB1DED53E8000); 348 | const PHYSFS_uint64 nanosecToMillisec = __PHYSFS_UI64(10000000); 349 | const PHYSFS_uint64 quad = (((PHYSFS_uint64) t->High) << 32) | t->Low; 350 | return (quad - winEpochToUnixEpoch) / nanosecToMillisec; 351 | } /* lzmasdkTimeToPhysfsTime */ 352 | 353 | 354 | static int SZIP_stat(void *opaque, const char *path, PHYSFS_Stat *stat) 355 | { 356 | SZIPinfo *info = (SZIPinfo *) opaque; 357 | SZIPentry *entry; 358 | PHYSFS_uint32 idx; 359 | 360 | entry = (SZIPentry *) __PHYSFS_DirTreeFind(&info->tree, path); 361 | BAIL_IF_ERRPASS(!entry, 0); 362 | idx = entry->dbidx; 363 | 364 | if (entry->tree.isdir) 365 | { 366 | stat->filesize = -1; 367 | stat->filetype = PHYSFS_FILETYPE_DIRECTORY; 368 | } /* if */ 369 | else 370 | { 371 | stat->filesize = (PHYSFS_sint64) SzArEx_GetFileSize(&info->db, idx); 372 | stat->filetype = PHYSFS_FILETYPE_REGULAR; 373 | } /* else */ 374 | 375 | if (info->db.MTime.Vals != NULL) 376 | stat->modtime = lzmasdkTimeToPhysfsTime(&info->db.MTime.Vals[idx]); 377 | else if (info->db.CTime.Vals != NULL) 378 | stat->modtime = lzmasdkTimeToPhysfsTime(&info->db.CTime.Vals[idx]); 379 | else 380 | stat->modtime = -1; 381 | 382 | if (info->db.CTime.Vals != NULL) 383 | stat->createtime = lzmasdkTimeToPhysfsTime(&info->db.CTime.Vals[idx]); 384 | else if (info->db.MTime.Vals != NULL) 385 | stat->createtime = lzmasdkTimeToPhysfsTime(&info->db.MTime.Vals[idx]); 386 | else 387 | stat->createtime = -1; 388 | 389 | stat->accesstime = -1; 390 | stat->readonly = 1; 391 | 392 | return 1; 393 | } /* SZIP_stat */ 394 | 395 | 396 | void SZIP_global_init(void) 397 | { 398 | /* this just needs to calculate some things, so it only ever 399 | has to run once, even after a deinit. */ 400 | static int generatedTable = 0; 401 | if (!generatedTable) 402 | { 403 | generatedTable = 1; 404 | CrcGenerateTable(); 405 | } /* if */ 406 | } /* SZIP_global_init */ 407 | 408 | 409 | const PHYSFS_Archiver __PHYSFS_Archiver_7Z = 410 | { 411 | CURRENT_PHYSFS_ARCHIVER_API_VERSION, 412 | { 413 | "7Z", 414 | "7zip archives", 415 | "Ryan C. Gordon ", 416 | "https://icculus.org/physfs/", 417 | 0, /* supportsSymlinks */ 418 | }, 419 | SZIP_openArchive, 420 | __PHYSFS_DirTreeEnumerate, 421 | SZIP_openRead, 422 | SZIP_openWrite, 423 | SZIP_openAppend, 424 | SZIP_remove, 425 | SZIP_mkdir, 426 | SZIP_stat, 427 | SZIP_closeArchive 428 | }; 429 | 430 | #endif /* defined PHYSFS_SUPPORTS_7Z */ 431 | 432 | /* end of physfs_archiver_7z.c ... */ 433 | 434 | -------------------------------------------------------------------------------- /src/physfs_archiver_dir.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Standard directory I/O support routines for PhysicsFS. 3 | * 4 | * Please see the file LICENSE.txt in the source's root directory. 5 | * 6 | * This file written by Ryan C. Gordon. 7 | */ 8 | 9 | #define __PHYSICSFS_INTERNAL__ 10 | #include "physfs_internal.h" 11 | 12 | /* There's no PHYSFS_Io interface here. Use __PHYSFS_createNativeIo(). */ 13 | 14 | 15 | 16 | static char *cvtToDependent(const char *prepend, const char *path, 17 | char *buf, const size_t buflen) 18 | { 19 | BAIL_IF(buf == NULL, PHYSFS_ERR_OUT_OF_MEMORY, NULL); 20 | snprintf(buf, buflen, "%s%s", prepend ? prepend : "", path); 21 | 22 | #if !__PHYSFS_STANDARD_DIRSEP 23 | assert(__PHYSFS_platformDirSeparator != '/'); 24 | { 25 | char *p; 26 | for (p = strchr(buf, '/'); p != NULL; p = strchr(p + 1, '/')) 27 | *p = __PHYSFS_platformDirSeparator; 28 | } /* if */ 29 | #endif 30 | 31 | return buf; 32 | } /* cvtToDependent */ 33 | 34 | 35 | #define CVT_TO_DEPENDENT(buf, pre, dir) { \ 36 | const size_t len = ((pre) ? strlen((char *) pre) : 0) + strlen(dir) + 1; \ 37 | buf = cvtToDependent((char*)pre,dir,(char*)__PHYSFS_smallAlloc(len),len); \ 38 | } 39 | 40 | 41 | 42 | static void *DIR_openArchive(PHYSFS_Io *io, const char *name, 43 | int forWriting, int *claimed) 44 | { 45 | PHYSFS_Stat st; 46 | const char dirsep = __PHYSFS_platformDirSeparator; 47 | char *retval = NULL; 48 | const size_t namelen = strlen(name); 49 | const size_t seplen = 1; 50 | 51 | assert(io == NULL); /* shouldn't create an Io for these. */ 52 | BAIL_IF_ERRPASS(!__PHYSFS_platformStat(name, &st, 1), NULL); 53 | 54 | if (st.filetype != PHYSFS_FILETYPE_DIRECTORY) 55 | BAIL(PHYSFS_ERR_UNSUPPORTED, NULL); 56 | 57 | *claimed = 1; 58 | retval = allocator.Malloc(namelen + seplen + 1); 59 | BAIL_IF(retval == NULL, PHYSFS_ERR_OUT_OF_MEMORY, NULL); 60 | 61 | strcpy(retval, name); 62 | 63 | /* make sure there's a dir separator at the end of the string */ 64 | if (retval[namelen - 1] != dirsep) 65 | { 66 | retval[namelen] = dirsep; 67 | retval[namelen + 1] = '\0'; 68 | } /* if */ 69 | 70 | return retval; 71 | } /* DIR_openArchive */ 72 | 73 | 74 | static PHYSFS_EnumerateCallbackResult DIR_enumerate(void *opaque, 75 | const char *dname, PHYSFS_EnumerateCallback cb, 76 | const char *origdir, void *callbackdata) 77 | { 78 | char *d; 79 | PHYSFS_EnumerateCallbackResult retval; 80 | CVT_TO_DEPENDENT(d, opaque, dname); 81 | BAIL_IF_ERRPASS(!d, PHYSFS_ENUM_ERROR); 82 | retval = __PHYSFS_platformEnumerate(d, cb, origdir, callbackdata); 83 | __PHYSFS_smallFree(d); 84 | return retval; 85 | } /* DIR_enumerate */ 86 | 87 | 88 | static PHYSFS_Io *doOpen(void *opaque, const char *name, const int mode) 89 | { 90 | PHYSFS_Io *io = NULL; 91 | char *f = NULL; 92 | 93 | CVT_TO_DEPENDENT(f, opaque, name); 94 | BAIL_IF_ERRPASS(!f, NULL); 95 | 96 | io = __PHYSFS_createNativeIo(f, mode); 97 | if (io == NULL) 98 | { 99 | const PHYSFS_ErrorCode err = PHYSFS_getLastErrorCode(); 100 | PHYSFS_Stat statbuf; 101 | __PHYSFS_platformStat(f, &statbuf, 0); /* !!! FIXME: why are we stating here? */ 102 | PHYSFS_setErrorCode(err); 103 | } /* if */ 104 | 105 | __PHYSFS_smallFree(f); 106 | 107 | return io; 108 | } /* doOpen */ 109 | 110 | 111 | static PHYSFS_Io *DIR_openRead(void *opaque, const char *filename) 112 | { 113 | return doOpen(opaque, filename, 'r'); 114 | } /* DIR_openRead */ 115 | 116 | 117 | static PHYSFS_Io *DIR_openWrite(void *opaque, const char *filename) 118 | { 119 | return doOpen(opaque, filename, 'w'); 120 | } /* DIR_openWrite */ 121 | 122 | 123 | static PHYSFS_Io *DIR_openAppend(void *opaque, const char *filename) 124 | { 125 | return doOpen(opaque, filename, 'a'); 126 | } /* DIR_openAppend */ 127 | 128 | 129 | static int DIR_remove(void *opaque, const char *name) 130 | { 131 | int retval; 132 | char *f; 133 | 134 | CVT_TO_DEPENDENT(f, opaque, name); 135 | BAIL_IF_ERRPASS(!f, 0); 136 | retval = __PHYSFS_platformDelete(f); 137 | __PHYSFS_smallFree(f); 138 | return retval; 139 | } /* DIR_remove */ 140 | 141 | 142 | static int DIR_mkdir(void *opaque, const char *name) 143 | { 144 | int retval; 145 | char *f; 146 | 147 | CVT_TO_DEPENDENT(f, opaque, name); 148 | BAIL_IF_ERRPASS(!f, 0); 149 | retval = __PHYSFS_platformMkDir(f); 150 | __PHYSFS_smallFree(f); 151 | return retval; 152 | } /* DIR_mkdir */ 153 | 154 | 155 | static void DIR_closeArchive(void *opaque) 156 | { 157 | allocator.Free(opaque); 158 | } /* DIR_closeArchive */ 159 | 160 | 161 | static int DIR_stat(void *opaque, const char *name, PHYSFS_Stat *stat) 162 | { 163 | int retval = 0; 164 | char *d; 165 | 166 | CVT_TO_DEPENDENT(d, opaque, name); 167 | BAIL_IF_ERRPASS(!d, 0); 168 | retval = __PHYSFS_platformStat(d, stat, 0); 169 | __PHYSFS_smallFree(d); 170 | return retval; 171 | } /* DIR_stat */ 172 | 173 | 174 | const PHYSFS_Archiver __PHYSFS_Archiver_DIR = 175 | { 176 | CURRENT_PHYSFS_ARCHIVER_API_VERSION, 177 | { 178 | "", 179 | "Non-archive, direct filesystem I/O", 180 | "Ryan C. Gordon ", 181 | "https://icculus.org/physfs/", 182 | 1, /* supportsSymlinks */ 183 | }, 184 | DIR_openArchive, 185 | DIR_enumerate, 186 | DIR_openRead, 187 | DIR_openWrite, 188 | DIR_openAppend, 189 | DIR_remove, 190 | DIR_mkdir, 191 | DIR_stat, 192 | DIR_closeArchive 193 | }; 194 | 195 | /* end of physfs_archiver_dir.c ... */ 196 | 197 | -------------------------------------------------------------------------------- /src/physfs_archiver_grp.c: -------------------------------------------------------------------------------- 1 | /* 2 | * GRP support routines for PhysicsFS. 3 | * 4 | * This driver handles BUILD engine archives ("groupfiles"). This format 5 | * (but not this driver) was put together by Ken Silverman. 6 | * 7 | * The format is simple enough. In Ken's words: 8 | * 9 | * What's the .GRP file format? 10 | * 11 | * The ".grp" file format is just a collection of a lot of files stored 12 | * into 1 big one. I tried to make the format as simple as possible: The 13 | * first 12 bytes contains my name, "KenSilverman". The next 4 bytes is 14 | * the number of files that were compacted into the group file. Then for 15 | * each file, there is a 16 byte structure, where the first 12 bytes are 16 | * the filename, and the last 4 bytes are the file's size. The rest of 17 | * the group file is just the raw data packed one after the other in the 18 | * same order as the list of files. 19 | * 20 | * (That info is from http://www.advsys.net/ken/build.htm ...) 21 | * 22 | * Please see the file LICENSE.txt in the source's root directory. 23 | * 24 | * This file written by Ryan C. Gordon. 25 | */ 26 | 27 | #define __PHYSICSFS_INTERNAL__ 28 | #include "physfs_internal.h" 29 | 30 | #if PHYSFS_SUPPORTS_GRP 31 | 32 | static int grpLoadEntries(PHYSFS_Io *io, const PHYSFS_uint32 count, void *arc) 33 | { 34 | PHYSFS_uint32 pos = 16 + (16 * count); /* past sig+metadata. */ 35 | PHYSFS_uint32 i; 36 | 37 | for (i = 0; i < count; i++) 38 | { 39 | char *ptr; 40 | char name[13]; 41 | PHYSFS_uint32 size; 42 | BAIL_IF_ERRPASS(!__PHYSFS_readAll(io, name, 12), 0); 43 | BAIL_IF_ERRPASS(!__PHYSFS_readAll(io, &size, 4), 0); 44 | 45 | name[12] = '\0'; /* name isn't null-terminated in file. */ 46 | if ((ptr = strchr(name, ' ')) != NULL) 47 | *ptr = '\0'; /* trim extra spaces. */ 48 | 49 | size = PHYSFS_swapULE32(size); 50 | BAIL_IF_ERRPASS(!UNPK_addEntry(arc, name, 0, -1, -1, pos, size), 0); 51 | 52 | pos += size; 53 | } /* for */ 54 | 55 | return 1; 56 | } /* grpLoadEntries */ 57 | 58 | 59 | static void *GRP_openArchive(PHYSFS_Io *io, const char *name, 60 | int forWriting, int *claimed) 61 | { 62 | PHYSFS_uint8 buf[12]; 63 | PHYSFS_uint32 count = 0; 64 | void *unpkarc = NULL; 65 | 66 | assert(io != NULL); /* shouldn't ever happen. */ 67 | 68 | BAIL_IF(forWriting, PHYSFS_ERR_READ_ONLY, NULL); 69 | 70 | BAIL_IF_ERRPASS(!__PHYSFS_readAll(io, buf, sizeof (buf)), NULL); 71 | if (memcmp(buf, "KenSilverman", sizeof (buf)) != 0) 72 | BAIL(PHYSFS_ERR_UNSUPPORTED, NULL); 73 | 74 | *claimed = 1; 75 | 76 | BAIL_IF_ERRPASS(!__PHYSFS_readAll(io, &count, sizeof(count)), NULL); 77 | count = PHYSFS_swapULE32(count); 78 | 79 | unpkarc = UNPK_openArchive(io); 80 | BAIL_IF_ERRPASS(!unpkarc, NULL); 81 | 82 | if (!grpLoadEntries(io, count, unpkarc)) 83 | { 84 | UNPK_abandonArchive(unpkarc); 85 | return NULL; 86 | } /* if */ 87 | 88 | return unpkarc; 89 | } /* GRP_openArchive */ 90 | 91 | 92 | const PHYSFS_Archiver __PHYSFS_Archiver_GRP = 93 | { 94 | CURRENT_PHYSFS_ARCHIVER_API_VERSION, 95 | { 96 | "GRP", 97 | "Build engine Groupfile format", 98 | "Ryan C. Gordon ", 99 | "https://icculus.org/physfs/", 100 | 0, /* supportsSymlinks */ 101 | }, 102 | GRP_openArchive, 103 | UNPK_enumerate, 104 | UNPK_openRead, 105 | UNPK_openWrite, 106 | UNPK_openAppend, 107 | UNPK_remove, 108 | UNPK_mkdir, 109 | UNPK_stat, 110 | UNPK_closeArchive 111 | }; 112 | 113 | #endif /* defined PHYSFS_SUPPORTS_GRP */ 114 | 115 | /* end of physfs_archiver_grp.c ... */ 116 | 117 | -------------------------------------------------------------------------------- /src/physfs_archiver_hog.c: -------------------------------------------------------------------------------- 1 | /* 2 | * HOG support routines for PhysicsFS. 3 | * 4 | * This driver handles Descent I/II/III HOG archives. 5 | * 6 | * The Descent I/II format is very simple: 7 | * 8 | * The file always starts with the 3-byte signature "DHF" (Descent 9 | * HOG file). After that the files of a HOG are just attached after 10 | * another, divided by a 17 bytes header, which specifies the name 11 | * and length (in bytes) of the forthcoming file! So you just read 12 | * the header with its information of how big the following file is, 13 | * and then skip exact that number of bytes to get to the next file 14 | * in that HOG. 15 | * 16 | * char sig[3] = {'D', 'H', 'F'}; // "DHF"=Descent HOG File 17 | * 18 | * struct { 19 | * char file_name[13]; // Filename, padded to 13 bytes with 0s 20 | * int file_size; // filesize in bytes 21 | * char data[file_size]; // The file data 22 | * } FILE_STRUCT; // Repeated until the end of the file. 23 | * 24 | * (That info is from http://www.descent2.com/ddn/specs/hog/) 25 | * 26 | * Descent 3 moved to HOG2 format, which starts with the chars "HOG2", 27 | * then 32-bits for the number of contained files, 32 bits for the offset 28 | * to the first file's data, then 56 bytes of 0xFF (reserved?). Then for 29 | * each file, there's 36 bytes for filename (null-terminated, rest of bytes 30 | * are garbage), 32-bits unknown/reserved (always zero?), 32-bits of length 31 | * of file data, 32-bits of time since Unix epoch. Then immediately following, 32 | * for each file is their uncompressed content, you can find its offset 33 | * by starting at the initial data offset and adding the filesize of each 34 | * prior file. 35 | * 36 | * This information was found at: 37 | * https://web.archive.org/web/20020213004051/http://descent-3.com/ddn/specs/hog/ 38 | * 39 | * 40 | * Please see the file LICENSE.txt in the source's root directory. 41 | * 42 | * This file written by Bradley Bell and Ryan C. Gordon. 43 | */ 44 | 45 | #define __PHYSICSFS_INTERNAL__ 46 | #include "physfs_internal.h" 47 | 48 | #if PHYSFS_SUPPORTS_HOG 49 | 50 | static int readui32(PHYSFS_Io *io, PHYSFS_uint32 *val) 51 | { 52 | PHYSFS_uint32 v; 53 | BAIL_IF_ERRPASS(!__PHYSFS_readAll(io, &v, sizeof (v)), 0); 54 | *val = PHYSFS_swapULE32(v); 55 | return 1; 56 | } /* readui32 */ 57 | 58 | static int hog1LoadEntries(PHYSFS_Io *io, void *arc) 59 | { 60 | const PHYSFS_uint64 iolen = io->length(io); 61 | PHYSFS_uint32 pos = 3; 62 | 63 | while (pos < iolen) 64 | { 65 | PHYSFS_uint32 size; 66 | char name[13]; 67 | 68 | BAIL_IF_ERRPASS(!__PHYSFS_readAll(io, name, 13), 0); 69 | BAIL_IF_ERRPASS(!readui32(io, &size), 0); 70 | name[12] = '\0'; /* just in case. */ 71 | pos += 13 + 4; 72 | 73 | BAIL_IF_ERRPASS(!UNPK_addEntry(arc, name, 0, -1, -1, pos, size), 0); 74 | pos += size; 75 | 76 | /* skip over entry */ 77 | BAIL_IF_ERRPASS(!io->seek(io, pos), 0); 78 | } /* while */ 79 | 80 | return 1; 81 | } /* hogLoadEntries */ 82 | 83 | static int hog2LoadEntries(PHYSFS_Io *io, void *arc) 84 | { 85 | PHYSFS_uint32 numfiles; 86 | PHYSFS_uint32 pos; 87 | PHYSFS_uint32 i; 88 | 89 | BAIL_IF_ERRPASS(!readui32(io, &numfiles), 0); 90 | BAIL_IF_ERRPASS(!readui32(io, &pos), 0); 91 | BAIL_IF_ERRPASS(!io->seek(io, 68), 0); /* skip to end of header. */ 92 | 93 | for (i = 0; i < numfiles; i++) { 94 | char name[37]; 95 | PHYSFS_uint32 reserved; 96 | PHYSFS_uint32 size; 97 | PHYSFS_uint32 mtime; 98 | BAIL_IF_ERRPASS(!__PHYSFS_readAll(io, name, 36), 0); 99 | BAIL_IF_ERRPASS(!readui32(io, &reserved), 0); 100 | BAIL_IF_ERRPASS(!readui32(io, &size), 0); 101 | BAIL_IF_ERRPASS(!readui32(io, &mtime), 0); 102 | name[36] = '\0'; /* just in case */ 103 | BAIL_IF_ERRPASS(!UNPK_addEntry(arc, name, 0, mtime, mtime, pos, size), 0); 104 | pos += size; 105 | } 106 | 107 | return 1; 108 | } /* hog2LoadEntries */ 109 | 110 | 111 | static void *HOG_openArchive(PHYSFS_Io *io, const char *name, 112 | int forWriting, int *claimed) 113 | { 114 | PHYSFS_uint8 buf[3]; 115 | void *unpkarc = NULL; 116 | int hog1 = 0; 117 | 118 | assert(io != NULL); /* shouldn't ever happen. */ 119 | BAIL_IF(forWriting, PHYSFS_ERR_READ_ONLY, NULL); 120 | BAIL_IF_ERRPASS(!__PHYSFS_readAll(io, buf, 3), NULL); 121 | 122 | if (memcmp(buf, "DHF", 3) == 0) 123 | hog1 = 1; /* original HOG (Descent 1 and 2) archive */ 124 | else 125 | { 126 | BAIL_IF(memcmp(buf, "HOG", 3) != 0, PHYSFS_ERR_UNSUPPORTED, NULL); /* Not HOG2 */ 127 | BAIL_IF_ERRPASS(!__PHYSFS_readAll(io, buf, 1), NULL); 128 | BAIL_IF(buf[0] != '2', PHYSFS_ERR_UNSUPPORTED, NULL); /* Not HOG2 */ 129 | } /* else */ 130 | 131 | *claimed = 1; 132 | 133 | unpkarc = UNPK_openArchive(io); 134 | BAIL_IF_ERRPASS(!unpkarc, NULL); 135 | 136 | if (!(hog1 ? hog1LoadEntries(io, unpkarc) : hog2LoadEntries(io, unpkarc))) 137 | { 138 | UNPK_abandonArchive(unpkarc); 139 | return NULL; 140 | } /* if */ 141 | 142 | return unpkarc; 143 | } /* HOG_openArchive */ 144 | 145 | 146 | const PHYSFS_Archiver __PHYSFS_Archiver_HOG = 147 | { 148 | CURRENT_PHYSFS_ARCHIVER_API_VERSION, 149 | { 150 | "HOG", 151 | "Descent I/II/III HOG file format", 152 | "Bradley Bell ", 153 | "https://icculus.org/physfs/", 154 | 0, /* supportsSymlinks */ 155 | }, 156 | HOG_openArchive, 157 | UNPK_enumerate, 158 | UNPK_openRead, 159 | UNPK_openWrite, 160 | UNPK_openAppend, 161 | UNPK_remove, 162 | UNPK_mkdir, 163 | UNPK_stat, 164 | UNPK_closeArchive 165 | }; 166 | 167 | #endif /* defined PHYSFS_SUPPORTS_HOG */ 168 | 169 | /* end of physfs_archiver_hog.c ... */ 170 | 171 | -------------------------------------------------------------------------------- /src/physfs_archiver_mvl.c: -------------------------------------------------------------------------------- 1 | /* 2 | * MVL support routines for PhysicsFS. 3 | * 4 | * This driver handles Descent II Movielib archives. 5 | * 6 | * The file format of MVL is quite easy... 7 | * 8 | * //MVL File format - Written by Heiko Herrmann 9 | * char sig[4] = {'D','M', 'V', 'L'}; // "DMVL"=Descent MoVie Library 10 | * 11 | * int num_files; // the number of files in this MVL 12 | * 13 | * struct { 14 | * char file_name[13]; // Filename, padded to 13 bytes with 0s 15 | * int file_size; // filesize in bytes 16 | * }DIR_STRUCT[num_files]; 17 | * 18 | * struct { 19 | * char data[file_size]; // The file data 20 | * }FILE_STRUCT[num_files]; 21 | * 22 | * (That info is from http://www.descent2.com/ddn/specs/mvl/) 23 | * 24 | * Please see the file LICENSE.txt in the source's root directory. 25 | * 26 | * This file written by Bradley Bell. 27 | * Based on grp.c by Ryan C. Gordon. 28 | */ 29 | 30 | #define __PHYSICSFS_INTERNAL__ 31 | #include "physfs_internal.h" 32 | 33 | #if PHYSFS_SUPPORTS_MVL 34 | 35 | static int mvlLoadEntries(PHYSFS_Io *io, const PHYSFS_uint32 count, void *arc) 36 | { 37 | PHYSFS_uint32 pos = 8 + (17 * count); /* past sig+metadata. */ 38 | PHYSFS_uint32 i; 39 | 40 | for (i = 0; i < count; i++) 41 | { 42 | PHYSFS_uint32 size; 43 | char name[13]; 44 | BAIL_IF_ERRPASS(!__PHYSFS_readAll(io, name, 13), 0); 45 | BAIL_IF_ERRPASS(!__PHYSFS_readAll(io, &size, 4), 0); 46 | name[12] = '\0'; /* just in case. */ 47 | size = PHYSFS_swapULE32(size); 48 | BAIL_IF_ERRPASS(!UNPK_addEntry(arc, name, 0, -1, -1, pos, size), 0); 49 | pos += size; 50 | } /* for */ 51 | 52 | return 1; 53 | } /* mvlLoadEntries */ 54 | 55 | 56 | static void *MVL_openArchive(PHYSFS_Io *io, const char *name, 57 | int forWriting, int *claimed) 58 | { 59 | PHYSFS_uint8 buf[4]; 60 | PHYSFS_uint32 count = 0; 61 | void *unpkarc; 62 | 63 | assert(io != NULL); /* shouldn't ever happen. */ 64 | BAIL_IF(forWriting, PHYSFS_ERR_READ_ONLY, NULL); 65 | BAIL_IF_ERRPASS(!__PHYSFS_readAll(io, buf, 4), NULL); 66 | BAIL_IF(memcmp(buf, "DMVL", 4) != 0, PHYSFS_ERR_UNSUPPORTED, NULL); 67 | 68 | *claimed = 1; 69 | 70 | BAIL_IF_ERRPASS(!__PHYSFS_readAll(io, &count, sizeof(count)), NULL); 71 | count = PHYSFS_swapULE32(count); 72 | 73 | unpkarc = UNPK_openArchive(io); 74 | BAIL_IF_ERRPASS(!unpkarc, NULL); 75 | 76 | if (!mvlLoadEntries(io, count, unpkarc)) 77 | { 78 | UNPK_abandonArchive(unpkarc); 79 | return NULL; 80 | } /* if */ 81 | 82 | return unpkarc; 83 | } /* MVL_openArchive */ 84 | 85 | 86 | const PHYSFS_Archiver __PHYSFS_Archiver_MVL = 87 | { 88 | CURRENT_PHYSFS_ARCHIVER_API_VERSION, 89 | { 90 | "MVL", 91 | "Descent II Movielib format", 92 | "Bradley Bell ", 93 | "https://icculus.org/physfs/", 94 | 0, /* supportsSymlinks */ 95 | }, 96 | MVL_openArchive, 97 | UNPK_enumerate, 98 | UNPK_openRead, 99 | UNPK_openWrite, 100 | UNPK_openAppend, 101 | UNPK_remove, 102 | UNPK_mkdir, 103 | UNPK_stat, 104 | UNPK_closeArchive 105 | }; 106 | 107 | #endif /* defined PHYSFS_SUPPORTS_MVL */ 108 | 109 | /* end of physfs_archiver_mvl.c ... */ 110 | 111 | -------------------------------------------------------------------------------- /src/physfs_archiver_qpak.c: -------------------------------------------------------------------------------- 1 | /* 2 | * QPAK support routines for PhysicsFS. 3 | * 4 | * This archiver handles the archive format utilized by Quake 1 and 2. 5 | * Quake3-based games use the PkZip/Info-Zip format (which our 6 | * physfs_archiver_zip.c handles). 7 | * 8 | * ======================================================================== 9 | * 10 | * This format info (in more detail) comes from: 11 | * https://web.archive.org/web/20040209101748/http://debian.fmi.uni-sofia.bg/~sergei/cgsr/docs/pak.txt 12 | * 13 | * Quake PAK Format 14 | * 15 | * Header 16 | * (4 bytes) signature = 'PACK' 17 | * (4 bytes) directory offset 18 | * (4 bytes) directory length 19 | * 20 | * Directory 21 | * (56 bytes) file name 22 | * (4 bytes) file position 23 | * (4 bytes) file length 24 | * 25 | * ======================================================================== 26 | * 27 | * Please see the file LICENSE.txt in the source's root directory. 28 | * 29 | * This file written by Ryan C. Gordon. 30 | */ 31 | 32 | #define __PHYSICSFS_INTERNAL__ 33 | #include "physfs_internal.h" 34 | 35 | #if PHYSFS_SUPPORTS_QPAK 36 | 37 | #define QPAK_SIG 0x4B434150 /* "PACK" in ASCII. */ 38 | 39 | static int qpakLoadEntries(PHYSFS_Io *io, const PHYSFS_uint32 count, void *arc) 40 | { 41 | PHYSFS_uint32 i; 42 | for (i = 0; i < count; i++) 43 | { 44 | PHYSFS_uint32 size; 45 | PHYSFS_uint32 pos; 46 | char name[56]; 47 | BAIL_IF_ERRPASS(!__PHYSFS_readAll(io, name, 56), 0); 48 | BAIL_IF_ERRPASS(!__PHYSFS_readAll(io, &pos, 4), 0); 49 | BAIL_IF_ERRPASS(!__PHYSFS_readAll(io, &size, 4), 0); 50 | size = PHYSFS_swapULE32(size); 51 | pos = PHYSFS_swapULE32(pos); 52 | BAIL_IF_ERRPASS(!UNPK_addEntry(arc, name, 0, -1, -1, pos, size), 0); 53 | } /* for */ 54 | 55 | return 1; 56 | } /* qpakLoadEntries */ 57 | 58 | 59 | static void *QPAK_openArchive(PHYSFS_Io *io, const char *name, 60 | int forWriting, int *claimed) 61 | { 62 | PHYSFS_uint32 val = 0; 63 | PHYSFS_uint32 pos = 0; 64 | PHYSFS_uint32 count = 0; 65 | void *unpkarc; 66 | 67 | assert(io != NULL); /* shouldn't ever happen. */ 68 | 69 | BAIL_IF(forWriting, PHYSFS_ERR_READ_ONLY, NULL); 70 | 71 | BAIL_IF_ERRPASS(!__PHYSFS_readAll(io, &val, 4), NULL); 72 | if (PHYSFS_swapULE32(val) != QPAK_SIG) 73 | BAIL(PHYSFS_ERR_UNSUPPORTED, NULL); 74 | 75 | *claimed = 1; 76 | 77 | BAIL_IF_ERRPASS(!__PHYSFS_readAll(io, &val, 4), NULL); 78 | pos = PHYSFS_swapULE32(val); /* directory table offset. */ 79 | 80 | BAIL_IF_ERRPASS(!__PHYSFS_readAll(io, &val, 4), NULL); 81 | count = PHYSFS_swapULE32(val); 82 | 83 | /* corrupted archive? */ 84 | BAIL_IF((count % 64) != 0, PHYSFS_ERR_CORRUPT, NULL); 85 | count /= 64; 86 | 87 | BAIL_IF_ERRPASS(!io->seek(io, pos), NULL); 88 | 89 | unpkarc = UNPK_openArchive(io); 90 | BAIL_IF_ERRPASS(!unpkarc, NULL); 91 | 92 | if (!qpakLoadEntries(io, count, unpkarc)) 93 | { 94 | UNPK_abandonArchive(unpkarc); 95 | return NULL; 96 | } /* if */ 97 | 98 | return unpkarc; 99 | } /* QPAK_openArchive */ 100 | 101 | 102 | const PHYSFS_Archiver __PHYSFS_Archiver_QPAK = 103 | { 104 | CURRENT_PHYSFS_ARCHIVER_API_VERSION, 105 | { 106 | "PAK", 107 | "Quake I/II format", 108 | "Ryan C. Gordon ", 109 | "https://icculus.org/physfs/", 110 | 0, /* supportsSymlinks */ 111 | }, 112 | QPAK_openArchive, 113 | UNPK_enumerate, 114 | UNPK_openRead, 115 | UNPK_openWrite, 116 | UNPK_openAppend, 117 | UNPK_remove, 118 | UNPK_mkdir, 119 | UNPK_stat, 120 | UNPK_closeArchive 121 | }; 122 | 123 | #endif /* defined PHYSFS_SUPPORTS_QPAK */ 124 | 125 | /* end of physfs_archiver_qpak.c ... */ 126 | 127 | -------------------------------------------------------------------------------- /src/physfs_archiver_slb.c: -------------------------------------------------------------------------------- 1 | /* 2 | * SLB support routines for PhysicsFS. 3 | * 4 | * This driver handles SLB archives ("slab files"). This uncompressed format 5 | * is used in I-War / Independence War and Independence War: Defiance. 6 | * 7 | * The format begins with four zero bytes (version?), the file count and the 8 | * location of the table of contents. Each ToC entry contains a 64-byte buffer 9 | * containing a zero-terminated filename, the offset of the data, and its size. 10 | * All the filenames begin with the separator character '\'. 11 | * 12 | * Please see the file LICENSE.txt in the source's root directory. 13 | * 14 | * This file written by Aleksi Nurmi, based on the GRP archiver by 15 | * Ryan C. Gordon. 16 | */ 17 | 18 | #define __PHYSICSFS_INTERNAL__ 19 | #include "physfs_internal.h" 20 | 21 | #if PHYSFS_SUPPORTS_SLB 22 | 23 | static int slbLoadEntries(PHYSFS_Io *io, const PHYSFS_uint32 count, void *arc) 24 | { 25 | PHYSFS_uint32 i; 26 | for (i = 0; i < count; i++) 27 | { 28 | PHYSFS_uint32 pos; 29 | PHYSFS_uint32 size; 30 | char name[64]; 31 | char backslash; 32 | char *ptr; 33 | 34 | /* don't include the '\' in the beginning */ 35 | BAIL_IF_ERRPASS(!__PHYSFS_readAll(io, &backslash, 1), 0); 36 | BAIL_IF(backslash != '\\', PHYSFS_ERR_CORRUPT, 0); 37 | 38 | /* read the rest of the buffer, 63 bytes */ 39 | BAIL_IF_ERRPASS(!__PHYSFS_readAll(io, &name, 63), 0); 40 | name[63] = '\0'; /* in case the name lacks the null terminator */ 41 | 42 | /* convert backslashes */ 43 | for (ptr = name; *ptr; ptr++) 44 | { 45 | if (*ptr == '\\') 46 | *ptr = '/'; 47 | } /* for */ 48 | 49 | BAIL_IF_ERRPASS(!__PHYSFS_readAll(io, &pos, 4), 0); 50 | pos = PHYSFS_swapULE32(pos); 51 | 52 | BAIL_IF_ERRPASS(!__PHYSFS_readAll(io, &size, 4), 0); 53 | size = PHYSFS_swapULE32(size); 54 | 55 | BAIL_IF_ERRPASS(!UNPK_addEntry(arc, name, 0, -1, -1, pos, size), 0); 56 | } /* for */ 57 | 58 | return 1; 59 | } /* slbLoadEntries */ 60 | 61 | 62 | static void *SLB_openArchive(PHYSFS_Io *io, const char *name, 63 | int forWriting, int *claimed) 64 | { 65 | PHYSFS_uint32 version; 66 | PHYSFS_uint32 count; 67 | PHYSFS_uint32 tocPos; 68 | void *unpkarc; 69 | 70 | /* There's no identifier on an SLB file, so we assume it's _not_ if the 71 | file count or tocPos is zero. Beyond that, we'll assume it's 72 | bogus/corrupt if the entries' filenames don't start with '\' or the 73 | tocPos is past the end of the file (seek will fail). This probably 74 | covers all meaningful cases where we would accidentally accept a non-SLB 75 | file with this archiver. */ 76 | 77 | assert(io != NULL); /* shouldn't ever happen. */ 78 | 79 | BAIL_IF(forWriting, PHYSFS_ERR_READ_ONLY, NULL); 80 | 81 | BAIL_IF_ERRPASS(!__PHYSFS_readAll(io, &version, sizeof (version)), NULL); 82 | version = PHYSFS_swapULE32(version); 83 | BAIL_IF(version != 0, PHYSFS_ERR_UNSUPPORTED, NULL); 84 | 85 | BAIL_IF_ERRPASS(!__PHYSFS_readAll(io, &count, sizeof (count)), NULL); 86 | count = PHYSFS_swapULE32(count); 87 | BAIL_IF(!count, PHYSFS_ERR_UNSUPPORTED, NULL); 88 | 89 | /* offset of the table of contents */ 90 | BAIL_IF_ERRPASS(!__PHYSFS_readAll(io, &tocPos, sizeof (tocPos)), NULL); 91 | tocPos = PHYSFS_swapULE32(tocPos); 92 | BAIL_IF(!tocPos, PHYSFS_ERR_UNSUPPORTED, NULL); 93 | 94 | /* seek to the table of contents */ 95 | BAIL_IF_ERRPASS(!io->seek(io, tocPos), NULL); 96 | 97 | unpkarc = UNPK_openArchive(io); 98 | BAIL_IF_ERRPASS(!unpkarc, NULL); 99 | 100 | if (!slbLoadEntries(io, count, unpkarc)) 101 | { 102 | UNPK_abandonArchive(unpkarc); 103 | return NULL; 104 | } /* if */ 105 | 106 | *claimed = 1; /* oh well. */ 107 | 108 | return unpkarc; 109 | } /* SLB_openArchive */ 110 | 111 | 112 | const PHYSFS_Archiver __PHYSFS_Archiver_SLB = 113 | { 114 | CURRENT_PHYSFS_ARCHIVER_API_VERSION, 115 | { 116 | "SLB", 117 | "I-War / Independence War Slab file", 118 | "Aleksi Nurmi ", 119 | "https://bitbucket.org/ahnurmi/", 120 | 0, /* supportsSymlinks */ 121 | }, 122 | SLB_openArchive, 123 | UNPK_enumerate, 124 | UNPK_openRead, 125 | UNPK_openWrite, 126 | UNPK_openAppend, 127 | UNPK_remove, 128 | UNPK_mkdir, 129 | UNPK_stat, 130 | UNPK_closeArchive 131 | }; 132 | 133 | #endif /* defined PHYSFS_SUPPORTS_SLB */ 134 | 135 | /* end of physfs_archiver_slb.c ... */ 136 | -------------------------------------------------------------------------------- /src/physfs_archiver_unpacked.c: -------------------------------------------------------------------------------- 1 | /* 2 | * High-level PhysicsFS archiver for simple unpacked file formats. 3 | * 4 | * This is a framework that basic archivers build on top of. It's for simple 5 | * formats that can just hand back a list of files and the offsets of their 6 | * uncompressed data. There are an alarming number of formats like this. 7 | * 8 | * RULES: Archive entries must be uncompressed. Dirs and files allowed, but no 9 | * symlinks, etc. We can relax some of these rules as necessary. 10 | * 11 | * Please see the file LICENSE.txt in the source's root directory. 12 | * 13 | * This file written by Ryan C. Gordon. 14 | */ 15 | 16 | #define __PHYSICSFS_INTERNAL__ 17 | #include "physfs_internal.h" 18 | 19 | typedef struct 20 | { 21 | __PHYSFS_DirTree tree; 22 | PHYSFS_Io *io; 23 | } UNPKinfo; 24 | 25 | typedef struct 26 | { 27 | __PHYSFS_DirTreeEntry tree; 28 | PHYSFS_uint64 startPos; 29 | PHYSFS_uint64 size; 30 | PHYSFS_sint64 ctime; 31 | PHYSFS_sint64 mtime; 32 | } UNPKentry; 33 | 34 | typedef struct 35 | { 36 | PHYSFS_Io *io; 37 | UNPKentry *entry; 38 | PHYSFS_uint32 curPos; 39 | } UNPKfileinfo; 40 | 41 | 42 | void UNPK_closeArchive(void *opaque) 43 | { 44 | UNPKinfo *info = ((UNPKinfo *) opaque); 45 | if (info) 46 | { 47 | __PHYSFS_DirTreeDeinit(&info->tree); 48 | 49 | if (info->io) 50 | info->io->destroy(info->io); 51 | 52 | allocator.Free(info); 53 | } /* if */ 54 | } /* UNPK_closeArchive */ 55 | 56 | void UNPK_abandonArchive(void *opaque) 57 | { 58 | UNPKinfo *info = ((UNPKinfo *) opaque); 59 | if (info) 60 | { 61 | info->io = NULL; 62 | UNPK_closeArchive(info); 63 | } /* if */ 64 | } /* UNPK_abandonArchive */ 65 | 66 | static PHYSFS_sint64 UNPK_read(PHYSFS_Io *io, void *buffer, PHYSFS_uint64 len) 67 | { 68 | UNPKfileinfo *finfo = (UNPKfileinfo *) io->opaque; 69 | const UNPKentry *entry = finfo->entry; 70 | const PHYSFS_uint64 bytesLeft = (PHYSFS_uint64)(entry->size-finfo->curPos); 71 | PHYSFS_sint64 rc; 72 | 73 | if (bytesLeft < len) 74 | len = bytesLeft; 75 | 76 | rc = finfo->io->read(finfo->io, buffer, len); 77 | if (rc > 0) 78 | finfo->curPos += (PHYSFS_uint32) rc; 79 | 80 | return rc; 81 | } /* UNPK_read */ 82 | 83 | 84 | static PHYSFS_sint64 UNPK_write(PHYSFS_Io *io, const void *b, PHYSFS_uint64 len) 85 | { 86 | BAIL(PHYSFS_ERR_READ_ONLY, -1); 87 | } /* UNPK_write */ 88 | 89 | 90 | static PHYSFS_sint64 UNPK_tell(PHYSFS_Io *io) 91 | { 92 | return ((UNPKfileinfo *) io->opaque)->curPos; 93 | } /* UNPK_tell */ 94 | 95 | 96 | static int UNPK_seek(PHYSFS_Io *io, PHYSFS_uint64 offset) 97 | { 98 | UNPKfileinfo *finfo = (UNPKfileinfo *) io->opaque; 99 | const UNPKentry *entry = finfo->entry; 100 | int rc; 101 | 102 | BAIL_IF(offset >= entry->size, PHYSFS_ERR_PAST_EOF, 0); 103 | rc = finfo->io->seek(finfo->io, entry->startPos + offset); 104 | if (rc) 105 | finfo->curPos = (PHYSFS_uint32) offset; 106 | 107 | return rc; 108 | } /* UNPK_seek */ 109 | 110 | 111 | static PHYSFS_sint64 UNPK_length(PHYSFS_Io *io) 112 | { 113 | const UNPKfileinfo *finfo = (UNPKfileinfo *) io->opaque; 114 | return ((PHYSFS_sint64) finfo->entry->size); 115 | } /* UNPK_length */ 116 | 117 | 118 | static PHYSFS_Io *UNPK_duplicate(PHYSFS_Io *_io) 119 | { 120 | UNPKfileinfo *origfinfo = (UNPKfileinfo *) _io->opaque; 121 | PHYSFS_Io *io = NULL; 122 | PHYSFS_Io *retval = (PHYSFS_Io *) allocator.Malloc(sizeof (PHYSFS_Io)); 123 | UNPKfileinfo *finfo = (UNPKfileinfo *) allocator.Malloc(sizeof (UNPKfileinfo)); 124 | GOTO_IF(!retval, PHYSFS_ERR_OUT_OF_MEMORY, UNPK_duplicate_failed); 125 | GOTO_IF(!finfo, PHYSFS_ERR_OUT_OF_MEMORY, UNPK_duplicate_failed); 126 | 127 | io = origfinfo->io->duplicate(origfinfo->io); 128 | if (!io) goto UNPK_duplicate_failed; 129 | finfo->io = io; 130 | finfo->entry = origfinfo->entry; 131 | finfo->curPos = 0; 132 | memcpy(retval, _io, sizeof (PHYSFS_Io)); 133 | retval->opaque = finfo; 134 | return retval; 135 | 136 | UNPK_duplicate_failed: 137 | if (finfo != NULL) allocator.Free(finfo); 138 | if (retval != NULL) allocator.Free(retval); 139 | if (io != NULL) io->destroy(io); 140 | return NULL; 141 | } /* UNPK_duplicate */ 142 | 143 | static int UNPK_flush(PHYSFS_Io *io) { return 1; /* no write support. */ } 144 | 145 | static void UNPK_destroy(PHYSFS_Io *io) 146 | { 147 | UNPKfileinfo *finfo = (UNPKfileinfo *) io->opaque; 148 | finfo->io->destroy(finfo->io); 149 | allocator.Free(finfo); 150 | allocator.Free(io); 151 | } /* UNPK_destroy */ 152 | 153 | 154 | static const PHYSFS_Io UNPK_Io = 155 | { 156 | CURRENT_PHYSFS_IO_API_VERSION, NULL, 157 | UNPK_read, 158 | UNPK_write, 159 | UNPK_seek, 160 | UNPK_tell, 161 | UNPK_length, 162 | UNPK_duplicate, 163 | UNPK_flush, 164 | UNPK_destroy 165 | }; 166 | 167 | 168 | static inline UNPKentry *findEntry(UNPKinfo *info, const char *path) 169 | { 170 | return (UNPKentry *) __PHYSFS_DirTreeFind(&info->tree, path); 171 | } /* findEntry */ 172 | 173 | 174 | PHYSFS_Io *UNPK_openRead(void *opaque, const char *name) 175 | { 176 | PHYSFS_Io *retval = NULL; 177 | UNPKinfo *info = (UNPKinfo *) opaque; 178 | UNPKfileinfo *finfo = NULL; 179 | UNPKentry *entry = findEntry(info, name); 180 | 181 | BAIL_IF_ERRPASS(!entry, NULL); 182 | BAIL_IF(entry->tree.isdir, PHYSFS_ERR_NOT_A_FILE, NULL); 183 | 184 | retval = (PHYSFS_Io *) allocator.Malloc(sizeof (PHYSFS_Io)); 185 | GOTO_IF(!retval, PHYSFS_ERR_OUT_OF_MEMORY, UNPK_openRead_failed); 186 | 187 | finfo = (UNPKfileinfo *) allocator.Malloc(sizeof (UNPKfileinfo)); 188 | GOTO_IF(!finfo, PHYSFS_ERR_OUT_OF_MEMORY, UNPK_openRead_failed); 189 | 190 | finfo->io = info->io->duplicate(info->io); 191 | GOTO_IF_ERRPASS(!finfo->io, UNPK_openRead_failed); 192 | 193 | if (!finfo->io->seek(finfo->io, entry->startPos)) 194 | goto UNPK_openRead_failed; 195 | 196 | finfo->curPos = 0; 197 | finfo->entry = entry; 198 | 199 | memcpy(retval, &UNPK_Io, sizeof (*retval)); 200 | retval->opaque = finfo; 201 | return retval; 202 | 203 | UNPK_openRead_failed: 204 | if (finfo != NULL) 205 | { 206 | if (finfo->io != NULL) 207 | finfo->io->destroy(finfo->io); 208 | allocator.Free(finfo); 209 | } /* if */ 210 | 211 | if (retval != NULL) 212 | allocator.Free(retval); 213 | 214 | return NULL; 215 | } /* UNPK_openRead */ 216 | 217 | 218 | PHYSFS_Io *UNPK_openWrite(void *opaque, const char *name) 219 | { 220 | BAIL(PHYSFS_ERR_READ_ONLY, NULL); 221 | } /* UNPK_openWrite */ 222 | 223 | 224 | PHYSFS_Io *UNPK_openAppend(void *opaque, const char *name) 225 | { 226 | BAIL(PHYSFS_ERR_READ_ONLY, NULL); 227 | } /* UNPK_openAppend */ 228 | 229 | 230 | int UNPK_remove(void *opaque, const char *name) 231 | { 232 | BAIL(PHYSFS_ERR_READ_ONLY, 0); 233 | } /* UNPK_remove */ 234 | 235 | 236 | int UNPK_mkdir(void *opaque, const char *name) 237 | { 238 | BAIL(PHYSFS_ERR_READ_ONLY, 0); 239 | } /* UNPK_mkdir */ 240 | 241 | 242 | int UNPK_stat(void *opaque, const char *path, PHYSFS_Stat *stat) 243 | { 244 | UNPKinfo *info = (UNPKinfo *) opaque; 245 | const UNPKentry *entry = findEntry(info, path); 246 | 247 | BAIL_IF_ERRPASS(!entry, 0); 248 | 249 | if (entry->tree.isdir) 250 | { 251 | stat->filetype = PHYSFS_FILETYPE_DIRECTORY; 252 | stat->filesize = 0; 253 | } /* if */ 254 | else 255 | { 256 | stat->filetype = PHYSFS_FILETYPE_REGULAR; 257 | stat->filesize = entry->size; 258 | } /* else */ 259 | 260 | stat->modtime = entry->mtime; 261 | stat->createtime = entry->ctime; 262 | stat->accesstime = -1; 263 | stat->readonly = 1; 264 | 265 | return 1; 266 | } /* UNPK_stat */ 267 | 268 | 269 | void *UNPK_addEntry(void *opaque, char *name, const int isdir, 270 | const PHYSFS_sint64 ctime, const PHYSFS_sint64 mtime, 271 | const PHYSFS_uint64 pos, const PHYSFS_uint64 len) 272 | { 273 | UNPKinfo *info = (UNPKinfo *) opaque; 274 | UNPKentry *entry; 275 | 276 | entry = (UNPKentry *) __PHYSFS_DirTreeAdd(&info->tree, name, isdir); 277 | BAIL_IF_ERRPASS(!entry, NULL); 278 | 279 | entry->startPos = isdir ? 0 : pos; 280 | entry->size = isdir ? 0 : len; 281 | entry->ctime = ctime; 282 | entry->mtime = mtime; 283 | 284 | return entry; 285 | } /* UNPK_addEntry */ 286 | 287 | 288 | void *UNPK_openArchive(PHYSFS_Io *io) 289 | { 290 | UNPKinfo *info = (UNPKinfo *) allocator.Malloc(sizeof (UNPKinfo)); 291 | BAIL_IF(!info, PHYSFS_ERR_OUT_OF_MEMORY, NULL); 292 | 293 | if (!__PHYSFS_DirTreeInit(&info->tree, sizeof (UNPKentry))) 294 | { 295 | allocator.Free(info); 296 | return NULL; 297 | } /* if */ 298 | 299 | info->io = io; 300 | 301 | return info; 302 | } /* UNPK_openArchive */ 303 | 304 | /* end of physfs_archiver_unpacked.c ... */ 305 | 306 | -------------------------------------------------------------------------------- /src/physfs_archiver_vdf.c: -------------------------------------------------------------------------------- 1 | /* 2 | * VDF support routines for PhysicsFS. 3 | * 4 | * This driver handles Gothic I/II VDF archives. 5 | * This format (but not this driver) was designed by Piranha Bytes for 6 | * use wih the ZenGin engine. 7 | * 8 | * This file was written by Francesco Bertolaccini, based on the UNPK archiver 9 | * by Ryan C. Gordon and the works of degenerated1123 and Nico Bendlin. 10 | */ 11 | 12 | #define __PHYSICSFS_INTERNAL__ 13 | #include "physfs_internal.h" 14 | 15 | #if PHYSFS_SUPPORTS_VDF 16 | 17 | #include 18 | 19 | #define VDF_COMMENT_LENGTH 256 20 | #define VDF_SIGNATURE_LENGTH 16 21 | #define VDF_ENTRY_NAME_LENGTH 64 22 | #define VDF_ENTRY_DIR 0x80000000 23 | 24 | static const char* VDF_SIGNATURE_G1 = "PSVDSC_V2.00\r\n\r\n"; 25 | static const char* VDF_SIGNATURE_G2 = "PSVDSC_V2.00\n\r\n\r"; 26 | 27 | 28 | static inline int readui32(PHYSFS_Io *io, PHYSFS_uint32 *val) 29 | { 30 | PHYSFS_uint32 v; 31 | BAIL_IF_ERRPASS(!__PHYSFS_readAll(io, &v, sizeof (v)), 0); 32 | *val = PHYSFS_swapULE32(v); 33 | return 1; 34 | } /* readui32 */ 35 | 36 | 37 | static PHYSFS_sint64 vdfDosTimeToEpoch(const PHYSFS_uint32 dostime) 38 | { 39 | /* VDF stores timestamps as 32bit DOS dates: the seconds are counted in 40 | 2-seconds intervals and the years are counted since 1 Jan. 1980 */ 41 | struct tm t; 42 | memset(&t, '\0', sizeof (t)); 43 | t.tm_year = ((int) ((dostime >> 25) & 0x7F)) + 80; /* 1980 to 1900 */ 44 | t.tm_mon = ((int) ((dostime >> 21) & 0xF)) - 1; /* 1-12 to 0-11 */ 45 | t.tm_mday = (int) ((dostime >> 16) & 0x1F); 46 | t.tm_hour = (int) ((dostime >> 11) & 0x1F); 47 | t.tm_min = (int) ((dostime >> 5) & 0x3F); 48 | t.tm_sec = ((int) ((dostime >> 0) & 0x1F)) * 2; /* 2 seconds to 1. */ 49 | return (PHYSFS_sint64) mktime(&t); 50 | } /* vdfDosTimeToEpoch */ 51 | 52 | 53 | static int vdfLoadEntries(PHYSFS_Io *io, const PHYSFS_uint32 count, 54 | const PHYSFS_sint64 ts, void *arc) 55 | { 56 | PHYSFS_uint32 i; 57 | 58 | for (i = 0; i < count; i++) 59 | { 60 | char name[VDF_ENTRY_NAME_LENGTH + 1]; 61 | int namei; 62 | PHYSFS_uint32 jump, size, type, attr; 63 | 64 | BAIL_IF_ERRPASS(!__PHYSFS_readAll(io, name, sizeof (name) - 1), 0); 65 | BAIL_IF_ERRPASS(!readui32(io, &jump), 0); 66 | BAIL_IF_ERRPASS(!readui32(io, &size), 0); 67 | BAIL_IF_ERRPASS(!readui32(io, &type), 0); 68 | BAIL_IF_ERRPASS(!readui32(io, &attr), 0); 69 | 70 | /* Trim whitespace off the end of the filename */ 71 | name[VDF_ENTRY_NAME_LENGTH] = '\0'; /* always null-terminated. */ 72 | for (namei = VDF_ENTRY_NAME_LENGTH - 1; namei >= 0; namei--) 73 | { 74 | /* We assume the filenames are low-ASCII; consider the archive 75 | corrupt if we see something above 127, since we don't know the 76 | encoding. (We can change this later if we find out these exist 77 | and are intended to be, say, latin-1 or UTF-8 encoding). */ 78 | BAIL_IF(((PHYSFS_uint8) name[namei]) > 127, PHYSFS_ERR_CORRUPT, 0); 79 | 80 | if (name[namei] == ' ') 81 | name[namei] = '\0'; 82 | else 83 | break; 84 | } /* for */ 85 | 86 | BAIL_IF(!name[0], PHYSFS_ERR_CORRUPT, 0); 87 | if (!(type & VDF_ENTRY_DIR)) { 88 | BAIL_IF_ERRPASS(!UNPK_addEntry(arc, name, 0, ts, ts, jump, size), 0); 89 | } 90 | } /* for */ 91 | 92 | return 1; 93 | } /* vdfLoadEntries */ 94 | 95 | 96 | static void *VDF_openArchive(PHYSFS_Io *io, const char *name, 97 | int forWriting, int *claimed) 98 | { 99 | PHYSFS_uint8 ignore[16]; 100 | PHYSFS_uint8 sig[VDF_SIGNATURE_LENGTH]; 101 | PHYSFS_uint32 count, timestamp, version, dataSize, rootCatOffset; 102 | void *unpkarc; 103 | 104 | assert(io != NULL); /* shouldn't ever happen. */ 105 | 106 | BAIL_IF(forWriting, PHYSFS_ERR_READ_ONLY, NULL); 107 | 108 | /* skip the 256-byte comment field. */ 109 | BAIL_IF_ERRPASS(!io->seek(io, VDF_COMMENT_LENGTH), NULL); 110 | 111 | BAIL_IF_ERRPASS(!__PHYSFS_readAll(io, sig, sizeof (sig)), NULL); 112 | 113 | if ((memcmp(sig, VDF_SIGNATURE_G1, VDF_SIGNATURE_LENGTH) != 0) && 114 | (memcmp(sig, VDF_SIGNATURE_G2, VDF_SIGNATURE_LENGTH) != 0)) 115 | { 116 | BAIL(PHYSFS_ERR_UNSUPPORTED, NULL); 117 | } /* if */ 118 | 119 | *claimed = 1; 120 | 121 | BAIL_IF_ERRPASS(!readui32(io, &count), NULL); 122 | BAIL_IF_ERRPASS(!__PHYSFS_readAll(io, ignore, 4), NULL); /* numFiles */ 123 | BAIL_IF_ERRPASS(!readui32(io, ×tamp), NULL); 124 | BAIL_IF_ERRPASS(!readui32(io, &dataSize), NULL); /* dataSize */ 125 | BAIL_IF_ERRPASS(!readui32(io, &rootCatOffset), NULL); /* rootCatOff */ 126 | BAIL_IF_ERRPASS(!readui32(io, &version), NULL); 127 | 128 | BAIL_IF(version != 0x50, PHYSFS_ERR_UNSUPPORTED, NULL); 129 | 130 | BAIL_IF_ERRPASS(!io->seek(io, rootCatOffset), NULL); 131 | 132 | unpkarc = UNPK_openArchive(io); 133 | BAIL_IF_ERRPASS(!unpkarc, NULL); 134 | 135 | if (!vdfLoadEntries(io, count, vdfDosTimeToEpoch(timestamp), unpkarc)) 136 | { 137 | UNPK_abandonArchive(unpkarc); 138 | return NULL; 139 | } /* if */ 140 | 141 | return unpkarc; 142 | } /* VDF_openArchive */ 143 | 144 | 145 | const PHYSFS_Archiver __PHYSFS_Archiver_VDF = 146 | { 147 | CURRENT_PHYSFS_ARCHIVER_API_VERSION, 148 | { 149 | "VDF", 150 | "Gothic I/II engine format", 151 | "Francesco Bertolaccini ", 152 | "https://github.com/frabert", 153 | 0, /* supportsSymlinks */ 154 | }, 155 | VDF_openArchive, 156 | UNPK_enumerate, 157 | UNPK_openRead, 158 | UNPK_openWrite, 159 | UNPK_openAppend, 160 | UNPK_remove, 161 | UNPK_mkdir, 162 | UNPK_stat, 163 | UNPK_closeArchive 164 | }; 165 | 166 | #endif /* defined PHYSFS_SUPPORTS_VDF */ 167 | 168 | /* end of physfs_archiver_vdf.c ... */ 169 | -------------------------------------------------------------------------------- /src/physfs_archiver_wad.c: -------------------------------------------------------------------------------- 1 | /* 2 | * WAD support routines for PhysicsFS. 3 | * 4 | * This driver handles DOOM engine archives ("wads"). 5 | * This format (but not this driver) was designed by id Software for use 6 | * with the DOOM engine. 7 | * The specs of the format are from the unofficial doom specs v1.666 8 | * found here: http://www.gamers.org/dhs/helpdocs/dmsp1666.html 9 | * The format of the archive: (from the specs) 10 | * 11 | * A WAD file has three parts: 12 | * (1) a twelve-byte header 13 | * (2) one or more "lumps" 14 | * (3) a directory or "info table" that contains the names, offsets, and 15 | * sizes of all the lumps in the WAD 16 | * 17 | * The header consists of three four-byte parts: 18 | * (a) an ASCII string which must be either "IWAD" or "PWAD" 19 | * (b) a uint32 which is the number of lumps in the wad 20 | * (c) a uint32 which is the file offset to the start of 21 | * the directory 22 | * 23 | * The directory has one 16-byte entry for every lump. Each entry consists 24 | * of three parts: 25 | * 26 | * (a) a uint32, the file offset to the start of the lump 27 | * (b) a uint32, the size of the lump in bytes 28 | * (c) an 8-byte ASCII string, the name of the lump, padded with zeros. 29 | * For example, the "DEMO1" entry in hexadecimal would be 30 | * (44 45 4D 4F 31 00 00 00) 31 | * 32 | * Note that there is no way to tell if an opened WAD archive is a 33 | * IWAD or PWAD with this archiver. 34 | * I couldn't think of a way to provide that information, without being too 35 | * hacky. 36 | * I don't think it's really that important though. 37 | * 38 | * 39 | * Please see the file LICENSE.txt in the source's root directory. 40 | * 41 | * This file written by Travis Wells, based on the GRP archiver by 42 | * Ryan C. Gordon. 43 | */ 44 | 45 | #define __PHYSICSFS_INTERNAL__ 46 | #include "physfs_internal.h" 47 | 48 | #if PHYSFS_SUPPORTS_WAD 49 | 50 | static int wadLoadEntries(PHYSFS_Io *io, const PHYSFS_uint32 count, void *arc) 51 | { 52 | PHYSFS_uint32 i; 53 | for (i = 0; i < count; i++) 54 | { 55 | PHYSFS_uint32 pos; 56 | PHYSFS_uint32 size; 57 | char name[9]; 58 | 59 | BAIL_IF_ERRPASS(!__PHYSFS_readAll(io, &pos, 4), 0); 60 | BAIL_IF_ERRPASS(!__PHYSFS_readAll(io, &size, 4), 0); 61 | BAIL_IF_ERRPASS(!__PHYSFS_readAll(io, name, 8), 0); 62 | 63 | name[8] = '\0'; /* name might not be null-terminated in file. */ 64 | size = PHYSFS_swapULE32(size); 65 | pos = PHYSFS_swapULE32(pos); 66 | BAIL_IF_ERRPASS(!UNPK_addEntry(arc, name, 0, -1, -1, pos, size), 0); 67 | } /* for */ 68 | 69 | return 1; 70 | } /* wadLoadEntries */ 71 | 72 | 73 | static void *WAD_openArchive(PHYSFS_Io *io, const char *name, 74 | int forWriting, int *claimed) 75 | { 76 | PHYSFS_uint8 buf[4]; 77 | PHYSFS_uint32 count; 78 | PHYSFS_uint32 directoryOffset; 79 | void *unpkarc; 80 | 81 | assert(io != NULL); /* shouldn't ever happen. */ 82 | 83 | BAIL_IF(forWriting, PHYSFS_ERR_READ_ONLY, NULL); 84 | BAIL_IF_ERRPASS(!__PHYSFS_readAll(io, buf, sizeof (buf)), NULL); 85 | if ((memcmp(buf, "IWAD", 4) != 0) && (memcmp(buf, "PWAD", 4) != 0)) 86 | BAIL(PHYSFS_ERR_UNSUPPORTED, NULL); 87 | 88 | *claimed = 1; 89 | 90 | BAIL_IF_ERRPASS(!__PHYSFS_readAll(io, &count, sizeof (count)), NULL); 91 | count = PHYSFS_swapULE32(count); 92 | 93 | BAIL_IF_ERRPASS(!__PHYSFS_readAll(io, &directoryOffset, 4), 0); 94 | directoryOffset = PHYSFS_swapULE32(directoryOffset); 95 | 96 | BAIL_IF_ERRPASS(!io->seek(io, directoryOffset), 0); 97 | 98 | unpkarc = UNPK_openArchive(io); 99 | BAIL_IF_ERRPASS(!unpkarc, NULL); 100 | 101 | if (!wadLoadEntries(io, count, unpkarc)) 102 | { 103 | UNPK_abandonArchive(unpkarc); 104 | return NULL; 105 | } /* if */ 106 | 107 | return unpkarc; 108 | } /* WAD_openArchive */ 109 | 110 | 111 | const PHYSFS_Archiver __PHYSFS_Archiver_WAD = 112 | { 113 | CURRENT_PHYSFS_ARCHIVER_API_VERSION, 114 | { 115 | "WAD", 116 | "DOOM engine format", 117 | "Travis Wells ", 118 | "http://www.3dmm2.com/doom/", 119 | 0, /* supportsSymlinks */ 120 | }, 121 | WAD_openArchive, 122 | UNPK_enumerate, 123 | UNPK_openRead, 124 | UNPK_openWrite, 125 | UNPK_openAppend, 126 | UNPK_remove, 127 | UNPK_mkdir, 128 | UNPK_stat, 129 | UNPK_closeArchive 130 | }; 131 | 132 | #endif /* defined PHYSFS_SUPPORTS_WAD */ 133 | 134 | /* end of physfs_archiver_wad.c ... */ 135 | 136 | -------------------------------------------------------------------------------- /src/physfs_byteorder.c: -------------------------------------------------------------------------------- 1 | /** 2 | * PhysicsFS; a portable, flexible file i/o abstraction. 3 | * 4 | * Documentation is in physfs.h. It's verbose, honest. :) 5 | * 6 | * Please see the file LICENSE.txt in the source's root directory. 7 | * 8 | * This file written by Ryan C. Gordon. 9 | */ 10 | 11 | #define __PHYSICSFS_INTERNAL__ 12 | #include "physfs_internal.h" 13 | 14 | #ifndef PHYSFS_Swap16 15 | static inline PHYSFS_uint16 PHYSFS_Swap16(PHYSFS_uint16 D) 16 | { 17 | return ((D<<8)|(D>>8)); 18 | } 19 | #endif 20 | #ifndef PHYSFS_Swap32 21 | static inline PHYSFS_uint32 PHYSFS_Swap32(PHYSFS_uint32 D) 22 | { 23 | return ((D<<24)|((D<<8)&0x00FF0000)|((D>>8)&0x0000FF00)|(D>>24)); 24 | } 25 | #endif 26 | #ifndef PHYSFS_NO_64BIT_SUPPORT 27 | #ifndef PHYSFS_Swap64 28 | static inline PHYSFS_uint64 PHYSFS_Swap64(PHYSFS_uint64 val) { 29 | PHYSFS_uint32 hi, lo; 30 | 31 | /* Separate into high and low 32-bit values and swap them */ 32 | lo = (PHYSFS_uint32)(val&0xFFFFFFFF); 33 | val >>= 32; 34 | hi = (PHYSFS_uint32)(val&0xFFFFFFFF); 35 | val = PHYSFS_Swap32(lo); 36 | val <<= 32; 37 | val |= PHYSFS_Swap32(hi); 38 | return val; 39 | } 40 | #endif 41 | #else 42 | #ifndef PHYSFS_Swap64 43 | /* This is mainly to keep compilers from complaining in PHYSFS code. 44 | If there is no real 64-bit datatype, then compilers will complain about 45 | the fake 64-bit datatype that PHYSFS provides when it compiles user code. 46 | */ 47 | #define PHYSFS_Swap64(X) (X) 48 | #endif 49 | #endif /* PHYSFS_NO_64BIT_SUPPORT */ 50 | 51 | 52 | /* Byteswap item from the specified endianness to the native endianness */ 53 | #if PHYSFS_BYTEORDER == PHYSFS_LIL_ENDIAN 54 | PHYSFS_uint16 PHYSFS_swapULE16(PHYSFS_uint16 x) { return x; } 55 | PHYSFS_sint16 PHYSFS_swapSLE16(PHYSFS_sint16 x) { return x; } 56 | PHYSFS_uint32 PHYSFS_swapULE32(PHYSFS_uint32 x) { return x; } 57 | PHYSFS_sint32 PHYSFS_swapSLE32(PHYSFS_sint32 x) { return x; } 58 | PHYSFS_uint64 PHYSFS_swapULE64(PHYSFS_uint64 x) { return x; } 59 | PHYSFS_sint64 PHYSFS_swapSLE64(PHYSFS_sint64 x) { return x; } 60 | 61 | PHYSFS_uint16 PHYSFS_swapUBE16(PHYSFS_uint16 x) { return PHYSFS_Swap16(x); } 62 | PHYSFS_sint16 PHYSFS_swapSBE16(PHYSFS_sint16 x) { return PHYSFS_Swap16(x); } 63 | PHYSFS_uint32 PHYSFS_swapUBE32(PHYSFS_uint32 x) { return PHYSFS_Swap32(x); } 64 | PHYSFS_sint32 PHYSFS_swapSBE32(PHYSFS_sint32 x) { return PHYSFS_Swap32(x); } 65 | PHYSFS_uint64 PHYSFS_swapUBE64(PHYSFS_uint64 x) { return PHYSFS_Swap64(x); } 66 | PHYSFS_sint64 PHYSFS_swapSBE64(PHYSFS_sint64 x) { return PHYSFS_Swap64(x); } 67 | #else 68 | PHYSFS_uint16 PHYSFS_swapULE16(PHYSFS_uint16 x) { return PHYSFS_Swap16(x); } 69 | PHYSFS_sint16 PHYSFS_swapSLE16(PHYSFS_sint16 x) { return PHYSFS_Swap16(x); } 70 | PHYSFS_uint32 PHYSFS_swapULE32(PHYSFS_uint32 x) { return PHYSFS_Swap32(x); } 71 | PHYSFS_sint32 PHYSFS_swapSLE32(PHYSFS_sint32 x) { return PHYSFS_Swap32(x); } 72 | PHYSFS_uint64 PHYSFS_swapULE64(PHYSFS_uint64 x) { return PHYSFS_Swap64(x); } 73 | PHYSFS_sint64 PHYSFS_swapSLE64(PHYSFS_sint64 x) { return PHYSFS_Swap64(x); } 74 | 75 | PHYSFS_uint16 PHYSFS_swapUBE16(PHYSFS_uint16 x) { return x; } 76 | PHYSFS_sint16 PHYSFS_swapSBE16(PHYSFS_sint16 x) { return x; } 77 | PHYSFS_uint32 PHYSFS_swapUBE32(PHYSFS_uint32 x) { return x; } 78 | PHYSFS_sint32 PHYSFS_swapSBE32(PHYSFS_sint32 x) { return x; } 79 | PHYSFS_uint64 PHYSFS_swapUBE64(PHYSFS_uint64 x) { return x; } 80 | PHYSFS_sint64 PHYSFS_swapSBE64(PHYSFS_sint64 x) { return x; } 81 | #endif 82 | 83 | static inline int readAll(PHYSFS_File *file, void *val, const size_t len) 84 | { 85 | return (PHYSFS_readBytes(file, val, len) == len); 86 | } /* readAll */ 87 | 88 | #define PHYSFS_BYTEORDER_READ(datatype, swaptype) \ 89 | int PHYSFS_read##swaptype(PHYSFS_File *file, PHYSFS_##datatype *val) { \ 90 | PHYSFS_##datatype in; \ 91 | BAIL_IF(val == NULL, PHYSFS_ERR_INVALID_ARGUMENT, 0); \ 92 | BAIL_IF_ERRPASS(!readAll(file, &in, sizeof (in)), 0); \ 93 | *val = PHYSFS_swap##swaptype(in); \ 94 | return 1; \ 95 | } 96 | 97 | PHYSFS_BYTEORDER_READ(sint16, SLE16) 98 | PHYSFS_BYTEORDER_READ(uint16, ULE16) 99 | PHYSFS_BYTEORDER_READ(sint16, SBE16) 100 | PHYSFS_BYTEORDER_READ(uint16, UBE16) 101 | PHYSFS_BYTEORDER_READ(sint32, SLE32) 102 | PHYSFS_BYTEORDER_READ(uint32, ULE32) 103 | PHYSFS_BYTEORDER_READ(sint32, SBE32) 104 | PHYSFS_BYTEORDER_READ(uint32, UBE32) 105 | PHYSFS_BYTEORDER_READ(sint64, SLE64) 106 | PHYSFS_BYTEORDER_READ(uint64, ULE64) 107 | PHYSFS_BYTEORDER_READ(sint64, SBE64) 108 | PHYSFS_BYTEORDER_READ(uint64, UBE64) 109 | 110 | 111 | static inline int writeAll(PHYSFS_File *f, const void *val, const size_t len) 112 | { 113 | return (PHYSFS_writeBytes(f, val, len) == len); 114 | } /* writeAll */ 115 | 116 | #define PHYSFS_BYTEORDER_WRITE(datatype, swaptype) \ 117 | int PHYSFS_write##swaptype(PHYSFS_File *file, PHYSFS_##datatype val) { \ 118 | const PHYSFS_##datatype out = PHYSFS_swap##swaptype(val); \ 119 | BAIL_IF_ERRPASS(!writeAll(file, &out, sizeof (out)), 0); \ 120 | return 1; \ 121 | } 122 | 123 | PHYSFS_BYTEORDER_WRITE(sint16, SLE16) 124 | PHYSFS_BYTEORDER_WRITE(uint16, ULE16) 125 | PHYSFS_BYTEORDER_WRITE(sint16, SBE16) 126 | PHYSFS_BYTEORDER_WRITE(uint16, UBE16) 127 | PHYSFS_BYTEORDER_WRITE(sint32, SLE32) 128 | PHYSFS_BYTEORDER_WRITE(uint32, ULE32) 129 | PHYSFS_BYTEORDER_WRITE(sint32, SBE32) 130 | PHYSFS_BYTEORDER_WRITE(uint32, UBE32) 131 | PHYSFS_BYTEORDER_WRITE(sint64, SLE64) 132 | PHYSFS_BYTEORDER_WRITE(uint64, ULE64) 133 | PHYSFS_BYTEORDER_WRITE(sint64, SBE64) 134 | PHYSFS_BYTEORDER_WRITE(uint64, UBE64) 135 | 136 | /* end of physfs_byteorder.c ... */ 137 | 138 | -------------------------------------------------------------------------------- /src/physfs_platform_android.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Android support routines for PhysicsFS. 3 | * 4 | * Please see the file LICENSE.txt in the source's root directory. 5 | * 6 | * This file written by Ryan C. Gordon. 7 | */ 8 | 9 | #define __PHYSICSFS_INTERNAL__ 10 | #include "physfs_platforms.h" 11 | 12 | #ifdef PHYSFS_PLATFORM_ANDROID 13 | 14 | #include 15 | #include 16 | #include "physfs_internal.h" 17 | 18 | static char *prefpath = NULL; 19 | 20 | 21 | int __PHYSFS_platformInit(void) 22 | { 23 | return 1; /* always succeed. */ 24 | } /* __PHYSFS_platformInit */ 25 | 26 | 27 | void __PHYSFS_platformDeinit(void) 28 | { 29 | if (prefpath) 30 | { 31 | allocator.Free(prefpath); 32 | prefpath = NULL; 33 | } /* if */ 34 | } /* __PHYSFS_platformDeinit */ 35 | 36 | 37 | void __PHYSFS_platformDetectAvailableCDs(PHYSFS_StringCallback cb, void *data) 38 | { 39 | /* no-op. */ 40 | } /* __PHYSFS_platformDetectAvailableCDs */ 41 | 42 | 43 | char *__PHYSFS_platformCalcBaseDir(const char *argv0) 44 | { 45 | /* as a cheat, we expect argv0 to be a PHYSFS_AndroidInit* on Android. */ 46 | PHYSFS_AndroidInit *ainit = (PHYSFS_AndroidInit *) argv0; 47 | char *retval = NULL; 48 | JNIEnv *jenv = NULL; 49 | jobject jcontext; 50 | 51 | if (ainit == NULL) 52 | return __PHYSFS_strdup("/"); /* oh well. */ 53 | 54 | jenv = (JNIEnv *) ainit->jnienv; 55 | jcontext = (jobject) ainit->context; 56 | 57 | if ((*jenv)->PushLocalFrame(jenv, 16) >= 0) 58 | { 59 | jobject jfileobj = 0; 60 | jmethodID jmeth = 0; 61 | jthrowable jexception = 0; 62 | jstring jstr = 0; 63 | 64 | jmeth = (*jenv)->GetMethodID(jenv, (*jenv)->GetObjectClass(jenv, jcontext), "getPackageResourcePath", "()Ljava/lang/String;"); 65 | jstr = (jstring)(*jenv)->CallObjectMethod(jenv, jcontext, jmeth); 66 | jexception = (*jenv)->ExceptionOccurred(jenv); /* this can't throw an exception, right? Just in case. */ 67 | if (jexception != NULL) 68 | (*jenv)->ExceptionClear(jenv); 69 | else 70 | { 71 | const char *path = (*jenv)->GetStringUTFChars(jenv, jstr, NULL); 72 | retval = __PHYSFS_strdup(path); 73 | (*jenv)->ReleaseStringUTFChars(jenv, jstr, path); 74 | } /* else */ 75 | 76 | /* We only can rely on the Activity being valid during this function call, 77 | so go ahead and grab the prefpath too. */ 78 | jmeth = (*jenv)->GetMethodID(jenv, (*jenv)->GetObjectClass(jenv, jcontext), "getFilesDir", "()Ljava/io/File;"); 79 | jfileobj = (*jenv)->CallObjectMethod(jenv, jcontext, jmeth); 80 | if (jfileobj) 81 | { 82 | jmeth = (*jenv)->GetMethodID(jenv, (*jenv)->GetObjectClass(jenv, jfileobj), "getCanonicalPath", "()Ljava/lang/String;"); 83 | jstr = (jstring)(*jenv)->CallObjectMethod(jenv, jfileobj, jmeth); 84 | jexception = (*jenv)->ExceptionOccurred(jenv); 85 | if (jexception != NULL) 86 | (*jenv)->ExceptionClear(jenv); 87 | else 88 | { 89 | const char *path = (*jenv)->GetStringUTFChars(jenv, jstr, NULL); 90 | const size_t len = strlen(path) + 2; 91 | prefpath = allocator.Malloc(len); 92 | if (prefpath) 93 | snprintf(prefpath, len, "%s/", path); 94 | (*jenv)->ReleaseStringUTFChars(jenv, jstr, path); 95 | } /* else */ 96 | } /* if */ 97 | 98 | (*jenv)->PopLocalFrame(jenv, NULL); 99 | } /* if */ 100 | 101 | /* we can't return NULL because then PhysicsFS will treat argv0 as a string, but it's a non-NULL jobject! */ 102 | if (retval == NULL) 103 | retval = __PHYSFS_strdup("/"); /* we pray this works. */ 104 | 105 | return retval; 106 | } /* __PHYSFS_platformCalcBaseDir */ 107 | 108 | 109 | char *__PHYSFS_platformCalcPrefDir(const char *org, const char *app) 110 | { 111 | return __PHYSFS_strdup(prefpath ? prefpath : "/"); 112 | } /* __PHYSFS_platformCalcPrefDir */ 113 | 114 | #endif /* PHYSFS_PLATFORM_ANDROID */ 115 | 116 | /* end of physfs_platform_android.c ... */ 117 | 118 | -------------------------------------------------------------------------------- /src/physfs_platform_apple.m: -------------------------------------------------------------------------------- 1 | /* 2 | * Apple platform (macOS, iOS, watchOS, etc) support routines for PhysicsFS. 3 | * 4 | * Please see the file LICENSE.txt in the source's root directory. 5 | * 6 | * This file written by Ryan C. Gordon. 7 | */ 8 | 9 | #define __PHYSICSFS_INTERNAL__ 10 | #include "physfs_platforms.h" 11 | 12 | #ifdef PHYSFS_PLATFORM_APPLE 13 | 14 | #include 15 | 16 | #include "physfs_internal.h" 17 | 18 | int __PHYSFS_platformInit(void) 19 | { 20 | return 1; /* success. */ 21 | } /* __PHYSFS_platformInit */ 22 | 23 | 24 | void __PHYSFS_platformDeinit(void) 25 | { 26 | /* no-op */ 27 | } /* __PHYSFS_platformDeinit */ 28 | 29 | 30 | char *__PHYSFS_platformCalcBaseDir(const char *argv0) 31 | { 32 | @autoreleasepool 33 | { 34 | NSString *path = [[NSBundle mainBundle] bundlePath]; 35 | BAIL_IF(!path, PHYSFS_ERR_OS_ERROR, NULL); 36 | size_t len = [path lengthOfBytesUsingEncoding:NSUTF8StringEncoding]; 37 | char *retval = (char *) allocator.Malloc(len + 2); 38 | BAIL_IF(!retval, PHYSFS_ERR_OUT_OF_MEMORY, NULL); 39 | [path getCString:retval maxLength:len+1 encoding:NSUTF8StringEncoding]; 40 | retval[len] = '/'; 41 | retval[len+1] = '\0'; 42 | return retval; /* whew. */ 43 | } /* @autoreleasepool */ 44 | } /* __PHYSFS_platformCalcBaseDir */ 45 | 46 | 47 | char *__PHYSFS_platformCalcPrefDir(const char *org, const char *app) 48 | { 49 | @autoreleasepool 50 | { 51 | NSArray *paths = NSSearchPathForDirectoriesInDomains(NSApplicationSupportDirectory, NSUserDomainMask, TRUE); 52 | BAIL_IF(!paths, PHYSFS_ERR_OS_ERROR, NULL); 53 | NSString *path = (NSString *) [paths objectAtIndex:0]; 54 | BAIL_IF(!path, PHYSFS_ERR_OS_ERROR, NULL); 55 | size_t len = [path lengthOfBytesUsingEncoding:NSUTF8StringEncoding]; 56 | const size_t applen = strlen(app); 57 | char *retval = (char *) allocator.Malloc(len + applen + 3); 58 | BAIL_IF(!retval, PHYSFS_ERR_OUT_OF_MEMORY, NULL); 59 | [path getCString:retval maxLength:len+1 encoding:NSUTF8StringEncoding]; 60 | snprintf(retval + len, applen + 3, "/%s/", app); 61 | return retval; /* whew. */ 62 | } /* @autoreleasepool */ 63 | } /* __PHYSFS_platformCalcPrefDir */ 64 | 65 | 66 | /* CD-ROM detection code... */ 67 | 68 | /* 69 | * Code based on sample from Apple Developer Connection: 70 | * https://developer.apple.com/samplecode/Sample_Code/Devices_and_Hardware/Disks/VolumeToBSDNode/VolumeToBSDNode.c.htm 71 | */ 72 | 73 | #if !defined(PHYSFS_NO_CDROM_SUPPORT) 74 | 75 | #include 76 | #include 77 | #include 78 | #include 79 | #include 80 | 81 | static int darwinIsWholeMedia(io_service_t service) 82 | { 83 | int retval = 0; 84 | CFTypeRef wholeMedia; 85 | 86 | if (!IOObjectConformsTo(service, kIOMediaClass)) 87 | return 0; 88 | 89 | wholeMedia = IORegistryEntryCreateCFProperty(service, 90 | CFSTR(kIOMediaWholeKey), 91 | NULL, 0); 92 | if (wholeMedia == NULL) 93 | return 0; 94 | 95 | retval = CFBooleanGetValue(wholeMedia); 96 | CFRelease(wholeMedia); 97 | 98 | return retval; 99 | } /* darwinIsWholeMedia */ 100 | 101 | 102 | static int darwinIsMountedDisc(char *bsdName, mach_port_t masterPort) 103 | { 104 | int retval = 0; 105 | CFMutableDictionaryRef matchingDict; 106 | kern_return_t rc; 107 | io_iterator_t iter; 108 | io_service_t service; 109 | 110 | if ((matchingDict = IOBSDNameMatching(masterPort, 0, bsdName)) == NULL) 111 | return 0; 112 | 113 | rc = IOServiceGetMatchingServices(masterPort, matchingDict, &iter); 114 | if ((rc != KERN_SUCCESS) || (!iter)) 115 | return 0; 116 | 117 | service = IOIteratorNext(iter); 118 | IOObjectRelease(iter); 119 | if (!service) 120 | return 0; 121 | 122 | rc = IORegistryEntryCreateIterator(service, kIOServicePlane, 123 | kIORegistryIterateRecursively | kIORegistryIterateParents, &iter); 124 | 125 | if (!iter) 126 | return 0; 127 | 128 | if (rc != KERN_SUCCESS) 129 | { 130 | IOObjectRelease(iter); 131 | return 0; 132 | } /* if */ 133 | 134 | IOObjectRetain(service); /* add an extra object reference... */ 135 | 136 | do 137 | { 138 | if (darwinIsWholeMedia(service)) 139 | { 140 | if ( (IOObjectConformsTo(service, kIOCDMediaClass)) || 141 | (IOObjectConformsTo(service, kIODVDMediaClass)) ) 142 | { 143 | retval = 1; 144 | } /* if */ 145 | } /* if */ 146 | IOObjectRelease(service); 147 | } while ((service = IOIteratorNext(iter)) && (!retval)); 148 | 149 | IOObjectRelease(iter); 150 | IOObjectRelease(service); 151 | 152 | return retval; 153 | } /* darwinIsMountedDisc */ 154 | 155 | #endif /* !defined(PHYSFS_NO_CDROM_SUPPORT) */ 156 | 157 | 158 | void __PHYSFS_platformDetectAvailableCDs(PHYSFS_StringCallback cb, void *data) 159 | { 160 | #if !defined(PHYSFS_NO_CDROM_SUPPORT) 161 | const char *devPrefix = "/dev/"; 162 | const int prefixLen = strlen(devPrefix); 163 | mach_port_t masterPort = 0; 164 | struct statfs *mntbufp; 165 | int i, mounts; 166 | 167 | if (IOMasterPort(MACH_PORT_NULL, &masterPort) != KERN_SUCCESS) 168 | BAIL(PHYSFS_ERR_OS_ERROR, ) /*return void*/; 169 | 170 | mounts = getmntinfo(&mntbufp, MNT_WAIT); /* NOT THREAD SAFE! */ 171 | for (i = 0; i < mounts; i++) 172 | { 173 | char *dev = mntbufp[i].f_mntfromname; 174 | char *mnt = mntbufp[i].f_mntonname; 175 | if (strncmp(dev, devPrefix, prefixLen) != 0) /* a virtual device? */ 176 | continue; 177 | 178 | dev += prefixLen; 179 | if (darwinIsMountedDisc(dev, masterPort)) 180 | cb(data, mnt); 181 | } /* for */ 182 | #endif /* !defined(PHYSFS_NO_CDROM_SUPPORT) */ 183 | } /* __PHYSFS_platformDetectAvailableCDs */ 184 | 185 | #endif /* PHYSFS_PLATFORM_APPLE */ 186 | 187 | /* end of physfs_platform_apple.m ... */ 188 | 189 | -------------------------------------------------------------------------------- /src/physfs_platform_haiku.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Haiku platform-dependent support routines for PhysicsFS. 3 | * 4 | * Please see the file LICENSE.txt in the source's root directory. 5 | * 6 | * This file written by Ryan C. Gordon. 7 | */ 8 | 9 | #define __PHYSICSFS_INTERNAL__ 10 | #include "physfs_platforms.h" 11 | 12 | #ifdef PHYSFS_PLATFORM_HAIKU 13 | 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | 24 | #include 25 | #include 26 | 27 | #include "physfs_internal.h" 28 | 29 | int __PHYSFS_platformInit(void) 30 | { 31 | return 1; /* always succeed. */ 32 | } /* __PHYSFS_platformInit */ 33 | 34 | 35 | void __PHYSFS_platformDeinit(void) 36 | { 37 | /* no-op */ 38 | } /* __PHYSFS_platformDeinit */ 39 | 40 | 41 | static char *getMountPoint(const char *devname, char *buf, size_t bufsize) 42 | { 43 | BVolumeRoster mounts; 44 | BVolume vol; 45 | 46 | mounts.Rewind(); 47 | while (mounts.GetNextVolume(&vol) == B_NO_ERROR) 48 | { 49 | fs_info fsinfo; 50 | fs_stat_dev(vol.Device(), &fsinfo); 51 | if (strcmp(devname, fsinfo.device_name) == 0) 52 | { 53 | BDirectory directory; 54 | BEntry entry; 55 | BPath path; 56 | const char *str; 57 | 58 | if ( (vol.GetRootDirectory(&directory) < B_OK) || 59 | (directory.GetEntry(&entry) < B_OK) || 60 | (entry.GetPath(&path) < B_OK) || 61 | ( (str = path.Path()) == NULL) ) 62 | return NULL; 63 | 64 | strncpy(buf, str, bufsize-1); 65 | buf[bufsize-1] = '\0'; 66 | return buf; 67 | } /* if */ 68 | } /* while */ 69 | 70 | return NULL; 71 | } /* getMountPoint */ 72 | 73 | 74 | /* 75 | * This function is lifted from Simple Directmedia Layer (SDL): 76 | * https://www.libsdl.org/ ... this is zlib-licensed code, too. 77 | */ 78 | static void tryDir(const char *d, PHYSFS_StringCallback callback, void *data) 79 | { 80 | BDirectory dir; 81 | dir.SetTo(d); 82 | if (dir.InitCheck() != B_NO_ERROR) 83 | return; 84 | 85 | dir.Rewind(); 86 | BEntry entry; 87 | while (dir.GetNextEntry(&entry) >= 0) 88 | { 89 | BPath path; 90 | const char *name; 91 | entry_ref e; 92 | 93 | if (entry.GetPath(&path) != B_NO_ERROR) 94 | continue; 95 | 96 | name = path.Path(); 97 | 98 | if (entry.GetRef(&e) != B_NO_ERROR) 99 | continue; 100 | 101 | if (entry.IsDirectory()) 102 | { 103 | if (strcmp(e.name, "floppy") != 0) 104 | tryDir(name, callback, data); 105 | continue; 106 | } /* if */ 107 | 108 | const int devfd = open(name, O_RDONLY); 109 | if (devfd < 0) 110 | continue; 111 | 112 | device_geometry g; 113 | const int rc = ioctl(devfd, B_GET_GEOMETRY, &g, sizeof (g)); 114 | close(devfd); 115 | if (rc < 0) 116 | continue; 117 | 118 | if (g.device_type != B_CD) 119 | continue; 120 | 121 | char mntpnt[B_FILE_NAME_LENGTH]; 122 | if (getMountPoint(name, mntpnt, sizeof (mntpnt))) 123 | callback(data, mntpnt); 124 | } /* while */ 125 | } /* tryDir */ 126 | 127 | 128 | void __PHYSFS_platformDetectAvailableCDs(PHYSFS_StringCallback cb, void *data) 129 | { 130 | tryDir("/dev/disk", cb, data); 131 | } /* __PHYSFS_platformDetectAvailableCDs */ 132 | 133 | 134 | static team_id getTeamID(void) 135 | { 136 | thread_info info; 137 | thread_id tid = find_thread(NULL); 138 | get_thread_info(tid, &info); 139 | return info.team; 140 | } /* getTeamID */ 141 | 142 | 143 | char *__PHYSFS_platformCalcBaseDir(const char *argv0) 144 | { 145 | image_info info; 146 | int32 cookie = 0; 147 | 148 | while (get_next_image_info(0, &cookie, &info) == B_OK) 149 | { 150 | if (info.type == B_APP_IMAGE) 151 | break; 152 | } /* while */ 153 | 154 | BEntry entry(info.name, true); 155 | BPath path; 156 | status_t rc = entry.GetPath(&path); /* (path) now has binary's path. */ 157 | assert(rc == B_OK); 158 | rc = path.GetParent(&path); /* chop filename, keep directory. */ 159 | assert(rc == B_OK); 160 | const char *str = path.Path(); 161 | assert(str != NULL); 162 | const size_t len = strlen(str); 163 | char *retval = (char *) allocator.Malloc(len + 2); 164 | BAIL_IF(!retval, PHYSFS_ERR_OUT_OF_MEMORY, NULL); 165 | strcpy(retval, str); 166 | retval[len] = '/'; 167 | retval[len+1] = '\0'; 168 | return retval; 169 | } /* __PHYSFS_platformCalcBaseDir */ 170 | 171 | 172 | char *__PHYSFS_platformCalcPrefDir(const char *org, const char *app) 173 | { 174 | const char *userdir = __PHYSFS_getUserDir(); 175 | const char *append = "config/settings/"; 176 | const size_t len = strlen(userdir) + strlen(append) + strlen(app) + 2; 177 | char *retval = (char *) allocator.Malloc(len); 178 | BAIL_IF(!retval, PHYSFS_ERR_OUT_OF_MEMORY, NULL); 179 | snprintf(retval, len, "%s%s%s/", userdir, append, app); 180 | return retval; 181 | } /* __PHYSFS_platformCalcPrefDir */ 182 | 183 | #endif /* PHYSFS_PLATFORM_HAIKU */ 184 | 185 | /* end of physfs_platform_haiku.cpp ... */ 186 | 187 | -------------------------------------------------------------------------------- /src/physfs_platform_posix.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Posix-esque support routines for PhysicsFS. 3 | * 4 | * Please see the file LICENSE.txt in the source's root directory. 5 | * 6 | * This file written by Ryan C. Gordon. 7 | */ 8 | 9 | /* !!! FIXME: check for EINTR? */ 10 | 11 | #define __PHYSICSFS_INTERNAL__ 12 | #include "physfs_platforms.h" 13 | 14 | #ifdef PHYSFS_PLATFORM_POSIX 15 | 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | 26 | #include "physfs_internal.h" 27 | 28 | 29 | static PHYSFS_ErrorCode errcodeFromErrnoError(const int err) 30 | { 31 | switch (err) 32 | { 33 | case 0: return PHYSFS_ERR_OK; 34 | case EACCES: return PHYSFS_ERR_PERMISSION; 35 | case EPERM: return PHYSFS_ERR_PERMISSION; 36 | case EDQUOT: return PHYSFS_ERR_NO_SPACE; 37 | case EIO: return PHYSFS_ERR_IO; 38 | case ELOOP: return PHYSFS_ERR_SYMLINK_LOOP; 39 | case EMLINK: return PHYSFS_ERR_NO_SPACE; 40 | case ENAMETOOLONG: return PHYSFS_ERR_BAD_FILENAME; 41 | case ENOENT: return PHYSFS_ERR_NOT_FOUND; 42 | case ENOSPC: return PHYSFS_ERR_NO_SPACE; 43 | case ENOTDIR: return PHYSFS_ERR_NOT_FOUND; 44 | case EISDIR: return PHYSFS_ERR_NOT_A_FILE; 45 | case EROFS: return PHYSFS_ERR_READ_ONLY; 46 | case ETXTBSY: return PHYSFS_ERR_BUSY; 47 | case EBUSY: return PHYSFS_ERR_BUSY; 48 | case ENOMEM: return PHYSFS_ERR_OUT_OF_MEMORY; 49 | case ENOTEMPTY: return PHYSFS_ERR_DIR_NOT_EMPTY; 50 | default: return PHYSFS_ERR_OS_ERROR; 51 | } /* switch */ 52 | } /* errcodeFromErrnoError */ 53 | 54 | 55 | static inline PHYSFS_ErrorCode errcodeFromErrno(void) 56 | { 57 | return errcodeFromErrnoError(errno); 58 | } /* errcodeFromErrno */ 59 | 60 | 61 | static char *getUserDirByUID(void) 62 | { 63 | uid_t uid = getuid(); 64 | struct passwd *pw; 65 | char *retval = NULL; 66 | 67 | pw = getpwuid(uid); 68 | if ((pw != NULL) && (pw->pw_dir != NULL) && (*pw->pw_dir != '\0')) 69 | { 70 | const size_t dlen = strlen(pw->pw_dir); 71 | const size_t add_dirsep = (pw->pw_dir[dlen-1] != '/') ? 1 : 0; 72 | retval = (char *) allocator.Malloc(dlen + 1 + add_dirsep); 73 | if (retval != NULL) 74 | { 75 | strcpy(retval, pw->pw_dir); 76 | if (add_dirsep) 77 | { 78 | retval[dlen] = '/'; 79 | retval[dlen+1] = '\0'; 80 | } /* if */ 81 | } /* if */ 82 | } /* if */ 83 | 84 | return retval; 85 | } /* getUserDirByUID */ 86 | 87 | 88 | char *__PHYSFS_platformCalcUserDir(void) 89 | { 90 | char *retval = NULL; 91 | char *envr = getenv("HOME"); 92 | 93 | /* if the environment variable was set, make sure it's really a dir. */ 94 | if (envr != NULL) 95 | { 96 | struct stat statbuf; 97 | if ((stat(envr, &statbuf) != -1) && (S_ISDIR(statbuf.st_mode))) 98 | { 99 | const size_t envrlen = strlen(envr); 100 | const size_t add_dirsep = (envr[envrlen-1] != '/') ? 1 : 0; 101 | retval = allocator.Malloc(envrlen + 1 + add_dirsep); 102 | if (retval) 103 | { 104 | strcpy(retval, envr); 105 | if (add_dirsep) 106 | { 107 | retval[envrlen] = '/'; 108 | retval[envrlen+1] = '\0'; 109 | } /* if */ 110 | } /* if */ 111 | } /* if */ 112 | } /* if */ 113 | 114 | if (retval == NULL) 115 | retval = getUserDirByUID(); 116 | 117 | return retval; 118 | } /* __PHYSFS_platformCalcUserDir */ 119 | 120 | 121 | PHYSFS_EnumerateCallbackResult __PHYSFS_platformEnumerate(const char *dirname, 122 | PHYSFS_EnumerateCallback callback, 123 | const char *origdir, void *callbackdata) 124 | { 125 | DIR *dir; 126 | struct dirent *ent; 127 | PHYSFS_EnumerateCallbackResult retval = PHYSFS_ENUM_OK; 128 | 129 | dir = opendir(dirname); 130 | BAIL_IF(dir == NULL, errcodeFromErrno(), PHYSFS_ENUM_ERROR); 131 | 132 | while ((retval == PHYSFS_ENUM_OK) && ((ent = readdir(dir)) != NULL)) 133 | { 134 | const char *name = ent->d_name; 135 | if (name[0] == '.') /* ignore "." and ".." */ 136 | { 137 | if ((name[1] == '\0') || ((name[1] == '.') && (name[2] == '\0'))) 138 | continue; 139 | } /* if */ 140 | 141 | retval = callback(callbackdata, origdir, name); 142 | if (retval == PHYSFS_ENUM_ERROR) 143 | PHYSFS_setErrorCode(PHYSFS_ERR_APP_CALLBACK); 144 | } /* while */ 145 | 146 | closedir(dir); 147 | 148 | return retval; 149 | } /* __PHYSFS_platformEnumerate */ 150 | 151 | 152 | int __PHYSFS_platformMkDir(const char *path) 153 | { 154 | const int rc = mkdir(path, S_IRWXU); 155 | BAIL_IF(rc == -1, errcodeFromErrno(), 0); 156 | return 1; 157 | } /* __PHYSFS_platformMkDir */ 158 | 159 | 160 | static void *doOpen(const char *filename, int mode) 161 | { 162 | const int appending = (mode & O_APPEND); 163 | int fd; 164 | int *retval; 165 | errno = 0; 166 | 167 | /* O_APPEND doesn't actually behave as we'd like. */ 168 | mode &= ~O_APPEND; 169 | 170 | fd = open(filename, mode, S_IRUSR | S_IWUSR); 171 | BAIL_IF(fd < 0, errcodeFromErrno(), NULL); 172 | 173 | if (appending) 174 | { 175 | if (lseek(fd, 0, SEEK_END) < 0) 176 | { 177 | const int err = errno; 178 | close(fd); 179 | BAIL(errcodeFromErrnoError(err), NULL); 180 | } /* if */ 181 | } /* if */ 182 | 183 | retval = (int *) allocator.Malloc(sizeof (int)); 184 | if (!retval) 185 | { 186 | close(fd); 187 | BAIL(PHYSFS_ERR_OUT_OF_MEMORY, NULL); 188 | } /* if */ 189 | 190 | *retval = fd; 191 | return ((void *) retval); 192 | } /* doOpen */ 193 | 194 | 195 | void *__PHYSFS_platformOpenRead(const char *filename) 196 | { 197 | return doOpen(filename, O_RDONLY); 198 | } /* __PHYSFS_platformOpenRead */ 199 | 200 | 201 | void *__PHYSFS_platformOpenWrite(const char *filename) 202 | { 203 | return doOpen(filename, O_WRONLY | O_CREAT | O_TRUNC); 204 | } /* __PHYSFS_platformOpenWrite */ 205 | 206 | 207 | void *__PHYSFS_platformOpenAppend(const char *filename) 208 | { 209 | return doOpen(filename, O_WRONLY | O_CREAT | O_APPEND); 210 | } /* __PHYSFS_platformOpenAppend */ 211 | 212 | 213 | PHYSFS_sint64 __PHYSFS_platformRead(void *opaque, void *buffer, 214 | PHYSFS_uint64 len) 215 | { 216 | const int fd = *((int *) opaque); 217 | ssize_t rc = 0; 218 | 219 | if (!__PHYSFS_ui64FitsAddressSpace(len)) 220 | BAIL(PHYSFS_ERR_INVALID_ARGUMENT, -1); 221 | 222 | rc = read(fd, buffer, (size_t) len); 223 | BAIL_IF(rc == -1, errcodeFromErrno(), -1); 224 | assert(rc >= 0); 225 | assert(rc <= len); 226 | return (PHYSFS_sint64) rc; 227 | } /* __PHYSFS_platformRead */ 228 | 229 | 230 | PHYSFS_sint64 __PHYSFS_platformWrite(void *opaque, const void *buffer, 231 | PHYSFS_uint64 len) 232 | { 233 | const int fd = *((int *) opaque); 234 | ssize_t rc = 0; 235 | 236 | if (!__PHYSFS_ui64FitsAddressSpace(len)) 237 | BAIL(PHYSFS_ERR_INVALID_ARGUMENT, -1); 238 | 239 | rc = write(fd, (void *) buffer, (size_t) len); 240 | BAIL_IF(rc == -1, errcodeFromErrno(), rc); 241 | assert(rc >= 0); 242 | assert(rc <= len); 243 | return (PHYSFS_sint64) rc; 244 | } /* __PHYSFS_platformWrite */ 245 | 246 | 247 | int __PHYSFS_platformSeek(void *opaque, PHYSFS_uint64 pos) 248 | { 249 | const int fd = *((int *) opaque); 250 | const off_t rc = lseek(fd, (off_t) pos, SEEK_SET); 251 | BAIL_IF(rc == -1, errcodeFromErrno(), 0); 252 | return 1; 253 | } /* __PHYSFS_platformSeek */ 254 | 255 | 256 | PHYSFS_sint64 __PHYSFS_platformTell(void *opaque) 257 | { 258 | const int fd = *((int *) opaque); 259 | PHYSFS_sint64 retval; 260 | retval = (PHYSFS_sint64) lseek(fd, 0, SEEK_CUR); 261 | BAIL_IF(retval == -1, errcodeFromErrno(), -1); 262 | return retval; 263 | } /* __PHYSFS_platformTell */ 264 | 265 | 266 | PHYSFS_sint64 __PHYSFS_platformFileLength(void *opaque) 267 | { 268 | const int fd = *((int *) opaque); 269 | struct stat statbuf; 270 | BAIL_IF(fstat(fd, &statbuf) == -1, errcodeFromErrno(), -1); 271 | return ((PHYSFS_sint64) statbuf.st_size); 272 | } /* __PHYSFS_platformFileLength */ 273 | 274 | 275 | int __PHYSFS_platformFlush(void *opaque) 276 | { 277 | const int fd = *((int *) opaque); 278 | if ((fcntl(fd, F_GETFL) & O_ACCMODE) != O_RDONLY) 279 | BAIL_IF(fsync(fd) == -1, errcodeFromErrno(), 0); 280 | return 1; 281 | } /* __PHYSFS_platformFlush */ 282 | 283 | 284 | void __PHYSFS_platformClose(void *opaque) 285 | { 286 | const int fd = *((int *) opaque); 287 | (void) close(fd); /* we don't check this. You should have used flush! */ 288 | allocator.Free(opaque); 289 | } /* __PHYSFS_platformClose */ 290 | 291 | 292 | int __PHYSFS_platformDelete(const char *path) 293 | { 294 | BAIL_IF(remove(path) == -1, errcodeFromErrno(), 0); 295 | return 1; 296 | } /* __PHYSFS_platformDelete */ 297 | 298 | 299 | int __PHYSFS_platformStat(const char *fname, PHYSFS_Stat *st, const int follow) 300 | { 301 | struct stat statbuf; 302 | const int rc = follow ? stat(fname, &statbuf) : lstat(fname, &statbuf); 303 | BAIL_IF(rc == -1, errcodeFromErrno(), 0); 304 | 305 | if (S_ISREG(statbuf.st_mode)) 306 | { 307 | st->filetype = PHYSFS_FILETYPE_REGULAR; 308 | st->filesize = statbuf.st_size; 309 | } /* if */ 310 | 311 | else if(S_ISDIR(statbuf.st_mode)) 312 | { 313 | st->filetype = PHYSFS_FILETYPE_DIRECTORY; 314 | st->filesize = 0; 315 | } /* else if */ 316 | 317 | else if(S_ISLNK(statbuf.st_mode)) 318 | { 319 | st->filetype = PHYSFS_FILETYPE_SYMLINK; 320 | st->filesize = 0; 321 | } /* else if */ 322 | 323 | else 324 | { 325 | st->filetype = PHYSFS_FILETYPE_OTHER; 326 | st->filesize = statbuf.st_size; 327 | } /* else */ 328 | 329 | st->modtime = statbuf.st_mtime; 330 | st->createtime = statbuf.st_ctime; 331 | st->accesstime = statbuf.st_atime; 332 | 333 | st->readonly = (access(fname, W_OK) == -1); 334 | return 1; 335 | } /* __PHYSFS_platformStat */ 336 | 337 | 338 | typedef struct 339 | { 340 | pthread_mutex_t mutex; 341 | pthread_t owner; 342 | PHYSFS_uint32 count; 343 | } PthreadMutex; 344 | 345 | 346 | void *__PHYSFS_platformGetThreadID(void) 347 | { 348 | return ( (void *) ((size_t) pthread_self()) ); 349 | } /* __PHYSFS_platformGetThreadID */ 350 | 351 | 352 | void *__PHYSFS_platformCreateMutex(void) 353 | { 354 | int rc; 355 | PthreadMutex *m = (PthreadMutex *) allocator.Malloc(sizeof (PthreadMutex)); 356 | BAIL_IF(!m, PHYSFS_ERR_OUT_OF_MEMORY, NULL); 357 | rc = pthread_mutex_init(&m->mutex, NULL); 358 | if (rc != 0) 359 | { 360 | allocator.Free(m); 361 | BAIL(PHYSFS_ERR_OS_ERROR, NULL); 362 | } /* if */ 363 | 364 | m->count = 0; 365 | m->owner = (pthread_t) 0xDEADBEEF; 366 | return ((void *) m); 367 | } /* __PHYSFS_platformCreateMutex */ 368 | 369 | 370 | void __PHYSFS_platformDestroyMutex(void *mutex) 371 | { 372 | PthreadMutex *m = (PthreadMutex *) mutex; 373 | 374 | /* Destroying a locked mutex is a bug, but we'll try to be helpful. */ 375 | if ((m->owner == pthread_self()) && (m->count > 0)) 376 | pthread_mutex_unlock(&m->mutex); 377 | 378 | pthread_mutex_destroy(&m->mutex); 379 | allocator.Free(m); 380 | } /* __PHYSFS_platformDestroyMutex */ 381 | 382 | 383 | int __PHYSFS_platformGrabMutex(void *mutex) 384 | { 385 | PthreadMutex *m = (PthreadMutex *) mutex; 386 | pthread_t tid = pthread_self(); 387 | if (m->owner != tid) 388 | { 389 | if (pthread_mutex_lock(&m->mutex) != 0) 390 | return 0; 391 | m->owner = tid; 392 | } /* if */ 393 | 394 | m->count++; 395 | return 1; 396 | } /* __PHYSFS_platformGrabMutex */ 397 | 398 | 399 | void __PHYSFS_platformReleaseMutex(void *mutex) 400 | { 401 | PthreadMutex *m = (PthreadMutex *) mutex; 402 | assert(m->owner == pthread_self()); /* catch programming errors. */ 403 | assert(m->count > 0); /* catch programming errors. */ 404 | if (m->owner == pthread_self()) 405 | { 406 | if (--m->count == 0) 407 | { 408 | m->owner = (pthread_t) 0xDEADBEEF; 409 | pthread_mutex_unlock(&m->mutex); 410 | } /* if */ 411 | } /* if */ 412 | } /* __PHYSFS_platformReleaseMutex */ 413 | 414 | #endif /* PHYSFS_PLATFORM_POSIX */ 415 | 416 | /* end of physfs_platform_posix.c ... */ 417 | 418 | -------------------------------------------------------------------------------- /src/physfs_platform_qnx.c: -------------------------------------------------------------------------------- 1 | /* 2 | * QNX support routines for PhysicsFS. 3 | * 4 | * Please see the file LICENSE.txt in the source's root directory. 5 | * 6 | * This file written by Ryan C. Gordon. 7 | */ 8 | 9 | /* This is tested against QNX 7 at the moment. */ 10 | 11 | #define __PHYSICSFS_INTERNAL__ 12 | #include "physfs_platforms.h" 13 | 14 | #ifdef PHYSFS_PLATFORM_QNX 15 | 16 | #include 17 | #include 18 | #include 19 | #include 20 | 21 | #include "physfs_internal.h" 22 | 23 | int __PHYSFS_platformInit(void) 24 | { 25 | return 1; /* always succeed. */ 26 | } /* __PHYSFS_platformInit */ 27 | 28 | 29 | void __PHYSFS_platformDeinit(void) 30 | { 31 | /* no-op */ 32 | } /* __PHYSFS_platformDeinit */ 33 | 34 | 35 | char *__PHYSFS_platformCalcBaseDir(const char *argv0) 36 | { 37 | char *retval = (char *) allocator.Malloc(PATH_MAX+1); 38 | if (retval == NULL) 39 | BAIL(PHYSFS_ERR_OUT_OF_MEMORY, NULL); 40 | else 41 | { 42 | const int fd = open("/proc/self/exefile", O_RDONLY); 43 | const ssize_t br = (fd == -1) ? -1 : read(fd, retval, PATH_MAX); 44 | char *ptr; 45 | 46 | if (fd != -1) 47 | close(fd); 48 | 49 | if ((br < 0) || (br > PATH_MAX)) 50 | { 51 | allocator.Free(retval); 52 | BAIL(PHYSFS_ERR_OS_ERROR, NULL); 53 | } /* if */ 54 | 55 | retval[br] = '\0'; 56 | ptr = strrchr(retval, '/'); 57 | if (ptr == NULL) /* uhoh! */ 58 | { 59 | allocator.Free(retval); 60 | BAIL(PHYSFS_ERR_OS_ERROR, NULL); 61 | } /* if */ 62 | 63 | ptr[1] = '\0'; /* chop off filename, leave dirs and '/' */ 64 | 65 | ptr = (char *) allocator.Realloc(retval, (ptr - retval) + 2); 66 | if (ptr != NULL) /* just shrinking buffer; don't care if it failed. */ 67 | retval = ptr; 68 | } /* else */ 69 | 70 | return retval; 71 | } /* __PHYSFS_platformCalcBaseDir */ 72 | 73 | 74 | char *__PHYSFS_platformCalcPrefDir(const char *org, const char *app) 75 | { 76 | /* !!! FIXME: this might be wrong; I don't know if there's a better method 77 | on QNX, or if it follows XDG specs, etc. */ 78 | char *retval = NULL; 79 | const char *home = __PHYSFS_getUserDir(); 80 | if (home) 81 | { 82 | const size_t len = strlen(home) + strlen(app) + 3; 83 | retval = (char *) allocator.Malloc(len); 84 | BAIL_IF(!retval, PHYSFS_ERR_OUT_OF_MEMORY, NULL); 85 | snprintf(retval, len, "%s.%s/", home, app); 86 | } /* if */ 87 | return retval; 88 | } /* __PHYSFS_platformCalcPrefDir */ 89 | 90 | 91 | #if !PHYSFS_NO_CDROM_SUPPORT 92 | #include 93 | #include 94 | #include 95 | #include 96 | #include 97 | 98 | static void checkPathForCD(const char *path, PHYSFS_StringCallback cb, void *d) 99 | { 100 | struct stat statbuf; 101 | int fd; 102 | 103 | /* The devctl() thing is QNX-specific. In this case, we query what is 104 | probably the mountpoint for the device. statvfs() on that mountpoint 105 | will tell use its filesystem type. */ 106 | 107 | if ( (stat(path, &statbuf) == 0) && 108 | (S_ISBLK(statbuf.st_mode)) && 109 | ((fd = open(path, O_RDONLY | O_NONBLOCK)) != -1) ) 110 | { 111 | char mnt[256] = { 0 }; 112 | const int rc = devctl(fd, DCMD_FSYS_MOUNTED_BY, mnt, sizeof (mnt), 0); 113 | close(fd); 114 | if ( (rc == EOK) && (mnt[0]) ) 115 | { 116 | struct statvfs statvfsbuf; 117 | if (statvfs(mnt, &statvfsbuf) == 0) 118 | { 119 | /* I don't know if this is a complete or accurate list. */ 120 | const char *fstype = statvfsbuf.f_basetype; 121 | const int iscd = ( (strcmp(fstype, "cd") == 0) || 122 | (strcmp(fstype, "udf") == 0) ); 123 | if (iscd) 124 | cb(d, mnt); 125 | } /* if */ 126 | } /* if */ 127 | } /* if */ 128 | } /* checkPathForCD */ 129 | 130 | static void checkDevForCD(const char *dev, PHYSFS_StringCallback cb, void *d) 131 | { 132 | size_t len; 133 | char *path; 134 | 135 | if (dev[0] == '.') /* ignore "." and ".." */ 136 | { 137 | if ((dev[1] == '\0') || ((dev[1] == '.') && (dev[2] == '\0'))) 138 | return; 139 | } /* if */ 140 | 141 | len = strlen(dev) + 6; 142 | path = (char *) __PHYSFS_smallAlloc(len); 143 | if (!path) 144 | return; /* oh well. */ 145 | 146 | snprintf(path, len, "/dev/%s", dev); 147 | checkPathForCD(path, cb, d); 148 | __PHYSFS_smallFree(path); 149 | } /* checkDevForCD */ 150 | #endif /* !PHYSFS_NO_CDROM_SUPPORT */ 151 | 152 | void __PHYSFS_platformDetectAvailableCDs(PHYSFS_StringCallback cb, void *data) 153 | { 154 | #if !PHYSFS_NO_CDROM_SUPPORT 155 | DIR *dirp = opendir("/dev"); 156 | if (dirp) 157 | { 158 | struct dirent *dent; 159 | while ((dent = readdir(dirp)) != NULL) 160 | checkDevForCD(dent->d_name, cb, data); 161 | closedir(dirp); 162 | } /* if */ 163 | #endif 164 | } /* __PHYSFS_platformDetectAvailableCDs */ 165 | 166 | #endif /* PHYSFS_PLATFORM_QNX */ 167 | 168 | /* end of physfs_platform_qnx.c ... */ 169 | 170 | -------------------------------------------------------------------------------- /src/physfs_platform_unix.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Unix support routines for PhysicsFS. 3 | * 4 | * Please see the file LICENSE.txt in the source's root directory. 5 | * 6 | * This file written by Ryan C. Gordon. 7 | */ 8 | 9 | #define __PHYSICSFS_INTERNAL__ 10 | #include "physfs_platforms.h" 11 | 12 | #ifdef PHYSFS_PLATFORM_UNIX 13 | 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | 26 | #if PHYSFS_NO_CDROM_SUPPORT 27 | #elif PHYSFS_PLATFORM_LINUX 28 | # define PHYSFS_HAVE_MNTENT_H 1 29 | #elif defined __CYGWIN__ 30 | # define PHYSFS_HAVE_MNTENT_H 1 31 | #elif PHYSFS_PLATFORM_SOLARIS 32 | # define PHYSFS_HAVE_SYS_MNTTAB_H 1 33 | #elif PHYSFS_PLATFORM_BSD 34 | # define PHYSFS_HAVE_SYS_UCRED_H 1 35 | #else 36 | # warning No CD-ROM support included. Either define your platform here, 37 | # warning or define PHYSFS_NO_CDROM_SUPPORT=1 to confirm this is intentional. 38 | #endif 39 | 40 | #ifdef PHYSFS_HAVE_SYS_UCRED_H 41 | # ifdef PHYSFS_HAVE_MNTENT_H 42 | # undef PHYSFS_HAVE_MNTENT_H /* don't do both... */ 43 | # endif 44 | # include 45 | # include 46 | #endif 47 | 48 | #ifdef PHYSFS_HAVE_MNTENT_H 49 | #include 50 | #endif 51 | 52 | #ifdef PHYSFS_HAVE_SYS_MNTTAB_H 53 | #include 54 | #endif 55 | 56 | #ifdef PHYSFS_PLATFORM_FREEBSD 57 | #include 58 | #endif 59 | 60 | 61 | #include "physfs_internal.h" 62 | 63 | int __PHYSFS_platformInit(void) 64 | { 65 | return 1; /* always succeed. */ 66 | } /* __PHYSFS_platformInit */ 67 | 68 | 69 | void __PHYSFS_platformDeinit(void) 70 | { 71 | /* no-op */ 72 | } /* __PHYSFS_platformDeinit */ 73 | 74 | 75 | void __PHYSFS_platformDetectAvailableCDs(PHYSFS_StringCallback cb, void *data) 76 | { 77 | #if (defined PHYSFS_NO_CDROM_SUPPORT) 78 | /* no-op. */ 79 | 80 | #elif (defined PHYSFS_HAVE_SYS_UCRED_H) 81 | int i; 82 | struct statfs *mntbufp = NULL; 83 | int mounts = getmntinfo(&mntbufp, MNT_NOWAIT); 84 | 85 | for (i = 0; i < mounts; i++) 86 | { 87 | int add_it = 0; 88 | 89 | if (strcmp(mntbufp[i].f_fstypename, "iso9660") == 0) 90 | add_it = 1; 91 | else if (strcmp( mntbufp[i].f_fstypename, "cd9660") == 0) 92 | add_it = 1; 93 | 94 | /* add other mount types here */ 95 | 96 | if (add_it) 97 | cb(data, mntbufp[i].f_mntonname); 98 | } /* for */ 99 | 100 | #elif (defined PHYSFS_HAVE_MNTENT_H) 101 | FILE *mounts = NULL; 102 | struct mntent *ent = NULL; 103 | 104 | mounts = setmntent("/etc/mtab", "r"); 105 | BAIL_IF(mounts == NULL, PHYSFS_ERR_IO, /*return void*/); 106 | 107 | while ( (ent = getmntent(mounts)) != NULL ) 108 | { 109 | int add_it = 0; 110 | if (strcmp(ent->mnt_type, "iso9660") == 0) 111 | add_it = 1; 112 | else if (strcmp(ent->mnt_type, "udf") == 0) 113 | add_it = 1; 114 | 115 | /* !!! FIXME: these might pick up floppy drives, right? */ 116 | else if (strcmp(ent->mnt_type, "auto") == 0) 117 | add_it = 1; 118 | else if (strcmp(ent->mnt_type, "supermount") == 0) 119 | add_it = 1; 120 | 121 | /* add other mount types here */ 122 | 123 | if (add_it) 124 | cb(data, ent->mnt_dir); 125 | } /* while */ 126 | 127 | endmntent(mounts); 128 | 129 | #elif (defined PHYSFS_HAVE_SYS_MNTTAB_H) 130 | FILE *mounts = fopen(MNTTAB, "r"); 131 | struct mnttab ent; 132 | 133 | BAIL_IF(mounts == NULL, PHYSFS_ERR_IO, /*return void*/); 134 | while (getmntent(mounts, &ent) == 0) 135 | { 136 | int add_it = 0; 137 | if (strcmp(ent.mnt_fstype, "hsfs") == 0) 138 | add_it = 1; 139 | 140 | /* add other mount types here */ 141 | 142 | if (add_it) 143 | cb(data, ent.mnt_mountp); 144 | } /* while */ 145 | 146 | fclose(mounts); 147 | #endif 148 | } /* __PHYSFS_platformDetectAvailableCDs */ 149 | 150 | 151 | /* 152 | * See where program (bin) resides in the $PATH specified by (envr). 153 | * returns a copy of the first element in envr that contains it, or NULL 154 | * if it doesn't exist or there were other problems. PHYSFS_SetError() is 155 | * called if we have a problem. 156 | * 157 | * (envr) will be scribbled over, and you are expected to allocator.Free() the 158 | * return value when you're done with it. 159 | */ 160 | static char *findBinaryInPath(const char *bin, char *envr) 161 | { 162 | size_t alloc_size = 0; 163 | char *exe = NULL; 164 | char *start = envr; 165 | char *ptr; 166 | 167 | assert(bin != NULL); 168 | assert(envr != NULL); 169 | 170 | do 171 | { 172 | size_t size; 173 | size_t binlen; 174 | 175 | ptr = strchr(start, ':'); /* find next $PATH separator. */ 176 | if (ptr) 177 | *ptr = '\0'; 178 | 179 | binlen = strlen(bin); 180 | size = strlen(start) + binlen + 2; 181 | if (size >= alloc_size) 182 | { 183 | char *x = (char *) allocator.Realloc(exe, size); 184 | if (!x) 185 | { 186 | if (exe != NULL) 187 | allocator.Free(exe); 188 | BAIL(PHYSFS_ERR_OUT_OF_MEMORY, NULL); 189 | } /* if */ 190 | 191 | alloc_size = size; 192 | exe = x; 193 | } /* if */ 194 | 195 | /* build full binary path... */ 196 | strcpy(exe, start); 197 | if ((exe[0] == '\0') || (exe[strlen(exe) - 1] != '/')) 198 | strcat(exe, "/"); 199 | strcat(exe, bin); 200 | 201 | if (access(exe, X_OK) == 0) /* Exists as executable? We're done. */ 202 | { 203 | exe[(size - binlen) - 1] = '\0'; /* chop off filename, leave '/' */ 204 | return exe; 205 | } /* if */ 206 | 207 | start = ptr + 1; /* start points to beginning of next element. */ 208 | } while (ptr != NULL); 209 | 210 | if (exe != NULL) 211 | allocator.Free(exe); 212 | 213 | return NULL; /* doesn't exist in path. */ 214 | } /* findBinaryInPath */ 215 | 216 | 217 | static char *readSymLink(const char *path) 218 | { 219 | ssize_t len = 64; 220 | ssize_t rc = -1; 221 | char *retval = NULL; 222 | 223 | while (1) 224 | { 225 | char *ptr = (char *) allocator.Realloc(retval, (size_t) len); 226 | if (ptr == NULL) 227 | break; /* out of memory. */ 228 | retval = ptr; 229 | 230 | rc = readlink(path, retval, len); 231 | if (rc == -1) 232 | break; /* not a symlink, i/o error, etc. */ 233 | 234 | else if (rc < len) 235 | { 236 | retval[rc] = '\0'; /* readlink doesn't null-terminate. */ 237 | return retval; /* we're good to go. */ 238 | } /* else if */ 239 | 240 | len *= 2; /* grow buffer, try again. */ 241 | } /* while */ 242 | 243 | if (retval != NULL) 244 | allocator.Free(retval); 245 | return NULL; 246 | } /* readSymLink */ 247 | 248 | 249 | char *__PHYSFS_platformCalcBaseDir(const char *argv0) 250 | { 251 | char *retval = NULL; 252 | const char *envr = NULL; 253 | 254 | /* Try to avoid using argv0 unless forced to. Try system-specific stuff. */ 255 | 256 | #if defined(PHYSFS_PLATFORM_FREEBSD) 257 | { 258 | char fullpath[PATH_MAX]; 259 | size_t buflen = sizeof (fullpath); 260 | int mib[4] = { CTL_KERN, KERN_PROC, KERN_PROC_PATHNAME, -1 }; 261 | if (sysctl(mib, 4, fullpath, &buflen, NULL, 0) != -1) 262 | retval = __PHYSFS_strdup(fullpath); 263 | } 264 | #elif defined(PHYSFS_PLATFORM_SOLARIS) 265 | { 266 | const char *path = getexecname(); 267 | if ((path != NULL) && (path[0] == '/')) /* must be absolute path... */ 268 | retval = __PHYSFS_strdup(path); 269 | } 270 | #endif 271 | 272 | /* If there's a Linux-like /proc filesystem, you can get the full path to 273 | * the current process from a symlink in there. 274 | */ 275 | 276 | if (!retval && (access("/proc", F_OK) == 0)) 277 | { 278 | retval = readSymLink("/proc/self/exe"); 279 | if (!retval) retval = readSymLink("/proc/curproc/file"); 280 | if (!retval) retval = readSymLink("/proc/curproc/exe"); 281 | if (retval == NULL) 282 | { 283 | /* older kernels don't have /proc/self ... try PID version... */ 284 | const unsigned long long pid = (unsigned long long) getpid(); 285 | char path[64]; 286 | const int rc = (int) snprintf(path,sizeof(path),"/proc/%llu/exe",pid); 287 | if ( (rc > 0) && (rc < sizeof(path)) ) 288 | retval = readSymLink(path); 289 | } /* if */ 290 | } /* if */ 291 | 292 | if (retval != NULL) /* chop off filename. */ 293 | { 294 | char *ptr = strrchr(retval, '/'); 295 | if (ptr != NULL) 296 | *(ptr+1) = '\0'; 297 | else /* shouldn't happen, but just in case... */ 298 | { 299 | allocator.Free(retval); 300 | retval = NULL; 301 | } /* else */ 302 | } /* if */ 303 | 304 | /* No /proc/self/exe, etc, but we have an argv[0] we can parse? */ 305 | if ((retval == NULL) && (argv0 != NULL)) 306 | { 307 | /* fast path: default behaviour can handle this. */ 308 | if (strchr(argv0, '/') != NULL) 309 | return NULL; /* higher level parses out real path from argv0. */ 310 | 311 | /* If there's no dirsep on argv0, then look through $PATH for it. */ 312 | envr = getenv("PATH"); 313 | if (envr != NULL) 314 | { 315 | char *path = (char *) __PHYSFS_smallAlloc(strlen(envr) + 1); 316 | BAIL_IF(!path, PHYSFS_ERR_OUT_OF_MEMORY, NULL); 317 | strcpy(path, envr); 318 | retval = findBinaryInPath(argv0, path); 319 | __PHYSFS_smallFree(path); 320 | } /* if */ 321 | } /* if */ 322 | 323 | if (retval != NULL) 324 | { 325 | /* try to shrink buffer... */ 326 | char *ptr = (char *) allocator.Realloc(retval, strlen(retval) + 1); 327 | if (ptr != NULL) 328 | retval = ptr; /* oh well if it failed. */ 329 | } /* if */ 330 | 331 | return retval; 332 | } /* __PHYSFS_platformCalcBaseDir */ 333 | 334 | 335 | char *__PHYSFS_platformCalcPrefDir(const char *org, const char *app) 336 | { 337 | /* 338 | * We use XDG's base directory spec, even if you're not on Linux. 339 | * This isn't strictly correct, but the results are relatively sane 340 | * in any case. 341 | * 342 | * https://standards.freedesktop.org/basedir-spec/basedir-spec-latest.html 343 | */ 344 | const char *envr = getenv("XDG_DATA_HOME"); 345 | const char *append = "/"; 346 | char *retval = NULL; 347 | size_t len = 0; 348 | 349 | if (!envr) 350 | { 351 | /* You end up with "$HOME/.local/share/Game Name 2" */ 352 | envr = __PHYSFS_getUserDir(); 353 | BAIL_IF_ERRPASS(!envr, NULL); /* oh well. */ 354 | append = ".local/share/"; 355 | } /* if */ 356 | 357 | len = strlen(envr) + strlen(append) + strlen(app) + 2; 358 | retval = (char *) allocator.Malloc(len); 359 | BAIL_IF(!retval, PHYSFS_ERR_OUT_OF_MEMORY, NULL); 360 | snprintf(retval, len, "%s%s%s/", envr, append, app); 361 | return retval; 362 | } /* __PHYSFS_platformCalcPrefDir */ 363 | 364 | #endif /* PHYSFS_PLATFORM_UNIX */ 365 | 366 | /* end of physfs_platform_unix.c ... */ 367 | 368 | -------------------------------------------------------------------------------- /src/physfs_platform_winrt.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Windows Runtime (WinRT) support routines for PhysicsFS. 3 | * 4 | * Please see the file LICENSE.txt in the source's root directory. 5 | * 6 | * This file originally written by Martin "T-Bone" Ahrnbom, but was mostly 7 | * merged into physfs_platform_windows.c by Ryan C. Gordon (so please harass 8 | * Ryan about bugs and not Martin). 9 | */ 10 | 11 | /* (There used to be instructions on how to make a WinRT project, but at 12 | this point, either CMake will do it for you or you should just drop 13 | PhysicsFS's sources into your existing project. --ryan.) */ 14 | 15 | #define __PHYSICSFS_INTERNAL__ 16 | #include "physfs_platforms.h" 17 | 18 | #ifdef PHYSFS_PLATFORM_WINRT 19 | 20 | #if defined(_MSC_VER) && !defined(_CRT_SECURE_NO_WARNINGS) 21 | #define _CRT_SECURE_NO_WARNINGS 1 22 | #endif 23 | #include 24 | 25 | #include "physfs_internal.h" 26 | 27 | const void *__PHYSFS_winrtCalcBaseDir(void) 28 | { 29 | return Windows::ApplicationModel::Package::Current->InstalledLocation->Path->Data(); 30 | } /* __PHYSFS_winrtCalcBaseDir */ 31 | 32 | const void *__PHYSFS_winrtCalcPrefDir(void) 33 | { 34 | return Windows::Storage::ApplicationData::Current->LocalFolder->Path->Data(); 35 | } /* __PHYSFS_winrtCalcBaseDir */ 36 | 37 | 38 | #endif /* PHYSFS_PLATFORM_WINRT */ 39 | 40 | /* end of physfs_platform_winrt.cpp ... */ 41 | 42 | -------------------------------------------------------------------------------- /src/physfs_platforms.h: -------------------------------------------------------------------------------- 1 | #ifndef _INCL_PHYSFS_PLATFORMS 2 | #define _INCL_PHYSFS_PLATFORMS 3 | 4 | #ifndef __PHYSICSFS_INTERNAL__ 5 | #error Do not include this header from your applications. 6 | #endif 7 | 8 | /* 9 | * These only define the platforms to determine which files in the platforms 10 | * directory should be compiled. For example, technically BeOS can be called 11 | * a "unix" system, but since it doesn't use unix.c, we don't define 12 | * PHYSFS_PLATFORM_UNIX on that system. 13 | */ 14 | 15 | #if (defined __HAIKU__) 16 | # define PHYSFS_PLATFORM_HAIKU 1 17 | # define PHYSFS_PLATFORM_POSIX 1 18 | #elif ((defined __BEOS__) || (defined __beos__)) 19 | # error BeOS support was dropped since PhysicsFS 2.1. Sorry. Try Haiku! 20 | #elif (defined _WIN32_WCE) || (defined _WIN64_WCE) 21 | # error PocketPC support was dropped since PhysicsFS 2.1. Sorry. Try WinRT! 22 | #elif (defined(_MSC_VER) && (_MSC_VER >= 1700) && !_USING_V110_SDK71_) /* _MSC_VER==1700 for MSVC 2012 */ 23 | # include 24 | # define PHYSFS_PLATFORM_WINDOWS 1 25 | # if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_APP) && !WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) 26 | # define PHYSFS_NO_CDROM_SUPPORT 1 27 | # define PHYSFS_PLATFORM_WINRT 1 28 | # endif 29 | #elif (((defined _WIN32) || (defined _WIN64)) && (!defined __CYGWIN__)) 30 | # define PHYSFS_PLATFORM_WINDOWS 1 31 | #elif defined(__OS2__) || defined(OS2) 32 | # define PHYSFS_PLATFORM_OS2 1 33 | #elif ((defined __MACH__) && (defined __APPLE__)) 34 | /* To check if iOS or not, we need to include this file */ 35 | # include 36 | # if ((TARGET_IPHONE_SIMULATOR) || (TARGET_OS_IPHONE)) 37 | # define PHYSFS_NO_CDROM_SUPPORT 1 38 | # endif 39 | # define PHYSFS_PLATFORM_APPLE 1 40 | # define PHYSFS_PLATFORM_POSIX 1 41 | #elif defined(macintosh) 42 | # error Classic Mac OS support was dropped from PhysicsFS 2.0. Move to OS X. 43 | #elif defined(__ANDROID__) 44 | # define PHYSFS_PLATFORM_LINUX 1 45 | # define PHYSFS_PLATFORM_ANDROID 1 46 | # define PHYSFS_PLATFORM_POSIX 1 47 | # define PHYSFS_NO_CDROM_SUPPORT 1 48 | #elif defined(__linux) 49 | # define PHYSFS_PLATFORM_LINUX 1 50 | # define PHYSFS_PLATFORM_UNIX 1 51 | # define PHYSFS_PLATFORM_POSIX 1 52 | #elif defined(__sun) || defined(sun) 53 | # define PHYSFS_PLATFORM_SOLARIS 1 54 | # define PHYSFS_PLATFORM_UNIX 1 55 | # define PHYSFS_PLATFORM_POSIX 1 56 | #elif defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__DragonFly__) 57 | # define PHYSFS_PLATFORM_FREEBSD 1 58 | # define PHYSFS_PLATFORM_BSD 1 59 | # define PHYSFS_PLATFORM_UNIX 1 60 | # define PHYSFS_PLATFORM_POSIX 1 61 | #elif defined(__NetBSD__) || defined(__OpenBSD__) || defined(__bsdi__) 62 | # define PHYSFS_PLATFORM_BSD 1 63 | # define PHYSFS_PLATFORM_UNIX 1 64 | # define PHYSFS_PLATFORM_POSIX 1 65 | #elif defined(__EMSCRIPTEN__) 66 | # define PHYSFS_NO_CDROM_SUPPORT 1 67 | # define PHYSFS_PLATFORM_UNIX 1 68 | # define PHYSFS_PLATFORM_POSIX 1 69 | #elif defined(__QNX__) 70 | # define PHYSFS_PLATFORM_QNX 1 71 | # define PHYSFS_PLATFORM_POSIX 1 72 | #elif defined(unix) || defined(__unix__) 73 | # define PHYSFS_PLATFORM_UNIX 1 74 | # define PHYSFS_PLATFORM_POSIX 1 75 | #else 76 | # error Unknown platform. 77 | #endif 78 | 79 | #endif /* include-once blocker. */ 80 | 81 | --------------------------------------------------------------------------------