This commit is contained in:
Vladislav Khorev 2018-09-23 22:01:30 +05:00
commit 1c5b99942f
59 changed files with 32460 additions and 0 deletions

24
.gitignore vendored Executable file
View File

@ -0,0 +1,24 @@
# ignore dot files/directories
.*
!.gitignore
*.autosave
*.pyc
*.user
*~
Thumbs.db
tags
tegra/
bin/
*.sdf
*.opensdf
*.obj
*.stamp
*.depend
*.rule
*.tmp
*/debug
*.suo
*.log
*.tlog
build

300
CMakeLists.txt Executable file
View File

@ -0,0 +1,300 @@
# PhysicsFS; a portable, flexible file i/o abstraction.
#
# Please see the file LICENSE.txt in the source's root directory.
# The CMake project file is meant to get this compiling on all sorts of
# platforms quickly, and serve as the way Unix platforms and Linux distros
# package up official builds, but you don't _need_ to use this; we have
# built PhysicsFS to (hopefully) be able to drop into your project and
# compile, using preprocessor checks for platform-specific bits instead of
# testing in here.
cmake_minimum_required(VERSION 2.8.4)
project(PhysicsFS)
set(PHYSFS_VERSION 3.0.1)
# Increment this if/when we break backwards compatibility.
set(PHYSFS_SOVERSION 1)
# I hate that they define "WIN32" ... we're about to move to Win64...I hope!
if(WIN32 AND NOT WINDOWS)
set(WINDOWS TRUE)
endif()
include_directories(./src)
if(APPLE)
set(OTHER_LDFLAGS ${OTHER_LDFLAGS} "-framework IOKit -framework Foundation")
set(PHYSFS_M_SRCS src/physfs_platform_apple.m)
endif()
if(CMAKE_COMPILER_IS_GNUCC)
# Don't use -rpath.
set(CMAKE_SKIP_RPATH ON CACHE BOOL "Skip RPATH" FORCE)
endif()
if(CMAKE_C_COMPILER_ID STREQUAL "SunPro")
add_definitions(-erroff=E_EMPTY_TRANSLATION_UNIT)
add_definitions(-xldscope=hidden)
endif()
if(HAIKU)
# We add this explicitly, since we don't want CMake to think this
# is a C++ project unless we're on Haiku.
set(PHYSFS_CPP_SRCS src/physfs_platform_haiku.cpp)
find_library(BE_LIBRARY be)
find_library(ROOT_LIBRARY root)
set(OPTIONAL_LIBRARY_LIBS ${OPTIONAL_LIBRARY_LIBS} ${BE_LIBRARY} ${ROOT_LIBRARY})
endif()
if(CMAKE_SYSTEM_NAME STREQUAL "WindowsPhone" OR CMAKE_SYSTEM_NAME STREQUAL "WindowsStore")
set(WINRT TRUE)
endif()
if(WINRT)
set(PHYSFS_CPP_SRCS src/physfs_platform_winrt.cpp)
endif()
if(UNIX AND NOT WINDOWS AND NOT APPLE) # (MingW and such might be UNIX _and_ WINDOWS!)
find_library(PTHREAD_LIBRARY pthread)
if(PTHREAD_LIBRARY)
set(OPTIONAL_LIBRARY_LIBS ${OPTIONAL_LIBRARY_LIBS} ${PTHREAD_LIBRARY})
endif()
endif()
# Almost everything is "compiled" here, but things that don't apply to the
# build are #ifdef'd out. This is to make it easy to embed PhysicsFS into
# another project or bring up a new build system: just compile all the source
# code and #define the things you want.
set(PHYSFS_SRCS
src/physfs.c
src/physfs_byteorder.c
src/physfs_unicode.c
src/physfs_platform_posix.c
src/physfs_platform_unix.c
src/physfs_platform_windows.c
src/physfs_platform_os2.c
src/physfs_platform_qnx.c
src/physfs_archiver_dir.c
src/physfs_archiver_unpacked.c
src/physfs_archiver_grp.c
src/physfs_archiver_hog.c
src/physfs_archiver_7z.c
src/physfs_archiver_mvl.c
src/physfs_archiver_qpak.c
src/physfs_archiver_wad.c
src/physfs_archiver_zip.c
src/physfs_archiver_slb.c
src/physfs_archiver_iso9660.c
src/physfs_archiver_vdf.c
${PHYSFS_CPP_SRCS}
${PHYSFS_M_SRCS}
)
# Archivers ...
# These are (mostly) on by default now, so these options are only useful for
# disabling them.
option(PHYSFS_ARCHIVE_ZIP "Enable ZIP support" TRUE)
if(NOT PHYSFS_ARCHIVE_ZIP)
add_definitions(-DPHYSFS_SUPPORTS_ZIP=0)
endif()
option(PHYSFS_ARCHIVE_7Z "Enable 7zip support" TRUE)
if(NOT PHYSFS_ARCHIVE_7Z)
add_definitions(-DPHYSFS_SUPPORTS_7Z=0)
endif()
option(PHYSFS_ARCHIVE_GRP "Enable Build Engine GRP support" TRUE)
if(NOT PHYSFS_ARCHIVE_GRP)
add_definitions(-DPHYSFS_SUPPORTS_GRP=0)
endif()
option(PHYSFS_ARCHIVE_WAD "Enable Doom WAD support" TRUE)
if(NOT PHYSFS_ARCHIVE_WAD)
add_definitions(-DPHYSFS_SUPPORTS_WAD=0)
endif()
option(PHYSFS_ARCHIVE_HOG "Enable Descent I/II HOG support" TRUE)
if(NOT PHYSFS_ARCHIVE_HOG)
add_definitions(-DPHYSFS_SUPPORTS_HOG=0)
endif()
option(PHYSFS_ARCHIVE_MVL "Enable Descent I/II MVL support" TRUE)
if(NOT PHYSFS_ARCHIVE_MVL)
add_definitions(-DPHYSFS_SUPPORTS_MVL=0)
endif()
option(PHYSFS_ARCHIVE_QPAK "Enable Quake I/II QPAK support" TRUE)
if(NOT PHYSFS_ARCHIVE_QPAK)
add_definitions(-DPHYSFS_SUPPORTS_QPAK=0)
endif()
option(PHYSFS_ARCHIVE_SLB "Enable I-War / Independence War SLB support" TRUE)
if(NOT PHYSFS_ARCHIVE_SLB)
add_definitions(-DPHYSFS_SUPPORTS_SLB=0)
endif()
option(PHYSFS_ARCHIVE_ISO9660 "Enable ISO9660 support" TRUE)
if(NOT PHYSFS_ARCHIVE_ISO9660)
add_definitions(-DPHYSFS_SUPPORTS_ISO9660=0)
endif()
option(PHYSFS_ARCHIVE_VDF "Enable Gothic I/II VDF archive support" TRUE)
if(NOT PHYSFS_ARCHIVE_VDF)
add_definitions(-DPHYSFS_SUPPORTS_VDF=0)
endif()
option(PHYSFS_BUILD_STATIC "Build static library" TRUE)
if(PHYSFS_BUILD_STATIC)
add_library(physfs-static STATIC ${PHYSFS_SRCS})
# Don't rename this on Windows, since DLLs will also produce an import
# library named "physfs.lib" which would conflict; Unix tend to like the
# same library name with a different extension for static libs, but
# Windows can just have a separate name.
if(NOT MSVC)
set_target_properties(physfs-static PROPERTIES OUTPUT_NAME "physfs")
endif()
if(WINRT)
# Ignore LNK4264 warnings; we don't author any WinRT components, just consume them, so we're okay in a static library.
set_target_properties(physfs-static PROPERTIES VS_WINRT_COMPONENT True)
set_target_properties(physfs-static PROPERTIES STATIC_LIBRARY_FLAGS "/ignore:4264")
endif()
set(PHYSFS_LIB_TARGET physfs-static)
set(PHYSFS_INSTALL_TARGETS ${PHYSFS_INSTALL_TARGETS} ";physfs-static")
endif()
option(PHYSFS_BUILD_SHARED "Build shared library" TRUE)
if(PHYSFS_BUILD_SHARED)
add_library(physfs SHARED ${PHYSFS_SRCS})
set_target_properties(physfs PROPERTIES MACOSX_RPATH 1)
set_target_properties(physfs PROPERTIES VERSION ${PHYSFS_VERSION})
set_target_properties(physfs PROPERTIES SOVERSION ${PHYSFS_SOVERSION})
if(WINRT)
set_target_properties(physfs PROPERTIES VS_WINRT_COMPONENT True)
endif()
target_link_libraries(physfs ${OPTIONAL_LIBRARY_LIBS} ${OTHER_LDFLAGS})
set(PHYSFS_LIB_TARGET physfs)
set(PHYSFS_INSTALL_TARGETS ${PHYSFS_INSTALL_TARGETS} ";physfs")
endif()
if(NOT PHYSFS_BUILD_SHARED AND NOT PHYSFS_BUILD_STATIC)
message(FATAL "Both shared and static libraries are disabled!")
endif()
# CMake FAQ says I need this...
if(PHYSFS_BUILD_SHARED AND PHYSFS_BUILD_STATIC AND NOT WINDOWS)
set_target_properties(physfs PROPERTIES CLEAN_DIRECT_OUTPUT 1)
set_target_properties(physfs-static PROPERTIES CLEAN_DIRECT_OUTPUT 1)
endif()
option(PHYSFS_BUILD_TEST "Build stdio test program." TRUE)
mark_as_advanced(PHYSFS_BUILD_TEST)
if(PHYSFS_BUILD_TEST)
find_path(READLINE_H readline/readline.h)
find_path(HISTORY_H readline/history.h)
if(READLINE_H AND HISTORY_H)
find_library(CURSES_LIBRARY NAMES curses ncurses)
set(CMAKE_REQUIRED_LIBRARIES ${CURSES_LIBRARY})
find_library(READLINE_LIBRARY readline)
if(READLINE_LIBRARY)
set(HAVE_SYSTEM_READLINE TRUE)
set(TEST_PHYSFS_LIBS ${TEST_PHYSFS_LIBS} ${READLINE_LIBRARY} ${CURSES_LIBRARY})
include_directories(SYSTEM ${READLINE_H} ${HISTORY_H})
add_definitions(-DPHYSFS_HAVE_READLINE=1)
endif()
endif()
add_executable(test_physfs test/test_physfs.c)
target_link_libraries(test_physfs ${PHYSFS_LIB_TARGET} ${TEST_PHYSFS_LIBS} ${OTHER_LDFLAGS})
set(PHYSFS_INSTALL_TARGETS ${PHYSFS_INSTALL_TARGETS} ";test_physfs")
endif()
install(TARGETS ${PHYSFS_INSTALL_TARGETS}
RUNTIME DESTINATION bin
LIBRARY DESTINATION lib${LIB_SUFFIX}
ARCHIVE DESTINATION lib${LIB_SUFFIX})
install(FILES src/physfs.h DESTINATION include)
find_package(Doxygen)
if(DOXYGEN_FOUND)
set(PHYSFS_OUTPUT_DOXYFILE "${CMAKE_CURRENT_BINARY_DIR}/Doxyfile")
configure_file(
"${CMAKE_CURRENT_SOURCE_DIR}/docs/Doxyfile"
"${PHYSFS_OUTPUT_DOXYFILE}"
COPYONLY
)
file(APPEND "${PHYSFS_OUTPUT_DOXYFILE}" "\n\n# Below auto-generated by cmake...\n\n")
file(APPEND "${PHYSFS_OUTPUT_DOXYFILE}" "PROJECT_NUMBER = \"${PHYSFS_VERSION}\"\n")
file(APPEND "${PHYSFS_OUTPUT_DOXYFILE}" "OUTPUT_DIRECTORY = \"${CMAKE_CURRENT_BINARY_DIR}/docs\"\n")
file(APPEND "${PHYSFS_OUTPUT_DOXYFILE}" "\n# End auto-generated section.\n\n")
add_custom_target(
docs
${DOXYGEN_EXECUTABLE} "${PHYSFS_OUTPUT_DOXYFILE}"
WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}"
COMMENT "Building documentation in 'docs' directory..."
)
else()
message(STATUS "Doxygen not found. You won't be able to build documentation.")
endif()
if(UNIX)
set(PHYSFS_TARBALL "${CMAKE_CURRENT_SOURCE_DIR}/../physfs-${PHYSFS_VERSION}.tar.bz2")
add_custom_target(
dist
hg archive -t tbz2 "${PHYSFS_TARBALL}"
WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}"
COMMENT "Building source tarball '${PHYSFS_TARBALL}'..."
)
add_custom_target(
uninstall
"${CMAKE_CURRENT_SOURCE_DIR}/extras/uninstall.sh"
WORKING_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}"
COMMENT "Uninstall the project..."
)
endif()
if(NOT MSVC)
configure_file(
"extras/physfs.pc.in"
"extras/physfs.pc"
@ONLY
)
install(
FILES "${CMAKE_CURRENT_BINARY_DIR}/extras/physfs.pc"
DESTINATION "lib${LIB_SUFFIX}/pkgconfig"
)
endif()
macro(message_bool_option _NAME _VALUE)
if(${_VALUE})
message(STATUS " ${_NAME}: enabled")
else()
message(STATUS " ${_NAME}: disabled")
endif()
endmacro()
message(STATUS "PhysicsFS will build with the following options:")
message_bool_option("ZIP support" PHYSFS_ARCHIVE_ZIP)
message_bool_option("7zip support" PHYSFS_ARCHIVE_7Z)
message_bool_option("GRP support" PHYSFS_ARCHIVE_GRP)
message_bool_option("WAD support" PHYSFS_ARCHIVE_WAD)
message_bool_option("HOG support" PHYSFS_ARCHIVE_HOG)
message_bool_option("MVL support" PHYSFS_ARCHIVE_MVL)
message_bool_option("QPAK support" PHYSFS_ARCHIVE_QPAK)
message_bool_option("SLB support" PHYSFS_ARCHIVE_SLB)
message_bool_option("VDF support" PHYSFS_ARCHIVE_VDF)
message_bool_option("ISO9660 support" PHYSFS_ARCHIVE_ISO9660)
message_bool_option("Build static library" PHYSFS_BUILD_STATIC)
message_bool_option("Build shared library" PHYSFS_BUILD_SHARED)
message_bool_option("Build stdio test program" PHYSFS_BUILD_TEST)
if(PHYSFS_BUILD_TEST)
message_bool_option(" Use readline in test program" HAVE_SYSTEM_READLINE)
endif()
# end of CMakeLists.txt ...

23
LICENSE.txt Executable file
View File

@ -0,0 +1,23 @@
Copyright (c) 2001-2017 Ryan C. Gordon and others.
This software is provided 'as-is', without any express or implied warranty.
In no event will the authors be held liable for any damages arising from
the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software in a
product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
Ryan C. Gordon <icculus@icculus.org>

9
README.txt Executable file
View File

@ -0,0 +1,9 @@
PhysicsFS; a portable, flexible file i/o abstraction.
https://icculus.org/physfs/
Please see the docs directory for documentation.
Please see LICENSE.txt for licensing information.

11
docs/CHANGELOG.txt Executable file
View File

@ -0,0 +1,11 @@
The changelog is no longer maintained by hand. It made sense to have a single
timeline when we were using CVS, but modern revision control tools make this
redundant, at best.
If you want a list of changes, updated in real time, just point your web
browser here:
https://hg.icculus.org/icculus/physfs/

171
docs/CREDITS.txt Executable file
View File

@ -0,0 +1,171 @@
Maintainer and general codemonkey:
Ryan C. Gordon
Tons of win32 help:
Adam Gates
More win32 hacking:
Gregory S. Read
Fixes for missing current working directories,
PHYSFS_setSaneConfig() improvements,
other bugfixes:
David Hedbor
Darwin support:
Patrick Stein
configure fixes,
RPM specfile:
Edward Rudd
GetLastModTime API,
other stuff:
John R. Hall
Various support, fixes and suggestions:
Alexander Pipelka
Russian translation,
QPAK archiver:
Ed Sinjiashvili
French translation:
Stéphane Peter
Debian package support:
Colin Bayer
"abs-file.h" in "extras" dir:
Adam D. Moss
WinCE port and other Win32 patches:
Corona688
German translation:
Michael Renner
Apple Project Builder support,
Mac OS X improvements:
Eric Wing
iPhone support:
Christian Gmeiner
WinRT support:
Martin Ahrnbom
HOG archiver,
MVL archiver:
Bradley Bell
MIX archiver:
Sebastian Steinhauer
Bug fixes:
Tolga Dalman
Initial PHYSFS_mount() work:
Philip D. Bober
Brazillian Portuguese translation:
Danny Angelo Carminati Grein
Spanish translation:
Pedro J. Pérez
MacOS Classic fixes,
MPW support,
bug fixes:
Chris Taylor
Mingw support,
General bug fixes:
Matze Braun
Haiku support:
scott mc
Bug fixes:
Jörg Walter
Bug fixes:
Olivier Boudeville
Bug fixes:
Henk Boom
Build system fixes:
Marc Kleine-Budde
Windows .rc file,
7zip/lzma archiver:
Dennis Schridde
OS/2 updates:
Dave Yeo
Bug fixes:
Patrice Mandin
PHYSFS_stat() API:
Christoph Nelles
Indy Sams
ISO9660 archiver:
Christoph Nelles
Bug fixes:
Steven Fuller
Bug fixes:
Tolga Dalman
Bug fixes:
Frank Becker
Bug fixes:
Norfanin
Bug fixes:
Evgeny Podjachev
Haiku fixes:
Chris Roberts
SLB archiver:
Aleksi Nurmi
Bug fixes:
Dmitry Marakasov
Bug fixes:
Andreas Karlsson
Bug fixes:
Michael Bacon
Bug fixes:
Xian Nox
Bug fixes:
Reto Schneider
pkg-config support:
Jonas Kulla
Bug fixes,
VDF archiver:
Francesco Bertolaccini
CMake fixes:
Tobias Markus
Bug fixes,
Rémi Verschelde
Other stuff:
Your name here! Patches go to icculus@icculus.org ...
/* end of CREDITS.txt ... */

1081
docs/Doxyfile Executable file

File diff suppressed because it is too large Load Diff

173
docs/INSTALL.txt Executable file
View File

@ -0,0 +1,173 @@
The latest PhysicsFS information and releases can be found at:
https://icculus.org/physfs/
Building is (ahem) very easy.
ALL PLATFORMS:
Please read the text file LICENSE.txt in the root of the source tree.
The license is extremely liberal, even to closed-source, commercial
applications.
If you've got Doxygen (http://www.doxygen.org/) installed, you can run it
without any command line arguments in the root of the source tree to generate
the API reference (or build the "docs" target from your build system). This
is optional. You can browse the API docs online here:
https://icculus.org/physfs/docs/
BUILD IT WITH YOUR OWN PROGRAM:
If you don't care about formal packaging: just add everything in the "src"
directory to whatever you use to build your program and compile it along with
everything else, and you're done. It should compile with any reasonable
ANSI C compiler, should build cleanly even with excessive compiler warnings
enabled, needs no extra configuration, and allows static linking.
WinRT and Haiku need C++ compilers for their system APIs, but if you aren't on
these platforms and don't have a C++ compiler, don't build the .cpp files.
Likewise: Apple platforms (macOS, iOS, etc) need an Objective-C compiler, but
if you aren't on these platforms and don't have a Objective-C compiler, don't
build the .m file. Everything you need is in the .c sources.
If this all worked for your specific project, you can stop reading now.
UNIX:
You will need CMake (https://www.cmake.org/) 2.4 or later installed.
Make a directory, wherever you like. This will be your build directory.
Chdir to your build directory. Run "cmake /where/i/unpacked/physfs" to
generate Makefiles. You can then run "ccmake ." and customize the build,
but the defaults are probably okay. You can have CMake generate KDevelop
or Ninja project files or whatever, if you prefer these.
Run "make". PhysicsFS will now build.
As root, run "make install".
If you get sick of the library, run "make uninstall" as root
and it will remove all traces of the library from the system paths.
Once you are satisfied, you can delete the build directory.
Primary Unix development is done with GNU/Linux, but PhysicsFS is known to
work out of the box with several flavors of Unix. It it doesn't work, patches
to get it running can be sent to icculus@icculus.org.
Windows:
If building with Cygwin, mingw32, MSYS, or something else that uses the GNU
toolchain, follow the Unix instructions, above.
If you want to use Visual Studio, nmake, or the Platform SDK, you will need
CMake (https://www.cmake.org/) 2.4 or later installed. Point CMake at the
CMakeLists.txt file in the root of the source directory and hit the
"Configure" button. After telling it what type of compiler you are targeting
(Borland, Visual Studio, etc), CMake will process for while and then give you
a list of options you can change (what archivers you want to support, etc).
If you aren't sure, the defaults are probably fine. Hit the "Configure"
button again, then "OK" once configuration has completed with options that
match your liking. Now project files for your favorite programming
environment will be generated for you in the directory you specified.
Go there and use them to build PhysicsFS.
PhysicsFS will only link directly against system libraries that have existed
since Windows NT 3.51. If there's a newer API we want to use, we try to
dynamically load it at runtime and fallback to a reasonable behaviour when
we can't find it. Note that Windows 98 and later _should_
work if you use the Microsoft Layer for Unicode (UNICOWS.DLL) to provide
some missing system APIs, but this is no longer tested as of PhysicsFS 2.1.0.
PhysicsFS 2.0.x is known to work with Windows 95 without UNICOWS.DLL.
PhysicsFS works on 32-bit and 64-bit Windows. There is no 16-bit Windows
support at all. Windows RT is covered below.
Windows RT:
Windows RT (Windows Phone, Windows Store, UWP) 8.0 and later are supported.
Make sure you include both physfs_platform_windows.c _and_
physfs_platform_winrt.cpp in your build, and that the C++ file has
"Consume Windows Runtime Extension" set to "Yes" in its Visual Studio
properties (from the command line, you want to compile this file with the
"/ZW" compiler switch). CMake can, in theory, generate a project file for
WinRT if you pick a recent Visual Studio target, choose manual cross-compile
options, and set the system name to "WindowsPhone" or "WindowsStore" and the
correct OS version (8.0 or later).
PocketPC/WindowsCE:
Support for PocketPC was removed in PhysicsFS 2.1.0. This was known to work
in the 1.0 releases, but wasn't tested in 2.0 and later. PhysicsFS should
work on modern Windows Phones (see "Windows RT" section).
macOS:
You will need CMake (https://www.cmake.org/) 2.4 or later installed.
You can either generate a Unix makefile with CMake, or generate an Xcode
project, whichever makes you more comfortable.
PowerPC and Intel Macs should both be supported.
MAC OS 8/9 ("Mac OS Classic"):
Classic Mac OS support has been dropped in PhysicsFS 2.0. Apple hasn't updated
pre-OSX versions in more than a decade at this point, none of the hardware
they've shipped will boot it for almost as many years, and finding
developer tools for it is becoming almost impossible. As the switch to Intel
hardware has removed the "Classic" emulation environment, it was time to
remove support from PhysicsFS. That being said, the PhysicsFS 1.0 branch can
still target back to Mac OS 8.5, so you can use that if you need support for
this legacy OS. We still very much support modern macOS, though: see above.
Emscripten:
Use the "Unix" instructions, above. You can install the Emscripten SDK and use
the extras/buildbot-emscripten.sh script to automate this for you.
BeOS, Zeta, YellowTab:
BeOS support was dropped in PhysicsFS 2.1.0. Consider installing Haiku, which
we still support.
Haiku:
Use the "Unix" instructions, above.
OS/2:
OS/2 is known to work with OpenWatcom and GCC-based compilers. I couldn't get
an OS/2 port of CMake to generate OpenWatcom project files (although it should
be able to do that in theory), it should be able to do Unix Makefiles with
GCC. It might be easier to just compile PhysicsFS along with the rest of
your project on this platform.
OTHER PLATFORMS:
Many Unix-like platforms might "just work" with CMake. Some of these platforms
are known to have worked at one time, but have not been heavily tested, if
tested at all. PhysicsFS is, as far as we know, 64-bit and byteorder clean,
and is known to compile on several compilers across many platforms. To
implement a new platform or archiver, please read the heavily-commented
physfs_internal.h and look at the physfs_platform_* and physfs_archiver_*
source files for examples.
--ryan. (icculus@icculus.org)

View File

@ -0,0 +1,18 @@
The API documentation is readable in a few ways:
- Read physfs.h; it's _heavily_ documented and the primary source of reference
documentation for the library.
- Run Doxygen over the header, which produces nicer-to-browse documentation in
HTML, LaTeX, manpage, etc formats. This is done for you if Doxygen is
installed and you build the "docs" target in whatever project files CMake
generated for you.
- Too much trouble? We generated the HTML reference for you, online here:
https://icculus.org/physfs/docs/
- We would love well-written tutorials for the latest version of PhysicsFS!
If you write one, we would love to list it here. Drop me a line about it:
icculus@icculus.org ... Thanks!
--ryan.

62
docs/TODO.txt Executable file
View File

@ -0,0 +1,62 @@
Stuff that needs to be done and wishlist:
These are in no particular order.
Some might be dupes, some might be done already, some might be bad ideas.
From https://icculus.org/pipermail/physfs/2009-March/000698.html ...
- Write support for various archives. I haven't decided how to do this yet,
but I'd like to.
- Add an API to expose a file's extended attributes to the application?
- Deprecate PHYSFS_setSaneConfig(). It really should have been in the extras
directory.
- Clean up the sources to match my ever-changing coding style. :)
From https://icculus.org/pipermail/physfs/2010-January/000826.html ...
- Lua bindings
From https://icculus.org/pipermail/physfs/2010-January/000833.html ...
- SWIG bindings
From old TODO.txt...
- Other archivers: perhaps tar(.gz|.bz2), RPM, ARJ, etc. These are less
important, since streaming archives aren't of much value to games (which
is why zipfiles are king: random access), but it could have uses for, say,
an installer/updater.
- Do symlinks in zip archiver work when they point to dirs?
- Enable more warnings?
- Use __cdecl in physfs.h?
- Look for FIXMEs (many marked with "!!!" in comments).
- fscanf and fprintf support in extras dir.
- Sanity check byte order at runtime.
- Memory locking?
- General code audit.
- Multiple write dirs with mount points?
Other stuff I thought of...
- moar asserts!
- constify!
- Does iPhone work?
- Fix CMake vs Doxygen.
- Doxygen replacement? (manpages suck.)
- Fix coding standards to match.
- See if we can ditch some #include lines...
- LZMA support in zip archiver?
- bzip2 support in zip archiver?
- Reduce the BAIL and GOTO macro use. A lot of these don't add anything.
- Change the term "search path" to something less confusing.
Probably other stuff. Requests and recommendations are welcome.
// end of TODO.txt ...

11
extras/README-CSharp.txt Executable file
View File

@ -0,0 +1,11 @@
There used to be C# bindings in this directory, but they have been
unmaintained for many years.
Instead, there is a more modern PhysicsFS wrapper for .NET available.
You can find it at https://github.com/frabert/SharpPhysFS
Thanks to Francesco Bertolaccini for his efforts on that project!
--ryan.

165
extras/abs-file.h Executable file
View File

@ -0,0 +1,165 @@
/*
* stdio/physfs abstraction layer 2003-04-02
*
* Adam D. Moss <adam@gimp.org> <aspirin@icculus.org>
*
* These wrapper macros and functions are designed to allow a program
* to perform file I/O with identical semantics and syntax regardless
* of whether PhysicsFS is being used or not.
*/
#ifndef _ABS_FILE_H
#define _ABS_FILE_H
/*
PLEASE NOTE: This license applies to abs-file.h ONLY (to make it clear that
you may embed this wrapper code within commercial software); PhysicsFS itself
is (at the time of writing) released under a different license with
additional restrictions.
Copyright (C) 2002-2003 Adam D. Moss (the "Author"). All Rights Reserved.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is fur-
nished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FIT-
NESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHOR BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CON-
NECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
Except as contained in this notice, the name of the Author of the
Software shall not be used in advertising or otherwise to promote the sale,
use or other dealings in this Software without prior written authorization
from the Author.
*/
#include <stdlib.h>
#include <stdio.h>
/*
* API:
*
* Macro/function use like stdio equivalent...
* -------------- ----------------------------
* MY_FILETYPE FILE
* MY_OPEN_FOR_READ fopen(..., "rb")
* MY_READ fread(...)
* MY_CLOSE fclose(...)
* MY_GETC fgetc(...)
* MY_GETS fgets(...)
* MY_ATEOF feof(...)
* MY_TELL ftell(...)
* MY_SEEK fseek(..., SEEK_SET)
* MY_REWIND rewind(...)
* MY_SETBUFFER (not a standard for stdio, does nothing there)
*/
/*
* Important DEFINEs:
* It is important to define these consistantly across the various
* compilation modules of your program if you wish to exchange file
* handles between them.
*
* USE_PHYSFS: Define USE_PHYSFS if PhysicsFS is being used; note that if
* you do intend to use PhysicsFS then you will still need to initialize
* PhysicsFS yourself and set up its search-paths.
*
* Optional DEFINEs:
*
* PHYSFS_DEFAULT_READ_BUFFER <bytes>: If set then abs-file.h sets the
* PhysicsFS buffer size to this value whenever you open a file. You
* may over-ride this on a per-filehandle basis by using the
* MY_SETBUFFER() macro (which simply does nothing when not using
* PhysicsFS). If you have not defined this value explicitly then
* abs-file.h will default to the same default buffer size as used by
* stdio if it can be determined, or 8192 bytes otherwise.
*/
#ifndef PHYSFS_DEFAULT_READ_BUFFER
#ifdef BUFSIZ
#define PHYSFS_DEFAULT_READ_BUFFER BUFSIZ
#else
#define PHYSFS_DEFAULT_READ_BUFFER 8192
#endif
#endif
#ifdef USE_PHYSFS
#include <physfs.h>
#define MY_FILETYPE PHYSFS_File
#define MY_SETBUFFER(fp,size) PHYSFS_setBuffer(fp,size)
#define MY_READ(p,s,n,fp) PHYSFS_read(fp,p,s,n)
#if PHYSFS_DEFAULT_READ_BUFFER
static MY_FILETYPE* MY_OPEN_FOR_READ(const char *const filename)
{
MY_FILETYPE *const file = PHYSFS_openRead(filename);
if (file) {
MY_SETBUFFER(file, PHYSFS_DEFAULT_READ_BUFFER);
}
return file;
}
#else
#define MY_OPEN_FOR_READ(fn) PHYSFS_openRead(fn)
#endif
static int MY_GETC(MY_FILETYPE *const fp) {
unsigned char c;
/*if (PHYSFS_eof(fp)) {
return EOF;
}
MY_READ(&c, 1, 1, fp);*/
if (MY_READ(&c, 1, 1, fp) != 1) {
return EOF;
}
return c;
}
static char * MY_GETS(char * const str, const int size,
MY_FILETYPE *const fp) {
int i = 0;
int c;
do {
if (i == size-1) {
break;
}
c = MY_GETC(fp);
if (c == EOF) {
break;
}
str[i++] = c;
} while (c != '\0' &&
c != -1 &&
c != '\n');
str[i] = '\0';
if (i == 0) {
return NULL;
}
return str;
}
#define MY_CLOSE(fp) PHYSFS_close(fp)
#define MY_ATEOF(fp) PHYSFS_eof(fp)
#define MY_TELL(fp) PHYSFS_tell(fp)
#define MY_SEEK(fp,o) PHYSFS_seek(fp,o)
#define MY_REWIND(fp) MY_SEEK(fp,0)
#else
#define MY_FILETYPE FILE
#define MY_READ(p,s,n,fp) fread(p,s,n,fp)
#define MY_OPEN_FOR_READ(n) fopen(n, "rb")
#define MY_GETC(fp) fgetc(fp)
#define MY_GETS(str,size,fp) fgets(str,size,fp)
#define MY_CLOSE(fp) fclose(fp)
#define MY_ATEOF(fp) feof(fp)
#define MY_TELL(fp) ftell(fp)
#define MY_SEEK(fp,o) fseek(fp,o, SEEK_SET)
#define MY_REWIND(fp) rewind(fp)
/*static void MY_SETBUFFER(const MY_FILETYPE *const file, const int num) { }*/
#define MY_SETBUFFER(fp,size)
#endif
#endif

101
extras/buildbot-checker.sh Executable file
View File

@ -0,0 +1,101 @@
#!/bin/bash
# This is a script used by some Buildbot buildslaves to push the project
# through Clang's static analyzer and prepare the output to be uploaded
# back to the buildmaster. You might find it useful too.
# Install Clang (you already have it on Mac OS X, apt-get install clang
# on Ubuntu, etc),
# or download checker at http://clang-analyzer.llvm.org/ and unpack it in
# /usr/local ... update CHECKERDIR as appropriate.
FINALDIR="$1"
CHECKERDIR="/usr/local/checker-279"
if [ ! -d "$CHECKERDIR" ]; then
echo "$CHECKERDIR not found. Trying /usr/share/clang ..." 1>&2
CHECKERDIR="/usr/share/clang/scan-build"
fi
if [ ! -d "$CHECKERDIR" ]; then
echo "$CHECKERDIR not found. Giving up." 1>&2
exit 1
fi
if [ -z "$MAKE" ]; then
OSTYPE=`uname -s`
if [ "$OSTYPE" == "Linux" ]; then
NCPU=`cat /proc/cpuinfo |grep vendor_id |wc -l`
let NCPU=$NCPU+1
elif [ "$OSTYPE" = "Darwin" ]; then
NCPU=`sysctl -n hw.ncpu`
elif [ "$OSTYPE" = "SunOS" ]; then
NCPU=`/usr/sbin/psrinfo |wc -l |sed -e 's/^ *//g;s/ *$//g'`
else
NCPU=1
fi
if [ -z "$NCPU" ]; then
NCPU=1
elif [ "$NCPU" = "0" ]; then
NCPU=1
fi
MAKE="make -j$NCPU"
fi
echo "\$MAKE is '$MAKE'"
MAKECMD="$MAKE"
unset MAKE # prevent warnings about jobserver mode.
set -x
set -e
cd `dirname "$0"`
cd ..
rm -rf checker-buildbot analysis
if [ ! -z "$FINALDIR" ]; then
rm -rf "$FINALDIR"
fi
mkdir checker-buildbot
cd checker-buildbot
# We turn off deprecated declarations, because we don't care about these warnings during static analysis.
# The -Wno-liblto is new since our checker-279 upgrade, I think; checker otherwise warns "libLTO.dylib relative to clang installed dir not found"
# You might want to do this for CMake-backed builds instead...
PATH="$CHECKERDIR/bin:$PATH" scan-build -o analysis cmake -Wno-dev -DPHYSFS_BUILD_SHARED=False -DCMAKE_BUILD_TYPE=Debug -DCMAKE_C_FLAGS="-Wno-deprecated-declarations" -DCMAKE_EXE_LINKER_FLAGS="-Wno-liblto" ..
# ...or run configure without the scan-build wrapper...
#CC="$CHECKERDIR/libexec/ccc-analyzer" CFLAGS="-O0 -Wno-deprecated-declarations" LDFLAGS="-Wno-liblto" ../configure --enable-assertions=enabled
rm -rf analysis
PATH="$CHECKERDIR/bin:$PATH" scan-build -o analysis $MAKECMD
if [ `ls -A analysis |wc -l` == 0 ] ; then
mkdir analysis/zarro
echo '<html><head><title>Zarro boogs</title></head><body>Static analysis: no issues to report.</body></html>' >analysis/zarro/index.html
fi
mv analysis/* ../analysis
rmdir analysis # Make sure this is empty.
cd ..
chmod -R a+r analysis
chmod -R go-w analysis
find analysis -type d -exec chmod a+x {} \;
if [ -x /usr/bin/xattr ]; then find analysis -exec /usr/bin/xattr -d com.apple.quarantine {} \; 2>/dev/null ; fi
if [ ! -z "$FINALDIR" ]; then
mv analysis "$FINALDIR"
else
FINALDIR=analysis
fi
rm -rf checker-buildbot
echo "Done. Final output is in '$FINALDIR' ..."
# end of checker-buildbot.sh ...

78
extras/buildbot-emscripten.sh Executable file
View File

@ -0,0 +1,78 @@
#!/bin/bash
if [ -z "$SDKDIR" ]; then
SDKDIR="/emsdk_portable"
fi
ENVSCRIPT="$SDKDIR/emsdk_env.sh"
if [ ! -f "$ENVSCRIPT" ]; then
echo "ERROR: This script expects the Emscripten SDK to be in '$SDKDIR'." 1>&2
echo "ERROR: Set the \$SDKDIR environment variable to override this." 1>&2
exit 1
fi
TARBALL="$1"
if [ -z $1 ]; then
TARBALL=physfs-emscripten.tar.xz
fi
cd `dirname "$0"`
cd ..
PHYSFSBASE=`pwd`
if [ -z "$MAKE" ]; then
OSTYPE=`uname -s`
if [ "$OSTYPE" == "Linux" ]; then
NCPU=`cat /proc/cpuinfo |grep vendor_id |wc -l`
let NCPU=$NCPU+1
elif [ "$OSTYPE" = "Darwin" ]; then
NCPU=`sysctl -n hw.ncpu`
elif [ "$OSTYPE" = "SunOS" ]; then
NCPU=`/usr/sbin/psrinfo |wc -l |sed -e 's/^ *//g;s/ *$//g'`
else
NCPU=1
fi
if [ -z "$NCPU" ]; then
NCPU=1
elif [ "$NCPU" = "0" ]; then
NCPU=1
fi
MAKE="make -j$NCPU"
fi
echo "\$MAKE is '$MAKE'"
MAKECMD="$MAKE"
unset MAKE # prevent warnings about jobserver mode.
echo "Setting up Emscripten SDK environment..."
source "$ENVSCRIPT"
echo "Setting up..."
cd "$PHYSFSBASE"
rm -rf buildbot
mkdir buildbot
cd buildbot
echo "Configuring..."
emcmake cmake -G "Unix Makefiles" -DPHYSFS_BUILD_SHARED=False -DCMAKE_BUILD_TYPE=MinSizeRel .. || exit $?
echo "Building..."
emmake $MAKECMD || exit $?
set -e
rm -rf "$TARBALL" physfs-emscripten
mkdir -p physfs-emscripten
echo "Archiving to '$TARBALL' ..."
cp ../src/physfs.h libphysfs.a physfs-emscripten
chmod -R a+r physfs-emscripten
chmod a+x physfs-emscripten
chmod -R go-w physfs-emscripten
tar -cJvvf "$TARBALL" physfs-emscripten
echo "Done."
exit 0
# end of emscripten-buildbot.sh ...

87
extras/buildbot-os2.sh Executable file
View File

@ -0,0 +1,87 @@
#!/bin/bash
# This is used by the buildbot to cross-compile for OS/2 from Linux, using
# OpenWatcom. In an ideal world, we wouldn't need this, but we need a few
# things to do this "properly" ...
#
# - OS/2 running as a VMware guest on the build machine
# - Buildbot-worker running on that OS/2 guest
# - CMake for OS/2 that...
# - ... has Open Watcom compiler support and...
# - ... a Watcom WMake project generator.
#
# Some of these things are doable (there is a CMake port for OS/2, you can
# use GCC with it), but since OpenWatcom will just target OS/2 on Linux just
# like it could on OS/2, it's easier and more efficient to just have a
# buildbot script compile it here.
#
# Note that we just blast all the C files through the wcc386 compiler and
# skip CMake entirely. We should fix this at some point.
set -e
ZIPFILE="$1"
if [ -z $ZIPFILE ]; then
ZIPFILE=physfs-os2.zip
fi
export WATCOM="/usr/local/share/watcom"
export PATH="$PATH:$WATCOM/binl"
CFLAGS="-i=\"$WATCOM/h;$WATCOM/h/os2;../src\" -wx -d0 -otexan -6r -zq -bt=os2 -fo=.obj -mf"
WLIBFLAGS="-b -c -n -q -p=512"
cd `dirname "$0"`
cd ..
mkdir -p buildbot
cd buildbot
rm -f test_physfs.obj
OKAY="1"
for src in ../src/*.c ; do
echo wcc386 $src $CFLAGS
wcc386 $src $CFLAGS || OKAY="0"
done
if [ "$OKAY" == "1" ]; then
echo wlib $WLIBFLAGS physfs.lib *.obj
wlib $WLIBFLAGS physfs.lib *.obj || OKAY="0"
fi
echo wcc386 ../test/test_physfs.c $CFLAGS
wcc386 ../test/test_physfs.c $CFLAGS || OKAY="0"
if [ "$OKAY" == "1" ]; then
LDFLAGS="name test_physfs d all sys os2v2 op m libr physfs op q op symf FIL test_physfs.obj"
echo wlink $LDFLAGS
wlink $LDFLAGS || OKAY="0"
fi
if [ "$OKAY" == "1" ]; then
F=`file test_physfs.exe`
echo "$F"
if [ "$F" != 'test_physfs.exe: MS-DOS executable, LX for OS/2 (console) i80386' ]; then
echo 1>&2 "ERROR: final binary doesn't appear to be OS/2 executable."
OKAY=0
fi
fi
if [ "$OKAY" == "1" ]; then
echo 1>&2 "Build succeeded."
set -e
rm -rf "$ZIPFILE" physfs-os2
mkdir -p physfs-os2
echo "Zipping to '$ZIPFILE' ..."
cp ../src/physfs.h physfs.lib physfs-os2
chmod -R a+r physfs-os2
chmod a+x physfs-os2
chmod -R go-w physfs-os2
zip -9r "$ZIPFILE" physfs-os2
echo "Done."
exit 0
else
echo 1>&2 "Build failed."
exit 1
fi

72
extras/buildbot-raspberrypi.sh Executable file
View File

@ -0,0 +1,72 @@
#!/bin/bash
# This is the script physfs-buildbot.icculus.org uses to cross-compile
# PhysicsFS from x86 Linux to Raspberry Pi. This script was originally from
# Simple Directmedia Layer ( https://www.libsdl.org/ ).
# The final tarball can be unpacked in the root directory of a RPi,
# so the PhysicsFS install lands in /usr/local. Run ldconfig, and then
# you should be able to build and run PhysicsFS-based software on your
# Pi. Standard configure scripts should be able to find PhysicsFS and
# build against it.
TARBALL="$1"
if [ -z $1 ]; then
TARBALL=physfs-raspberrypi.tar.xz
fi
OSTYPE=`uname -s`
if [ "$OSTYPE" != "Linux" ]; then
# !!! FIXME
echo "This only works on x86 or x64-64 Linux at the moment." 1>&2
exit 1
fi
if [ "x$MAKE" == "x" ]; then
NCPU=`cat /proc/cpuinfo |grep vendor_id |wc -l`
let NCPU=$NCPU+1
MAKE="make -j$NCPU"
fi
echo "\$MAKE is '$MAKE'"
MAKECMD="$MAKE"
unset MAKE # prevent warnings about jobserver mode.
BUILDBOTDIR="raspberrypi-buildbot"
PARENTDIR="$PWD"
set -e
set -x
rm -f $TARBALL
rm -rf $BUILDBOTDIR
mkdir -p $BUILDBOTDIR
pushd $BUILDBOTDIR
SYSROOT="/opt/rpi-sysroot"
cmake -G "Unix Makefiles" \
-DCMAKE_C_COMPILER="/opt/rpi-tools/arm-bcm2708/gcc-linaro-arm-linux-gnueabihf-raspbian/bin/arm-linux-gnueabihf-gcc" \
-DCMAKE_BUILD_TYPE=MinSizeRel \
-DCMAKE_SYSROOT="$SYSROOT" \
-DCMAKE_FIND_ROOT_PATH="$SYSROOT" \
-DCMAKE_SYSTEM_NAME="Linux" \
-DCMAKE_SYSTEM_VERSION=1 \
-DCMAKE_FIND_ROOT_PATH_MODE_PROGRAM=NEVER \
-DCMAKE_FIND_ROOT_PATH_MODE_LIBRARY=ONLY \
-DCMAKE_FIND_ROOT_PATH_MODE_INCLUDE=ONLY \
..
$MAKECMD
rm -rf "$TARBALL" physfs-raspberrypi
mkdir -p physfs-raspberrypi
echo "Archiving to '$TARBALL' ..."
cp -a ../src/physfs.h libphysfs.a libphysfs.so* physfs-raspberrypi
chmod -R a+r physfs-raspberrypi
chmod a+x physfs-raspberrypi physfs-raspberrypi/*.so*
chmod -R go-w physfs-raspberrypi
tar -cJvvf "$TARBALL" physfs-raspberrypi
set +x
echo "Done."

1414
extras/casefolding.txt Executable file

File diff suppressed because it is too large Load Diff

239
extras/globbing.c Executable file
View File

@ -0,0 +1,239 @@
/** \file globbing.c */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <assert.h>
#include "globbing.h"
/**
* Please see globbing.h for details.
*
* License: this code is public domain. I make no warranty that it is useful,
* correct, harmless, or environmentally safe.
*
* This particular file may be used however you like, including copying it
* verbatim into a closed-source project, exploiting it commercially, and
* removing any trace of my name from the source (although I hope you won't
* do that). I welcome enhancements and corrections to this file, but I do
* not require you to send me patches if you make changes. This code has
* NO WARRANTY.
*
* Unless otherwise stated, the rest of PhysicsFS falls under the zlib license.
* Please see the file LICENSE.txt in the source's root directory.
*
* \author Ryan C. Gordon.
*/
static int matchesPattern(const char *fname, const char *wildcard,
int caseSensitive)
{
char x, y;
const char *fnameptr = fname;
const char *wildptr = wildcard;
while ((*wildptr) && (*fnameptr))
{
y = *wildptr;
if (y == '*')
{
do
{
wildptr++; /* skip multiple '*' in a row... */
} while (*wildptr == '*');
y = (caseSensitive) ? *wildptr : (char) tolower(*wildptr);
while (1)
{
x = (caseSensitive) ? *fnameptr : (char) tolower(*fnameptr);
if ((!x) || (x == y))
break;
else
fnameptr++;
} /* while */
} /* if */
else if (y == '?')
{
wildptr++;
fnameptr++;
} /* else if */
else
{
if (caseSensitive)
x = *fnameptr;
else
{
x = tolower(*fnameptr);
y = tolower(y);
} /* if */
wildptr++;
fnameptr++;
if (x != y)
return 0;
} /* else */
} /* while */
while (*wildptr == '*')
wildptr++;
return (*fnameptr == *wildptr);
} /* matchesPattern */
typedef struct
{
const PHYSFS_Allocator *allocator;
const char *wildcard;
int caseSensitive;
PHYSFS_EnumFilesCallback callback;
void *origData;
} WildcardCallbackData;
/*
* This callback sits between the enumerator and the enduser callback,
* filtering out files that don't match the wildcard pattern.
*/
static void wildcardCallback(void *_d, const char *origdir, const char *fname)
{
const WildcardCallbackData *data = (const WildcardCallbackData *) _d;
if (matchesPattern(fname, data->wildcard, data->caseSensitive))
data->callback(data->origData, origdir, fname);
} /* wildcardCallback */
void PHYSFSEXT_enumerateFilesCallbackWildcard(const char *dir,
const char *wildcard,
int caseSensitive,
PHYSFS_EnumFilesCallback c,
void *d)
{
WildcardCallbackData data;
data.allocator = PHYSFS_getAllocator();
data.wildcard = wildcard;
data.caseSensitive = caseSensitive;
data.callback = c;
data.origData = d;
PHYSFS_enumerateFilesCallback(dir, wildcardCallback, &data);
} /* PHYSFSEXT_enumerateFilesCallbackWildcard */
void PHYSFSEXT_freeEnumeration(char **list)
{
const PHYSFS_Allocator *allocator = PHYSFS_getAllocator();
int i;
if (list != NULL)
{
for (i = 0; list[i] != NULL; i++)
allocator->Free(list[i]);
allocator->Free(list);
} /* if */
} /* PHYSFSEXT_freeEnumeration */
char **PHYSFSEXT_enumerateFilesWildcard(const char *dir, const char *wildcard,
int caseSensitive)
{
const PHYSFS_Allocator *allocator = PHYSFS_getAllocator();
char **list = PHYSFS_enumerateFiles(dir);
char **retval = NULL;
int totalmatches = 0;
int matches = 0;
char **i;
for (i = list; *i != NULL; i++)
{
#if 0
printf("matchesPattern: '%s' vs '%s' (%s) ... %s\n", *i, wildcard,
caseSensitive ? "case" : "nocase",
matchesPattern(*i, wildcard, caseSensitive) ? "true" : "false");
#endif
if (matchesPattern(*i, wildcard, caseSensitive))
totalmatches++;
} /* for */
retval = (char **) allocator->Malloc(sizeof (char *) * (totalmatches+1));
if (retval != NULL)
{
for (i = list; ((matches < totalmatches) && (*i != NULL)); i++)
{
if (matchesPattern(*i, wildcard, caseSensitive))
{
retval[matches] = (char *) allocator->Malloc(strlen(*i) + 1);
if (retval[matches] == NULL)
{
while (matches--)
allocator->Free(retval[matches]);
allocator->Free(retval);
retval = NULL;
break;
} /* if */
strcpy(retval[matches], *i);
matches++;
} /* if */
} /* for */
if (retval != NULL)
{
assert(totalmatches == matches);
retval[matches] = NULL;
} /* if */
} /* if */
PHYSFS_freeList(list);
return retval;
} /* PHYSFSEXT_enumerateFilesWildcard */
#ifdef TEST_PHYSFSEXT_ENUMERATEFILESWILDCARD
int main(int argc, char **argv)
{
int rc;
char **flist;
char **i;
if (argc != 3)
{
printf("USAGE: %s <pattern> <caseSen>\n"
" where <caseSen> is 1 or 0.\n", argv[0]);
return 1;
} /* if */
if (!PHYSFS_init(argv[0]))
{
fprintf(stderr, "PHYSFS_init(): %s\n", PHYSFS_getLastError());
return 1;
} /* if */
if (!PHYSFS_addToSearchPath(".", 1))
{
fprintf(stderr, "PHYSFS_addToSearchPath(): %s\n", PHYSFS_getLastError());
PHYSFS_deinit();
return 1;
} /* if */
flist = PHYSFSEXT_enumerateFilesWildcard("/", argv[1], atoi(argv[2]));
rc = 0;
for (i = flist; *i; i++)
{
printf("%s\n", *i);
rc++;
} /* for */
printf("\n total %d files.\n\n", rc);
PHYSFSEXT_freeEnumeration(flist);
PHYSFS_deinit();
return 0;
} /* main */
#endif
/* end of globbing.c ... */

165
extras/globbing.h Executable file
View File

@ -0,0 +1,165 @@
#ifndef INCL_PHYSFSEXT_GLOBBING_H
#define INCL_PHYSFSEXT_GLOBBING_H
/** \file globbing.h */
#include "physfs.h"
/**
* \mainpage PhysicsFS globbing
*
* This is an extension to PhysicsFS to let you search for files with basic
* wildcard matching, regardless of what sort of filesystem or archive they
* reside in. It does this by enumerating directories as needed and manually
* locating matching entries.
*
* Usage: Set up PhysicsFS as you normally would, then use
* PHYSFSEXT_enumerateFilesWildcard() when enumerating files. This is just
* like PHYSFS_enumerateFiles(), but it returns a subset that matches your
* wildcard pattern. You must call PHYSFSEXT_freeEnumeration() on the results,
* just PHYSFS_enumerateFiles() would do with PHYSFS_freeList().
*
* License: this code is public domain. I make no warranty that it is useful,
* correct, harmless, or environmentally safe.
*
* This particular file may be used however you like, including copying it
* verbatim into a closed-source project, exploiting it commercially, and
* removing any trace of my name from the source (although I hope you won't
* do that). I welcome enhancements and corrections to this file, but I do
* not require you to send me patches if you make changes. This code has
* NO WARRANTY.
*
* Unless otherwise stated, the rest of PhysicsFS falls under the zlib license.
* Please see LICENSE.txt in the source's "docs" directory.
*
* \author Ryan C. Gordon.
*/
#ifdef __cplusplus
extern "C" {
#endif
/**
* \fn char **PHYSFS_enumerateFilesWildcard(const char *dir, const char *wildcard, int caseSensitive)
* \brief Get a file listing of a search path's directory, filtered with a wildcard pattern.
*
* Matching directories are interpolated. That is, if "C:\mydir" is in the
* search path and contains a directory "savegames" that contains "x.sav",
* "y.Sav", and "z.txt", and there is also a "C:\userdir" in the search path
* that has a "savegames" subdirectory with "w.sav", then the following code:
*
* \code
* char **rc = PHYSFS_enumerateFilesWildcard("savegames", "*.sav", 0);
* char **i;
*
* for (i = rc; *i != NULL; i++)
* printf(" * We've got [%s].\n", *i);
*
* PHYSFS_freeList(rc);
* \endcode
*
* ...will print:
*
* \verbatim
* We've got [x.sav].
* We've got [y.Sav].
* We've got [w.sav].\endverbatim
*
* Feel free to sort the list however you like. We only promise there will
* be no duplicates, but not what order the final list will come back in.
*
* Wildcard strings can use the '*' and '?' characters, currently.
* Matches can be case-insensitive if you pass a zero for argument 3.
*
* Don't forget to call PHYSFSEXT_freeEnumerator() with the return value from
* this function when you are done with it. As we use PhysicsFS's allocator
* for this list, you must free it before calling PHYSFS_deinit().
* Do not use PHYSFS_freeList() on the returned value!
*
* \param dir directory in platform-independent notation to enumerate.
* \param wildcard Wildcard pattern to use for filtering.
* \param caseSensitive Zero for case-insensitive matching,
* non-zero for case-sensitive.
* \return Null-terminated array of null-terminated strings.
*
* \sa PHYSFSEXT_freeEnumeration
*/
PHYSFS_DECL char **PHYSFSEXT_enumerateFilesWildcard(const char *dir,
const char *wildcard,
int caseSensitive);
/**
* \fn void PHYSFSEXT_freeEnumeration(char **list)
* \brief Free data returned by PHYSFSEXT_enumerateFilesWildcard
*
* Conceptually, this works like PHYSFS_freeList(), but is used with data
* returned by PHYSFSEXT_enumerateFilesWildcard() only. Be sure to call this
* on any returned data from that function before
*
* \param list Pointer previously returned by
* PHYSFSEXT_enumerateFilesWildcard(). It is safe to pass a
* NULL here.
*
* \sa PHYSFSEXT_enumerateFilesWildcard
*/
PHYSFS_DECL void PHYSFSEXT_freeEnumeration(char **list);
/**
* \fn void PHYSFSEXT_enumerateFilesCallbackWildcard(const char *dir, const char *wildcard, int caseSensitive, PHYSFS_EnumFilesCallback c, void *d);
* \brief Get a file listing of a search path's directory, filtered with a wildcard pattern, using an application-defined callback.
*
* This function is equivalent to PHYSFSEXT_enumerateFilesWildcard(). It
* reports file listings, filtered by a wildcard pattern.
*
* Unlike PHYSFS_enumerateFiles(), this function does not return an array.
* Rather, it calls a function specified by the application once per
* element of the search path:
*
* \code
*
* static void printDir(void *data, const char *origdir, const char *fname)
* {
* printf(" * We've got [%s] in [%s].\n", fname, origdir);
* }
*
* // ...
* PHYSFS_enumerateFilesCallbackWildcard("savegames","*.sav",0,printDir,NULL);
* \endcode
*
* Items sent to the callback are not guaranteed to be in any order whatsoever.
* There is no sorting done at this level, and if you need that, you should
* probably use PHYSFS_enumerateFilesWildcard() instead, which guarantees
* alphabetical sorting. This form reports whatever is discovered in each
* archive before moving on to the next. Even within one archive, we can't
* guarantee what order it will discover data. <em>Any sorting you find in
* these callbacks is just pure luck. Do not rely on it.</em> As this walks
* the entire list of archives, you may receive duplicate filenames.
*
* Wildcard strings can use the '*' and '?' characters, currently.
* Matches can be case-insensitive if you pass a zero for argument 3.
*
* \param dir Directory, in platform-independent notation, to enumerate.
* \param wildcard Wildcard pattern to use for filtering.
* \param caseSensitive Zero for case-insensitive matching,
* non-zero for case-sensitive.
* \param c Callback function to notify about search path elements.
* \param d Application-defined data passed to callback. Can be NULL.
*
* \sa PHYSFS_EnumFilesCallback
* \sa PHYSFS_enumerateFiles
*/
PHYSFS_DECL void PHYSFSEXT_enumerateFilesCallbackWildcard(const char *dir,
const char *wildcard,
int caseSensitive,
PHYSFS_EnumFilesCallback c,
void *d);
#ifdef __cplusplus
}
#endif
#endif /* include-once blocker. */
/* end of globbing.h ... */

199
extras/ignorecase.c Executable file
View File

@ -0,0 +1,199 @@
/** \file ignorecase.c */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include "physfs.h"
#include "ignorecase.h"
/**
* Please see ignorecase.h for details.
*
* License: this code is public domain. I make no warranty that it is useful,
* correct, harmless, or environmentally safe.
*
* This particular file may be used however you like, including copying it
* verbatim into a closed-source project, exploiting it commercially, and
* removing any trace of my name from the source (although I hope you won't
* do that). I welcome enhancements and corrections to this file, but I do
* not require you to send me patches if you make changes. This code has
* NO WARRANTY.
*
* Unless otherwise stated, the rest of PhysicsFS falls under the zlib license.
* Please see LICENSE.txt in the root of the source tree.
*
* \author Ryan C. Gordon.
*/
static int locateOneElement(char *buf)
{
char *ptr;
char **rc;
char **i;
if (PHYSFS_exists(buf))
return 1; /* quick rejection: exists in current case. */
ptr = strrchr(buf, '/'); /* find entry at end of path. */
if (ptr == NULL)
{
rc = PHYSFS_enumerateFiles("/");
ptr = buf;
} /* if */
else
{
*ptr = '\0';
rc = PHYSFS_enumerateFiles(buf);
*ptr = '/';
ptr++; /* point past dirsep to entry itself. */
} /* else */
for (i = rc; *i != NULL; i++)
{
if (PHYSFS_utf8stricmp(*i, ptr) == 0)
{
strcpy(ptr, *i); /* found a match. Overwrite with this case. */
PHYSFS_freeList(rc);
return 1;
} /* if */
} /* for */
/* no match at all... */
PHYSFS_freeList(rc);
return 0;
} /* locateOneElement */
int PHYSFSEXT_locateCorrectCase(char *buf)
{
int rc;
char *ptr;
while (*buf == '/') /* skip any '/' at start of string... */
buf++;
ptr = buf;
if (*ptr == '\0')
return 0; /* Uh...I guess that's success. */
while ( (ptr = strchr(ptr + 1, '/')) != NULL )
{
*ptr = '\0'; /* block this path section off */
rc = locateOneElement(buf);
*ptr = '/'; /* restore path separator */
if (!rc)
return -2; /* missing element in path. */
} /* while */
/* check final element... */
return locateOneElement(buf) ? 0 : -1;
} /* PHYSFSEXT_locateCorrectCase */
#ifdef TEST_PHYSFSEXT_LOCATECORRECTCASE
int main(int argc, char **argv)
{
int rc;
char buf[128];
PHYSFS_File *f;
if (!PHYSFS_init(argv[0]))
{
fprintf(stderr, "PHYSFS_init(): %s\n", PHYSFS_getLastError());
return 1;
} /* if */
if (!PHYSFS_addToSearchPath(".", 1))
{
fprintf(stderr, "PHYSFS_addToSearchPath(): %s\n", PHYSFS_getLastError());
PHYSFS_deinit();
return 1;
} /* if */
if (!PHYSFS_setWriteDir("."))
{
fprintf(stderr, "PHYSFS_setWriteDir(): %s\n", PHYSFS_getLastError());
PHYSFS_deinit();
return 1;
} /* if */
if (!PHYSFS_mkdir("/a/b/c"))
{
fprintf(stderr, "PHYSFS_mkdir(): %s\n", PHYSFS_getLastError());
PHYSFS_deinit();
return 1;
} /* if */
if (!PHYSFS_mkdir("/a/b/C"))
{
fprintf(stderr, "PHYSFS_mkdir(): %s\n", PHYSFS_getLastError());
PHYSFS_deinit();
return 1;
} /* if */
f = PHYSFS_openWrite("/a/b/c/x.txt");
PHYSFS_close(f);
if (f == NULL)
{
fprintf(stderr, "PHYSFS_openWrite(): %s\n", PHYSFS_getLastError());
PHYSFS_deinit();
return 1;
} /* if */
f = PHYSFS_openWrite("/a/b/C/X.txt");
PHYSFS_close(f);
if (f == NULL)
{
fprintf(stderr, "PHYSFS_openWrite(): %s\n", PHYSFS_getLastError());
PHYSFS_deinit();
return 1;
} /* if */
strcpy(buf, "/a/b/c/x.txt");
rc = PHYSFSEXT_locateCorrectCase(buf);
if ((rc != 0) || (strcmp(buf, "/a/b/c/x.txt") != 0))
printf("test 1 failed\n");
strcpy(buf, "/a/B/c/x.txt");
rc = PHYSFSEXT_locateCorrectCase(buf);
if ((rc != 0) || (strcmp(buf, "/a/b/c/x.txt") != 0))
printf("test 2 failed\n");
strcpy(buf, "/a/b/C/x.txt");
rc = PHYSFSEXT_locateCorrectCase(buf);
if ((rc != 0) || (strcmp(buf, "/a/b/C/X.txt") != 0))
printf("test 3 failed\n");
strcpy(buf, "/a/b/c/X.txt");
rc = PHYSFSEXT_locateCorrectCase(buf);
if ((rc != 0) || (strcmp(buf, "/a/b/c/x.txt") != 0))
printf("test 4 failed\n");
strcpy(buf, "/a/b/c/z.txt");
rc = PHYSFSEXT_locateCorrectCase(buf);
if ((rc != -1) || (strcmp(buf, "/a/b/c/z.txt") != 0))
printf("test 5 failed\n");
strcpy(buf, "/A/B/Z/z.txt");
rc = PHYSFSEXT_locateCorrectCase(buf);
if ((rc != -2) || (strcmp(buf, "/a/b/Z/z.txt") != 0))
printf("test 6 failed\n");
printf("Testing completed.\n");
printf(" If no errors were reported, you're good to go.\n");
PHYSFS_delete("/a/b/c/x.txt");
PHYSFS_delete("/a/b/C/X.txt");
PHYSFS_delete("/a/b/c");
PHYSFS_delete("/a/b/C");
PHYSFS_delete("/a/b");
PHYSFS_delete("/a");
PHYSFS_deinit();
return 0;
} /* main */
#endif
/* end of ignorecase.c ... */

86
extras/ignorecase.h Executable file
View File

@ -0,0 +1,86 @@
#ifndef INCL_PHYSFSEXT_IGNORECASE_H
#define INCL_PHYSFSEXT_IGNORECASE_H
/** \file ignorecase.h */
/**
* \mainpage PhysicsFS ignorecase
*
* This is an extension to PhysicsFS to let you handle files in a
* case-insensitive manner, regardless of what sort of filesystem or
* archive they reside in. It does this by enumerating directories as
* needed and manually locating matching entries.
*
* Please note that this brings with it some caveats:
* - On filesystems that are case-insensitive to start with, such as those
* used on Windows or MacOS, you are adding extra overhead.
* - On filesystems that are case-sensitive, you might select the wrong dir
* or file (which brings security considerations and potential bugs). This
* code favours exact case matches, but you will lose access to otherwise
* duplicate filenames, or you might go down a wrong directory tree, etc.
* In practice, this is rarely a problem, but you need to be aware of it.
* - This doesn't do _anything_ with the write directory; you're on your
* own for opening the right files for writing. You can sort of get around
* this by adding your write directory to the search path, but then the
* interpolated directory tree can screw you up even more.
*
* This code should be considered an aid for legacy code. New development
* shouldn't do things that require this aid in the first place. :)
*
* Usage: Set up PhysicsFS as you normally would, then use
* PHYSFSEXT_locateCorrectCase() to get a "correct" pathname to pass to
* functions like PHYSFS_openRead(), etc.
*
* License: this code is public domain. I make no warranty that it is useful,
* correct, harmless, or environmentally safe.
*
* This particular file may be used however you like, including copying it
* verbatim into a closed-source project, exploiting it commercially, and
* removing any trace of my name from the source (although I hope you won't
* do that). I welcome enhancements and corrections to this file, but I do
* not require you to send me patches if you make changes. This code has
* NO WARRANTY.
*
* Unless otherwise stated, the rest of PhysicsFS falls under the zlib license.
* Please see LICENSE.txt in the root of the source tree.
*
* \author Ryan C. Gordon.
*/
#ifdef __cplusplus
extern "C" {
#endif
/**
* \fn int PHYSFSEXT_locateCorrectCase(char *buf)
* \brief Find an existing filename with matching case.
*
* This function will look for a path/filename that matches the passed in
* buffer. Each element of the buffer's path is checked for a
* case-insensitive match. The buffer must specify a null-terminated string
* in platform-independent notation.
*
* Please note results may be skewed differently depending on whether symlinks
* are enabled or not.
*
* Each element of the buffer is overwritten with the actual case of an
* existing match. If there is no match, the search aborts and reports an
* error. Exact matches are favored over case-insensitive matches.
*
* THIS IS RISKY. Please do not use this function for anything but legacy code.
*
* \param buf Buffer with null-terminated string of path/file to locate.
* This buffer will be modified by this function.
* \return zero if match was found, -1 if the final element (the file itself)
* is missing, -2 if one of the parent directories is missing.
*/
int PHYSFSEXT_locateCorrectCase(char *buf);
#ifdef __cplusplus
}
#endif
#endif /* include-once blocker. */
/* end of ignorecase.h ... */

276
extras/makecasefoldhashtable.pl Executable file
View File

@ -0,0 +1,276 @@
#!/usr/bin/perl -w
use warnings;
use strict;
my $HASHBUCKETS1_16 = 256;
my $HASHBUCKETS1_32 = 16;
my $HASHBUCKETS2_16 = 16;
my $HASHBUCKETS3_16 = 4;
print <<__EOF__;
/*
* This file is part of PhysicsFS (https://icculus.org/physfs/)
*
* This data generated by physfs/extras/makecasefoldhashtable.pl ...
* Do not manually edit this file!
*
* Please see the file LICENSE.txt in the source's root directory.
*/
#ifndef _INCLUDE_PHYSFS_CASEFOLDING_H_
#define _INCLUDE_PHYSFS_CASEFOLDING_H_
#ifndef __PHYSICSFS_INTERNAL__
#error Do not include this header from your applications.
#endif
/* We build three simple hashmaps here: one that maps Unicode codepoints to
a one, two, or three lowercase codepoints. To retrieve this info: look at
case_fold_hashX, where X is 1, 2, or 3. Most foldable codepoints fold to one,
a few dozen fold to two, and a handful fold to three. If the codepoint isn't
in any of these hashes, it doesn't fold (no separate upper and lowercase).
Almost all these codepoints fit into 16 bits, so we hash them as such to save
memory. If a codepoint is > 0xFFFF, we have separate hashes for them,
since there are (currently) only about 120 of them and (currently) all of them
map to a single lowercase codepoint. */
typedef struct CaseFoldMapping1_32
{
PHYSFS_uint32 from;
PHYSFS_uint32 to0;
} CaseFoldMapping1_32;
typedef struct CaseFoldMapping1_16
{
PHYSFS_uint16 from;
PHYSFS_uint16 to0;
} CaseFoldMapping1_16;
typedef struct CaseFoldMapping2_16
{
PHYSFS_uint16 from;
PHYSFS_uint16 to0;
PHYSFS_uint16 to1;
} CaseFoldMapping2_16;
typedef struct CaseFoldMapping3_16
{
PHYSFS_uint16 from;
PHYSFS_uint16 to0;
PHYSFS_uint16 to1;
PHYSFS_uint16 to2;
} CaseFoldMapping3_16;
typedef struct CaseFoldHashBucket1_16
{
const CaseFoldMapping1_16 *list;
const PHYSFS_uint8 count;
} CaseFoldHashBucket1_16;
typedef struct CaseFoldHashBucket1_32
{
const CaseFoldMapping1_32 *list;
const PHYSFS_uint8 count;
} CaseFoldHashBucket1_32;
typedef struct CaseFoldHashBucket2_16
{
const CaseFoldMapping2_16 *list;
const PHYSFS_uint8 count;
} CaseFoldHashBucket2_16;
typedef struct CaseFoldHashBucket3_16
{
const CaseFoldMapping3_16 *list;
const PHYSFS_uint8 count;
} CaseFoldHashBucket3_16;
__EOF__
my @foldPairs1_16;
my @foldPairs2_16;
my @foldPairs3_16;
my @foldPairs1_32;
for (my $i = 0; $i < $HASHBUCKETS1_16; $i++) {
$foldPairs1_16[$i] = '';
}
for (my $i = 0; $i < $HASHBUCKETS1_32; $i++) {
$foldPairs1_32[$i] = '';
}
for (my $i = 0; $i < $HASHBUCKETS2_16; $i++) {
$foldPairs2_16[$i] = '';
}
for (my $i = 0; $i < $HASHBUCKETS3_16; $i++) {
$foldPairs3_16[$i] = '';
}
open(FH,'<','casefolding.txt') or die("failed to open casefolding.txt: $!\n");
while (<FH>) {
chomp;
# strip comments from textfile...
s/\#.*\Z//;
# strip whitespace...
s/\A\s+//;
s/\s+\Z//;
next if not /\A([a-fA-F0-9]+)\;\s*(.)\;\s*(.+)\;/;
my ($code, $status, $mapping) = ($1, $2, $3);
my $hexxed = hex($code);
#print("// code '$code' status '$status' mapping '$mapping'\n");
if (($status eq 'C') or ($status eq 'F')) {
my ($map1, $map2, $map3) = (undef, undef, undef);
$map1 = $1 if $mapping =~ s/\A([a-fA-F0-9]+)(\s*|\Z)//;
$map2 = $1 if $mapping =~ s/\A([a-fA-F0-9]+)(\s*|\Z)//;
$map3 = $1 if $mapping =~ s/\A([a-fA-F0-9]+)(\s*|\Z)//;
die("mapping space too small for '$code'\n") if ($mapping ne '');
die("problem parsing mapping for '$code'\n") if (not defined($map1));
if ($hexxed < 128) {
# Just ignore these, we'll handle the low-ASCII ones ourselves.
} elsif ($hexxed > 0xFFFF) {
# We just need to add the 32-bit 2 and/or 3 codepoint maps if this die()'s here.
die("Uhoh, a codepoint > 0xFFFF that folds to multiple codepoints! Fixme.") if defined($map2);
my $hashed = (($hexxed ^ ($hexxed >> 8)) & ($HASHBUCKETS1_32-1));
#print("// hexxed '$hexxed' hashed1 '$hashed'\n");
$foldPairs1_32[$hashed] .= " { 0x$code, 0x$map1 },\n";
} elsif (not defined($map2)) {
my $hashed = (($hexxed ^ ($hexxed >> 8)) & ($HASHBUCKETS1_16-1));
#print("// hexxed '$hexxed' hashed1 '$hashed'\n");
$foldPairs1_16[$hashed] .= " { 0x$code, 0x$map1 },\n";
} elsif (not defined($map3)) {
my $hashed = (($hexxed ^ ($hexxed >> 8)) & ($HASHBUCKETS2_16-1));
#print("// hexxed '$hexxed' hashed2 '$hashed'\n");
$foldPairs2_16[$hashed] .= " { 0x$code, 0x$map1, 0x$map2 },\n";
} else {
my $hashed = (($hexxed ^ ($hexxed >> 8)) & ($HASHBUCKETS3_16-1));
#print("// hexxed '$hexxed' hashed3 '$hashed'\n");
$foldPairs3_16[$hashed] .= " { 0x$code, 0x$map1, 0x$map2, 0x$map3 },\n";
}
}
}
close(FH);
for (my $i = 0; $i < $HASHBUCKETS1_16; $i++) {
$foldPairs1_16[$i] =~ s/,\n\Z//;
my $str = $foldPairs1_16[$i];
next if $str eq '';
my $num = '000' . $i;
$num =~ s/\A.*?(\d\d\d)\Z/$1/;
my $sym = "case_fold1_16_${num}";
print("static const CaseFoldMapping1_16 ${sym}[] = {\n$str\n};\n\n");
}
for (my $i = 0; $i < $HASHBUCKETS1_32; $i++) {
$foldPairs1_32[$i] =~ s/,\n\Z//;
my $str = $foldPairs1_32[$i];
next if $str eq '';
my $num = '000' . $i;
$num =~ s/\A.*?(\d\d\d)\Z/$1/;
my $sym = "case_fold1_32_${num}";
print("static const CaseFoldMapping1_32 ${sym}[] = {\n$str\n};\n\n");
}
for (my $i = 0; $i < $HASHBUCKETS2_16; $i++) {
$foldPairs2_16[$i] =~ s/,\n\Z//;
my $str = $foldPairs2_16[$i];
next if $str eq '';
my $num = '000' . $i;
$num =~ s/\A.*?(\d\d\d)\Z/$1/;
my $sym = "case_fold2_16_${num}";
print("static const CaseFoldMapping2_16 ${sym}[] = {\n$str\n};\n\n");
}
for (my $i = 0; $i < $HASHBUCKETS3_16; $i++) {
$foldPairs3_16[$i] =~ s/,\n\Z//;
my $str = $foldPairs3_16[$i];
next if $str eq '';
my $num = '000' . $i;
$num =~ s/\A.*?(\d\d\d)\Z/$1/;
my $sym = "case_fold3_16_${num}";
print("static const CaseFoldMapping3_16 ${sym}[] = {\n$str\n};\n\n");
}
print("static const CaseFoldHashBucket1_16 case_fold_hash1_16[] = {\n");
for (my $i = 0; $i < $HASHBUCKETS1_16; $i++) {
my $str = $foldPairs1_16[$i];
if ($str eq '') {
print(" { NULL, 0 },\n");
} else {
my $num = '000' . $i;
$num =~ s/\A.*?(\d\d\d)\Z/$1/;
my $sym = "case_fold1_16_${num}";
print(" { $sym, __PHYSFS_ARRAYLEN($sym) },\n");
}
}
print("};\n\n");
print("static const CaseFoldHashBucket1_32 case_fold_hash1_32[] = {\n");
for (my $i = 0; $i < $HASHBUCKETS1_32; $i++) {
my $str = $foldPairs1_32[$i];
if ($str eq '') {
print(" { NULL, 0 },\n");
} else {
my $num = '000' . $i;
$num =~ s/\A.*?(\d\d\d)\Z/$1/;
my $sym = "case_fold1_32_${num}";
print(" { $sym, __PHYSFS_ARRAYLEN($sym) },\n");
}
}
print("};\n\n");
print("static const CaseFoldHashBucket2_16 case_fold_hash2_16[] = {\n");
for (my $i = 0; $i < $HASHBUCKETS2_16; $i++) {
my $str = $foldPairs2_16[$i];
if ($str eq '') {
print(" { NULL, 0 },\n");
} else {
my $num = '000' . $i;
$num =~ s/\A.*?(\d\d\d)\Z/$1/;
my $sym = "case_fold2_16_${num}";
print(" { $sym, __PHYSFS_ARRAYLEN($sym) },\n");
}
}
print("};\n\n");
print("static const CaseFoldHashBucket3_16 case_fold_hash3_16[] = {\n");
for (my $i = 0; $i < $HASHBUCKETS3_16; $i++) {
my $str = $foldPairs3_16[$i];
if ($str eq '') {
print(" { NULL, 0 },\n");
} else {
my $num = '000' . $i;
$num =~ s/\A.*?(\d\d\d)\Z/$1/;
my $sym = "case_fold3_16_${num}";
print(" { $sym, __PHYSFS_ARRAYLEN($sym) },\n");
}
}
print("};\n\n");
print <<__EOF__;
#endif /* _INCLUDE_PHYSFS_CASEFOLDING_H_ */
/* end of physfs_casefolding.h ... */
__EOF__
exit 0;
# end of makecashfoldhashtable.pl ...

11
extras/physfs.pc.in Executable file
View File

@ -0,0 +1,11 @@
prefix=@CMAKE_INSTALL_PREFIX@
exec_prefix=${prefix}
libdir=${exec_prefix}/lib
includedir=${prefix}/include
Name: PhysicsFS
Description: PhysicsFS is a library to provide abstract access to various archives.
URL: https://icculus.org/physfs/
Version: @PHYSFS_VERSION@
Libs: -L${libdir} -lphysfs
Cflags: -I${includedir}

400
extras/physfshttpd.c Executable file
View File

@ -0,0 +1,400 @@
/*
* This is a quick and dirty HTTP server that uses PhysicsFS to retrieve
* files. It is not robust at all, probably buggy, and definitely poorly
* designed. It's just meant to show that it can be done.
*
* Basically, you compile this code, and run it:
* ./physfshttpd archive1.zip archive2.zip /path/to/a/real/dir etc...
*
* The files are appended in order to the PhysicsFS search path, and when
* a client request comes in, it looks for the file in said search path.
*
* My goal was to make this work in less than 300 lines of C, so again, it's
* not to be used for any serious purpose. Patches to make this application
* suck less will be readily and gratefully accepted.
*
* Command line I used to build this on Linux:
* gcc -Wall -Werror -g -o bin/physfshttpd extras/physfshttpd.c -lphysfs
*
* License: this code is public domain. I make no warranty that it is useful,
* correct, harmless, or environmentally safe.
*
* This particular file may be used however you like, including copying it
* verbatim into a closed-source project, exploiting it commercially, and
* removing any trace of my name from the source (although I hope you won't
* do that). I welcome enhancements and corrections to this file, but I do
* not require you to send me patches if you make changes. This code has
* NO WARRANTY.
*
* Unless otherwise stated, the rest of PhysicsFS falls under the zlib license.
* Please see LICENSE.txt in the root of the source tree.
*
* This file was written by Ryan C. Gordon. (icculus@icculus.org).
*/
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <ctype.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#ifndef LACKING_SIGNALS
#include <signal.h>
#endif
#ifndef LACKING_PROTOENT
#include <netdb.h>
#endif
#include "physfs.h"
#define DEFAULT_PORTNUM 8080
typedef struct
{
int sock;
struct sockaddr *addr;
socklen_t addrlen;
} http_args;
#define txt404 \
"HTTP/1.0 404 Not Found\n" \
"Connection: close\n" \
"Content-Type: text/html; charset=utf-8\n" \
"\n" \
"<html><head><title>404 Not Found</title></head>\n" \
"<body>Can't find '%s'.</body></html>\n\n" \
#define txt200 \
"HTTP/1.0 200 OK\n" \
"Connection: close\n" \
"Content-Type: %s\n" \
"\n"
static const char *lastError(void)
{
return PHYSFS_getErrorByCode(PHYSFS_getLastErrorCode());
} /* lastError */
static int writeAll(const char *ipstr, const int sock, void *buf, const size_t len)
{
if (write(sock, buf, len) != len)
{
printf("%s: Write error to socket.\n", ipstr);
return 0;
} /* if */
return 1;
} /* writeAll */
static int writeString(const char *ipstr, const int sock, const char *fmt, ...)
{
/* none of this is robust against large strings or HTML escaping. */
char buffer[1024];
int len;
va_list ap;
va_start(ap, fmt);
len = vsnprintf(buffer, sizeof (buffer), fmt, ap);
va_end(ap);
if (len < 0)
{
printf("uhoh, vsnprintf() failed!\n");
return 0;
} /* if */
return writeAll(ipstr, sock, buffer, len);
} /* writeString */
static void feed_file_http(const char *ipstr, int sock, const char *fname)
{
PHYSFS_File *in = PHYSFS_openRead(fname);
if (in == NULL)
{
printf("%s: Can't open [%s]: %s.\n", ipstr, fname, lastError());
writeString(ipstr, sock, txt404, fname);
return;
} /* if */
/* !!! FIXME: mimetype */
if (writeString(ipstr, sock, txt200, "text/plain; charset=utf-8"))
{
do
{
char buffer[1024];
PHYSFS_sint64 br = PHYSFS_readBytes(in, buffer, sizeof (buffer));
if (br == -1)
{
printf("%s: Read error: %s.\n", ipstr, lastError());
break;
} /* if */
else if (!writeAll(ipstr, sock, buffer, (size_t) br))
{
break;
} /* else if */
} while (!PHYSFS_eof(in));
} /* if */
PHYSFS_close(in);
} /* feed_file_http */
static void feed_dirlist_http(const char *ipstr, int sock,
const char *dname, char **list)
{
int i;
if (!writeString(ipstr, sock, txt200, "text/html; charset=utf-8"))
return;
else if (!writeString(ipstr, sock,
"<html><head><title>Directory %s</title></head>"
"<body><p><h1>Directory %s</h1></p><p><ul>\n",
dname, dname))
return;
if (strcmp(dname, "/") == 0)
dname = "";
for (i = 0; list[i]; i++)
{
const char *fname = list[i];
if (!writeString(ipstr, sock,
"<li><a href='%s/%s'>%s</a></li>\n", dname, fname, fname))
break;
} /* for */
writeString(ipstr, sock, "</ul></body></html>\n");
} /* feed_dirlist_http */
static void feed_dir_http(const char *ipstr, int sock, const char *dname)
{
char **list = PHYSFS_enumerateFiles(dname);
if (list == NULL)
{
printf("%s: Can't enumerate directory [%s]: %s.\n",
ipstr, dname, lastError());
writeString(ipstr, sock, txt404, dname);
return;
} /* if */
feed_dirlist_http(ipstr, sock, dname, list);
PHYSFS_freeList(list);
} /* feed_dir_http */
static void feed_http_request(const char *ipstr, int sock, const char *fname)
{
PHYSFS_Stat statbuf;
printf("%s: requested [%s].\n", ipstr, fname);
if (!PHYSFS_stat(fname, &statbuf))
{
printf("%s: Can't stat [%s]: %s.\n", ipstr, fname, lastError());
writeString(ipstr, sock, txt404, fname);
return;
} /* if */
if (statbuf.filetype == PHYSFS_FILETYPE_DIRECTORY)
feed_dir_http(ipstr, sock, fname);
else
feed_file_http(ipstr, sock, fname);
} /* feed_http_request */
static void *do_http(void *_args)
{
http_args *args = (http_args *) _args;
char ipstr[128];
char buffer[512];
char *ptr;
strncpy(ipstr, inet_ntoa(((struct sockaddr_in *) args->addr)->sin_addr),
sizeof (ipstr));
ipstr[sizeof (ipstr) - 1] = '\0';
printf("%s: connected.\n", ipstr);
read(args->sock, buffer, sizeof (buffer));
buffer[sizeof (buffer) - 1] = '\0';
ptr = strchr(buffer, '\n');
if (!ptr)
printf("%s: potentially bogus request.\n", ipstr);
else
{
*ptr = '\0';
ptr = strchr(buffer, '\r');
if (ptr != NULL)
*ptr = '\0';
if ((toupper(buffer[0]) == 'G') &&
(toupper(buffer[1]) == 'E') &&
(toupper(buffer[2]) == 'T') &&
(toupper(buffer[3]) == ' ') &&
(toupper(buffer[4]) == '/'))
{
ptr = strchr(buffer + 5, ' ');
if (ptr != NULL)
*ptr = '\0';
feed_http_request(ipstr, args->sock, buffer + 4);
} /* if */
} /* else */
/* !!! FIXME: Time the transfer. */
printf("%s: closing connection.\n", ipstr);
close(args->sock);
free(args->addr);
free(args);
return NULL;
} /* do_http */
static void serve_http_request(int sock, struct sockaddr *addr,
socklen_t addrlen)
{
http_args *args = (http_args *) malloc(sizeof (http_args));
if (args == NULL)
{
printf("out of memory.\n");
return;
} /* if */
args->addr = (struct sockaddr *) malloc(addrlen);
if (args->addr == NULL)
{
free(args);
printf("out of memory.\n");
return;
} /* if */
args->sock = sock;
args->addrlen = addrlen;
memcpy(args->addr, addr, addrlen);
/* !!! FIXME: optionally spin a thread... */
do_http((void *) args);
} /* server_http_request */
static int create_listen_socket(short portnum)
{
int retval = -1;
int protocol = 0; /* pray this is right. */
#ifndef LACKING_PROTOENT
struct protoent *prot;
setprotoent(0);
prot = getprotobyname("tcp");
if (prot != NULL)
protocol = prot->p_proto;
#endif
retval = socket(PF_INET, SOCK_STREAM, protocol);
if (retval >= 0)
{
struct sockaddr_in addr;
addr.sin_family = AF_INET;
addr.sin_port = htons(portnum);
addr.sin_addr.s_addr = INADDR_ANY;
if ((bind(retval, (struct sockaddr *) &addr, (socklen_t) sizeof (addr)) == -1) ||
(listen(retval, 5) == -1))
{
close(retval);
retval = -1;
} /* if */
} /* if */
return retval;
} /* create_listen_socket */
static int listensocket = -1;
void at_exit_cleanup(void)
{
/*
* !!! FIXME: If thread support, signal threads to terminate and
* !!! FIXME: wait for them to clean up.
*/
if (listensocket >= 0)
close(listensocket);
if (!PHYSFS_deinit())
printf("PHYSFS_deinit() failed: %s\n", lastError());
} /* at_exit_cleanup */
int main(int argc, char **argv)
{
int i;
int portnum = DEFAULT_PORTNUM;
setbuf(stdout, NULL);
setbuf(stderr, NULL);
#ifndef LACKING_SIGNALS
/* I'm not sure if this qualifies as a cheap trick... */
signal(SIGTERM, exit);
signal(SIGINT, exit);
signal(SIGFPE, exit);
signal(SIGSEGV, exit);
signal(SIGPIPE, exit);
signal(SIGILL, exit);
#endif
if (argc == 1)
{
printf("USAGE: %s <archive1> [archive2 [... archiveN]]\n", argv[0]);
return 42;
} /* if */
if (!PHYSFS_init(argv[0]))
{
printf("PHYSFS_init() failed: %s\n", lastError());
return 42;
} /* if */
/* normally, this is bad practice, but oh well. */
atexit(at_exit_cleanup);
for (i = 1; i < argc; i++)
{
if (!PHYSFS_mount(argv[i], NULL, 1))
printf(" WARNING: failed to add [%s] to search path.\n", argv[i]);
} /* else */
listensocket = create_listen_socket(portnum);
if (listensocket < 0)
{
printf("listen socket failed to create.\n");
return 42;
} /* if */
while (1) /* infinite loop for now. */
{
struct sockaddr addr;
socklen_t len;
int s = accept(listensocket, &addr, &len);
if (s < 0)
{
printf("accept() failed: %s\n", strerror(errno));
close(listensocket);
return 42;
} /* if */
serve_http_request(s, &addr, len);
} /* while */
return 0;
} /* main */
/* end of physfshttpd.c ... */

245
extras/physfsrwops.c Executable file
View File

@ -0,0 +1,245 @@
/*
* This code provides a glue layer between PhysicsFS and Simple Directmedia
* Layer's (SDL) RWops i/o abstraction.
*
* License: this code is public domain. I make no warranty that it is useful,
* correct, harmless, or environmentally safe.
*
* This particular file may be used however you like, including copying it
* verbatim into a closed-source project, exploiting it commercially, and
* removing any trace of my name from the source (although I hope you won't
* do that). I welcome enhancements and corrections to this file, but I do
* not require you to send me patches if you make changes. This code has
* NO WARRANTY.
*
* Unless otherwise stated, the rest of PhysicsFS falls under the zlib license.
* Please see LICENSE.txt in the root of the source tree.
*
* SDL 1.2 falls under the LGPL license. SDL 1.3+ is zlib, like PhysicsFS.
* You can get SDL at https://www.libsdl.org/
*
* This file was written by Ryan C. Gordon. (icculus@icculus.org).
*/
#include <stdio.h> /* used for SEEK_SET, SEEK_CUR, SEEK_END ... */
#include "physfsrwops.h"
/* SDL's RWOPS interface changed a little in SDL 2.0... */
#if defined(SDL_VERSION_ATLEAST)
#if SDL_VERSION_ATLEAST(2, 0, 0)
#define TARGET_SDL2 1
#endif
#endif
#if !TARGET_SDL2
#define RW_SEEK_SET SEEK_SET
#define RW_SEEK_CUR SEEK_CUR
#define RW_SEEK_END SEEK_END
#endif
#if TARGET_SDL2
static Sint64 SDLCALL physfsrwops_size(struct SDL_RWops *rw)
{
PHYSFS_File *handle = (PHYSFS_File *) rw->hidden.unknown.data1;
return (Sint64) PHYSFS_fileLength(handle);
} /* physfsrwops_size */
#endif
#if TARGET_SDL2
static Sint64 SDLCALL physfsrwops_seek(struct SDL_RWops *rw, Sint64 offset, int whence)
#else
static int physfsrwops_seek(SDL_RWops *rw, int offset, int whence)
#endif
{
PHYSFS_File *handle = (PHYSFS_File *) rw->hidden.unknown.data1;
PHYSFS_sint64 pos = 0;
if (whence == RW_SEEK_SET)
pos = (PHYSFS_sint64) offset;
else if (whence == RW_SEEK_CUR)
{
const PHYSFS_sint64 current = PHYSFS_tell(handle);
if (current == -1)
{
SDL_SetError("Can't find position in file: %s",
PHYSFS_getLastError());
return -1;
} /* if */
if (offset == 0) /* this is a "tell" call. We're done. */
{
#if TARGET_SDL2
return (Sint64) current;
#else
return (int) current;
#endif
} /* if */
pos = current + ((PHYSFS_sint64) offset);
} /* else if */
else if (whence == RW_SEEK_END)
{
const PHYSFS_sint64 len = PHYSFS_fileLength(handle);
if (len == -1)
{
SDL_SetError("Can't find end of file: %s", PHYSFS_getLastError());
return -1;
} /* if */
pos = len + ((PHYSFS_sint64) offset);
} /* else if */
else
{
SDL_SetError("Invalid 'whence' parameter.");
return -1;
} /* else */
if ( pos < 0 )
{
SDL_SetError("Attempt to seek past start of file.");
return -1;
} /* if */
if (!PHYSFS_seek(handle, (PHYSFS_uint64) pos))
{
SDL_SetError("PhysicsFS error: %s", PHYSFS_getLastError());
return -1;
} /* if */
#if TARGET_SDL2
return (Sint64) pos;
#else
return (int) pos;
#endif
} /* physfsrwops_seek */
#if TARGET_SDL2
static size_t SDLCALL physfsrwops_read(struct SDL_RWops *rw, void *ptr,
size_t size, size_t maxnum)
#else
static int physfsrwops_read(SDL_RWops *rw, void *ptr, int size, int maxnum)
#endif
{
PHYSFS_File *handle = (PHYSFS_File *) rw->hidden.unknown.data1;
const PHYSFS_uint64 readlen = (PHYSFS_uint64) (maxnum * size);
const PHYSFS_sint64 rc = PHYSFS_readBytes(handle, ptr, readlen);
if (rc != ((PHYSFS_sint64) readlen))
{
if (!PHYSFS_eof(handle)) /* not EOF? Must be an error. */
{
SDL_SetError("PhysicsFS error: %s", PHYSFS_getLastError());
#if TARGET_SDL2
return 0;
#else
return -1;
#endif
} /* if */
} /* if */
#if TARGET_SDL2
return (size_t) rc / size;
#else
return (int) rc / size;
#endif
} /* physfsrwops_read */
#if TARGET_SDL2
static size_t SDLCALL physfsrwops_write(struct SDL_RWops *rw, const void *ptr,
size_t size, size_t num)
#else
static int physfsrwops_write(SDL_RWops *rw, const void *ptr, int size, int num)
#endif
{
PHYSFS_File *handle = (PHYSFS_File *) rw->hidden.unknown.data1;
const PHYSFS_uint64 writelen = (PHYSFS_uint64) (num * size);
const PHYSFS_sint64 rc = PHYSFS_writeBytes(handle, ptr, writelen);
if (rc != ((PHYSFS_sint64) writelen))
SDL_SetError("PhysicsFS error: %s", PHYSFS_getLastError());
#if TARGET_SDL2
return (size_t) rc;
#else
return (int) rc;
#endif
} /* physfsrwops_write */
static int physfsrwops_close(SDL_RWops *rw)
{
PHYSFS_File *handle = (PHYSFS_File *) rw->hidden.unknown.data1;
if (!PHYSFS_close(handle))
{
SDL_SetError("PhysicsFS error: %s", PHYSFS_getLastError());
return -1;
} /* if */
SDL_FreeRW(rw);
return 0;
} /* physfsrwops_close */
static SDL_RWops *create_rwops(PHYSFS_File *handle)
{
SDL_RWops *retval = NULL;
if (handle == NULL)
SDL_SetError("PhysicsFS error: %s", PHYSFS_getLastError());
else
{
retval = SDL_AllocRW();
if (retval != NULL)
{
#if TARGET_SDL2
retval->size = physfsrwops_size;
#endif
retval->seek = physfsrwops_seek;
retval->read = physfsrwops_read;
retval->write = physfsrwops_write;
retval->close = physfsrwops_close;
retval->hidden.unknown.data1 = handle;
} /* if */
} /* else */
return retval;
} /* create_rwops */
SDL_RWops *PHYSFSRWOPS_makeRWops(PHYSFS_File *handle)
{
SDL_RWops *retval = NULL;
if (handle == NULL)
SDL_SetError("NULL pointer passed to PHYSFSRWOPS_makeRWops().");
else
retval = create_rwops(handle);
return retval;
} /* PHYSFSRWOPS_makeRWops */
SDL_RWops *PHYSFSRWOPS_openRead(const char *fname)
{
return create_rwops(PHYSFS_openRead(fname));
} /* PHYSFSRWOPS_openRead */
SDL_RWops *PHYSFSRWOPS_openWrite(const char *fname)
{
return create_rwops(PHYSFS_openWrite(fname));
} /* PHYSFSRWOPS_openWrite */
SDL_RWops *PHYSFSRWOPS_openAppend(const char *fname)
{
return create_rwops(PHYSFS_openAppend(fname));
} /* PHYSFSRWOPS_openAppend */
/* end of physfsrwops.c ... */

89
extras/physfsrwops.h Executable file
View File

@ -0,0 +1,89 @@
/*
* This code provides a glue layer between PhysicsFS and Simple Directmedia
* Layer's (SDL) RWops i/o abstraction.
*
* License: this code is public domain. I make no warranty that it is useful,
* correct, harmless, or environmentally safe.
*
* This particular file may be used however you like, including copying it
* verbatim into a closed-source project, exploiting it commercially, and
* removing any trace of my name from the source (although I hope you won't
* do that). I welcome enhancements and corrections to this file, but I do
* not require you to send me patches if you make changes. This code has
* NO WARRANTY.
*
* Unless otherwise stated, the rest of PhysicsFS falls under the zlib license.
* Please see LICENSE.txt in the root of the source tree.
*
* SDL 1.2 falls under the LGPL license. SDL 1.3+ is zlib, like PhysicsFS.
* You can get SDL at https://www.libsdl.org/
*
* This file was written by Ryan C. Gordon. (icculus@icculus.org).
*/
#ifndef _INCLUDE_PHYSFSRWOPS_H_
#define _INCLUDE_PHYSFSRWOPS_H_
#include "physfs.h"
#include "SDL.h"
#ifdef __cplusplus
extern "C" {
#endif
/**
* Open a platform-independent filename for reading, and make it accessible
* via an SDL_RWops structure. The file will be closed in PhysicsFS when the
* RWops is closed. PhysicsFS should be configured to your liking before
* opening files through this method.
*
* @param filename File to open in platform-independent notation.
* @return A valid SDL_RWops structure on success, NULL on error. Specifics
* of the error can be gleaned from PHYSFS_getLastError().
*/
PHYSFS_DECL SDL_RWops *PHYSFSRWOPS_openRead(const char *fname);
/**
* Open a platform-independent filename for writing, and make it accessible
* via an SDL_RWops structure. The file will be closed in PhysicsFS when the
* RWops is closed. PhysicsFS should be configured to your liking before
* opening files through this method.
*
* @param filename File to open in platform-independent notation.
* @return A valid SDL_RWops structure on success, NULL on error. Specifics
* of the error can be gleaned from PHYSFS_getLastError().
*/
PHYSFS_DECL SDL_RWops *PHYSFSRWOPS_openWrite(const char *fname);
/**
* Open a platform-independent filename for appending, and make it accessible
* via an SDL_RWops structure. The file will be closed in PhysicsFS when the
* RWops is closed. PhysicsFS should be configured to your liking before
* opening files through this method.
*
* @param filename File to open in platform-independent notation.
* @return A valid SDL_RWops structure on success, NULL on error. Specifics
* of the error can be gleaned from PHYSFS_getLastError().
*/
PHYSFS_DECL SDL_RWops *PHYSFSRWOPS_openAppend(const char *fname);
/**
* Make a SDL_RWops from an existing PhysicsFS file handle. You should
* dispose of any references to the handle after successful creation of
* the RWops. The actual PhysicsFS handle will be destroyed when the
* RWops is closed.
*
* @param handle a valid PhysicsFS file handle.
* @return A valid SDL_RWops structure on success, NULL on error. Specifics
* of the error can be gleaned from PHYSFS_getLastError().
*/
PHYSFS_DECL SDL_RWops *PHYSFSRWOPS_makeRWops(PHYSFS_File *handle);
#ifdef __cplusplus
}
#endif
#endif /* include-once blocker */
/* end of physfsrwops.h ... */

181
extras/physfsunpack.c Executable file
View File

@ -0,0 +1,181 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include "physfs.h"
static int failure = 0;
static void modTimeToStr(PHYSFS_sint64 modtime, char *modstr, size_t strsize)
{
const char *str = "unknown modtime";
if (modtime != -1)
{
time_t t = (time_t) modtime;
str = ctime(&t);
} /* if */
strncpy(modstr, str, strsize);
modstr[strsize-1] = '\0';
strsize = strlen(modstr);
while ((modstr[strsize-1] == '\n') || (modstr[strsize-1] == '\r'))
modstr[--strsize] = '\0';
} /* modTimeToStr */
static void fail(const char *what, const char *why)
{
if (why == NULL)
why = PHYSFS_getLastError();
fprintf(stderr, "%s failed: %s\n", what, why);
failure = 1;
} /* fail */
static void dumpFile(const char *fname)
{
const int origfailure = failure;
PHYSFS_File *out = NULL;
PHYSFS_File *in = NULL;
failure = 0;
if ((in = PHYSFS_openRead(fname)) == NULL)
fail("\nPHYSFS_openRead", NULL);
else if ((out = PHYSFS_openWrite(fname)) == NULL)
fail("\nPHYSFS_openWrite", NULL);
else
{
char modstr[64];
PHYSFS_sint64 size = PHYSFS_fileLength(in);
printf("(");
if (size == -1)
printf("?");
else
printf("%lld", (long long) size);
printf(" bytes");
modTimeToStr(PHYSFS_getLastModTime(fname), modstr, sizeof (modstr));
printf(", %s)\n", modstr);
while ( (!failure) && (!PHYSFS_eof(in)) )
{
static char buf[64 * 1024];
PHYSFS_sint64 br = PHYSFS_read(in, buf, 1, sizeof (buf));
if (br == -1)
fail("PHYSFS_read", NULL);
else
{
PHYSFS_sint64 bw = PHYSFS_write(out, buf, 1, br);
if (bw != br)
fail("PHYSFS_write", NULL);
else
size -= bw;
} /* else */
} /* while */
if ((!failure) && (size != 0))
fail("PHYSFS_eof", "BUG! eof != PHYSFS_fileLength bytes!");
} /* else */
if (in != NULL)
PHYSFS_close(in);
if (out != NULL)
{
if (!PHYSFS_close(out))
fail("PHYSFS_close", NULL);
} /* if */
if (failure)
PHYSFS_delete(fname);
else
failure = origfailure;
} /* dumpFile */
static void unpackCallback(void *_depth, const char *origdir, const char *str)
{
int depth = *((int *) _depth);
const int len = strlen(origdir) + strlen(str) + 2;
char *fname = (char *) malloc(len);
if (fname == NULL)
fail("malloc", "Out of memory!");
else
{
if (strcmp(origdir, "/") == 0)
origdir = "";
snprintf(fname, len, "%s/%s", origdir, str);
printf("%s ", fname);
if (PHYSFS_isDirectory(fname))
{
depth++;
printf("(directory)\n");
if (!PHYSFS_mkdir(fname))
fail("PHYSFS_mkdir", NULL);
else
PHYSFS_enumerateFilesCallback(fname, unpackCallback, &depth);
} /* if */
else if (PHYSFS_isSymbolicLink(fname))
{
printf("(symlink)\n");
/* !!! FIXME: ? if (!symlink(fname, */
} /* else if */
else /* ...file. */
{
dumpFile(fname);
} /* else */
free(fname);
} /* else */
} /* unpackCallback */
int main(int argc, char **argv)
{
int zero = 0;
if (argc != 3)
{
fprintf(stderr, "USAGE: %s <archive> <unpackDirectory>\n", argv[0]);
return 1;
} /* if */
if (!PHYSFS_init(argv[0]))
{
fprintf(stderr, "PHYSFS_init() failed: %s\n", PHYSFS_getLastError());
return 2;
} /* if */
if (!PHYSFS_setWriteDir(argv[2]))
{
fprintf(stderr, "PHYSFS_setWriteDir('%s') failed: %s\n",
argv[2], PHYSFS_getLastError());
return 3;
} /* if */
if (!PHYSFS_mount(argv[1], NULL, 1))
{
fprintf(stderr, "PHYSFS_mount('%s') failed: %s\n",
argv[1], PHYSFS_getLastError());
return 4;
} /* if */
PHYSFS_permitSymbolicLinks(1);
PHYSFS_enumerateFilesCallback("/", unpackCallback, &zero);
PHYSFS_deinit();
if (failure)
return 5;
return 0;
} /* main */
/* end of physfsunpack.c ... */

66
extras/selfextract.c Executable file
View File

@ -0,0 +1,66 @@
/*
* This code shows how to read a zipfile included in an app's binary.
*
* License: this code is public domain. I make no warranty that it is useful,
* correct, harmless, or environmentally safe.
*
* This particular file may be used however you like, including copying it
* verbatim into a closed-source project, exploiting it commercially, and
* removing any trace of my name from the source (although I hope you won't
* do that). I welcome enhancements and corrections to this file, but I do
* not require you to send me patches if you make changes. This code has
* NO WARRANTY.
*
* Unless otherwise stated, the rest of PhysicsFS falls under the zlib license.
* Please see LICENSE.txt in the root of the source tree.
*
* This file was written by Ryan C. Gordon. (icculus@icculus.org).
*/
/*
* Compile this program and then attach a .zip file to the end of the
* compiled binary.
*
* On Linux, something like this will build the final binary:
* gcc -o selfextract.tmp selfextract.c -lphysfs && \
* cat selfextract.tmp myzipfile.zip >> selfextract && \
* chmod a+x selfextract && \
* rm -f selfextract.tmp
*
* This may not work on all platforms, and it probably only works with
* .zip files, since they are designed to be appended to another file.
*/
#include <stdio.h>
#include "physfs.h"
int main(int argc, char **argv)
{
int rc = 0;
if (!PHYSFS_init(argv[0]))
{
printf("PHYSFS_init() failed: %s\n", PHYSFS_getLastError());
return 42;
} /* if */
rc = PHYSFS_addToSearchPath(argv[0], 0);
if (!rc)
{
printf("Couldn't find self-extract data: %s\n", PHYSFS_getLastError());
printf("This might mean you didn't append a zipfile to the binary.\n");
return 42;
} /* if */
char **files = PHYSFS_enumerateFiles("/");
char **i;
for (i = files; *i != NULL; i++)
{
const char *dirorfile = PHYSFS_isDirectory(*i) ? "Directory" : "File";
printf(" * %s '%s' is in root of attached data.\n", dirorfile, *i);
} /* for */
PHYSFS_freeList(files);
return 0;
} /* main */

10
extras/uninstall.sh Executable file
View File

@ -0,0 +1,10 @@
#!/bin/sh
if [ ! -f "./install_manifest.txt" ]; then
echo "ERROR: This needs to be run from your CMake build directory after installing." 1>&2
exit 1
fi
xargs rm -vf < install_manifest.txt
exit 0

3302
src/physfs.c Executable file

File diff suppressed because it is too large Load Diff

3854
src/physfs.h Executable file

File diff suppressed because it is too large Load Diff

428
src/physfs_archiver_7z.c Executable file
View File

@ -0,0 +1,428 @@
/*
* 7zip support routines for PhysicsFS.
*
* Please see the file LICENSE.txt in the source's root directory.
*
* This file was written by Ryan C. Gordon.
*/
#define __PHYSICSFS_INTERNAL__
#include "physfs_internal.h"
#if PHYSFS_SUPPORTS_7Z
#include "physfs_lzmasdk.h"
typedef struct
{
ISeekInStream seekStream; /* lzma sdk i/o interface (lower level). */
PHYSFS_Io *io; /* physfs i/o interface for this archive. */
CLookToRead lookStream; /* lzma sdk i/o interface (higher level). */
} SZIPLookToRead;
/* One SZIPentry is kept for each file in an open 7zip archive. */
typedef struct
{
__PHYSFS_DirTreeEntry tree; /* manages directory tree */
PHYSFS_uint32 dbidx; /* index into lzma sdk database */
} SZIPentry;
/* One SZIPinfo is kept for each open 7zip archive. */
typedef struct
{
__PHYSFS_DirTree tree; /* manages directory tree. */
PHYSFS_Io *io; /* physfs i/o interface for this archive. */
CSzArEx db; /* lzma sdk archive database object. */
} SZIPinfo;
static PHYSFS_ErrorCode szipErrorCode(const SRes rc)
{
switch (rc)
{
case SZ_OK: return PHYSFS_ERR_OK;
case SZ_ERROR_DATA: return PHYSFS_ERR_CORRUPT;
case SZ_ERROR_MEM: return PHYSFS_ERR_OUT_OF_MEMORY;
case SZ_ERROR_CRC: return PHYSFS_ERR_CORRUPT;
case SZ_ERROR_UNSUPPORTED: return PHYSFS_ERR_UNSUPPORTED;
case SZ_ERROR_INPUT_EOF: return PHYSFS_ERR_CORRUPT;
case SZ_ERROR_OUTPUT_EOF: return PHYSFS_ERR_IO;
case SZ_ERROR_READ: return PHYSFS_ERR_IO;
case SZ_ERROR_WRITE: return PHYSFS_ERR_IO;
case SZ_ERROR_ARCHIVE: return PHYSFS_ERR_CORRUPT;
case SZ_ERROR_NO_ARCHIVE: return PHYSFS_ERR_UNSUPPORTED;
default: break;
} /* switch */
return PHYSFS_ERR_OTHER_ERROR;
} /* szipErrorCode */
/* LZMA SDK's ISzAlloc interface ... */
static void *SZIP_ISzAlloc_Alloc(void *p, size_t size)
{
return allocator.Malloc(size ? size : 1);
} /* SZIP_ISzAlloc_Alloc */
static void SZIP_ISzAlloc_Free(void *p, void *address)
{
if (address)
allocator.Free(address);
} /* SZIP_ISzAlloc_Free */
static ISzAlloc SZIP_SzAlloc = {
SZIP_ISzAlloc_Alloc, SZIP_ISzAlloc_Free
};
/* we implement ISeekInStream, and then wrap that in LZMA SDK's CLookToRead,
which implements the higher-level ILookInStream on top of that, handling
buffering and such for us. */
/* LZMA SDK's ISeekInStream interface ... */
static SRes SZIP_ISeekInStream_Read(void *p, void *buf, size_t *size)
{
SZIPLookToRead *stream = (SZIPLookToRead *) p;
PHYSFS_Io *io = stream->io;
const PHYSFS_uint64 len = (PHYSFS_uint64) *size;
const PHYSFS_sint64 rc = (len == 0) ? 0 : io->read(io, buf, len);
if (rc < 0)
{
*size = 0;
return SZ_ERROR_READ;
} /* if */
*size = (size_t) rc;
return SZ_OK;
} /* SZIP_ISeekInStream_Read */
static SRes SZIP_ISeekInStream_Seek(void *p, Int64 *pos, ESzSeek origin)
{
SZIPLookToRead *stream = (SZIPLookToRead *) p;
PHYSFS_Io *io = stream->io;
PHYSFS_sint64 base;
PHYSFS_uint64 newpos;
switch (origin)
{
case SZ_SEEK_SET:
base = 0;
break;
case SZ_SEEK_CUR:
base = io->tell(io);
break;
case SZ_SEEK_END:
base = io->length(io);
break;
default:
return SZ_ERROR_FAIL;
} /* switch */
if (base < 0)
return SZ_ERROR_FAIL;
else if ((*pos < 0) && (((Int64) base) < -*pos))
return SZ_ERROR_FAIL;
newpos = (PHYSFS_uint64) (((Int64) base) + *pos);
if (!io->seek(io, newpos))
return SZ_ERROR_FAIL;
*pos = (Int64) newpos;
return SZ_OK;
} /* SZIP_ISeekInStream_Seek */
static void szipInitStream(SZIPLookToRead *stream, PHYSFS_Io *io)
{
stream->seekStream.Read = SZIP_ISeekInStream_Read;
stream->seekStream.Seek = SZIP_ISeekInStream_Seek;
stream->io = io;
/* !!! FIXME: can we use lookahead? Is there value to it? */
LookToRead_Init(&stream->lookStream);
LookToRead_CreateVTable(&stream->lookStream, False);
stream->lookStream.realStream = &stream->seekStream;
} /* szipInitStream */
/* Do this in a separate function so we can smallAlloc without looping. */
static int szipLoadEntry(SZIPinfo *info, const PHYSFS_uint32 idx)
{
const size_t utf16len = SzArEx_GetFileNameUtf16(&info->db, idx, NULL);
const size_t utf16buflen = utf16len * 2;
PHYSFS_uint16 *utf16 = (PHYSFS_uint16 *) __PHYSFS_smallAlloc(utf16buflen);
const size_t utf8buflen = utf16len * 4;
char *utf8 = (char *) __PHYSFS_smallAlloc(utf8buflen);
int retval = 0;
if (utf16 && utf8)
{
const int isdir = SzArEx_IsDir(&info->db, idx) != 0;
SZIPentry *entry;
SzArEx_GetFileNameUtf16(&info->db, idx, (UInt16 *) utf16);
PHYSFS_utf8FromUtf16(utf16, utf8, utf8buflen);
entry = (SZIPentry*) __PHYSFS_DirTreeAdd(&info->tree, utf8, isdir);
retval = (entry != NULL);
if (retval)
entry->dbidx = idx;
} /* if */
__PHYSFS_smallFree(utf8);
__PHYSFS_smallFree(utf16);
return retval;
} /* szipLoadEntry */
static int szipLoadEntries(SZIPinfo *info)
{
int retval = 0;
if (__PHYSFS_DirTreeInit(&info->tree, sizeof (SZIPentry)))
{
const PHYSFS_uint32 count = info->db.NumFiles;
PHYSFS_uint32 i;
for (i = 0; i < count; i++)
BAIL_IF_ERRPASS(!szipLoadEntry(info, i), 0);
retval = 1;
} /* if */
return retval;
} /* szipLoadEntries */
static void SZIP_closeArchive(void *opaque)
{
SZIPinfo *info = (SZIPinfo *) opaque;
if (info)
{
SzArEx_Free(&info->db, &SZIP_SzAlloc);
__PHYSFS_DirTreeDeinit(&info->tree);
allocator.Free(info);
} /* if */
} /* SZIP_closeArchive */
static void *SZIP_openArchive(PHYSFS_Io *io, const char *name,
int forWriting, int *claimed)
{
static const PHYSFS_uint8 wantedsig[] = { '7','z',0xBC,0xAF,0x27,0x1C };
SZIPLookToRead stream;
ISzAlloc *alloc = &SZIP_SzAlloc;
SZIPinfo *info = NULL;
SRes rc;
PHYSFS_uint8 sig[6];
PHYSFS_sint64 pos;
BAIL_IF(forWriting, PHYSFS_ERR_READ_ONLY, NULL);
pos = io->tell(io);
BAIL_IF_ERRPASS(pos == -1, NULL);
BAIL_IF_ERRPASS(io->read(io, sig, 6) != 6, NULL);
*claimed = (memcmp(sig, wantedsig, 6) == 0);
BAIL_IF_ERRPASS(!io->seek(io, pos), NULL);
info = (SZIPinfo *) allocator.Malloc(sizeof (SZIPinfo));
BAIL_IF(!info, PHYSFS_ERR_OUT_OF_MEMORY, NULL);
memset(info, '\0', sizeof (*info));
SzArEx_Init(&info->db);
info->io = io;
szipInitStream(&stream, io);
rc = SzArEx_Open(&info->db, &stream.lookStream.s, alloc, alloc);
GOTO_IF(rc != SZ_OK, szipErrorCode(rc), failed);
GOTO_IF_ERRPASS(!szipLoadEntries(info), failed);
return info;
failed:
info->io = NULL; /* don't let cleanup destroy the PHYSFS_Io. */
SZIP_closeArchive(info);
return NULL;
} /* SZIP_openArchive */
static PHYSFS_Io *SZIP_openRead(void *opaque, const char *path)
{
/* !!! FIXME: the current lzma sdk C API only allows you to decompress
!!! FIXME: the entire file at once, which isn't ideal. Fix this in the
!!! FIXME: SDK and then convert this all to a streaming interface. */
SZIPinfo *info = (SZIPinfo *) opaque;
SZIPentry *entry = (SZIPentry *) __PHYSFS_DirTreeFind(&info->tree, path);
ISzAlloc *alloc = &SZIP_SzAlloc;
SZIPLookToRead stream;
PHYSFS_Io *retval = NULL;
PHYSFS_Io *io = NULL;
UInt32 blockIndex = 0xFFFFFFFF;
Byte *outBuffer = NULL;
size_t outBufferSize = 0;
size_t offset = 0;
size_t outSizeProcessed = 0;
void *buf = NULL;
SRes rc;
BAIL_IF_ERRPASS(!entry, NULL);
BAIL_IF(entry->tree.isdir, PHYSFS_ERR_NOT_A_FILE, NULL);
io = info->io->duplicate(info->io);
GOTO_IF_ERRPASS(!io, SZIP_openRead_failed);
szipInitStream(&stream, io);
rc = SzArEx_Extract(&info->db, &stream.lookStream.s, entry->dbidx,
&blockIndex, &outBuffer, &outBufferSize, &offset,
&outSizeProcessed, alloc, alloc);
GOTO_IF(rc != SZ_OK, szipErrorCode(rc), SZIP_openRead_failed);
io->destroy(io);
io = NULL;
buf = allocator.Malloc(outSizeProcessed);
GOTO_IF(rc != SZ_OK, PHYSFS_ERR_OUT_OF_MEMORY, SZIP_openRead_failed);
memcpy(buf, outBuffer + offset, outSizeProcessed);
alloc->Free(alloc, outBuffer);
outBuffer = NULL;
retval = __PHYSFS_createMemoryIo(buf, outSizeProcessed, allocator.Free);
GOTO_IF_ERRPASS(!retval, SZIP_openRead_failed);
return retval;
SZIP_openRead_failed:
if (io != NULL)
io->destroy(io);
if (buf)
allocator.Free(buf);
if (outBuffer)
alloc->Free(alloc, outBuffer);
return NULL;
} /* SZIP_openRead */
static PHYSFS_Io *SZIP_openWrite(void *opaque, const char *filename)
{
BAIL(PHYSFS_ERR_READ_ONLY, NULL);
} /* SZIP_openWrite */
static PHYSFS_Io *SZIP_openAppend(void *opaque, const char *filename)
{
BAIL(PHYSFS_ERR_READ_ONLY, NULL);
} /* SZIP_openAppend */
static int SZIP_remove(void *opaque, const char *name)
{
BAIL(PHYSFS_ERR_READ_ONLY, 0);
} /* SZIP_remove */
static int SZIP_mkdir(void *opaque, const char *name)
{
BAIL(PHYSFS_ERR_READ_ONLY, 0);
} /* SZIP_mkdir */
static inline PHYSFS_uint64 lzmasdkTimeToPhysfsTime(const CNtfsFileTime *t)
{
const PHYSFS_uint64 winEpochToUnixEpoch = __PHYSFS_UI64(0x019DB1DED53E8000);
const PHYSFS_uint64 nanosecToMillisec = __PHYSFS_UI64(10000000);
const PHYSFS_uint64 quad = (((PHYSFS_uint64) t->High) << 32) | t->Low;
return (quad - winEpochToUnixEpoch) / nanosecToMillisec;
} /* lzmasdkTimeToPhysfsTime */
static int SZIP_stat(void *opaque, const char *path, PHYSFS_Stat *stat)
{
SZIPinfo *info = (SZIPinfo *) opaque;
SZIPentry *entry;
PHYSFS_uint32 idx;
entry = (SZIPentry *) __PHYSFS_DirTreeFind(&info->tree, path);
BAIL_IF_ERRPASS(!entry, 0);
idx = entry->dbidx;
if (entry->tree.isdir)
{
stat->filesize = -1;
stat->filetype = PHYSFS_FILETYPE_DIRECTORY;
} /* if */
else
{
stat->filesize = (PHYSFS_sint64) SzArEx_GetFileSize(&info->db, idx);
stat->filetype = PHYSFS_FILETYPE_REGULAR;
} /* else */
if (info->db.MTime.Vals != NULL)
stat->modtime = lzmasdkTimeToPhysfsTime(&info->db.MTime.Vals[idx]);
else if (info->db.CTime.Vals != NULL)
stat->modtime = lzmasdkTimeToPhysfsTime(&info->db.CTime.Vals[idx]);
else
stat->modtime = -1;
if (info->db.CTime.Vals != NULL)
stat->createtime = lzmasdkTimeToPhysfsTime(&info->db.CTime.Vals[idx]);
else if (info->db.MTime.Vals != NULL)
stat->createtime = lzmasdkTimeToPhysfsTime(&info->db.MTime.Vals[idx]);
else
stat->createtime = -1;
stat->accesstime = -1;
stat->readonly = 1;
return 1;
} /* SZIP_stat */
void SZIP_global_init(void)
{
/* this just needs to calculate some things, so it only ever
has to run once, even after a deinit. */
static int generatedTable = 0;
if (!generatedTable)
{
generatedTable = 1;
CrcGenerateTable();
} /* if */
} /* SZIP_global_init */
const PHYSFS_Archiver __PHYSFS_Archiver_7Z =
{
CURRENT_PHYSFS_ARCHIVER_API_VERSION,
{
"7Z",
"7zip archives",
"Ryan C. Gordon <icculus@icculus.org>",
"https://icculus.org/physfs/",
0, /* supportsSymlinks */
},
SZIP_openArchive,
__PHYSFS_DirTreeEnumerate,
SZIP_openRead,
SZIP_openWrite,
SZIP_openAppend,
SZIP_remove,
SZIP_mkdir,
SZIP_stat,
SZIP_closeArchive
};
#endif /* defined PHYSFS_SUPPORTS_7Z */
/* end of physfs_archiver_7z.c ... */

196
src/physfs_archiver_dir.c Executable file
View File

@ -0,0 +1,196 @@
/*
* Standard directory I/O support routines for PhysicsFS.
*
* Please see the file LICENSE.txt in the source's root directory.
*
* This file written by Ryan C. Gordon.
*/
#define __PHYSICSFS_INTERNAL__
#include "physfs_internal.h"
/* There's no PHYSFS_Io interface here. Use __PHYSFS_createNativeIo(). */
static char *cvtToDependent(const char *prepend, const char *path,
char *buf, const size_t buflen)
{
BAIL_IF(buf == NULL, PHYSFS_ERR_OUT_OF_MEMORY, NULL);
snprintf(buf, buflen, "%s%s", prepend ? prepend : "", path);
#if !__PHYSFS_STANDARD_DIRSEP
assert(__PHYSFS_platformDirSeparator != '/');
{
char *p;
for (p = strchr(buf, '/'); p != NULL; p = strchr(p + 1, '/'))
*p = __PHYSFS_platformDirSeparator;
} /* if */
#endif
return buf;
} /* cvtToDependent */
#define CVT_TO_DEPENDENT(buf, pre, dir) { \
const size_t len = ((pre) ? strlen((char *) pre) : 0) + strlen(dir) + 1; \
buf = cvtToDependent((char*)pre,dir,(char*)__PHYSFS_smallAlloc(len),len); \
}
static void *DIR_openArchive(PHYSFS_Io *io, const char *name,
int forWriting, int *claimed)
{
PHYSFS_Stat st;
const char dirsep = __PHYSFS_platformDirSeparator;
char *retval = NULL;
const size_t namelen = strlen(name);
const size_t seplen = 1;
assert(io == NULL); /* shouldn't create an Io for these. */
BAIL_IF_ERRPASS(!__PHYSFS_platformStat(name, &st, 1), NULL);
if (st.filetype != PHYSFS_FILETYPE_DIRECTORY)
BAIL(PHYSFS_ERR_UNSUPPORTED, NULL);
*claimed = 1;
retval = allocator.Malloc(namelen + seplen + 1);
BAIL_IF(retval == NULL, PHYSFS_ERR_OUT_OF_MEMORY, NULL);
strcpy(retval, name);
/* make sure there's a dir separator at the end of the string */
if (retval[namelen - 1] != dirsep)
{
retval[namelen] = dirsep;
retval[namelen + 1] = '\0';
} /* if */
return retval;
} /* DIR_openArchive */
static PHYSFS_EnumerateCallbackResult DIR_enumerate(void *opaque,
const char *dname, PHYSFS_EnumerateCallback cb,
const char *origdir, void *callbackdata)
{
char *d;
PHYSFS_EnumerateCallbackResult retval;
CVT_TO_DEPENDENT(d, opaque, dname);
BAIL_IF_ERRPASS(!d, PHYSFS_ENUM_ERROR);
retval = __PHYSFS_platformEnumerate(d, cb, origdir, callbackdata);
__PHYSFS_smallFree(d);
return retval;
} /* DIR_enumerate */
static PHYSFS_Io *doOpen(void *opaque, const char *name, const int mode)
{
PHYSFS_Io *io = NULL;
char *f = NULL;
CVT_TO_DEPENDENT(f, opaque, name);
BAIL_IF_ERRPASS(!f, NULL);
io = __PHYSFS_createNativeIo(f, mode);
if (io == NULL)
{
const PHYSFS_ErrorCode err = PHYSFS_getLastErrorCode();
PHYSFS_Stat statbuf;
__PHYSFS_platformStat(f, &statbuf, 0); /* !!! FIXME: why are we stating here? */
PHYSFS_setErrorCode(err);
} /* if */
__PHYSFS_smallFree(f);
return io;
} /* doOpen */
static PHYSFS_Io *DIR_openRead(void *opaque, const char *filename)
{
return doOpen(opaque, filename, 'r');
} /* DIR_openRead */
static PHYSFS_Io *DIR_openWrite(void *opaque, const char *filename)
{
return doOpen(opaque, filename, 'w');
} /* DIR_openWrite */
static PHYSFS_Io *DIR_openAppend(void *opaque, const char *filename)
{
return doOpen(opaque, filename, 'a');
} /* DIR_openAppend */
static int DIR_remove(void *opaque, const char *name)
{
int retval;
char *f;
CVT_TO_DEPENDENT(f, opaque, name);
BAIL_IF_ERRPASS(!f, 0);
retval = __PHYSFS_platformDelete(f);
__PHYSFS_smallFree(f);
return retval;
} /* DIR_remove */
static int DIR_mkdir(void *opaque, const char *name)
{
int retval;
char *f;
CVT_TO_DEPENDENT(f, opaque, name);
BAIL_IF_ERRPASS(!f, 0);
retval = __PHYSFS_platformMkDir(f);
__PHYSFS_smallFree(f);
return retval;
} /* DIR_mkdir */
static void DIR_closeArchive(void *opaque)
{
allocator.Free(opaque);
} /* DIR_closeArchive */
static int DIR_stat(void *opaque, const char *name, PHYSFS_Stat *stat)
{
int retval = 0;
char *d;
CVT_TO_DEPENDENT(d, opaque, name);
BAIL_IF_ERRPASS(!d, 0);
retval = __PHYSFS_platformStat(d, stat, 0);
__PHYSFS_smallFree(d);
return retval;
} /* DIR_stat */
const PHYSFS_Archiver __PHYSFS_Archiver_DIR =
{
CURRENT_PHYSFS_ARCHIVER_API_VERSION,
{
"",
"Non-archive, direct filesystem I/O",
"Ryan C. Gordon <icculus@icculus.org>",
"https://icculus.org/physfs/",
1, /* supportsSymlinks */
},
DIR_openArchive,
DIR_enumerate,
DIR_openRead,
DIR_openWrite,
DIR_openAppend,
DIR_remove,
DIR_mkdir,
DIR_stat,
DIR_closeArchive
};
/* end of physfs_archiver_dir.c ... */

116
src/physfs_archiver_grp.c Executable file
View File

@ -0,0 +1,116 @@
/*
* GRP support routines for PhysicsFS.
*
* This driver handles BUILD engine archives ("groupfiles"). This format
* (but not this driver) was put together by Ken Silverman.
*
* The format is simple enough. In Ken's words:
*
* What's the .GRP file format?
*
* The ".grp" file format is just a collection of a lot of files stored
* into 1 big one. I tried to make the format as simple as possible: The
* first 12 bytes contains my name, "KenSilverman". The next 4 bytes is
* the number of files that were compacted into the group file. Then for
* each file, there is a 16 byte structure, where the first 12 bytes are
* the filename, and the last 4 bytes are the file's size. The rest of
* the group file is just the raw data packed one after the other in the
* same order as the list of files.
*
* (That info is from http://www.advsys.net/ken/build.htm ...)
*
* Please see the file LICENSE.txt in the source's root directory.
*
* This file written by Ryan C. Gordon.
*/
#define __PHYSICSFS_INTERNAL__
#include "physfs_internal.h"
#if PHYSFS_SUPPORTS_GRP
static int grpLoadEntries(PHYSFS_Io *io, const PHYSFS_uint32 count, void *arc)
{
PHYSFS_uint32 pos = 16 + (16 * count); /* past sig+metadata. */
PHYSFS_uint32 i;
for (i = 0; i < count; i++)
{
char *ptr;
char name[13];
PHYSFS_uint32 size;
BAIL_IF_ERRPASS(!__PHYSFS_readAll(io, name, 12), 0);
BAIL_IF_ERRPASS(!__PHYSFS_readAll(io, &size, 4), 0);
name[12] = '\0'; /* name isn't null-terminated in file. */
if ((ptr = strchr(name, ' ')) != NULL)
*ptr = '\0'; /* trim extra spaces. */
size = PHYSFS_swapULE32(size);
BAIL_IF_ERRPASS(!UNPK_addEntry(arc, name, 0, -1, -1, pos, size), 0);
pos += size;
} /* for */
return 1;
} /* grpLoadEntries */
static void *GRP_openArchive(PHYSFS_Io *io, const char *name,
int forWriting, int *claimed)
{
PHYSFS_uint8 buf[12];
PHYSFS_uint32 count = 0;
void *unpkarc = NULL;
assert(io != NULL); /* shouldn't ever happen. */
BAIL_IF(forWriting, PHYSFS_ERR_READ_ONLY, NULL);
BAIL_IF_ERRPASS(!__PHYSFS_readAll(io, buf, sizeof (buf)), NULL);
if (memcmp(buf, "KenSilverman", sizeof (buf)) != 0)
BAIL(PHYSFS_ERR_UNSUPPORTED, NULL);
*claimed = 1;
BAIL_IF_ERRPASS(!__PHYSFS_readAll(io, &count, sizeof(count)), NULL);
count = PHYSFS_swapULE32(count);
unpkarc = UNPK_openArchive(io);
BAIL_IF_ERRPASS(!unpkarc, NULL);
if (!grpLoadEntries(io, count, unpkarc))
{
UNPK_abandonArchive(unpkarc);
return NULL;
} /* if */
return unpkarc;
} /* GRP_openArchive */
const PHYSFS_Archiver __PHYSFS_Archiver_GRP =
{
CURRENT_PHYSFS_ARCHIVER_API_VERSION,
{
"GRP",
"Build engine Groupfile format",
"Ryan C. Gordon <icculus@icculus.org>",
"https://icculus.org/physfs/",
0, /* supportsSymlinks */
},
GRP_openArchive,
UNPK_enumerate,
UNPK_openRead,
UNPK_openWrite,
UNPK_openAppend,
UNPK_remove,
UNPK_mkdir,
UNPK_stat,
UNPK_closeArchive
};
#endif /* defined PHYSFS_SUPPORTS_GRP */
/* end of physfs_archiver_grp.c ... */

114
src/physfs_archiver_hog.c Executable file
View File

@ -0,0 +1,114 @@
/*
* HOG support routines for PhysicsFS.
*
* This driver handles Descent I/II HOG archives.
*
* The format is very simple:
*
* The file always starts with the 3-byte signature "DHF" (Descent
* HOG file). After that the files of a HOG are just attached after
* another, divided by a 17 bytes header, which specifies the name
* and length (in bytes) of the forthcoming file! So you just read
* the header with its information of how big the following file is,
* and then skip exact that number of bytes to get to the next file
* in that HOG.
*
* char sig[3] = {'D', 'H', 'F'}; // "DHF"=Descent HOG File
*
* struct {
* char file_name[13]; // Filename, padded to 13 bytes with 0s
* int file_size; // filesize in bytes
* char data[file_size]; // The file data
* } FILE_STRUCT; // Repeated until the end of the file.
*
* (That info is from http://www.descent2.com/ddn/specs/hog/)
*
* Please see the file LICENSE.txt in the source's root directory.
*
* This file written by Bradley Bell.
* Based on grp.c by Ryan C. Gordon.
*/
#define __PHYSICSFS_INTERNAL__
#include "physfs_internal.h"
#if PHYSFS_SUPPORTS_HOG
static int hogLoadEntries(PHYSFS_Io *io, void *arc)
{
const PHYSFS_uint64 iolen = io->length(io);
PHYSFS_uint32 pos = 3;
while (pos < iolen)
{
PHYSFS_uint32 size;
char name[13];
BAIL_IF_ERRPASS(!__PHYSFS_readAll(io, name, 13), 0);
BAIL_IF_ERRPASS(!__PHYSFS_readAll(io, &size, 4), 0);
name[12] = '\0'; /* just in case. */
pos += 13 + 4;
size = PHYSFS_swapULE32(size);
BAIL_IF_ERRPASS(!UNPK_addEntry(arc, name, 0, -1, -1, pos, size), 0);
pos += size;
/* skip over entry */
BAIL_IF_ERRPASS(!io->seek(io, pos), 0);
} /* while */
return 1;
} /* hogLoadEntries */
static void *HOG_openArchive(PHYSFS_Io *io, const char *name,
int forWriting, int *claimed)
{
PHYSFS_uint8 buf[3];
void *unpkarc = NULL;
assert(io != NULL); /* shouldn't ever happen. */
BAIL_IF(forWriting, PHYSFS_ERR_READ_ONLY, NULL);
BAIL_IF_ERRPASS(!__PHYSFS_readAll(io, buf, 3), NULL);
BAIL_IF(memcmp(buf, "DHF", 3) != 0, PHYSFS_ERR_UNSUPPORTED, NULL);
*claimed = 1;
unpkarc = UNPK_openArchive(io);
BAIL_IF_ERRPASS(!unpkarc, NULL);
if (!hogLoadEntries(io, unpkarc))
{
UNPK_abandonArchive(unpkarc);
return NULL;
} /* if */
return unpkarc;
} /* HOG_openArchive */
const PHYSFS_Archiver __PHYSFS_Archiver_HOG =
{
CURRENT_PHYSFS_ARCHIVER_API_VERSION,
{
"HOG",
"Descent I/II HOG file format",
"Bradley Bell <btb@icculus.org>",
"https://icculus.org/physfs/",
0, /* supportsSymlinks */
},
HOG_openArchive,
UNPK_enumerate,
UNPK_openRead,
UNPK_openWrite,
UNPK_openAppend,
UNPK_remove,
UNPK_mkdir,
UNPK_stat,
UNPK_closeArchive
};
#endif /* defined PHYSFS_SUPPORTS_HOG */
/* end of physfs_archiver_hog.c ... */

386
src/physfs_archiver_iso9660.c Executable file
View File

@ -0,0 +1,386 @@
/*
* ISO9660 support routines for PhysicsFS.
*
* Please see the file LICENSE.txt in the source's root directory.
*
* This file originally written by Christoph Nelles, but was largely
* rewritten by Ryan C. Gordon (so please harass Ryan about bugs and not
* Christoph).
*/
/*
* Handles CD-ROM disk images (and raw CD-ROM devices).
*
* Not supported:
* - Rock Ridge (needed for sparse files, device nodes and symlinks, etc).
* - Non 2048 Sectors
* - TRANS.TBL (maps 8.3 filenames on old discs to long filenames).
* - Multiextents (4gb max file size without it).
* - UDF
*
* Deviations from the standard
* - Ignores mandatory sort order
* - Allows various invalid file names
*
* Problems
* - Ambiguities in the standard
*/
#define __PHYSICSFS_INTERNAL__
#include "physfs_internal.h"
#if PHYSFS_SUPPORTS_ISO9660
#include <time.h>
/* ISO9660 often stores values in both big and little endian formats: little
first, followed by big. While technically there might be different values
in each, we just always use the littleendian ones and swap ourselves. The
fields aren't aligned anyhow, so you have to serialize them in any case
to avoid crashes on many CPU archs in any case. */
static int iso9660LoadEntries(PHYSFS_Io *io, const int joliet,
const char *base, const PHYSFS_uint64 dirstart,
const PHYSFS_uint64 dirend, void *unpkarc);
static int iso9660AddEntry(PHYSFS_Io *io, const int joliet, const int isdir,
const char *base, PHYSFS_uint8 *fname,
const int fnamelen, const PHYSFS_sint64 ts,
const PHYSFS_uint64 pos, const PHYSFS_uint64 len,
void *unpkarc)
{
char *fullpath;
char *fnamecpy;
size_t baselen;
size_t fullpathlen;
void *entry;
int i;
if (fnamelen == 1 && ((fname[0] == 0) || (fname[0] == 1)))
return 1; /* Magic that represents "." and "..", ignore */
BAIL_IF(fnamelen == 0, PHYSFS_ERR_CORRUPT, 0);
assert(fnamelen > 0);
assert(fnamelen <= 255);
BAIL_IF(joliet && (fnamelen % 2), PHYSFS_ERR_CORRUPT, 0);
/* Joliet is UCS-2, so at most UTF-8 will double the byte size */
baselen = strlen(base);
fullpathlen = baselen + (fnamelen * (joliet ? 2 : 1)) + 2;
fullpath = (char *) __PHYSFS_smallAlloc(fullpathlen);
BAIL_IF(!fullpath, PHYSFS_ERR_OUT_OF_MEMORY, 0);
fnamecpy = fullpath;
if (baselen > 0)
{
snprintf(fullpath, fullpathlen, "%s/", base);
fnamecpy += baselen + 1;
fullpathlen -= baselen - 1;
} /* if */
if (joliet)
{
PHYSFS_uint16 *ucs2 = (PHYSFS_uint16 *) fname;
int total = fnamelen / 2;
for (i = 0; i < total; i++)
ucs2[i] = PHYSFS_swapUBE16(ucs2[i]);
ucs2[total] = '\0';
PHYSFS_utf8FromUcs2(ucs2, fnamecpy, fullpathlen);
} /* if */
else
{
for (i = 0; i < fnamelen; i++)
{
/* We assume the filenames are low-ASCII; consider the archive
corrupt if we see something above 127, since we don't know the
encoding. (We can change this later if we find out these exist
and are intended to be, say, latin-1 or UTF-8 encoding). */
BAIL_IF(fname[i] > 127, PHYSFS_ERR_CORRUPT, 0);
fnamecpy[i] = fname[i];
} /* for */
fnamecpy[fnamelen] = '\0';
if (!isdir)
{
/* find last SEPARATOR2 */
char *ptr = strrchr(fnamecpy, ';');
if (ptr && (ptr != fnamecpy))
*(ptr--) = '\0';
else
ptr = fnamecpy + (fnamelen - 1);
/* chop out any trailing '.', as done in all implementations */
if (*ptr == '.')
*ptr = '\0';
} /* if */
} /* else */
entry = UNPK_addEntry(unpkarc, fullpath, isdir, ts, ts, pos, len);
if ((entry) && (isdir))
{
if (!iso9660LoadEntries(io, joliet, fullpath, pos, pos + len, unpkarc))
entry = NULL; /* so we report a failure later. */
} /* if */
__PHYSFS_smallFree(fullpath);
return entry != NULL;
} /* iso9660AddEntry */
static int iso9660LoadEntries(PHYSFS_Io *io, const int joliet,
const char *base, const PHYSFS_uint64 dirstart,
const PHYSFS_uint64 dirend, void *unpkarc)
{
PHYSFS_uint64 readpos = dirstart;
while (1)
{
PHYSFS_uint8 recordlen;
PHYSFS_uint8 extattrlen;
PHYSFS_uint32 extent;
PHYSFS_uint32 datalen;
PHYSFS_uint8 ignore[4];
PHYSFS_uint8 year, month, day, hour, minute, second, offset;
PHYSFS_uint8 flags;
PHYSFS_uint8 fnamelen;
PHYSFS_uint8 fname[256];
PHYSFS_sint64 timestamp;
struct tm t;
int isdir;
int multiextent;
BAIL_IF_ERRPASS(!io->seek(io, readpos), 0);
/* recordlen = 0 -> no more entries or fill entry */
BAIL_IF_ERRPASS(!__PHYSFS_readAll(io, &recordlen, 1), 0);
if (recordlen > 0)
readpos += recordlen; /* ready to seek to next record. */
else
{
PHYSFS_uint64 nextpos;
/* if we are in the last sector of the directory & it's 0 -> end */
if ((dirend - 2048) <= (readpos - 1))
break; /* finished */
/* else skip to the next sector & continue; */
nextpos = (((readpos - 1) / 2048) + 1) * 2048;
/* whoops, can't make forward progress! */
BAIL_IF(nextpos == readpos, PHYSFS_ERR_CORRUPT, 0);
readpos = nextpos;
continue; /* start back at upper loop. */
} /* else */
BAIL_IF_ERRPASS(!__PHYSFS_readAll(io, &extattrlen, 1), 0);
BAIL_IF_ERRPASS(!__PHYSFS_readAll(io, &extent, 4), 0);
BAIL_IF_ERRPASS(!__PHYSFS_readAll(io, ignore, 4), 0); /* extent be */
BAIL_IF_ERRPASS(!__PHYSFS_readAll(io, &datalen, 4), 0);
BAIL_IF_ERRPASS(!__PHYSFS_readAll(io, ignore, 4), 0); /* datalen be */
/* record timestamp */
BAIL_IF_ERRPASS(!__PHYSFS_readAll(io, &year, 1), 0);
BAIL_IF_ERRPASS(!__PHYSFS_readAll(io, &month, 1), 0);
BAIL_IF_ERRPASS(!__PHYSFS_readAll(io, &day, 1), 0);
BAIL_IF_ERRPASS(!__PHYSFS_readAll(io, &hour, 1), 0);
BAIL_IF_ERRPASS(!__PHYSFS_readAll(io, &minute, 1), 0);
BAIL_IF_ERRPASS(!__PHYSFS_readAll(io, &second, 1), 0);
BAIL_IF_ERRPASS(!__PHYSFS_readAll(io, &offset, 1), 0);
BAIL_IF_ERRPASS(!__PHYSFS_readAll(io, &flags, 1), 0);
isdir = (flags & (1 << 1)) != 0;
multiextent = (flags & (1 << 7)) != 0;
BAIL_IF(multiextent, PHYSFS_ERR_UNSUPPORTED, 0); /* !!! FIXME */
BAIL_IF_ERRPASS(!__PHYSFS_readAll(io, ignore, 1), 0); /* unit size */
BAIL_IF_ERRPASS(!__PHYSFS_readAll(io, ignore, 1), 0); /* interleave gap */
BAIL_IF_ERRPASS(!__PHYSFS_readAll(io, ignore, 2), 0); /* seqnum le */
BAIL_IF_ERRPASS(!__PHYSFS_readAll(io, ignore, 2), 0); /* seqnum be */
BAIL_IF_ERRPASS(!__PHYSFS_readAll(io, &fnamelen, 1), 0);
BAIL_IF_ERRPASS(!__PHYSFS_readAll(io, fname, fnamelen), 0);
t.tm_sec = second;
t.tm_min = minute;
t.tm_hour = hour;
t.tm_mday = day;
t.tm_mon = month - 1;
t.tm_year = year;
t.tm_wday = 0;
t.tm_yday = 0;
t.tm_isdst = -1;
timestamp = (PHYSFS_sint64) mktime(&t);
extent += extattrlen; /* skip extended attribute record. */
/* infinite loop, corrupt file? */
BAIL_IF((extent * 2048) == dirstart, PHYSFS_ERR_CORRUPT, 0);
if (!iso9660AddEntry(io, joliet, isdir, base, fname, fnamelen,
timestamp, extent * 2048, datalen, unpkarc))
{
return 0;
} /* if */
} /* while */
return 1;
} /* iso9660LoadEntries */
static int parseVolumeDescriptor(PHYSFS_Io *io, PHYSFS_uint64 *_rootpos,
PHYSFS_uint64 *_rootlen, int *_joliet,
int *_claimed)
{
PHYSFS_uint64 pos = 32768; /* start at the Primary Volume Descriptor */
int found = 0;
int done = 0;
*_joliet = 0;
while (!done)
{
PHYSFS_uint8 type;
PHYSFS_uint8 identifier[5];
PHYSFS_uint8 version;
PHYSFS_uint8 flags;
PHYSFS_uint8 escapeseqs[32];
PHYSFS_uint8 ignore[32];
PHYSFS_uint16 blocksize;
PHYSFS_uint32 extent;
PHYSFS_uint32 datalen;
BAIL_IF_ERRPASS(!io->seek(io, pos), 0);
pos += 2048; /* each volume descriptor is 2048 bytes */
BAIL_IF_ERRPASS(!__PHYSFS_readAll(io, &type, 1), 0);
BAIL_IF_ERRPASS(!__PHYSFS_readAll(io, &identifier, 5), 0);
if (memcmp(identifier, "CD001", 5) != 0) /* maybe not an iso? */
{
BAIL_IF(!*_claimed, PHYSFS_ERR_UNSUPPORTED, 0);
continue; /* just skip this one */
} /* if */
*_claimed = 1; /* okay, this is probably an iso. */
BAIL_IF_ERRPASS(!__PHYSFS_readAll(io, &version, 1), 0); /* version */
BAIL_IF(version != 1, PHYSFS_ERR_UNSUPPORTED, 0);
BAIL_IF_ERRPASS(!__PHYSFS_readAll(io, &flags, 1), 0);
BAIL_IF_ERRPASS(!__PHYSFS_readAll(io, ignore, 32), 0); /* system id */
BAIL_IF_ERRPASS(!__PHYSFS_readAll(io, ignore, 32), 0); /* volume id */
BAIL_IF_ERRPASS(!__PHYSFS_readAll(io, ignore, 8), 0); /* reserved */
BAIL_IF_ERRPASS(!__PHYSFS_readAll(io, ignore, 4), 0); /* space le */
BAIL_IF_ERRPASS(!__PHYSFS_readAll(io, ignore, 4), 0); /* space be */
BAIL_IF_ERRPASS(!__PHYSFS_readAll(io, escapeseqs, 32), 0);
BAIL_IF_ERRPASS(!__PHYSFS_readAll(io, ignore, 2), 0); /* setsize le */
BAIL_IF_ERRPASS(!__PHYSFS_readAll(io, ignore, 2), 0); /* setsize be */
BAIL_IF_ERRPASS(!__PHYSFS_readAll(io, ignore, 2), 0); /* seq num le */
BAIL_IF_ERRPASS(!__PHYSFS_readAll(io, ignore, 2), 0); /* seq num be */
BAIL_IF_ERRPASS(!__PHYSFS_readAll(io, &blocksize, 2), 0);
BAIL_IF_ERRPASS(!__PHYSFS_readAll(io, ignore, 2), 0); /* blocklen be */
BAIL_IF_ERRPASS(!__PHYSFS_readAll(io, ignore, 4), 0); /* pthtablen le */
BAIL_IF_ERRPASS(!__PHYSFS_readAll(io, ignore, 4), 0); /* pthtablen be */
BAIL_IF_ERRPASS(!__PHYSFS_readAll(io, ignore, 4), 0); /* pthtabpos le */
BAIL_IF_ERRPASS(!__PHYSFS_readAll(io, ignore, 4), 0); /* optpthtabpos le */
BAIL_IF_ERRPASS(!__PHYSFS_readAll(io, ignore, 4), 0); /* pthtabpos be */
BAIL_IF_ERRPASS(!__PHYSFS_readAll(io, ignore, 4), 0); /* optpthtabpos be */
/* root directory record... */
BAIL_IF_ERRPASS(!__PHYSFS_readAll(io, ignore, 1), 0); /* len */
BAIL_IF_ERRPASS(!__PHYSFS_readAll(io, ignore, 1), 0); /* attr len */
BAIL_IF_ERRPASS(!__PHYSFS_readAll(io, &extent, 4), 0);
BAIL_IF_ERRPASS(!__PHYSFS_readAll(io, ignore, 4), 0); /* extent be */
BAIL_IF_ERRPASS(!__PHYSFS_readAll(io, &datalen, 4), 0);
BAIL_IF_ERRPASS(!__PHYSFS_readAll(io, ignore, 4), 0); /* datalen be */
/* !!! FIXME: deal with this properly. */
blocksize = PHYSFS_swapULE32(blocksize);
BAIL_IF(blocksize && (blocksize != 2048), PHYSFS_ERR_UNSUPPORTED, 0);
switch (type)
{
case 1: /* Primary Volume Descriptor */
case 2: /* Supplementary Volume Descriptor */
if (found < type)
{
*_rootpos = PHYSFS_swapULE32(extent) * 2048;
*_rootlen = PHYSFS_swapULE32(datalen);
found = type;
if (found == 2) /* possible Joliet volume */
{
const PHYSFS_uint8 *s = escapeseqs;
*_joliet = !(flags & 1) &&
(s[0] == 0x25) && (s[1] == 0x2F) &&
((s[2] == 0x40) || (s[2] == 0x43) || (s[2] == 0x45));
} /* if */
} /* if */
break;
case 255: /* type 255 terminates the volume descriptor list */
done = 1;
break;
default:
break; /* skip unknown types. */
} /* switch */
} /* while */
BAIL_IF(!found, PHYSFS_ERR_CORRUPT, 0);
return 1;
} /* parseVolumeDescriptor */
static void *ISO9660_openArchive(PHYSFS_Io *io, const char *filename,
int forWriting, int *claimed)
{
PHYSFS_uint64 rootpos = 0;
PHYSFS_uint64 len = 0;
int joliet = 0;
void *unpkarc = NULL;
assert(io != NULL); /* shouldn't ever happen. */
BAIL_IF(forWriting, PHYSFS_ERR_READ_ONLY, NULL);
if (!parseVolumeDescriptor(io, &rootpos, &len, &joliet, claimed))
return NULL;
unpkarc = UNPK_openArchive(io);
BAIL_IF_ERRPASS(!unpkarc, NULL);
if (!iso9660LoadEntries(io, joliet, "", rootpos, rootpos + len, unpkarc))
{
UNPK_abandonArchive(unpkarc);
return NULL;
} /* if */
return unpkarc;
} /* ISO9660_openArchive */
const PHYSFS_Archiver __PHYSFS_Archiver_ISO9660 =
{
CURRENT_PHYSFS_ARCHIVER_API_VERSION,
{
"ISO",
"ISO9660 image file",
"Ryan C. Gordon <icculus@icculus.org>",
"https://icculus.org/physfs/",
0, /* supportsSymlinks */
},
ISO9660_openArchive,
UNPK_enumerate,
UNPK_openRead,
UNPK_openWrite,
UNPK_openAppend,
UNPK_remove,
UNPK_mkdir,
UNPK_stat,
UNPK_closeArchive
};
#endif /* defined PHYSFS_SUPPORTS_ISO9660 */
/* end of physfs_archiver_iso9660.c ... */

110
src/physfs_archiver_mvl.c Executable file
View File

@ -0,0 +1,110 @@
/*
* MVL support routines for PhysicsFS.
*
* This driver handles Descent II Movielib archives.
*
* The file format of MVL is quite easy...
*
* //MVL File format - Written by Heiko Herrmann
* char sig[4] = {'D','M', 'V', 'L'}; // "DMVL"=Descent MoVie Library
*
* int num_files; // the number of files in this MVL
*
* struct {
* char file_name[13]; // Filename, padded to 13 bytes with 0s
* int file_size; // filesize in bytes
* }DIR_STRUCT[num_files];
*
* struct {
* char data[file_size]; // The file data
* }FILE_STRUCT[num_files];
*
* (That info is from http://www.descent2.com/ddn/specs/mvl/)
*
* Please see the file LICENSE.txt in the source's root directory.
*
* This file written by Bradley Bell.
* Based on grp.c by Ryan C. Gordon.
*/
#define __PHYSICSFS_INTERNAL__
#include "physfs_internal.h"
#if PHYSFS_SUPPORTS_MVL
static int mvlLoadEntries(PHYSFS_Io *io, const PHYSFS_uint32 count, void *arc)
{
PHYSFS_uint32 pos = 8 + (17 * count); /* past sig+metadata. */
PHYSFS_uint32 i;
for (i = 0; i < count; i++)
{
PHYSFS_uint32 size;
char name[13];
BAIL_IF_ERRPASS(!__PHYSFS_readAll(io, name, 13), 0);
BAIL_IF_ERRPASS(!__PHYSFS_readAll(io, &size, 4), 0);
name[12] = '\0'; /* just in case. */
size = PHYSFS_swapULE32(size);
BAIL_IF_ERRPASS(!UNPK_addEntry(arc, name, 0, -1, -1, pos, size), 0);
pos += size;
} /* for */
return 1;
} /* mvlLoadEntries */
static void *MVL_openArchive(PHYSFS_Io *io, const char *name,
int forWriting, int *claimed)
{
PHYSFS_uint8 buf[4];
PHYSFS_uint32 count = 0;
void *unpkarc;
assert(io != NULL); /* shouldn't ever happen. */
BAIL_IF(forWriting, PHYSFS_ERR_READ_ONLY, NULL);
BAIL_IF_ERRPASS(!__PHYSFS_readAll(io, buf, 4), NULL);
BAIL_IF(memcmp(buf, "DMVL", 4) != 0, PHYSFS_ERR_UNSUPPORTED, NULL);
*claimed = 1;
BAIL_IF_ERRPASS(!__PHYSFS_readAll(io, &count, sizeof(count)), NULL);
count = PHYSFS_swapULE32(count);
unpkarc = UNPK_openArchive(io);
BAIL_IF_ERRPASS(!unpkarc, NULL);
if (!mvlLoadEntries(io, count, unpkarc))
{
UNPK_abandonArchive(unpkarc);
return NULL;
} /* if */
return unpkarc;
} /* MVL_openArchive */
const PHYSFS_Archiver __PHYSFS_Archiver_MVL =
{
CURRENT_PHYSFS_ARCHIVER_API_VERSION,
{
"MVL",
"Descent II Movielib format",
"Bradley Bell <btb@icculus.org>",
"https://icculus.org/physfs/",
0, /* supportsSymlinks */
},
MVL_openArchive,
UNPK_enumerate,
UNPK_openRead,
UNPK_openWrite,
UNPK_openAppend,
UNPK_remove,
UNPK_mkdir,
UNPK_stat,
UNPK_closeArchive
};
#endif /* defined PHYSFS_SUPPORTS_MVL */
/* end of physfs_archiver_mvl.c ... */

126
src/physfs_archiver_qpak.c Executable file
View File

@ -0,0 +1,126 @@
/*
* QPAK support routines for PhysicsFS.
*
* This archiver handles the archive format utilized by Quake 1 and 2.
* Quake3-based games use the PkZip/Info-Zip format (which our
* physfs_archiver_zip.c handles).
*
* ========================================================================
*
* This format info (in more detail) comes from:
* https://web.archive.org/web/20040209101748/http://debian.fmi.uni-sofia.bg/~sergei/cgsr/docs/pak.txt
*
* Quake PAK Format
*
* Header
* (4 bytes) signature = 'PACK'
* (4 bytes) directory offset
* (4 bytes) directory length
*
* Directory
* (56 bytes) file name
* (4 bytes) file position
* (4 bytes) file length
*
* ========================================================================
*
* Please see the file LICENSE.txt in the source's root directory.
*
* This file written by Ryan C. Gordon.
*/
#define __PHYSICSFS_INTERNAL__
#include "physfs_internal.h"
#if PHYSFS_SUPPORTS_QPAK
#define QPAK_SIG 0x4B434150 /* "PACK" in ASCII. */
static int qpakLoadEntries(PHYSFS_Io *io, const PHYSFS_uint32 count, void *arc)
{
PHYSFS_uint32 i;
for (i = 0; i < count; i++)
{
PHYSFS_uint32 size;
PHYSFS_uint32 pos;
char name[56];
BAIL_IF_ERRPASS(!__PHYSFS_readAll(io, name, 56), 0);
BAIL_IF_ERRPASS(!__PHYSFS_readAll(io, &pos, 4), 0);
BAIL_IF_ERRPASS(!__PHYSFS_readAll(io, &size, 4), 0);
size = PHYSFS_swapULE32(size);
pos = PHYSFS_swapULE32(pos);
BAIL_IF_ERRPASS(!UNPK_addEntry(arc, name, 0, -1, -1, pos, size), 0);
} /* for */
return 1;
} /* qpakLoadEntries */
static void *QPAK_openArchive(PHYSFS_Io *io, const char *name,
int forWriting, int *claimed)
{
PHYSFS_uint32 val = 0;
PHYSFS_uint32 pos = 0;
PHYSFS_uint32 count = 0;
void *unpkarc;
assert(io != NULL); /* shouldn't ever happen. */
BAIL_IF(forWriting, PHYSFS_ERR_READ_ONLY, NULL);
BAIL_IF_ERRPASS(!__PHYSFS_readAll(io, &val, 4), NULL);
if (PHYSFS_swapULE32(val) != QPAK_SIG)
BAIL(PHYSFS_ERR_UNSUPPORTED, NULL);
*claimed = 1;
BAIL_IF_ERRPASS(!__PHYSFS_readAll(io, &val, 4), NULL);
pos = PHYSFS_swapULE32(val); /* directory table offset. */
BAIL_IF_ERRPASS(!__PHYSFS_readAll(io, &val, 4), NULL);
count = PHYSFS_swapULE32(val);
/* corrupted archive? */
BAIL_IF((count % 64) != 0, PHYSFS_ERR_CORRUPT, NULL);
count /= 64;
BAIL_IF_ERRPASS(!io->seek(io, pos), NULL);
unpkarc = UNPK_openArchive(io);
BAIL_IF_ERRPASS(!unpkarc, NULL);
if (!qpakLoadEntries(io, count, unpkarc))
{
UNPK_abandonArchive(unpkarc);
return NULL;
} /* if */
return unpkarc;
} /* QPAK_openArchive */
const PHYSFS_Archiver __PHYSFS_Archiver_QPAK =
{
CURRENT_PHYSFS_ARCHIVER_API_VERSION,
{
"PAK",
"Quake I/II format",
"Ryan C. Gordon <icculus@icculus.org>",
"https://icculus.org/physfs/",
0, /* supportsSymlinks */
},
QPAK_openArchive,
UNPK_enumerate,
UNPK_openRead,
UNPK_openWrite,
UNPK_openAppend,
UNPK_remove,
UNPK_mkdir,
UNPK_stat,
UNPK_closeArchive
};
#endif /* defined PHYSFS_SUPPORTS_QPAK */
/* end of physfs_archiver_qpak.c ... */

135
src/physfs_archiver_slb.c Executable file
View File

@ -0,0 +1,135 @@
/*
* SLB support routines for PhysicsFS.
*
* This driver handles SLB archives ("slab files"). This uncompressed format
* is used in I-War / Independence War and Independence War: Defiance.
*
* The format begins with four zero bytes (version?), the file count and the
* location of the table of contents. Each ToC entry contains a 64-byte buffer
* containing a zero-terminated filename, the offset of the data, and its size.
* All the filenames begin with the separator character '\'.
*
* Please see the file LICENSE.txt in the source's root directory.
*
* This file written by Aleksi Nurmi, based on the GRP archiver by
* Ryan C. Gordon.
*/
#define __PHYSICSFS_INTERNAL__
#include "physfs_internal.h"
#if PHYSFS_SUPPORTS_SLB
static int slbLoadEntries(PHYSFS_Io *io, const PHYSFS_uint32 count, void *arc)
{
PHYSFS_uint32 i;
for (i = 0; i < count; i++)
{
PHYSFS_uint32 pos;
PHYSFS_uint32 size;
char name[64];
char backslash;
char *ptr;
/* don't include the '\' in the beginning */
BAIL_IF_ERRPASS(!__PHYSFS_readAll(io, &backslash, 1), 0);
BAIL_IF(backslash != '\\', PHYSFS_ERR_CORRUPT, 0);
/* read the rest of the buffer, 63 bytes */
BAIL_IF_ERRPASS(!__PHYSFS_readAll(io, &name, 63), 0);
name[63] = '\0'; /* in case the name lacks the null terminator */
/* convert backslashes */
for (ptr = name; *ptr; ptr++)
{
if (*ptr == '\\')
*ptr = '/';
} /* for */
BAIL_IF_ERRPASS(!__PHYSFS_readAll(io, &pos, 4), 0);
pos = PHYSFS_swapULE32(pos);
BAIL_IF_ERRPASS(!__PHYSFS_readAll(io, &size, 4), 0);
size = PHYSFS_swapULE32(size);
BAIL_IF_ERRPASS(!UNPK_addEntry(arc, name, 0, -1, -1, pos, size), 0);
} /* for */
return 1;
} /* slbLoadEntries */
static void *SLB_openArchive(PHYSFS_Io *io, const char *name,
int forWriting, int *claimed)
{
PHYSFS_uint32 version;
PHYSFS_uint32 count;
PHYSFS_uint32 tocPos;
void *unpkarc;
/* There's no identifier on an SLB file, so we assume it's _not_ if the
file count or tocPos is zero. Beyond that, we'll assume it's
bogus/corrupt if the entries' filenames don't start with '\' or the
tocPos is past the end of the file (seek will fail). This probably
covers all meaningful cases where we would accidentally accept a non-SLB
file with this archiver. */
assert(io != NULL); /* shouldn't ever happen. */
BAIL_IF(forWriting, PHYSFS_ERR_READ_ONLY, NULL);
BAIL_IF_ERRPASS(!__PHYSFS_readAll(io, &version, sizeof (version)), NULL);
version = PHYSFS_swapULE32(version);
BAIL_IF(version != 0, PHYSFS_ERR_UNSUPPORTED, NULL);
BAIL_IF_ERRPASS(!__PHYSFS_readAll(io, &count, sizeof (count)), NULL);
count = PHYSFS_swapULE32(count);
BAIL_IF(!count, PHYSFS_ERR_UNSUPPORTED, NULL);
/* offset of the table of contents */
BAIL_IF_ERRPASS(!__PHYSFS_readAll(io, &tocPos, sizeof (tocPos)), NULL);
tocPos = PHYSFS_swapULE32(tocPos);
BAIL_IF(!tocPos, PHYSFS_ERR_UNSUPPORTED, NULL);
/* seek to the table of contents */
BAIL_IF_ERRPASS(!io->seek(io, tocPos), NULL);
unpkarc = UNPK_openArchive(io);
BAIL_IF_ERRPASS(!unpkarc, NULL);
if (!slbLoadEntries(io, count, unpkarc))
{
UNPK_abandonArchive(unpkarc);
return NULL;
} /* if */
*claimed = 1; /* oh well. */
return unpkarc;
} /* SLB_openArchive */
const PHYSFS_Archiver __PHYSFS_Archiver_SLB =
{
CURRENT_PHYSFS_ARCHIVER_API_VERSION,
{
"SLB",
"I-War / Independence War Slab file",
"Aleksi Nurmi <aleksi.nurmi@gmail.com>",
"https://bitbucket.org/ahnurmi/",
0, /* supportsSymlinks */
},
SLB_openArchive,
UNPK_enumerate,
UNPK_openRead,
UNPK_openWrite,
UNPK_openAppend,
UNPK_remove,
UNPK_mkdir,
UNPK_stat,
UNPK_closeArchive
};
#endif /* defined PHYSFS_SUPPORTS_SLB */
/* end of physfs_archiver_slb.c ... */

305
src/physfs_archiver_unpacked.c Executable file
View File

@ -0,0 +1,305 @@
/*
* High-level PhysicsFS archiver for simple unpacked file formats.
*
* This is a framework that basic archivers build on top of. It's for simple
* formats that can just hand back a list of files and the offsets of their
* uncompressed data. There are an alarming number of formats like this.
*
* RULES: Archive entries must be uncompressed. Dirs and files allowed, but no
* symlinks, etc. We can relax some of these rules as necessary.
*
* Please see the file LICENSE.txt in the source's root directory.
*
* This file written by Ryan C. Gordon.
*/
#define __PHYSICSFS_INTERNAL__
#include "physfs_internal.h"
typedef struct
{
__PHYSFS_DirTree tree;
PHYSFS_Io *io;
} UNPKinfo;
typedef struct
{
__PHYSFS_DirTreeEntry tree;
PHYSFS_uint64 startPos;
PHYSFS_uint64 size;
PHYSFS_sint64 ctime;
PHYSFS_sint64 mtime;
} UNPKentry;
typedef struct
{
PHYSFS_Io *io;
UNPKentry *entry;
PHYSFS_uint32 curPos;
} UNPKfileinfo;
void UNPK_closeArchive(void *opaque)
{
UNPKinfo *info = ((UNPKinfo *) opaque);
if (info)
{
__PHYSFS_DirTreeDeinit(&info->tree);
if (info->io)
info->io->destroy(info->io);
allocator.Free(info);
} /* if */
} /* UNPK_closeArchive */
void UNPK_abandonArchive(void *opaque)
{
UNPKinfo *info = ((UNPKinfo *) opaque);
if (info)
{
info->io = NULL;
UNPK_closeArchive(info);
} /* if */
} /* UNPK_abandonArchive */
static PHYSFS_sint64 UNPK_read(PHYSFS_Io *io, void *buffer, PHYSFS_uint64 len)
{
UNPKfileinfo *finfo = (UNPKfileinfo *) io->opaque;
const UNPKentry *entry = finfo->entry;
const PHYSFS_uint64 bytesLeft = (PHYSFS_uint64)(entry->size-finfo->curPos);
PHYSFS_sint64 rc;
if (bytesLeft < len)
len = bytesLeft;
rc = finfo->io->read(finfo->io, buffer, len);
if (rc > 0)
finfo->curPos += (PHYSFS_uint32) rc;
return rc;
} /* UNPK_read */
static PHYSFS_sint64 UNPK_write(PHYSFS_Io *io, const void *b, PHYSFS_uint64 len)
{
BAIL(PHYSFS_ERR_READ_ONLY, -1);
} /* UNPK_write */
static PHYSFS_sint64 UNPK_tell(PHYSFS_Io *io)
{
return ((UNPKfileinfo *) io->opaque)->curPos;
} /* UNPK_tell */
static int UNPK_seek(PHYSFS_Io *io, PHYSFS_uint64 offset)
{
UNPKfileinfo *finfo = (UNPKfileinfo *) io->opaque;
const UNPKentry *entry = finfo->entry;
int rc;
BAIL_IF(offset >= entry->size, PHYSFS_ERR_PAST_EOF, 0);
rc = finfo->io->seek(finfo->io, entry->startPos + offset);
if (rc)
finfo->curPos = (PHYSFS_uint32) offset;
return rc;
} /* UNPK_seek */
static PHYSFS_sint64 UNPK_length(PHYSFS_Io *io)
{
const UNPKfileinfo *finfo = (UNPKfileinfo *) io->opaque;
return ((PHYSFS_sint64) finfo->entry->size);
} /* UNPK_length */
static PHYSFS_Io *UNPK_duplicate(PHYSFS_Io *_io)
{
UNPKfileinfo *origfinfo = (UNPKfileinfo *) _io->opaque;
PHYSFS_Io *io = NULL;
PHYSFS_Io *retval = (PHYSFS_Io *) allocator.Malloc(sizeof (PHYSFS_Io));
UNPKfileinfo *finfo = (UNPKfileinfo *) allocator.Malloc(sizeof (UNPKfileinfo));
GOTO_IF(!retval, PHYSFS_ERR_OUT_OF_MEMORY, UNPK_duplicate_failed);
GOTO_IF(!finfo, PHYSFS_ERR_OUT_OF_MEMORY, UNPK_duplicate_failed);
io = origfinfo->io->duplicate(origfinfo->io);
if (!io) goto UNPK_duplicate_failed;
finfo->io = io;
finfo->entry = origfinfo->entry;
finfo->curPos = 0;
memcpy(retval, _io, sizeof (PHYSFS_Io));
retval->opaque = finfo;
return retval;
UNPK_duplicate_failed:
if (finfo != NULL) allocator.Free(finfo);
if (retval != NULL) allocator.Free(retval);
if (io != NULL) io->destroy(io);
return NULL;
} /* UNPK_duplicate */
static int UNPK_flush(PHYSFS_Io *io) { return 1; /* no write support. */ }
static void UNPK_destroy(PHYSFS_Io *io)
{
UNPKfileinfo *finfo = (UNPKfileinfo *) io->opaque;
finfo->io->destroy(finfo->io);
allocator.Free(finfo);
allocator.Free(io);
} /* UNPK_destroy */
static const PHYSFS_Io UNPK_Io =
{
CURRENT_PHYSFS_IO_API_VERSION, NULL,
UNPK_read,
UNPK_write,
UNPK_seek,
UNPK_tell,
UNPK_length,
UNPK_duplicate,
UNPK_flush,
UNPK_destroy
};
static inline UNPKentry *findEntry(UNPKinfo *info, const char *path)
{
return (UNPKentry *) __PHYSFS_DirTreeFind(&info->tree, path);
} /* findEntry */
PHYSFS_Io *UNPK_openRead(void *opaque, const char *name)
{
PHYSFS_Io *retval = NULL;
UNPKinfo *info = (UNPKinfo *) opaque;
UNPKfileinfo *finfo = NULL;
UNPKentry *entry = findEntry(info, name);
BAIL_IF_ERRPASS(!entry, NULL);
BAIL_IF(entry->tree.isdir, PHYSFS_ERR_NOT_A_FILE, NULL);
retval = (PHYSFS_Io *) allocator.Malloc(sizeof (PHYSFS_Io));
GOTO_IF(!retval, PHYSFS_ERR_OUT_OF_MEMORY, UNPK_openRead_failed);
finfo = (UNPKfileinfo *) allocator.Malloc(sizeof (UNPKfileinfo));
GOTO_IF(!finfo, PHYSFS_ERR_OUT_OF_MEMORY, UNPK_openRead_failed);
finfo->io = info->io->duplicate(info->io);
GOTO_IF_ERRPASS(!finfo->io, UNPK_openRead_failed);
if (!finfo->io->seek(finfo->io, entry->startPos))
goto UNPK_openRead_failed;
finfo->curPos = 0;
finfo->entry = entry;
memcpy(retval, &UNPK_Io, sizeof (*retval));
retval->opaque = finfo;
return retval;
UNPK_openRead_failed:
if (finfo != NULL)
{
if (finfo->io != NULL)
finfo->io->destroy(finfo->io);
allocator.Free(finfo);
} /* if */
if (retval != NULL)
allocator.Free(retval);
return NULL;
} /* UNPK_openRead */
PHYSFS_Io *UNPK_openWrite(void *opaque, const char *name)
{
BAIL(PHYSFS_ERR_READ_ONLY, NULL);
} /* UNPK_openWrite */
PHYSFS_Io *UNPK_openAppend(void *opaque, const char *name)
{
BAIL(PHYSFS_ERR_READ_ONLY, NULL);
} /* UNPK_openAppend */
int UNPK_remove(void *opaque, const char *name)
{
BAIL(PHYSFS_ERR_READ_ONLY, 0);
} /* UNPK_remove */
int UNPK_mkdir(void *opaque, const char *name)
{
BAIL(PHYSFS_ERR_READ_ONLY, 0);
} /* UNPK_mkdir */
int UNPK_stat(void *opaque, const char *path, PHYSFS_Stat *stat)
{
UNPKinfo *info = (UNPKinfo *) opaque;
const UNPKentry *entry = findEntry(info, path);
BAIL_IF_ERRPASS(!entry, 0);
if (entry->tree.isdir)
{
stat->filetype = PHYSFS_FILETYPE_DIRECTORY;
stat->filesize = 0;
} /* if */
else
{
stat->filetype = PHYSFS_FILETYPE_REGULAR;
stat->filesize = entry->size;
} /* else */
stat->modtime = entry->mtime;
stat->createtime = entry->ctime;
stat->accesstime = -1;
stat->readonly = 1;
return 1;
} /* UNPK_stat */
void *UNPK_addEntry(void *opaque, char *name, const int isdir,
const PHYSFS_sint64 ctime, const PHYSFS_sint64 mtime,
const PHYSFS_uint64 pos, const PHYSFS_uint64 len)
{
UNPKinfo *info = (UNPKinfo *) opaque;
UNPKentry *entry;
entry = (UNPKentry *) __PHYSFS_DirTreeAdd(&info->tree, name, isdir);
BAIL_IF_ERRPASS(!entry, NULL);
entry->startPos = isdir ? 0 : pos;
entry->size = isdir ? 0 : len;
entry->ctime = ctime;
entry->mtime = mtime;
return entry;
} /* UNPK_addEntry */
void *UNPK_openArchive(PHYSFS_Io *io)
{
UNPKinfo *info = (UNPKinfo *) allocator.Malloc(sizeof (UNPKinfo));
BAIL_IF(!info, PHYSFS_ERR_OUT_OF_MEMORY, NULL);
if (!__PHYSFS_DirTreeInit(&info->tree, sizeof (UNPKentry)))
{
allocator.Free(info);
return NULL;
} /* if */
info->io = io;
return info;
} /* UNPK_openArchive */
/* end of physfs_archiver_unpacked.c ... */

168
src/physfs_archiver_vdf.c Executable file
View File

@ -0,0 +1,168 @@
/*
* VDF support routines for PhysicsFS.
*
* This driver handles Gothic I/II VDF archives.
* This format (but not this driver) was designed by Piranha Bytes for
* use wih the ZenGin engine.
*
* This file was written by Francesco Bertolaccini, based on the UNPK archiver
* by Ryan C. Gordon and the works of degenerated1123 and Nico Bendlin.
*/
#define __PHYSICSFS_INTERNAL__
#include "physfs_internal.h"
#if PHYSFS_SUPPORTS_VDF
#include <time.h>
#define VDF_COMMENT_LENGTH 256
#define VDF_SIGNATURE_LENGTH 16
#define VDF_ENTRY_NAME_LENGTH 64
#define VDF_ENTRY_DIR 0x80000000
static const char* VDF_SIGNATURE_G1 = "PSVDSC_V2.00\r\n\r\n";
static const char* VDF_SIGNATURE_G2 = "PSVDSC_V2.00\n\r\n\r";
static inline int readui32(PHYSFS_Io *io, PHYSFS_uint32 *val)
{
PHYSFS_uint32 v;
BAIL_IF_ERRPASS(!__PHYSFS_readAll(io, &v, sizeof (v)), 0);
*val = PHYSFS_swapULE32(v);
return 1;
} /* readui32 */
static PHYSFS_sint64 vdfDosTimeToEpoch(const PHYSFS_uint32 dostime)
{
/* VDF stores timestamps as 32bit DOS dates: the seconds are counted in
2-seconds intervals and the years are counted since 1 Jan. 1980 */
struct tm t;
memset(&t, '\0', sizeof (t));
t.tm_year = ((int) ((dostime >> 25) & 0x7F)) + 80; /* 1980 to 1900 */
t.tm_mon = ((int) ((dostime >> 21) & 0xF)) - 1; /* 1-12 to 0-11 */
t.tm_mday = (int) ((dostime >> 16) & 0x1F);
t.tm_hour = (int) ((dostime >> 11) & 0x1F);
t.tm_min = (int) ((dostime >> 5) & 0x3F);
t.tm_sec = ((int) ((dostime >> 0) & 0x1F)) * 2; /* 2 seconds to 1. */
return (PHYSFS_sint64) mktime(&t);
} /* vdfDosTimeToEpoch */
static int vdfLoadEntries(PHYSFS_Io *io, const PHYSFS_uint32 count,
const PHYSFS_sint64 ts, void *arc)
{
PHYSFS_uint32 i;
for (i = 0; i < count; i++)
{
char name[VDF_ENTRY_NAME_LENGTH + 1];
int namei;
PHYSFS_uint32 jump, size, type, attr;
BAIL_IF_ERRPASS(!__PHYSFS_readAll(io, name, sizeof (name) - 1), 0);
BAIL_IF_ERRPASS(!readui32(io, &jump), 0);
BAIL_IF_ERRPASS(!readui32(io, &size), 0);
BAIL_IF_ERRPASS(!readui32(io, &type), 0);
BAIL_IF_ERRPASS(!readui32(io, &attr), 0);
/* Trim whitespace off the end of the filename */
name[VDF_ENTRY_NAME_LENGTH] = '\0'; /* always null-terminated. */
for (namei = VDF_ENTRY_NAME_LENGTH - 1; namei >= 0; namei--)
{
/* We assume the filenames are low-ASCII; consider the archive
corrupt if we see something above 127, since we don't know the
encoding. (We can change this later if we find out these exist
and are intended to be, say, latin-1 or UTF-8 encoding). */
BAIL_IF(((PHYSFS_uint8) name[namei]) > 127, PHYSFS_ERR_CORRUPT, 0);
if (name[namei] == ' ')
name[namei] = '\0';
else
break;
} /* for */
BAIL_IF(!name[0], PHYSFS_ERR_CORRUPT, 0);
if (!(type & VDF_ENTRY_DIR)) {
BAIL_IF_ERRPASS(!UNPK_addEntry(arc, name, 0, ts, ts, jump, size), 0);
}
} /* for */
return 1;
} /* vdfLoadEntries */
static void *VDF_openArchive(PHYSFS_Io *io, const char *name,
int forWriting, int *claimed)
{
PHYSFS_uint8 ignore[16];
PHYSFS_uint8 sig[VDF_SIGNATURE_LENGTH];
PHYSFS_uint32 count, timestamp, version, dataSize, rootCatOffset;
void *unpkarc;
assert(io != NULL); /* shouldn't ever happen. */
BAIL_IF(forWriting, PHYSFS_ERR_READ_ONLY, NULL);
/* skip the 256-byte comment field. */
BAIL_IF_ERRPASS(!io->seek(io, VDF_COMMENT_LENGTH), NULL);
BAIL_IF_ERRPASS(!__PHYSFS_readAll(io, sig, sizeof (sig)), NULL);
if ((memcmp(sig, VDF_SIGNATURE_G1, VDF_SIGNATURE_LENGTH) != 0) &&
(memcmp(sig, VDF_SIGNATURE_G2, VDF_SIGNATURE_LENGTH) != 0))
{
BAIL(PHYSFS_ERR_UNSUPPORTED, NULL);
} /* if */
*claimed = 1;
BAIL_IF_ERRPASS(!readui32(io, &count), NULL);
BAIL_IF_ERRPASS(!__PHYSFS_readAll(io, ignore, 4), NULL); /* numFiles */
BAIL_IF_ERRPASS(!readui32(io, &timestamp), NULL);
BAIL_IF_ERRPASS(!readui32(io, &dataSize), NULL); /* dataSize */
BAIL_IF_ERRPASS(!readui32(io, &rootCatOffset), NULL); /* rootCatOff */
BAIL_IF_ERRPASS(!readui32(io, &version), NULL);
BAIL_IF(version != 0x50, PHYSFS_ERR_UNSUPPORTED, NULL);
BAIL_IF_ERRPASS(!io->seek(io, rootCatOffset), NULL);
unpkarc = UNPK_openArchive(io);
BAIL_IF_ERRPASS(!unpkarc, NULL);
if (!vdfLoadEntries(io, count, vdfDosTimeToEpoch(timestamp), unpkarc))
{
UNPK_abandonArchive(unpkarc);
return NULL;
} /* if */
return unpkarc;
} /* VDF_openArchive */
const PHYSFS_Archiver __PHYSFS_Archiver_VDF =
{
CURRENT_PHYSFS_ARCHIVER_API_VERSION,
{
"VDF",
"Gothic I/II engine format",
"Francesco Bertolaccini <bertolaccinifrancesco@gmail.com>",
"https://github.com/frabert",
0, /* supportsSymlinks */
},
VDF_openArchive,
UNPK_enumerate,
UNPK_openRead,
UNPK_openWrite,
UNPK_openAppend,
UNPK_remove,
UNPK_mkdir,
UNPK_stat,
UNPK_closeArchive
};
#endif /* defined PHYSFS_SUPPORTS_VDF */
/* end of physfs_archiver_vdf.c ... */

135
src/physfs_archiver_wad.c Executable file
View File

@ -0,0 +1,135 @@
/*
* WAD support routines for PhysicsFS.
*
* This driver handles DOOM engine archives ("wads").
* This format (but not this driver) was designed by id Software for use
* with the DOOM engine.
* The specs of the format are from the unofficial doom specs v1.666
* found here: http://www.gamers.org/dhs/helpdocs/dmsp1666.html
* The format of the archive: (from the specs)
*
* A WAD file has three parts:
* (1) a twelve-byte header
* (2) one or more "lumps"
* (3) a directory or "info table" that contains the names, offsets, and
* sizes of all the lumps in the WAD
*
* The header consists of three four-byte parts:
* (a) an ASCII string which must be either "IWAD" or "PWAD"
* (b) a uint32 which is the number of lumps in the wad
* (c) a uint32 which is the file offset to the start of
* the directory
*
* The directory has one 16-byte entry for every lump. Each entry consists
* of three parts:
*
* (a) a uint32, the file offset to the start of the lump
* (b) a uint32, the size of the lump in bytes
* (c) an 8-byte ASCII string, the name of the lump, padded with zeros.
* For example, the "DEMO1" entry in hexadecimal would be
* (44 45 4D 4F 31 00 00 00)
*
* Note that there is no way to tell if an opened WAD archive is a
* IWAD or PWAD with this archiver.
* I couldn't think of a way to provide that information, without being too
* hacky.
* I don't think it's really that important though.
*
*
* Please see the file LICENSE.txt in the source's root directory.
*
* This file written by Travis Wells, based on the GRP archiver by
* Ryan C. Gordon.
*/
#define __PHYSICSFS_INTERNAL__
#include "physfs_internal.h"
#if PHYSFS_SUPPORTS_WAD
static int wadLoadEntries(PHYSFS_Io *io, const PHYSFS_uint32 count, void *arc)
{
PHYSFS_uint32 i;
for (i = 0; i < count; i++)
{
PHYSFS_uint32 pos;
PHYSFS_uint32 size;
char name[9];
BAIL_IF_ERRPASS(!__PHYSFS_readAll(io, &pos, 4), 0);
BAIL_IF_ERRPASS(!__PHYSFS_readAll(io, &size, 4), 0);
BAIL_IF_ERRPASS(!__PHYSFS_readAll(io, name, 8), 0);
name[8] = '\0'; /* name might not be null-terminated in file. */
size = PHYSFS_swapULE32(size);
pos = PHYSFS_swapULE32(pos);
BAIL_IF_ERRPASS(!UNPK_addEntry(arc, name, 0, -1, -1, pos, size), 0);
} /* for */
return 1;
} /* wadLoadEntries */
static void *WAD_openArchive(PHYSFS_Io *io, const char *name,
int forWriting, int *claimed)
{
PHYSFS_uint8 buf[4];
PHYSFS_uint32 count;
PHYSFS_uint32 directoryOffset;
void *unpkarc;
assert(io != NULL); /* shouldn't ever happen. */
BAIL_IF(forWriting, PHYSFS_ERR_READ_ONLY, NULL);
BAIL_IF_ERRPASS(!__PHYSFS_readAll(io, buf, sizeof (buf)), NULL);
if ((memcmp(buf, "IWAD", 4) != 0) && (memcmp(buf, "PWAD", 4) != 0))
BAIL(PHYSFS_ERR_UNSUPPORTED, NULL);
*claimed = 1;
BAIL_IF_ERRPASS(!__PHYSFS_readAll(io, &count, sizeof (count)), NULL);
count = PHYSFS_swapULE32(count);
BAIL_IF_ERRPASS(!__PHYSFS_readAll(io, &directoryOffset, 4), 0);
directoryOffset = PHYSFS_swapULE32(directoryOffset);
BAIL_IF_ERRPASS(!io->seek(io, directoryOffset), 0);
unpkarc = UNPK_openArchive(io);
BAIL_IF_ERRPASS(!unpkarc, NULL);
if (!wadLoadEntries(io, count, unpkarc))
{
UNPK_abandonArchive(unpkarc);
return NULL;
} /* if */
return unpkarc;
} /* WAD_openArchive */
const PHYSFS_Archiver __PHYSFS_Archiver_WAD =
{
CURRENT_PHYSFS_ARCHIVER_API_VERSION,
{
"WAD",
"DOOM engine format",
"Travis Wells <traviswells@mchsi.com>",
"http://www.3dmm2.com/doom/",
0, /* supportsSymlinks */
},
WAD_openArchive,
UNPK_enumerate,
UNPK_openRead,
UNPK_openWrite,
UNPK_openAppend,
UNPK_remove,
UNPK_mkdir,
UNPK_stat,
UNPK_closeArchive
};
#endif /* defined PHYSFS_SUPPORTS_WAD */
/* end of physfs_archiver_wad.c ... */

1710
src/physfs_archiver_zip.c Executable file

File diff suppressed because it is too large Load Diff

137
src/physfs_byteorder.c Executable file
View File

@ -0,0 +1,137 @@
/**
* PhysicsFS; a portable, flexible file i/o abstraction.
*
* Documentation is in physfs.h. It's verbose, honest. :)
*
* Please see the file LICENSE.txt in the source's root directory.
*
* This file written by Ryan C. Gordon.
*/
#define __PHYSICSFS_INTERNAL__
#include "physfs_internal.h"
#ifndef PHYSFS_Swap16
static inline PHYSFS_uint16 PHYSFS_Swap16(PHYSFS_uint16 D)
{
return ((D<<8)|(D>>8));
}
#endif
#ifndef PHYSFS_Swap32
static inline PHYSFS_uint32 PHYSFS_Swap32(PHYSFS_uint32 D)
{
return ((D<<24)|((D<<8)&0x00FF0000)|((D>>8)&0x0000FF00)|(D>>24));
}
#endif
#ifndef PHYSFS_NO_64BIT_SUPPORT
#ifndef PHYSFS_Swap64
static inline PHYSFS_uint64 PHYSFS_Swap64(PHYSFS_uint64 val) {
PHYSFS_uint32 hi, lo;
/* Separate into high and low 32-bit values and swap them */
lo = (PHYSFS_uint32)(val&0xFFFFFFFF);
val >>= 32;
hi = (PHYSFS_uint32)(val&0xFFFFFFFF);
val = PHYSFS_Swap32(lo);
val <<= 32;
val |= PHYSFS_Swap32(hi);
return val;
}
#endif
#else
#ifndef PHYSFS_Swap64
/* This is mainly to keep compilers from complaining in PHYSFS code.
If there is no real 64-bit datatype, then compilers will complain about
the fake 64-bit datatype that PHYSFS provides when it compiles user code.
*/
#define PHYSFS_Swap64(X) (X)
#endif
#endif /* PHYSFS_NO_64BIT_SUPPORT */
/* Byteswap item from the specified endianness to the native endianness */
#if PHYSFS_BYTEORDER == PHYSFS_LIL_ENDIAN
PHYSFS_uint16 PHYSFS_swapULE16(PHYSFS_uint16 x) { return x; }
PHYSFS_sint16 PHYSFS_swapSLE16(PHYSFS_sint16 x) { return x; }
PHYSFS_uint32 PHYSFS_swapULE32(PHYSFS_uint32 x) { return x; }
PHYSFS_sint32 PHYSFS_swapSLE32(PHYSFS_sint32 x) { return x; }
PHYSFS_uint64 PHYSFS_swapULE64(PHYSFS_uint64 x) { return x; }
PHYSFS_sint64 PHYSFS_swapSLE64(PHYSFS_sint64 x) { return x; }
PHYSFS_uint16 PHYSFS_swapUBE16(PHYSFS_uint16 x) { return PHYSFS_Swap16(x); }
PHYSFS_sint16 PHYSFS_swapSBE16(PHYSFS_sint16 x) { return PHYSFS_Swap16(x); }
PHYSFS_uint32 PHYSFS_swapUBE32(PHYSFS_uint32 x) { return PHYSFS_Swap32(x); }
PHYSFS_sint32 PHYSFS_swapSBE32(PHYSFS_sint32 x) { return PHYSFS_Swap32(x); }
PHYSFS_uint64 PHYSFS_swapUBE64(PHYSFS_uint64 x) { return PHYSFS_Swap64(x); }
PHYSFS_sint64 PHYSFS_swapSBE64(PHYSFS_sint64 x) { return PHYSFS_Swap64(x); }
#else
PHYSFS_uint16 PHYSFS_swapULE16(PHYSFS_uint16 x) { return PHYSFS_Swap16(x); }
PHYSFS_sint16 PHYSFS_swapSLE16(PHYSFS_sint16 x) { return PHYSFS_Swap16(x); }
PHYSFS_uint32 PHYSFS_swapULE32(PHYSFS_uint32 x) { return PHYSFS_Swap32(x); }
PHYSFS_sint32 PHYSFS_swapSLE32(PHYSFS_sint32 x) { return PHYSFS_Swap32(x); }
PHYSFS_uint64 PHYSFS_swapULE64(PHYSFS_uint64 x) { return PHYSFS_Swap64(x); }
PHYSFS_sint64 PHYSFS_swapSLE64(PHYSFS_sint64 x) { return PHYSFS_Swap64(x); }
PHYSFS_uint16 PHYSFS_swapUBE16(PHYSFS_uint16 x) { return x; }
PHYSFS_sint16 PHYSFS_swapSBE16(PHYSFS_sint16 x) { return x; }
PHYSFS_uint32 PHYSFS_swapUBE32(PHYSFS_uint32 x) { return x; }
PHYSFS_sint32 PHYSFS_swapSBE32(PHYSFS_sint32 x) { return x; }
PHYSFS_uint64 PHYSFS_swapUBE64(PHYSFS_uint64 x) { return x; }
PHYSFS_sint64 PHYSFS_swapSBE64(PHYSFS_sint64 x) { return x; }
#endif
static inline int readAll(PHYSFS_File *file, void *val, const size_t len)
{
return (PHYSFS_readBytes(file, val, len) == len);
} /* readAll */
#define PHYSFS_BYTEORDER_READ(datatype, swaptype) \
int PHYSFS_read##swaptype(PHYSFS_File *file, PHYSFS_##datatype *val) { \
PHYSFS_##datatype in; \
BAIL_IF(val == NULL, PHYSFS_ERR_INVALID_ARGUMENT, 0); \
BAIL_IF_ERRPASS(!readAll(file, &in, sizeof (in)), 0); \
*val = PHYSFS_swap##swaptype(in); \
return 1; \
}
PHYSFS_BYTEORDER_READ(sint16, SLE16)
PHYSFS_BYTEORDER_READ(uint16, ULE16)
PHYSFS_BYTEORDER_READ(sint16, SBE16)
PHYSFS_BYTEORDER_READ(uint16, UBE16)
PHYSFS_BYTEORDER_READ(sint32, SLE32)
PHYSFS_BYTEORDER_READ(uint32, ULE32)
PHYSFS_BYTEORDER_READ(sint32, SBE32)
PHYSFS_BYTEORDER_READ(uint32, UBE32)
PHYSFS_BYTEORDER_READ(sint64, SLE64)
PHYSFS_BYTEORDER_READ(uint64, ULE64)
PHYSFS_BYTEORDER_READ(sint64, SBE64)
PHYSFS_BYTEORDER_READ(uint64, UBE64)
static inline int writeAll(PHYSFS_File *f, const void *val, const size_t len)
{
return (PHYSFS_writeBytes(f, val, len) == len);
} /* writeAll */
#define PHYSFS_BYTEORDER_WRITE(datatype, swaptype) \
int PHYSFS_write##swaptype(PHYSFS_File *file, PHYSFS_##datatype val) { \
const PHYSFS_##datatype out = PHYSFS_swap##swaptype(val); \
BAIL_IF_ERRPASS(!writeAll(file, &out, sizeof (out)), 0); \
return 1; \
}
PHYSFS_BYTEORDER_WRITE(sint16, SLE16)
PHYSFS_BYTEORDER_WRITE(uint16, ULE16)
PHYSFS_BYTEORDER_WRITE(sint16, SBE16)
PHYSFS_BYTEORDER_WRITE(uint16, UBE16)
PHYSFS_BYTEORDER_WRITE(sint32, SLE32)
PHYSFS_BYTEORDER_WRITE(uint32, ULE32)
PHYSFS_BYTEORDER_WRITE(sint32, SBE32)
PHYSFS_BYTEORDER_WRITE(uint32, UBE32)
PHYSFS_BYTEORDER_WRITE(sint64, SLE64)
PHYSFS_BYTEORDER_WRITE(uint64, ULE64)
PHYSFS_BYTEORDER_WRITE(sint64, SBE64)
PHYSFS_BYTEORDER_WRITE(uint64, UBE64)
/* end of physfs_byteorder.c ... */

2572
src/physfs_casefolding.h Executable file

File diff suppressed because it is too large Load Diff

720
src/physfs_internal.h Executable file
View File

@ -0,0 +1,720 @@
/*
* Internal function/structure declaration. Do NOT include in your
* application.
*
* Please see the file LICENSE.txt in the source's root directory.
*
* This file written by Ryan C. Gordon.
*/
#ifndef _INCLUDE_PHYSFS_INTERNAL_H_
#define _INCLUDE_PHYSFS_INTERNAL_H_
#ifndef __PHYSICSFS_INTERNAL__
#error Do not include this header from your applications.
#endif
/* Turn off MSVC warnings that are aggressively anti-portability. */
#if defined(_MSC_VER) && !defined(_CRT_SECURE_NO_WARNINGS)
#define _CRT_SECURE_NO_WARNINGS 1
#endif
#include "physfs.h"
/* The holy trinity. */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "physfs_platforms.h"
#include <assert.h>
#define __PHYSFS_COMPILE_TIME_ASSERT(name, x) \
typedef int __PHYSFS_compile_time_assert_##name[(x) * 2 - 1]
/* !!! FIXME: remove this when revamping stack allocation code... */
#if defined(_MSC_VER) || defined(__MINGW32__) || defined(__WATCOMC__)
#include <malloc.h>
#endif
#ifdef PHYSFS_PLATFORM_SOLARIS
#include <alloca.h>
#endif
#ifdef __cplusplus
extern "C" {
#endif
#ifdef __GNUC__
#define PHYSFS_MINIMUM_GCC_VERSION(major, minor) \
( ((__GNUC__ << 16) + __GNUC_MINOR__) >= (((major) << 16) + (minor)) )
#else
#define PHYSFS_MINIMUM_GCC_VERSION(major, minor) (0)
#endif
#ifdef __cplusplus
/* C++ always has a real inline keyword. */
#elif (defined macintosh) && !(defined __MWERKS__)
# define inline
#elif (defined _MSC_VER)
# define inline __inline
#endif
#if defined(PHYSFS_PLATFORM_LINUX) && !defined(_FILE_OFFSET_BITS)
#define _FILE_OFFSET_BITS 64
#endif
/* All public APIs need to be in physfs.h with a PHYSFS_DECL.
All file-private symbols need to be marked "static".
Everything shared between PhysicsFS sources needs to be in this
file between the visibility pragma blocks. */
#if PHYSFS_MINIMUM_GCC_VERSION(4,0) || defined(__clang__)
#define PHYSFS_HAVE_PRAGMA_VISIBILITY 1
#endif
#if PHYSFS_HAVE_PRAGMA_VISIBILITY
#pragma GCC visibility push(hidden)
#endif
/* These are the build-in archivers. We list them all as "extern" here without
#ifdefs to keep it tidy, but obviously you need to make sure these are
wrapped in PHYSFS_SUPPORTS_* checks before actually referencing them. */
extern const PHYSFS_Archiver __PHYSFS_Archiver_DIR;
extern const PHYSFS_Archiver __PHYSFS_Archiver_ZIP;
extern const PHYSFS_Archiver __PHYSFS_Archiver_7Z;
extern const PHYSFS_Archiver __PHYSFS_Archiver_GRP;
extern const PHYSFS_Archiver __PHYSFS_Archiver_QPAK;
extern const PHYSFS_Archiver __PHYSFS_Archiver_HOG;
extern const PHYSFS_Archiver __PHYSFS_Archiver_MVL;
extern const PHYSFS_Archiver __PHYSFS_Archiver_WAD;
extern const PHYSFS_Archiver __PHYSFS_Archiver_SLB;
extern const PHYSFS_Archiver __PHYSFS_Archiver_ISO9660;
extern const PHYSFS_Archiver __PHYSFS_Archiver_VDF;
/* a real C99-compliant snprintf() is in Visual Studio 2015,
but just use this everywhere for binary compatibility. */
#if defined(_MSC_VER)
int __PHYSFS_msvc_vsnprintf(char *outBuf, size_t size, const char *format, va_list ap);
int __PHYSFS_msvc_snprintf(char *outBuf, size_t size, const char *format, ...);
#define vsnprintf __PHYSFS_msvc_vsnprintf
#define snprintf __PHYSFS_msvc_snprintf
#endif
/* Some simple wrappers around WinRT C++ interfaces we can call from C. */
#ifdef PHYSFS_PLATFORM_WINRT
const void *__PHYSFS_winrtCalcBaseDir(void);
const void *__PHYSFS_winrtCalcPrefDir(void);
#endif
/* atomic operations. */
#if defined(_MSC_VER) && (_MSC_VER >= 1500)
#include <intrin.h>
__PHYSFS_COMPILE_TIME_ASSERT(LongEqualsInt, sizeof (int) == sizeof (long));
#define __PHYSFS_ATOMIC_INCR(ptrval) _InterlockedIncrement((long*)(ptrval))
#define __PHYSFS_ATOMIC_DECR(ptrval) _InterlockedDecrement((long*)(ptrval))
#elif defined(__clang__) || (defined(__GNUC__) && (((__GNUC__ * 10000) + (__GNUC_MINOR__ * 100)) >= 40100))
#define __PHYSFS_ATOMIC_INCR(ptrval) __sync_fetch_and_add(ptrval, 1)
#define __PHYSFS_ATOMIC_DECR(ptrval) __sync_fetch_and_add(ptrval, -1)
#else
#define PHYSFS_NEED_ATOMIC_OP_FALLBACK 1
int __PHYSFS_ATOMIC_INCR(int *ptrval);
int __PHYSFS_ATOMIC_DECR(int *ptrval);
#endif
/*
* Interface for small allocations. If you need a little scratch space for
* a throwaway buffer or string, use this. It will make small allocations
* on the stack if possible, and use allocator.Malloc() if they are too
* large. This helps reduce malloc pressure.
* There are some rules, though:
* NEVER return a pointer from this, as stack-allocated buffers go away
* when your function returns.
* NEVER allocate in a loop, as stack-allocated pointers will pile up. Call
* a function that uses smallAlloc from your loop, so the allocation can
* free each time.
* NEVER call smallAlloc with any complex expression (it's a macro that WILL
* have side effects...it references the argument multiple times). Use a
* variable or a literal.
* NEVER free a pointer from this with anything but smallFree. It will not
* be a valid pointer to the allocator, regardless of where the memory came
* from.
* NEVER realloc a pointer from this.
* NEVER forget to use smallFree: it may not be a pointer from the stack.
* NEVER forget to check for NULL...allocation can fail here, of course!
*/
#define __PHYSFS_SMALLALLOCTHRESHOLD 256
void *__PHYSFS_initSmallAlloc(void *ptr, const size_t len);
#define __PHYSFS_smallAlloc(bytes) ( \
__PHYSFS_initSmallAlloc( \
(((bytes) < __PHYSFS_SMALLALLOCTHRESHOLD) ? \
alloca((size_t)((bytes)+sizeof(void*))) : NULL), (bytes)) \
)
void __PHYSFS_smallFree(void *ptr);
/* Use the allocation hooks. */
#define malloc(x) Do not use malloc() directly.
#define realloc(x, y) Do not use realloc() directly.
#define free(x) Do not use free() directly.
/* !!! FIXME: add alloca check here. */
#ifndef PHYSFS_SUPPORTS_ZIP
#define PHYSFS_SUPPORTS_ZIP 1
#endif
#ifndef PHYSFS_SUPPORTS_7Z
#define PHYSFS_SUPPORTS_7Z 1
#endif
#ifndef PHYSFS_SUPPORTS_GRP
#define PHYSFS_SUPPORTS_GRP 1
#endif
#ifndef PHYSFS_SUPPORTS_HOG
#define PHYSFS_SUPPORTS_HOG 1
#endif
#ifndef PHYSFS_SUPPORTS_MVL
#define PHYSFS_SUPPORTS_MVL 1
#endif
#ifndef PHYSFS_SUPPORTS_WAD
#define PHYSFS_SUPPORTS_WAD 1
#endif
#ifndef PHYSFS_SUPPORTS_QPAK
#define PHYSFS_SUPPORTS_QPAK 1
#endif
#ifndef PHYSFS_SUPPORTS_SLB
#define PHYSFS_SUPPORTS_SLB 1
#endif
#ifndef PHYSFS_SUPPORTS_ISO9660
#define PHYSFS_SUPPORTS_ISO9660 1
#endif
#ifndef PHYSFS_SUPPORTS_VDF
#define PHYSFS_SUPPORTS_VDF 1
#endif
#if PHYSFS_SUPPORTS_7Z
/* 7zip support needs a global init function called at startup (no deinit). */
extern void SZIP_global_init(void);
#endif
/* The latest supported PHYSFS_Io::version value. */
#define CURRENT_PHYSFS_IO_API_VERSION 0
/* The latest supported PHYSFS_Archiver::version value. */
#define CURRENT_PHYSFS_ARCHIVER_API_VERSION 0
/* This byteorder stuff was lifted from SDL. https://www.libsdl.org/ */
#define PHYSFS_LIL_ENDIAN 1234
#define PHYSFS_BIG_ENDIAN 4321
#ifdef __linux__
#include <endian.h>
#define PHYSFS_BYTEORDER __BYTE_ORDER
#else /* __linux__ */
#if defined(__hppa__) || \
defined(__m68k__) || defined(mc68000) || defined(_M_M68K) || \
(defined(__MIPS__) && defined(__MISPEB__)) || \
defined(__ppc__) || defined(__POWERPC__) || defined(_M_PPC) || \
defined(__sparc__)
#define PHYSFS_BYTEORDER PHYSFS_BIG_ENDIAN
#else
#define PHYSFS_BYTEORDER PHYSFS_LIL_ENDIAN
#endif
#endif /* __linux__ */
/*
* When sorting the entries in an archive, we use a modified QuickSort.
* When there are less then PHYSFS_QUICKSORT_THRESHOLD entries left to sort,
* we switch over to a BubbleSort for the remainder. Tweak to taste.
*
* You can override this setting by defining PHYSFS_QUICKSORT_THRESHOLD
* before #including "physfs_internal.h".
*/
#ifndef PHYSFS_QUICKSORT_THRESHOLD
#define PHYSFS_QUICKSORT_THRESHOLD 4
#endif
/*
* Sort an array (or whatever) of (max) elements. This uses a mixture of
* a QuickSort and BubbleSort internally.
* (cmpfn) is used to determine ordering, and (swapfn) does the actual
* swapping of elements in the list.
*/
void __PHYSFS_sort(void *entries, size_t max,
int (*cmpfn)(void *, size_t, size_t),
void (*swapfn)(void *, size_t, size_t));
/* These get used all over for lessening code clutter. */
/* "ERRPASS" means "something else just set the error state for us" and is
just to make it clear where the responsibility for the error state lays. */
#define BAIL(e, r) do { if (e) PHYSFS_setErrorCode(e); return r; } while (0)
#define BAIL_ERRPASS(r) do { return r; } while (0)
#define BAIL_IF(c, e, r) do { if (c) { if (e) PHYSFS_setErrorCode(e); return r; } } while (0)
#define BAIL_IF_ERRPASS(c, r) do { if (c) { return r; } } while (0)
#define BAIL_MUTEX(e, m, r) do { if (e) PHYSFS_setErrorCode(e); __PHYSFS_platformReleaseMutex(m); return r; } while (0)
#define BAIL_MUTEX_ERRPASS(m, r) do { __PHYSFS_platformReleaseMutex(m); return r; } while (0)
#define BAIL_IF_MUTEX(c, e, m, r) do { if (c) { if (e) PHYSFS_setErrorCode(e); __PHYSFS_platformReleaseMutex(m); return r; } } while (0)
#define BAIL_IF_MUTEX_ERRPASS(c, m, r) do { if (c) { __PHYSFS_platformReleaseMutex(m); return r; } } while (0)
#define GOTO(e, g) do { if (e) PHYSFS_setErrorCode(e); goto g; } while (0)
#define GOTO_ERRPASS(g) do { goto g; } while (0)
#define GOTO_IF(c, e, g) do { if (c) { if (e) PHYSFS_setErrorCode(e); goto g; } } while (0)
#define GOTO_IF_ERRPASS(c, g) do { if (c) { goto g; } } while (0)
#define GOTO_MUTEX(e, m, g) do { if (e) PHYSFS_setErrorCode(e); __PHYSFS_platformReleaseMutex(m); goto g; } while (0)
#define GOTO_MUTEX_ERRPASS(m, g) do { __PHYSFS_platformReleaseMutex(m); goto g; } while (0)
#define GOTO_IF_MUTEX(c, e, m, g) do { if (c) { if (e) PHYSFS_setErrorCode(e); __PHYSFS_platformReleaseMutex(m); goto g; } } while (0)
#define GOTO_IF_MUTEX_ERRPASS(c, m, g) do { if (c) { __PHYSFS_platformReleaseMutex(m); goto g; } } while (0)
#define __PHYSFS_ARRAYLEN(x) ( (sizeof (x)) / (sizeof (x[0])) )
#ifdef PHYSFS_NO_64BIT_SUPPORT
#define __PHYSFS_SI64(x) ((PHYSFS_sint64) (x))
#define __PHYSFS_UI64(x) ((PHYSFS_uint64) (x))
#elif (defined __GNUC__)
#define __PHYSFS_SI64(x) x##LL
#define __PHYSFS_UI64(x) x##ULL
#elif (defined _MSC_VER)
#define __PHYSFS_SI64(x) x##i64
#define __PHYSFS_UI64(x) x##ui64
#else
#define __PHYSFS_SI64(x) ((PHYSFS_sint64) (x))
#define __PHYSFS_UI64(x) ((PHYSFS_uint64) (x))
#endif
/*
* Check if a ui64 will fit in the platform's address space.
* The initial sizeof check will optimize this macro out entirely on
* 64-bit (and larger?!) platforms, and the other condition will
* return zero or non-zero if the variable will fit in the platform's
* size_t, suitable to pass to malloc. This is kinda messy, but effective.
*/
#define __PHYSFS_ui64FitsAddressSpace(s) ( \
(sizeof (PHYSFS_uint64) <= sizeof (size_t)) || \
((s) < (__PHYSFS_UI64(0xFFFFFFFFFFFFFFFF) >> (64-(sizeof(size_t)*8)))) \
)
/*
* Like strdup(), but uses the current PhysicsFS allocator.
*/
char *__PHYSFS_strdup(const char *str);
/*
* Give a hash value for a C string (uses djb's xor hashing algorithm).
*/
PHYSFS_uint32 __PHYSFS_hashString(const char *str, size_t len);
/*
* The current allocator. Not valid before PHYSFS_init is called!
*/
extern PHYSFS_Allocator __PHYSFS_AllocatorHooks;
/* convenience macro to make this less cumbersome internally... */
#define allocator __PHYSFS_AllocatorHooks
/*
* Create a PHYSFS_Io for a file in the physical filesystem.
* This path is in platform-dependent notation. (mode) must be 'r', 'w', or
* 'a' for Read, Write, or Append.
*/
PHYSFS_Io *__PHYSFS_createNativeIo(const char *path, const int mode);
/*
* Create a PHYSFS_Io for a buffer of memory (READ-ONLY). If you already
* have one of these, just use its duplicate() method, and it'll increment
* its refcount without allocating a copy of the buffer.
*/
PHYSFS_Io *__PHYSFS_createMemoryIo(const void *buf, PHYSFS_uint64 len,
void (*destruct)(void *));
/*
* Read (len) bytes from (io) into (buf). Returns non-zero on success,
* zero on i/o error. Literally: "return (io->read(io, buf, len) == len);"
*/
int __PHYSFS_readAll(PHYSFS_Io *io, void *buf, const size_t len);
/* These are shared between some archivers. */
void UNPK_abandonArchive(void *opaque);
void UNPK_closeArchive(void *opaque);
void *UNPK_openArchive(PHYSFS_Io *io);
void *UNPK_addEntry(void *opaque, char *name, const int isdir,
const PHYSFS_sint64 ctime, const PHYSFS_sint64 mtime,
const PHYSFS_uint64 pos, const PHYSFS_uint64 len);
PHYSFS_Io *UNPK_openRead(void *opaque, const char *name);
PHYSFS_Io *UNPK_openWrite(void *opaque, const char *name);
PHYSFS_Io *UNPK_openAppend(void *opaque, const char *name);
int UNPK_remove(void *opaque, const char *name);
int UNPK_mkdir(void *opaque, const char *name);
int UNPK_stat(void *opaque, const char *fn, PHYSFS_Stat *st);
#define UNPK_enumerate __PHYSFS_DirTreeEnumerate
/* Optional API many archivers use this to manage their directory tree. */
/* !!! FIXME: document this better. */
typedef struct __PHYSFS_DirTreeEntry
{
char *name; /* Full path in archive. */
struct __PHYSFS_DirTreeEntry *hashnext; /* next item in hash bucket. */
struct __PHYSFS_DirTreeEntry *children; /* linked list of kids, if dir. */
struct __PHYSFS_DirTreeEntry *sibling; /* next item in same dir. */
int isdir;
} __PHYSFS_DirTreeEntry;
typedef struct __PHYSFS_DirTree
{
__PHYSFS_DirTreeEntry *root; /* root of directory tree. */
__PHYSFS_DirTreeEntry **hash; /* all entries hashed for fast lookup. */
size_t hashBuckets; /* number of buckets in hash. */
size_t entrylen; /* size in bytes of entries (including subclass). */
} __PHYSFS_DirTree;
int __PHYSFS_DirTreeInit(__PHYSFS_DirTree *dt, const size_t entrylen);
void *__PHYSFS_DirTreeAdd(__PHYSFS_DirTree *dt, char *name, const int isdir);
void *__PHYSFS_DirTreeFind(__PHYSFS_DirTree *dt, const char *path);
PHYSFS_EnumerateCallbackResult __PHYSFS_DirTreeEnumerate(void *opaque,
const char *dname, PHYSFS_EnumerateCallback cb,
const char *origdir, void *callbackdata);
void __PHYSFS_DirTreeDeinit(__PHYSFS_DirTree *dt);
/*--------------------------------------------------------------------------*/
/*--------------------------------------------------------------------------*/
/*------------ ----------------*/
/*------------ You MUST implement the following functions ----------------*/
/*------------ if porting to a new platform. ----------------*/
/*------------ (see platform/unix.c for an example) ----------------*/
/*------------ ----------------*/
/*--------------------------------------------------------------------------*/
/*--------------------------------------------------------------------------*/
/*
* The dir separator; '/' on unix, '\\' on win32, ":" on MacOS, etc...
* Obviously, this isn't a function. If you need more than one char for this,
* you'll need to pull some old pieces of PhysicsFS out of revision control.
*/
#if defined(PHYSFS_PLATFORM_WINDOWS) || defined(PHYSFS_PLATFORM_OS2)
#define __PHYSFS_platformDirSeparator '\\'
#else
#define __PHYSFS_STANDARD_DIRSEP 1
#define __PHYSFS_platformDirSeparator '/'
#endif
/*
* Initialize the platform. This is called when PHYSFS_init() is called from
* the application.
*
* Return zero if there was a catastrophic failure (which prevents you from
* functioning at all), and non-zero otherwise.
*/
int __PHYSFS_platformInit(void);
/*
* Deinitialize the platform. This is called when PHYSFS_deinit() is called
* from the application. You can use this to clean up anything you've
* allocated in your platform driver.
*/
void __PHYSFS_platformDeinit(void);
/*
* Open a file for reading. (filename) is in platform-dependent notation. The
* file pointer should be positioned on the first byte of the file.
*
* The return value will be some platform-specific datatype that is opaque to
* the caller; it could be a (FILE *) under Unix, or a (HANDLE *) under win32.
*
* The same file can be opened for read multiple times, and each should have
* a unique file handle; this is frequently employed to prevent race
* conditions in the archivers.
*
* Call PHYSFS_setErrorCode() and return (NULL) if the file can't be opened.
*/
void *__PHYSFS_platformOpenRead(const char *filename);
/*
* Open a file for writing. (filename) is in platform-dependent notation. If
* the file exists, it should be truncated to zero bytes, and if it doesn't
* exist, it should be created as a zero-byte file. The file pointer should
* be positioned on the first byte of the file.
*
* The return value will be some platform-specific datatype that is opaque to
* the caller; it could be a (FILE *) under Unix, or a (HANDLE *) under win32,
* etc.
*
* Opening a file for write multiple times has undefined results.
*
* Call PHYSFS_setErrorCode() and return (NULL) if the file can't be opened.
*/
void *__PHYSFS_platformOpenWrite(const char *filename);
/*
* Open a file for appending. (filename) is in platform-dependent notation. If
* the file exists, the file pointer should be place just past the end of the
* file, so that the first write will be one byte after the current end of
* the file. If the file doesn't exist, it should be created as a zero-byte
* file. The file pointer should be positioned on the first byte of the file.
*
* The return value will be some platform-specific datatype that is opaque to
* the caller; it could be a (FILE *) under Unix, or a (HANDLE *) under win32,
* etc.
*
* Opening a file for append multiple times has undefined results.
*
* Call PHYSFS_setErrorCode() and return (NULL) if the file can't be opened.
*/
void *__PHYSFS_platformOpenAppend(const char *filename);
/*
* Read more data from a platform-specific file handle. (opaque) should be
* cast to whatever data type your platform uses. Read a maximum of (len)
* 8-bit bytes to the area pointed to by (buf). If there isn't enough data
* available, return the number of bytes read, and position the file pointer
* immediately after those bytes.
* On success, return (len) and position the file pointer immediately past
* the end of the last read byte. Return (-1) if there is a catastrophic
* error, and call PHYSFS_setErrorCode() to describe the problem; the file
* pointer should not move in such a case. A partial read is success; only
* return (-1) on total failure; presumably, the next read call after a
* partial read will fail as such.
*/
PHYSFS_sint64 __PHYSFS_platformRead(void *opaque, void *buf, PHYSFS_uint64 len);
/*
* Write more data to a platform-specific file handle. (opaque) should be
* cast to whatever data type your platform uses. Write a maximum of (len)
* 8-bit bytes from the area pointed to by (buffer). If there is a problem,
* return the number of bytes written, and position the file pointer
* immediately after those bytes. Return (-1) if there is a catastrophic
* error, and call PHYSFS_setErrorCode() to describe the problem; the file
* pointer should not move in such a case. A partial write is success; only
* return (-1) on total failure; presumably, the next write call after a
* partial write will fail as such.
*/
PHYSFS_sint64 __PHYSFS_platformWrite(void *opaque, const void *buffer,
PHYSFS_uint64 len);
/*
* Set the file pointer to a new position. (opaque) should be cast to
* whatever data type your platform uses. (pos) specifies the number
* of 8-bit bytes to seek to from the start of the file. Seeking past the
* end of the file is an error condition, and you should check for it.
*
* Not all file types can seek; this is to be expected by the caller.
*
* On error, call PHYSFS_setErrorCode() and return zero. On success, return
* a non-zero value.
*/
int __PHYSFS_platformSeek(void *opaque, PHYSFS_uint64 pos);
/*
* Get the file pointer's position, in an 8-bit byte offset from the start of
* the file. (opaque) should be cast to whatever data type your platform
* uses.
*
* Not all file types can "tell"; this is to be expected by the caller.
*
* On error, call PHYSFS_setErrorCode() and return -1. On success, return >= 0.
*/
PHYSFS_sint64 __PHYSFS_platformTell(void *opaque);
/*
* Determine the current size of a file, in 8-bit bytes, from an open file.
*
* The caller expects that this information may not be available for all
* file types on all platforms.
*
* Return -1 if you can't do it, and call PHYSFS_setErrorCode(). Otherwise,
* return the file length in 8-bit bytes.
*/
PHYSFS_sint64 __PHYSFS_platformFileLength(void *handle);
/*
* Read filesystem metadata for a specific path.
*
* This needs to fill in all the fields of (stat). For fields that might not
* mean anything on a platform (access time, perhaps), choose a reasonable
* default. if (follow), we want to follow symlinks and stat what they
* link to and not the link itself.
*
* Return zero on failure, non-zero on success.
*/
int __PHYSFS_platformStat(const char *fn, PHYSFS_Stat *stat, const int follow);
/*
* Flush any pending writes to disk. (opaque) should be cast to whatever data
* type your platform uses. Be sure to check for errors; the caller expects
* that this function can fail if there was a flushing error, etc.
*
* Return zero on failure, non-zero on success.
*/
int __PHYSFS_platformFlush(void *opaque);
/*
* Close file and deallocate resources. (opaque) should be cast to whatever
* data type your platform uses. This should close the file in any scenario:
* flushing is a separate function call, and this function should never fail.
*
* You should clean up all resources associated with (opaque); the pointer
* will be considered invalid after this call.
*/
void __PHYSFS_platformClose(void *opaque);
/*
* Platform implementation of PHYSFS_getCdRomDirsCallback()...
* CD directories are discovered and reported to the callback one at a time.
* Pointers passed to the callback are assumed to be invalid to the
* application after the callback returns, so you can free them or whatever.
* Callback does not assume results will be sorted in any meaningful way.
*/
void __PHYSFS_platformDetectAvailableCDs(PHYSFS_StringCallback cb, void *data);
/*
* Calculate the base dir, if your platform needs special consideration.
* Just return NULL if the standard routines will suffice. (see
* calculateBaseDir() in physfs.c ...)
* Your string must end with a dir separator if you don't return NULL.
* Caller will allocator.Free() the retval if it's not NULL.
*/
char *__PHYSFS_platformCalcBaseDir(const char *argv0);
/*
* Get the platform-specific user dir.
* As of PhysicsFS 2.1, returning NULL means fatal error.
* Your string must end with a dir separator if you don't return NULL.
* Caller will allocator.Free() the retval if it's not NULL.
*/
char *__PHYSFS_platformCalcUserDir(void);
/* This is the cached version from PHYSFS_init(). This is a fast call. */
const char *__PHYSFS_getUserDir(void); /* not deprecated internal version. */
/*
* Get the platform-specific pref dir.
* Returning NULL means fatal error.
* Your string must end with a dir separator if you don't return NULL.
* Caller will allocator.Free() the retval if it's not NULL.
* Caller will make missing directories if necessary; this just reports
* the final path.
*/
char *__PHYSFS_platformCalcPrefDir(const char *org, const char *app);
/*
* Return a pointer that uniquely identifies the current thread.
* On a platform without threading, (0x1) will suffice. These numbers are
* arbitrary; the only requirement is that no two threads have the same
* pointer.
*/
void *__PHYSFS_platformGetThreadID(void);
/*
* Enumerate a directory of files. This follows the rules for the
* PHYSFS_Archiver::enumerate() method, except that the (dirName) that is
* passed to this function is converted to platform-DEPENDENT notation by
* the caller. The PHYSFS_Archiver version uses platform-independent
* notation. Note that ".", "..", and other meta-entries should always
* be ignored.
*/
PHYSFS_EnumerateCallbackResult __PHYSFS_platformEnumerate(const char *dirname,
PHYSFS_EnumerateCallback callback,
const char *origdir, void *callbackdata);
/*
* Make a directory in the actual filesystem. (path) is specified in
* platform-dependent notation. On error, return zero and set the error
* message. Return non-zero on success.
*/
int __PHYSFS_platformMkDir(const char *path);
/*
* Remove a file or directory entry in the actual filesystem. (path) is
* specified in platform-dependent notation. Note that this deletes files
* _and_ directories, so you might need to do some determination.
* Non-empty directories should report an error and not delete themselves
* or their contents.
*
* Deleting a symlink should remove the link, not what it points to.
*
* On error, return zero and set the error message. Return non-zero on success.
*/
int __PHYSFS_platformDelete(const char *path);
/*
* Create a platform-specific mutex. This can be whatever datatype your
* platform uses for mutexes, but it is cast to a (void *) for abstractness.
*
* Return (NULL) if you couldn't create one. Systems without threads can
* return any arbitrary non-NULL value.
*/
void *__PHYSFS_platformCreateMutex(void);
/*
* Destroy a platform-specific mutex, and clean up any resources associated
* with it. (mutex) is a value previously returned by
* __PHYSFS_platformCreateMutex(). This can be a no-op on single-threaded
* platforms.
*/
void __PHYSFS_platformDestroyMutex(void *mutex);
/*
* Grab possession of a platform-specific mutex. Mutexes should be recursive;
* that is, the same thread should be able to call this function multiple
* times in a row without causing a deadlock. This function should block
* until a thread can gain possession of the mutex.
*
* Return non-zero if the mutex was grabbed, zero if there was an
* unrecoverable problem grabbing it (this should not be a matter of
* timing out! We're talking major system errors; block until the mutex
* is available otherwise.)
*
* _DO NOT_ call PHYSFS_setErrorCode() in here! Since setErrorCode calls this
* function, you'll cause an infinite recursion. This means you can't
* use the BAIL_*MACRO* macros, either.
*/
int __PHYSFS_platformGrabMutex(void *mutex);
/*
* Relinquish possession of the mutex when this method has been called
* once for each time that platformGrabMutex was called. Once possession has
* been released, the next thread in line to grab the mutex (if any) may
* proceed.
*
* _DO NOT_ call PHYSFS_setErrorCode() in here! Since setErrorCode calls this
* function, you'll cause an infinite recursion. This means you can't
* use the BAIL_*MACRO* macros, either.
*/
void __PHYSFS_platformReleaseMutex(void *mutex);
#if PHYSFS_HAVE_PRAGMA_VISIBILITY
#pragma GCC visibility pop
#endif
#ifdef __cplusplus
}
#endif
#endif
/* end of physfs_internal.h ... */

6028
src/physfs_lzmasdk.h Executable file

File diff suppressed because it is too large Load Diff

701
src/physfs_miniz.h Executable file
View File

@ -0,0 +1,701 @@
/* tinfl.c v1.11 - public domain inflate with zlib header parsing/adler32 checking (inflate-only subset of miniz.c)
See "unlicense" statement at the end of this file.
Rich Geldreich <richgel99@gmail.com>, last updated May 20, 2011
Implements RFC 1950: https://www.ietf.org/rfc/rfc1950.txt and RFC 1951: https://www.ietf.org/rfc/rfc1951.txt
The entire decompressor coroutine is implemented in tinfl_decompress(). The other functions are optional high-level helpers.
*/
#ifndef TINFL_HEADER_INCLUDED
#define TINFL_HEADER_INCLUDED
typedef PHYSFS_uint8 mz_uint8;
typedef PHYSFS_sint16 mz_int16;
typedef PHYSFS_uint16 mz_uint16;
typedef PHYSFS_uint32 mz_uint32;
typedef unsigned int mz_uint;
typedef PHYSFS_uint64 mz_uint64;
/* For more compatibility with zlib, miniz.c uses unsigned long for some parameters/struct members. */
typedef unsigned long mz_ulong;
/* Heap allocation callbacks. */
typedef void *(*mz_alloc_func)(void *opaque, unsigned int items, unsigned int size);
typedef void (*mz_free_func)(void *opaque, void *address);
#if defined(_M_IX86) || defined(_M_X64)
/* Set MINIZ_USE_UNALIGNED_LOADS_AND_STORES to 1 if integer loads and stores to unaligned addresses are acceptable on the target platform (slightly faster). */
#define MINIZ_USE_UNALIGNED_LOADS_AND_STORES 1
/* Set MINIZ_LITTLE_ENDIAN to 1 if the processor is little endian. */
#define MINIZ_LITTLE_ENDIAN 1
#endif
#if defined(_WIN64) || defined(__MINGW64__) || defined(_LP64) || defined(__LP64__)
/* Set MINIZ_HAS_64BIT_REGISTERS to 1 if the processor has 64-bit general purpose registers (enables 64-bit bitbuffer in inflator) */
#define MINIZ_HAS_64BIT_REGISTERS 1
#endif
/* Works around MSVC's spammy "warning C4127: conditional expression is constant" message. */
#ifdef _MSC_VER
#define MZ_MACRO_END while (0, 0)
#else
#define MZ_MACRO_END while (0)
#endif
/* Decompression flags. */
enum
{
TINFL_FLAG_PARSE_ZLIB_HEADER = 1,
TINFL_FLAG_HAS_MORE_INPUT = 2,
TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF = 4,
TINFL_FLAG_COMPUTE_ADLER32 = 8
};
struct tinfl_decompressor_tag; typedef struct tinfl_decompressor_tag tinfl_decompressor;
/* Max size of LZ dictionary. */
#define TINFL_LZ_DICT_SIZE 32768
/* Return status. */
typedef enum
{
TINFL_STATUS_BAD_PARAM = -3,
TINFL_STATUS_ADLER32_MISMATCH = -2,
TINFL_STATUS_FAILED = -1,
TINFL_STATUS_DONE = 0,
TINFL_STATUS_NEEDS_MORE_INPUT = 1,
TINFL_STATUS_HAS_MORE_OUTPUT = 2
} tinfl_status;
/* Initializes the decompressor to its initial state. */
#define tinfl_init(r) do { (r)->m_state = 0; } MZ_MACRO_END
#define tinfl_get_adler32(r) (r)->m_check_adler32
/* Main low-level decompressor coroutine function. This is the only function actually needed for decompression. All the other functions are just high-level helpers for improved usability. */
/* This is a universal API, i.e. it can be used as a building block to build any desired higher level decompression API. In the limit case, it can be called once per every byte input or output. */
static tinfl_status tinfl_decompress(tinfl_decompressor *r, const mz_uint8 *pIn_buf_next, size_t *pIn_buf_size, mz_uint8 *pOut_buf_start, mz_uint8 *pOut_buf_next, size_t *pOut_buf_size, const mz_uint32 decomp_flags);
/* Internal/private bits follow. */
enum
{
TINFL_MAX_HUFF_TABLES = 3, TINFL_MAX_HUFF_SYMBOLS_0 = 288, TINFL_MAX_HUFF_SYMBOLS_1 = 32, TINFL_MAX_HUFF_SYMBOLS_2 = 19,
TINFL_FAST_LOOKUP_BITS = 10, TINFL_FAST_LOOKUP_SIZE = 1 << TINFL_FAST_LOOKUP_BITS
};
typedef struct
{
mz_uint8 m_code_size[TINFL_MAX_HUFF_SYMBOLS_0];
mz_int16 m_look_up[TINFL_FAST_LOOKUP_SIZE], m_tree[TINFL_MAX_HUFF_SYMBOLS_0 * 2];
} tinfl_huff_table;
#if MINIZ_HAS_64BIT_REGISTERS
#define TINFL_USE_64BIT_BITBUF 1
#endif
#if TINFL_USE_64BIT_BITBUF
typedef mz_uint64 tinfl_bit_buf_t;
#define TINFL_BITBUF_SIZE (64)
#else
typedef mz_uint32 tinfl_bit_buf_t;
#define TINFL_BITBUF_SIZE (32)
#endif
struct tinfl_decompressor_tag
{
mz_uint32 m_state, m_num_bits, m_zhdr0, m_zhdr1, m_z_adler32, m_final, m_type, m_check_adler32, m_dist, m_counter, m_num_extra, m_table_sizes[TINFL_MAX_HUFF_TABLES];
tinfl_bit_buf_t m_bit_buf;
size_t m_dist_from_out_buf_start;
tinfl_huff_table m_tables[TINFL_MAX_HUFF_TABLES];
mz_uint8 m_raw_header[4], m_len_codes[TINFL_MAX_HUFF_SYMBOLS_0 + TINFL_MAX_HUFF_SYMBOLS_1 + 137];
};
#endif /* #ifdef TINFL_HEADER_INCLUDED */
/* ------------------- End of Header: Implementation follows. (If you only want the header, define MINIZ_HEADER_FILE_ONLY.) */
#ifndef TINFL_HEADER_FILE_ONLY
#define MZ_MAX(a,b) (((a)>(b))?(a):(b))
#define MZ_MIN(a,b) (((a)<(b))?(a):(b))
#define MZ_CLEAR_OBJ(obj) memset(&(obj), 0, sizeof(obj))
#if MINIZ_USE_UNALIGNED_LOADS_AND_STORES && MINIZ_LITTLE_ENDIAN
#define MZ_READ_LE16(p) *((const mz_uint16 *)(p))
#define MZ_READ_LE32(p) *((const mz_uint32 *)(p))
#else
#define MZ_READ_LE16(p) ((mz_uint32)(((const mz_uint8 *)(p))[0]) | ((mz_uint32)(((const mz_uint8 *)(p))[1]) << 8U))
#define MZ_READ_LE32(p) ((mz_uint32)(((const mz_uint8 *)(p))[0]) | ((mz_uint32)(((const mz_uint8 *)(p))[1]) << 8U) | ((mz_uint32)(((const mz_uint8 *)(p))[2]) << 16U) | ((mz_uint32)(((const mz_uint8 *)(p))[3]) << 24U))
#endif
#define TINFL_MEMCPY(d, s, l) memcpy(d, s, l)
#define TINFL_MEMSET(p, c, l) memset(p, c, l)
#define TINFL_CR_BEGIN switch(r->m_state) { case 0:
#define TINFL_CR_RETURN(state_index, result) do { status = result; r->m_state = state_index; goto common_exit; case state_index:; } MZ_MACRO_END
#define TINFL_CR_RETURN_FOREVER(state_index, result) do { for ( ; ; ) { TINFL_CR_RETURN(state_index, result); } } MZ_MACRO_END
#define TINFL_CR_FINISH }
/* TODO: If the caller has indicated that there's no more input, and we attempt to read beyond the input buf, then something is wrong with the input because the inflator never */
/* reads ahead more than it needs to. Currently TINFL_GET_BYTE() pads the end of the stream with 0's in this scenario. */
#define TINFL_GET_BYTE(state_index, c) do { \
if (pIn_buf_cur >= pIn_buf_end) { \
for ( ; ; ) { \
if (decomp_flags & TINFL_FLAG_HAS_MORE_INPUT) { \
TINFL_CR_RETURN(state_index, TINFL_STATUS_NEEDS_MORE_INPUT); \
if (pIn_buf_cur < pIn_buf_end) { \
c = *pIn_buf_cur++; \
break; \
} \
} else { \
c = 0; \
break; \
} \
} \
} else c = *pIn_buf_cur++; } MZ_MACRO_END
#define TINFL_NEED_BITS(state_index, n) do { mz_uint c; TINFL_GET_BYTE(state_index, c); bit_buf |= (((tinfl_bit_buf_t)c) << num_bits); num_bits += 8; } while (num_bits < (mz_uint)(n))
#define TINFL_SKIP_BITS(state_index, n) do { if (num_bits < (mz_uint)(n)) { TINFL_NEED_BITS(state_index, n); } bit_buf >>= (n); num_bits -= (n); } MZ_MACRO_END
#define TINFL_GET_BITS(state_index, b, n) do { if (num_bits < (mz_uint)(n)) { TINFL_NEED_BITS(state_index, n); } b = bit_buf & ((1 << (n)) - 1); bit_buf >>= (n); num_bits -= (n); } MZ_MACRO_END
/* TINFL_HUFF_BITBUF_FILL() is only used rarely, when the number of bytes remaining in the input buffer falls below 2. */
/* It reads just enough bytes from the input stream that are needed to decode the next Huffman code (and absolutely no more). It works by trying to fully decode a */
/* Huffman code by using whatever bits are currently present in the bit buffer. If this fails, it reads another byte, and tries again until it succeeds or until the */
/* bit buffer contains >=15 bits (deflate's max. Huffman code size). */
#define TINFL_HUFF_BITBUF_FILL(state_index, pHuff) \
do { \
temp = (pHuff)->m_look_up[bit_buf & (TINFL_FAST_LOOKUP_SIZE - 1)]; \
if (temp >= 0) { \
code_len = temp >> 9; \
if ((code_len) && (num_bits >= code_len)) \
break; \
} else if (num_bits > TINFL_FAST_LOOKUP_BITS) { \
code_len = TINFL_FAST_LOOKUP_BITS; \
do { \
temp = (pHuff)->m_tree[~temp + ((bit_buf >> code_len++) & 1)]; \
} while ((temp < 0) && (num_bits >= (code_len + 1))); if (temp >= 0) break; \
} TINFL_GET_BYTE(state_index, c); bit_buf |= (((tinfl_bit_buf_t)c) << num_bits); num_bits += 8; \
} while (num_bits < 15);
/* TINFL_HUFF_DECODE() decodes the next Huffman coded symbol. It's more complex than you would initially expect because the zlib API expects the decompressor to never read */
/* beyond the final byte of the deflate stream. (In other words, when this macro wants to read another byte from the input, it REALLY needs another byte in order to fully */
/* decode the next Huffman code.) Handling this properly is particularly important on raw deflate (non-zlib) streams, which aren't followed by a byte aligned adler-32. */
/* The slow path is only executed at the very end of the input buffer. */
#define TINFL_HUFF_DECODE(state_index, sym, pHuff) do { \
int temp; mz_uint code_len, c; \
if (num_bits < 15) { \
if ((pIn_buf_end - pIn_buf_cur) < 2) { \
TINFL_HUFF_BITBUF_FILL(state_index, pHuff); \
} else { \
bit_buf |= (((tinfl_bit_buf_t)pIn_buf_cur[0]) << num_bits) | (((tinfl_bit_buf_t)pIn_buf_cur[1]) << (num_bits + 8)); pIn_buf_cur += 2; num_bits += 16; \
} \
} \
if ((temp = (pHuff)->m_look_up[bit_buf & (TINFL_FAST_LOOKUP_SIZE - 1)]) >= 0) \
code_len = temp >> 9, temp &= 511; \
else { \
code_len = TINFL_FAST_LOOKUP_BITS; do { temp = (pHuff)->m_tree[~temp + ((bit_buf >> code_len++) & 1)]; } while (temp < 0); \
} sym = temp; bit_buf >>= code_len; num_bits -= code_len; } MZ_MACRO_END
static tinfl_status tinfl_decompress(tinfl_decompressor *r, const mz_uint8 *pIn_buf_next, size_t *pIn_buf_size, mz_uint8 *pOut_buf_start, mz_uint8 *pOut_buf_next, size_t *pOut_buf_size, const mz_uint32 decomp_flags)
{
static const int s_length_base[31] = { 3,4,5,6,7,8,9,10,11,13, 15,17,19,23,27,31,35,43,51,59, 67,83,99,115,131,163,195,227,258,0,0 };
static const int s_length_extra[31]= { 0,0,0,0,0,0,0,0,1,1,1,1,2,2,2,2,3,3,3,3,4,4,4,4,5,5,5,5,0,0,0 };
static const int s_dist_base[32] = { 1,2,3,4,5,7,9,13,17,25,33,49,65,97,129,193, 257,385,513,769,1025,1537,2049,3073,4097,6145,8193,12289,16385,24577,0,0};
static const int s_dist_extra[32] = { 0,0,0,0,1,1,2,2,3,3,4,4,5,5,6,6,7,7,8,8,9,9,10,10,11,11,12,12,13,13};
static const mz_uint8 s_length_dezigzag[19] = { 16,17,18,0,8,7,9,6,10,5,11,4,12,3,13,2,14,1,15 };
static const int s_min_table_sizes[3] = { 257, 1, 4 };
tinfl_status status = TINFL_STATUS_FAILED; mz_uint32 num_bits, dist, counter, num_extra; tinfl_bit_buf_t bit_buf;
const mz_uint8 *pIn_buf_cur = pIn_buf_next, *const pIn_buf_end = pIn_buf_next + *pIn_buf_size;
mz_uint8 *pOut_buf_cur = pOut_buf_next, *const pOut_buf_end = pOut_buf_next + *pOut_buf_size;
size_t out_buf_size_mask = (decomp_flags & TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF) ? (size_t)-1 : ((pOut_buf_next - pOut_buf_start) + *pOut_buf_size) - 1, dist_from_out_buf_start;
/* Ensure the output buffer's size is a power of 2, unless the output buffer is large enough to hold the entire output file (in which case it doesn't matter). */
if (((out_buf_size_mask + 1) & out_buf_size_mask) || (pOut_buf_next < pOut_buf_start)) { *pIn_buf_size = *pOut_buf_size = 0; return TINFL_STATUS_BAD_PARAM; }
num_bits = r->m_num_bits; bit_buf = r->m_bit_buf; dist = r->m_dist; counter = r->m_counter; num_extra = r->m_num_extra; dist_from_out_buf_start = r->m_dist_from_out_buf_start;
TINFL_CR_BEGIN
bit_buf = num_bits = dist = counter = num_extra = r->m_zhdr0 = r->m_zhdr1 = 0; r->m_z_adler32 = r->m_check_adler32 = 1;
if (decomp_flags & TINFL_FLAG_PARSE_ZLIB_HEADER)
{
TINFL_GET_BYTE(1, r->m_zhdr0); TINFL_GET_BYTE(2, r->m_zhdr1);
counter = (((r->m_zhdr0 * 256 + r->m_zhdr1) % 31 != 0) || (r->m_zhdr1 & 32) || ((r->m_zhdr0 & 15) != 8));
if (!(decomp_flags & TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF)) counter |= (((1U << (8U + (r->m_zhdr0 >> 4))) > 32768U) || ((out_buf_size_mask + 1) < (size_t)(1U << (8U + (r->m_zhdr0 >> 4)))));
if (counter) { TINFL_CR_RETURN_FOREVER(36, TINFL_STATUS_FAILED); }
}
do
{
TINFL_GET_BITS(3, r->m_final, 3); r->m_type = r->m_final >> 1;
if (r->m_type == 0)
{
TINFL_SKIP_BITS(5, num_bits & 7);
for (counter = 0; counter < 4; ++counter) { if (num_bits) TINFL_GET_BITS(6, r->m_raw_header[counter], 8); else TINFL_GET_BYTE(7, r->m_raw_header[counter]); }
if ((counter = (r->m_raw_header[0] | (r->m_raw_header[1] << 8))) != (mz_uint)(0xFFFF ^ (r->m_raw_header[2] | (r->m_raw_header[3] << 8)))) { TINFL_CR_RETURN_FOREVER(39, TINFL_STATUS_FAILED); }
while ((counter) && (num_bits))
{
TINFL_GET_BITS(51, dist, 8);
while (pOut_buf_cur >= pOut_buf_end) { TINFL_CR_RETURN(52, TINFL_STATUS_HAS_MORE_OUTPUT); }
*pOut_buf_cur++ = (mz_uint8)dist;
counter--;
}
while (counter)
{
size_t n; while (pOut_buf_cur >= pOut_buf_end) { TINFL_CR_RETURN(9, TINFL_STATUS_HAS_MORE_OUTPUT); }
while (pIn_buf_cur >= pIn_buf_end)
{
if (decomp_flags & TINFL_FLAG_HAS_MORE_INPUT)
{
TINFL_CR_RETURN(38, TINFL_STATUS_NEEDS_MORE_INPUT);
}
else
{
TINFL_CR_RETURN_FOREVER(40, TINFL_STATUS_FAILED);
}
}
n = MZ_MIN(MZ_MIN((size_t)(pOut_buf_end - pOut_buf_cur), (size_t)(pIn_buf_end - pIn_buf_cur)), counter);
TINFL_MEMCPY(pOut_buf_cur, pIn_buf_cur, n); pIn_buf_cur += n; pOut_buf_cur += n; counter -= (mz_uint)n;
}
}
else if (r->m_type == 3)
{
TINFL_CR_RETURN_FOREVER(10, TINFL_STATUS_FAILED);
}
else
{
if (r->m_type == 1)
{
mz_uint8 *p = r->m_tables[0].m_code_size; mz_uint i;
r->m_table_sizes[0] = 288; r->m_table_sizes[1] = 32; TINFL_MEMSET(r->m_tables[1].m_code_size, 5, 32);
for ( i = 0; i <= 143; ++i) *p++ = 8;
for ( ; i <= 255; ++i) *p++ = 9;
for ( ; i <= 279; ++i) *p++ = 7;
for ( ; i <= 287; ++i) *p++ = 8;
}
else
{
for (counter = 0; counter < 3; counter++) { TINFL_GET_BITS(11, r->m_table_sizes[counter], "\05\05\04"[counter]); r->m_table_sizes[counter] += s_min_table_sizes[counter]; }
MZ_CLEAR_OBJ(r->m_tables[2].m_code_size); for (counter = 0; counter < r->m_table_sizes[2]; counter++) { mz_uint s; TINFL_GET_BITS(14, s, 3); r->m_tables[2].m_code_size[s_length_dezigzag[counter]] = (mz_uint8)s; }
r->m_table_sizes[2] = 19;
}
for ( ; (int)r->m_type >= 0; r->m_type--)
{
int tree_next, tree_cur; tinfl_huff_table *pTable;
mz_uint i, j, used_syms, total, sym_index, next_code[17], total_syms[16]; pTable = &r->m_tables[r->m_type]; MZ_CLEAR_OBJ(total_syms); MZ_CLEAR_OBJ(pTable->m_look_up); MZ_CLEAR_OBJ(pTable->m_tree);
for (i = 0; i < r->m_table_sizes[r->m_type]; ++i) total_syms[pTable->m_code_size[i]]++;
used_syms = 0, total = 0; next_code[0] = next_code[1] = 0;
for (i = 1; i <= 15; ++i) { used_syms += total_syms[i]; next_code[i + 1] = (total = ((total + total_syms[i]) << 1)); }
if ((65536 != total) && (used_syms > 1))
{
TINFL_CR_RETURN_FOREVER(35, TINFL_STATUS_FAILED);
}
for (tree_next = -1, sym_index = 0; sym_index < r->m_table_sizes[r->m_type]; ++sym_index)
{
mz_uint rev_code = 0, l, cur_code, code_size = pTable->m_code_size[sym_index]; if (!code_size) continue;
cur_code = next_code[code_size]++; for (l = code_size; l > 0; l--, cur_code >>= 1) rev_code = (rev_code << 1) | (cur_code & 1);
if (code_size <= TINFL_FAST_LOOKUP_BITS) { mz_int16 k = (mz_int16)((code_size << 9) | sym_index); while (rev_code < TINFL_FAST_LOOKUP_SIZE) { pTable->m_look_up[rev_code] = k; rev_code += (1 << code_size); } continue; }
if (0 == (tree_cur = pTable->m_look_up[rev_code & (TINFL_FAST_LOOKUP_SIZE - 1)])) { pTable->m_look_up[rev_code & (TINFL_FAST_LOOKUP_SIZE - 1)] = (mz_int16)tree_next; tree_cur = tree_next; tree_next -= 2; }
rev_code >>= (TINFL_FAST_LOOKUP_BITS - 1);
for (j = code_size; j > (TINFL_FAST_LOOKUP_BITS + 1); j--)
{
tree_cur -= ((rev_code >>= 1) & 1);
if (!pTable->m_tree[-tree_cur - 1]) { pTable->m_tree[-tree_cur - 1] = (mz_int16)tree_next; tree_cur = tree_next; tree_next -= 2; } else tree_cur = pTable->m_tree[-tree_cur - 1];
}
tree_cur -= ((rev_code >>= 1) & 1); pTable->m_tree[-tree_cur - 1] = (mz_int16)sym_index;
}
if (r->m_type == 2)
{
for (counter = 0; counter < (r->m_table_sizes[0] + r->m_table_sizes[1]); )
{
mz_uint s; TINFL_HUFF_DECODE(16, dist, &r->m_tables[2]); if (dist < 16) { r->m_len_codes[counter++] = (mz_uint8)dist; continue; }
if ((dist == 16) && (!counter))
{
TINFL_CR_RETURN_FOREVER(17, TINFL_STATUS_FAILED);
}
num_extra = "\02\03\07"[dist - 16]; TINFL_GET_BITS(18, s, num_extra); s += "\03\03\013"[dist - 16];
TINFL_MEMSET(r->m_len_codes + counter, (dist == 16) ? r->m_len_codes[counter - 1] : 0, s); counter += s;
}
if ((r->m_table_sizes[0] + r->m_table_sizes[1]) != counter)
{
TINFL_CR_RETURN_FOREVER(21, TINFL_STATUS_FAILED);
}
TINFL_MEMCPY(r->m_tables[0].m_code_size, r->m_len_codes, r->m_table_sizes[0]); TINFL_MEMCPY(r->m_tables[1].m_code_size, r->m_len_codes + r->m_table_sizes[0], r->m_table_sizes[1]);
}
}
for ( ; ; )
{
mz_uint8 *pSrc;
for ( ; ; )
{
if (((pIn_buf_end - pIn_buf_cur) < 4) || ((pOut_buf_end - pOut_buf_cur) < 2))
{
TINFL_HUFF_DECODE(23, counter, &r->m_tables[0]);
if (counter >= 256)
break;
while (pOut_buf_cur >= pOut_buf_end) { TINFL_CR_RETURN(24, TINFL_STATUS_HAS_MORE_OUTPUT); }
*pOut_buf_cur++ = (mz_uint8)counter;
}
else
{
int sym2; mz_uint code_len;
#if TINFL_USE_64BIT_BITBUF
if (num_bits < 30) { bit_buf |= (((tinfl_bit_buf_t)MZ_READ_LE32(pIn_buf_cur)) << num_bits); pIn_buf_cur += 4; num_bits += 32; }
#else
if (num_bits < 15) { bit_buf |= (((tinfl_bit_buf_t)MZ_READ_LE16(pIn_buf_cur)) << num_bits); pIn_buf_cur += 2; num_bits += 16; }
#endif
if ((sym2 = r->m_tables[0].m_look_up[bit_buf & (TINFL_FAST_LOOKUP_SIZE - 1)]) >= 0)
code_len = sym2 >> 9;
else
{
code_len = TINFL_FAST_LOOKUP_BITS; do { sym2 = r->m_tables[0].m_tree[~sym2 + ((bit_buf >> code_len++) & 1)]; } while (sym2 < 0);
}
counter = sym2; bit_buf >>= code_len; num_bits -= code_len;
if (counter & 256)
break;
#if !TINFL_USE_64BIT_BITBUF
if (num_bits < 15) { bit_buf |= (((tinfl_bit_buf_t)MZ_READ_LE16(pIn_buf_cur)) << num_bits); pIn_buf_cur += 2; num_bits += 16; }
#endif
if ((sym2 = r->m_tables[0].m_look_up[bit_buf & (TINFL_FAST_LOOKUP_SIZE - 1)]) >= 0)
code_len = sym2 >> 9;
else
{
code_len = TINFL_FAST_LOOKUP_BITS; do { sym2 = r->m_tables[0].m_tree[~sym2 + ((bit_buf >> code_len++) & 1)]; } while (sym2 < 0);
}
bit_buf >>= code_len; num_bits -= code_len;
pOut_buf_cur[0] = (mz_uint8)counter;
if (sym2 & 256)
{
pOut_buf_cur++;
counter = sym2;
break;
}
pOut_buf_cur[1] = (mz_uint8)sym2;
pOut_buf_cur += 2;
}
}
if ((counter &= 511) == 256) break;
num_extra = s_length_extra[counter - 257]; counter = s_length_base[counter - 257];
if (num_extra) { mz_uint extra_bits; TINFL_GET_BITS(25, extra_bits, num_extra); counter += extra_bits; }
TINFL_HUFF_DECODE(26, dist, &r->m_tables[1]);
num_extra = s_dist_extra[dist]; dist = s_dist_base[dist];
if (num_extra) { mz_uint extra_bits; TINFL_GET_BITS(27, extra_bits, num_extra); dist += extra_bits; }
dist_from_out_buf_start = pOut_buf_cur - pOut_buf_start;
if ((dist > dist_from_out_buf_start) && (decomp_flags & TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF))
{
TINFL_CR_RETURN_FOREVER(37, TINFL_STATUS_FAILED);
}
pSrc = pOut_buf_start + ((dist_from_out_buf_start - dist) & out_buf_size_mask);
if ((MZ_MAX(pOut_buf_cur, pSrc) + counter) > pOut_buf_end)
{
while (counter--)
{
while (pOut_buf_cur >= pOut_buf_end) { TINFL_CR_RETURN(53, TINFL_STATUS_HAS_MORE_OUTPUT); }
*pOut_buf_cur++ = pOut_buf_start[(dist_from_out_buf_start++ - dist) & out_buf_size_mask];
}
continue;
}
#if MINIZ_USE_UNALIGNED_LOADS_AND_STORES
else if ((counter >= 9) && (counter <= dist))
{
const mz_uint8 *pSrc_end = pSrc + (counter & ~7);
do
{
((mz_uint32 *)pOut_buf_cur)[0] = ((const mz_uint32 *)pSrc)[0];
((mz_uint32 *)pOut_buf_cur)[1] = ((const mz_uint32 *)pSrc)[1];
pOut_buf_cur += 8;
} while ((pSrc += 8) < pSrc_end);
if ((counter &= 7) < 3)
{
if (counter)
{
pOut_buf_cur[0] = pSrc[0];
if (counter > 1)
pOut_buf_cur[1] = pSrc[1];
pOut_buf_cur += counter;
}
continue;
}
}
#endif
do
{
pOut_buf_cur[0] = pSrc[0];
pOut_buf_cur[1] = pSrc[1];
pOut_buf_cur[2] = pSrc[2];
pOut_buf_cur += 3; pSrc += 3;
} while ((int)(counter -= 3) > 2);
if ((int)counter > 0)
{
pOut_buf_cur[0] = pSrc[0];
if ((int)counter > 1)
pOut_buf_cur[1] = pSrc[1];
pOut_buf_cur += counter;
}
}
}
} while (!(r->m_final & 1));
if (decomp_flags & TINFL_FLAG_PARSE_ZLIB_HEADER)
{
TINFL_SKIP_BITS(32, num_bits & 7); for (counter = 0; counter < 4; ++counter) { mz_uint s; if (num_bits) TINFL_GET_BITS(41, s, 8); else TINFL_GET_BYTE(42, s); r->m_z_adler32 = (r->m_z_adler32 << 8) | s; }
}
TINFL_CR_RETURN_FOREVER(34, TINFL_STATUS_DONE);
TINFL_CR_FINISH
common_exit:
r->m_num_bits = num_bits; r->m_bit_buf = bit_buf; r->m_dist = dist; r->m_counter = counter; r->m_num_extra = num_extra; r->m_dist_from_out_buf_start = dist_from_out_buf_start;
*pIn_buf_size = pIn_buf_cur - pIn_buf_next; *pOut_buf_size = pOut_buf_cur - pOut_buf_next;
if ((decomp_flags & (TINFL_FLAG_PARSE_ZLIB_HEADER | TINFL_FLAG_COMPUTE_ADLER32)) && (status >= 0))
{
const mz_uint8 *ptr = pOut_buf_next; size_t buf_len = *pOut_buf_size;
mz_uint32 i, s1 = r->m_check_adler32 & 0xffff, s2 = r->m_check_adler32 >> 16; size_t block_len = buf_len % 5552;
while (buf_len)
{
for (i = 0; i + 7 < block_len; i += 8, ptr += 8)
{
s1 += ptr[0], s2 += s1; s1 += ptr[1], s2 += s1; s1 += ptr[2], s2 += s1; s1 += ptr[3], s2 += s1;
s1 += ptr[4], s2 += s1; s1 += ptr[5], s2 += s1; s1 += ptr[6], s2 += s1; s1 += ptr[7], s2 += s1;
}
for ( ; i < block_len; ++i) s1 += *ptr++, s2 += s1;
s1 %= 65521U, s2 %= 65521U; buf_len -= block_len; block_len = 5552;
}
r->m_check_adler32 = (s2 << 16) + s1; if ((status == TINFL_STATUS_DONE) && (decomp_flags & TINFL_FLAG_PARSE_ZLIB_HEADER) && (r->m_check_adler32 != r->m_z_adler32)) status = TINFL_STATUS_ADLER32_MISMATCH;
}
return status;
}
/* Flush values. For typical usage you only need MZ_NO_FLUSH and MZ_FINISH. The other stuff is for advanced use. */
enum { MZ_NO_FLUSH = 0, MZ_PARTIAL_FLUSH = 1, MZ_SYNC_FLUSH = 2, MZ_FULL_FLUSH = 3, MZ_FINISH = 4, MZ_BLOCK = 5 };
/* Return status codes. MZ_PARAM_ERROR is non-standard. */
enum { MZ_OK = 0, MZ_STREAM_END = 1, MZ_NEED_DICT = 2, MZ_ERRNO = -1, MZ_STREAM_ERROR = -2, MZ_DATA_ERROR = -3, MZ_MEM_ERROR = -4, MZ_BUF_ERROR = -5, MZ_VERSION_ERROR = -6, MZ_PARAM_ERROR = -10000 };
/* Compression levels. */
enum { MZ_NO_COMPRESSION = 0, MZ_BEST_SPEED = 1, MZ_BEST_COMPRESSION = 9, MZ_DEFAULT_COMPRESSION = -1 };
/* Window bits */
#define MZ_DEFAULT_WINDOW_BITS 15
struct mz_internal_state;
/* Compression/decompression stream struct. */
typedef struct mz_stream_s
{
const unsigned char *next_in; /* pointer to next byte to read */
unsigned int avail_in; /* number of bytes available at next_in */
mz_ulong total_in; /* total number of bytes consumed so far */
unsigned char *next_out; /* pointer to next byte to write */
unsigned int avail_out; /* number of bytes that can be written to next_out */
mz_ulong total_out; /* total number of bytes produced so far */
char *msg; /* error msg (unused) */
struct mz_internal_state *state; /* internal state, allocated by zalloc/zfree */
mz_alloc_func zalloc; /* optional heap allocation function (defaults to malloc) */
mz_free_func zfree; /* optional heap free function (defaults to free) */
void *opaque; /* heap alloc function user pointer */
int data_type; /* data_type (unused) */
mz_ulong adler; /* adler32 of the source or uncompressed data */
mz_ulong reserved; /* not used */
} mz_stream;
typedef mz_stream *mz_streamp;
typedef struct
{
tinfl_decompressor m_decomp;
mz_uint m_dict_ofs, m_dict_avail, m_first_call, m_has_flushed; int m_window_bits;
mz_uint8 m_dict[TINFL_LZ_DICT_SIZE];
tinfl_status m_last_status;
} inflate_state;
static int mz_inflateInit2(mz_streamp pStream, int window_bits)
{
inflate_state *pDecomp;
if (!pStream) return MZ_STREAM_ERROR;
if ((window_bits != MZ_DEFAULT_WINDOW_BITS) && (-window_bits != MZ_DEFAULT_WINDOW_BITS)) return MZ_PARAM_ERROR;
pStream->data_type = 0;
pStream->adler = 0;
pStream->msg = NULL;
pStream->total_in = 0;
pStream->total_out = 0;
pStream->reserved = 0;
/* if (!pStream->zalloc) pStream->zalloc = def_alloc_func; */
/* if (!pStream->zfree) pStream->zfree = def_free_func; */
pDecomp = (inflate_state*)pStream->zalloc(pStream->opaque, 1, sizeof(inflate_state));
if (!pDecomp) return MZ_MEM_ERROR;
pStream->state = (struct mz_internal_state *)pDecomp;
tinfl_init(&pDecomp->m_decomp);
pDecomp->m_dict_ofs = 0;
pDecomp->m_dict_avail = 0;
pDecomp->m_last_status = TINFL_STATUS_NEEDS_MORE_INPUT;
pDecomp->m_first_call = 1;
pDecomp->m_has_flushed = 0;
pDecomp->m_window_bits = window_bits;
return MZ_OK;
}
static int mz_inflate(mz_streamp pStream, int flush)
{
inflate_state* pState;
mz_uint n, first_call, decomp_flags = TINFL_FLAG_COMPUTE_ADLER32;
size_t in_bytes, out_bytes, orig_avail_in;
tinfl_status status;
if ((!pStream) || (!pStream->state)) return MZ_STREAM_ERROR;
if (flush == MZ_PARTIAL_FLUSH) flush = MZ_SYNC_FLUSH;
if ((flush) && (flush != MZ_SYNC_FLUSH) && (flush != MZ_FINISH)) return MZ_STREAM_ERROR;
pState = (inflate_state*)pStream->state;
if (pState->m_window_bits > 0) decomp_flags |= TINFL_FLAG_PARSE_ZLIB_HEADER;
orig_avail_in = pStream->avail_in;
first_call = pState->m_first_call; pState->m_first_call = 0;
if (pState->m_last_status < 0) return MZ_DATA_ERROR;
if (pState->m_has_flushed && (flush != MZ_FINISH)) return MZ_STREAM_ERROR;
pState->m_has_flushed |= (flush == MZ_FINISH);
if ((flush == MZ_FINISH) && (first_call))
{
/* MZ_FINISH on the first call implies that the input and output buffers are large enough to hold the entire compressed/decompressed file. */
decomp_flags |= TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF;
in_bytes = pStream->avail_in; out_bytes = pStream->avail_out;
status = tinfl_decompress(&pState->m_decomp, pStream->next_in, &in_bytes, pStream->next_out, pStream->next_out, &out_bytes, decomp_flags);
pState->m_last_status = status;
pStream->next_in += (mz_uint)in_bytes; pStream->avail_in -= (mz_uint)in_bytes; pStream->total_in += (mz_uint)in_bytes;
pStream->adler = tinfl_get_adler32(&pState->m_decomp);
pStream->next_out += (mz_uint)out_bytes; pStream->avail_out -= (mz_uint)out_bytes; pStream->total_out += (mz_uint)out_bytes;
if (status < 0)
return MZ_DATA_ERROR;
else if (status != TINFL_STATUS_DONE)
{
pState->m_last_status = TINFL_STATUS_FAILED;
return MZ_BUF_ERROR;
}
return MZ_STREAM_END;
}
/* flush != MZ_FINISH then we must assume there's more input. */
if (flush != MZ_FINISH) decomp_flags |= TINFL_FLAG_HAS_MORE_INPUT;
if (pState->m_dict_avail)
{
n = MZ_MIN(pState->m_dict_avail, pStream->avail_out);
memcpy(pStream->next_out, pState->m_dict + pState->m_dict_ofs, n);
pStream->next_out += n; pStream->avail_out -= n; pStream->total_out += n;
pState->m_dict_avail -= n; pState->m_dict_ofs = (pState->m_dict_ofs + n) & (TINFL_LZ_DICT_SIZE - 1);
return ((pState->m_last_status == TINFL_STATUS_DONE) && (!pState->m_dict_avail)) ? MZ_STREAM_END : MZ_OK;
}
for ( ; ; )
{
in_bytes = pStream->avail_in;
out_bytes = TINFL_LZ_DICT_SIZE - pState->m_dict_ofs;
status = tinfl_decompress(&pState->m_decomp, pStream->next_in, &in_bytes, pState->m_dict, pState->m_dict + pState->m_dict_ofs, &out_bytes, decomp_flags);
pState->m_last_status = status;
pStream->next_in += (mz_uint)in_bytes; pStream->avail_in -= (mz_uint)in_bytes;
pStream->total_in += (mz_uint)in_bytes; pStream->adler = tinfl_get_adler32(&pState->m_decomp);
pState->m_dict_avail = (mz_uint)out_bytes;
n = MZ_MIN(pState->m_dict_avail, pStream->avail_out);
memcpy(pStream->next_out, pState->m_dict + pState->m_dict_ofs, n);
pStream->next_out += n; pStream->avail_out -= n; pStream->total_out += n;
pState->m_dict_avail -= n; pState->m_dict_ofs = (pState->m_dict_ofs + n) & (TINFL_LZ_DICT_SIZE - 1);
if (status < 0)
return MZ_DATA_ERROR; /* Stream is corrupted (there could be some uncompressed data left in the output dictionary - oh well). */
else if ((status == TINFL_STATUS_NEEDS_MORE_INPUT) && (!orig_avail_in))
return MZ_BUF_ERROR; /* Signal caller that we can't make forward progress without supplying more input or by setting flush to MZ_FINISH. */
else if (flush == MZ_FINISH)
{
/* The output buffer MUST be large to hold the remaining uncompressed data when flush==MZ_FINISH. */
if (status == TINFL_STATUS_DONE)
return pState->m_dict_avail ? MZ_BUF_ERROR : MZ_STREAM_END;
/* status here must be TINFL_STATUS_HAS_MORE_OUTPUT, which means there's at least 1 more byte on the way. If there's no more room left in the output buffer then something is wrong. */
else if (!pStream->avail_out)
return MZ_BUF_ERROR;
}
else if ((status == TINFL_STATUS_DONE) || (!pStream->avail_in) || (!pStream->avail_out) || (pState->m_dict_avail))
break;
}
return ((status == TINFL_STATUS_DONE) && (!pState->m_dict_avail)) ? MZ_STREAM_END : MZ_OK;
}
static int mz_inflateEnd(mz_streamp pStream)
{
if (!pStream)
return MZ_STREAM_ERROR;
if (pStream->state)
{
pStream->zfree(pStream->opaque, pStream->state);
pStream->state = NULL;
}
return MZ_OK;
}
/* make this a drop-in replacement for zlib... */
#define voidpf void*
#define uInt unsigned int
#define z_stream mz_stream
#define inflateInit2 mz_inflateInit2
#define inflate mz_inflate
#define inflateEnd mz_inflateEnd
#define Z_SYNC_FLUSH MZ_SYNC_FLUSH
#define Z_FINISH MZ_FINISH
#define Z_OK MZ_OK
#define Z_STREAM_END MZ_STREAM_END
#define Z_NEED_DICT MZ_NEED_DICT
#define Z_ERRNO MZ_ERRNO
#define Z_STREAM_ERROR MZ_STREAM_ERROR
#define Z_DATA_ERROR MZ_DATA_ERROR
#define Z_MEM_ERROR MZ_MEM_ERROR
#define Z_BUF_ERROR MZ_BUF_ERROR
#define Z_VERSION_ERROR MZ_VERSION_ERROR
#define MAX_WBITS 15
#endif /* #ifndef TINFL_HEADER_FILE_ONLY */
/*
This is free and unencumbered software released into the public domain.
Anyone is free to copy, modify, publish, use, compile, sell, or
distribute this software, either in source code form or as a compiled
binary, for any purpose, commercial or non-commercial, and by any
means.
In jurisdictions that recognize copyright laws, the author or authors
of this software dedicate any and all copyright interest in the
software to the public domain. We make this dedication for the benefit
of the public at large and to the detriment of our heirs and
successors. We intend this dedication to be an overt act of
relinquishment in perpetuity of all present and future rights to this
software under copyright law.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
OTHER DEALINGS IN THE SOFTWARE.
For more information, please refer to <https://unlicense.org/>
*/

188
src/physfs_platform_apple.m Executable file
View File

@ -0,0 +1,188 @@
/*
* Apple platform (macOS, iOS, watchOS, etc) support routines for PhysicsFS.
*
* Please see the file LICENSE.txt in the source's root directory.
*
* This file written by Ryan C. Gordon.
*/
#define __PHYSICSFS_INTERNAL__
#include "physfs_platforms.h"
#ifdef PHYSFS_PLATFORM_APPLE
#include <Foundation/Foundation.h>
#include "physfs_internal.h"
int __PHYSFS_platformInit(void)
{
return 1; /* success. */
} /* __PHYSFS_platformInit */
void __PHYSFS_platformDeinit(void)
{
/* no-op */
} /* __PHYSFS_platformDeinit */
char *__PHYSFS_platformCalcBaseDir(const char *argv0)
{
@autoreleasepool
{
NSString *path = [[NSBundle mainBundle] bundlePath];
BAIL_IF(!path, PHYSFS_ERR_OS_ERROR, NULL);
size_t len = [path lengthOfBytesUsingEncoding:NSUTF8StringEncoding];
char *retval = (char *) allocator.Malloc(len + 2);
BAIL_IF(!retval, PHYSFS_ERR_OUT_OF_MEMORY, NULL);
[path getCString:retval maxLength:len+1 encoding:NSUTF8StringEncoding];
retval[len] = '/';
retval[len+1] = '\0';
return retval; /* whew. */
} /* @autoreleasepool */
} /* __PHYSFS_platformCalcBaseDir */
char *__PHYSFS_platformCalcPrefDir(const char *org, const char *app)
{
@autoreleasepool
{
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSApplicationSupportDirectory, NSUserDomainMask, TRUE);
BAIL_IF(!paths, PHYSFS_ERR_OS_ERROR, NULL);
NSString *path = (NSString *) paths[0];
BAIL_IF(!path, PHYSFS_ERR_OS_ERROR, NULL);
size_t len = [path lengthOfBytesUsingEncoding:NSUTF8StringEncoding];
const size_t applen = strlen(app);
char *retval = (char *) allocator.Malloc(len + applen + 3);
BAIL_IF(!retval, PHYSFS_ERR_OUT_OF_MEMORY, NULL);
[path getCString:retval maxLength:len+1 encoding:NSUTF8StringEncoding];
snprintf(retval + len, applen + 3, "/%s/", app);
return retval; /* whew. */
} /* @autoreleasepool */
} /* __PHYSFS_platformCalcPrefDir */
/* CD-ROM detection code... */
/*
* Code based on sample from Apple Developer Connection:
* https://developer.apple.com/samplecode/Sample_Code/Devices_and_Hardware/Disks/VolumeToBSDNode/VolumeToBSDNode.c.htm
*/
#if !defined(PHYSFS_NO_CDROM_SUPPORT)
#include <IOKit/IOKitLib.h>
#include <IOKit/storage/IOMedia.h>
#include <IOKit/storage/IOCDMedia.h>
#include <IOKit/storage/IODVDMedia.h>
#include <sys/mount.h>
static int darwinIsWholeMedia(io_service_t service)
{
int retval = 0;
CFTypeRef wholeMedia;
if (!IOObjectConformsTo(service, kIOMediaClass))
return 0;
wholeMedia = IORegistryEntryCreateCFProperty(service,
CFSTR(kIOMediaWholeKey),
NULL, 0);
if (wholeMedia == NULL)
return 0;
retval = CFBooleanGetValue(wholeMedia);
CFRelease(wholeMedia);
return retval;
} /* darwinIsWholeMedia */
static int darwinIsMountedDisc(char *bsdName, mach_port_t masterPort)
{
int retval = 0;
CFMutableDictionaryRef matchingDict;
kern_return_t rc;
io_iterator_t iter;
io_service_t service;
if ((matchingDict = IOBSDNameMatching(masterPort, 0, bsdName)) == NULL)
return 0;
rc = IOServiceGetMatchingServices(masterPort, matchingDict, &iter);
if ((rc != KERN_SUCCESS) || (!iter))
return 0;
service = IOIteratorNext(iter);
IOObjectRelease(iter);
if (!service)
return 0;
rc = IORegistryEntryCreateIterator(service, kIOServicePlane,
kIORegistryIterateRecursively | kIORegistryIterateParents, &iter);
if (!iter)
return 0;
if (rc != KERN_SUCCESS)
{
IOObjectRelease(iter);
return 0;
} /* if */
IOObjectRetain(service); /* add an extra object reference... */
do
{
if (darwinIsWholeMedia(service))
{
if ( (IOObjectConformsTo(service, kIOCDMediaClass)) ||
(IOObjectConformsTo(service, kIODVDMediaClass)) )
{
retval = 1;
} /* if */
} /* if */
IOObjectRelease(service);
} while ((service = IOIteratorNext(iter)) && (!retval));
IOObjectRelease(iter);
IOObjectRelease(service);
return retval;
} /* darwinIsMountedDisc */
#endif /* !defined(PHYSFS_NO_CDROM_SUPPORT) */
void __PHYSFS_platformDetectAvailableCDs(PHYSFS_StringCallback cb, void *data)
{
#if !defined(PHYSFS_NO_CDROM_SUPPORT)
const char *devPrefix = "/dev/";
const int prefixLen = strlen(devPrefix);
mach_port_t masterPort = 0;
struct statfs *mntbufp;
int i, mounts;
if (IOMasterPort(MACH_PORT_NULL, &masterPort) != KERN_SUCCESS)
BAIL(PHYSFS_ERR_OS_ERROR, ) /*return void*/;
mounts = getmntinfo(&mntbufp, MNT_WAIT); /* NOT THREAD SAFE! */
for (i = 0; i < mounts; i++)
{
char *dev = mntbufp[i].f_mntfromname;
char *mnt = mntbufp[i].f_mntonname;
if (strncmp(dev, devPrefix, prefixLen) != 0) /* a virtual device? */
continue;
dev += prefixLen;
if (darwinIsMountedDisc(dev, masterPort))
cb(data, mnt);
} /* for */
#endif /* !defined(PHYSFS_NO_CDROM_SUPPORT) */
} /* __PHYSFS_platformDetectAvailableCDs */
#endif /* PHYSFS_PLATFORM_APPLE */
/* end of physfs_platform_apple.m ... */

186
src/physfs_platform_haiku.cpp Executable file
View File

@ -0,0 +1,186 @@
/*
* Haiku platform-dependent support routines for PhysicsFS.
*
* Please see the file LICENSE.txt in the source's root directory.
*
* This file written by Ryan C. Gordon.
*/
#define __PHYSICSFS_INTERNAL__
#include "physfs_platforms.h"
#ifdef PHYSFS_PLATFORM_HAIKU
#include <os/kernel/OS.h>
#include <os/app/Roster.h>
#include <os/storage/Volume.h>
#include <os/storage/VolumeRoster.h>
#include <os/storage/Directory.h>
#include <os/storage/Entry.h>
#include <os/storage/Path.h>
#include <os/kernel/fs_info.h>
#include <os/device/scsi.h>
#include <errno.h>
#include <unistd.h>
#include "physfs_internal.h"
int __PHYSFS_platformInit(void)
{
return 1; /* always succeed. */
} /* __PHYSFS_platformInit */
void __PHYSFS_platformDeinit(void)
{
/* no-op */
} /* __PHYSFS_platformDeinit */
static char *getMountPoint(const char *devname, char *buf, size_t bufsize)
{
BVolumeRoster mounts;
BVolume vol;
mounts.Rewind();
while (mounts.GetNextVolume(&vol) == B_NO_ERROR)
{
fs_info fsinfo;
fs_stat_dev(vol.Device(), &fsinfo);
if (strcmp(devname, fsinfo.device_name) == 0)
{
BDirectory directory;
BEntry entry;
BPath path;
const char *str;
if ( (vol.GetRootDirectory(&directory) < B_OK) ||
(directory.GetEntry(&entry) < B_OK) ||
(entry.GetPath(&path) < B_OK) ||
( (str = path.Path()) == NULL) )
return NULL;
strncpy(buf, str, bufsize-1);
buf[bufsize-1] = '\0';
return buf;
} /* if */
} /* while */
return NULL;
} /* getMountPoint */
/*
* This function is lifted from Simple Directmedia Layer (SDL):
* https://www.libsdl.org/ ... this is zlib-licensed code, too.
*/
static void tryDir(const char *d, PHYSFS_StringCallback callback, void *data)
{
BDirectory dir;
dir.SetTo(d);
if (dir.InitCheck() != B_NO_ERROR)
return;
dir.Rewind();
BEntry entry;
while (dir.GetNextEntry(&entry) >= 0)
{
BPath path;
const char *name;
entry_ref e;
if (entry.GetPath(&path) != B_NO_ERROR)
continue;
name = path.Path();
if (entry.GetRef(&e) != B_NO_ERROR)
continue;
if (entry.IsDirectory())
{
if (strcmp(e.name, "floppy") != 0)
tryDir(name, callback, data);
continue;
} /* if */
const int devfd = open(name, O_RDONLY);
if (devfd < 0)
continue;
device_geometry g;
const int rc = ioctl(devfd, B_GET_GEOMETRY, &g, sizeof (g));
close(devfd);
if (rc < 0)
continue;
if (g.device_type != B_CD)
continue;
char mntpnt[B_FILE_NAME_LENGTH];
if (getMountPoint(name, mntpnt, sizeof (mntpnt)))
callback(data, mntpnt);
} /* while */
} /* tryDir */
void __PHYSFS_platformDetectAvailableCDs(PHYSFS_StringCallback cb, void *data)
{
tryDir("/dev/disk", cb, data);
} /* __PHYSFS_platformDetectAvailableCDs */
static team_id getTeamID(void)
{
thread_info info;
thread_id tid = find_thread(NULL);
get_thread_info(tid, &info);
return info.team;
} /* getTeamID */
char *__PHYSFS_platformCalcBaseDir(const char *argv0)
{
image_info info;
int32 cookie = 0;
while (get_next_image_info(0, &cookie, &info) == B_OK)
{
if (info.type == B_APP_IMAGE)
break;
} /* while */
BEntry entry(info.name, true);
BPath path;
status_t rc = entry.GetPath(&path); /* (path) now has binary's path. */
assert(rc == B_OK);
rc = path.GetParent(&path); /* chop filename, keep directory. */
assert(rc == B_OK);
const char *str = path.Path();
assert(str != NULL);
const size_t len = strlen(str);
char *retval = (char *) allocator.Malloc(len + 2);
BAIL_IF(!retval, PHYSFS_ERR_OUT_OF_MEMORY, NULL);
strcpy(retval, str);
retval[len] = '/';
retval[len+1] = '\0';
return retval;
} /* __PHYSFS_platformCalcBaseDir */
char *__PHYSFS_platformCalcPrefDir(const char *org, const char *app)
{
const char *userdir = __PHYSFS_getUserDir();
const char *append = "config/settings/";
const size_t len = strlen(userdir) + strlen(append) + strlen(app) + 2;
char *retval = (char *) allocator.Malloc(len);
BAIL_IF(!retval, PHYSFS_ERR_OUT_OF_MEMORY, NULL);
snprintf(retval, len, "%s%s%s/", userdir, append, app);
return retval;
} /* __PHYSFS_platformCalcPrefDir */
#endif /* PHYSFS_PLATFORM_HAIKU */
/* end of physfs_platform_haiku.cpp ... */

812
src/physfs_platform_os2.c Executable file
View File

@ -0,0 +1,812 @@
/*
* OS/2 support routines for PhysicsFS.
*
* Please see the file LICENSE.txt in the source's root directory.
*
* This file written by Ryan C. Gordon.
*/
#define __PHYSICSFS_INTERNAL__
#include "physfs_platforms.h"
#ifdef PHYSFS_PLATFORM_OS2
#define INCL_DOSMODULEMGR
#define INCL_DOSSEMAPHORES
#define INCL_DOSDATETIME
#define INCL_DOSFILEMGR
#define INCL_DOSMODULEMGR
#define INCL_DOSERRORS
#define INCL_DOSPROCESS
#define INCL_DOSDEVICES
#define INCL_DOSDEVIOCTL
#define INCL_DOSMISC
#include <os2.h>
#include <uconv.h>
#include <errno.h>
#include <time.h>
#include <ctype.h>
#include "physfs_internal.h"
static HMODULE uconvdll = 0;
static UconvObject uconv = 0;
static int (_System *pUniCreateUconvObject)(UniChar *, UconvObject *) = NULL;
static int (_System *pUniFreeUconvObject)(UconvObject *) = NULL;
static int (_System *pUniUconvToUcs)(UconvObject,void **,size_t *, UniChar**, size_t *, size_t *) = NULL;
static int (_System *pUniUconvFromUcs)(UconvObject,UniChar **,size_t *,void **,size_t *,size_t *) = NULL;
static PHYSFS_ErrorCode errcodeFromAPIRET(const APIRET rc)
{
switch (rc)
{
case NO_ERROR: return PHYSFS_ERR_OK; /* not an error. */
case ERROR_INTERRUPT: return PHYSFS_ERR_OK; /* not an error. */
case ERROR_TIMEOUT: return PHYSFS_ERR_OK; /* not an error. */
case ERROR_NOT_ENOUGH_MEMORY: return PHYSFS_ERR_OUT_OF_MEMORY;
case ERROR_FILE_NOT_FOUND: return PHYSFS_ERR_NOT_FOUND;
case ERROR_PATH_NOT_FOUND: return PHYSFS_ERR_NOT_FOUND;
case ERROR_ACCESS_DENIED: return PHYSFS_ERR_PERMISSION;
case ERROR_NOT_DOS_DISK: return PHYSFS_ERR_NOT_FOUND;
case ERROR_SHARING_VIOLATION: return PHYSFS_ERR_PERMISSION;
case ERROR_CANNOT_MAKE: return PHYSFS_ERR_IO; /* maybe this is wrong? */
case ERROR_DEVICE_IN_USE: return PHYSFS_ERR_BUSY;
case ERROR_OPEN_FAILED: return PHYSFS_ERR_IO; /* maybe this is wrong? */
case ERROR_DISK_FULL: return PHYSFS_ERR_NO_SPACE;
case ERROR_PIPE_BUSY: return PHYSFS_ERR_BUSY;
case ERROR_SHARING_BUFFER_EXCEEDED: return PHYSFS_ERR_IO;
case ERROR_FILENAME_EXCED_RANGE: return PHYSFS_ERR_BAD_FILENAME;
case ERROR_META_EXPANSION_TOO_LONG: return PHYSFS_ERR_BAD_FILENAME;
case ERROR_TOO_MANY_HANDLES: return PHYSFS_ERR_IO;
case ERROR_TOO_MANY_OPEN_FILES: return PHYSFS_ERR_IO;
case ERROR_NO_MORE_SEARCH_HANDLES: return PHYSFS_ERR_IO;
case ERROR_SEEK_ON_DEVICE: return PHYSFS_ERR_IO;
case ERROR_NEGATIVE_SEEK: return PHYSFS_ERR_INVALID_ARGUMENT;
case ERROR_WRITE_PROTECT: return PHYSFS_ERR_PERMISSION;
case ERROR_WRITE_FAULT: return PHYSFS_ERR_IO;
case ERROR_UNCERTAIN_MEDIA: return PHYSFS_ERR_IO;
case ERROR_PROTECTION_VIOLATION: return PHYSFS_ERR_IO;
case ERROR_BROKEN_PIPE: return PHYSFS_ERR_IO;
/* !!! FIXME: some of these might be PHYSFS_ERR_BAD_FILENAME, etc */
case ERROR_LOCK_VIOLATION:
case ERROR_GEN_FAILURE:
case ERROR_INVALID_PARAMETER:
case ERROR_INVALID_NAME:
case ERROR_INVALID_DRIVE:
case ERROR_INVALID_HANDLE:
case ERROR_INVALID_FUNCTION:
case ERROR_INVALID_LEVEL:
case ERROR_INVALID_CATEGORY:
case ERROR_DUPLICATE_NAME:
case ERROR_BUFFER_OVERFLOW:
case ERROR_BAD_LENGTH:
case ERROR_BAD_DRIVER_LEVEL:
case ERROR_DIRECT_ACCESS_HANDLE:
case ERROR_NOT_OWNER:
return PHYSFS_ERR_OS_ERROR;
default: break;
} /* switch */
return PHYSFS_ERR_OTHER_ERROR;
} /* errcodeFromAPIRET */
static char *cvtUtf8ToCodepage(const char *utf8str)
{
const size_t len = strlen(utf8str) + 1;
const size_t uc2buflen = len * sizeof (UniChar);
UniChar *uc2ptr = (UniChar *) __PHYSFS_smallAlloc(uc2buflen);
UniChar *uc2str = uc2ptr;
char *cpptr = NULL;
char *cpstr = NULL;
size_t subs = 0;
size_t unilen;
BAIL_IF(!uc2str, PHYSFS_ERR_OUT_OF_MEMORY, NULL);
PHYSFS_utf8ToUcs2(utf8str, (PHYSFS_uint16 *) uc2str, uc2buflen);
for (unilen = 0; uc2str[unilen]; unilen++) { /* spin */ }
unilen++; /* null terminator. */
if (!uconvdll)
{
/* There's really not much we can do on older OS/2s except pray this
is latin1-compatible. */
size_t i;
cpptr = (char *) allocator.Malloc(unilen);
cpstr = cpptr;
GOTO_IF(!cpptr, PHYSFS_ERR_OUT_OF_MEMORY, failed);
for (i = 0; i < unilen; i++)
{
const UniChar ch = uc2str[i];
GOTO_IF(ch > 0xFF, PHYSFS_ERR_BAD_FILENAME, failed);
cpptr[i] = (char) ((unsigned char) ch);
} /* for */
__PHYSFS_smallFree(uc2ptr);
return cpstr;
} /* if */
else
{
int rc;
size_t cplen = unilen * 4; /* overallocate, just in case. */
cpptr = (char *) allocator.Malloc(cplen);
GOTO_IF(!cpptr, PHYSFS_ERR_OUT_OF_MEMORY, failed);
cpstr = cpptr;
rc = pUniUconvFromUcs(uconv, &uc2str, &unilen, (void **) &cpstr, &cplen, &subs);
GOTO_IF(rc != ULS_SUCCESS, PHYSFS_ERR_BAD_FILENAME, failed);
GOTO_IF(subs > 0, PHYSFS_ERR_BAD_FILENAME, failed);
assert(unilen == 0);
__PHYSFS_smallFree(uc2ptr);
return cpptr;
} /* else */
failed:
__PHYSFS_smallFree(uc2ptr);
allocator.Free(cpptr);
return NULL;
} /* cvtUtf8ToCodepage */
static char *cvtCodepageToUtf8(const char *cpstr)
{
const size_t len = strlen(cpstr) + 1;
char *retvalbuf = (char *) allocator.Malloc(len * 4);
char *retval = NULL;
BAIL_IF(!retvalbuf, PHYSFS_ERR_OUT_OF_MEMORY, NULL);
if (!uconvdll)
{
/* There's really not much we can do on older OS/2s except pray this
is latin1-compatible. */
retval = retvalbuf;
PHYSFS_utf8FromLatin1(cpstr, retval, len * 4);
} /* if */
else
{
int rc;
size_t cplen = len;
size_t unilen = len;
size_t subs = 0;
UniChar *uc2ptr = __PHYSFS_smallAlloc(len * sizeof (UniChar));
UniChar *uc2str = uc2ptr;
BAIL_IF(!uc2ptr, PHYSFS_ERR_OUT_OF_MEMORY, NULL);
rc = pUniUconvToUcs(uconv, (void **) &cpstr, &cplen, &uc2str, &unilen, &subs);
GOTO_IF(rc != ULS_SUCCESS, PHYSFS_ERR_BAD_FILENAME, done);
GOTO_IF(subs > 0, PHYSFS_ERR_BAD_FILENAME, done);
assert(cplen == 0);
retval = retvalbuf;
PHYSFS_utf8FromUcs2((const PHYSFS_uint16 *) uc2ptr, retval, len * 4);
done:
__PHYSFS_smallFree(uc2ptr);
} /* else */
return retval;
} /* cvtCodepageToUtf8 */
/* (be gentle, this function isn't very robust.) */
static char *cvtPathToCorrectCase(char *buf)
{
char *retval = buf;
char *fname = buf + 3; /* point to first element. */
char *ptr = strchr(fname, '\\'); /* find end of first element. */
buf[0] = toupper(buf[0]); /* capitalize drive letter. */
/*
* Go through each path element, and enumerate its parent dir until
* a case-insensitive match is found. If one is (and it SHOULD be)
* then overwrite the original element with the correct case.
* If there's an error, or the path has vanished for some reason, it
* won't hurt to have the original case, so we just keep going.
*/
while ((fname != NULL) && (*fname != '\0'))
{
char spec[CCHMAXPATH];
FILEFINDBUF3 fb;
HDIR hdir = HDIR_CREATE;
ULONG count = 1;
APIRET rc;
*(fname - 1) = '\0'; /* isolate parent dir string. */
strcpy(spec, buf); /* copy isolated parent dir... */
strcat(spec, "\\*.*"); /* ...and add wildcard search spec. */
if (ptr != NULL) /* isolate element to find (fname is the start). */
*ptr = '\0';
rc = DosFindFirst((unsigned char *) spec, &hdir, FILE_DIRECTORY,
&fb, sizeof (fb), &count, FIL_STANDARD);
if (rc == NO_ERROR)
{
while (count == 1) /* while still entries to enumerate... */
{
int cmp;
char *utf8 = cvtCodepageToUtf8(fb.achName);
if (!utf8) /* ugh, maybe we'll get lucky with the C runtime. */
cmp = stricmp(fb.achName, fname);
else
{
cmp = PHYSFS_utf8stricmp(utf8, fname);
allocator.Free(utf8);
} /* else */
if (cmp == 0)
{
strcpy(fname, fb.achName);
break; /* there it is. Overwrite and stop searching. */
} /* if */
DosFindNext(hdir, &fb, sizeof (fb), &count);
} /* while */
DosFindClose(hdir);
} /* if */
*(fname - 1) = '\\'; /* unisolate parent dir. */
fname = ptr; /* point to next element. */
if (ptr != NULL)
{
*ptr = '\\'; /* unisolate element. */
ptr = strchr(++fname, '\\'); /* find next element. */
} /* if */
} /* while */
return retval;
} /* cvtPathToCorrectCase */
static void prepUnicodeSupport(void)
{
/* really old OS/2 might not have Unicode support _at all_, so load
the system library and do without if it doesn't exist. */
int ok = 0;
char buf[CCHMAXPATH];
UniChar defstr[] = { 0 };
if (DosLoadModule(buf, sizeof (buf) - 1, "uconv", &uconvdll) == NO_ERROR)
{
#define LOAD(x) (DosQueryProcAddr(uconvdll,0,#x,(PFN*)&p##x)==NO_ERROR)
ok = LOAD(UniCreateUconvObject) &&
LOAD(UniFreeUconvObject) &&
LOAD(UniUconvToUcs) &&
LOAD(UniUconvFromUcs);
#undef LOAD
} /* else */
if (!ok || (pUniCreateUconvObject(defstr, &uconv) != ULS_SUCCESS))
{
/* oh well, live without it. */
if (uconvdll)
{
if (uconv)
pUniFreeUconvObject(uconv);
DosFreeModule(uconvdll);
uconvdll = 0;
} /* if */
} /* if */
} /* prepUnicodeSupport */
int __PHYSFS_platformInit(void)
{
prepUnicodeSupport();
return 1; /* ready to go! */
} /* __PHYSFS_platformInit */
void __PHYSFS_platformDeinit(void)
{
if (uconvdll)
{
pUniFreeUconvObject(uconv);
uconv = 0;
DosFreeModule(uconvdll);
uconvdll = 0;
} /* if */
} /* __PHYSFS_platformDeinit */
static int discIsInserted(ULONG drive)
{
int rc;
char buf[20];
DosError(FERR_DISABLEHARDERR | FERR_DISABLEEXCEPTION);
rc = DosQueryFSInfo(drive + 1, FSIL_VOLSER, buf, sizeof (buf));
DosError(FERR_ENABLEHARDERR | FERR_ENABLEEXCEPTION);
return (rc == NO_ERROR);
} /* is_cdrom_inserted */
/* looks like "CD01" in ASCII (littleendian)...used for an ioctl. */
#define CD01 0x31304443
static int isCdRomDrive(ULONG drive)
{
PHYSFS_uint32 param, data;
ULONG ul1, ul2;
APIRET rc;
HFILE hfile = NULLHANDLE;
unsigned char drivename[3] = { 0, ':', '\0' };
drivename[0] = 'A' + drive;
rc = DosOpen(drivename, &hfile, &ul1, 0, 0,
OPEN_ACTION_OPEN_IF_EXISTS | OPEN_ACTION_FAIL_IF_NEW,
OPEN_FLAGS_DASD | OPEN_FLAGS_FAIL_ON_ERROR |
OPEN_FLAGS_NOINHERIT | OPEN_SHARE_DENYNONE, NULL);
if (rc != NO_ERROR)
return 0;
data = 0;
param = PHYSFS_swapULE32(CD01);
ul1 = ul2 = sizeof (PHYSFS_uint32);
rc = DosDevIOCtl(hfile, IOCTL_CDROMDISK, CDROMDISK_GETDRIVER,
&param, sizeof (param), &ul1, &data, sizeof (data), &ul2);
DosClose(hfile);
return ((rc == NO_ERROR) && (PHYSFS_swapULE32(data) == CD01));
} /* isCdRomDrive */
void __PHYSFS_platformDetectAvailableCDs(PHYSFS_StringCallback cb, void *data)
{
ULONG dummy = 0;
ULONG drivemap = 0;
ULONG i, bit;
const APIRET rc = DosQueryCurrentDisk(&dummy, &drivemap);
BAIL_IF(rc != NO_ERROR, errcodeFromAPIRET(rc),);
for (i = 0, bit = 1; i < 26; i++, bit <<= 1)
{
if (drivemap & bit) /* this logical drive exists. */
{
if ((isCdRomDrive(i)) && (discIsInserted(i)))
{
char drive[4] = "x:\\";
drive[0] = ('A' + i);
cb(data, drive);
} /* if */
} /* if */
} /* for */
} /* __PHYSFS_platformDetectAvailableCDs */
char *__PHYSFS_platformCalcBaseDir(const char *argv0)
{
char *retval = NULL;
char buf[CCHMAXPATH];
APIRET rc;
PTIB ptib;
PPIB ppib;
PHYSFS_sint32 len;
rc = DosGetInfoBlocks(&ptib, &ppib);
BAIL_IF(rc != NO_ERROR, errcodeFromAPIRET(rc), 0);
rc = DosQueryModuleName(ppib->pib_hmte, sizeof (buf), (PCHAR) buf);
BAIL_IF(rc != NO_ERROR, errcodeFromAPIRET(rc), 0);
retval = cvtCodepageToUtf8(buf);
BAIL_IF_ERRPASS(!retval, NULL);
/* chop off filename, leave path. */
for (len = strlen(retval) - 1; len >= 0; len--)
{
if (retval[len] == '\\')
{
retval[len + 1] = '\0';
break;
} /* if */
} /* for */
assert(len > 0); /* should have been a "x:\\" on the front on string. */
/* The string is capitalized! Figure out the REAL case... */
return cvtPathToCorrectCase(retval);
} /* __PHYSFS_platformCalcBaseDir */
char *__PHYSFS_platformCalcUserDir(void)
{
return __PHYSFS_platformCalcBaseDir(NULL); /* !!! FIXME: ? */
} /* __PHYSFS_platformCalcUserDir */
char *__PHYSFS_platformCalcPrefDir(const char *org, const char *app)
{
return __PHYSFS_platformCalcBaseDir(NULL); /* !!! FIXME: ? */
} /* __PHYSFS_platformCalcPrefDir */
PHYSFS_EnumerateCallbackResult __PHYSFS_platformEnumerate(const char *dirname,
PHYSFS_EnumerateCallback callback,
const char *origdir, void *callbackdata)
{
PHYSFS_EnumerateCallbackResult retval = PHYSFS_ENUM_OK;
size_t utf8len = strlen(dirname);
char *utf8 = (char *) __PHYSFS_smallAlloc(utf8len + 5);
char *cpspec = NULL;
FILEFINDBUF3 fb;
HDIR hdir = HDIR_CREATE;
ULONG count = 1;
APIRET rc;
BAIL_IF(!utf8, PHYSFS_ERR_OUT_OF_MEMORY, PHYSFS_ENUM_ERROR);
strcpy(utf8, dirname);
if (utf8[utf8len - 1] != '\\')
strcpy(utf8 + utf8len, "\\*.*");
else
strcpy(utf8 + utf8len, "*.*");
cpspec = cvtUtf8ToCodepage(utf8);
__PHYSFS_smallFree(utf8);
BAIL_IF_ERRPASS(!cpspec, PHYSFS_ENUM_ERROR);
rc = DosFindFirst((unsigned char *) cpspec, &hdir,
FILE_DIRECTORY | FILE_ARCHIVED |
FILE_READONLY | FILE_HIDDEN | FILE_SYSTEM,
&fb, sizeof (fb), &count, FIL_STANDARD);
allocator.Free(cpspec);
BAIL_IF(rc != NO_ERROR, errcodeFromAPIRET(rc), PHYSFS_ENUM_ERROR);
while (count == 1)
{
if ((strcmp(fb.achName, ".") != 0) && (strcmp(fb.achName, "..") != 0))
{
utf8 = cvtCodepageToUtf8(fb.achName);
if (!utf8)
retval = PHYSFS_ENUM_ERROR;
else
{
retval = callback(callbackdata, origdir, utf8);
allocator.Free(utf8);
if (retval == PHYSFS_ENUM_ERROR)
PHYSFS_setErrorCode(PHYSFS_ERR_APP_CALLBACK);
} /* else */
} /* if */
if (retval != PHYSFS_ENUM_OK)
break;
DosFindNext(hdir, &fb, sizeof (fb), &count);
} /* while */
DosFindClose(hdir);
return retval;
} /* __PHYSFS_platformEnumerate */
char *__PHYSFS_platformCurrentDir(void)
{
char *retval;
char *cpstr;
char *utf8;
ULONG currentDisk;
ULONG dummy;
ULONG pathSize = 0;
APIRET rc;
BYTE byte;
rc = DosQueryCurrentDisk(&currentDisk, &dummy);
BAIL_IF(rc != NO_ERROR, errcodeFromAPIRET(rc), NULL);
/* The first call just tells us how much space we need for the string. */
rc = DosQueryCurrentDir(currentDisk, &byte, &pathSize);
pathSize++; /* Add space for null terminator. */
cpstr = (char *) __PHYSFS_smallAlloc(pathSize);
BAIL_IF(cpstr == NULL, PHYSFS_ERR_OUT_OF_MEMORY, NULL);
/* Actually get the string this time. */
rc = DosQueryCurrentDir(currentDisk, (PBYTE) cpstr, &pathSize);
if (rc != NO_ERROR)
{
__PHYSFS_smallFree(cpstr);
BAIL(errcodeFromAPIRET(rc), NULL);
} /* if */
utf8 = cvtCodepageToUtf8(cpstr);
__PHYSFS_smallFree(cpstr);
BAIL_IF_ERRPASS(utf8 == NULL, NULL);
/* +4 for "x:\\" drive selector and null terminator. */
retval = (char *) allocator.Malloc(strlen(utf8) + 4);
if (retval == NULL)
{
allocator.Free(utf8);
BAIL(PHYSFS_ERR_OUT_OF_MEMORY, NULL);
} /* if */
retval[0] = ('A' + (currentDisk - 1));
retval[1] = ':';
retval[2] = '\\';
strcpy(retval + 3, utf8);
allocator.Free(utf8);
return retval;
} /* __PHYSFS_platformCurrentDir */
int __PHYSFS_platformMkDir(const char *filename)
{
APIRET rc;
char *cpstr = cvtUtf8ToCodepage(filename);
BAIL_IF_ERRPASS(!cpstr, 0);
rc = DosCreateDir((unsigned char *) cpstr, NULL);
allocator.Free(cpstr);
BAIL_IF(rc != NO_ERROR, errcodeFromAPIRET(rc), 0);
return 1;
} /* __PHYSFS_platformMkDir */
static HFILE openFile(const char *filename, const ULONG flags, const ULONG mode)
{
char *cpfname = cvtUtf8ToCodepage(filename);
ULONG action = 0;
HFILE hfile = NULLHANDLE;
APIRET rc;
BAIL_IF_ERRPASS(!cpfname, 0);
rc = DosOpen(cpfname, &hfile, &action, 0, FILE_NORMAL, flags, mode, NULL);
allocator.Free(cpfname);
BAIL_IF(rc != NO_ERROR, errcodeFromAPIRET(rc), 0);
return hfile;
} /* openFile */
void *__PHYSFS_platformOpenRead(const char *filename)
{
/*
* File must be opened SHARE_DENYWRITE and ACCESS_READONLY, otherwise
* DosQueryFileInfo() will fail if we try to get a file length, etc.
*/
return (void *) openFile(filename,
OPEN_ACTION_OPEN_IF_EXISTS | OPEN_ACTION_FAIL_IF_NEW,
OPEN_FLAGS_FAIL_ON_ERROR | OPEN_FLAGS_NO_LOCALITY |
OPEN_FLAGS_NOINHERIT | OPEN_SHARE_DENYWRITE |
OPEN_ACCESS_READONLY);
} /* __PHYSFS_platformOpenRead */
void *__PHYSFS_platformOpenWrite(const char *filename)
{
return (void *) openFile(filename,
OPEN_ACTION_REPLACE_IF_EXISTS |
OPEN_ACTION_CREATE_IF_NEW,
OPEN_FLAGS_FAIL_ON_ERROR | OPEN_FLAGS_NO_LOCALITY |
OPEN_FLAGS_NOINHERIT | OPEN_SHARE_DENYWRITE);
} /* __PHYSFS_platformOpenWrite */
void *__PHYSFS_platformOpenAppend(const char *filename)
{
APIRET rc;
ULONG dummy = 0;
HFILE hfile;
/*
* File must be opened SHARE_DENYWRITE and ACCESS_READWRITE, otherwise
* DosQueryFileInfo() will fail if we try to get a file length, etc.
*/
hfile = openFile(filename,
OPEN_ACTION_OPEN_IF_EXISTS | OPEN_ACTION_CREATE_IF_NEW,
OPEN_FLAGS_FAIL_ON_ERROR | OPEN_FLAGS_NO_LOCALITY |
OPEN_FLAGS_NOINHERIT | OPEN_SHARE_DENYWRITE |
OPEN_ACCESS_READWRITE);
BAIL_IF_ERRPASS(!hfile, NULL);
rc = DosSetFilePtr(hfile, 0, FILE_END, &dummy);
if (rc != NO_ERROR)
{
DosClose(hfile);
BAIL(errcodeFromAPIRET(rc), NULL);
} /* if */
return ((void *) hfile);
} /* __PHYSFS_platformOpenAppend */
PHYSFS_sint64 __PHYSFS_platformRead(void *opaque, void *buf, PHYSFS_uint64 len)
{
ULONG br = 0;
APIRET rc;
BAIL_IF(!__PHYSFS_ui64FitsAddressSpace(len),PHYSFS_ERR_INVALID_ARGUMENT,-1);
rc = DosRead((HFILE) opaque, buf, (ULONG) len, &br);
BAIL_IF(rc != NO_ERROR, errcodeFromAPIRET(rc), (br > 0) ? ((PHYSFS_sint64) br) : -1);
return (PHYSFS_sint64) br;
} /* __PHYSFS_platformRead */
PHYSFS_sint64 __PHYSFS_platformWrite(void *opaque, const void *buf,
PHYSFS_uint64 len)
{
ULONG bw = 0;
APIRET rc;
BAIL_IF(!__PHYSFS_ui64FitsAddressSpace(len),PHYSFS_ERR_INVALID_ARGUMENT,-1);
rc = DosWrite((HFILE) opaque, (void *) buf, (ULONG) len, &bw);
BAIL_IF(rc != NO_ERROR, errcodeFromAPIRET(rc), (bw > 0) ? ((PHYSFS_sint64) bw) : -1);
return (PHYSFS_sint64) bw;
} /* __PHYSFS_platformWrite */
int __PHYSFS_platformSeek(void *opaque, PHYSFS_uint64 pos)
{
ULONG dummy;
HFILE hfile = (HFILE) opaque;
LONG dist = (LONG) pos;
APIRET rc;
/* hooray for 32-bit filesystem limits! :) */
BAIL_IF((PHYSFS_uint64) dist != pos, PHYSFS_ERR_INVALID_ARGUMENT, 0);
rc = DosSetFilePtr(hfile, dist, FILE_BEGIN, &dummy);
BAIL_IF(rc != NO_ERROR, errcodeFromAPIRET(rc), 0);
return 1;
} /* __PHYSFS_platformSeek */
PHYSFS_sint64 __PHYSFS_platformTell(void *opaque)
{
ULONG pos;
HFILE hfile = (HFILE) opaque;
const APIRET rc = DosSetFilePtr(hfile, 0, FILE_CURRENT, &pos);
BAIL_IF(rc != NO_ERROR, errcodeFromAPIRET(rc), -1);
return ((PHYSFS_sint64) pos);
} /* __PHYSFS_platformTell */
PHYSFS_sint64 __PHYSFS_platformFileLength(void *opaque)
{
FILESTATUS3 fs;
HFILE hfile = (HFILE) opaque;
const APIRET rc = DosQueryFileInfo(hfile, FIL_STANDARD, &fs, sizeof (fs));
BAIL_IF(rc != NO_ERROR, errcodeFromAPIRET(rc), -1);
return ((PHYSFS_sint64) fs.cbFile);
} /* __PHYSFS_platformFileLength */
int __PHYSFS_platformFlush(void *opaque)
{
const APIRET rc = DosResetBuffer((HFILE) opaque);
BAIL_IF(rc != NO_ERROR, errcodeFromAPIRET(rc), 0);
return 1;
} /* __PHYSFS_platformFlush */
void __PHYSFS_platformClose(void *opaque)
{
DosClose((HFILE) opaque); /* ignore errors. You should have flushed! */
} /* __PHYSFS_platformClose */
int __PHYSFS_platformDelete(const char *path)
{
char *cppath = cvtUtf8ToCodepage(path);
FILESTATUS3 fs;
APIRET rc;
int retval = 0;
BAIL_IF_ERRPASS(!cppath, 0);
rc = DosQueryPathInfo(cppath, FIL_STANDARD, &fs, sizeof (fs));
GOTO_IF(rc != NO_ERROR, errcodeFromAPIRET(rc), done);
rc = (fs.attrFile & FILE_DIRECTORY) ? DosDeleteDir(path) : DosDelete(path);
GOTO_IF(rc != NO_ERROR, errcodeFromAPIRET(rc), done);
retval = 1; /* success */
done:
allocator.Free(cppath);
return retval;
} /* __PHYSFS_platformDelete */
/* Convert to a format PhysicsFS can grok... */
PHYSFS_sint64 os2TimeToUnixTime(const FDATE *date, const FTIME *time)
{
struct tm tm;
tm.tm_sec = ((PHYSFS_uint32) time->twosecs) * 2;
tm.tm_min = time->minutes;
tm.tm_hour = time->hours;
tm.tm_mday = date->day;
tm.tm_mon = date->month;
tm.tm_year = ((PHYSFS_uint32) date->year) + 80;
tm.tm_wday = -1 /*st_localtz.wDayOfWeek*/;
tm.tm_yday = -1;
tm.tm_isdst = -1;
return (PHYSFS_sint64) mktime(&tm);
} /* os2TimeToUnixTime */
int __PHYSFS_platformStat(const char *filename, PHYSFS_Stat *stat, const int follow)
{
char *cpfname = cvtUtf8ToCodepage(filename);
FILESTATUS3 fs;
int retval = 0;
APIRET rc;
BAIL_IF_ERRPASS(!cpfname, 0);
rc = DosQueryPathInfo(cpfname, FIL_STANDARD, &fs, sizeof (fs));
GOTO_IF(rc != NO_ERROR, errcodeFromAPIRET(rc), done);
if (fs.attrFile & FILE_DIRECTORY)
{
stat->filetype = PHYSFS_FILETYPE_DIRECTORY;
stat->filesize = 0;
} /* if */
else
{
stat->filetype = PHYSFS_FILETYPE_REGULAR;
stat->filesize = fs.cbFile;
} /* else */
stat->modtime = os2TimeToUnixTime(&fs.fdateLastWrite, &fs.ftimeLastWrite);
if (stat->modtime < 0)
stat->modtime = 0;
stat->accesstime = os2TimeToUnixTime(&fs.fdateLastAccess, &fs.ftimeLastAccess);
if (stat->accesstime < 0)
stat->accesstime = 0;
stat->createtime = os2TimeToUnixTime(&fs.fdateCreation, &fs.ftimeCreation);
if (stat->createtime < 0)
stat->createtime = 0;
stat->readonly = ((fs.attrFile & FILE_READONLY) == FILE_READONLY);
return 1; /* success */
done:
allocator.Free(cpfname);
return retval;
} /* __PHYSFS_platformStat */
void *__PHYSFS_platformGetThreadID(void)
{
PTIB ptib;
PPIB ppib;
/*
* Allegedly, this API never fails, but we'll punt and return a
* default value (zero might as well do) if it does.
*/
const APIRET rc = DosGetInfoBlocks(&ptib, &ppib);
BAIL_IF(rc != NO_ERROR, errcodeFromAPIRET(rc), 0);
return ((void *) ptib->tib_ordinal);
} /* __PHYSFS_platformGetThreadID */
void *__PHYSFS_platformCreateMutex(void)
{
HMTX hmtx = NULLHANDLE;
const APIRET rc = DosCreateMutexSem(NULL, &hmtx, 0, 0);
BAIL_IF(rc != NO_ERROR, errcodeFromAPIRET(rc), NULL);
return ((void *) hmtx);
} /* __PHYSFS_platformCreateMutex */
void __PHYSFS_platformDestroyMutex(void *mutex)
{
DosCloseMutexSem((HMTX) mutex);
} /* __PHYSFS_platformDestroyMutex */
int __PHYSFS_platformGrabMutex(void *mutex)
{
/* Do _NOT_ set the physfs error message in here! */
return (DosRequestMutexSem((HMTX) mutex, SEM_INDEFINITE_WAIT) == NO_ERROR);
} /* __PHYSFS_platformGrabMutex */
void __PHYSFS_platformReleaseMutex(void *mutex)
{
DosReleaseMutexSem((HMTX) mutex);
} /* __PHYSFS_platformReleaseMutex */
#endif /* PHYSFS_PLATFORM_OS2 */
/* end of physfs_platform_os2.c ... */

417
src/physfs_platform_posix.c Executable file
View File

@ -0,0 +1,417 @@
/*
* Posix-esque support routines for PhysicsFS.
*
* Please see the file LICENSE.txt in the source's root directory.
*
* This file written by Ryan C. Gordon.
*/
/* !!! FIXME: check for EINTR? */
#define __PHYSICSFS_INTERNAL__
#include "physfs_platforms.h"
#ifdef PHYSFS_PLATFORM_POSIX
#include <unistd.h>
#include <ctype.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <pwd.h>
#include <dirent.h>
#include <errno.h>
#include <fcntl.h>
#include <pthread.h>
#include "physfs_internal.h"
static PHYSFS_ErrorCode errcodeFromErrnoError(const int err)
{
switch (err)
{
case 0: return PHYSFS_ERR_OK;
case EACCES: return PHYSFS_ERR_PERMISSION;
case EPERM: return PHYSFS_ERR_PERMISSION;
case EDQUOT: return PHYSFS_ERR_NO_SPACE;
case EIO: return PHYSFS_ERR_IO;
case ELOOP: return PHYSFS_ERR_SYMLINK_LOOP;
case EMLINK: return PHYSFS_ERR_NO_SPACE;
case ENAMETOOLONG: return PHYSFS_ERR_BAD_FILENAME;
case ENOENT: return PHYSFS_ERR_NOT_FOUND;
case ENOSPC: return PHYSFS_ERR_NO_SPACE;
case ENOTDIR: return PHYSFS_ERR_NOT_FOUND;
case EISDIR: return PHYSFS_ERR_NOT_A_FILE;
case EROFS: return PHYSFS_ERR_READ_ONLY;
case ETXTBSY: return PHYSFS_ERR_BUSY;
case EBUSY: return PHYSFS_ERR_BUSY;
case ENOMEM: return PHYSFS_ERR_OUT_OF_MEMORY;
case ENOTEMPTY: return PHYSFS_ERR_DIR_NOT_EMPTY;
default: return PHYSFS_ERR_OS_ERROR;
} /* switch */
} /* errcodeFromErrnoError */
static inline PHYSFS_ErrorCode errcodeFromErrno(void)
{
return errcodeFromErrnoError(errno);
} /* errcodeFromErrno */
static char *getUserDirByUID(void)
{
uid_t uid = getuid();
struct passwd *pw;
char *retval = NULL;
pw = getpwuid(uid);
if ((pw != NULL) && (pw->pw_dir != NULL) && (*pw->pw_dir != '\0'))
{
const size_t dlen = strlen(pw->pw_dir);
const size_t add_dirsep = (pw->pw_dir[dlen-1] != '/') ? 1 : 0;
retval = (char *) allocator.Malloc(dlen + 1 + add_dirsep);
if (retval != NULL)
{
strcpy(retval, pw->pw_dir);
if (add_dirsep)
{
retval[dlen] = '/';
retval[dlen+1] = '\0';
} /* if */
} /* if */
} /* if */
return retval;
} /* getUserDirByUID */
char *__PHYSFS_platformCalcUserDir(void)
{
char *retval = NULL;
char *envr = getenv("HOME");
/* if the environment variable was set, make sure it's really a dir. */
if (envr != NULL)
{
struct stat statbuf;
if ((stat(envr, &statbuf) != -1) && (S_ISDIR(statbuf.st_mode)))
{
const size_t envrlen = strlen(envr);
const size_t add_dirsep = (envr[envrlen-1] != '/') ? 1 : 0;
retval = allocator.Malloc(envrlen + 1 + add_dirsep);
if (retval)
{
strcpy(retval, envr);
if (add_dirsep)
{
retval[envrlen] = '/';
retval[envrlen+1] = '\0';
} /* if */
} /* if */
} /* if */
} /* if */
if (retval == NULL)
retval = getUserDirByUID();
return retval;
} /* __PHYSFS_platformCalcUserDir */
PHYSFS_EnumerateCallbackResult __PHYSFS_platformEnumerate(const char *dirname,
PHYSFS_EnumerateCallback callback,
const char *origdir, void *callbackdata)
{
DIR *dir;
struct dirent *ent;
PHYSFS_EnumerateCallbackResult retval = PHYSFS_ENUM_OK;
dir = opendir(dirname);
BAIL_IF(dir == NULL, errcodeFromErrno(), PHYSFS_ENUM_ERROR);
while ((retval == PHYSFS_ENUM_OK) && ((ent = readdir(dir)) != NULL))
{
const char *name = ent->d_name;
if (name[0] == '.') /* ignore "." and ".." */
{
if ((name[1] == '\0') || ((name[1] == '.') && (name[2] == '\0')))
continue;
} /* if */
retval = callback(callbackdata, origdir, name);
if (retval == PHYSFS_ENUM_ERROR)
PHYSFS_setErrorCode(PHYSFS_ERR_APP_CALLBACK);
} /* while */
closedir(dir);
return retval;
} /* __PHYSFS_platformEnumerate */
int __PHYSFS_platformMkDir(const char *path)
{
const int rc = mkdir(path, S_IRWXU);
BAIL_IF(rc == -1, errcodeFromErrno(), 0);
return 1;
} /* __PHYSFS_platformMkDir */
static void *doOpen(const char *filename, int mode)
{
const int appending = (mode & O_APPEND);
int fd;
int *retval;
errno = 0;
/* O_APPEND doesn't actually behave as we'd like. */
mode &= ~O_APPEND;
fd = open(filename, mode, S_IRUSR | S_IWUSR);
BAIL_IF(fd < 0, errcodeFromErrno(), NULL);
if (appending)
{
if (lseek(fd, 0, SEEK_END) < 0)
{
const int err = errno;
close(fd);
BAIL(errcodeFromErrnoError(err), NULL);
} /* if */
} /* if */
retval = (int *) allocator.Malloc(sizeof (int));
if (!retval)
{
close(fd);
BAIL(PHYSFS_ERR_OUT_OF_MEMORY, NULL);
} /* if */
*retval = fd;
return ((void *) retval);
} /* doOpen */
void *__PHYSFS_platformOpenRead(const char *filename)
{
return doOpen(filename, O_RDONLY);
} /* __PHYSFS_platformOpenRead */
void *__PHYSFS_platformOpenWrite(const char *filename)
{
return doOpen(filename, O_WRONLY | O_CREAT | O_TRUNC);
} /* __PHYSFS_platformOpenWrite */
void *__PHYSFS_platformOpenAppend(const char *filename)
{
return doOpen(filename, O_WRONLY | O_CREAT | O_APPEND);
} /* __PHYSFS_platformOpenAppend */
PHYSFS_sint64 __PHYSFS_platformRead(void *opaque, void *buffer,
PHYSFS_uint64 len)
{
const int fd = *((int *) opaque);
ssize_t rc = 0;
if (!__PHYSFS_ui64FitsAddressSpace(len))
BAIL(PHYSFS_ERR_INVALID_ARGUMENT, -1);
rc = read(fd, buffer, (size_t) len);
BAIL_IF(rc == -1, errcodeFromErrno(), -1);
assert(rc >= 0);
assert(rc <= len);
return (PHYSFS_sint64) rc;
} /* __PHYSFS_platformRead */
PHYSFS_sint64 __PHYSFS_platformWrite(void *opaque, const void *buffer,
PHYSFS_uint64 len)
{
const int fd = *((int *) opaque);
ssize_t rc = 0;
if (!__PHYSFS_ui64FitsAddressSpace(len))
BAIL(PHYSFS_ERR_INVALID_ARGUMENT, -1);
rc = write(fd, (void *) buffer, (size_t) len);
BAIL_IF(rc == -1, errcodeFromErrno(), rc);
assert(rc >= 0);
assert(rc <= len);
return (PHYSFS_sint64) rc;
} /* __PHYSFS_platformWrite */
int __PHYSFS_platformSeek(void *opaque, PHYSFS_uint64 pos)
{
const int fd = *((int *) opaque);
const off_t rc = lseek(fd, (off_t) pos, SEEK_SET);
BAIL_IF(rc == -1, errcodeFromErrno(), 0);
return 1;
} /* __PHYSFS_platformSeek */
PHYSFS_sint64 __PHYSFS_platformTell(void *opaque)
{
const int fd = *((int *) opaque);
PHYSFS_sint64 retval;
retval = (PHYSFS_sint64) lseek(fd, 0, SEEK_CUR);
BAIL_IF(retval == -1, errcodeFromErrno(), -1);
return retval;
} /* __PHYSFS_platformTell */
PHYSFS_sint64 __PHYSFS_platformFileLength(void *opaque)
{
const int fd = *((int *) opaque);
struct stat statbuf;
BAIL_IF(fstat(fd, &statbuf) == -1, errcodeFromErrno(), -1);
return ((PHYSFS_sint64) statbuf.st_size);
} /* __PHYSFS_platformFileLength */
int __PHYSFS_platformFlush(void *opaque)
{
const int fd = *((int *) opaque);
if ((fcntl(fd, F_GETFL) & O_ACCMODE) != O_RDONLY)
BAIL_IF(fsync(fd) == -1, errcodeFromErrno(), 0);
return 1;
} /* __PHYSFS_platformFlush */
void __PHYSFS_platformClose(void *opaque)
{
const int fd = *((int *) opaque);
(void) close(fd); /* we don't check this. You should have used flush! */
allocator.Free(opaque);
} /* __PHYSFS_platformClose */
int __PHYSFS_platformDelete(const char *path)
{
BAIL_IF(remove(path) == -1, errcodeFromErrno(), 0);
return 1;
} /* __PHYSFS_platformDelete */
int __PHYSFS_platformStat(const char *fname, PHYSFS_Stat *st, const int follow)
{
struct stat statbuf;
const int rc = follow ? stat(fname, &statbuf) : lstat(fname, &statbuf);
BAIL_IF(rc == -1, errcodeFromErrno(), 0);
if (S_ISREG(statbuf.st_mode))
{
st->filetype = PHYSFS_FILETYPE_REGULAR;
st->filesize = statbuf.st_size;
} /* if */
else if(S_ISDIR(statbuf.st_mode))
{
st->filetype = PHYSFS_FILETYPE_DIRECTORY;
st->filesize = 0;
} /* else if */
else if(S_ISLNK(statbuf.st_mode))
{
st->filetype = PHYSFS_FILETYPE_SYMLINK;
st->filesize = 0;
} /* else if */
else
{
st->filetype = PHYSFS_FILETYPE_OTHER;
st->filesize = statbuf.st_size;
} /* else */
st->modtime = statbuf.st_mtime;
st->createtime = statbuf.st_ctime;
st->accesstime = statbuf.st_atime;
st->readonly = (access(fname, W_OK) == -1);
return 1;
} /* __PHYSFS_platformStat */
typedef struct
{
pthread_mutex_t mutex;
pthread_t owner;
PHYSFS_uint32 count;
} PthreadMutex;
void *__PHYSFS_platformGetThreadID(void)
{
return ( (void *) ((size_t) pthread_self()) );
} /* __PHYSFS_platformGetThreadID */
void *__PHYSFS_platformCreateMutex(void)
{
int rc;
PthreadMutex *m = (PthreadMutex *) allocator.Malloc(sizeof (PthreadMutex));
BAIL_IF(!m, PHYSFS_ERR_OUT_OF_MEMORY, NULL);
rc = pthread_mutex_init(&m->mutex, NULL);
if (rc != 0)
{
allocator.Free(m);
BAIL(PHYSFS_ERR_OS_ERROR, NULL);
} /* if */
m->count = 0;
m->owner = (pthread_t) 0xDEADBEEF;
return ((void *) m);
} /* __PHYSFS_platformCreateMutex */
void __PHYSFS_platformDestroyMutex(void *mutex)
{
PthreadMutex *m = (PthreadMutex *) mutex;
/* Destroying a locked mutex is a bug, but we'll try to be helpful. */
if ((m->owner == pthread_self()) && (m->count > 0))
pthread_mutex_unlock(&m->mutex);
pthread_mutex_destroy(&m->mutex);
allocator.Free(m);
} /* __PHYSFS_platformDestroyMutex */
int __PHYSFS_platformGrabMutex(void *mutex)
{
PthreadMutex *m = (PthreadMutex *) mutex;
pthread_t tid = pthread_self();
if (m->owner != tid)
{
if (pthread_mutex_lock(&m->mutex) != 0)
return 0;
m->owner = tid;
} /* if */
m->count++;
return 1;
} /* __PHYSFS_platformGrabMutex */
void __PHYSFS_platformReleaseMutex(void *mutex)
{
PthreadMutex *m = (PthreadMutex *) mutex;
assert(m->owner == pthread_self()); /* catch programming errors. */
assert(m->count > 0); /* catch programming errors. */
if (m->owner == pthread_self())
{
if (--m->count == 0)
{
m->owner = (pthread_t) 0xDEADBEEF;
pthread_mutex_unlock(&m->mutex);
} /* if */
} /* if */
} /* __PHYSFS_platformReleaseMutex */
#endif /* PHYSFS_PLATFORM_POSIX */
/* end of physfs_platform_posix.c ... */

169
src/physfs_platform_qnx.c Executable file
View File

@ -0,0 +1,169 @@
/*
* QNX support routines for PhysicsFS.
*
* Please see the file LICENSE.txt in the source's root directory.
*
* This file written by Ryan C. Gordon.
*/
/* This is tested against QNX 7 at the moment. */
#define __PHYSICSFS_INTERNAL__
#include "physfs_platforms.h"
#ifdef PHYSFS_PLATFORM_QNX
#include <fcntl.h>
#include <unistd.h>
#include <sys/types.h>
#include <limits.h>
#include "physfs_internal.h"
int __PHYSFS_platformInit(void)
{
return 1; /* always succeed. */
} /* __PHYSFS_platformInit */
void __PHYSFS_platformDeinit(void)
{
/* no-op */
} /* __PHYSFS_platformDeinit */
char *__PHYSFS_platformCalcBaseDir(const char *argv0)
{
char *retval = (char *) allocator.Malloc(PATH_MAX+1);
if (retval == NULL)
BAIL(PHYSFS_ERR_OUT_OF_MEMORY, NULL);
else
{
const int fd = open("/proc/self/exefile", O_RDONLY);
const ssize_t br = (fd == -1) ? -1 : read(fd, retval, PATH_MAX);
char *ptr;
if (fd != -1)
close(fd);
if ((br < 0) || (br > PATH_MAX))
{
allocator.Free(retval);
BAIL(PHYSFS_ERR_OS_ERROR, NULL);
} /* if */
retval[br] = '\0';
ptr = strrchr(retval, '/');
if (ptr == NULL) /* uhoh! */
{
allocator.Free(retval);
BAIL(PHYSFS_ERR_OS_ERROR, NULL);
} /* if */
ptr[1] = '\0'; /* chop off filename, leave dirs and '/' */
ptr = (char *) allocator.Realloc(retval, (ptr - retval) + 2);
if (ptr != NULL) /* just shrinking buffer; don't care if it failed. */
retval = ptr;
} /* else */
return retval;
} /* __PHYSFS_platformCalcBaseDir */
char *__PHYSFS_platformCalcPrefDir(const char *org, const char *app)
{
/* !!! FIXME: this might be wrong; I don't know if there's a better method
on QNX, or if it follows XDG specs, etc. */
char *retval = NULL;
const char *home = __PHYSFS_getUserDir();
if (home)
{
const size_t len = strlen(home) + strlen(app) + 3;
retval = (char *) allocator.Malloc(len);
BAIL_IF(!retval, PHYSFS_ERR_OUT_OF_MEMORY, NULL);
snprintf(retval, len, "%s.%s/", home, app);
} /* if */
return retval;
} /* __PHYSFS_platformCalcPrefDir */
#if !PHYSFS_NO_CDROM_SUPPORT
#include <devctl.h>
#include <sys/dcmd_blk.h>
#include <dirent.h>
#include <sys/stat.h>
#include <sys/statvfs.h>
static void checkPathForCD(const char *path, PHYSFS_StringCallback cb, void *d)
{
struct stat statbuf;
int fd;
/* The devctl() thing is QNX-specific. In this case, we query what is
probably the mountpoint for the device. statvfs() on that mountpoint
will tell use its filesystem type. */
if ( (stat(path, &statbuf) == 0) &&
(S_ISBLK(statbuf.st_mode)) &&
((fd = open(path, O_RDONLY | O_NONBLOCK)) != -1) )
{
char mnt[256] = { 0 };
const int rc = devctl(fd, DCMD_FSYS_MOUNTED_BY, mnt, sizeof (mnt), 0);
close(fd);
if ( (rc == EOK) && (mnt[0]) )
{
struct statvfs statvfsbuf;
if (statvfs(mnt, &statvfsbuf) == 0)
{
/* I don't know if this is a complete or accurate list. */
const char *fstype = statvfsbuf.f_basetype;
const int iscd = ( (strcmp(fstype, "cd") == 0) ||
(strcmp(fstype, "udf") == 0) );
if (iscd)
cb(d, mnt);
} /* if */
} /* if */
} /* if */
} /* checkPathForCD */
static void checkDevForCD(const char *dev, PHYSFS_StringCallback cb, void *d)
{
size_t len;
char *path;
if (dev[0] == '.') /* ignore "." and ".." */
{
if ((dev[1] == '\0') || ((dev[1] == '.') && (dev[2] == '\0')))
return;
} /* if */
len = strlen(dev) + 6;
path = (char *) __PHYSFS_smallAlloc(len);
if (!path)
return; /* oh well. */
snprintf(path, len, "/dev/%s", dev);
checkPathForCD(path, cb, d);
__PHYSFS_smallFree(path);
} /* checkDevForCD */
#endif /* !PHYSFS_NO_CDROM_SUPPORT */
void __PHYSFS_platformDetectAvailableCDs(PHYSFS_StringCallback cb, void *data)
{
#if !PHYSFS_NO_CDROM_SUPPORT
DIR *dirp = opendir("/dev");
if (dirp)
{
struct dirent *dent;
while ((dent = readdir(dirp)) != NULL)
checkDevForCD(dent->d_name, cb, data);
closedir(dirp);
} /* if */
#endif
} /* __PHYSFS_platformDetectAvailableCDs */
#endif /* PHYSFS_PLATFORM_QNX */
/* end of physfs_platform_qnx.c ... */

367
src/physfs_platform_unix.c Executable file
View File

@ -0,0 +1,367 @@
/*
* Unix support routines for PhysicsFS.
*
* Please see the file LICENSE.txt in the source's root directory.
*
* This file written by Ryan C. Gordon.
*/
#define __PHYSICSFS_INTERNAL__
#include "physfs_platforms.h"
#ifdef PHYSFS_PLATFORM_UNIX
#include <ctype.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <pwd.h>
#include <sys/stat.h>
#include <sys/param.h>
#include <dirent.h>
#include <time.h>
#include <errno.h>
#include <limits.h>
#if PHYSFS_NO_CDROM_SUPPORT
#elif PHYSFS_PLATFORM_LINUX
# define PHYSFS_HAVE_MNTENT_H 1
#elif defined __CYGWIN__
# define PHYSFS_HAVE_MNTENT_H 1
#elif PHYSFS_PLATFORM_SOLARIS
# define PHYSFS_HAVE_SYS_MNTTAB_H 1
#elif PHYSFS_PLATFORM_BSD
# define PHYSFS_HAVE_SYS_UCRED_H 1
#else
# warning No CD-ROM support included. Either define your platform here,
# warning or define PHYSFS_NO_CDROM_SUPPORT=1 to confirm this is intentional.
#endif
#ifdef PHYSFS_HAVE_SYS_UCRED_H
# ifdef PHYSFS_HAVE_MNTENT_H
# undef PHYSFS_HAVE_MNTENT_H /* don't do both... */
# endif
# include <sys/mount.h>
# include <sys/ucred.h>
#endif
#ifdef PHYSFS_HAVE_MNTENT_H
#include <mntent.h>
#endif
#ifdef PHYSFS_HAVE_SYS_MNTTAB_H
#include <sys/mnttab.h>
#endif
#ifdef PHYSFS_PLATFORM_FREEBSD
#include <sys/sysctl.h>
#endif
#include "physfs_internal.h"
int __PHYSFS_platformInit(void)
{
return 1; /* always succeed. */
} /* __PHYSFS_platformInit */
void __PHYSFS_platformDeinit(void)
{
/* no-op */
} /* __PHYSFS_platformDeinit */
void __PHYSFS_platformDetectAvailableCDs(PHYSFS_StringCallback cb, void *data)
{
#if (defined PHYSFS_NO_CDROM_SUPPORT)
/* no-op. */
#elif (defined PHYSFS_HAVE_SYS_UCRED_H)
int i;
struct statfs *mntbufp = NULL;
int mounts = getmntinfo(&mntbufp, MNT_NOWAIT);
for (i = 0; i < mounts; i++)
{
int add_it = 0;
if (strcmp(mntbufp[i].f_fstypename, "iso9660") == 0)
add_it = 1;
else if (strcmp( mntbufp[i].f_fstypename, "cd9660") == 0)
add_it = 1;
/* add other mount types here */
if (add_it)
cb(data, mntbufp[i].f_mntonname);
} /* for */
#elif (defined PHYSFS_HAVE_MNTENT_H)
FILE *mounts = NULL;
struct mntent *ent = NULL;
mounts = setmntent("/etc/mtab", "r");
BAIL_IF(mounts == NULL, PHYSFS_ERR_IO, /*return void*/);
while ( (ent = getmntent(mounts)) != NULL )
{
int add_it = 0;
if (strcmp(ent->mnt_type, "iso9660") == 0)
add_it = 1;
else if (strcmp(ent->mnt_type, "udf") == 0)
add_it = 1;
/* !!! FIXME: these might pick up floppy drives, right? */
else if (strcmp(ent->mnt_type, "auto") == 0)
add_it = 1;
else if (strcmp(ent->mnt_type, "supermount") == 0)
add_it = 1;
/* add other mount types here */
if (add_it)
cb(data, ent->mnt_dir);
} /* while */
endmntent(mounts);
#elif (defined PHYSFS_HAVE_SYS_MNTTAB_H)
FILE *mounts = fopen(MNTTAB, "r");
struct mnttab ent;
BAIL_IF(mounts == NULL, PHYSFS_ERR_IO, /*return void*/);
while (getmntent(mounts, &ent) == 0)
{
int add_it = 0;
if (strcmp(ent.mnt_fstype, "hsfs") == 0)
add_it = 1;
/* add other mount types here */
if (add_it)
cb(data, ent.mnt_mountp);
} /* while */
fclose(mounts);
#endif
} /* __PHYSFS_platformDetectAvailableCDs */
/*
* See where program (bin) resides in the $PATH specified by (envr).
* returns a copy of the first element in envr that contains it, or NULL
* if it doesn't exist or there were other problems. PHYSFS_SetError() is
* called if we have a problem.
*
* (envr) will be scribbled over, and you are expected to allocator.Free() the
* return value when you're done with it.
*/
static char *findBinaryInPath(const char *bin, char *envr)
{
size_t alloc_size = 0;
char *exe = NULL;
char *start = envr;
char *ptr;
assert(bin != NULL);
assert(envr != NULL);
do
{
size_t size;
size_t binlen;
ptr = strchr(start, ':'); /* find next $PATH separator. */
if (ptr)
*ptr = '\0';
binlen = strlen(bin);
size = strlen(start) + binlen + 2;
if (size >= alloc_size)
{
char *x = (char *) allocator.Realloc(exe, size);
if (!x)
{
if (exe != NULL)
allocator.Free(exe);
BAIL(PHYSFS_ERR_OUT_OF_MEMORY, NULL);
} /* if */
alloc_size = size;
exe = x;
} /* if */
/* build full binary path... */
strcpy(exe, start);
if ((exe[0] == '\0') || (exe[strlen(exe) - 1] != '/'))
strcat(exe, "/");
strcat(exe, bin);
if (access(exe, X_OK) == 0) /* Exists as executable? We're done. */
{
exe[(size - binlen) - 1] = '\0'; /* chop off filename, leave '/' */
return exe;
} /* if */
start = ptr + 1; /* start points to beginning of next element. */
} while (ptr != NULL);
if (exe != NULL)
allocator.Free(exe);
return NULL; /* doesn't exist in path. */
} /* findBinaryInPath */
static char *readSymLink(const char *path)
{
ssize_t len = 64;
ssize_t rc = -1;
char *retval = NULL;
while (1)
{
char *ptr = (char *) allocator.Realloc(retval, (size_t) len);
if (ptr == NULL)
break; /* out of memory. */
retval = ptr;
rc = readlink(path, retval, len);
if (rc == -1)
break; /* not a symlink, i/o error, etc. */
else if (rc < len)
{
retval[rc] = '\0'; /* readlink doesn't null-terminate. */
return retval; /* we're good to go. */
} /* else if */
len *= 2; /* grow buffer, try again. */
} /* while */
if (retval != NULL)
allocator.Free(retval);
return NULL;
} /* readSymLink */
char *__PHYSFS_platformCalcBaseDir(const char *argv0)
{
char *retval = NULL;
const char *envr = NULL;
/* Try to avoid using argv0 unless forced to. Try system-specific stuff. */
#if defined(PHYSFS_PLATFORM_FREEBSD)
{
char fullpath[PATH_MAX];
size_t buflen = sizeof (fullpath);
int mib[4] = { CTL_KERN, KERN_PROC, KERN_PROC_PATHNAME, -1 };
if (sysctl(mib, 4, fullpath, &buflen, NULL, 0) != -1)
retval = __PHYSFS_strdup(fullpath);
}
#elif defined(PHYSFS_PLATFORM_SOLARIS)
{
const char *path = getexecname();
if ((path != NULL) && (path[0] == '/')) /* must be absolute path... */
retval = __PHYSFS_strdup(path);
}
#endif
/* If there's a Linux-like /proc filesystem, you can get the full path to
* the current process from a symlink in there.
*/
if (!retval && (access("/proc", F_OK) == 0))
{
retval = readSymLink("/proc/self/exe");
if (!retval) retval = readSymLink("/proc/curproc/file");
if (!retval) retval = readSymLink("/proc/curproc/exe");
if (retval == NULL)
{
/* older kernels don't have /proc/self ... try PID version... */
const unsigned long long pid = (unsigned long long) getpid();
char path[64];
const int rc = (int) snprintf(path,sizeof(path),"/proc/%llu/exe",pid);
if ( (rc > 0) && (rc < sizeof(path)) )
retval = readSymLink(path);
} /* if */
} /* if */
if (retval != NULL) /* chop off filename. */
{
char *ptr = strrchr(retval, '/');
if (ptr != NULL)
*(ptr+1) = '\0';
else /* shouldn't happen, but just in case... */
{
allocator.Free(retval);
retval = NULL;
} /* else */
} /* if */
/* No /proc/self/exe, etc, but we have an argv[0] we can parse? */
if ((retval == NULL) && (argv0 != NULL))
{
/* fast path: default behaviour can handle this. */
if (strchr(argv0, '/') != NULL)
return NULL; /* higher level parses out real path from argv0. */
/* If there's no dirsep on argv0, then look through $PATH for it. */
envr = getenv("PATH");
if (envr != NULL)
{
char *path = (char *) __PHYSFS_smallAlloc(strlen(envr) + 1);
BAIL_IF(!path, PHYSFS_ERR_OUT_OF_MEMORY, NULL);
strcpy(path, envr);
retval = findBinaryInPath(argv0, path);
__PHYSFS_smallFree(path);
} /* if */
} /* if */
if (retval != NULL)
{
/* try to shrink buffer... */
char *ptr = (char *) allocator.Realloc(retval, strlen(retval) + 1);
if (ptr != NULL)
retval = ptr; /* oh well if it failed. */
} /* if */
return retval;
} /* __PHYSFS_platformCalcBaseDir */
char *__PHYSFS_platformCalcPrefDir(const char *org, const char *app)
{
/*
* We use XDG's base directory spec, even if you're not on Linux.
* This isn't strictly correct, but the results are relatively sane
* in any case.
*
* https://standards.freedesktop.org/basedir-spec/basedir-spec-latest.html
*/
const char *envr = getenv("XDG_DATA_HOME");
const char *append = "/";
char *retval = NULL;
size_t len = 0;
if (!envr)
{
/* You end up with "$HOME/.local/share/Game Name 2" */
envr = __PHYSFS_getUserDir();
BAIL_IF_ERRPASS(!envr, NULL); /* oh well. */
append = ".local/share/";
} /* if */
len = strlen(envr) + strlen(append) + strlen(app) + 2;
retval = (char *) allocator.Malloc(len);
BAIL_IF(!retval, PHYSFS_ERR_OUT_OF_MEMORY, NULL);
snprintf(retval, len, "%s%s%s/", envr, append, app);
return retval;
} /* __PHYSFS_platformCalcPrefDir */
#endif /* PHYSFS_PLATFORM_UNIX */
/* end of physfs_platform_unix.c ... */

1020
src/physfs_platform_windows.c Executable file

File diff suppressed because it is too large Load Diff

41
src/physfs_platform_winrt.cpp Executable file
View File

@ -0,0 +1,41 @@
/*
* Windows Runtime (WinRT) support routines for PhysicsFS.
*
* Please see the file LICENSE.txt in the source's root directory.
*
* This file originally written by Martin "T-Bone" Ahrnbom, but was mostly
* merged into physfs_platform_windows.c by Ryan C. Gordon (so please harass
* Ryan about bugs and not Martin).
*/
/* (There used to be instructions on how to make a WinRT project, but at
this point, either CMake will do it for you or you should just drop
PhysicsFS's sources into your existing project. --ryan.) */
#define __PHYSICSFS_INTERNAL__
#include "physfs_platforms.h"
#ifdef PHYSFS_PLATFORM_WINRT
#if defined(_MSC_VER) && !defined(_CRT_SECURE_NO_WARNINGS)
#define _CRT_SECURE_NO_WARNINGS 1
#endif
#include <windows.h>
#include "physfs_internal.h"
const void *__PHYSFS_winrtCalcBaseDir(void)
{
return Windows::ApplicationModel::Package::Current->InstalledLocation->Path->Data();
} /* __PHYSFS_winrtCalcBaseDir */
const void *__PHYSFS_winrtCalcPrefDir(void)
{
return Windows::Storage::ApplicationData::Current->LocalFolder->Path->Data();
} /* __PHYSFS_winrtCalcBaseDir */
#endif /* PHYSFS_PLATFORM_WINRT */
/* end of physfs_platform_winrt.cpp ... */

80
src/physfs_platforms.h Executable file
View File

@ -0,0 +1,80 @@
#ifndef _INCL_PHYSFS_PLATFORMS
#define _INCL_PHYSFS_PLATFORMS
#ifndef __PHYSICSFS_INTERNAL__
#error Do not include this header from your applications.
#endif
/*
* These only define the platforms to determine which files in the platforms
* directory should be compiled. For example, technically BeOS can be called
* a "unix" system, but since it doesn't use unix.c, we don't define
* PHYSFS_PLATFORM_UNIX on that system.
*/
#if (defined __HAIKU__)
# define PHYSFS_PLATFORM_HAIKU 1
# define PHYSFS_PLATFORM_POSIX 1
#elif ((defined __BEOS__) || (defined __beos__))
# error BeOS support was dropped since PhysicsFS 2.1. Sorry. Try Haiku!
#elif (defined _WIN32_WCE) || (defined _WIN64_WCE)
# error PocketPC support was dropped since PhysicsFS 2.1. Sorry. Try WinRT!
#elif (defined(_MSC_VER) && (_MSC_VER >= 1700) && !_USING_V110_SDK71_) /* _MSC_VER==1700 for MSVC 2012 */
# include <winapifamily.h>
# define PHYSFS_PLATFORM_WINDOWS 1
# if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_APP) && !WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP)
# define PHYSFS_NO_CDROM_SUPPORT 1
# define PHYSFS_PLATFORM_WINRT 1
# endif
#elif (((defined _WIN32) || (defined _WIN64)) && (!defined __CYGWIN__))
# define PHYSFS_PLATFORM_WINDOWS 1
#elif defined(__OS2__) || defined(OS2)
# define PHYSFS_PLATFORM_OS2 1
#elif ((defined __MACH__) && (defined __APPLE__))
/* To check if iOS or not, we need to include this file */
# include <TargetConditionals.h>
# if ((TARGET_IPHONE_SIMULATOR) || (TARGET_OS_IPHONE))
# define PHYSFS_NO_CDROM_SUPPORT 1
# endif
# define PHYSFS_PLATFORM_APPLE 1
# define PHYSFS_PLATFORM_POSIX 1
#elif defined(macintosh)
# error Classic Mac OS support was dropped from PhysicsFS 2.0. Move to OS X.
#elif defined(ANDROID)
# define PHYSFS_PLATFORM_LINUX 1
# define PHYSFS_PLATFORM_UNIX 1
# define PHYSFS_PLATFORM_POSIX 1
# define PHYSFS_NO_CDROM_SUPPORT 1
#elif defined(__linux)
# define PHYSFS_PLATFORM_LINUX 1
# define PHYSFS_PLATFORM_UNIX 1
# define PHYSFS_PLATFORM_POSIX 1
#elif defined(__sun) || defined(sun)
# define PHYSFS_PLATFORM_SOLARIS 1
# define PHYSFS_PLATFORM_UNIX 1
# define PHYSFS_PLATFORM_POSIX 1
#elif defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__DragonFly__)
# define PHYSFS_PLATFORM_FREEBSD 1
# define PHYSFS_PLATFORM_BSD 1
# define PHYSFS_PLATFORM_UNIX 1
# define PHYSFS_PLATFORM_POSIX 1
#elif defined(__NetBSD__) || defined(__OpenBSD__) || defined(__bsdi__)
# define PHYSFS_PLATFORM_BSD 1
# define PHYSFS_PLATFORM_UNIX 1
# define PHYSFS_PLATFORM_POSIX 1
#elif defined(__EMSCRIPTEN__)
# define PHYSFS_NO_CDROM_SUPPORT 1
# define PHYSFS_PLATFORM_UNIX 1
# define PHYSFS_PLATFORM_POSIX 1
#elif defined(__QNX__)
# define PHYSFS_PLATFORM_QNX 1
# define PHYSFS_PLATFORM_POSIX 1
#elif defined(unix) || defined(__unix__)
# define PHYSFS_PLATFORM_UNIX 1
# define PHYSFS_PLATFORM_POSIX 1
#else
# error Unknown platform.
#endif
#endif /* include-once blocker. */

567
src/physfs_unicode.c Executable file
View File

@ -0,0 +1,567 @@
#define __PHYSICSFS_INTERNAL__
#include "physfs_internal.h"
#include "physfs_casefolding.h"
/*
* From rfc3629, the UTF-8 spec:
* https://www.ietf.org/rfc/rfc3629.txt
*
* Char. number range | UTF-8 octet sequence
* (hexadecimal) | (binary)
* --------------------+---------------------------------------------
* 0000 0000-0000 007F | 0xxxxxxx
* 0000 0080-0000 07FF | 110xxxxx 10xxxxxx
* 0000 0800-0000 FFFF | 1110xxxx 10xxxxxx 10xxxxxx
* 0001 0000-0010 FFFF | 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
*/
/*
* This may not be the best value, but it's one that isn't represented
* in Unicode (0x10FFFF is the largest codepoint value). We return this
* value from utf8codepoint() if there's bogus bits in the
* stream. utf8codepoint() will turn this value into something
* reasonable (like a question mark), for text that wants to try to recover,
* whereas utf8valid() will use the value to determine if a string has bad
* bits.
*/
#define UNICODE_BOGUS_CHAR_VALUE 0xFFFFFFFF
/*
* This is the codepoint we currently return when there was bogus bits in a
* UTF-8 string. May not fly in Asian locales?
*/
#define UNICODE_BOGUS_CHAR_CODEPOINT '?'
static PHYSFS_uint32 utf8codepoint(const char **_str)
{
const char *str = *_str;
PHYSFS_uint32 retval = 0;
PHYSFS_uint32 octet = (PHYSFS_uint32) ((PHYSFS_uint8) *str);
PHYSFS_uint32 octet2, octet3, octet4;
if (octet == 0) /* null terminator, end of string. */
return 0;
else if (octet < 128) /* one octet char: 0 to 127 */
{
(*_str)++; /* skip to next possible start of codepoint. */
return octet;
} /* else if */
else if ((octet > 127) && (octet < 192)) /* bad (starts with 10xxxxxx). */
{
/*
* Apparently each of these is supposed to be flagged as a bogus
* char, instead of just resyncing to the next valid codepoint.
*/
(*_str)++; /* skip to next possible start of codepoint. */
return UNICODE_BOGUS_CHAR_VALUE;
} /* else if */
else if (octet < 224) /* two octets */
{
(*_str)++; /* advance at least one byte in case of an error */
octet -= (128+64);
octet2 = (PHYSFS_uint32) ((PHYSFS_uint8) *(++str));
if ((octet2 & (128+64)) != 128) /* Format isn't 10xxxxxx? */
return UNICODE_BOGUS_CHAR_VALUE;
*_str += 1; /* skip to next possible start of codepoint. */
retval = ((octet << 6) | (octet2 - 128));
if ((retval >= 0x80) && (retval <= 0x7FF))
return retval;
} /* else if */
else if (octet < 240) /* three octets */
{
(*_str)++; /* advance at least one byte in case of an error */
octet -= (128+64+32);
octet2 = (PHYSFS_uint32) ((PHYSFS_uint8) *(++str));
if ((octet2 & (128+64)) != 128) /* Format isn't 10xxxxxx? */
return UNICODE_BOGUS_CHAR_VALUE;
octet3 = (PHYSFS_uint32) ((PHYSFS_uint8) *(++str));
if ((octet3 & (128+64)) != 128) /* Format isn't 10xxxxxx? */
return UNICODE_BOGUS_CHAR_VALUE;
*_str += 2; /* skip to next possible start of codepoint. */
retval = ( ((octet << 12)) | ((octet2-128) << 6) | ((octet3-128)) );
/* There are seven "UTF-16 surrogates" that are illegal in UTF-8. */
switch (retval)
{
case 0xD800:
case 0xDB7F:
case 0xDB80:
case 0xDBFF:
case 0xDC00:
case 0xDF80:
case 0xDFFF:
return UNICODE_BOGUS_CHAR_VALUE;
} /* switch */
/* 0xFFFE and 0xFFFF are illegal, too, so we check them at the edge. */
if ((retval >= 0x800) && (retval <= 0xFFFD))
return retval;
} /* else if */
else if (octet < 248) /* four octets */
{
(*_str)++; /* advance at least one byte in case of an error */
octet -= (128+64+32+16);
octet2 = (PHYSFS_uint32) ((PHYSFS_uint8) *(++str));
if ((octet2 & (128+64)) != 128) /* Format isn't 10xxxxxx? */
return UNICODE_BOGUS_CHAR_VALUE;
octet3 = (PHYSFS_uint32) ((PHYSFS_uint8) *(++str));
if ((octet3 & (128+64)) != 128) /* Format isn't 10xxxxxx? */
return UNICODE_BOGUS_CHAR_VALUE;
octet4 = (PHYSFS_uint32) ((PHYSFS_uint8) *(++str));
if ((octet4 & (128+64)) != 128) /* Format isn't 10xxxxxx? */
return UNICODE_BOGUS_CHAR_VALUE;
*_str += 3; /* skip to next possible start of codepoint. */
retval = ( ((octet << 18)) | ((octet2 - 128) << 12) |
((octet3 - 128) << 6) | ((octet4 - 128)) );
if ((retval >= 0x10000) && (retval <= 0x10FFFF))
return retval;
} /* else if */
/*
* Five and six octet sequences became illegal in rfc3629.
* We throw the codepoint away, but parse them to make sure we move
* ahead the right number of bytes and don't overflow the buffer.
*/
else if (octet < 252) /* five octets */
{
(*_str)++; /* advance at least one byte in case of an error */
octet = (PHYSFS_uint32) ((PHYSFS_uint8) *(++str));
if ((octet & (128+64)) != 128) /* Format isn't 10xxxxxx? */
return UNICODE_BOGUS_CHAR_VALUE;
octet = (PHYSFS_uint32) ((PHYSFS_uint8) *(++str));
if ((octet & (128+64)) != 128) /* Format isn't 10xxxxxx? */
return UNICODE_BOGUS_CHAR_VALUE;
octet = (PHYSFS_uint32) ((PHYSFS_uint8) *(++str));
if ((octet & (128+64)) != 128) /* Format isn't 10xxxxxx? */
return UNICODE_BOGUS_CHAR_VALUE;
octet = (PHYSFS_uint32) ((PHYSFS_uint8) *(++str));
if ((octet & (128+64)) != 128) /* Format isn't 10xxxxxx? */
return UNICODE_BOGUS_CHAR_VALUE;
*_str += 4; /* skip to next possible start of codepoint. */
return UNICODE_BOGUS_CHAR_VALUE;
} /* else if */
else /* six octets */
{
(*_str)++; /* advance at least one byte in case of an error */
octet = (PHYSFS_uint32) ((PHYSFS_uint8) *(++str));
if ((octet & (128+64)) != 128) /* Format isn't 10xxxxxx? */
return UNICODE_BOGUS_CHAR_VALUE;
octet = (PHYSFS_uint32) ((PHYSFS_uint8) *(++str));
if ((octet & (128+64)) != 128) /* Format isn't 10xxxxxx? */
return UNICODE_BOGUS_CHAR_VALUE;
octet = (PHYSFS_uint32) ((PHYSFS_uint8) *(++str));
if ((octet & (128+64)) != 128) /* Format isn't 10xxxxxx? */
return UNICODE_BOGUS_CHAR_VALUE;
octet = (PHYSFS_uint32) ((PHYSFS_uint8) *(++str));
if ((octet & (128+64)) != 128) /* Format isn't 10xxxxxx? */
return UNICODE_BOGUS_CHAR_VALUE;
octet = (PHYSFS_uint32) ((PHYSFS_uint8) *(++str));
if ((octet & (128+64)) != 128) /* Format isn't 10xxxxxx? */
return UNICODE_BOGUS_CHAR_VALUE;
*_str += 6; /* skip to next possible start of codepoint. */
return UNICODE_BOGUS_CHAR_VALUE;
} /* else if */
return UNICODE_BOGUS_CHAR_VALUE;
} /* utf8codepoint */
static PHYSFS_uint32 utf16codepoint(const PHYSFS_uint16 **_str)
{
const PHYSFS_uint16 *src = *_str;
PHYSFS_uint32 cp = (PHYSFS_uint32) *(src++);
if (cp == 0) /* null terminator, end of string. */
return 0;
/* Orphaned second half of surrogate pair? */
else if ((cp >= 0xDC00) && (cp <= 0xDFFF))
cp = UNICODE_BOGUS_CHAR_CODEPOINT;
else if ((cp >= 0xD800) && (cp <= 0xDBFF)) /* start surrogate pair! */
{
const PHYSFS_uint32 pair = (PHYSFS_uint32) *src;
if (pair == 0)
cp = UNICODE_BOGUS_CHAR_CODEPOINT;
else if ((pair < 0xDC00) || (pair > 0xDFFF))
cp = UNICODE_BOGUS_CHAR_CODEPOINT;
else
{
src++; /* eat the other surrogate. */
cp = (((cp - 0xD800) << 10) | (pair - 0xDC00));
} /* else */
} /* else if */
*_str = src;
return cp;
} /* utf16codepoint */
static PHYSFS_uint32 utf32codepoint(const PHYSFS_uint32 **_str)
{
const PHYSFS_uint32 *src = *_str;
PHYSFS_uint32 cp = *(src++);
if (cp == 0) /* null terminator, end of string. */
return 0;
else if (cp > 0x10FFF)
cp = UNICODE_BOGUS_CHAR_CODEPOINT;
*_str = src;
return cp;
} /* utf32codepoint */
void PHYSFS_utf8ToUcs4(const char *src, PHYSFS_uint32 *dst, PHYSFS_uint64 len)
{
len -= sizeof (PHYSFS_uint32); /* save room for null char. */
while (len >= sizeof (PHYSFS_uint32))
{
PHYSFS_uint32 cp = utf8codepoint(&src);
if (cp == 0)
break;
else if (cp == UNICODE_BOGUS_CHAR_VALUE)
cp = UNICODE_BOGUS_CHAR_CODEPOINT;
*(dst++) = cp;
len -= sizeof (PHYSFS_uint32);
} /* while */
*dst = 0;
} /* PHYSFS_utf8ToUcs4 */
void PHYSFS_utf8ToUcs2(const char *src, PHYSFS_uint16 *dst, PHYSFS_uint64 len)
{
len -= sizeof (PHYSFS_uint16); /* save room for null char. */
while (len >= sizeof (PHYSFS_uint16))
{
PHYSFS_uint32 cp = utf8codepoint(&src);
if (cp == 0)
break;
else if (cp == UNICODE_BOGUS_CHAR_VALUE)
cp = UNICODE_BOGUS_CHAR_CODEPOINT;
if (cp > 0xFFFF) /* UTF-16 surrogates (bogus chars in UCS-2) */
cp = UNICODE_BOGUS_CHAR_CODEPOINT;
*(dst++) = cp;
len -= sizeof (PHYSFS_uint16);
} /* while */
*dst = 0;
} /* PHYSFS_utf8ToUcs2 */
void PHYSFS_utf8ToUtf16(const char *src, PHYSFS_uint16 *dst, PHYSFS_uint64 len)
{
len -= sizeof (PHYSFS_uint16); /* save room for null char. */
while (len >= sizeof (PHYSFS_uint16))
{
PHYSFS_uint32 cp = utf8codepoint(&src);
if (cp == 0)
break;
else if (cp == UNICODE_BOGUS_CHAR_VALUE)
cp = UNICODE_BOGUS_CHAR_CODEPOINT;
if (cp > 0xFFFF) /* encode as surrogate pair */
{
if (len < (sizeof (PHYSFS_uint16) * 2))
break; /* not enough room for the pair, stop now. */
cp -= 0x10000; /* Make this a 20-bit value */
*(dst++) = 0xD800 + ((cp >> 10) & 0x3FF);
len -= sizeof (PHYSFS_uint16);
cp = 0xDC00 + (cp & 0x3FF);
} /* if */
*(dst++) = cp;
len -= sizeof (PHYSFS_uint16);
} /* while */
*dst = 0;
} /* PHYSFS_utf8ToUtf16 */
static void utf8fromcodepoint(PHYSFS_uint32 cp, char **_dst, PHYSFS_uint64 *_len)
{
char *dst = *_dst;
PHYSFS_uint64 len = *_len;
if (len == 0)
return;
if (cp > 0x10FFFF)
cp = UNICODE_BOGUS_CHAR_CODEPOINT;
else if ((cp == 0xFFFE) || (cp == 0xFFFF)) /* illegal values. */
cp = UNICODE_BOGUS_CHAR_CODEPOINT;
else
{
/* There are seven "UTF-16 surrogates" that are illegal in UTF-8. */
switch (cp)
{
case 0xD800:
case 0xDB7F:
case 0xDB80:
case 0xDBFF:
case 0xDC00:
case 0xDF80:
case 0xDFFF:
cp = UNICODE_BOGUS_CHAR_CODEPOINT;
} /* switch */
} /* else */
/* Do the encoding... */
if (cp < 0x80)
{
*(dst++) = (char) cp;
len--;
} /* if */
else if (cp < 0x800)
{
if (len < 2)
len = 0;
else
{
*(dst++) = (char) ((cp >> 6) | 128 | 64);
*(dst++) = (char) (cp & 0x3F) | 128;
len -= 2;
} /* else */
} /* else if */
else if (cp < 0x10000)
{
if (len < 3)
len = 0;
else
{
*(dst++) = (char) ((cp >> 12) | 128 | 64 | 32);
*(dst++) = (char) ((cp >> 6) & 0x3F) | 128;
*(dst++) = (char) (cp & 0x3F) | 128;
len -= 3;
} /* else */
} /* else if */
else
{
if (len < 4)
len = 0;
else
{
*(dst++) = (char) ((cp >> 18) | 128 | 64 | 32 | 16);
*(dst++) = (char) ((cp >> 12) & 0x3F) | 128;
*(dst++) = (char) ((cp >> 6) & 0x3F) | 128;
*(dst++) = (char) (cp & 0x3F) | 128;
len -= 4;
} /* else if */
} /* else */
*_dst = dst;
*_len = len;
} /* utf8fromcodepoint */
#define UTF8FROMTYPE(typ, src, dst, len) \
if (len == 0) return; \
len--; \
while (len) \
{ \
const PHYSFS_uint32 cp = (PHYSFS_uint32) ((typ) (*(src++))); \
if (cp == 0) break; \
utf8fromcodepoint(cp, &dst, &len); \
} \
*dst = '\0'; \
void PHYSFS_utf8FromUcs4(const PHYSFS_uint32 *src, char *dst, PHYSFS_uint64 len)
{
UTF8FROMTYPE(PHYSFS_uint32, src, dst, len);
} /* PHYSFS_utf8FromUcs4 */
void PHYSFS_utf8FromUcs2(const PHYSFS_uint16 *src, char *dst, PHYSFS_uint64 len)
{
UTF8FROMTYPE(PHYSFS_uint64, src, dst, len);
} /* PHYSFS_utf8FromUcs2 */
/* latin1 maps to unicode codepoints directly, we just utf-8 encode it. */
void PHYSFS_utf8FromLatin1(const char *src, char *dst, PHYSFS_uint64 len)
{
UTF8FROMTYPE(PHYSFS_uint8, src, dst, len);
} /* PHYSFS_utf8FromLatin1 */
#undef UTF8FROMTYPE
void PHYSFS_utf8FromUtf16(const PHYSFS_uint16 *src, char *dst, PHYSFS_uint64 len)
{
if (len == 0)
return;
len--;
while (len)
{
const PHYSFS_uint32 cp = utf16codepoint(&src);
if (!cp)
break;
utf8fromcodepoint(cp, &dst, &len);
} /* while */
*dst = '\0';
} /* PHYSFS_utf8FromUtf16 */
int PHYSFS_caseFold(const PHYSFS_uint32 from, PHYSFS_uint32 *to)
{
int i;
if (from < 128) /* low-ASCII, easy! */
{
if ((from >= 'A') && (from <= 'Z'))
*to = from - ('A' - 'a');
else
*to = from;
return 1;
} /* if */
else if (from <= 0xFFFF)
{
const PHYSFS_uint8 hash = ((from ^ (from >> 8)) & 0xFF);
const PHYSFS_uint16 from16 = (PHYSFS_uint16) from;
{
const CaseFoldHashBucket1_16 *bucket = &case_fold_hash1_16[hash];
const int count = (int) bucket->count;
for (i = 0; i < count; i++)
{
const CaseFoldMapping1_16 *mapping = &bucket->list[i];
if (mapping->from == from16)
{
*to = mapping->to0;
return 1;
} /* if */
} /* for */
}
{
const CaseFoldHashBucket2_16 *bucket = &case_fold_hash2_16[hash & 15];
const int count = (int) bucket->count;
for (i = 0; i < count; i++)
{
const CaseFoldMapping2_16 *mapping = &bucket->list[i];
if (mapping->from == from16)
{
to[0] = mapping->to0;
to[1] = mapping->to1;
return 2;
} /* if */
} /* for */
}
{
const CaseFoldHashBucket3_16 *bucket = &case_fold_hash3_16[hash & 3];
const int count = (int) bucket->count;
for (i = 0; i < count; i++)
{
const CaseFoldMapping3_16 *mapping = &bucket->list[i];
if (mapping->from == from16)
{
to[0] = mapping->to0;
to[1] = mapping->to1;
to[2] = mapping->to2;
return 3;
} /* if */
} /* for */
}
} /* else if */
else /* codepoint that doesn't fit in 16 bits. */
{
const PHYSFS_uint8 hash = ((from ^ (from >> 8)) & 0xFF);
const CaseFoldHashBucket1_32 *bucket = &case_fold_hash1_32[hash & 15];
const int count = (int) bucket->count;
for (i = 0; i < count; i++)
{
const CaseFoldMapping1_32 *mapping = &bucket->list[i];
if (mapping->from == from)
{
*to = mapping->to0;
return 1;
} /* if */
} /* for */
} /* else */
/* Not found...there's no remapping for this codepoint. */
*to = from;
return 1;
} /* PHYSFS_caseFold */
#define UTFSTRICMP(bits) \
PHYSFS_uint32 folded1[3], folded2[3]; \
int head1 = 0, tail1 = 0, head2 = 0, tail2 = 0; \
while (1) { \
PHYSFS_uint32 cp1, cp2; \
if (head1 != tail1) { \
cp1 = folded1[tail1++]; \
} else { \
head1 = PHYSFS_caseFold(utf##bits##codepoint(&str1), folded1); \
cp1 = folded1[0]; \
tail1 = 1; \
} \
if (head2 != tail2) { \
cp2 = folded2[tail2++]; \
} else { \
head2 = PHYSFS_caseFold(utf##bits##codepoint(&str2), folded2); \
cp2 = folded2[0]; \
tail2 = 1; \
} \
if (cp1 < cp2) { \
return -1; \
} else if (cp1 > cp2) { \
return 1; \
} else if (cp1 == 0) { \
break; /* complete match. */ \
} \
} \
return 0
int PHYSFS_utf8stricmp(const char *str1, const char *str2)
{
UTFSTRICMP(8);
} /* PHYSFS_utf8stricmp */
int PHYSFS_utf16stricmp(const PHYSFS_uint16 *str1, const PHYSFS_uint16 *str2)
{
UTFSTRICMP(16);
} /* PHYSFS_utf16stricmp */
int PHYSFS_ucs4stricmp(const PHYSFS_uint32 *str1, const PHYSFS_uint32 *str2)
{
UTFSTRICMP(32);
} /* PHYSFS_ucs4stricmp */
#undef UTFSTRICMP
/* end of physfs_unicode.c ... */

1603
test/test_physfs.c Executable file

File diff suppressed because it is too large Load Diff