From fa9978ec083996eabe5f8953ebd5793a54165469 Mon Sep 17 00:00:00 2001 From: Miku AuahDark Date: Sat, 24 Sep 2022 13:26:28 +0800 Subject: [PATCH 01/29] Use Ubuntu 20.04 for Linux build. actions/runner-images#6002 --- .github/workflows/main.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 97115f1e7..4a7f075cf 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 From 544bba8ef00381d799593bd517a83f18f26bb68b Mon Sep 17 00:00:00 2001 From: Miku AuahDark Date: Sat, 15 Apr 2023 16:16:42 +0800 Subject: [PATCH 02/29] Update GitHub Actions dependencies. --- .github/workflows/main.yml | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 4a7f075cf..386ff68bc 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -17,11 +17,11 @@ 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: Build AppImage @@ -29,7 +29,7 @@ jobs: - 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 @@ -40,13 +40,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 +58,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 +71,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 +91,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 +99,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 From c849f71d14a3992778b31b7d393b0ff7ab6734bc Mon Sep 17 00:00:00 2001 From: Miku AuahDark Date: Sat, 15 Apr 2023 16:17:42 +0800 Subject: [PATCH 03/29] Artifact AppImage debug symbols. --- .github/workflows/main.yml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 386ff68bc..57f342444 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -33,6 +33,11 @@ jobs: 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: From 78e7c44424bdb2fb6d055f2c62b50b611187b7a9 Mon Sep 17 00:00:00 2001 From: Sasha Szpakowski Date: Sat, 15 Apr 2023 09:37:22 -0300 Subject: [PATCH 04/29] update version --- extra/appveyor/appveyor.yml | 2 +- extra/windows/love.rc | Bin 3632 -> 3632 bytes platform/unix/configure.ac | 2 +- platform/unix/love.6 | 2 +- .../xcode/liblove.xcodeproj/project.pbxproj | 6 +++--- platform/xcode/love.xcodeproj/project.pbxproj | 12 ++++++------ src/common/version.h | 6 +++--- 7 files changed, 15 insertions(+), 15 deletions(-) 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 c2f06a42f20ad0d7791c3c230de9071ab6622636..edbf420cb303947dabab2dba30038be450e77e8a 100644 GIT binary patch delta 26 icmdlWvq5Hq3JbF-gVN+i7Wv6;ELM!Bn;Tiya{>Tlzz077 delta 26 icmdlWvq5Hq3JbFdgVN+i7Wv6;ELMyrn;Tiya{>Tls0Td& 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..588230091 100644 --- a/platform/xcode/liblove.xcodeproj/project.pbxproj +++ b/platform/xcode/liblove.xcodeproj/project.pbxproj @@ -5194,7 +5194,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 +5230,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 +5267,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/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 From 4ab9a1ce8cbffb30a38476818ff5d3989196683f Mon Sep 17 00:00:00 2001 From: Miku AuahDark Date: Wed, 19 Apr 2023 16:26:06 +0800 Subject: [PATCH 05/29] Workaround alignment issue in Linux 32-bit platforms. Apparently geting alignment right pre-C++17 is not easy. Well, 2^32 fits entirely in Lua "double" 2^53 so it's fine to assume alignment of 1. Fixes #1916. --- src/common/runtime.cpp | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/src/common/runtime.cpp b/src/common/runtime.cpp index 6031581f0..19b9b31d1 100644 --- a/src/common/runtime.cpp +++ b/src/common/runtime.cpp @@ -30,6 +30,7 @@ // C++ #include #include +#include #include #include #include @@ -136,16 +137,25 @@ static ObjectKey luax_computeloveobjectkey(lua_State *L, love::Object *object) // 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); +#if UINTPTR_MAX == 0xffffffff + // https://github.com/love2d/love/issues/1916 + // This appears to be ABI violation on 32-bit platforms. However it seems + // there's no reliable way to get the correct alignment pre-C++17. Consider + // that 32-bit still fits in 2^53 range, it's perfectly fine to assume + // alignment of 1. + constexpr size_t minalign = 1; +#else + constexpr size_t minalign = LOVE_ALIGNOF(std::max_align_t); +#endif 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; From 7828d1e0fb3f59c4201033b3a2c1c163cb30be78 Mon Sep 17 00:00:00 2001 From: Sasha Szpakowski Date: Sun, 23 Apr 2023 21:19:32 -0300 Subject: [PATCH 06/29] Fix macOS colorspace backport --- src/modules/window/sdl/Window.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/modules/window/sdl/Window.cpp b/src/modules/window/sdl/Window.cpp index 767a3fc56..f2290112a 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); From 39763bba2f7e8d953d2887b69301f68b94d7fb83 Mon Sep 17 00:00:00 2001 From: Miku AuahDark Date: Wed, 26 Apr 2023 22:18:53 +0800 Subject: [PATCH 07/29] Retry make getdeps 250 times. --- .github/workflows/main.yml | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 57f342444..9e96663d2 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -24,6 +24,16 @@ jobs: 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 From c8e7d4e28a060d8e9f13b7fee549cf270995360a Mon Sep 17 00:00:00 2001 From: Miku AuahDark Date: Wed, 3 May 2023 19:05:01 +0800 Subject: [PATCH 08/29] Change the usage of constexpr to const. VS2013 doesn't support them. --- src/common/runtime.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/common/runtime.cpp b/src/common/runtime.cpp index 19b9b31d1..f9efa8005 100644 --- a/src/common/runtime.cpp +++ b/src/common/runtime.cpp @@ -143,9 +143,9 @@ static ObjectKey luax_computeloveobjectkey(lua_State *L, love::Object *object) // there's no reliable way to get the correct alignment pre-C++17. Consider // that 32-bit still fits in 2^53 range, it's perfectly fine to assume // alignment of 1. - constexpr size_t minalign = 1; + const size_t minalign = 1; #else - constexpr size_t minalign = LOVE_ALIGNOF(std::max_align_t); + const size_t minalign = LOVE_ALIGNOF(std::max_align_t); #endif uintptr_t key = (uintptr_t) object; From efc54d5124db13af0f20d2bad0004f30298c6627 Mon Sep 17 00:00:00 2001 From: Joel Schumacher Date: Wed, 17 May 2023 23:46:20 +0200 Subject: [PATCH 09/29] Add PHYSFS_Io StripSuffixIo to skip codesign signatures MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When a fused löve executable is signed with codesign on Windows some data is appended to the executable, preventing physfs from finding the zip file at the end of the file. To help physfs out a bit, we try to find the zip ourselves in the case of regular mount failure by looking for it's characteristic end of central directory record and, once found, pretend to physfs that the file is shorter than it actually is and ends at the end of the zip file. --- CMakeLists.txt | 2 + src/modules/filesystem/physfs/Filesystem.cpp | 29 ++- src/modules/filesystem/physfs/PhysfsIo.cpp | 203 +++++++++++++++++++ src/modules/filesystem/physfs/PhysfsIo.h | 160 +++++++++++++++ 4 files changed, 390 insertions(+), 4 deletions(-) create mode 100644 src/modules/filesystem/physfs/PhysfsIo.cpp create mode 100644 src/modules/filesystem/physfs/PhysfsIo.h diff --git a/CMakeLists.txt b/CMakeLists.txt index c89eb61df..ae1bf6bdd 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -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/src/modules/filesystem/physfs/Filesystem.cpp b/src/modules/filesystem/physfs/Filesystem.cpp index 28e562c57..ecd6fb187 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" @@ -145,7 +146,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 +166,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 +286,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..ee2454d0d --- /dev/null +++ b/src/modules/filesystem/physfs/PhysfsIo.h @@ -0,0 +1,160 @@ +/** + * 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 + { + /*.version =*/ Derived::version, + /*.opaque =*/ this, + /*.read =*/ staticRead, // may be NULL + /*.write =*/ staticWrite, // may be NULL + /*.seek =*/ staticSeek, + /*.tell =*/ staticTell, + /*.length =*/ staticLength, + /*.duplicate =*/ staticDuplicate, + /*.flush =*/ staticFlush, // may be NULL + /*.destroy =*/ staticDestroy, + } + { + } + +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); } + + ~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 From f2ff90c68bc7de6ac0f9834694803a84a1c630ab Mon Sep 17 00:00:00 2001 From: Miku AuahDark Date: Tue, 23 May 2023 10:45:09 +0800 Subject: [PATCH 10/29] Workaround alignment issue in ENet on 32-bit platforms. --- src/libraries/enet/enet.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/libraries/enet/enet.cpp b/src/libraries/enet/enet.cpp index bca527cd6..b713efaef 100644 --- a/src/libraries/enet/enet.cpp +++ b/src/libraries/enet/enet.cpp @@ -148,7 +148,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) From f7432bd2d4ffe71490aee457753a759e73e6618e Mon Sep 17 00:00:00 2001 From: Miku AuahDark Date: Tue, 23 May 2023 10:55:01 +0800 Subject: [PATCH 11/29] Swap alignment workaround from processor to ternary operator. --- src/common/runtime.cpp | 17 ++++++----------- 1 file changed, 6 insertions(+), 11 deletions(-) diff --git a/src/common/runtime.cpp b/src/common/runtime.cpp index f9efa8005..a65c51b2f 100644 --- a/src/common/runtime.cpp +++ b/src/common/runtime.cpp @@ -136,17 +136,12 @@ 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. -#if UINTPTR_MAX == 0xffffffff - // https://github.com/love2d/love/issues/1916 - // This appears to be ABI violation on 32-bit platforms. However it seems - // there's no reliable way to get the correct alignment pre-C++17. Consider - // that 32-bit still fits in 2^53 range, it's perfectly fine to assume - // alignment of 1. - const size_t minalign = 1; -#else - const size_t minalign = LOVE_ALIGNOF(std::max_align_t); -#endif + // 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) From 4cd0bc0de44a4aa3fc20eb884b223ee56aa6cc37 Mon Sep 17 00:00:00 2001 From: Miku AuahDark Date: Tue, 23 May 2023 11:12:34 +0800 Subject: [PATCH 12/29] Improve full-lightuserdata check on 32-bit platforms. On 32-bit platforms, full-lightuserdata is always supported since their pointer fits entirely in 2^48 limitation that LuaJIT had in older version. Also suppress warning about key > 0x20000000000000ULL is always false on enet.cpp when compiling on 32-bit platforms. --- src/common/runtime.cpp | 4 ++++ src/libraries/enet/enet.cpp | 13 ++++++++++--- 2 files changed, 14 insertions(+), 3 deletions(-) diff --git a/src/common/runtime.cpp b/src/common/runtime.cpp index a65c51b2f..0915f7bd2 100644 --- a/src/common/runtime.cpp +++ b/src/common/runtime.cpp @@ -106,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 diff --git a/src/libraries/enet/enet.cpp b/src/libraries/enet/enet.cpp index b713efaef..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 @@ -168,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); } From f1a5e47aaeb293e977b22446352a1698a723abe7 Mon Sep 17 00:00:00 2001 From: ImagicTheCat Date: Wed, 24 May 2023 16:08:27 +0200 Subject: [PATCH 13/29] Fix/improve MD5 hashing. --- src/modules/data/HashFunction.cpp | 32 +++++++++++++++++-------------- 1 file changed, 18 insertions(+), 14 deletions(-) diff --git a/src/modules/data/HashFunction.cpp b/src/modules/data/HashFunction.cpp index 34d9e008d..79f4d25ef 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; From 4304132f8f8c81e49c478c777ad420b4784be517 Mon Sep 17 00:00:00 2001 From: ImagicTheCat Date: Wed, 24 May 2023 16:09:34 +0200 Subject: [PATCH 14/29] Fix/improve SHA1 hashing. --- src/modules/data/HashFunction.cpp | 26 +++++++++++--------------- 1 file changed, 11 insertions(+), 15 deletions(-) diff --git a/src/modules/data/HashFunction.cpp b/src/modules/data/HashFunction.cpp index 79f4d25ef..49c5d970c 100644 --- a/src/modules/data/HashFunction.cpp +++ b/src/modules/data/HashFunction.cpp @@ -204,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]; From ade382072d24dfbf75f0615f26b1481ea389ada3 Mon Sep 17 00:00:00 2001 From: ImagicTheCat Date: Wed, 24 May 2023 16:10:53 +0200 Subject: [PATCH 15/29] Fix/improve SHA-224/256 hashing. --- src/modules/data/HashFunction.cpp | 26 +++++++++++--------------- 1 file changed, 11 insertions(+), 15 deletions(-) diff --git a/src/modules/data/HashFunction.cpp b/src/modules/data/HashFunction.cpp index 49c5d970c..b661c7af1 100644 --- a/src/modules/data/HashFunction.cpp +++ b/src/modules/data/HashFunction.cpp @@ -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]; From 22e68868e195f83e53fbafe994c0f13f9d13f5a0 Mon Sep 17 00:00:00 2001 From: ImagicTheCat Date: Wed, 24 May 2023 16:21:35 +0200 Subject: [PATCH 16/29] Fix/improve SHA-384/512 hashing. --- src/modules/data/HashFunction.cpp | 29 ++++++++++++----------------- 1 file changed, 12 insertions(+), 17 deletions(-) diff --git a/src/modules/data/HashFunction.cpp b/src/modules/data/HashFunction.cpp index b661c7af1..03c67f68f 100644 --- a/src/modules/data/HashFunction.cpp +++ b/src/modules/data/HashFunction.cpp @@ -455,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; - - uint8 *padded = new uint8[paddedLength + 16]; - paddedLength += 8; + // Compute final padded length, accounting for the appended bit (byte) and size + uint64 paddedLength = extend_multiple(length + 1 + 16, 128); + + 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]; From a161207f023cc5a6eb4bba281fff8128b5dd1df7 Mon Sep 17 00:00:00 2001 From: Sasha Szpakowski Date: Sun, 28 May 2023 12:30:32 -0300 Subject: [PATCH 17/29] update PhysFS to 3.2.0. Fixes #1880. --- src/libraries/physfs/physfs.c | 125 +++++++++++++----- src/libraries/physfs/physfs.h | 44 +++++- src/libraries/physfs/physfs_archiver_7z.c | 11 +- src/libraries/physfs/physfs_archiver_grp.c | 2 +- src/libraries/physfs/physfs_archiver_hog.c | 78 +++++++++-- .../physfs/physfs_archiver_iso9660.c | 5 +- src/libraries/physfs/physfs_archiver_mvl.c | 2 +- src/libraries/physfs/physfs_archiver_qpak.c | 3 +- src/libraries/physfs/physfs_archiver_slb.c | 5 +- .../physfs/physfs_archiver_unpacked.c | 4 +- src/libraries/physfs/physfs_archiver_vdf.c | 3 +- src/libraries/physfs/physfs_archiver_wad.c | 2 +- src/libraries/physfs/physfs_archiver_zip.c | 16 ++- src/libraries/physfs/physfs_internal.h | 67 ++++++++-- src/libraries/physfs/physfs_lzmasdk.h | 3 + src/libraries/physfs/physfs_miniz.h | 18 ++- .../physfs/physfs_platform_android.c | 117 ++++++++++++++++ src/libraries/physfs/physfs_platform_apple.m | 25 +++- src/libraries/physfs/physfs_platform_os2.c | 8 +- src/libraries/physfs/physfs_platform_posix.c | 48 +++++-- src/libraries/physfs/physfs_platform_unix.c | 16 ++- .../physfs/physfs_platform_windows.c | 10 +- src/libraries/physfs/physfs_platforms.h | 10 +- src/libraries/physfs/physfs_unicode.c | 19 ++- src/modules/filesystem/physfs/Filesystem.cpp | 7 + 25 files changed, 522 insertions(+), 126 deletions(-) create mode 100644 src/libraries/physfs/physfs_platform_android.c 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/filesystem/physfs/Filesystem.cpp b/src/modules/filesystem/physfs/Filesystem.cpp index 28e562c57..e5d7d93d3 100644 --- a/src/modules/filesystem/physfs/Filesystem.cpp +++ b/src/modules/filesystem/physfs/Filesystem.cpp @@ -123,6 +123,13 @@ const char *Filesystem::getName() const void Filesystem::init(const char *arg0) { +#ifdef LOVE_ANDROID + // TODO: This should be a pointer to an initializeed PHYSFS_AndroidInit + // struct on android. But it's only used for PHYSFS_getBaseDir and + // PHYSFS_getPrefDir, which we don't use right now... + arg0 = nullptr; +#endif + if (!PHYSFS_init(arg0)) throw love::Exception("Failed to initialize filesystem: %s", PHYSFS_getErrorByCode(PHYSFS_getLastErrorCode())); From 5f733ef0fa6b0f6941274967b54a8b1f98d84ed5 Mon Sep 17 00:00:00 2001 From: Sasha Szpakowski Date: Mon, 29 May 2023 09:43:44 -0300 Subject: [PATCH 18/29] Add new PhysfsIo file to xcode project --- platform/xcode/liblove.xcodeproj/project.pbxproj | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/platform/xcode/liblove.xcodeproj/project.pbxproj b/platform/xcode/liblove.xcodeproj/project.pbxproj index 588230091..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 */, From 93c2709f7ce7e2256a6f334a86bd20aad57bc547 Mon Sep 17 00:00:00 2001 From: Sasha Szpakowski Date: Mon, 29 May 2023 10:29:46 -0300 Subject: [PATCH 19/29] Maybe fix build when VS2013 is used --- src/modules/filesystem/physfs/PhysfsIo.h | 39 ++++++++++++++---------- 1 file changed, 23 insertions(+), 16 deletions(-) diff --git a/src/modules/filesystem/physfs/PhysfsIo.h b/src/modules/filesystem/physfs/PhysfsIo.h index ee2454d0d..1b95c8d8e 100644 --- a/src/modules/filesystem/physfs/PhysfsIo.h +++ b/src/modules/filesystem/physfs/PhysfsIo.h @@ -35,26 +35,31 @@ namespace physfs { template -struct PhysfsIo : PHYSFS_Io { +struct PhysfsIo : PHYSFS_Io +{ protected: + PhysfsIo() - : PHYSFS_Io - { - /*.version =*/ Derived::version, - /*.opaque =*/ this, - /*.read =*/ staticRead, // may be NULL - /*.write =*/ staticWrite, // may be NULL - /*.seek =*/ staticSeek, - /*.tell =*/ staticTell, - /*.length =*/ staticLength, - /*.duplicate =*/ staticDuplicate, - /*.flush =*/ staticFlush, // may be NULL - /*.destroy =*/ staticDestroy, - } + : 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) { @@ -109,7 +114,8 @@ struct PhysfsIo : PHYSFS_Io { } }; -struct StripSuffixIo : public PhysfsIo { +struct StripSuffixIo : public PhysfsIo +{ static const uint32_t version = 0; std::string filename; @@ -119,7 +125,7 @@ struct StripSuffixIo : public PhysfsIo { // because Physfs will take ownership of this object and call destroy on it later. static StripSuffixIo* create(std::string f) { return new StripSuffixIo(f); } - ~StripSuffixIo() + virtual ~StripSuffixIo() { if (file) { @@ -142,6 +148,7 @@ struct StripSuffixIo : public PhysfsIo { int64_t flush(); private: + StripSuffixIo(std::string f) : filename(std::move(f)) , file(std::fopen(filename.c_str(), "rb")) From e9c564e9894478a0f40d7c84be7c92a947eb2e59 Mon Sep 17 00:00:00 2001 From: Miku AuahDark Date: Thu, 1 Jun 2023 15:10:43 +0800 Subject: [PATCH 20/29] Use new PhysfsIo class for Android AAsset wrapper. --- src/common/android.cpp | 181 ++++++++++------------- src/modules/filesystem/physfs/PhysfsIo.h | 2 +- 2 files changed, 82 insertions(+), 101 deletions(-) diff --git a/src/common/android.cpp b/src/common/android.cpp index 882c78635..696d91c14 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; } diff --git a/src/modules/filesystem/physfs/PhysfsIo.h b/src/modules/filesystem/physfs/PhysfsIo.h index 1b95c8d8e..35b92cc6c 100644 --- a/src/modules/filesystem/physfs/PhysfsIo.h +++ b/src/modules/filesystem/physfs/PhysfsIo.h @@ -40,7 +40,7 @@ struct PhysfsIo : PHYSFS_Io protected: PhysfsIo() - : PHYSFS_Io() + : PHYSFS_Io() { // Direct initialization of PHYSFS_Io members in the initializer list // doesn't work in VS2013. From e559fd5e685f2a1b30ae0610f753acda03004ca2 Mon Sep 17 00:00:00 2001 From: Miku AuahDark Date: Thu, 1 Jun 2023 15:33:04 +0800 Subject: [PATCH 21/29] Pass pointer of PHYSFS_AndroidInit struct in Android when initializing PhysFS. --- src/common/android.cpp | 9 +++++++++ src/common/android.h | 6 ++++++ src/modules/filesystem/physfs/Filesystem.cpp | 5 +---- 3 files changed, 16 insertions(+), 4 deletions(-) diff --git a/src/common/android.cpp b/src/common/android.cpp index 696d91c14..a0a1f4444 100644 --- a/src/common/android.cpp +++ b/src/common/android.cpp @@ -804,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/modules/filesystem/physfs/Filesystem.cpp b/src/modules/filesystem/physfs/Filesystem.cpp index 09edabdd0..d3c650269 100644 --- a/src/modules/filesystem/physfs/Filesystem.cpp +++ b/src/modules/filesystem/physfs/Filesystem.cpp @@ -125,10 +125,7 @@ const char *Filesystem::getName() const void Filesystem::init(const char *arg0) { #ifdef LOVE_ANDROID - // TODO: This should be a pointer to an initializeed PHYSFS_AndroidInit - // struct on android. But it's only used for PHYSFS_getBaseDir and - // PHYSFS_getPrefDir, which we don't use right now... - arg0 = nullptr; + arg0 = love::android::getArg0(); #endif if (!PHYSFS_init(arg0)) From ec0c65642b6d17ba48d365fdcd36dd5cb6db8338 Mon Sep 17 00:00:00 2001 From: George Paton Date: Thu, 15 Jun 2023 19:22:57 +1000 Subject: [PATCH 22/29] Fix #1943 duplicate frames being skipped when decoding Theora --- .../video/theora/TheoraVideoStream.cpp | 21 ++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/src/modules/video/theora/TheoraVideoStream.cpp b/src/modules/video/theora/TheoraVideoStream.cpp index 7e109ae7b..ff48e9526 100644 --- a/src/modules/video/theora/TheoraVideoStream.cpp +++ b/src/modules/video/theora/TheoraVideoStream.cpp @@ -229,17 +229,24 @@ void TheoraVideoStream::threadedFillBackBuffer(double dt) failedSeek = true; } + if (packet.granulepos > 0) { + th_decode_ctl(decoder, TH_DECCTL_SET_GRANPOS, &packet.granulepos, sizeof(packet.granulepos)); + } + + // TODO: Should be checking the result of th_decode_packetin th_decode_ycbcr_out(decoder, bufferinfo); hasFrame = true; - ogg_int64_t granulePosition; - do - { - if (demuxer.readPacket(packet)) - return; - } while (th_decode_packetin(decoder, &packet, &granulePosition) != 0); + // Decode the current packet for the frame + ogg_int64_t decoderPosition; + th_decode_packetin(decoder, &packet, &decoderPosition); + + // Prepare the next packet for the next frame + if (demuxer.readPacket(packet)) + return; + 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 From f06d6c62815ebf10f70f680a82b30313d8c5f8b6 Mon Sep 17 00:00:00 2001 From: Sasha Szpakowski Date: Sat, 17 Jun 2023 20:33:59 -0300 Subject: [PATCH 23/29] Only add platform field to love.joystick.getGamepadMappingString's result if it's not there already. --- src/modules/joystick/sdl/Joystick.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) 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; } From df7a55911df5e84bd6eb46cebd48220b7305c62a Mon Sep 17 00:00:00 2001 From: Sasha Szpakowski Date: Sat, 17 Jun 2023 22:16:18 -0300 Subject: [PATCH 24/29] Fix love.joystick.setGamepadMapping breaking the mapping string when replacing an existing binding. issue #1936. --- src/modules/joystick/sdl/JoystickModule.cpp | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/src/modules/joystick/sdl/JoystickModule.cpp b/src/modules/joystick/sdl/JoystickModule.cpp index dd5d3bf2a..d979fc02b 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. From 241378e43748e18bb20c29eb885d839882d827f2 Mon Sep 17 00:00:00 2001 From: George Paton Date: Sun, 18 Jun 2023 12:39:56 +1000 Subject: [PATCH 25/29] Revert to old packetin loop, set granulePosition after each packet read in --- src/modules/video/theora/TheoraVideoStream.cpp | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/src/modules/video/theora/TheoraVideoStream.cpp b/src/modules/video/theora/TheoraVideoStream.cpp index ff48e9526..a54ccb42d 100644 --- a/src/modules/video/theora/TheoraVideoStream.cpp +++ b/src/modules/video/theora/TheoraVideoStream.cpp @@ -229,21 +229,18 @@ void TheoraVideoStream::threadedFillBackBuffer(double dt) failedSeek = true; } - if (packet.granulepos > 0) { - th_decode_ctl(decoder, TH_DECCTL_SET_GRANPOS, &packet.granulepos, sizeof(packet.granulepos)); - } - - // TODO: Should be checking the result of th_decode_packetin th_decode_ycbcr_out(decoder, bufferinfo); hasFrame = true; - // Decode the current packet for the frame ogg_int64_t decoderPosition; - th_decode_packetin(decoder, &packet, &decoderPosition); + do + { + if (demuxer.readPacket(packet)) + return; - // Prepare the next packet for the next frame - if (demuxer.readPacket(packet)) - return; + 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, decoderPosition); From 5f4f0b424bc48f8afd3d63bb96c9965b2917ced0 Mon Sep 17 00:00:00 2001 From: Sasha Szpakowski Date: Sun, 18 Jun 2023 11:54:17 -0300 Subject: [PATCH 26/29] love.joystick: more fixes for duplicate platform fields in gamepad mapping strings. --- src/modules/joystick/sdl/JoystickModule.cpp | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/modules/joystick/sdl/JoystickModule.cpp b/src/modules/joystick/sdl/JoystickModule.cpp index d979fc02b..de45fe0ff 100644 --- a/src/modules/joystick/sdl/JoystickModule.cpp +++ b/src/modules/joystick/sdl/JoystickModule.cpp @@ -470,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; } @@ -494,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; From e582677344954d43369fb1a16a520b75c610cb0a Mon Sep 17 00:00:00 2001 From: Sasha Szpakowski Date: Sat, 1 Jul 2023 17:53:29 -0300 Subject: [PATCH 27/29] Fix mouse position backward compatibility with new SDL2 versions. Clamp the mouse coordinates to the window's dimensions. --- src/modules/event/sdl/Event.cpp | 18 ++++++++++++++++ src/modules/mouse/sdl/Mouse.cpp | 35 ++++++++++++++++++------------- src/modules/window/Window.h | 2 ++ src/modules/window/sdl/Window.cpp | 7 +++++++ src/modules/window/sdl/Window.h | 2 ++ 5 files changed, 50 insertions(+), 14 deletions(-) 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/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/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 f2290112a..e9175f3d4 100644 --- a/src/modules/window/sdl/Window.cpp +++ b/src/modules/window/sdl/Window.cpp @@ -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; From 7706ac7ba9f02e3d301e2b21183773d18f27ea7f Mon Sep 17 00:00:00 2001 From: Sasha Szpakowski Date: Sat, 22 Jul 2023 15:24:10 -0300 Subject: [PATCH 28/29] iOS: don't try to open audio recording devices for capture. It hard-crashes inside the alcCaptureOpenDevice call with Apple's OpenAL implementation. See #1912. --- src/modules/audio/openal/RecordingDevice.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/modules/audio/openal/RecordingDevice.cpp b/src/modules/audio/openal/RecordingDevice.cpp index 9e61c1fcf..6b6c86326 100644 --- a/src/modules/audio/openal/RecordingDevice.cpp +++ b/src/modules/audio/openal/RecordingDevice.cpp @@ -67,7 +67,11 @@ bool RecordingDevice::start(int samples, int sampleRate, int bitDepth, int chann if (isRecording()) stop(); + // This hard-crashes on iOS with Apple's OpenAL implementation, even when + // the user gives permission to the app. +#ifndef LOVE_IOS device = alcCaptureOpenDevice(name.c_str(), sampleRate, format, samples); +#endif if (device == nullptr) return false; From 536e7e9f696a84925da6695180d0603e065f5819 Mon Sep 17 00:00:00 2001 From: Miku AuahDark Date: Thu, 3 Aug 2023 17:54:46 +0800 Subject: [PATCH 29/29] configure.ac: Specify CXXFLAGS instead of CPPFLAGS. Autotools uses CPPFLAGS for C preprocessor and CXXFLAGS for C++ compile flags. --- platform/unix/configure.ac | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/platform/unix/configure.ac b/platform/unix/configure.ac index 1d681e06e..a6552f94a 100644 --- a/platform/unix/configure.ac +++ b/platform/unix/configure.ac @@ -31,7 +31,7 @@ ACLOVE_CPP11_TEST # Add -fvisibility=hidden and -fvisibility-inlines-hidden CFLAGS="-fvisibility=hidden $CFLAGS" -CPPFLAGS="-fvisibility=hidden -fvisibility-inlines-hidden $CPPFLAGS" +CXXFLAGS="-fvisibility=hidden -fvisibility-inlines-hidden $CXXFLAGS" # Allow people on OSX to use autotools, they need their platform files AC_ARG_ENABLE([osx],