diff --git a/project/jni/physfs/.hg_archival.txt b/project/jni/physfs/.hg_archival.txt new file mode 100644 index 000000000..aaa959b25 --- /dev/null +++ b/project/jni/physfs/.hg_archival.txt @@ -0,0 +1,5 @@ +repo: 7672c9962ce627edaaa67ff54fe4ad8f9a46dc2b +node: 92a35b8603186c536e4cc126458dfc2d2809b19c +branch: stable-2.0 +latesttag: release-2.0.2 +latesttagdistance: 1 diff --git a/project/jni/physfs/.hgignore b/project/jni/physfs/.hgignore new file mode 100644 index 000000000..d015a85dd --- /dev/null +++ b/project/jni/physfs/.hgignore @@ -0,0 +1,2 @@ +syntax:glob +cmake-build diff --git a/project/jni/physfs/.hgtags b/project/jni/physfs/.hgtags new file mode 100644 index 000000000..ad3173b02 --- /dev/null +++ b/project/jni/physfs/.hgtags @@ -0,0 +1,16 @@ +0bb92a5f0fffd2452cc737346e8b796c213a5688 release-0.1.1 +2f2afcbd8abd784f738ac45b0368044763d63748 release-0.1.0 +3c7cf50a58fbf220154acd4bdfdf00a21f259eb7 release-0.1.8 +473b50402f55b2340fc286775d1b78d18a810362 release-0.1.3 +60b5f566a2585d78b2ffadd8d9c16299d0340820 release-1.0.0 +67aff4091bf129f7167ed87f937b15f31093e19e release-0.1.9 +6ad1722bbcaec1265cb74c9b7be13fe02a547d37 release-0.1.7 +8f3ccaaea1cd5dc19235882494d6102e5e9176fb release-0.1.2 +c966316c89981bea6ccaa2c2909bb303bfeeb82b release-0.1.6 +d2f04ab4b4127757234af6b30bfc98ad4ee9cb15 release-0.1.4 +d94f1ccac8095509c57ad640d54796aea0d260f0 release-0.1.5 +fe0c1d6f40afa6fca09a277a1ade59231f16c66f release-1.1.1 +5d70fca3be361258edfb59c3edaba5abe75a1e88 release-2.0.0 +df04959950eb3830c39adfa983789f70f86062d7 release-1.1.0 +94771621792f838aa4cacf9a1e1f4f86c1cb0711 release-2.0.1 +236afd18dd8cae34adb9897024bdcecc1dc8ca5d release-2.0.2 diff --git a/project/jni/physfs/Android.mk b/project/jni/physfs/Android.mk new file mode 100644 index 000000000..dba1f47f4 --- /dev/null +++ b/project/jni/physfs/Android.mk @@ -0,0 +1,24 @@ +LOCAL_PATH := $(call my-dir) + +include $(CLEAR_VARS) + +LOCAL_MODULE := physfs + +APP_SUBDIRS := . archivers platform include + +LOCAL_CFLAGS := -O2 -fsigned-char -Dphysfs_EXPORTS -D_REENTRANT -D_THREAD_SAFE -DHAVE_ASSERT_H=1 -DPHYSFS_SUPPORTS_ZIP=1 -DPHYSFS_SUPPORTS_GRP=1 -DPHYSFS_SUPPORTS_HOG=1 -DPHYSFS_SUPPORTS_MVL=1 -DPHYSFS_SUPPORTS_QPAK=1 -D_FILE_OFFSET_BITS=32 -D_LARGE_FILES -DPHYSFS_NO_CDROM_SUPPORT=1 + +LOCAL_CPP_EXTENSION := .cpp + +LOCAL_C_INCLUDES := $(foreach F, $(APP_SUBDIRS), $(LOCAL_PATH)/$(F)) + +LOCAL_SRC_FILES := $(foreach F, $(APP_SUBDIRS), $(addprefix $(F)/,$(notdir $(wildcard $(LOCAL_PATH)/$(F)/*.c)))) + +LOCAL_STATIC_LIBRARIES := + +LOCAL_SHARED_LIBRARIES := lzma + +LOCAL_LDLIBS := -lz + +include $(BUILD_SHARED_LIBRARY) + diff --git a/project/jni/physfs/CHANGELOG.txt b/project/jni/physfs/CHANGELOG.txt new file mode 100644 index 000000000..fc18f779c --- /dev/null +++ b/project/jni/physfs/CHANGELOG.txt @@ -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: + + http://hg.icculus.org/icculus/physfs/ + + diff --git a/project/jni/physfs/CMakeLists.txt b/project/jni/physfs/CMakeLists.txt new file mode 100644 index 000000000..e5807bae7 --- /dev/null +++ b/project/jni/physfs/CMakeLists.txt @@ -0,0 +1,388 @@ +# PhysicsFS; a portable, flexible file i/o abstraction. +# Copyright (C) 2007 Ryan C. Gordon. +# +# Please see the file LICENSE.txt in the source's root directory. + +CMAKE_MINIMUM_REQUIRED(VERSION 2.4) + +PROJECT(PhysicsFS) +SET(PHYSFS_VERSION 2.0.2) + +# 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(WIN32 AND NOT WINDOWS) + +# Bleh, let's do it for "APPLE" too. +IF(APPLE AND NOT MACOSX) + SET(MACOSX TRUE) +ENDIF(APPLE AND NOT MACOSX) + +INCLUDE(CheckIncludeFile) +INCLUDE(CheckLibraryExists) +INCLUDE(CheckCSourceCompiles) + +INCLUDE_DIRECTORIES(.) +#INCLUDE_DIRECTORIES(platform) +#INCLUDE_DIRECTORIES(archivers) + +IF(MACOSX) + # Fallback to older OS X on PowerPC to support wider range of systems... + IF(CMAKE_OSX_ARCHITECTURES MATCHES ppc) + ADD_DEFINITIONS(-DMAC_OS_X_VERSION_MIN_REQUIRED=1020) + SET(OTHER_LDFLAGS ${OTHER_LDFLAGS} " -mmacosx-version-min=10.2") + ENDIF(CMAKE_OSX_ARCHITECTURES MATCHES ppc) + + # Need these everywhere... + ADD_DEFINITIONS(-fno-common) + SET(OTHER_LDFLAGS ${OTHER_LDFLAGS} " -framework Carbon -framework IOKit") +ENDIF(MACOSX) + +# Add some gcc-specific command lines. +IF(CMAKE_COMPILER_IS_GNUCC) + # Always build with debug symbols...you can strip it later. + ADD_DEFINITIONS(-g -pipe -Werror -fsigned-char) + + # Stupid BeOS generates warnings in the system headers. + IF(NOT BEOS) + ADD_DEFINITIONS(-Wall) + ENDIF(NOT BEOS) + + CHECK_C_SOURCE_COMPILES(" + #if ((defined(__GNUC__)) && (__GNUC__ >= 4)) + int main(int argc, char **argv) { int is_gcc4 = 1; return 0; } + #else + #error This is not gcc4. + #endif + " PHYSFS_IS_GCC4) + + IF(PHYSFS_IS_GCC4) + # Not supported on several operating systems at this time. + IF(NOT OS2 AND NOT SOLARIS AND NOT WINDOWS) + ADD_DEFINITIONS(-fvisibility=hidden) + ENDIF(NOT OS2 AND NOT SOLARIS AND NOT WINDOWS) + ENDIF(PHYSFS_IS_GCC4) +ENDIF(CMAKE_COMPILER_IS_GNUCC) + +IF(MSVC) + # VS.NET 8.0 got really really anal about strcpy, etc, which even if we + # cleaned up our code, zlib, etc still use...so disable the warning. + ADD_DEFINITIONS(-D_CRT_SECURE_NO_WARNINGS=1) +ENDIF(MSVC) + +# Basic chunks of source code ... + +SET(ZLIB_SRCS + zlib123/adler32.c + zlib123/compress.c + zlib123/crc32.c + zlib123/deflate.c + zlib123/gzio.c + zlib123/infback.c + zlib123/inffast.c + zlib123/inflate.c + zlib123/inftrees.c + zlib123/trees.c + zlib123/uncompr.c + zlib123/zutil.c +) + +SET(LZMA_SRCS + lzma/C/7zCrc.c + lzma/C/Archive/7z/7zBuffer.c + lzma/C/Archive/7z/7zDecode.c + lzma/C/Archive/7z/7zExtract.c + lzma/C/Archive/7z/7zHeader.c + lzma/C/Archive/7z/7zIn.c + lzma/C/Archive/7z/7zItem.c + lzma/C/Archive/7z/7zMethodID.c + lzma/C/Compress/Branch/BranchX86.c + lzma/C/Compress/Branch/BranchX86_2.c + lzma/C/Compress/Lzma/LzmaDecode.c +) + +IF(BEOS) + # We add this explicitly, since we don't want CMake to think this + # is a C++ project unless we're on BeOS. + SET(PHYSFS_BEOS_SRCS platform/beos.cpp) + FIND_LIBRARY(BE_LIBRARY be) + FIND_LIBRARY(ROOT_LIBRARY root) + SET(OPTIONAL_LIBRARY_LIBS ${OPTIONAL_LIBRARY_LIBS} ${BE_LIBRARY} ${ROOT_LIBRARY}) +ENDIF(BEOS) + +# 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 + physfs.c + physfs_byteorder.c + physfs_unicode.c + platform/os2.c + platform/pocketpc.c + platform/posix.c + platform/unix.c + platform/macosx.c + platform/windows.c + archivers/dir.c + archivers/grp.c + archivers/hog.c + archivers/lzma.c + archivers/mvl.c + archivers/qpak.c + archivers/wad.c + archivers/zip.c + ${PHYSFS_BEOS_SRCS} +) + + +# platform layers ... + +IF(UNIX) + IF(BEOS) + SET(PHYSFS_HAVE_CDROM_SUPPORT TRUE) + SET(PHYSFS_HAVE_THREAD_SUPPORT TRUE) + SET(HAVE_PTHREAD_H TRUE) + ELSE(BEOS) + # !!! FIXME + # AC_DEFINE([PHYSFS_HAVE_LLSEEK], 1, [define if we have llseek]) + CHECK_INCLUDE_FILE(sys/ucred.h HAVE_UCRED_H) + IF(HAVE_UCRED_H) + ADD_DEFINITIONS(-DPHYSFS_HAVE_SYS_UCRED_H=1) + SET(PHYSFS_HAVE_CDROM_SUPPORT TRUE) + ENDIF(HAVE_UCRED_H) + + CHECK_INCLUDE_FILE(mntent.h HAVE_MNTENT_H) + IF(HAVE_MNTENT_H) + ADD_DEFINITIONS(-DPHYSFS_HAVE_MNTENT_H=1) + SET(PHYSFS_HAVE_CDROM_SUPPORT TRUE) + ENDIF(HAVE_MNTENT_H) + + CHECK_INCLUDE_FILE(pthread.h HAVE_PTHREAD_H) + IF(HAVE_PTHREAD_H) + SET(PHYSFS_HAVE_THREAD_SUPPORT TRUE) + ENDIF(HAVE_PTHREAD_H) + ENDIF(BEOS) +ENDIF(UNIX) + +IF(WINDOWS) + SET(PHYSFS_HAVE_CDROM_SUPPORT TRUE) + SET(PHYSFS_HAVE_THREAD_SUPPORT TRUE) +ENDIF(WINDOWS) + +IF(NOT PHYSFS_HAVE_CDROM_SUPPORT) + ADD_DEFINITIONS(-DPHYSFS_NO_CDROM_SUPPORT=1) + MESSAGE(WARNING " ***") + MESSAGE(WARNING " *** There is no CD-ROM support in this build!") + MESSAGE(WARNING " *** PhysicsFS will just pretend there are no discs.") + MESSAGE(WARNING " *** This may be fine, depending on how PhysicsFS is used,") + MESSAGE(WARNING " *** but is this what you REALLY wanted?") + MESSAGE(WARNING " *** (Maybe fix CMakeLists.txt, or write a platform driver?)") + MESSAGE(WARNING " ***") +ENDIF(NOT PHYSFS_HAVE_CDROM_SUPPORT) + +IF(PHYSFS_HAVE_THREAD_SUPPORT) + ADD_DEFINITIONS(-D_REENTRANT -D_THREAD_SAFE) +ELSE(PHYSFS_HAVE_THREAD_SUPPORT) + ADD_DEFINITIONS(-DPHYSFS_NO_THREAD_SUPPORT=1) + MESSAGE(WARNING " ***") + MESSAGE(WARNING " *** There is no thread support in this build!") + MESSAGE(WARNING " *** PhysicsFS will NOT be reentrant!") + MESSAGE(WARNING " *** This may be fine, depending on how PhysicsFS is used,") + MESSAGE(WARNING " *** but is this what you REALLY wanted?") + MESSAGE(WARNING " *** (Maybe fix CMakeLists.txt, or write a platform driver?)") + MESSAGE(WARNING " ***") +ENDIF(PHYSFS_HAVE_THREAD_SUPPORT) + +CHECK_INCLUDE_FILE(assert.h HAVE_ASSERT_H) +IF(HAVE_ASSERT_H) + ADD_DEFINITIONS(-DHAVE_ASSERT_H=1) +ENDIF(HAVE_ASSERT_H) + + + +# Archivers ... + +OPTION(PHYSFS_ARCHIVE_ZIP "Enable ZIP support" TRUE) +IF(PHYSFS_ARCHIVE_ZIP) + ADD_DEFINITIONS(-DPHYSFS_SUPPORTS_ZIP=1) + SET(PHYSFS_NEED_ZLIB TRUE) +ENDIF(PHYSFS_ARCHIVE_ZIP) + +OPTION(PHYSFS_ARCHIVE_7Z "Enable 7zip support" TRUE) +IF(PHYSFS_ARCHIVE_7Z) + ADD_DEFINITIONS(-DPHYSFS_SUPPORTS_7Z=1) + # !!! FIXME: rename to 7z.c? + SET(PHYSFS_SRCS ${PHYSFS_SRCS} ${LZMA_SRCS}) +ENDIF(PHYSFS_ARCHIVE_7Z) + +OPTION(PHYSFS_ARCHIVE_GRP "Enable Build Engine GRP support" TRUE) +IF(PHYSFS_ARCHIVE_GRP) + ADD_DEFINITIONS(-DPHYSFS_SUPPORTS_GRP=1) +ENDIF(PHYSFS_ARCHIVE_GRP) + +OPTION(PHYSFS_ARCHIVE_WAD "Enable Doom WAD support" TRUE) +IF(PHYSFS_ARCHIVE_WAD) + ADD_DEFINITIONS(-DPHYSFS_SUPPORTS_WAD=1) +ENDIF(PHYSFS_ARCHIVE_WAD) + +OPTION(PHYSFS_ARCHIVE_HOG "Enable Descent I/II HOG support" TRUE) +IF(PHYSFS_ARCHIVE_HOG) + ADD_DEFINITIONS(-DPHYSFS_SUPPORTS_HOG=1) +ENDIF(PHYSFS_ARCHIVE_HOG) + +OPTION(PHYSFS_ARCHIVE_MVL "Enable Descent I/II MVL support" TRUE) +IF(PHYSFS_ARCHIVE_MVL) + ADD_DEFINITIONS(-DPHYSFS_SUPPORTS_MVL=1) +ENDIF(PHYSFS_ARCHIVE_MVL) + +OPTION(PHYSFS_ARCHIVE_QPAK "Enable Quake I/II QPAK support" TRUE) +IF(PHYSFS_ARCHIVE_QPAK) + ADD_DEFINITIONS(-DPHYSFS_SUPPORTS_QPAK=1) +ENDIF(PHYSFS_ARCHIVE_QPAK) + + +# See if some archiver required zlib, and see about using system version. + +IF(PHYSFS_NEED_ZLIB) + FIND_PACKAGE(ZLIB) + + IF(ZLIB_FOUND) + OPTION(PHYSFS_INTERNAL_ZLIB "Link own zlib instead of system library" FALSE) + ELSE(HAVE_SYSTEM_ZLIB) + SET(PHYSFS_INTERNAL_ZLIB TRUE) + ENDIF(ZLIB_FOUND) + + IF(PHYSFS_INTERNAL_ZLIB) + INCLUDE_DIRECTORIES(zlib123) + ADD_DEFINITIONS(-DZ_PREFIX=1) + SET(PHYSFS_SRCS ${PHYSFS_SRCS} ${ZLIB_SRCS}) + ELSE(PHYSFS_INTERNAL_ZLIB) + SET(OPTIONAL_LIBRARY_LIBS ${OPTIONAL_LIBRARY_LIBS} ${ZLIB_LIBRARY}) + INCLUDE_DIRECTORIES(${ZLIB_INCLUDE_DIR}) + ENDIF(PHYSFS_INTERNAL_ZLIB) +ENDIF(PHYSFS_NEED_ZLIB) + +OPTION(PHYSFS_BUILD_STATIC "Build static library" TRUE) +IF(PHYSFS_BUILD_STATIC) + ADD_LIBRARY(physfs-static STATIC ${PHYSFS_SRCS}) + SET_TARGET_PROPERTIES(physfs-static PROPERTIES OUTPUT_NAME "physfs") + SET(PHYSFS_LIB_TARGET physfs-static) + SET(PHYSFS_INSTALL_TARGETS ${PHYSFS_INSTALL_TARGETS} ";physfs-static") +ENDIF(PHYSFS_BUILD_STATIC) + +OPTION(PHYSFS_BUILD_SHARED "Build shared library" TRUE) +IF(PHYSFS_BUILD_SHARED) + ADD_LIBRARY(physfs SHARED ${PHYSFS_SRCS}) + SET_TARGET_PROPERTIES(physfs PROPERTIES VERSION ${PHYSFS_VERSION}) + SET_TARGET_PROPERTIES(physfs PROPERTIES SOVERSION ${PHYSFS_SOVERSION}) + TARGET_LINK_LIBRARIES(physfs ${OPTIONAL_LIBRARY_LIBS} ${OTHER_LDFLAGS}) + SET(PHYSFS_LIB_TARGET physfs) + SET(PHYSFS_INSTALL_TARGETS ${PHYSFS_INSTALL_TARGETS} ";physfs") +ENDIF(PHYSFS_BUILD_SHARED) + +IF(NOT PHYSFS_BUILD_SHARED AND NOT PHYSFS_BUILD_STATIC) + MESSAGE(FATAL "Both shared and static libraries are disabled!") +ENDIF(NOT PHYSFS_BUILD_SHARED AND NOT PHYSFS_BUILD_STATIC) + +# CMake FAQ says I need this... +IF(PHYSFS_BUILD_SHARED AND PHYSFS_BUILD_STATIC) + SET_TARGET_PROPERTIES(physfs PROPERTIES CLEAN_DIRECT_OUTPUT 1) + SET_TARGET_PROPERTIES(physfs-static PROPERTIES CLEAN_DIRECT_OUTPUT 1) +ENDIF(PHYSFS_BUILD_SHARED AND PHYSFS_BUILD_STATIC) + +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) + FIND_LIBRARY(HISTORY_LIBRARY history) + IF(READLINE_LIBRARY AND HISTORY_LIBRARY) + SET(HAVE_SYSTEM_READLINE TRUE) + SET(TEST_PHYSFS_LIBS ${TEST_PHYSFS_LIBS} ${READLINE_LIBRARY} ${CURSES_LIBRARY}) + INCLUDE_DIRECTORIES(${READLINE_H} ${HISTORY_H}) + ADD_DEFINITIONS(-DPHYSFS_HAVE_READLINE=1) + ENDIF(READLINE_LIBRARY AND HISTORY_LIBRARY) + ENDIF(READLINE_H AND HISTORY_H) + 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(PHYSFS_BUILD_TEST) + +OPTION(PHYSFS_BUILD_WX_TEST "Build wxWidgets test program." TRUE) +MARK_AS_ADVANCED(PHYSFS_BUILD_WX_TEST) +IF(PHYSFS_BUILD_WX_TEST) + SET(wxWidgets_USE_LIBS base core adv) + SET(wxWidgets_INCLUDE_DIRS_NO_SYSTEM 1) + FIND_PACKAGE(wxWidgets) + IF(wxWidgets_FOUND) + INCLUDE(${wxWidgets_USE_FILE}) + ADD_EXECUTABLE(wxtest_physfs test/wxtest_physfs.cpp) + SET_SOURCE_FILES_PROPERTIES(test/wxtest_physfs.cpp COMPILE_FLAGS ${wxWidgets_CXX_FLAGS}) + TARGET_LINK_LIBRARIES(wxtest_physfs ${PHYSFS_LIB_TARGET} ${wxWidgets_LIBRARIES} ${OTHER_LDFLAGS}) + SET(PHYSFS_INSTALL_TARGETS ${PHYSFS_INSTALL_TARGETS} ";wxtest_physfs") + ELSE(wxWidgets_FOUND) + MESSAGE(STATUS "wxWidgets not found. Disabling wx test app.") + SET(PHYSFS_BUILD_WX_TEST FALSE) + ENDIF(wxWidgets_FOUND) +ENDIF(PHYSFS_BUILD_WX_TEST) + +INSTALL(TARGETS ${PHYSFS_INSTALL_TARGETS} + RUNTIME DESTINATION bin + LIBRARY DESTINATION lib${LIB_SUFFIX} + ARCHIVE DESTINATION lib${LIB_SUFFIX}) +INSTALL(FILES physfs.h DESTINATION include) + +FIND_PACKAGE(Doxygen) +IF(DOXYGEN_FOUND) + ADD_CUSTOM_TARGET(docs ${DOXYGEN_EXECUTABLE} COMMENT "Building documentation") +ELSE(DOXYGEN_FOUND) + MESSAGE(STATUS "Doxygen not found. You won't be able to build documentation.") +ENDIF(DOXYGEN_FOUND) + +IF(UNIX) + SET(PHYSFS_TARBALL "${CMAKE_CURRENT_SOURCE_DIR}/../physfs-${PHYSFS_VERSION}.tar.gz") + ADD_CUSTOM_TARGET( + dist + hg archive -t tgz "${PHYSFS_TARBALL}" + WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}" + COMMENT "Building source tarball '${PHYSFS_TARBALL}'..." + ) +ENDIF(UNIX) + +MACRO(MESSAGE_BOOL_OPTION _NAME _VALUE) + IF(${_VALUE}) + MESSAGE(STATUS " ${_NAME}: enabled") + ELSE(${_VALUE}) + MESSAGE(STATUS " ${_NAME}: disabled") + ENDIF(${_VALUE}) +ENDMACRO(MESSAGE_BOOL_OPTION) + +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("CD-ROM drive support" PHYSFS_HAVE_CDROM_SUPPORT) +MESSAGE_BOOL_OPTION("Thread safety" PHYSFS_HAVE_THREAD_SUPPORT) +MESSAGE_BOOL_OPTION("Build own zlib" PHYSFS_INTERNAL_ZLIB) +MESSAGE_BOOL_OPTION("Build static library" PHYSFS_BUILD_STATIC) +MESSAGE_BOOL_OPTION("Build shared library" PHYSFS_BUILD_SHARED) +MESSAGE_BOOL_OPTION("Build wxWidgets test program" PHYSFS_BUILD_WX_TEST) +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(PHYSFS_BUILD_TEST) + +# end of CMakeLists.txt ... + diff --git a/project/jni/physfs/CREDITS.txt b/project/jni/physfs/CREDITS.txt new file mode 100644 index 000000000..dab8876de --- /dev/null +++ b/project/jni/physfs/CREDITS.txt @@ -0,0 +1,113 @@ +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, +Ruby bindings, +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 + +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 + +Other stuff: + Your name here! Patches go to icculus@icculus.org ... + +/* end of CREDITS.txt ... */ + diff --git a/project/jni/physfs/Doxyfile b/project/jni/physfs/Doxyfile new file mode 100644 index 000000000..0b913fced --- /dev/null +++ b/project/jni/physfs/Doxyfile @@ -0,0 +1,1079 @@ +# Doxyfile 1.3.4 + +# This file describes the settings to be used by the documentation system +# doxygen (www.doxygen.org) for a project +# +# All text after a hash (#) is considered a comment and will be ignored +# The format is: +# TAG = value [value, ...] +# For lists items can also be appended using: +# TAG += value [value, ...] +# Values that contain spaces should be placed between quotes (" ") + +#--------------------------------------------------------------------------- +# Project related configuration options +#--------------------------------------------------------------------------- + +# The PROJECT_NAME tag is a single word (or a sequence of words surrounded +# by quotes) that should identify the project. + +PROJECT_NAME = physfs + +# The PROJECT_NUMBER tag can be used to enter a project or revision number. +# This could be handy for archiving the generated documentation or +# if some version control system is used. + +PROJECT_NUMBER = 2.0.2 + +# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) +# base path where the generated documentation will be put. +# If a relative path is entered, it will be relative to the location +# where doxygen was started. If left blank the current directory will be used. + +OUTPUT_DIRECTORY = docs + +# The OUTPUT_LANGUAGE tag is used to specify the language in which all +# documentation generated by doxygen is written. Doxygen will use this +# information to generate all constant output in the proper language. +# The default language is English, other supported languages are: +# Brazilian, Catalan, Chinese, Chinese-Traditional, Croatian, Czech, Danish, Dutch, +# Finnish, French, German, Greek, Hungarian, Italian, Japanese, Japanese-en +# (Japanese with English messages), Korean, Norwegian, Polish, Portuguese, +# Romanian, Russian, Serbian, Slovak, Slovene, Spanish, Swedish, and Ukrainian. + +OUTPUT_LANGUAGE = English + +# This tag can be used to specify the encoding used in the generated output. +# The encoding is not always determined by the language that is chosen, +# but also whether or not the output is meant for Windows or non-Windows users. +# In case there is a difference, setting the USE_WINDOWS_ENCODING tag to YES +# forces the Windows encoding (this is the default for the Windows binary), +# whereas setting the tag to NO uses a Unix-style encoding (the default for +# all platforms other than Windows). + +USE_WINDOWS_ENCODING = NO + +# If the BRIEF_MEMBER_DESC tag is set to YES (the default) Doxygen will +# include brief member descriptions after the members that are listed in +# the file and class documentation (similar to JavaDoc). +# Set to NO to disable this. + +BRIEF_MEMBER_DESC = YES + +# If the REPEAT_BRIEF tag is set to YES (the default) Doxygen will prepend +# the brief description of a member or function before the detailed description. +# Note: if both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the +# brief descriptions will be completely suppressed. + +REPEAT_BRIEF = YES + +# If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then +# Doxygen will generate a detailed section even if there is only a brief +# description. + +ALWAYS_DETAILED_SEC = NO + +# If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all inherited +# members of a class in the documentation of that class as if those members were +# ordinary class members. Constructors, destructors and assignment operators of +# the base classes will not be shown. + +INLINE_INHERITED_MEMB = NO + +# If the FULL_PATH_NAMES tag is set to YES then Doxygen will prepend the full +# path before files name in the file list and in the header files. If set +# to NO the shortest path that makes the file name unique will be used. + +FULL_PATH_NAMES = NO + +# If the FULL_PATH_NAMES tag is set to YES then the STRIP_FROM_PATH tag +# can be used to strip a user-defined part of the path. Stripping is +# only done if one of the specified strings matches the left-hand part of +# the path. It is allowed to use relative paths in the argument list. + +STRIP_FROM_PATH = + +# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter +# (but less readable) file names. This can be useful is your file systems +# doesn't support long names like on DOS, Mac, or CD-ROM. + +SHORT_NAMES = NO + +# If the JAVADOC_AUTOBRIEF tag is set to YES then Doxygen +# will interpret the first line (until the first dot) of a JavaDoc-style +# comment as the brief description. If set to NO, the JavaDoc +# comments will behave just like the Qt-style comments (thus requiring an +# explict @brief command for a brief description. + +JAVADOC_AUTOBRIEF = NO + +# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make Doxygen +# treat a multi-line C++ special comment block (i.e. a block of //! or /// +# comments) as a brief description. This used to be the default behaviour. +# The new default is to treat a multi-line C++ comment block as a detailed +# description. Set this tag to YES if you prefer the old behaviour instead. + +MULTILINE_CPP_IS_BRIEF = NO + +# If the DETAILS_AT_TOP tag is set to YES then Doxygen +# will output the detailed description near the top, like JavaDoc. +# If set to NO, the detailed description appears after the member +# documentation. + +DETAILS_AT_TOP = NO + +# If the INHERIT_DOCS tag is set to YES (the default) then an undocumented +# member inherits the documentation from any documented member that it +# reimplements. + +INHERIT_DOCS = YES + +# If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC +# tag is set to YES, then doxygen will reuse the documentation of the first +# member in the group (if any) for the other members of the group. By default +# all members of a group must be documented explicitly. + +DISTRIBUTE_GROUP_DOC = NO + +# The TAB_SIZE tag can be used to set the number of spaces in a tab. +# Doxygen uses this value to replace tabs by spaces in code fragments. + +TAB_SIZE = 4 + +# This tag can be used to specify a number of aliases that acts +# as commands in the documentation. An alias has the form "name=value". +# For example adding "sideeffect=\par Side Effects:\n" will allow you to +# put the command \sideeffect (or @sideeffect) in the documentation, which +# will result in a user-defined paragraph with heading "Side Effects:". +# You can put \n's in the value part of an alias to insert newlines. + +ALIASES = + +# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C sources +# only. Doxygen will then generate output that is more tailored for C. +# For instance, some of the names that are used will be different. The list +# of all members will be omitted, etc. + +OPTIMIZE_OUTPUT_FOR_C = YES + +# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java sources +# only. Doxygen will then generate output that is more tailored for Java. +# For instance, namespaces will be presented as packages, qualified scopes +# will look different, etc. + +OPTIMIZE_OUTPUT_JAVA = NO + +# Set the SUBGROUPING tag to YES (the default) to allow class member groups of +# the same type (for instance a group of public functions) to be put as a +# subgroup of that type (e.g. under the Public Functions section). Set it to +# NO to prevent subgrouping. Alternatively, this can be done per class using +# the \nosubgrouping command. + +SUBGROUPING = YES + +#--------------------------------------------------------------------------- +# Build related configuration options +#--------------------------------------------------------------------------- + +# If the EXTRACT_ALL tag is set to YES doxygen will assume all entities in +# documentation are documented, even if no documentation was available. +# Private class members and static file members will be hidden unless +# the EXTRACT_PRIVATE and EXTRACT_STATIC tags are set to YES + +EXTRACT_ALL = NO + +# If the EXTRACT_PRIVATE tag is set to YES all private members of a class +# will be included in the documentation. + +EXTRACT_PRIVATE = NO + +# If the EXTRACT_STATIC tag is set to YES all static members of a file +# will be included in the documentation. + +EXTRACT_STATIC = NO + +# If the EXTRACT_LOCAL_CLASSES tag is set to YES classes (and structs) +# defined locally in source files will be included in the documentation. +# If set to NO only classes defined in header files are included. + +EXTRACT_LOCAL_CLASSES = NO + +# If the HIDE_UNDOC_MEMBERS tag is set to YES, Doxygen will hide all +# undocumented members of documented classes, files or namespaces. +# If set to NO (the default) these members will be included in the +# various overviews, but no documentation section is generated. +# This option has no effect if EXTRACT_ALL is enabled. + +HIDE_UNDOC_MEMBERS = NO + +# If the HIDE_UNDOC_CLASSES tag is set to YES, Doxygen will hide all +# undocumented classes that are normally visible in the class hierarchy. +# If set to NO (the default) these classes will be included in the various +# overviews. This option has no effect if EXTRACT_ALL is enabled. + +HIDE_UNDOC_CLASSES = NO + +# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, Doxygen will hide all +# friend (class|struct|union) declarations. +# If set to NO (the default) these declarations will be included in the +# documentation. + +HIDE_FRIEND_COMPOUNDS = NO + +# If the HIDE_IN_BODY_DOCS tag is set to YES, Doxygen will hide any +# documentation blocks found inside the body of a function. +# If set to NO (the default) these blocks will be appended to the +# function's detailed documentation block. + +HIDE_IN_BODY_DOCS = NO + +# The INTERNAL_DOCS tag determines if documentation +# that is typed after a \internal command is included. If the tag is set +# to NO (the default) then the documentation will be excluded. +# Set it to YES to include the internal documentation. + +INTERNAL_DOCS = NO + +# If the CASE_SENSE_NAMES tag is set to NO then Doxygen will only generate +# file names in lower-case letters. If set to YES upper-case letters are also +# allowed. This is useful if you have classes or files whose names only differ +# in case and if your file system supports case sensitive file names. Windows +# users are advised to set this option to NO. + +CASE_SENSE_NAMES = YES + +# If the HIDE_SCOPE_NAMES tag is set to NO (the default) then Doxygen +# will show members with their full class and namespace scopes in the +# documentation. If set to YES the scope will be hidden. + +HIDE_SCOPE_NAMES = NO + +# If the SHOW_INCLUDE_FILES tag is set to YES (the default) then Doxygen +# will put a list of the files that are included by a file in the documentation +# of that file. + +SHOW_INCLUDE_FILES = YES + +# If the INLINE_INFO tag is set to YES (the default) then a tag [inline] +# is inserted in the documentation for inline members. + +INLINE_INFO = YES + +# If the SORT_MEMBER_DOCS tag is set to YES (the default) then doxygen +# will sort the (detailed) documentation of file and class members +# alphabetically by member name. If set to NO the members will appear in +# declaration order. + +SORT_MEMBER_DOCS = YES + +# The GENERATE_TODOLIST tag can be used to enable (YES) or +# disable (NO) the todo list. This list is created by putting \todo +# commands in the documentation. + +GENERATE_TODOLIST = YES + +# The GENERATE_TESTLIST tag can be used to enable (YES) or +# disable (NO) the test list. This list is created by putting \test +# commands in the documentation. + +GENERATE_TESTLIST = YES + +# The GENERATE_BUGLIST tag can be used to enable (YES) or +# disable (NO) the bug list. This list is created by putting \bug +# commands in the documentation. + +GENERATE_BUGLIST = YES + +# The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or +# disable (NO) the deprecated list. This list is created by putting +# \deprecated commands in the documentation. + +GENERATE_DEPRECATEDLIST= YES + +# The ENABLED_SECTIONS tag can be used to enable conditional +# documentation sections, marked by \if sectionname ... \endif. + +ENABLED_SECTIONS = + +# The MAX_INITIALIZER_LINES tag determines the maximum number of lines +# the initial value of a variable or define consists of for it to appear in +# the documentation. If the initializer consists of more lines than specified +# here it will be hidden. Use a value of 0 to hide initializers completely. +# The appearance of the initializer of individual variables and defines in the +# documentation can be controlled using \showinitializer or \hideinitializer +# command in the documentation regardless of this setting. + +MAX_INITIALIZER_LINES = 30 + +# Set the SHOW_USED_FILES tag to NO to disable the list of files generated +# at the bottom of the documentation of classes and structs. If set to YES the +# list will mention the files that were used to generate the documentation. + +SHOW_USED_FILES = YES + +#--------------------------------------------------------------------------- +# configuration options related to warning and progress messages +#--------------------------------------------------------------------------- + +# The QUIET tag can be used to turn on/off the messages that are generated +# by doxygen. Possible values are YES and NO. If left blank NO is used. + +QUIET = NO + +# The WARNINGS tag can be used to turn on/off the warning messages that are +# generated by doxygen. Possible values are YES and NO. If left blank +# NO is used. + +WARNINGS = YES + +# If WARN_IF_UNDOCUMENTED is set to YES, then doxygen will generate warnings +# for undocumented members. If EXTRACT_ALL is set to YES then this flag will +# automatically be disabled. + +WARN_IF_UNDOCUMENTED = YES + +# If WARN_IF_DOC_ERROR is set to YES, doxygen will generate warnings for +# potential errors in the documentation, such as not documenting some +# parameters in a documented function, or documenting parameters that +# don't exist or using markup commands wrongly. + +WARN_IF_DOC_ERROR = YES + +# The WARN_FORMAT tag determines the format of the warning messages that +# doxygen can produce. The string should contain the $file, $line, and $text +# tags, which will be replaced by the file and line number from which the +# warning originated and the warning text. + +WARN_FORMAT = "$file:$line: $text" + +# The WARN_LOGFILE tag can be used to specify a file to which warning +# and error messages should be written. If left blank the output is written +# to stderr. + +WARN_LOGFILE = + +#--------------------------------------------------------------------------- +# configuration options related to the input files +#--------------------------------------------------------------------------- + +# The INPUT tag can be used to specify the files and/or directories that contain +# documented source files. You may enter file names like "myfile.cpp" or +# directories like "/usr/src/myproject". Separate the files or directories +# with spaces. + +INPUT = physfs.h + +# If the value of the INPUT tag contains directories, you can use the +# FILE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp +# and *.h) to filter out the source-files in the directories. If left +# blank the following patterns are tested: +# *.c *.cc *.cxx *.cpp *.c++ *.java *.ii *.ixx *.ipp *.i++ *.inl *.h *.hh *.hxx *.hpp +# *.h++ *.idl *.odl *.cs *.php *.php3 *.inc + +FILE_PATTERNS = + +# The RECURSIVE tag can be used to turn specify whether or not subdirectories +# should be searched for input files as well. Possible values are YES and NO. +# If left blank NO is used. + +RECURSIVE = NO + +# The EXCLUDE tag can be used to specify files and/or directories that should +# excluded from the INPUT source files. This way you can easily exclude a +# subdirectory from a directory tree whose root is specified with the INPUT tag. + +EXCLUDE = + +# The EXCLUDE_SYMLINKS tag can be used select whether or not files or directories +# that are symbolic links (a Unix filesystem feature) are excluded from the input. + +EXCLUDE_SYMLINKS = NO + +# If the value of the INPUT tag contains directories, you can use the +# EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude +# certain files from those directories. + +EXCLUDE_PATTERNS = + +# The EXAMPLE_PATH tag can be used to specify one or more files or +# directories that contain example code fragments that are included (see +# the \include command). + +EXAMPLE_PATH = + +# If the value of the EXAMPLE_PATH tag contains directories, you can use the +# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp +# and *.h) to filter out the source-files in the directories. If left +# blank all files are included. + +EXAMPLE_PATTERNS = + +# If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be +# searched for input files to be used with the \include or \dontinclude +# commands irrespective of the value of the RECURSIVE tag. +# Possible values are YES and NO. If left blank NO is used. + +EXAMPLE_RECURSIVE = NO + +# The IMAGE_PATH tag can be used to specify one or more files or +# directories that contain image that are included in the documentation (see +# the \image command). + +IMAGE_PATH = + +# The INPUT_FILTER tag can be used to specify a program that doxygen should +# invoke to filter for each input file. Doxygen will invoke the filter program +# by executing (via popen()) the command , where +# is the value of the INPUT_FILTER tag, and is the name of an +# input file. Doxygen will then use the output that the filter program writes +# to standard output. + +INPUT_FILTER = + +# If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using +# INPUT_FILTER) will be used to filter the input files when producing source +# files to browse (i.e. when SOURCE_BROWSER is set to YES). + +FILTER_SOURCE_FILES = NO + +#--------------------------------------------------------------------------- +# configuration options related to source browsing +#--------------------------------------------------------------------------- + +# If the SOURCE_BROWSER tag is set to YES then a list of source files will +# be generated. Documented entities will be cross-referenced with these sources. + +SOURCE_BROWSER = NO + +# Setting the INLINE_SOURCES tag to YES will include the body +# of functions and classes directly in the documentation. + +INLINE_SOURCES = NO + +# Setting the STRIP_CODE_COMMENTS tag to YES (the default) will instruct +# doxygen to hide any special comment blocks from generated source code +# fragments. Normal C and C++ comments will always remain visible. + +STRIP_CODE_COMMENTS = YES + +# If the REFERENCED_BY_RELATION tag is set to YES (the default) +# then for each documented function all documented +# functions referencing it will be listed. + +REFERENCED_BY_RELATION = YES + +# If the REFERENCES_RELATION tag is set to YES (the default) +# then for each documented function all documented entities +# called/used by that function will be listed. + +REFERENCES_RELATION = YES + +# If the VERBATIM_HEADERS tag is set to YES (the default) then Doxygen +# will generate a verbatim copy of the header file for each class for +# which an include is specified. Set to NO to disable this. + +VERBATIM_HEADERS = YES + +#--------------------------------------------------------------------------- +# configuration options related to the alphabetical class index +#--------------------------------------------------------------------------- + +# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index +# of all compounds will be generated. Enable this if the project +# contains a lot of classes, structs, unions or interfaces. + +ALPHABETICAL_INDEX = NO + +# If the alphabetical index is enabled (see ALPHABETICAL_INDEX) then +# the COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns +# in which this list will be split (can be a number in the range [1..20]) + +COLS_IN_ALPHA_INDEX = 5 + +# In case all classes in a project start with a common prefix, all +# classes will be put under the same header in the alphabetical index. +# The IGNORE_PREFIX tag can be used to specify one or more prefixes that +# should be ignored while generating the index headers. + +IGNORE_PREFIX = + +#--------------------------------------------------------------------------- +# configuration options related to the HTML output +#--------------------------------------------------------------------------- + +# If the GENERATE_HTML tag is set to YES (the default) Doxygen will +# generate HTML output. + +GENERATE_HTML = YES + +# The HTML_OUTPUT tag is used to specify where the HTML docs will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `html' will be used as the default path. + +HTML_OUTPUT = html + +# The HTML_FILE_EXTENSION tag can be used to specify the file extension for +# each generated HTML page (for example: .htm,.php,.asp). If it is left blank +# doxygen will generate files with .html extension. + +HTML_FILE_EXTENSION = .html + +# The HTML_HEADER tag can be used to specify a personal HTML header for +# each generated HTML page. If it is left blank doxygen will generate a +# standard header. + +HTML_HEADER = + +# The HTML_FOOTER tag can be used to specify a personal HTML footer for +# each generated HTML page. If it is left blank doxygen will generate a +# standard footer. + +HTML_FOOTER = + +# The HTML_STYLESHEET tag can be used to specify a user-defined cascading +# style sheet that is used by each HTML page. It can be used to +# fine-tune the look of the HTML output. If the tag is left blank doxygen +# will generate a default style sheet + +HTML_STYLESHEET = + +# If the HTML_ALIGN_MEMBERS tag is set to YES, the members of classes, +# files or namespaces will be aligned in HTML using tables. If set to +# NO a bullet list will be used. + +HTML_ALIGN_MEMBERS = YES + +# If the GENERATE_HTMLHELP tag is set to YES, additional index files +# will be generated that can be used as input for tools like the +# Microsoft HTML help workshop to generate a compressed HTML help file (.chm) +# of the generated HTML documentation. + +GENERATE_HTMLHELP = NO + +# If the GENERATE_HTMLHELP tag is set to YES, the CHM_FILE tag can +# be used to specify the file name of the resulting .chm file. You +# can add a path in front of the file if the result should not be +# written to the html output dir. + +CHM_FILE = + +# If the GENERATE_HTMLHELP tag is set to YES, the HHC_LOCATION tag can +# be used to specify the location (absolute path including file name) of +# the HTML help compiler (hhc.exe). If non-empty doxygen will try to run +# the HTML help compiler on the generated index.hhp. + +HHC_LOCATION = + +# If the GENERATE_HTMLHELP tag is set to YES, the GENERATE_CHI flag +# controls if a separate .chi index file is generated (YES) or that +# it should be included in the master .chm file (NO). + +GENERATE_CHI = NO + +# If the GENERATE_HTMLHELP tag is set to YES, the BINARY_TOC flag +# controls whether a binary table of contents is generated (YES) or a +# normal table of contents (NO) in the .chm file. + +BINARY_TOC = NO + +# The TOC_EXPAND flag can be set to YES to add extra items for group members +# to the contents of the HTML help documentation and to the tree view. + +TOC_EXPAND = NO + +# The DISABLE_INDEX tag can be used to turn on/off the condensed index at +# top of each HTML page. The value NO (the default) enables the index and +# the value YES disables it. + +DISABLE_INDEX = NO + +# This tag can be used to set the number of enum values (range [1..20]) +# that doxygen will group on one line in the generated HTML documentation. + +ENUM_VALUES_PER_LINE = 4 + +# If the GENERATE_TREEVIEW tag is set to YES, a side panel will be +# generated containing a tree-like index structure (just like the one that +# is generated for HTML Help). For this to work a browser that supports +# JavaScript, DHTML, CSS and frames is required (for instance Mozilla 1.0+, +# Netscape 6.0+, Internet explorer 5.0+, or Konqueror). Windows users are +# probably better off using the HTML help feature. + +GENERATE_TREEVIEW = NO + +# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be +# used to set the initial width (in pixels) of the frame in which the tree +# is shown. + +TREEVIEW_WIDTH = 250 + +#--------------------------------------------------------------------------- +# configuration options related to the LaTeX output +#--------------------------------------------------------------------------- + +# If the GENERATE_LATEX tag is set to YES (the default) Doxygen will +# generate Latex output. + +GENERATE_LATEX = YES + +# The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `latex' will be used as the default path. + +LATEX_OUTPUT = latex + +# The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be +# invoked. If left blank `latex' will be used as the default command name. + +LATEX_CMD_NAME = latex + +# The MAKEINDEX_CMD_NAME tag can be used to specify the command name to +# generate index for LaTeX. If left blank `makeindex' will be used as the +# default command name. + +MAKEINDEX_CMD_NAME = makeindex + +# If the COMPACT_LATEX tag is set to YES Doxygen generates more compact +# LaTeX documents. This may be useful for small projects and may help to +# save some trees in general. + +COMPACT_LATEX = NO + +# The PAPER_TYPE tag can be used to set the paper type that is used +# by the printer. Possible values are: a4, a4wide, letter, legal and +# executive. If left blank a4wide will be used. + +PAPER_TYPE = a4wide + +# The EXTRA_PACKAGES tag can be to specify one or more names of LaTeX +# packages that should be included in the LaTeX output. + +EXTRA_PACKAGES = + +# The LATEX_HEADER tag can be used to specify a personal LaTeX header for +# the generated latex document. The header should contain everything until +# the first chapter. If it is left blank doxygen will generate a +# standard header. Notice: only use this tag if you know what you are doing! + +LATEX_HEADER = + +# If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated +# is prepared for conversion to pdf (using ps2pdf). The pdf file will +# contain links (just like the HTML output) instead of page references +# This makes the output suitable for online browsing using a pdf viewer. + +PDF_HYPERLINKS = NO + +# If the USE_PDFLATEX tag is set to YES, pdflatex will be used instead of +# plain latex in the generated Makefile. Set this option to YES to get a +# higher quality PDF documentation. + +USE_PDFLATEX = NO + +# If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \\batchmode. +# command to the generated LaTeX files. This will instruct LaTeX to keep +# running if errors occur, instead of asking the user for help. +# This option is also used when generating formulas in HTML. + +LATEX_BATCHMODE = NO + +# If LATEX_HIDE_INDICES is set to YES then doxygen will not +# include the index chapters (such as File Index, Compound Index, etc.) +# in the output. + +LATEX_HIDE_INDICES = NO + +#--------------------------------------------------------------------------- +# configuration options related to the RTF output +#--------------------------------------------------------------------------- + +# If the GENERATE_RTF tag is set to YES Doxygen will generate RTF output +# The RTF output is optimised for Word 97 and may not look very pretty with +# other RTF readers or editors. + +GENERATE_RTF = NO + +# The RTF_OUTPUT tag is used to specify where the RTF docs will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `rtf' will be used as the default path. + +RTF_OUTPUT = rtf + +# If the COMPACT_RTF tag is set to YES Doxygen generates more compact +# RTF documents. This may be useful for small projects and may help to +# save some trees in general. + +COMPACT_RTF = NO + +# If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated +# will contain hyperlink fields. The RTF file will +# contain links (just like the HTML output) instead of page references. +# This makes the output suitable for online browsing using WORD or other +# programs which support those fields. +# Note: wordpad (write) and others do not support links. + +RTF_HYPERLINKS = NO + +# Load stylesheet definitions from file. Syntax is similar to doxygen's +# config file, i.e. a series of assigments. You only have to provide +# replacements, missing definitions are set to their default value. + +RTF_STYLESHEET_FILE = + +# Set optional variables used in the generation of an rtf document. +# Syntax is similar to doxygen's config file. + +RTF_EXTENSIONS_FILE = + +#--------------------------------------------------------------------------- +# configuration options related to the man page output +#--------------------------------------------------------------------------- + +# If the GENERATE_MAN tag is set to YES (the default) Doxygen will +# generate man pages + +GENERATE_MAN = YES + +# The MAN_OUTPUT tag is used to specify where the man pages will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `man' will be used as the default path. + +MAN_OUTPUT = man + +# The MAN_EXTENSION tag determines the extension that is added to +# the generated man pages (default is the subroutine's section .3) + +MAN_EXTENSION = .3 + +# If the MAN_LINKS tag is set to YES and Doxygen generates man output, +# then it will generate one additional man file for each entity +# documented in the real man page(s). These additional files +# only source the real man page, but without them the man command +# would be unable to find the correct page. The default is NO. + +MAN_LINKS = YES + +#--------------------------------------------------------------------------- +# configuration options related to the XML output +#--------------------------------------------------------------------------- + +# If the GENERATE_XML tag is set to YES Doxygen will +# generate an XML file that captures the structure of +# the code including all documentation. Note that this +# feature is still experimental and incomplete at the +# moment. + +GENERATE_XML = NO + +# The XML_OUTPUT tag is used to specify where the XML pages will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `xml' will be used as the default path. + +XML_OUTPUT = xml + +# The XML_SCHEMA tag can be used to specify an XML schema, +# which can be used by a validating XML parser to check the +# syntax of the XML files. + +XML_SCHEMA = + +# The XML_DTD tag can be used to specify an XML DTD, +# which can be used by a validating XML parser to check the +# syntax of the XML files. + +XML_DTD = + +#--------------------------------------------------------------------------- +# configuration options for the AutoGen Definitions output +#--------------------------------------------------------------------------- + +# If the GENERATE_AUTOGEN_DEF tag is set to YES Doxygen will +# generate an AutoGen Definitions (see autogen.sf.net) file +# that captures the structure of the code including all +# documentation. Note that this feature is still experimental +# and incomplete at the moment. + +GENERATE_AUTOGEN_DEF = NO + +#--------------------------------------------------------------------------- +# configuration options related to the Perl module output +#--------------------------------------------------------------------------- + +# If the GENERATE_PERLMOD tag is set to YES Doxygen will +# generate a Perl module file that captures the structure of +# the code including all documentation. Note that this +# feature is still experimental and incomplete at the +# moment. + +GENERATE_PERLMOD = NO + +# If the PERLMOD_LATEX tag is set to YES Doxygen will generate +# the necessary Makefile rules, Perl scripts and LaTeX code to be able +# to generate PDF and DVI output from the Perl module output. + +PERLMOD_LATEX = NO + +# If the PERLMOD_PRETTY tag is set to YES the Perl module output will be +# nicely formatted so it can be parsed by a human reader. This is useful +# if you want to understand what is going on. On the other hand, if this +# tag is set to NO the size of the Perl module output will be much smaller +# and Perl will parse it just the same. + +PERLMOD_PRETTY = YES + +# The names of the make variables in the generated doxyrules.make file +# are prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX. +# This is useful so different doxyrules.make files included by the same +# Makefile don't overwrite each other's variables. + +PERLMOD_MAKEVAR_PREFIX = + +#--------------------------------------------------------------------------- +# Configuration options related to the preprocessor +#--------------------------------------------------------------------------- + +# If the ENABLE_PREPROCESSING tag is set to YES (the default) Doxygen will +# evaluate all C-preprocessor directives found in the sources and include +# files. + +ENABLE_PREPROCESSING = YES + +# If the MACRO_EXPANSION tag is set to YES Doxygen will expand all macro +# names in the source code. If set to NO (the default) only conditional +# compilation will be performed. Macro expansion can be done in a controlled +# way by setting EXPAND_ONLY_PREDEF to YES. + +MACRO_EXPANSION = YES + +# If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES +# then the macro expansion is limited to the macros specified with the +# PREDEFINED and EXPAND_AS_PREDEFINED tags. + +EXPAND_ONLY_PREDEF = YES + +# If the SEARCH_INCLUDES tag is set to YES (the default) the includes files +# in the INCLUDE_PATH (see below) will be search if a #include is found. + +SEARCH_INCLUDES = YES + +# The INCLUDE_PATH tag can be used to specify one or more directories that +# contain include files that are not input files but should be processed by +# the preprocessor. + +INCLUDE_PATH = + +# You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard +# patterns (like *.h and *.hpp) to filter out the header-files in the +# directories. If left blank, the patterns specified with FILE_PATTERNS will +# be used. + +INCLUDE_FILE_PATTERNS = + +# The PREDEFINED tag can be used to specify one or more macro names that +# are defined before the preprocessor is started (similar to the -D option of +# gcc). The argument of the tag is a list of macros of the form: name +# or name=definition (no spaces). If the definition and the = are +# omitted =1 is assumed. + +PREDEFINED = DOXYGEN_SHOULD_IGNORE_THIS=1 \ + __EXPORT__= + +# If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then +# this tag can be used to specify a list of macro names that should be expanded. +# The macro definition that is found in the sources will be used. +# Use the PREDEFINED tag if you want to use a different macro definition. + +EXPAND_AS_DEFINED = + +# If the SKIP_FUNCTION_MACROS tag is set to YES (the default) then +# doxygen's preprocessor will remove all function-like macros that are alone +# on a line, have an all uppercase name, and do not end with a semicolon. Such +# function macros are typically used for boiler-plate code, and will confuse the +# parser if not removed. + +SKIP_FUNCTION_MACROS = YES + +#--------------------------------------------------------------------------- +# Configuration::addtions related to external references +#--------------------------------------------------------------------------- + +# The TAGFILES option can be used to specify one or more tagfiles. +# Optionally an initial location of the external documentation +# can be added for each tagfile. The format of a tag file without +# this location is as follows: +# TAGFILES = file1 file2 ... +# Adding location for the tag files is done as follows: +# TAGFILES = file1=loc1 "file2 = loc2" ... +# where "loc1" and "loc2" can be relative or absolute paths or +# URLs. If a location is present for each tag, the installdox tool +# does not have to be run to correct the links. +# Note that each tag file must have a unique name +# (where the name does NOT include the path) +# If a tag file is not located in the directory in which doxygen +# is run, you must also specify the path to the tagfile here. + +TAGFILES = + +# When a file name is specified after GENERATE_TAGFILE, doxygen will create +# a tag file that is based on the input files it reads. + +GENERATE_TAGFILE = + +# If the ALLEXTERNALS tag is set to YES all external classes will be listed +# in the class index. If set to NO only the inherited external classes +# will be listed. + +ALLEXTERNALS = NO + +# If the EXTERNAL_GROUPS tag is set to YES all external groups will be listed +# in the modules index. If set to NO, only the current project's groups will +# be listed. + +EXTERNAL_GROUPS = YES + +# The PERL_PATH should be the absolute path and name of the perl script +# interpreter (i.e. the result of `which perl'). + +PERL_PATH = /usr/bin/perl + +#--------------------------------------------------------------------------- +# Configuration options related to the dot tool +#--------------------------------------------------------------------------- + +# If the CLASS_DIAGRAMS tag is set to YES (the default) Doxygen will +# generate a inheritance diagram (in HTML, RTF and LaTeX) for classes with base or +# super classes. Setting the tag to NO turns the diagrams off. Note that this +# option is superceded by the HAVE_DOT option below. This is only a fallback. It is +# recommended to install and use dot, since it yields more powerful graphs. + +CLASS_DIAGRAMS = NO + +# If set to YES, the inheritance and collaboration graphs will hide +# inheritance and usage relations if the target is undocumented +# or is not a class. + +HIDE_UNDOC_RELATIONS = YES + +# If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is +# available from the path. This tool is part of Graphviz, a graph visualization +# toolkit from AT&T and Lucent Bell Labs. The other options in this section +# have no effect if this option is set to NO (the default) + +HAVE_DOT = NO + +# If the CLASS_GRAPH and HAVE_DOT tags are set to YES then doxygen +# will generate a graph for each documented class showing the direct and +# indirect inheritance relations. Setting this tag to YES will force the +# the CLASS_DIAGRAMS tag to NO. + +CLASS_GRAPH = NO + +# If the COLLABORATION_GRAPH and HAVE_DOT tags are set to YES then doxygen +# will generate a graph for each documented class showing the direct and +# indirect implementation dependencies (inheritance, containment, and +# class references variables) of the class with other documented classes. + +COLLABORATION_GRAPH = NO + +# If the UML_LOOK tag is set to YES doxygen will generate inheritance and +# collaboration diagrams in a style similiar to the OMG's Unified Modeling +# Language. + +UML_LOOK = NO + +# If set to YES, the inheritance and collaboration graphs will show the +# relations between templates and their instances. + +TEMPLATE_RELATIONS = NO + +# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDE_GRAPH, and HAVE_DOT +# tags are set to YES then doxygen will generate a graph for each documented +# file showing the direct and indirect include dependencies of the file with +# other documented files. + +INCLUDE_GRAPH = NO + +# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDED_BY_GRAPH, and +# HAVE_DOT tags are set to YES then doxygen will generate a graph for each +# documented header file showing the documented files that directly or +# indirectly include this file. + +INCLUDED_BY_GRAPH = YES + +# If the CALL_GRAPH and HAVE_DOT tags are set to YES then doxygen will +# generate a call dependency graph for every global function or class method. +# Note that enabling this option will significantly increase the time of a run. +# So in most cases it will be better to enable call graphs for selected +# functions only using the \callgraph command. + +CALL_GRAPH = NO + +# If the GRAPHICAL_HIERARCHY and HAVE_DOT tags are set to YES then doxygen +# will graphical hierarchy of all classes instead of a textual one. + +GRAPHICAL_HIERARCHY = YES + +# The DOT_IMAGE_FORMAT tag can be used to set the image format of the images +# generated by dot. Possible values are png, jpg, or gif +# If left blank png will be used. + +DOT_IMAGE_FORMAT = png + +# The tag DOT_PATH can be used to specify the path where the dot tool can be +# found. If left blank, it is assumed the dot tool can be found on the path. + +DOT_PATH = + +# The DOTFILE_DIRS tag can be used to specify one or more directories that +# contain dot files that are included in the documentation (see the +# \dotfile command). + +DOTFILE_DIRS = + +# The MAX_DOT_GRAPH_WIDTH tag can be used to set the maximum allowed width +# (in pixels) of the graphs generated by dot. If a graph becomes larger than +# this value, doxygen will try to truncate the graph, so that it fits within +# the specified constraint. Beware that most browsers cannot cope with very +# large images. + +MAX_DOT_GRAPH_WIDTH = 1024 + +# The MAX_DOT_GRAPH_HEIGHT tag can be used to set the maximum allows height +# (in pixels) of the graphs generated by dot. If a graph becomes larger than +# this value, doxygen will try to truncate the graph, so that it fits within +# the specified constraint. Beware that most browsers cannot cope with very +# large images. + +MAX_DOT_GRAPH_HEIGHT = 1024 + +# The MAX_DOT_GRAPH_DEPTH tag can be used to set the maximum depth of the +# graphs generated by dot. A depth value of 3 means that only nodes reachable +# from the root by following a path via at most 3 edges will be shown. Nodes that +# lay further from the root node will be omitted. Note that setting this option to +# 1 or 2 may greatly reduce the computation time needed for large code bases. Also +# note that a graph may be further truncated if the graph's image dimensions are +# not sufficient to fit the graph (see MAX_DOT_GRAPH_WIDTH and MAX_DOT_GRAPH_HEIGHT). +# If 0 is used for the depth value (the default), the graph is not depth-constrained. + +MAX_DOT_GRAPH_DEPTH = 0 + +# If the GENERATE_LEGEND tag is set to YES (the default) Doxygen will +# generate a legend page explaining the meaning of the various boxes and +# arrows in the dot generated graphs. + +GENERATE_LEGEND = YES + +# If the DOT_CLEANUP tag is set to YES (the default) Doxygen will +# remove the intermediate dot files that are used to generate +# the various graphs. + +DOT_CLEANUP = YES + +#--------------------------------------------------------------------------- +# Configuration::addtions related to the search engine +#--------------------------------------------------------------------------- + +# The SEARCHENGINE tag specifies whether or not a search engine should be +# used. If set to NO the values of all tags below this one will be ignored. + +SEARCHENGINE = NO diff --git a/project/jni/physfs/INSTALL.txt b/project/jni/physfs/INSTALL.txt new file mode 100644 index 000000000..acbbcc01f --- /dev/null +++ b/project/jni/physfs/INSTALL.txt @@ -0,0 +1,153 @@ + +The latest PhysicsFS information and releases can be found at: + http://icculus.org/physfs/ + +Building is (ahem) very easy. + + +ALL PLATFORMS: + +Please understand your rights and mine: read the text file LICENSE.txt in the + root of the source tree. If you can't abide by it, delete this source tree + now. 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: + + http://icculus.org/physfs/docs/ + + + + +UNIX: + +You will need CMake (http://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 + project files if you prefer these. + +Run "make". PhysicsFS will now build. + +As root, run "make install". + If you get sick of the library, run "xargs rm < install_manifest.txt" 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. + + + +BeOS, Zeta, and Haiku: + +Use the "Unix" instructions, above. The CMake port to BeOS is fairly new at + the time of this writing, but it works. You can get a build of CMake from + bebits.com or build it yourself from source from cmake.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 (http://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 95 and 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...this is used for Unicode support and + locating user-specific directories, etc. + +PhysicsFS has not been tested on 64-bit Windows, but probably works. There is + no 16-bit Windows support at all. Reports of success and problems can go to + Ryan at icculus@icculus.org ... + +If someone is willing to maintain prebuilt PhysicsFS DLLs, I'd like to hear +from you; send an email to icculus@icculus.org ... + + + +PocketPC/WindowsCE: + +Code exists for PocketPC support, and there are shipping titles that used + PhysicsFS 1.0 on PocketPC...but it isn't tested in 2.0, and is probably + broken with the new build system. Please send patches. + + + +MAC OS 8/9: + +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 Mac OS X, though: see below. + + + +MAC OS X: + +You will need CMake (http://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. + +If someone is willing to maintain prebuilt PhysicsFS Shared Libraries for + Mac OS X, I'd like to hear from you; send an email to icculus@icculus.org. + + + +OS/2: + +You need Innotek GCC and libc installed (or kLIBC). I tried this on a stock + Warp 4 install, no fixpaks. You need to install link386.exe (Selective + Install, "link object modules" option). Once klibc and GCC are installed + correctly, unpack the source to PhysicsFS and run the script + file "makeos2.cmd". I know this isn't ideal, but I wanted to have this build + without users having to hunt down a "make" program. + +Someone please port CMake to OS/2. Ideally I'd like to be able to target + Innotek GCC and OpenWatcom with CMake. + +If someone is willing to maintain prebuilt PhysicsFS Shared Libraries for + OS/2, I'd like to hear from you; send an email to icculus@icculus.org. + + + +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 in the platform/ and archiver/ directories for + examples. + +--ryan. (icculus@icculus.org) + diff --git a/project/jni/physfs/LICENSE.txt b/project/jni/physfs/LICENSE.txt new file mode 100644 index 000000000..a59b5dea1 --- /dev/null +++ b/project/jni/physfs/LICENSE.txt @@ -0,0 +1,44 @@ + + Copyright (c) 2001-2011 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 + + + + +Notes, separate from the license. This is not legal advice. + +Versions of PhysicsFS prior to 0.1.9 are licensed under the GNU Lesser General + Public License, which restricts you significantly more. For your own safety, + please make sure you've got 0.1.9 or later if you plan to use physfs in a + commercial or closed-source project. + +Optional pieces of PhysicsFS may fall under other licenses, please consult +your lawyer for legal advice, which this is not... + + zlib: if you enable ZIP archive support, PhysicsFS uses zlib. Its license + requirements are identical to PhysicsFS. + Please see zlib123/README for details. + + lzma: if you enable LZMA (7zip) support, PhysicsFS uses the lzma sdk. + It uses the LGPL license, with exceptions for closed-source programs. + Please see lzma/lzma.txt for details. + diff --git a/project/jni/physfs/TODO.txt b/project/jni/physfs/TODO.txt new file mode 100644 index 000000000..9425051b2 --- /dev/null +++ b/project/jni/physfs/TODO.txt @@ -0,0 +1,45 @@ +Stuff that needs to be done and wishlist: + +These are in no particular order. +Some might be dupes, some might be done already. + +UNICODE: +- OS/2: Codepages. No full Unicode in the filesystem, but we can probably make + a conversion effort. + + +Stuff: +- 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. +- Reduce malloc() pressure all over the place. We fragment memory like mad. +- profile string list interpolation. +- We have two different ways to find dir entries in zip.c. +- 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). +- Find some way to relax or remove the security model for external tools. +- OSX shouldn't use ~/.app for userdir. +- fscanf and fprintf support in extras dir. +- Why do we call it openArchive and dirClose? +- Sanity check byte order at runtime. +- Memory locking? +- Find a better name than dvoid and fvoid. +- Can windows.c and pocketpc.c get merged? +- There's so much cut-and-paste between archivers...can this be reduced? +- General code audit. +- Multiple write dirs with mount points? +- Deprecate PHYSFS_setSaneConfig and move it to extras? +- Why is physfsrwops.c cut-and-pasted into the ruby bindings? +- Replace code from SDL... +- Should file enumeration return an error or set error state? +- Need "getmountpoint" command in test_physfs.c ... +- Look for calloc() calls that aren't going through the allocation hooks. +- Write up a simple HOWTO on embedding physicsfs in another project. +- Archivers need abstracted i/o to read from memory or files (archives in archives?) +- Probably other stuff. Requests and recommendations are welcome. + +// end of TODO.txt ... + diff --git a/project/jni/physfs/archivers/dir.c b/project/jni/physfs/archivers/dir.c new file mode 100644 index 000000000..a5807434b --- /dev/null +++ b/project/jni/physfs/archivers/dir.c @@ -0,0 +1,283 @@ +/* + * 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. + */ + +#include +#include +#include +#include "physfs.h" + +#define __PHYSICSFS_INTERNAL__ +#include "physfs_internal.h" + +static PHYSFS_sint64 DIR_read(fvoid *opaque, void *buffer, + PHYSFS_uint32 objSize, PHYSFS_uint32 objCount) +{ + PHYSFS_sint64 retval; + retval = __PHYSFS_platformRead(opaque, buffer, objSize, objCount); + return(retval); +} /* DIR_read */ + + +static PHYSFS_sint64 DIR_write(fvoid *opaque, const void *buffer, + PHYSFS_uint32 objSize, PHYSFS_uint32 objCount) +{ + PHYSFS_sint64 retval; + retval = __PHYSFS_platformWrite(opaque, buffer, objSize, objCount); + return(retval); +} /* DIR_write */ + + +static int DIR_eof(fvoid *opaque) +{ + return(__PHYSFS_platformEOF(opaque)); +} /* DIR_eof */ + + +static PHYSFS_sint64 DIR_tell(fvoid *opaque) +{ + return(__PHYSFS_platformTell(opaque)); +} /* DIR_tell */ + + +static int DIR_seek(fvoid *opaque, PHYSFS_uint64 offset) +{ + return(__PHYSFS_platformSeek(opaque, offset)); +} /* DIR_seek */ + + +static PHYSFS_sint64 DIR_fileLength(fvoid *opaque) +{ + return(__PHYSFS_platformFileLength(opaque)); +} /* DIR_fileLength */ + + +static int DIR_fileClose(fvoid *opaque) +{ + /* + * we manually flush the buffer, since that's the place a close will + * most likely fail, but that will leave the file handle in an undefined + * state if it fails. Flush failures we can recover from. + */ + BAIL_IF_MACRO(!__PHYSFS_platformFlush(opaque), NULL, 0); + BAIL_IF_MACRO(!__PHYSFS_platformClose(opaque), NULL, 0); + return(1); +} /* DIR_fileClose */ + + +static int DIR_isArchive(const char *filename, int forWriting) +{ + /* directories ARE archives in this driver... */ + return(__PHYSFS_platformIsDirectory(filename)); +} /* DIR_isArchive */ + + +static void *DIR_openArchive(const char *name, int forWriting) +{ + const char *dirsep = PHYSFS_getDirSeparator(); + char *retval = NULL; + size_t namelen = strlen(name); + size_t seplen = strlen(dirsep); + + /* !!! FIXME: when is this not called right before openArchive? */ + BAIL_IF_MACRO(!DIR_isArchive(name, forWriting), + ERR_UNSUPPORTED_ARCHIVE, 0); + + retval = allocator.Malloc(namelen + seplen + 1); + BAIL_IF_MACRO(retval == NULL, ERR_OUT_OF_MEMORY, NULL); + + /* make sure there's a dir separator at the end of the string */ + strcpy(retval, name); + if (strcmp((name + namelen) - seplen, dirsep) != 0) + strcat(retval, dirsep); + + return(retval); +} /* DIR_openArchive */ + + +static void DIR_enumerateFiles(dvoid *opaque, const char *dname, + int omitSymLinks, PHYSFS_EnumFilesCallback cb, + const char *origdir, void *callbackdata) +{ + char *d = __PHYSFS_platformCvtToDependent((char *)opaque, dname, NULL); + if (d != NULL) + { + __PHYSFS_platformEnumerateFiles(d, omitSymLinks, cb, + origdir, callbackdata); + allocator.Free(d); + } /* if */ +} /* DIR_enumerateFiles */ + + +static int DIR_exists(dvoid *opaque, const char *name) +{ + char *f = __PHYSFS_platformCvtToDependent((char *) opaque, name, NULL); + int retval; + + BAIL_IF_MACRO(f == NULL, NULL, 0); + retval = __PHYSFS_platformExists(f); + allocator.Free(f); + return(retval); +} /* DIR_exists */ + + +static int DIR_isDirectory(dvoid *opaque, const char *name, int *fileExists) +{ + char *d = __PHYSFS_platformCvtToDependent((char *) opaque, name, NULL); + int retval = 0; + + BAIL_IF_MACRO(d == NULL, NULL, 0); + *fileExists = __PHYSFS_platformExists(d); + if (*fileExists) + retval = __PHYSFS_platformIsDirectory(d); + allocator.Free(d); + return(retval); +} /* DIR_isDirectory */ + + +static int DIR_isSymLink(dvoid *opaque, const char *name, int *fileExists) +{ + char *f = __PHYSFS_platformCvtToDependent((char *) opaque, name, NULL); + int retval = 0; + + BAIL_IF_MACRO(f == NULL, NULL, 0); + *fileExists = __PHYSFS_platformExists(f); + if (*fileExists) + retval = __PHYSFS_platformIsSymLink(f); + allocator.Free(f); + return(retval); +} /* DIR_isSymLink */ + + +static PHYSFS_sint64 DIR_getLastModTime(dvoid *opaque, + const char *name, + int *fileExists) +{ + char *d = __PHYSFS_platformCvtToDependent((char *) opaque, name, NULL); + PHYSFS_sint64 retval = -1; + + BAIL_IF_MACRO(d == NULL, NULL, 0); + *fileExists = __PHYSFS_platformExists(d); + if (*fileExists) + retval = __PHYSFS_platformGetLastModTime(d); + allocator.Free(d); + return(retval); +} /* DIR_getLastModTime */ + + +static fvoid *doOpen(dvoid *opaque, const char *name, + void *(*openFunc)(const char *filename), + int *fileExists) +{ + char *f = __PHYSFS_platformCvtToDependent((char *) opaque, name, NULL); + void *rc = NULL; + + BAIL_IF_MACRO(f == NULL, NULL, NULL); + + if (fileExists != NULL) + { + *fileExists = __PHYSFS_platformExists(f); + if (!(*fileExists)) + { + allocator.Free(f); + return(NULL); + } /* if */ + } /* if */ + + rc = openFunc(f); + allocator.Free(f); + + return((fvoid *) rc); +} /* doOpen */ + + +static fvoid *DIR_openRead(dvoid *opaque, const char *fnm, int *exist) +{ + return(doOpen(opaque, fnm, __PHYSFS_platformOpenRead, exist)); +} /* DIR_openRead */ + + +static fvoid *DIR_openWrite(dvoid *opaque, const char *filename) +{ + return(doOpen(opaque, filename, __PHYSFS_platformOpenWrite, NULL)); +} /* DIR_openWrite */ + + +static fvoid *DIR_openAppend(dvoid *opaque, const char *filename) +{ + return(doOpen(opaque, filename, __PHYSFS_platformOpenAppend, NULL)); +} /* DIR_openAppend */ + + +static int DIR_remove(dvoid *opaque, const char *name) +{ + char *f = __PHYSFS_platformCvtToDependent((char *) opaque, name, NULL); + int retval; + + BAIL_IF_MACRO(f == NULL, NULL, 0); + retval = __PHYSFS_platformDelete(f); + allocator.Free(f); + return(retval); +} /* DIR_remove */ + + +static int DIR_mkdir(dvoid *opaque, const char *name) +{ + char *f = __PHYSFS_platformCvtToDependent((char *) opaque, name, NULL); + int retval; + + BAIL_IF_MACRO(f == NULL, NULL, 0); + retval = __PHYSFS_platformMkDir(f); + allocator.Free(f); + return(retval); +} /* DIR_mkdir */ + + +static void DIR_dirClose(dvoid *opaque) +{ + allocator.Free(opaque); +} /* DIR_dirClose */ + + + +const PHYSFS_ArchiveInfo __PHYSFS_ArchiveInfo_DIR = +{ + "", + DIR_ARCHIVE_DESCRIPTION, + "Ryan C. Gordon ", + "http://icculus.org/physfs/", +}; + + + +const PHYSFS_Archiver __PHYSFS_Archiver_DIR = +{ + &__PHYSFS_ArchiveInfo_DIR, + DIR_isArchive, /* isArchive() method */ + DIR_openArchive, /* openArchive() method */ + DIR_enumerateFiles, /* enumerateFiles() method */ + DIR_exists, /* exists() method */ + DIR_isDirectory, /* isDirectory() method */ + DIR_isSymLink, /* isSymLink() method */ + DIR_getLastModTime, /* getLastModTime() method */ + DIR_openRead, /* openRead() method */ + DIR_openWrite, /* openWrite() method */ + DIR_openAppend, /* openAppend() method */ + DIR_remove, /* remove() method */ + DIR_mkdir, /* mkdir() method */ + DIR_dirClose, /* dirClose() method */ + DIR_read, /* read() method */ + DIR_write, /* write() method */ + DIR_eof, /* eof() method */ + DIR_tell, /* tell() method */ + DIR_seek, /* seek() method */ + DIR_fileLength, /* fileLength() method */ + DIR_fileClose /* fileClose() method */ +}; + +/* end of dir.c ... */ + diff --git a/project/jni/physfs/archivers/grp.c b/project/jni/physfs/archivers/grp.c new file mode 100644 index 000000000..8c63f1ed7 --- /dev/null +++ b/project/jni/physfs/archivers/grp.c @@ -0,0 +1,475 @@ +/* + * 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. + */ + +#if (defined PHYSFS_SUPPORTS_GRP) + +#include +#include +#include +#include "physfs.h" + +#define __PHYSICSFS_INTERNAL__ +#include "physfs_internal.h" + +typedef struct +{ + char name[13]; + PHYSFS_uint32 startPos; + PHYSFS_uint32 size; +} GRPentry; + +typedef struct +{ + char *filename; + PHYSFS_sint64 last_mod_time; + PHYSFS_uint32 entryCount; + GRPentry *entries; +} GRPinfo; + +typedef struct +{ + void *handle; + GRPentry *entry; + PHYSFS_uint32 curPos; +} GRPfileinfo; + + +static void GRP_dirClose(dvoid *opaque) +{ + GRPinfo *info = ((GRPinfo *) opaque); + allocator.Free(info->filename); + allocator.Free(info->entries); + allocator.Free(info); +} /* GRP_dirClose */ + + +static PHYSFS_sint64 GRP_read(fvoid *opaque, void *buffer, + PHYSFS_uint32 objSize, PHYSFS_uint32 objCount) +{ + GRPfileinfo *finfo = (GRPfileinfo *) opaque; + GRPentry *entry = finfo->entry; + PHYSFS_uint32 bytesLeft = entry->size - finfo->curPos; + PHYSFS_uint32 objsLeft = (bytesLeft / objSize); + PHYSFS_sint64 rc; + + if (objsLeft < objCount) + objCount = objsLeft; + + rc = __PHYSFS_platformRead(finfo->handle, buffer, objSize, objCount); + if (rc > 0) + finfo->curPos += (PHYSFS_uint32) (rc * objSize); + + return(rc); +} /* GRP_read */ + + +static PHYSFS_sint64 GRP_write(fvoid *opaque, const void *buffer, + PHYSFS_uint32 objSize, PHYSFS_uint32 objCount) +{ + BAIL_MACRO(ERR_NOT_SUPPORTED, -1); +} /* GRP_write */ + + +static int GRP_eof(fvoid *opaque) +{ + GRPfileinfo *finfo = (GRPfileinfo *) opaque; + GRPentry *entry = finfo->entry; + return(finfo->curPos >= entry->size); +} /* GRP_eof */ + + +static PHYSFS_sint64 GRP_tell(fvoid *opaque) +{ + return(((GRPfileinfo *) opaque)->curPos); +} /* GRP_tell */ + + +static int GRP_seek(fvoid *opaque, PHYSFS_uint64 offset) +{ + GRPfileinfo *finfo = (GRPfileinfo *) opaque; + GRPentry *entry = finfo->entry; + int rc; + + BAIL_IF_MACRO(offset < 0, ERR_INVALID_ARGUMENT, 0); + BAIL_IF_MACRO(offset >= entry->size, ERR_PAST_EOF, 0); + rc = __PHYSFS_platformSeek(finfo->handle, entry->startPos + offset); + if (rc) + finfo->curPos = (PHYSFS_uint32) offset; + + return(rc); +} /* GRP_seek */ + + +static PHYSFS_sint64 GRP_fileLength(fvoid *opaque) +{ + GRPfileinfo *finfo = (GRPfileinfo *) opaque; + return((PHYSFS_sint64) finfo->entry->size); +} /* GRP_fileLength */ + + +static int GRP_fileClose(fvoid *opaque) +{ + GRPfileinfo *finfo = (GRPfileinfo *) opaque; + BAIL_IF_MACRO(!__PHYSFS_platformClose(finfo->handle), NULL, 0); + allocator.Free(finfo); + return(1); +} /* GRP_fileClose */ + + +static int grp_open(const char *filename, int forWriting, + void **fh, PHYSFS_uint32 *count) +{ + PHYSFS_uint8 buf[12]; + + *fh = NULL; + BAIL_IF_MACRO(forWriting, ERR_ARC_IS_READ_ONLY, 0); + + *fh = __PHYSFS_platformOpenRead(filename); + BAIL_IF_MACRO(*fh == NULL, NULL, 0); + + if (__PHYSFS_platformRead(*fh, buf, 12, 1) != 1) + goto openGrp_failed; + + if (memcmp(buf, "KenSilverman", 12) != 0) + { + __PHYSFS_setError(ERR_UNSUPPORTED_ARCHIVE); + goto openGrp_failed; + } /* if */ + + if (__PHYSFS_platformRead(*fh, count, sizeof (PHYSFS_uint32), 1) != 1) + goto openGrp_failed; + + *count = PHYSFS_swapULE32(*count); + + return(1); + +openGrp_failed: + if (*fh != NULL) + __PHYSFS_platformClose(*fh); + + *count = -1; + *fh = NULL; + return(0); +} /* grp_open */ + + +static int GRP_isArchive(const char *filename, int forWriting) +{ + void *fh; + PHYSFS_uint32 fileCount; + int retval = grp_open(filename, forWriting, &fh, &fileCount); + + if (fh != NULL) + __PHYSFS_platformClose(fh); + + return(retval); +} /* GRP_isArchive */ + + +static int grp_entry_cmp(void *_a, PHYSFS_uint32 one, PHYSFS_uint32 two) +{ + if (one != two) + { + const GRPentry *a = (const GRPentry *) _a; + return(strcmp(a[one].name, a[two].name)); + } /* if */ + + return 0; +} /* grp_entry_cmp */ + + +static void grp_entry_swap(void *_a, PHYSFS_uint32 one, PHYSFS_uint32 two) +{ + if (one != two) + { + GRPentry tmp; + GRPentry *first = &(((GRPentry *) _a)[one]); + GRPentry *second = &(((GRPentry *) _a)[two]); + memcpy(&tmp, first, sizeof (GRPentry)); + memcpy(first, second, sizeof (GRPentry)); + memcpy(second, &tmp, sizeof (GRPentry)); + } /* if */ +} /* grp_entry_swap */ + + +static int grp_load_entries(const char *name, int forWriting, GRPinfo *info) +{ + void *fh = NULL; + PHYSFS_uint32 fileCount; + PHYSFS_uint32 location = 16; /* sizeof sig. */ + GRPentry *entry; + char *ptr; + + BAIL_IF_MACRO(!grp_open(name, forWriting, &fh, &fileCount), NULL, 0); + info->entryCount = fileCount; + info->entries = (GRPentry *) allocator.Malloc(sizeof(GRPentry)*fileCount); + if (info->entries == NULL) + { + __PHYSFS_platformClose(fh); + BAIL_MACRO(ERR_OUT_OF_MEMORY, 0); + } /* if */ + + location += (16 * fileCount); + + for (entry = info->entries; fileCount > 0; fileCount--, entry++) + { + if (__PHYSFS_platformRead(fh, &entry->name, 12, 1) != 1) + { + __PHYSFS_platformClose(fh); + return(0); + } /* if */ + + entry->name[12] = '\0'; /* name isn't null-terminated in file. */ + if ((ptr = strchr(entry->name, ' ')) != NULL) + *ptr = '\0'; /* trim extra spaces. */ + + if (__PHYSFS_platformRead(fh, &entry->size, 4, 1) != 1) + { + __PHYSFS_platformClose(fh); + return(0); + } /* if */ + + entry->size = PHYSFS_swapULE32(entry->size); + entry->startPos = location; + location += entry->size; + } /* for */ + + __PHYSFS_platformClose(fh); + + __PHYSFS_sort(info->entries, info->entryCount, + grp_entry_cmp, grp_entry_swap); + return(1); +} /* grp_load_entries */ + + +static void *GRP_openArchive(const char *name, int forWriting) +{ + PHYSFS_sint64 modtime = __PHYSFS_platformGetLastModTime(name); + GRPinfo *info = (GRPinfo *) allocator.Malloc(sizeof (GRPinfo)); + + BAIL_IF_MACRO(info == NULL, ERR_OUT_OF_MEMORY, 0); + + memset(info, '\0', sizeof (GRPinfo)); + info->filename = (char *) allocator.Malloc(strlen(name) + 1); + GOTO_IF_MACRO(!info->filename, ERR_OUT_OF_MEMORY, GRP_openArchive_failed); + + if (!grp_load_entries(name, forWriting, info)) + goto GRP_openArchive_failed; + + strcpy(info->filename, name); + info->last_mod_time = modtime; + + return(info); + +GRP_openArchive_failed: + if (info != NULL) + { + if (info->filename != NULL) + allocator.Free(info->filename); + if (info->entries != NULL) + allocator.Free(info->entries); + allocator.Free(info); + } /* if */ + + return(NULL); +} /* GRP_openArchive */ + + +static void GRP_enumerateFiles(dvoid *opaque, const char *dname, + int omitSymLinks, PHYSFS_EnumFilesCallback cb, + const char *origdir, void *callbackdata) +{ + /* no directories in GRP files. */ + if (*dname == '\0') + { + GRPinfo *info = (GRPinfo *) opaque; + GRPentry *entry = info->entries; + PHYSFS_uint32 max = info->entryCount; + PHYSFS_uint32 i; + + for (i = 0; i < max; i++, entry++) + cb(callbackdata, origdir, entry->name); + } /* if */ +} /* GRP_enumerateFiles */ + + +static GRPentry *grp_find_entry(GRPinfo *info, const char *name) +{ + char *ptr = strchr(name, '.'); + GRPentry *a = info->entries; + PHYSFS_sint32 lo = 0; + PHYSFS_sint32 hi = (PHYSFS_sint32) (info->entryCount - 1); + PHYSFS_sint32 middle; + int rc; + + /* + * Rule out filenames to avoid unneeded processing...no dirs, + * big filenames, or extensions > 3 chars. + */ + BAIL_IF_MACRO((ptr) && (strlen(ptr) > 4), ERR_NO_SUCH_FILE, NULL); + BAIL_IF_MACRO(strlen(name) > 12, ERR_NO_SUCH_FILE, NULL); + BAIL_IF_MACRO(strchr(name, '/') != NULL, ERR_NO_SUCH_FILE, NULL); + + while (lo <= hi) + { + middle = lo + ((hi - lo) / 2); + rc = strcmp(name, a[middle].name); + if (rc == 0) /* found it! */ + return(&a[middle]); + else if (rc > 0) + lo = middle + 1; + else + hi = middle - 1; + } /* while */ + + BAIL_MACRO(ERR_NO_SUCH_FILE, NULL); +} /* grp_find_entry */ + + +static int GRP_exists(dvoid *opaque, const char *name) +{ + return(grp_find_entry((GRPinfo *) opaque, name) != NULL); +} /* GRP_exists */ + + +static int GRP_isDirectory(dvoid *opaque, const char *name, int *fileExists) +{ + *fileExists = GRP_exists(opaque, name); + return(0); /* never directories in a groupfile. */ +} /* GRP_isDirectory */ + + +static int GRP_isSymLink(dvoid *opaque, const char *name, int *fileExists) +{ + *fileExists = GRP_exists(opaque, name); + return(0); /* never symlinks in a groupfile. */ +} /* GRP_isSymLink */ + + +static PHYSFS_sint64 GRP_getLastModTime(dvoid *opaque, + const char *name, + int *fileExists) +{ + GRPinfo *info = (GRPinfo *) opaque; + PHYSFS_sint64 retval = -1; + + *fileExists = (grp_find_entry(info, name) != NULL); + if (*fileExists) /* use time of GRP itself in the physical filesystem. */ + retval = info->last_mod_time; + + return(retval); +} /* GRP_getLastModTime */ + + +static fvoid *GRP_openRead(dvoid *opaque, const char *fnm, int *fileExists) +{ + GRPinfo *info = (GRPinfo *) opaque; + GRPfileinfo *finfo; + GRPentry *entry; + + entry = grp_find_entry(info, fnm); + *fileExists = (entry != NULL); + BAIL_IF_MACRO(entry == NULL, NULL, NULL); + + finfo = (GRPfileinfo *) allocator.Malloc(sizeof (GRPfileinfo)); + BAIL_IF_MACRO(finfo == NULL, ERR_OUT_OF_MEMORY, NULL); + + finfo->handle = __PHYSFS_platformOpenRead(info->filename); + if ( (finfo->handle == NULL) || + (!__PHYSFS_platformSeek(finfo->handle, entry->startPos)) ) + { + allocator.Free(finfo); + return(NULL); + } /* if */ + + finfo->curPos = 0; + finfo->entry = entry; + return(finfo); +} /* GRP_openRead */ + + +static fvoid *GRP_openWrite(dvoid *opaque, const char *name) +{ + BAIL_MACRO(ERR_NOT_SUPPORTED, NULL); +} /* GRP_openWrite */ + + +static fvoid *GRP_openAppend(dvoid *opaque, const char *name) +{ + BAIL_MACRO(ERR_NOT_SUPPORTED, NULL); +} /* GRP_openAppend */ + + +static int GRP_remove(dvoid *opaque, const char *name) +{ + BAIL_MACRO(ERR_NOT_SUPPORTED, 0); +} /* GRP_remove */ + + +static int GRP_mkdir(dvoid *opaque, const char *name) +{ + BAIL_MACRO(ERR_NOT_SUPPORTED, 0); +} /* GRP_mkdir */ + + +const PHYSFS_ArchiveInfo __PHYSFS_ArchiveInfo_GRP = +{ + "GRP", + GRP_ARCHIVE_DESCRIPTION, + "Ryan C. Gordon ", + "http://icculus.org/physfs/", +}; + + +const PHYSFS_Archiver __PHYSFS_Archiver_GRP = +{ + &__PHYSFS_ArchiveInfo_GRP, + GRP_isArchive, /* isArchive() method */ + GRP_openArchive, /* openArchive() method */ + GRP_enumerateFiles, /* enumerateFiles() method */ + GRP_exists, /* exists() method */ + GRP_isDirectory, /* isDirectory() method */ + GRP_isSymLink, /* isSymLink() method */ + GRP_getLastModTime, /* getLastModTime() method */ + GRP_openRead, /* openRead() method */ + GRP_openWrite, /* openWrite() method */ + GRP_openAppend, /* openAppend() method */ + GRP_remove, /* remove() method */ + GRP_mkdir, /* mkdir() method */ + GRP_dirClose, /* dirClose() method */ + GRP_read, /* read() method */ + GRP_write, /* write() method */ + GRP_eof, /* eof() method */ + GRP_tell, /* tell() method */ + GRP_seek, /* seek() method */ + GRP_fileLength, /* fileLength() method */ + GRP_fileClose /* fileClose() method */ +}; + +#endif /* defined PHYSFS_SUPPORTS_GRP */ + +/* end of grp.c ... */ + diff --git a/project/jni/physfs/archivers/hog.c b/project/jni/physfs/archivers/hog.c new file mode 100644 index 000000000..43620f6fa --- /dev/null +++ b/project/jni/physfs/archivers/hog.c @@ -0,0 +1,514 @@ +/* + * 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. + */ + +#if (defined PHYSFS_SUPPORTS_HOG) + +#include +#include +#include +#include "physfs.h" + +#define __PHYSICSFS_INTERNAL__ +#include "physfs_internal.h" + +/* + * One HOGentry is kept for each file in an open HOG archive. + */ +typedef struct +{ + char name[13]; + PHYSFS_uint32 startPos; + PHYSFS_uint32 size; +} HOGentry; + +/* + * One HOGinfo is kept for each open HOG archive. + */ +typedef struct +{ + char *filename; + PHYSFS_sint64 last_mod_time; + PHYSFS_uint32 entryCount; + HOGentry *entries; +} HOGinfo; + +/* + * One HOGfileinfo is kept for each open file in a HOG archive. + */ +typedef struct +{ + void *handle; + HOGentry *entry; + PHYSFS_uint32 curPos; +} HOGfileinfo; + + +static void HOG_dirClose(dvoid *opaque) +{ + HOGinfo *info = ((HOGinfo *) opaque); + allocator.Free(info->filename); + allocator.Free(info->entries); + allocator.Free(info); +} /* HOG_dirClose */ + + +static PHYSFS_sint64 HOG_read(fvoid *opaque, void *buffer, + PHYSFS_uint32 objSize, PHYSFS_uint32 objCount) +{ + HOGfileinfo *finfo = (HOGfileinfo *) opaque; + HOGentry *entry = finfo->entry; + PHYSFS_uint32 bytesLeft = entry->size - finfo->curPos; + PHYSFS_uint32 objsLeft = (bytesLeft / objSize); + PHYSFS_sint64 rc; + + if (objsLeft < objCount) + objCount = objsLeft; + + rc = __PHYSFS_platformRead(finfo->handle, buffer, objSize, objCount); + if (rc > 0) + finfo->curPos += (PHYSFS_uint32) (rc * objSize); + + return(rc); +} /* HOG_read */ + + +static PHYSFS_sint64 HOG_write(fvoid *opaque, const void *buffer, + PHYSFS_uint32 objSize, PHYSFS_uint32 objCount) +{ + BAIL_MACRO(ERR_NOT_SUPPORTED, -1); +} /* HOG_write */ + + +static int HOG_eof(fvoid *opaque) +{ + HOGfileinfo *finfo = (HOGfileinfo *) opaque; + HOGentry *entry = finfo->entry; + return(finfo->curPos >= entry->size); +} /* HOG_eof */ + + +static PHYSFS_sint64 HOG_tell(fvoid *opaque) +{ + return(((HOGfileinfo *) opaque)->curPos); +} /* HOG_tell */ + + +static int HOG_seek(fvoid *opaque, PHYSFS_uint64 offset) +{ + HOGfileinfo *finfo = (HOGfileinfo *) opaque; + HOGentry *entry = finfo->entry; + int rc; + + BAIL_IF_MACRO(offset < 0, ERR_INVALID_ARGUMENT, 0); + BAIL_IF_MACRO(offset >= entry->size, ERR_PAST_EOF, 0); + rc = __PHYSFS_platformSeek(finfo->handle, entry->startPos + offset); + if (rc) + finfo->curPos = (PHYSFS_uint32) offset; + + return(rc); +} /* HOG_seek */ + + +static PHYSFS_sint64 HOG_fileLength(fvoid *opaque) +{ + HOGfileinfo *finfo = (HOGfileinfo *) opaque; + return((PHYSFS_sint64) finfo->entry->size); +} /* HOG_fileLength */ + + +static int HOG_fileClose(fvoid *opaque) +{ + HOGfileinfo *finfo = (HOGfileinfo *) opaque; + BAIL_IF_MACRO(!__PHYSFS_platformClose(finfo->handle), NULL, 0); + allocator.Free(finfo); + return(1); +} /* HOG_fileClose */ + + +static int hog_open(const char *filename, int forWriting, + void **fh, PHYSFS_uint32 *count) +{ + PHYSFS_uint8 buf[13]; + PHYSFS_uint32 size; + PHYSFS_sint64 pos; + + *count = 0; + + *fh = NULL; + BAIL_IF_MACRO(forWriting, ERR_ARC_IS_READ_ONLY, 0); + + *fh = __PHYSFS_platformOpenRead(filename); + BAIL_IF_MACRO(*fh == NULL, NULL, 0); + + if (__PHYSFS_platformRead(*fh, buf, 3, 1) != 1) + goto openHog_failed; + + if (memcmp(buf, "DHF", 3) != 0) + { + __PHYSFS_setError(ERR_UNSUPPORTED_ARCHIVE); + goto openHog_failed; + } /* if */ + + while (1) + { + if (__PHYSFS_platformRead(*fh, buf, 13, 1) != 1) + break; /* eof here is ok */ + + if (__PHYSFS_platformRead(*fh, &size, 4, 1) != 1) + goto openHog_failed; + + size = PHYSFS_swapULE32(size); + + (*count)++; + + /* Skip over entry... */ + pos = __PHYSFS_platformTell(*fh); + if (pos == -1) + goto openHog_failed; + if (!__PHYSFS_platformSeek(*fh, pos + size)) + goto openHog_failed; + } /* while */ + + /* Rewind to start of entries... */ + if (!__PHYSFS_platformSeek(*fh, 3)) + goto openHog_failed; + + return(1); + +openHog_failed: + if (*fh != NULL) + __PHYSFS_platformClose(*fh); + + *count = -1; + *fh = NULL; + return(0); +} /* hog_open */ + + +static int HOG_isArchive(const char *filename, int forWriting) +{ + void *fh; + PHYSFS_uint32 fileCount; + int retval = hog_open(filename, forWriting, &fh, &fileCount); + + if (fh != NULL) + __PHYSFS_platformClose(fh); + + return(retval); +} /* HOG_isArchive */ + + +static int hog_entry_cmp(void *_a, PHYSFS_uint32 one, PHYSFS_uint32 two) +{ + if (one != two) + { + const HOGentry *a = (const HOGentry *) _a; + return(__PHYSFS_stricmpASCII(a[one].name, a[two].name)); + } /* if */ + + return 0; +} /* hog_entry_cmp */ + + +static void hog_entry_swap(void *_a, PHYSFS_uint32 one, PHYSFS_uint32 two) +{ + if (one != two) + { + HOGentry tmp; + HOGentry *first = &(((HOGentry *) _a)[one]); + HOGentry *second = &(((HOGentry *) _a)[two]); + memcpy(&tmp, first, sizeof (HOGentry)); + memcpy(first, second, sizeof (HOGentry)); + memcpy(second, &tmp, sizeof (HOGentry)); + } /* if */ +} /* hog_entry_swap */ + + +static int hog_load_entries(const char *name, int forWriting, HOGinfo *info) +{ + void *fh = NULL; + PHYSFS_uint32 fileCount; + HOGentry *entry; + + BAIL_IF_MACRO(!hog_open(name, forWriting, &fh, &fileCount), NULL, 0); + info->entryCount = fileCount; + info->entries = (HOGentry *) allocator.Malloc(sizeof(HOGentry)*fileCount); + if (info->entries == NULL) + { + __PHYSFS_platformClose(fh); + BAIL_MACRO(ERR_OUT_OF_MEMORY, 0); + } /* if */ + + for (entry = info->entries; fileCount > 0; fileCount--, entry++) + { + if (__PHYSFS_platformRead(fh, &entry->name, 13, 1) != 1) + { + __PHYSFS_platformClose(fh); + return(0); + } /* if */ + + if (__PHYSFS_platformRead(fh, &entry->size, 4, 1) != 1) + { + __PHYSFS_platformClose(fh); + return(0); + } /* if */ + + entry->size = PHYSFS_swapULE32(entry->size); + entry->startPos = (unsigned int) __PHYSFS_platformTell(fh); + if (entry->startPos == -1) + { + __PHYSFS_platformClose(fh); + return(0); + } + + /* Skip over entry */ + if (!__PHYSFS_platformSeek(fh, entry->startPos + entry->size)) + { + __PHYSFS_platformClose(fh); + return(0); + } + } /* for */ + + __PHYSFS_platformClose(fh); + + __PHYSFS_sort(info->entries, info->entryCount, + hog_entry_cmp, hog_entry_swap); + return(1); +} /* hog_load_entries */ + + +static void *HOG_openArchive(const char *name, int forWriting) +{ + PHYSFS_sint64 modtime = __PHYSFS_platformGetLastModTime(name); + HOGinfo *info = (HOGinfo *) allocator.Malloc(sizeof (HOGinfo)); + + BAIL_IF_MACRO(info == NULL, ERR_OUT_OF_MEMORY, 0); + memset(info, '\0', sizeof (HOGinfo)); + info->filename = (char *) allocator.Malloc(strlen(name) + 1); + GOTO_IF_MACRO(!info->filename, ERR_OUT_OF_MEMORY, HOG_openArchive_failed); + + if (!hog_load_entries(name, forWriting, info)) + goto HOG_openArchive_failed; + + strcpy(info->filename, name); + info->last_mod_time = modtime; + + return(info); + +HOG_openArchive_failed: + if (info != NULL) + { + if (info->filename != NULL) + allocator.Free(info->filename); + if (info->entries != NULL) + allocator.Free(info->entries); + allocator.Free(info); + } /* if */ + + return(NULL); +} /* HOG_openArchive */ + + +static void HOG_enumerateFiles(dvoid *opaque, const char *dname, + int omitSymLinks, PHYSFS_EnumFilesCallback cb, + const char *origdir, void *callbackdata) +{ + /* no directories in HOG files. */ + if (*dname == '\0') + { + HOGinfo *info = (HOGinfo *) opaque; + HOGentry *entry = info->entries; + PHYSFS_uint32 max = info->entryCount; + PHYSFS_uint32 i; + + for (i = 0; i < max; i++, entry++) + cb(callbackdata, origdir, entry->name); + } /* if */ +} /* HOG_enumerateFiles */ + + +static HOGentry *hog_find_entry(HOGinfo *info, const char *name) +{ + char *ptr = strchr(name, '.'); + HOGentry *a = info->entries; + PHYSFS_sint32 lo = 0; + PHYSFS_sint32 hi = (PHYSFS_sint32) (info->entryCount - 1); + PHYSFS_sint32 middle; + int rc; + + /* + * Rule out filenames to avoid unneeded processing...no dirs, + * big filenames, or extensions > 3 chars. + */ + BAIL_IF_MACRO((ptr) && (strlen(ptr) > 4), ERR_NO_SUCH_FILE, NULL); + BAIL_IF_MACRO(strlen(name) > 12, ERR_NO_SUCH_FILE, NULL); + BAIL_IF_MACRO(strchr(name, '/') != NULL, ERR_NO_SUCH_FILE, NULL); + + while (lo <= hi) + { + middle = lo + ((hi - lo) / 2); + rc = __PHYSFS_stricmpASCII(name, a[middle].name); + if (rc == 0) /* found it! */ + return(&a[middle]); + else if (rc > 0) + lo = middle + 1; + else + hi = middle - 1; + } /* while */ + + BAIL_MACRO(ERR_NO_SUCH_FILE, NULL); +} /* hog_find_entry */ + + +static int HOG_exists(dvoid *opaque, const char *name) +{ + return(hog_find_entry(((HOGinfo *) opaque), name) != NULL); +} /* HOG_exists */ + + +static int HOG_isDirectory(dvoid *opaque, const char *name, int *fileExists) +{ + *fileExists = HOG_exists(opaque, name); + return(0); /* never directories in a groupfile. */ +} /* HOG_isDirectory */ + + +static int HOG_isSymLink(dvoid *opaque, const char *name, int *fileExists) +{ + *fileExists = HOG_exists(opaque, name); + return(0); /* never symlinks in a groupfile. */ +} /* HOG_isSymLink */ + + +static PHYSFS_sint64 HOG_getLastModTime(dvoid *opaque, + const char *name, + int *fileExists) +{ + HOGinfo *info = ((HOGinfo *) opaque); + PHYSFS_sint64 retval = -1; + + *fileExists = (hog_find_entry(info, name) != NULL); + if (*fileExists) /* use time of HOG itself in the physical filesystem. */ + retval = info->last_mod_time; + + return(retval); +} /* HOG_getLastModTime */ + + +static fvoid *HOG_openRead(dvoid *opaque, const char *fnm, int *fileExists) +{ + HOGinfo *info = ((HOGinfo *) opaque); + HOGfileinfo *finfo; + HOGentry *entry; + + entry = hog_find_entry(info, fnm); + *fileExists = (entry != NULL); + BAIL_IF_MACRO(entry == NULL, NULL, NULL); + + finfo = (HOGfileinfo *) allocator.Malloc(sizeof (HOGfileinfo)); + BAIL_IF_MACRO(finfo == NULL, ERR_OUT_OF_MEMORY, NULL); + + finfo->handle = __PHYSFS_platformOpenRead(info->filename); + if ( (finfo->handle == NULL) || + (!__PHYSFS_platformSeek(finfo->handle, entry->startPos)) ) + { + allocator.Free(finfo); + return(NULL); + } /* if */ + + finfo->curPos = 0; + finfo->entry = entry; + return(finfo); +} /* HOG_openRead */ + + +static fvoid *HOG_openWrite(dvoid *opaque, const char *name) +{ + BAIL_MACRO(ERR_NOT_SUPPORTED, NULL); +} /* HOG_openWrite */ + + +static fvoid *HOG_openAppend(dvoid *opaque, const char *name) +{ + BAIL_MACRO(ERR_NOT_SUPPORTED, NULL); +} /* HOG_openAppend */ + + +static int HOG_remove(dvoid *opaque, const char *name) +{ + BAIL_MACRO(ERR_NOT_SUPPORTED, 0); +} /* HOG_remove */ + + +static int HOG_mkdir(dvoid *opaque, const char *name) +{ + BAIL_MACRO(ERR_NOT_SUPPORTED, 0); +} /* HOG_mkdir */ + + +const PHYSFS_ArchiveInfo __PHYSFS_ArchiveInfo_HOG = +{ + "HOG", + HOG_ARCHIVE_DESCRIPTION, + "Bradley Bell ", + "http://icculus.org/physfs/", +}; + + +const PHYSFS_Archiver __PHYSFS_Archiver_HOG = +{ + &__PHYSFS_ArchiveInfo_HOG, + HOG_isArchive, /* isArchive() method */ + HOG_openArchive, /* openArchive() method */ + HOG_enumerateFiles, /* enumerateFiles() method */ + HOG_exists, /* exists() method */ + HOG_isDirectory, /* isDirectory() method */ + HOG_isSymLink, /* isSymLink() method */ + HOG_getLastModTime, /* getLastModTime() method */ + HOG_openRead, /* openRead() method */ + HOG_openWrite, /* openWrite() method */ + HOG_openAppend, /* openAppend() method */ + HOG_remove, /* remove() method */ + HOG_mkdir, /* mkdir() method */ + HOG_dirClose, /* dirClose() method */ + HOG_read, /* read() method */ + HOG_write, /* write() method */ + HOG_eof, /* eof() method */ + HOG_tell, /* tell() method */ + HOG_seek, /* seek() method */ + HOG_fileLength, /* fileLength() method */ + HOG_fileClose /* fileClose() method */ +}; + +#endif /* defined PHYSFS_SUPPORTS_HOG */ + +/* end of hog.c ... */ + diff --git a/project/jni/physfs/archivers/lzma.c b/project/jni/physfs/archivers/lzma.c new file mode 100644 index 000000000..dfdefc180 --- /dev/null +++ b/project/jni/physfs/archivers/lzma.c @@ -0,0 +1,736 @@ +/* + * LZMA support routines for PhysicsFS. + * + * Please see the file lzma.txt in the lzma/ directory. + * + * This file was written by Dennis Schridde, with some peeking at "7zMain.c" + * by Igor Pavlov. + */ + +#if (defined PHYSFS_SUPPORTS_7Z) + +#include +#include +#include + +#include "physfs.h" + +#define __PHYSICSFS_INTERNAL__ +#include "physfs_internal.h" + +#include "lzma/C/7zCrc.h" +#include "lzma/C/Archive/7z/7zIn.h" +#include "lzma/C/Archive/7z/7zExtract.h" + + +/* 7z internal from 7zIn.c */ +extern int TestSignatureCandidate(Byte *testBytes); + + +#ifdef _LZMA_IN_CB +# define BUFFER_SIZE (1 << 12) +#endif /* _LZMA_IN_CB */ + + +/* + * Carries filestream metadata through 7z + */ +typedef struct _FileInputStream +{ + ISzAlloc allocImp; /* Allocation implementation, used by 7z */ + ISzAlloc allocTempImp; /* Temporary allocation implementation, used by 7z */ + ISzInStream inStream; /* Input stream with read callbacks, used by 7z */ + void *file; /* Filehandle, used by read implementation */ +#ifdef _LZMA_IN_CB + Byte buffer[BUFFER_SIZE]; /* Buffer, used by read implementation */ +#endif /* _LZMA_IN_CB */ +} FileInputStream; + +/* + * In the 7z format archives are splited into blocks, those are called folders + * Set by LZMA_read() +*/ +typedef struct _LZMAfolder +{ + PHYSFS_uint32 index; /* Index of folder in archive */ + PHYSFS_uint32 references; /* Number of files using this block */ + PHYSFS_uint8 *cache; /* Cached folder */ + size_t size; /* Size of folder */ +} LZMAfolder; + +/* + * Set by LZMA_openArchive(), except folder which gets it's values + * in LZMA_read() + */ +typedef struct _LZMAarchive +{ + struct _LZMAfile *files; /* Array of files, size == archive->db.Database.NumFiles */ + LZMAfolder *folders; /* Array of folders, size == archive->db.Database.NumFolders */ + CArchiveDatabaseEx db; /* For 7z: Database */ + FileInputStream stream; /* For 7z: Input file incl. read and seek callbacks */ +} LZMAarchive; + +/* Set by LZMA_openArchive(), except offset which is set by LZMA_read() */ +typedef struct _LZMAfile +{ + PHYSFS_uint32 index; /* Index of file in archive */ + LZMAarchive *archive; /* Link to corresponding archive */ + LZMAfolder *folder; /* Link to corresponding folder */ + CFileItem *item; /* For 7z: File info, eg. name, size */ + size_t offset; /* Offset in folder */ + size_t position; /* Current "virtual" position in file */ +} LZMAfile; + + +/* Memory management implementations to be passed to 7z */ + +static void *SzAllocPhysicsFS(size_t size) +{ + return ((size == 0) ? NULL : allocator.Malloc(size)); +} /* SzAllocPhysicsFS */ + + +static void SzFreePhysicsFS(void *address) +{ + if (address != NULL) + allocator.Free(address); +} /* SzFreePhysicsFS */ + + +/* Filesystem implementations to be passed to 7z */ + +#ifdef _LZMA_IN_CB + +/* + * Read implementation, to be passed to 7z + * WARNING: If the ISzInStream in 'object' is not contained in a valid FileInputStream this _will_ break horribly! + */ +SZ_RESULT SzFileReadImp(void *object, void **buffer, size_t maxReqSize, + size_t *processedSize) +{ + FileInputStream *s = (FileInputStream *)(object - offsetof(FileInputStream, inStream)); /* HACK! */ + PHYSFS_sint64 processedSizeLoc = 0; + + if (maxReqSize > BUFFER_SIZE) + maxReqSize = BUFFER_SIZE; + processedSizeLoc = __PHYSFS_platformRead(s->file, s->buffer, 1, maxReqSize); + *buffer = s->buffer; + if (processedSize != NULL) + *processedSize = (size_t) processedSizeLoc; + + return SZ_OK; +} /* SzFileReadImp */ + +#else + +/* + * Read implementation, to be passed to 7z + * WARNING: If the ISzInStream in 'object' is not contained in a valid FileInputStream this _will_ break horribly! + */ +SZ_RESULT SzFileReadImp(void *object, void *buffer, size_t size, + size_t *processedSize) +{ + FileInputStream *s = (FileInputStream *)((unsigned long)object - offsetof(FileInputStream, inStream)); /* HACK! */ + size_t processedSizeLoc = __PHYSFS_platformRead(s->file, buffer, 1, size); + if (processedSize != 0) + *processedSize = processedSizeLoc; + return SZ_OK; +} /* SzFileReadImp */ + +#endif + +/* + * Seek implementation, to be passed to 7z + * WARNING: If the ISzInStream in 'object' is not contained in a valid FileInputStream this _will_ break horribly! + */ +SZ_RESULT SzFileSeekImp(void *object, CFileSize pos) +{ + FileInputStream *s = (FileInputStream *)((unsigned long)object - offsetof(FileInputStream, inStream)); /* HACK! */ + if (__PHYSFS_platformSeek(s->file, (PHYSFS_uint64) pos)) + return SZ_OK; + return SZE_FAIL; +} /* SzFileSeekImp */ + + +/* + * Translate Microsoft FILETIME (used by 7zip) into UNIX timestamp + */ +static PHYSFS_sint64 lzma_filetime_to_unix_timestamp(CArchiveFileTime *ft) +{ + /* MS counts in nanoseconds ... */ + const PHYSFS_uint64 FILETIME_NANOTICKS_PER_SECOND = __PHYSFS_UI64(10000000); + /* MS likes to count seconds since 01.01.1601 ... */ + const PHYSFS_uint64 FILETIME_UNIX_DIFF = __PHYSFS_UI64(11644473600); + + PHYSFS_uint64 filetime = ft->Low | ((PHYSFS_uint64)ft->High << 32); + return filetime/FILETIME_NANOTICKS_PER_SECOND - FILETIME_UNIX_DIFF; +} /* lzma_filetime_to_unix_timestamp */ + + +/* + * Compare a file with a given name, C89 stdlib variant + * Used for sorting + */ +static int lzma_file_cmp_stdlib(const void *key, const void *object) +{ + const char *name = (const char *) key; + LZMAfile *file = (LZMAfile *) object; + return(strcmp(name, file->item->Name)); +} /* lzma_file_cmp_posix */ + + +/* + * Compare two files with each other based on the name + * Used for sorting + */ +static int lzma_file_cmp(void *_a, PHYSFS_uint32 one, PHYSFS_uint32 two) +{ + LZMAfile *files = (LZMAfile *) _a; + return(strcmp(files[one].item->Name, files[two].item->Name)); +} /* lzma_file_cmp */ + + +/* + * Swap two entries in the file array + */ +static void lzma_file_swap(void *_a, PHYSFS_uint32 one, PHYSFS_uint32 two) +{ + LZMAfile tmp; + LZMAfile *first = &(((LZMAfile *) _a)[one]); + LZMAfile *second = &(((LZMAfile *) _a)[two]); + memcpy(&tmp, first, sizeof (LZMAfile)); + memcpy(first, second, sizeof (LZMAfile)); + memcpy(second, &tmp, sizeof (LZMAfile)); +} /* lzma_file_swap */ + + +/* + * Find entry 'name' in 'archive' + */ +static LZMAfile * lzma_find_file(LZMAarchive *archive, const char *name) +{ + LZMAfile *file = bsearch(name, archive->files, archive->db.Database.NumFiles, sizeof(*archive->files), lzma_file_cmp_stdlib); /* FIXME: Should become __PHYSFS_search!!! */ + + BAIL_IF_MACRO(file == NULL, ERR_NO_SUCH_FILE, NULL); + + return(file); +} /* lzma_find_file */ + + +/* + * Load metadata for the file at given index + */ +static int lzma_file_init(LZMAarchive *archive, PHYSFS_uint32 fileIndex) +{ + LZMAfile *file = &archive->files[fileIndex]; + PHYSFS_uint32 folderIndex = archive->db.FileIndexToFolderIndexMap[fileIndex]; + + file->index = fileIndex; /* Store index into 7z array, since we sort our own. */ + file->archive = archive; + file->folder = (folderIndex != (PHYSFS_uint32)-1 ? &archive->folders[folderIndex] : NULL); /* Directories don't have a folder (they contain no own data...) */ + file->item = &archive->db.Database.Files[fileIndex]; /* Holds crucial data and is often referenced -> Store link */ + file->position = 0; + file->offset = 0; /* Offset will be set by LZMA_read() */ + + return(1); +} /* lzma_load_file */ + + +/* + * Load metadata for all files + */ +static int lzma_files_init(LZMAarchive *archive) +{ + PHYSFS_uint32 fileIndex = 0, numFiles = archive->db.Database.NumFiles; + + for (fileIndex = 0; fileIndex < numFiles; fileIndex++ ) + { + if (!lzma_file_init(archive, fileIndex)) + { + return(0); /* FALSE on failure */ + } + } /* for */ + + __PHYSFS_sort(archive->files, numFiles, lzma_file_cmp, lzma_file_swap); + + return(1); +} /* lzma_load_files */ + + +/* + * Initialise specified archive + */ +static void lzma_archive_init(LZMAarchive *archive) +{ + memset(archive, 0, sizeof(*archive)); + + /* Prepare callbacks for 7z */ + archive->stream.inStream.Read = SzFileReadImp; + archive->stream.inStream.Seek = SzFileSeekImp; + + archive->stream.allocImp.Alloc = SzAllocPhysicsFS; + archive->stream.allocImp.Free = SzFreePhysicsFS; + + archive->stream.allocTempImp.Alloc = SzAllocPhysicsFS; + archive->stream.allocTempImp.Free = SzFreePhysicsFS; +} + + +/* + * Deinitialise archive + */ +static void lzma_archive_exit(LZMAarchive *archive) +{ + /* Free arrays */ + allocator.Free(archive->folders); + allocator.Free(archive->files); + allocator.Free(archive); +} + +/* + * Wrap all 7z calls in this, so the physfs error state is set appropriately. + */ +static int lzma_err(SZ_RESULT rc) +{ + switch (rc) + { + case SZ_OK: /* Same as LZMA_RESULT_OK */ + break; + case SZE_DATA_ERROR: /* Same as LZMA_RESULT_DATA_ERROR */ + __PHYSFS_setError(ERR_DATA_ERROR); + break; + case SZE_OUTOFMEMORY: + __PHYSFS_setError(ERR_OUT_OF_MEMORY); + break; + case SZE_CRC_ERROR: + __PHYSFS_setError(ERR_CORRUPTED); + break; + case SZE_NOTIMPL: + __PHYSFS_setError(ERR_NOT_IMPLEMENTED); + break; + case SZE_FAIL: + __PHYSFS_setError(ERR_UNKNOWN_ERROR); /* !!! FIXME: right? */ + break; + case SZE_ARCHIVE_ERROR: + __PHYSFS_setError(ERR_CORRUPTED); /* !!! FIXME: right? */ + break; + default: + __PHYSFS_setError(ERR_UNKNOWN_ERROR); + } /* switch */ + + return(rc); +} /* lzma_err */ + + +static PHYSFS_sint64 LZMA_read(fvoid *opaque, void *outBuffer, + PHYSFS_uint32 objSize, PHYSFS_uint32 objCount) +{ + LZMAfile *file = (LZMAfile *) opaque; + + size_t wantedSize = objSize*objCount; + size_t remainingSize = file->item->Size - file->position; + size_t fileSize = 0; + + BAIL_IF_MACRO(wantedSize == 0, NULL, 0); /* quick rejection. */ + BAIL_IF_MACRO(remainingSize == 0, ERR_PAST_EOF, 0); + + if (remainingSize < wantedSize) + { + wantedSize = remainingSize - (remainingSize % objSize); + objCount = (PHYSFS_uint32) (remainingSize / objSize); + BAIL_IF_MACRO(objCount == 0, ERR_PAST_EOF, 0); /* quick rejection. */ + __PHYSFS_setError(ERR_PAST_EOF); /* this is always true here. */ + } /* if */ + + /* Only decompress the folder if it is not allready cached */ + if (file->folder->cache == NULL) + { + int rc = lzma_err(SzExtract( + &file->archive->stream.inStream, /* compressed data */ + &file->archive->db, /* 7z's database, containing everything */ + file->index, /* Index into database arrays */ + /* Index of cached folder, will be changed by SzExtract */ + &file->folder->index, + /* Cache for decompressed folder, allocated/freed by SzExtract */ + &file->folder->cache, + /* Size of cache, will be changed by SzExtract */ + &file->folder->size, + /* Offset of this file inside the cache, set by SzExtract */ + &file->offset, + &fileSize, /* Size of this file */ + &file->archive->stream.allocImp, + &file->archive->stream.allocTempImp)); + + if (rc != SZ_OK) + return -1; + } /* if */ + + /* Copy wanted bytes over from cache to outBuffer */ + memcpy(outBuffer, + (file->folder->cache + + file->offset + file->position), + wantedSize); + file->position += wantedSize; /* Increase virtual position */ + + return objCount; +} /* LZMA_read */ + + +static PHYSFS_sint64 LZMA_write(fvoid *opaque, const void *buf, + PHYSFS_uint32 objSize, PHYSFS_uint32 objCount) +{ + BAIL_MACRO(ERR_NOT_SUPPORTED, -1); +} /* LZMA_write */ + + +static int LZMA_eof(fvoid *opaque) +{ + LZMAfile *file = (LZMAfile *) opaque; + return (file->position >= file->item->Size); +} /* LZMA_eof */ + + +static PHYSFS_sint64 LZMA_tell(fvoid *opaque) +{ + LZMAfile *file = (LZMAfile *) opaque; + return (file->position); +} /* LZMA_tell */ + + +static int LZMA_seek(fvoid *opaque, PHYSFS_uint64 offset) +{ + LZMAfile *file = (LZMAfile *) opaque; + + BAIL_IF_MACRO(offset < 0, ERR_SEEK_OUT_OF_RANGE, 0); + BAIL_IF_MACRO(offset > file->item->Size, ERR_PAST_EOF, 0); + + file->position = offset; /* We only use a virtual position... */ + + return 1; +} /* LZMA_seek */ + + +static PHYSFS_sint64 LZMA_fileLength(fvoid *opaque) +{ + LZMAfile *file = (LZMAfile *) opaque; + return (file->item->Size); +} /* LZMA_fileLength */ + + +static int LZMA_fileClose(fvoid *opaque) +{ + LZMAfile *file = (LZMAfile *) opaque; + + BAIL_IF_MACRO(file->folder == NULL, ERR_NOT_A_FILE, 0); + + /* Only decrease refcount if someone actually requested this file... Prevents from overflows and close-on-open... */ + if (file->folder->references > 0) + file->folder->references--; + if (file->folder->references == 0) + { + /* Free the cache which might have been allocated by LZMA_read() */ + allocator.Free(file->folder->cache); + file->folder->cache = NULL; + } + + return(1); +} /* LZMA_fileClose */ + + +static int LZMA_isArchive(const char *filename, int forWriting) +{ + PHYSFS_uint8 sig[k7zSignatureSize]; + void *in; + + BAIL_IF_MACRO(forWriting, ERR_ARC_IS_READ_ONLY, 0); + + in = __PHYSFS_platformOpenRead(filename); + BAIL_IF_MACRO(in == NULL, NULL, 0); + + /* Read signature bytes */ + if (__PHYSFS_platformRead(in, sig, k7zSignatureSize, 1) != 1) + { + __PHYSFS_platformClose(in); /* Don't forget to close the file before returning... */ + BAIL_MACRO(NULL, 0); + } + + __PHYSFS_platformClose(in); + + /* Test whether sig is the 7z signature */ + return(TestSignatureCandidate(sig)); +} /* LZMA_isArchive */ + + +static void *LZMA_openArchive(const char *name, int forWriting) +{ + size_t len = 0; + LZMAarchive *archive = NULL; + + BAIL_IF_MACRO(forWriting, ERR_ARC_IS_READ_ONLY, NULL); + BAIL_IF_MACRO(!LZMA_isArchive(name,forWriting), ERR_UNSUPPORTED_ARCHIVE, 0); + + archive = (LZMAarchive *) allocator.Malloc(sizeof (LZMAarchive)); + BAIL_IF_MACRO(archive == NULL, ERR_OUT_OF_MEMORY, NULL); + + lzma_archive_init(archive); + + if ( (archive->stream.file = __PHYSFS_platformOpenRead(name)) == NULL ) + { + __PHYSFS_platformClose(archive->stream.file); + lzma_archive_exit(archive); + return(NULL); /* Error is set by platformOpenRead! */ + } + + CrcGenerateTable(); + SzArDbExInit(&archive->db); + if (lzma_err(SzArchiveOpen(&archive->stream.inStream, + &archive->db, + &archive->stream.allocImp, + &archive->stream.allocTempImp)) != SZ_OK) + { + SzArDbExFree(&archive->db, SzFreePhysicsFS); + __PHYSFS_platformClose(archive->stream.file); + lzma_archive_exit(archive); + return NULL; /* Error is set by lzma_err! */ + } /* if */ + + len = archive->db.Database.NumFiles * sizeof (LZMAfile); + archive->files = (LZMAfile *) allocator.Malloc(len); + if (archive->files == NULL) + { + SzArDbExFree(&archive->db, SzFreePhysicsFS); + __PHYSFS_platformClose(archive->stream.file); + lzma_archive_exit(archive); + BAIL_MACRO(ERR_OUT_OF_MEMORY, NULL); + } + + /* + * Init with 0 so we know when a folder is already cached + * Values will be set by LZMA_openRead() + */ + memset(archive->files, 0, len); + + len = archive->db.Database.NumFolders * sizeof (LZMAfolder); + archive->folders = (LZMAfolder *) allocator.Malloc(len); + if (archive->folders == NULL) + { + SzArDbExFree(&archive->db, SzFreePhysicsFS); + __PHYSFS_platformClose(archive->stream.file); + lzma_archive_exit(archive); + BAIL_MACRO(ERR_OUT_OF_MEMORY, NULL); + } + + /* + * Init with 0 so we know when a folder is already cached + * Values will be set by LZMA_read() + */ + memset(archive->folders, 0, len); + + if(!lzma_files_init(archive)) + { + SzArDbExFree(&archive->db, SzFreePhysicsFS); + __PHYSFS_platformClose(archive->stream.file); + lzma_archive_exit(archive); + BAIL_MACRO(ERR_UNKNOWN_ERROR, NULL); + } + + return(archive); +} /* LZMA_openArchive */ + + +/* + * Moved to seperate function so we can use alloca then immediately throw + * away the allocated stack space... + */ +static void doEnumCallback(PHYSFS_EnumFilesCallback cb, void *callbackdata, + const char *odir, const char *str, size_t flen) +{ + char *newstr = __PHYSFS_smallAlloc(flen + 1); + if (newstr == NULL) + return; + + memcpy(newstr, str, flen); + newstr[flen] = '\0'; + cb(callbackdata, odir, newstr); + __PHYSFS_smallFree(newstr); +} /* doEnumCallback */ + + +static void LZMA_enumerateFiles(dvoid *opaque, const char *dname, + int omitSymLinks, PHYSFS_EnumFilesCallback cb, + const char *origdir, void *callbackdata) +{ + size_t dlen = strlen(dname), + dlen_inc = dlen + ((dlen > 0) ? 1 : 0); + LZMAarchive *archive = (LZMAarchive *) opaque; + LZMAfile *file = NULL, + *lastFile = &archive->files[archive->db.Database.NumFiles]; + if (dlen) + { + file = lzma_find_file(archive, dname); + if (file != NULL) /* if 'file' is NULL it should stay so, otherwise errors will not be handled */ + file += 1; + } + else + { + file = archive->files; + } + + BAIL_IF_MACRO(file == NULL, ERR_NO_SUCH_FILE, ); + + while (file < lastFile) + { + const char * fname = file->item->Name; + const char * dirNameEnd = fname + dlen_inc; + + if (strncmp(dname, fname, dlen) != 0) /* Stop after mismatch, archive->files is sorted */ + break; + + if (strchr(dirNameEnd, '/')) /* Skip subdirs */ + { + file++; + continue; + } + + /* Do the actual callback... */ + doEnumCallback(cb, callbackdata, origdir, dirNameEnd, strlen(dirNameEnd)); + + file++; + } +} /* LZMA_enumerateFiles */ + + +static int LZMA_exists(dvoid *opaque, const char *name) +{ + LZMAarchive *archive = (LZMAarchive *) opaque; + return(lzma_find_file(archive, name) != NULL); +} /* LZMA_exists */ + + +static PHYSFS_sint64 LZMA_getLastModTime(dvoid *opaque, + const char *name, + int *fileExists) +{ + LZMAarchive *archive = (LZMAarchive *) opaque; + LZMAfile *file = lzma_find_file(archive, name); + + *fileExists = (file != NULL); + + BAIL_IF_MACRO(file == NULL, NULL, -1); + BAIL_IF_MACRO(!file->item->IsLastWriteTimeDefined, NULL, -1); /* write-time may not be defined for every file */ + + return(lzma_filetime_to_unix_timestamp(&file->item->LastWriteTime)); +} /* LZMA_getLastModTime */ + + +static int LZMA_isDirectory(dvoid *opaque, const char *name, int *fileExists) +{ + LZMAarchive *archive = (LZMAarchive *) opaque; + LZMAfile *file = lzma_find_file(archive, name); + + *fileExists = (file != NULL); + + return(file == NULL ? 0 : file->item->IsDirectory); +} /* LZMA_isDirectory */ + + +static int LZMA_isSymLink(dvoid *opaque, const char *name, int *fileExists) +{ + BAIL_MACRO(ERR_NOT_SUPPORTED, 0); +} /* LZMA_isSymLink */ + + +static fvoid *LZMA_openRead(dvoid *opaque, const char *name, int *fileExists) +{ + LZMAarchive *archive = (LZMAarchive *) opaque; + LZMAfile *file = lzma_find_file(archive, name); + + *fileExists = (file != NULL); + BAIL_IF_MACRO(file == NULL, ERR_NO_SUCH_FILE, NULL); + BAIL_IF_MACRO(file->folder == NULL, ERR_NOT_A_FILE, NULL); + + file->position = 0; + file->folder->references++; /* Increase refcount for automatic cleanup... */ + + return(file); +} /* LZMA_openRead */ + + +static fvoid *LZMA_openWrite(dvoid *opaque, const char *filename) +{ + BAIL_MACRO(ERR_NOT_SUPPORTED, NULL); +} /* LZMA_openWrite */ + + +static fvoid *LZMA_openAppend(dvoid *opaque, const char *filename) +{ + BAIL_MACRO(ERR_NOT_SUPPORTED, NULL); +} /* LZMA_openAppend */ + + +static void LZMA_dirClose(dvoid *opaque) +{ + LZMAarchive *archive = (LZMAarchive *) opaque; + PHYSFS_uint32 fileIndex = 0, numFiles = archive->db.Database.NumFiles; + + for (fileIndex = 0; fileIndex < numFiles; fileIndex++) + { + LZMA_fileClose(&archive->files[fileIndex]); + } /* for */ + + SzArDbExFree(&archive->db, SzFreePhysicsFS); + __PHYSFS_platformClose(archive->stream.file); + lzma_archive_exit(archive); +} /* LZMA_dirClose */ + + +static int LZMA_remove(dvoid *opaque, const char *name) +{ + BAIL_MACRO(ERR_NOT_SUPPORTED, 0); +} /* LZMA_remove */ + + +static int LZMA_mkdir(dvoid *opaque, const char *name) +{ + BAIL_MACRO(ERR_NOT_SUPPORTED, 0); +} /* LZMA_mkdir */ + + +const PHYSFS_ArchiveInfo __PHYSFS_ArchiveInfo_LZMA = +{ + "7Z", + LZMA_ARCHIVE_DESCRIPTION, + "Dennis Schridde ", + "http://icculus.org/physfs/", +}; + + +const PHYSFS_Archiver __PHYSFS_Archiver_LZMA = +{ + &__PHYSFS_ArchiveInfo_LZMA, + LZMA_isArchive, /* isArchive() method */ + LZMA_openArchive, /* openArchive() method */ + LZMA_enumerateFiles, /* enumerateFiles() method */ + LZMA_exists, /* exists() method */ + LZMA_isDirectory, /* isDirectory() method */ + LZMA_isSymLink, /* isSymLink() method */ + LZMA_getLastModTime, /* getLastModTime() method */ + LZMA_openRead, /* openRead() method */ + LZMA_openWrite, /* openWrite() method */ + LZMA_openAppend, /* openAppend() method */ + LZMA_remove, /* remove() method */ + LZMA_mkdir, /* mkdir() method */ + LZMA_dirClose, /* dirClose() method */ + LZMA_read, /* read() method */ + LZMA_write, /* write() method */ + LZMA_eof, /* eof() method */ + LZMA_tell, /* tell() method */ + LZMA_seek, /* seek() method */ + LZMA_fileLength, /* fileLength() method */ + LZMA_fileClose /* fileClose() method */ +}; + +#endif /* defined PHYSFS_SUPPORTS_7Z */ + +/* end of lzma.c ... */ + diff --git a/project/jni/physfs/archivers/mvl.c b/project/jni/physfs/archivers/mvl.c new file mode 100644 index 000000000..8fbda239d --- /dev/null +++ b/project/jni/physfs/archivers/mvl.c @@ -0,0 +1,471 @@ +/* + * 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. + */ + +#if (defined PHYSFS_SUPPORTS_MVL) + +#include +#include +#include +#include "physfs.h" + +#define __PHYSICSFS_INTERNAL__ +#include "physfs_internal.h" + +typedef struct +{ + char name[13]; + PHYSFS_uint32 startPos; + PHYSFS_uint32 size; +} MVLentry; + +typedef struct +{ + char *filename; + PHYSFS_sint64 last_mod_time; + PHYSFS_uint32 entryCount; + MVLentry *entries; +} MVLinfo; + +typedef struct +{ + void *handle; + MVLentry *entry; + PHYSFS_uint32 curPos; +} MVLfileinfo; + + +static void MVL_dirClose(dvoid *opaque) +{ + MVLinfo *info = ((MVLinfo *) opaque); + allocator.Free(info->filename); + allocator.Free(info->entries); + allocator.Free(info); +} /* MVL_dirClose */ + + +static PHYSFS_sint64 MVL_read(fvoid *opaque, void *buffer, + PHYSFS_uint32 objSize, PHYSFS_uint32 objCount) +{ + MVLfileinfo *finfo = (MVLfileinfo *) opaque; + MVLentry *entry = finfo->entry; + PHYSFS_uint32 bytesLeft = entry->size - finfo->curPos; + PHYSFS_uint32 objsLeft = (bytesLeft / objSize); + PHYSFS_sint64 rc; + + if (objsLeft < objCount) + objCount = objsLeft; + + rc = __PHYSFS_platformRead(finfo->handle, buffer, objSize, objCount); + if (rc > 0) + finfo->curPos += (PHYSFS_uint32) (rc * objSize); + + return(rc); +} /* MVL_read */ + + +static PHYSFS_sint64 MVL_write(fvoid *opaque, const void *buffer, + PHYSFS_uint32 objSize, PHYSFS_uint32 objCount) +{ + BAIL_MACRO(ERR_NOT_SUPPORTED, -1); +} /* MVL_write */ + + +static int MVL_eof(fvoid *opaque) +{ + MVLfileinfo *finfo = (MVLfileinfo *) opaque; + MVLentry *entry = finfo->entry; + return(finfo->curPos >= entry->size); +} /* MVL_eof */ + + +static PHYSFS_sint64 MVL_tell(fvoid *opaque) +{ + return(((MVLfileinfo *) opaque)->curPos); +} /* MVL_tell */ + + +static int MVL_seek(fvoid *opaque, PHYSFS_uint64 offset) +{ + MVLfileinfo *finfo = (MVLfileinfo *) opaque; + MVLentry *entry = finfo->entry; + int rc; + + BAIL_IF_MACRO(offset < 0, ERR_INVALID_ARGUMENT, 0); + BAIL_IF_MACRO(offset >= entry->size, ERR_PAST_EOF, 0); + rc = __PHYSFS_platformSeek(finfo->handle, entry->startPos + offset); + if (rc) + finfo->curPos = (PHYSFS_uint32) offset; + + return(rc); +} /* MVL_seek */ + + +static PHYSFS_sint64 MVL_fileLength(fvoid *opaque) +{ + MVLfileinfo *finfo = (MVLfileinfo *) opaque; + return((PHYSFS_sint64) finfo->entry->size); +} /* MVL_fileLength */ + + +static int MVL_fileClose(fvoid *opaque) +{ + MVLfileinfo *finfo = (MVLfileinfo *) opaque; + BAIL_IF_MACRO(!__PHYSFS_platformClose(finfo->handle), NULL, 0); + allocator.Free(finfo); + return(1); +} /* MVL_fileClose */ + + +static int mvl_open(const char *filename, int forWriting, + void **fh, PHYSFS_uint32 *count) +{ + PHYSFS_uint8 buf[4]; + + *fh = NULL; + BAIL_IF_MACRO(forWriting, ERR_ARC_IS_READ_ONLY, 0); + + *fh = __PHYSFS_platformOpenRead(filename); + BAIL_IF_MACRO(*fh == NULL, NULL, 0); + + if (__PHYSFS_platformRead(*fh, buf, 4, 1) != 1) + goto openMvl_failed; + + if (memcmp(buf, "DMVL", 4) != 0) + { + __PHYSFS_setError(ERR_UNSUPPORTED_ARCHIVE); + goto openMvl_failed; + } /* if */ + + if (__PHYSFS_platformRead(*fh, count, sizeof (PHYSFS_uint32), 1) != 1) + goto openMvl_failed; + + *count = PHYSFS_swapULE32(*count); + + return(1); + +openMvl_failed: + if (*fh != NULL) + __PHYSFS_platformClose(*fh); + + *count = -1; + *fh = NULL; + return(0); +} /* mvl_open */ + + +static int MVL_isArchive(const char *filename, int forWriting) +{ + void *fh; + PHYSFS_uint32 fileCount; + int retval = mvl_open(filename, forWriting, &fh, &fileCount); + + if (fh != NULL) + __PHYSFS_platformClose(fh); + + return(retval); +} /* MVL_isArchive */ + + +static int mvl_entry_cmp(void *_a, PHYSFS_uint32 one, PHYSFS_uint32 two) +{ + if (one != two) + { + const MVLentry *a = (const MVLentry *) _a; + return(strcmp(a[one].name, a[two].name)); + } /* if */ + + return 0; +} /* mvl_entry_cmp */ + + +static void mvl_entry_swap(void *_a, PHYSFS_uint32 one, PHYSFS_uint32 two) +{ + if (one != two) + { + MVLentry tmp; + MVLentry *first = &(((MVLentry *) _a)[one]); + MVLentry *second = &(((MVLentry *) _a)[two]); + memcpy(&tmp, first, sizeof (MVLentry)); + memcpy(first, second, sizeof (MVLentry)); + memcpy(second, &tmp, sizeof (MVLentry)); + } /* if */ +} /* mvl_entry_swap */ + + +static int mvl_load_entries(const char *name, int forWriting, MVLinfo *info) +{ + void *fh = NULL; + PHYSFS_uint32 fileCount; + PHYSFS_uint32 location = 8; /* sizeof sig. */ + MVLentry *entry; + + BAIL_IF_MACRO(!mvl_open(name, forWriting, &fh, &fileCount), NULL, 0); + info->entryCount = fileCount; + info->entries = (MVLentry *) allocator.Malloc(sizeof(MVLentry)*fileCount); + if (info->entries == NULL) + { + __PHYSFS_platformClose(fh); + BAIL_MACRO(ERR_OUT_OF_MEMORY, 0); + } /* if */ + + location += (17 * fileCount); + + for (entry = info->entries; fileCount > 0; fileCount--, entry++) + { + if (__PHYSFS_platformRead(fh, &entry->name, 13, 1) != 1) + { + __PHYSFS_platformClose(fh); + return(0); + } /* if */ + + if (__PHYSFS_platformRead(fh, &entry->size, 4, 1) != 1) + { + __PHYSFS_platformClose(fh); + return(0); + } /* if */ + + entry->size = PHYSFS_swapULE32(entry->size); + entry->startPos = location; + location += entry->size; + } /* for */ + + __PHYSFS_platformClose(fh); + + __PHYSFS_sort(info->entries, info->entryCount, + mvl_entry_cmp, mvl_entry_swap); + return(1); +} /* mvl_load_entries */ + + +static void *MVL_openArchive(const char *name, int forWriting) +{ + PHYSFS_sint64 modtime = __PHYSFS_platformGetLastModTime(name); + MVLinfo *info = (MVLinfo *) allocator.Malloc(sizeof (MVLinfo)); + + BAIL_IF_MACRO(info == NULL, ERR_OUT_OF_MEMORY, NULL); + memset(info, '\0', sizeof (MVLinfo)); + + info->filename = (char *) allocator.Malloc(strlen(name) + 1); + GOTO_IF_MACRO(!info->filename, ERR_OUT_OF_MEMORY, MVL_openArchive_failed); + if (!mvl_load_entries(name, forWriting, info)) + goto MVL_openArchive_failed; + + strcpy(info->filename, name); + info->last_mod_time = modtime; + return(info); + +MVL_openArchive_failed: + if (info != NULL) + { + if (info->filename != NULL) + allocator.Free(info->filename); + if (info->entries != NULL) + allocator.Free(info->entries); + allocator.Free(info); + } /* if */ + + return(NULL); +} /* MVL_openArchive */ + + +static void MVL_enumerateFiles(dvoid *opaque, const char *dname, + int omitSymLinks, PHYSFS_EnumFilesCallback cb, + const char *origdir, void *callbackdata) +{ + /* no directories in MVL files. */ + if (*dname == '\0') + { + MVLinfo *info = ((MVLinfo *) opaque); + MVLentry *entry = info->entries; + PHYSFS_uint32 max = info->entryCount; + PHYSFS_uint32 i; + + for (i = 0; i < max; i++, entry++) + cb(callbackdata, origdir, entry->name); + } /* if */ +} /* MVL_enumerateFiles */ + + +static MVLentry *mvl_find_entry(MVLinfo *info, const char *name) +{ + char *ptr = strchr(name, '.'); + MVLentry *a = info->entries; + PHYSFS_sint32 lo = 0; + PHYSFS_sint32 hi = (PHYSFS_sint32) (info->entryCount - 1); + PHYSFS_sint32 middle; + int rc; + + /* + * Rule out filenames to avoid unneeded processing...no dirs, + * big filenames, or extensions > 3 chars. + */ + BAIL_IF_MACRO((ptr) && (strlen(ptr) > 4), ERR_NO_SUCH_FILE, NULL); + BAIL_IF_MACRO(strlen(name) > 12, ERR_NO_SUCH_FILE, NULL); + BAIL_IF_MACRO(strchr(name, '/') != NULL, ERR_NO_SUCH_FILE, NULL); + + while (lo <= hi) + { + middle = lo + ((hi - lo) / 2); + rc = __PHYSFS_stricmpASCII(name, a[middle].name); + if (rc == 0) /* found it! */ + return(&a[middle]); + else if (rc > 0) + lo = middle + 1; + else + hi = middle - 1; + } /* while */ + + BAIL_MACRO(ERR_NO_SUCH_FILE, NULL); +} /* mvl_find_entry */ + + +static int MVL_exists(dvoid *opaque, const char *name) +{ + return(mvl_find_entry(((MVLinfo *) opaque), name) != NULL); +} /* MVL_exists */ + + +static int MVL_isDirectory(dvoid *opaque, const char *name, int *fileExists) +{ + *fileExists = MVL_exists(opaque, name); + return(0); /* never directories in a groupfile. */ +} /* MVL_isDirectory */ + + +static int MVL_isSymLink(dvoid *opaque, const char *name, int *fileExists) +{ + *fileExists = MVL_exists(opaque, name); + return(0); /* never symlinks in a groupfile. */ +} /* MVL_isSymLink */ + + +static PHYSFS_sint64 MVL_getLastModTime(dvoid *opaque, + const char *name, + int *fileExists) +{ + MVLinfo *info = ((MVLinfo *) opaque); + PHYSFS_sint64 retval = -1; + + *fileExists = (mvl_find_entry(info, name) != NULL); + if (*fileExists) /* use time of MVL itself in the physical filesystem. */ + retval = info->last_mod_time; + + return(retval); +} /* MVL_getLastModTime */ + + +static fvoid *MVL_openRead(dvoid *opaque, const char *fnm, int *fileExists) +{ + MVLinfo *info = ((MVLinfo *) opaque); + MVLfileinfo *finfo; + MVLentry *entry; + + entry = mvl_find_entry(info, fnm); + *fileExists = (entry != NULL); + BAIL_IF_MACRO(entry == NULL, NULL, NULL); + + finfo = (MVLfileinfo *) allocator.Malloc(sizeof (MVLfileinfo)); + BAIL_IF_MACRO(finfo == NULL, ERR_OUT_OF_MEMORY, NULL); + + finfo->handle = __PHYSFS_platformOpenRead(info->filename); + if ( (finfo->handle == NULL) || + (!__PHYSFS_platformSeek(finfo->handle, entry->startPos)) ) + { + allocator.Free(finfo); + return(NULL); + } /* if */ + + finfo->curPos = 0; + finfo->entry = entry; + return(finfo); +} /* MVL_openRead */ + + +static fvoid *MVL_openWrite(dvoid *opaque, const char *name) +{ + BAIL_MACRO(ERR_NOT_SUPPORTED, NULL); +} /* MVL_openWrite */ + + +static fvoid *MVL_openAppend(dvoid *opaque, const char *name) +{ + BAIL_MACRO(ERR_NOT_SUPPORTED, NULL); +} /* MVL_openAppend */ + + +static int MVL_remove(dvoid *opaque, const char *name) +{ + BAIL_MACRO(ERR_NOT_SUPPORTED, 0); +} /* MVL_remove */ + + +static int MVL_mkdir(dvoid *opaque, const char *name) +{ + BAIL_MACRO(ERR_NOT_SUPPORTED, 0); +} /* MVL_mkdir */ + + +const PHYSFS_ArchiveInfo __PHYSFS_ArchiveInfo_MVL = +{ + "MVL", + MVL_ARCHIVE_DESCRIPTION, + "Bradley Bell ", + "http://icculus.org/physfs/", +}; + + +const PHYSFS_Archiver __PHYSFS_Archiver_MVL = +{ + &__PHYSFS_ArchiveInfo_MVL, + MVL_isArchive, /* isArchive() method */ + MVL_openArchive, /* openArchive() method */ + MVL_enumerateFiles, /* enumerateFiles() method */ + MVL_exists, /* exists() method */ + MVL_isDirectory, /* isDirectory() method */ + MVL_isSymLink, /* isSymLink() method */ + MVL_getLastModTime, /* getLastModTime() method */ + MVL_openRead, /* openRead() method */ + MVL_openWrite, /* openWrite() method */ + MVL_openAppend, /* openAppend() method */ + MVL_remove, /* remove() method */ + MVL_mkdir, /* mkdir() method */ + MVL_dirClose, /* dirClose() method */ + MVL_read, /* read() method */ + MVL_write, /* write() method */ + MVL_eof, /* eof() method */ + MVL_tell, /* tell() method */ + MVL_seek, /* seek() method */ + MVL_fileLength, /* fileLength() method */ + MVL_fileClose /* fileClose() method */ +}; + +#endif /* defined PHYSFS_SUPPORTS_MVL */ + +/* end of mvl.c ... */ + diff --git a/project/jni/physfs/archivers/qpak.c b/project/jni/physfs/archivers/qpak.c new file mode 100644 index 000000000..1aa7a32b7 --- /dev/null +++ b/project/jni/physfs/archivers/qpak.c @@ -0,0 +1,633 @@ +/* + * 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 zip.c + * archiver handles). + * + * ======================================================================== + * + * This format info (in more detail) comes from: + * 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. + */ + +#if (defined PHYSFS_SUPPORTS_QPAK) + +#include +#include +#include +#include "physfs.h" + +#define __PHYSICSFS_INTERNAL__ +#include "physfs_internal.h" + +#if 1 /* Make this case insensitive? */ +#define QPAK_strcmp(x, y) __PHYSFS_stricmpASCII(x, y) +#define QPAK_strncmp(x, y, z) __PHYSFS_strnicmpASCII(x, y, z) +#else +#define QPAK_strcmp(x, y) strcmp(x, y) +#define QPAK_strncmp(x, y, z) strncmp(x, y, z) +#endif + + +typedef struct +{ + char name[56]; + PHYSFS_uint32 startPos; + PHYSFS_uint32 size; +} QPAKentry; + +typedef struct +{ + char *filename; + PHYSFS_sint64 last_mod_time; + PHYSFS_uint32 entryCount; + QPAKentry *entries; +} QPAKinfo; + +typedef struct +{ + void *handle; + QPAKentry *entry; + PHYSFS_uint32 curPos; +} QPAKfileinfo; + +/* Magic numbers... */ +#define QPAK_SIG 0x4b434150 /* "PACK" in ASCII. */ + + +static void QPAK_dirClose(dvoid *opaque) +{ + QPAKinfo *info = ((QPAKinfo *) opaque); + allocator.Free(info->filename); + allocator.Free(info->entries); + allocator.Free(info); +} /* QPAK_dirClose */ + + +static PHYSFS_sint64 QPAK_read(fvoid *opaque, void *buffer, + PHYSFS_uint32 objSize, PHYSFS_uint32 objCount) +{ + QPAKfileinfo *finfo = (QPAKfileinfo *) opaque; + QPAKentry *entry = finfo->entry; + PHYSFS_uint32 bytesLeft = entry->size - finfo->curPos; + PHYSFS_uint32 objsLeft = (bytesLeft / objSize); + PHYSFS_sint64 rc; + + if (objsLeft < objCount) + objCount = objsLeft; + + rc = __PHYSFS_platformRead(finfo->handle, buffer, objSize, objCount); + if (rc > 0) + finfo->curPos += (PHYSFS_uint32) (rc * objSize); + + return(rc); +} /* QPAK_read */ + + +static PHYSFS_sint64 QPAK_write(fvoid *opaque, const void *buffer, + PHYSFS_uint32 objSize, PHYSFS_uint32 objCount) +{ + BAIL_MACRO(ERR_NOT_SUPPORTED, -1); +} /* QPAK_write */ + + +static int QPAK_eof(fvoid *opaque) +{ + QPAKfileinfo *finfo = (QPAKfileinfo *) opaque; + QPAKentry *entry = finfo->entry; + return(finfo->curPos >= entry->size); +} /* QPAK_eof */ + + +static PHYSFS_sint64 QPAK_tell(fvoid *opaque) +{ + return(((QPAKfileinfo *) opaque)->curPos); +} /* QPAK_tell */ + + +static int QPAK_seek(fvoid *opaque, PHYSFS_uint64 offset) +{ + QPAKfileinfo *finfo = (QPAKfileinfo *) opaque; + QPAKentry *entry = finfo->entry; + int rc; + + BAIL_IF_MACRO(offset < 0, ERR_INVALID_ARGUMENT, 0); + BAIL_IF_MACRO(offset >= entry->size, ERR_PAST_EOF, 0); + rc = __PHYSFS_platformSeek(finfo->handle, entry->startPos + offset); + if (rc) + finfo->curPos = (PHYSFS_uint32) offset; + + return(rc); +} /* QPAK_seek */ + + +static PHYSFS_sint64 QPAK_fileLength(fvoid *opaque) +{ + QPAKfileinfo *finfo = (QPAKfileinfo *) opaque; + return((PHYSFS_sint64) finfo->entry->size); +} /* QPAK_fileLength */ + + +static int QPAK_fileClose(fvoid *opaque) +{ + QPAKfileinfo *finfo = (QPAKfileinfo *) opaque; + BAIL_IF_MACRO(!__PHYSFS_platformClose(finfo->handle), NULL, 0); + allocator.Free(finfo); + return(1); +} /* QPAK_fileClose */ + + +static int qpak_open(const char *filename, int forWriting, + void **fh, PHYSFS_uint32 *count) +{ + PHYSFS_uint32 buf; + + *fh = NULL; + BAIL_IF_MACRO(forWriting, ERR_ARC_IS_READ_ONLY, 0); + + *fh = __PHYSFS_platformOpenRead(filename); + BAIL_IF_MACRO(*fh == NULL, NULL, 0); + + if (__PHYSFS_platformRead(*fh, &buf, sizeof (PHYSFS_uint32), 1) != 1) + goto openQpak_failed; + + buf = PHYSFS_swapULE32(buf); + GOTO_IF_MACRO(buf != QPAK_SIG, ERR_UNSUPPORTED_ARCHIVE, openQpak_failed); + + if (__PHYSFS_platformRead(*fh, &buf, sizeof (PHYSFS_uint32), 1) != 1) + goto openQpak_failed; + + buf = PHYSFS_swapULE32(buf); /* directory table offset. */ + + if (__PHYSFS_platformRead(*fh, count, sizeof (PHYSFS_uint32), 1) != 1) + goto openQpak_failed; + + *count = PHYSFS_swapULE32(*count); + + /* corrupted archive? */ + GOTO_IF_MACRO((*count % 64) != 0, ERR_CORRUPTED, openQpak_failed); + + if (!__PHYSFS_platformSeek(*fh, buf)) + goto openQpak_failed; + + *count /= 64; + return(1); + +openQpak_failed: + if (*fh != NULL) + __PHYSFS_platformClose(*fh); + + *count = -1; + *fh = NULL; + return(0); +} /* qpak_open */ + + +static int QPAK_isArchive(const char *filename, int forWriting) +{ + void *fh; + PHYSFS_uint32 fileCount; + int retval = qpak_open(filename, forWriting, &fh, &fileCount); + + if (fh != NULL) + __PHYSFS_platformClose(fh); + + return(retval); +} /* QPAK_isArchive */ + + +static int qpak_entry_cmp(void *_a, PHYSFS_uint32 one, PHYSFS_uint32 two) +{ + if (one != two) + { + const QPAKentry *a = (const QPAKentry *) _a; + return(QPAK_strcmp(a[one].name, a[two].name)); + } /* if */ + + return 0; +} /* qpak_entry_cmp */ + + +static void qpak_entry_swap(void *_a, PHYSFS_uint32 one, PHYSFS_uint32 two) +{ + if (one != two) + { + QPAKentry tmp; + QPAKentry *first = &(((QPAKentry *) _a)[one]); + QPAKentry *second = &(((QPAKentry *) _a)[two]); + memcpy(&tmp, first, sizeof (QPAKentry)); + memcpy(first, second, sizeof (QPAKentry)); + memcpy(second, &tmp, sizeof (QPAKentry)); + } /* if */ +} /* qpak_entry_swap */ + + +static int qpak_load_entries(const char *name, int forWriting, QPAKinfo *info) +{ + void *fh = NULL; + PHYSFS_uint32 fileCount; + QPAKentry *entry; + + BAIL_IF_MACRO(!qpak_open(name, forWriting, &fh, &fileCount), NULL, 0); + info->entryCount = fileCount; + info->entries = (QPAKentry*) allocator.Malloc(sizeof(QPAKentry)*fileCount); + if (info->entries == NULL) + { + __PHYSFS_platformClose(fh); + BAIL_MACRO(ERR_OUT_OF_MEMORY, 0); + } /* if */ + + for (entry = info->entries; fileCount > 0; fileCount--, entry++) + { + PHYSFS_uint32 loc; + + if (__PHYSFS_platformRead(fh,&entry->name,sizeof(entry->name),1) != 1) + { + __PHYSFS_platformClose(fh); + return(0); + } /* if */ + + if (__PHYSFS_platformRead(fh,&loc,sizeof(loc),1) != 1) + { + __PHYSFS_platformClose(fh); + return(0); + } /* if */ + + if (__PHYSFS_platformRead(fh,&entry->size,sizeof(entry->size),1) != 1) + { + __PHYSFS_platformClose(fh); + return(0); + } /* if */ + + entry->size = PHYSFS_swapULE32(entry->size); + entry->startPos = PHYSFS_swapULE32(loc); + } /* for */ + + __PHYSFS_platformClose(fh); + + __PHYSFS_sort(info->entries, info->entryCount, + qpak_entry_cmp, qpak_entry_swap); + return(1); +} /* qpak_load_entries */ + + +static void *QPAK_openArchive(const char *name, int forWriting) +{ + QPAKinfo *info = (QPAKinfo *) allocator.Malloc(sizeof (QPAKinfo)); + PHYSFS_sint64 modtime = __PHYSFS_platformGetLastModTime(name); + + BAIL_IF_MACRO(info == NULL, ERR_OUT_OF_MEMORY, NULL); + memset(info, '\0', sizeof (QPAKinfo)); + + info->filename = (char *) allocator.Malloc(strlen(name) + 1); + if (info->filename == NULL) + { + __PHYSFS_setError(ERR_OUT_OF_MEMORY); + goto QPAK_openArchive_failed; + } /* if */ + + if (!qpak_load_entries(name, forWriting, info)) + goto QPAK_openArchive_failed; + + strcpy(info->filename, name); + info->last_mod_time = modtime; + return(info); + +QPAK_openArchive_failed: + if (info != NULL) + { + if (info->filename != NULL) + allocator.Free(info->filename); + if (info->entries != NULL) + allocator.Free(info->entries); + allocator.Free(info); + } /* if */ + + return(NULL); +} /* QPAK_openArchive */ + + +static PHYSFS_sint32 qpak_find_start_of_dir(QPAKinfo *info, const char *path, + int stop_on_first_find) +{ + PHYSFS_sint32 lo = 0; + PHYSFS_sint32 hi = (PHYSFS_sint32) (info->entryCount - 1); + PHYSFS_sint32 middle; + PHYSFS_uint32 dlen = strlen(path); + PHYSFS_sint32 retval = -1; + const char *name; + int rc; + + if (*path == '\0') /* root dir? */ + return(0); + + if ((dlen > 0) && (path[dlen - 1] == '/')) /* ignore trailing slash. */ + dlen--; + + while (lo <= hi) + { + middle = lo + ((hi - lo) / 2); + name = info->entries[middle].name; + rc = QPAK_strncmp(path, name, dlen); + if (rc == 0) + { + char ch = name[dlen]; + if (ch < '/') /* make sure this isn't just a substr match. */ + rc = -1; + else if (ch > '/') + rc = 1; + else + { + if (stop_on_first_find) /* Just checking dir's existance? */ + return(middle); + + if (name[dlen + 1] == '\0') /* Skip initial dir entry. */ + return(middle + 1); + + /* there might be more entries earlier in the list. */ + retval = middle; + hi = middle - 1; + } /* else */ + } /* if */ + + if (rc > 0) + lo = middle + 1; + else + hi = middle - 1; + } /* while */ + + return(retval); +} /* qpak_find_start_of_dir */ + + +/* + * Moved to seperate function so we can use alloca then immediately throw + * away the allocated stack space... + */ +static void doEnumCallback(PHYSFS_EnumFilesCallback cb, void *callbackdata, + const char *odir, const char *str, PHYSFS_sint32 ln) +{ + char *newstr = __PHYSFS_smallAlloc(ln + 1); + if (newstr == NULL) + return; + + memcpy(newstr, str, ln); + newstr[ln] = '\0'; + cb(callbackdata, odir, newstr); + __PHYSFS_smallFree(newstr); +} /* doEnumCallback */ + + +static void QPAK_enumerateFiles(dvoid *opaque, const char *dname, + int omitSymLinks, PHYSFS_EnumFilesCallback cb, + const char *origdir, void *callbackdata) +{ + QPAKinfo *info = ((QPAKinfo *) opaque); + PHYSFS_sint32 dlen, dlen_inc, max, i; + + i = qpak_find_start_of_dir(info, dname, 0); + if (i == -1) /* no such directory. */ + return; + + dlen = strlen(dname); + if ((dlen > 0) && (dname[dlen - 1] == '/')) /* ignore trailing slash. */ + dlen--; + + dlen_inc = ((dlen > 0) ? 1 : 0) + dlen; + max = (PHYSFS_sint32) info->entryCount; + while (i < max) + { + char *add; + char *ptr; + PHYSFS_sint32 ln; + char *e = info->entries[i].name; + if ((dlen) && ((QPAK_strncmp(e, dname, dlen)) || (e[dlen] != '/'))) + break; /* past end of this dir; we're done. */ + + add = e + dlen_inc; + ptr = strchr(add, '/'); + ln = (PHYSFS_sint32) ((ptr) ? ptr-add : strlen(add)); + doEnumCallback(cb, callbackdata, origdir, add, ln); + ln += dlen_inc; /* point past entry to children... */ + + /* increment counter and skip children of subdirs... */ + while ((++i < max) && (ptr != NULL)) + { + char *e_new = info->entries[i].name; + if ((QPAK_strncmp(e, e_new, ln) != 0) || (e_new[ln] != '/')) + break; + } /* while */ + } /* while */ +} /* QPAK_enumerateFiles */ + + +/* + * This will find the QPAKentry associated with a path in platform-independent + * notation. Directories don't have QPAKentries associated with them, but + * (*isDir) will be set to non-zero if a dir was hit. + */ +static QPAKentry *qpak_find_entry(QPAKinfo *info, const char *path, int *isDir) +{ + QPAKentry *a = info->entries; + PHYSFS_sint32 pathlen = strlen(path); + PHYSFS_sint32 lo = 0; + PHYSFS_sint32 hi = (PHYSFS_sint32) (info->entryCount - 1); + PHYSFS_sint32 middle; + const char *thispath = NULL; + int rc; + + while (lo <= hi) + { + middle = lo + ((hi - lo) / 2); + thispath = a[middle].name; + rc = QPAK_strncmp(path, thispath, pathlen); + + if (rc > 0) + lo = middle + 1; + + else if (rc < 0) + hi = middle - 1; + + else /* substring match...might be dir or entry or nothing. */ + { + if (isDir != NULL) + { + *isDir = (thispath[pathlen] == '/'); + if (*isDir) + return(NULL); + } /* if */ + + if (thispath[pathlen] == '\0') /* found entry? */ + return(&a[middle]); + /* adjust search params, try again. */ + else if (thispath[pathlen] > '/') + hi = middle - 1; + else + lo = middle + 1; + } /* if */ + } /* while */ + + if (isDir != NULL) + *isDir = 0; + + BAIL_MACRO(ERR_NO_SUCH_FILE, NULL); +} /* qpak_find_entry */ + + +static int QPAK_exists(dvoid *opaque, const char *name) +{ + int isDir; + QPAKinfo *info = (QPAKinfo *) opaque; + QPAKentry *entry = qpak_find_entry(info, name, &isDir); + return((entry != NULL) || (isDir)); +} /* QPAK_exists */ + + +static int QPAK_isDirectory(dvoid *opaque, const char *name, int *fileExists) +{ + QPAKinfo *info = (QPAKinfo *) opaque; + int isDir; + QPAKentry *entry = qpak_find_entry(info, name, &isDir); + + *fileExists = ((isDir) || (entry != NULL)); + if (isDir) + return(1); /* definitely a dir. */ + + BAIL_MACRO(ERR_NO_SUCH_FILE, 0); +} /* QPAK_isDirectory */ + + +static int QPAK_isSymLink(dvoid *opaque, const char *name, int *fileExists) +{ + *fileExists = QPAK_exists(opaque, name); + return(0); /* never symlinks in a quake pak. */ +} /* QPAK_isSymLink */ + + +static PHYSFS_sint64 QPAK_getLastModTime(dvoid *opaque, + const char *name, + int *fileExists) +{ + int isDir; + QPAKinfo *info = ((QPAKinfo *) opaque); + PHYSFS_sint64 retval = -1; + QPAKentry *entry = qpak_find_entry(info, name, &isDir); + + *fileExists = ((isDir) || (entry != NULL)); + if (*fileExists) /* use time of QPAK itself in the physical filesystem. */ + retval = info->last_mod_time; + + return(retval); +} /* QPAK_getLastModTime */ + + +static fvoid *QPAK_openRead(dvoid *opaque, const char *fnm, int *fileExists) +{ + QPAKinfo *info = ((QPAKinfo *) opaque); + QPAKfileinfo *finfo; + QPAKentry *entry; + int isDir; + + entry = qpak_find_entry(info, fnm, &isDir); + *fileExists = ((entry != NULL) || (isDir)); + BAIL_IF_MACRO(isDir, ERR_NOT_A_FILE, NULL); + BAIL_IF_MACRO(entry == NULL, ERR_NO_SUCH_FILE, NULL); + + finfo = (QPAKfileinfo *) allocator.Malloc(sizeof (QPAKfileinfo)); + BAIL_IF_MACRO(finfo == NULL, ERR_OUT_OF_MEMORY, NULL); + + finfo->handle = __PHYSFS_platformOpenRead(info->filename); + if ( (finfo->handle == NULL) || + (!__PHYSFS_platformSeek(finfo->handle, entry->startPos)) ) + { + allocator.Free(finfo); + return(NULL); + } /* if */ + + finfo->curPos = 0; + finfo->entry = entry; + return(finfo); +} /* QPAK_openRead */ + + +static fvoid *QPAK_openWrite(dvoid *opaque, const char *name) +{ + BAIL_MACRO(ERR_NOT_SUPPORTED, NULL); +} /* QPAK_openWrite */ + + +static fvoid *QPAK_openAppend(dvoid *opaque, const char *name) +{ + BAIL_MACRO(ERR_NOT_SUPPORTED, NULL); +} /* QPAK_openAppend */ + + +static int QPAK_remove(dvoid *opaque, const char *name) +{ + BAIL_MACRO(ERR_NOT_SUPPORTED, 0); +} /* QPAK_remove */ + + +static int QPAK_mkdir(dvoid *opaque, const char *name) +{ + BAIL_MACRO(ERR_NOT_SUPPORTED, 0); +} /* QPAK_mkdir */ + + +const PHYSFS_ArchiveInfo __PHYSFS_ArchiveInfo_QPAK = +{ + "PAK", + QPAK_ARCHIVE_DESCRIPTION, + "Ryan C. Gordon ", + "http://icculus.org/physfs/", +}; + + +const PHYSFS_Archiver __PHYSFS_Archiver_QPAK = +{ + &__PHYSFS_ArchiveInfo_QPAK, + QPAK_isArchive, /* isArchive() method */ + QPAK_openArchive, /* openArchive() method */ + QPAK_enumerateFiles, /* enumerateFiles() method */ + QPAK_exists, /* exists() method */ + QPAK_isDirectory, /* isDirectory() method */ + QPAK_isSymLink, /* isSymLink() method */ + QPAK_getLastModTime, /* getLastModTime() method */ + QPAK_openRead, /* openRead() method */ + QPAK_openWrite, /* openWrite() method */ + QPAK_openAppend, /* openAppend() method */ + QPAK_remove, /* remove() method */ + QPAK_mkdir, /* mkdir() method */ + QPAK_dirClose, /* dirClose() method */ + QPAK_read, /* read() method */ + QPAK_write, /* write() method */ + QPAK_eof, /* eof() method */ + QPAK_tell, /* tell() method */ + QPAK_seek, /* seek() method */ + QPAK_fileLength, /* fileLength() method */ + QPAK_fileClose /* fileClose() method */ +}; + +#endif /* defined PHYSFS_SUPPORTS_QPAK */ + +/* end of qpak.c ... */ + diff --git a/project/jni/physfs/archivers/wad.c b/project/jni/physfs/archivers/wad.c new file mode 100644 index 000000000..cb2626cb4 --- /dev/null +++ b/project/jni/physfs/archivers/wad.c @@ -0,0 +1,534 @@ +/* + * 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 4-byte (long) integer which is the number of lumps in the wad + * (c) a long integer 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 long integer, the file offset to the start of the lump + * (b) a long integer, 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. + */ + +#if (defined PHYSFS_SUPPORTS_WAD) + +#include +#include +#include +#include "physfs.h" + +#define __PHYSICSFS_INTERNAL__ +#include "physfs_internal.h" + +typedef struct +{ + char name[18]; + PHYSFS_uint32 startPos; + PHYSFS_uint32 size; +} WADentry; + +typedef struct +{ + char *filename; + PHYSFS_sint64 last_mod_time; + PHYSFS_uint32 entryCount; + PHYSFS_uint32 entryOffset; + WADentry *entries; +} WADinfo; + +typedef struct +{ + void *handle; + WADentry *entry; + PHYSFS_uint32 curPos; +} WADfileinfo; + + +static void WAD_dirClose(dvoid *opaque) +{ + WADinfo *info = ((WADinfo *) opaque); + allocator.Free(info->filename); + allocator.Free(info->entries); + allocator.Free(info); +} /* WAD_dirClose */ + + +static PHYSFS_sint64 WAD_read(fvoid *opaque, void *buffer, + PHYSFS_uint32 objSize, PHYSFS_uint32 objCount) +{ + WADfileinfo *finfo = (WADfileinfo *) opaque; + WADentry *entry = finfo->entry; + PHYSFS_uint32 bytesLeft = entry->size - finfo->curPos; + PHYSFS_uint32 objsLeft = (bytesLeft / objSize); + PHYSFS_sint64 rc; + + if (objsLeft < objCount) + objCount = objsLeft; + + rc = __PHYSFS_platformRead(finfo->handle, buffer, objSize, objCount); + if (rc > 0) + finfo->curPos += (PHYSFS_uint32) (rc * objSize); + + return(rc); +} /* WAD_read */ + + +static PHYSFS_sint64 WAD_write(fvoid *opaque, const void *buffer, + PHYSFS_uint32 objSize, PHYSFS_uint32 objCount) +{ + BAIL_MACRO(ERR_NOT_SUPPORTED, -1); +} /* WAD_write */ + + +static int WAD_eof(fvoid *opaque) +{ + WADfileinfo *finfo = (WADfileinfo *) opaque; + WADentry *entry = finfo->entry; + return(finfo->curPos >= entry->size); +} /* WAD_eof */ + + +static PHYSFS_sint64 WAD_tell(fvoid *opaque) +{ + return(((WADfileinfo *) opaque)->curPos); +} /* WAD_tell */ + + +static int WAD_seek(fvoid *opaque, PHYSFS_uint64 offset) +{ + WADfileinfo *finfo = (WADfileinfo *) opaque; + WADentry *entry = finfo->entry; + int rc; + + BAIL_IF_MACRO(offset < 0, ERR_INVALID_ARGUMENT, 0); + BAIL_IF_MACRO(offset >= entry->size, ERR_PAST_EOF, 0); + rc = __PHYSFS_platformSeek(finfo->handle, entry->startPos + offset); + if (rc) + finfo->curPos = (PHYSFS_uint32) offset; + + return(rc); +} /* WAD_seek */ + + +static PHYSFS_sint64 WAD_fileLength(fvoid *opaque) +{ + WADfileinfo *finfo = (WADfileinfo *) opaque; + return((PHYSFS_sint64) finfo->entry->size); +} /* WAD_fileLength */ + + +static int WAD_fileClose(fvoid *opaque) +{ + WADfileinfo *finfo = (WADfileinfo *) opaque; + BAIL_IF_MACRO(!__PHYSFS_platformClose(finfo->handle), NULL, 0); + allocator.Free(finfo); + return(1); +} /* WAD_fileClose */ + + +static int wad_open(const char *filename, int forWriting, + void **fh, PHYSFS_uint32 *count,PHYSFS_uint32 *offset) +{ + PHYSFS_uint8 buf[4]; + + *fh = NULL; + BAIL_IF_MACRO(forWriting, ERR_ARC_IS_READ_ONLY, 0); + + *fh = __PHYSFS_platformOpenRead(filename); + BAIL_IF_MACRO(*fh == NULL, NULL, 0); + + if (__PHYSFS_platformRead(*fh, buf, 4, 1) != 1) + goto openWad_failed; + + if (memcmp(buf, "IWAD", 4) != 0 && memcmp(buf, "PWAD", 4) != 0) + { + __PHYSFS_setError(ERR_UNSUPPORTED_ARCHIVE); + goto openWad_failed; + } /* if */ + + if (__PHYSFS_platformRead(*fh, count, sizeof (PHYSFS_uint32), 1) != 1) + goto openWad_failed; + + *count = PHYSFS_swapULE32(*count); + + if (__PHYSFS_platformRead(*fh, offset, sizeof (PHYSFS_uint32), 1) != 1) + goto openWad_failed; + + *offset = PHYSFS_swapULE32(*offset); + + return(1); + +openWad_failed: + if (*fh != NULL) + __PHYSFS_platformClose(*fh); + + *count = -1; + *fh = NULL; + return(0); +} /* wad_open */ + + +static int WAD_isArchive(const char *filename, int forWriting) +{ + void *fh; + PHYSFS_uint32 fileCount,offset; + int retval = wad_open(filename, forWriting, &fh, &fileCount,&offset); + + if (fh != NULL) + __PHYSFS_platformClose(fh); + + return(retval); +} /* WAD_isArchive */ + + +static int wad_entry_cmp(void *_a, PHYSFS_uint32 one, PHYSFS_uint32 two) +{ + if (one != two) + { + const WADentry *a = (const WADentry *) _a; + return(strcmp(a[one].name, a[two].name)); + } /* if */ + + return 0; +} /* wad_entry_cmp */ + + +static void wad_entry_swap(void *_a, PHYSFS_uint32 one, PHYSFS_uint32 two) +{ + if (one != two) + { + WADentry tmp; + WADentry *first = &(((WADentry *) _a)[one]); + WADentry *second = &(((WADentry *) _a)[two]); + memcpy(&tmp, first, sizeof (WADentry)); + memcpy(first, second, sizeof (WADentry)); + memcpy(second, &tmp, sizeof (WADentry)); + } /* if */ +} /* wad_entry_swap */ + + +static int wad_load_entries(const char *name, int forWriting, WADinfo *info) +{ + void *fh = NULL; + PHYSFS_uint32 fileCount; + PHYSFS_uint32 directoryOffset; + WADentry *entry; + char lastDirectory[9]; + + lastDirectory[8] = 0; /* Make sure lastDirectory stays null-terminated. */ + + BAIL_IF_MACRO(!wad_open(name, forWriting, &fh, &fileCount,&directoryOffset), NULL, 0); + info->entryCount = fileCount; + info->entries = (WADentry *) allocator.Malloc(sizeof(WADentry)*fileCount); + if (info->entries == NULL) + { + __PHYSFS_platformClose(fh); + BAIL_MACRO(ERR_OUT_OF_MEMORY, 0); + } /* if */ + + __PHYSFS_platformSeek(fh,directoryOffset); + + for (entry = info->entries; fileCount > 0; fileCount--, entry++) + { + if (__PHYSFS_platformRead(fh, &entry->startPos, 4, 1) != 1) + { + __PHYSFS_platformClose(fh); + return(0); + } /* if */ + + if (__PHYSFS_platformRead(fh, &entry->size, 4, 1) != 1) + { + __PHYSFS_platformClose(fh); + return(0); + } /* if */ + + if (__PHYSFS_platformRead(fh, &entry->name, 8, 1) != 1) + { + __PHYSFS_platformClose(fh); + return(0); + } /* if */ + + entry->name[8] = '\0'; /* name might not be null-terminated in file. */ + entry->size = PHYSFS_swapULE32(entry->size); + entry->startPos = PHYSFS_swapULE32(entry->startPos); + } /* for */ + + __PHYSFS_platformClose(fh); + + __PHYSFS_sort(info->entries, info->entryCount, + wad_entry_cmp, wad_entry_swap); + return(1); +} /* wad_load_entries */ + + +static void *WAD_openArchive(const char *name, int forWriting) +{ + PHYSFS_sint64 modtime = __PHYSFS_platformGetLastModTime(name); + WADinfo *info = (WADinfo *) allocator.Malloc(sizeof (WADinfo)); + + BAIL_IF_MACRO(info == NULL, ERR_OUT_OF_MEMORY, NULL); + memset(info, '\0', sizeof (WADinfo)); + + info->filename = (char *) allocator.Malloc(strlen(name) + 1); + GOTO_IF_MACRO(!info->filename, ERR_OUT_OF_MEMORY, WAD_openArchive_failed); + + if (!wad_load_entries(name, forWriting, info)) + goto WAD_openArchive_failed; + + strcpy(info->filename, name); + info->last_mod_time = modtime; + return(info); + +WAD_openArchive_failed: + if (info != NULL) + { + if (info->filename != NULL) + allocator.Free(info->filename); + if (info->entries != NULL) + allocator.Free(info->entries); + allocator.Free(info); + } /* if */ + + return(NULL); +} /* WAD_openArchive */ + + +static void WAD_enumerateFiles(dvoid *opaque, const char *dname, + int omitSymLinks, PHYSFS_EnumFilesCallback cb, + const char *origdir, void *callbackdata) +{ + WADinfo *info = ((WADinfo *) opaque); + WADentry *entry = info->entries; + PHYSFS_uint32 max = info->entryCount; + PHYSFS_uint32 i; + const char *name; + char *sep; + + if (*dname == '\0') /* root directory enumeration? */ + { + for (i = 0; i < max; i++, entry++) + { + name = entry->name; + if (strchr(name, '/') == NULL) + cb(callbackdata, origdir, name); + } /* for */ + } /* if */ + else + { + for (i = 0; i < max; i++, entry++) + { + name = entry->name; + sep = strchr(name, '/'); + if (sep != NULL) + { + if (strncmp(dname, name, (sep - name)) == 0) + cb(callbackdata, origdir, sep + 1); + } /* if */ + } /* for */ + } /* else */ +} /* WAD_enumerateFiles */ + + +static WADentry *wad_find_entry(WADinfo *info, const char *name) +{ + WADentry *a = info->entries; + PHYSFS_sint32 lo = 0; + PHYSFS_sint32 hi = (PHYSFS_sint32) (info->entryCount - 1); + PHYSFS_sint32 middle; + int rc; + + while (lo <= hi) + { + middle = lo + ((hi - lo) / 2); + rc = strcmp(name, a[middle].name); + if (rc == 0) /* found it! */ + return(&a[middle]); + else if (rc > 0) + lo = middle + 1; + else + hi = middle - 1; + } /* while */ + + BAIL_MACRO(ERR_NO_SUCH_FILE, NULL); +} /* wad_find_entry */ + + +static int WAD_exists(dvoid *opaque, const char *name) +{ + return(wad_find_entry(((WADinfo *) opaque), name) != NULL); +} /* WAD_exists */ + + +static int WAD_isDirectory(dvoid *opaque, const char *name, int *fileExists) +{ + WADentry *entry = wad_find_entry(((WADinfo *) opaque), name); + if (entry != NULL) + { + char *n; + + *fileExists = 1; + + /* Can't be a directory if it's a subdirectory. */ + if (strchr(entry->name, '/') != NULL) + return(0); + + /* Check if it matches "MAP??" or "E?M?" ... */ + n = entry->name; + if ((n[0] == 'E' && n[2] == 'M') || + (n[0] == 'M' && n[1] == 'A' && n[2] == 'P' && n[6] == 0)) + { + return(1); + } /* if */ + return(0); + } /* if */ + else + { + *fileExists = 0; + return(0); + } /* else */ +} /* WAD_isDirectory */ + + +static int WAD_isSymLink(dvoid *opaque, const char *name, int *fileExists) +{ + *fileExists = WAD_exists(opaque, name); + return(0); /* never symlinks in a wad. */ +} /* WAD_isSymLink */ + + +static PHYSFS_sint64 WAD_getLastModTime(dvoid *opaque, + const char *name, + int *fileExists) +{ + WADinfo *info = ((WADinfo *) opaque); + PHYSFS_sint64 retval = -1; + + *fileExists = (wad_find_entry(info, name) != NULL); + if (*fileExists) /* use time of WAD itself in the physical filesystem. */ + retval = info->last_mod_time; + + return(retval); +} /* WAD_getLastModTime */ + + +static fvoid *WAD_openRead(dvoid *opaque, const char *fnm, int *fileExists) +{ + WADinfo *info = ((WADinfo *) opaque); + WADfileinfo *finfo; + WADentry *entry; + + entry = wad_find_entry(info, fnm); + *fileExists = (entry != NULL); + BAIL_IF_MACRO(entry == NULL, NULL, NULL); + + finfo = (WADfileinfo *) allocator.Malloc(sizeof (WADfileinfo)); + BAIL_IF_MACRO(finfo == NULL, ERR_OUT_OF_MEMORY, NULL); + + finfo->handle = __PHYSFS_platformOpenRead(info->filename); + if ( (finfo->handle == NULL) || + (!__PHYSFS_platformSeek(finfo->handle, entry->startPos)) ) + { + allocator.Free(finfo); + return(NULL); + } /* if */ + + finfo->curPos = 0; + finfo->entry = entry; + return(finfo); +} /* WAD_openRead */ + + +static fvoid *WAD_openWrite(dvoid *opaque, const char *name) +{ + BAIL_MACRO(ERR_NOT_SUPPORTED, NULL); +} /* WAD_openWrite */ + + +static fvoid *WAD_openAppend(dvoid *opaque, const char *name) +{ + BAIL_MACRO(ERR_NOT_SUPPORTED, NULL); +} /* WAD_openAppend */ + + +static int WAD_remove(dvoid *opaque, const char *name) +{ + BAIL_MACRO(ERR_NOT_SUPPORTED, 0); +} /* WAD_remove */ + + +static int WAD_mkdir(dvoid *opaque, const char *name) +{ + BAIL_MACRO(ERR_NOT_SUPPORTED, 0); +} /* WAD_mkdir */ + + +const PHYSFS_ArchiveInfo __PHYSFS_ArchiveInfo_WAD = +{ + "WAD", + WAD_ARCHIVE_DESCRIPTION, + "Travis Wells ", + "http://www.3dmm2.com/doom/", +}; + + +const PHYSFS_Archiver __PHYSFS_Archiver_WAD = +{ + &__PHYSFS_ArchiveInfo_WAD, + WAD_isArchive, /* isArchive() method */ + WAD_openArchive, /* openArchive() method */ + WAD_enumerateFiles, /* enumerateFiles() method */ + WAD_exists, /* exists() method */ + WAD_isDirectory, /* isDirectory() method */ + WAD_isSymLink, /* isSymLink() method */ + WAD_getLastModTime, /* getLastModTime() method */ + WAD_openRead, /* openRead() method */ + WAD_openWrite, /* openWrite() method */ + WAD_openAppend, /* openAppend() method */ + WAD_remove, /* remove() method */ + WAD_mkdir, /* mkdir() method */ + WAD_dirClose, /* dirClose() method */ + WAD_read, /* read() method */ + WAD_write, /* write() method */ + WAD_eof, /* eof() method */ + WAD_tell, /* tell() method */ + WAD_seek, /* seek() method */ + WAD_fileLength, /* fileLength() method */ + WAD_fileClose /* fileClose() method */ +}; + +#endif /* defined PHYSFS_SUPPORTS_WAD */ + +/* end of wad.c ... */ + diff --git a/project/jni/physfs/archivers/zip.c b/project/jni/physfs/archivers/zip.c new file mode 100644 index 000000000..b546df225 --- /dev/null +++ b/project/jni/physfs/archivers/zip.c @@ -0,0 +1,1454 @@ +/* + * ZIP support routines for PhysicsFS. + * + * Please see the file LICENSE.txt in the source's root directory. + * + * This file written by Ryan C. Gordon, with some peeking at "unzip.c" + * by Gilles Vollant. + */ + +#if (defined PHYSFS_SUPPORTS_ZIP) + +#include +#include +#include +#ifndef _WIN32_WCE +#include +#include +#endif +#include "physfs.h" +#include "zlib.h" + +#define __PHYSICSFS_INTERNAL__ +#include "physfs_internal.h" + +/* + * A buffer of ZIP_READBUFSIZE is allocated for each compressed file opened, + * and is freed when you close the file; compressed data is read into + * this buffer, and then is decompressed into the buffer passed to + * PHYSFS_read(). + * + * Uncompressed entries in a zipfile do not allocate this buffer; they just + * read data directly into the buffer passed to PHYSFS_read(). + * + * Depending on your speed and memory requirements, you should tweak this + * value. + */ +#define ZIP_READBUFSIZE (16 * 1024) + + +/* + * Entries are "unresolved" until they are first opened. At that time, + * local file headers parsed/validated, data offsets will be updated to look + * at the actual file data instead of the header, and symlinks will be + * followed and optimized. This means that we don't seek and read around the + * archive until forced to do so, and after the first time, we had to do + * less reading and parsing, which is very CD-ROM friendly. + */ +typedef enum +{ + ZIP_UNRESOLVED_FILE, + ZIP_UNRESOLVED_SYMLINK, + ZIP_RESOLVING, + ZIP_RESOLVED, + ZIP_BROKEN_FILE, + ZIP_BROKEN_SYMLINK +} ZipResolveType; + + +/* + * One ZIPentry is kept for each file in an open ZIP archive. + */ +typedef struct _ZIPentry +{ + char *name; /* Name of file in archive */ + struct _ZIPentry *symlink; /* NULL or file we symlink to */ + ZipResolveType resolved; /* Have we resolved file/symlink? */ + PHYSFS_uint32 offset; /* offset of data in archive */ + PHYSFS_uint16 version; /* version made by */ + PHYSFS_uint16 version_needed; /* version needed to extract */ + PHYSFS_uint16 compression_method; /* compression method */ + PHYSFS_uint32 crc; /* crc-32 */ + PHYSFS_uint32 compressed_size; /* compressed size */ + PHYSFS_uint32 uncompressed_size; /* uncompressed size */ + PHYSFS_sint64 last_mod_time; /* last file mod time */ +} ZIPentry; + +/* + * One ZIPinfo is kept for each open ZIP archive. + */ +typedef struct +{ + char *archiveName; /* path to ZIP in platform-dependent notation. */ + PHYSFS_uint16 entryCount; /* Number of files in ZIP. */ + ZIPentry *entries; /* info on all files in ZIP. */ +} ZIPinfo; + +/* + * One ZIPfileinfo is kept for each open file in a ZIP archive. + */ +typedef struct +{ + ZIPentry *entry; /* Info on file. */ + void *handle; /* physical file handle. */ + PHYSFS_uint32 compressed_position; /* offset in compressed data. */ + PHYSFS_uint32 uncompressed_position; /* tell() position. */ + PHYSFS_uint8 *buffer; /* decompression buffer. */ + z_stream stream; /* zlib stream state. */ +} ZIPfileinfo; + + +/* Magic numbers... */ +#define ZIP_LOCAL_FILE_SIG 0x04034b50 +#define ZIP_CENTRAL_DIR_SIG 0x02014b50 +#define ZIP_END_OF_CENTRAL_DIR_SIG 0x06054b50 + +/* compression methods... */ +#define COMPMETH_NONE 0 +/* ...and others... */ + + +#define UNIX_FILETYPE_MASK 0170000 +#define UNIX_FILETYPE_SYMLINK 0120000 + + +/* + * Bridge physfs allocation functions to zlib's format... + */ +static voidpf zlibPhysfsAlloc(voidpf opaque, uInt items, uInt size) +{ + return(((PHYSFS_Allocator *) opaque)->Malloc(items * size)); +} /* zlibPhysfsAlloc */ + +/* + * Bridge physfs allocation functions to zlib's format... + */ +static void zlibPhysfsFree(voidpf opaque, voidpf address) +{ + ((PHYSFS_Allocator *) opaque)->Free(address); +} /* zlibPhysfsFree */ + + +/* + * Construct a new z_stream to a sane state. + */ +static void initializeZStream(z_stream *pstr) +{ + memset(pstr, '\0', sizeof (z_stream)); + pstr->zalloc = zlibPhysfsAlloc; + pstr->zfree = zlibPhysfsFree; + pstr->opaque = &allocator; +} /* initializeZStream */ + + +static const char *zlib_error_string(int rc) +{ + switch (rc) + { + case Z_OK: return(NULL); /* not an error. */ + case Z_STREAM_END: return(NULL); /* not an error. */ +#ifndef _WIN32_WCE + case Z_ERRNO: return(strerror(errno)); +#endif + case Z_NEED_DICT: return(ERR_NEED_DICT); + case Z_DATA_ERROR: return(ERR_DATA_ERROR); + case Z_MEM_ERROR: return(ERR_MEMORY_ERROR); + case Z_BUF_ERROR: return(ERR_BUFFER_ERROR); + case Z_VERSION_ERROR: return(ERR_VERSION_ERROR); + default: return(ERR_UNKNOWN_ERROR); + } /* switch */ + + return(NULL); +} /* zlib_error_string */ + + +/* + * Wrap all zlib calls in this, so the physfs error state is set appropriately. + */ +static int zlib_err(int rc) +{ + const char *str = zlib_error_string(rc); + if (str != NULL) + __PHYSFS_setError(str); + return(rc); +} /* zlib_err */ + + +/* + * Read an unsigned 32-bit int and swap to native byte order. + */ +static int readui32(void *in, PHYSFS_uint32 *val) +{ + PHYSFS_uint32 v; + BAIL_IF_MACRO(__PHYSFS_platformRead(in, &v, sizeof (v), 1) != 1, NULL, 0); + *val = PHYSFS_swapULE32(v); + return(1); +} /* readui32 */ + + +/* + * Read an unsigned 16-bit int and swap to native byte order. + */ +static int readui16(void *in, PHYSFS_uint16 *val) +{ + PHYSFS_uint16 v; + BAIL_IF_MACRO(__PHYSFS_platformRead(in, &v, sizeof (v), 1) != 1, NULL, 0); + *val = PHYSFS_swapULE16(v); + return(1); +} /* readui16 */ + + +static PHYSFS_sint64 ZIP_read(fvoid *opaque, void *buf, + PHYSFS_uint32 objSize, PHYSFS_uint32 objCount) +{ + ZIPfileinfo *finfo = (ZIPfileinfo *) opaque; + ZIPentry *entry = finfo->entry; + PHYSFS_sint64 retval = 0; + PHYSFS_sint64 maxread = ((PHYSFS_sint64) objSize) * objCount; + PHYSFS_sint64 avail = entry->uncompressed_size - + finfo->uncompressed_position; + + BAIL_IF_MACRO(maxread == 0, NULL, 0); /* quick rejection. */ + + if (avail < maxread) + { + maxread = avail - (avail % objSize); + objCount = (PHYSFS_uint32) (maxread / objSize); + BAIL_IF_MACRO(objCount == 0, ERR_PAST_EOF, 0); /* quick rejection. */ + __PHYSFS_setError(ERR_PAST_EOF); /* this is always true here. */ + } /* if */ + + if (entry->compression_method == COMPMETH_NONE) + { + retval = __PHYSFS_platformRead(finfo->handle, buf, objSize, objCount); + } /* if */ + + else + { + finfo->stream.next_out = buf; + finfo->stream.avail_out = objSize * objCount; + + while (retval < maxread) + { + PHYSFS_uint32 before = finfo->stream.total_out; + int rc; + + if (finfo->stream.avail_in == 0) + { + PHYSFS_sint64 br; + + br = entry->compressed_size - finfo->compressed_position; + if (br > 0) + { + if (br > ZIP_READBUFSIZE) + br = ZIP_READBUFSIZE; + + br = __PHYSFS_platformRead(finfo->handle, + finfo->buffer, + 1, (PHYSFS_uint32) br); + if (br <= 0) + break; + + finfo->compressed_position += (PHYSFS_uint32) br; + finfo->stream.next_in = finfo->buffer; + finfo->stream.avail_in = (PHYSFS_uint32) br; + } /* if */ + } /* if */ + + rc = zlib_err(inflate(&finfo->stream, Z_SYNC_FLUSH)); + retval += (finfo->stream.total_out - before); + + if (rc != Z_OK) + break; + } /* while */ + + retval /= objSize; + } /* else */ + + if (retval > 0) + finfo->uncompressed_position += (PHYSFS_uint32) (retval * objSize); + + return(retval); +} /* ZIP_read */ + + +static PHYSFS_sint64 ZIP_write(fvoid *opaque, const void *buf, + PHYSFS_uint32 objSize, PHYSFS_uint32 objCount) +{ + BAIL_MACRO(ERR_NOT_SUPPORTED, -1); +} /* ZIP_write */ + + +static int ZIP_eof(fvoid *opaque) +{ + ZIPfileinfo *finfo = (ZIPfileinfo *) opaque; + return(finfo->uncompressed_position >= finfo->entry->uncompressed_size); +} /* ZIP_eof */ + + +static PHYSFS_sint64 ZIP_tell(fvoid *opaque) +{ + return(((ZIPfileinfo *) opaque)->uncompressed_position); +} /* ZIP_tell */ + + +static int ZIP_seek(fvoid *opaque, PHYSFS_uint64 offset) +{ + ZIPfileinfo *finfo = (ZIPfileinfo *) opaque; + ZIPentry *entry = finfo->entry; + void *in = finfo->handle; + + BAIL_IF_MACRO(offset > entry->uncompressed_size, ERR_PAST_EOF, 0); + + if (entry->compression_method == COMPMETH_NONE) + { + PHYSFS_sint64 newpos = offset + entry->offset; + BAIL_IF_MACRO(!__PHYSFS_platformSeek(in, newpos), NULL, 0); + finfo->uncompressed_position = (PHYSFS_uint32) offset; + } /* if */ + + else + { + /* + * If seeking backwards, we need to redecode the file + * from the start and throw away the compressed bits until we hit + * the offset we need. If seeking forward, we still need to + * decode, but we don't rewind first. + */ + if (offset < finfo->uncompressed_position) + { + /* we do a copy so state is sane if inflateInit2() fails. */ + z_stream str; + initializeZStream(&str); + if (zlib_err(inflateInit2(&str, -MAX_WBITS)) != Z_OK) + return(0); + + if (!__PHYSFS_platformSeek(in, entry->offset)) + return(0); + + inflateEnd(&finfo->stream); + memcpy(&finfo->stream, &str, sizeof (z_stream)); + finfo->uncompressed_position = finfo->compressed_position = 0; + } /* if */ + + while (finfo->uncompressed_position != offset) + { + PHYSFS_uint8 buf[512]; + PHYSFS_uint32 maxread; + + maxread = (PHYSFS_uint32) (offset - finfo->uncompressed_position); + if (maxread > sizeof (buf)) + maxread = sizeof (buf); + + if (ZIP_read(finfo, buf, maxread, 1) != 1) + return(0); + } /* while */ + } /* else */ + + return(1); +} /* ZIP_seek */ + + +static PHYSFS_sint64 ZIP_fileLength(fvoid *opaque) +{ + ZIPfileinfo *finfo = (ZIPfileinfo *) opaque; + return(finfo->entry->uncompressed_size); +} /* ZIP_fileLength */ + + +static int ZIP_fileClose(fvoid *opaque) +{ + ZIPfileinfo *finfo = (ZIPfileinfo *) opaque; + BAIL_IF_MACRO(!__PHYSFS_platformClose(finfo->handle), NULL, 0); + + if (finfo->entry->compression_method != COMPMETH_NONE) + inflateEnd(&finfo->stream); + + if (finfo->buffer != NULL) + allocator.Free(finfo->buffer); + + allocator.Free(finfo); + return(1); +} /* ZIP_fileClose */ + + +static PHYSFS_sint64 zip_find_end_of_central_dir(void *in, PHYSFS_sint64 *len) +{ + PHYSFS_uint8 buf[256]; + PHYSFS_uint8 extra[4] = { 0, 0, 0, 0 }; + PHYSFS_sint32 i = 0; + PHYSFS_sint64 filelen; + PHYSFS_sint64 filepos; + PHYSFS_sint32 maxread; + PHYSFS_sint32 totalread = 0; + int found = 0; + + filelen = __PHYSFS_platformFileLength(in); + BAIL_IF_MACRO(filelen == -1, NULL, 0); /* !!! FIXME: unlocalized string */ + BAIL_IF_MACRO(filelen > 0xFFFFFFFF, "ZIP bigger than 2 gigs?!", 0); + + /* + * Jump to the end of the file and start reading backwards. + * The last thing in the file is the zipfile comment, which is variable + * length, and the field that specifies its size is before it in the + * file (argh!)...this means that we need to scan backwards until we + * hit the end-of-central-dir signature. We can then sanity check that + * the comment was as big as it should be to make sure we're in the + * right place. The comment length field is 16 bits, so we can stop + * searching for that signature after a little more than 64k at most, + * and call it a corrupted zipfile. + */ + + if (sizeof (buf) < filelen) + { + filepos = filelen - sizeof (buf); + maxread = sizeof (buf); + } /* if */ + else + { + filepos = 0; + maxread = (PHYSFS_uint32) filelen; + } /* else */ + + while ((totalread < filelen) && (totalread < 65557)) + { + BAIL_IF_MACRO(!__PHYSFS_platformSeek(in, filepos), NULL, -1); + + /* make sure we catch a signature between buffers. */ + if (totalread != 0) + { + if (__PHYSFS_platformRead(in, buf, maxread - 4, 1) != 1) + return(-1); + memcpy(&buf[maxread - 4], &extra, sizeof (extra)); + totalread += maxread - 4; + } /* if */ + else + { + if (__PHYSFS_platformRead(in, buf, maxread, 1) != 1) + return(-1); + totalread += maxread; + } /* else */ + + memcpy(&extra, buf, sizeof (extra)); + + for (i = maxread - 4; i > 0; i--) + { + if ((buf[i + 0] == 0x50) && + (buf[i + 1] == 0x4B) && + (buf[i + 2] == 0x05) && + (buf[i + 3] == 0x06) ) + { + found = 1; /* that's the signature! */ + break; + } /* if */ + } /* for */ + + if (found) + break; + + filepos -= (maxread - 4); + if (filepos < 0) + filepos = 0; + } /* while */ + + BAIL_IF_MACRO(!found, ERR_NOT_AN_ARCHIVE, -1); + + if (len != NULL) + *len = filelen; + + return(filepos + i); +} /* zip_find_end_of_central_dir */ + + +static int ZIP_isArchive(const char *filename, int forWriting) +{ + PHYSFS_uint32 sig; + int retval = 0; + void *in; + + in = __PHYSFS_platformOpenRead(filename); + BAIL_IF_MACRO(in == NULL, NULL, 0); + + /* + * The first thing in a zip file might be the signature of the + * first local file record, so it makes for a quick determination. + */ + if (readui32(in, &sig)) + { + retval = (sig == ZIP_LOCAL_FILE_SIG); + if (!retval) + { + /* + * No sig...might be a ZIP with data at the start + * (a self-extracting executable, etc), so we'll have to do + * it the hard way... + */ + retval = (zip_find_end_of_central_dir(in, NULL) != -1); + } /* if */ + } /* if */ + + __PHYSFS_platformClose(in); + return(retval); +} /* ZIP_isArchive */ + + +static void zip_free_entries(ZIPentry *entries, PHYSFS_uint32 max) +{ + PHYSFS_uint32 i; + for (i = 0; i < max; i++) + { + ZIPentry *entry = &entries[i]; + if (entry->name != NULL) + allocator.Free(entry->name); + } /* for */ + + allocator.Free(entries); +} /* zip_free_entries */ + + +/* + * This will find the ZIPentry associated with a path in platform-independent + * notation. Directories don't have ZIPentries associated with them, but + * (*isDir) will be set to non-zero if a dir was hit. + */ +static ZIPentry *zip_find_entry(ZIPinfo *info, const char *path, int *isDir) +{ + ZIPentry *a = info->entries; + PHYSFS_sint32 pathlen = strlen(path); + PHYSFS_sint32 lo = 0; + PHYSFS_sint32 hi = (PHYSFS_sint32) (info->entryCount - 1); + PHYSFS_sint32 middle; + const char *thispath = NULL; + int rc; + + while (lo <= hi) + { + middle = lo + ((hi - lo) / 2); + thispath = a[middle].name; + rc = strncmp(path, thispath, pathlen); + + if (rc > 0) + lo = middle + 1; + + else if (rc < 0) + hi = middle - 1; + + else /* substring match...might be dir or entry or nothing. */ + { + if (isDir != NULL) + { + *isDir = (thispath[pathlen] == '/'); + if (*isDir) + return(NULL); + } /* if */ + + if (thispath[pathlen] == '\0') /* found entry? */ + return(&a[middle]); + /* adjust search params, try again. */ + else if (thispath[pathlen] > '/') + hi = middle - 1; + else + lo = middle + 1; + } /* if */ + } /* while */ + + if (isDir != NULL) + *isDir = 0; + + BAIL_MACRO(ERR_NO_SUCH_FILE, NULL); +} /* zip_find_entry */ + + +/* Convert paths from old, buggy DOS zippers... */ +static void zip_convert_dos_path(ZIPentry *entry, char *path) +{ + PHYSFS_uint8 hosttype = (PHYSFS_uint8) ((entry->version >> 8) & 0xFF); + if (hosttype == 0) /* FS_FAT_ */ + { + while (*path) + { + if (*path == '\\') + *path = '/'; + path++; + } /* while */ + } /* if */ +} /* zip_convert_dos_path */ + + +static void zip_expand_symlink_path(char *path) +{ + char *ptr = path; + char *prevptr = path; + + while (1) + { + ptr = strchr(ptr, '/'); + if (ptr == NULL) + break; + + if (*(ptr + 1) == '.') + { + if (*(ptr + 2) == '/') + { + /* current dir in middle of string: ditch it. */ + memmove(ptr, ptr + 2, strlen(ptr + 2) + 1); + } /* else if */ + + else if (*(ptr + 2) == '\0') + { + /* current dir at end of string: ditch it. */ + *ptr = '\0'; + } /* else if */ + + else if (*(ptr + 2) == '.') + { + if (*(ptr + 3) == '/') + { + /* parent dir in middle: move back one, if possible. */ + memmove(prevptr, ptr + 4, strlen(ptr + 4) + 1); + ptr = prevptr; + while (prevptr != path) + { + prevptr--; + if (*prevptr == '/') + { + prevptr++; + break; + } /* if */ + } /* while */ + } /* if */ + + if (*(ptr + 3) == '\0') + { + /* parent dir at end: move back one, if possible. */ + *prevptr = '\0'; + } /* if */ + } /* if */ + } /* if */ + else + { + prevptr = ptr; + } /* else */ + } /* while */ +} /* zip_expand_symlink_path */ + +/* (forward reference: zip_follow_symlink and zip_resolve call each other.) */ +static int zip_resolve(void *in, ZIPinfo *info, ZIPentry *entry); + +/* + * Look for the entry named by (path). If it exists, resolve it, and return + * a pointer to that entry. If it's another symlink, keep resolving until you + * hit a real file and then return a pointer to the final non-symlink entry. + * If there's a problem, return NULL. (path) is always free()'d by this + * function. + */ +static ZIPentry *zip_follow_symlink(void *in, ZIPinfo *info, char *path) +{ + ZIPentry *entry; + + zip_expand_symlink_path(path); + entry = zip_find_entry(info, path, NULL); + if (entry != NULL) + { + if (!zip_resolve(in, info, entry)) /* recursive! */ + entry = NULL; + else + { + if (entry->symlink != NULL) + entry = entry->symlink; + } /* else */ + } /* if */ + + allocator.Free(path); + return(entry); +} /* zip_follow_symlink */ + + +static int zip_resolve_symlink(void *in, ZIPinfo *info, ZIPentry *entry) +{ + char *path; + PHYSFS_uint32 size = entry->uncompressed_size; + int rc = 0; + + /* + * We've already parsed the local file header of the symlink at this + * point. Now we need to read the actual link from the file data and + * follow it. + */ + + BAIL_IF_MACRO(!__PHYSFS_platformSeek(in, entry->offset), NULL, 0); + + path = (char *) allocator.Malloc(size + 1); + BAIL_IF_MACRO(path == NULL, ERR_OUT_OF_MEMORY, 0); + + if (entry->compression_method == COMPMETH_NONE) + rc = (__PHYSFS_platformRead(in, path, size, 1) == 1); + + else /* symlink target path is compressed... */ + { + z_stream stream; + PHYSFS_uint32 complen = entry->compressed_size; + PHYSFS_uint8 *compressed = (PHYSFS_uint8*) __PHYSFS_smallAlloc(complen); + if (compressed != NULL) + { + if (__PHYSFS_platformRead(in, compressed, complen, 1) == 1) + { + initializeZStream(&stream); + stream.next_in = compressed; + stream.avail_in = complen; + stream.next_out = (unsigned char *) path; + stream.avail_out = size; + if (zlib_err(inflateInit2(&stream, -MAX_WBITS)) == Z_OK) + { + rc = zlib_err(inflate(&stream, Z_FINISH)); + inflateEnd(&stream); + + /* both are acceptable outcomes... */ + rc = ((rc == Z_OK) || (rc == Z_STREAM_END)); + } /* if */ + } /* if */ + __PHYSFS_smallFree(compressed); + } /* if */ + } /* else */ + + if (!rc) + allocator.Free(path); + else + { + path[entry->uncompressed_size] = '\0'; /* null-terminate it. */ + zip_convert_dos_path(entry, path); + entry->symlink = zip_follow_symlink(in, info, path); + } /* else */ + + return(entry->symlink != NULL); +} /* zip_resolve_symlink */ + + +/* + * Parse the local file header of an entry, and update entry->offset. + */ +static int zip_parse_local(void *in, ZIPentry *entry) +{ + PHYSFS_uint32 ui32; + PHYSFS_uint16 ui16; + PHYSFS_uint16 fnamelen; + PHYSFS_uint16 extralen; + + /* + * crc and (un)compressed_size are always zero if this is a "JAR" + * archive created with Sun's Java tools, apparently. We only + * consider this archive corrupted if those entries don't match and + * aren't zero. That seems to work well. + */ + + BAIL_IF_MACRO(!__PHYSFS_platformSeek(in, entry->offset), NULL, 0); + BAIL_IF_MACRO(!readui32(in, &ui32), NULL, 0); + BAIL_IF_MACRO(ui32 != ZIP_LOCAL_FILE_SIG, ERR_CORRUPTED, 0); + BAIL_IF_MACRO(!readui16(in, &ui16), NULL, 0); + BAIL_IF_MACRO(ui16 != entry->version_needed, ERR_CORRUPTED, 0); + BAIL_IF_MACRO(!readui16(in, &ui16), NULL, 0); /* general bits. */ + BAIL_IF_MACRO(!readui16(in, &ui16), NULL, 0); + BAIL_IF_MACRO(ui16 != entry->compression_method, ERR_CORRUPTED, 0); + BAIL_IF_MACRO(!readui32(in, &ui32), NULL, 0); /* date/time */ + BAIL_IF_MACRO(!readui32(in, &ui32), NULL, 0); + BAIL_IF_MACRO(ui32 && (ui32 != entry->crc), ERR_CORRUPTED, 0); + BAIL_IF_MACRO(!readui32(in, &ui32), NULL, 0); + BAIL_IF_MACRO(ui32 && (ui32 != entry->compressed_size), ERR_CORRUPTED, 0); + BAIL_IF_MACRO(!readui32(in, &ui32), NULL, 0); + BAIL_IF_MACRO(ui32 && (ui32 != entry->uncompressed_size),ERR_CORRUPTED,0); + BAIL_IF_MACRO(!readui16(in, &fnamelen), NULL, 0); + BAIL_IF_MACRO(!readui16(in, &extralen), NULL, 0); + + entry->offset += fnamelen + extralen + 30; + return(1); +} /* zip_parse_local */ + + +static int zip_resolve(void *in, ZIPinfo *info, ZIPentry *entry) +{ + int retval = 1; + ZipResolveType resolve_type = entry->resolved; + + /* Don't bother if we've failed to resolve this entry before. */ + BAIL_IF_MACRO(resolve_type == ZIP_BROKEN_FILE, ERR_CORRUPTED, 0); + BAIL_IF_MACRO(resolve_type == ZIP_BROKEN_SYMLINK, ERR_CORRUPTED, 0); + + /* uhoh...infinite symlink loop! */ + BAIL_IF_MACRO(resolve_type == ZIP_RESOLVING, ERR_SYMLINK_LOOP, 0); + + /* + * We fix up the offset to point to the actual data on the + * first open, since we don't want to seek across the whole file on + * archive open (can be SLOW on large, CD-stored files), but we + * need to check the local file header...not just for corruption, + * but since it stores offset info the central directory does not. + */ + if (resolve_type != ZIP_RESOLVED) + { + entry->resolved = ZIP_RESOLVING; + + retval = zip_parse_local(in, entry); + if (retval) + { + /* + * If it's a symlink, find the original file. This will cause + * resolution of other entries (other symlinks and, eventually, + * the real file) if all goes well. + */ + if (resolve_type == ZIP_UNRESOLVED_SYMLINK) + retval = zip_resolve_symlink(in, info, entry); + } /* if */ + + if (resolve_type == ZIP_UNRESOLVED_SYMLINK) + entry->resolved = ((retval) ? ZIP_RESOLVED : ZIP_BROKEN_SYMLINK); + else if (resolve_type == ZIP_UNRESOLVED_FILE) + entry->resolved = ((retval) ? ZIP_RESOLVED : ZIP_BROKEN_FILE); + } /* if */ + + return(retval); +} /* zip_resolve */ + + +static int zip_version_does_symlinks(PHYSFS_uint32 version) +{ + int retval = 0; + PHYSFS_uint8 hosttype = (PHYSFS_uint8) ((version >> 8) & 0xFF); + + switch (hosttype) + { + /* + * These are the platforms that can NOT build an archive with + * symlinks, according to the Info-ZIP project. + */ + case 0: /* FS_FAT_ */ + case 1: /* AMIGA_ */ + case 2: /* VMS_ */ + case 4: /* VM_CSM_ */ + case 6: /* FS_HPFS_ */ + case 11: /* FS_NTFS_ */ + case 14: /* FS_VFAT_ */ + case 13: /* ACORN_ */ + case 15: /* MVS_ */ + case 18: /* THEOS_ */ + break; /* do nothing. */ + + default: /* assume the rest to be unix-like. */ + retval = 1; + break; + } /* switch */ + + return(retval); +} /* zip_version_does_symlinks */ + + +static int zip_entry_is_symlink(const ZIPentry *entry) +{ + return((entry->resolved == ZIP_UNRESOLVED_SYMLINK) || + (entry->resolved == ZIP_BROKEN_SYMLINK) || + (entry->symlink)); +} /* zip_entry_is_symlink */ + + +static int zip_has_symlink_attr(ZIPentry *entry, PHYSFS_uint32 extern_attr) +{ + PHYSFS_uint16 xattr = ((extern_attr >> 16) & 0xFFFF); + + return ( + (zip_version_does_symlinks(entry->version)) && + (entry->uncompressed_size > 0) && + ((xattr & UNIX_FILETYPE_MASK) == UNIX_FILETYPE_SYMLINK) + ); +} /* zip_has_symlink_attr */ + + +static PHYSFS_sint64 zip_dos_time_to_physfs_time(PHYSFS_uint32 dostime) +{ +#ifdef _WIN32_WCE + /* We have no struct tm and no mktime right now. + FIXME: This should probably be fixed at some point. + */ + return -1; +#else + PHYSFS_uint32 dosdate; + struct tm unixtime; + memset(&unixtime, '\0', sizeof (unixtime)); + + dosdate = (PHYSFS_uint32) ((dostime >> 16) & 0xFFFF); + dostime &= 0xFFFF; + + /* dissect date */ + unixtime.tm_year = ((dosdate >> 9) & 0x7F) + 80; + unixtime.tm_mon = ((dosdate >> 5) & 0x0F) - 1; + unixtime.tm_mday = ((dosdate ) & 0x1F); + + /* dissect time */ + unixtime.tm_hour = ((dostime >> 11) & 0x1F); + unixtime.tm_min = ((dostime >> 5) & 0x3F); + unixtime.tm_sec = ((dostime << 1) & 0x3E); + + /* let mktime calculate daylight savings time. */ + unixtime.tm_isdst = -1; + + return((PHYSFS_sint64) mktime(&unixtime)); +#endif +} /* zip_dos_time_to_physfs_time */ + + +static int zip_load_entry(void *in, ZIPentry *entry, PHYSFS_uint32 ofs_fixup) +{ + PHYSFS_uint16 fnamelen, extralen, commentlen; + PHYSFS_uint32 external_attr; + PHYSFS_uint16 ui16; + PHYSFS_uint32 ui32; + PHYSFS_sint64 si64; + + /* sanity check with central directory signature... */ + BAIL_IF_MACRO(!readui32(in, &ui32), NULL, 0); + BAIL_IF_MACRO(ui32 != ZIP_CENTRAL_DIR_SIG, ERR_CORRUPTED, 0); + + /* Get the pertinent parts of the record... */ + BAIL_IF_MACRO(!readui16(in, &entry->version), NULL, 0); + BAIL_IF_MACRO(!readui16(in, &entry->version_needed), NULL, 0); + BAIL_IF_MACRO(!readui16(in, &ui16), NULL, 0); /* general bits */ + BAIL_IF_MACRO(!readui16(in, &entry->compression_method), NULL, 0); + BAIL_IF_MACRO(!readui32(in, &ui32), NULL, 0); + entry->last_mod_time = zip_dos_time_to_physfs_time(ui32); + BAIL_IF_MACRO(!readui32(in, &entry->crc), NULL, 0); + BAIL_IF_MACRO(!readui32(in, &entry->compressed_size), NULL, 0); + BAIL_IF_MACRO(!readui32(in, &entry->uncompressed_size), NULL, 0); + BAIL_IF_MACRO(!readui16(in, &fnamelen), NULL, 0); + BAIL_IF_MACRO(!readui16(in, &extralen), NULL, 0); + BAIL_IF_MACRO(!readui16(in, &commentlen), NULL, 0); + BAIL_IF_MACRO(!readui16(in, &ui16), NULL, 0); /* disk number start */ + BAIL_IF_MACRO(!readui16(in, &ui16), NULL, 0); /* internal file attribs */ + BAIL_IF_MACRO(!readui32(in, &external_attr), NULL, 0); + BAIL_IF_MACRO(!readui32(in, &entry->offset), NULL, 0); + entry->offset += ofs_fixup; + + entry->symlink = NULL; /* will be resolved later, if necessary. */ + entry->resolved = (zip_has_symlink_attr(entry, external_attr)) ? + ZIP_UNRESOLVED_SYMLINK : ZIP_UNRESOLVED_FILE; + + entry->name = (char *) allocator.Malloc(fnamelen + 1); + BAIL_IF_MACRO(entry->name == NULL, ERR_OUT_OF_MEMORY, 0); + if (__PHYSFS_platformRead(in, entry->name, fnamelen, 1) != 1) + goto zip_load_entry_puked; + + entry->name[fnamelen] = '\0'; /* null-terminate the filename. */ + zip_convert_dos_path(entry, entry->name); + + si64 = __PHYSFS_platformTell(in); + if (si64 == -1) + goto zip_load_entry_puked; + + /* seek to the start of the next entry in the central directory... */ + if (!__PHYSFS_platformSeek(in, si64 + extralen + commentlen)) + goto zip_load_entry_puked; + + return(1); /* success. */ + +zip_load_entry_puked: + allocator.Free(entry->name); + return(0); /* failure. */ +} /* zip_load_entry */ + + +static int zip_entry_cmp(void *_a, PHYSFS_uint32 one, PHYSFS_uint32 two) +{ + if (one != two) + { + const ZIPentry *a = (const ZIPentry *) _a; + return(strcmp(a[one].name, a[two].name)); + } /* if */ + + return 0; +} /* zip_entry_cmp */ + + +static void zip_entry_swap(void *_a, PHYSFS_uint32 one, PHYSFS_uint32 two) +{ + if (one != two) + { + ZIPentry tmp; + ZIPentry *first = &(((ZIPentry *) _a)[one]); + ZIPentry *second = &(((ZIPentry *) _a)[two]); + memcpy(&tmp, first, sizeof (ZIPentry)); + memcpy(first, second, sizeof (ZIPentry)); + memcpy(second, &tmp, sizeof (ZIPentry)); + } /* if */ +} /* zip_entry_swap */ + + +static int zip_load_entries(void *in, ZIPinfo *info, + PHYSFS_uint32 data_ofs, PHYSFS_uint32 central_ofs) +{ + PHYSFS_uint32 max = info->entryCount; + PHYSFS_uint32 i; + + BAIL_IF_MACRO(!__PHYSFS_platformSeek(in, central_ofs), NULL, 0); + + info->entries = (ZIPentry *) allocator.Malloc(sizeof (ZIPentry) * max); + BAIL_IF_MACRO(info->entries == NULL, ERR_OUT_OF_MEMORY, 0); + + for (i = 0; i < max; i++) + { + if (!zip_load_entry(in, &info->entries[i], data_ofs)) + { + zip_free_entries(info->entries, i); + return(0); + } /* if */ + } /* for */ + + __PHYSFS_sort(info->entries, max, zip_entry_cmp, zip_entry_swap); + return(1); +} /* zip_load_entries */ + + +static int zip_parse_end_of_central_dir(void *in, ZIPinfo *info, + PHYSFS_uint32 *data_start, + PHYSFS_uint32 *central_dir_ofs) +{ + PHYSFS_uint32 ui32; + PHYSFS_uint16 ui16; + PHYSFS_sint64 len; + PHYSFS_sint64 pos; + + /* find the end-of-central-dir record, and seek to it. */ + pos = zip_find_end_of_central_dir(in, &len); + BAIL_IF_MACRO(pos == -1, NULL, 0); + BAIL_IF_MACRO(!__PHYSFS_platformSeek(in, pos), NULL, 0); + + /* check signature again, just in case. */ + BAIL_IF_MACRO(!readui32(in, &ui32), NULL, 0); + BAIL_IF_MACRO(ui32 != ZIP_END_OF_CENTRAL_DIR_SIG, ERR_NOT_AN_ARCHIVE, 0); + + /* number of this disk */ + BAIL_IF_MACRO(!readui16(in, &ui16), NULL, 0); + BAIL_IF_MACRO(ui16 != 0, ERR_UNSUPPORTED_ARCHIVE, 0); + + /* number of the disk with the start of the central directory */ + BAIL_IF_MACRO(!readui16(in, &ui16), NULL, 0); + BAIL_IF_MACRO(ui16 != 0, ERR_UNSUPPORTED_ARCHIVE, 0); + + /* total number of entries in the central dir on this disk */ + BAIL_IF_MACRO(!readui16(in, &ui16), NULL, 0); + + /* total number of entries in the central dir */ + BAIL_IF_MACRO(!readui16(in, &info->entryCount), NULL, 0); + BAIL_IF_MACRO(ui16 != info->entryCount, ERR_UNSUPPORTED_ARCHIVE, 0); + + /* size of the central directory */ + BAIL_IF_MACRO(!readui32(in, &ui32), NULL, 0); + + /* offset of central directory */ + BAIL_IF_MACRO(!readui32(in, central_dir_ofs), NULL, 0); + BAIL_IF_MACRO(pos < *central_dir_ofs + ui32, ERR_UNSUPPORTED_ARCHIVE, 0); + + /* + * For self-extracting archives, etc, there's crapola in the file + * before the zipfile records; we calculate how much data there is + * prepended by determining how far the central directory offset is + * from where it is supposed to be (start of end-of-central-dir minus + * sizeof central dir)...the difference in bytes is how much arbitrary + * data is at the start of the physical file. + */ + *data_start = (PHYSFS_uint32) (pos - (*central_dir_ofs + ui32)); + + /* Now that we know the difference, fix up the central dir offset... */ + *central_dir_ofs += *data_start; + + /* zipfile comment length */ + BAIL_IF_MACRO(!readui16(in, &ui16), NULL, 0); + + /* + * Make sure that the comment length matches to the end of file... + * If it doesn't, we're either in the wrong part of the file, or the + * file is corrupted, but we give up either way. + */ + BAIL_IF_MACRO((pos + 22 + ui16) != len, ERR_UNSUPPORTED_ARCHIVE, 0); + + return(1); /* made it. */ +} /* zip_parse_end_of_central_dir */ + + +static ZIPinfo *zip_create_zipinfo(const char *name) +{ + char *ptr; + ZIPinfo *info = (ZIPinfo *) allocator.Malloc(sizeof (ZIPinfo)); + BAIL_IF_MACRO(info == NULL, ERR_OUT_OF_MEMORY, 0); + memset(info, '\0', sizeof (ZIPinfo)); + + ptr = (char *) allocator.Malloc(strlen(name) + 1); + if (ptr == NULL) + { + allocator.Free(info); + BAIL_MACRO(ERR_OUT_OF_MEMORY, NULL); + } /* if */ + + info->archiveName = ptr; + strcpy(info->archiveName, name); + return(info); +} /* zip_create_zipinfo */ + + +static void *ZIP_openArchive(const char *name, int forWriting) +{ + void *in = NULL; + ZIPinfo *info = NULL; + PHYSFS_uint32 data_start; + PHYSFS_uint32 cent_dir_ofs; + + BAIL_IF_MACRO(forWriting, ERR_ARC_IS_READ_ONLY, NULL); + + if ((in = __PHYSFS_platformOpenRead(name)) == NULL) + goto zip_openarchive_failed; + + if ((info = zip_create_zipinfo(name)) == NULL) + goto zip_openarchive_failed; + + if (!zip_parse_end_of_central_dir(in, info, &data_start, ¢_dir_ofs)) + goto zip_openarchive_failed; + + if (!zip_load_entries(in, info, data_start, cent_dir_ofs)) + goto zip_openarchive_failed; + + __PHYSFS_platformClose(in); + return(info); + +zip_openarchive_failed: + if (info != NULL) + { + if (info->archiveName != NULL) + allocator.Free(info->archiveName); + allocator.Free(info); + } /* if */ + + if (in != NULL) + __PHYSFS_platformClose(in); + + return(NULL); +} /* ZIP_openArchive */ + + +static PHYSFS_sint32 zip_find_start_of_dir(ZIPinfo *info, const char *path, + int stop_on_first_find) +{ + PHYSFS_sint32 lo = 0; + PHYSFS_sint32 hi = (PHYSFS_sint32) (info->entryCount - 1); + PHYSFS_sint32 middle; + PHYSFS_uint32 dlen = strlen(path); + PHYSFS_sint32 retval = -1; + const char *name; + int rc; + + if (*path == '\0') /* root dir? */ + return(0); + + if ((dlen > 0) && (path[dlen - 1] == '/')) /* ignore trailing slash. */ + dlen--; + + while (lo <= hi) + { + middle = lo + ((hi - lo) / 2); + name = info->entries[middle].name; + rc = strncmp(path, name, dlen); + if (rc == 0) + { + char ch = name[dlen]; + if ('/' < ch) /* make sure this isn't just a substr match. */ + rc = -1; + else if ('/' > ch) + rc = 1; + else + { + if (stop_on_first_find) /* Just checking dir's existance? */ + return(middle); + + if (name[dlen + 1] == '\0') /* Skip initial dir entry. */ + return(middle + 1); + + /* there might be more entries earlier in the list. */ + retval = middle; + hi = middle - 1; + } /* else */ + } /* if */ + + if (rc > 0) + lo = middle + 1; + else + hi = middle - 1; + } /* while */ + + return(retval); +} /* zip_find_start_of_dir */ + + +/* + * Moved to seperate function so we can use alloca then immediately throw + * away the allocated stack space... + */ +static void doEnumCallback(PHYSFS_EnumFilesCallback cb, void *callbackdata, + const char *odir, const char *str, PHYSFS_sint32 ln) +{ + char *newstr = __PHYSFS_smallAlloc(ln + 1); + if (newstr == NULL) + return; + + memcpy(newstr, str, ln); + newstr[ln] = '\0'; + cb(callbackdata, odir, newstr); + __PHYSFS_smallFree(newstr); +} /* doEnumCallback */ + + +static void ZIP_enumerateFiles(dvoid *opaque, const char *dname, + int omitSymLinks, PHYSFS_EnumFilesCallback cb, + const char *origdir, void *callbackdata) +{ + ZIPinfo *info = ((ZIPinfo *) opaque); + PHYSFS_sint32 dlen, dlen_inc, max, i; + + i = zip_find_start_of_dir(info, dname, 0); + if (i == -1) /* no such directory. */ + return; + + dlen = strlen(dname); + if ((dlen > 0) && (dname[dlen - 1] == '/')) /* ignore trailing slash. */ + dlen--; + + dlen_inc = ((dlen > 0) ? 1 : 0) + dlen; + max = (PHYSFS_sint32) info->entryCount; + while (i < max) + { + char *e = info->entries[i].name; + if ((dlen) && ((strncmp(e, dname, dlen) != 0) || (e[dlen] != '/'))) + break; /* past end of this dir; we're done. */ + + if ((omitSymLinks) && (zip_entry_is_symlink(&info->entries[i]))) + i++; + else + { + char *add = e + dlen_inc; + char *ptr = strchr(add, '/'); + PHYSFS_sint32 ln = (PHYSFS_sint32) ((ptr) ? ptr-add : strlen(add)); + doEnumCallback(cb, callbackdata, origdir, add, ln); + ln += dlen_inc; /* point past entry to children... */ + + /* increment counter and skip children of subdirs... */ + while ((++i < max) && (ptr != NULL)) + { + char *e_new = info->entries[i].name; + if ((strncmp(e, e_new, ln) != 0) || (e_new[ln] != '/')) + break; + } /* while */ + } /* else */ + } /* while */ +} /* ZIP_enumerateFiles */ + + +static int ZIP_exists(dvoid *opaque, const char *name) +{ + int isDir; + ZIPinfo *info = (ZIPinfo *) opaque; + ZIPentry *entry = zip_find_entry(info, name, &isDir); + return((entry != NULL) || (isDir)); +} /* ZIP_exists */ + + +static PHYSFS_sint64 ZIP_getLastModTime(dvoid *opaque, + const char *name, + int *fileExists) +{ + int isDir; + ZIPinfo *info = (ZIPinfo *) opaque; + ZIPentry *entry = zip_find_entry(info, name, &isDir); + + *fileExists = ((isDir) || (entry != NULL)); + if (isDir) + return(1); /* Best I can do for a dir... */ + + BAIL_IF_MACRO(entry == NULL, NULL, -1); + return(entry->last_mod_time); +} /* ZIP_getLastModTime */ + + +static int ZIP_isDirectory(dvoid *opaque, const char *name, int *fileExists) +{ + ZIPinfo *info = (ZIPinfo *) opaque; + int isDir; + ZIPentry *entry = zip_find_entry(info, name, &isDir); + + *fileExists = ((isDir) || (entry != NULL)); + if (isDir) + return(1); /* definitely a dir. */ + + /* Follow symlinks. This means we might need to resolve entries. */ + BAIL_IF_MACRO(entry == NULL, ERR_NO_SUCH_FILE, 0); + + if (entry->resolved == ZIP_UNRESOLVED_SYMLINK) /* gotta resolve it. */ + { + int rc; + void *in = __PHYSFS_platformOpenRead(info->archiveName); + BAIL_IF_MACRO(in == NULL, NULL, 0); + rc = zip_resolve(in, info, entry); + __PHYSFS_platformClose(in); + if (!rc) + return(0); + } /* if */ + + BAIL_IF_MACRO(entry->resolved == ZIP_BROKEN_SYMLINK, NULL, 0); + BAIL_IF_MACRO(entry->symlink == NULL, ERR_NOT_A_DIR, 0); + + return(zip_find_start_of_dir(info, entry->symlink->name, 1) >= 0); +} /* ZIP_isDirectory */ + + +static int ZIP_isSymLink(dvoid *opaque, const char *name, int *fileExists) +{ + int isDir; + const ZIPentry *entry = zip_find_entry((ZIPinfo *) opaque, name, &isDir); + *fileExists = ((isDir) || (entry != NULL)); + BAIL_IF_MACRO(entry == NULL, NULL, 0); + return(zip_entry_is_symlink(entry)); +} /* ZIP_isSymLink */ + + +static void *zip_get_file_handle(const char *fn, ZIPinfo *inf, ZIPentry *entry) +{ + int success; + void *retval = __PHYSFS_platformOpenRead(fn); + BAIL_IF_MACRO(retval == NULL, NULL, NULL); + + success = zip_resolve(retval, inf, entry); + if (success) + { + PHYSFS_sint64 offset; + offset = ((entry->symlink) ? entry->symlink->offset : entry->offset); + success = __PHYSFS_platformSeek(retval, offset); + } /* if */ + + if (!success) + { + __PHYSFS_platformClose(retval); + retval = NULL; + } /* if */ + + return(retval); +} /* zip_get_file_handle */ + + +static fvoid *ZIP_openRead(dvoid *opaque, const char *fnm, int *fileExists) +{ + ZIPinfo *info = (ZIPinfo *) opaque; + ZIPentry *entry = zip_find_entry(info, fnm, NULL); + ZIPfileinfo *finfo = NULL; + void *in; + + *fileExists = (entry != NULL); + BAIL_IF_MACRO(entry == NULL, NULL, NULL); + + in = zip_get_file_handle(info->archiveName, info, entry); + BAIL_IF_MACRO(in == NULL, NULL, NULL); + + finfo = (ZIPfileinfo *) allocator.Malloc(sizeof (ZIPfileinfo)); + if (finfo == NULL) + { + __PHYSFS_platformClose(in); + BAIL_MACRO(ERR_OUT_OF_MEMORY, NULL); + } /* if */ + + memset(finfo, '\0', sizeof (ZIPfileinfo)); + finfo->handle = in; + finfo->entry = ((entry->symlink != NULL) ? entry->symlink : entry); + initializeZStream(&finfo->stream); + if (finfo->entry->compression_method != COMPMETH_NONE) + { + if (zlib_err(inflateInit2(&finfo->stream, -MAX_WBITS)) != Z_OK) + { + ZIP_fileClose(finfo); + return(NULL); + } /* if */ + + finfo->buffer = (PHYSFS_uint8 *) allocator.Malloc(ZIP_READBUFSIZE); + if (finfo->buffer == NULL) + { + ZIP_fileClose(finfo); + BAIL_MACRO(ERR_OUT_OF_MEMORY, NULL); + } /* if */ + } /* if */ + + return(finfo); +} /* ZIP_openRead */ + + +static fvoid *ZIP_openWrite(dvoid *opaque, const char *filename) +{ + BAIL_MACRO(ERR_NOT_SUPPORTED, NULL); +} /* ZIP_openWrite */ + + +static fvoid *ZIP_openAppend(dvoid *opaque, const char *filename) +{ + BAIL_MACRO(ERR_NOT_SUPPORTED, NULL); +} /* ZIP_openAppend */ + + +static void ZIP_dirClose(dvoid *opaque) +{ + ZIPinfo *zi = (ZIPinfo *) (opaque); + zip_free_entries(zi->entries, zi->entryCount); + allocator.Free(zi->archiveName); + allocator.Free(zi); +} /* ZIP_dirClose */ + + +static int ZIP_remove(dvoid *opaque, const char *name) +{ + BAIL_MACRO(ERR_NOT_SUPPORTED, 0); +} /* ZIP_remove */ + + +static int ZIP_mkdir(dvoid *opaque, const char *name) +{ + BAIL_MACRO(ERR_NOT_SUPPORTED, 0); +} /* ZIP_mkdir */ + + +const PHYSFS_ArchiveInfo __PHYSFS_ArchiveInfo_ZIP = +{ + "ZIP", + ZIP_ARCHIVE_DESCRIPTION, + "Ryan C. Gordon ", + "http://icculus.org/physfs/", +}; + + +const PHYSFS_Archiver __PHYSFS_Archiver_ZIP = +{ + &__PHYSFS_ArchiveInfo_ZIP, + ZIP_isArchive, /* isArchive() method */ + ZIP_openArchive, /* openArchive() method */ + ZIP_enumerateFiles, /* enumerateFiles() method */ + ZIP_exists, /* exists() method */ + ZIP_isDirectory, /* isDirectory() method */ + ZIP_isSymLink, /* isSymLink() method */ + ZIP_getLastModTime, /* getLastModTime() method */ + ZIP_openRead, /* openRead() method */ + ZIP_openWrite, /* openWrite() method */ + ZIP_openAppend, /* openAppend() method */ + ZIP_remove, /* remove() method */ + ZIP_mkdir, /* mkdir() method */ + ZIP_dirClose, /* dirClose() method */ + ZIP_read, /* read() method */ + ZIP_write, /* write() method */ + ZIP_eof, /* eof() method */ + ZIP_tell, /* tell() method */ + ZIP_seek, /* seek() method */ + ZIP_fileLength, /* fileLength() method */ + ZIP_fileClose /* fileClose() method */ +}; + +#endif /* defined PHYSFS_SUPPORTS_ZIP */ + +/* end of zip.c ... */ + diff --git a/project/jni/physfs/include/physfs.h b/project/jni/physfs/include/physfs.h new file mode 100644 index 000000000..23a8ddd66 --- /dev/null +++ b/project/jni/physfs/include/physfs.h @@ -0,0 +1,2395 @@ +/** + * \file physfs.h + * + * Main header file for PhysicsFS. + */ + +/** + * \mainpage PhysicsFS + * + * The latest version of PhysicsFS can be found at: + * http://icculus.org/physfs/ + * + * PhysicsFS; a portable, flexible file i/o abstraction. + * + * This API gives you access to a system file system in ways superior to the + * stdio or system i/o calls. The brief benefits: + * + * - It's portable. + * - It's safe. No file access is permitted outside the specified dirs. + * - It's flexible. Archives (.ZIP files) can be used transparently as + * directory structures. + * + * This system is largely inspired by Quake 3's PK3 files and the related + * fs_* cvars. If you've ever tinkered with these, then this API will be + * familiar to you. + * + * With PhysicsFS, you have a single writing directory and multiple + * directories (the "search path") for reading. You can think of this as a + * filesystem within a filesystem. If (on Windows) you were to set the + * writing directory to "C:\MyGame\MyWritingDirectory", then no PHYSFS calls + * could touch anything above this directory, including the "C:\MyGame" and + * "C:\" directories. This prevents an application's internal scripting + * language from piddling over c:\\config.sys, for example. If you'd rather + * give PHYSFS full access to the system's REAL file system, set the writing + * dir to "C:\", but that's generally A Bad Thing for several reasons. + * + * Drive letters are hidden in PhysicsFS once you set up your initial paths. + * The search path creates a single, hierarchical directory structure. + * Not only does this lend itself well to general abstraction with archives, + * it also gives better support to operating systems like MacOS and Unix. + * Generally speaking, you shouldn't ever hardcode a drive letter; not only + * does this hurt portability to non-Microsoft OSes, but it limits your win32 + * users to a single drive, too. Use the PhysicsFS abstraction functions and + * allow user-defined configuration options, too. When opening a file, you + * specify it like it was on a Unix filesystem: if you want to write to + * "C:\MyGame\MyConfigFiles\game.cfg", then you might set the write dir to + * "C:\MyGame" and then open "MyConfigFiles/game.cfg". This gives an + * abstraction across all platforms. Specifying a file in this way is termed + * "platform-independent notation" in this documentation. Specifying a + * a filename in a form such as "C:\mydir\myfile" or + * "MacOS hard drive:My Directory:My File" is termed "platform-dependent + * notation". The only time you use platform-dependent notation is when + * setting up your write directory and search path; after that, all file + * access into those directories are done with platform-independent notation. + * + * All files opened for writing are opened in relation to the write directory, + * which is the root of the writable filesystem. When opening a file for + * reading, PhysicsFS goes through the search path. This is NOT the + * same thing as the PATH environment variable. An application using + * PhysicsFS specifies directories to be searched which may be actual + * directories, or archive files that contain files and subdirectories of + * their own. See the end of these docs for currently supported archive + * formats. + * + * Once the search path is defined, you may open files for reading. If you've + * got the following search path defined (to use a win32 example again): + * + * - C:\\mygame + * - C:\\mygame\\myuserfiles + * - D:\\mygamescdromdatafiles + * - C:\\mygame\\installeddatafiles.zip + * + * Then a call to PHYSFS_openRead("textfiles/myfile.txt") (note the directory + * separator, lack of drive letter, and lack of dir separator at the start of + * the string; this is platform-independent notation) will check for + * C:\\mygame\\textfiles\\myfile.txt, then + * C:\\mygame\\myuserfiles\\textfiles\\myfile.txt, then + * D:\\mygamescdromdatafiles\\textfiles\\myfile.txt, then, finally, for + * textfiles\\myfile.txt inside of C:\\mygame\\installeddatafiles.zip. + * Remember that most archive types and platform filesystems store their + * filenames in a case-sensitive manner, so you should be careful to specify + * it correctly. + * + * Files opened through PhysicsFS may NOT contain "." or ".." or ":" as dir + * elements. Not only are these meaningless on MacOS Classic and/or Unix, + * they are a security hole. Also, symbolic links (which can be found in + * some archive types and directly in the filesystem on Unix platforms) are + * NOT followed until you call PHYSFS_permitSymbolicLinks(). That's left to + * your own discretion, as following a symlink can allow for access outside + * the write dir and search paths. For portability, there is no mechanism for + * creating new symlinks in PhysicsFS. + * + * The write dir is not included in the search path unless you specifically + * add it. While you CAN change the write dir as many times as you like, + * you should probably set it once and stick to it. Remember that your + * program will not have permission to write in every directory on Unix and + * NT systems. + * + * All files are opened in binary mode; there is no endline conversion for + * textfiles. Other than that, PhysicsFS has some convenience functions for + * platform-independence. There is a function to tell you the current + * platform's dir separator ("\\" on windows, "/" on Unix, ":" on MacOS), + * which is needed only to set up your search/write paths. There is a + * function to tell you what CD-ROM drives contain accessible discs, and a + * function to recommend a good search path, etc. + * + * A recommended order for the search path is the write dir, then the base dir, + * then the cdrom dir, then any archives discovered. Quake 3 does something + * like this, but moves the archives to the start of the search path. Build + * Engine games, like Duke Nukem 3D and Blood, place the archives last, and + * use the base dir for both searching and writing. There is a helper + * function (PHYSFS_setSaneConfig()) that puts together a basic configuration + * for you, based on a few parameters. Also see the comments on + * PHYSFS_getBaseDir(), and PHYSFS_getUserDir() for info on what those + * are and how they can help you determine an optimal search path. + * + * PhysicsFS 2.0 adds the concept of "mounting" archives to arbitrary points + * in the search path. If a zipfile contains "maps/level.map" and you mount + * that archive at "mods/mymod", then you would have to open + * "mods/mymod/maps/level.map" to access the file, even though "mods/mymod" + * isn't actually specified in the .zip file. Unlike the Unix mentality of + * mounting a filesystem, "mods/mymod" doesn't actually have to exist when + * mounting the zipfile. It's a "virtual" directory. The mounting mechanism + * allows the developer to seperate archives in the tree and avoid trampling + * over files when added new archives, such as including mod support in a + * game...keeping external content on a tight leash in this manner can be of + * utmost importance to some applications. + * + * PhysicsFS is mostly thread safe. The error messages returned by + * PHYSFS_getLastError are unique by thread, and library-state-setting + * functions are mutex'd. For efficiency, individual file accesses are + * not locked, so you can not safely read/write/seek/close/etc the same + * file from two threads at the same time. Other race conditions are bugs + * that should be reported/patched. + * + * While you CAN use stdio/syscall file access in a program that has PHYSFS_* + * calls, doing so is not recommended, and you can not use system + * filehandles with PhysicsFS and vice versa. + * + * Note that archives need not be named as such: if you have a ZIP file and + * rename it with a .PKG extension, the file will still be recognized as a + * ZIP archive by PhysicsFS; the file's contents are used to determine its + * type where possible. + * + * Currently supported archive types: + * - .ZIP (pkZip/WinZip/Info-ZIP compatible) + * - .GRP (Build Engine groupfile archives) + * - .PAK (Quake I/II archive format) + * - .HOG (Descent I/II HOG file archives) + * - .MVL (Descent II movielib archives) + * - .WAD (DOOM engine archives) + * + * + * String policy for PhysicsFS 2.0 and later: + * + * PhysicsFS 1.0 could only deal with null-terminated ASCII strings. All high + * ASCII chars resulted in undefined behaviour, and there was no Unicode + * support at all. PhysicsFS 2.0 supports Unicode without breaking binary + * compatibility with the 1.0 API by using UTF-8 encoding of all strings + * passed in and out of the library. + * + * All strings passed through PhysicsFS are in null-terminated UTF-8 format. + * This means that if all you care about is English (ASCII characters <= 127) + * then you just use regular C strings. If you care about Unicode (and you + * should!) then you need to figure out what your platform wants, needs, and + * offers. If you are on Windows and build with Unicode support, your TCHAR + * strings are two bytes per character (this is called "UCS-2 encoding"). You + * should convert them to UTF-8 before handing them to PhysicsFS with + * PHYSFS_utf8FromUcs2(). If you're using Unix or Mac OS X, your wchar_t + * strings are four bytes per character ("UCS-4 encoding"). Use + * PHYSFS_utf8FromUcs4(). Mac OS X can give you UTF-8 directly from a + * CFString, and many Unixes generally give you C strings in UTF-8 format + * everywhere. If you have a single-byte high ASCII charset, like so-many + * European "codepages" you may be out of luck. We'll convert from "Latin1" + * to UTF-8 only, and never back to Latin1. If you're above ASCII 127, all + * bets are off: move to Unicode or use your platform's facilities. Passing a + * C string with high-ASCII data that isn't UTF-8 encoded will NOT do what + * you expect! + * + * Naturally, there's also PHYSFS_utf8ToUcs2() and PHYSFS_utf8ToUcs4() to get + * data back into a format you like. Behind the scenes, PhysicsFS will use + * Unicode where possible: the UTF-8 strings on Windows will be converted + * and used with the multibyte Windows APIs, for example. + * + * PhysicsFS offers basic encoding conversion support, but not a whole string + * library. Get your stuff into whatever format you can work with. + * + * Some platforms and archivers don't offer full Unicode support behind the + * scenes. For example, OS/2 only offers "codepages" and the filesystem + * itself doesn't support multibyte encodings. We make an earnest effort to + * convert to/from the current locale here, but all bets are off if + * you want to hand an arbitrary Japanese character through to these systems. + * Modern OSes (Mac OS X, Linux, Windows, PocketPC, etc) should all be fine. + * Many game-specific archivers are seriously unprepared for Unicode (the + * Descent HOG/MVL and Build Engine GRP archivers, for example, only offer a + * DOS 8.3 filename, for example). Nothing can be done for these, but they + * tend to be legacy formats for existing content that was all ASCII (and + * thus, valid UTF-8) anyhow. Other formats, like .ZIP, don't explicitly + * offer Unicode support, but unofficially expect filenames to be UTF-8 + * encoded, and thus Just Work. Most everything does the right thing without + * bothering you, but it's good to be aware of these nuances in case they + * don't. + * + * + * Other stuff: + * + * Please see the file LICENSE.txt in the source's root directory for licensing + * and redistribution rights. + * + * Please see the file CREDITS.txt in the source's root directory for a more or + * less complete list of who's responsible for this. + * + * \author Ryan C. Gordon. + */ + +#ifndef _INCLUDE_PHYSFS_H_ +#define _INCLUDE_PHYSFS_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +#ifndef DOXYGEN_SHOULD_IGNORE_THIS +#if (defined _MSC_VER) +#define __EXPORT__ __declspec(dllexport) +#elif (__GNUC__ >= 3) +#define __EXPORT__ __attribute__((visibility("default"))) +#else +#define __EXPORT__ +#endif +#endif /* DOXYGEN_SHOULD_IGNORE_THIS */ + +/** + * \typedef PHYSFS_uint8 + * \brief An unsigned, 8-bit integer type. + */ +typedef unsigned char PHYSFS_uint8; + +/** + * \typedef PHYSFS_sint8 + * \brief A signed, 8-bit integer type. + */ +typedef signed char PHYSFS_sint8; + +/** + * \typedef PHYSFS_uint16 + * \brief An unsigned, 16-bit integer type. + */ +typedef unsigned short PHYSFS_uint16; + +/** + * \typedef PHYSFS_sint16 + * \brief A signed, 16-bit integer type. + */ +typedef signed short PHYSFS_sint16; + +/** + * \typedef PHYSFS_uint32 + * \brief An unsigned, 32-bit integer type. + */ +typedef unsigned int PHYSFS_uint32; + +/** + * \typedef PHYSFS_sint32 + * \brief A signed, 32-bit integer type. + */ +typedef signed int PHYSFS_sint32; + +/** + * \typedef PHYSFS_uint64 + * \brief An unsigned, 64-bit integer type. + * \warning on platforms without any sort of 64-bit datatype, this is + * equivalent to PHYSFS_uint32! + */ + +/** + * \typedef PHYSFS_sint64 + * \brief A signed, 64-bit integer type. + * \warning on platforms without any sort of 64-bit datatype, this is + * equivalent to PHYSFS_sint32! + */ + + +#if (defined PHYSFS_NO_64BIT_SUPPORT) /* oh well. */ +typedef PHYSFS_uint32 PHYSFS_uint64; +typedef PHYSFS_sint32 PHYSFS_sint64; +#elif (defined _MSC_VER) +typedef signed __int64 PHYSFS_sint64; +typedef unsigned __int64 PHYSFS_uint64; +#else +typedef unsigned long long PHYSFS_uint64; +typedef signed long long PHYSFS_sint64; +#endif + + +#ifndef DOXYGEN_SHOULD_IGNORE_THIS +/* Make sure the types really have the right sizes */ +#define PHYSFS_COMPILE_TIME_ASSERT(name, x) \ + typedef int PHYSFS_dummy_ ## name[(x) * 2 - 1] + +PHYSFS_COMPILE_TIME_ASSERT(uint8, sizeof(PHYSFS_uint8) == 1); +PHYSFS_COMPILE_TIME_ASSERT(sint8, sizeof(PHYSFS_sint8) == 1); +PHYSFS_COMPILE_TIME_ASSERT(uint16, sizeof(PHYSFS_uint16) == 2); +PHYSFS_COMPILE_TIME_ASSERT(sint16, sizeof(PHYSFS_sint16) == 2); +PHYSFS_COMPILE_TIME_ASSERT(uint32, sizeof(PHYSFS_uint32) == 4); +PHYSFS_COMPILE_TIME_ASSERT(sint32, sizeof(PHYSFS_sint32) == 4); + +#ifndef PHYSFS_NO_64BIT_SUPPORT +PHYSFS_COMPILE_TIME_ASSERT(uint64, sizeof(PHYSFS_uint64) == 8); +PHYSFS_COMPILE_TIME_ASSERT(sint64, sizeof(PHYSFS_sint64) == 8); +#endif + +#undef PHYSFS_COMPILE_TIME_ASSERT + +#endif /* DOXYGEN_SHOULD_IGNORE_THIS */ + + +/** + * \struct PHYSFS_File + * \brief A PhysicsFS file handle. + * + * You get a pointer to one of these when you open a file for reading, + * writing, or appending via PhysicsFS. + * + * As you can see from the lack of meaningful fields, you should treat this + * as opaque data. Don't try to manipulate the file handle, just pass the + * pointer you got, unmolested, to various PhysicsFS APIs. + * + * \sa PHYSFS_openRead + * \sa PHYSFS_openWrite + * \sa PHYSFS_openAppend + * \sa PHYSFS_close + * \sa PHYSFS_read + * \sa PHYSFS_write + * \sa PHYSFS_seek + * \sa PHYSFS_tell + * \sa PHYSFS_eof + * \sa PHYSFS_setBuffer + * \sa PHYSFS_flush + */ +typedef struct PHYSFS_File +{ + void *opaque; /**< That's all you get. Don't touch. */ +} PHYSFS_File; + + +/** + * \def PHYSFS_file + * \brief 1.0 API compatibility define. + * + * PHYSFS_file is identical to PHYSFS_File. This #define is here for backwards + * compatibility with the 1.0 API, which had an inconsistent capitalization + * convention in this case. New code should use PHYSFS_File, as this #define + * may go away someday. + * + * \sa PHYSFS_File + */ +#define PHYSFS_file PHYSFS_File + + +/** + * \struct PHYSFS_ArchiveInfo + * \brief Information on various PhysicsFS-supported archives. + * + * This structure gives you details on what sort of archives are supported + * by this implementation of PhysicsFS. Archives tend to be things like + * ZIP files and such. + * + * \warning Not all binaries are created equal! PhysicsFS can be built with + * or without support for various archives. You can check with + * PHYSFS_supportedArchiveTypes() to see if your archive type is + * supported. + * + * \sa PHYSFS_supportedArchiveTypes + */ +typedef struct PHYSFS_ArchiveInfo +{ + const char *extension; /**< Archive file extension: "ZIP", for example. */ + const char *description; /**< Human-readable archive description. */ + const char *author; /**< Person who did support for this archive. */ + const char *url; /**< URL related to this archive */ +} PHYSFS_ArchiveInfo; + + +/** + * \struct PHYSFS_Version + * \brief Information the version of PhysicsFS in use. + * + * Represents the library's version as three levels: major revision + * (increments with massive changes, additions, and enhancements), + * minor revision (increments with backwards-compatible changes to the + * major revision), and patchlevel (increments with fixes to the minor + * revision). + * + * \sa PHYSFS_VERSION + * \sa PHYSFS_getLinkedVersion + */ +typedef struct PHYSFS_Version +{ + PHYSFS_uint8 major; /**< major revision */ + PHYSFS_uint8 minor; /**< minor revision */ + PHYSFS_uint8 patch; /**< patchlevel */ +} PHYSFS_Version; + +#ifndef DOXYGEN_SHOULD_IGNORE_THIS +#define PHYSFS_VER_MAJOR 2 +#define PHYSFS_VER_MINOR 0 +#define PHYSFS_VER_PATCH 2 +#endif /* DOXYGEN_SHOULD_IGNORE_THIS */ + + +/* PhysicsFS state stuff ... */ + +/** + * \def PHYSFS_VERSION(x) + * \brief Macro to determine PhysicsFS version program was compiled against. + * + * This macro fills in a PHYSFS_Version structure with the version of the + * library you compiled against. This is determined by what header the + * compiler uses. Note that if you dynamically linked the library, you might + * have a slightly newer or older version at runtime. That version can be + * determined with PHYSFS_getLinkedVersion(), which, unlike PHYSFS_VERSION, + * is not a macro. + * + * \param x A pointer to a PHYSFS_Version struct to initialize. + * + * \sa PHYSFS_Version + * \sa PHYSFS_getLinkedVersion + */ +#define PHYSFS_VERSION(x) \ +{ \ + (x)->major = PHYSFS_VER_MAJOR; \ + (x)->minor = PHYSFS_VER_MINOR; \ + (x)->patch = PHYSFS_VER_PATCH; \ +} + + +/** + * \fn void PHYSFS_getLinkedVersion(PHYSFS_Version *ver) + * \brief Get the version of PhysicsFS that is linked against your program. + * + * If you are using a shared library (DLL) version of PhysFS, then it is + * possible that it will be different than the version you compiled against. + * + * This is a real function; the macro PHYSFS_VERSION tells you what version + * of PhysFS you compiled against: + * + * \code + * PHYSFS_Version compiled; + * PHYSFS_Version linked; + * + * PHYSFS_VERSION(&compiled); + * PHYSFS_getLinkedVersion(&linked); + * printf("We compiled against PhysFS version %d.%d.%d ...\n", + * compiled.major, compiled.minor, compiled.patch); + * printf("But we linked against PhysFS version %d.%d.%d.\n", + * linked.major, linked.minor, linked.patch); + * \endcode + * + * This function may be called safely at any time, even before PHYSFS_init(). + * + * \sa PHYSFS_VERSION + */ +__EXPORT__ void PHYSFS_getLinkedVersion(PHYSFS_Version *ver); + + +/** + * \fn int PHYSFS_init(const char *argv0) + * \brief Initialize the PhysicsFS library. + * + * This must be called before any other PhysicsFS function. + * + * This should be called prior to any attempts to change your process's + * current working directory. + * + * \param argv0 the argv[0] string passed to your program's mainline. + * This may be NULL on most platforms (such as ones without a + * standard main() function), but you should always try to pass + * something in here. Unix-like systems such as Linux _need_ to + * pass argv[0] from main() in here. + * \return nonzero on success, zero on error. Specifics of the error can be + * gleaned from PHYSFS_getLastError(). + * + * \sa PHYSFS_deinit + * \sa PHYSFS_isInit + */ +__EXPORT__ int PHYSFS_init(const char *argv0); + + +/** + * \fn int PHYSFS_deinit(void) + * \brief Deinitialize the PhysicsFS library. + * + * This closes any files opened via PhysicsFS, blanks the search/write paths, + * frees memory, and invalidates all of your file handles. + * + * Note that this call can FAIL if there's a file open for writing that + * refuses to close (for example, the underlying operating system was + * buffering writes to network filesystem, and the fileserver has crashed, + * or a hard drive has failed, etc). It is usually best to close all write + * handles yourself before calling this function, so that you can gracefully + * handle a specific failure. + * + * Once successfully deinitialized, PHYSFS_init() can be called again to + * restart the subsystem. All default API states are restored at this + * point, with the exception of any custom allocator you might have + * specified, which survives between initializations. + * + * \return nonzero on success, zero on error. Specifics of the error can be + * gleaned from PHYSFS_getLastError(). If failure, state of PhysFS is + * undefined, and probably badly screwed up. + * + * \sa PHYSFS_init + * \sa PHYSFS_isInit + */ +__EXPORT__ int PHYSFS_deinit(void); + + +/** + * \fn const PHYSFS_ArchiveInfo **PHYSFS_supportedArchiveTypes(void) + * \brief Get a list of supported archive types. + * + * Get a list of archive types supported by this implementation of PhysicFS. + * These are the file formats usable for search path entries. This is for + * informational purposes only. Note that the extension listed is merely + * convention: if we list "ZIP", you can open a PkZip-compatible archive + * with an extension of "XYZ", if you like. + * + * The returned value is an array of pointers to PHYSFS_ArchiveInfo structures, + * with a NULL entry to signify the end of the list: + * + * \code + * PHYSFS_ArchiveInfo **i; + * + * for (i = PHYSFS_supportedArchiveTypes(); *i != NULL; i++) + * { + * printf("Supported archive: [%s], which is [%s].\n", + * (*i)->extension, (*i)->description); + * } + * \endcode + * + * The return values are pointers to static internal memory, and should + * be considered READ ONLY, and never freed. + * + * \return READ ONLY Null-terminated array of READ ONLY structures. + */ +__EXPORT__ const PHYSFS_ArchiveInfo **PHYSFS_supportedArchiveTypes(void); + + +/** + * \fn void PHYSFS_freeList(void *listVar) + * \brief Deallocate resources of lists returned by PhysicsFS. + * + * Certain PhysicsFS functions return lists of information that are + * dynamically allocated. Use this function to free those resources. + * + * \param listVar List of information specified as freeable by this function. + * + * \sa PHYSFS_getCdRomDirs + * \sa PHYSFS_enumerateFiles + * \sa PHYSFS_getSearchPath + */ +__EXPORT__ void PHYSFS_freeList(void *listVar); + + +/** + * \fn const char *PHYSFS_getLastError(void) + * \brief Get human-readable error information. + * + * Get the last PhysicsFS error message as a human-readable, null-terminated + * string. This will be NULL if there's been no error since the last call to + * this function. The pointer returned by this call points to an internal + * buffer. Each thread has a unique error state associated with it, but each + * time a new error message is set, it will overwrite the previous one + * associated with that thread. It is safe to call this function at anytime, + * even before PHYSFS_init(). + * + * It is not wise to expect a specific string of characters here, since the + * error message may be localized into an unfamiliar language. These strings + * are meant to be passed on directly to the user. + * + * \return READ ONLY string of last error message. + */ +__EXPORT__ const char *PHYSFS_getLastError(void); + + +/** + * \fn const char *PHYSFS_getDirSeparator(void) + * \brief Get platform-dependent dir separator string. + * + * This returns "\\" on win32, "/" on Unix, and ":" on MacOS. It may be more + * than one character, depending on the platform, and your code should take + * that into account. Note that this is only useful for setting up the + * search/write paths, since access into those dirs always use '/' + * (platform-independent notation) to separate directories. This is also + * handy for getting platform-independent access when using stdio calls. + * + * \return READ ONLY null-terminated string of platform's dir separator. + */ +__EXPORT__ const char *PHYSFS_getDirSeparator(void); + + +/** + * \fn void PHYSFS_permitSymbolicLinks(int allow) + * \brief Enable or disable following of symbolic links. + * + * Some physical filesystems and archives contain files that are just pointers + * to other files. On the physical filesystem, opening such a link will + * (transparently) open the file that is pointed to. + * + * By default, PhysicsFS will check if a file is really a symlink during open + * calls and fail if it is. Otherwise, the link could take you outside the + * write and search paths, and compromise security. + * + * If you want to take that risk, call this function with a non-zero parameter. + * Note that this is more for sandboxing a program's scripting language, in + * case untrusted scripts try to compromise the system. Generally speaking, + * a user could very well have a legitimate reason to set up a symlink, so + * unless you feel there's a specific danger in allowing them, you should + * permit them. + * + * Symlinks are only explicitly checked when dealing with filenames + * in platform-independent notation. That is, when setting up your + * search and write paths, etc, symlinks are never checked for. + * + * Symbolic link permission can be enabled or disabled at any time after + * you've called PHYSFS_init(), and is disabled by default. + * + * \param allow nonzero to permit symlinks, zero to deny linking. + * + * \sa PHYSFS_symbolicLinksPermitted + */ +__EXPORT__ void PHYSFS_permitSymbolicLinks(int allow); + + +/* !!! FIXME: const this? */ +/** + * \fn char **PHYSFS_getCdRomDirs(void) + * \brief Get an array of paths to available CD-ROM drives. + * + * The dirs returned are platform-dependent ("D:\" on Win32, "/cdrom" or + * whatnot on Unix). Dirs are only returned if there is a disc ready and + * accessible in the drive. So if you've got two drives (D: and E:), and only + * E: has a disc in it, then that's all you get. If the user inserts a disc + * in D: and you call this function again, you get both drives. If, on a + * Unix box, the user unmounts a disc and remounts it elsewhere, the next + * call to this function will reflect that change. + * + * This function refers to "CD-ROM" media, but it really means "inserted disc + * media," such as DVD-ROM, HD-DVD, CDRW, and Blu-Ray discs. It looks for + * filesystems, and as such won't report an audio CD, unless there's a + * mounted filesystem track on it. + * + * The returned value is an array of strings, with a NULL entry to signify the + * end of the list: + * + * \code + * char **cds = PHYSFS_getCdRomDirs(); + * char **i; + * + * for (i = cds; *i != NULL; i++) + * printf("cdrom dir [%s] is available.\n", *i); + * + * PHYSFS_freeList(cds); + * \endcode + * + * This call may block while drives spin up. Be forewarned. + * + * When you are done with the returned information, you may dispose of the + * resources by calling PHYSFS_freeList() with the returned pointer. + * + * \return Null-terminated array of null-terminated strings. + * + * \sa PHYSFS_getCdRomDirsCallback + */ +__EXPORT__ char **PHYSFS_getCdRomDirs(void); + + +/** + * \fn const char *PHYSFS_getBaseDir(void) + * \brief Get the path where the application resides. + * + * Helper function. + * + * Get the "base dir". This is the directory where the application was run + * from, which is probably the installation directory, and may or may not + * be the process's current working directory. + * + * You should probably use the base dir in your search path. + * + * \return READ ONLY string of base dir in platform-dependent notation. + * + * \sa PHYSFS_getUserDir + */ +__EXPORT__ const char *PHYSFS_getBaseDir(void); + + +/** + * \fn const char *PHYSFS_getUserDir(void) + * \brief Get the path where user's home directory resides. + * + * Helper function. + * + * Get the "user dir". This is meant to be a suggestion of where a specific + * user of the system can store files. On Unix, this is her home directory. + * On systems with no concept of multiple home directories (MacOS, win95), + * this will default to something like "C:\mybasedir\users\username" + * where "username" will either be the login name, or "default" if the + * platform doesn't support multiple users, either. + * + * You should probably use the user dir as the basis for your write dir, and + * also put it near the beginning of your search path. + * + * \return READ ONLY string of user dir in platform-dependent notation. + * + * \sa PHYSFS_getBaseDir + */ +__EXPORT__ const char *PHYSFS_getUserDir(void); + + +/** + * \fn const char *PHYSFS_getWriteDir(void) + * \brief Get path where PhysicsFS will allow file writing. + * + * Get the current write dir. The default write dir is NULL. + * + * \return READ ONLY string of write dir in platform-dependent notation, + * OR NULL IF NO WRITE PATH IS CURRENTLY SET. + * + * \sa PHYSFS_setWriteDir + */ +__EXPORT__ const char *PHYSFS_getWriteDir(void); + + +/** + * \fn int PHYSFS_setWriteDir(const char *newDir) + * \brief Tell PhysicsFS where it may write files. + * + * Set a new write dir. This will override the previous setting. + * + * This call will fail (and fail to change the write dir) if the current + * write dir still has files open in it. + * + * \param newDir The new directory to be the root of the write dir, + * specified in platform-dependent notation. Setting to NULL + * disables the write dir, so no files can be opened for + * writing via PhysicsFS. + * \return non-zero on success, zero on failure. All attempts to open a file + * for writing via PhysicsFS will fail until this call succeeds. + * Specifics of the error can be gleaned from PHYSFS_getLastError(). + * + * \sa PHYSFS_getWriteDir + */ +__EXPORT__ int PHYSFS_setWriteDir(const char *newDir); + + +/** + * \fn int PHYSFS_addToSearchPath(const char *newDir, int appendToPath) + * \brief Add an archive or directory to the search path. + * + * This is a legacy call in PhysicsFS 2.0, equivalent to: + * PHYSFS_mount(newDir, NULL, appendToPath); + * + * You must use this and not PHYSFS_mount if binary compatibility with + * PhysicsFS 1.0 is important (which it may not be for many people). + * + * \sa PHYSFS_mount + * \sa PHYSFS_removeFromSearchPath + * \sa PHYSFS_getSearchPath + */ +__EXPORT__ int PHYSFS_addToSearchPath(const char *newDir, int appendToPath); + + +/** + * \fn int PHYSFS_removeFromSearchPath(const char *oldDir) + * \brief Remove a directory or archive from the search path. + * + * This must be a (case-sensitive) match to a dir or archive already in the + * search path, specified in platform-dependent notation. + * + * This call will fail (and fail to remove from the path) if the element still + * has files open in it. + * + * \param oldDir dir/archive to remove. + * \return nonzero on success, zero on failure. + * Specifics of the error can be gleaned from PHYSFS_getLastError(). + * + * \sa PHYSFS_addToSearchPath + * \sa PHYSFS_getSearchPath + */ +__EXPORT__ int PHYSFS_removeFromSearchPath(const char *oldDir); + + +/** + * \fn char **PHYSFS_getSearchPath(void) + * \brief Get the current search path. + * + * The default search path is an empty list. + * + * The returned value is an array of strings, with a NULL entry to signify the + * end of the list: + * + * \code + * char **i; + * + * for (i = PHYSFS_getSearchPath(); *i != NULL; i++) + * printf("[%s] is in the search path.\n", *i); + * \endcode + * + * When you are done with the returned information, you may dispose of the + * resources by calling PHYSFS_freeList() with the returned pointer. + * + * \return Null-terminated array of null-terminated strings. NULL if there + * was a problem (read: OUT OF MEMORY). + * + * \sa PHYSFS_getSearchPathCallback + * \sa PHYSFS_addToSearchPath + * \sa PHYSFS_removeFromSearchPath + */ +__EXPORT__ char **PHYSFS_getSearchPath(void); + + +/** + * \fn int PHYSFS_setSaneConfig(const char *organization, const char *appName, const char *archiveExt, int includeCdRoms, int archivesFirst) + * \brief Set up sane, default paths. + * + * Helper function. + * + * The write dir will be set to "userdir/.organization/appName", which is + * created if it doesn't exist. + * + * The above is sufficient to make sure your program's configuration directory + * is separated from other clutter, and platform-independent. The period + * before "mygame" even hides the directory on Unix systems. + * + * The search path will be: + * + * - The Write Dir (created if it doesn't exist) + * - The Base Dir (PHYSFS_getBaseDir()) + * - All found CD-ROM dirs (optionally) + * + * These directories are then searched for files ending with the extension + * (archiveExt), which, if they are valid and supported archives, will also + * be added to the search path. If you specified "PKG" for (archiveExt), and + * there's a file named data.PKG in the base dir, it'll be checked. Archives + * can either be appended or prepended to the search path in alphabetical + * order, regardless of which directories they were found in. + * + * All of this can be accomplished from the application, but this just does it + * all for you. Feel free to add more to the search path manually, too. + * + * \param organization Name of your company/group/etc to be used as a + * dirname, so keep it small, and no-frills. + * + * \param appName Program-specific name of your program, to separate it + * from other programs using PhysicsFS. + * + * \param archiveExt File extension used by your program to specify an + * archive. For example, Quake 3 uses "pk3", even though + * they are just zipfiles. Specify NULL to not dig out + * archives automatically. Do not specify the '.' char; + * If you want to look for ZIP files, specify "ZIP" and + * not ".ZIP" ... the archive search is case-insensitive. + * + * \param includeCdRoms Non-zero to include CD-ROMs in the search path, and + * (if (archiveExt) != NULL) search them for archives. + * This may cause a significant amount of blocking + * while discs are accessed, and if there are no discs + * in the drive (or even not mounted on Unix systems), + * then they may not be made available anyhow. You may + * want to specify zero and handle the disc setup + * yourself. + * + * \param archivesFirst Non-zero to prepend the archives to the search path. + * Zero to append them. Ignored if !(archiveExt). + * + * \return nonzero on success, zero on error. Specifics of the error can be + * gleaned from PHYSFS_getLastError(). + */ +__EXPORT__ int PHYSFS_setSaneConfig(const char *organization, + const char *appName, + const char *archiveExt, + int includeCdRoms, + int archivesFirst); + + +/* Directory management stuff ... */ + +/** + * \fn int PHYSFS_mkdir(const char *dirName) + * \brief Create a directory. + * + * This is specified in platform-independent notation in relation to the + * write dir. All missing parent directories are also created if they + * don't exist. + * + * So if you've got the write dir set to "C:\mygame\writedir" and call + * PHYSFS_mkdir("downloads/maps") then the directories + * "C:\mygame\writedir\downloads" and "C:\mygame\writedir\downloads\maps" + * will be created if possible. If the creation of "maps" fails after we + * have successfully created "downloads", then the function leaves the + * created directory behind and reports failure. + * + * \param dirName New dir to create. + * \return nonzero on success, zero on error. Specifics of the error can be + * gleaned from PHYSFS_getLastError(). + * + * \sa PHYSFS_delete + */ +__EXPORT__ int PHYSFS_mkdir(const char *dirName); + + +/** + * \fn int PHYSFS_delete(const char *filename) + * \brief Delete a file or directory. + * + * (filename) is specified in platform-independent notation in relation to the + * write dir. + * + * A directory must be empty before this call can delete it. + * + * Deleting a symlink will remove the link, not what it points to, regardless + * of whether you "permitSymLinks" or not. + * + * So if you've got the write dir set to "C:\mygame\writedir" and call + * PHYSFS_delete("downloads/maps/level1.map") then the file + * "C:\mygame\writedir\downloads\maps\level1.map" is removed from the + * physical filesystem, if it exists and the operating system permits the + * deletion. + * + * Note that on Unix systems, deleting a file may be successful, but the + * actual file won't be removed until all processes that have an open + * filehandle to it (including your program) close their handles. + * + * Chances are, the bits that make up the file still exist, they are just + * made available to be written over at a later point. Don't consider this + * a security method or anything. :) + * + * \param filename Filename to delete. + * \return nonzero on success, zero on error. Specifics of the error can be + * gleaned from PHYSFS_getLastError(). + */ +__EXPORT__ int PHYSFS_delete(const char *filename); + + +/** + * \fn const char *PHYSFS_getRealDir(const char *filename) + * \brief Figure out where in the search path a file resides. + * + * The file is specified in platform-independent notation. The returned + * filename will be the element of the search path where the file was found, + * which may be a directory, or an archive. Even if there are multiple + * matches in different parts of the search path, only the first one found + * is used, just like when opening a file. + * + * So, if you look for "maps/level1.map", and C:\\mygame is in your search + * path and C:\\mygame\\maps\\level1.map exists, then "C:\mygame" is returned. + * + * If a any part of a match is a symbolic link, and you've not explicitly + * permitted symlinks, then it will be ignored, and the search for a match + * will continue. + * + * If you specify a fake directory that only exists as a mount point, it'll + * be associated with the first archive mounted there, even though that + * directory isn't necessarily contained in a real archive. + * + * \param filename file to look for. + * \return READ ONLY string of element of search path containing the + * the file in question. NULL if not found. + */ +__EXPORT__ const char *PHYSFS_getRealDir(const char *filename); + + +/** + * \fn char **PHYSFS_enumerateFiles(const char *dir) + * \brief Get a file listing of a search path's directory. + * + * 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.sav", 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_enumerateFiles("savegames"); + * 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 [z.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. + * + * Don't forget to call PHYSFS_freeList() with the return value from this + * function when you are done with it. + * + * \param dir directory in platform-independent notation to enumerate. + * \return Null-terminated array of null-terminated strings. + * + * \sa PHYSFS_enumerateFilesCallback + */ +__EXPORT__ char **PHYSFS_enumerateFiles(const char *dir); + + +/** + * \fn int PHYSFS_exists(const char *fname) + * \brief Determine if a file exists in the search path. + * + * Reports true if there is an entry anywhere in the search path by the + * name of (fname). + * + * Note that entries that are symlinks are ignored if + * PHYSFS_permitSymbolicLinks(1) hasn't been called, so you + * might end up further down in the search path than expected. + * + * \param fname filename in platform-independent notation. + * \return non-zero if filename exists. zero otherwise. + * + * \sa PHYSFS_isDirectory + * \sa PHYSFS_isSymbolicLink + */ +__EXPORT__ int PHYSFS_exists(const char *fname); + + +/** + * \fn int PHYSFS_isDirectory(const char *fname) + * \brief Determine if a file in the search path is really a directory. + * + * Determine if the first occurence of (fname) in the search path is + * really a directory entry. + * + * Note that entries that are symlinks are ignored if + * PHYSFS_permitSymbolicLinks(1) hasn't been called, so you + * might end up further down in the search path than expected. + * + * \param fname filename in platform-independent notation. + * \return non-zero if filename exists and is a directory. zero otherwise. + * + * \sa PHYSFS_exists + * \sa PHYSFS_isSymbolicLink + */ +__EXPORT__ int PHYSFS_isDirectory(const char *fname); + + +/** + * \fn int PHYSFS_isSymbolicLink(const char *fname) + * \brief Determine if a file in the search path is really a symbolic link. + * + * Determine if the first occurence of (fname) in the search path is + * really a symbolic link. + * + * Note that entries that are symlinks are ignored if + * PHYSFS_permitSymbolicLinks(1) hasn't been called, and as such, + * this function will always return 0 in that case. + * + * \param fname filename in platform-independent notation. + * \return non-zero if filename exists and is a symlink. zero otherwise. + * + * \sa PHYSFS_exists + * \sa PHYSFS_isDirectory + */ +__EXPORT__ int PHYSFS_isSymbolicLink(const char *fname); + + +/** + * \fn PHYSFS_sint64 PHYSFS_getLastModTime(const char *filename) + * \brief Get the last modification time of a file. + * + * The modtime is returned as a number of seconds since the epoch + * (Jan 1, 1970). The exact derivation and accuracy of this time depends on + * the particular archiver. If there is no reasonable way to obtain this + * information for a particular archiver, or there was some sort of error, + * this function returns (-1). + * + * \param filename filename to check, in platform-independent notation. + * \return last modified time of the file. -1 if it can't be determined. + */ +__EXPORT__ PHYSFS_sint64 PHYSFS_getLastModTime(const char *filename); + + +/* i/o stuff... */ + +/** + * \fn PHYSFS_File *PHYSFS_openWrite(const char *filename) + * \brief Open a file for writing. + * + * Open a file for writing, in platform-independent notation and in relation + * to the write dir as the root of the writable filesystem. The specified + * file is created if it doesn't exist. If it does exist, it is truncated to + * zero bytes, and the writing offset is set to the start. + * + * Note that entries that are symlinks are ignored if + * PHYSFS_permitSymbolicLinks(1) hasn't been called, and opening a + * symlink with this function will fail in such a case. + * + * \param filename File to open. + * \return A valid PhysicsFS filehandle on success, NULL on error. Specifics + * of the error can be gleaned from PHYSFS_getLastError(). + * + * \sa PHYSFS_openRead + * \sa PHYSFS_openAppend + * \sa PHYSFS_write + * \sa PHYSFS_close + */ +__EXPORT__ PHYSFS_File *PHYSFS_openWrite(const char *filename); + + +/** + * \fn PHYSFS_File *PHYSFS_openAppend(const char *filename) + * \brief Open a file for appending. + * + * Open a file for writing, in platform-independent notation and in relation + * to the write dir as the root of the writable filesystem. The specified + * file is created if it doesn't exist. If it does exist, the writing offset + * is set to the end of the file, so the first write will be the byte after + * the end. + * + * Note that entries that are symlinks are ignored if + * PHYSFS_permitSymbolicLinks(1) hasn't been called, and opening a + * symlink with this function will fail in such a case. + * + * \param filename File to open. + * \return A valid PhysicsFS filehandle on success, NULL on error. Specifics + * of the error can be gleaned from PHYSFS_getLastError(). + * + * \sa PHYSFS_openRead + * \sa PHYSFS_openWrite + * \sa PHYSFS_write + * \sa PHYSFS_close + */ +__EXPORT__ PHYSFS_File *PHYSFS_openAppend(const char *filename); + + +/** + * \fn PHYSFS_File *PHYSFS_openRead(const char *filename) + * \brief Open a file for reading. + * + * Open a file for reading, in platform-independent notation. The search path + * is checked one at a time until a matching file is found, in which case an + * abstract filehandle is associated with it, and reading may be done. + * The reading offset is set to the first byte of the file. + * + * Note that entries that are symlinks are ignored if + * PHYSFS_permitSymbolicLinks(1) hasn't been called, and opening a + * symlink with this function will fail in such a case. + * + * \param filename File to open. + * \return A valid PhysicsFS filehandle on success, NULL on error. Specifics + * of the error can be gleaned from PHYSFS_getLastError(). + * + * \sa PHYSFS_openWrite + * \sa PHYSFS_openAppend + * \sa PHYSFS_read + * \sa PHYSFS_close + */ +__EXPORT__ PHYSFS_File *PHYSFS_openRead(const char *filename); + + +/** + * \fn int PHYSFS_close(PHYSFS_File *handle) + * \brief Close a PhysicsFS filehandle. + * + * This call is capable of failing if the operating system was buffering + * writes to the physical media, and, now forced to write those changes to + * physical media, can not store the data for some reason. In such a case, + * the filehandle stays open. A well-written program should ALWAYS check the + * return value from the close call in addition to every writing call! + * + * \param handle handle returned from PHYSFS_open*(). + * \return nonzero on success, zero on error. Specifics of the error can be + * gleaned from PHYSFS_getLastError(). + * + * \sa PHYSFS_openRead + * \sa PHYSFS_openWrite + * \sa PHYSFS_openAppend + */ +__EXPORT__ int PHYSFS_close(PHYSFS_File *handle); + + +/** + * \fn PHYSFS_sint64 PHYSFS_read(PHYSFS_File *handle, void *buffer, PHYSFS_uint32 objSize, PHYSFS_uint32 objCount) + * \brief Read data from a PhysicsFS filehandle + * + * The file must be opened for reading. + * + * \param handle handle returned from PHYSFS_openRead(). + * \param buffer buffer to store read data into. + * \param objSize size in bytes of objects being read from (handle). + * \param objCount number of (objSize) objects to read from (handle). + * \return number of objects read. PHYSFS_getLastError() can shed light on + * the reason this might be < (objCount), as can PHYSFS_eof(). + * -1 if complete failure. + * + * \sa PHYSFS_eof + */ +__EXPORT__ PHYSFS_sint64 PHYSFS_read(PHYSFS_File *handle, + void *buffer, + PHYSFS_uint32 objSize, + PHYSFS_uint32 objCount); + +/** + * \fn PHYSFS_sint64 PHYSFS_write(PHYSFS_File *handle, const void *buffer, PHYSFS_uint32 objSize, PHYSFS_uint32 objCount) + * \brief Write data to a PhysicsFS filehandle + * + * The file must be opened for writing. + * + * \param handle retval from PHYSFS_openWrite() or PHYSFS_openAppend(). + * \param buffer buffer of bytes to write to (handle). + * \param objSize size in bytes of objects being written to (handle). + * \param objCount number of (objSize) objects to write to (handle). + * \return number of objects written. PHYSFS_getLastError() can shed light on + * the reason this might be < (objCount). -1 if complete failure. + */ +__EXPORT__ PHYSFS_sint64 PHYSFS_write(PHYSFS_File *handle, + const void *buffer, + PHYSFS_uint32 objSize, + PHYSFS_uint32 objCount); + + +/* File position stuff... */ + +/** + * \fn int PHYSFS_eof(PHYSFS_File *handle) + * \brief Check for end-of-file state on a PhysicsFS filehandle. + * + * Determine if the end of file has been reached in a PhysicsFS filehandle. + * + * \param handle handle returned from PHYSFS_openRead(). + * \return nonzero if EOF, zero if not. + * + * \sa PHYSFS_read + * \sa PHYSFS_tell + */ +__EXPORT__ int PHYSFS_eof(PHYSFS_File *handle); + + +/** + * \fn PHYSFS_sint64 PHYSFS_tell(PHYSFS_File *handle) + * \brief Determine current position within a PhysicsFS filehandle. + * + * \param handle handle returned from PHYSFS_open*(). + * \return offset in bytes from start of file. -1 if error occurred. + * Specifics of the error can be gleaned from PHYSFS_getLastError(). + * + * \sa PHYSFS_seek + */ +__EXPORT__ PHYSFS_sint64 PHYSFS_tell(PHYSFS_File *handle); + + +/** + * \fn int PHYSFS_seek(PHYSFS_File *handle, PHYSFS_uint64 pos) + * \brief Seek to a new position within a PhysicsFS filehandle. + * + * The next read or write will occur at that place. Seeking past the + * beginning or end of the file is not allowed, and causes an error. + * + * \param handle handle returned from PHYSFS_open*(). + * \param pos number of bytes from start of file to seek to. + * \return nonzero on success, zero on error. Specifics of the error can be + * gleaned from PHYSFS_getLastError(). + * + * \sa PHYSFS_tell + */ +__EXPORT__ int PHYSFS_seek(PHYSFS_File *handle, PHYSFS_uint64 pos); + + +/** + * \fn PHYSFS_sint64 PHYSFS_fileLength(PHYSFS_File *handle) + * \brief Get total length of a file in bytes. + * + * Note that if the file size can't be determined (since the archive is + * "streamed" or whatnot) than this will report (-1). Also note that if + * another process/thread is writing to this file at the same time, then + * the information this function supplies could be incorrect before you + * get it. Use with caution, or better yet, don't use at all. + * + * \param handle handle returned from PHYSFS_open*(). + * \return size in bytes of the file. -1 if can't be determined. + * + * \sa PHYSFS_tell + * \sa PHYSFS_seek + */ +__EXPORT__ PHYSFS_sint64 PHYSFS_fileLength(PHYSFS_File *handle); + + +/* Buffering stuff... */ + +/** + * \fn int PHYSFS_setBuffer(PHYSFS_File *handle, PHYSFS_uint64 bufsize) + * \brief Set up buffering for a PhysicsFS file handle. + * + * Define an i/o buffer for a file handle. A memory block of (bufsize) bytes + * will be allocated and associated with (handle). + * + * For files opened for reading, up to (bufsize) bytes are read from (handle) + * and stored in the internal buffer. Calls to PHYSFS_read() will pull + * from this buffer until it is empty, and then refill it for more reading. + * Note that compressed files, like ZIP archives, will decompress while + * buffering, so this can be handy for offsetting CPU-intensive operations. + * The buffer isn't filled until you do your next read. + * + * For files opened for writing, data will be buffered to memory until the + * buffer is full or the buffer is flushed. Closing a handle implicitly + * causes a flush...check your return values! + * + * Seeking, etc transparently accounts for buffering. + * + * You can resize an existing buffer by calling this function more than once + * on the same file. Setting the buffer size to zero will free an existing + * buffer. + * + * PhysicsFS file handles are unbuffered by default. + * + * Please check the return value of this function! Failures can include + * not being able to seek backwards in a read-only file when removing the + * buffer, not being able to allocate the buffer, and not being able to + * flush the buffer to disk, among other unexpected problems. + * + * \param handle handle returned from PHYSFS_open*(). + * \param bufsize size, in bytes, of buffer to allocate. + * \return nonzero if successful, zero on error. + * + * \sa PHYSFS_flush + * \sa PHYSFS_read + * \sa PHYSFS_write + * \sa PHYSFS_close + */ +__EXPORT__ int PHYSFS_setBuffer(PHYSFS_File *handle, PHYSFS_uint64 bufsize); + + +/** + * \fn int PHYSFS_flush(PHYSFS_File *handle) + * \brief Flush a buffered PhysicsFS file handle. + * + * For buffered files opened for writing, this will put the current contents + * of the buffer to disk and flag the buffer as empty if possible. + * + * For buffered files opened for reading or unbuffered files, this is a safe + * no-op, and will report success. + * + * \param handle handle returned from PHYSFS_open*(). + * \return nonzero if successful, zero on error. + * + * \sa PHYSFS_setBuffer + * \sa PHYSFS_close + */ +__EXPORT__ int PHYSFS_flush(PHYSFS_File *handle); + + +/* Byteorder stuff... */ + +/** + * \fn PHYSFS_sint16 PHYSFS_swapSLE16(PHYSFS_sint16 val) + * \brief Swap littleendian signed 16 to platform's native byte order. + * + * Take a 16-bit signed value in littleendian format and convert it to + * the platform's native byte order. + * + * \param val value to convert + * \return converted value. + */ +__EXPORT__ PHYSFS_sint16 PHYSFS_swapSLE16(PHYSFS_sint16 val); + + +/** + * \fn PHYSFS_uint16 PHYSFS_swapULE16(PHYSFS_uint16 val) + * \brief Swap littleendian unsigned 16 to platform's native byte order. + * + * Take a 16-bit unsigned value in littleendian format and convert it to + * the platform's native byte order. + * + * \param val value to convert + * \return converted value. + */ +__EXPORT__ PHYSFS_uint16 PHYSFS_swapULE16(PHYSFS_uint16 val); + +/** + * \fn PHYSFS_sint32 PHYSFS_swapSLE32(PHYSFS_sint32 val) + * \brief Swap littleendian signed 32 to platform's native byte order. + * + * Take a 32-bit signed value in littleendian format and convert it to + * the platform's native byte order. + * + * \param val value to convert + * \return converted value. + */ +__EXPORT__ PHYSFS_sint32 PHYSFS_swapSLE32(PHYSFS_sint32 val); + + +/** + * \fn PHYSFS_uint32 PHYSFS_swapULE32(PHYSFS_uint32 val) + * \brief Swap littleendian unsigned 32 to platform's native byte order. + * + * Take a 32-bit unsigned value in littleendian format and convert it to + * the platform's native byte order. + * + * \param val value to convert + * \return converted value. + */ +__EXPORT__ PHYSFS_uint32 PHYSFS_swapULE32(PHYSFS_uint32 val); + +/** + * \fn PHYSFS_sint64 PHYSFS_swapSLE64(PHYSFS_sint64 val) + * \brief Swap littleendian signed 64 to platform's native byte order. + * + * Take a 64-bit signed value in littleendian format and convert it to + * the platform's native byte order. + * + * \param val value to convert + * \return converted value. + * + * \warning Remember, PHYSFS_uint64 is only 32 bits on platforms without + * any sort of 64-bit support. + */ +__EXPORT__ PHYSFS_sint64 PHYSFS_swapSLE64(PHYSFS_sint64 val); + + +/** + * \fn PHYSFS_uint64 PHYSFS_swapULE64(PHYSFS_uint64 val) + * \brief Swap littleendian unsigned 64 to platform's native byte order. + * + * Take a 64-bit unsigned value in littleendian format and convert it to + * the platform's native byte order. + * + * \param val value to convert + * \return converted value. + * + * \warning Remember, PHYSFS_uint64 is only 32 bits on platforms without + * any sort of 64-bit support. + */ +__EXPORT__ PHYSFS_uint64 PHYSFS_swapULE64(PHYSFS_uint64 val); + + +/** + * \fn PHYSFS_sint16 PHYSFS_swapSBE16(PHYSFS_sint16 val) + * \brief Swap bigendian signed 16 to platform's native byte order. + * + * Take a 16-bit signed value in bigendian format and convert it to + * the platform's native byte order. + * + * \param val value to convert + * \return converted value. + */ +__EXPORT__ PHYSFS_sint16 PHYSFS_swapSBE16(PHYSFS_sint16 val); + + +/** + * \fn PHYSFS_uint16 PHYSFS_swapUBE16(PHYSFS_uint16 val) + * \brief Swap bigendian unsigned 16 to platform's native byte order. + * + * Take a 16-bit unsigned value in bigendian format and convert it to + * the platform's native byte order. + * + * \param val value to convert + * \return converted value. + */ +__EXPORT__ PHYSFS_uint16 PHYSFS_swapUBE16(PHYSFS_uint16 val); + +/** + * \fn PHYSFS_sint32 PHYSFS_swapSBE32(PHYSFS_sint32 val) + * \brief Swap bigendian signed 32 to platform's native byte order. + * + * Take a 32-bit signed value in bigendian format and convert it to + * the platform's native byte order. + * + * \param val value to convert + * \return converted value. + */ +__EXPORT__ PHYSFS_sint32 PHYSFS_swapSBE32(PHYSFS_sint32 val); + + +/** + * \fn PHYSFS_uint32 PHYSFS_swapUBE32(PHYSFS_uint32 val) + * \brief Swap bigendian unsigned 32 to platform's native byte order. + * + * Take a 32-bit unsigned value in bigendian format and convert it to + * the platform's native byte order. + * + * \param val value to convert + * \return converted value. + */ +__EXPORT__ PHYSFS_uint32 PHYSFS_swapUBE32(PHYSFS_uint32 val); + + +/** + * \fn PHYSFS_sint64 PHYSFS_swapSBE64(PHYSFS_sint64 val) + * \brief Swap bigendian signed 64 to platform's native byte order. + * + * Take a 64-bit signed value in bigendian format and convert it to + * the platform's native byte order. + * + * \param val value to convert + * \return converted value. + * + * \warning Remember, PHYSFS_uint64 is only 32 bits on platforms without + * any sort of 64-bit support. + */ +__EXPORT__ PHYSFS_sint64 PHYSFS_swapSBE64(PHYSFS_sint64 val); + + +/** + * \fn PHYSFS_uint64 PHYSFS_swapUBE64(PHYSFS_uint64 val) + * \brief Swap bigendian unsigned 64 to platform's native byte order. + * + * Take a 64-bit unsigned value in bigendian format and convert it to + * the platform's native byte order. + * + * \param val value to convert + * \return converted value. + * + * \warning Remember, PHYSFS_uint64 is only 32 bits on platforms without + * any sort of 64-bit support. + */ +__EXPORT__ PHYSFS_uint64 PHYSFS_swapUBE64(PHYSFS_uint64 val); + + +/** + * \fn int PHYSFS_readSLE16(PHYSFS_File *file, PHYSFS_sint16 *val) + * \brief Read and convert a signed 16-bit littleendian value. + * + * Convenience function. Read a signed 16-bit littleendian value from a + * file and convert it to the platform's native byte order. + * + * \param file PhysicsFS file handle from which to read. + * \param val pointer to where value should be stored. + * \return zero on failure, non-zero on success. If successful, (*val) will + * store the result. On failure, you can find out what went wrong + * from PHYSFS_getLastError(). + */ +__EXPORT__ int PHYSFS_readSLE16(PHYSFS_File *file, PHYSFS_sint16 *val); + + +/** + * \fn int PHYSFS_readULE16(PHYSFS_File *file, PHYSFS_uint16 *val) + * \brief Read and convert an unsigned 16-bit littleendian value. + * + * Convenience function. Read an unsigned 16-bit littleendian value from a + * file and convert it to the platform's native byte order. + * + * \param file PhysicsFS file handle from which to read. + * \param val pointer to where value should be stored. + * \return zero on failure, non-zero on success. If successful, (*val) will + * store the result. On failure, you can find out what went wrong + * from PHYSFS_getLastError(). + * + */ +__EXPORT__ int PHYSFS_readULE16(PHYSFS_File *file, PHYSFS_uint16 *val); + + +/** + * \fn int PHYSFS_readSBE16(PHYSFS_File *file, PHYSFS_sint16 *val) + * \brief Read and convert a signed 16-bit bigendian value. + * + * Convenience function. Read a signed 16-bit bigendian value from a + * file and convert it to the platform's native byte order. + * + * \param file PhysicsFS file handle from which to read. + * \param val pointer to where value should be stored. + * \return zero on failure, non-zero on success. If successful, (*val) will + * store the result. On failure, you can find out what went wrong + * from PHYSFS_getLastError(). + */ +__EXPORT__ int PHYSFS_readSBE16(PHYSFS_File *file, PHYSFS_sint16 *val); + + +/** + * \fn int PHYSFS_readUBE16(PHYSFS_File *file, PHYSFS_uint16 *val) + * \brief Read and convert an unsigned 16-bit bigendian value. + * + * Convenience function. Read an unsigned 16-bit bigendian value from a + * file and convert it to the platform's native byte order. + * + * \param file PhysicsFS file handle from which to read. + * \param val pointer to where value should be stored. + * \return zero on failure, non-zero on success. If successful, (*val) will + * store the result. On failure, you can find out what went wrong + * from PHYSFS_getLastError(). + * + */ +__EXPORT__ int PHYSFS_readUBE16(PHYSFS_File *file, PHYSFS_uint16 *val); + + +/** + * \fn int PHYSFS_readSLE32(PHYSFS_File *file, PHYSFS_sint32 *val) + * \brief Read and convert a signed 32-bit littleendian value. + * + * Convenience function. Read a signed 32-bit littleendian value from a + * file and convert it to the platform's native byte order. + * + * \param file PhysicsFS file handle from which to read. + * \param val pointer to where value should be stored. + * \return zero on failure, non-zero on success. If successful, (*val) will + * store the result. On failure, you can find out what went wrong + * from PHYSFS_getLastError(). + */ +__EXPORT__ int PHYSFS_readSLE32(PHYSFS_File *file, PHYSFS_sint32 *val); + + +/** + * \fn int PHYSFS_readULE32(PHYSFS_File *file, PHYSFS_uint32 *val) + * \brief Read and convert an unsigned 32-bit littleendian value. + * + * Convenience function. Read an unsigned 32-bit littleendian value from a + * file and convert it to the platform's native byte order. + * + * \param file PhysicsFS file handle from which to read. + * \param val pointer to where value should be stored. + * \return zero on failure, non-zero on success. If successful, (*val) will + * store the result. On failure, you can find out what went wrong + * from PHYSFS_getLastError(). + * + */ +__EXPORT__ int PHYSFS_readULE32(PHYSFS_File *file, PHYSFS_uint32 *val); + + +/** + * \fn int PHYSFS_readSBE32(PHYSFS_File *file, PHYSFS_sint32 *val) + * \brief Read and convert a signed 32-bit bigendian value. + * + * Convenience function. Read a signed 32-bit bigendian value from a + * file and convert it to the platform's native byte order. + * + * \param file PhysicsFS file handle from which to read. + * \param val pointer to where value should be stored. + * \return zero on failure, non-zero on success. If successful, (*val) will + * store the result. On failure, you can find out what went wrong + * from PHYSFS_getLastError(). + */ +__EXPORT__ int PHYSFS_readSBE32(PHYSFS_File *file, PHYSFS_sint32 *val); + + +/** + * \fn int PHYSFS_readUBE32(PHYSFS_File *file, PHYSFS_uint32 *val) + * \brief Read and convert an unsigned 32-bit bigendian value. + * + * Convenience function. Read an unsigned 32-bit bigendian value from a + * file and convert it to the platform's native byte order. + * + * \param file PhysicsFS file handle from which to read. + * \param val pointer to where value should be stored. + * \return zero on failure, non-zero on success. If successful, (*val) will + * store the result. On failure, you can find out what went wrong + * from PHYSFS_getLastError(). + * + */ +__EXPORT__ int PHYSFS_readUBE32(PHYSFS_File *file, PHYSFS_uint32 *val); + + +/** + * \fn int PHYSFS_readSLE64(PHYSFS_File *file, PHYSFS_sint64 *val) + * \brief Read and convert a signed 64-bit littleendian value. + * + * Convenience function. Read a signed 64-bit littleendian value from a + * file and convert it to the platform's native byte order. + * + * \param file PhysicsFS file handle from which to read. + * \param val pointer to where value should be stored. + * \return zero on failure, non-zero on success. If successful, (*val) will + * store the result. On failure, you can find out what went wrong + * from PHYSFS_getLastError(). + * + * \warning Remember, PHYSFS_sint64 is only 32 bits on platforms without + * any sort of 64-bit support. + */ +__EXPORT__ int PHYSFS_readSLE64(PHYSFS_File *file, PHYSFS_sint64 *val); + + +/** + * \fn int PHYSFS_readULE64(PHYSFS_File *file, PHYSFS_uint64 *val) + * \brief Read and convert an unsigned 64-bit littleendian value. + * + * Convenience function. Read an unsigned 64-bit littleendian value from a + * file and convert it to the platform's native byte order. + * + * \param file PhysicsFS file handle from which to read. + * \param val pointer to where value should be stored. + * \return zero on failure, non-zero on success. If successful, (*val) will + * store the result. On failure, you can find out what went wrong + * from PHYSFS_getLastError(). + * + * \warning Remember, PHYSFS_uint64 is only 32 bits on platforms without + * any sort of 64-bit support. + */ +__EXPORT__ int PHYSFS_readULE64(PHYSFS_File *file, PHYSFS_uint64 *val); + + +/** + * \fn int PHYSFS_readSBE64(PHYSFS_File *file, PHYSFS_sint64 *val) + * \brief Read and convert a signed 64-bit bigendian value. + * + * Convenience function. Read a signed 64-bit bigendian value from a + * file and convert it to the platform's native byte order. + * + * \param file PhysicsFS file handle from which to read. + * \param val pointer to where value should be stored. + * \return zero on failure, non-zero on success. If successful, (*val) will + * store the result. On failure, you can find out what went wrong + * from PHYSFS_getLastError(). + * + * \warning Remember, PHYSFS_sint64 is only 32 bits on platforms without + * any sort of 64-bit support. + */ +__EXPORT__ int PHYSFS_readSBE64(PHYSFS_File *file, PHYSFS_sint64 *val); + + +/** + * \fn int PHYSFS_readUBE64(PHYSFS_File *file, PHYSFS_uint64 *val) + * \brief Read and convert an unsigned 64-bit bigendian value. + * + * Convenience function. Read an unsigned 64-bit bigendian value from a + * file and convert it to the platform's native byte order. + * + * \param file PhysicsFS file handle from which to read. + * \param val pointer to where value should be stored. + * \return zero on failure, non-zero on success. If successful, (*val) will + * store the result. On failure, you can find out what went wrong + * from PHYSFS_getLastError(). + * + * \warning Remember, PHYSFS_uint64 is only 32 bits on platforms without + * any sort of 64-bit support. + */ +__EXPORT__ int PHYSFS_readUBE64(PHYSFS_File *file, PHYSFS_uint64 *val); + + +/** + * \fn int PHYSFS_writeSLE16(PHYSFS_File *file, PHYSFS_sint16 val) + * \brief Convert and write a signed 16-bit littleendian value. + * + * Convenience function. Convert a signed 16-bit value from the platform's + * native byte order to littleendian and write it to a file. + * + * \param file PhysicsFS file handle to which to write. + * \param val Value to convert and write. + * \return zero on failure, non-zero on success. On failure, you can + * find out what went wrong from PHYSFS_getLastError(). + */ +__EXPORT__ int PHYSFS_writeSLE16(PHYSFS_File *file, PHYSFS_sint16 val); + + +/** + * \fn int PHYSFS_writeULE16(PHYSFS_File *file, PHYSFS_uint16 val) + * \brief Convert and write an unsigned 16-bit littleendian value. + * + * Convenience function. Convert an unsigned 16-bit value from the platform's + * native byte order to littleendian and write it to a file. + * + * \param file PhysicsFS file handle to which to write. + * \param val Value to convert and write. + * \return zero on failure, non-zero on success. On failure, you can + * find out what went wrong from PHYSFS_getLastError(). + */ +__EXPORT__ int PHYSFS_writeULE16(PHYSFS_File *file, PHYSFS_uint16 val); + + +/** + * \fn int PHYSFS_writeSBE16(PHYSFS_File *file, PHYSFS_sint16 val) + * \brief Convert and write a signed 16-bit bigendian value. + * + * Convenience function. Convert a signed 16-bit value from the platform's + * native byte order to bigendian and write it to a file. + * + * \param file PhysicsFS file handle to which to write. + * \param val Value to convert and write. + * \return zero on failure, non-zero on success. On failure, you can + * find out what went wrong from PHYSFS_getLastError(). + */ +__EXPORT__ int PHYSFS_writeSBE16(PHYSFS_File *file, PHYSFS_sint16 val); + + +/** + * \fn int PHYSFS_writeUBE16(PHYSFS_File *file, PHYSFS_uint16 val) + * \brief Convert and write an unsigned 16-bit bigendian value. + * + * Convenience function. Convert an unsigned 16-bit value from the platform's + * native byte order to bigendian and write it to a file. + * + * \param file PhysicsFS file handle to which to write. + * \param val Value to convert and write. + * \return zero on failure, non-zero on success. On failure, you can + * find out what went wrong from PHYSFS_getLastError(). + */ +__EXPORT__ int PHYSFS_writeUBE16(PHYSFS_File *file, PHYSFS_uint16 val); + + +/** + * \fn int PHYSFS_writeSLE32(PHYSFS_File *file, PHYSFS_sint32 val) + * \brief Convert and write a signed 32-bit littleendian value. + * + * Convenience function. Convert a signed 32-bit value from the platform's + * native byte order to littleendian and write it to a file. + * + * \param file PhysicsFS file handle to which to write. + * \param val Value to convert and write. + * \return zero on failure, non-zero on success. On failure, you can + * find out what went wrong from PHYSFS_getLastError(). + */ +__EXPORT__ int PHYSFS_writeSLE32(PHYSFS_File *file, PHYSFS_sint32 val); + + +/** + * \fn int PHYSFS_writeULE32(PHYSFS_File *file, PHYSFS_uint32 val) + * \brief Convert and write an unsigned 32-bit littleendian value. + * + * Convenience function. Convert an unsigned 32-bit value from the platform's + * native byte order to littleendian and write it to a file. + * + * \param file PhysicsFS file handle to which to write. + * \param val Value to convert and write. + * \return zero on failure, non-zero on success. On failure, you can + * find out what went wrong from PHYSFS_getLastError(). + */ +__EXPORT__ int PHYSFS_writeULE32(PHYSFS_File *file, PHYSFS_uint32 val); + + +/** + * \fn int PHYSFS_writeSBE32(PHYSFS_File *file, PHYSFS_sint32 val) + * \brief Convert and write a signed 32-bit bigendian value. + * + * Convenience function. Convert a signed 32-bit value from the platform's + * native byte order to bigendian and write it to a file. + * + * \param file PhysicsFS file handle to which to write. + * \param val Value to convert and write. + * \return zero on failure, non-zero on success. On failure, you can + * find out what went wrong from PHYSFS_getLastError(). + */ +__EXPORT__ int PHYSFS_writeSBE32(PHYSFS_File *file, PHYSFS_sint32 val); + + +/** + * \fn int PHYSFS_writeUBE32(PHYSFS_File *file, PHYSFS_uint32 val) + * \brief Convert and write an unsigned 32-bit bigendian value. + * + * Convenience function. Convert an unsigned 32-bit value from the platform's + * native byte order to bigendian and write it to a file. + * + * \param file PhysicsFS file handle to which to write. + * \param val Value to convert and write. + * \return zero on failure, non-zero on success. On failure, you can + * find out what went wrong from PHYSFS_getLastError(). + */ +__EXPORT__ int PHYSFS_writeUBE32(PHYSFS_File *file, PHYSFS_uint32 val); + + +/** + * \fn int PHYSFS_writeSLE64(PHYSFS_File *file, PHYSFS_sint64 val) + * \brief Convert and write a signed 64-bit littleendian value. + * + * Convenience function. Convert a signed 64-bit value from the platform's + * native byte order to littleendian and write it to a file. + * + * \param file PhysicsFS file handle to which to write. + * \param val Value to convert and write. + * \return zero on failure, non-zero on success. On failure, you can + * find out what went wrong from PHYSFS_getLastError(). + * + * \warning Remember, PHYSFS_uint64 is only 32 bits on platforms without + * any sort of 64-bit support. + */ +__EXPORT__ int PHYSFS_writeSLE64(PHYSFS_File *file, PHYSFS_sint64 val); + + +/** + * \fn int PHYSFS_writeULE64(PHYSFS_File *file, PHYSFS_uint64 val) + * \brief Convert and write an unsigned 64-bit littleendian value. + * + * Convenience function. Convert an unsigned 64-bit value from the platform's + * native byte order to littleendian and write it to a file. + * + * \param file PhysicsFS file handle to which to write. + * \param val Value to convert and write. + * \return zero on failure, non-zero on success. On failure, you can + * find out what went wrong from PHYSFS_getLastError(). + * + * \warning Remember, PHYSFS_uint64 is only 32 bits on platforms without + * any sort of 64-bit support. + */ +__EXPORT__ int PHYSFS_writeULE64(PHYSFS_File *file, PHYSFS_uint64 val); + + +/** + * \fn int PHYSFS_writeSBE64(PHYSFS_File *file, PHYSFS_sint64 val) + * \brief Convert and write a signed 64-bit bigending value. + * + * Convenience function. Convert a signed 64-bit value from the platform's + * native byte order to bigendian and write it to a file. + * + * \param file PhysicsFS file handle to which to write. + * \param val Value to convert and write. + * \return zero on failure, non-zero on success. On failure, you can + * find out what went wrong from PHYSFS_getLastError(). + * + * \warning Remember, PHYSFS_uint64 is only 32 bits on platforms without + * any sort of 64-bit support. + */ +__EXPORT__ int PHYSFS_writeSBE64(PHYSFS_File *file, PHYSFS_sint64 val); + + +/** + * \fn int PHYSFS_writeUBE64(PHYSFS_File *file, PHYSFS_uint64 val) + * \brief Convert and write an unsigned 64-bit bigendian value. + * + * Convenience function. Convert an unsigned 64-bit value from the platform's + * native byte order to bigendian and write it to a file. + * + * \param file PhysicsFS file handle to which to write. + * \param val Value to convert and write. + * \return zero on failure, non-zero on success. On failure, you can + * find out what went wrong from PHYSFS_getLastError(). + * + * \warning Remember, PHYSFS_uint64 is only 32 bits on platforms without + * any sort of 64-bit support. + */ +__EXPORT__ int PHYSFS_writeUBE64(PHYSFS_File *file, PHYSFS_uint64 val); + + +/* Everything above this line is part of the PhysicsFS 1.0 API. */ + +/** + * \fn int PHYSFS_isInit(void) + * \brief Determine if the PhysicsFS library is initialized. + * + * Once PHYSFS_init() returns successfully, this will return non-zero. + * Before a successful PHYSFS_init() and after PHYSFS_deinit() returns + * successfully, this will return zero. This function is safe to call at + * any time. + * + * \return non-zero if library is initialized, zero if library is not. + * + * \sa PHYSFS_init + * \sa PHYSFS_deinit + */ +__EXPORT__ int PHYSFS_isInit(void); + + +/** + * \fn int PHYSFS_symbolicLinksPermitted(void) + * \brief Determine if the symbolic links are permitted. + * + * This reports the setting from the last call to PHYSFS_permitSymbolicLinks(). + * If PHYSFS_permitSymbolicLinks() hasn't been called since the library was + * last initialized, symbolic links are implicitly disabled. + * + * \return non-zero if symlinks are permitted, zero if not. + * + * \sa PHYSFS_permitSymbolicLinks + */ +__EXPORT__ int PHYSFS_symbolicLinksPermitted(void); + + +/** + * \struct PHYSFS_Allocator + * \brief PhysicsFS allocation function pointers. + * + * (This is for limited, hardcore use. If you don't immediately see a need + * for it, you can probably ignore this forever.) + * + * You create one of these structures for use with PHYSFS_setAllocator. + * Allocators are assumed to be reentrant by the caller; please mutex + * accordingly. + * + * Allocations are always discussed in 64-bits, for future expansion...we're + * on the cusp of a 64-bit transition, and we'll probably be allocating 6 + * gigabytes like it's nothing sooner or later, and I don't want to change + * this again at that point. If you're on a 32-bit platform and have to + * downcast, it's okay to return NULL if the allocation is greater than + * 4 gigabytes, since you'd have to do so anyhow. + * + * \sa PHYSFS_setAllocator + */ +typedef struct PHYSFS_Allocator +{ + int (*Init)(void); /**< Initialize. Can be NULL. Zero on failure. */ + void (*Deinit)(void); /**< Deinitialize your allocator. Can be NULL. */ + void *(*Malloc)(PHYSFS_uint64); /**< Allocate like malloc(). */ + void *(*Realloc)(void *, PHYSFS_uint64); /**< Reallocate like realloc(). */ + void (*Free)(void *); /**< Free memory from Malloc or Realloc. */ +} PHYSFS_Allocator; + + +/** + * \fn int PHYSFS_setAllocator(const PHYSFS_Allocator *allocator) + * \brief Hook your own allocation routines into PhysicsFS. + * + * (This is for limited, hardcore use. If you don't immediately see a need + * for it, you can probably ignore this forever.) + * + * By default, PhysicsFS will use whatever is reasonable for a platform + * to manage dynamic memory (usually ANSI C malloc/realloc/calloc/free, but + * some platforms might use something else), but in some uncommon cases, the + * app might want more control over the library's memory management. This + * lets you redirect PhysicsFS to use your own allocation routines instead. + * You can only call this function before PHYSFS_init(); if the library is + * initialized, it'll reject your efforts to change the allocator mid-stream. + * You may call this function after PHYSFS_deinit() if you are willing to + * shut down the library and restart it with a new allocator; this is a safe + * and supported operation. The allocator remains intact between deinit/init + * calls. If you want to return to the platform's default allocator, pass a + * NULL in here. + * + * If you aren't immediately sure what to do with this function, you can + * safely ignore it altogether. + * + * \param allocator Structure containing your allocator's entry points. + * \return zero on failure, non-zero on success. This call only fails + * when used between PHYSFS_init() and PHYSFS_deinit() calls. + */ +__EXPORT__ int PHYSFS_setAllocator(const PHYSFS_Allocator *allocator); + + +/** + * \fn int PHYSFS_mount(const char *newDir, const char *mountPoint, int appendToPath) + * \brief Add an archive or directory to the search path. + * + * If this is a duplicate, the entry is not added again, even though the + * function succeeds. You may not add the same archive to two different + * mountpoints: duplicate checking is done against the archive and not the + * mountpoint. + * + * When you mount an archive, it is added to a virtual file system...all files + * in all of the archives are interpolated into a single hierachical file + * tree. Two archives mounted at the same place (or an archive with files + * overlapping another mountpoint) may have overlapping files: in such a case, + * the file earliest in the search path is selected, and the other files are + * inaccessible to the application. This allows archives to be used to + * override previous revisions; you can use the mounting mechanism to place + * archives at a specific point in the file tree and prevent overlap; this + * is useful for downloadable mods that might trample over application data + * or each other, for example. + * + * The mountpoint does not need to exist prior to mounting, which is different + * than those familiar with the Unix concept of "mounting" may not expect. + * As well, more than one archive can be mounted to the same mountpoint, or + * mountpoints and archive contents can overlap...the interpolation mechanism + * still functions as usual. + * + * \param newDir directory or archive to add to the path, in + * platform-dependent notation. + * \param mountPoint Location in the interpolated tree that this archive + * will be "mounted", in platform-independent notation. + * NULL or "" is equivalent to "/". + * \param appendToPath nonzero to append to search path, zero to prepend. + * \return nonzero if added to path, zero on failure (bogus archive, dir + * missing, etc). Specifics of the error can be + * gleaned from PHYSFS_getLastError(). + * + * \sa PHYSFS_removeFromSearchPath + * \sa PHYSFS_getSearchPath + * \sa PHYSFS_getMountPoint + */ +__EXPORT__ int PHYSFS_mount(const char *newDir, const char *mountPoint, int appendToPath); + +/** + * \fn int PHYSFS_getMountPoint(const char *dir) + * \brief Determine a mounted archive's mountpoint. + * + * You give this function the name of an archive or dir you successfully + * added to the search path, and it reports the location in the interpolated + * tree where it is mounted. Files mounted with a NULL mountpoint or through + * PHYSFS_addToSearchPath() will report "/". The return value is READ ONLY + * and valid until the archive is removed from the search path. + * + * \param dir directory or archive previously added to the path, in + * platform-dependent notation. This must match the string + * used when adding, even if your string would also reference + * the same file with a different string of characters. + * \return READ-ONLY string of mount point if added to path, NULL on failure + * (bogus archive, etc) Specifics of the error can be gleaned from + * PHYSFS_getLastError(). + * + * \sa PHYSFS_removeFromSearchPath + * \sa PHYSFS_getSearchPath + * \sa PHYSFS_getMountPoint + */ +__EXPORT__ const char *PHYSFS_getMountPoint(const char *dir); + + +/** + * \typedef PHYSFS_StringCallback + * \brief Function signature for callbacks that report strings. + * + * These are used to report a list of strings to an original caller, one + * string per callback. All strings are UTF-8 encoded. Functions should not + * try to modify or free the string's memory. + * + * These callbacks are used, starting in PhysicsFS 1.1, as an alternative to + * functions that would return lists that need to be cleaned up with + * PHYSFS_freeList(). The callback means that the library doesn't need to + * allocate an entire list and all the strings up front. + * + * Be aware that promises data ordering in the list versions are not + * necessarily so in the callback versions. Check the documentation on + * specific APIs, but strings may not be sorted as you expect. + * + * \param data User-defined data pointer, passed through from the API + * that eventually called the callback. + * \param str The string data about which the callback is meant to inform. + * + * \sa PHYSFS_getCdRomDirsCallback + * \sa PHYSFS_getSearchPathCallback + */ +typedef void (*PHYSFS_StringCallback)(void *data, const char *str); + + +/** + * \typedef PHYSFS_EnumFilesCallback + * \brief Function signature for callbacks that enumerate files. + * + * These are used to report a list of directory entries to an original caller, + * one file/dir/symlink per callback. All strings are UTF-8 encoded. + * Functions should not try to modify or free any string's memory. + * + * These callbacks are used, starting in PhysicsFS 1.1, as an alternative to + * functions that would return lists that need to be cleaned up with + * PHYSFS_freeList(). The callback means that the library doesn't need to + * allocate an entire list and all the strings up front. + * + * Be aware that promises data ordering in the list versions are not + * necessarily so in the callback versions. Check the documentation on + * specific APIs, but strings may not be sorted as you expect. + * + * \param data User-defined data pointer, passed through from the API + * that eventually called the callback. + * \param origdir A string containing the full path, in platform-independent + * notation, of the directory containing this file. In most + * cases, this is the directory on which you requested + * enumeration, passed in the callback for your convenience. + * \param fname The filename that is being enumerated. It may not be in + * alphabetical order compared to other callbacks that have + * fired, and it will not contain the full path. You can + * recreate the fullpath with $origdir/$fname ... The file + * can be a subdirectory, a file, a symlink, etc. + * + * \sa PHYSFS_enumerateFilesCallback + */ +typedef void (*PHYSFS_EnumFilesCallback)(void *data, const char *origdir, + const char *fname); + + +/** + * \fn void PHYSFS_getCdRomDirsCallback(PHYSFS_StringCallback c, void *d) + * \brief Enumerate CD-ROM directories, using an application-defined callback. + * + * Internally, PHYSFS_getCdRomDirs() just calls this function and then builds + * a list before returning to the application, so functionality is identical + * except for how the information is represented to the application. + * + * Unlike PHYSFS_getCdRomDirs(), this function does not return an array. + * Rather, it calls a function specified by the application once per + * detected disc: + * + * \code + * + * static void foundDisc(void *data, const char *cddir) + * { + * printf("cdrom dir [%s] is available.\n", cddir); + * } + * + * // ... + * PHYSFS_getCdRomDirsCallback(foundDisc, NULL); + * \endcode + * + * This call may block while drives spin up. Be forewarned. + * + * \param c Callback function to notify about detected drives. + * \param d Application-defined data passed to callback. Can be NULL. + * + * \sa PHYSFS_StringCallback + * \sa PHYSFS_getCdRomDirs + */ +__EXPORT__ void PHYSFS_getCdRomDirsCallback(PHYSFS_StringCallback c, void *d); + + +/** + * \fn void PHYSFS_getSearchPathCallback(PHYSFS_StringCallback c, void *d) + * \brief Enumerate the search path, using an application-defined callback. + * + * Internally, PHYSFS_getSearchPath() just calls this function and then builds + * a list before returning to the application, so functionality is identical + * except for how the information is represented to the application. + * + * Unlike PHYSFS_getSearchPath(), 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 printSearchPath(void *data, const char *pathItem) + * { + * printf("[%s] is in the search path.\n", pathItem); + * } + * + * // ... + * PHYSFS_getSearchPathCallback(printSearchPath, NULL); + * \endcode + * + * Elements of the search path are reported in order search priority, so the + * first archive/dir that would be examined when looking for a file is the + * first element passed through the callback. + * + * \param c Callback function to notify about search path elements. + * \param d Application-defined data passed to callback. Can be NULL. + * + * \sa PHYSFS_StringCallback + * \sa PHYSFS_getSearchPath + */ +__EXPORT__ void PHYSFS_getSearchPathCallback(PHYSFS_StringCallback c, void *d); + + +/** + * \fn void PHYSFS_enumerateFilesCallback(const char *dir, PHYSFS_EnumFilesCallback c, void *d) + * \brief Get a file listing of a search path's directory, using an application-defined callback. + * + * Internally, PHYSFS_enumerateFiles() just calls this function and then builds + * a list before returning to the application, so functionality is identical + * except for how the information is represented to the application. + * + * 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_enumerateFilesCallback("/some/path", 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_enumerateFiles() 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. Any sorting you find in + * these callbacks is just pure luck. Do not rely on it. + * + * \param dir Directory, in platform-independent notation, to enumerate. + * \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 + */ +__EXPORT__ void PHYSFS_enumerateFilesCallback(const char *dir, + PHYSFS_EnumFilesCallback c, + void *d); + +/** + * \fn void PHYSFS_utf8FromUcs4(const PHYSFS_uint32 *src, char *dst, PHYSFS_uint64 len) + * \brief Convert a UCS-4 string to a UTF-8 string. + * + * UCS-4 strings are 32-bits per character: \c wchar_t on Unix. + * + * To ensure that the destination buffer is large enough for the conversion, + * please allocate a buffer that is the same size as the source buffer. UTF-8 + * never uses more than 32-bits per character, so while it may shrink a UCS-4 + * string, it will never expand it. + * + * Strings that don't fit in the destination buffer will be truncated, but + * will always be null-terminated and never have an incomplete UTF-8 + * sequence at the end. If the buffer length is 0, this function does nothing. + * + * \param src Null-terminated source string in UCS-4 format. + * \param dst Buffer to store converted UTF-8 string. + * \param len Size, in bytes, of destination buffer. + */ +__EXPORT__ void PHYSFS_utf8FromUcs4(const PHYSFS_uint32 *src, char *dst, + PHYSFS_uint64 len); + +/** + * \fn void PHYSFS_utf8ToUcs4(const char *src, PHYSFS_uint32 *dst, PHYSFS_uint64 len) + * \brief Convert a UTF-8 string to a UCS-4 string. + * + * UCS-4 strings are 32-bits per character: \c wchar_t on Unix. + * + * To ensure that the destination buffer is large enough for the conversion, + * please allocate a buffer that is four times the size of the source buffer. + * UTF-8 uses from one to four bytes per character, but UCS-4 always uses + * four, so an entirely low-ASCII string will quadruple in size! + * + * Strings that don't fit in the destination buffer will be truncated, but + * will always be null-terminated and never have an incomplete UCS-4 + * sequence at the end. If the buffer length is 0, this function does nothing. + * + * \param src Null-terminated source string in UTF-8 format. + * \param dst Buffer to store converted UCS-4 string. + * \param len Size, in bytes, of destination buffer. + */ +__EXPORT__ void PHYSFS_utf8ToUcs4(const char *src, PHYSFS_uint32 *dst, + PHYSFS_uint64 len); + +/** + * \fn void PHYSFS_utf8FromUcs2(const PHYSFS_uint16 *src, char *dst, PHYSFS_uint64 len) + * \brief Convert a UCS-2 string to a UTF-8 string. + * + * UCS-2 strings are 16-bits per character: \c TCHAR on Windows, when building + * with Unicode support. + * + * To ensure that the destination buffer is large enough for the conversion, + * please allocate a buffer that is double the size of the source buffer. + * UTF-8 never uses more than 32-bits per character, so while it may shrink + * a UCS-2 string, it may also expand it. + * + * Strings that don't fit in the destination buffer will be truncated, but + * will always be null-terminated and never have an incomplete UTF-8 + * sequence at the end. If the buffer length is 0, this function does nothing. + * + * Please note that UCS-2 is not UTF-16; we do not support the "surrogate" + * values at this time. + * + * \param src Null-terminated source string in UCS-2 format. + * \param dst Buffer to store converted UTF-8 string. + * \param len Size, in bytes, of destination buffer. + */ +__EXPORT__ void PHYSFS_utf8FromUcs2(const PHYSFS_uint16 *src, char *dst, + PHYSFS_uint64 len); + +/** + * \fn PHYSFS_utf8ToUcs2(const char *src, PHYSFS_uint16 *dst, PHYSFS_uint64 len) + * \brief Convert a UTF-8 string to a UCS-2 string. + * + * UCS-2 strings are 16-bits per character: \c TCHAR on Windows, when building + * with Unicode support. + * + * To ensure that the destination buffer is large enough for the conversion, + * please allocate a buffer that is double the size of the source buffer. + * UTF-8 uses from one to four bytes per character, but UCS-2 always uses + * two, so an entirely low-ASCII string will double in size! + * + * Strings that don't fit in the destination buffer will be truncated, but + * will always be null-terminated and never have an incomplete UCS-2 + * sequence at the end. If the buffer length is 0, this function does nothing. + * + * Please note that UCS-2 is not UTF-16; we do not support the "surrogate" + * values at this time. + * + * \param src Null-terminated source string in UTF-8 format. + * \param dst Buffer to store converted UCS-2 string. + * \param len Size, in bytes, of destination buffer. + */ +__EXPORT__ void PHYSFS_utf8ToUcs2(const char *src, PHYSFS_uint16 *dst, + PHYSFS_uint64 len); + +/** + * \fn void PHYSFS_utf8FromLatin1(const char *src, char *dst, PHYSFS_uint64 len) + * \brief Convert a UTF-8 string to a Latin1 string. + * + * Latin1 strings are 8-bits per character: a popular "high ASCII" + * encoding. + * + * To ensure that the destination buffer is large enough for the conversion, + * please allocate a buffer that is double the size of the source buffer. + * UTF-8 expands latin1 codepoints over 127 from 1 to 2 bytes, so the string + * may grow in some cases. + * + * Strings that don't fit in the destination buffer will be truncated, but + * will always be null-terminated and never have an incomplete UTF-8 + * sequence at the end. If the buffer length is 0, this function does nothing. + * + * Please note that we do not supply a UTF-8 to Latin1 converter, since Latin1 + * can't express most Unicode codepoints. It's a legacy encoding; you should + * be converting away from it at all times. + * + * \param src Null-terminated source string in Latin1 format. + * \param dst Buffer to store converted UTF-8 string. + * \param len Size, in bytes, of destination buffer. + */ +__EXPORT__ void PHYSFS_utf8FromLatin1(const char *src, char *dst, + PHYSFS_uint64 len); + +/* Everything above this line is part of the PhysicsFS 2.0 API. */ + + +#ifdef __cplusplus +} +#endif + +#endif /* !defined _INCLUDE_PHYSFS_H_ */ + +/* end of physfs.h ... */ + diff --git a/project/jni/physfs/include/physfs_casefolding.h b/project/jni/physfs/include/physfs_casefolding.h new file mode 100644 index 000000000..0e50f1e7d --- /dev/null +++ b/project/jni/physfs/include/physfs_casefolding.h @@ -0,0 +1,2013 @@ +/* + * This file is part of PhysicsFS (http://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 __PHYSICSFS_INTERNAL__ +#error Do not include this header from your applications. +#endif + +static const CaseFoldMapping case_fold_000[] = { + { 0x0202, 0x0203, 0x0000, 0x0000 }, + { 0x0404, 0x0454, 0x0000, 0x0000 }, + { 0x1E1E, 0x1E1F, 0x0000, 0x0000 }, + { 0x2C2C, 0x2C5C, 0x0000, 0x0000 }, + { 0x10404, 0x1042C, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_001[] = { + { 0x0100, 0x0101, 0x0000, 0x0000 }, + { 0x0405, 0x0455, 0x0000, 0x0000 }, + { 0x0504, 0x0505, 0x0000, 0x0000 }, + { 0x2C2D, 0x2C5D, 0x0000, 0x0000 }, + { 0x10405, 0x1042D, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_002[] = { + { 0x0200, 0x0201, 0x0000, 0x0000 }, + { 0x0406, 0x0456, 0x0000, 0x0000 }, + { 0x1E1C, 0x1E1D, 0x0000, 0x0000 }, + { 0x1F1D, 0x1F15, 0x0000, 0x0000 }, + { 0x2C2E, 0x2C5E, 0x0000, 0x0000 }, + { 0x10406, 0x1042E, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_003[] = { + { 0x0102, 0x0103, 0x0000, 0x0000 }, + { 0x0407, 0x0457, 0x0000, 0x0000 }, + { 0x0506, 0x0507, 0x0000, 0x0000 }, + { 0x1F1C, 0x1F14, 0x0000, 0x0000 }, + { 0x10407, 0x1042F, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_004[] = { + { 0x0206, 0x0207, 0x0000, 0x0000 }, + { 0x0400, 0x0450, 0x0000, 0x0000 }, + { 0x1E1A, 0x1E1B, 0x0000, 0x0000 }, + { 0x1F1B, 0x1F13, 0x0000, 0x0000 }, + { 0x2C28, 0x2C58, 0x0000, 0x0000 }, + { 0x10400, 0x10428, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_005[] = { + { 0x0104, 0x0105, 0x0000, 0x0000 }, + { 0x0401, 0x0451, 0x0000, 0x0000 }, + { 0x0500, 0x0501, 0x0000, 0x0000 }, + { 0x1F1A, 0x1F12, 0x0000, 0x0000 }, + { 0x2C29, 0x2C59, 0x0000, 0x0000 }, + { 0x10401, 0x10429, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_006[] = { + { 0x0204, 0x0205, 0x0000, 0x0000 }, + { 0x0402, 0x0452, 0x0000, 0x0000 }, + { 0x1E18, 0x1E19, 0x0000, 0x0000 }, + { 0x1F19, 0x1F11, 0x0000, 0x0000 }, + { 0x2C2A, 0x2C5A, 0x0000, 0x0000 }, + { 0x10402, 0x1042A, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_007[] = { + { 0x0106, 0x0107, 0x0000, 0x0000 }, + { 0x0403, 0x0453, 0x0000, 0x0000 }, + { 0x0502, 0x0503, 0x0000, 0x0000 }, + { 0x1F18, 0x1F10, 0x0000, 0x0000 }, + { 0x2126, 0x03C9, 0x0000, 0x0000 }, + { 0x2C2B, 0x2C5B, 0x0000, 0x0000 }, + { 0x10403, 0x1042B, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_008[] = { + { 0x020A, 0x020B, 0x0000, 0x0000 }, + { 0x040C, 0x045C, 0x0000, 0x0000 }, + { 0x1E16, 0x1E17, 0x0000, 0x0000 }, + { 0x2C24, 0x2C54, 0x0000, 0x0000 }, + { 0x1040C, 0x10434, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_009[] = { + { 0x0108, 0x0109, 0x0000, 0x0000 }, + { 0x040D, 0x045D, 0x0000, 0x0000 }, + { 0x050C, 0x050D, 0x0000, 0x0000 }, + { 0x2C25, 0x2C55, 0x0000, 0x0000 }, + { 0x1040D, 0x10435, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_010[] = { + { 0x0208, 0x0209, 0x0000, 0x0000 }, + { 0x040E, 0x045E, 0x0000, 0x0000 }, + { 0x1E14, 0x1E15, 0x0000, 0x0000 }, + { 0x212B, 0x00E5, 0x0000, 0x0000 }, + { 0x2C26, 0x2C56, 0x0000, 0x0000 }, + { 0x1040E, 0x10436, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_011[] = { + { 0x010A, 0x010B, 0x0000, 0x0000 }, + { 0x040F, 0x045F, 0x0000, 0x0000 }, + { 0x050E, 0x050F, 0x0000, 0x0000 }, + { 0x212A, 0x006B, 0x0000, 0x0000 }, + { 0x2C27, 0x2C57, 0x0000, 0x0000 }, + { 0x1040F, 0x10437, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_012[] = { + { 0x020E, 0x020F, 0x0000, 0x0000 }, + { 0x0408, 0x0458, 0x0000, 0x0000 }, + { 0x1E12, 0x1E13, 0x0000, 0x0000 }, + { 0x2C20, 0x2C50, 0x0000, 0x0000 }, + { 0x10408, 0x10430, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_013[] = { + { 0x010C, 0x010D, 0x0000, 0x0000 }, + { 0x0409, 0x0459, 0x0000, 0x0000 }, + { 0x0508, 0x0509, 0x0000, 0x0000 }, + { 0x2C21, 0x2C51, 0x0000, 0x0000 }, + { 0x10409, 0x10431, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_014[] = { + { 0x020C, 0x020D, 0x0000, 0x0000 }, + { 0x040A, 0x045A, 0x0000, 0x0000 }, + { 0x1E10, 0x1E11, 0x0000, 0x0000 }, + { 0x2C22, 0x2C52, 0x0000, 0x0000 }, + { 0x1040A, 0x10432, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_015[] = { + { 0x010E, 0x010F, 0x0000, 0x0000 }, + { 0x040B, 0x045B, 0x0000, 0x0000 }, + { 0x050A, 0x050B, 0x0000, 0x0000 }, + { 0x2C23, 0x2C53, 0x0000, 0x0000 }, + { 0x1040B, 0x10433, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_016[] = { + { 0x0212, 0x0213, 0x0000, 0x0000 }, + { 0x0414, 0x0434, 0x0000, 0x0000 }, + { 0x1E0E, 0x1E0F, 0x0000, 0x0000 }, + { 0x1F0F, 0x1F07, 0x0000, 0x0000 }, + { 0x10414, 0x1043C, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_017[] = { + { 0x0110, 0x0111, 0x0000, 0x0000 }, + { 0x0415, 0x0435, 0x0000, 0x0000 }, + { 0x1F0E, 0x1F06, 0x0000, 0x0000 }, + { 0x10415, 0x1043D, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_018[] = { + { 0x0210, 0x0211, 0x0000, 0x0000 }, + { 0x0416, 0x0436, 0x0000, 0x0000 }, + { 0x1E0C, 0x1E0D, 0x0000, 0x0000 }, + { 0x1F0D, 0x1F05, 0x0000, 0x0000 }, + { 0x10416, 0x1043E, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_019[] = { + { 0x0112, 0x0113, 0x0000, 0x0000 }, + { 0x0417, 0x0437, 0x0000, 0x0000 }, + { 0x1F0C, 0x1F04, 0x0000, 0x0000 }, + { 0x10417, 0x1043F, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_020[] = { + { 0x0216, 0x0217, 0x0000, 0x0000 }, + { 0x0410, 0x0430, 0x0000, 0x0000 }, + { 0x1E0A, 0x1E0B, 0x0000, 0x0000 }, + { 0x1F0B, 0x1F03, 0x0000, 0x0000 }, + { 0x10410, 0x10438, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_021[] = { + { 0x0114, 0x0115, 0x0000, 0x0000 }, + { 0x0411, 0x0431, 0x0000, 0x0000 }, + { 0x1F0A, 0x1F02, 0x0000, 0x0000 }, + { 0x10411, 0x10439, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_022[] = { + { 0x0214, 0x0215, 0x0000, 0x0000 }, + { 0x0412, 0x0432, 0x0000, 0x0000 }, + { 0x1E08, 0x1E09, 0x0000, 0x0000 }, + { 0x1F09, 0x1F01, 0x0000, 0x0000 }, + { 0x10412, 0x1043A, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_023[] = { + { 0x0116, 0x0117, 0x0000, 0x0000 }, + { 0x0413, 0x0433, 0x0000, 0x0000 }, + { 0x1F08, 0x1F00, 0x0000, 0x0000 }, + { 0x10413, 0x1043B, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_024[] = { + { 0x021A, 0x021B, 0x0000, 0x0000 }, + { 0x041C, 0x043C, 0x0000, 0x0000 }, + { 0x1E06, 0x1E07, 0x0000, 0x0000 }, + { 0x1041C, 0x10444, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_025[] = { + { 0x0118, 0x0119, 0x0000, 0x0000 }, + { 0x041D, 0x043D, 0x0000, 0x0000 }, + { 0x1041D, 0x10445, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_026[] = { + { 0x0218, 0x0219, 0x0000, 0x0000 }, + { 0x041E, 0x043E, 0x0000, 0x0000 }, + { 0x1E04, 0x1E05, 0x0000, 0x0000 }, + { 0x1041E, 0x10446, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_027[] = { + { 0x011A, 0x011B, 0x0000, 0x0000 }, + { 0x041F, 0x043F, 0x0000, 0x0000 }, + { 0x1041F, 0x10447, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_028[] = { + { 0x021E, 0x021F, 0x0000, 0x0000 }, + { 0x0418, 0x0438, 0x0000, 0x0000 }, + { 0x1E02, 0x1E03, 0x0000, 0x0000 }, + { 0x10418, 0x10440, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_029[] = { + { 0x011C, 0x011D, 0x0000, 0x0000 }, + { 0x0419, 0x0439, 0x0000, 0x0000 }, + { 0x10419, 0x10441, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_030[] = { + { 0x021C, 0x021D, 0x0000, 0x0000 }, + { 0x041A, 0x043A, 0x0000, 0x0000 }, + { 0x1E00, 0x1E01, 0x0000, 0x0000 }, + { 0x1041A, 0x10442, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_031[] = { + { 0x011E, 0x011F, 0x0000, 0x0000 }, + { 0x041B, 0x043B, 0x0000, 0x0000 }, + { 0x1041B, 0x10443, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_032[] = { + { 0x0222, 0x0223, 0x0000, 0x0000 }, + { 0x0424, 0x0444, 0x0000, 0x0000 }, + { 0x1E3E, 0x1E3F, 0x0000, 0x0000 }, + { 0x1F3F, 0x1F37, 0x0000, 0x0000 }, + { 0x2C0C, 0x2C3C, 0x0000, 0x0000 }, + { 0x10424, 0x1044C, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_033[] = { + { 0x0120, 0x0121, 0x0000, 0x0000 }, + { 0x0425, 0x0445, 0x0000, 0x0000 }, + { 0x1F3E, 0x1F36, 0x0000, 0x0000 }, + { 0x2C0D, 0x2C3D, 0x0000, 0x0000 }, + { 0x10425, 0x1044D, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_034[] = { + { 0x0220, 0x019E, 0x0000, 0x0000 }, + { 0x0426, 0x0446, 0x0000, 0x0000 }, + { 0x1E3C, 0x1E3D, 0x0000, 0x0000 }, + { 0x1F3D, 0x1F35, 0x0000, 0x0000 }, + { 0x2C0E, 0x2C3E, 0x0000, 0x0000 }, + { 0x10426, 0x1044E, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_035[] = { + { 0x0122, 0x0123, 0x0000, 0x0000 }, + { 0x0427, 0x0447, 0x0000, 0x0000 }, + { 0x1F3C, 0x1F34, 0x0000, 0x0000 }, + { 0x2C0F, 0x2C3F, 0x0000, 0x0000 }, + { 0x10427, 0x1044F, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_036[] = { + { 0x0226, 0x0227, 0x0000, 0x0000 }, + { 0x0420, 0x0440, 0x0000, 0x0000 }, + { 0x1E3A, 0x1E3B, 0x0000, 0x0000 }, + { 0x1F3B, 0x1F33, 0x0000, 0x0000 }, + { 0x2C08, 0x2C38, 0x0000, 0x0000 }, + { 0x10420, 0x10448, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_037[] = { + { 0x0124, 0x0125, 0x0000, 0x0000 }, + { 0x0421, 0x0441, 0x0000, 0x0000 }, + { 0x1F3A, 0x1F32, 0x0000, 0x0000 }, + { 0x2C09, 0x2C39, 0x0000, 0x0000 }, + { 0x10421, 0x10449, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_038[] = { + { 0x0224, 0x0225, 0x0000, 0x0000 }, + { 0x0422, 0x0442, 0x0000, 0x0000 }, + { 0x1E38, 0x1E39, 0x0000, 0x0000 }, + { 0x1F39, 0x1F31, 0x0000, 0x0000 }, + { 0x2C0A, 0x2C3A, 0x0000, 0x0000 }, + { 0x10422, 0x1044A, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_039[] = { + { 0x0126, 0x0127, 0x0000, 0x0000 }, + { 0x0423, 0x0443, 0x0000, 0x0000 }, + { 0x1F38, 0x1F30, 0x0000, 0x0000 }, + { 0x2C0B, 0x2C3B, 0x0000, 0x0000 }, + { 0x10423, 0x1044B, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_040[] = { + { 0x022A, 0x022B, 0x0000, 0x0000 }, + { 0x042C, 0x044C, 0x0000, 0x0000 }, + { 0x1E36, 0x1E37, 0x0000, 0x0000 }, + { 0x2C04, 0x2C34, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_041[] = { + { 0x0128, 0x0129, 0x0000, 0x0000 }, + { 0x042D, 0x044D, 0x0000, 0x0000 }, + { 0x2C05, 0x2C35, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_042[] = { + { 0x0228, 0x0229, 0x0000, 0x0000 }, + { 0x042E, 0x044E, 0x0000, 0x0000 }, + { 0x1E34, 0x1E35, 0x0000, 0x0000 }, + { 0x2C06, 0x2C36, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_043[] = { + { 0x012A, 0x012B, 0x0000, 0x0000 }, + { 0x042F, 0x044F, 0x0000, 0x0000 }, + { 0x2C07, 0x2C37, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_044[] = { + { 0x022E, 0x022F, 0x0000, 0x0000 }, + { 0x0428, 0x0448, 0x0000, 0x0000 }, + { 0x1E32, 0x1E33, 0x0000, 0x0000 }, + { 0x2C00, 0x2C30, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_045[] = { + { 0x012C, 0x012D, 0x0000, 0x0000 }, + { 0x0429, 0x0449, 0x0000, 0x0000 }, + { 0x2C01, 0x2C31, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_046[] = { + { 0x022C, 0x022D, 0x0000, 0x0000 }, + { 0x042A, 0x044A, 0x0000, 0x0000 }, + { 0x1E30, 0x1E31, 0x0000, 0x0000 }, + { 0x2C02, 0x2C32, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_047[] = { + { 0x012E, 0x012F, 0x0000, 0x0000 }, + { 0x042B, 0x044B, 0x0000, 0x0000 }, + { 0x2C03, 0x2C33, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_048[] = { + { 0x0232, 0x0233, 0x0000, 0x0000 }, + { 0x0535, 0x0565, 0x0000, 0x0000 }, + { 0x1E2E, 0x1E2F, 0x0000, 0x0000 }, + { 0x1F2F, 0x1F27, 0x0000, 0x0000 }, + { 0x2C1C, 0x2C4C, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_049[] = { + { 0x0130, 0x0069, 0x0307, 0x0000 }, + { 0x0534, 0x0564, 0x0000, 0x0000 }, + { 0x1F2E, 0x1F26, 0x0000, 0x0000 }, + { 0x2C1D, 0x2C4D, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_050[] = { + { 0x0230, 0x0231, 0x0000, 0x0000 }, + { 0x0537, 0x0567, 0x0000, 0x0000 }, + { 0x1E2C, 0x1E2D, 0x0000, 0x0000 }, + { 0x1F2D, 0x1F25, 0x0000, 0x0000 }, + { 0x2C1E, 0x2C4E, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_051[] = { + { 0x0132, 0x0133, 0x0000, 0x0000 }, + { 0x0536, 0x0566, 0x0000, 0x0000 }, + { 0x1F2C, 0x1F24, 0x0000, 0x0000 }, + { 0x2C1F, 0x2C4F, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_052[] = { + { 0x0531, 0x0561, 0x0000, 0x0000 }, + { 0x1E2A, 0x1E2B, 0x0000, 0x0000 }, + { 0x1F2B, 0x1F23, 0x0000, 0x0000 }, + { 0x2C18, 0x2C48, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_053[] = { + { 0x0134, 0x0135, 0x0000, 0x0000 }, + { 0x1F2A, 0x1F22, 0x0000, 0x0000 }, + { 0x2C19, 0x2C49, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_054[] = { + { 0x0533, 0x0563, 0x0000, 0x0000 }, + { 0x1E28, 0x1E29, 0x0000, 0x0000 }, + { 0x1F29, 0x1F21, 0x0000, 0x0000 }, + { 0x2C1A, 0x2C4A, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_055[] = { + { 0x0136, 0x0137, 0x0000, 0x0000 }, + { 0x0532, 0x0562, 0x0000, 0x0000 }, + { 0x1F28, 0x1F20, 0x0000, 0x0000 }, + { 0x2C1B, 0x2C4B, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_056[] = { + { 0x0139, 0x013A, 0x0000, 0x0000 }, + { 0x053D, 0x056D, 0x0000, 0x0000 }, + { 0x1E26, 0x1E27, 0x0000, 0x0000 }, + { 0x2C14, 0x2C44, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_057[] = { + { 0x023B, 0x023C, 0x0000, 0x0000 }, + { 0x053C, 0x056C, 0x0000, 0x0000 }, + { 0x2C15, 0x2C45, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_058[] = { + { 0x013B, 0x013C, 0x0000, 0x0000 }, + { 0x053F, 0x056F, 0x0000, 0x0000 }, + { 0x1E24, 0x1E25, 0x0000, 0x0000 }, + { 0x2C16, 0x2C46, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_059[] = { + { 0x053E, 0x056E, 0x0000, 0x0000 }, + { 0x2C17, 0x2C47, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_060[] = { + { 0x013D, 0x013E, 0x0000, 0x0000 }, + { 0x0539, 0x0569, 0x0000, 0x0000 }, + { 0x1E22, 0x1E23, 0x0000, 0x0000 }, + { 0x2C10, 0x2C40, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_061[] = { + { 0x0538, 0x0568, 0x0000, 0x0000 }, + { 0x2C11, 0x2C41, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_062[] = { + { 0x013F, 0x0140, 0x0000, 0x0000 }, + { 0x053B, 0x056B, 0x0000, 0x0000 }, + { 0x1E20, 0x1E21, 0x0000, 0x0000 }, + { 0x2C12, 0x2C42, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_063[] = { + { 0x023D, 0x019A, 0x0000, 0x0000 }, + { 0x053A, 0x056A, 0x0000, 0x0000 }, + { 0x2C13, 0x2C43, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_064[] = { + { 0x0141, 0x0142, 0x0000, 0x0000 }, + { 0x0545, 0x0575, 0x0000, 0x0000 }, + { 0x1E5E, 0x1E5F, 0x0000, 0x0000 }, + { 0x1F5F, 0x1F57, 0x0000, 0x0000 }, + { 0x2161, 0x2171, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_065[] = { + { 0x0041, 0x0061, 0x0000, 0x0000 }, + { 0x0544, 0x0574, 0x0000, 0x0000 }, + { 0x2160, 0x2170, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_066[] = { + { 0x0042, 0x0062, 0x0000, 0x0000 }, + { 0x0143, 0x0144, 0x0000, 0x0000 }, + { 0x0547, 0x0577, 0x0000, 0x0000 }, + { 0x1E5C, 0x1E5D, 0x0000, 0x0000 }, + { 0x1F5D, 0x1F55, 0x0000, 0x0000 }, + { 0x2163, 0x2173, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_067[] = { + { 0x0043, 0x0063, 0x0000, 0x0000 }, + { 0x0241, 0x0294, 0x0000, 0x0000 }, + { 0x0546, 0x0576, 0x0000, 0x0000 }, + { 0x2162, 0x2172, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_068[] = { + { 0x0044, 0x0064, 0x0000, 0x0000 }, + { 0x0145, 0x0146, 0x0000, 0x0000 }, + { 0x0541, 0x0571, 0x0000, 0x0000 }, + { 0x1E5A, 0x1E5B, 0x0000, 0x0000 }, + { 0x1F5B, 0x1F53, 0x0000, 0x0000 }, + { 0x2165, 0x2175, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_069[] = { + { 0x0045, 0x0065, 0x0000, 0x0000 }, + { 0x0540, 0x0570, 0x0000, 0x0000 }, + { 0x2164, 0x2174, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_070[] = { + { 0x0046, 0x0066, 0x0000, 0x0000 }, + { 0x0147, 0x0148, 0x0000, 0x0000 }, + { 0x0345, 0x03B9, 0x0000, 0x0000 }, + { 0x0543, 0x0573, 0x0000, 0x0000 }, + { 0x1E58, 0x1E59, 0x0000, 0x0000 }, + { 0x1F59, 0x1F51, 0x0000, 0x0000 }, + { 0x2167, 0x2177, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_071[] = { + { 0x0047, 0x0067, 0x0000, 0x0000 }, + { 0x0542, 0x0572, 0x0000, 0x0000 }, + { 0x2166, 0x2176, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_072[] = { + { 0x0048, 0x0068, 0x0000, 0x0000 }, + { 0x0149, 0x02BC, 0x006E, 0x0000 }, + { 0x054D, 0x057D, 0x0000, 0x0000 }, + { 0x1E56, 0x1E57, 0x0000, 0x0000 }, + { 0x2169, 0x2179, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_073[] = { + { 0x0049, 0x0069, 0x0000, 0x0000 }, + { 0x054C, 0x057C, 0x0000, 0x0000 }, + { 0x1F56, 0x03C5, 0x0313, 0x0342 }, + { 0x2168, 0x2178, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_074[] = { + { 0x004A, 0x006A, 0x0000, 0x0000 }, + { 0x054F, 0x057F, 0x0000, 0x0000 }, + { 0x1E54, 0x1E55, 0x0000, 0x0000 }, + { 0x216B, 0x217B, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_075[] = { + { 0x004B, 0x006B, 0x0000, 0x0000 }, + { 0x014A, 0x014B, 0x0000, 0x0000 }, + { 0x054E, 0x057E, 0x0000, 0x0000 }, + { 0x1F54, 0x03C5, 0x0313, 0x0301 }, + { 0x216A, 0x217A, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_076[] = { + { 0x004C, 0x006C, 0x0000, 0x0000 }, + { 0x0549, 0x0579, 0x0000, 0x0000 }, + { 0x1E52, 0x1E53, 0x0000, 0x0000 }, + { 0x216D, 0x217D, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_077[] = { + { 0x004D, 0x006D, 0x0000, 0x0000 }, + { 0x014C, 0x014D, 0x0000, 0x0000 }, + { 0x0548, 0x0578, 0x0000, 0x0000 }, + { 0x1F52, 0x03C5, 0x0313, 0x0300 }, + { 0x216C, 0x217C, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_078[] = { + { 0x004E, 0x006E, 0x0000, 0x0000 }, + { 0x054B, 0x057B, 0x0000, 0x0000 }, + { 0x1E50, 0x1E51, 0x0000, 0x0000 }, + { 0x216F, 0x217F, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_079[] = { + { 0x004F, 0x006F, 0x0000, 0x0000 }, + { 0x014E, 0x014F, 0x0000, 0x0000 }, + { 0x054A, 0x057A, 0x0000, 0x0000 }, + { 0x1F50, 0x03C5, 0x0313, 0x0000 }, + { 0x216E, 0x217E, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_080[] = { + { 0x0050, 0x0070, 0x0000, 0x0000 }, + { 0x0555, 0x0585, 0x0000, 0x0000 }, + { 0x1E4E, 0x1E4F, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_081[] = { + { 0x0051, 0x0071, 0x0000, 0x0000 }, + { 0x0150, 0x0151, 0x0000, 0x0000 }, + { 0x0554, 0x0584, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_082[] = { + { 0x0052, 0x0072, 0x0000, 0x0000 }, + { 0x1E4C, 0x1E4D, 0x0000, 0x0000 }, + { 0x1F4D, 0x1F45, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_083[] = { + { 0x0053, 0x0073, 0x0000, 0x0000 }, + { 0x0152, 0x0153, 0x0000, 0x0000 }, + { 0x0556, 0x0586, 0x0000, 0x0000 }, + { 0x1F4C, 0x1F44, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_084[] = { + { 0x0054, 0x0074, 0x0000, 0x0000 }, + { 0x0551, 0x0581, 0x0000, 0x0000 }, + { 0x1E4A, 0x1E4B, 0x0000, 0x0000 }, + { 0x1F4B, 0x1F43, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_085[] = { + { 0x0055, 0x0075, 0x0000, 0x0000 }, + { 0x0154, 0x0155, 0x0000, 0x0000 }, + { 0x0550, 0x0580, 0x0000, 0x0000 }, + { 0x1F4A, 0x1F42, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_086[] = { + { 0x0056, 0x0076, 0x0000, 0x0000 }, + { 0x0553, 0x0583, 0x0000, 0x0000 }, + { 0x1E48, 0x1E49, 0x0000, 0x0000 }, + { 0x1F49, 0x1F41, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_087[] = { + { 0x0057, 0x0077, 0x0000, 0x0000 }, + { 0x0156, 0x0157, 0x0000, 0x0000 }, + { 0x0552, 0x0582, 0x0000, 0x0000 }, + { 0x1F48, 0x1F40, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_088[] = { + { 0x0058, 0x0078, 0x0000, 0x0000 }, + { 0x1E46, 0x1E47, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_089[] = { + { 0x0059, 0x0079, 0x0000, 0x0000 }, + { 0x0158, 0x0159, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_090[] = { + { 0x005A, 0x007A, 0x0000, 0x0000 }, + { 0x1E44, 0x1E45, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_091[] = { + { 0x015A, 0x015B, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_092[] = { + { 0x1E42, 0x1E43, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_093[] = { + { 0x015C, 0x015D, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_094[] = { + { 0x1E40, 0x1E41, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_095[] = { + { 0x015E, 0x015F, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_096[] = { + { 0x0464, 0x0465, 0x0000, 0x0000 }, + { 0x1E7E, 0x1E7F, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_097[] = { + { 0x0160, 0x0161, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_098[] = { + { 0x0466, 0x0467, 0x0000, 0x0000 }, + { 0x1E7C, 0x1E7D, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_099[] = { + { 0x0162, 0x0163, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_100[] = { + { 0x0460, 0x0461, 0x0000, 0x0000 }, + { 0x1E7A, 0x1E7B, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_101[] = { + { 0x0164, 0x0165, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_102[] = { + { 0x0462, 0x0463, 0x0000, 0x0000 }, + { 0x1E78, 0x1E79, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_103[] = { + { 0x0166, 0x0167, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_104[] = { + { 0x046C, 0x046D, 0x0000, 0x0000 }, + { 0x1E76, 0x1E77, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_105[] = { + { 0x0168, 0x0169, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_106[] = { + { 0x046E, 0x046F, 0x0000, 0x0000 }, + { 0x1E74, 0x1E75, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_107[] = { + { 0x016A, 0x016B, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_108[] = { + { 0x0468, 0x0469, 0x0000, 0x0000 }, + { 0x1E72, 0x1E73, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_109[] = { + { 0x016C, 0x016D, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_110[] = { + { 0x046A, 0x046B, 0x0000, 0x0000 }, + { 0x1E70, 0x1E71, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_111[] = { + { 0x016E, 0x016F, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_112[] = { + { 0x0474, 0x0475, 0x0000, 0x0000 }, + { 0x1E6E, 0x1E6F, 0x0000, 0x0000 }, + { 0x1F6F, 0x1F67, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_113[] = { + { 0x0170, 0x0171, 0x0000, 0x0000 }, + { 0x1F6E, 0x1F66, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_114[] = { + { 0x0476, 0x0477, 0x0000, 0x0000 }, + { 0x1E6C, 0x1E6D, 0x0000, 0x0000 }, + { 0x1F6D, 0x1F65, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_115[] = { + { 0x0172, 0x0173, 0x0000, 0x0000 }, + { 0x1F6C, 0x1F64, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_116[] = { + { 0x0470, 0x0471, 0x0000, 0x0000 }, + { 0x1E6A, 0x1E6B, 0x0000, 0x0000 }, + { 0x1F6B, 0x1F63, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_117[] = { + { 0x0174, 0x0175, 0x0000, 0x0000 }, + { 0x1F6A, 0x1F62, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_118[] = { + { 0x0472, 0x0473, 0x0000, 0x0000 }, + { 0x1E68, 0x1E69, 0x0000, 0x0000 }, + { 0x1F69, 0x1F61, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_119[] = { + { 0x0176, 0x0177, 0x0000, 0x0000 }, + { 0x1F68, 0x1F60, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_120[] = { + { 0x0179, 0x017A, 0x0000, 0x0000 }, + { 0x047C, 0x047D, 0x0000, 0x0000 }, + { 0x1E66, 0x1E67, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_121[] = { + { 0x0178, 0x00FF, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_122[] = { + { 0x017B, 0x017C, 0x0000, 0x0000 }, + { 0x047E, 0x047F, 0x0000, 0x0000 }, + { 0x1E64, 0x1E65, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_124[] = { + { 0x017D, 0x017E, 0x0000, 0x0000 }, + { 0x0478, 0x0479, 0x0000, 0x0000 }, + { 0x1E62, 0x1E63, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_126[] = { + { 0x017F, 0x0073, 0x0000, 0x0000 }, + { 0x047A, 0x047B, 0x0000, 0x0000 }, + { 0x1E60, 0x1E61, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_128[] = { + { 0x0181, 0x0253, 0x0000, 0x0000 }, + { 0x1F9F, 0x1F27, 0x03B9, 0x0000 }, + { 0x2CAC, 0x2CAD, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_129[] = { + { 0x1F9E, 0x1F26, 0x03B9, 0x0000 } +}; + +static const CaseFoldMapping case_fold_130[] = { + { 0x0587, 0x0565, 0x0582, 0x0000 }, + { 0x1F9D, 0x1F25, 0x03B9, 0x0000 }, + { 0x2CAE, 0x2CAF, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_131[] = { + { 0x0182, 0x0183, 0x0000, 0x0000 }, + { 0x1F9C, 0x1F24, 0x03B9, 0x0000 } +}; + +static const CaseFoldMapping case_fold_132[] = { + { 0x0480, 0x0481, 0x0000, 0x0000 }, + { 0x1E9A, 0x0061, 0x02BE, 0x0000 }, + { 0x1F9B, 0x1F23, 0x03B9, 0x0000 }, + { 0x2CA8, 0x2CA9, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_133[] = { + { 0x0184, 0x0185, 0x0000, 0x0000 }, + { 0x0386, 0x03AC, 0x0000, 0x0000 }, + { 0x1E9B, 0x1E61, 0x0000, 0x0000 }, + { 0x1F9A, 0x1F22, 0x03B9, 0x0000 } +}; + +static const CaseFoldMapping case_fold_134[] = { + { 0x0187, 0x0188, 0x0000, 0x0000 }, + { 0x1E98, 0x0077, 0x030A, 0x0000 }, + { 0x1F99, 0x1F21, 0x03B9, 0x0000 }, + { 0x2CAA, 0x2CAB, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_135[] = { + { 0x0186, 0x0254, 0x0000, 0x0000 }, + { 0x1E99, 0x0079, 0x030A, 0x0000 }, + { 0x1F98, 0x1F20, 0x03B9, 0x0000 } +}; + +static const CaseFoldMapping case_fold_136[] = { + { 0x0189, 0x0256, 0x0000, 0x0000 }, + { 0x048C, 0x048D, 0x0000, 0x0000 }, + { 0x1E96, 0x0068, 0x0331, 0x0000 }, + { 0x1F97, 0x1F27, 0x03B9, 0x0000 }, + { 0x2CA4, 0x2CA5, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_137[] = { + { 0x038A, 0x03AF, 0x0000, 0x0000 }, + { 0x1E97, 0x0074, 0x0308, 0x0000 }, + { 0x1F96, 0x1F26, 0x03B9, 0x0000 } +}; + +static const CaseFoldMapping case_fold_138[] = { + { 0x018B, 0x018C, 0x0000, 0x0000 }, + { 0x0389, 0x03AE, 0x0000, 0x0000 }, + { 0x048E, 0x048F, 0x0000, 0x0000 }, + { 0x1E94, 0x1E95, 0x0000, 0x0000 }, + { 0x1F95, 0x1F25, 0x03B9, 0x0000 }, + { 0x2CA6, 0x2CA7, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_139[] = { + { 0x018A, 0x0257, 0x0000, 0x0000 }, + { 0x0388, 0x03AD, 0x0000, 0x0000 }, + { 0x1F94, 0x1F24, 0x03B9, 0x0000 } +}; + +static const CaseFoldMapping case_fold_140[] = { + { 0x038F, 0x03CE, 0x0000, 0x0000 }, + { 0x1E92, 0x1E93, 0x0000, 0x0000 }, + { 0x1F93, 0x1F23, 0x03B9, 0x0000 }, + { 0x2CA0, 0x2CA1, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_141[] = { + { 0x038E, 0x03CD, 0x0000, 0x0000 }, + { 0x1F92, 0x1F22, 0x03B9, 0x0000 } +}; + +static const CaseFoldMapping case_fold_142[] = { + { 0x018F, 0x0259, 0x0000, 0x0000 }, + { 0x048A, 0x048B, 0x0000, 0x0000 }, + { 0x1E90, 0x1E91, 0x0000, 0x0000 }, + { 0x1F91, 0x1F21, 0x03B9, 0x0000 }, + { 0x2CA2, 0x2CA3, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_143[] = { + { 0x018E, 0x01DD, 0x0000, 0x0000 }, + { 0x038C, 0x03CC, 0x0000, 0x0000 }, + { 0x1F90, 0x1F20, 0x03B9, 0x0000 } +}; + +static const CaseFoldMapping case_fold_144[] = { + { 0x0191, 0x0192, 0x0000, 0x0000 }, + { 0x0393, 0x03B3, 0x0000, 0x0000 }, + { 0x0494, 0x0495, 0x0000, 0x0000 }, + { 0x1E8E, 0x1E8F, 0x0000, 0x0000 }, + { 0x1F8F, 0x1F07, 0x03B9, 0x0000 }, + { 0x2CBC, 0x2CBD, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_145[] = { + { 0x0190, 0x025B, 0x0000, 0x0000 }, + { 0x0392, 0x03B2, 0x0000, 0x0000 }, + { 0x1F8E, 0x1F06, 0x03B9, 0x0000 } +}; + +static const CaseFoldMapping case_fold_146[] = { + { 0x0193, 0x0260, 0x0000, 0x0000 }, + { 0x0391, 0x03B1, 0x0000, 0x0000 }, + { 0x0496, 0x0497, 0x0000, 0x0000 }, + { 0x1E8C, 0x1E8D, 0x0000, 0x0000 }, + { 0x1F8D, 0x1F05, 0x03B9, 0x0000 }, + { 0x24B6, 0x24D0, 0x0000, 0x0000 }, + { 0x2CBE, 0x2CBF, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_147[] = { + { 0x0390, 0x03B9, 0x0308, 0x0301 }, + { 0x1F8C, 0x1F04, 0x03B9, 0x0000 }, + { 0x24B7, 0x24D1, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_148[] = { + { 0x0397, 0x03B7, 0x0000, 0x0000 }, + { 0x0490, 0x0491, 0x0000, 0x0000 }, + { 0x1E8A, 0x1E8B, 0x0000, 0x0000 }, + { 0x1F8B, 0x1F03, 0x03B9, 0x0000 }, + { 0x2CB8, 0x2CB9, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_149[] = { + { 0x0194, 0x0263, 0x0000, 0x0000 }, + { 0x0396, 0x03B6, 0x0000, 0x0000 }, + { 0x1F8A, 0x1F02, 0x03B9, 0x0000 } +}; + +static const CaseFoldMapping case_fold_150[] = { + { 0x0197, 0x0268, 0x0000, 0x0000 }, + { 0x0395, 0x03B5, 0x0000, 0x0000 }, + { 0x0492, 0x0493, 0x0000, 0x0000 }, + { 0x1E88, 0x1E89, 0x0000, 0x0000 }, + { 0x1F89, 0x1F01, 0x03B9, 0x0000 }, + { 0x2CBA, 0x2CBB, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_151[] = { + { 0x0196, 0x0269, 0x0000, 0x0000 }, + { 0x0394, 0x03B4, 0x0000, 0x0000 }, + { 0x1F88, 0x1F00, 0x03B9, 0x0000 } +}; + +static const CaseFoldMapping case_fold_152[] = { + { 0x039B, 0x03BB, 0x0000, 0x0000 }, + { 0x049C, 0x049D, 0x0000, 0x0000 }, + { 0x1E86, 0x1E87, 0x0000, 0x0000 }, + { 0x1F87, 0x1F07, 0x03B9, 0x0000 }, + { 0x24BC, 0x24D6, 0x0000, 0x0000 }, + { 0x2CB4, 0x2CB5, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_153[] = { + { 0x0198, 0x0199, 0x0000, 0x0000 }, + { 0x039A, 0x03BA, 0x0000, 0x0000 }, + { 0x1F86, 0x1F06, 0x03B9, 0x0000 }, + { 0x24BD, 0x24D7, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_154[] = { + { 0x0399, 0x03B9, 0x0000, 0x0000 }, + { 0x049E, 0x049F, 0x0000, 0x0000 }, + { 0x1E84, 0x1E85, 0x0000, 0x0000 }, + { 0x1F85, 0x1F05, 0x03B9, 0x0000 }, + { 0x24BE, 0x24D8, 0x0000, 0x0000 }, + { 0x2CB6, 0x2CB7, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_155[] = { + { 0x0398, 0x03B8, 0x0000, 0x0000 }, + { 0x1F84, 0x1F04, 0x03B9, 0x0000 }, + { 0x24BF, 0x24D9, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_156[] = { + { 0x019D, 0x0272, 0x0000, 0x0000 }, + { 0x039F, 0x03BF, 0x0000, 0x0000 }, + { 0x0498, 0x0499, 0x0000, 0x0000 }, + { 0x1E82, 0x1E83, 0x0000, 0x0000 }, + { 0x1F83, 0x1F03, 0x03B9, 0x0000 }, + { 0x24B8, 0x24D2, 0x0000, 0x0000 }, + { 0x2CB0, 0x2CB1, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_157[] = { + { 0x019C, 0x026F, 0x0000, 0x0000 }, + { 0x039E, 0x03BE, 0x0000, 0x0000 }, + { 0x1F82, 0x1F02, 0x03B9, 0x0000 }, + { 0x24B9, 0x24D3, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_158[] = { + { 0x019F, 0x0275, 0x0000, 0x0000 }, + { 0x039D, 0x03BD, 0x0000, 0x0000 }, + { 0x049A, 0x049B, 0x0000, 0x0000 }, + { 0x1E80, 0x1E81, 0x0000, 0x0000 }, + { 0x1F81, 0x1F01, 0x03B9, 0x0000 }, + { 0x24BA, 0x24D4, 0x0000, 0x0000 }, + { 0x2CB2, 0x2CB3, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_159[] = { + { 0x039C, 0x03BC, 0x0000, 0x0000 }, + { 0x1F80, 0x1F00, 0x03B9, 0x0000 }, + { 0x24BB, 0x24D5, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_160[] = { + { 0x03A3, 0x03C3, 0x0000, 0x0000 }, + { 0x04A4, 0x04A5, 0x0000, 0x0000 }, + { 0x10B0, 0x2D10, 0x0000, 0x0000 }, + { 0x1EBE, 0x1EBF, 0x0000, 0x0000 }, + { 0x2C8C, 0x2C8D, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_161[] = { + { 0x01A0, 0x01A1, 0x0000, 0x0000 }, + { 0x10B1, 0x2D11, 0x0000, 0x0000 }, + { 0x1FBE, 0x03B9, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_162[] = { + { 0x03A1, 0x03C1, 0x0000, 0x0000 }, + { 0x04A6, 0x04A7, 0x0000, 0x0000 }, + { 0x10B2, 0x2D12, 0x0000, 0x0000 }, + { 0x1EBC, 0x1EBD, 0x0000, 0x0000 }, + { 0x2C8E, 0x2C8F, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_163[] = { + { 0x01A2, 0x01A3, 0x0000, 0x0000 }, + { 0x03A0, 0x03C0, 0x0000, 0x0000 }, + { 0x10B3, 0x2D13, 0x0000, 0x0000 }, + { 0x1FBC, 0x03B1, 0x03B9, 0x0000 } +}; + +static const CaseFoldMapping case_fold_164[] = { + { 0x03A7, 0x03C7, 0x0000, 0x0000 }, + { 0x04A0, 0x04A1, 0x0000, 0x0000 }, + { 0x10B4, 0x2D14, 0x0000, 0x0000 }, + { 0x1EBA, 0x1EBB, 0x0000, 0x0000 }, + { 0x1FBB, 0x1F71, 0x0000, 0x0000 }, + { 0x2C88, 0x2C89, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_165[] = { + { 0x01A4, 0x01A5, 0x0000, 0x0000 }, + { 0x03A6, 0x03C6, 0x0000, 0x0000 }, + { 0x10B5, 0x2D15, 0x0000, 0x0000 }, + { 0x1FBA, 0x1F70, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_166[] = { + { 0x01A7, 0x01A8, 0x0000, 0x0000 }, + { 0x03A5, 0x03C5, 0x0000, 0x0000 }, + { 0x04A2, 0x04A3, 0x0000, 0x0000 }, + { 0x10B6, 0x2D16, 0x0000, 0x0000 }, + { 0x1EB8, 0x1EB9, 0x0000, 0x0000 }, + { 0x1FB9, 0x1FB1, 0x0000, 0x0000 }, + { 0x2C8A, 0x2C8B, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_167[] = { + { 0x01A6, 0x0280, 0x0000, 0x0000 }, + { 0x03A4, 0x03C4, 0x0000, 0x0000 }, + { 0x10B7, 0x2D17, 0x0000, 0x0000 }, + { 0x1FB8, 0x1FB0, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_168[] = { + { 0x01A9, 0x0283, 0x0000, 0x0000 }, + { 0x03AB, 0x03CB, 0x0000, 0x0000 }, + { 0x04AC, 0x04AD, 0x0000, 0x0000 }, + { 0x10B8, 0x2D18, 0x0000, 0x0000 }, + { 0x1EB6, 0x1EB7, 0x0000, 0x0000 }, + { 0x1FB7, 0x03B1, 0x0342, 0x03B9 }, + { 0x2C84, 0x2C85, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_169[] = { + { 0x03AA, 0x03CA, 0x0000, 0x0000 }, + { 0x10B9, 0x2D19, 0x0000, 0x0000 }, + { 0x1FB6, 0x03B1, 0x0342, 0x0000 } +}; + +static const CaseFoldMapping case_fold_170[] = { + { 0x03A9, 0x03C9, 0x0000, 0x0000 }, + { 0x04AE, 0x04AF, 0x0000, 0x0000 }, + { 0x10BA, 0x2D1A, 0x0000, 0x0000 }, + { 0x1EB4, 0x1EB5, 0x0000, 0x0000 }, + { 0x2C86, 0x2C87, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_171[] = { + { 0x03A8, 0x03C8, 0x0000, 0x0000 }, + { 0x10BB, 0x2D1B, 0x0000, 0x0000 }, + { 0x1FB4, 0x03AC, 0x03B9, 0x0000 } +}; + +static const CaseFoldMapping case_fold_172[] = { + { 0x04A8, 0x04A9, 0x0000, 0x0000 }, + { 0x10BC, 0x2D1C, 0x0000, 0x0000 }, + { 0x1EB2, 0x1EB3, 0x0000, 0x0000 }, + { 0x1FB3, 0x03B1, 0x03B9, 0x0000 }, + { 0x2C80, 0x2C81, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_173[] = { + { 0x01AC, 0x01AD, 0x0000, 0x0000 }, + { 0x10BD, 0x2D1D, 0x0000, 0x0000 }, + { 0x1FB2, 0x1F70, 0x03B9, 0x0000 } +}; + +static const CaseFoldMapping case_fold_174[] = { + { 0x01AF, 0x01B0, 0x0000, 0x0000 }, + { 0x04AA, 0x04AB, 0x0000, 0x0000 }, + { 0x10BE, 0x2D1E, 0x0000, 0x0000 }, + { 0x1EB0, 0x1EB1, 0x0000, 0x0000 }, + { 0x2C82, 0x2C83, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_175[] = { + { 0x01AE, 0x0288, 0x0000, 0x0000 }, + { 0x10BF, 0x2D1F, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_176[] = { + { 0x01B1, 0x028A, 0x0000, 0x0000 }, + { 0x04B4, 0x04B5, 0x0000, 0x0000 }, + { 0x10A0, 0x2D00, 0x0000, 0x0000 }, + { 0x1EAE, 0x1EAF, 0x0000, 0x0000 }, + { 0x1FAF, 0x1F67, 0x03B9, 0x0000 }, + { 0x2C9C, 0x2C9D, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_177[] = { + { 0x10A1, 0x2D01, 0x0000, 0x0000 }, + { 0x1FAE, 0x1F66, 0x03B9, 0x0000 } +}; + +static const CaseFoldMapping case_fold_178[] = { + { 0x01B3, 0x01B4, 0x0000, 0x0000 }, + { 0x04B6, 0x04B7, 0x0000, 0x0000 }, + { 0x10A2, 0x2D02, 0x0000, 0x0000 }, + { 0x1EAC, 0x1EAD, 0x0000, 0x0000 }, + { 0x1FAD, 0x1F65, 0x03B9, 0x0000 }, + { 0x2C9E, 0x2C9F, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_179[] = { + { 0x01B2, 0x028B, 0x0000, 0x0000 }, + { 0x03B0, 0x03C5, 0x0308, 0x0301 }, + { 0x10A3, 0x2D03, 0x0000, 0x0000 }, + { 0x1FAC, 0x1F64, 0x03B9, 0x0000 } +}; + +static const CaseFoldMapping case_fold_180[] = { + { 0x01B5, 0x01B6, 0x0000, 0x0000 }, + { 0x04B0, 0x04B1, 0x0000, 0x0000 }, + { 0x10A4, 0x2D04, 0x0000, 0x0000 }, + { 0x1EAA, 0x1EAB, 0x0000, 0x0000 }, + { 0x1FAB, 0x1F63, 0x03B9, 0x0000 }, + { 0x2C98, 0x2C99, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_181[] = { + { 0x00B5, 0x03BC, 0x0000, 0x0000 }, + { 0x10A5, 0x2D05, 0x0000, 0x0000 }, + { 0x1FAA, 0x1F62, 0x03B9, 0x0000 } +}; + +static const CaseFoldMapping case_fold_182[] = { + { 0x01B7, 0x0292, 0x0000, 0x0000 }, + { 0x04B2, 0x04B3, 0x0000, 0x0000 }, + { 0x10A6, 0x2D06, 0x0000, 0x0000 }, + { 0x1EA8, 0x1EA9, 0x0000, 0x0000 }, + { 0x1FA9, 0x1F61, 0x03B9, 0x0000 }, + { 0x2C9A, 0x2C9B, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_183[] = { + { 0x10A7, 0x2D07, 0x0000, 0x0000 }, + { 0x1FA8, 0x1F60, 0x03B9, 0x0000 } +}; + +static const CaseFoldMapping case_fold_184[] = { + { 0x04BC, 0x04BD, 0x0000, 0x0000 }, + { 0x10A8, 0x2D08, 0x0000, 0x0000 }, + { 0x1EA6, 0x1EA7, 0x0000, 0x0000 }, + { 0x1FA7, 0x1F67, 0x03B9, 0x0000 }, + { 0x2C94, 0x2C95, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_185[] = { + { 0x01B8, 0x01B9, 0x0000, 0x0000 }, + { 0x10A9, 0x2D09, 0x0000, 0x0000 }, + { 0x1FA6, 0x1F66, 0x03B9, 0x0000 } +}; + +static const CaseFoldMapping case_fold_186[] = { + { 0x04BE, 0x04BF, 0x0000, 0x0000 }, + { 0x10AA, 0x2D0A, 0x0000, 0x0000 }, + { 0x1EA4, 0x1EA5, 0x0000, 0x0000 }, + { 0x1FA5, 0x1F65, 0x03B9, 0x0000 }, + { 0x2C96, 0x2C97, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_187[] = { + { 0x10AB, 0x2D0B, 0x0000, 0x0000 }, + { 0x1FA4, 0x1F64, 0x03B9, 0x0000 } +}; + +static const CaseFoldMapping case_fold_188[] = { + { 0x04B8, 0x04B9, 0x0000, 0x0000 }, + { 0x10AC, 0x2D0C, 0x0000, 0x0000 }, + { 0x1EA2, 0x1EA3, 0x0000, 0x0000 }, + { 0x1FA3, 0x1F63, 0x03B9, 0x0000 }, + { 0x2C90, 0x2C91, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_189[] = { + { 0x01BC, 0x01BD, 0x0000, 0x0000 }, + { 0x10AD, 0x2D0D, 0x0000, 0x0000 }, + { 0x1FA2, 0x1F62, 0x03B9, 0x0000 } +}; + +static const CaseFoldMapping case_fold_190[] = { + { 0x04BA, 0x04BB, 0x0000, 0x0000 }, + { 0x10AE, 0x2D0E, 0x0000, 0x0000 }, + { 0x1EA0, 0x1EA1, 0x0000, 0x0000 }, + { 0x1FA1, 0x1F61, 0x03B9, 0x0000 }, + { 0x2C92, 0x2C93, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_191[] = { + { 0x10AF, 0x2D0F, 0x0000, 0x0000 }, + { 0x1FA0, 0x1F60, 0x03B9, 0x0000 } +}; + +static const CaseFoldMapping case_fold_192[] = { + { 0x00C0, 0x00E0, 0x0000, 0x0000 }, + { 0x1EDE, 0x1EDF, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_193[] = { + { 0x00C1, 0x00E1, 0x0000, 0x0000 }, + { 0x03C2, 0x03C3, 0x0000, 0x0000 }, + { 0x04C5, 0x04C6, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_194[] = { + { 0x00C2, 0x00E2, 0x0000, 0x0000 }, + { 0x1EDC, 0x1EDD, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_195[] = { + { 0x00C3, 0x00E3, 0x0000, 0x0000 }, + { 0x04C7, 0x04C8, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_196[] = { + { 0x00C4, 0x00E4, 0x0000, 0x0000 }, + { 0x01C5, 0x01C6, 0x0000, 0x0000 }, + { 0x1EDA, 0x1EDB, 0x0000, 0x0000 }, + { 0x1FDB, 0x1F77, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_197[] = { + { 0x00C5, 0x00E5, 0x0000, 0x0000 }, + { 0x01C4, 0x01C6, 0x0000, 0x0000 }, + { 0x04C1, 0x04C2, 0x0000, 0x0000 }, + { 0x1FDA, 0x1F76, 0x0000, 0x0000 }, + { 0xFF3A, 0xFF5A, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_198[] = { + { 0x00C6, 0x00E6, 0x0000, 0x0000 }, + { 0x01C7, 0x01C9, 0x0000, 0x0000 }, + { 0x1ED8, 0x1ED9, 0x0000, 0x0000 }, + { 0x1FD9, 0x1FD1, 0x0000, 0x0000 }, + { 0xFF39, 0xFF59, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_199[] = { + { 0x00C7, 0x00E7, 0x0000, 0x0000 }, + { 0x04C3, 0x04C4, 0x0000, 0x0000 }, + { 0x1FD8, 0x1FD0, 0x0000, 0x0000 }, + { 0xFF38, 0xFF58, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_200[] = { + { 0x00C8, 0x00E8, 0x0000, 0x0000 }, + { 0x1ED6, 0x1ED7, 0x0000, 0x0000 }, + { 0x1FD7, 0x03B9, 0x0308, 0x0342 }, + { 0xFF37, 0xFF57, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_201[] = { + { 0x00C9, 0x00E9, 0x0000, 0x0000 }, + { 0x01C8, 0x01C9, 0x0000, 0x0000 }, + { 0x04CD, 0x04CE, 0x0000, 0x0000 }, + { 0x1FD6, 0x03B9, 0x0342, 0x0000 }, + { 0xFF36, 0xFF56, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_202[] = { + { 0x00CA, 0x00EA, 0x0000, 0x0000 }, + { 0x01CB, 0x01CC, 0x0000, 0x0000 }, + { 0x1ED4, 0x1ED5, 0x0000, 0x0000 }, + { 0xFF35, 0xFF55, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_203[] = { + { 0x00CB, 0x00EB, 0x0000, 0x0000 }, + { 0x01CA, 0x01CC, 0x0000, 0x0000 }, + { 0xFF34, 0xFF54, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_204[] = { + { 0x00CC, 0x00EC, 0x0000, 0x0000 }, + { 0x01CD, 0x01CE, 0x0000, 0x0000 }, + { 0x1ED2, 0x1ED3, 0x0000, 0x0000 }, + { 0x1FD3, 0x03B9, 0x0308, 0x0301 }, + { 0x2CE0, 0x2CE1, 0x0000, 0x0000 }, + { 0xFF33, 0xFF53, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_205[] = { + { 0x00CD, 0x00ED, 0x0000, 0x0000 }, + { 0x04C9, 0x04CA, 0x0000, 0x0000 }, + { 0x1FD2, 0x03B9, 0x0308, 0x0300 }, + { 0xFF32, 0xFF52, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_206[] = { + { 0x00CE, 0x00EE, 0x0000, 0x0000 }, + { 0x01CF, 0x01D0, 0x0000, 0x0000 }, + { 0x1ED0, 0x1ED1, 0x0000, 0x0000 }, + { 0x2CE2, 0x2CE3, 0x0000, 0x0000 }, + { 0xFF31, 0xFF51, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_207[] = { + { 0x00CF, 0x00EF, 0x0000, 0x0000 }, + { 0x04CB, 0x04CC, 0x0000, 0x0000 }, + { 0xFF30, 0xFF50, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_208[] = { + { 0x00D0, 0x00F0, 0x0000, 0x0000 }, + { 0x01D1, 0x01D2, 0x0000, 0x0000 }, + { 0x04D4, 0x04D5, 0x0000, 0x0000 }, + { 0x10C0, 0x2D20, 0x0000, 0x0000 }, + { 0x1ECE, 0x1ECF, 0x0000, 0x0000 }, + { 0xFF2F, 0xFF4F, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_209[] = { + { 0x00D1, 0x00F1, 0x0000, 0x0000 }, + { 0x10C1, 0x2D21, 0x0000, 0x0000 }, + { 0xFF2E, 0xFF4E, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_210[] = { + { 0x00D2, 0x00F2, 0x0000, 0x0000 }, + { 0x01D3, 0x01D4, 0x0000, 0x0000 }, + { 0x03D1, 0x03B8, 0x0000, 0x0000 }, + { 0x04D6, 0x04D7, 0x0000, 0x0000 }, + { 0x10C2, 0x2D22, 0x0000, 0x0000 }, + { 0x1ECC, 0x1ECD, 0x0000, 0x0000 }, + { 0xFF2D, 0xFF4D, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_211[] = { + { 0x00D3, 0x00F3, 0x0000, 0x0000 }, + { 0x03D0, 0x03B2, 0x0000, 0x0000 }, + { 0x10C3, 0x2D23, 0x0000, 0x0000 }, + { 0x1FCC, 0x03B7, 0x03B9, 0x0000 }, + { 0xFF2C, 0xFF4C, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_212[] = { + { 0x00D4, 0x00F4, 0x0000, 0x0000 }, + { 0x01D5, 0x01D6, 0x0000, 0x0000 }, + { 0x04D0, 0x04D1, 0x0000, 0x0000 }, + { 0x10C4, 0x2D24, 0x0000, 0x0000 }, + { 0x1ECA, 0x1ECB, 0x0000, 0x0000 }, + { 0x1FCB, 0x1F75, 0x0000, 0x0000 }, + { 0xFF2B, 0xFF4B, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_213[] = { + { 0x00D5, 0x00F5, 0x0000, 0x0000 }, + { 0x03D6, 0x03C0, 0x0000, 0x0000 }, + { 0x10C5, 0x2D25, 0x0000, 0x0000 }, + { 0x1FCA, 0x1F74, 0x0000, 0x0000 }, + { 0xFF2A, 0xFF4A, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_214[] = { + { 0x00D6, 0x00F6, 0x0000, 0x0000 }, + { 0x01D7, 0x01D8, 0x0000, 0x0000 }, + { 0x03D5, 0x03C6, 0x0000, 0x0000 }, + { 0x04D2, 0x04D3, 0x0000, 0x0000 }, + { 0x1EC8, 0x1EC9, 0x0000, 0x0000 }, + { 0x1FC9, 0x1F73, 0x0000, 0x0000 }, + { 0xFF29, 0xFF49, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_215[] = { + { 0x1FC8, 0x1F72, 0x0000, 0x0000 }, + { 0xFF28, 0xFF48, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_216[] = { + { 0x00D8, 0x00F8, 0x0000, 0x0000 }, + { 0x01D9, 0x01DA, 0x0000, 0x0000 }, + { 0x04DC, 0x04DD, 0x0000, 0x0000 }, + { 0x1EC6, 0x1EC7, 0x0000, 0x0000 }, + { 0x1FC7, 0x03B7, 0x0342, 0x03B9 }, + { 0xFF27, 0xFF47, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_217[] = { + { 0x00D9, 0x00F9, 0x0000, 0x0000 }, + { 0x03DA, 0x03DB, 0x0000, 0x0000 }, + { 0x1FC6, 0x03B7, 0x0342, 0x0000 }, + { 0xFF26, 0xFF46, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_218[] = { + { 0x00DA, 0x00FA, 0x0000, 0x0000 }, + { 0x01DB, 0x01DC, 0x0000, 0x0000 }, + { 0x04DE, 0x04DF, 0x0000, 0x0000 }, + { 0x1EC4, 0x1EC5, 0x0000, 0x0000 }, + { 0xFF25, 0xFF45, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_219[] = { + { 0x00DB, 0x00FB, 0x0000, 0x0000 }, + { 0x03D8, 0x03D9, 0x0000, 0x0000 }, + { 0x1FC4, 0x03AE, 0x03B9, 0x0000 }, + { 0xFF24, 0xFF44, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_220[] = { + { 0x00DC, 0x00FC, 0x0000, 0x0000 }, + { 0x04D8, 0x04D9, 0x0000, 0x0000 }, + { 0x1EC2, 0x1EC3, 0x0000, 0x0000 }, + { 0x1FC3, 0x03B7, 0x03B9, 0x0000 }, + { 0xFF23, 0xFF43, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_221[] = { + { 0x00DD, 0x00FD, 0x0000, 0x0000 }, + { 0x03DE, 0x03DF, 0x0000, 0x0000 }, + { 0x1FC2, 0x1F74, 0x03B9, 0x0000 }, + { 0xFF22, 0xFF42, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_222[] = { + { 0x00DE, 0x00FE, 0x0000, 0x0000 }, + { 0x04DA, 0x04DB, 0x0000, 0x0000 }, + { 0x1EC0, 0x1EC1, 0x0000, 0x0000 }, + { 0xFF21, 0xFF41, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_223[] = { + { 0x00DF, 0x0073, 0x0073, 0x0000 }, + { 0x01DE, 0x01DF, 0x0000, 0x0000 }, + { 0x03DC, 0x03DD, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_224[] = { + { 0x04E4, 0x04E5, 0x0000, 0x0000 }, + { 0x24C4, 0x24DE, 0x0000, 0x0000 }, + { 0x2CCC, 0x2CCD, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_225[] = { + { 0x01E0, 0x01E1, 0x0000, 0x0000 }, + { 0x03E2, 0x03E3, 0x0000, 0x0000 }, + { 0x24C5, 0x24DF, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_226[] = { + { 0x04E6, 0x04E7, 0x0000, 0x0000 }, + { 0x24C6, 0x24E0, 0x0000, 0x0000 }, + { 0x2CCE, 0x2CCF, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_227[] = { + { 0x01E2, 0x01E3, 0x0000, 0x0000 }, + { 0x03E0, 0x03E1, 0x0000, 0x0000 }, + { 0x1FFC, 0x03C9, 0x03B9, 0x0000 }, + { 0x24C7, 0x24E1, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_228[] = { + { 0x04E0, 0x04E1, 0x0000, 0x0000 }, + { 0x1FFB, 0x1F7D, 0x0000, 0x0000 }, + { 0x24C0, 0x24DA, 0x0000, 0x0000 }, + { 0x2CC8, 0x2CC9, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_229[] = { + { 0x01E4, 0x01E5, 0x0000, 0x0000 }, + { 0x03E6, 0x03E7, 0x0000, 0x0000 }, + { 0x1FFA, 0x1F7C, 0x0000, 0x0000 }, + { 0x24C1, 0x24DB, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_230[] = { + { 0x04E2, 0x04E3, 0x0000, 0x0000 }, + { 0x1EF8, 0x1EF9, 0x0000, 0x0000 }, + { 0x1FF9, 0x1F79, 0x0000, 0x0000 }, + { 0x24C2, 0x24DC, 0x0000, 0x0000 }, + { 0x2CCA, 0x2CCB, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_231[] = { + { 0x01E6, 0x01E7, 0x0000, 0x0000 }, + { 0x03E4, 0x03E5, 0x0000, 0x0000 }, + { 0x1FF8, 0x1F78, 0x0000, 0x0000 }, + { 0x24C3, 0x24DD, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_232[] = { + { 0x04EC, 0x04ED, 0x0000, 0x0000 }, + { 0x1EF6, 0x1EF7, 0x0000, 0x0000 }, + { 0x1FF7, 0x03C9, 0x0342, 0x03B9 }, + { 0x24CC, 0x24E6, 0x0000, 0x0000 }, + { 0x2CC4, 0x2CC5, 0x0000, 0x0000 }, + { 0xFB13, 0x0574, 0x0576, 0x0000 } +}; + +static const CaseFoldMapping case_fold_233[] = { + { 0x01E8, 0x01E9, 0x0000, 0x0000 }, + { 0x03EA, 0x03EB, 0x0000, 0x0000 }, + { 0x1FF6, 0x03C9, 0x0342, 0x0000 }, + { 0x24CD, 0x24E7, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_234[] = { + { 0x04EE, 0x04EF, 0x0000, 0x0000 }, + { 0x1EF4, 0x1EF5, 0x0000, 0x0000 }, + { 0x24CE, 0x24E8, 0x0000, 0x0000 }, + { 0x2CC6, 0x2CC7, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_235[] = { + { 0x01EA, 0x01EB, 0x0000, 0x0000 }, + { 0x03E8, 0x03E9, 0x0000, 0x0000 }, + { 0x1FF4, 0x03CE, 0x03B9, 0x0000 }, + { 0x24CF, 0x24E9, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_236[] = { + { 0x04E8, 0x04E9, 0x0000, 0x0000 }, + { 0x1EF2, 0x1EF3, 0x0000, 0x0000 }, + { 0x1FF3, 0x03C9, 0x03B9, 0x0000 }, + { 0x24C8, 0x24E2, 0x0000, 0x0000 }, + { 0x2CC0, 0x2CC1, 0x0000, 0x0000 }, + { 0xFB17, 0x0574, 0x056D, 0x0000 } +}; + +static const CaseFoldMapping case_fold_237[] = { + { 0x01EC, 0x01ED, 0x0000, 0x0000 }, + { 0x03EE, 0x03EF, 0x0000, 0x0000 }, + { 0x1FF2, 0x1F7C, 0x03B9, 0x0000 }, + { 0x24C9, 0x24E3, 0x0000, 0x0000 }, + { 0xFB16, 0x057E, 0x0576, 0x0000 } +}; + +static const CaseFoldMapping case_fold_238[] = { + { 0x04EA, 0x04EB, 0x0000, 0x0000 }, + { 0x1EF0, 0x1EF1, 0x0000, 0x0000 }, + { 0x24CA, 0x24E4, 0x0000, 0x0000 }, + { 0x2CC2, 0x2CC3, 0x0000, 0x0000 }, + { 0xFB15, 0x0574, 0x056B, 0x0000 } +}; + +static const CaseFoldMapping case_fold_239[] = { + { 0x01EE, 0x01EF, 0x0000, 0x0000 }, + { 0x03EC, 0x03ED, 0x0000, 0x0000 }, + { 0x24CB, 0x24E5, 0x0000, 0x0000 }, + { 0xFB14, 0x0574, 0x0565, 0x0000 } +}; + +static const CaseFoldMapping case_fold_240[] = { + { 0x01F1, 0x01F3, 0x0000, 0x0000 }, + { 0x04F4, 0x04F5, 0x0000, 0x0000 }, + { 0x1EEE, 0x1EEF, 0x0000, 0x0000 }, + { 0x2CDC, 0x2CDD, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_241[] = { + { 0x01F0, 0x006A, 0x030C, 0x0000 } +}; + +static const CaseFoldMapping case_fold_242[] = { + { 0x03F1, 0x03C1, 0x0000, 0x0000 }, + { 0x04F6, 0x04F7, 0x0000, 0x0000 }, + { 0x1EEC, 0x1EED, 0x0000, 0x0000 }, + { 0x2CDE, 0x2CDF, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_243[] = { + { 0x01F2, 0x01F3, 0x0000, 0x0000 }, + { 0x03F0, 0x03BA, 0x0000, 0x0000 }, + { 0x1FEC, 0x1FE5, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_244[] = { + { 0x03F7, 0x03F8, 0x0000, 0x0000 }, + { 0x04F0, 0x04F1, 0x0000, 0x0000 }, + { 0x1EEA, 0x1EEB, 0x0000, 0x0000 }, + { 0x1FEB, 0x1F7B, 0x0000, 0x0000 }, + { 0x2CD8, 0x2CD9, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_245[] = { + { 0x01F4, 0x01F5, 0x0000, 0x0000 }, + { 0x1FEA, 0x1F7A, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_246[] = { + { 0x01F7, 0x01BF, 0x0000, 0x0000 }, + { 0x03F5, 0x03B5, 0x0000, 0x0000 }, + { 0x04F2, 0x04F3, 0x0000, 0x0000 }, + { 0x1EE8, 0x1EE9, 0x0000, 0x0000 }, + { 0x1FE9, 0x1FE1, 0x0000, 0x0000 }, + { 0x2CDA, 0x2CDB, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_247[] = { + { 0x01F6, 0x0195, 0x0000, 0x0000 }, + { 0x03F4, 0x03B8, 0x0000, 0x0000 }, + { 0x1FE8, 0x1FE0, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_248[] = { + { 0x1EE6, 0x1EE7, 0x0000, 0x0000 }, + { 0x1FE7, 0x03C5, 0x0308, 0x0342 }, + { 0x2CD4, 0x2CD5, 0x0000, 0x0000 }, + { 0xFB03, 0x0066, 0x0066, 0x0069 } +}; + +static const CaseFoldMapping case_fold_249[] = { + { 0x01F8, 0x01F9, 0x0000, 0x0000 }, + { 0x03FA, 0x03FB, 0x0000, 0x0000 }, + { 0x1FE6, 0x03C5, 0x0342, 0x0000 }, + { 0xFB02, 0x0066, 0x006C, 0x0000 } +}; + +static const CaseFoldMapping case_fold_250[] = { + { 0x03F9, 0x03F2, 0x0000, 0x0000 }, + { 0x1EE4, 0x1EE5, 0x0000, 0x0000 }, + { 0x2CD6, 0x2CD7, 0x0000, 0x0000 }, + { 0xFB01, 0x0066, 0x0069, 0x0000 } +}; + +static const CaseFoldMapping case_fold_251[] = { + { 0x01FA, 0x01FB, 0x0000, 0x0000 }, + { 0x1FE4, 0x03C1, 0x0313, 0x0000 }, + { 0xFB00, 0x0066, 0x0066, 0x0000 } +}; + +static const CaseFoldMapping case_fold_252[] = { + { 0x04F8, 0x04F9, 0x0000, 0x0000 }, + { 0x1EE2, 0x1EE3, 0x0000, 0x0000 }, + { 0x1FE3, 0x03C5, 0x0308, 0x0301 }, + { 0x2CD0, 0x2CD1, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_253[] = { + { 0x01FC, 0x01FD, 0x0000, 0x0000 }, + { 0x1FE2, 0x03C5, 0x0308, 0x0300 }, + { 0xFB06, 0x0073, 0x0074, 0x0000 } +}; + +static const CaseFoldMapping case_fold_254[] = { + { 0x1EE0, 0x1EE1, 0x0000, 0x0000 }, + { 0x2CD2, 0x2CD3, 0x0000, 0x0000 }, + { 0xFB05, 0x0073, 0x0074, 0x0000 } +}; + +static const CaseFoldMapping case_fold_255[] = { + { 0x01FE, 0x01FF, 0x0000, 0x0000 }, + { 0xFB04, 0x0066, 0x0066, 0x006C } +}; + + +static const CaseFoldHashBucket case_fold_hash[256] = { + { __PHYSFS_ARRAYLEN(case_fold_000), case_fold_000 }, + { __PHYSFS_ARRAYLEN(case_fold_001), case_fold_001 }, + { __PHYSFS_ARRAYLEN(case_fold_002), case_fold_002 }, + { __PHYSFS_ARRAYLEN(case_fold_003), case_fold_003 }, + { __PHYSFS_ARRAYLEN(case_fold_004), case_fold_004 }, + { __PHYSFS_ARRAYLEN(case_fold_005), case_fold_005 }, + { __PHYSFS_ARRAYLEN(case_fold_006), case_fold_006 }, + { __PHYSFS_ARRAYLEN(case_fold_007), case_fold_007 }, + { __PHYSFS_ARRAYLEN(case_fold_008), case_fold_008 }, + { __PHYSFS_ARRAYLEN(case_fold_009), case_fold_009 }, + { __PHYSFS_ARRAYLEN(case_fold_010), case_fold_010 }, + { __PHYSFS_ARRAYLEN(case_fold_011), case_fold_011 }, + { __PHYSFS_ARRAYLEN(case_fold_012), case_fold_012 }, + { __PHYSFS_ARRAYLEN(case_fold_013), case_fold_013 }, + { __PHYSFS_ARRAYLEN(case_fold_014), case_fold_014 }, + { __PHYSFS_ARRAYLEN(case_fold_015), case_fold_015 }, + { __PHYSFS_ARRAYLEN(case_fold_016), case_fold_016 }, + { __PHYSFS_ARRAYLEN(case_fold_017), case_fold_017 }, + { __PHYSFS_ARRAYLEN(case_fold_018), case_fold_018 }, + { __PHYSFS_ARRAYLEN(case_fold_019), case_fold_019 }, + { __PHYSFS_ARRAYLEN(case_fold_020), case_fold_020 }, + { __PHYSFS_ARRAYLEN(case_fold_021), case_fold_021 }, + { __PHYSFS_ARRAYLEN(case_fold_022), case_fold_022 }, + { __PHYSFS_ARRAYLEN(case_fold_023), case_fold_023 }, + { __PHYSFS_ARRAYLEN(case_fold_024), case_fold_024 }, + { __PHYSFS_ARRAYLEN(case_fold_025), case_fold_025 }, + { __PHYSFS_ARRAYLEN(case_fold_026), case_fold_026 }, + { __PHYSFS_ARRAYLEN(case_fold_027), case_fold_027 }, + { __PHYSFS_ARRAYLEN(case_fold_028), case_fold_028 }, + { __PHYSFS_ARRAYLEN(case_fold_029), case_fold_029 }, + { __PHYSFS_ARRAYLEN(case_fold_030), case_fold_030 }, + { __PHYSFS_ARRAYLEN(case_fold_031), case_fold_031 }, + { __PHYSFS_ARRAYLEN(case_fold_032), case_fold_032 }, + { __PHYSFS_ARRAYLEN(case_fold_033), case_fold_033 }, + { __PHYSFS_ARRAYLEN(case_fold_034), case_fold_034 }, + { __PHYSFS_ARRAYLEN(case_fold_035), case_fold_035 }, + { __PHYSFS_ARRAYLEN(case_fold_036), case_fold_036 }, + { __PHYSFS_ARRAYLEN(case_fold_037), case_fold_037 }, + { __PHYSFS_ARRAYLEN(case_fold_038), case_fold_038 }, + { __PHYSFS_ARRAYLEN(case_fold_039), case_fold_039 }, + { __PHYSFS_ARRAYLEN(case_fold_040), case_fold_040 }, + { __PHYSFS_ARRAYLEN(case_fold_041), case_fold_041 }, + { __PHYSFS_ARRAYLEN(case_fold_042), case_fold_042 }, + { __PHYSFS_ARRAYLEN(case_fold_043), case_fold_043 }, + { __PHYSFS_ARRAYLEN(case_fold_044), case_fold_044 }, + { __PHYSFS_ARRAYLEN(case_fold_045), case_fold_045 }, + { __PHYSFS_ARRAYLEN(case_fold_046), case_fold_046 }, + { __PHYSFS_ARRAYLEN(case_fold_047), case_fold_047 }, + { __PHYSFS_ARRAYLEN(case_fold_048), case_fold_048 }, + { __PHYSFS_ARRAYLEN(case_fold_049), case_fold_049 }, + { __PHYSFS_ARRAYLEN(case_fold_050), case_fold_050 }, + { __PHYSFS_ARRAYLEN(case_fold_051), case_fold_051 }, + { __PHYSFS_ARRAYLEN(case_fold_052), case_fold_052 }, + { __PHYSFS_ARRAYLEN(case_fold_053), case_fold_053 }, + { __PHYSFS_ARRAYLEN(case_fold_054), case_fold_054 }, + { __PHYSFS_ARRAYLEN(case_fold_055), case_fold_055 }, + { __PHYSFS_ARRAYLEN(case_fold_056), case_fold_056 }, + { __PHYSFS_ARRAYLEN(case_fold_057), case_fold_057 }, + { __PHYSFS_ARRAYLEN(case_fold_058), case_fold_058 }, + { __PHYSFS_ARRAYLEN(case_fold_059), case_fold_059 }, + { __PHYSFS_ARRAYLEN(case_fold_060), case_fold_060 }, + { __PHYSFS_ARRAYLEN(case_fold_061), case_fold_061 }, + { __PHYSFS_ARRAYLEN(case_fold_062), case_fold_062 }, + { __PHYSFS_ARRAYLEN(case_fold_063), case_fold_063 }, + { __PHYSFS_ARRAYLEN(case_fold_064), case_fold_064 }, + { __PHYSFS_ARRAYLEN(case_fold_065), case_fold_065 }, + { __PHYSFS_ARRAYLEN(case_fold_066), case_fold_066 }, + { __PHYSFS_ARRAYLEN(case_fold_067), case_fold_067 }, + { __PHYSFS_ARRAYLEN(case_fold_068), case_fold_068 }, + { __PHYSFS_ARRAYLEN(case_fold_069), case_fold_069 }, + { __PHYSFS_ARRAYLEN(case_fold_070), case_fold_070 }, + { __PHYSFS_ARRAYLEN(case_fold_071), case_fold_071 }, + { __PHYSFS_ARRAYLEN(case_fold_072), case_fold_072 }, + { __PHYSFS_ARRAYLEN(case_fold_073), case_fold_073 }, + { __PHYSFS_ARRAYLEN(case_fold_074), case_fold_074 }, + { __PHYSFS_ARRAYLEN(case_fold_075), case_fold_075 }, + { __PHYSFS_ARRAYLEN(case_fold_076), case_fold_076 }, + { __PHYSFS_ARRAYLEN(case_fold_077), case_fold_077 }, + { __PHYSFS_ARRAYLEN(case_fold_078), case_fold_078 }, + { __PHYSFS_ARRAYLEN(case_fold_079), case_fold_079 }, + { __PHYSFS_ARRAYLEN(case_fold_080), case_fold_080 }, + { __PHYSFS_ARRAYLEN(case_fold_081), case_fold_081 }, + { __PHYSFS_ARRAYLEN(case_fold_082), case_fold_082 }, + { __PHYSFS_ARRAYLEN(case_fold_083), case_fold_083 }, + { __PHYSFS_ARRAYLEN(case_fold_084), case_fold_084 }, + { __PHYSFS_ARRAYLEN(case_fold_085), case_fold_085 }, + { __PHYSFS_ARRAYLEN(case_fold_086), case_fold_086 }, + { __PHYSFS_ARRAYLEN(case_fold_087), case_fold_087 }, + { __PHYSFS_ARRAYLEN(case_fold_088), case_fold_088 }, + { __PHYSFS_ARRAYLEN(case_fold_089), case_fold_089 }, + { __PHYSFS_ARRAYLEN(case_fold_090), case_fold_090 }, + { __PHYSFS_ARRAYLEN(case_fold_091), case_fold_091 }, + { __PHYSFS_ARRAYLEN(case_fold_092), case_fold_092 }, + { __PHYSFS_ARRAYLEN(case_fold_093), case_fold_093 }, + { __PHYSFS_ARRAYLEN(case_fold_094), case_fold_094 }, + { __PHYSFS_ARRAYLEN(case_fold_095), case_fold_095 }, + { __PHYSFS_ARRAYLEN(case_fold_096), case_fold_096 }, + { __PHYSFS_ARRAYLEN(case_fold_097), case_fold_097 }, + { __PHYSFS_ARRAYLEN(case_fold_098), case_fold_098 }, + { __PHYSFS_ARRAYLEN(case_fold_099), case_fold_099 }, + { __PHYSFS_ARRAYLEN(case_fold_100), case_fold_100 }, + { __PHYSFS_ARRAYLEN(case_fold_101), case_fold_101 }, + { __PHYSFS_ARRAYLEN(case_fold_102), case_fold_102 }, + { __PHYSFS_ARRAYLEN(case_fold_103), case_fold_103 }, + { __PHYSFS_ARRAYLEN(case_fold_104), case_fold_104 }, + { __PHYSFS_ARRAYLEN(case_fold_105), case_fold_105 }, + { __PHYSFS_ARRAYLEN(case_fold_106), case_fold_106 }, + { __PHYSFS_ARRAYLEN(case_fold_107), case_fold_107 }, + { __PHYSFS_ARRAYLEN(case_fold_108), case_fold_108 }, + { __PHYSFS_ARRAYLEN(case_fold_109), case_fold_109 }, + { __PHYSFS_ARRAYLEN(case_fold_110), case_fold_110 }, + { __PHYSFS_ARRAYLEN(case_fold_111), case_fold_111 }, + { __PHYSFS_ARRAYLEN(case_fold_112), case_fold_112 }, + { __PHYSFS_ARRAYLEN(case_fold_113), case_fold_113 }, + { __PHYSFS_ARRAYLEN(case_fold_114), case_fold_114 }, + { __PHYSFS_ARRAYLEN(case_fold_115), case_fold_115 }, + { __PHYSFS_ARRAYLEN(case_fold_116), case_fold_116 }, + { __PHYSFS_ARRAYLEN(case_fold_117), case_fold_117 }, + { __PHYSFS_ARRAYLEN(case_fold_118), case_fold_118 }, + { __PHYSFS_ARRAYLEN(case_fold_119), case_fold_119 }, + { __PHYSFS_ARRAYLEN(case_fold_120), case_fold_120 }, + { __PHYSFS_ARRAYLEN(case_fold_121), case_fold_121 }, + { __PHYSFS_ARRAYLEN(case_fold_122), case_fold_122 }, + { 0, NULL }, + { __PHYSFS_ARRAYLEN(case_fold_124), case_fold_124 }, + { 0, NULL }, + { __PHYSFS_ARRAYLEN(case_fold_126), case_fold_126 }, + { 0, NULL }, + { __PHYSFS_ARRAYLEN(case_fold_128), case_fold_128 }, + { __PHYSFS_ARRAYLEN(case_fold_129), case_fold_129 }, + { __PHYSFS_ARRAYLEN(case_fold_130), case_fold_130 }, + { __PHYSFS_ARRAYLEN(case_fold_131), case_fold_131 }, + { __PHYSFS_ARRAYLEN(case_fold_132), case_fold_132 }, + { __PHYSFS_ARRAYLEN(case_fold_133), case_fold_133 }, + { __PHYSFS_ARRAYLEN(case_fold_134), case_fold_134 }, + { __PHYSFS_ARRAYLEN(case_fold_135), case_fold_135 }, + { __PHYSFS_ARRAYLEN(case_fold_136), case_fold_136 }, + { __PHYSFS_ARRAYLEN(case_fold_137), case_fold_137 }, + { __PHYSFS_ARRAYLEN(case_fold_138), case_fold_138 }, + { __PHYSFS_ARRAYLEN(case_fold_139), case_fold_139 }, + { __PHYSFS_ARRAYLEN(case_fold_140), case_fold_140 }, + { __PHYSFS_ARRAYLEN(case_fold_141), case_fold_141 }, + { __PHYSFS_ARRAYLEN(case_fold_142), case_fold_142 }, + { __PHYSFS_ARRAYLEN(case_fold_143), case_fold_143 }, + { __PHYSFS_ARRAYLEN(case_fold_144), case_fold_144 }, + { __PHYSFS_ARRAYLEN(case_fold_145), case_fold_145 }, + { __PHYSFS_ARRAYLEN(case_fold_146), case_fold_146 }, + { __PHYSFS_ARRAYLEN(case_fold_147), case_fold_147 }, + { __PHYSFS_ARRAYLEN(case_fold_148), case_fold_148 }, + { __PHYSFS_ARRAYLEN(case_fold_149), case_fold_149 }, + { __PHYSFS_ARRAYLEN(case_fold_150), case_fold_150 }, + { __PHYSFS_ARRAYLEN(case_fold_151), case_fold_151 }, + { __PHYSFS_ARRAYLEN(case_fold_152), case_fold_152 }, + { __PHYSFS_ARRAYLEN(case_fold_153), case_fold_153 }, + { __PHYSFS_ARRAYLEN(case_fold_154), case_fold_154 }, + { __PHYSFS_ARRAYLEN(case_fold_155), case_fold_155 }, + { __PHYSFS_ARRAYLEN(case_fold_156), case_fold_156 }, + { __PHYSFS_ARRAYLEN(case_fold_157), case_fold_157 }, + { __PHYSFS_ARRAYLEN(case_fold_158), case_fold_158 }, + { __PHYSFS_ARRAYLEN(case_fold_159), case_fold_159 }, + { __PHYSFS_ARRAYLEN(case_fold_160), case_fold_160 }, + { __PHYSFS_ARRAYLEN(case_fold_161), case_fold_161 }, + { __PHYSFS_ARRAYLEN(case_fold_162), case_fold_162 }, + { __PHYSFS_ARRAYLEN(case_fold_163), case_fold_163 }, + { __PHYSFS_ARRAYLEN(case_fold_164), case_fold_164 }, + { __PHYSFS_ARRAYLEN(case_fold_165), case_fold_165 }, + { __PHYSFS_ARRAYLEN(case_fold_166), case_fold_166 }, + { __PHYSFS_ARRAYLEN(case_fold_167), case_fold_167 }, + { __PHYSFS_ARRAYLEN(case_fold_168), case_fold_168 }, + { __PHYSFS_ARRAYLEN(case_fold_169), case_fold_169 }, + { __PHYSFS_ARRAYLEN(case_fold_170), case_fold_170 }, + { __PHYSFS_ARRAYLEN(case_fold_171), case_fold_171 }, + { __PHYSFS_ARRAYLEN(case_fold_172), case_fold_172 }, + { __PHYSFS_ARRAYLEN(case_fold_173), case_fold_173 }, + { __PHYSFS_ARRAYLEN(case_fold_174), case_fold_174 }, + { __PHYSFS_ARRAYLEN(case_fold_175), case_fold_175 }, + { __PHYSFS_ARRAYLEN(case_fold_176), case_fold_176 }, + { __PHYSFS_ARRAYLEN(case_fold_177), case_fold_177 }, + { __PHYSFS_ARRAYLEN(case_fold_178), case_fold_178 }, + { __PHYSFS_ARRAYLEN(case_fold_179), case_fold_179 }, + { __PHYSFS_ARRAYLEN(case_fold_180), case_fold_180 }, + { __PHYSFS_ARRAYLEN(case_fold_181), case_fold_181 }, + { __PHYSFS_ARRAYLEN(case_fold_182), case_fold_182 }, + { __PHYSFS_ARRAYLEN(case_fold_183), case_fold_183 }, + { __PHYSFS_ARRAYLEN(case_fold_184), case_fold_184 }, + { __PHYSFS_ARRAYLEN(case_fold_185), case_fold_185 }, + { __PHYSFS_ARRAYLEN(case_fold_186), case_fold_186 }, + { __PHYSFS_ARRAYLEN(case_fold_187), case_fold_187 }, + { __PHYSFS_ARRAYLEN(case_fold_188), case_fold_188 }, + { __PHYSFS_ARRAYLEN(case_fold_189), case_fold_189 }, + { __PHYSFS_ARRAYLEN(case_fold_190), case_fold_190 }, + { __PHYSFS_ARRAYLEN(case_fold_191), case_fold_191 }, + { __PHYSFS_ARRAYLEN(case_fold_192), case_fold_192 }, + { __PHYSFS_ARRAYLEN(case_fold_193), case_fold_193 }, + { __PHYSFS_ARRAYLEN(case_fold_194), case_fold_194 }, + { __PHYSFS_ARRAYLEN(case_fold_195), case_fold_195 }, + { __PHYSFS_ARRAYLEN(case_fold_196), case_fold_196 }, + { __PHYSFS_ARRAYLEN(case_fold_197), case_fold_197 }, + { __PHYSFS_ARRAYLEN(case_fold_198), case_fold_198 }, + { __PHYSFS_ARRAYLEN(case_fold_199), case_fold_199 }, + { __PHYSFS_ARRAYLEN(case_fold_200), case_fold_200 }, + { __PHYSFS_ARRAYLEN(case_fold_201), case_fold_201 }, + { __PHYSFS_ARRAYLEN(case_fold_202), case_fold_202 }, + { __PHYSFS_ARRAYLEN(case_fold_203), case_fold_203 }, + { __PHYSFS_ARRAYLEN(case_fold_204), case_fold_204 }, + { __PHYSFS_ARRAYLEN(case_fold_205), case_fold_205 }, + { __PHYSFS_ARRAYLEN(case_fold_206), case_fold_206 }, + { __PHYSFS_ARRAYLEN(case_fold_207), case_fold_207 }, + { __PHYSFS_ARRAYLEN(case_fold_208), case_fold_208 }, + { __PHYSFS_ARRAYLEN(case_fold_209), case_fold_209 }, + { __PHYSFS_ARRAYLEN(case_fold_210), case_fold_210 }, + { __PHYSFS_ARRAYLEN(case_fold_211), case_fold_211 }, + { __PHYSFS_ARRAYLEN(case_fold_212), case_fold_212 }, + { __PHYSFS_ARRAYLEN(case_fold_213), case_fold_213 }, + { __PHYSFS_ARRAYLEN(case_fold_214), case_fold_214 }, + { __PHYSFS_ARRAYLEN(case_fold_215), case_fold_215 }, + { __PHYSFS_ARRAYLEN(case_fold_216), case_fold_216 }, + { __PHYSFS_ARRAYLEN(case_fold_217), case_fold_217 }, + { __PHYSFS_ARRAYLEN(case_fold_218), case_fold_218 }, + { __PHYSFS_ARRAYLEN(case_fold_219), case_fold_219 }, + { __PHYSFS_ARRAYLEN(case_fold_220), case_fold_220 }, + { __PHYSFS_ARRAYLEN(case_fold_221), case_fold_221 }, + { __PHYSFS_ARRAYLEN(case_fold_222), case_fold_222 }, + { __PHYSFS_ARRAYLEN(case_fold_223), case_fold_223 }, + { __PHYSFS_ARRAYLEN(case_fold_224), case_fold_224 }, + { __PHYSFS_ARRAYLEN(case_fold_225), case_fold_225 }, + { __PHYSFS_ARRAYLEN(case_fold_226), case_fold_226 }, + { __PHYSFS_ARRAYLEN(case_fold_227), case_fold_227 }, + { __PHYSFS_ARRAYLEN(case_fold_228), case_fold_228 }, + { __PHYSFS_ARRAYLEN(case_fold_229), case_fold_229 }, + { __PHYSFS_ARRAYLEN(case_fold_230), case_fold_230 }, + { __PHYSFS_ARRAYLEN(case_fold_231), case_fold_231 }, + { __PHYSFS_ARRAYLEN(case_fold_232), case_fold_232 }, + { __PHYSFS_ARRAYLEN(case_fold_233), case_fold_233 }, + { __PHYSFS_ARRAYLEN(case_fold_234), case_fold_234 }, + { __PHYSFS_ARRAYLEN(case_fold_235), case_fold_235 }, + { __PHYSFS_ARRAYLEN(case_fold_236), case_fold_236 }, + { __PHYSFS_ARRAYLEN(case_fold_237), case_fold_237 }, + { __PHYSFS_ARRAYLEN(case_fold_238), case_fold_238 }, + { __PHYSFS_ARRAYLEN(case_fold_239), case_fold_239 }, + { __PHYSFS_ARRAYLEN(case_fold_240), case_fold_240 }, + { __PHYSFS_ARRAYLEN(case_fold_241), case_fold_241 }, + { __PHYSFS_ARRAYLEN(case_fold_242), case_fold_242 }, + { __PHYSFS_ARRAYLEN(case_fold_243), case_fold_243 }, + { __PHYSFS_ARRAYLEN(case_fold_244), case_fold_244 }, + { __PHYSFS_ARRAYLEN(case_fold_245), case_fold_245 }, + { __PHYSFS_ARRAYLEN(case_fold_246), case_fold_246 }, + { __PHYSFS_ARRAYLEN(case_fold_247), case_fold_247 }, + { __PHYSFS_ARRAYLEN(case_fold_248), case_fold_248 }, + { __PHYSFS_ARRAYLEN(case_fold_249), case_fold_249 }, + { __PHYSFS_ARRAYLEN(case_fold_250), case_fold_250 }, + { __PHYSFS_ARRAYLEN(case_fold_251), case_fold_251 }, + { __PHYSFS_ARRAYLEN(case_fold_252), case_fold_252 }, + { __PHYSFS_ARRAYLEN(case_fold_253), case_fold_253 }, + { __PHYSFS_ARRAYLEN(case_fold_254), case_fold_254 }, + { __PHYSFS_ARRAYLEN(case_fold_255), case_fold_255 }, +}; + diff --git a/project/jni/physfs/include/physfs_internal.h b/project/jni/physfs/include/physfs_internal.h new file mode 100644 index 000000000..eebec9824 --- /dev/null +++ b/project/jni/physfs/include/physfs_internal.h @@ -0,0 +1,1496 @@ +/* + * 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 + +#include "physfs.h" + +#include /* make sure NULL is defined... */ + +#ifdef HAVE_ASSERT_H +#include +#elif (!defined assert) +#define assert(x) +#endif + +/* !!! FIXME: remove this when revamping stack allocation code... */ +#if defined(_MSC_VER) || defined(__MINGW32__) +#include +#endif + +#if defined(__sun) || defined(sun) +#include +#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 + +/* + * 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 128 +void *__PHYSFS_initSmallAlloc(void *ptr, PHYSFS_uint64 len); + +#define __PHYSFS_smallAlloc(bytes) ( \ + __PHYSFS_initSmallAlloc((((bytes) < __PHYSFS_SMALLALLOCTHRESHOLD) ? \ + alloca((size_t)((bytes)+1)) : 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. */ + +/* The LANG section. */ +/* please send questions/translations to Ryan: icculus@icculus.org. */ + +#if (!defined PHYSFS_LANG) +# define PHYSFS_LANG PHYSFS_LANG_ENGLISH +#endif + +/* All language strings are UTF-8 encoded! */ +#define PHYSFS_LANG_ENGLISH 1 /* English by Ryan C. Gordon */ +#define PHYSFS_LANG_RUSSIAN 2 /* Russian by Ed Sinjiashvili */ +#define PHYSFS_LANG_SPANISH 3 /* Spanish by Pedro J. Pérez */ +#define PHYSFS_LANG_FRENCH 4 /* French by Stéphane Peter */ +#define PHYSFS_LANG_GERMAN 5 /* German by Michael Renner */ +#define PHYSFS_LANG_PORTUGUESE_BR 6 /* pt-br by Danny Angelo Carminati Grein */ + +#if (PHYSFS_LANG == PHYSFS_LANG_ENGLISH) + #define DIR_ARCHIVE_DESCRIPTION "Non-archive, direct filesystem I/O" + #define GRP_ARCHIVE_DESCRIPTION "Build engine Groupfile format" + #define HOG_ARCHIVE_DESCRIPTION "Descent I/II HOG file format" + #define MVL_ARCHIVE_DESCRIPTION "Descent II Movielib format" + #define QPAK_ARCHIVE_DESCRIPTION "Quake I/II format" + #define ZIP_ARCHIVE_DESCRIPTION "PkZip/WinZip/Info-Zip compatible" + #define WAD_ARCHIVE_DESCRIPTION "DOOM engine format" + #define LZMA_ARCHIVE_DESCRIPTION "LZMA (7zip) format" + + #define ERR_IS_INITIALIZED "Already initialized" + #define ERR_NOT_INITIALIZED "Not initialized" + #define ERR_INVALID_ARGUMENT "Invalid argument" + #define ERR_FILES_STILL_OPEN "Files still open" + #define ERR_NO_DIR_CREATE "Failed to create directories" + #define ERR_OUT_OF_MEMORY "Out of memory" + #define ERR_NOT_IN_SEARCH_PATH "No such entry in search path" + #define ERR_NOT_SUPPORTED "Operation not supported" + #define ERR_UNSUPPORTED_ARCHIVE "Archive type unsupported" + #define ERR_NOT_A_HANDLE "Not a file handle" + #define ERR_INSECURE_FNAME "Insecure filename" + #define ERR_SYMLINK_DISALLOWED "Symbolic links are disabled" + #define ERR_NO_WRITE_DIR "Write directory is not set" + #define ERR_NO_SUCH_FILE "File not found" + #define ERR_NO_SUCH_PATH "Path not found" + #define ERR_NO_SUCH_VOLUME "Volume not found" + #define ERR_PAST_EOF "Past end of file" + #define ERR_ARC_IS_READ_ONLY "Archive is read-only" + #define ERR_IO_ERROR "I/O error" + #define ERR_CANT_SET_WRITE_DIR "Can't set write directory" + #define ERR_SYMLINK_LOOP "Infinite symbolic link loop" + #define ERR_COMPRESSION "(De)compression error" + #define ERR_NOT_IMPLEMENTED "Not implemented" + #define ERR_OS_ERROR "Operating system reported error" + #define ERR_FILE_EXISTS "File already exists" + #define ERR_NOT_A_FILE "Not a file" + #define ERR_NOT_A_DIR "Not a directory" + #define ERR_NOT_AN_ARCHIVE "Not an archive" + #define ERR_CORRUPTED "Corrupted archive" + #define ERR_SEEK_OUT_OF_RANGE "Seek out of range" + #define ERR_BAD_FILENAME "Bad filename" + #define ERR_PHYSFS_BAD_OS_CALL "(BUG) PhysicsFS made a bad system call" + #define ERR_ARGV0_IS_NULL "argv0 is NULL" + #define ERR_NEED_DICT "need dictionary" + #define ERR_DATA_ERROR "data error" + #define ERR_MEMORY_ERROR "memory error" + #define ERR_BUFFER_ERROR "buffer error" + #define ERR_VERSION_ERROR "version error" + #define ERR_UNKNOWN_ERROR "unknown error" + #define ERR_SEARCHPATH_TRUNC "Search path was truncated" + #define ERR_GETMODFN_TRUNC "GetModuleFileName() was truncated" + #define ERR_GETMODFN_NO_DIR "GetModuleFileName() had no dir" + #define ERR_DISK_FULL "Disk is full" + #define ERR_DIRECTORY_FULL "Directory full" + #define ERR_MACOS_GENERIC "MacOS reported error (%d)" + #define ERR_OS2_GENERIC "OS/2 reported error (%d)" + #define ERR_VOL_LOCKED_HW "Volume is locked through hardware" + #define ERR_VOL_LOCKED_SW "Volume is locked through software" + #define ERR_FILE_LOCKED "File is locked" + #define ERR_FILE_OR_DIR_BUSY "File/directory is busy" + #define ERR_FILE_ALREADY_OPEN_W "File already open for writing" + #define ERR_FILE_ALREADY_OPEN_R "File already open for reading" + #define ERR_INVALID_REFNUM "Invalid reference number" + #define ERR_GETTING_FILE_POS "Error getting file position" + #define ERR_VOLUME_OFFLINE "Volume is offline" + #define ERR_PERMISSION_DENIED "Permission denied" + #define ERR_VOL_ALREADY_ONLINE "Volume already online" + #define ERR_NO_SUCH_DRIVE "No such drive" + #define ERR_NOT_MAC_DISK "Not a Macintosh disk" + #define ERR_VOL_EXTERNAL_FS "Volume belongs to an external filesystem" + #define ERR_PROBLEM_RENAME "Problem during rename" + #define ERR_BAD_MASTER_BLOCK "Bad master directory block" + #define ERR_CANT_MOVE_FORBIDDEN "Attempt to move forbidden" + #define ERR_WRONG_VOL_TYPE "Wrong volume type" + #define ERR_SERVER_VOL_LOST "Server volume has been disconnected" + #define ERR_FILE_ID_NOT_FOUND "File ID not found" + #define ERR_FILE_ID_EXISTS "File ID already exists" + #define ERR_SERVER_NO_RESPOND "Server not responding" + #define ERR_USER_AUTH_FAILED "User authentication failed" + #define ERR_PWORD_EXPIRED "Password has expired on server" + #define ERR_ACCESS_DENIED "Access denied" + #define ERR_NOT_A_DOS_DISK "Not a DOS disk" + #define ERR_SHARING_VIOLATION "Sharing violation" + #define ERR_CANNOT_MAKE "Cannot make" + #define ERR_DEV_IN_USE "Device already in use" + #define ERR_OPEN_FAILED "Open failed" + #define ERR_PIPE_BUSY "Pipe is busy" + #define ERR_SHARING_BUF_EXCEEDED "Sharing buffer exceeded" + #define ERR_TOO_MANY_HANDLES "Too many open handles" + #define ERR_SEEK_ERROR "Seek error" + #define ERR_DEL_CWD "Trying to delete current working directory" + #define ERR_WRITE_PROTECT_ERROR "Write protect error" + #define ERR_WRITE_FAULT "Write fault" + #define ERR_LOCK_VIOLATION "Lock violation" + #define ERR_GEN_FAILURE "General failure" + #define ERR_UNCERTAIN_MEDIA "Uncertain media" + #define ERR_PROT_VIOLATION "Protection violation" + #define ERR_BROKEN_PIPE "Broken pipe" + +#elif (PHYSFS_LANG == PHYSFS_LANG_GERMAN) + #define DIR_ARCHIVE_DESCRIPTION "Kein Archiv, direkte Ein/Ausgabe in das Dateisystem" + #define GRP_ARCHIVE_DESCRIPTION "Build engine Groupfile format" + #define HOG_ARCHIVE_DESCRIPTION "Descent I/II HOG file format" + #define MVL_ARCHIVE_DESCRIPTION "Descent II Movielib format" + #define QPAK_ARCHIVE_DESCRIPTION "Quake I/II format" + #define ZIP_ARCHIVE_DESCRIPTION "PkZip/WinZip/Info-Zip kompatibel" + #define WAD_ARCHIVE_DESCRIPTION "DOOM engine format" /* !!! FIXME: translate this line if needed */ + #define LZMA_ARCHIVE_DESCRIPTION "LZMA (7zip) format" /* !!! FIXME: translate this line if needed */ + + #define ERR_IS_INITIALIZED "Bereits initialisiert" + #define ERR_NOT_INITIALIZED "Nicht initialisiert" + #define ERR_INVALID_ARGUMENT "Ungültiges Argument" + #define ERR_FILES_STILL_OPEN "Dateien noch immer geöffnet" + #define ERR_NO_DIR_CREATE "Fehler beim Erzeugen der Verzeichnisse" + #define ERR_OUT_OF_MEMORY "Kein Speicher mehr frei" + #define ERR_NOT_IN_SEARCH_PATH "Eintrag nicht im Suchpfad enthalten" + #define ERR_NOT_SUPPORTED "Befehl nicht unterstützt" + #define ERR_UNSUPPORTED_ARCHIVE "Archiv-Typ nicht unterstützt" + #define ERR_NOT_A_HANDLE "Ist kein Dateideskriptor" + #define ERR_INSECURE_FNAME "Unsicherer Dateiname" + #define ERR_SYMLINK_DISALLOWED "Symbolische Verweise deaktiviert" + #define ERR_NO_WRITE_DIR "Schreibverzeichnis ist nicht gesetzt" + #define ERR_NO_SUCH_FILE "Datei nicht gefunden" + #define ERR_NO_SUCH_PATH "Pfad nicht gefunden" + #define ERR_NO_SUCH_VOLUME "Datencontainer nicht gefunden" + #define ERR_PAST_EOF "Hinter dem Ende der Datei" + #define ERR_ARC_IS_READ_ONLY "Archiv ist schreibgeschützt" + #define ERR_IO_ERROR "Ein/Ausgabe Fehler" + #define ERR_CANT_SET_WRITE_DIR "Kann Schreibverzeichnis nicht setzen" + #define ERR_SYMLINK_LOOP "Endlosschleife durch symbolische Verweise" + #define ERR_COMPRESSION "(De)Kompressionsfehler" + #define ERR_NOT_IMPLEMENTED "Nicht implementiert" + #define ERR_OS_ERROR "Betriebssystem meldete Fehler" + #define ERR_FILE_EXISTS "Datei existiert bereits" + #define ERR_NOT_A_FILE "Ist keine Datei" + #define ERR_NOT_A_DIR "Ist kein Verzeichnis" + #define ERR_NOT_AN_ARCHIVE "Ist kein Archiv" + #define ERR_CORRUPTED "Beschädigtes Archiv" + #define ERR_SEEK_OUT_OF_RANGE "Suche war ausserhalb der Reichweite" + #define ERR_BAD_FILENAME "Unzulässiger Dateiname" + #define ERR_PHYSFS_BAD_OS_CALL "(BUG) PhysicsFS verursachte einen ungültigen Systemaufruf" + #define ERR_ARGV0_IS_NULL "argv0 ist NULL" + #define ERR_NEED_DICT "brauche Wörterbuch" + #define ERR_DATA_ERROR "Datenfehler" + #define ERR_MEMORY_ERROR "Speicherfehler" + #define ERR_BUFFER_ERROR "Bufferfehler" + #define ERR_VERSION_ERROR "Versionskonflikt" + #define ERR_UNKNOWN_ERROR "Unbekannter Fehler" + #define ERR_SEARCHPATH_TRUNC "Suchpfad war abgeschnitten" + #define ERR_GETMODFN_TRUNC "GetModuleFileName() war abgeschnitten" + #define ERR_GETMODFN_NO_DIR "GetModuleFileName() bekam kein Verzeichnis" + #define ERR_DISK_FULL "Laufwerk ist voll" + #define ERR_DIRECTORY_FULL "Verzeichnis ist voll" + #define ERR_MACOS_GENERIC "MacOS meldete Fehler (%d)" + #define ERR_OS2_GENERIC "OS/2 meldete Fehler (%d)" + #define ERR_VOL_LOCKED_HW "Datencontainer ist durch Hardware gesperrt" + #define ERR_VOL_LOCKED_SW "Datencontainer ist durch Software gesperrt" + #define ERR_FILE_LOCKED "Datei ist gesperrt" + #define ERR_FILE_OR_DIR_BUSY "Datei/Verzeichnis ist beschäftigt" + #define ERR_FILE_ALREADY_OPEN_W "Datei schon im Schreibmodus geöffnet" + #define ERR_FILE_ALREADY_OPEN_R "Datei schon im Lesemodus geöffnet" + #define ERR_INVALID_REFNUM "Ungültige Referenznummer" + #define ERR_GETTING_FILE_POS "Fehler beim Finden der Dateiposition" + #define ERR_VOLUME_OFFLINE "Datencontainer ist offline" + #define ERR_PERMISSION_DENIED "Zugriff verweigert" + #define ERR_VOL_ALREADY_ONLINE "Datencontainer ist bereits online" + #define ERR_NO_SUCH_DRIVE "Laufwerk nicht vorhanden" + #define ERR_NOT_MAC_DISK "Ist kein Macintosh Laufwerk" + #define ERR_VOL_EXTERNAL_FS "Datencontainer liegt auf einem externen Dateisystem" + #define ERR_PROBLEM_RENAME "Fehler beim Umbenennen" + #define ERR_BAD_MASTER_BLOCK "Beschädigter Hauptverzeichnisblock" + #define ERR_CANT_MOVE_FORBIDDEN "Verschieben nicht erlaubt" + #define ERR_WRONG_VOL_TYPE "Falscher Datencontainer-Typ" + #define ERR_SERVER_VOL_LOST "Datencontainer am Server wurde getrennt" + #define ERR_FILE_ID_NOT_FOUND "Dateikennung nicht gefunden" + #define ERR_FILE_ID_EXISTS "Dateikennung existiert bereits" + #define ERR_SERVER_NO_RESPOND "Server antwortet nicht" + #define ERR_USER_AUTH_FAILED "Benutzerauthentifizierung fehlgeschlagen" + #define ERR_PWORD_EXPIRED "Passwort am Server ist abgelaufen" + #define ERR_ACCESS_DENIED "Zugriff verweigert" + #define ERR_NOT_A_DOS_DISK "Ist kein DOS-Laufwerk" + #define ERR_SHARING_VIOLATION "Zugriffsverletzung" + #define ERR_CANNOT_MAKE "Kann nicht erzeugen" + #define ERR_DEV_IN_USE "Gerät wird bereits benutzt" + #define ERR_OPEN_FAILED "Öffnen fehlgeschlagen" + #define ERR_PIPE_BUSY "Pipeverbindung ist belegt" + #define ERR_SHARING_BUF_EXCEEDED "Zugriffsbuffer überschritten" + #define ERR_TOO_MANY_HANDLES "Zu viele offene Dateien" + #define ERR_SEEK_ERROR "Fehler beim Suchen" + #define ERR_DEL_CWD "Aktuelles Arbeitsverzeichnis darf nicht gelöscht werden" + #define ERR_WRITE_PROTECT_ERROR "Schreibschutzfehler" + #define ERR_WRITE_FAULT "Schreibfehler" + #define ERR_LOCK_VIOLATION "Sperrverletzung" + #define ERR_GEN_FAILURE "Allgemeiner Fehler" + #define ERR_UNCERTAIN_MEDIA "Unsicheres Medium" + #define ERR_PROT_VIOLATION "Schutzverletzung" + #define ERR_BROKEN_PIPE "Pipeverbindung unterbrochen" + +#elif (PHYSFS_LANG == PHYSFS_LANG_RUSSIAN) + #define DIR_ARCHIVE_DESCRIPTION "Не архив, непосредственный ввод/вывод файловой системы" + #define GRP_ARCHIVE_DESCRIPTION "Формат группового файла Build engine" + #define HOG_ARCHIVE_DESCRIPTION "Descent I/II HOG file format" + #define MVL_ARCHIVE_DESCRIPTION "Descent II Movielib format" + #define ZIP_ARCHIVE_DESCRIPTION "PkZip/WinZip/Info-Zip совместимый" + #define WAD_ARCHIVE_DESCRIPTION "DOOM engine format" /* !!! FIXME: translate this line if needed */ + #define LZMA_ARCHIVE_DESCRIPTION "LZMA (7zip) format" /* !!! FIXME: translate this line if needed */ + + #define ERR_IS_INITIALIZED "Уже инициализирован" + #define ERR_NOT_INITIALIZED "Не инициализирован" + #define ERR_INVALID_ARGUMENT "Неверный аргумент" + #define ERR_FILES_STILL_OPEN "Файлы еще открыты" + #define ERR_NO_DIR_CREATE "Не могу создать каталоги" + #define ERR_OUT_OF_MEMORY "Кончилась память" + #define ERR_NOT_IN_SEARCH_PATH "Нет такого элемента в пути поиска" + #define ERR_NOT_SUPPORTED "Операция не поддерживается" + #define ERR_UNSUPPORTED_ARCHIVE "Архивы такого типа не поддерживаются" + #define ERR_NOT_A_HANDLE "Не файловый дескриптор" + #define ERR_INSECURE_FNAME "Небезопасное имя файла" + #define ERR_SYMLINK_DISALLOWED "Символьные ссылки отключены" + #define ERR_NO_WRITE_DIR "Каталог для записи не установлен" + #define ERR_NO_SUCH_FILE "Файл не найден" + #define ERR_NO_SUCH_PATH "Путь не найден" + #define ERR_NO_SUCH_VOLUME "Том не найден" + #define ERR_PAST_EOF "За концом файла" + #define ERR_ARC_IS_READ_ONLY "Архив только для чтения" + #define ERR_IO_ERROR "Ошибка ввода/вывода" + #define ERR_CANT_SET_WRITE_DIR "Не могу установить каталог для записи" + #define ERR_SYMLINK_LOOP "Бесконечный цикл символьной ссылки" + #define ERR_COMPRESSION "Ошибка (Рас)паковки" + #define ERR_NOT_IMPLEMENTED "Не реализовано" + #define ERR_OS_ERROR "Операционная система сообщила ошибку" + #define ERR_FILE_EXISTS "Файл уже существует" + #define ERR_NOT_A_FILE "Не файл" + #define ERR_NOT_A_DIR "Не каталог" + #define ERR_NOT_AN_ARCHIVE "Не архив" + #define ERR_CORRUPTED "Поврежденный архив" + #define ERR_SEEK_OUT_OF_RANGE "Позиционирование за пределы" + #define ERR_BAD_FILENAME "Неверное имя файла" + #define ERR_PHYSFS_BAD_OS_CALL "(BUG) PhysicsFS выполнила неверный системный вызов" + #define ERR_ARGV0_IS_NULL "argv0 is NULL" + #define ERR_NEED_DICT "нужен словарь" + #define ERR_DATA_ERROR "ошибка данных" + #define ERR_MEMORY_ERROR "ошибка памяти" + #define ERR_BUFFER_ERROR "ошибка буфера" + #define ERR_VERSION_ERROR "ошибка версии" + #define ERR_UNKNOWN_ERROR "неизвестная ошибка" + #define ERR_SEARCHPATH_TRUNC "Путь поиска обрезан" + #define ERR_GETMODFN_TRUNC "GetModuleFileName() обрезан" + #define ERR_GETMODFN_NO_DIR "GetModuleFileName() не получил каталог" + #define ERR_DISK_FULL "Диск полон" + #define ERR_DIRECTORY_FULL "Каталог полон" + #define ERR_MACOS_GENERIC "MacOS сообщила ошибку (%d)" + #define ERR_OS2_GENERIC "OS/2 сообщила ошибку (%d)" + #define ERR_VOL_LOCKED_HW "Том блокирован аппаратно" + #define ERR_VOL_LOCKED_SW "Том блокирован программно" + #define ERR_FILE_LOCKED "Файл заблокирован" + #define ERR_FILE_OR_DIR_BUSY "Файл/каталог занят" + #define ERR_FILE_ALREADY_OPEN_W "Файл уже открыт на запись" + #define ERR_FILE_ALREADY_OPEN_R "Файл уже открыт на чтение" + #define ERR_INVALID_REFNUM "Неверное количество ссылок" + #define ERR_GETTING_FILE_POS "Ошибка при получении позиции файла" + #define ERR_VOLUME_OFFLINE "Том отсоединен" + #define ERR_PERMISSION_DENIED "Отказано в разрешении" + #define ERR_VOL_ALREADY_ONLINE "Том уже подсоединен" + #define ERR_NO_SUCH_DRIVE "Нет такого диска" + #define ERR_NOT_MAC_DISK "Не диск Macintosh" + #define ERR_VOL_EXTERNAL_FS "Том принадлежит внешней файловой системе" + #define ERR_PROBLEM_RENAME "Проблема при переименовании" + #define ERR_BAD_MASTER_BLOCK "Плохой главный блок каталога" + #define ERR_CANT_MOVE_FORBIDDEN "Попытка переместить запрещена" + #define ERR_WRONG_VOL_TYPE "Неверный тип тома" + #define ERR_SERVER_VOL_LOST "Серверный том был отсоединен" + #define ERR_FILE_ID_NOT_FOUND "Идентификатор файла не найден" + #define ERR_FILE_ID_EXISTS "Идентификатор файла уже существует" + #define ERR_SERVER_NO_RESPOND "Сервер не отвечает" + #define ERR_USER_AUTH_FAILED "Идентификация пользователя не удалась" + #define ERR_PWORD_EXPIRED "Пароль на сервере устарел" + #define ERR_ACCESS_DENIED "Отказано в доступе" + #define ERR_NOT_A_DOS_DISK "Не диск DOS" + #define ERR_SHARING_VIOLATION "Нарушение совместного доступа" + #define ERR_CANNOT_MAKE "Не могу собрать" + #define ERR_DEV_IN_USE "Устройство уже используется" + #define ERR_OPEN_FAILED "Открытие не удалось" + #define ERR_PIPE_BUSY "Конвейер занят" + #define ERR_SHARING_BUF_EXCEEDED "Разделяемый буфер переполнен" + #define ERR_TOO_MANY_HANDLES "Слишком много открытых дескрипторов" + #define ERR_SEEK_ERROR "Ошибка позиционирования" + #define ERR_DEL_CWD "Попытка удалить текущий рабочий каталог" + #define ERR_WRITE_PROTECT_ERROR "Ошибка защиты записи" + #define ERR_WRITE_FAULT "Ошибка записи" + #define ERR_LOCK_VIOLATION "Нарушение блокировки" + #define ERR_GEN_FAILURE "Общий сбой" + #define ERR_UNCERTAIN_MEDIA "Неопределенный носитель" + #define ERR_PROT_VIOLATION "Нарушение защиты" + #define ERR_BROKEN_PIPE "Сломанный конвейер" + + +#elif (PHYSFS_LANG == PHYSFS_LANG_FRENCH) + #define DIR_ARCHIVE_DESCRIPTION "Pas d'archive, E/S directes sur système de fichiers" + #define GRP_ARCHIVE_DESCRIPTION "Format Groupfile du moteur Build" + #define HOG_ARCHIVE_DESCRIPTION "Descent I/II HOG file format" + #define MVL_ARCHIVE_DESCRIPTION "Descent II Movielib format" + #define QPAK_ARCHIVE_DESCRIPTION "Quake I/II format" + #define ZIP_ARCHIVE_DESCRIPTION "Compatible PkZip/WinZip/Info-Zip" + #define WAD_ARCHIVE_DESCRIPTION "Format WAD du moteur DOOM" + #define LZMA_ARCHIVE_DESCRIPTION "LZMA (7zip) format" /* !!! FIXME: translate this line if needed */ + + #define ERR_IS_INITIALIZED "Déjà initialisé" + #define ERR_NOT_INITIALIZED "Non initialisé" + #define ERR_INVALID_ARGUMENT "Argument invalide" + #define ERR_FILES_STILL_OPEN "Fichiers encore ouverts" + #define ERR_NO_DIR_CREATE "Echec de la création de répertoires" + #define ERR_OUT_OF_MEMORY "A court de mémoire" + #define ERR_NOT_IN_SEARCH_PATH "Aucune entrée dans le chemin de recherche" + #define ERR_NOT_SUPPORTED "Opération non supportée" + #define ERR_UNSUPPORTED_ARCHIVE "Type d'archive non supportée" + #define ERR_NOT_A_HANDLE "Pas un descripteur de fichier" + #define ERR_INSECURE_FNAME "Nom de fichier dangereux" + #define ERR_SYMLINK_DISALLOWED "Les liens symboliques sont désactivés" + #define ERR_NO_WRITE_DIR "Le répertoire d'écriture n'est pas spécifié" + #define ERR_NO_SUCH_FILE "Fichier non trouvé" + #define ERR_NO_SUCH_PATH "Chemin non trouvé" + #define ERR_NO_SUCH_VOLUME "Volume non trouvé" + #define ERR_PAST_EOF "Au-delà de la fin du fichier" + #define ERR_ARC_IS_READ_ONLY "L'archive est en lecture seule" + #define ERR_IO_ERROR "Erreur E/S" + #define ERR_CANT_SET_WRITE_DIR "Ne peut utiliser le répertoire d'écriture" + #define ERR_SYMLINK_LOOP "Boucle infinie dans les liens symboliques" + #define ERR_COMPRESSION "Erreur de (dé)compression" + #define ERR_NOT_IMPLEMENTED "Non implémenté" + #define ERR_OS_ERROR "Erreur rapportée par le système d'exploitation" + #define ERR_FILE_EXISTS "Le fichier existe déjà" + #define ERR_NOT_A_FILE "Pas un fichier" + #define ERR_NOT_A_DIR "Pas un répertoire" + #define ERR_NOT_AN_ARCHIVE "Pas une archive" + #define ERR_CORRUPTED "Archive corrompue" + #define ERR_SEEK_OUT_OF_RANGE "Pointeur de fichier hors de portée" + #define ERR_BAD_FILENAME "Mauvais nom de fichier" + #define ERR_PHYSFS_BAD_OS_CALL "(BOGUE) PhysicsFS a fait un mauvais appel système, le salaud" + #define ERR_ARGV0_IS_NULL "argv0 est NULL" + #define ERR_NEED_DICT "a besoin du dico" + #define ERR_DATA_ERROR "erreur de données" + #define ERR_MEMORY_ERROR "erreur mémoire" + #define ERR_BUFFER_ERROR "erreur tampon" + #define ERR_VERSION_ERROR "erreur de version" + #define ERR_UNKNOWN_ERROR "erreur inconnue" + #define ERR_SEARCHPATH_TRUNC "Le chemin de recherche a été tronqué" + #define ERR_GETMODFN_TRUNC "GetModuleFileName() a été tronqué" + #define ERR_GETMODFN_NO_DIR "GetModuleFileName() n'a pas de répertoire" + #define ERR_DISK_FULL "Disque plein" + #define ERR_DIRECTORY_FULL "Répertoire plein" + #define ERR_MACOS_GENERIC "Erreur rapportée par MacOS (%d)" + #define ERR_OS2_GENERIC "Erreur rapportée par OS/2 (%d)" + #define ERR_VOL_LOCKED_HW "Le volume est verrouillé matériellement" + #define ERR_VOL_LOCKED_SW "Le volume est verrouillé par logiciel" + #define ERR_FILE_LOCKED "Fichier verrouillé" + #define ERR_FILE_OR_DIR_BUSY "Fichier/répertoire occupé" + #define ERR_FILE_ALREADY_OPEN_W "Fichier déjà ouvert en écriture" + #define ERR_FILE_ALREADY_OPEN_R "Fichier déjà ouvert en lecture" + #define ERR_INVALID_REFNUM "Numéro de référence invalide" + #define ERR_GETTING_FILE_POS "Erreur lors de l'obtention de la position du pointeur de fichier" + #define ERR_VOLUME_OFFLINE "Le volume n'est pas en ligne" + #define ERR_PERMISSION_DENIED "Permission refusée" + #define ERR_VOL_ALREADY_ONLINE "Volumé déjà en ligne" + #define ERR_NO_SUCH_DRIVE "Lecteur inexistant" + #define ERR_NOT_MAC_DISK "Pas un disque Macintosh" + #define ERR_VOL_EXTERNAL_FS "Le volume appartient à un système de fichiers externe" + #define ERR_PROBLEM_RENAME "Problème lors du renommage" + #define ERR_BAD_MASTER_BLOCK "Mauvais block maitre de répertoire" + #define ERR_CANT_MOVE_FORBIDDEN "Essai de déplacement interdit" + #define ERR_WRONG_VOL_TYPE "Mauvais type de volume" + #define ERR_SERVER_VOL_LOST "Le volume serveur a été déconnecté" + #define ERR_FILE_ID_NOT_FOUND "Identificateur de fichier non trouvé" + #define ERR_FILE_ID_EXISTS "Identificateur de fichier existe déjà" + #define ERR_SERVER_NO_RESPOND "Le serveur ne répond pas" + #define ERR_USER_AUTH_FAILED "Authentification de l'utilisateur échouée" + #define ERR_PWORD_EXPIRED "Le mot de passe a expiré sur le serveur" + #define ERR_ACCESS_DENIED "Accès refusé" + #define ERR_NOT_A_DOS_DISK "Pas un disque DOS" + #define ERR_SHARING_VIOLATION "Violation de partage" + #define ERR_CANNOT_MAKE "Ne peut faire" + #define ERR_DEV_IN_USE "Périphérique déjà en utilisation" + #define ERR_OPEN_FAILED "Ouverture échouée" + #define ERR_PIPE_BUSY "Le tube est occupé" + #define ERR_SHARING_BUF_EXCEEDED "Tampon de partage dépassé" + #define ERR_TOO_MANY_HANDLES "Trop de descripteurs ouverts" + #define ERR_SEEK_ERROR "Erreur de positionement" + #define ERR_DEL_CWD "Essai de supprimer le répertoire courant" + #define ERR_WRITE_PROTECT_ERROR "Erreur de protection en écriture" + #define ERR_WRITE_FAULT "Erreur d'écriture" + #define ERR_LOCK_VIOLATION "Violation de verrou" + #define ERR_GEN_FAILURE "Echec général" + #define ERR_UNCERTAIN_MEDIA "Média incertain" + #define ERR_PROT_VIOLATION "Violation de protection" + #define ERR_BROKEN_PIPE "Tube cassé" + +#elif (PHYSFS_LANG == PHYSFS_LANG_PORTUGUESE_BR) + #define DIR_ARCHIVE_DESCRIPTION "Não arquivo, E/S sistema de arquivos direto" + #define GRP_ARCHIVE_DESCRIPTION "Formato Groupfile do engine Build" + #define HOG_ARCHIVE_DESCRIPTION "Formato Descent I/II HOG file" + #define MVL_ARCHIVE_DESCRIPTION "Formato Descent II Movielib" + #define QPAK_ARCHIVE_DESCRIPTION "Formato Quake I/II" + #define ZIP_ARCHIVE_DESCRIPTION "Formato compatível PkZip/WinZip/Info-Zip" + #define WAD_ARCHIVE_DESCRIPTION "Formato WAD do engine DOOM" + #define WAD_ARCHIVE_DESCRIPTION "DOOM engine format" /* !!! FIXME: translate this line if needed */ + #define LZMA_ARCHIVE_DESCRIPTION "LZMA (7zip) format" /* !!! FIXME: translate this line if needed */ + + #define ERR_IS_INITIALIZED "Já inicializado" + #define ERR_NOT_INITIALIZED "Não inicializado" + #define ERR_INVALID_ARGUMENT "Argumento inválido" + #define ERR_FILES_STILL_OPEN "Arquivos ainda abertos" + #define ERR_NO_DIR_CREATE "Falha na criação de diretórios" + #define ERR_OUT_OF_MEMORY "Memória insuficiente" + #define ERR_NOT_IN_SEARCH_PATH "Entrada não encontrada no caminho de busca" + #define ERR_NOT_SUPPORTED "Operação não suportada" + #define ERR_UNSUPPORTED_ARCHIVE "Tipo de arquivo não suportado" + #define ERR_NOT_A_HANDLE "Não é um handler de arquivo" + #define ERR_INSECURE_FNAME "Nome de arquivo inseguro" + #define ERR_SYMLINK_DISALLOWED "Links simbólicos desabilitados" + #define ERR_NO_WRITE_DIR "Diretório de escrita não definido" + #define ERR_NO_SUCH_FILE "Arquivo não encontrado" + #define ERR_NO_SUCH_PATH "Caminho não encontrado" + #define ERR_NO_SUCH_VOLUME "Volume não encontrado" + #define ERR_PAST_EOF "Passou o fim do arquivo" + #define ERR_ARC_IS_READ_ONLY "Arquivo é somente de leitura" + #define ERR_IO_ERROR "Erro de E/S" + #define ERR_CANT_SET_WRITE_DIR "Não foi possível definir diretório de escrita" + #define ERR_SYMLINK_LOOP "Loop infinito de link simbólico" + #define ERR_COMPRESSION "Erro de (Des)compressão" + #define ERR_NOT_IMPLEMENTED "Não implementado" + #define ERR_OS_ERROR "Erro reportado pelo Sistema Operacional" + #define ERR_FILE_EXISTS "Arquivo já existente" + #define ERR_NOT_A_FILE "Não é um arquivo" + #define ERR_NOT_A_DIR "Não é um diretório" + #define ERR_NOT_AN_ARCHIVE "Não é um pacote" + #define ERR_CORRUPTED "Pacote corrompido" + #define ERR_SEEK_OUT_OF_RANGE "Posicionamento além do tamanho" + #define ERR_BAD_FILENAME "Nome de arquivo inválido" + #define ERR_PHYSFS_BAD_OS_CALL "(BUG) PhysicsFS realizou uma chamada de sistema inválida" + #define ERR_ARGV0_IS_NULL "argv0 é NULL" + #define ERR_NEED_DICT "precisa de diretório" + #define ERR_DATA_ERROR "erro nos dados" + #define ERR_MEMORY_ERROR "erro de memória" + #define ERR_BUFFER_ERROR "erro de buffer" + #define ERR_VERSION_ERROR "erro na version" + #define ERR_UNKNOWN_ERROR "erro desconhecido" + #define ERR_SEARCHPATH_TRUNC "Caminho de procura quebrado" + #define ERR_GETMODFN_TRUNC "GetModuleFileName() foi quebrado" + #define ERR_GETMODFN_NO_DIR "GetModuleFileName() nao teve diretório" + #define ERR_DISK_FULL "Disco cheio" + #define ERR_DIRECTORY_FULL "Diretório cheio" + #define ERR_MACOS_GENERIC "MacOS reportou um erro (%d)" + #define ERR_OS2_GENERIC "OS/2 reportou um erro (%d)" + #define ERR_VOL_LOCKED_HW "Volume travado por hardware" + #define ERR_VOL_LOCKED_SW "Volume travado por software" + #define ERR_FILE_LOCKED "Arquivo travado" + #define ERR_FILE_OR_DIR_BUSY "Arquivo/Diretório está em uso" + #define ERR_FILE_ALREADY_OPEN_W "Arquivo já aberto para escrita" + #define ERR_FILE_ALREADY_OPEN_R "Arquivo já aberto para leitura" + #define ERR_INVALID_REFNUM "Número de referência" + #define ERR_GETTING_FILE_POS "Erro ao tentar obter posição do arquivo" + #define ERR_VOLUME_OFFLINE "Volume está indisponível" + #define ERR_PERMISSION_DENIED "Permissão negada" + #define ERR_VOL_ALREADY_ONLINE "Volume disponível" + #define ERR_NO_SUCH_DRIVE "Drive inexistente" + #define ERR_NOT_MAC_DISK "Não é um disco Macintosh" + #define ERR_VOL_EXTERNAL_FS "Volume pertence a um sistema de arquivos externo" + #define ERR_PROBLEM_RENAME "Problema durante renomeação" + #define ERR_BAD_MASTER_BLOCK "Bloco master do diretório inválido" + #define ERR_CANT_MOVE_FORBIDDEN "Tentativa de mover proibida" + #define ERR_WRONG_VOL_TYPE "Tipo inválido de volume" + #define ERR_SERVER_VOL_LOST "Volume servidor desconectado" + #define ERR_FILE_ID_NOT_FOUND "ID de Arquivo não encontrado" + #define ERR_FILE_ID_EXISTS "ID de Arquivo já existente" + #define ERR_SERVER_NO_RESPOND "Servidor não respondendo" + #define ERR_USER_AUTH_FAILED "Autenticação de usuário falhada" + #define ERR_PWORD_EXPIRED "Password foi expirada no servidor" + #define ERR_ACCESS_DENIED "Accesso negado" + #define ERR_NOT_A_DOS_DISK "Não é um disco DOS" + #define ERR_SHARING_VIOLATION "Violação de compartilhamento" + #define ERR_CANNOT_MAKE "Não pode ser feito" + #define ERR_DEV_IN_USE "Device já em uso" + #define ERR_OPEN_FAILED "Falaha na abertura" + #define ERR_PIPE_BUSY "Fila ocupada" + #define ERR_SHARING_BUF_EXCEEDED "Buffer de compartilhamento excedeu" + #define ERR_TOO_MANY_HANDLES "Muitos handles abertos" + #define ERR_SEEK_ERROR "Erro de posicionamento" + #define ERR_DEL_CWD "Tentando remover diretório de trabalho atual" + #define ERR_WRITE_PROTECT_ERROR "Erro de proteção de escrita" + #define ERR_WRITE_FAULT "Erro de escrita" + #define ERR_LOCK_VIOLATION "Violação de trava" + #define ERR_GEN_FAILURE "Falha geral" + #define ERR_UNCERTAIN_MEDIA "Media incerta" + #define ERR_PROT_VIOLATION "Violação de proteção" + #define ERR_BROKEN_PIPE "Fila quebrada" + +#elif (PHYSFS_LANG == PHYSFS_LANG_SPANISH) + #define DIR_ARCHIVE_DESCRIPTION "No es un archivo, E/S directa al sistema de ficheros" + #define GRP_ARCHIVE_DESCRIPTION "Formato Build engine Groupfile" + #define HOG_ARCHIVE_DESCRIPTION "Formato Descent I/II HOG file" + #define MVL_ARCHIVE_DESCRIPTION "Formato Descent II Movielib" + #define QPAK_ARCHIVE_DESCRIPTION "Formato Quake I/II" + #define ZIP_ARCHIVE_DESCRIPTION "Compatible con PkZip/WinZip/Info-Zip" + #define WAD_ARCHIVE_DESCRIPTION "DOOM engine format" /* !!! FIXME: translate this line if needed */ + #define LZMA_ARCHIVE_DESCRIPTION "LZMA (7zip) format" /* !!! FIXME: translate this line if needed */ + + #define ERR_IS_INITIALIZED "Ya estaba inicializado" + #define ERR_NOT_INITIALIZED "No está inicializado" + #define ERR_INVALID_ARGUMENT "Argumento inválido" + #define ERR_FILES_STILL_OPEN "Archivos aún abiertos" + #define ERR_NO_DIR_CREATE "Fallo al crear los directorios" + #define ERR_OUT_OF_MEMORY "Memoria agotada" + #define ERR_NOT_IN_SEARCH_PATH "No existe tal entrada en la ruta de búsqueda" + #define ERR_NOT_SUPPORTED "Operación no soportada" + #define ERR_UNSUPPORTED_ARCHIVE "Tipo de archivo no soportado" + #define ERR_NOT_A_HANDLE "No es un manejador de ficheo (file handle)" + #define ERR_INSECURE_FNAME "Nombre de archivo inseguro" + #define ERR_SYMLINK_DISALLOWED "Los enlaces simbólicos están desactivados" + #define ERR_NO_WRITE_DIR "No has configurado un directorio de escritura" + #define ERR_NO_SUCH_FILE "Archivo no encontrado" + #define ERR_NO_SUCH_PATH "Ruta no encontrada" + #define ERR_NO_SUCH_VOLUME "Volumen no encontrado" + #define ERR_PAST_EOF "Te pasaste del final del archivo" + #define ERR_ARC_IS_READ_ONLY "El archivo es de sólo lectura" + #define ERR_IO_ERROR "Error E/S" + #define ERR_CANT_SET_WRITE_DIR "No puedo configurar el directorio de escritura" + #define ERR_SYMLINK_LOOP "Bucle infnito de enlaces simbólicos" + #define ERR_COMPRESSION "Error de (des)compresión" + #define ERR_NOT_IMPLEMENTED "No implementado" + #define ERR_OS_ERROR "El sistema operativo ha devuelto un error" + #define ERR_FILE_EXISTS "El archivo ya existe" + #define ERR_NOT_A_FILE "No es un archivo" + #define ERR_NOT_A_DIR "No es un directorio" + #define ERR_NOT_AN_ARCHIVE "No es un archivo" + #define ERR_CORRUPTED "Archivo corrupto" + #define ERR_SEEK_OUT_OF_RANGE "Búsqueda fuera de rango" + #define ERR_BAD_FILENAME "Nombre de archivo incorrecto" + #define ERR_PHYSFS_BAD_OS_CALL "(BUG) PhysicsFS ha hecho una llamada incorrecta al sistema" + #define ERR_ARGV0_IS_NULL "argv0 es NULL" + #define ERR_NEED_DICT "necesito diccionario" + #define ERR_DATA_ERROR "error de datos" + #define ERR_MEMORY_ERROR "error de memoria" + #define ERR_BUFFER_ERROR "error de buffer" + #define ERR_VERSION_ERROR "error de versión" + #define ERR_UNKNOWN_ERROR "error desconocido" + #define ERR_SEARCHPATH_TRUNC "La ruta de búsqueda ha sido truncada" + #define ERR_GETMODFN_TRUNC "GetModuleFileName() ha sido truncado" + #define ERR_GETMODFN_NO_DIR "GetModuleFileName() no tenia directorio" + #define ERR_DISK_FULL "El disco está lleno" + #define ERR_DIRECTORY_FULL "El directorio está lleno" + #define ERR_MACOS_GENERIC "MacOS ha devuelto un error (%d)" + #define ERR_OS2_GENERIC "OS/2 ha devuelto un error (%d)" + #define ERR_VOL_LOCKED_HW "El volumen está bloqueado por el hardware" + #define ERR_VOL_LOCKED_SW "El volumen está bloqueado por el software" + #define ERR_FILE_LOCKED "El archivo está bloqueado" + #define ERR_FILE_OR_DIR_BUSY "Fichero o directorio ocupados" + #define ERR_FILE_ALREADY_OPEN_W "Fichero ya abierto para escritura" + #define ERR_FILE_ALREADY_OPEN_R "Fichero ya abierto para lectura" + #define ERR_INVALID_REFNUM "El número de referencia no es válido" + #define ERR_GETTING_FILE_POS "Error al tomar la posición del fichero" + #define ERR_VOLUME_OFFLINE "El volumen está desconectado" + #define ERR_PERMISSION_DENIED "Permiso denegado" + #define ERR_VOL_ALREADY_ONLINE "El volumen ya estaba conectado" + #define ERR_NO_SUCH_DRIVE "No existe tal unidad" + #define ERR_NOT_MAC_DISK "No es un disco Macintosh" + #define ERR_VOL_EXTERNAL_FS "El volumen pertence a un sistema de ficheros externo" + #define ERR_PROBLEM_RENAME "Problemas al renombrar" + #define ERR_BAD_MASTER_BLOCK "Bloque maestro de directorios incorrecto" + #define ERR_CANT_MOVE_FORBIDDEN "Intento de mover forbidden" + #define ERR_WRONG_VOL_TYPE "Tipo de volumen incorrecto" + #define ERR_SERVER_VOL_LOST "El servidor de volúmenes ha sido desconectado" + #define ERR_FILE_ID_NOT_FOUND "Identificador de archivo no encontrado" + #define ERR_FILE_ID_EXISTS "El identificador de archivo ya existe" + #define ERR_SERVER_NO_RESPOND "El servidor no responde" + #define ERR_USER_AUTH_FAILED "Fallo al autentificar el usuario" + #define ERR_PWORD_EXPIRED "La Password en el servidor ha caducado" + #define ERR_ACCESS_DENIED "Acceso denegado" + #define ERR_NOT_A_DOS_DISK "No es un disco de DOS" + #define ERR_SHARING_VIOLATION "Violación al compartir" + #define ERR_CANNOT_MAKE "No puedo hacer make" + #define ERR_DEV_IN_USE "El dispositivo ya estaba en uso" + #define ERR_OPEN_FAILED "Fallo al abrir" + #define ERR_PIPE_BUSY "Tubería ocupada" + #define ERR_SHARING_BUF_EXCEEDED "Buffer de compartición sobrepasado" + #define ERR_TOO_MANY_HANDLES "Demasiados manejadores (handles)" + #define ERR_SEEK_ERROR "Error de búsqueda" + #define ERR_DEL_CWD "Intentando borrar el directorio de trabajo actual" + #define ERR_WRITE_PROTECT_ERROR "Error de protección contra escritura" + #define ERR_WRITE_FAULT "Fallo al escribir" + #define ERR_LOCK_VIOLATION "Violación del bloqueo" + #define ERR_GEN_FAILURE "Fallo general" + #define ERR_UNCERTAIN_MEDIA "Medio incierto" + #define ERR_PROT_VIOLATION "Violación de la protección" + #define ERR_BROKEN_PIPE "Tubería rota" + +#else + #error Please define PHYSFS_LANG. +#endif + +/* end LANG section. */ + +struct __PHYSFS_DIRHANDLE__; +struct __PHYSFS_FILEFUNCTIONS__; + + +/* !!! FIXME: find something better than "dvoid" and "fvoid" ... */ +/* Opaque data for file and dir handlers... */ +typedef void dvoid; +typedef void fvoid; + + +typedef struct +{ + /* + * Basic info about this archiver... + */ + const PHYSFS_ArchiveInfo *info; + + + /* + * DIRECTORY ROUTINES: + * These functions are for dir handles. Generate a handle with the + * openArchive() method, then pass it as the "opaque" dvoid to the + * others. + * + * Symlinks should always be followed; PhysicsFS will use the + * isSymLink() method and make a judgement on whether to + * continue to call other methods based on that. + */ + + + /* + * Returns non-zero if (filename) is a valid archive that this + * driver can handle. This filename is in platform-dependent + * notation. forWriting is non-zero if this is to be used for + * the write directory, and zero if this is to be used for an + * element of the search path. + */ + int (*isArchive)(const char *filename, int forWriting); + + /* + * Open a dirhandle for dir/archive (name). + * This filename is in platform-dependent notation. + * forWriting is non-zero if this is to be used for + * the write directory, and zero if this is to be used for an + * element of the search path. + * Returns NULL on failure, and calls __PHYSFS_setError(). + * Returns non-NULL on success. The pointer returned will be + * passed as the "opaque" parameter for later calls. + */ + void *(*openArchive)(const char *name, int forWriting); + + /* + * List all files in (dirname). Each file is passed to (callback), + * where a copy is made if appropriate, so you should dispose of + * it properly upon return from the callback. + * You should omit symlinks if (omitSymLinks) is non-zero. + * If you have a failure, report as much as you can. + * (dirname) is in platform-independent notation. + */ + void (*enumerateFiles)(dvoid *opaque, + const char *dirname, + int omitSymLinks, + PHYSFS_EnumFilesCallback callback, + const char *origdir, + void *callbackdata); + + /* + * Returns non-zero if filename can be opened for reading. + * This filename is in platform-independent notation. + * You should not follow symlinks. + */ + int (*exists)(dvoid *opaque, const char *name); + + /* + * Returns non-zero if filename is really a directory. + * This filename is in platform-independent notation. + * Symlinks should be followed; if what the symlink points + * to is missing, or isn't a directory, then the retval is zero. + * + * Regardless of success or failure, please set *fileExists to + * non-zero if the file existed (even if it's a broken symlink!), + * zero if it did not. + */ + int (*isDirectory)(dvoid *opaque, const char *name, int *fileExists); + + /* + * Returns non-zero if filename is really a symlink. + * This filename is in platform-independent notation. + * + * Regardless of success or failure, please set *fileExists to + * non-zero if the file existed (even if it's a broken symlink!), + * zero if it did not. + */ + int (*isSymLink)(dvoid *opaque, const char *name, int *fileExists); + + /* + * Retrieve the last modification time (mtime) of a file. + * Returns -1 on failure, or the file's mtime in seconds since + * the epoch (Jan 1, 1970) on success. + * This filename is in platform-independent notation. + * + * Regardless of success or failure, please set *exists to + * non-zero if the file existed (even if it's a broken symlink!), + * zero if it did not. + */ + PHYSFS_sint64 (*getLastModTime)(dvoid *opaque, const char *fnm, int *exist); + + /* + * Open file for reading. + * This filename is in platform-independent notation. + * If you can't handle multiple opens of the same file, + * you can opt to fail for the second call. + * Fail if the file does not exist. + * Returns NULL on failure, and calls __PHYSFS_setError(). + * Returns non-NULL on success. The pointer returned will be + * passed as the "opaque" parameter for later file calls. + * + * Regardless of success or failure, please set *fileExists to + * non-zero if the file existed (even if it's a broken symlink!), + * zero if it did not. + */ + fvoid *(*openRead)(dvoid *opaque, const char *fname, int *fileExists); + + /* + * Open file for writing. + * If the file does not exist, it should be created. If it exists, + * it should be truncated to zero bytes. The writing + * offset should be the start of the file. + * This filename is in platform-independent notation. + * If you can't handle multiple opens of the same file, + * you can opt to fail for the second call. + * Returns NULL on failure, and calls __PHYSFS_setError(). + * Returns non-NULL on success. The pointer returned will be + * passed as the "opaque" parameter for later file calls. + */ + fvoid *(*openWrite)(dvoid *opaque, const char *filename); + + /* + * Open file for appending. + * If the file does not exist, it should be created. The writing + * offset should be the end of the file. + * This filename is in platform-independent notation. + * If you can't handle multiple opens of the same file, + * you can opt to fail for the second call. + * Returns NULL on failure, and calls __PHYSFS_setError(). + * Returns non-NULL on success. The pointer returned will be + * passed as the "opaque" parameter for later file calls. + */ + fvoid *(*openAppend)(dvoid *opaque, const char *filename); + + /* + * Delete a file in the archive/directory. + * Return non-zero on success, zero on failure. + * This filename is in platform-independent notation. + * This method may be NULL. + * On failure, call __PHYSFS_setError(). + */ + int (*remove)(dvoid *opaque, const char *filename); + + /* + * Create a directory in the archive/directory. + * If the application is trying to make multiple dirs, PhysicsFS + * will split them up into multiple calls before passing them to + * your driver. + * Return non-zero on success, zero on failure. + * This filename is in platform-independent notation. + * This method may be NULL. + * On failure, call __PHYSFS_setError(). + */ + int (*mkdir)(dvoid *opaque, const char *filename); + + /* + * Close directories/archives, and free any associated memory, + * including (opaque) itself if applicable. Implementation can assume + * that it won't be called if there are still files open from + * this archive. + */ + void (*dirClose)(dvoid *opaque); + + + + /* + * FILE ROUTINES: + * These functions are for file handles generated by the open*() methods. + * They are distinguished by taking a "fvoid" instead of a "dvoid" for + * the opaque handle. + */ + + /* + * Read more from the file. + * Returns number of objects of (objSize) bytes read from file, -1 + * if complete failure. + * On failure, call __PHYSFS_setError(). + */ + PHYSFS_sint64 (*read)(fvoid *opaque, void *buffer, + PHYSFS_uint32 objSize, PHYSFS_uint32 objCount); + + /* + * Write more to the file. Archives don't have to implement this. + * (Set it to NULL if not implemented). + * Returns number of objects of (objSize) bytes written to file, -1 + * if complete failure. + * On failure, call __PHYSFS_setError(). + */ + PHYSFS_sint64 (*write)(fvoid *opaque, const void *buffer, + PHYSFS_uint32 objSize, PHYSFS_uint32 objCount); + + /* + * Returns non-zero if at end of file. + */ + int (*eof)(fvoid *opaque); + + /* + * Returns byte offset from start of file. + */ + PHYSFS_sint64 (*tell)(fvoid *opaque); + + /* + * Move read/write pointer to byte offset from start of file. + * Returns non-zero on success, zero on error. + * On failure, call __PHYSFS_setError(). + */ + int (*seek)(fvoid *opaque, PHYSFS_uint64 offset); + + /* + * Return number of bytes available in the file, or -1 if you + * aren't able to determine. + * On failure, call __PHYSFS_setError(). + */ + PHYSFS_sint64 (*fileLength)(fvoid *opaque); + + /* + * Close the file, and free associated resources, including (opaque) + * if applicable. Returns non-zero on success, zero if can't close + * file. On failure, call __PHYSFS_setError(). + */ + int (*fileClose)(fvoid *opaque); +} PHYSFS_Archiver; + + +/* + * Call this to set the message returned by PHYSFS_getLastError(). + * Please only use the ERR_* constants above, or add new constants to the + * above group, but I want these all in one place. + * + * Calling this with a NULL argument is a safe no-op. + */ +void __PHYSFS_setError(const char *err); + + +/* + * Convert (dirName) to platform-dependent notation, then prepend (prepend) + * and append (append) to the converted string. + * + * So, on Win32, calling: + * __PHYSFS_convertToDependent("C:\", "my/files", NULL); + * ...will return the string "C:\my\files". + * + * This is a convenience function; you might want to hack something out that + * is less generic (and therefore more efficient). + * + * Be sure to free() the return value when done with it. + */ +char *__PHYSFS_convertToDependent(const char *prepend, + const char *dirName, + const char *append); + + +/* This byteorder stuff was lifted from SDL. http://www.libsdl.org/ */ +#define PHYSFS_LIL_ENDIAN 1234 +#define PHYSFS_BIG_ENDIAN 4321 + +#if defined(__i386__) || defined(__ia64__) || defined(_M_IX86) || defined(_M_IA64) || \ + (defined(__alpha__) || defined(__alpha)) || \ + defined(__arm__) || defined(ARM) || \ + (defined(__mips__) && defined(__MIPSEL__)) || \ + defined(__SYMBIAN32__) || \ + defined(__x86_64__) || \ + defined(__LITTLE_ENDIAN__) +#define PHYSFS_BYTEORDER PHYSFS_LIL_ENDIAN +#else +#define PHYSFS_BYTEORDER PHYSFS_BIG_ENDIAN +#endif + + +/* + * 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. + * + * See zip.c for an example. + */ +void __PHYSFS_sort(void *entries, PHYSFS_uint32 max, + int (*cmpfn)(void *, PHYSFS_uint32, PHYSFS_uint32), + void (*swapfn)(void *, PHYSFS_uint32, PHYSFS_uint32)); + + +/* These get used all over for lessening code clutter. */ +#define BAIL_MACRO(e, r) { __PHYSFS_setError(e); return r; } +#define BAIL_IF_MACRO(c, e, r) if (c) { __PHYSFS_setError(e); return r; } +#define BAIL_MACRO_MUTEX(e, m, r) { __PHYSFS_setError(e); __PHYSFS_platformReleaseMutex(m); return r; } +#define BAIL_IF_MACRO_MUTEX(c, e, m, r) if (c) { __PHYSFS_setError(e); __PHYSFS_platformReleaseMutex(m); return r; } +#define GOTO_MACRO(e, g) { __PHYSFS_setError(e); goto g; } +#define GOTO_IF_MACRO(c, e, g) if (c) { __PHYSFS_setError(e); goto g; } +#define GOTO_MACRO_MUTEX(e, m, g) { __PHYSFS_setError(e); __PHYSFS_platformReleaseMutex(m); goto g; } +#define GOTO_IF_MACRO_MUTEX(c, e, m, g) if (c) { __PHYSFS_setError(e); __PHYSFS_platformReleaseMutex(m); goto g; } + +#define __PHYSFS_ARRAYLEN(x) ( (sizeof (x)) / (sizeof (x[0])) ) + +#if (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) x +#define __PHYSFS_UI64(x) 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)))) \ +) + + +/* + * This is a strcasecmp() or stricmp() replacement that expects both strings + * to be in UTF-8 encoding. It will do "case folding" to decide if the + * Unicode codepoints in the strings match. + * + * It will report which string is "greater than" the other, but be aware that + * this doesn't necessarily mean anything: 'a' may be "less than" 'b', but + * a random Kanji codepoint has no meaningful alphabetically relationship to + * a Greek Lambda, but being able to assign a reliable "value" makes sorting + * algorithms possible, if not entirely sane. Most cases should treat the + * return value as "equal" or "not equal". + */ +int __PHYSFS_utf8strcasecmp(const char *s1, const char *s2); + +/* + * This works like __PHYSFS_utf8strcasecmp(), but takes a character (NOT BYTE + * COUNT) argument, like strcasencmp(). + */ +int __PHYSFS_utf8strnicmp(const char *s1, const char *s2, PHYSFS_uint32 l); + +/* + * stricmp() that guarantees to only work with low ASCII. The C runtime + * stricmp() might try to apply a locale/codepage/etc, which we don't want. + */ +int __PHYSFS_stricmpASCII(const char *s1, const char *s2); + +/* + * strnicmp() that guarantees to only work with low ASCII. The C runtime + * strnicmp() might try to apply a locale/codepage/etc, which we don't want. + */ +int __PHYSFS_strnicmpASCII(const char *s1, const char *s2, PHYSFS_uint32 l); + + +/* + * 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 + +/*--------------------------------------------------------------------------*/ +/*--------------------------------------------------------------------------*/ +/*------------ ----------------*/ +/*------------ 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, but it IS a null-terminated string. + */ +extern const char *__PHYSFS_platformDirSeparator; + + +/* + * Initialize the platform. This is called when PHYSFS_init() is called from + * the application. You can use this to (for example) determine what version + * of Windows you're running. + * + * 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. + * + * Return zero if there was a catastrophic failure (which prevents you from + * functioning at all), and non-zero otherwise. + */ +int __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_setError() 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_setError() 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_setError() 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 (count) + * objects of (size) 8-bit bytes to the area pointed to by (buffer). If there + * isn't enough data available, return the number of full objects read, and + * position the file pointer at the start of the first incomplete object. + * On success, return (count) and position the file pointer one byte past + * the end of the last read object. Return (-1) if there is a catastrophic + * error, and call __PHYSFS_setError() to describe the problem; the file + * pointer should not move in such a case. + */ +PHYSFS_sint64 __PHYSFS_platformRead(void *opaque, void *buffer, + PHYSFS_uint32 size, PHYSFS_uint32 count); + +/* + * Write more data to a platform-specific file handle. (opaque) should be + * cast to whatever data type your platform uses. Write a maximum of (count) + * objects of (size) 8-bit bytes from the area pointed to by (buffer). If + * there isn't enough data available, return the number of full objects + * written, and position the file pointer at the start of the first + * incomplete object. Return (-1) if there is a catastrophic error, and call + * __PHYSFS_setError() to describe the problem; the file pointer should not + * move in such a case. + */ +PHYSFS_sint64 __PHYSFS_platformWrite(void *opaque, const void *buffer, + PHYSFS_uint32 size, PHYSFS_uint32 count); + +/* + * 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_setError() 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_setError() and return zero. On success, return + * a non-zero value. + */ +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_setError(). Otherwise, + * return the file length in 8-bit bytes. + */ +PHYSFS_sint64 __PHYSFS_platformFileLength(void *handle); + +/* + * Determine if a file is at EOF. (opaque) should be cast to whatever data + * type your platform uses. + * + * The caller expects that there was a short read before calling this. + * + * Return non-zero if EOF, zero if it is _not_ EOF. + */ +int __PHYSFS_platformEOF(void *opaque); + +/* + * 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); + +/* + * Flush and close a file. (opaque) should be cast to whatever data type + * your platform uses. Be sure to check for errors when closing; the + * caller expects that this function can fail if there was a flushing + * error, etc. + * + * You should clean up all resources associated with (opaque). + * + * Return zero on failure, non-zero on success. + */ +int __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 ...) + * Caller will free() the retval if it's not NULL. + */ +char *__PHYSFS_platformCalcBaseDir(const char *argv0); + +/* + * Get the platform-specific user name. + * Caller will free() the retval if it's not NULL. If it's NULL, the username + * will default to "default". + */ +char *__PHYSFS_platformGetUserName(void); + +/* + * Get the platform-specific user dir. + * Caller will free() the retval if it's not NULL. If it's NULL, the userdir + * will default to basedir/username. + */ +char *__PHYSFS_platformGetUserDir(void); + +/* + * Return a number that uniquely identifies the current thread. + * On a platform without threading, (1) will suffice. These numbers are + * arbitrary; the only requirement is that no two threads have the same + * number. + */ +PHYSFS_uint64 __PHYSFS_platformGetThreadID(void); + +/* + * Return non-zero if filename (in platform-dependent notation) exists. + * Symlinks should NOT be followed; at this stage, we do not care what the + * symlink points to. Please call __PHYSFS_SetError() with the details of + * why the file does not exist, if it doesn't; you are in a better position + * to know (path not found, bogus filename, file itself is missing, etc). + */ +int __PHYSFS_platformExists(const char *fname); + +/* + * Return the last modified time (in seconds since the epoch) of a file. + * Returns -1 on failure. (fname) is in platform-dependent notation. + * Symlinks should be followed; if what the symlink points to is missing, + * then the retval is -1. + */ +PHYSFS_sint64 __PHYSFS_platformGetLastModTime(const char *fname); + +/* + * Return non-zero if filename (in platform-dependent notation) is a symlink. + */ +int __PHYSFS_platformIsSymLink(const char *fname); + + +/* + * Return non-zero if filename (in platform-dependent notation) is a symlink. + * Symlinks should be followed; if what the symlink points to is missing, + * or isn't a directory, then the retval is false. + */ +int __PHYSFS_platformIsDirectory(const char *fname); + + +/* + * Convert (dirName) to platform-dependent notation, then prepend (prepend) + * and append (append) to the converted string. + * + * So, on Win32, calling: + * __PHYSFS_platformCvtToDependent("C:\", "my/files", NULL); + * ...will return the string "C:\my\files". + * + * This can be implemented in a platform-specific manner, so you can get + * get a speed boost that the default implementation can't, since + * you can make assumptions about the size of strings, etc.. + * + * Platforms that choose not to implement this may just call + * __PHYSFS_convertToDependent() as a passthrough, which may fit the bill + * already. + * + * Be sure to free() the return value when done with it. + */ +char *__PHYSFS_platformCvtToDependent(const char *prepend, + const char *dirName, + const char *append); + + +/* + * Enumerate a directory of files. This follows the rules for the + * PHYSFS_Archiver->enumerateFiles() method (see above), 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 + * metaentries should always be ignored. + */ +void __PHYSFS_platformEnumerateFiles(const char *dirname, + int omitSymLinks, + PHYSFS_EnumFilesCallback callback, + const char *origdir, + void *callbackdata); + + +/* + * Get the current working directory. The return value should be an + * absolute path in platform-dependent notation. The caller will deallocate + * the return value with the standard C runtime free() function when it + * is done with it. + * On error, return NULL and set the error message. + */ +char *__PHYSFS_platformCurrentDir(void); + + +/* + * Get the real physical path to a file. (path) is specified in + * platform-dependent notation, as should your return value be. + * All relative paths should be removed, leaving you with an absolute + * path. Symlinks should be resolved, too, so that the returned value is + * the most direct path to a file. + * The return value will be deallocated with the standard C runtime free() + * function when the caller is done with it. + * On error, return NULL and set the error message. + */ +char *__PHYSFS_platformRealPath(const char *path); + + +/* + * 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_setError() in here! Since setError 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_setError() in here! Since setError 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); + +/* + * Called at the start of PHYSFS_init() to prepare the allocator, if the user + * hasn't selected their own allocator via PHYSFS_setAllocator(). + * If the platform has a custom allocator, it should fill in the fields of + * (a) with the proper function pointers and return non-zero. + * If the platform just wants to use malloc()/free()/etc, return zero + * immediately and the higher level will handle it. The Init and Deinit + * fields of (a) are optional...set them to NULL if you don't need them. + * Everything else must be implemented. All rules follow those for + * PHYSFS_setAllocator(). If Init isn't NULL, it will be called shortly + * after this function returns non-zero. + */ +int __PHYSFS_platformSetDefaultAllocator(PHYSFS_Allocator *a); + +#ifdef __cplusplus +} +#endif + +#endif + +/* end of physfs_internal.h ... */ + diff --git a/project/jni/physfs/include/physfs_platforms.h b/project/jni/physfs/include/physfs_platforms.h new file mode 100644 index 000000000..9a185fcb5 --- /dev/null +++ b/project/jni/physfs/include/physfs_platforms.h @@ -0,0 +1,49 @@ +#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 +# define PHYSFS_PLATFORM_BEOS +# define PHYSFS_PLATFORM_POSIX +#elif ((defined __BEOS__) || (defined __beos__)) +# define PHYSFS_PLATFORM_BEOS +# define PHYSFS_PLATFORM_POSIX +#elif (defined _WIN32_WCE) || (defined _WIN64_WCE) +# define PHYSFS_PLATFORM_POCKETPC +#elif (((defined _WIN32) || (defined _WIN64)) && (!defined __CYGWIN__)) +# define PHYSFS_PLATFORM_WINDOWS +#elif (defined OS2) +# define PHYSFS_PLATFORM_OS2 +#elif ((defined __MACH__) && (defined __APPLE__)) +/* To check if iphone or not, we need to include this file */ +# include +# if ((TARGET_IPHONE_SIMULATOR) || (TARGET_OS_IPHONE)) +# define PHYSFS_PLATFORM_UNIX +# define PHYSFS_PLATFORM_POSIX +# define PHYSFS_NO_CDROM_SUPPORT +# else +# define PHYSFS_PLATFORM_MACOSX +# define PHYSFS_PLATFORM_POSIX +# endif +#elif defined(macintosh) +# error Classic Mac OS support was dropped from PhysicsFS 2.0. Move to OS X. +#elif defined(unix) +# define PHYSFS_PLATFORM_UNIX +# define PHYSFS_PLATFORM_POSIX +#else +# error Unknown platform. +#endif + +#endif /* include-once blocker. */ + diff --git a/project/jni/physfs/physfs.c b/project/jni/physfs/physfs.c new file mode 100644 index 000000000..a04260360 --- /dev/null +++ b/project/jni/physfs/physfs.c @@ -0,0 +1,2221 @@ +/** + * 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. + */ + +#include +#include +#include +#include "physfs.h" + +#define __PHYSICSFS_INTERNAL__ +#include "physfs_internal.h" + + +typedef struct __PHYSFS_DIRHANDLE__ +{ + void *opaque; /* Instance data unique to the archiver. */ + char *dirName; /* Path to archive in platform-dependent notation. */ + char *mountPoint; /* Mountpoint in virtual file tree. */ + const PHYSFS_Archiver *funcs; /* Ptr to archiver info for this handle. */ + struct __PHYSFS_DIRHANDLE__ *next; /* linked list stuff. */ +} DirHandle; + + +typedef struct __PHYSFS_FILEHANDLE__ +{ + void *opaque; /* Instance data unique to the archiver for this file. */ + PHYSFS_uint8 forReading; /* Non-zero if reading, zero if write/append */ + const DirHandle *dirHandle; /* Archiver instance that created this */ + const PHYSFS_Archiver *funcs; /* Ptr to archiver info for this handle. */ + PHYSFS_uint8 *buffer; /* Buffer, if set (NULL otherwise). Don't touch! */ + PHYSFS_uint32 bufsize; /* Bufsize, if set (0 otherwise). Don't touch! */ + PHYSFS_uint32 buffill; /* Buffer fill size. Don't touch! */ + PHYSFS_uint32 bufpos; /* Buffer position. Don't touch! */ + struct __PHYSFS_FILEHANDLE__ *next; /* linked list stuff. */ +} FileHandle; + + +typedef struct __PHYSFS_ERRMSGTYPE__ +{ + PHYSFS_uint64 tid; + int errorAvailable; + char errorString[80]; + struct __PHYSFS_ERRMSGTYPE__ *next; +} ErrMsg; + + +/* The various i/o drivers...some of these may not be compiled in. */ +extern const PHYSFS_ArchiveInfo __PHYSFS_ArchiveInfo_ZIP; +extern const PHYSFS_Archiver __PHYSFS_Archiver_ZIP; +extern const PHYSFS_ArchiveInfo __PHYSFS_ArchiveInfo_LZMA; +extern const PHYSFS_Archiver __PHYSFS_Archiver_LZMA; +extern const PHYSFS_ArchiveInfo __PHYSFS_ArchiveInfo_GRP; +extern const PHYSFS_Archiver __PHYSFS_Archiver_GRP; +extern const PHYSFS_ArchiveInfo __PHYSFS_ArchiveInfo_QPAK; +extern const PHYSFS_Archiver __PHYSFS_Archiver_QPAK; +extern const PHYSFS_ArchiveInfo __PHYSFS_ArchiveInfo_HOG; +extern const PHYSFS_Archiver __PHYSFS_Archiver_HOG; +extern const PHYSFS_ArchiveInfo __PHYSFS_ArchiveInfo_MVL; +extern const PHYSFS_Archiver __PHYSFS_Archiver_MVL; +extern const PHYSFS_ArchiveInfo __PHYSFS_ArchiveInfo_WAD; +extern const PHYSFS_Archiver __PHYSFS_Archiver_WAD; +extern const PHYSFS_Archiver __PHYSFS_Archiver_DIR; + + +static const PHYSFS_ArchiveInfo *supported_types[] = +{ +#if (defined PHYSFS_SUPPORTS_ZIP) + &__PHYSFS_ArchiveInfo_ZIP, +#endif +#if (defined PHYSFS_SUPPORTS_7Z) + &__PHYSFS_ArchiveInfo_LZMA, +#endif +#if (defined PHYSFS_SUPPORTS_GRP) + &__PHYSFS_ArchiveInfo_GRP, +#endif +#if (defined PHYSFS_SUPPORTS_QPAK) + &__PHYSFS_ArchiveInfo_QPAK, +#endif +#if (defined PHYSFS_SUPPORTS_HOG) + &__PHYSFS_ArchiveInfo_HOG, +#endif +#if (defined PHYSFS_SUPPORTS_MVL) + &__PHYSFS_ArchiveInfo_MVL, +#endif +#if (defined PHYSFS_SUPPORTS_WAD) + &__PHYSFS_ArchiveInfo_WAD, +#endif + NULL +}; + +static const PHYSFS_Archiver *archivers[] = +{ + &__PHYSFS_Archiver_DIR, +#if (defined PHYSFS_SUPPORTS_ZIP) + &__PHYSFS_Archiver_ZIP, +#endif +#if (defined PHYSFS_SUPPORTS_7Z) + &__PHYSFS_Archiver_LZMA, +#endif +#if (defined PHYSFS_SUPPORTS_GRP) + &__PHYSFS_Archiver_GRP, +#endif +#if (defined PHYSFS_SUPPORTS_QPAK) + &__PHYSFS_Archiver_QPAK, +#endif +#if (defined PHYSFS_SUPPORTS_HOG) + &__PHYSFS_Archiver_HOG, +#endif +#if (defined PHYSFS_SUPPORTS_MVL) + &__PHYSFS_Archiver_MVL, +#endif +#if (defined PHYSFS_SUPPORTS_WAD) + &__PHYSFS_Archiver_WAD, +#endif + NULL +}; + + + +/* General PhysicsFS state ... */ +static int initialized = 0; +static ErrMsg *errorMessages = NULL; +static DirHandle *searchPath = NULL; +static DirHandle *writeDir = NULL; +static FileHandle *openWriteList = NULL; +static FileHandle *openReadList = NULL; +static char *baseDir = NULL; +static char *userDir = NULL; +static int allowSymLinks = 0; + +/* mutexes ... */ +static void *errorLock = NULL; /* protects error message list. */ +static void *stateLock = NULL; /* protects other PhysFS static state. */ + +/* allocator ... */ +static int externalAllocator = 0; +PHYSFS_Allocator allocator; + + +/* functions ... */ + +typedef struct +{ + char **list; + PHYSFS_uint32 size; + const char *errorstr; +} EnumStringListCallbackData; + +static void enumStringListCallback(void *data, const char *str) +{ + void *ptr; + char *newstr; + EnumStringListCallbackData *pecd = (EnumStringListCallbackData *) data; + + if (pecd->errorstr) + return; + + ptr = allocator.Realloc(pecd->list, (pecd->size + 2) * sizeof (char *)); + newstr = (char *) allocator.Malloc(strlen(str) + 1); + if (ptr != NULL) + pecd->list = (char **) ptr; + + if ((ptr == NULL) || (newstr == NULL)) + { + pecd->errorstr = ERR_OUT_OF_MEMORY; + pecd->list[pecd->size] = NULL; + PHYSFS_freeList(pecd->list); + return; + } /* if */ + + strcpy(newstr, str); + pecd->list[pecd->size] = newstr; + pecd->size++; +} /* enumStringListCallback */ + + +static char **doEnumStringList(void (*func)(PHYSFS_StringCallback, void *)) +{ + EnumStringListCallbackData ecd; + memset(&ecd, '\0', sizeof (ecd)); + ecd.list = (char **) allocator.Malloc(sizeof (char *)); + BAIL_IF_MACRO(ecd.list == NULL, ERR_OUT_OF_MEMORY, NULL); + func(enumStringListCallback, &ecd); + BAIL_IF_MACRO(ecd.errorstr != NULL, ecd.errorstr, NULL); + ecd.list[ecd.size] = NULL; + return(ecd.list); +} /* doEnumStringList */ + + +static void __PHYSFS_bubble_sort(void *a, PHYSFS_uint32 lo, PHYSFS_uint32 hi, + int (*cmpfn)(void *, PHYSFS_uint32, PHYSFS_uint32), + void (*swapfn)(void *, PHYSFS_uint32, PHYSFS_uint32)) +{ + PHYSFS_uint32 i; + int sorted; + + do + { + sorted = 1; + for (i = lo; i < hi; i++) + { + if (cmpfn(a, i, i + 1) > 0) + { + swapfn(a, i, i + 1); + sorted = 0; + } /* if */ + } /* for */ + } while (!sorted); +} /* __PHYSFS_bubble_sort */ + + +static void __PHYSFS_quick_sort(void *a, PHYSFS_uint32 lo, PHYSFS_uint32 hi, + int (*cmpfn)(void *, PHYSFS_uint32, PHYSFS_uint32), + void (*swapfn)(void *, PHYSFS_uint32, PHYSFS_uint32)) +{ + PHYSFS_uint32 i; + PHYSFS_uint32 j; + PHYSFS_uint32 v; + + if ((hi - lo) <= PHYSFS_QUICKSORT_THRESHOLD) + __PHYSFS_bubble_sort(a, lo, hi, cmpfn, swapfn); + else + { + i = (hi + lo) / 2; + + if (cmpfn(a, lo, i) > 0) swapfn(a, lo, i); + if (cmpfn(a, lo, hi) > 0) swapfn(a, lo, hi); + if (cmpfn(a, i, hi) > 0) swapfn(a, i, hi); + + j = hi - 1; + swapfn(a, i, j); + i = lo; + v = j; + while (1) + { + while(cmpfn(a, ++i, v) < 0) { /* do nothing */ } + while(cmpfn(a, --j, v) > 0) { /* do nothing */ } + if (j < i) + break; + swapfn(a, i, j); + } /* while */ + if (i != (hi-1)) + swapfn(a, i, hi-1); + __PHYSFS_quick_sort(a, lo, j, cmpfn, swapfn); + __PHYSFS_quick_sort(a, i+1, hi, cmpfn, swapfn); + } /* else */ +} /* __PHYSFS_quick_sort */ + + +void __PHYSFS_sort(void *entries, PHYSFS_uint32 max, + int (*cmpfn)(void *, PHYSFS_uint32, PHYSFS_uint32), + void (*swapfn)(void *, PHYSFS_uint32, PHYSFS_uint32)) +{ + /* + * Quicksort w/ Bubblesort fallback algorithm inspired by code from here: + * http://www.cs.ubc.ca/spider/harrison/Java/sorting-demo.html + */ + __PHYSFS_quick_sort(entries, 0, max - 1, cmpfn, swapfn); +} /* __PHYSFS_sort */ + + +static ErrMsg *findErrorForCurrentThread(void) +{ + ErrMsg *i; + PHYSFS_uint64 tid; + + if (errorLock != NULL) + __PHYSFS_platformGrabMutex(errorLock); + + if (errorMessages != NULL) + { + tid = __PHYSFS_platformGetThreadID(); + + for (i = errorMessages; i != NULL; i = i->next) + { + if (i->tid == tid) + { + if (errorLock != NULL) + __PHYSFS_platformReleaseMutex(errorLock); + return(i); + } /* if */ + } /* for */ + } /* if */ + + if (errorLock != NULL) + __PHYSFS_platformReleaseMutex(errorLock); + + return(NULL); /* no error available. */ +} /* findErrorForCurrentThread */ + + +void __PHYSFS_setError(const char *str) +{ + ErrMsg *err; + + if (str == NULL) + return; + + err = findErrorForCurrentThread(); + + if (err == NULL) + { + err = (ErrMsg *) allocator.Malloc(sizeof (ErrMsg)); + if (err == NULL) + return; /* uhh...? */ + + memset((void *) err, '\0', sizeof (ErrMsg)); + err->tid = __PHYSFS_platformGetThreadID(); + + if (errorLock != NULL) + __PHYSFS_platformGrabMutex(errorLock); + + err->next = errorMessages; + errorMessages = err; + + if (errorLock != NULL) + __PHYSFS_platformReleaseMutex(errorLock); + } /* if */ + + err->errorAvailable = 1; + strncpy(err->errorString, str, sizeof (err->errorString)); + err->errorString[sizeof (err->errorString) - 1] = '\0'; +} /* __PHYSFS_setError */ + + +const char *PHYSFS_getLastError(void) +{ + ErrMsg *err = findErrorForCurrentThread(); + + if ((err == NULL) || (!err->errorAvailable)) + return(NULL); + + err->errorAvailable = 0; + return(err->errorString); +} /* PHYSFS_getLastError */ + + +/* MAKE SURE that errorLock is held before calling this! */ +static void freeErrorMessages(void) +{ + ErrMsg *i; + ErrMsg *next; + + for (i = errorMessages; i != NULL; i = next) + { + next = i->next; + allocator.Free(i); + } /* for */ + + errorMessages = NULL; +} /* freeErrorMessages */ + + +void PHYSFS_getLinkedVersion(PHYSFS_Version *ver) +{ + if (ver != NULL) + { + ver->major = PHYSFS_VER_MAJOR; + ver->minor = PHYSFS_VER_MINOR; + ver->patch = PHYSFS_VER_PATCH; + } /* if */ +} /* PHYSFS_getLinkedVersion */ + + +static const char *find_filename_extension(const char *fname) +{ + const char *retval = strchr(fname, '.'); + const char *p = retval; + + while (p != NULL) + { + p = strchr(p + 1, '.'); + if (p != NULL) + retval = p; + } /* while */ + + if (retval != NULL) + retval++; /* skip '.' */ + + return(retval); +} /* find_filename_extension */ + + +static DirHandle *tryOpenDir(const PHYSFS_Archiver *funcs, + const char *d, int forWriting) +{ + DirHandle *retval = NULL; + if (funcs->isArchive(d, forWriting)) + { + void *opaque = funcs->openArchive(d, forWriting); + if (opaque != NULL) + { + retval = (DirHandle *) allocator.Malloc(sizeof (DirHandle)); + if (retval == NULL) + funcs->dirClose(opaque); + else + { + memset(retval, '\0', sizeof (DirHandle)); + retval->mountPoint = NULL; + retval->funcs = funcs; + retval->opaque = opaque; + } /* else */ + } /* if */ + } /* if */ + + return(retval); +} /* tryOpenDir */ + + +static DirHandle *openDirectory(const char *d, int forWriting) +{ + DirHandle *retval = NULL; + const PHYSFS_Archiver **i; + const char *ext; + + BAIL_IF_MACRO(!__PHYSFS_platformExists(d), ERR_NO_SUCH_FILE, NULL); + + ext = find_filename_extension(d); + if (ext != NULL) + { + /* Look for archivers with matching file extensions first... */ + for (i = archivers; (*i != NULL) && (retval == NULL); i++) + { + if (__PHYSFS_stricmpASCII(ext, (*i)->info->extension) == 0) + retval = tryOpenDir(*i, d, forWriting); + } /* for */ + + /* failing an exact file extension match, try all the others... */ + for (i = archivers; (*i != NULL) && (retval == NULL); i++) + { + if (__PHYSFS_stricmpASCII(ext, (*i)->info->extension) != 0) + retval = tryOpenDir(*i, d, forWriting); + } /* for */ + } /* if */ + + else /* no extension? Try them all. */ + { + for (i = archivers; (*i != NULL) && (retval == NULL); i++) + retval = tryOpenDir(*i, d, forWriting); + } /* else */ + + BAIL_IF_MACRO(retval == NULL, ERR_UNSUPPORTED_ARCHIVE, NULL); + return(retval); +} /* openDirectory */ + + +/* + * Make a platform-independent path string sane. Doesn't actually check the + * file hierarchy, it just cleans up the string. + * (dst) must be a buffer at least as big as (src), as this is where the + * cleaned up string is deposited. + * If there are illegal bits in the path (".." entries, etc) then we + * return zero and (dst) is undefined. Non-zero if the path was sanitized. + */ +static int sanitizePlatformIndependentPath(const char *src, char *dst) +{ + char *prev; + char ch; + + while (*src == '/') /* skip initial '/' chars... */ + src++; + + prev = dst; + do + { + ch = *(src++); + + if ((ch == ':') || (ch == '\\')) /* illegal chars in a physfs path. */ + BAIL_MACRO(ERR_INSECURE_FNAME, 0); + + if (ch == '/') /* path separator. */ + { + *dst = '\0'; /* "." and ".." are illegal pathnames. */ + if ((strcmp(prev, ".") == 0) || (strcmp(prev, "..") == 0)) + BAIL_MACRO(ERR_INSECURE_FNAME, 0); + + while (*src == '/') /* chop out doubles... */ + src++; + + if (*src == '\0') /* ends with a pathsep? */ + break; /* we're done, don't add final pathsep to dst. */ + + prev = dst + 1; + } /* if */ + + *(dst++) = ch; + } while (ch != '\0'); + + return(1); +} /* sanitizePlatformIndependentPath */ + + +/* + * Figure out if (fname) is part of (h)'s mountpoint. (fname) must be an + * output from sanitizePlatformIndependentPath(), so that it is in a known + * state. + * + * This only finds legitimate segments of a mountpoint. If the mountpoint is + * "/a/b/c" and (fname) is "/a/b/c", "/", or "/a/b/c/d", then the results are + * all zero. "/a/b" will succeed, though. + */ +static int partOfMountPoint(DirHandle *h, char *fname) +{ + /* !!! FIXME: This code feels gross. */ + int rc; + size_t len, mntpntlen; + + if (h->mountPoint == NULL) + return(0); + else if (*fname == '\0') + return(1); + + len = strlen(fname); + mntpntlen = strlen(h->mountPoint); + if (len > mntpntlen) /* can't be a subset of mountpoint. */ + return(0); + + /* if true, must be not a match or a complete match, but not a subset. */ + if ((len + 1) == mntpntlen) + return(0); + + rc = strncmp(fname, h->mountPoint, len); /* !!! FIXME: case insensitive? */ + if (rc != 0) + return(0); /* not a match. */ + + /* make sure /a/b matches /a/b/ and not /a/bc ... */ + return(h->mountPoint[len] == '/'); +} /* partOfMountPoint */ + + +static DirHandle *createDirHandle(const char *newDir, + const char *mountPoint, + int forWriting) +{ + DirHandle *dirHandle = NULL; + char *tmpmntpnt = NULL; + + GOTO_IF_MACRO(!newDir, ERR_INVALID_ARGUMENT, badDirHandle); + if (mountPoint != NULL) + { + const size_t len = strlen(mountPoint) + 1; + tmpmntpnt = (char *) __PHYSFS_smallAlloc(len); + GOTO_IF_MACRO(!tmpmntpnt, ERR_OUT_OF_MEMORY, badDirHandle); + if (!sanitizePlatformIndependentPath(mountPoint, tmpmntpnt)) + goto badDirHandle; + mountPoint = tmpmntpnt; /* sanitized version. */ + } /* if */ + + dirHandle = openDirectory(newDir, forWriting); + GOTO_IF_MACRO(!dirHandle, NULL, badDirHandle); + + dirHandle->dirName = (char *) allocator.Malloc(strlen(newDir) + 1); + GOTO_IF_MACRO(!dirHandle->dirName, ERR_OUT_OF_MEMORY, badDirHandle); + strcpy(dirHandle->dirName, newDir); + + if ((mountPoint != NULL) && (*mountPoint != '\0')) + { + dirHandle->mountPoint = (char *)allocator.Malloc(strlen(mountPoint)+2); + GOTO_IF_MACRO(!dirHandle->mountPoint, ERR_OUT_OF_MEMORY, badDirHandle); + strcpy(dirHandle->mountPoint, mountPoint); + strcat(dirHandle->mountPoint, "/"); + } /* if */ + + __PHYSFS_smallFree(tmpmntpnt); + return(dirHandle); + +badDirHandle: + if (dirHandle != NULL) + { + dirHandle->funcs->dirClose(dirHandle->opaque); + allocator.Free(dirHandle->dirName); + allocator.Free(dirHandle->mountPoint); + allocator.Free(dirHandle); + } /* if */ + + __PHYSFS_smallFree(tmpmntpnt); + return(NULL); +} /* createDirHandle */ + + +/* MAKE SURE you've got the stateLock held before calling this! */ +static int freeDirHandle(DirHandle *dh, FileHandle *openList) +{ + FileHandle *i; + + if (dh == NULL) + return(1); + + for (i = openList; i != NULL; i = i->next) + BAIL_IF_MACRO(i->dirHandle == dh, ERR_FILES_STILL_OPEN, 0); + + dh->funcs->dirClose(dh->opaque); + allocator.Free(dh->dirName); + allocator.Free(dh->mountPoint); + allocator.Free(dh); + return(1); +} /* freeDirHandle */ + + +static char *calculateUserDir(void) +{ + char *retval = __PHYSFS_platformGetUserDir(); + if (retval != NULL) + { + /* make sure it really exists and is normalized. */ + char *ptr = __PHYSFS_platformRealPath(retval); + allocator.Free(retval); + retval = ptr; + } /* if */ + + if (retval == NULL) + { + const char *dirsep = PHYSFS_getDirSeparator(); + const char *uname = __PHYSFS_platformGetUserName(); + const char *str = (uname != NULL) ? uname : "default"; + + retval = (char *) allocator.Malloc(strlen(baseDir) + strlen(str) + + strlen(dirsep) + 6); + + if (retval == NULL) + __PHYSFS_setError(ERR_OUT_OF_MEMORY); + else + sprintf(retval, "%susers%s%s", baseDir, dirsep, str); + + allocator.Free((void *) uname); + } /* else */ + + return(retval); +} /* calculateUserDir */ + + +static int appendDirSep(char **dir) +{ + const char *dirsep = PHYSFS_getDirSeparator(); + char *ptr; + + if (strcmp((*dir + strlen(*dir)) - strlen(dirsep), dirsep) == 0) + return(1); + + ptr = (char *) allocator.Realloc(*dir, strlen(*dir) + strlen(dirsep) + 1); + if (!ptr) + { + allocator.Free(*dir); + return(0); + } /* if */ + + strcat(ptr, dirsep); + *dir = ptr; + return(1); +} /* appendDirSep */ + + +static char *calculateBaseDir(const char *argv0) +{ + char *retval = NULL; + const char *dirsep = NULL; + char *ptr = NULL; + + /* Give the platform layer first shot at this. */ + retval = __PHYSFS_platformCalcBaseDir(argv0); + if (retval != NULL) + return(retval); + + /* We need argv0 to go on. */ + BAIL_IF_MACRO(argv0 == NULL, ERR_ARGV0_IS_NULL, NULL); + + dirsep = PHYSFS_getDirSeparator(); + if (strlen(dirsep) == 1) /* fast path. */ + ptr = strrchr(argv0, *dirsep); + else + { + ptr = strstr(argv0, dirsep); + if (ptr != NULL) + { + char *p = ptr; + while (p != NULL) + { + ptr = p; + p = strstr(p + 1, dirsep); + } /* while */ + } /* if */ + } /* else */ + + if (ptr != NULL) + { + size_t size = (size_t) (ptr - argv0); + retval = (char *) allocator.Malloc(size + 1); + BAIL_IF_MACRO(retval == NULL, ERR_OUT_OF_MEMORY, NULL); + memcpy(retval, argv0, size); + retval[size] = '\0'; + return(retval); + } /* if */ + + /* argv0 wasn't helpful. */ + BAIL_MACRO(ERR_INVALID_ARGUMENT, NULL); + return(NULL); +} /* calculateBaseDir */ + + +static int initializeMutexes(void) +{ + errorLock = __PHYSFS_platformCreateMutex(); + if (errorLock == NULL) + goto initializeMutexes_failed; + + stateLock = __PHYSFS_platformCreateMutex(); + if (stateLock == NULL) + goto initializeMutexes_failed; + + return(1); /* success. */ + +initializeMutexes_failed: + if (errorLock != NULL) + __PHYSFS_platformDestroyMutex(errorLock); + + if (stateLock != NULL) + __PHYSFS_platformDestroyMutex(stateLock); + + errorLock = stateLock = NULL; + return(0); /* failed. */ +} /* initializeMutexes */ + + +static void setDefaultAllocator(void); + +int PHYSFS_init(const char *argv0) +{ + char *ptr; + + BAIL_IF_MACRO(initialized, ERR_IS_INITIALIZED, 0); + + if (!externalAllocator) + setDefaultAllocator(); + + if (allocator.Init != NULL) + BAIL_IF_MACRO(!allocator.Init(), NULL, 0); + + BAIL_IF_MACRO(!__PHYSFS_platformInit(), NULL, 0); + + BAIL_IF_MACRO(!initializeMutexes(), NULL, 0); + + baseDir = calculateBaseDir(argv0); + BAIL_IF_MACRO(baseDir == NULL, NULL, 0); + + /* !!! FIXME: only call this if we got this from argv0 (unreliable). */ + ptr = __PHYSFS_platformRealPath(baseDir); + allocator.Free(baseDir); + BAIL_IF_MACRO(ptr == NULL, NULL, 0); + baseDir = ptr; + + BAIL_IF_MACRO(!appendDirSep(&baseDir), NULL, 0); + + userDir = calculateUserDir(); + if ((userDir == NULL) || (!appendDirSep(&userDir))) + { + allocator.Free(baseDir); + baseDir = NULL; + return(0); + } /* if */ + + initialized = 1; + + /* This makes sure that the error subsystem is initialized. */ + __PHYSFS_setError(PHYSFS_getLastError()); + + return(1); +} /* PHYSFS_init */ + + +/* MAKE SURE you hold stateLock before calling this! */ +static int closeFileHandleList(FileHandle **list) +{ + FileHandle *i; + FileHandle *next = NULL; + + for (i = *list; i != NULL; i = next) + { + next = i->next; + if (!i->funcs->fileClose(i->opaque)) + { + *list = i; + return(0); + } /* if */ + + allocator.Free(i); + } /* for */ + + *list = NULL; + return(1); +} /* closeFileHandleList */ + + +/* MAKE SURE you hold the stateLock before calling this! */ +static void freeSearchPath(void) +{ + DirHandle *i; + DirHandle *next = NULL; + + closeFileHandleList(&openReadList); + + if (searchPath != NULL) + { + for (i = searchPath; i != NULL; i = next) + { + next = i->next; + freeDirHandle(i, openReadList); + } /* for */ + searchPath = NULL; + } /* if */ +} /* freeSearchPath */ + + +int PHYSFS_deinit(void) +{ + BAIL_IF_MACRO(!initialized, ERR_NOT_INITIALIZED, 0); + BAIL_IF_MACRO(!__PHYSFS_platformDeinit(), NULL, 0); + + closeFileHandleList(&openWriteList); + BAIL_IF_MACRO(!PHYSFS_setWriteDir(NULL), ERR_FILES_STILL_OPEN, 0); + + freeSearchPath(); + freeErrorMessages(); + + if (baseDir != NULL) + { + allocator.Free(baseDir); + baseDir = NULL; + } /* if */ + + if (userDir != NULL) + { + allocator.Free(userDir); + userDir = NULL; + } /* if */ + + allowSymLinks = 0; + initialized = 0; + + __PHYSFS_platformDestroyMutex(errorLock); + __PHYSFS_platformDestroyMutex(stateLock); + + if (allocator.Deinit != NULL) + allocator.Deinit(); + + errorLock = stateLock = NULL; + return(1); +} /* PHYSFS_deinit */ + + +int PHYSFS_isInit(void) +{ + return(initialized); +} /* PHYSFS_isInit */ + + +const PHYSFS_ArchiveInfo **PHYSFS_supportedArchiveTypes(void) +{ + return(supported_types); +} /* PHYSFS_supportedArchiveTypes */ + + +void PHYSFS_freeList(void *list) +{ + void **i; + for (i = (void **) list; *i != NULL; i++) + allocator.Free(*i); + + allocator.Free(list); +} /* PHYSFS_freeList */ + + +const char *PHYSFS_getDirSeparator(void) +{ + return(__PHYSFS_platformDirSeparator); +} /* PHYSFS_getDirSeparator */ + + +char **PHYSFS_getCdRomDirs(void) +{ + return(doEnumStringList(__PHYSFS_platformDetectAvailableCDs)); +} /* PHYSFS_getCdRomDirs */ + + +void PHYSFS_getCdRomDirsCallback(PHYSFS_StringCallback callback, void *data) +{ + __PHYSFS_platformDetectAvailableCDs(callback, data); +} /* PHYSFS_getCdRomDirsCallback */ + + +const char *PHYSFS_getBaseDir(void) +{ + return(baseDir); /* this is calculated in PHYSFS_init()... */ +} /* PHYSFS_getBaseDir */ + + +const char *PHYSFS_getUserDir(void) +{ + return(userDir); /* this is calculated in PHYSFS_init()... */ +} /* PHYSFS_getUserDir */ + + +const char *PHYSFS_getWriteDir(void) +{ + const char *retval = NULL; + + __PHYSFS_platformGrabMutex(stateLock); + if (writeDir != NULL) + retval = writeDir->dirName; + __PHYSFS_platformReleaseMutex(stateLock); + + return(retval); +} /* PHYSFS_getWriteDir */ + + +int PHYSFS_setWriteDir(const char *newDir) +{ + int retval = 1; + + __PHYSFS_platformGrabMutex(stateLock); + + if (writeDir != NULL) + { + BAIL_IF_MACRO_MUTEX(!freeDirHandle(writeDir, openWriteList), NULL, + stateLock, 0); + writeDir = NULL; + } /* if */ + + if (newDir != NULL) + { + writeDir = createDirHandle(newDir, NULL, 1); + retval = (writeDir != NULL); + } /* if */ + + __PHYSFS_platformReleaseMutex(stateLock); + + return(retval); +} /* PHYSFS_setWriteDir */ + + +int PHYSFS_mount(const char *newDir, const char *mountPoint, int appendToPath) +{ + DirHandle *dh; + DirHandle *prev = NULL; + DirHandle *i; + + BAIL_IF_MACRO(newDir == NULL, ERR_INVALID_ARGUMENT, 0); + + if (mountPoint == NULL) + mountPoint = "/"; + + __PHYSFS_platformGrabMutex(stateLock); + + for (i = searchPath; i != NULL; i = i->next) + { + /* already in search path? */ + BAIL_IF_MACRO_MUTEX(strcmp(newDir, i->dirName)==0, NULL, stateLock, 1); + prev = i; + } /* for */ + + dh = createDirHandle(newDir, mountPoint, 0); + BAIL_IF_MACRO_MUTEX(dh == NULL, NULL, stateLock, 0); + + if (appendToPath) + { + if (prev == NULL) + searchPath = dh; + else + prev->next = dh; + } /* if */ + else + { + dh->next = searchPath; + searchPath = dh; + } /* else */ + + __PHYSFS_platformReleaseMutex(stateLock); + return(1); +} /* PHYSFS_mount */ + + +int PHYSFS_addToSearchPath(const char *newDir, int appendToPath) +{ + return(PHYSFS_mount(newDir, NULL, appendToPath)); +} /* PHYSFS_addToSearchPath */ + + +int PHYSFS_removeFromSearchPath(const char *oldDir) +{ + DirHandle *i; + DirHandle *prev = NULL; + DirHandle *next = NULL; + + BAIL_IF_MACRO(oldDir == NULL, ERR_INVALID_ARGUMENT, 0); + + __PHYSFS_platformGrabMutex(stateLock); + for (i = searchPath; i != NULL; i = i->next) + { + if (strcmp(i->dirName, oldDir) == 0) + { + next = i->next; + BAIL_IF_MACRO_MUTEX(!freeDirHandle(i, openReadList), NULL, + stateLock, 0); + + if (prev == NULL) + searchPath = next; + else + prev->next = next; + + BAIL_MACRO_MUTEX(NULL, stateLock, 1); + } /* if */ + prev = i; + } /* for */ + + BAIL_MACRO_MUTEX(ERR_NOT_IN_SEARCH_PATH, stateLock, 0); +} /* PHYSFS_removeFromSearchPath */ + + +char **PHYSFS_getSearchPath(void) +{ + return(doEnumStringList(PHYSFS_getSearchPathCallback)); +} /* PHYSFS_getSearchPath */ + + +const char *PHYSFS_getMountPoint(const char *dir) +{ + DirHandle *i; + __PHYSFS_platformGrabMutex(stateLock); + for (i = searchPath; i != NULL; i = i->next) + { + if (strcmp(i->dirName, dir) == 0) + { + const char *retval = ((i->mountPoint) ? i->mountPoint : "/"); + __PHYSFS_platformReleaseMutex(stateLock); + return(retval); + } /* if */ + } /* for */ + __PHYSFS_platformReleaseMutex(stateLock); + + BAIL_MACRO(ERR_NOT_IN_SEARCH_PATH, NULL); +} /* PHYSFS_getMountPoint */ + + +void PHYSFS_getSearchPathCallback(PHYSFS_StringCallback callback, void *data) +{ + DirHandle *i; + + __PHYSFS_platformGrabMutex(stateLock); + + for (i = searchPath; i != NULL; i = i->next) + callback(data, i->dirName); + + __PHYSFS_platformReleaseMutex(stateLock); +} /* PHYSFS_getSearchPathCallback */ + + +/* Split out to avoid stack allocation in a loop. */ +static void setSaneCfgAddPath(const char *i, const size_t l, const char *dirsep, + int archivesFirst) +{ + const char *d = PHYSFS_getRealDir(i); + const size_t allocsize = strlen(d) + strlen(dirsep) + l + 1; + char *str = (char *) __PHYSFS_smallAlloc(allocsize); + if (str != NULL) + { + sprintf(str, "%s%s%s", d, dirsep, i); + PHYSFS_addToSearchPath(str, archivesFirst == 0); + __PHYSFS_smallFree(str); + } /* if */ +} /* setSaneCfgAddPath */ + + +int PHYSFS_setSaneConfig(const char *organization, const char *appName, + const char *archiveExt, int includeCdRoms, + int archivesFirst) +{ + const char *basedir = PHYSFS_getBaseDir(); + const char *userdir = PHYSFS_getUserDir(); + const char *dirsep = PHYSFS_getDirSeparator(); + PHYSFS_uint64 len = 0; + char *str = NULL; + + BAIL_IF_MACRO(!initialized, ERR_NOT_INITIALIZED, 0); + + /* set write dir... */ + len = (strlen(userdir) + (strlen(organization) * 2) + + (strlen(appName) * 2) + (strlen(dirsep) * 3) + 2); + + str = (char *) __PHYSFS_smallAlloc(len); + + BAIL_IF_MACRO(str == NULL, ERR_OUT_OF_MEMORY, 0); + sprintf(str, "%s.%s%s%s", userdir, organization, dirsep, appName); + + if (!PHYSFS_setWriteDir(str)) + { + int no_write = 0; + sprintf(str, ".%s/%s", organization, appName); + if ( (PHYSFS_setWriteDir(userdir)) && + (PHYSFS_mkdir(str)) ) + { + sprintf(str, "%s.%s%s%s", userdir, organization, dirsep, appName); + if (!PHYSFS_setWriteDir(str)) + no_write = 1; + } /* if */ + else + { + no_write = 1; + } /* else */ + + if (no_write) + { + PHYSFS_setWriteDir(NULL); /* just in case. */ + __PHYSFS_smallFree(str); + BAIL_MACRO(ERR_CANT_SET_WRITE_DIR, 0); + } /* if */ + } /* if */ + + /* Put write dir first in search path... */ + PHYSFS_addToSearchPath(str, 0); + __PHYSFS_smallFree(str); + + /* Put base path on search path... */ + PHYSFS_addToSearchPath(basedir, 1); + + /* handle CD-ROMs... */ + if (includeCdRoms) + { + char **cds = PHYSFS_getCdRomDirs(); + char **i; + for (i = cds; *i != NULL; i++) + PHYSFS_addToSearchPath(*i, 1); + + PHYSFS_freeList(cds); + } /* if */ + + /* Root out archives, and add them to search path... */ + if (archiveExt != NULL) + { + char **rc = PHYSFS_enumerateFiles("/"); + char **i; + size_t extlen = strlen(archiveExt); + char *ext; + + for (i = rc; *i != NULL; i++) + { + size_t l = strlen(*i); + if ((l > extlen) && ((*i)[l - extlen - 1] == '.')) + { + ext = (*i) + (l - extlen); + if (__PHYSFS_stricmpASCII(ext, archiveExt) == 0) + setSaneCfgAddPath(*i, l, dirsep, archivesFirst); + } /* if */ + } /* for */ + + PHYSFS_freeList(rc); + } /* if */ + + return(1); +} /* PHYSFS_setSaneConfig */ + + +void PHYSFS_permitSymbolicLinks(int allow) +{ + allowSymLinks = allow; +} /* PHYSFS_permitSymbolicLinks */ + + +int PHYSFS_symbolicLinksPermitted(void) +{ + return(allowSymLinks); +} /* PHYSFS_symbolicLinksPermitted */ + + +/* string manipulation in C makes my ass itch. */ +char *__PHYSFS_convertToDependent(const char *prepend, + const char *dirName, + const char *append) +{ + const char *dirsep = __PHYSFS_platformDirSeparator; + size_t sepsize = strlen(dirsep); + char *str; + char *i1; + char *i2; + size_t allocSize; + + while (*dirName == '/') /* !!! FIXME: pass through sanitize function. */ + dirName++; + + allocSize = strlen(dirName) + 1; + if (prepend != NULL) + allocSize += strlen(prepend) + sepsize; + if (append != NULL) + allocSize += strlen(append) + sepsize; + + /* make sure there's enough space if the dir separator is bigger. */ + if (sepsize > 1) + { + str = (char *) dirName; + do + { + str = strchr(str, '/'); + if (str != NULL) + { + allocSize += (sepsize - 1); + str++; + } /* if */ + } while (str != NULL); + } /* if */ + + str = (char *) allocator.Malloc(allocSize); + BAIL_IF_MACRO(str == NULL, ERR_OUT_OF_MEMORY, NULL); + + if (prepend == NULL) + *str = '\0'; + else + { + strcpy(str, prepend); + strcat(str, dirsep); + } /* else */ + + for (i1 = (char *) dirName, i2 = str + strlen(str); *i1; i1++, i2++) + { + if (*i1 == '/') + { + strcpy(i2, dirsep); + i2 += sepsize; + } /* if */ + else + { + *i2 = *i1; + } /* else */ + } /* for */ + *i2 = '\0'; + + if (append) + { + strcat(str, dirsep); + strcat(str, append); + } /* if */ + + return(str); +} /* __PHYSFS_convertToDependent */ + + +/* + * Verify that (fname) (in platform-independent notation), in relation + * to (h) is secure. That means that each element of fname is checked + * for symlinks (if they aren't permitted). This also allows for quick + * rejection of files that exist outside an archive's mountpoint. + * + * With some exceptions (like PHYSFS_mkdir(), which builds multiple subdirs + * at a time), you should always pass zero for "allowMissing" for efficiency. + * + * (fname) must point to an output from sanitizePlatformIndependentPath(), + * since it will make sure that path names are in the right format for + * passing certain checks. It will also do checks for "insecure" pathnames + * like ".." which should be done once instead of once per archive. This also + * gives us license to treat (fname) as scratch space in this function. + * + * Returns non-zero if string is safe, zero if there's a security issue. + * PHYSFS_getLastError() will specify what was wrong. (*fname) will be + * updated to point past any mount point elements so it is prepared to + * be used with the archiver directly. + */ +static int verifyPath(DirHandle *h, char **_fname, int allowMissing) +{ + char *fname = *_fname; + int retval = 1; + char *start; + char *end; + + if (*fname == '\0') /* quick rejection. */ + return(1); + + /* !!! FIXME: This codeblock sucks. */ + if (h->mountPoint != NULL) /* NULL mountpoint means "/". */ + { + size_t mntpntlen = strlen(h->mountPoint); + size_t len = strlen(fname); + assert(mntpntlen > 1); /* root mount points should be NULL. */ + /* not under the mountpoint, so skip this archive. */ + BAIL_IF_MACRO(len < mntpntlen-1, ERR_NO_SUCH_PATH, 0); + /* !!! FIXME: Case insensitive? */ + retval = strncmp(h->mountPoint, fname, mntpntlen-1); + BAIL_IF_MACRO(retval != 0, ERR_NO_SUCH_PATH, 0); + if (len > mntpntlen-1) /* corner case... */ + BAIL_IF_MACRO(fname[mntpntlen-1] != '/', ERR_NO_SUCH_PATH, 0); + fname += mntpntlen-1; /* move to start of actual archive path. */ + if (*fname == '/') + fname++; + *_fname = fname; /* skip mountpoint for later use. */ + retval = 1; /* may be reset, below. */ + } /* if */ + + start = fname; + if (!allowSymLinks) + { + while (1) + { + int rc = 0; + end = strchr(start, '/'); + + if (end != NULL) *end = '\0'; + rc = h->funcs->isSymLink(h->opaque, fname, &retval); + if (end != NULL) *end = '/'; + + BAIL_IF_MACRO(rc, ERR_SYMLINK_DISALLOWED, 0); /* insecure. */ + + /* break out early if path element is missing. */ + if (!retval) + { + /* + * We need to clear it if it's the last element of the path, + * since this might be a non-existant file we're opening + * for writing... + */ + if ((end == NULL) || (allowMissing)) + retval = 1; + break; + } /* if */ + + if (end == NULL) + break; + + start = end + 1; + } /* while */ + } /* if */ + + return(retval); +} /* verifyPath */ + + +static int doMkdir(const char *_dname, char *dname) +{ + DirHandle *h; + char *start; + char *end; + int retval = 0; + int exists = 1; /* force existance check on first path element. */ + + BAIL_IF_MACRO(!sanitizePlatformIndependentPath(_dname, dname), NULL, 0); + + __PHYSFS_platformGrabMutex(stateLock); + BAIL_IF_MACRO_MUTEX(writeDir == NULL, ERR_NO_WRITE_DIR, stateLock, 0); + h = writeDir; + BAIL_IF_MACRO_MUTEX(!verifyPath(h, &dname, 1), NULL, stateLock, 0); + + start = dname; + while (1) + { + end = strchr(start, '/'); + if (end != NULL) + *end = '\0'; + + /* only check for existance if all parent dirs existed, too... */ + if (exists) + retval = h->funcs->isDirectory(h->opaque, dname, &exists); + + if (!exists) + retval = h->funcs->mkdir(h->opaque, dname); + + if (!retval) + break; + + if (end == NULL) + break; + + *end = '/'; + start = end + 1; + } /* while */ + + __PHYSFS_platformReleaseMutex(stateLock); + return(retval); +} /* doMkdir */ + + +int PHYSFS_mkdir(const char *_dname) +{ + int retval = 0; + char *dname; + size_t len; + + BAIL_IF_MACRO(_dname == NULL, ERR_INVALID_ARGUMENT, 0); + len = strlen(_dname) + 1; + dname = (char *) __PHYSFS_smallAlloc(len); + BAIL_IF_MACRO(dname == NULL, ERR_OUT_OF_MEMORY, 0); + retval = doMkdir(_dname, dname); + __PHYSFS_smallFree(dname); + return(retval); +} /* PHYSFS_mkdir */ + + +static int doDelete(const char *_fname, char *fname) +{ + int retval; + DirHandle *h; + BAIL_IF_MACRO(!sanitizePlatformIndependentPath(_fname, fname), NULL, 0); + + __PHYSFS_platformGrabMutex(stateLock); + + BAIL_IF_MACRO_MUTEX(writeDir == NULL, ERR_NO_WRITE_DIR, stateLock, 0); + h = writeDir; + BAIL_IF_MACRO_MUTEX(!verifyPath(h, &fname, 0), NULL, stateLock, 0); + retval = h->funcs->remove(h->opaque, fname); + + __PHYSFS_platformReleaseMutex(stateLock); + return(retval); +} /* doDelete */ + + +int PHYSFS_delete(const char *_fname) +{ + int retval; + char *fname; + size_t len; + + BAIL_IF_MACRO(_fname == NULL, ERR_INVALID_ARGUMENT, 0); + len = strlen(_fname) + 1; + fname = (char *) __PHYSFS_smallAlloc(len); + BAIL_IF_MACRO(fname == NULL, ERR_OUT_OF_MEMORY, 0); + retval = doDelete(_fname, fname); + __PHYSFS_smallFree(fname); + return(retval); +} /* PHYSFS_delete */ + + +const char *PHYSFS_getRealDir(const char *_fname) +{ + const char *retval = NULL; + char *fname = NULL; + size_t len; + + BAIL_IF_MACRO(_fname == NULL, ERR_INVALID_ARGUMENT, NULL); + len = strlen(_fname) + 1; + fname = __PHYSFS_smallAlloc(len); + BAIL_IF_MACRO(fname == NULL, ERR_OUT_OF_MEMORY, NULL); + if (sanitizePlatformIndependentPath(_fname, fname)) + { + DirHandle *i; + __PHYSFS_platformGrabMutex(stateLock); + for (i = searchPath; ((i != NULL) && (retval == NULL)); i = i->next) + { + char *arcfname = fname; + if (partOfMountPoint(i, arcfname)) + retval = i->dirName; + else if (verifyPath(i, &arcfname, 0)) + { + if (i->funcs->exists(i->opaque, arcfname)) + retval = i->dirName; + } /* if */ + } /* for */ + __PHYSFS_platformReleaseMutex(stateLock); + } /* if */ + + __PHYSFS_smallFree(fname); + return(retval); +} /* PHYSFS_getRealDir */ + + +static int locateInStringList(const char *str, + char **list, + PHYSFS_uint32 *pos) +{ + PHYSFS_uint32 len = *pos; + PHYSFS_uint32 half_len; + PHYSFS_uint32 lo = 0; + PHYSFS_uint32 middle; + int cmp; + + while (len > 0) + { + half_len = len >> 1; + middle = lo + half_len; + cmp = strcmp(list[middle], str); + + if (cmp == 0) /* it's in the list already. */ + return(1); + else if (cmp > 0) + len = half_len; + else + { + lo = middle + 1; + len -= half_len + 1; + } /* else */ + } /* while */ + + *pos = lo; + return(0); +} /* locateInStringList */ + + +static void enumFilesCallback(void *data, const char *origdir, const char *str) +{ + PHYSFS_uint32 pos; + void *ptr; + char *newstr; + EnumStringListCallbackData *pecd = (EnumStringListCallbackData *) data; + + /* + * See if file is in the list already, and if not, insert it in there + * alphabetically... + */ + pos = pecd->size; + if (locateInStringList(str, pecd->list, &pos)) + return; /* already in the list. */ + + ptr = allocator.Realloc(pecd->list, (pecd->size + 2) * sizeof (char *)); + newstr = (char *) allocator.Malloc(strlen(str) + 1); + if (ptr != NULL) + pecd->list = (char **) ptr; + + if ((ptr == NULL) || (newstr == NULL)) + return; /* better luck next time. */ + + strcpy(newstr, str); + + if (pos != pecd->size) + { + memmove(&pecd->list[pos+1], &pecd->list[pos], + sizeof (char *) * ((pecd->size) - pos)); + } /* if */ + + pecd->list[pos] = newstr; + pecd->size++; +} /* enumFilesCallback */ + + +char **PHYSFS_enumerateFiles(const char *path) +{ + EnumStringListCallbackData ecd; + memset(&ecd, '\0', sizeof (ecd)); + ecd.list = (char **) allocator.Malloc(sizeof (char *)); + BAIL_IF_MACRO(ecd.list == NULL, ERR_OUT_OF_MEMORY, NULL); + PHYSFS_enumerateFilesCallback(path, enumFilesCallback, &ecd); + ecd.list[ecd.size] = NULL; + return(ecd.list); +} /* PHYSFS_enumerateFiles */ + + +/* + * Broke out to seperate function so we can use stack allocation gratuitously. + */ +static void enumerateFromMountPoint(DirHandle *i, const char *arcfname, + PHYSFS_EnumFilesCallback callback, + const char *_fname, void *data) +{ + const size_t len = strlen(arcfname); + char *ptr = NULL; + char *end = NULL; + const size_t slen = strlen(i->mountPoint) + 1; + char *mountPoint = (char *) __PHYSFS_smallAlloc(slen); + + if (mountPoint == NULL) + return; /* oh well. */ + + strcpy(mountPoint, i->mountPoint); + ptr = mountPoint + ((len) ? len + 1 : 0); + end = strchr(ptr, '/'); + assert(end); /* should always find a terminating '/'. */ + *end = '\0'; + callback(data, _fname, ptr); + __PHYSFS_smallFree(mountPoint); +} /* enumerateFromMountPoint */ + + +/* !!! FIXME: this should report error conditions. */ +void PHYSFS_enumerateFilesCallback(const char *_fname, + PHYSFS_EnumFilesCallback callback, + void *data) +{ + size_t len; + char *fname; + + BAIL_IF_MACRO(_fname == NULL, ERR_INVALID_ARGUMENT, ) /*0*/; + BAIL_IF_MACRO(callback == NULL, ERR_INVALID_ARGUMENT, ) /*0*/; + + len = strlen(_fname) + 1; + fname = (char *) __PHYSFS_smallAlloc(len); + BAIL_IF_MACRO(fname == NULL, ERR_OUT_OF_MEMORY, ) /*0*/; + + if (sanitizePlatformIndependentPath(_fname, fname)) + { + DirHandle *i; + int noSyms; + + __PHYSFS_platformGrabMutex(stateLock); + noSyms = !allowSymLinks; + for (i = searchPath; i != NULL; i = i->next) + { + char *arcfname = fname; + if (partOfMountPoint(i, arcfname)) + enumerateFromMountPoint(i, arcfname, callback, _fname, data); + + else if (verifyPath(i, &arcfname, 0)) + { + i->funcs->enumerateFiles(i->opaque, arcfname, noSyms, + callback, _fname, data); + } /* else if */ + } /* for */ + __PHYSFS_platformReleaseMutex(stateLock); + } /* if */ + + __PHYSFS_smallFree(fname); +} /* PHYSFS_enumerateFilesCallback */ + + +int PHYSFS_exists(const char *fname) +{ + return(PHYSFS_getRealDir(fname) != NULL); +} /* PHYSFS_exists */ + + +PHYSFS_sint64 PHYSFS_getLastModTime(const char *_fname) +{ + PHYSFS_sint64 retval = -1; + char *fname; + size_t len; + + BAIL_IF_MACRO(_fname == NULL, ERR_INVALID_ARGUMENT, -1); + len = strlen(_fname) + 1; + fname = (char *) __PHYSFS_smallAlloc(len); + BAIL_IF_MACRO(fname == NULL, ERR_OUT_OF_MEMORY, -1); + + if (sanitizePlatformIndependentPath(_fname, fname)) + { + if (*fname == '\0') /* eh...punt if it's the root dir. */ + retval = 1; /* !!! FIXME: Maybe this should be an error? */ + else + { + DirHandle *i; + int exists = 0; + __PHYSFS_platformGrabMutex(stateLock); + for (i = searchPath; ((i != NULL) && (!exists)); i = i->next) + { + char *arcfname = fname; + exists = partOfMountPoint(i, arcfname); + if (exists) + retval = 1; /* !!! FIXME: What's the right value? */ + else if (verifyPath(i, &arcfname, 0)) + { + retval = i->funcs->getLastModTime(i->opaque, arcfname, + &exists); + } /* else if */ + } /* for */ + __PHYSFS_platformReleaseMutex(stateLock); + } /* else */ + } /* if */ + + __PHYSFS_smallFree(fname); + return(retval); +} /* PHYSFS_getLastModTime */ + + +int PHYSFS_isDirectory(const char *_fname) +{ + int retval = 0; + size_t len; + char *fname; + + BAIL_IF_MACRO(_fname == NULL, ERR_INVALID_ARGUMENT, 0); + len = strlen(_fname) + 1; + fname = (char *) __PHYSFS_smallAlloc(len); + BAIL_IF_MACRO(fname == NULL, ERR_OUT_OF_MEMORY, 0); + + if (!sanitizePlatformIndependentPath(_fname, fname)) + retval = 0; + + else if (*fname == '\0') + retval = 1; /* Root is always a dir. :) */ + + else + { + DirHandle *i; + int exists = 0; + + __PHYSFS_platformGrabMutex(stateLock); + for (i = searchPath; ((i != NULL) && (!exists)); i = i->next) + { + char *arcfname = fname; + if ((exists = partOfMountPoint(i, arcfname)) != 0) + retval = 1; + else if (verifyPath(i, &arcfname, 0)) + retval = i->funcs->isDirectory(i->opaque, arcfname, &exists); + } /* for */ + __PHYSFS_platformReleaseMutex(stateLock); + } /* else */ + + __PHYSFS_smallFree(fname); + return(retval); +} /* PHYSFS_isDirectory */ + + +int PHYSFS_isSymbolicLink(const char *_fname) +{ + int retval = 0; + size_t len; + char *fname; + + BAIL_IF_MACRO(!allowSymLinks, ERR_SYMLINK_DISALLOWED, 0); + + BAIL_IF_MACRO(_fname == NULL, ERR_INVALID_ARGUMENT, 0); + len = strlen(_fname) + 1; + fname = (char *) __PHYSFS_smallAlloc(len); + BAIL_IF_MACRO(fname == NULL, ERR_OUT_OF_MEMORY, 0); + + if (!sanitizePlatformIndependentPath(_fname, fname)) + retval = 0; + + else if (*fname == '\0') + retval = 1; /* Root is never a symlink. */ + + else + { + DirHandle *i; + int fileExists = 0; + + __PHYSFS_platformGrabMutex(stateLock); + for (i = searchPath; ((i != NULL) && (!fileExists)); i = i->next) + { + char *arcfname = fname; + if ((fileExists = partOfMountPoint(i, arcfname)) != 0) + retval = 0; /* virtual dir...not a symlink. */ + else if (verifyPath(i, &arcfname, 0)) + retval = i->funcs->isSymLink(i->opaque, arcfname, &fileExists); + } /* for */ + __PHYSFS_platformReleaseMutex(stateLock); + } /* else */ + + __PHYSFS_smallFree(fname); + return(retval); +} /* PHYSFS_isSymbolicLink */ + + +static PHYSFS_File *doOpenWrite(const char *_fname, int appending) +{ + FileHandle *fh = NULL; + size_t len; + char *fname; + + BAIL_IF_MACRO(_fname == NULL, ERR_INVALID_ARGUMENT, 0); + len = strlen(_fname) + 1; + fname = (char *) __PHYSFS_smallAlloc(len); + BAIL_IF_MACRO(fname == NULL, ERR_OUT_OF_MEMORY, 0); + + if (sanitizePlatformIndependentPath(_fname, fname)) + { + void *opaque = NULL; + DirHandle *h = NULL; + const PHYSFS_Archiver *f; + + __PHYSFS_platformGrabMutex(stateLock); + + GOTO_IF_MACRO(!writeDir, ERR_NO_WRITE_DIR, doOpenWriteEnd); + + h = writeDir; + GOTO_IF_MACRO(!verifyPath(h, &fname, 0), NULL, doOpenWriteEnd); + + f = h->funcs; + if (appending) + opaque = f->openAppend(h->opaque, fname); + else + opaque = f->openWrite(h->opaque, fname); + + GOTO_IF_MACRO(opaque == NULL, NULL, doOpenWriteEnd); + + fh = (FileHandle *) allocator.Malloc(sizeof (FileHandle)); + if (fh == NULL) + { + f->fileClose(opaque); + GOTO_MACRO(ERR_OUT_OF_MEMORY, doOpenWriteEnd); + } /* if */ + else + { + memset(fh, '\0', sizeof (FileHandle)); + fh->opaque = opaque; + fh->dirHandle = h; + fh->funcs = h->funcs; + fh->next = openWriteList; + openWriteList = fh; + } /* else */ + + doOpenWriteEnd: + __PHYSFS_platformReleaseMutex(stateLock); + } /* if */ + + __PHYSFS_smallFree(fname); + return((PHYSFS_File *) fh); +} /* doOpenWrite */ + + +PHYSFS_File *PHYSFS_openWrite(const char *filename) +{ + return(doOpenWrite(filename, 0)); +} /* PHYSFS_openWrite */ + + +PHYSFS_File *PHYSFS_openAppend(const char *filename) +{ + return(doOpenWrite(filename, 1)); +} /* PHYSFS_openAppend */ + + +PHYSFS_File *PHYSFS_openRead(const char *_fname) +{ + FileHandle *fh = NULL; + char *fname; + size_t len; + + BAIL_IF_MACRO(_fname == NULL, ERR_INVALID_ARGUMENT, 0); + len = strlen(_fname) + 1; + fname = (char *) __PHYSFS_smallAlloc(len); + BAIL_IF_MACRO(fname == NULL, ERR_OUT_OF_MEMORY, 0); + + if (sanitizePlatformIndependentPath(_fname, fname)) + { + int fileExists = 0; + DirHandle *i = NULL; + fvoid *opaque = NULL; + + __PHYSFS_platformGrabMutex(stateLock); + + GOTO_IF_MACRO(!searchPath, ERR_NO_SUCH_PATH, openReadEnd); + + /* !!! FIXME: Why aren't we using a for loop here? */ + i = searchPath; + + do + { + char *arcfname = fname; + if (verifyPath(i, &arcfname, 0)) + { + opaque = i->funcs->openRead(i->opaque, arcfname, &fileExists); + if (opaque) + break; + } /* if */ + i = i->next; + } while ((i != NULL) && (!fileExists)); + + /* !!! FIXME: may not set an error if openRead didn't fail. */ + GOTO_IF_MACRO(opaque == NULL, NULL, openReadEnd); + + fh = (FileHandle *) allocator.Malloc(sizeof (FileHandle)); + if (fh == NULL) + { + i->funcs->fileClose(opaque); + GOTO_MACRO(ERR_OUT_OF_MEMORY, openReadEnd); + } /* if */ + + memset(fh, '\0', sizeof (FileHandle)); + fh->opaque = opaque; + fh->forReading = 1; + fh->dirHandle = i; + fh->funcs = i->funcs; + fh->next = openReadList; + openReadList = fh; + + openReadEnd: + __PHYSFS_platformReleaseMutex(stateLock); + } /* if */ + + __PHYSFS_smallFree(fname); + return((PHYSFS_File *) fh); +} /* PHYSFS_openRead */ + + +static int closeHandleInOpenList(FileHandle **list, FileHandle *handle) +{ + FileHandle *prev = NULL; + FileHandle *i; + int rc = 1; + + for (i = *list; i != NULL; i = i->next) + { + if (i == handle) /* handle is in this list? */ + { + PHYSFS_uint8 *tmp = handle->buffer; + rc = PHYSFS_flush((PHYSFS_File *) handle); + if (rc) + rc = handle->funcs->fileClose(handle->opaque); + if (!rc) + return(-1); + + if (tmp != NULL) /* free any associated buffer. */ + allocator.Free(tmp); + + if (prev == NULL) + *list = handle->next; + else + prev->next = handle->next; + + allocator.Free(handle); + return(1); + } /* if */ + prev = i; + } /* for */ + + return(0); +} /* closeHandleInOpenList */ + + +int PHYSFS_close(PHYSFS_File *_handle) +{ + FileHandle *handle = (FileHandle *) _handle; + int rc; + + __PHYSFS_platformGrabMutex(stateLock); + + /* -1 == close failure. 0 == not found. 1 == success. */ + rc = closeHandleInOpenList(&openReadList, handle); + BAIL_IF_MACRO_MUTEX(rc == -1, NULL, stateLock, 0); + if (!rc) + { + rc = closeHandleInOpenList(&openWriteList, handle); + BAIL_IF_MACRO_MUTEX(rc == -1, NULL, stateLock, 0); + } /* if */ + + __PHYSFS_platformReleaseMutex(stateLock); + BAIL_IF_MACRO(!rc, ERR_NOT_A_HANDLE, 0); + return(1); +} /* PHYSFS_close */ + + +static PHYSFS_sint64 doBufferedRead(FileHandle *fh, void *buffer, + PHYSFS_uint32 objSize, + PHYSFS_uint32 objCount) +{ + PHYSFS_sint64 retval = 0; + PHYSFS_uint32 remainder = 0; + + while (objCount > 0) + { + PHYSFS_uint32 buffered = fh->buffill - fh->bufpos; + PHYSFS_uint64 mustread = (objSize * objCount) - remainder; + PHYSFS_uint32 copied; + + if (buffered == 0) /* need to refill buffer? */ + { + PHYSFS_sint64 rc = fh->funcs->read(fh->opaque, fh->buffer, + 1, fh->bufsize); + if (rc <= 0) + { + fh->bufpos -= remainder; + return(((rc == -1) && (retval == 0)) ? -1 : retval); + } /* if */ + + buffered = fh->buffill = (PHYSFS_uint32) rc; + fh->bufpos = 0; + } /* if */ + + if (buffered > mustread) + buffered = (PHYSFS_uint32) mustread; + + memcpy(buffer, fh->buffer + fh->bufpos, (size_t) buffered); + buffer = ((PHYSFS_uint8 *) buffer) + buffered; + fh->bufpos += buffered; + buffered += remainder; /* take remainder into account. */ + copied = (buffered / objSize); + remainder = (buffered % objSize); + retval += copied; + objCount -= copied; + } /* while */ + + return(retval); +} /* doBufferedRead */ + + +PHYSFS_sint64 PHYSFS_read(PHYSFS_File *handle, void *buffer, + PHYSFS_uint32 objSize, PHYSFS_uint32 objCount) +{ + FileHandle *fh = (FileHandle *) handle; + + BAIL_IF_MACRO(!fh->forReading, ERR_FILE_ALREADY_OPEN_W, -1); + BAIL_IF_MACRO(objSize == 0, NULL, 0); + BAIL_IF_MACRO(objCount == 0, NULL, 0); + if (fh->buffer != NULL) + return(doBufferedRead(fh, buffer, objSize, objCount)); + + return(fh->funcs->read(fh->opaque, buffer, objSize, objCount)); +} /* PHYSFS_read */ + + +static PHYSFS_sint64 doBufferedWrite(PHYSFS_File *handle, const void *buffer, + PHYSFS_uint32 objSize, + PHYSFS_uint32 objCount) +{ + FileHandle *fh = (FileHandle *) handle; + + /* whole thing fits in the buffer? */ + if (fh->buffill + (objSize * objCount) < fh->bufsize) + { + memcpy(fh->buffer + fh->buffill, buffer, objSize * objCount); + fh->buffill += (objSize * objCount); + return(objCount); + } /* if */ + + /* would overflow buffer. Flush and then write the new objects, too. */ + BAIL_IF_MACRO(!PHYSFS_flush(handle), NULL, -1); + return(fh->funcs->write(fh->opaque, buffer, objSize, objCount)); +} /* doBufferedWrite */ + + +PHYSFS_sint64 PHYSFS_write(PHYSFS_File *handle, const void *buffer, + PHYSFS_uint32 objSize, PHYSFS_uint32 objCount) +{ + FileHandle *fh = (FileHandle *) handle; + + BAIL_IF_MACRO(fh->forReading, ERR_FILE_ALREADY_OPEN_R, -1); + BAIL_IF_MACRO(objSize == 0, NULL, 0); + BAIL_IF_MACRO(objCount == 0, NULL, 0); + if (fh->buffer != NULL) + return(doBufferedWrite(handle, buffer, objSize, objCount)); + + return(fh->funcs->write(fh->opaque, buffer, objSize, objCount)); +} /* PHYSFS_write */ + + +int PHYSFS_eof(PHYSFS_File *handle) +{ + FileHandle *fh = (FileHandle *) handle; + + if (!fh->forReading) /* never EOF on files opened for write/append. */ + return(0); + + /* eof if buffer is empty and archiver says so. */ + return((fh->bufpos == fh->buffill) && (fh->funcs->eof(fh->opaque))); +} /* PHYSFS_eof */ + + +PHYSFS_sint64 PHYSFS_tell(PHYSFS_File *handle) +{ + FileHandle *fh = (FileHandle *) handle; + PHYSFS_sint64 pos = fh->funcs->tell(fh->opaque); + PHYSFS_sint64 retval = fh->forReading ? + (pos - fh->buffill) + fh->bufpos : + (pos + fh->buffill); + return(retval); +} /* PHYSFS_tell */ + + +int PHYSFS_seek(PHYSFS_File *handle, PHYSFS_uint64 pos) +{ + FileHandle *fh = (FileHandle *) handle; + BAIL_IF_MACRO(!PHYSFS_flush(handle), NULL, 0); + + if (fh->buffer && fh->forReading) + { + /* avoid throwing away our precious buffer if seeking within it. */ + PHYSFS_sint64 offset = pos - PHYSFS_tell(handle); + if ( /* seeking within the already-buffered range? */ + ((offset >= 0) && (offset <= fh->buffill - fh->bufpos)) /* fwd */ + || ((offset < 0) && (-offset <= fh->bufpos)) /* backward */ ) + { + fh->bufpos += (PHYSFS_uint32) offset; + return(1); /* successful seek */ + } /* if */ + } /* if */ + + /* we have to fall back to a 'raw' seek. */ + fh->buffill = fh->bufpos = 0; + return(fh->funcs->seek(fh->opaque, pos)); +} /* PHYSFS_seek */ + + +PHYSFS_sint64 PHYSFS_fileLength(PHYSFS_File *handle) +{ + FileHandle *fh = (FileHandle *) handle; + return(fh->funcs->fileLength(fh->opaque)); +} /* PHYSFS_filelength */ + + +int PHYSFS_setBuffer(PHYSFS_File *handle, PHYSFS_uint64 _bufsize) +{ + FileHandle *fh = (FileHandle *) handle; + PHYSFS_uint32 bufsize; + + /* !!! FIXME: Unlocalized string. */ + BAIL_IF_MACRO(_bufsize > 0xFFFFFFFF, "buffer must fit in 32-bits", 0); + bufsize = (PHYSFS_uint32) _bufsize; + + BAIL_IF_MACRO(!PHYSFS_flush(handle), NULL, 0); + + /* + * For reads, we need to move the file pointer to where it would be + * if we weren't buffering, so that the next read will get the + * right chunk of stuff from the file. PHYSFS_flush() handles writes. + */ + if ((fh->forReading) && (fh->buffill != fh->bufpos)) + { + PHYSFS_uint64 pos; + PHYSFS_sint64 curpos = fh->funcs->tell(fh->opaque); + BAIL_IF_MACRO(curpos == -1, NULL, 0); + pos = ((curpos - fh->buffill) + fh->bufpos); + BAIL_IF_MACRO(!fh->funcs->seek(fh->opaque, pos), NULL, 0); + } /* if */ + + if (bufsize == 0) /* delete existing buffer. */ + { + if (fh->buffer != NULL) + { + allocator.Free(fh->buffer); + fh->buffer = NULL; + } /* if */ + } /* if */ + + else + { + PHYSFS_uint8 *newbuf; + newbuf = (PHYSFS_uint8 *) allocator.Realloc(fh->buffer, bufsize); + BAIL_IF_MACRO(newbuf == NULL, ERR_OUT_OF_MEMORY, 0); + fh->buffer = newbuf; + } /* else */ + + fh->bufsize = bufsize; + fh->buffill = fh->bufpos = 0; + return(1); +} /* PHYSFS_setBuffer */ + + +int PHYSFS_flush(PHYSFS_File *handle) +{ + FileHandle *fh = (FileHandle *) handle; + PHYSFS_sint64 rc; + + if ((fh->forReading) || (fh->bufpos == fh->buffill)) + return(1); /* open for read or buffer empty are successful no-ops. */ + + /* dump buffer to disk. */ + rc = fh->funcs->write(fh->opaque, fh->buffer + fh->bufpos, + fh->buffill - fh->bufpos, 1); + BAIL_IF_MACRO(rc <= 0, NULL, 0); + fh->bufpos = fh->buffill = 0; + return(1); +} /* PHYSFS_flush */ + + +int PHYSFS_setAllocator(const PHYSFS_Allocator *a) +{ + BAIL_IF_MACRO(initialized, ERR_IS_INITIALIZED, 0); + externalAllocator = (a != NULL); + if (externalAllocator) + memcpy(&allocator, a, sizeof (PHYSFS_Allocator)); + + return(1); +} /* PHYSFS_setAllocator */ + + +static void *mallocAllocatorMalloc(PHYSFS_uint64 s) +{ + BAIL_IF_MACRO(__PHYSFS_ui64FitsAddressSpace(s), ERR_OUT_OF_MEMORY, NULL); + #undef malloc + return(malloc((size_t) s)); +} /* mallocAllocatorMalloc */ + + +static void *mallocAllocatorRealloc(void *ptr, PHYSFS_uint64 s) +{ + BAIL_IF_MACRO(__PHYSFS_ui64FitsAddressSpace(s), ERR_OUT_OF_MEMORY, NULL); + #undef realloc + return(realloc(ptr, (size_t) s)); +} /* mallocAllocatorRealloc */ + + +static void mallocAllocatorFree(void *ptr) +{ + #undef free + free(ptr); +} /* mallocAllocatorFree */ + + +static void setDefaultAllocator(void) +{ + assert(!externalAllocator); + if (!__PHYSFS_platformSetDefaultAllocator(&allocator)) + { + allocator.Init = NULL; + allocator.Deinit = NULL; + allocator.Malloc = mallocAllocatorMalloc; + allocator.Realloc = mallocAllocatorRealloc; + allocator.Free = mallocAllocatorFree; + } /* if */ +} /* setDefaultAllocator */ + + +void *__PHYSFS_initSmallAlloc(void *ptr, PHYSFS_uint64 len) +{ + const char useHeap = ((ptr == NULL) ? 1 : 0); + if (useHeap) /* too large for stack allocation or alloca() failed. */ + ptr = allocator.Malloc(len+1); + + if (ptr != NULL) + { + char *retval = (char *) ptr; + /*printf("%s alloc'd (%d) bytes at (%p).\n", + useHeap ? "heap" : "stack", (int) len, ptr);*/ + *retval = useHeap; + return(retval+1); + } /* if */ + + return(NULL); /* allocation failed. */ +} /* __PHYSFS_initSmallAlloc */ + + +void __PHYSFS_smallFree(void *ptr) +{ + if (ptr != NULL) + { + char *block = ((char *) ptr) - 1; + const char useHeap = *block; + if (useHeap) + allocator.Free(block); + /*printf("%s free'd (%p).\n", useHeap ? "heap" : "stack", block);*/ + } /* if */ +} /* __PHYSFS_smallFree */ + +/* end of physfs.c ... */ + diff --git a/project/jni/physfs/physfs_byteorder.c b/project/jni/physfs/physfs_byteorder.c new file mode 100644 index 000000000..ee3688b37 --- /dev/null +++ b/project/jni/physfs/physfs_byteorder.c @@ -0,0 +1,312 @@ +/** + * 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. + */ + +#include +#include + +#define __PHYSICSFS_INTERNAL__ +#include "physfs_internal.h" + +#if (defined macintosh) && !(defined __MWERKS__) +#define __inline__ +#endif + +#if (defined _MSC_VER) +#define __inline__ __inline +#endif + +#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 + + +int PHYSFS_readSLE16(PHYSFS_File *file, PHYSFS_sint16 *val) +{ + PHYSFS_sint16 in; + BAIL_IF_MACRO(val == NULL, ERR_INVALID_ARGUMENT, 0); + BAIL_IF_MACRO(PHYSFS_read(file, &in, sizeof (in), 1) != 1, NULL, 0); + *val = PHYSFS_swapSLE16(in); + return(1); +} /* PHYSFS_readSLE16 */ + + +int PHYSFS_readULE16(PHYSFS_File *file, PHYSFS_uint16 *val) +{ + PHYSFS_uint16 in; + BAIL_IF_MACRO(val == NULL, ERR_INVALID_ARGUMENT, 0); + BAIL_IF_MACRO(PHYSFS_read(file, &in, sizeof (in), 1) != 1, NULL, 0); + *val = PHYSFS_swapULE16(in); + return(1); +} /* PHYSFS_readULE16 */ + + +int PHYSFS_readSBE16(PHYSFS_File *file, PHYSFS_sint16 *val) +{ + PHYSFS_sint16 in; + BAIL_IF_MACRO(val == NULL, ERR_INVALID_ARGUMENT, 0); + BAIL_IF_MACRO(PHYSFS_read(file, &in, sizeof (in), 1) != 1, NULL, 0); + *val = PHYSFS_swapSBE16(in); + return(1); +} /* PHYSFS_readSBE16 */ + + +int PHYSFS_readUBE16(PHYSFS_File *file, PHYSFS_uint16 *val) +{ + PHYSFS_uint16 in; + BAIL_IF_MACRO(val == NULL, ERR_INVALID_ARGUMENT, 0); + BAIL_IF_MACRO(PHYSFS_read(file, &in, sizeof (in), 1) != 1, NULL, 0); + *val = PHYSFS_swapUBE16(in); + return(1); +} /* PHYSFS_readUBE16 */ + + +int PHYSFS_readSLE32(PHYSFS_File *file, PHYSFS_sint32 *val) +{ + PHYSFS_sint32 in; + BAIL_IF_MACRO(val == NULL, ERR_INVALID_ARGUMENT, 0); + BAIL_IF_MACRO(PHYSFS_read(file, &in, sizeof (in), 1) != 1, NULL, 0); + *val = PHYSFS_swapSLE32(in); + return(1); +} /* PHYSFS_readSLE32 */ + + +int PHYSFS_readULE32(PHYSFS_File *file, PHYSFS_uint32 *val) +{ + PHYSFS_uint32 in; + BAIL_IF_MACRO(val == NULL, ERR_INVALID_ARGUMENT, 0); + BAIL_IF_MACRO(PHYSFS_read(file, &in, sizeof (in), 1) != 1, NULL, 0); + *val = PHYSFS_swapULE32(in); + return(1); +} /* PHYSFS_readULE32 */ + + +int PHYSFS_readSBE32(PHYSFS_File *file, PHYSFS_sint32 *val) +{ + PHYSFS_sint32 in; + BAIL_IF_MACRO(val == NULL, ERR_INVALID_ARGUMENT, 0); + BAIL_IF_MACRO(PHYSFS_read(file, &in, sizeof (in), 1) != 1, NULL, 0); + *val = PHYSFS_swapSBE32(in); + return(1); +} /* PHYSFS_readSBE32 */ + + +int PHYSFS_readUBE32(PHYSFS_File *file, PHYSFS_uint32 *val) +{ + PHYSFS_uint32 in; + BAIL_IF_MACRO(val == NULL, ERR_INVALID_ARGUMENT, 0); + BAIL_IF_MACRO(PHYSFS_read(file, &in, sizeof (in), 1) != 1, NULL, 0); + *val = PHYSFS_swapUBE32(in); + return(1); +} /* PHYSFS_readUBE32 */ + + +int PHYSFS_readSLE64(PHYSFS_File *file, PHYSFS_sint64 *val) +{ + PHYSFS_sint64 in; + BAIL_IF_MACRO(val == NULL, ERR_INVALID_ARGUMENT, 0); + BAIL_IF_MACRO(PHYSFS_read(file, &in, sizeof (in), 1) != 1, NULL, 0); + *val = PHYSFS_swapSLE64(in); + return(1); +} /* PHYSFS_readSLE64 */ + + +int PHYSFS_readULE64(PHYSFS_File *file, PHYSFS_uint64 *val) +{ + PHYSFS_uint64 in; + BAIL_IF_MACRO(val == NULL, ERR_INVALID_ARGUMENT, 0); + BAIL_IF_MACRO(PHYSFS_read(file, &in, sizeof (in), 1) != 1, NULL, 0); + *val = PHYSFS_swapULE64(in); + return(1); +} /* PHYSFS_readULE64 */ + + +int PHYSFS_readSBE64(PHYSFS_File *file, PHYSFS_sint64 *val) +{ + PHYSFS_sint64 in; + BAIL_IF_MACRO(val == NULL, ERR_INVALID_ARGUMENT, 0); + BAIL_IF_MACRO(PHYSFS_read(file, &in, sizeof (in), 1) != 1, NULL, 0); + *val = PHYSFS_swapSBE64(in); + return(1); +} /* PHYSFS_readSBE64 */ + + +int PHYSFS_readUBE64(PHYSFS_File *file, PHYSFS_uint64 *val) +{ + PHYSFS_uint64 in; + BAIL_IF_MACRO(val == NULL, ERR_INVALID_ARGUMENT, 0); + BAIL_IF_MACRO(PHYSFS_read(file, &in, sizeof (in), 1) != 1, NULL, 0); + *val = PHYSFS_swapUBE64(in); + return(1); +} /* PHYSFS_readUBE64 */ + + + +int PHYSFS_writeSLE16(PHYSFS_File *file, PHYSFS_sint16 val) +{ + PHYSFS_sint16 out = PHYSFS_swapSLE16(val); + BAIL_IF_MACRO(PHYSFS_write(file, &out, sizeof (out), 1) != 1, NULL, 0); + return(1); +} /* PHYSFS_writeSLE16 */ + + +int PHYSFS_writeULE16(PHYSFS_File *file, PHYSFS_uint16 val) +{ + PHYSFS_uint16 out = PHYSFS_swapULE16(val); + BAIL_IF_MACRO(PHYSFS_write(file, &out, sizeof (out), 1) != 1, NULL, 0); + return(1); +} /* PHYSFS_writeULE16 */ + + +int PHYSFS_writeSBE16(PHYSFS_File *file, PHYSFS_sint16 val) +{ + PHYSFS_sint16 out = PHYSFS_swapSBE16(val); + BAIL_IF_MACRO(PHYSFS_write(file, &out, sizeof (out), 1) != 1, NULL, 0); + return(1); +} /* PHYSFS_writeSBE16 */ + + +int PHYSFS_writeUBE16(PHYSFS_File *file, PHYSFS_uint16 val) +{ + PHYSFS_uint16 out = PHYSFS_swapUBE16(val); + BAIL_IF_MACRO(PHYSFS_write(file, &out, sizeof (out), 1) != 1, NULL, 0); + return(1); +} /* PHYSFS_writeUBE16 */ + + +int PHYSFS_writeSLE32(PHYSFS_File *file, PHYSFS_sint32 val) +{ + PHYSFS_sint32 out = PHYSFS_swapSLE32(val); + BAIL_IF_MACRO(PHYSFS_write(file, &out, sizeof (out), 1) != 1, NULL, 0); + return(1); +} /* PHYSFS_writeSLE32 */ + + +int PHYSFS_writeULE32(PHYSFS_File *file, PHYSFS_uint32 val) +{ + PHYSFS_uint32 out = PHYSFS_swapULE32(val); + BAIL_IF_MACRO(PHYSFS_write(file, &out, sizeof (out), 1) != 1, NULL, 0); + return(1); +} /* PHYSFS_writeULE32 */ + + +int PHYSFS_writeSBE32(PHYSFS_File *file, PHYSFS_sint32 val) +{ + PHYSFS_sint32 out = PHYSFS_swapSBE32(val); + BAIL_IF_MACRO(PHYSFS_write(file, &out, sizeof (out), 1) != 1, NULL, 0); + return(1); +} /* PHYSFS_writeSBE32 */ + + +int PHYSFS_writeUBE32(PHYSFS_File *file, PHYSFS_uint32 val) +{ + PHYSFS_uint32 out = PHYSFS_swapUBE32(val); + BAIL_IF_MACRO(PHYSFS_write(file, &out, sizeof (out), 1) != 1, NULL, 0); + return(1); +} /* PHYSFS_writeUBE32 */ + + +int PHYSFS_writeSLE64(PHYSFS_File *file, PHYSFS_sint64 val) +{ + PHYSFS_sint64 out = PHYSFS_swapSLE64(val); + BAIL_IF_MACRO(PHYSFS_write(file, &out, sizeof (out), 1) != 1, NULL, 0); + return(1); +} /* PHYSFS_writeSLE64 */ + + +int PHYSFS_writeULE64(PHYSFS_File *file, PHYSFS_uint64 val) +{ + PHYSFS_uint64 out = PHYSFS_swapULE64(val); + BAIL_IF_MACRO(PHYSFS_write(file, &out, sizeof (out), 1) != 1, NULL, 0); + return(1); +} /* PHYSFS_writeULE64 */ + + +int PHYSFS_writeSBE64(PHYSFS_File *file, PHYSFS_sint64 val) +{ + PHYSFS_sint64 out = PHYSFS_swapSBE64(val); + BAIL_IF_MACRO(PHYSFS_write(file, &out, sizeof (out), 1) != 1, NULL, 0); + return(1); +} /* PHYSFS_writeSBE64 */ + + +int PHYSFS_writeUBE64(PHYSFS_File *file, PHYSFS_uint64 val) +{ + PHYSFS_uint64 out = PHYSFS_swapUBE64(val); + BAIL_IF_MACRO(PHYSFS_write(file, &out, sizeof (out), 1) != 1, NULL, 0); + return(1); +} /* PHYSFS_writeUBE64 */ + +/* end of physfs_byteorder.c ... */ + diff --git a/project/jni/physfs/physfs_unicode.c b/project/jni/physfs/physfs_unicode.c new file mode 100644 index 000000000..d9cc73ffc --- /dev/null +++ b/project/jni/physfs/physfs_unicode.c @@ -0,0 +1,460 @@ +#include "physfs.h" + +#define __PHYSICSFS_INTERNAL__ +#include "physfs_internal.h" + + +/* + * From rfc3629, the UTF-8 spec: + * http://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 */ + { + octet -= (128+64); + octet2 = (PHYSFS_uint32) ((PHYSFS_uint8) *(++str)); + if ((octet2 & (128+64)) != 128) /* Format isn't 10xxxxxx? */ + return UNICODE_BOGUS_CHAR_VALUE; + + *_str += 2; /* 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 */ + { + 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 += 3; /* 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 */ + { + 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 += 4; /* 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 */ + { + 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 += 5; /* skip to next possible start of codepoint. */ + return UNICODE_BOGUS_CHAR_VALUE; + } /* else if */ + + else /* six octets */ + { + 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 */ + + +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; + + /* !!! BLUESKY: UTF-16 surrogates? */ + if (cp > 0xFFFF) + cp = UNICODE_BOGUS_CHAR_CODEPOINT; + + *(dst++) = cp; + len -= sizeof (PHYSFS_uint16); + } /* while */ + + *dst = 0; +} /* PHYSFS_utf8ToUcs2 */ + +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_utf8FromUcs4 */ + +/* 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 + + +typedef struct CaseFoldMapping +{ + PHYSFS_uint32 from; + PHYSFS_uint32 to0; + PHYSFS_uint32 to1; + PHYSFS_uint32 to2; +} CaseFoldMapping; + +typedef struct CaseFoldHashBucket +{ + const PHYSFS_uint8 count; + const CaseFoldMapping *list; +} CaseFoldHashBucket; + +#include "physfs_casefolding.h" + +static void locate_case_fold_mapping(const PHYSFS_uint32 from, + PHYSFS_uint32 *to) +{ + PHYSFS_uint32 i; + const PHYSFS_uint8 hashed = ((from ^ (from >> 8)) & 0xFF); + const CaseFoldHashBucket *bucket = &case_fold_hash[hashed]; + const CaseFoldMapping *mapping = bucket->list; + + for (i = 0; i < bucket->count; i++, mapping++) + { + if (mapping->from == from) + { + to[0] = mapping->to0; + to[1] = mapping->to1; + to[2] = mapping->to2; + return; + } /* if */ + } /* for */ + + /* Not found...there's no remapping for this codepoint. */ + to[0] = from; + to[1] = 0; + to[2] = 0; +} /* locate_case_fold_mapping */ + + +static int utf8codepointcmp(const PHYSFS_uint32 cp1, const PHYSFS_uint32 cp2) +{ + PHYSFS_uint32 folded1[3], folded2[3]; + locate_case_fold_mapping(cp1, folded1); + locate_case_fold_mapping(cp2, folded2); + return ( (folded1[0] == folded2[0]) && + (folded1[1] == folded2[1]) && + (folded1[2] == folded2[2]) ); +} /* utf8codepointcmp */ + + +int __PHYSFS_utf8strcasecmp(const char *str1, const char *str2) +{ + while (1) + { + const PHYSFS_uint32 cp1 = utf8codepoint(&str1); + const PHYSFS_uint32 cp2 = utf8codepoint(&str2); + if (!utf8codepointcmp(cp1, cp2)) return 0; + if (cp1 == 0) return 1; + } /* while */ + + return 0; /* shouldn't hit this. */ +} /* __PHYSFS_utf8strcasecmp */ + + +int __PHYSFS_utf8strnicmp(const char *str1, const char *str2, PHYSFS_uint32 n) +{ + while (n > 0) + { + const PHYSFS_uint32 cp1 = utf8codepoint(&str1); + const PHYSFS_uint32 cp2 = utf8codepoint(&str2); + if (!utf8codepointcmp(cp1, cp2)) return 0; + if (cp1 == 0) return 1; + n--; + } /* while */ + + return 1; /* matched to n chars. */ +} /* __PHYSFS_utf8strnicmp */ + + +int __PHYSFS_stricmpASCII(const char *str1, const char *str2) +{ + while (1) + { + const char ch1 = *(str1++); + const char ch2 = *(str2++); + const char cp1 = ((ch1 >= 'A') && (ch1 <= 'Z')) ? (ch1+32) : ch1; + const char cp2 = ((ch2 >= 'A') && (ch2 <= 'Z')) ? (ch2+32) : ch2; + if (cp1 < cp2) + return -1; + else if (cp1 > cp2) + return 1; + else if (cp1 == 0) /* they're both null chars? */ + return 0; + } /* while */ + + return 0; /* shouldn't hit this. */ +} /* __PHYSFS_stricmpASCII */ + + +int __PHYSFS_strnicmpASCII(const char *str1, const char *str2, PHYSFS_uint32 n) +{ + while (n-- > 0) + { + const char ch1 = *(str1++); + const char ch2 = *(str2++); + const char cp1 = ((ch1 >= 'A') && (ch1 <= 'Z')) ? (ch1+32) : ch1; + const char cp2 = ((ch2 >= 'A') && (ch2 <= 'Z')) ? (ch2+32) : ch2; + if (cp1 < cp2) + return -1; + else if (cp1 > cp2) + return 1; + else if (cp1 == 0) /* they're both null chars? */ + return 0; + } /* while */ + + return 0; +} /* __PHYSFS_stricmpASCII */ + + +/* end of physfs_unicode.c ... */ + diff --git a/project/jni/physfs/platform/beos.cpp b/project/jni/physfs/platform/beos.cpp new file mode 100644 index 000000000..414b2729c --- /dev/null +++ b/project/jni/physfs/platform/beos.cpp @@ -0,0 +1,256 @@ +/* + * BeOS 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_BEOS + +#ifdef PHYSFS_PLATFORM_HAIKU +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#else +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#endif + +#include +#include +#include +#include +#include + +#include "physfs_internal.h" + + +int __PHYSFS_platformInit(void) +{ + return(1); /* always succeed. */ +} /* __PHYSFS_platformInit */ + + +int __PHYSFS_platformDeinit(void) +{ + return(1); /* always succeed. */ +} /* __PHYSFS_platformDeinit */ + + +static char *getMountPoint(const char *devname) +{ + 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) + { + //char buf[B_FILE_NAME_LENGTH]; + BDirectory directory; + BEntry entry; + BPath path; + status_t rc; + rc = vol.GetRootDirectory(&directory); + BAIL_IF_MACRO(rc < B_OK, strerror(rc), NULL); + rc = directory.GetEntry(&entry); + BAIL_IF_MACRO(rc < B_OK, strerror(rc), NULL); + rc = entry.GetPath(&path); + BAIL_IF_MACRO(rc < B_OK, strerror(rc), NULL); + const char *str = path.Path(); + BAIL_IF_MACRO(str == NULL, ERR_OS_ERROR, NULL); /* ?! */ + char *retval = (char *) allocator.Malloc(strlen(str) + 1); + BAIL_IF_MACRO(retval == NULL, ERR_OUT_OF_MEMORY, NULL); + strcpy(retval, str); + return(retval); + } /* if */ + } /* while */ + + return(NULL); +} /* getMountPoint */ + + + /* + * This function is lifted from Simple Directmedia Layer (SDL): + * http://www.libsdl.org/ + */ +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); + } /* if */ + + else + { + bool add_it = false; + int devfd; + device_geometry g; + + if (strcmp(e.name, "raw") == 0) /* ignore partitions. */ + { + int devfd = open(name, O_RDONLY); + if (devfd >= 0) + { + if (ioctl(devfd, B_GET_GEOMETRY, &g, sizeof(g)) >= 0) + { + if (g.device_type == B_CD) + { + char *mntpnt = getMountPoint(name); + if (mntpnt != NULL) + { + callback(data, mntpnt); + allocator.Free(mntpnt); /* !!! FIXME: lose this malloc! */ + } /* if */ + } /* if */ + } /* if */ + } /* if */ + } /* if */ + + close(devfd); + } /* else */ + } /* 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; + } + + 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); + char *retval = (char *) allocator.Malloc(strlen(str) + 1); + BAIL_IF_MACRO(retval == NULL, ERR_OUT_OF_MEMORY, NULL); + strcpy(retval, str); + return(retval); +} /* __PHYSFS_platformCalcBaseDir */ + + +PHYSFS_uint64 __PHYSFS_platformGetThreadID(void) +{ + return((PHYSFS_uint64) find_thread(NULL)); +} /* __PHYSFS_platformGetThreadID */ + + +char *__PHYSFS_platformRealPath(const char *path) +{ + BPath normalized(path, NULL, true); /* force normalization of path. */ + const char *resolved_path = normalized.Path(); + BAIL_IF_MACRO(resolved_path == NULL, ERR_NO_SUCH_FILE, NULL); + char *retval = (char *) allocator.Malloc(strlen(resolved_path) + 1); + BAIL_IF_MACRO(retval == NULL, ERR_OUT_OF_MEMORY, NULL); + strcpy(retval, resolved_path); + return(retval); +} /* __PHYSFS_platformRealPath */ + + +char *__PHYSFS_platformCurrentDir(void) +{ + return(__PHYSFS_platformRealPath(".")); /* let BPath sort it out. */ +} /* __PHYSFS_platformCurrentDir */ + + +void *__PHYSFS_platformCreateMutex(void) +{ + return(new BLocker("PhysicsFS lock", true)); +} /* __PHYSFS_platformCreateMutex */ + + +void __PHYSFS_platformDestroyMutex(void *mutex) +{ + delete ((BLocker *) mutex); +} /* __PHYSFS_platformDestroyMutex */ + + +int __PHYSFS_platformGrabMutex(void *mutex) +{ + return ((BLocker *) mutex)->Lock() ? 1 : 0; +} /* __PHYSFS_platformGrabMutex */ + + +void __PHYSFS_platformReleaseMutex(void *mutex) +{ + ((BLocker *) mutex)->Unlock(); +} /* __PHYSFS_platformReleaseMutex */ + + +int __PHYSFS_platformSetDefaultAllocator(PHYSFS_Allocator *a) +{ + return(0); /* just use malloc() and friends. */ +} /* __PHYSFS_platformSetDefaultAllocator */ + +#endif /* PHYSFS_PLATFORM_BEOS */ + +/* end of beos.cpp ... */ + diff --git a/project/jni/physfs/platform/macosx.c b/project/jni/physfs/platform/macosx.c new file mode 100644 index 000000000..4dde270c3 --- /dev/null +++ b/project/jni/physfs/platform/macosx.c @@ -0,0 +1,396 @@ +/* + * Mac OS X 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_MACOSX + +#include +#include +#include +#include +#include + +/* Seems to get defined in some system header... */ +#ifdef Free +#undef Free +#endif + +#include "physfs_internal.h" + + +/* Wrap PHYSFS_Allocator in a CFAllocator... */ +static CFAllocatorRef cfallocator = NULL; + +CFStringRef cfallocDesc(const void *info) +{ + return(CFStringCreateWithCString(cfallocator, "PhysicsFS", + kCFStringEncodingASCII)); +} /* cfallocDesc */ + + +static void *cfallocMalloc(CFIndex allocSize, CFOptionFlags hint, void *info) +{ + return allocator.Malloc(allocSize); +} /* cfallocMalloc */ + + +static void cfallocFree(void *ptr, void *info) +{ + allocator.Free(ptr); +} /* cfallocFree */ + + +static void *cfallocRealloc(void *ptr, CFIndex newsize, + CFOptionFlags hint, void *info) +{ + if ((ptr == NULL) || (newsize <= 0)) + return NULL; /* ADC docs say you should always return NULL here. */ + return allocator.Realloc(ptr, newsize); +} /* cfallocRealloc */ + + +int __PHYSFS_platformInit(void) +{ + /* set up a CFAllocator, so Carbon can use the physfs allocator, too. */ + CFAllocatorContext ctx; + memset(&ctx, '\0', sizeof (ctx)); + ctx.copyDescription = cfallocDesc; + ctx.allocate = cfallocMalloc; + ctx.reallocate = cfallocRealloc; + ctx.deallocate = cfallocFree; + cfallocator = CFAllocatorCreate(kCFAllocatorUseContext, &ctx); + BAIL_IF_MACRO(cfallocator == NULL, ERR_OUT_OF_MEMORY, 0); + return(1); /* success. */ +} /* __PHYSFS_platformInit */ + + +int __PHYSFS_platformDeinit(void) +{ + CFRelease(cfallocator); + cfallocator = NULL; + return(1); /* always succeed. */ +} /* __PHYSFS_platformDeinit */ + + +/* CD-ROM detection code... */ + +/* + * Code based on sample from Apple Developer Connection: + * http://developer.apple.com/samplecode/Sample_Code/Devices_and_Hardware/Disks/VolumeToBSDNode/VolumeToBSDNode.c.htm + */ + +static int darwinIsWholeMedia(io_service_t service) +{ + int retval = 0; + CFTypeRef wholeMedia; + + if (!IOObjectConformsTo(service, kIOMediaClass)) + return(0); + + wholeMedia = IORegistryEntryCreateCFProperty(service, + CFSTR(kIOMediaWholeKey), + cfallocator, 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 */ + + +void __PHYSFS_platformDetectAvailableCDs(PHYSFS_StringCallback cb, void *data) +{ + 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_MACRO(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 */ +} /* __PHYSFS_platformDetectAvailableCDs */ + + +static char *convertCFString(CFStringRef cfstr) +{ + CFIndex len = CFStringGetMaximumSizeForEncoding(CFStringGetLength(cfstr), + kCFStringEncodingUTF8) + 1; + char *retval = (char *) allocator.Malloc(len); + BAIL_IF_MACRO(retval == NULL, ERR_OUT_OF_MEMORY, NULL); + + if (CFStringGetCString(cfstr, retval, len, kCFStringEncodingUTF8)) + { + /* shrink overallocated buffer if possible... */ + CFIndex newlen = strlen(retval) + 1; + if (newlen < len) + { + void *ptr = allocator.Realloc(retval, newlen); + if (ptr != NULL) + retval = (char *) ptr; + } /* if */ + } /* if */ + + else /* probably shouldn't fail, but just in case... */ + { + allocator.Free(retval); + BAIL_MACRO(ERR_OUT_OF_MEMORY, NULL); + } /* else */ + + return(retval); +} /* convertCFString */ + + +char *__PHYSFS_platformCalcBaseDir(const char *argv0) +{ + ProcessSerialNumber psn = { 0, kCurrentProcess }; + FSRef fsref; + CFRange cfrange; + CFURLRef cfurl = NULL; + CFStringRef cfstr = NULL; + CFMutableStringRef cfmutstr = NULL; + char *retval = NULL; + + BAIL_IF_MACRO(GetProcessBundleLocation(&psn, &fsref) != noErr, NULL, NULL); + cfurl = CFURLCreateFromFSRef(cfallocator, &fsref); + BAIL_IF_MACRO(cfurl == NULL, NULL, NULL); + cfstr = CFURLCopyFileSystemPath(cfurl, kCFURLPOSIXPathStyle); + CFRelease(cfurl); + BAIL_IF_MACRO(cfstr == NULL, NULL, NULL); + cfmutstr = CFStringCreateMutableCopy(cfallocator, 0, cfstr); + CFRelease(cfstr); + BAIL_IF_MACRO(cfmutstr == NULL, NULL, NULL); + + /* Find last dirsep so we can chop the binary's filename from the path. */ + cfrange = CFStringFind(cfmutstr, CFSTR("/"), kCFCompareBackwards); + if (cfrange.location == kCFNotFound) + { + assert(0); /* shouldn't ever hit this... */ + CFRelease(cfmutstr); + return(NULL); + } /* if */ + + /* chop the "/exename" from the end of the path string... */ + cfrange.length = CFStringGetLength(cfmutstr) - cfrange.location; + CFStringDelete(cfmutstr, cfrange); + + /* If we're an Application Bundle, chop everything but the base. */ + cfrange = CFStringFind(cfmutstr, CFSTR("/Contents/MacOS"), + kCFCompareCaseInsensitive | + kCFCompareBackwards | + kCFCompareAnchored); + + if (cfrange.location != kCFNotFound) + CFStringDelete(cfmutstr, cfrange); /* chop that, too. */ + + retval = convertCFString(cfmutstr); + CFRelease(cfmutstr); + + return(retval); /* whew. */ +} /* __PHYSFS_platformCalcBaseDir */ + + +/* !!! FIXME */ +#define osxerr(x) x + +char *__PHYSFS_platformRealPath(const char *path) +{ + /* The symlink and relative path resolving happens in FSPathMakeRef() */ + FSRef fsref; + CFURLRef cfurl = NULL; + CFStringRef cfstr = NULL; + char *retval = NULL; + OSStatus rc = osxerr(FSPathMakeRef((UInt8 *) path, &fsref, NULL)); + BAIL_IF_MACRO(rc != noErr, NULL, NULL); + + /* Now get it to spit out a full path. */ + cfurl = CFURLCreateFromFSRef(cfallocator, &fsref); + BAIL_IF_MACRO(cfurl == NULL, ERR_OUT_OF_MEMORY, NULL); + cfstr = CFURLCopyFileSystemPath(cfurl, kCFURLPOSIXPathStyle); + CFRelease(cfurl); + BAIL_IF_MACRO(cfstr == NULL, ERR_OUT_OF_MEMORY, NULL); + retval = convertCFString(cfstr); + CFRelease(cfstr); + + return(retval); +} /* __PHYSFS_platformRealPath */ + + +char *__PHYSFS_platformCurrentDir(void) +{ + return(__PHYSFS_platformRealPath(".")); /* let CFURL sort it out. */ +} /* __PHYSFS_platformCurrentDir */ + + +/* Platform allocator uses default CFAllocator at PHYSFS_init() time. */ + +static CFAllocatorRef cfallocdef = NULL; + +static int macosxAllocatorInit(void) +{ + int retval = 0; + cfallocdef = CFAllocatorGetDefault(); + retval = (cfallocdef != NULL); + if (retval) + CFRetain(cfallocdef); + return(retval); +} /* macosxAllocatorInit */ + + +static void macosxAllocatorDeinit(void) +{ + if (cfallocdef != NULL) + { + CFRelease(cfallocdef); + cfallocdef = NULL; + } /* if */ +} /* macosxAllocatorDeinit */ + + +static void *macosxAllocatorMalloc(PHYSFS_uint64 s) +{ + BAIL_IF_MACRO(__PHYSFS_ui64FitsAddressSpace(s), ERR_OUT_OF_MEMORY, NULL); + return(CFAllocatorAllocate(cfallocdef, (CFIndex) s, 0)); +} /* macosxAllocatorMalloc */ + + +static void *macosxAllocatorRealloc(void *ptr, PHYSFS_uint64 s) +{ + BAIL_IF_MACRO(__PHYSFS_ui64FitsAddressSpace(s), ERR_OUT_OF_MEMORY, NULL); + return(CFAllocatorReallocate(cfallocdef, ptr, (CFIndex) s, 0)); +} /* macosxAllocatorRealloc */ + + +static void macosxAllocatorFree(void *ptr) +{ + CFAllocatorDeallocate(cfallocdef, ptr); +} /* macosxAllocatorFree */ + + +int __PHYSFS_platformSetDefaultAllocator(PHYSFS_Allocator *a) +{ + allocator.Init = macosxAllocatorInit; + allocator.Deinit = macosxAllocatorDeinit; + allocator.Malloc = macosxAllocatorMalloc; + allocator.Realloc = macosxAllocatorRealloc; + allocator.Free = macosxAllocatorFree; + return(1); /* return non-zero: we're supplying custom allocator. */ +} /* __PHYSFS_platformSetDefaultAllocator */ + + +PHYSFS_uint64 __PHYSFS_platformGetThreadID(void) +{ + return( (PHYSFS_uint64) ((size_t) MPCurrentTaskID()) ); +} /* __PHYSFS_platformGetThreadID */ + + +void *__PHYSFS_platformCreateMutex(void) +{ + MPCriticalRegionID m = NULL; + if (osxerr(MPCreateCriticalRegion(&m)) != noErr) + return NULL; + return m; +} /* __PHYSFS_platformCreateMutex */ + + +void __PHYSFS_platformDestroyMutex(void *mutex) +{ + MPCriticalRegionID m = (MPCriticalRegionID) mutex; + MPDeleteCriticalRegion(m); +} /* __PHYSFS_platformDestroyMutex */ + + +int __PHYSFS_platformGrabMutex(void *mutex) +{ + MPCriticalRegionID m = (MPCriticalRegionID) mutex; + if (MPEnterCriticalRegion(m, kDurationForever) != noErr) + return(0); + return(1); +} /* __PHYSFS_platformGrabMutex */ + + +void __PHYSFS_platformReleaseMutex(void *mutex) +{ + MPCriticalRegionID m = (MPCriticalRegionID) mutex; + MPExitCriticalRegion(m); +} /* __PHYSFS_platformReleaseMutex */ + +#endif /* PHYSFS_PLATFORM_MACOSX */ + +/* end of macosx.c ... */ + diff --git a/project/jni/physfs/platform/os2.c b/project/jni/physfs/platform/os2.c new file mode 100644 index 000000000..3a6b0d8b7 --- /dev/null +++ b/project/jni/physfs/platform/os2.c @@ -0,0 +1,702 @@ +/* + * 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_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 + +#include +#include +#include +#include +#include +#include + +#include "physfs_internal.h" + +const char *__PHYSFS_platformDirSeparator = "\\"; + + +static const char *get_os2_error_string(APIRET rc) +{ + switch (rc) + { + case NO_ERROR: return(NULL); /* not an error. */ + case ERROR_INTERRUPT: return(NULL); /* not an error. */ + case ERROR_TIMEOUT: return(NULL); /* not an error. */ + case ERROR_NOT_ENOUGH_MEMORY: return(ERR_OUT_OF_MEMORY); + case ERROR_FILE_NOT_FOUND: return(ERR_NO_SUCH_FILE); + case ERROR_PATH_NOT_FOUND: return(ERR_NO_SUCH_PATH); + case ERROR_ACCESS_DENIED: return(ERR_ACCESS_DENIED); + case ERROR_NOT_DOS_DISK: return(ERR_NOT_A_DOS_DISK); + case ERROR_SHARING_VIOLATION: return(ERR_SHARING_VIOLATION); + case ERROR_CANNOT_MAKE: return(ERR_CANNOT_MAKE); + case ERROR_DEVICE_IN_USE: return(ERR_DEV_IN_USE); + case ERROR_OPEN_FAILED: return(ERR_OPEN_FAILED); + case ERROR_DISK_FULL: return(ERR_DISK_FULL); + case ERROR_PIPE_BUSY: return(ERR_PIPE_BUSY); + case ERROR_SHARING_BUFFER_EXCEEDED: return(ERR_SHARING_BUF_EXCEEDED); + case ERROR_FILENAME_EXCED_RANGE: return(ERR_BAD_FILENAME); + case ERROR_META_EXPANSION_TOO_LONG: return(ERR_BAD_FILENAME); + case ERROR_TOO_MANY_HANDLES: return(ERR_TOO_MANY_HANDLES); + case ERROR_TOO_MANY_OPEN_FILES: return(ERR_TOO_MANY_HANDLES); + case ERROR_NO_MORE_SEARCH_HANDLES: return(ERR_TOO_MANY_HANDLES); + case ERROR_SEEK_ON_DEVICE: return(ERR_SEEK_ERROR); + case ERROR_NEGATIVE_SEEK: return(ERR_SEEK_OUT_OF_RANGE); + /*!!! FIXME: Where did this go? case ERROR_DEL_CURRENT_DIRECTORY: return(ERR_DEL_CWD);*/ + case ERROR_WRITE_PROTECT: return(ERR_WRITE_PROTECT_ERROR); + case ERROR_WRITE_FAULT: return(ERR_WRITE_FAULT); + case ERROR_LOCK_VIOLATION: return(ERR_LOCK_VIOLATION); + case ERROR_GEN_FAILURE: return(ERR_GEN_FAILURE); + case ERROR_UNCERTAIN_MEDIA: return(ERR_UNCERTAIN_MEDIA); + case ERROR_PROTECTION_VIOLATION: return(ERR_PROT_VIOLATION); + case ERROR_BROKEN_PIPE: return(ERR_BROKEN_PIPE); + + 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(ERR_PHYSFS_BAD_OS_CALL); + + default: return(ERR_OS2_GENERIC); + } /* switch */ + + return(NULL); +} /* get_os2_error_string */ + + +static APIRET os2err(APIRET retval) +{ + char buf[128]; + const char *err = get_os2_error_string(retval); + if (err == ERR_OS2_GENERIC) + { + snprintf(buf, sizeof (buf), ERR_OS2_GENERIC, (int) retval); + err = buf; + } /* if */ + + if (err != NULL) + __PHYSFS_setError(err); + + return(retval); +} /* os2err */ + + +/* (be gentle, this function isn't very robust.) */ +static void cvt_path_to_correct_case(char *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) + { + 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(spec, &hdir, FILE_DIRECTORY, + &fb, sizeof (fb), &count, FIL_STANDARD); + if (rc == NO_ERROR) + { + while (count == 1) /* while still entries to enumerate... */ + { + if (__PHYSFS_stricmpASCII(fb.achName, fname) == 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 */ +} /* cvt_file_to_correct_case */ + + +static char *baseDir = NULL; + +int __PHYSFS_platformInit(void) +{ + char buf[CCHMAXPATH]; + APIRET rc; + PTIB ptib; + PPIB ppib; + PHYSFS_sint32 len; + + assert(baseDir == NULL); + BAIL_IF_MACRO(os2err(DosGetInfoBlocks(&ptib, &ppib)) != NO_ERROR, NULL, 0); + rc = DosQueryModuleName(ppib->pib_hmte, sizeof (buf), (PCHAR) buf); + BAIL_IF_MACRO(os2err(rc) != NO_ERROR, NULL, 0); + + /* chop off filename, leave path. */ + for (len = strlen(buf) - 1; len >= 0; len--) + { + if (buf[len] == '\\') + { + buf[len] = '\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... */ + cvt_path_to_correct_case(buf); + + baseDir = (char *) allocator.Malloc(len + 1); + BAIL_IF_MACRO(baseDir == NULL, ERR_OUT_OF_MEMORY, 0); + strcpy(baseDir, buf); + return(1); /* success. */ +} /* __PHYSFS_platformInit */ + + +int __PHYSFS_platformDeinit(void) +{ + assert(baseDir != NULL); + allocator.Free(baseDir); + baseDir = NULL; + return(1); /* success. */ +} /* __PHYSFS_platformDeinit */ + + +static int disc_is_inserted(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 is_cdrom_drive(ULONG drive) +{ + PHYSFS_uint32 param, data; + ULONG ul1, ul2; + APIRET rc; + HFILE hfile = NULLHANDLE; + char drivename[3] = { 'A' + drive, ':', '\0' }; + + 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); + BAIL_IF_MACRO(rc != NO_ERROR, NULL, 0); + + data = 0; + param = PHYSFS_swapULE32(CD01); + ul1 = ul2 = sizeof (PHYSFS_uint32); + rc = DosDevIOCtl(hfile, IOCTL_CDROMDISK, CDROMDISK_GETDRIVER, + ¶m, sizeof (param), &ul1, &data, sizeof (data), &ul2); + + DosClose(hfile); + return((rc == NO_ERROR) && (PHYSFS_swapULE32(data) == CD01)); +} /* is_cdrom_drive */ + + +void __PHYSFS_platformDetectAvailableCDs(PHYSFS_StringCallback cb, void *data) +{ + ULONG dummy = 0; + ULONG drivemap = 0; + ULONG i, bit; + APIRET rc = DosQueryCurrentDisk(&dummy, &drivemap); + if (os2err(rc) != NO_ERROR) + return; + + for (i = 0, bit = 1; i < 26; i++, bit <<= 1) + { + if (drivemap & bit) /* this logical drive exists. */ + { + if ((is_cdrom_drive(i)) && (disc_is_inserted(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 = (char *) allocator.Malloc(strlen(baseDir) + 1); + BAIL_IF_MACRO(retval == NULL, ERR_OUT_OF_MEMORY, NULL); + strcpy(retval, baseDir); /* calculated at init time. */ + return(retval); +} /* __PHYSFS_platformCalcBaseDir */ + + +char *__PHYSFS_platformGetUserName(void) +{ + return(NULL); /* (*shrug*) */ +} /* __PHYSFS_platformGetUserName */ + + +char *__PHYSFS_platformGetUserDir(void) +{ + return(__PHYSFS_platformCalcBaseDir(NULL)); +} /* __PHYSFS_platformGetUserDir */ + + +int __PHYSFS_platformExists(const char *fname) +{ + FILESTATUS3 fs; + APIRET rc = DosQueryPathInfo(fname, FIL_STANDARD, &fs, sizeof (fs)); + return(os2err(rc) == NO_ERROR); +} /* __PHYSFS_platformExists */ + + +int __PHYSFS_platformIsSymLink(const char *fname) +{ + return(0); /* no symlinks in OS/2. */ +} /* __PHYSFS_platformIsSymlink */ + + +int __PHYSFS_platformIsDirectory(const char *fname) +{ + FILESTATUS3 fs; + APIRET rc = DosQueryPathInfo(fname, FIL_STANDARD, &fs, sizeof (fs)); + BAIL_IF_MACRO(os2err(rc) != NO_ERROR, NULL, 0) + return((fs.attrFile & FILE_DIRECTORY) != 0); +} /* __PHYSFS_platformIsDirectory */ + + +/* !!! FIXME: can we lose the malloc here? */ +char *__PHYSFS_platformCvtToDependent(const char *prepend, + const char *dirName, + const char *append) +{ + int len = ((prepend) ? strlen(prepend) : 0) + + ((append) ? strlen(append) : 0) + + strlen(dirName) + 1; + char *retval = allocator.Malloc(len); + char *p; + + BAIL_IF_MACRO(retval == NULL, ERR_OUT_OF_MEMORY, NULL); + + if (prepend) + strcpy(retval, prepend); + else + retval[0] = '\0'; + + strcat(retval, dirName); + + if (append) + strcat(retval, append); + + for (p = strchr(retval, '/'); p != NULL; p = strchr(p + 1, '/')) + *p = '\\'; + + return(retval); +} /* __PHYSFS_platformCvtToDependent */ + + +void __PHYSFS_platformEnumerateFiles(const char *dirname, + int omitSymLinks, + PHYSFS_EnumFilesCallback callback, + const char *origdir, + void *callbackdata) +{ + char spec[CCHMAXPATH]; + FILEFINDBUF3 fb; + HDIR hdir = HDIR_CREATE; + ULONG count = 1; + APIRET rc; + + if (strlen(dirname) > sizeof (spec) - 5) + { + __PHYSFS_setError(ERR_BAD_FILENAME); + return; + } /* if */ + + strcpy(spec, dirname); + strcat(spec, (spec[strlen(spec) - 1] != '\\') ? "\\*.*" : "*.*"); + + rc = DosFindFirst(spec, &hdir, + FILE_DIRECTORY | FILE_ARCHIVED | + FILE_READONLY | FILE_HIDDEN | FILE_SYSTEM, + &fb, sizeof (fb), &count, FIL_STANDARD); + + if (os2err(rc) != NO_ERROR) + return; + + while (count == 1) + { + if ((strcmp(fb.achName, ".") != 0) && (strcmp(fb.achName, "..") != 0)) + callback(callbackdata, origdir, fb.achName); + + DosFindNext(hdir, &fb, sizeof (fb), &count); + } /* while */ + + DosFindClose(hdir); +} /* __PHYSFS_platformEnumerateFiles */ + + +char *__PHYSFS_platformCurrentDir(void) +{ + char *retval; + ULONG currentDisk; + ULONG dummy; + ULONG pathSize = 0; + APIRET rc; + BYTE byte; + + rc = DosQueryCurrentDisk(¤tDisk, &dummy); + BAIL_IF_MACRO(os2err(rc) != NO_ERROR, NULL, 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. */ + retval = (char *) allocator.Malloc(pathSize + 3); /* plus "x:\\" */ + BAIL_IF_MACRO(retval == NULL, ERR_OUT_OF_MEMORY, NULL); + + /* Actually get the string this time. */ + rc = DosQueryCurrentDir(currentDisk, (PBYTE) (retval + 3), &pathSize); + if (os2err(rc) != NO_ERROR) + { + allocator.Free(retval); + return(NULL); + } /* if */ + + retval[0] = ('A' + (currentDisk - 1)); + retval[1] = ':'; + retval[2] = '\\'; + return(retval); +} /* __PHYSFS_platformCurrentDir */ + + +char *__PHYSFS_platformRealPath(const char *path) +{ + char buf[CCHMAXPATH]; + char *retval; + APIRET rc = DosQueryPathInfo(path, FIL_QUERYFULLNAME, buf, sizeof (buf)); + BAIL_IF_MACRO(os2err(rc) != NO_ERROR, NULL, NULL); + retval = (char *) allocator.Malloc(strlen(buf) + 1); + BAIL_IF_MACRO(retval == NULL, ERR_OUT_OF_MEMORY, NULL); + strcpy(retval, buf); + return(retval); +} /* __PHYSFS_platformRealPath */ + + +int __PHYSFS_platformMkDir(const char *path) +{ + return(os2err(DosCreateDir(path, NULL)) == NO_ERROR); +} /* __PHYSFS_platformMkDir */ + + +void *__PHYSFS_platformOpenRead(const char *filename) +{ + ULONG actionTaken = 0; + HFILE hfile = NULLHANDLE; + + /* + * File must be opened SHARE_DENYWRITE and ACCESS_READONLY, otherwise + * DosQueryFileInfo() will fail if we try to get a file length, etc. + */ + os2err(DosOpen(filename, &hfile, &actionTaken, 0, FILE_NORMAL, + 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, NULL)); + + return((void *) hfile); +} /* __PHYSFS_platformOpenRead */ + + +void *__PHYSFS_platformOpenWrite(const char *filename) +{ + ULONG actionTaken = 0; + HFILE hfile = NULLHANDLE; + + /* + * File must be opened SHARE_DENYWRITE and ACCESS_READWRITE, otherwise + * DosQueryFileInfo() will fail if we try to get a file length, etc. + */ + os2err(DosOpen(filename, &hfile, &actionTaken, 0, FILE_NORMAL, + 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 | + OPEN_ACCESS_READWRITE, NULL)); + + return((void *) hfile); +} /* __PHYSFS_platformOpenWrite */ + + +void *__PHYSFS_platformOpenAppend(const char *filename) +{ + ULONG dummy = 0; + HFILE hfile = NULLHANDLE; + APIRET rc; + + /* + * File must be opened SHARE_DENYWRITE and ACCESS_READWRITE, otherwise + * DosQueryFileInfo() will fail if we try to get a file length, etc. + */ + rc = os2err(DosOpen(filename, &hfile, &dummy, 0, FILE_NORMAL, + 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, NULL)); + + if (rc == NO_ERROR) + { + if (os2err(DosSetFilePtr(hfile, 0, FILE_END, &dummy)) != NO_ERROR) + { + DosClose(hfile); + hfile = NULLHANDLE; + } /* if */ + } /* if */ + + return((void *) hfile); +} /* __PHYSFS_platformOpenAppend */ + + +PHYSFS_sint64 __PHYSFS_platformRead(void *opaque, void *buffer, + PHYSFS_uint32 size, PHYSFS_uint32 count) +{ + HFILE hfile = (HFILE) opaque; + PHYSFS_sint64 retval; + ULONG br; + + for (retval = 0; retval < count; retval++) + { + os2err(DosRead(hfile, buffer, size, &br)); + if (br < size) + { + DosSetFilePtr(hfile, -br, FILE_CURRENT, &br); /* try to cleanup. */ + return(retval); + } /* if */ + + buffer = (void *) ( ((char *) buffer) + size ); + } /* for */ + + return(retval); +} /* __PHYSFS_platformRead */ + + +PHYSFS_sint64 __PHYSFS_platformWrite(void *opaque, const void *buffer, + PHYSFS_uint32 size, PHYSFS_uint32 count) +{ + HFILE hfile = (HFILE) opaque; + PHYSFS_sint64 retval; + ULONG bw; + + for (retval = 0; retval < count; retval++) + { + os2err(DosWrite(hfile, buffer, size, &bw)); + if (bw < size) + { + DosSetFilePtr(hfile, -bw, FILE_CURRENT, &bw); /* try to cleanup. */ + return(retval); + } /* if */ + + buffer = (void *) ( ((char *) buffer) + size ); + } /* for */ + + return(retval); +} /* __PHYSFS_platformWrite */ + + +int __PHYSFS_platformSeek(void *opaque, PHYSFS_uint64 pos) +{ + ULONG dummy; + HFILE hfile = (HFILE) opaque; + LONG dist = (LONG) pos; + + /* hooray for 32-bit filesystem limits! :) */ + BAIL_IF_MACRO((PHYSFS_uint64) dist != pos, ERR_SEEK_OUT_OF_RANGE, 0); + + return(os2err(DosSetFilePtr(hfile, dist, FILE_BEGIN, &dummy)) == NO_ERROR); +} /* __PHYSFS_platformSeek */ + + +PHYSFS_sint64 __PHYSFS_platformTell(void *opaque) +{ + ULONG pos; + HFILE hfile = (HFILE) opaque; + APIRET rc = os2err(DosSetFilePtr(hfile, 0, FILE_CURRENT, &pos)); + BAIL_IF_MACRO(rc != NO_ERROR, NULL, -1); + return((PHYSFS_sint64) pos); +} /* __PHYSFS_platformTell */ + + +PHYSFS_sint64 __PHYSFS_platformFileLength(void *opaque) +{ + FILESTATUS3 fs; + HFILE hfile = (HFILE) opaque; + APIRET rc = DosQueryFileInfo(hfile, FIL_STANDARD, &fs, sizeof (fs)); + BAIL_IF_MACRO(os2err(rc) != NO_ERROR, NULL, -1); + return((PHYSFS_sint64) fs.cbFile); +} /* __PHYSFS_platformFileLength */ + + +int __PHYSFS_platformEOF(void *opaque) +{ + PHYSFS_sint64 len, pos; + + len = __PHYSFS_platformFileLength(opaque); + BAIL_IF_MACRO(len == -1, NULL, 1); /* (*shrug*) */ + pos = __PHYSFS_platformTell(opaque); + BAIL_IF_MACRO(pos == -1, NULL, 1); /* (*shrug*) */ + + return(pos >= len); +} /* __PHYSFS_platformEOF */ + + +int __PHYSFS_platformFlush(void *opaque) +{ + return(os2err(DosResetBuffer((HFILE) opaque)) == NO_ERROR); +} /* __PHYSFS_platformFlush */ + + +int __PHYSFS_platformClose(void *opaque) +{ + return(os2err(DosClose((HFILE) opaque)) == NO_ERROR); +} /* __PHYSFS_platformClose */ + + +int __PHYSFS_platformDelete(const char *path) +{ + if (__PHYSFS_platformIsDirectory(path)) + return(os2err(DosDeleteDir(path)) == NO_ERROR); + + return(os2err(DosDelete(path)) == NO_ERROR); +} /* __PHYSFS_platformDelete */ + + +PHYSFS_sint64 __PHYSFS_platformGetLastModTime(const char *fname) +{ + PHYSFS_sint64 retval; + struct tm tm; + FILESTATUS3 fs; + APIRET rc = DosQueryPathInfo(fname, FIL_STANDARD, &fs, sizeof (fs)); + BAIL_IF_MACRO(os2err(rc) != NO_ERROR, NULL, -1); + + /* Convert to a format that mktime() can grok... */ + tm.tm_sec = ((PHYSFS_uint32) fs.ftimeLastWrite.twosecs) * 2; + tm.tm_min = fs.ftimeLastWrite.minutes; + tm.tm_hour = fs.ftimeLastWrite.hours; + tm.tm_mday = fs.fdateLastWrite.day; + tm.tm_mon = fs.fdateLastWrite.month; + tm.tm_year = ((PHYSFS_uint32) fs.fdateLastWrite.year) + 80; + tm.tm_wday = -1 /*st_localtz.wDayOfWeek*/; + tm.tm_yday = -1; + tm.tm_isdst = -1; + + /* Convert to a format PhysicsFS can grok... */ + retval = (PHYSFS_sint64) mktime(&tm); + BAIL_IF_MACRO(retval == -1, strerror(errno), -1); + return(retval); +} /* __PHYSFS_platformGetLastModTime */ + + +PHYSFS_uint64 __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. + */ + BAIL_IF_MACRO(os2err(DosGetInfoBlocks(&ptib, &ppib)) != NO_ERROR, 0, 0); + return((PHYSFS_uint64) ptib->tib_ordinal); +} /* __PHYSFS_platformGetThreadID */ + + +void *__PHYSFS_platformCreateMutex(void) +{ + HMTX hmtx = NULLHANDLE; + os2err(DosCreateMutexSem(NULL, &hmtx, 0, 0)); + return((void *) hmtx); +} /* __PHYSFS_platformCreateMutex */ + + +void __PHYSFS_platformDestroyMutex(void *mutex) +{ + DosCloseMutexSem((HMTX) mutex); +} /* __PHYSFS_platformDestroyMutex */ + + +int __PHYSFS_platformGrabMutex(void *mutex) +{ + /* Do _NOT_ call os2err() (which sets the physfs error msg) in here! */ + return(DosRequestMutexSem((HMTX) mutex, SEM_INDEFINITE_WAIT) == NO_ERROR); +} /* __PHYSFS_platformGrabMutex */ + + +void __PHYSFS_platformReleaseMutex(void *mutex) +{ + DosReleaseMutexSem((HMTX) mutex); +} /* __PHYSFS_platformReleaseMutex */ + + +/* !!! FIXME: Don't use C runtime for allocators? */ +int __PHYSFS_platformSetDefaultAllocator(PHYSFS_Allocator *a) +{ + return(0); /* just use malloc() and friends. */ +} /* __PHYSFS_platformSetDefaultAllocator */ + +#endif /* PHYSFS_PLATFORM_OS2 */ + +/* end of os2.c ... */ + diff --git a/project/jni/physfs/platform/pocketpc.c b/project/jni/physfs/platform/pocketpc.c new file mode 100644 index 000000000..15137fc83 --- /dev/null +++ b/project/jni/physfs/platform/pocketpc.c @@ -0,0 +1,608 @@ +/* + * PocketPC 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_POCKETPC + +#include +#include + +#include "physfs_internal.h" + +#define INVALID_FILE_ATTRIBUTES 0xFFFFFFFF +#define INVALID_SET_FILE_POINTER 0xFFFFFFFF +typedef struct +{ + HANDLE handle; + int readonly; +} winCEfile; + + +const char *__PHYSFS_platformDirSeparator = "\\"; +static char *userDir = NULL; + +/* + * Figure out what the last failing Win32 API call was, and + * generate a human-readable string for the error message. + * + * The return value is a static buffer that is overwritten with + * each call to this function. + */ +static const char *win32strerror(void) +{ + static TCHAR msgbuf[255]; + TCHAR *ptr = msgbuf; + + FormatMessage( + FORMAT_MESSAGE_FROM_SYSTEM | + FORMAT_MESSAGE_IGNORE_INSERTS, + NULL, + GetLastError(), + MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), /* Default language */ + msgbuf, + sizeof (msgbuf) / sizeof (TCHAR), + NULL + ); + + /* chop off newlines. */ + for (ptr = msgbuf; *ptr; ptr++) + { + if ((*ptr == '\n') || (*ptr == '\r')) + { + *ptr = ' '; + break; + } /* if */ + } /* for */ + + return((const char *) msgbuf); +} /* win32strerror */ + + +/* !!! FIXME: need to check all of these for NULLs. */ +#define UTF8_TO_UNICODE_STACK_MACRO(w_assignto, str) { \ + if (str == NULL) \ + w_assignto = NULL; \ + else { \ + const PHYSFS_uint64 len = (PHYSFS_uint64) ((strlen(str) * 4) + 1); \ + w_assignto = (char *) __PHYSFS_smallAlloc(len); \ + PHYSFS_uc2fromutf8(str, (PHYSFS_uint16 *) w_assignto, len); \ + } \ +} \ + + +static char *getExePath() +{ + DWORD buflen; + int success = 0; + TCHAR *ptr = NULL; + TCHAR *retval = (TCHAR*) allocator.Malloc(sizeof (TCHAR) * (MAX_PATH + 1)); + char *charretval; + BAIL_IF_MACRO(retval == NULL, ERR_OUT_OF_MEMORY, NULL); + + retval[0] = _T('\0'); + /* !!! FIXME: don't preallocate here? */ + /* !!! FIXME: use smallAlloc? */ + buflen = GetModuleFileName(NULL, retval, MAX_PATH + 1); + if (buflen <= 0) + __PHYSFS_setError(win32strerror()); + else + { + retval[buflen] = '\0'; /* does API always null-terminate this? */ + ptr = retval+buflen; + while( ptr != retval ) + { + if( *ptr != _T('\\') ) + *ptr-- = _T('\0'); + else + break; + } /* while */ + success = 1; + } /* else */ + + if (!success) + { + allocator.Free(retval); + return(NULL); /* physfs error message will be set, above. */ + } /* if */ + + buflen = (buflen * 4) + 1; + charretval = (char *) allocator.Malloc(buflen); + if (charretval != NULL) + PHYSFS_utf8fromucs2((const PHYSFS_uint16 *) retval, charretval, buflen); + allocator.Free(retval); + return(charretval); /* w00t. */ +} /* getExePath */ + + +int __PHYSFS_platformInit(void) +{ + userDir = getExePath(); + BAIL_IF_MACRO(userDir == NULL, NULL, 0); /* failed? */ + return(1); /* always succeed. */ +} /* __PHYSFS_platformInit */ + + +int __PHYSFS_platformDeinit(void) +{ + allocator.Free(userDir); + return(1); /* always succeed. */ +} /* __PHYSFS_platformDeinit */ + + +void __PHYSFS_platformDetectAvailableCDs(PHYSFS_StringCallback cb, void *data) +{ + /* no-op on this platform. */ +} /* __PHYSFS_platformDetectAvailableCDs */ + + +char *__PHYSFS_platformCalcBaseDir(const char *argv0) +{ + return(getExePath()); +} /* __PHYSFS_platformCalcBaseDir */ + + +char *__PHYSFS_platformGetUserName(void) +{ + BAIL_MACRO(ERR_NOT_IMPLEMENTED, NULL); +} /* __PHYSFS_platformGetUserName */ + + +char *__PHYSFS_platformGetUserDir(void) +{ + return userDir; + BAIL_MACRO(ERR_NOT_IMPLEMENTED, NULL); +} /* __PHYSFS_platformGetUserDir */ + + +PHYSFS_uint64 __PHYSFS_platformGetThreadID(void) +{ + return(1); /* single threaded. */ +} /* __PHYSFS_platformGetThreadID */ + + +int __PHYSFS_platformExists(const char *fname) +{ + int retval = 0; + wchar_t *w_fname = NULL; + + UTF8_TO_UNICODE_STACK_MACRO(w_fname, fname); + if (w_fname != NULL) + retval = (GetFileAttributes(w_fname) != INVALID_FILE_ATTRIBUTES); + __PHYSFS_smallFree(w_fname); + + return(retval); +} /* __PHYSFS_platformExists */ + + +int __PHYSFS_platformIsSymLink(const char *fname) +{ + BAIL_MACRO(ERR_NOT_IMPLEMENTED, 0); +} /* __PHYSFS_platformIsSymlink */ + + +int __PHYSFS_platformIsDirectory(const char *fname) +{ + int retval = 0; + wchar_t *w_fname = NULL; + + UTF8_TO_UNICODE_STACK_MACRO(w_fname, fname); + if (w_fname != NULL) + retval = ((GetFileAttributes(w_fname) & FILE_ATTRIBUTE_DIRECTORY) != 0); + __PHYSFS_smallFree(w_fname); + + return(retval); +} /* __PHYSFS_platformIsDirectory */ + + +char *__PHYSFS_platformCvtToDependent(const char *prepend, + const char *dirName, + const char *append) +{ + int len = ((prepend) ? strlen(prepend) : 0) + + ((append) ? strlen(append) : 0) + + strlen(dirName) + 1; + char *retval = (char *) allocator.Malloc(len); + char *p; + + BAIL_IF_MACRO(retval == NULL, ERR_OUT_OF_MEMORY, NULL); + + if (prepend) + strcpy(retval, prepend); + else + retval[0] = '\0'; + + strcat(retval, dirName); + + if (append) + strcat(retval, append); + + for (p = strchr(retval, '/'); p != NULL; p = strchr(p + 1, '/')) + *p = '\\'; + + return(retval); +} /* __PHYSFS_platformCvtToDependent */ + + +static int doEnumCallback(const wchar_t *w_fname) +{ + const PHYSFS_uint64 len = (PHYSFS_uint64) ((wcslen(w_fname) * 4) + 1); + char *str = (char *) __PHYSFS_smallAlloc(len); + PHYSFS_utf8fromucs2((const PHYSFS_uint16 *) w_fname, str, len); + callback(callbackdata, origdir, str); + __PHYSFS_smallFree(str); + return 1; +} /* doEnumCallback */ + + +void __PHYSFS_platformEnumerateFiles(const char *dirname, + int omitSymLinks, + PHYSFS_EnumFilesCallback callback, + const char *origdir, + void *callbackdata) +{ + HANDLE dir; + WIN32_FIND_DATA ent; + char *SearchPath; + wchar_t *w_SearchPath; + size_t len = strlen(dirname); + + /* Allocate a new string for path, maybe '\\', "*", and NULL terminator */ + SearchPath = (char *) __PHYSFS_smallAlloc(len + 3); + BAIL_IF_MACRO(SearchPath == NULL, ERR_OUT_OF_MEMORY, NULL); + + /* Copy current dirname */ + strcpy(SearchPath, dirname); + + /* if there's no '\\' at the end of the path, stick one in there. */ + if (SearchPath[len - 1] != '\\') + { + SearchPath[len++] = '\\'; + SearchPath[len] = '\0'; + } /* if */ + + /* Append the "*" to the end of the string */ + strcat(SearchPath, "*"); + + UTF8_TO_UNICODE_STACK_MACRO(w_SearchPath, SearchPath); + __PHYSFS_smallFree(SearchPath); + dir = FindFirstFile(w_SearchPath, &ent); + __PHYSFS_smallFree(w_SearchPath); + + if (dir == INVALID_HANDLE_VALUE) + return; + + do + { + const char *str = NULL; + + if (wcscmp(ent.cFileName, L".") == 0) + continue; + + if (wcscmp(ent.cFileName, L"..") == 0) + continue; + + if (!doEnumCallback(ent.cFileName)) + break; + } while (FindNextFile(dir, &ent) != 0); + + FindClose(dir); +} /* __PHYSFS_platformEnumerateFiles */ + + +char *__PHYSFS_platformCurrentDir(void) +{ + return("\\"); +} /* __PHYSFS_platformCurrentDir */ + + +char *__PHYSFS_platformRealPath(const char *path) +{ + char *retval = (char *) allocator.Malloc(strlen(path) + 1); + strcpy(retval,path); + return(retval); +} /* __PHYSFS_platformRealPath */ + + +int __PHYSFS_platformMkDir(const char *path) +{ + int retval = 0; + wchar_t *w_path = NULL; + UTF8_TO_UNICODE_STACK_MACRO(w_path, path); + if (w_path != NULL) + { + retval = CreateDirectory(w_path, NULL); + __PHYSFS_smallFree(w_fname); + } /* if */ + return(retval); +} /* __PHYSFS_platformMkDir */ + + +static void *doOpen(const char *fname, DWORD mode, DWORD creation, int rdonly) +{ + HANDLE fileHandle; + winCEfile *retval; + wchar_t *w_fname = NULL; + + UTF8_TO_UNICODE_STACK_MACRO(w_fname, fname); + fileHandle = CreateFile(w_fname, mode, FILE_SHARE_READ | FILE_SHARE_WRITE, + NULL, creation, FILE_ATTRIBUTE_NORMAL, NULL); + __PHYSFS_smallFree(w_fname); + + BAIL_IF_MACRO(fileHandle == INVALID_HANDLE_VALUE, win32strerror(), NULL); + + retval = (winCEfile *) allocator.Malloc(sizeof (winCEfile)); + if (retval == NULL) + { + CloseHandle(fileHandle); + BAIL_MACRO(ERR_OUT_OF_MEMORY, NULL); + } /* if */ + + retval->readonly = rdonly; + retval->handle = fileHandle; + return(retval); +} /* doOpen */ + + +void *__PHYSFS_platformOpenRead(const char *filename) +{ + return(doOpen(filename, GENERIC_READ, OPEN_EXISTING, 1)); +} /* __PHYSFS_platformOpenRead */ + + +void *__PHYSFS_platformOpenWrite(const char *filename) +{ + return(doOpen(filename, GENERIC_WRITE, CREATE_ALWAYS, 0)); +} /* __PHYSFS_platformOpenWrite */ + + +void *__PHYSFS_platformOpenAppend(const char *filename) +{ + void *retval = doOpen(filename, GENERIC_WRITE, OPEN_ALWAYS, 0); + if (retval != NULL) + { + HANDLE h = ((winCEfile *) retval)->handle; + if (SetFilePointer(h, 0, NULL, FILE_END) == INVALID_SET_FILE_POINTER) + { + const char *err = win32strerror(); + CloseHandle(h); + allocator.Free(retval); + BAIL_MACRO(err, NULL); + } /* if */ + } /* if */ + + return(retval); + +} /* __PHYSFS_platformOpenAppend */ + + +PHYSFS_sint64 __PHYSFS_platformRead(void *opaque, void *buffer, + PHYSFS_uint32 size, PHYSFS_uint32 count) +{ + HANDLE Handle = ((winCEfile *) opaque)->handle; + DWORD CountOfBytesRead; + PHYSFS_sint64 retval; + + /* Read data from the file */ + /*!!! - uint32 might be a greater # than DWORD */ + if (!ReadFile(Handle, buffer, count * size, &CountOfBytesRead, NULL)) + { + retval = -1; + } /* if */ + else + { + /* Return the number of "objects" read. */ + /* !!! - What if not the right amount of bytes was read to make an object? */ + retval = CountOfBytesRead / size; + } /* else */ + + return(retval); + +} /* __PHYSFS_platformRead */ + + +PHYSFS_sint64 __PHYSFS_platformWrite(void *opaque, const void *buffer, + PHYSFS_uint32 size, PHYSFS_uint32 count) +{ + HANDLE Handle = ((winCEfile *) opaque)->handle; + DWORD CountOfBytesWritten; + PHYSFS_sint64 retval; + + /* Read data from the file */ + /*!!! - uint32 might be a greater # than DWORD */ + if (!WriteFile(Handle, buffer, count * size, &CountOfBytesWritten, NULL)) + { + retval = -1; + } /* if */ + else + { + /* Return the number of "objects" read. */ + /*!!! - What if not the right number of bytes was written? */ + retval = CountOfBytesWritten / size; + } /* else */ + + return(retval); + +} /* __PHYSFS_platformWrite */ + + +int __PHYSFS_platformSeek(void *opaque, PHYSFS_uint64 pos) +{ + HANDLE Handle = ((winCEfile *) opaque)->handle; + DWORD HighOrderPos; + DWORD rc; + + /* Get the high order 32-bits of the position */ + //HighOrderPos = HIGHORDER_UINT64(pos); + HighOrderPos = (unsigned long)(pos>>32); + + /*!!! SetFilePointer needs a signed 64-bit value. */ + /* Move pointer "pos" count from start of file */ + rc = SetFilePointer(Handle, (unsigned long)(pos&0x00000000ffffffff), + &HighOrderPos, FILE_BEGIN); + + if ((rc == INVALID_SET_FILE_POINTER) && (GetLastError() != NO_ERROR)) + { + BAIL_MACRO(win32strerror(), 0); + } + + return(1); /* No error occured */ +} /* __PHYSFS_platformSeek */ + + +PHYSFS_sint64 __PHYSFS_platformTell(void *opaque) +{ + HANDLE Handle = ((winCEfile *) opaque)->handle; + DWORD HighPos = 0; + DWORD LowPos; + PHYSFS_sint64 retval; + + /* Get current position */ + LowPos = SetFilePointer(Handle, 0, &HighPos, FILE_CURRENT); + if ((LowPos == INVALID_SET_FILE_POINTER) && (GetLastError() != NO_ERROR)) + { + BAIL_MACRO(win32strerror(), 0); + } /* if */ + else + { + /* Combine the high/low order to create the 64-bit position value */ + retval = (((PHYSFS_uint64) HighPos) << 32) | LowPos; + //assert(retval >= 0); + } /* else */ + + return(retval); +} /* __PHYSFS_platformTell */ + + +PHYSFS_sint64 __PHYSFS_platformFileLength(void *opaque) +{ + HANDLE Handle = ((winCEfile *) opaque)->handle; + DWORD SizeHigh; + DWORD SizeLow; + PHYSFS_sint64 retval; + + SizeLow = GetFileSize(Handle, &SizeHigh); + if ((SizeLow == INVALID_SET_FILE_POINTER) && (GetLastError() != NO_ERROR)) + { + BAIL_MACRO(win32strerror(), -1); + } /* if */ + else + { + /* Combine the high/low order to create the 64-bit position value */ + retval = (((PHYSFS_uint64) SizeHigh) << 32) | SizeLow; + //assert(retval >= 0); + } /* else */ + + return(retval); +} /* __PHYSFS_platformFileLength */ + + +int __PHYSFS_platformEOF(void *opaque) +{ + PHYSFS_sint64 FilePosition; + int retval = 0; + + /* Get the current position in the file */ + if ((FilePosition = __PHYSFS_platformTell(opaque)) != 0) + { + /* Non-zero if EOF is equal to the file length */ + retval = FilePosition == __PHYSFS_platformFileLength(opaque); + } /* if */ + + return(retval); +} /* __PHYSFS_platformEOF */ + + +int __PHYSFS_platformFlush(void *opaque) +{ + winCEfile *fh = ((winCEfile *) opaque); + if (!fh->readonly) + BAIL_IF_MACRO(!FlushFileBuffers(fh->handle), win32strerror(), 0); + + return(1); +} /* __PHYSFS_platformFlush */ + + +int __PHYSFS_platformClose(void *opaque) +{ + HANDLE Handle = ((winCEfile *) opaque)->handle; + BAIL_IF_MACRO(!CloseHandle(Handle), win32strerror(), 0); + allocator.Free(opaque); + return(1); +} /* __PHYSFS_platformClose */ + + +int __PHYSFS_platformDelete(const char *path) +{ + wchar_t *w_path = NULL; + UTF8_TO_UNICODE_STACK_MACRO(w_path, path); + + /* If filename is a folder */ + if (GetFileAttributes(w_path) == FILE_ATTRIBUTE_DIRECTORY) + { + int retval = !RemoveDirectory(w_path); + __PHYSFS_smallFree(w_path); + BAIL_IF_MACRO(retval, win32strerror(), 0); + } /* if */ + else + { + int retval = !DeleteFile(w_path); + __PHYSFS_smallFree(w_path); + BAIL_IF_MACRO(retval, win32strerror(), 0); + } /* else */ + + return(1); /* if you got here, it worked. */ +} /* __PHYSFS_platformDelete */ + + +/* + * !!! FIXME: why aren't we using Critical Sections instead of Mutexes? + * !!! FIXME: mutexes on Windows are for cross-process sync. CritSects are + * !!! FIXME: mutexes for threads in a single process and are faster. + */ +void *__PHYSFS_platformCreateMutex(void) +{ + return((void *) CreateMutex(NULL, FALSE, NULL)); +} /* __PHYSFS_platformCreateMutex */ + + +void __PHYSFS_platformDestroyMutex(void *mutex) +{ + CloseHandle((HANDLE) mutex); +} /* __PHYSFS_platformDestroyMutex */ + + +int __PHYSFS_platformGrabMutex(void *mutex) +{ + return(WaitForSingleObject((HANDLE) mutex, INFINITE) != WAIT_FAILED); +} /* __PHYSFS_platformGrabMutex */ + + +void __PHYSFS_platformReleaseMutex(void *mutex) +{ + ReleaseMutex((HANDLE) mutex); +} /* __PHYSFS_platformReleaseMutex */ + + +PHYSFS_sint64 __PHYSFS_platformGetLastModTime(const char *fname) +{ + BAIL_MACRO(ERR_NOT_IMPLEMENTED, -1); +} /* __PHYSFS_platformGetLastModTime */ + + +/* !!! FIXME: Don't use C runtime for allocators? */ +int __PHYSFS_platformSetDefaultAllocator(PHYSFS_Allocator *a) +{ + return(0); /* just use malloc() and friends. */ +} /* __PHYSFS_platformSetDefaultAllocator */ + +#endif /* PHYSFS_PLATFORM_POCKETPC */ + +/* end of pocketpc.c ... */ + diff --git a/project/jni/physfs/platform/posix.c b/project/jni/physfs/platform/posix.c new file mode 100644 index 000000000..dd7a20999 --- /dev/null +++ b/project/jni/physfs/platform/posix.c @@ -0,0 +1,424 @@ +/* + * 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. + */ + +#define __PHYSICSFS_INTERNAL__ +#include "physfs_platforms.h" + +#ifdef PHYSFS_PLATFORM_POSIX + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef PHYSFS_HAVE_LLSEEK +#include +#endif + +#include "physfs_internal.h" + + +const char *__PHYSFS_platformDirSeparator = "/"; + + +char *__PHYSFS_platformCopyEnvironmentVariable(const char *varname) +{ + const char *envr = getenv(varname); + char *retval = NULL; + + if (envr != NULL) + { + retval = (char *) allocator.Malloc(strlen(envr) + 1); + if (retval != NULL) + strcpy(retval, envr); + } /* if */ + + return(retval); +} /* __PHYSFS_platformCopyEnvironmentVariable */ + + +static char *getUserNameByUID(void) +{ + uid_t uid = getuid(); + struct passwd *pw; + char *retval = NULL; + + pw = getpwuid(uid); + if ((pw != NULL) && (pw->pw_name != NULL)) + { + retval = (char *) allocator.Malloc(strlen(pw->pw_name) + 1); + if (retval != NULL) + strcpy(retval, pw->pw_name); + } /* if */ + + return(retval); +} /* getUserNameByUID */ + + +static char *getUserDirByUID(void) +{ + uid_t uid = getuid(); + struct passwd *pw; + char *retval = NULL; + + pw = getpwuid(uid); + if ((pw != NULL) && (pw->pw_dir != NULL)) + { + retval = (char *) allocator.Malloc(strlen(pw->pw_dir) + 1); + if (retval != NULL) + strcpy(retval, pw->pw_dir); + } /* if */ + + return(retval); +} /* getUserDirByUID */ + + +char *__PHYSFS_platformGetUserName(void) +{ + char *retval = getUserNameByUID(); + if (retval == NULL) + retval = __PHYSFS_platformCopyEnvironmentVariable("USER"); + return(retval); +} /* __PHYSFS_platformGetUserName */ + + +char *__PHYSFS_platformGetUserDir(void) +{ + char *retval = __PHYSFS_platformCopyEnvironmentVariable("HOME"); + + /* if the environment variable was set, make sure it's really a dir. */ + if (retval != NULL) + { + struct stat statbuf; + if ((stat(retval, &statbuf) == -1) || (S_ISDIR(statbuf.st_mode) == 0)) + { + allocator.Free(retval); + retval = NULL; + } /* if */ + } /* if */ + + if (retval == NULL) + retval = getUserDirByUID(); + + return(retval); +} /* __PHYSFS_platformGetUserDir */ + + +int __PHYSFS_platformExists(const char *fname) +{ + struct stat statbuf; + BAIL_IF_MACRO(lstat(fname, &statbuf) == -1, strerror(errno), 0); + return(1); +} /* __PHYSFS_platformExists */ + + +int __PHYSFS_platformIsSymLink(const char *fname) +{ + struct stat statbuf; + BAIL_IF_MACRO(lstat(fname, &statbuf) == -1, strerror(errno), 0); + return( (S_ISLNK(statbuf.st_mode)) ? 1 : 0 ); +} /* __PHYSFS_platformIsSymlink */ + + +int __PHYSFS_platformIsDirectory(const char *fname) +{ + struct stat statbuf; + BAIL_IF_MACRO(stat(fname, &statbuf) == -1, strerror(errno), 0); + return( (S_ISDIR(statbuf.st_mode)) ? 1 : 0 ); +} /* __PHYSFS_platformIsDirectory */ + + +char *__PHYSFS_platformCvtToDependent(const char *prepend, + const char *dirName, + const char *append) +{ + int len = ((prepend) ? strlen(prepend) : 0) + + ((append) ? strlen(append) : 0) + + strlen(dirName) + 1; + char *retval = (char *) allocator.Malloc(len); + + BAIL_IF_MACRO(retval == NULL, ERR_OUT_OF_MEMORY, NULL); + + /* platform-independent notation is Unix-style already. :) */ + + if (prepend) + strcpy(retval, prepend); + else + retval[0] = '\0'; + + strcat(retval, dirName); + + if (append) + strcat(retval, append); + + return(retval); +} /* __PHYSFS_platformCvtToDependent */ + + + +void __PHYSFS_platformEnumerateFiles(const char *dirname, + int omitSymLinks, + PHYSFS_EnumFilesCallback callback, + const char *origdir, + void *callbackdata) +{ + DIR *dir; + struct dirent *ent; + int bufsize = 0; + char *buf = NULL; + int dlen = 0; + + if (omitSymLinks) /* !!! FIXME: this malloc sucks. */ + { + dlen = strlen(dirname); + bufsize = dlen + 256; + buf = (char *) allocator.Malloc(bufsize); + if (buf == NULL) + return; + strcpy(buf, dirname); + if (buf[dlen - 1] != '/') + { + buf[dlen++] = '/'; + buf[dlen] = '\0'; + } /* if */ + } /* if */ + + errno = 0; + dir = opendir(dirname); + if (dir == NULL) + { + allocator.Free(buf); + return; + } /* if */ + + while ((ent = readdir(dir)) != NULL) + { + if (strcmp(ent->d_name, ".") == 0) + continue; + + if (strcmp(ent->d_name, "..") == 0) + continue; + + if (omitSymLinks) + { + char *p; + int len = strlen(ent->d_name) + dlen + 1; + if (len > bufsize) + { + p = (char *) allocator.Realloc(buf, len); + if (p == NULL) + continue; + buf = p; + bufsize = len; + } /* if */ + + strcpy(buf + dlen, ent->d_name); + if (__PHYSFS_platformIsSymLink(buf)) + continue; + } /* if */ + + callback(callbackdata, origdir, ent->d_name); + } /* while */ + + allocator.Free(buf); + closedir(dir); +} /* __PHYSFS_platformEnumerateFiles */ + + +int __PHYSFS_platformMkDir(const char *path) +{ + int rc; + errno = 0; + rc = mkdir(path, S_IRWXU); + BAIL_IF_MACRO(rc == -1, strerror(errno), 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_MACRO(fd < 0, strerror(errno), NULL); + + if (appending) + { + if (lseek(fd, 0, SEEK_END) < 0) + { + close(fd); + BAIL_MACRO(strerror(errno), NULL); + } /* if */ + } /* if */ + + retval = (int *) allocator.Malloc(sizeof (int)); + if (retval == NULL) + { + close(fd); + BAIL_MACRO(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_uint32 size, PHYSFS_uint32 count) +{ + int fd = *((int *) opaque); + int max = size * count; + int rc = read(fd, buffer, max); + + BAIL_IF_MACRO(rc == -1, strerror(errno), rc); + assert(rc <= max); + + if ((rc < max) && (size > 1)) + lseek(fd, -(rc % size), SEEK_CUR); /* rollback to object boundary. */ + + return(rc / size); +} /* __PHYSFS_platformRead */ + + +PHYSFS_sint64 __PHYSFS_platformWrite(void *opaque, const void *buffer, + PHYSFS_uint32 size, PHYSFS_uint32 count) +{ + int fd = *((int *) opaque); + int max = size * count; + int rc = write(fd, (void *) buffer, max); + + BAIL_IF_MACRO(rc == -1, strerror(errno), rc); + assert(rc <= max); + + if ((rc < max) && (size > 1)) + lseek(fd, -(rc % size), SEEK_CUR); /* rollback to object boundary. */ + + return(rc / size); +} /* __PHYSFS_platformWrite */ + + +int __PHYSFS_platformSeek(void *opaque, PHYSFS_uint64 pos) +{ + int fd = *((int *) opaque); + + #ifdef PHYSFS_HAVE_LLSEEK + unsigned long offset_high = ((pos >> 32) & 0xFFFFFFFF); + unsigned long offset_low = (pos & 0xFFFFFFFF); + loff_t retoffset; + int rc = llseek(fd, offset_high, offset_low, &retoffset, SEEK_SET); + BAIL_IF_MACRO(rc == -1, strerror(errno), 0); + #else + BAIL_IF_MACRO(lseek(fd, (int) pos, SEEK_SET) == -1, strerror(errno), 0); + #endif + + return(1); +} /* __PHYSFS_platformSeek */ + + +PHYSFS_sint64 __PHYSFS_platformTell(void *opaque) +{ + int fd = *((int *) opaque); + PHYSFS_sint64 retval; + + #ifdef PHYSFS_HAVE_LLSEEK + loff_t retoffset; + int rc = llseek(fd, 0, &retoffset, SEEK_CUR); + BAIL_IF_MACRO(rc == -1, strerror(errno), -1); + retval = (PHYSFS_sint64) retoffset; + #else + retval = (PHYSFS_sint64) lseek(fd, 0, SEEK_CUR); + BAIL_IF_MACRO(retval == -1, strerror(errno), -1); + #endif + + return(retval); +} /* __PHYSFS_platformTell */ + + +PHYSFS_sint64 __PHYSFS_platformFileLength(void *opaque) +{ + int fd = *((int *) opaque); + struct stat statbuf; + BAIL_IF_MACRO(fstat(fd, &statbuf) == -1, strerror(errno), -1); + return((PHYSFS_sint64) statbuf.st_size); +} /* __PHYSFS_platformFileLength */ + + +int __PHYSFS_platformEOF(void *opaque) +{ + PHYSFS_sint64 pos = __PHYSFS_platformTell(opaque); + PHYSFS_sint64 len = __PHYSFS_platformFileLength(opaque); + return(pos >= len); +} /* __PHYSFS_platformEOF */ + + +int __PHYSFS_platformFlush(void *opaque) +{ + int fd = *((int *) opaque); + BAIL_IF_MACRO(fsync(fd) == -1, strerror(errno), 0); + return(1); +} /* __PHYSFS_platformFlush */ + + +int __PHYSFS_platformClose(void *opaque) +{ + int fd = *((int *) opaque); + BAIL_IF_MACRO(close(fd) == -1, strerror(errno), 0); + allocator.Free(opaque); + return(1); +} /* __PHYSFS_platformClose */ + + +int __PHYSFS_platformDelete(const char *path) +{ + BAIL_IF_MACRO(remove(path) == -1, strerror(errno), 0); + return(1); +} /* __PHYSFS_platformDelete */ + + +PHYSFS_sint64 __PHYSFS_platformGetLastModTime(const char *fname) +{ + struct stat statbuf; + BAIL_IF_MACRO(stat(fname, &statbuf) < 0, strerror(errno), -1); + return statbuf.st_mtime; +} /* __PHYSFS_platformGetLastModTime */ + +#endif /* PHYSFS_PLATFORM_POSIX */ + +/* end of posix.c ... */ + diff --git a/project/jni/physfs/platform/unix.c b/project/jni/physfs/platform/unix.c new file mode 100644 index 000000000..4e714b40b --- /dev/null +++ b/project/jni/physfs/platform/unix.c @@ -0,0 +1,446 @@ +/* + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#if (!defined PHYSFS_NO_THREAD_SUPPORT) +#include +#endif + +#ifdef PHYSFS_HAVE_SYS_UCRED_H +# ifdef PHYSFS_HAVE_MNTENT_H +# undef PHYSFS_HAVE_MNTENT_H /* don't do both... */ +# endif +# include +# include +#endif + +#ifdef PHYSFS_HAVE_MNTENT_H +#include +#endif + +#include "physfs_internal.h" + +#ifndef MAXPATHLEN +#define MAXPATHLEN 1024 +#endif + +int __PHYSFS_platformInit(void) +{ + return(1); /* always succeed. */ +} /* __PHYSFS_platformInit */ + + +int __PHYSFS_platformDeinit(void) +{ + return(1); /* always succeed. */ +} /* __PHYSFS_platformDeinit */ + + +#ifdef PHYSFS_NO_CDROM_SUPPORT + +/* Stub version for platforms without CD-ROM support. */ +void __PHYSFS_platformDetectAvailableCDs(PHYSFS_StringCallback cb, void *data) +{ +} /* __PHYSFS_platformDetectAvailableCDs */ + +#elif (defined PHYSFS_HAVE_SYS_UCRED_H) + +void __PHYSFS_platformDetectAvailableCDs(PHYSFS_StringCallback cb, void *data) +{ + int i; + struct statfs *mntbufp = NULL; + int mounts = getmntinfo(&mntbufp, MNT_WAIT); + + 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 */ +} /* __PHYSFS_platformDetectAvailableCDs */ + +#elif (defined PHYSFS_HAVE_MNTENT_H) + +void __PHYSFS_platformDetectAvailableCDs(PHYSFS_StringCallback cb, void *data) +{ + FILE *mounts = NULL; + struct mntent *ent = NULL; + + mounts = setmntent("/etc/mtab", "r"); + BAIL_IF_MACRO(mounts == NULL, ERR_IO_ERROR, /*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); + +} /* __PHYSFS_platformDetectAvailableCDs */ + +#endif + + +/* this is in posix.c ... */ +extern char *__PHYSFS_platformCopyEnvironmentVariable(const char *varname); + + +/* + * 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; + + BAIL_IF_MACRO(bin == NULL, ERR_INVALID_ARGUMENT, NULL); + BAIL_IF_MACRO(envr == NULL, ERR_INVALID_ARGUMENT, NULL); + + do + { + size_t size; + ptr = strchr(start, ':'); /* find next $PATH separator. */ + if (ptr) + *ptr = '\0'; + + size = strlen(start) + strlen(bin) + 2; + if (size > alloc_size) + { + char *x = (char *) allocator.Realloc(exe, size); + if (x == NULL) + { + if (exe != NULL) + allocator.Free(exe); + BAIL_MACRO(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. */ + { + strcpy(exe, start); /* i'm lazy. piss off. */ + 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; + char *envr = NULL; + + /* fast path: default behaviour can handle this. */ + if ( (argv0 != NULL) && (strchr(argv0, '/') != NULL) ) + return(NULL); /* higher level will parse out real path from argv0. */ + + /* + * Try to avoid using argv0 unless forced to. If there's a Linux-like + * /proc filesystem, you can get the full path to the current process from + * the /proc/self/exe symlink. + */ + retval = readSymLink("/proc/self/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 (retval != NULL) /* chop off filename. */ + { + char *ptr = strrchr(retval, '/'); + if (ptr != NULL) + *ptr = '\0'; + } /* if */ + + if ((retval == NULL) && (argv0 != NULL)) + { + /* If there's no dirsep on argv0, then look through $PATH for it. */ + envr = __PHYSFS_platformCopyEnvironmentVariable("PATH"); + BAIL_IF_MACRO(!envr, NULL, NULL); + retval = findBinaryInPath(argv0, envr); + allocator.Free(envr); + } /* 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_platformRealPath(const char *path) +{ + char resolved_path[MAXPATHLEN]; + char *retval = NULL; + + errno = 0; + BAIL_IF_MACRO(!realpath(path, resolved_path), strerror(errno), NULL); + retval = (char *) allocator.Malloc(strlen(resolved_path) + 1); + BAIL_IF_MACRO(retval == NULL, ERR_OUT_OF_MEMORY, NULL); + strcpy(retval, resolved_path); + + return(retval); +} /* __PHYSFS_platformRealPath */ + + +char *__PHYSFS_platformCurrentDir(void) +{ + /* + * This can't just do platformRealPath("."), since that would eventually + * just end up calling back into here. + */ + + int allocSize = 0; + char *retval = NULL; + char *ptr; + + do + { + allocSize += 100; + ptr = (char *) allocator.Realloc(retval, allocSize); + if (ptr == NULL) + { + if (retval != NULL) + allocator.Free(retval); + BAIL_MACRO(ERR_OUT_OF_MEMORY, NULL); + } /* if */ + + retval = ptr; + ptr = getcwd(retval, allocSize); + } while (ptr == NULL && errno == ERANGE); + + if (ptr == NULL && errno) + { + /* + * getcwd() failed for some reason, for example current + * directory not existing. + */ + if (retval != NULL) + allocator.Free(retval); + BAIL_MACRO(ERR_NO_SUCH_FILE, NULL); + } /* if */ + + return(retval); +} /* __PHYSFS_platformCurrentDir */ + + +int __PHYSFS_platformSetDefaultAllocator(PHYSFS_Allocator *a) +{ + return(0); /* just use malloc() and friends. */ +} /* __PHYSFS_platformSetDefaultAllocator */ + + +#if (defined PHYSFS_NO_THREAD_SUPPORT) + +PHYSFS_uint64 __PHYSFS_platformGetThreadID(void) { return(0x0001); } +void *__PHYSFS_platformCreateMutex(void) { return((void *) 0x0001); } +void __PHYSFS_platformDestroyMutex(void *mutex) {} +int __PHYSFS_platformGrabMutex(void *mutex) { return(1); } +void __PHYSFS_platformReleaseMutex(void *mutex) {} + +#else + +typedef struct +{ + pthread_mutex_t mutex; + pthread_t owner; + PHYSFS_uint32 count; +} PthreadMutex; + +/* Just in case; this is a panic value. */ +#if ((!defined SIZEOF_INT) || (SIZEOF_INT <= 0)) +# define SIZEOF_INT 4 +#endif + +#if (SIZEOF_INT == 4) +# define PHTREAD_TO_UI64(thr) ( (PHYSFS_uint64) ((PHYSFS_uint32) (thr)) ) +#elif (SIZEOF_INT == 2) +# define PHTREAD_TO_UI64(thr) ( (PHYSFS_uint64) ((PHYSFS_uint16) (thr)) ) +#elif (SIZEOF_INT == 1) +# define PHTREAD_TO_UI64(thr) ( (PHYSFS_uint64) ((PHYSFS_uint8) (thr)) ) +#else +# define PHTREAD_TO_UI64(thr) ((PHYSFS_uint64) (thr)) +#endif + +PHYSFS_uint64 __PHYSFS_platformGetThreadID(void) +{ + return(PHTREAD_TO_UI64(pthread_self())); +} /* __PHYSFS_platformGetThreadID */ + + +void *__PHYSFS_platformCreateMutex(void) +{ + int rc; + PthreadMutex *m = (PthreadMutex *) allocator.Malloc(sizeof (PthreadMutex)); + BAIL_IF_MACRO(m == NULL, ERR_OUT_OF_MEMORY, NULL); + rc = pthread_mutex_init(&m->mutex, NULL); + if (rc != 0) + { + allocator.Free(m); + BAIL_MACRO(strerror(rc), 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; + 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_NO_THREAD_SUPPORT */ + +#endif /* PHYSFS_PLATFORM_UNIX */ + +/* end of unix.c ... */ + diff --git a/project/jni/physfs/platform/windows.c b/project/jni/physfs/platform/windows.c new file mode 100644 index 000000000..b50952e9e --- /dev/null +++ b/project/jni/physfs/platform/windows.c @@ -0,0 +1,1403 @@ +/* + * Windows support routines for PhysicsFS. + * + * Please see the file LICENSE.txt in the source's root directory. + * + * This file written by Ryan C. Gordon, and made sane by Gregory S. Read. + */ + +#define __PHYSICSFS_INTERNAL__ +#include "physfs_platforms.h" + +#ifdef PHYSFS_PLATFORM_WINDOWS + +/* Forcibly disable UNICODE, since we manage this ourselves. */ +#ifdef UNICODE +#undef UNICODE +#endif + +#include +#include +#include +#include +#include +#include +#include + +#include "physfs_internal.h" + +#define LOWORDER_UINT64(pos) ((PHYSFS_uint32) (pos & 0xFFFFFFFF)) +#define HIGHORDER_UINT64(pos) ((PHYSFS_uint32) ((pos >> 32) & 0xFFFFFFFF)) + +/* + * Users without the platform SDK don't have this defined. The original docs + * for SetFilePointer() just said to compare with 0xFFFFFFFF, so this should + * work as desired. + */ +#define PHYSFS_INVALID_SET_FILE_POINTER 0xFFFFFFFF + +/* just in case... */ +#define PHYSFS_INVALID_FILE_ATTRIBUTES 0xFFFFFFFF + +/* Not defined before the Vista SDK. */ +#define PHYSFS_IO_REPARSE_TAG_SYMLINK 0xA000000C + + +#define UTF8_TO_UNICODE_STACK_MACRO(w_assignto, str) { \ + if (str == NULL) \ + w_assignto = NULL; \ + else { \ + const PHYSFS_uint64 len = (PHYSFS_uint64) ((strlen(str) + 1) * 2); \ + w_assignto = (WCHAR *) __PHYSFS_smallAlloc(len); \ + if (w_assignto != NULL) \ + PHYSFS_utf8ToUcs2(str, (PHYSFS_uint16 *) w_assignto, len); \ + } \ +} \ + +static PHYSFS_uint64 wStrLen(const WCHAR *wstr) +{ + PHYSFS_uint64 len = 0; + while (*(wstr++)) + len++; + return(len); +} /* wStrLen */ + +static char *unicodeToUtf8Heap(const WCHAR *w_str) +{ + char *retval = NULL; + if (w_str != NULL) + { + void *ptr = NULL; + const PHYSFS_uint64 len = (wStrLen(w_str) * 4) + 1; + retval = allocator.Malloc(len); + BAIL_IF_MACRO(retval == NULL, ERR_OUT_OF_MEMORY, NULL); + PHYSFS_utf8FromUcs2((const PHYSFS_uint16 *) w_str, retval, len); + ptr = allocator.Realloc(retval, strlen(retval) + 1); /* shrink. */ + if (ptr != NULL) + retval = (char *) ptr; + } /* if */ + return(retval); +} /* unicodeToUtf8Heap */ + + +static char *codepageToUtf8Heap(const char *cpstr) +{ + char *retval = NULL; + if (cpstr != NULL) + { + const int len = (int) (strlen(cpstr) + 1); + WCHAR *wbuf = (WCHAR *) __PHYSFS_smallAlloc(len * sizeof (WCHAR)); + BAIL_IF_MACRO(wbuf == NULL, ERR_OUT_OF_MEMORY, NULL); + MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, cpstr, len, wbuf, len); + retval = (char *) allocator.Malloc(len * 4); + if (retval == NULL) + __PHYSFS_setError(ERR_OUT_OF_MEMORY); + else + PHYSFS_utf8FromUcs2(wbuf, retval, len * 4); + __PHYSFS_smallFree(wbuf); + } /* if */ + return(retval); +} /* codepageToUtf8Heap */ + + +typedef struct +{ + HANDLE handle; + int readonly; +} WinApiFile; + + +static char *userDir = NULL; +static int osHasUnicode = 0; + + +/* pointers for APIs that may not exist on some Windows versions... */ +static HANDLE libKernel32 = NULL; +static HANDLE libUserEnv = NULL; +static HANDLE libAdvApi32 = NULL; +static DWORD (WINAPI *pGetModuleFileNameW)(HMODULE, LPWCH, DWORD); +static BOOL (WINAPI *pGetUserProfileDirectoryW)(HANDLE, LPWSTR, LPDWORD); +static BOOL (WINAPI *pGetUserNameW)(LPWSTR, LPDWORD); +static DWORD (WINAPI *pGetFileAttributesW)(LPCWSTR); +static HANDLE (WINAPI *pFindFirstFileW)(LPCWSTR, LPWIN32_FIND_DATAW); +static BOOL (WINAPI *pFindNextFileW)(HANDLE, LPWIN32_FIND_DATAW); +static DWORD (WINAPI *pGetCurrentDirectoryW)(DWORD, LPWSTR); +static BOOL (WINAPI *pDeleteFileW)(LPCWSTR); +static BOOL (WINAPI *pRemoveDirectoryW)(LPCWSTR); +static BOOL (WINAPI *pCreateDirectoryW)(LPCWSTR, LPSECURITY_ATTRIBUTES); +static BOOL (WINAPI *pGetFileAttributesExA) + (LPCSTR, GET_FILEEX_INFO_LEVELS, LPVOID); +static BOOL (WINAPI *pGetFileAttributesExW) + (LPCWSTR, GET_FILEEX_INFO_LEVELS, LPVOID); +static DWORD (WINAPI *pFormatMessageW) + (DWORD, LPCVOID, DWORD, DWORD, LPWSTR, DWORD, va_list *); +static HANDLE (WINAPI *pCreateFileW) + (LPCWSTR, DWORD, DWORD, LPSECURITY_ATTRIBUTES, DWORD, DWORD, HANDLE); + + +/* + * Fallbacks for missing Unicode functions on Win95/98/ME. These are filled + * into the function pointers if looking up the real Unicode entry points + * in the system DLLs fails, so they're never used on WinNT/XP/Vista/etc. + * They make an earnest effort to convert to/from UTF-8 and UCS-2 to + * the user's current codepage. + */ + +static BOOL WINAPI fallbackGetUserNameW(LPWSTR buf, LPDWORD len) +{ + const DWORD cplen = *len; + char *cpstr = __PHYSFS_smallAlloc(cplen); + BOOL retval = GetUserNameA(cpstr, len); + if (buf != NULL) + MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, cpstr, cplen, buf, *len); + __PHYSFS_smallFree(cpstr); + return(retval); +} /* fallbackGetUserNameW */ + +static DWORD WINAPI fallbackFormatMessageW(DWORD dwFlags, LPCVOID lpSource, + DWORD dwMessageId, DWORD dwLangId, + LPWSTR lpBuf, DWORD nSize, + va_list *Arguments) +{ + char *cpbuf = (char *) __PHYSFS_smallAlloc(nSize); + DWORD retval = FormatMessageA(dwFlags, lpSource, dwMessageId, dwLangId, + cpbuf, nSize, Arguments); + if (retval > 0) + MultiByteToWideChar(CP_ACP,MB_PRECOMPOSED,cpbuf,retval,lpBuf,nSize); + __PHYSFS_smallFree(cpbuf); + return(retval); +} /* fallbackFormatMessageW */ + +static DWORD WINAPI fallbackGetModuleFileNameW(HMODULE hMod, LPWCH lpBuf, + DWORD nSize) +{ + char *cpbuf = (char *) __PHYSFS_smallAlloc(nSize); + DWORD retval = GetModuleFileNameA(hMod, cpbuf, nSize); + if (retval > 0) + MultiByteToWideChar(CP_ACP,MB_PRECOMPOSED,cpbuf,retval,lpBuf,nSize); + __PHYSFS_smallFree(cpbuf); + return(retval); +} /* fallbackGetModuleFileNameW */ + +static DWORD WINAPI fallbackGetFileAttributesW(LPCWSTR fname) +{ + DWORD retval = 0; + const int buflen = (int) (wStrLen(fname) + 1); + char *cpstr = (char *) __PHYSFS_smallAlloc(buflen); + WideCharToMultiByte(CP_ACP, 0, fname, buflen, cpstr, buflen, NULL, NULL); + retval = GetFileAttributesA(cpstr); + __PHYSFS_smallFree(cpstr); + return(retval); +} /* fallbackGetFileAttributesW */ + +static DWORD WINAPI fallbackGetCurrentDirectoryW(DWORD buflen, LPWSTR buf) +{ + DWORD retval = 0; + char *cpbuf = NULL; + if (buf != NULL) + cpbuf = (char *) __PHYSFS_smallAlloc(buflen); + retval = GetCurrentDirectoryA(buflen, cpbuf); + if (cpbuf != NULL) + { + MultiByteToWideChar(CP_ACP,MB_PRECOMPOSED,cpbuf,retval,buf,buflen); + __PHYSFS_smallFree(cpbuf); + } /* if */ + return(retval); +} /* fallbackGetCurrentDirectoryW */ + +static BOOL WINAPI fallbackRemoveDirectoryW(LPCWSTR dname) +{ + BOOL retval = 0; + const int buflen = (int) (wStrLen(dname) + 1); + char *cpstr = (char *) __PHYSFS_smallAlloc(buflen); + WideCharToMultiByte(CP_ACP, 0, dname, buflen, cpstr, buflen, NULL, NULL); + retval = RemoveDirectoryA(cpstr); + __PHYSFS_smallFree(cpstr); + return(retval); +} /* fallbackRemoveDirectoryW */ + +static BOOL WINAPI fallbackCreateDirectoryW(LPCWSTR dname, + LPSECURITY_ATTRIBUTES attr) +{ + BOOL retval = 0; + const int buflen = (int) (wStrLen(dname) + 1); + char *cpstr = (char *) __PHYSFS_smallAlloc(buflen); + WideCharToMultiByte(CP_ACP, 0, dname, buflen, cpstr, buflen, NULL, NULL); + retval = CreateDirectoryA(cpstr, attr); + __PHYSFS_smallFree(cpstr); + return(retval); +} /* fallbackCreateDirectoryW */ + +static BOOL WINAPI fallbackDeleteFileW(LPCWSTR fname) +{ + BOOL retval = 0; + const int buflen = (int) (wStrLen(fname) + 1); + char *cpstr = (char *) __PHYSFS_smallAlloc(buflen); + WideCharToMultiByte(CP_ACP, 0, fname, buflen, cpstr, buflen, NULL, NULL); + retval = DeleteFileA(cpstr); + __PHYSFS_smallFree(cpstr); + return(retval); +} /* fallbackDeleteFileW */ + +static HANDLE WINAPI fallbackCreateFileW(LPCWSTR fname, + DWORD dwDesiredAccess, DWORD dwShareMode, + LPSECURITY_ATTRIBUTES lpSecurityAttrs, + DWORD dwCreationDisposition, + DWORD dwFlagsAndAttrs, HANDLE hTemplFile) +{ + HANDLE retval; + const int buflen = (int) (wStrLen(fname) + 1); + char *cpstr = (char *) __PHYSFS_smallAlloc(buflen); + WideCharToMultiByte(CP_ACP, 0, fname, buflen, cpstr, buflen, NULL, NULL); + retval = CreateFileA(cpstr, dwDesiredAccess, dwShareMode, lpSecurityAttrs, + dwCreationDisposition, dwFlagsAndAttrs, hTemplFile); + __PHYSFS_smallFree(cpstr); + return(retval); +} /* fallbackCreateFileW */ + + +#if (PHYSFS_MINIMUM_GCC_VERSION(3,3)) + typedef FARPROC __attribute__((__may_alias__)) PHYSFS_FARPROC; +#else + typedef FARPROC PHYSFS_FARPROC; +#endif + + +static void symLookup(HMODULE dll, PHYSFS_FARPROC *addr, const char *sym, + int reallyLook, PHYSFS_FARPROC fallback) +{ + PHYSFS_FARPROC proc; + proc = (PHYSFS_FARPROC) ((reallyLook) ? GetProcAddress(dll, sym) : NULL); + if (proc == NULL) + proc = fallback; /* may also be NULL. */ + *addr = proc; +} /* symLookup */ + + +static int findApiSymbols(void) +{ + HMODULE dll = NULL; + + #define LOOKUP_NOFALLBACK(x, reallyLook) \ + symLookup(dll, (PHYSFS_FARPROC *) &p##x, #x, reallyLook, NULL) + + #define LOOKUP(x, reallyLook) \ + symLookup(dll, (PHYSFS_FARPROC *) &p##x, #x, \ + reallyLook, (PHYSFS_FARPROC) fallback##x) + + /* Apparently Win9x HAS the Unicode entry points, they just don't WORK. */ + /* ...so don't look them up unless we're on NT+. (see osHasUnicode.) */ + + dll = libUserEnv = LoadLibraryA("userenv.dll"); + if (dll != NULL) + LOOKUP_NOFALLBACK(GetUserProfileDirectoryW, osHasUnicode); + + /* !!! FIXME: what do they call advapi32.dll on Win64? */ + dll = libAdvApi32 = LoadLibraryA("advapi32.dll"); + if (dll != NULL) + LOOKUP(GetUserNameW, osHasUnicode); + + /* !!! FIXME: what do they call kernel32.dll on Win64? */ + dll = libKernel32 = LoadLibraryA("kernel32.dll"); + if (dll != NULL) + { + LOOKUP_NOFALLBACK(GetFileAttributesExA, 1); + LOOKUP_NOFALLBACK(GetFileAttributesExW, osHasUnicode); + LOOKUP_NOFALLBACK(FindFirstFileW, osHasUnicode); + LOOKUP_NOFALLBACK(FindNextFileW, osHasUnicode); + LOOKUP(GetModuleFileNameW, osHasUnicode); + LOOKUP(FormatMessageW, osHasUnicode); + LOOKUP(GetFileAttributesW, osHasUnicode); + LOOKUP(GetCurrentDirectoryW, osHasUnicode); + LOOKUP(CreateDirectoryW, osHasUnicode); + LOOKUP(RemoveDirectoryW, osHasUnicode); + LOOKUP(CreateFileW, osHasUnicode); + LOOKUP(DeleteFileW, osHasUnicode); + } /* if */ + + #undef LOOKUP_NOFALLBACK + #undef LOOKUP + + return(1); +} /* findApiSymbols */ + + +const char *__PHYSFS_platformDirSeparator = "\\"; + + +/* + * Figure out what the last failing Windows API call was, and + * generate a human-readable string for the error message. + * + * The return value is a static buffer that is overwritten with + * each call to this function. + */ +static const char *winApiStrError(void) +{ + static char utf8buf[255]; + WCHAR msgbuf[255]; + WCHAR *ptr; + DWORD rc = pFormatMessageW( + FORMAT_MESSAGE_FROM_SYSTEM | + FORMAT_MESSAGE_IGNORE_INSERTS, + NULL, GetLastError(), + MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), + msgbuf, __PHYSFS_ARRAYLEN(msgbuf), + NULL); + + if (rc == 0) + msgbuf[0] = '\0'; /* oh well. */ + + /* chop off newlines. */ + for (ptr = msgbuf; *ptr; ptr++) + { + if ((*ptr == '\n') || (*ptr == '\r')) + { + *ptr = '\0'; + break; + } /* if */ + } /* for */ + + /* may truncate, but oh well. */ + PHYSFS_utf8FromUcs2((PHYSFS_uint16 *) msgbuf, utf8buf, sizeof (utf8buf)); + return((const char *) utf8buf); +} /* winApiStrError */ + + +static char *getExePath(void) +{ + DWORD buflen = 64; + LPWSTR modpath = NULL; + char *retval = NULL; + + while (1) + { + DWORD rc; + void *ptr; + + if ( !(ptr = allocator.Realloc(modpath, buflen*sizeof(WCHAR))) ) + { + allocator.Free(modpath); + BAIL_MACRO(ERR_OUT_OF_MEMORY, NULL); + } /* if */ + modpath = (LPWSTR) ptr; + + rc = pGetModuleFileNameW(NULL, modpath, buflen); + if (rc == 0) + { + allocator.Free(modpath); + BAIL_MACRO(winApiStrError(), NULL); + } /* if */ + + if (rc < buflen) + { + buflen = rc; + break; + } /* if */ + + buflen *= 2; + } /* while */ + + if (buflen > 0) /* just in case... */ + { + WCHAR *ptr = (modpath + buflen) - 1; + while (ptr != modpath) + { + if (*ptr == '\\') + break; + ptr--; + } /* while */ + + if ((ptr == modpath) && (*ptr != '\\')) + __PHYSFS_setError(ERR_GETMODFN_NO_DIR); + else + { + *(ptr + 1) = '\0'; /* chop off filename. */ + retval = unicodeToUtf8Heap(modpath); + } /* else */ + } /* else */ + allocator.Free(modpath); + + return(retval); /* w00t. */ +} /* getExePath */ + + +/* + * Try to make use of GetUserProfileDirectoryW(), which isn't available on + * some common variants of Win32. If we can't use this, we just punt and + * use the physfs base dir for the user dir, too. + * + * On success, module-scope variable (userDir) will have a pointer to + * a malloc()'d string of the user's profile dir, and a non-zero value is + * returned. If we can't determine the profile dir, (userDir) will + * be NULL, and zero is returned. + */ +static int determineUserDir(void) +{ + if (userDir != NULL) + return(1); /* already good to go. */ + + /* + * GetUserProfileDirectoryW() is only available on NT 4.0 and later. + * This means Win95/98/ME (and CE?) users have to do without, so for + * them, we'll default to the base directory when we can't get the + * function pointer. Since this is originally an NT API, we don't + * offer a non-Unicode fallback. + */ + if (pGetUserProfileDirectoryW != NULL) + { + HANDLE accessToken = NULL; /* Security handle to process */ + HANDLE processHandle = GetCurrentProcess(); + if (OpenProcessToken(processHandle, TOKEN_QUERY, &accessToken)) + { + DWORD psize = 0; + WCHAR dummy = 0; + LPWSTR wstr = NULL; + BOOL rc = 0; + + /* + * Should fail. Will write the size of the profile path in + * psize. Also note that the second parameter can't be + * NULL or the function fails. + */ + rc = pGetUserProfileDirectoryW(accessToken, &dummy, &psize); + assert(!rc); /* !!! FIXME: handle this gracefully. */ + + /* Allocate memory for the profile directory */ + wstr = (LPWSTR) __PHYSFS_smallAlloc(psize * sizeof (WCHAR)); + if (wstr != NULL) + { + if (pGetUserProfileDirectoryW(accessToken, wstr, &psize)) + userDir = unicodeToUtf8Heap(wstr); + __PHYSFS_smallFree(wstr); + } /* else */ + } /* if */ + + CloseHandle(accessToken); + } /* if */ + + if (userDir == NULL) /* couldn't get profile for some reason. */ + { + /* Might just be a non-NT system; resort to the basedir. */ + userDir = getExePath(); + BAIL_IF_MACRO(userDir == NULL, NULL, 0); /* STILL failed?! */ + } /* if */ + + return(1); /* We made it: hit the showers. */ +} /* determineUserDir */ + + +static BOOL mediaInDrive(const char *drive) +{ + UINT oldErrorMode; + DWORD tmp; + BOOL retval; + + /* Prevent windows warning message appearing when checking media size */ + oldErrorMode = SetErrorMode(SEM_FAILCRITICALERRORS); + + /* If this function succeeds, there's media in the drive */ + retval = GetVolumeInformationA(drive, NULL, 0, NULL, NULL, &tmp, NULL, 0); + + /* Revert back to old windows error handler */ + SetErrorMode(oldErrorMode); + + return(retval); +} /* mediaInDrive */ + + +void __PHYSFS_platformDetectAvailableCDs(PHYSFS_StringCallback cb, void *data) +{ + /* !!! FIXME: Can CD drives be non-drive letter paths? */ + /* !!! FIXME: (so can they be Unicode paths?) */ + char drive_str[4] = "x:\\"; + char ch; + for (ch = 'A'; ch <= 'Z'; ch++) + { + drive_str[0] = ch; + if (GetDriveType(drive_str) == DRIVE_CDROM && mediaInDrive(drive_str)) + cb(data, drive_str); + } /* for */ +} /* __PHYSFS_platformDetectAvailableCDs */ + + +char *__PHYSFS_platformCalcBaseDir(const char *argv0) +{ + if ((argv0 != NULL) && (strchr(argv0, '\\') != NULL)) + return(NULL); /* default behaviour can handle this. */ + + return(getExePath()); +} /* __PHYSFS_platformCalcBaseDir */ + + +char *__PHYSFS_platformGetUserName(void) +{ + DWORD bufsize = 0; + char *retval = NULL; + + if (pGetUserNameW(NULL, &bufsize) == 0) /* This SHOULD fail. */ + { + LPWSTR wbuf = (LPWSTR) __PHYSFS_smallAlloc(bufsize * sizeof (WCHAR)); + BAIL_IF_MACRO(wbuf == NULL, ERR_OUT_OF_MEMORY, NULL); + if (pGetUserNameW(wbuf, &bufsize) == 0) /* ?! */ + __PHYSFS_setError(winApiStrError()); + else + retval = unicodeToUtf8Heap(wbuf); + __PHYSFS_smallFree(wbuf); + } /* if */ + + return(retval); +} /* __PHYSFS_platformGetUserName */ + + +char *__PHYSFS_platformGetUserDir(void) +{ + char *retval = (char *) allocator.Malloc(strlen(userDir) + 1); + BAIL_IF_MACRO(retval == NULL, ERR_OUT_OF_MEMORY, NULL); + strcpy(retval, userDir); /* calculated at init time. */ + return(retval); +} /* __PHYSFS_platformGetUserDir */ + + +PHYSFS_uint64 __PHYSFS_platformGetThreadID(void) +{ + return((PHYSFS_uint64) GetCurrentThreadId()); +} /* __PHYSFS_platformGetThreadID */ + + +static int doPlatformExists(LPWSTR wpath) +{ + BAIL_IF_MACRO + ( + pGetFileAttributesW(wpath) == PHYSFS_INVALID_FILE_ATTRIBUTES, + winApiStrError(), 0 + ); + return(1); +} /* doPlatformExists */ + + +int __PHYSFS_platformExists(const char *fname) +{ + int retval = 0; + LPWSTR wpath; + UTF8_TO_UNICODE_STACK_MACRO(wpath, fname); + BAIL_IF_MACRO(wpath == NULL, ERR_OUT_OF_MEMORY, 0); + retval = doPlatformExists(wpath); + __PHYSFS_smallFree(wpath); + return(retval); +} /* __PHYSFS_platformExists */ + + +static int isSymlinkAttrs(const DWORD attr, const DWORD tag) +{ + return ( (attr & FILE_ATTRIBUTE_REPARSE_POINT) && + (tag == PHYSFS_IO_REPARSE_TAG_SYMLINK) ); +} /* isSymlinkAttrs */ + + +int __PHYSFS_platformIsSymLink(const char *fname) +{ + /* !!! FIXME: + * Windows Vista can have NTFS symlinks. Can older Windows releases have + * them when talking to a network file server? What happens when you + * mount a NTFS partition on XP that was plugged into a Vista install + * that made a symlink? + */ + + int retval = 0; + LPWSTR wpath; + HANDLE dir; + WIN32_FIND_DATAW entw; + + /* no unicode entry points? Probably no symlinks. */ + BAIL_IF_MACRO(pFindFirstFileW == NULL, NULL, 0); + + UTF8_TO_UNICODE_STACK_MACRO(wpath, fname); + BAIL_IF_MACRO(wpath == NULL, ERR_OUT_OF_MEMORY, 0); + + /* !!! FIXME: filter wildcard chars? */ + dir = pFindFirstFileW(wpath, &entw); + if (dir != INVALID_HANDLE_VALUE) + { + retval = isSymlinkAttrs(entw.dwFileAttributes, entw.dwReserved0); + FindClose(dir); + } /* if */ + + __PHYSFS_smallFree(wpath); + return(retval); +} /* __PHYSFS_platformIsSymlink */ + + +int __PHYSFS_platformIsDirectory(const char *fname) +{ + int retval = 0; + LPWSTR wpath; + UTF8_TO_UNICODE_STACK_MACRO(wpath, fname); + BAIL_IF_MACRO(wpath == NULL, ERR_OUT_OF_MEMORY, 0); + retval = ((pGetFileAttributesW(wpath) & FILE_ATTRIBUTE_DIRECTORY) != 0); + __PHYSFS_smallFree(wpath); + return(retval); +} /* __PHYSFS_platformIsDirectory */ + + +char *__PHYSFS_platformCvtToDependent(const char *prepend, + const char *dirName, + const char *append) +{ + int len = ((prepend) ? strlen(prepend) : 0) + + ((append) ? strlen(append) : 0) + + strlen(dirName) + 1; + char *retval = (char *) allocator.Malloc(len); + char *p; + + BAIL_IF_MACRO(retval == NULL, ERR_OUT_OF_MEMORY, NULL); + + if (prepend) + strcpy(retval, prepend); + else + retval[0] = '\0'; + + strcat(retval, dirName); + + if (append) + strcat(retval, append); + + for (p = strchr(retval, '/'); p != NULL; p = strchr(p + 1, '/')) + *p = '\\'; + + return(retval); +} /* __PHYSFS_platformCvtToDependent */ + + +void __PHYSFS_platformEnumerateFiles(const char *dirname, + int omitSymLinks, + PHYSFS_EnumFilesCallback callback, + const char *origdir, + void *callbackdata) +{ + const int unicode = (pFindFirstFileW != NULL) && (pFindNextFileW != NULL); + HANDLE dir = INVALID_HANDLE_VALUE; + WIN32_FIND_DATA ent; + WIN32_FIND_DATAW entw; + size_t len = strlen(dirname); + char *searchPath = NULL; + WCHAR *wSearchPath = NULL; + char *utf8 = NULL; + + /* Allocate a new string for path, maybe '\\', "*", and NULL terminator */ + searchPath = (char *) __PHYSFS_smallAlloc(len + 3); + if (searchPath == NULL) + return; + + /* Copy current dirname */ + strcpy(searchPath, dirname); + + /* if there's no '\\' at the end of the path, stick one in there. */ + if (searchPath[len - 1] != '\\') + { + searchPath[len++] = '\\'; + searchPath[len] = '\0'; + } /* if */ + + /* Append the "*" to the end of the string */ + strcat(searchPath, "*"); + + UTF8_TO_UNICODE_STACK_MACRO(wSearchPath, searchPath); + if (wSearchPath == NULL) + return; /* oh well. */ + + if (unicode) + dir = pFindFirstFileW(wSearchPath, &entw); + else + { + const int len = (int) (wStrLen(wSearchPath) + 1); + char *cp = (char *) __PHYSFS_smallAlloc(len); + if (cp != NULL) + { + WideCharToMultiByte(CP_ACP, 0, wSearchPath, len, cp, len, 0, 0); + dir = FindFirstFileA(cp, &ent); + __PHYSFS_smallFree(cp); + } /* if */ + } /* else */ + + __PHYSFS_smallFree(wSearchPath); + __PHYSFS_smallFree(searchPath); + if (dir == INVALID_HANDLE_VALUE) + return; + + if (unicode) + { + do + { + const DWORD attr = entw.dwFileAttributes; + const DWORD tag = entw.dwReserved0; + const WCHAR *fn = entw.cFileName; + if ((fn[0] == '.') && (fn[1] == '\0')) + continue; + if ((fn[0] == '.') && (fn[1] == '.') && (fn[2] == '\0')) + continue; + if ((omitSymLinks) && (isSymlinkAttrs(attr, tag))) + continue; + + utf8 = unicodeToUtf8Heap(fn); + if (utf8 != NULL) + { + callback(callbackdata, origdir, utf8); + allocator.Free(utf8); + } /* if */ + } while (pFindNextFileW(dir, &entw) != 0); + } /* if */ + + else /* ANSI fallback. */ + { + do + { + const DWORD attr = ent.dwFileAttributes; + const DWORD tag = ent.dwReserved0; + const char *fn = ent.cFileName; + if ((fn[0] == '.') && (fn[1] == '\0')) + continue; + if ((fn[0] == '.') && (fn[1] == '.') && (fn[2] == '\0')) + continue; + if ((omitSymLinks) && (isSymlinkAttrs(attr, tag))) + continue; + + utf8 = codepageToUtf8Heap(fn); + if (utf8 != NULL) + { + callback(callbackdata, origdir, utf8); + allocator.Free(utf8); + } /* if */ + } while (FindNextFileA(dir, &ent) != 0); + } /* else */ + + FindClose(dir); +} /* __PHYSFS_platformEnumerateFiles */ + + +char *__PHYSFS_platformCurrentDir(void) +{ + char *retval = NULL; + WCHAR *wbuf = NULL; + DWORD buflen = 0; + + buflen = pGetCurrentDirectoryW(buflen, NULL); + wbuf = (WCHAR *) __PHYSFS_smallAlloc((buflen + 2) * sizeof (WCHAR)); + BAIL_IF_MACRO(wbuf == NULL, ERR_OUT_OF_MEMORY, NULL); + pGetCurrentDirectoryW(buflen, wbuf); + + if (wbuf[buflen - 2] == '\\') + wbuf[buflen-1] = '\0'; /* just in case... */ + else + { + wbuf[buflen - 1] = '\\'; + wbuf[buflen] = '\0'; + } /* else */ + + retval = unicodeToUtf8Heap(wbuf); + __PHYSFS_smallFree(wbuf); + return(retval); +} /* __PHYSFS_platformCurrentDir */ + + +/* this could probably use a cleanup. */ +char *__PHYSFS_platformRealPath(const char *path) +{ + /* !!! FIXME: this should return NULL if (path) doesn't exist? */ + /* !!! FIXME: Need to handle symlinks in Vista... */ + /* !!! FIXME: try GetFullPathName() instead? */ + /* this function should be UTF-8 clean. */ + char *retval = NULL; + char *p = NULL; + + BAIL_IF_MACRO(path == NULL, ERR_INVALID_ARGUMENT, NULL); + BAIL_IF_MACRO(*path == '\0', ERR_INVALID_ARGUMENT, NULL); + + retval = (char *) allocator.Malloc(MAX_PATH); + BAIL_IF_MACRO(retval == NULL, ERR_OUT_OF_MEMORY, NULL); + + /* + * If in \\server\path format, it's already an absolute path. + * We'll need to check for "." and ".." dirs, though, just in case. + */ + if ((path[0] == '\\') && (path[1] == '\\')) + strcpy(retval, path); + + else + { + char *currentDir = __PHYSFS_platformCurrentDir(); + if (currentDir == NULL) + { + allocator.Free(retval); + BAIL_MACRO(ERR_OUT_OF_MEMORY, NULL); + } /* if */ + + if (path[1] == ':') /* drive letter specified? */ + { + /* + * Apparently, "D:mypath" is the same as "D:\\mypath" if + * D: is not the current drive. However, if D: is the + * current drive, then "D:mypath" is a relative path. Ugh. + */ + if (path[2] == '\\') /* maybe an absolute path? */ + strcpy(retval, path); + else /* definitely an absolute path. */ + { + if (path[0] == currentDir[0]) /* current drive; relative. */ + { + strcpy(retval, currentDir); + strcat(retval, path + 2); + } /* if */ + + else /* not current drive; absolute. */ + { + retval[0] = path[0]; + retval[1] = ':'; + retval[2] = '\\'; + strcpy(retval + 3, path + 2); + } /* else */ + } /* else */ + } /* if */ + + else /* no drive letter specified. */ + { + if (path[0] == '\\') /* absolute path. */ + { + retval[0] = currentDir[0]; + retval[1] = ':'; + strcpy(retval + 2, path); + } /* if */ + else + { + strcpy(retval, currentDir); + strcat(retval, path); + } /* else */ + } /* else */ + + allocator.Free(currentDir); + } /* else */ + + /* (whew.) Ok, now take out "." and ".." path entries... */ + + p = retval; + while ( (p = strstr(p, "\\.")) != NULL) + { + /* it's a "." entry that doesn't end the string. */ + if (p[2] == '\\') + memmove(p + 1, p + 3, strlen(p + 3) + 1); + + /* it's a "." entry that ends the string. */ + else if (p[2] == '\0') + p[0] = '\0'; + + /* it's a ".." entry. */ + else if (p[2] == '.') + { + char *prevEntry = p - 1; + while ((prevEntry != retval) && (*prevEntry != '\\')) + prevEntry--; + + if (prevEntry == retval) /* make it look like a "." entry. */ + memmove(p + 1, p + 2, strlen(p + 2) + 1); + else + { + if (p[3] != '\0') /* doesn't end string. */ + *prevEntry = '\0'; + else /* ends string. */ + memmove(prevEntry + 1, p + 4, strlen(p + 4) + 1); + + p = prevEntry; + } /* else */ + } /* else if */ + + else + { + p++; /* look past current char. */ + } /* else */ + } /* while */ + + /* shrink the retval's memory block if possible... */ + p = (char *) allocator.Realloc(retval, strlen(retval) + 1); + if (p != NULL) + retval = p; + + return(retval); +} /* __PHYSFS_platformRealPath */ + + +int __PHYSFS_platformMkDir(const char *path) +{ + WCHAR *wpath; + DWORD rc; + UTF8_TO_UNICODE_STACK_MACRO(wpath, path); + rc = pCreateDirectoryW(wpath, NULL); + __PHYSFS_smallFree(wpath); + BAIL_IF_MACRO(rc == 0, winApiStrError(), 0); + return(1); +} /* __PHYSFS_platformMkDir */ + + + /* + * Get OS info and save the important parts. + * + * Returns non-zero if successful, otherwise it returns zero on failure. + */ +static int getOSInfo(void) +{ + OSVERSIONINFO osVerInfo; /* Information about the OS */ + osVerInfo.dwOSVersionInfoSize = sizeof(osVerInfo); + BAIL_IF_MACRO(!GetVersionEx(&osVerInfo), winApiStrError(), 0); + osHasUnicode = (osVerInfo.dwPlatformId != VER_PLATFORM_WIN32_WINDOWS); + return(1); +} /* getOSInfo */ + + +int __PHYSFS_platformInit(void) +{ + BAIL_IF_MACRO(!getOSInfo(), NULL, 0); + BAIL_IF_MACRO(!findApiSymbols(), NULL, 0); + BAIL_IF_MACRO(!determineUserDir(), NULL, 0); + return(1); /* It's all good */ +} /* __PHYSFS_platformInit */ + + +int __PHYSFS_platformDeinit(void) +{ + HANDLE *libs[] = { &libKernel32, &libUserEnv, &libAdvApi32, NULL }; + int i; + + allocator.Free(userDir); + userDir = NULL; + + for (i = 0; libs[i] != NULL; i++) + { + const HANDLE lib = *(libs[i]); + if (lib) + FreeLibrary(lib); + *(libs[i]) = NULL; + } /* for */ + + return(1); /* It's all good */ +} /* __PHYSFS_platformDeinit */ + + +static void *doOpen(const char *fname, DWORD mode, DWORD creation, int rdonly) +{ + HANDLE fileHandle; + WinApiFile *retval; + WCHAR *wfname; + + UTF8_TO_UNICODE_STACK_MACRO(wfname, fname); + BAIL_IF_MACRO(wfname == NULL, ERR_OUT_OF_MEMORY, NULL); + fileHandle = pCreateFileW(wfname, mode, FILE_SHARE_READ | FILE_SHARE_WRITE, + NULL, creation, FILE_ATTRIBUTE_NORMAL, NULL); + __PHYSFS_smallFree(wfname); + + BAIL_IF_MACRO + ( + fileHandle == INVALID_HANDLE_VALUE, + winApiStrError(), NULL + ); + + retval = (WinApiFile *) allocator.Malloc(sizeof (WinApiFile)); + if (retval == NULL) + { + CloseHandle(fileHandle); + BAIL_MACRO(ERR_OUT_OF_MEMORY, NULL); + } /* if */ + + retval->readonly = rdonly; + retval->handle = fileHandle; + return(retval); +} /* doOpen */ + + +void *__PHYSFS_platformOpenRead(const char *filename) +{ + return(doOpen(filename, GENERIC_READ, OPEN_EXISTING, 1)); +} /* __PHYSFS_platformOpenRead */ + + +void *__PHYSFS_platformOpenWrite(const char *filename) +{ + return(doOpen(filename, GENERIC_WRITE, CREATE_ALWAYS, 0)); +} /* __PHYSFS_platformOpenWrite */ + + +void *__PHYSFS_platformOpenAppend(const char *filename) +{ + void *retval = doOpen(filename, GENERIC_WRITE, OPEN_ALWAYS, 0); + if (retval != NULL) + { + HANDLE h = ((WinApiFile *) retval)->handle; + DWORD rc = SetFilePointer(h, 0, NULL, FILE_END); + if (rc == PHYSFS_INVALID_SET_FILE_POINTER) + { + const char *err = winApiStrError(); + CloseHandle(h); + allocator.Free(retval); + BAIL_MACRO(err, NULL); + } /* if */ + } /* if */ + + return(retval); +} /* __PHYSFS_platformOpenAppend */ + + +PHYSFS_sint64 __PHYSFS_platformRead(void *opaque, void *buffer, + PHYSFS_uint32 size, PHYSFS_uint32 count) +{ + HANDLE Handle = ((WinApiFile *) opaque)->handle; + DWORD CountOfBytesRead; + PHYSFS_sint64 retval; + + /* Read data from the file */ + /* !!! FIXME: uint32 might be a greater # than DWORD */ + if(!ReadFile(Handle, buffer, count * size, &CountOfBytesRead, NULL)) + { + BAIL_MACRO(winApiStrError(), -1); + } /* if */ + else + { + /* Return the number of "objects" read. */ + /* !!! FIXME: What if not the right amount of bytes was read to make an object? */ + retval = CountOfBytesRead / size; + } /* else */ + + return(retval); +} /* __PHYSFS_platformRead */ + + +PHYSFS_sint64 __PHYSFS_platformWrite(void *opaque, const void *buffer, + PHYSFS_uint32 size, PHYSFS_uint32 count) +{ + HANDLE Handle = ((WinApiFile *) opaque)->handle; + DWORD CountOfBytesWritten; + PHYSFS_sint64 retval; + + /* Read data from the file */ + /* !!! FIXME: uint32 might be a greater # than DWORD */ + if(!WriteFile(Handle, buffer, count * size, &CountOfBytesWritten, NULL)) + { + BAIL_MACRO(winApiStrError(), -1); + } /* if */ + else + { + /* Return the number of "objects" read. */ + /* !!! FIXME: What if not the right number of bytes was written? */ + retval = CountOfBytesWritten / size; + } /* else */ + + return(retval); +} /* __PHYSFS_platformWrite */ + + +int __PHYSFS_platformSeek(void *opaque, PHYSFS_uint64 pos) +{ + HANDLE Handle = ((WinApiFile *) opaque)->handle; + LONG HighOrderPos; + PLONG pHighOrderPos; + DWORD rc; + + /* Get the high order 32-bits of the position */ + HighOrderPos = HIGHORDER_UINT64(pos); + + /* + * MSDN: "If you do not need the high-order 32 bits, this + * pointer must be set to NULL." + */ + pHighOrderPos = (HighOrderPos) ? &HighOrderPos : NULL; + + /* + * !!! FIXME: MSDN: "Windows Me/98/95: If the pointer + * !!! FIXME: lpDistanceToMoveHigh is not NULL, then it must + * !!! FIXME: point to either 0, INVALID_SET_FILE_POINTER, or + * !!! FIXME: the sign extension of the value of lDistanceToMove. + * !!! FIXME: Any other value will be rejected." + */ + + /* Move pointer "pos" count from start of file */ + rc = SetFilePointer(Handle, LOWORDER_UINT64(pos), + pHighOrderPos, FILE_BEGIN); + + if ( (rc == PHYSFS_INVALID_SET_FILE_POINTER) && + (GetLastError() != NO_ERROR) ) + { + BAIL_MACRO(winApiStrError(), 0); + } /* if */ + + return(1); /* No error occured */ +} /* __PHYSFS_platformSeek */ + + +PHYSFS_sint64 __PHYSFS_platformTell(void *opaque) +{ + HANDLE Handle = ((WinApiFile *) opaque)->handle; + LONG HighPos = 0; + DWORD LowPos; + PHYSFS_sint64 retval; + + /* Get current position */ + LowPos = SetFilePointer(Handle, 0, &HighPos, FILE_CURRENT); + if ( (LowPos == PHYSFS_INVALID_SET_FILE_POINTER) && + (GetLastError() != NO_ERROR) ) + { + BAIL_MACRO(winApiStrError(), 0); + } /* if */ + else + { + /* Combine the high/low order to create the 64-bit position value */ + retval = (((PHYSFS_uint64) HighPos) << 32) | LowPos; + assert(retval >= 0); + } /* else */ + + return(retval); +} /* __PHYSFS_platformTell */ + + +PHYSFS_sint64 __PHYSFS_platformFileLength(void *opaque) +{ + HANDLE Handle = ((WinApiFile *) opaque)->handle; + DWORD SizeHigh; + DWORD SizeLow; + PHYSFS_sint64 retval; + + SizeLow = GetFileSize(Handle, &SizeHigh); + if ( (SizeLow == PHYSFS_INVALID_SET_FILE_POINTER) && + (GetLastError() != NO_ERROR) ) + { + BAIL_MACRO(winApiStrError(), -1); + } /* if */ + else + { + /* Combine the high/low order to create the 64-bit position value */ + retval = (((PHYSFS_uint64) SizeHigh) << 32) | SizeLow; + assert(retval >= 0); + } /* else */ + + return(retval); +} /* __PHYSFS_platformFileLength */ + + +int __PHYSFS_platformEOF(void *opaque) +{ + PHYSFS_sint64 FilePosition; + int retval = 0; + + /* Get the current position in the file */ + if ((FilePosition = __PHYSFS_platformTell(opaque)) != 0) + { + /* Non-zero if EOF is equal to the file length */ + retval = FilePosition == __PHYSFS_platformFileLength(opaque); + } /* if */ + + return(retval); +} /* __PHYSFS_platformEOF */ + + +int __PHYSFS_platformFlush(void *opaque) +{ + WinApiFile *fh = ((WinApiFile *) opaque); + if (!fh->readonly) + BAIL_IF_MACRO(!FlushFileBuffers(fh->handle), winApiStrError(), 0); + + return(1); +} /* __PHYSFS_platformFlush */ + + +int __PHYSFS_platformClose(void *opaque) +{ + HANDLE Handle = ((WinApiFile *) opaque)->handle; + BAIL_IF_MACRO(!CloseHandle(Handle), winApiStrError(), 0); + allocator.Free(opaque); + return(1); +} /* __PHYSFS_platformClose */ + + +static int doPlatformDelete(LPWSTR wpath) +{ + /* If filename is a folder */ + if (pGetFileAttributesW(wpath) & FILE_ATTRIBUTE_DIRECTORY) + { + BAIL_IF_MACRO(!pRemoveDirectoryW(wpath), winApiStrError(), 0); + } /* if */ + else + { + BAIL_IF_MACRO(!pDeleteFileW(wpath), winApiStrError(), 0); + } /* else */ + + return(1); /* if you made it here, it worked. */ +} /* doPlatformDelete */ + + +int __PHYSFS_platformDelete(const char *path) +{ + int retval = 0; + LPWSTR wpath; + UTF8_TO_UNICODE_STACK_MACRO(wpath, path); + BAIL_IF_MACRO(wpath == NULL, ERR_OUT_OF_MEMORY, 0); + retval = doPlatformDelete(wpath); + __PHYSFS_smallFree(wpath); + return(retval); +} /* __PHYSFS_platformDelete */ + + +/* + * !!! FIXME: why aren't we using Critical Sections instead of Mutexes? + * !!! FIXME: mutexes on Windows are for cross-process sync. CritSects are + * !!! FIXME: mutexes for threads in a single process and are faster. + */ +void *__PHYSFS_platformCreateMutex(void) +{ + return((void *) CreateMutex(NULL, FALSE, NULL)); +} /* __PHYSFS_platformCreateMutex */ + + +void __PHYSFS_platformDestroyMutex(void *mutex) +{ + CloseHandle((HANDLE) mutex); +} /* __PHYSFS_platformDestroyMutex */ + + +int __PHYSFS_platformGrabMutex(void *mutex) +{ + return(WaitForSingleObject((HANDLE) mutex, INFINITE) != WAIT_FAILED); +} /* __PHYSFS_platformGrabMutex */ + + +void __PHYSFS_platformReleaseMutex(void *mutex) +{ + ReleaseMutex((HANDLE) mutex); +} /* __PHYSFS_platformReleaseMutex */ + + +static PHYSFS_sint64 FileTimeToPhysfsTime(const FILETIME *ft) +{ + SYSTEMTIME st_utc; + SYSTEMTIME st_localtz; + TIME_ZONE_INFORMATION tzi; + DWORD tzid; + PHYSFS_sint64 retval; + struct tm tm; + + BAIL_IF_MACRO(!FileTimeToSystemTime(ft, &st_utc), winApiStrError(), -1); + tzid = GetTimeZoneInformation(&tzi); + BAIL_IF_MACRO(tzid == TIME_ZONE_ID_INVALID, winApiStrError(), -1); + + /* (This API is unsupported and fails on non-NT systems. */ + if (!SystemTimeToTzSpecificLocalTime(&tzi, &st_utc, &st_localtz)) + { + /* do it by hand. Grumble... */ + ULARGE_INTEGER ui64; + FILETIME new_ft; + ui64.LowPart = ft->dwLowDateTime; + ui64.HighPart = ft->dwHighDateTime; + + if (tzid == TIME_ZONE_ID_STANDARD) + tzi.Bias += tzi.StandardBias; + else if (tzid == TIME_ZONE_ID_DAYLIGHT) + tzi.Bias += tzi.DaylightBias; + + /* convert from minutes to 100-nanosecond increments... */ + ui64.QuadPart -= (((LONGLONG) tzi.Bias) * (600000000)); + + /* Move it back into a FILETIME structure... */ + new_ft.dwLowDateTime = ui64.LowPart; + new_ft.dwHighDateTime = ui64.HighPart; + + /* Convert to something human-readable... */ + if (!FileTimeToSystemTime(&new_ft, &st_localtz)) + BAIL_MACRO(winApiStrError(), -1); + } /* if */ + + /* Convert to a format that mktime() can grok... */ + tm.tm_sec = st_localtz.wSecond; + tm.tm_min = st_localtz.wMinute; + tm.tm_hour = st_localtz.wHour; + tm.tm_mday = st_localtz.wDay; + tm.tm_mon = st_localtz.wMonth - 1; + tm.tm_year = st_localtz.wYear - 1900; + tm.tm_wday = -1 /*st_localtz.wDayOfWeek*/; + tm.tm_yday = -1; + tm.tm_isdst = -1; + + /* Convert to a format PhysicsFS can grok... */ + retval = (PHYSFS_sint64) mktime(&tm); + BAIL_IF_MACRO(retval == -1, strerror(errno), -1); + return(retval); +} /* FileTimeToPhysfsTime */ + + +PHYSFS_sint64 __PHYSFS_platformGetLastModTime(const char *fname) +{ + PHYSFS_sint64 retval = -1; + WIN32_FILE_ATTRIBUTE_DATA attr; + int rc = 0; + + memset(&attr, '\0', sizeof (attr)); + + /* GetFileAttributesEx didn't show up until Win98 and NT4. */ + if ((pGetFileAttributesExW != NULL) || (pGetFileAttributesExA != NULL)) + { + WCHAR *wstr; + UTF8_TO_UNICODE_STACK_MACRO(wstr, fname); + if (wstr != NULL) /* if NULL, maybe the fallback will work. */ + { + if (pGetFileAttributesExW != NULL) /* NT/XP/Vista/etc system. */ + rc = pGetFileAttributesExW(wstr, GetFileExInfoStandard, &attr); + else /* Win98/ME system */ + { + const int len = (int) (wStrLen(wstr) + 1); + char *cp = (char *) __PHYSFS_smallAlloc(len); + if (cp != NULL) + { + WideCharToMultiByte(CP_ACP, 0, wstr, len, cp, len, 0, 0); + rc = pGetFileAttributesExA(cp, GetFileExInfoStandard, &attr); + __PHYSFS_smallFree(cp); + } /* if */ + } /* else */ + __PHYSFS_smallFree(wstr); + } /* if */ + } /* if */ + + if (rc) /* had API entry point and it worked. */ + { + /* 0 return value indicates an error or not supported */ + if ( (attr.ftLastWriteTime.dwHighDateTime != 0) || + (attr.ftLastWriteTime.dwLowDateTime != 0) ) + { + retval = FileTimeToPhysfsTime(&attr.ftLastWriteTime); + } /* if */ + } /* if */ + + /* GetFileTime() has been in the Win32 API since the start. */ + if (retval == -1) /* try a fallback... */ + { + FILETIME ft; + BOOL rc; + const char *err; + WinApiFile *f = (WinApiFile *) __PHYSFS_platformOpenRead(fname); + BAIL_IF_MACRO(f == NULL, NULL, -1) + rc = GetFileTime(f->handle, NULL, NULL, &ft); + err = winApiStrError(); + CloseHandle(f->handle); + allocator.Free(f); + BAIL_IF_MACRO(!rc, err, -1); + retval = FileTimeToPhysfsTime(&ft); + } /* if */ + + return(retval); +} /* __PHYSFS_platformGetLastModTime */ + + +/* !!! FIXME: Don't use C runtime for allocators? */ +int __PHYSFS_platformSetDefaultAllocator(PHYSFS_Allocator *a) +{ + return(0); /* just use malloc() and friends. */ +} /* __PHYSFS_platformSetDefaultAllocator */ + +#endif /* PHYSFS_PLATFORM_WINDOWS */ + +/* end of windows.c ... */ + +