diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 97115f1e7..9e96663d2 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -3,7 +3,7 @@ on: [push, pull_request] jobs: linux-os: - runs-on: ubuntu-18.04 + runs-on: ubuntu-20.04 steps: - name: Update APT run: sudo apt-get update @@ -17,22 +17,37 @@ jobs: libegl1-mesa-dev libibus-1.0-dev fcitx-libs-dev libsamplerate0-dev \ libsndio-dev libwayland-dev libxkbcommon-dev libdrm-dev libgbm-dev - name: Checkout love-appimage-source - uses: actions/checkout@v2 + uses: actions/checkout@v3 with: repository: love2d/love-appimage-source - name: Checkout LÖVE - uses: actions/checkout@v2 + uses: actions/checkout@v3 with: path: love2d-${{ github.sha }} + - name: Get Dependencies for AppImage + shell: python + env: + LOVE_BRANCH: ${{ github.sha }} + run: | + import os + for i in range(250): + if os.system(f"make getdeps LOVE_BRANCH={os.environ['LOVE_BRANCH']}") == 0: + raise SystemExit(0) + raise Exception("make getdeps failed") - name: Build AppImage run: make LOVE_BRANCH=${{ github.sha }} - name: Print LuaJIT branch run: git -C LuaJIT-v2.1 branch -v - name: Artifact - uses: actions/upload-artifact@v2 + uses: actions/upload-artifact@v3 with: name: love-x86_64.AppImage path: love-${{ github.sha }}.AppImage + - name: Artifact Debug Symbols + uses: actions/upload-artifact@v3 + with: + name: love-x86_64-AppImage-debug + path: love-${{ github.sha }}.AppImage-debug.tar.gz windows-os: runs-on: windows-latest strategy: @@ -40,13 +55,13 @@ jobs: platform: [Win32, x64] steps: - name: Clone Megasource - uses: actions/checkout@v2 + uses: actions/checkout@v3 with: path: megasource repository: love2d/megasource ref: main - name: Checkout - uses: actions/checkout@v2 + uses: actions/checkout@v3 with: path: megasource/libs/love - name: Configure @@ -58,12 +73,12 @@ jobs: shell: cmd run: cmake --build build --config Release --target install -j2 - name: Artifact - uses: actions/upload-artifact@v2 + uses: actions/upload-artifact@v3 with: name: love-windows-${{ matrix.platform }} path: install - name: Artifact JIT Modules - uses: actions/upload-artifact@v2 + uses: actions/upload-artifact@v3 with: name: love-windows-jitmodules-${{ matrix.platform }} path: build/libs/LuaJIT/src/jit/*.lua @@ -71,9 +86,9 @@ jobs: runs-on: macos-latest steps: - name: Checkout - uses: actions/checkout@v2 + uses: actions/checkout@v3 - name: Clone Dependencies - uses: actions/checkout@v2 + uses: actions/checkout@v3 with: path: apple-dependencies repository: love2d/love-apple-dependencies @@ -91,7 +106,7 @@ jobs: run: ditto -c -k --sequesterRsrc --keepParent love-macos/love.app love-macos.zip - name: Artifact - uses: actions/upload-artifact@v2 + uses: actions/upload-artifact@v3 with: name: love-macos path: love-macos.zip @@ -99,9 +114,9 @@ jobs: runs-on: macos-latest steps: - name: Checkout - uses: actions/checkout@v2 + uses: actions/checkout@v3 - name: Clone Dependencies - uses: actions/checkout@v2 + uses: actions/checkout@v3 with: path: apple-dependencies repository: love2d/love-apple-dependencies diff --git a/CMakeLists.txt b/CMakeLists.txt index c89eb61df..dae7726af 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -328,7 +328,7 @@ set(LOVE_SRC_COMMON if (APPLE) set(LOVE_SRC_COMMON ${LOVE_SRC_COMMON} - src/common/macosx.mm + src/common/macos.mm ) set(LOVE_LINK_LIBRARIES ${LOVE_LINK_LIBRARIES} objc) set(LOVE_LINK_LIBRARIES ${LOVE_LINK_LIBRARIES} "-framework CoreFoundation") @@ -477,6 +477,8 @@ set(LOVE_SRC_MODULE_FILESYSTEM_PHYSFS src/modules/filesystem/physfs/File.h src/modules/filesystem/physfs/Filesystem.cpp src/modules/filesystem/physfs/Filesystem.h + src/modules/filesystem/physfs/PhysfsIo.h + src/modules/filesystem/physfs/PhysfsIo.cpp ) set(LOVE_SRC_MODULE_FILESYSTEM diff --git a/extra/appveyor/appveyor.yml b/extra/appveyor/appveyor.yml index 639d824db..5e29cc30e 100644 --- a/extra/appveyor/appveyor.yml +++ b/extra/appveyor/appveyor.yml @@ -1,4 +1,4 @@ -version: 11.4.{build} +version: 11.5.{build} image: Visual Studio 2013 diff --git a/extra/windows/love.rc b/extra/windows/love.rc index c2f06a42f..edbf420cb 100644 Binary files a/extra/windows/love.rc and b/extra/windows/love.rc differ diff --git a/platform/unix/configure.ac b/platform/unix/configure.ac index 4f4a47a3d..1d681e06e 100644 --- a/platform/unix/configure.ac +++ b/platform/unix/configure.ac @@ -1,4 +1,4 @@ -AC_INIT([love], [11.4]) +AC_INIT([love], [11.5]) AC_CONFIG_HEADERS([config.h]) AC_CONFIG_AUX_DIR([platform/unix]) AC_CONFIG_MACRO_DIR([platform/unix/m4]) diff --git a/platform/unix/love.6 b/platform/unix/love.6 index a2eb4c3d6..b2c3ea75e 100644 --- a/platform/unix/love.6 +++ b/platform/unix/love.6 @@ -12,7 +12,7 @@ .\" 3. This notice may not be removed or altered from any source distribution. .Dd March 31, 2018 .Dt LOVE 6 -.Os LÖVE 11.4 +.Os LÖVE 11.5 .Sh NAME .Nm love .Nd 2D game development framework diff --git a/platform/xcode/liblove.xcodeproj/project.pbxproj b/platform/xcode/liblove.xcodeproj/project.pbxproj index 2dcebd1eb..2430f6d1e 100644 --- a/platform/xcode/liblove.xcodeproj/project.pbxproj +++ b/platform/xcode/liblove.xcodeproj/project.pbxproj @@ -54,6 +54,9 @@ 217DFC101D9F6D490055D849 /* url.lua.h in Headers */ = {isa = PBXBuildFile; fileRef = 217DFBD41D9F6D490055D849 /* url.lua.h */; }; 217DFC111D9F6D490055D849 /* usocket.c in Sources */ = {isa = PBXBuildFile; fileRef = 217DFBD51D9F6D490055D849 /* usocket.c */; }; 217DFC121D9F6D490055D849 /* usocket.h in Headers */ = {isa = PBXBuildFile; fileRef = 217DFBD61D9F6D490055D849 /* usocket.h */; }; + D943E58E2A24D56000D80361 /* PhysfsIo.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D943E58C2A24D56000D80361 /* PhysfsIo.cpp */; }; + D943E58F2A24D56000D80361 /* PhysfsIo.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D943E58C2A24D56000D80361 /* PhysfsIo.cpp */; }; + D943E5902A24D56000D80361 /* PhysfsIo.h in Headers */ = {isa = PBXBuildFile; fileRef = D943E58D2A24D56000D80361 /* PhysfsIo.h */; }; FA0A3A5F23366CE9001C269E /* floattypes.h in Headers */ = {isa = PBXBuildFile; fileRef = FA0A3A5D23366CE9001C269E /* floattypes.h */; }; FA0A3A6023366CE9001C269E /* floattypes.cpp in Sources */ = {isa = PBXBuildFile; fileRef = FA0A3A5E23366CE9001C269E /* floattypes.cpp */; }; FA0A3A6123366CE9001C269E /* floattypes.cpp in Sources */ = {isa = PBXBuildFile; fileRef = FA0A3A5E23366CE9001C269E /* floattypes.cpp */; }; @@ -1267,6 +1270,8 @@ 217DFBD41D9F6D490055D849 /* url.lua.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = url.lua.h; sourceTree = ""; }; 217DFBD51D9F6D490055D849 /* usocket.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = usocket.c; sourceTree = ""; }; 217DFBD61D9F6D490055D849 /* usocket.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = usocket.h; sourceTree = ""; }; + D943E58C2A24D56000D80361 /* PhysfsIo.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = PhysfsIo.cpp; sourceTree = ""; }; + D943E58D2A24D56000D80361 /* PhysfsIo.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PhysfsIo.h; sourceTree = ""; }; FA08F5AE16C7525600F007B5 /* liblove-macosx.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = "liblove-macosx.plist"; path = "macosx/liblove-macosx.plist"; sourceTree = ""; }; FA0A3A5D23366CE9001C269E /* floattypes.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = floattypes.h; sourceTree = ""; }; FA0A3A5E23366CE9001C269E /* floattypes.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = floattypes.cpp; sourceTree = ""; }; @@ -2719,6 +2724,8 @@ FA0B7B651A95902C000E1D17 /* File.h */, FA0B7B661A95902C000E1D17 /* Filesystem.cpp */, FA0B7B671A95902C000E1D17 /* Filesystem.h */, + D943E58C2A24D56000D80361 /* PhysfsIo.cpp */, + D943E58D2A24D56000D80361 /* PhysfsIo.h */, ); path = physfs; sourceTree = ""; @@ -3815,6 +3822,7 @@ FA0B7EEA1A95902D000E1D17 /* wrap_Window.h in Headers */, FA1557C01CE90A2C00AFF582 /* tinyexr.h in Headers */, FA0B7E381A95902C000E1D17 /* WheelJoint.h in Headers */, + D943E5902A24D56000D80361 /* PhysfsIo.h in Headers */, FA0B7D851A95902C000E1D17 /* Image.h in Headers */, FA0B7E7D1A95902C000E1D17 /* wrap_World.h in Headers */, FA0B7EBD1A95902C000E1D17 /* LuaThread.h in Headers */, @@ -4265,6 +4273,7 @@ FA0B7E161A95902C000E1D17 /* Joint.cpp in Sources */, FA0B7EE91A95902D000E1D17 /* wrap_Window.cpp in Sources */, FA1583E21E196180005E603B /* wrap_Shader.cpp in Sources */, + D943E58F2A24D56000D80361 /* PhysfsIo.cpp in Sources */, FA0B7AB91A958EA3000E1D17 /* enet.cpp in Sources */, FA0B7E281A95902C000E1D17 /* PulleyJoint.cpp in Sources */, FA56AA391FAFF02000A43D5F /* memory.cpp in Sources */, @@ -4656,6 +4665,7 @@ FAF140A01E20934C00F898D2 /* RemoveTree.cpp in Sources */, FA0B7E151A95902C000E1D17 /* Joint.cpp in Sources */, FA0B7EE81A95902D000E1D17 /* wrap_Window.cpp in Sources */, + D943E58E2A24D56000D80361 /* PhysfsIo.cpp in Sources */, FA0B7E271A95902C000E1D17 /* PulleyJoint.cpp in Sources */, FA1BA0B71E17043400AA2803 /* wrap_Shader.cpp in Sources */, FA0B7B301A958EA3000E1D17 /* wuff.c in Sources */, @@ -5194,7 +5204,7 @@ INFOPLIST_FILE = "macosx/liblove-macosx.plist"; LD_DYLIB_INSTALL_NAME = "@rpath/$(EXECUTABLE_PATH)"; LD_RUNPATH_SEARCH_PATHS = "@loader_path/../../../"; - MARKETING_VERSION = 11.4; + MARKETING_VERSION = 11.5; OTHER_LDFLAGS = ( "-undefined", dynamic_lookup, @@ -5230,7 +5240,7 @@ INFOPLIST_FILE = "macosx/liblove-macosx.plist"; LD_DYLIB_INSTALL_NAME = "@rpath/$(EXECUTABLE_PATH)"; LD_RUNPATH_SEARCH_PATHS = "@loader_path/../../../"; - MARKETING_VERSION = 11.4; + MARKETING_VERSION = 11.5; OTHER_LDFLAGS = ( "-undefined", dynamic_lookup, @@ -5267,7 +5277,7 @@ INFOPLIST_FILE = "macosx/liblove-macosx.plist"; LD_DYLIB_INSTALL_NAME = "@rpath/$(EXECUTABLE_PATH)"; LD_RUNPATH_SEARCH_PATHS = "@loader_path/../../../"; - MARKETING_VERSION = 11.4; + MARKETING_VERSION = 11.5; OTHER_LDFLAGS = ( "-undefined", dynamic_lookup, diff --git a/platform/xcode/love.xcodeproj/project.pbxproj b/platform/xcode/love.xcodeproj/project.pbxproj index 2328e9b7c..f176ab4d2 100644 --- a/platform/xcode/love.xcodeproj/project.pbxproj +++ b/platform/xcode/love.xcodeproj/project.pbxproj @@ -464,7 +464,7 @@ "$(inherited)", "@executable_path/../Frameworks", ); - MARKETING_VERSION = 11.4; + MARKETING_VERSION = 11.5; PRODUCT_BUNDLE_IDENTIFIER = org.love2d.love; PRODUCT_NAME = love; }; @@ -496,7 +496,7 @@ "$(inherited)", "@executable_path/../Frameworks", ); - MARKETING_VERSION = 11.4; + MARKETING_VERSION = 11.5; PRODUCT_BUNDLE_IDENTIFIER = org.love2d.love; PRODUCT_NAME = love; }; @@ -697,7 +697,7 @@ "$(inherited)", "@executable_path/Frameworks", ); - MARKETING_VERSION = 11.4; + MARKETING_VERSION = 11.5; MTL_ENABLE_DEBUG_INFO = YES; PRODUCT_BUNDLE_IDENTIFIER = org.love2d.love; PRODUCT_NAME = love; @@ -747,7 +747,7 @@ "$(inherited)", "@executable_path/Frameworks", ); - MARKETING_VERSION = 11.4; + MARKETING_VERSION = 11.5; MTL_ENABLE_DEBUG_INFO = NO; PRODUCT_BUNDLE_IDENTIFIER = org.love2d.love; PRODUCT_NAME = love; @@ -798,7 +798,7 @@ "$(inherited)", "@executable_path/Frameworks", ); - MARKETING_VERSION = 11.4; + MARKETING_VERSION = 11.5; MTL_ENABLE_DEBUG_INFO = NO; PRODUCT_BUNDLE_IDENTIFIER = org.love2d.love; PRODUCT_NAME = love; @@ -912,7 +912,7 @@ "$(inherited)", "@executable_path/../Frameworks", ); - MARKETING_VERSION = 11.4; + MARKETING_VERSION = 11.5; PRODUCT_BUNDLE_IDENTIFIER = org.love2d.love; PRODUCT_NAME = love; }; diff --git a/src/common/android.cpp b/src/common/android.cpp index 882c78635..a0a1f4444 100644 --- a/src/common/android.cpp +++ b/src/common/android.cpp @@ -35,7 +35,7 @@ #include #include -#include "physfs.h" +#include "filesystem/physfs/PhysfsIo.h" namespace love { @@ -390,127 +390,109 @@ static AAssetManager *getAssetManager() namespace aasset { -namespace io -{ -struct AssetInfo +struct AssetInfo: public love::filesystem::physfs::PhysfsIo { + static const uint32_t version = 0; + AAssetManager *assetManager; AAsset *asset; char *filename; size_t size; -}; - -static std::unordered_map fileTree; - -PHYSFS_sint64 read(PHYSFS_Io *io, void *buf, PHYSFS_uint64 len) -{ - AAsset *asset = ((AssetInfo *) io->opaque)->asset; - int readed = AAsset_read(asset, buf, (size_t) len); - PHYSFS_setErrorCode(readed < 0 ? PHYSFS_ERR_OS_ERROR : PHYSFS_ERR_OK); - return (PHYSFS_sint64) readed; -} + static AssetInfo *fromAAsset(AAssetManager *assetManager, const char *filename, AAsset *asset) + { + return new AssetInfo(assetManager, filename, asset); + } -PHYSFS_sint64 write(PHYSFS_Io *io, const void *buf, PHYSFS_uint64 len) -{ - LOVE_UNUSED(io); - LOVE_UNUSED(buf); - LOVE_UNUSED(len); + int64_t read(void* buf, uint64_t len) const + { + int readed = AAsset_read(asset, buf, (size_t) len); - PHYSFS_setErrorCode(PHYSFS_ERR_READ_ONLY); - return -1; -} + PHYSFS_setErrorCode(readed < 0 ? PHYSFS_ERR_OS_ERROR : PHYSFS_ERR_OK); + return (PHYSFS_sint64) readed; + } -int seek(PHYSFS_Io *io, PHYSFS_uint64 offset) -{ - AAsset *asset = ((AssetInfo *) io->opaque)->asset; - int success = AAsset_seek64(asset, (off64_t) offset, SEEK_SET) != -1; + int64_t write(const void* buf, uint64_t len) const + { + LOVE_UNUSED(buf); + LOVE_UNUSED(len); - PHYSFS_setErrorCode(success ? PHYSFS_ERR_OK : PHYSFS_ERR_OS_ERROR); - return success; -} + PHYSFS_setErrorCode(PHYSFS_ERR_READ_ONLY); + return -1; + } -PHYSFS_sint64 tell(PHYSFS_Io *io) -{ - AAsset *asset = ((AssetInfo *) io->opaque)->asset; - off64_t len = AAsset_getLength64(asset); - off64_t remain = AAsset_getRemainingLength64(asset); + int64_t seek(uint64_t offset) const + { + int64_t success = AAsset_seek64(asset, (off64_t) offset, SEEK_SET) != -1; - return len - remain; -} + PHYSFS_setErrorCode(success ? PHYSFS_ERR_OK : PHYSFS_ERR_OS_ERROR); + return success; + } -PHYSFS_sint64 length(PHYSFS_Io *io) -{ - AAsset *asset = ((AssetInfo *) io->opaque)->asset; - return AAsset_getLength64(asset); -} + int64_t tell() const + { + off64_t len = AAsset_getLength64(asset); + off64_t remain = AAsset_getRemainingLength64(asset); -// Forward declaration -PHYSFS_Io *fromAAsset(AAssetManager *assetManager, const char *filename, AAsset *asset); + return len - remain; + } -PHYSFS_Io *duplicate(PHYSFS_Io *io) -{ - AssetInfo *assetInfo = (AssetInfo *) io->opaque; - AAsset *asset = AAssetManager_open(assetInfo->assetManager, assetInfo->filename, AASSET_MODE_RANDOM); + int64_t length() const + { + return AAsset_getLength64(asset); + } - if (asset == nullptr) + int64_t flush() const { - PHYSFS_setErrorCode(PHYSFS_ERR_OS_ERROR); - return nullptr; + // Do nothing + PHYSFS_setErrorCode(PHYSFS_ERR_OK); + return 1; } - AAsset_seek64(asset, tell(io), SEEK_SET); - return fromAAsset(assetInfo->assetManager, assetInfo->filename, asset); -} + AssetInfo *duplicate() const + { + AAsset *newAsset = AAssetManager_open(assetManager, filename, AASSET_MODE_RANDOM); -void destroy(PHYSFS_Io *io) -{ - AssetInfo *assetInfo = (AssetInfo *) io->opaque; - AAsset_close(assetInfo->asset); - delete[] assetInfo->filename; - delete assetInfo; - delete io; -} + if (newAsset == nullptr) + { + PHYSFS_setErrorCode(PHYSFS_ERR_OS_ERROR); + return nullptr; + } -PHYSFS_Io *fromAAsset(AAssetManager *assetManager, const char *filename, AAsset *asset) -{ - // Create AssetInfo - AssetInfo *assetInfo = new (std::nothrow) AssetInfo(); - assetInfo->assetManager = assetManager; - assetInfo->asset = asset; - assetInfo->size = strlen(filename) + 1; - assetInfo->filename = new (std::nothrow) char[assetInfo->size]; - memcpy(assetInfo->filename, filename, assetInfo->size); + AAsset_seek64(asset, tell(), SEEK_SET); + return fromAAsset(assetManager, filename, asset); + } - // Create PHYSFS_Io - PHYSFS_Io *io = new (std::nothrow) PHYSFS_Io(); - io->version = 0; - io->opaque = assetInfo; - io->read = read; - io->write = write; - io->seek = seek; - io->tell = tell; - io->length = length; - io->duplicate = duplicate; - io->flush = nullptr; - io->destroy = destroy; + ~AssetInfo() override + { + AAsset_close(asset); + delete[] filename; + } - return io; -} +private: + AssetInfo(AAssetManager *assetManager, const char *filename, AAsset *asset) + : assetManager(assetManager) + , asset(asset) + , size(strlen(filename) + 1) + { + this->filename = new (std::nothrow) char[size]; + memcpy(this->filename, filename, size); + } +}; -} +static std::unordered_map fileTree; void *openArchive(PHYSFS_Io *io, const char *name, int forWrite, int *claimed) { - if (io->opaque == nullptr || memcmp(io->opaque, "ASET", 4) != 0) + if (forWrite || io->opaque == nullptr || memcmp(io->opaque, "ASET", 4) != 0) return nullptr; // It's our archive *claimed = 1; AAssetManager *assetManager = getAssetManager(); - if (io::fileTree.empty()) + if (fileTree.empty()) { // AAssetDir_getNextFileName intentionally excludes directories, so // we have to use JNI that calls AssetManager.list() recursively. @@ -526,7 +508,7 @@ void *openArchive(PHYSFS_Io *io, const char *name, int forWrite, int *claimed) jstring jstr = (jstring) env->GetObjectArrayElement(list, i); const char *str = env->GetStringUTFChars(jstr, nullptr); - io::fileTree[str + 1] = str[0] == 'd' ? PHYSFS_FILETYPE_DIRECTORY : PHYSFS_FILETYPE_REGULAR; + fileTree[str + 1] = str[0] == 'd' ? PHYSFS_FILETYPE_DIRECTORY : PHYSFS_FILETYPE_REGULAR; env->ReleaseStringUTFChars(jstr, str); env->DeleteLocalRef(jstr); @@ -557,9 +539,9 @@ PHYSFS_EnumerateCallbackResult enumerate( if (path[0] != 0) { - FileTreeIterator result = io::fileTree.find(path); + FileTreeIterator result = fileTree.find(path); - if (result == io::fileTree.end() || result->second != PHYSFS_FILETYPE_DIRECTORY) + if (result == fileTree.end() || result->second != PHYSFS_FILETYPE_DIRECTORY) { PHYSFS_setErrorCode(PHYSFS_ERR_NOT_FOUND); return PHYSFS_ENUM_ERROR; @@ -617,7 +599,7 @@ PHYSFS_Io *openRead(void *opaque, const char *name) } PHYSFS_setErrorCode(PHYSFS_ERR_OK); - return io::fromAAsset(assetManager, name, file); + return AssetInfo::fromAAsset(assetManager, name, file); } PHYSFS_Io *openWriteAppend(void *opaque, const char *name) @@ -642,11 +624,12 @@ int removeMkdir(void *opaque, const char *name) int stat(void *opaque, const char *name, PHYSFS_Stat *out) { + using FileTreeIterator = std::unordered_map::iterator; LOVE_UNUSED(opaque); - auto result = io::fileTree.find(name); + FileTreeIterator result = fileTree.find(name); - if (result != io::fileTree.end()) + if (result != fileTree.end()) { out->filetype = result->second; out->modtime = -1; @@ -657,11 +640,9 @@ int stat(void *opaque, const char *name, PHYSFS_Stat *out) PHYSFS_setErrorCode(PHYSFS_ERR_OK); return 1; } - else - { - PHYSFS_setErrorCode(PHYSFS_ERR_NOT_FOUND); - return 0; - } + + PHYSFS_setErrorCode(PHYSFS_ERR_NOT_FOUND); + return 0; } void closeArchive(void *opaque) @@ -763,7 +744,7 @@ bool checkFusedGame(void **physfsIO_Out) AAsset *asset = AAssetManager_open(assetManager, "game.love", AASSET_MODE_RANDOM); if (asset) { - io = aasset::io::fromAAsset(assetManager, "game.love", asset); + io = aasset::AssetInfo::fromAAsset(assetManager, "game.love", asset); return true; } @@ -823,6 +804,15 @@ const char *getCRequirePath() return path; } +const char *getArg0() +{ + static PHYSFS_AndroidInit androidInit = { + SDL_AndroidGetJNIEnv(), + SDL_AndroidGetActivity() + }; + return (const char *) &androidInit; +} + } // android } // love diff --git a/src/common/android.h b/src/common/android.h index 44d2a074d..5e263f713 100644 --- a/src/common/android.h +++ b/src/common/android.h @@ -103,6 +103,12 @@ bool checkFusedGame(void **physfsIO_Out); const char *getCRequirePath(); +/** + * Retrieve PHYSFS_AndroidInit structure. + * @return Pointer to PHYSFS_AndroidInit structure, casted to pointer of char. + */ +const char *getArg0(); + } // android } // love diff --git a/src/common/runtime.cpp b/src/common/runtime.cpp index 6031581f0..0915f7bd2 100644 --- a/src/common/runtime.cpp +++ b/src/common/runtime.cpp @@ -30,6 +30,7 @@ // C++ #include #include +#include #include #include #include @@ -105,6 +106,10 @@ static bool luax_isfulllightuserdatasupported(lua_State *L) static bool checked = false; static bool supported = false; + if (sizeof(void*) == 4) + // 32-bit platforms always supports full-lightuserdata. + return true; + if (!checked) { lua_pushcclosure(L, [](lua_State *L) -> int @@ -135,17 +140,21 @@ static ObjectKey luax_computeloveobjectkey(lua_State *L, love::Object *object) // can store all possible integers up to 2^53. We can store pointers that // use more than 53 bits if their alignment is guaranteed to be more than 1. // For example an alignment requirement of 8 means we can shift the - // pointer's bits by 3. - const size_t minalign = LOVE_ALIGNOF(std::max_align_t); + // pointer's bits by 3. However, this is not always reliable on 32-bit platforms + // as can be seen in this bug report: https://github.com/love2d/love/issues/1916. + // It appears to be ABI violation. However it seems there's no reliable way to + // get the correct alignment pre-C++17. Consider that 32-bit pointer still fits + // in 2^53 range, it's perfectly fine to assume alignment of 1 there. + const size_t minalign = sizeof(void*) == 8 ? LOVE_ALIGNOF(std::max_align_t) : 1; uintptr_t key = (uintptr_t) object; if ((key & (minalign - 1)) != 0) { luaL_error(L, "Cannot push love object to Lua: unexpected alignment " - "(pointer is %p but alignment should be %d)", object, minalign); + "(pointer is %p but alignment should be %d)", object, (int) minalign); } - static const size_t shift = (size_t) log2(LOVE_ALIGNOF(std::max_align_t)); + static const size_t shift = (size_t) log2(minalign); key >>= shift; diff --git a/src/common/version.h b/src/common/version.h index 8e97b8eee..e99f977e6 100644 --- a/src/common/version.h +++ b/src/common/version.h @@ -25,12 +25,12 @@ namespace love { // Version stuff. -#define LOVE_VERSION_STRING "11.4" +#define LOVE_VERSION_STRING "11.5" static const int VERSION_MAJOR = 11; -static const int VERSION_MINOR = 4; +static const int VERSION_MINOR = 5; static const int VERSION_REV = 0; static const char *VERSION = LOVE_VERSION_STRING; -static const char *VERSION_COMPATIBILITY[] = { VERSION, "11.0", "11.1", "11.2", "11.3", 0 }; +static const char *VERSION_COMPATIBILITY[] = { VERSION, "11.0", "11.1", "11.2", "11.3", "11.4", 0 }; static const char *VERSION_CODENAME = "Mysterious Mysteries"; } // love diff --git a/src/libraries/enet/enet.cpp b/src/libraries/enet/enet.cpp index bca527cd6..7d4a7f514 100644 --- a/src/libraries/enet/enet.cpp +++ b/src/libraries/enet/enet.cpp @@ -123,6 +123,10 @@ static bool supports_full_lightuserdata(lua_State *L) static bool checked = false; static bool supported = false; + if (sizeof(void*) == 4) + // 32-bit platforms always supports full-lightuserdata. + return true; + if (!checked) { lua_pushcclosure(L, [](lua_State* L) -> int @@ -148,7 +152,11 @@ static uintptr_t compute_peer_key(lua_State *L, ENetPeer *peer) // pointers that use more than 53 bits if their alignment is guaranteed to // be more than 1. For example an alignment requirement of 8 means we can // shift the pointer's bits by 3. - const size_t minalign = std::min(ENET_ALIGNOF(ENetPeer), ENET_ALIGNOF(std::max_align_t)); + + // Please see these for the reason of this ternary operator: + // * https://github.com/love2d/love/issues/1916 + // * https://github.com/love2d/love/commit/4ab9a1ce8c + const size_t minalign = sizeof(void*) == 8 ? std::min(ENET_ALIGNOF(ENetPeer), ENET_ALIGNOF(std::max_align_t)) : 1; uintptr_t key = (uintptr_t) peer; if ((key & (minalign - 1)) != 0) @@ -164,13 +172,16 @@ static uintptr_t compute_peer_key(lua_State *L, ENetPeer *peer) static void push_peer_key(lua_State *L, uintptr_t key) { - // If full 64-bit lightuserdata is supported, always use that. Otherwise, - // if the key is smaller than 2^53 (which is integer precision for double - // datatype), then push number. Otherwise, throw error. + // If full 64-bit lightuserdata is supported (or it's 32-bit platform), + // always use that. Otherwise, if the key is smaller than 2^53 (which is + // integer precision for double datatype) on 64-bit platform, then push + // number. Otherwise, throw error. if (supports_full_lightuserdata(L)) lua_pushlightuserdata(L, (void*) key); +#if UINTPTR_MAX == 0xffffffffffffffff else if (key > 0x20000000000000ULL) // 2^53 luaL_error(L, "Cannot push enet peer to Lua: pointer value %p is too large", key); +#endif else lua_pushnumber(L, (lua_Number) key); } diff --git a/src/libraries/physfs/physfs.c b/src/libraries/physfs/physfs.c index eec40b2bf..6476e0a7b 100644 --- a/src/libraries/physfs/physfs.c +++ b/src/libraries/physfs/physfs.c @@ -12,8 +12,6 @@ #include "physfs_internal.h" #if defined(_MSC_VER) -#include - /* this code came from https://stackoverflow.com/a/8712996 */ int __PHYSFS_msvc_vsnprintf(char *outBuf, size_t size, const char *format, va_list ap) { @@ -104,8 +102,8 @@ static inline int __PHYSFS_atomicAdd(int *ptrval, const int val) { int retval; __PHYSFS_platformGrabMutex(stateLock); + *ptrval += val; retval = *ptrval; - *ptrval = retval + val; __PHYSFS_platformReleaseMutex(stateLock); return retval; } /* __PHYSFS_atomicAdd */ @@ -923,12 +921,12 @@ static DirHandle *openDirectory(PHYSFS_Io *io, const char *d, int forWriting) retval = tryOpenDir(io, *i, d, forWriting, &claimed); } /* else */ - errcode = currentErrorCode(); + errcode = claimed ? currentErrorCode() : PHYSFS_ERR_UNSUPPORTED; if ((!retval) && (created_io)) io->destroy(io); - BAIL_IF(!retval, claimed ? errcode : PHYSFS_ERR_UNSUPPORTED, NULL); + BAIL_IF(!retval, errcode, NULL); return retval; } /* openDirectory */ @@ -1095,6 +1093,8 @@ static int freeDirHandle(DirHandle *dh, FileHandle *openList) BAIL_IF(i->dirHandle == dh, PHYSFS_ERR_FILES_STILL_OPEN, 0); dh->funcs->closeArchive(dh->opaque); + + if (dh->root) allocator.Free(dh->root); allocator.Free(dh->dirName); allocator.Free(dh->mountPoint); allocator.Free(dh); @@ -1233,7 +1233,9 @@ int PHYSFS_init(const char *argv0) if (!userDir) goto initFailed; /* Platform layer is required to append a dirsep. */ + #ifndef __ANDROID__ /* it's an APK file, not a directory, on Android. */ assert(baseDir[strlen(baseDir) - 1] == __PHYSFS_platformDirSeparator); + #endif assert(userDir[strlen(userDir) - 1] == __PHYSFS_platformDirSeparator); if (!initStaticArchivers()) goto initFailed; @@ -1433,15 +1435,60 @@ char *__PHYSFS_strdup(const char *str) } /* __PHYSFS_strdup */ -PHYSFS_uint32 __PHYSFS_hashString(const char *str, size_t len) +PHYSFS_uint32 __PHYSFS_hashString(const char *str) { PHYSFS_uint32 hash = 5381; - while (len--) - hash = ((hash << 5) + hash) ^ *(str++); + while (1) + { + const char ch = *(str++); + if (ch == 0) + break; + hash = ((hash << 5) + hash) ^ ch; + } /* while */ return hash; } /* __PHYSFS_hashString */ +PHYSFS_uint32 __PHYSFS_hashStringCaseFold(const char *str) +{ + PHYSFS_uint32 hash = 5381; + while (1) + { + const PHYSFS_uint32 cp = __PHYSFS_utf8codepoint(&str); + if (cp == 0) + break; + else + { + PHYSFS_uint32 folded[3]; + const int numbytes = (int) (PHYSFS_caseFold(cp, folded) * sizeof (PHYSFS_uint32)); + const char *bytes = (const char *) folded; + int i; + for (i = 0; i < numbytes; i++) + hash = ((hash << 5) + hash) ^ *(bytes++); + } /* else */ + } /* while */ + + return hash; +} /* __PHYSFS_hashStringCaseFold */ + + +PHYSFS_uint32 __PHYSFS_hashStringCaseFoldUSAscii(const char *str) +{ + PHYSFS_uint32 hash = 5381; + while (1) + { + char ch = *(str++); + if (ch == 0) + break; + else if ((ch >= 'A') && (ch <= 'Z')) + ch -= ('A' - 'a'); + + hash = ((hash << 5) + hash) ^ ch; + } /* while */ + return hash; +} /* __PHYSFS_hashStringCaseFoldUSAscii */ + + /* MAKE SURE you hold stateLock before calling this! */ static int doRegisterArchiver(const PHYSFS_Archiver *_archiver) { @@ -1733,10 +1780,10 @@ int PHYSFS_setRoot(const char *archive, const char *subdir) if (i->root) allocator.Free(i->root); i->root = ptr; - i->rootlen = len; + i->rootlen = strlen(i->root); /* in case sanitizePlatformIndependentPath changed subdir */ - if (longest_root < len) - longest_root = len; + if (longest_root < i->rootlen) + longest_root = i->rootlen; } /* else */ break; @@ -2107,10 +2154,10 @@ static int verifyPath(DirHandle *h, char **_fname, int allowMissing) if (h->root) { const int isempty = (*fname == '\0'); - fname -= h->rootlen - 1; + fname -= h->rootlen + (isempty ? 0 : 1); strcpy(fname, h->root); if (!isempty) - fname[h->rootlen - 2] = '/'; + fname[h->rootlen] = '/'; *_fname = fname; } /* if */ @@ -2187,7 +2234,12 @@ static int doMkdir(const char *_dname, char *dname) const int rc = h->funcs->stat(h->opaque, dname, &statbuf); if ((!rc) && (currentErrorCode() == PHYSFS_ERR_NOT_FOUND)) exists = 0; - retval = ((rc) && (statbuf.filetype == PHYSFS_FILETYPE_DIRECTORY)); + /* verifyPath made sure that (dname) doesn't have symlinks if they aren't + allowed, but it's possible the mounted writeDir itself has symlinks in it, + (for example "/var" on iOS is a symlink, and the prefpath will be somewhere + under that)...if we mounted that writeDir, we must allow those symlinks here + unconditionally. */ + retval = ( (rc) && ((statbuf.filetype == PHYSFS_FILETYPE_DIRECTORY) || (statbuf.filetype == PHYSFS_FILETYPE_SYMLINK)) ); } /* if */ if (!exists) @@ -2265,10 +2317,10 @@ static DirHandle *getRealDirHandle(const char *_fname) BAIL_IF(!_fname, PHYSFS_ERR_INVALID_ARGUMENT, NULL); __PHYSFS_platformGrabMutex(stateLock); - len = strlen(_fname) + longest_root + 1; + len = strlen(_fname) + longest_root + 2; allocated_fname = __PHYSFS_smallAlloc(len); BAIL_IF_MUTEX(!allocated_fname, PHYSFS_ERR_OUT_OF_MEMORY, stateLock, NULL); - fname = allocated_fname + longest_root; + fname = allocated_fname + longest_root + 1; if (sanitizePlatformIndependentPath(_fname, fname)) { DirHandle *i; @@ -2496,10 +2548,10 @@ int PHYSFS_enumerate(const char *_fn, PHYSFS_EnumerateCallback cb, void *data) __PHYSFS_platformGrabMutex(stateLock); - len = strlen(_fn) + longest_root + 1; + len = strlen(_fn) + longest_root + 2; allocated_fname = (char *) __PHYSFS_smallAlloc(len); BAIL_IF_MUTEX(!allocated_fname, PHYSFS_ERR_OUT_OF_MEMORY, stateLock, 0); - fname = allocated_fname + longest_root; + fname = allocated_fname + longest_root + 1; if (!sanitizePlatformIndependentPath(_fn, fname)) retval = PHYSFS_ENUM_STOP; else @@ -2702,10 +2754,10 @@ PHYSFS_File *PHYSFS_openRead(const char *_fname) BAIL_IF_MUTEX(!searchPath, PHYSFS_ERR_NOT_FOUND, stateLock, 0); - len = strlen(_fname) + longest_root + 1; + len = strlen(_fname) + longest_root + 2; allocated_fname = (char *) __PHYSFS_smallAlloc(len); BAIL_IF_MUTEX(!allocated_fname, PHYSFS_ERR_OUT_OF_MEMORY, stateLock, 0); - fname = allocated_fname + longest_root; + fname = allocated_fname + longest_root + 1; if (sanitizePlatformIndependentPath(_fname, fname)) { @@ -2731,13 +2783,15 @@ PHYSFS_File *PHYSFS_openRead(const char *_fname) io->destroy(io); PHYSFS_setErrorCode(PHYSFS_ERR_OUT_OF_MEMORY); } /* if */ - - memset(fh, '\0', sizeof (FileHandle)); - fh->io = io; - fh->forReading = 1; - fh->dirHandle = i; - fh->next = openReadList; - openReadList = fh; + else + { + memset(fh, '\0', sizeof (FileHandle)); + fh->io = io; + fh->forReading = 1; + fh->dirHandle = i; + fh->next = openReadList; + openReadList = fh; + } /* else */ } /* if */ } /* if */ @@ -3091,10 +3145,10 @@ int PHYSFS_stat(const char *_fname, PHYSFS_Stat *stat) stat->readonly = 1; __PHYSFS_platformGrabMutex(stateLock); - len = strlen(_fname) + longest_root + 1; + len = strlen(_fname) + longest_root + 2; allocated_fname = (char *) __PHYSFS_smallAlloc(len); BAIL_IF_MUTEX(!allocated_fname, PHYSFS_ERR_OUT_OF_MEMORY, stateLock, 0); - fname = allocated_fname + longest_root; + fname = allocated_fname + longest_root + 1; if (sanitizePlatformIndependentPath(_fname, fname)) { @@ -3227,7 +3281,7 @@ static void setDefaultAllocator(void) } /* setDefaultAllocator */ -int __PHYSFS_DirTreeInit(__PHYSFS_DirTree *dt, const size_t entrylen) +int __PHYSFS_DirTreeInit(__PHYSFS_DirTree *dt, const size_t entrylen, const int case_sensitive, const int only_usascii) { static char rootpath[2] = { '/', '\0' }; size_t alloclen; @@ -3235,6 +3289,8 @@ int __PHYSFS_DirTreeInit(__PHYSFS_DirTree *dt, const size_t entrylen) assert(entrylen >= sizeof (__PHYSFS_DirTreeEntry)); memset(dt, '\0', sizeof (*dt)); + dt->case_sensitive = case_sensitive; + dt->only_usascii = only_usascii; dt->root = (__PHYSFS_DirTreeEntry *) allocator.Malloc(entrylen); BAIL_IF(!dt->root, PHYSFS_ERR_OUT_OF_MEMORY, 0); @@ -3255,9 +3311,10 @@ int __PHYSFS_DirTreeInit(__PHYSFS_DirTree *dt, const size_t entrylen) } /* __PHYSFS_DirTreeInit */ -static inline PHYSFS_uint32 hashPathName(__PHYSFS_DirTree *dt, const char *name) +static PHYSFS_uint32 hashPathName(__PHYSFS_DirTree *dt, const char *name) { - return __PHYSFS_hashString(name, strlen(name)) % dt->hashBuckets; + const PHYSFS_uint32 hashval = dt->case_sensitive ? __PHYSFS_hashString(name) : dt->only_usascii ? __PHYSFS_hashStringCaseFoldUSAscii(name) : __PHYSFS_hashStringCaseFold(name); + return hashval % dt->hashBuckets; } /* hashPathName */ @@ -3318,6 +3375,7 @@ void *__PHYSFS_DirTreeAdd(__PHYSFS_DirTree *dt, char *name, const int isdir) /* Find the __PHYSFS_DirTreeEntry for a path in platform-independent notation. */ void *__PHYSFS_DirTreeFind(__PHYSFS_DirTree *dt, const char *path) { + const int cs = dt->case_sensitive; PHYSFS_uint32 hashval; __PHYSFS_DirTreeEntry *prev = NULL; __PHYSFS_DirTreeEntry *retval; @@ -3328,7 +3386,8 @@ void *__PHYSFS_DirTreeFind(__PHYSFS_DirTree *dt, const char *path) hashval = hashPathName(dt, path); for (retval = dt->hash[hashval]; retval; retval = retval->hashnext) { - if (strcmp(retval->name, path) == 0) + const int cmp = cs ? strcmp(retval->name, path) : PHYSFS_utf8stricmp(retval->name, path); + if (cmp == 0) { if (prev != NULL) /* move this to the front of the list */ { diff --git a/src/libraries/physfs/physfs.h b/src/libraries/physfs/physfs.h index 49d49dae4..6e55ce4e5 100644 --- a/src/libraries/physfs/physfs.h +++ b/src/libraries/physfs/physfs.h @@ -145,7 +145,7 @@ * - .ISO (ISO9660 files, CD-ROM images) * - .GRP (Build Engine groupfile archives) * - .PAK (Quake I/II archive format) - * - .HOG (Descent I/II HOG file archives) + * - .HOG (Descent I/II/III HOG file archives) * - .MVL (Descent II movielib archives) * - .WAD (DOOM engine archives) * - .VDF (Gothic I/II engine archives) @@ -225,7 +225,9 @@ extern "C" { #if defined(PHYSFS_DECL) /* do nothing. */ -#elif defined(_MSC_VER) +#elif defined(PHYSFS_STATIC) +#define PHYSFS_DECL /**/ +#elif defined(_WIN32) || defined(__OS2__) #define PHYSFS_DECL __declspec(dllexport) #elif defined(__SUNPRO_C) #define PHYSFS_DECL __global @@ -433,7 +435,7 @@ typedef struct PHYSFS_Version #ifndef DOXYGEN_SHOULD_IGNORE_THIS #define PHYSFS_VER_MAJOR 3 -#define PHYSFS_VER_MINOR 1 +#define PHYSFS_VER_MINOR 2 #define PHYSFS_VER_PATCH 0 #endif /* DOXYGEN_SHOULD_IGNORE_THIS */ @@ -493,6 +495,14 @@ typedef struct PHYSFS_Version PHYSFS_DECL void PHYSFS_getLinkedVersion(PHYSFS_Version *ver); +#ifdef __ANDROID__ +typedef struct PHYSFS_AndroidInit +{ + void *jnienv; + void *context; +} PHYSFS_AndroidInit; +#endif + /** * \fn int PHYSFS_init(const char *argv0) * \brief Initialize the PhysicsFS library. @@ -502,11 +512,22 @@ PHYSFS_DECL void PHYSFS_getLinkedVersion(PHYSFS_Version *ver); * This should be called prior to any attempts to change your process's * current working directory. * + * \warning On Android, argv0 should be a non-NULL pointer to a + * PHYSFS_AndroidInit struct. This struct must hold a valid JNIEnv * + * and a JNI jobject of a Context (either the application context or + * the current Activity is fine). Both are cast to a void * so we + * don't need jni.h included wherever physfs.h is. PhysicsFS + * uses these objects to query some system details. PhysicsFS does + * not hold a reference to the JNIEnv or Context past the call to + * PHYSFS_init(). If you pass a NULL here, PHYSFS_init can still + * succeed, but PHYSFS_getBaseDir() and PHYSFS_getPrefDir() will be + * incorrect. + * * \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. + * something in here. Many Unix-like systems _need_ to pass argv[0] + * from main() in here. See warning about Android, too! * \return nonzero on success, zero on error. Specifics of the error can be * gleaned from PHYSFS_getLastError(). * @@ -762,6 +783,15 @@ PHYSFS_DECL char **PHYSFS_getCdRomDirs(void); * * You should probably use the base dir in your search path. * + * \warning On most platforms, this is a directory; on Android, this gives + * you the path to the app's package (APK) file. As APK files are + * just .zip files, you can mount them in PhysicsFS like regular + * directories. You'll probably want to call + * PHYSFS_setRoot(basedir, "/assets") after mounting to make your + * app's actual data available directly without all the Android + * metadata and directory offset. Note that if you passed a NULL to + * PHYSFS_init(), you will not get the APK file here. + * * \return READ ONLY string of base dir in platform-dependent notation. * * \sa PHYSFS_getPrefDir @@ -2702,10 +2732,10 @@ typedef PHYSFS_EnumerateCallbackResult (*PHYSFS_EnumerateCallback)(void *data, * * \code * - * static int printDir(void *data, const char *origdir, const char *fname) + * static PHYSFS_EnumerateCallbackResult printDir(void *data, const char *origdir, const char *fname) * { * printf(" * We've got [%s] in [%s].\n", fname, origdir); - * return 1; // give me more data, please. + * return PHYSFS_ENUM_OK; // give me more data, please. * } * * // ... diff --git a/src/libraries/physfs/physfs_archiver_7z.c b/src/libraries/physfs/physfs_archiver_7z.c index 914f69164..44be3c9a9 100644 --- a/src/libraries/physfs/physfs_archiver_7z.c +++ b/src/libraries/physfs/physfs_archiver_7z.c @@ -185,7 +185,7 @@ static int szipLoadEntries(SZIPinfo *info) { int retval = 0; - if (__PHYSFS_DirTreeInit(&info->tree, sizeof (SZIPentry))) + if (__PHYSFS_DirTreeInit(&info->tree, sizeof (SZIPentry), 1, 0)) { const PHYSFS_uint32 count = info->db.NumFiles; PHYSFS_uint32 i; @@ -285,13 +285,16 @@ static PHYSFS_Io *SZIP_openRead(void *opaque, const char *path) &blockIndex, &outBuffer, &outBufferSize, &offset, &outSizeProcessed, alloc, alloc); GOTO_IF(rc != SZ_OK, szipErrorCode(rc), SZIP_openRead_failed); + GOTO_IF(outBuffer == NULL, PHYSFS_ERR_OUT_OF_MEMORY, SZIP_openRead_failed); io->destroy(io); io = NULL; - buf = allocator.Malloc(outSizeProcessed); - GOTO_IF(rc != SZ_OK, PHYSFS_ERR_OUT_OF_MEMORY, SZIP_openRead_failed); - memcpy(buf, outBuffer + offset, outSizeProcessed); + buf = allocator.Malloc(outSizeProcessed ? outSizeProcessed : 1); + GOTO_IF(buf == NULL, PHYSFS_ERR_OUT_OF_MEMORY, SZIP_openRead_failed); + + if (outSizeProcessed > 0) + memcpy(buf, outBuffer + offset, outSizeProcessed); alloc->Free(alloc, outBuffer); outBuffer = NULL; diff --git a/src/libraries/physfs/physfs_archiver_grp.c b/src/libraries/physfs/physfs_archiver_grp.c index 758475e49..9a2978a6c 100644 --- a/src/libraries/physfs/physfs_archiver_grp.c +++ b/src/libraries/physfs/physfs_archiver_grp.c @@ -76,7 +76,7 @@ static void *GRP_openArchive(PHYSFS_Io *io, const char *name, BAIL_IF_ERRPASS(!__PHYSFS_readAll(io, &count, sizeof(count)), NULL); count = PHYSFS_swapULE32(count); - unpkarc = UNPK_openArchive(io); + unpkarc = UNPK_openArchive(io, 0, 1); BAIL_IF_ERRPASS(!unpkarc, NULL); if (!grpLoadEntries(io, count, unpkarc)) diff --git a/src/libraries/physfs/physfs_archiver_hog.c b/src/libraries/physfs/physfs_archiver_hog.c index 832209f86..a8184780a 100644 --- a/src/libraries/physfs/physfs_archiver_hog.c +++ b/src/libraries/physfs/physfs_archiver_hog.c @@ -1,9 +1,9 @@ /* * HOG support routines for PhysicsFS. * - * This driver handles Descent I/II HOG archives. + * This driver handles Descent I/II/III HOG archives. * - * The format is very simple: + * The Descent I/II 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 @@ -23,10 +23,23 @@ * * (That info is from http://www.descent2.com/ddn/specs/hog/) * + * Descent 3 moved to HOG2 format, which starts with the chars "HOG2", + * then 32-bits for the number of contained files, 32 bits for the offset + * to the first file's data, then 56 bytes of 0xFF (reserved?). Then for + * each file, there's 36 bytes for filename (null-terminated, rest of bytes + * are garbage), 32-bits unknown/reserved (always zero?), 32-bits of length + * of file data, 32-bits of time since Unix epoch. Then immediately following, + * for each file is their uncompressed content, you can find its offset + * by starting at the initial data offset and adding the filesize of each + * prior file. + * + * This information was found at: + * https://web.archive.org/web/20020213004051/http://descent-3.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. + * This file written by Bradley Bell and Ryan C. Gordon. */ #define __PHYSICSFS_INTERNAL__ @@ -34,7 +47,15 @@ #if PHYSFS_SUPPORTS_HOG -static int hogLoadEntries(PHYSFS_Io *io, void *arc) +static int readui32(PHYSFS_Io *io, PHYSFS_uint32 *val) +{ + PHYSFS_uint32 v; + BAIL_IF_ERRPASS(!__PHYSFS_readAll(io, &v, sizeof (v)), 0); + *val = PHYSFS_swapULE32(v); + return 1; +} /* readui32 */ + +static int hog1LoadEntries(PHYSFS_Io *io, void *arc) { const PHYSFS_uint64 iolen = io->length(io); PHYSFS_uint32 pos = 3; @@ -45,11 +66,10 @@ static int hogLoadEntries(PHYSFS_Io *io, void *arc) char name[13]; BAIL_IF_ERRPASS(!__PHYSFS_readAll(io, name, 13), 0); - BAIL_IF_ERRPASS(!__PHYSFS_readAll(io, &size, 4), 0); + BAIL_IF_ERRPASS(!readui32(io, &size), 0); name[12] = '\0'; /* just in case. */ pos += 13 + 4; - size = PHYSFS_swapULE32(size); BAIL_IF_ERRPASS(!UNPK_addEntry(arc, name, 0, -1, -1, pos, size), 0); pos += size; @@ -60,24 +80,60 @@ static int hogLoadEntries(PHYSFS_Io *io, void *arc) return 1; } /* hogLoadEntries */ +static int hog2LoadEntries(PHYSFS_Io *io, void *arc) +{ + PHYSFS_uint32 numfiles; + PHYSFS_uint32 pos; + PHYSFS_uint32 i; + + BAIL_IF_ERRPASS(!readui32(io, &numfiles), 0); + BAIL_IF_ERRPASS(!readui32(io, &pos), 0); + BAIL_IF_ERRPASS(!io->seek(io, 68), 0); /* skip to end of header. */ + + for (i = 0; i < numfiles; i++) { + char name[37]; + PHYSFS_uint32 reserved; + PHYSFS_uint32 size; + PHYSFS_uint32 mtime; + BAIL_IF_ERRPASS(!__PHYSFS_readAll(io, name, 36), 0); + BAIL_IF_ERRPASS(!readui32(io, &reserved), 0); + BAIL_IF_ERRPASS(!readui32(io, &size), 0); + BAIL_IF_ERRPASS(!readui32(io, &mtime), 0); + name[36] = '\0'; /* just in case */ + BAIL_IF_ERRPASS(!UNPK_addEntry(arc, name, 0, mtime, mtime, pos, size), 0); + pos += size; + } + + return 1; +} /* hog2LoadEntries */ + static void *HOG_openArchive(PHYSFS_Io *io, const char *name, int forWriting, int *claimed) { PHYSFS_uint8 buf[3]; void *unpkarc = NULL; + int hog1 = 0; assert(io != NULL); /* shouldn't ever happen. */ BAIL_IF(forWriting, PHYSFS_ERR_READ_ONLY, NULL); BAIL_IF_ERRPASS(!__PHYSFS_readAll(io, buf, 3), NULL); - BAIL_IF(memcmp(buf, "DHF", 3) != 0, PHYSFS_ERR_UNSUPPORTED, NULL); + + if (memcmp(buf, "DHF", 3) == 0) + hog1 = 1; /* original HOG (Descent 1 and 2) archive */ + else + { + BAIL_IF(memcmp(buf, "HOG", 3) != 0, PHYSFS_ERR_UNSUPPORTED, NULL); /* Not HOG2 */ + BAIL_IF_ERRPASS(!__PHYSFS_readAll(io, buf, 1), NULL); + BAIL_IF(buf[0] != '2', PHYSFS_ERR_UNSUPPORTED, NULL); /* Not HOG2 */ + } /* else */ *claimed = 1; - unpkarc = UNPK_openArchive(io); + unpkarc = UNPK_openArchive(io, 0, 1); BAIL_IF_ERRPASS(!unpkarc, NULL); - if (!hogLoadEntries(io, unpkarc)) + if (!(hog1 ? hog1LoadEntries(io, unpkarc) : hog2LoadEntries(io, unpkarc))) { UNPK_abandonArchive(unpkarc); return NULL; @@ -92,7 +148,7 @@ const PHYSFS_Archiver __PHYSFS_Archiver_HOG = CURRENT_PHYSFS_ARCHIVER_API_VERSION, { "HOG", - "Descent I/II HOG file format", + "Descent I/II/III HOG file format", "Bradley Bell ", "https://icculus.org/physfs/", 0, /* supportsSymlinks */ diff --git a/src/libraries/physfs/physfs_archiver_iso9660.c b/src/libraries/physfs/physfs_archiver_iso9660.c index 965c83f65..e92d677fe 100644 --- a/src/libraries/physfs/physfs_archiver_iso9660.c +++ b/src/libraries/physfs/physfs_archiver_iso9660.c @@ -251,7 +251,7 @@ static int parseVolumeDescriptor(PHYSFS_Io *io, PHYSFS_uint64 *_rootpos, pos += 2048; /* each volume descriptor is 2048 bytes */ BAIL_IF_ERRPASS(!__PHYSFS_readAll(io, &type, 1), 0); - BAIL_IF_ERRPASS(!__PHYSFS_readAll(io, &identifier, 5), 0); + BAIL_IF_ERRPASS(!__PHYSFS_readAll(io, identifier, 5), 0); if (memcmp(identifier, "CD001", 5) != 0) /* maybe not an iso? */ { @@ -346,7 +346,8 @@ static void *ISO9660_openArchive(PHYSFS_Io *io, const char *filename, if (!parseVolumeDescriptor(io, &rootpos, &len, &joliet, claimed)) return NULL; - unpkarc = UNPK_openArchive(io); + /* !!! FIXME: check case_sensitive and only_usascii params for this archive. */ + unpkarc = UNPK_openArchive(io, 1, 0); BAIL_IF_ERRPASS(!unpkarc, NULL); if (!iso9660LoadEntries(io, joliet, "", rootpos, rootpos + len, unpkarc)) diff --git a/src/libraries/physfs/physfs_archiver_mvl.c b/src/libraries/physfs/physfs_archiver_mvl.c index 78b59f1a1..7a5c43262 100644 --- a/src/libraries/physfs/physfs_archiver_mvl.c +++ b/src/libraries/physfs/physfs_archiver_mvl.c @@ -70,7 +70,7 @@ static void *MVL_openArchive(PHYSFS_Io *io, const char *name, BAIL_IF_ERRPASS(!__PHYSFS_readAll(io, &count, sizeof(count)), NULL); count = PHYSFS_swapULE32(count); - unpkarc = UNPK_openArchive(io); + unpkarc = UNPK_openArchive(io, 0, 1); BAIL_IF_ERRPASS(!unpkarc, NULL); if (!mvlLoadEntries(io, count, unpkarc)) diff --git a/src/libraries/physfs/physfs_archiver_qpak.c b/src/libraries/physfs/physfs_archiver_qpak.c index 15a5f2dfe..ddca271f6 100644 --- a/src/libraries/physfs/physfs_archiver_qpak.c +++ b/src/libraries/physfs/physfs_archiver_qpak.c @@ -86,7 +86,8 @@ static void *QPAK_openArchive(PHYSFS_Io *io, const char *name, BAIL_IF_ERRPASS(!io->seek(io, pos), NULL); - unpkarc = UNPK_openArchive(io); + /* !!! FIXME: check case_sensitive and only_usascii params for this archive. */ + unpkarc = UNPK_openArchive(io, 1, 0); BAIL_IF_ERRPASS(!unpkarc, NULL); if (!qpakLoadEntries(io, count, unpkarc)) diff --git a/src/libraries/physfs/physfs_archiver_slb.c b/src/libraries/physfs/physfs_archiver_slb.c index 4fc28d40c..58f3bc807 100644 --- a/src/libraries/physfs/physfs_archiver_slb.c +++ b/src/libraries/physfs/physfs_archiver_slb.c @@ -36,7 +36,7 @@ static int slbLoadEntries(PHYSFS_Io *io, const PHYSFS_uint32 count, void *arc) BAIL_IF(backslash != '\\', PHYSFS_ERR_CORRUPT, 0); /* read the rest of the buffer, 63 bytes */ - BAIL_IF_ERRPASS(!__PHYSFS_readAll(io, &name, 63), 0); + BAIL_IF_ERRPASS(!__PHYSFS_readAll(io, name, 63), 0); name[63] = '\0'; /* in case the name lacks the null terminator */ /* convert backslashes */ @@ -94,7 +94,8 @@ static void *SLB_openArchive(PHYSFS_Io *io, const char *name, /* seek to the table of contents */ BAIL_IF_ERRPASS(!io->seek(io, tocPos), NULL); - unpkarc = UNPK_openArchive(io); + /* !!! FIXME: check case_sensitive and only_usascii params for this archive. */ + unpkarc = UNPK_openArchive(io, 1, 0); BAIL_IF_ERRPASS(!unpkarc, NULL); if (!slbLoadEntries(io, count, unpkarc)) diff --git a/src/libraries/physfs/physfs_archiver_unpacked.c b/src/libraries/physfs/physfs_archiver_unpacked.c index 575efef32..fbb12a041 100644 --- a/src/libraries/physfs/physfs_archiver_unpacked.c +++ b/src/libraries/physfs/physfs_archiver_unpacked.c @@ -285,12 +285,12 @@ void *UNPK_addEntry(void *opaque, char *name, const int isdir, } /* UNPK_addEntry */ -void *UNPK_openArchive(PHYSFS_Io *io) +void *UNPK_openArchive(PHYSFS_Io *io, const int case_sensitive, const int only_usascii) { UNPKinfo *info = (UNPKinfo *) allocator.Malloc(sizeof (UNPKinfo)); BAIL_IF(!info, PHYSFS_ERR_OUT_OF_MEMORY, NULL); - if (!__PHYSFS_DirTreeInit(&info->tree, sizeof (UNPKentry))) + if (!__PHYSFS_DirTreeInit(&info->tree, sizeof (UNPKentry), case_sensitive, only_usascii)) { allocator.Free(info); return NULL; diff --git a/src/libraries/physfs/physfs_archiver_vdf.c b/src/libraries/physfs/physfs_archiver_vdf.c index 99bbb2a60..6d3a23f8f 100644 --- a/src/libraries/physfs/physfs_archiver_vdf.c +++ b/src/libraries/physfs/physfs_archiver_vdf.c @@ -129,7 +129,8 @@ static void *VDF_openArchive(PHYSFS_Io *io, const char *name, BAIL_IF_ERRPASS(!io->seek(io, rootCatOffset), NULL); - unpkarc = UNPK_openArchive(io); + /* !!! FIXME: check case_sensitive and only_usascii params for this archive. */ + unpkarc = UNPK_openArchive(io, 1, 0); BAIL_IF_ERRPASS(!unpkarc, NULL); if (!vdfLoadEntries(io, count, vdfDosTimeToEpoch(timestamp), unpkarc)) diff --git a/src/libraries/physfs/physfs_archiver_wad.c b/src/libraries/physfs/physfs_archiver_wad.c index b094c5b82..d3ae04565 100644 --- a/src/libraries/physfs/physfs_archiver_wad.c +++ b/src/libraries/physfs/physfs_archiver_wad.c @@ -95,7 +95,7 @@ static void *WAD_openArchive(PHYSFS_Io *io, const char *name, BAIL_IF_ERRPASS(!io->seek(io, directoryOffset), 0); - unpkarc = UNPK_openArchive(io); + unpkarc = UNPK_openArchive(io, 0, 1); BAIL_IF_ERRPASS(!unpkarc, NULL); if (!wadLoadEntries(io, count, unpkarc)) diff --git a/src/libraries/physfs/physfs_archiver_zip.c b/src/libraries/physfs/physfs_archiver_zip.c index 9972628fd..7327a6195 100644 --- a/src/libraries/physfs/physfs_archiver_zip.c +++ b/src/libraries/physfs/physfs_archiver_zip.c @@ -15,6 +15,11 @@ #include #include +#if (PHYSFS_BYTEORDER == PHYSFS_LIL_ENDIAN) +#define MINIZ_LITTLE_ENDIAN 1 +#else +#define MINIZ_LITTLE_ENDIAN 0 +#endif #include "physfs_miniz.h" /* @@ -568,7 +573,7 @@ static PHYSFS_sint64 zip_find_end_of_central_dir(PHYSFS_Io *io, PHYSFS_sint64 *l { if (!__PHYSFS_readAll(io, buf, maxread - 4)) return -1; - memcpy(&buf[maxread - 4], &extra, sizeof (extra)); + memcpy(&buf[maxread - 4], extra, sizeof (extra)); totalread += maxread - 4; } /* if */ else @@ -578,7 +583,7 @@ static PHYSFS_sint64 zip_find_end_of_central_dir(PHYSFS_Io *io, PHYSFS_sint64 *l totalread += maxread; } /* else */ - memcpy(&extra, buf, sizeof (extra)); + memcpy(extra, buf, sizeof (extra)); for (i = maxread - 4; i > 0; i--) { @@ -833,7 +838,10 @@ static int zip_parse_local(PHYSFS_Io *io, ZIPentry *entry) BAIL_IF_ERRPASS(!readui32(io, &ui32), 0); BAIL_IF(ui32 != ZIP_LOCAL_FILE_SIG, PHYSFS_ERR_CORRUPT, 0); BAIL_IF_ERRPASS(!readui16(io, &ui16), 0); - BAIL_IF(ui16 != entry->version_needed, PHYSFS_ERR_CORRUPT, 0); + /* Windows Explorer might rewrite the entire central directory, setting + this field to 2.0/MS-DOS for all files, so favor the local version, + which it leaves intact if it didn't alter that specific file. */ + entry->version_needed = ui16; BAIL_IF_ERRPASS(!readui16(io, &ui16), 0); /* general bits. */ BAIL_IF_ERRPASS(!readui16(io, &ui16), 0); BAIL_IF(ui16 != entry->compression_method, PHYSFS_ERR_CORRUPT, 0); @@ -1482,7 +1490,7 @@ static void *ZIP_openArchive(PHYSFS_Io *io, const char *name, if (!zip_parse_end_of_central_dir(info, &dstart, &cdir_ofs, &count)) goto ZIP_openarchive_failed; - else if (!__PHYSFS_DirTreeInit(&info->tree, sizeof (ZIPentry))) + else if (!__PHYSFS_DirTreeInit(&info->tree, sizeof (ZIPentry), 1, 0)) goto ZIP_openarchive_failed; root = (ZIPentry *) info->tree.root; diff --git a/src/libraries/physfs/physfs_internal.h b/src/libraries/physfs/physfs_internal.h index 29126234a..2200d4d52 100644 --- a/src/libraries/physfs/physfs_internal.h +++ b/src/libraries/physfs/physfs_internal.h @@ -38,7 +38,7 @@ #include #endif -#ifdef PHYSFS_PLATFORM_SOLARIS +#if defined(PHYSFS_PLATFORM_SOLARIS) || defined(PHYSFS_PLATFORM_LINUX) #include #endif @@ -69,7 +69,7 @@ extern "C" { All file-private symbols need to be marked "static". Everything shared between PhysicsFS sources needs to be in this file between the visibility pragma blocks. */ -#if PHYSFS_MINIMUM_GCC_VERSION(4,0) || defined(__clang__) +#if !defined(_WIN32) && (PHYSFS_MINIMUM_GCC_VERSION(4,0) || defined(__clang__)) #define PHYSFS_HAVE_PRAGMA_VISIBILITY 1 #endif @@ -95,6 +95,7 @@ extern const PHYSFS_Archiver __PHYSFS_Archiver_VDF; /* a real C99-compliant snprintf() is in Visual Studio 2015, but just use this everywhere for binary compatibility. */ #if defined(_MSC_VER) +#include int __PHYSFS_msvc_vsnprintf(char *outBuf, size_t size, const char *format, va_list ap); int __PHYSFS_msvc_snprintf(char *outBuf, size_t size, const char *format, ...); #define vsnprintf __PHYSFS_msvc_vsnprintf @@ -108,14 +109,24 @@ const void *__PHYSFS_winrtCalcPrefDir(void); #endif /* atomic operations. */ +/* increment/decrement operations return the final incremented/decremented value. */ #if defined(_MSC_VER) && (_MSC_VER >= 1500) #include __PHYSFS_COMPILE_TIME_ASSERT(LongEqualsInt, sizeof (int) == sizeof (long)); #define __PHYSFS_ATOMIC_INCR(ptrval) _InterlockedIncrement((long*)(ptrval)) #define __PHYSFS_ATOMIC_DECR(ptrval) _InterlockedDecrement((long*)(ptrval)) #elif defined(__clang__) || (defined(__GNUC__) && (((__GNUC__ * 10000) + (__GNUC_MINOR__ * 100)) >= 40100)) -#define __PHYSFS_ATOMIC_INCR(ptrval) __sync_fetch_and_add(ptrval, 1) -#define __PHYSFS_ATOMIC_DECR(ptrval) __sync_fetch_and_add(ptrval, -1) +#define __PHYSFS_ATOMIC_INCR(ptrval) __sync_add_and_fetch(ptrval, 1) +#define __PHYSFS_ATOMIC_DECR(ptrval) __sync_add_and_fetch(ptrval, -1) +#elif defined(__WATCOMC__) && defined(__386__) +extern __inline int _xadd_watcom(volatile int *a, int v); +#pragma aux _xadd_watcom = \ + "lock xadd [ecx], eax" \ + parm [ecx] [eax] \ + value [eax] \ + modify exact [eax]; +#define __PHYSFS_ATOMIC_INCR(ptrval) (_xadd_watcom(ptrval, 1)+1) +#define __PHYSFS_ATOMIC_DECR(ptrval) (_xadd_watcom(ptrval, -1)-1) #else #define PHYSFS_NEED_ATOMIC_OP_FALLBACK 1 int __PHYSFS_ATOMIC_INCR(int *ptrval); @@ -213,6 +224,7 @@ extern void SZIP_global_init(void); /* The latest supported PHYSFS_Archiver::version value. */ #define CURRENT_PHYSFS_ARCHIVER_API_VERSION 0 + /* This byteorder stuff was lifted from SDL. https://www.libsdl.org/ */ #define PHYSFS_LIL_ENDIAN 1234 #define PHYSFS_BIG_ENDIAN 4321 @@ -220,11 +232,26 @@ extern void SZIP_global_init(void); #ifdef __linux__ #include #define PHYSFS_BYTEORDER __BYTE_ORDER -#else /* __linux__ */ +#elif defined(__OpenBSD__) || defined(__DragonFly__) +#include +#define PHYSFS_BYTEORDER BYTE_ORDER +#elif defined(__FreeBSD__) || defined(__NetBSD__) +#include +#define PHYSFS_BYTEORDER BYTE_ORDER +/* predefs from newer gcc and clang versions: */ +#elif defined(__ORDER_LITTLE_ENDIAN__) && defined(__ORDER_BIG_ENDIAN__) && defined(__BYTE_ORDER__) +#if (__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__) +#define PHYSFS_BYTEORDER PHYSFS_LIL_ENDIAN +#elif (__BYTE_ORDER__ == __ORDER_BIG_ENDIAN__) +#define PHYSFS_BYTEORDER PHYSFS_BIG_ENDIAN +#else +#error Unsupported endianness +#endif /**/ +#else #if defined(__hppa__) || \ defined(__m68k__) || defined(mc68000) || defined(_M_M68K) || \ - (defined(__MIPS__) && defined(__MISPEB__)) || \ - defined(__ppc__) || defined(__POWERPC__) || defined(_M_PPC) || \ + (defined(__MIPS__) && defined(__MIPSEB__)) || \ + defined(__ppc__) || defined(__POWERPC__) || defined(__powerpc__) || defined(__PPC__) || \ defined(__sparc__) #define PHYSFS_BYTEORDER PHYSFS_BIG_ENDIAN #else @@ -312,7 +339,18 @@ char *__PHYSFS_strdup(const char *str); /* * Give a hash value for a C string (uses djb's xor hashing algorithm). */ -PHYSFS_uint32 __PHYSFS_hashString(const char *str, size_t len); +PHYSFS_uint32 __PHYSFS_hashString(const char *str); + +/* + * Give a hash value for a C string (uses djb's xor hashing algorithm), case folding as it goes. + */ +PHYSFS_uint32 __PHYSFS_hashStringCaseFold(const char *str); + +/* + * Give a hash value for a C string (uses djb's xor hashing algorithm), case folding as it goes, + * assuming that this is only US-ASCII chars (one byte per char, only 'A' through 'Z' need folding). + */ +PHYSFS_uint32 __PHYSFS_hashStringCaseFoldUSAscii(const char *str); /* @@ -348,9 +386,10 @@ int __PHYSFS_readAll(PHYSFS_Io *io, void *buf, const size_t len); /* These are shared between some archivers. */ +/* LOTS of legacy formats that only use US ASCII, not actually UTF-8, so let them optimize here. */ +void *UNPK_openArchive(PHYSFS_Io *io, const int case_sensitive, const int only_usascii); void UNPK_abandonArchive(void *opaque); void UNPK_closeArchive(void *opaque); -void *UNPK_openArchive(PHYSFS_Io *io); void *UNPK_addEntry(void *opaque, char *name, const int isdir, const PHYSFS_sint64 ctime, const PHYSFS_sint64 mtime, const PHYSFS_uint64 pos, const PHYSFS_uint64 len); @@ -382,10 +421,13 @@ typedef struct __PHYSFS_DirTree __PHYSFS_DirTreeEntry **hash; /* all entries hashed for fast lookup. */ size_t hashBuckets; /* number of buckets in hash. */ size_t entrylen; /* size in bytes of entries (including subclass). */ + int case_sensitive; /* non-zero to treat entries as case-sensitive in DirTreeFind */ + int only_usascii; /* non-zero to treat paths as US ASCII only (one byte per char, only 'A' through 'Z' are considered for case folding). */ } __PHYSFS_DirTree; -int __PHYSFS_DirTreeInit(__PHYSFS_DirTree *dt, const size_t entrylen); +/* LOTS of legacy formats that only use US ASCII, not actually UTF-8, so let them optimize here. */ +int __PHYSFS_DirTreeInit(__PHYSFS_DirTree *dt, const size_t entrylen, const int case_sensitive, const int only_usascii); void *__PHYSFS_DirTreeAdd(__PHYSFS_DirTree *dt, char *name, const int isdir); void *__PHYSFS_DirTreeFind(__PHYSFS_DirTree *dt, const char *path); PHYSFS_EnumerateCallbackResult __PHYSFS_DirTreeEnumerate(void *opaque, @@ -715,6 +757,11 @@ int __PHYSFS_platformGrabMutex(void *mutex); */ void __PHYSFS_platformReleaseMutex(void *mutex); + +/* !!! FIXME: move to public API? */ +PHYSFS_uint32 __PHYSFS_utf8codepoint(const char **_str); + + #if PHYSFS_HAVE_PRAGMA_VISIBILITY #pragma GCC visibility pop #endif diff --git a/src/libraries/physfs/physfs_lzmasdk.h b/src/libraries/physfs/physfs_lzmasdk.h index c86972e57..d2bbcc652 100644 --- a/src/libraries/physfs/physfs_lzmasdk.h +++ b/src/libraries/physfs/physfs_lzmasdk.h @@ -506,6 +506,7 @@ MY_CPU_LE_UNALIGN means that CPU is LITTLE ENDIAN and CPU supports unaligned mem #endif #if defined(MY_CPU_AMD64) \ + || defined(_M_ARM64) \ || defined(_M_IA64) \ || defined(__AARCH64EL__) \ || defined(__AARCH64EB__) @@ -531,6 +532,8 @@ MY_CPU_LE_UNALIGN means that CPU is LITTLE ENDIAN and CPU supports unaligned mem #if defined(_WIN32) && defined(_M_ARM) #define MY_CPU_ARM_LE +#elif defined(_WIN64) && defined(_M_ARM64) +#define MY_CPU_ARM_LE #endif #if defined(_WIN32) && defined(_M_IA64) diff --git a/src/libraries/physfs/physfs_miniz.h b/src/libraries/physfs/physfs_miniz.h index 489c52d60..e0fddb023 100644 --- a/src/libraries/physfs/physfs_miniz.h +++ b/src/libraries/physfs/physfs_miniz.h @@ -22,12 +22,14 @@ typedef unsigned long mz_ulong; typedef void *(*mz_alloc_func)(void *opaque, unsigned int items, unsigned int size); typedef void (*mz_free_func)(void *opaque, void *address); +#ifndef MINIZ_LITTLE_ENDIAN /* if not defined by PHYSFS */ #if defined(_M_IX86) || defined(_M_X64) /* Set MINIZ_USE_UNALIGNED_LOADS_AND_STORES to 1 if integer loads and stores to unaligned addresses are acceptable on the target platform (slightly faster). */ #define MINIZ_USE_UNALIGNED_LOADS_AND_STORES 1 /* Set MINIZ_LITTLE_ENDIAN to 1 if the processor is little endian. */ #define MINIZ_LITTLE_ENDIAN 1 #endif +#endif /**/ #if defined(_WIN64) || defined(__MINGW64__) || defined(_LP64) || defined(__LP64__) /* Set MINIZ_HAS_64BIT_REGISTERS to 1 if the processor has 64-bit general purpose registers (enables 64-bit bitbuffer in inflator) */ @@ -117,6 +119,8 @@ struct tinfl_decompressor_tag #define MZ_MAX(a,b) (((a)>(b))?(a):(b)) #define MZ_MIN(a,b) (((a)<(b))?(a):(b)) #define MZ_CLEAR_OBJ(obj) memset(&(obj), 0, sizeof(obj)) +#define MZ_CLEAR_ARR(obj) memset((obj), 0, sizeof(obj)) +#define MZ_CLEAR_PTR(obj) memset((obj), 0, sizeof(*obj)) #if MINIZ_USE_UNALIGNED_LOADS_AND_STORES && MINIZ_LITTLE_ENDIAN #define MZ_READ_LE16(p) *((const mz_uint16 *)(p)) @@ -166,13 +170,17 @@ struct tinfl_decompressor_tag if (temp >= 0) { \ code_len = temp >> 9; \ if ((code_len) && (num_bits >= code_len)) \ - break; \ + break; \ } else if (num_bits > TINFL_FAST_LOOKUP_BITS) { \ code_len = TINFL_FAST_LOOKUP_BITS; \ do { \ temp = (pHuff)->m_tree[~temp + ((bit_buf >> code_len++) & 1)]; \ - } while ((temp < 0) && (num_bits >= (code_len + 1))); if (temp >= 0) break; \ - } TINFL_GET_BYTE(state_index, c); bit_buf |= (((tinfl_bit_buf_t)c) << num_bits); num_bits += 8; \ + } while ((temp < 0) && (num_bits >= (code_len + 1))); \ + if (temp >= 0) break; \ + } \ + TINFL_GET_BYTE(state_index, c); \ + bit_buf |= (((tinfl_bit_buf_t)c) << num_bits); \ + num_bits += 8; \ } while (num_bits < 15); /* TINFL_HUFF_DECODE() decodes the next Huffman coded symbol. It's more complex than you would initially expect because the zlib API expects the decompressor to never read */ @@ -274,13 +282,13 @@ static tinfl_status tinfl_decompress(tinfl_decompressor *r, const mz_uint8 *pIn_ else { for (counter = 0; counter < 3; counter++) { TINFL_GET_BITS(11, r->m_table_sizes[counter], "\05\05\04"[counter]); r->m_table_sizes[counter] += s_min_table_sizes[counter]; } - MZ_CLEAR_OBJ(r->m_tables[2].m_code_size); for (counter = 0; counter < r->m_table_sizes[2]; counter++) { mz_uint s; TINFL_GET_BITS(14, s, 3); r->m_tables[2].m_code_size[s_length_dezigzag[counter]] = (mz_uint8)s; } + MZ_CLEAR_ARR(r->m_tables[2].m_code_size); for (counter = 0; counter < r->m_table_sizes[2]; counter++) { mz_uint s; TINFL_GET_BITS(14, s, 3); r->m_tables[2].m_code_size[s_length_dezigzag[counter]] = (mz_uint8)s; } r->m_table_sizes[2] = 19; } for ( ; (int)r->m_type >= 0; r->m_type--) { int tree_next, tree_cur; tinfl_huff_table *pTable; - mz_uint i, j, used_syms, total, sym_index, next_code[17], total_syms[16]; pTable = &r->m_tables[r->m_type]; MZ_CLEAR_OBJ(total_syms); MZ_CLEAR_OBJ(pTable->m_look_up); MZ_CLEAR_OBJ(pTable->m_tree); + mz_uint i, j, used_syms, total, sym_index, next_code[17], total_syms[16]; pTable = &r->m_tables[r->m_type]; MZ_CLEAR_ARR(total_syms); MZ_CLEAR_ARR(pTable->m_look_up); MZ_CLEAR_ARR(pTable->m_tree); for (i = 0; i < r->m_table_sizes[r->m_type]; ++i) total_syms[pTable->m_code_size[i]]++; used_syms = 0, total = 0; next_code[0] = next_code[1] = 0; for (i = 1; i <= 15; ++i) { used_syms += total_syms[i]; next_code[i + 1] = (total = ((total + total_syms[i]) << 1)); } diff --git a/src/libraries/physfs/physfs_platform_android.c b/src/libraries/physfs/physfs_platform_android.c new file mode 100644 index 000000000..f892feda4 --- /dev/null +++ b/src/libraries/physfs/physfs_platform_android.c @@ -0,0 +1,117 @@ +/* + * Android 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_ANDROID + +#include +#include +#include "physfs_internal.h" + +static char *prefpath = NULL; + + +int __PHYSFS_platformInit(void) +{ + return 1; /* always succeed. */ +} /* __PHYSFS_platformInit */ + + +void __PHYSFS_platformDeinit(void) +{ + if (prefpath) + { + allocator.Free(prefpath); + prefpath = NULL; + } /* if */ +} /* __PHYSFS_platformDeinit */ + + +void __PHYSFS_platformDetectAvailableCDs(PHYSFS_StringCallback cb, void *data) +{ + /* no-op. */ +} /* __PHYSFS_platformDetectAvailableCDs */ + + +char *__PHYSFS_platformCalcBaseDir(const char *argv0) +{ + /* as a cheat, we expect argv0 to be a PHYSFS_AndroidInit* on Android. */ + PHYSFS_AndroidInit *ainit = (PHYSFS_AndroidInit *) argv0; + char *retval = NULL; + JNIEnv *jenv = NULL; + jobject jcontext; + + if (ainit == NULL) + return __PHYSFS_strdup("/"); /* oh well. */ + + jenv = (JNIEnv *) ainit->jnienv; + jcontext = (jobject) ainit->context; + + if ((*jenv)->PushLocalFrame(jenv, 16) >= 0) + { + jobject jfileobj = 0; + jmethodID jmeth = 0; + jthrowable jexception = 0; + jstring jstr = 0; + + jmeth = (*jenv)->GetMethodID(jenv, (*jenv)->GetObjectClass(jenv, jcontext), "getPackageResourcePath", "()Ljava/lang/String;"); + jstr = (jstring)(*jenv)->CallObjectMethod(jenv, jcontext, jmeth); + jexception = (*jenv)->ExceptionOccurred(jenv); /* this can't throw an exception, right? Just in case. */ + if (jexception != NULL) + (*jenv)->ExceptionClear(jenv); + else + { + const char *path = (*jenv)->GetStringUTFChars(jenv, jstr, NULL); + retval = __PHYSFS_strdup(path); + (*jenv)->ReleaseStringUTFChars(jenv, jstr, path); + } /* else */ + + /* We only can rely on the Activity being valid during this function call, + so go ahead and grab the prefpath too. */ + jmeth = (*jenv)->GetMethodID(jenv, (*jenv)->GetObjectClass(jenv, jcontext), "getFilesDir", "()Ljava/io/File;"); + jfileobj = (*jenv)->CallObjectMethod(jenv, jcontext, jmeth); + if (jfileobj) + { + jmeth = (*jenv)->GetMethodID(jenv, (*jenv)->GetObjectClass(jenv, jfileobj), "getCanonicalPath", "()Ljava/lang/String;"); + jstr = (jstring)(*jenv)->CallObjectMethod(jenv, jfileobj, jmeth); + jexception = (*jenv)->ExceptionOccurred(jenv); + if (jexception != NULL) + (*jenv)->ExceptionClear(jenv); + else + { + const char *path = (*jenv)->GetStringUTFChars(jenv, jstr, NULL); + const size_t len = strlen(path) + 2; + prefpath = allocator.Malloc(len); + if (prefpath) + snprintf(prefpath, len, "%s/", path); + (*jenv)->ReleaseStringUTFChars(jenv, jstr, path); + } /* else */ + } /* if */ + + (*jenv)->PopLocalFrame(jenv, NULL); + } /* if */ + + /* we can't return NULL because then PhysicsFS will treat argv0 as a string, but it's a non-NULL jobject! */ + if (retval == NULL) + retval = __PHYSFS_strdup("/"); /* we pray this works. */ + + return retval; +} /* __PHYSFS_platformCalcBaseDir */ + + +char *__PHYSFS_platformCalcPrefDir(const char *org, const char *app) +{ + return __PHYSFS_strdup(prefpath ? prefpath : "/"); +} /* __PHYSFS_platformCalcPrefDir */ + +#endif /* PHYSFS_PLATFORM_ANDROID */ + +/* end of physfs_platform_android.c ... */ + diff --git a/src/libraries/physfs/physfs_platform_apple.m b/src/libraries/physfs/physfs_platform_apple.m index e6207f5b7..f98f10d75 100644 --- a/src/libraries/physfs/physfs_platform_apple.m +++ b/src/libraries/physfs/physfs_platform_apple.m @@ -12,6 +12,7 @@ #ifdef PHYSFS_PLATFORM_APPLE #include +#include #include "physfs_internal.h" @@ -99,7 +100,7 @@ static int darwinIsWholeMedia(io_service_t service) } /* darwinIsWholeMedia */ -static int darwinIsMountedDisc(char *bsdName, mach_port_t masterPort) +static int darwinIsMountedDisc(char *bsdName, mach_port_t mainPort) { int retval = 0; CFMutableDictionaryRef matchingDict; @@ -107,10 +108,10 @@ static int darwinIsMountedDisc(char *bsdName, mach_port_t masterPort) io_iterator_t iter; io_service_t service; - if ((matchingDict = IOBSDNameMatching(masterPort, 0, bsdName)) == NULL) + if ((matchingDict = IOBSDNameMatching(mainPort, 0, bsdName)) == NULL) return 0; - rc = IOServiceGetMatchingServices(masterPort, matchingDict, &iter); + rc = IOServiceGetMatchingServices(mainPort, matchingDict, &iter); if ((rc != KERN_SUCCESS) || (!iter)) return 0; @@ -158,13 +159,25 @@ static int darwinIsMountedDisc(char *bsdName, mach_port_t masterPort) void __PHYSFS_platformDetectAvailableCDs(PHYSFS_StringCallback cb, void *data) { #if !defined(PHYSFS_NO_CDROM_SUPPORT) + /* macOS 12.0 changed "master" names to "main". */ + typedef kern_return_t (*ioMainPortFn)(mach_port_t, mach_port_t *); + static ioMainPortFn ioMainPort = NULL; const char *devPrefix = "/dev/"; const int prefixLen = strlen(devPrefix); - mach_port_t masterPort = 0; + mach_port_t mainPort = 0; struct statfs *mntbufp; int i, mounts; - if (IOMasterPort(MACH_PORT_NULL, &masterPort) != KERN_SUCCESS) + if (ioMainPort == NULL) + { + ioMainPort = (ioMainPortFn) dlsym(RTLD_DEFAULT, "IOMainPort"); + if (!ioMainPort) + ioMainPort = (ioMainPortFn) dlsym(RTLD_DEFAULT, "IOMasterPort"); + if (!ioMainPort) + return; /* oh well, no CD-ROMs for you. */ + } /* if */ + + if (ioMainPort(MACH_PORT_NULL, &mainPort) != KERN_SUCCESS) BAIL(PHYSFS_ERR_OS_ERROR, ) /*return void*/; mounts = getmntinfo(&mntbufp, MNT_WAIT); /* NOT THREAD SAFE! */ @@ -176,7 +189,7 @@ void __PHYSFS_platformDetectAvailableCDs(PHYSFS_StringCallback cb, void *data) continue; dev += prefixLen; - if (darwinIsMountedDisc(dev, masterPort)) + if (darwinIsMountedDisc(dev, mainPort)) cb(data, mnt); } /* for */ #endif /* !defined(PHYSFS_NO_CDROM_SUPPORT) */ diff --git a/src/libraries/physfs/physfs_platform_os2.c b/src/libraries/physfs/physfs_platform_os2.c index 8cc8044cf..bf47a9dcf 100644 --- a/src/libraries/physfs/physfs_platform_os2.c +++ b/src/libraries/physfs/physfs_platform_os2.c @@ -222,7 +222,7 @@ static char *cvtPathToCorrectCase(char *buf) if (ptr != NULL) /* isolate element to find (fname is the start). */ *ptr = '\0'; - rc = DosFindFirst((unsigned char *) spec, &hdir, FILE_DIRECTORY, + rc = DosFindFirst(spec, &hdir, FILE_DIRECTORY, &fb, sizeof (fb), &count, FIL_STANDARD); if (rc == NO_ERROR) { @@ -331,7 +331,7 @@ static int isCdRomDrive(ULONG drive) ULONG ul1, ul2; APIRET rc; HFILE hfile = NULLHANDLE; - unsigned char drivename[3] = { 0, ':', '\0' }; + char drivename[3] = { 0, ':', '\0' }; drivename[0] = 'A' + drive; @@ -443,7 +443,7 @@ PHYSFS_EnumerateCallbackResult __PHYSFS_platformEnumerate(const char *dirname, __PHYSFS_smallFree(utf8); BAIL_IF_ERRPASS(!cpspec, PHYSFS_ENUM_ERROR); - rc = DosFindFirst((unsigned char *) cpspec, &hdir, + rc = DosFindFirst(cpspec, &hdir, FILE_DIRECTORY | FILE_ARCHIVED | FILE_READONLY | FILE_HIDDEN | FILE_SYSTEM, &fb, sizeof (fb), &count, FIL_STANDARD); @@ -535,7 +535,7 @@ int __PHYSFS_platformMkDir(const char *filename) APIRET rc; char *cpstr = cvtUtf8ToCodepage(filename); BAIL_IF_ERRPASS(!cpstr, 0); - rc = DosCreateDir((unsigned char *) cpstr, NULL); + rc = DosCreateDir(cpstr, NULL); allocator.Free(cpstr); BAIL_IF(rc != NO_ERROR, errcodeFromAPIRET(rc), 0); return 1; diff --git a/src/libraries/physfs/physfs_platform_posix.c b/src/libraries/physfs/physfs_platform_posix.c index fa2159cd8..7ba5e1070 100644 --- a/src/libraries/physfs/physfs_platform_posix.c +++ b/src/libraries/physfs/physfs_platform_posix.c @@ -6,8 +6,6 @@ * This file written by Ryan C. Gordon. */ -/* !!! FIXME: check for EINTR? */ - #define __PHYSICSFS_INTERNAL__ #include "physfs_platforms.h" @@ -157,19 +155,41 @@ int __PHYSFS_platformMkDir(const char *path) } /* __PHYSFS_platformMkDir */ +#if !defined(O_CLOEXEC) && defined(FD_CLOEXEC) +static inline void set_CLOEXEC(int fildes) +{ + int flags = fcntl(fildes, F_GETFD); + if (flags != -1) { + fcntl(fildes, F_SETFD, flags | FD_CLOEXEC); + } +} +#endif + 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); +#ifdef O_CLOEXEC + /* Add O_CLOEXEC if defined */ + mode |= O_CLOEXEC; +#endif + + do { + fd = open(filename, mode, S_IRUSR | S_IWUSR); + } while ((fd < 0) && (errno == EINTR)); BAIL_IF(fd < 0, errcodeFromErrno(), NULL); +#if !defined(O_CLOEXEC) && defined(FD_CLOEXEC) + set_CLOEXEC(fd); +#endif + if (appending) { if (lseek(fd, 0, SEEK_END) < 0) @@ -219,7 +239,9 @@ PHYSFS_sint64 __PHYSFS_platformRead(void *opaque, void *buffer, if (!__PHYSFS_ui64FitsAddressSpace(len)) BAIL(PHYSFS_ERR_INVALID_ARGUMENT, -1); - rc = read(fd, buffer, (size_t) len); + do { + rc = read(fd, buffer, (size_t) len); + } while ((rc == -1) && (errno == EINTR)); BAIL_IF(rc == -1, errcodeFromErrno(), -1); assert(rc >= 0); assert(rc <= len); @@ -236,7 +258,9 @@ PHYSFS_sint64 __PHYSFS_platformWrite(void *opaque, const void *buffer, if (!__PHYSFS_ui64FitsAddressSpace(len)) BAIL(PHYSFS_ERR_INVALID_ARGUMENT, -1); - rc = write(fd, (void *) buffer, (size_t) len); + do { + rc = write(fd, (void *) buffer, (size_t) len); + } while ((rc == -1) && (errno == EINTR)); BAIL_IF(rc == -1, errcodeFromErrno(), rc); assert(rc >= 0); assert(rc <= len); @@ -275,8 +299,13 @@ PHYSFS_sint64 __PHYSFS_platformFileLength(void *opaque) int __PHYSFS_platformFlush(void *opaque) { const int fd = *((int *) opaque); - if ((fcntl(fd, F_GETFL) & O_ACCMODE) != O_RDONLY) - BAIL_IF(fsync(fd) == -1, errcodeFromErrno(), 0); + int rc = -1; + if ((fcntl(fd, F_GETFL) & O_ACCMODE) != O_RDONLY) { + do { + rc = fsync(fd); + } while ((rc == -1) && (errno == EINTR)); + BAIL_IF(rc == -1, errcodeFromErrno(), 0); + } return 1; } /* __PHYSFS_platformFlush */ @@ -284,7 +313,10 @@ int __PHYSFS_platformFlush(void *opaque) void __PHYSFS_platformClose(void *opaque) { const int fd = *((int *) opaque); - (void) close(fd); /* we don't check this. You should have used flush! */ + int rc = -1; + do { + rc = close(fd); /* we don't check this. You should have used flush! */ + } while ((rc == -1) && (errno == EINTR)); allocator.Free(opaque); } /* __PHYSFS_platformClose */ diff --git a/src/libraries/physfs/physfs_platform_unix.c b/src/libraries/physfs/physfs_platform_unix.c index 10d93a7e5..b945e03fd 100644 --- a/src/libraries/physfs/physfs_platform_unix.c +++ b/src/libraries/physfs/physfs_platform_unix.c @@ -261,12 +261,6 @@ char *__PHYSFS_platformCalcBaseDir(const char *argv0) if (sysctl(mib, 4, fullpath, &buflen, NULL, 0) != -1) retval = __PHYSFS_strdup(fullpath); } - #elif defined(PHYSFS_PLATFORM_SOLARIS) - { - const char *path = getexecname(); - if ((path != NULL) && (path[0] == '/')) /* must be absolute path... */ - retval = __PHYSFS_strdup(path); - } #endif /* If there's a Linux-like /proc filesystem, you can get the full path to @@ -278,6 +272,7 @@ char *__PHYSFS_platformCalcBaseDir(const char *argv0) retval = readSymLink("/proc/self/exe"); if (!retval) retval = readSymLink("/proc/curproc/file"); if (!retval) retval = readSymLink("/proc/curproc/exe"); + if (!retval) retval = readSymLink("/proc/self/path/a.out"); if (retval == NULL) { /* older kernels don't have /proc/self ... try PID version... */ @@ -289,6 +284,15 @@ char *__PHYSFS_platformCalcBaseDir(const char *argv0) } /* if */ } /* if */ + #if defined(PHYSFS_PLATFORM_SOLARIS) + if (!retval) /* try getexecname() if /proc didn't pan out. This may not be an absolute path! */ + { + const char *path = getexecname(); + if ((path != NULL) && (path[0] == '/')) /* must be absolute path... */ + retval = __PHYSFS_strdup(path); + } /* if */ + #endif + if (retval != NULL) /* chop off filename. */ { char *ptr = strrchr(retval, '/'); diff --git a/src/libraries/physfs/physfs_platform_windows.c b/src/libraries/physfs/physfs_platform_windows.c index c92e98d07..652300c9a 100644 --- a/src/libraries/physfs/physfs_platform_windows.c +++ b/src/libraries/physfs/physfs_platform_windows.c @@ -101,7 +101,7 @@ static char *unicodeToUtf8Heap(const WCHAR *w_str) static inline HANDLE winFindFirstFileW(const WCHAR *path, LPWIN32_FIND_DATAW d) { - #ifdef PHYSFS_PLATFORM_WINRT + #if defined(PHYSFS_PLATFORM_WINRT) || (_WIN32_WINNT >= 0x0501) // Windows XP+ return FindFirstFileExW(path, FindExInfoStandard, d, FindExSearchNameMatch, NULL, 0); #else @@ -111,7 +111,7 @@ static inline HANDLE winFindFirstFileW(const WCHAR *path, LPWIN32_FIND_DATAW d) static inline BOOL winInitializeCriticalSection(LPCRITICAL_SECTION lpcs) { - #ifdef PHYSFS_PLATFORM_WINRT + #if defined(PHYSFS_PLATFORM_WINRT) || (_WIN32_WINNT >= 0x0600) // Windows Vista+ return InitializeCriticalSectionEx(lpcs, 2000, 0); #else InitializeCriticalSection(lpcs); @@ -123,7 +123,7 @@ static inline HANDLE winCreateFileW(const WCHAR *wfname, const DWORD mode, const DWORD creation) { const DWORD share = FILE_SHARE_READ | FILE_SHARE_WRITE; - #ifdef PHYSFS_PLATFORM_WINRT + #if defined(PHYSFS_PLATFORM_WINRT) || (_WIN32_WINNT >= 0x0602) // Windows 8+ return CreateFile2(wfname, mode, share, creation, NULL); #else return CreateFileW(wfname, mode, share, NULL, creation, @@ -134,7 +134,7 @@ static inline HANDLE winCreateFileW(const WCHAR *wfname, const DWORD mode, static BOOL winSetFilePointer(HANDLE h, const PHYSFS_sint64 pos, PHYSFS_sint64 *_newpos, const DWORD whence) { - #ifdef PHYSFS_PLATFORM_WINRT + #if defined(PHYSFS_PLATFORM_WINRT) || (_WIN32_WINNT >= 0x0501) // Windows XP+ LARGE_INTEGER lipos; LARGE_INTEGER linewpos; BOOL rc; @@ -158,7 +158,7 @@ static BOOL winSetFilePointer(HANDLE h, const PHYSFS_sint64 pos, static PHYSFS_sint64 winGetFileSize(HANDLE h) { - #ifdef PHYSFS_PLATFORM_WINRT + #if defined(PHYSFS_PLATFORM_WINRT) || (_WIN32_WINNT >= 0x0600) // Windows Vista+ FILE_STANDARD_INFO info; const BOOL rc = GetFileInformationByHandleEx(h, FileStandardInfo, &info, sizeof (info)); diff --git a/src/libraries/physfs/physfs_platforms.h b/src/libraries/physfs/physfs_platforms.h index d4e4bfdc3..1ac17d9c6 100644 --- a/src/libraries/physfs/physfs_platforms.h +++ b/src/libraries/physfs/physfs_platforms.h @@ -40,11 +40,11 @@ # define PHYSFS_PLATFORM_POSIX 1 #elif defined(macintosh) # error Classic Mac OS support was dropped from PhysicsFS 2.0. Move to OS X. -#elif defined(ANDROID) -# define PHYSFS_PLATFORM_LINUX 1 -# define PHYSFS_PLATFORM_UNIX 1 -# define PHYSFS_PLATFORM_POSIX 1 -# define PHYSFS_NO_CDROM_SUPPORT 1 +#elif defined(__ANDROID__) + # define PHYSFS_PLATFORM_LINUX 1 + # define PHYSFS_PLATFORM_ANDROID 1 + # define PHYSFS_PLATFORM_POSIX 1 + # define PHYSFS_NO_CDROM_SUPPORT 1 #elif defined(__linux) # define PHYSFS_PLATFORM_LINUX 1 # define PHYSFS_PLATFORM_UNIX 1 diff --git a/src/libraries/physfs/physfs_unicode.c b/src/libraries/physfs/physfs_unicode.c index 0e0060224..bab4f8bb7 100644 --- a/src/libraries/physfs/physfs_unicode.c +++ b/src/libraries/physfs/physfs_unicode.c @@ -21,8 +21,8 @@ /* * 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 + * value from __PHYSFS_utf8codepoint() if there's bogus bits in the + * stream. __PHYSFS_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. @@ -35,7 +35,7 @@ */ #define UNICODE_BOGUS_CHAR_CODEPOINT '?' -static PHYSFS_uint32 utf8codepoint(const char **_str) +PHYSFS_uint32 __PHYSFS_utf8codepoint(const char **_str) { const char *str = *_str; PHYSFS_uint32 retval = 0; @@ -188,6 +188,11 @@ static PHYSFS_uint32 utf8codepoint(const char **_str) } /* else if */ return UNICODE_BOGUS_CHAR_VALUE; +} /* __PHYSFS_utf8codepoint */ + +static inline PHYSFS_uint32 utf8codepoint(const char **_str) +{ + return __PHYSFS_utf8codepoint(_str); } /* utf8codepoint */ static PHYSFS_uint32 utf16codepoint(const PHYSFS_uint16 **_str) @@ -210,7 +215,7 @@ static PHYSFS_uint32 utf16codepoint(const PHYSFS_uint16 **_str) else { src++; /* eat the other surrogate. */ - cp = (((cp - 0xD800) << 10) | (pair - 0xDC00)); + cp = 0x10000 + (((cp - 0xD800) << 10) | (pair - 0xDC00)); } /* else */ } /* else if */ @@ -238,7 +243,7 @@ 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); + PHYSFS_uint32 cp = __PHYSFS_utf8codepoint(&src); if (cp == 0) break; else if (cp == UNICODE_BOGUS_CHAR_VALUE) @@ -256,7 +261,7 @@ 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); + PHYSFS_uint32 cp = __PHYSFS_utf8codepoint(&src); if (cp == 0) break; else if (cp == UNICODE_BOGUS_CHAR_VALUE) @@ -278,7 +283,7 @@ void PHYSFS_utf8ToUtf16(const char *src, PHYSFS_uint16 *dst, PHYSFS_uint64 len) len -= sizeof (PHYSFS_uint16); /* save room for null char. */ while (len >= sizeof (PHYSFS_uint16)) { - PHYSFS_uint32 cp = utf8codepoint(&src); + PHYSFS_uint32 cp = __PHYSFS_utf8codepoint(&src); if (cp == 0) break; else if (cp == UNICODE_BOGUS_CHAR_VALUE) diff --git a/src/modules/data/HashFunction.cpp b/src/modules/data/HashFunction.cpp index 34d9e008d..03c67f68f 100644 --- a/src/modules/data/HashFunction.cpp +++ b/src/modules/data/HashFunction.cpp @@ -50,6 +50,13 @@ inline uint64 rightrot(uint64 x, uint8 amount) return (x >> amount) | (x << (64 - amount)); } +// Extend the value of `a` to make it a multiple of `n`. +inline uint64 extend_multiple(uint64 a, uint64 n) +{ + uint64 r = a % n; + return r == 0 ? a : a + (n-r); +} + /** * The following implementation is based on the pseudocode provided by multiple * authors on wikipedia: https://en.wikipedia.org/wiki/MD5 @@ -79,25 +86,22 @@ class MD5 : public HashFunction uint32 c0 = 0x98badcfe; uint32 d0 = 0x10325476; - //Do the required padding (MD5, SHA1 and SHA2 use the same padding) - uint64 paddedLength = length + 1; //Consider the appended bit - if (paddedLength % 64 < 56) - paddedLength += 56 - paddedLength % 64; - if (paddedLength % 64 > 56) - paddedLength += 120 - paddedLength % 64; + // Compute final padded length, accounting for the appended bit (byte) and size + uint64 paddedLength = extend_multiple(length + 1 + 8, 64); - uint8 *padded = new uint8[paddedLength + 8]; + uint32 *padded = new uint32[paddedLength / 4]; memcpy(padded, input, length); - memset(padded + length, 0, paddedLength - length); - padded[length] = 0x80; + memset(((uint8*)padded) + length, 0, paddedLength - length); + *(((uint8*)padded) + length) = 0x80; // append bit - //Now we need the length in bits - *((uint64*) &padded[paddedLength]) = length * 8; - paddedLength += 8; + // Append length in bits + uint64 bit_length = length * 8; + memcpy(((uint8*)padded) + paddedLength - 8, &bit_length, 8); - for (uint64 i = 0; i < paddedLength; i += 64) + // Process chunks + for (uint64 i = 0; i < paddedLength/4; i += 16) { - uint32 *chunk = (uint32*) &padded[i]; + uint32 *chunk = &padded[i]; uint32 A = a0; uint32 B = b0; @@ -200,29 +204,25 @@ class SHA1 : public HashFunction 0x67452301, 0xEFCDAB89, 0x98BADCFE, 0x10325476, 0xC3D2E1F0 }; - //Do the required padding (MD5, SHA1 and SHA2 use the same padding) - uint64 paddedLength = length + 1; //Consider the appended bit - if (paddedLength % 64 < 56) - paddedLength += 56 - paddedLength % 64; - if (paddedLength % 64 > 56) - paddedLength += 120 - paddedLength % 64; + // Compute final padded length, accounting for the appended bit (byte) and size + uint64 paddedLength = extend_multiple(length + 1 + 8, 64); - uint8 *padded = new uint8[paddedLength + 8]; + uint32 *padded = new uint32[paddedLength / 4]; memcpy(padded, input, length); - memset(padded + length, 0, paddedLength - length); - padded[length] = 0x80; + memset(((uint8*)padded) + length, 0, paddedLength - length); + *(((uint8*)padded) + length) = 0x80; // append bit - // Now we need the length in bits (big endian) - length *= 8; - for (int i = 0; i < 8; ++i, ++paddedLength) - padded[paddedLength] = (length >> (56 - i * 8)) & 0xFF; + // Append length in bits (big endian) + uint64 bit_length = length * 8; + for (int i = 0; i < 8; ++i) + *(((uint8*)padded) + (paddedLength - 8 + i)) = (bit_length >> (56 - i * 8)) & 0xFF; // Allocate our extended words uint32 words[80]; - for (uint64 i = 0; i < paddedLength; i += 64) + for (uint64 i = 0; i < paddedLength/4; i += 16) { - uint32 *chunk = (uint32*) &padded[i]; + uint32 *chunk = &padded[i]; for (int j = 0; j < 16; j++) { char *c = (char*) &words[j]; @@ -303,22 +303,18 @@ class SHA256 : public HashFunction if (!isSupported(function)) throw love::Exception("Hash function not supported by SHA-224/SHA-256 implementation"); - //Do the required padding (MD5, SHA1 and SHA2 use the same padding) - uint64 paddedLength = length + 1; //Consider the appended bit - if (paddedLength % 64 < 56) - paddedLength += 56 - paddedLength % 64; - if (paddedLength % 64 > 56) - paddedLength += 120 - paddedLength % 64; + // Compute final padded length, accounting for the appended bit (byte) and size + uint64 paddedLength = extend_multiple(length + 1 + 8, 64); - uint8 *padded = new uint8[paddedLength + 8]; + uint32 *padded = new uint32[paddedLength / 4]; memcpy(padded, input, length); - memset(padded + length, 0, paddedLength - length); - padded[length] = 0x80; + memset(((uint8*)padded) + length, 0, paddedLength - length); + *(((uint8*)padded) + length) = 0x80; // append bit - // Now we need the length in bits (big endian) - length *= 8; - for (int i = 0; i < 8; ++i, ++paddedLength) - padded[paddedLength] = (length >> (56 - i * 8)) & 0xFF; + // Append length in bits (big endian) + uint64 bit_length = length * 8; + for (int i = 0; i < 8; ++i) + *(((uint8*)padded) + (paddedLength - 8 + i)) = (bit_length >> (56 - i * 8)) & 0xFF; uint32 intermediate[8]; if (function == FUNCTION_SHA224) @@ -329,9 +325,9 @@ class SHA256 : public HashFunction // Allocate our extended words uint32 words[64]; - for (uint64 i = 0; i < paddedLength; i += 64) + for (uint64 i = 0; i < paddedLength/4; i += 16) { - uint32 *chunk = (uint32*) &padded[i]; + uint32 *chunk = &padded[i]; for (int j = 0; j < 16; j++) { char *c = (char*) &words[j]; @@ -459,31 +455,26 @@ class SHA512 : public HashFunction else memcpy(intermediates, initial512, sizeof(intermediates)); - //Do the required padding - uint64 paddedLength = length + 1; //Consider the appended bit - if (paddedLength % 128 < 112) - paddedLength += 112 - paddedLength % 128; - if (paddedLength % 128 > 112) - paddedLength += 240 - paddedLength % 128; + // Compute final padded length, accounting for the appended bit (byte) and size + uint64 paddedLength = extend_multiple(length + 1 + 16, 128); - uint8 *padded = new uint8[paddedLength + 16]; - paddedLength += 8; + uint64 *padded = new uint64[paddedLength / 8]; memcpy(padded, input, length); - memset(padded + length, 0, paddedLength - length); - padded[length] = 0x80; + memset(((uint8*)padded) + length, 0, paddedLength - length); + *(((uint8*)padded) + length) = 0x80; // append bit - // Now we need the length in bits (big endian), note we only write a 64-bit int, so + // Append length in bits (big endian), note we only write a 64-bit int, so // we have filled the first 8 bytes with zeroes - length *= 8; - for (int i = 0; i < 8; ++i, ++paddedLength) - padded[paddedLength] = (length >> (56 - i * 8)) & 0xFF; + uint64 bit_length = length * 8; + for (int i = 0; i < 8; ++i) + *(((uint8*)padded) + (paddedLength - 8 + i)) = (bit_length >> (56 - i * 8)) & 0xFF; // Allocate our extended words uint64 words[80]; - for (uint64 i = 0; i < paddedLength; i += 128) + for (uint64 i = 0; i < paddedLength/8; i += 16) { - uint64 *chunk = (uint64*) &padded[i]; + uint64 *chunk = &padded[i]; for (int j = 0; j < 16; ++j) { char *c = (char*) &words[j]; diff --git a/src/modules/event/sdl/Event.cpp b/src/modules/event/sdl/Event.cpp index e5c3f4219..c1f217a9c 100644 --- a/src/modules/event/sdl/Event.cpp +++ b/src/modules/event/sdl/Event.cpp @@ -51,6 +51,13 @@ static void windowToDPICoords(double *x, double *y) window->windowToDPICoords(x, y); } +static void clampToWindow(double *x, double *y) +{ + auto window = Module::getInstance(Module::M_WINDOW); + if (window) + window->clampPositionInWindow(x, y); +} + #ifndef LOVE_MACOSX static void normalizedToDPICoords(double *x, double *y) { @@ -253,8 +260,16 @@ Message *Event::convert(const SDL_Event &e) double y = (double) e.motion.y; double xrel = (double) e.motion.xrel; double yrel = (double) e.motion.yrel; + + // SDL reports mouse coordinates outside the window bounds when click-and- + // dragging. For compatibility we clamp instead since user code may not be + // able to handle out-of-bounds coordinates. SDL has a hint to turn off + // auto capture, but it doesn't report the mouse's position at the edge of + // the window if the mouse moves fast enough when it's off. + clampToWindow(&x, &y); windowToDPICoords(&x, &y); windowToDPICoords(&xrel, &yrel); + vargs.emplace_back(x); vargs.emplace_back(y); vargs.emplace_back(xrel); @@ -280,7 +295,10 @@ Message *Event::convert(const SDL_Event &e) double px = (double) e.button.x; double py = (double) e.button.y; + + clampToWindow(&px, &py); windowToDPICoords(&px, &py); + vargs.emplace_back(px); vargs.emplace_back(py); vargs.emplace_back((double) button); diff --git a/src/modules/filesystem/physfs/Filesystem.cpp b/src/modules/filesystem/physfs/Filesystem.cpp index 28e562c57..d3c650269 100644 --- a/src/modules/filesystem/physfs/Filesystem.cpp +++ b/src/modules/filesystem/physfs/Filesystem.cpp @@ -27,6 +27,7 @@ #include "Filesystem.h" #include "File.h" +#include "PhysfsIo.h" // PhysFS #include "libraries/physfs/physfs.h" @@ -123,6 +124,10 @@ const char *Filesystem::getName() const void Filesystem::init(const char *arg0) { +#ifdef LOVE_ANDROID + arg0 = love::android::getArg0(); +#endif + if (!PHYSFS_init(arg0)) throw love::Exception("Failed to initialize filesystem: %s", PHYSFS_getErrorByCode(PHYSFS_getLastErrorCode())); @@ -145,7 +150,7 @@ bool Filesystem::isFused() const return fused; } -bool Filesystem::setIdentity(const char *ident, bool appendToPath) +bool Filesystem::setIdentity(const char *ident, bool appendToPath) { if (!PHYSFS_isInit()) return false; @@ -165,8 +170,8 @@ bool Filesystem::setIdentity(const char *ident, bool appendToPath) else save_path_full += save_path_relative; - save_path_full = normalize(save_path_full); - + save_path_full = normalize(save_path_full); + #ifdef LOVE_ANDROID if (save_identity == "") save_identity = "unnamed"; @@ -285,7 +290,27 @@ bool Filesystem::setSource(const char *source) #else // Add the directory. if (!PHYSFS_mount(new_search_path.c_str(), nullptr, 1)) - return false; + { + // It's possible there is additional data at the end of the fused executable, + // e.g. for signed windows executables (the signature). + // In this case let's try a little bit harder to find the zip file. + // This is not used by default because I assume that the physfs IOs are probably + // more robust and more performant, so they should be favored, if possible. + auto io = StripSuffixIo::create(new_search_path); + if (!io->determineStrippedLength()) + { + delete io; + return false; + } + if (!PHYSFS_mountIo(io, io->filename.c_str(), nullptr, 1)) + { + // If PHYSFS_mountIo fails, io->destroy(io) is not called and we have + // to delete ourselves. + delete io; + return false; + } + return true; + } #endif // Save the game source. diff --git a/src/modules/filesystem/physfs/PhysfsIo.cpp b/src/modules/filesystem/physfs/PhysfsIo.cpp new file mode 100644 index 000000000..cdcea8dd2 --- /dev/null +++ b/src/modules/filesystem/physfs/PhysfsIo.cpp @@ -0,0 +1,203 @@ +/** + * Copyright (c) 2006-2023 LOVE Development Team + * + * 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. + **/ + +#include +#include + +#include "PhysfsIo.h" + +namespace love +{ +namespace filesystem +{ +namespace physfs +{ + +bool StripSuffixIo::determineStrippedLength() +{ + if (!file) { + return false; + } + const int64_t fullSize = fullLength(); + int64_t chunkSize = std::min(fullSize, (int64_t)8192); + std::string buffer; + buffer.reserve(chunkSize); + int64_t i = fullSize - chunkSize; + // I don't think we really need to go through the whole file. The main known use + // case for this functionality is to skip windows codesign signatures, which are + // from what I have seen ~12KB or so, but trying is better than just failing. + while (i >= 0) + { + buffer.resize(chunkSize); + if (seek(i) == 0) + return false; + const auto n = read(&buffer[0], chunkSize); + if (n <= 0) + return false; + buffer.resize(n); + // We are looking for the magic bytes that indicate the start + // of the "End of cental directory record (EOCD)". + // As this is most likely not a multi-disk zip, we could include 4 bytes of 0x00, + // but I'd rather make fewer assumptions. + const auto endOfCentralDirectory = buffer.rfind("\x50\x4B\x05\x06"); + if (endOfCentralDirectory != std::string::npos) + { + i = i + endOfCentralDirectory; + break; + } + if (i == 0) + break; + i = std::max((int64_t)0, i - chunkSize); + } + + if (i > 0) + { + // The EOCD record is at least 22 bytes but may include a comment + if (i + 22 > fullSize) + return false; // Incomplete central directory + // The comment length (u16) is located 20 bytes from the start of the EOCD record + if (seek(i + 20) == 0) + return false; + uint8_t buffer[2]; + const auto n = read(buffer, 2); + if (n <= 0) + return false; + const auto commentSize = (buffer[1] << 8) | buffer[0]; + if (i + 22 + commentSize > fullSize) // Comment incomplete + return false; + // We pretend the file ends just after the comment + // (which should be the end of the embedded zip file) + strippedLength_ = i + 22 + commentSize; + } + else + { + strippedLength_ = fullSize; + } + + if (seek(0) == 0) + return false; + return true; +} + +int64_t StripSuffixIo::read(void* buf, uint64_t len) +{ + if (!file) + { + PHYSFS_setErrorCode(PHYSFS_ERR_OS_ERROR); + return -1; + } + const auto ret = std::fread(buf, 1, len, file); + if (ret == 0) + { + if (std::feof(file)) + { + PHYSFS_setErrorCode(PHYSFS_ERR_OK); + return 0; + } + else + { + PHYSFS_setErrorCode(PHYSFS_ERR_OS_ERROR); + return -1; + } + } + else if (ret < len && std::ferror(file)) + { + PHYSFS_setErrorCode(PHYSFS_ERR_OS_ERROR); + return -1; + } + PHYSFS_setErrorCode(PHYSFS_ERR_OK); + return ret; +} + +int64_t StripSuffixIo::write(const void* /*buf*/, uint64_t /*len*/) +{ + PHYSFS_setErrorCode(PHYSFS_ERR_READ_ONLY); + return -1; +} + +int64_t StripSuffixIo::seek(uint64_t offset) +{ + if (!file) + { + PHYSFS_setErrorCode(PHYSFS_ERR_OS_ERROR); + return 0; + } + const auto ret = std::fseek(file, offset, SEEK_SET); + PHYSFS_setErrorCode(ret != 0 ? PHYSFS_ERR_OS_ERROR : PHYSFS_ERR_OK); + return ret == 0 ? 1 : 0; +} + +int64_t StripSuffixIo::tell() +{ + if (!file) + { + PHYSFS_setErrorCode(PHYSFS_ERR_OS_ERROR); + return -1; + } + return std::ftell(file); +} + +int64_t StripSuffixIo::length() +{ + return strippedLength_; +} + +int64_t StripSuffixIo::flush() +{ + if (!file) + { + PHYSFS_setErrorCode(PHYSFS_ERR_OS_ERROR); + return 0; + } + return std::fflush(file) == 0 ? 1 : 0; +} + +int64_t StripSuffixIo::fullLength() +{ + assert(file); + const auto cur = std::ftell(file); + if (cur == -1) + { + PHYSFS_setErrorCode(PHYSFS_ERR_OS_ERROR); + return -1; + } + if (std::fseek(file, 0, SEEK_END) != 0) + { + PHYSFS_setErrorCode(PHYSFS_ERR_OS_ERROR); + return -1; + } + const auto len = std::ftell(file); + if (len == -1) + { + PHYSFS_setErrorCode(PHYSFS_ERR_OS_ERROR); + return -1; + } + if (std::fseek(file, cur, SEEK_SET) != 0) + { + // We do have the length now, but something is wrong, so we return an error anyways + PHYSFS_setErrorCode(PHYSFS_ERR_OS_ERROR); + return -1; + } + return len; +} + +} // physfs +} // filesystem +} // love diff --git a/src/modules/filesystem/physfs/PhysfsIo.h b/src/modules/filesystem/physfs/PhysfsIo.h new file mode 100644 index 000000000..35b92cc6c --- /dev/null +++ b/src/modules/filesystem/physfs/PhysfsIo.h @@ -0,0 +1,167 @@ +/** + * Copyright (c) 2006-2023 LOVE Development Team + * + * 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. + **/ + +#ifndef LOVE_FILESYSTEM_PHYSFS_PHYSFSIO_H +#define LOVE_FILESYSTEM_PHYSFS_PHYSFSIO_H + +#include +#include +#include + +#include "libraries/physfs/physfs.h" + +namespace love +{ +namespace filesystem +{ +namespace physfs +{ + +template +struct PhysfsIo : PHYSFS_Io +{ +protected: + + PhysfsIo() + : PHYSFS_Io() + { + // Direct initialization of PHYSFS_Io members in the initializer list + // doesn't work in VS2013. + this->version = Derived::version; + this->opaque = this; + this->read = staticRead; // May be null. + this->write = staticWrite; // May be null. + this->seek = staticSeek; + this->tell = staticTell; + this->length = staticLength; + this->duplicate = staticDuplicate; + this->flush = staticFlush; // May be null. + this->destroy = staticDestroy; + } + + virtual ~PhysfsIo() {} + +private: + + // Returns: number of bytes read, 0 on EOF, -1 on failure + static PHYSFS_sint64 staticRead(struct PHYSFS_Io* io, void* buf, PHYSFS_uint64 len) + { + return derived(io)->read(buf, len); + } + + // Returns: number of bytes written, -1 on failure + static PHYSFS_sint64 staticWrite(struct PHYSFS_Io* io, const void* buf, PHYSFS_uint64 len) + { + return derived(io)->write(buf, len); + } + + // Returns: non-zero on success, zero on error + static int staticSeek(struct PHYSFS_Io* io, PHYSFS_uint64 offset) + { + return derived(io)->seek(offset); + } + + // Returns: current offset from start, -1 on error + static PHYSFS_sint64 staticTell(struct PHYSFS_Io* io) + { + return derived(io)->tell(); + } + + // Returns: total size in bytes, -1 on error + static PHYSFS_sint64 staticLength(struct PHYSFS_Io* io) + { + return derived(io)->length(); + } + + static struct PHYSFS_Io* staticDuplicate(struct PHYSFS_Io* io) + { + // Just use copy constructor + return new Derived(*derived(io)); + } + + // Returns: non-zero on success, zero on error + static int staticFlush(struct PHYSFS_Io* io) + { + return derived(io)->flush(); + } + + static void staticDestroy(struct PHYSFS_Io* io) + { + // Just use destructor + delete derived(io); + } + + static Derived* derived(PHYSFS_Io* io) + { + return static_cast(reinterpret_cast(io->opaque)); + } +}; + +struct StripSuffixIo : public PhysfsIo +{ + static const uint32_t version = 0; + + std::string filename; + FILE* file = nullptr; + + // The constructor is private in favor of this function to prevent stack allocation + // because Physfs will take ownership of this object and call destroy on it later. + static StripSuffixIo* create(std::string f) { return new StripSuffixIo(f); } + + virtual ~StripSuffixIo() + { + if (file) + { + std::fclose(file); + } + } + + StripSuffixIo(const StripSuffixIo& other) + : StripSuffixIo(other.filename) + { + } + + bool determineStrippedLength(); + + int64_t read(void* buf, uint64_t len); + int64_t write(const void* buf, uint64_t len); + int64_t seek(uint64_t offset); + int64_t tell(); + int64_t length(); + int64_t flush(); + +private: + + StripSuffixIo(std::string f) + : filename(std::move(f)) + , file(std::fopen(filename.c_str(), "rb")) + { + } + + int64_t fullLength(); + + int64_t strippedLength_ = -1; +}; + +} // physfs +} // filesystem +} // love + +#endif diff --git a/src/modules/joystick/sdl/Joystick.cpp b/src/modules/joystick/sdl/Joystick.cpp index f451cbaf1..5d860cceb 100644 --- a/src/modules/joystick/sdl/Joystick.cpp +++ b/src/modules/joystick/sdl/Joystick.cpp @@ -323,7 +323,9 @@ std::string Joystick::getGamepadMappingString() const // Matches SDL_GameControllerAddMappingsFromRW. if (mappingstr.find_last_of(',') != mappingstr.length() - 1) mappingstr += ","; - mappingstr += "platform:" + std::string(SDL_GetPlatform()); + + if (mappingstr.find("platform:") == std::string::npos) + mappingstr += "platform:" + std::string(SDL_GetPlatform()); return mappingstr; } diff --git a/src/modules/joystick/sdl/JoystickModule.cpp b/src/modules/joystick/sdl/JoystickModule.cpp index dd5d3bf2a..de45fe0ff 100644 --- a/src/modules/joystick/sdl/JoystickModule.cpp +++ b/src/modules/joystick/sdl/JoystickModule.cpp @@ -259,12 +259,17 @@ bool JoystickModule::setGamepadMapping(const std::string &guid, Joystick::Gamepa if (endpos == std::string::npos) endpos = mapstr.length() - 1; - mapstr.replace(findpos + 1, endpos - findpos + 1, insertstr); + mapstr.replace(findpos + 1, endpos - findpos, insertstr); } else { - // Just append to the end if we don't need to replace anything. - mapstr += insertstr; + // Just append to the end (or before the platform section if that exists), + // if we don't need to replace anything. + size_t platformpos = mapstr.find("platform:"); + if (platformpos != std::string::npos) + mapstr.insert(platformpos, insertstr); + else + mapstr += insertstr; } // 1 == added, 0 == updated, -1 == error. @@ -465,7 +470,9 @@ std::string JoystickModule::getGamepadMappingString(const std::string &guid) con // Matches SDL_GameControllerAddMappingsFromRW. if (mapping.find_last_of(',') != mapping.length() - 1) mapping += ","; - mapping += "platform:" + std::string(SDL_GetPlatform()); + + if (mapping.find("platform:") == std::string::npos) + mapping += "platform:" + std::string(SDL_GetPlatform()); return mapping; } @@ -489,8 +496,10 @@ std::string JoystickModule::saveGamepadMappings() mapping += ","; // Matches SDL_GameControllerAddMappingsFromRW. - mapping += "platform:" + std::string(SDL_GetPlatform()) + ",\n"; - mappings += mapping; + if (mapping.find("platform:") == std::string::npos) + mapping += "platform:" + std::string(SDL_GetPlatform()) + ","; + + mappings += mapping + "\n"; } return mappings; diff --git a/src/modules/mouse/sdl/Mouse.cpp b/src/modules/mouse/sdl/Mouse.cpp index d8a8290dc..8ff555ea2 100644 --- a/src/modules/mouse/sdl/Mouse.cpp +++ b/src/modules/mouse/sdl/Mouse.cpp @@ -49,6 +49,13 @@ static void DPIToWindowCoords(double *x, double *y) window->DPIToWindowCoords(x, y); } +static void clampToWindow(double *x, double *y) +{ + auto window = Module::getInstance(Module::M_WINDOW); + if (window) + window->clampPositionInWindow(x, y); +} + const char *Mouse::getName() const { return "love.mouse.sdl"; @@ -119,24 +126,16 @@ bool Mouse::isCursorSupported() const double Mouse::getX() const { - int x; - SDL_GetMouseState(&x, nullptr); - - double dx = (double) x; - windowToDPICoords(&dx, nullptr); - - return dx; + double x, y; + getPosition(x, y); + return x; } double Mouse::getY() const { - int y; - SDL_GetMouseState(nullptr, &y); - - double dy = (double) y; - windowToDPICoords(nullptr, &dy); - - return dy; + double x, y; + getPosition(x, y); + return y; } void Mouse::getPosition(double &x, double &y) const @@ -146,6 +145,14 @@ void Mouse::getPosition(double &x, double &y) const x = (double) mx; y = (double) my; + + // SDL reports mouse coordinates outside the window bounds when click-and- + // dragging. For compatibility we clamp instead since user code may not be + // able to handle out-of-bounds coordinates. SDL has a hint to turn off + // auto capture, but it doesn't report the mouse's position at the edge of + // the window if the mouse moves fast enough when it's off. + clampToWindow(&x, &y); + windowToDPICoords(&x, &y); } diff --git a/src/modules/video/theora/TheoraVideoStream.cpp b/src/modules/video/theora/TheoraVideoStream.cpp index 7e109ae7b..a54ccb42d 100644 --- a/src/modules/video/theora/TheoraVideoStream.cpp +++ b/src/modules/video/theora/TheoraVideoStream.cpp @@ -232,14 +232,18 @@ void TheoraVideoStream::threadedFillBackBuffer(double dt) th_decode_ycbcr_out(decoder, bufferinfo); hasFrame = true; - ogg_int64_t granulePosition; + ogg_int64_t decoderPosition; do { if (demuxer.readPacket(packet)) return; - } while (th_decode_packetin(decoder, &packet, &granulePosition) != 0); + + if (packet.granulepos > 0) + th_decode_ctl(decoder, TH_DECCTL_SET_GRANPOS, &packet.granulepos, sizeof(packet.granulepos)); + } while (th_decode_packetin(decoder, &packet, &decoderPosition) != 0); + lastFrame = nextFrame; - nextFrame = th_granule_time(decoder, granulePosition); + nextFrame = th_granule_time(decoder, decoderPosition); } // Only swap once, even if we read many frames to get here diff --git a/src/modules/window/Window.h b/src/modules/window/Window.h index 430516fe6..8b010bace 100644 --- a/src/modules/window/Window.h +++ b/src/modules/window/Window.h @@ -193,6 +193,8 @@ class Window : public Module virtual int getPixelWidth() const = 0; virtual int getPixelHeight() const = 0; + virtual void clampPositionInWindow(double *wx, double *wy) const = 0; + // Note: window-space coordinates are not necessarily the same as // density-independent units (which toPixels and fromPixels use.) virtual void windowToPixelCoords(double *x, double *y) const = 0; diff --git a/src/modules/window/sdl/Window.cpp b/src/modules/window/sdl/Window.cpp index 767a3fc56..e9175f3d4 100644 --- a/src/modules/window/sdl/Window.cpp +++ b/src/modules/window/sdl/Window.cpp @@ -311,8 +311,8 @@ bool Window::createWindowAndContext(int x, int y, int w, int h, Uint32 windowfla return false; } -#ifdef LOVE_MACOS - love::macos::setWindowSRGBColorSpace(window); +#ifdef LOVE_MACOSX + love::macosx::setWindowSRGBColorSpace(window); #endif context = SDL_GL_CreateContext(window); @@ -1124,6 +1124,13 @@ int Window::getPixelHeight() const return pixelHeight; } +void Window::clampPositionInWindow(double *wx, double *wy) const +{ + if (wx != nullptr) + *wx = std::min(std::max(0.0, *wx), (double) getWidth() - 1); + if (wy != nullptr) + *wy = std::min(std::max(0.0, *wy), (double) getHeight() - 1); +} void Window::windowToPixelCoords(double *x, double *y) const { diff --git a/src/modules/window/sdl/Window.h b/src/modules/window/sdl/Window.h index ad7c093b9..02b9e788a 100644 --- a/src/modules/window/sdl/Window.h +++ b/src/modules/window/sdl/Window.h @@ -104,6 +104,8 @@ class Window final : public love::window::Window int getPixelWidth() const override; int getPixelHeight() const override; + void clampPositionInWindow(double *wx, double *wy) const override; + void windowToPixelCoords(double *x, double *y) const override; void pixelToWindowCoords(double *x, double *y) const override;