From 878ad785bcb121a7faea1fe66f48ed7c894af609 Mon Sep 17 00:00:00 2001 From: Alex Szpakowski Date: Sun, 16 Jan 2022 18:41:14 -0400 Subject: [PATCH 01/55] Remove obsolete macOS window min size workaround. Fixes #1444. The reason for the SetWindowMinSize call after exiting fullscreen doesn't seem to be needed anymore, and it was breaking setFullscreen(false) in Linux. --- src/modules/window/sdl/Window.cpp | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/modules/window/sdl/Window.cpp b/src/modules/window/sdl/Window.cpp index c7103420d..798b606ed 100644 --- a/src/modules/window/sdl/Window.cpp +++ b/src/modules/window/sdl/Window.cpp @@ -723,11 +723,6 @@ bool Window::setFullscreen(bool fullscreen, Window::FullscreenType fstype) { SDL_GL_MakeCurrent(window, context); updateSettings(newsettings, true); - - // Apparently this gets un-set when we exit fullscreen (at least in OS X). - if (!fullscreen) - SDL_SetWindowMinimumSize(window, settings.minwidth, settings.minheight); - return true; } From 456c16b9c4a4e4593ad411d3597b9d86593b7eb9 Mon Sep 17 00:00:00 2001 From: Alex Szpakowski Date: Fri, 4 Feb 2022 20:27:13 -0400 Subject: [PATCH 02/55] macOS: Fix love.framework's runpath search path Fixes loading love.framework from a standalone Lua executable, and potentially fixes some code signing issues (#1737) --- platform/xcode/liblove.xcodeproj/project.pbxproj | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/platform/xcode/liblove.xcodeproj/project.pbxproj b/platform/xcode/liblove.xcodeproj/project.pbxproj index ac729053a..a9e7bb610 100644 --- a/platform/xcode/liblove.xcodeproj/project.pbxproj +++ b/platform/xcode/liblove.xcodeproj/project.pbxproj @@ -4951,7 +4951,6 @@ "\"$(SRCROOT)/../../src/libraries/enet/libenet/include\"", ); IPHONEOS_DEPLOYMENT_TARGET = 8.0; - LD_RUNPATH_SEARCH_PATHS = "@rpath"; LIBRARY_SEARCH_PATHS = ""; MACOSX_DEPLOYMENT_TARGET = 10.7; ONLY_ACTIVE_ARCH = NO; @@ -5017,7 +5016,6 @@ "\"$(SRCROOT)/../../src/libraries/enet/libenet/include\"", ); IPHONEOS_DEPLOYMENT_TARGET = 8.0; - LD_RUNPATH_SEARCH_PATHS = "@rpath"; LIBRARY_SEARCH_PATHS = ""; MACOSX_DEPLOYMENT_TARGET = 10.7; ONLY_ACTIVE_ARCH = YES; @@ -5158,7 +5156,6 @@ "\"$(SRCROOT)/../../src/libraries/enet/libenet/include\"", ); IPHONEOS_DEPLOYMENT_TARGET = 8.0; - LD_RUNPATH_SEARCH_PATHS = "@rpath"; LIBRARY_SEARCH_PATHS = ""; LLVM_LTO = YES; MACOSX_DEPLOYMENT_TARGET = 10.7; @@ -5193,6 +5190,7 @@ ); INFOPLIST_FILE = "macosx/liblove-macosx.plist"; LD_DYLIB_INSTALL_NAME = "@rpath/$(EXECUTABLE_PATH)"; + LD_RUNPATH_SEARCH_PATHS = "@loader_path/../../../"; LIBRARY_SEARCH_PATHS = ( "$(inherited)", "$(PROJECT_DIR)/ios/libraries/freetype", @@ -5232,6 +5230,7 @@ ); INFOPLIST_FILE = "macosx/liblove-macosx.plist"; LD_DYLIB_INSTALL_NAME = "@rpath/$(EXECUTABLE_PATH)"; + LD_RUNPATH_SEARCH_PATHS = "@loader_path/../../../"; LIBRARY_SEARCH_PATHS = ( "$(inherited)", "$(PROJECT_DIR)/ios/libraries/freetype", @@ -5272,6 +5271,7 @@ ); INFOPLIST_FILE = "macosx/liblove-macosx.plist"; LD_DYLIB_INSTALL_NAME = "@rpath/$(EXECUTABLE_PATH)"; + LD_RUNPATH_SEARCH_PATHS = "@loader_path/../../../"; LIBRARY_SEARCH_PATHS = ( "$(inherited)", "$(PROJECT_DIR)/ios/libraries/freetype", From 7216a022f9f66682be91441b402a1983b29295ca Mon Sep 17 00:00:00 2001 From: Alex Szpakowski Date: Fri, 4 Feb 2022 22:09:50 -0400 Subject: [PATCH 03/55] metal: remove unused internal code --- src/modules/graphics/metal/Graphics.mm | 5 ----- src/modules/graphics/metal/Shader.mm | 5 ----- 2 files changed, 10 deletions(-) diff --git a/src/modules/graphics/metal/Graphics.mm b/src/modules/graphics/metal/Graphics.mm index 4466eb51d..baf263867 100644 --- a/src/modules/graphics/metal/Graphics.mm +++ b/src/modules/graphics/metal/Graphics.mm @@ -130,11 +130,6 @@ static inline id getMTLTexture(love::graphics::Texture *tex) return tex ? (__bridge id)(void *) tex->getHandle() : nil; } -static inline id getMTLSampler(love::graphics::Texture *tex) -{ - return tex ? (__bridge id)(void *) tex->getSamplerHandle() : nil; -} - static inline id getMTLRenderTarget(love::graphics::Texture *tex) { return tex ? (__bridge id)(void *) tex->getRenderTargetHandle() : nil; diff --git a/src/modules/graphics/metal/Shader.mm b/src/modules/graphics/metal/Shader.mm index 7e8687e9f..2b8464db7 100644 --- a/src/modules/graphics/metal/Shader.mm +++ b/src/modules/graphics/metal/Shader.mm @@ -232,11 +232,6 @@ static inline id getMTLTexture(love::graphics::Buffer *buffer) return buffer ? (__bridge id)(void *) buffer->getTexelBufferHandle() : nil; } -static inline id getMTLSampler(love::graphics::Texture *tex) -{ - return tex ? (__bridge id)(void *) tex->getSamplerHandle() : nil; -} - static inline id getMTLBuffer(love::graphics::Buffer *buffer) { return buffer ? (__bridge id)(void *) buffer->getHandle() : nil; From f6c65422f58e18da81c6929d35fa494fd04131eb Mon Sep 17 00:00:00 2001 From: Miku AuahDark Date: Sat, 5 Feb 2022 10:58:26 +0800 Subject: [PATCH 04/55] Don't use lua.hpp in lua-https --- src/libraries/luahttps/src/lua/main.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/libraries/luahttps/src/lua/main.cpp b/src/libraries/luahttps/src/lua/main.cpp index 9ebff25d5..d4304b86a 100644 --- a/src/libraries/luahttps/src/lua/main.cpp +++ b/src/libraries/luahttps/src/lua/main.cpp @@ -1,4 +1,8 @@ -#include +extern "C" +{ +#include +#include +} #include "../common/HTTPS.h" #include "../common/config.h" From a6b6103b87091c9528307d2025551c02833fad25 Mon Sep 17 00:00:00 2001 From: Mansour Moufid Date: Sun, 6 Feb 2022 16:38:21 -0500 Subject: [PATCH 05/55] Enable LuaJIT on macOS. --- CMakeLists.txt | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 7c59eaa6d..9251797f8 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -48,12 +48,16 @@ else() set(LOVE_TARGET_PLATFORM x86) endif() -option(LOVE_JIT "Use LuaJIT" TRUE) +if(APPLE) + option(LOVE_JIT "Use LuaJIT" FALSE) +else() + option(LOVE_JIT "Use LuaJIT" TRUE) +endif() option(LOVE_MPG123 "Use mpg123" TRUE) if(LOVE_JIT) if(APPLE) - message(FATAL_ERROR "JIT not supported yet on Mac. Please use -DLOVE_JIT=0.") + message(WARNING "JIT not supported yet on Mac.") endif() message(STATUS "LuaJIT: Enabled") else() From 440bbeb9b3195000f215e23e2721f5ad3ffad54e Mon Sep 17 00:00:00 2001 From: Mansour Moufid Date: Sun, 6 Feb 2022 16:38:47 -0500 Subject: [PATCH 06/55] Link to system frameworks. --- CMakeLists.txt | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CMakeLists.txt b/CMakeLists.txt index 9251797f8..b461e8ebd 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -321,6 +321,9 @@ if (APPLE) set(LOVE_SRC_COMMON ${LOVE_SRC_COMMON} src/common/macosx.mm ) + set(LOVE_LINK_LIBRARIES ${LOVE_LINK_LIBRARIES} objc) + set(LOVE_LINK_LIBRARIES ${LOVE_LINK_LIBRARIES} "-framework CoreFoundation") + set(LOVE_LINK_LIBRARIES ${LOVE_LINK_LIBRARIES} "-framework AppKit") endif() source_group("common" FILES ${LOVE_SRC_COMMON}) @@ -1612,6 +1615,7 @@ if(APPLE) set(LOVE_SRC_3P_PHYSFS ${LOVE_SRC_3P_PHYSFS} src/libraries/physfs/physfs_platform_apple.m ) + set(LOVE_LINK_LIBRARIES ${LOVE_LINK_LIBRARIES} "-framework IOKit") endif() add_library(love_3p_physfs ${LOVE_SRC_3P_PHYSFS}) From b822b9cbe7807a3cd2f1de842ae0cd4fd1f45caa Mon Sep 17 00:00:00 2001 From: Mansour Moufid Date: Sun, 6 Feb 2022 17:06:37 -0500 Subject: [PATCH 07/55] Prepend LOVE_INCLUDE_DIRS rather than append. --- CMakeLists.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/CMakeLists.txt b/CMakeLists.txt index b461e8ebd..82b1c7883 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1735,6 +1735,7 @@ set(LOVE_LIB_SRC ) include_directories( + BEFORE src src/libraries src/modules From c28731ea809d52c5c7c8e8950d7cb68080a7992e Mon Sep 17 00:00:00 2001 From: Alex Szpakowski Date: Sat, 12 Feb 2022 09:37:30 -0400 Subject: [PATCH 08/55] opengl: fix love's internal quad index buffer failing to populate --- src/modules/graphics/Graphics.cpp | 6 ++++-- src/modules/graphics/metal/Buffer.mm | 4 ++-- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/src/modules/graphics/Graphics.cpp b/src/modules/graphics/Graphics.cpp index 92e1b3408..14f839c83 100644 --- a/src/modules/graphics/Graphics.cpp +++ b/src/modules/graphics/Graphics.cpp @@ -248,8 +248,10 @@ void Graphics::createQuadIndexBuffer() Buffer::Settings settings(BUFFERUSAGEFLAG_INDEX, BUFFERDATAUSAGE_STATIC); quadIndexBuffer = newBuffer(settings, DATAFORMAT_UINT16, nullptr, size, 0); - Buffer::Mapper map(*quadIndexBuffer); - fillIndices(TRIANGLEINDEX_QUADS, 0, LOVE_UINT16_MAX, (uint16 *) map.data); + { + Buffer::Mapper map(*quadIndexBuffer); + fillIndices(TRIANGLEINDEX_QUADS, 0, LOVE_UINT16_MAX, (uint16 *) map.data); + } quadIndexBuffer->setImmutable(true); } diff --git a/src/modules/graphics/metal/Buffer.mm b/src/modules/graphics/metal/Buffer.mm index b4ac8ef5b..07cc47f3a 100644 --- a/src/modules/graphics/metal/Buffer.mm +++ b/src/modules/graphics/metal/Buffer.mm @@ -111,7 +111,7 @@ Buffer::~Buffer() void *Buffer::map(MapType /*map*/, size_t offset, size_t size) { @autoreleasepool { - if (size == 0) + if (size == 0 || isImmutable()) return nullptr; Range r(offset, size); @@ -160,7 +160,7 @@ void Buffer::unmap(size_t usedoffset, size_t usedsize) void Buffer::fill(size_t offset, size_t size, const void *data) { @autoreleasepool { - if (size == 0) + if (size == 0 || isImmutable()) return; size_t buffersize = getSize(); From 0d744192593a63c9d2d209d83a60111afc4b835f Mon Sep 17 00:00:00 2001 From: Alex Szpakowski Date: Sun, 13 Feb 2022 16:11:58 -0400 Subject: [PATCH 09/55] cmake: add warning about use on macOS --- CMakeLists.txt | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/CMakeLists.txt b/CMakeLists.txt index 82b1c7883..a4ccb5409 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -36,6 +36,11 @@ set(CMAKE_POSITION_INDEPENDENT_CODE TRUE) set (CMAKE_CXX_STANDARD 11) +if(APPLE) + message(WARNING "CMake is not an officially supported build system for love on Apple platforms.") + message(WARNING "Use the prebuilt .app or the xcode project in platform/xcode/ instead.") +endif() + if(MSVC) set(LOVE_CONSOLE_EXE_NAME lovec) endif() From caf89998ccc236e113da5cbfb1d115e765156e44 Mon Sep 17 00:00:00 2001 From: Alex Szpakowski Date: Tue, 15 Feb 2022 21:12:13 -0400 Subject: [PATCH 10/55] iOS: fix shader compilation with metal --- src/modules/graphics/metal/Shader.mm | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/modules/graphics/metal/Shader.mm b/src/modules/graphics/metal/Shader.mm index 2b8464db7..ba5db96aa 100644 --- a/src/modules/graphics/metal/Shader.mm +++ b/src/modules/graphics/metal/Shader.mm @@ -308,6 +308,11 @@ Shader::Shader(id device, StrongRef stag bool forcedefault = false; bool forwardcompat = true; +#ifdef LOVE_IOS + defaultversion = 320; + defaultprofile = EEsProfile; +#endif + if (!tshader->parse(&defaultTBuiltInResource, defaultversion, defaultprofile, forcedefault, forwardcompat, EShMsgSuppressWarnings)) { const char *stagename = "unknown"; From 171533dbf8ffa6ddb1455589a787529a7390478e Mon Sep 17 00:00:00 2001 From: Alex Szpakowski Date: Tue, 15 Feb 2022 21:16:55 -0400 Subject: [PATCH 11/55] Another shader fix for metal+iOS --- src/modules/graphics/metal/Shader.mm | 1 + 1 file changed, 1 insertion(+) diff --git a/src/modules/graphics/metal/Shader.mm b/src/modules/graphics/metal/Shader.mm index ba5db96aa..d3060488f 100644 --- a/src/modules/graphics/metal/Shader.mm +++ b/src/modules/graphics/metal/Shader.mm @@ -311,6 +311,7 @@ Shader::Shader(id device, StrongRef stag #ifdef LOVE_IOS defaultversion = 320; defaultprofile = EEsProfile; + forcedefault = true; #endif if (!tshader->parse(&defaultTBuiltInResource, defaultversion, defaultprofile, forcedefault, forwardcompat, EShMsgSuppressWarnings)) From 6e35ffe2d8ba2900d284c50642210cf8e2c323a4 Mon Sep 17 00:00:00 2001 From: Alex Szpakowski Date: Sat, 19 Feb 2022 14:17:37 -0400 Subject: [PATCH 12/55] metal: fix window pixel size when resizing --- src/modules/window/sdl/Window.cpp | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/src/modules/window/sdl/Window.cpp b/src/modules/window/sdl/Window.cpp index 839d55d6e..234e95887 100644 --- a/src/modules/window/sdl/Window.cpp +++ b/src/modules/window/sdl/Window.cpp @@ -660,7 +660,17 @@ bool Window::onSizeChanged(int width, int height) windowWidth = width; windowHeight = height; - SDL_GL_GetDrawableSize(window, &pixelWidth, &pixelHeight); + if (glcontext != nullptr) + SDL_GL_GetDrawableSize(window, &pixelWidth, &pixelHeight); +#ifdef LOVE_GRAPHICS_METAL + else if (metalView != nullptr) + SDL_Metal_GetDrawableSize(window, &pixelWidth, &pixelHeight); +#endif + else + { + pixelWidth = width; + pixelHeight = height; + } if (graphics.get()) { From bc1c38d361209d701146b25e6c8ae991cedc1899 Mon Sep 17 00:00:00 2001 From: Alex Szpakowski Date: Sat, 19 Feb 2022 16:34:52 -0400 Subject: [PATCH 13/55] improve logic for detecting when Texture:generateMipmaps is supported --- src/modules/graphics/Texture.cpp | 51 ++++++++++++++++++++++----- src/modules/graphics/Texture.h | 1 + src/modules/graphics/metal/Texture.mm | 2 -- 3 files changed, 44 insertions(+), 10 deletions(-) diff --git a/src/modules/graphics/Texture.cpp b/src/modules/graphics/Texture.cpp index b0c1c0924..7da786e4d 100644 --- a/src/modules/graphics/Texture.cpp +++ b/src/modules/graphics/Texture.cpp @@ -232,8 +232,13 @@ Texture::Texture(Graphics *gfx, const Settings &settings, const Slices *slices) if (mipmapsMode != MIPMAPS_NONE) mipmapCount = getTotalMipmapCount(pixelWidth, pixelHeight, depth); - if (mipmapsMode == MIPMAPS_AUTO && isPixelFormatDepthStencil(format)) - throw love::Exception("Automatic mipmap generation cannot be used for depth/stencil textures."); + const char *miperr = nullptr; + if (mipmapsMode == MIPMAPS_AUTO && !supportsGenerateMipmaps(miperr)) + { + const char *fstr = "unknown"; + love::getConstant(format, fstr); + throw love::Exception("Automatic mipmap generation is not supported for textures with the %s pixel format.", fstr); + } if (pixelWidth <= 0 || pixelHeight <= 0 || layers <= 0 || depth <= 0) throw love::Exception("Texture dimensions must be greater than 0."); @@ -511,19 +516,49 @@ void Texture::replacePixels(const void *data, size_t size, int slice, int mipmap generateMipmaps(); } -void Texture::generateMipmaps() +bool Texture::supportsGenerateMipmaps(const char *&outReason) const { - if (getMipmapCount() == 1 || getMipmapsMode() == MIPMAPS_NONE) - throw love::Exception("generateMipmaps can only be called on a Texture which was created with mipmaps enabled."); + if (getMipmapsMode() == MIPMAPS_NONE) + { + outReason = "generateMipmaps can only be called on a Texture which was created with mipmaps enabled."; + return false; + } if (isPixelFormatCompressed(format)) - throw love::Exception("generateMipmaps cannot be called on a compressed Texture."); + { + outReason = "generateMipmaps cannot be called on a compressed Texture."; + return false; + } if (isPixelFormatDepthStencil(format)) - throw love::Exception("generateMipmaps cannot be called on a depth/stencil Texture."); + { + outReason = "generateMipmaps cannot be called on a depth/stencil Texture."; + return false; + } if (isPixelFormatInteger(format)) - throw love::Exception("generateMipmaps cannot be called on an integer Texture."); + { + outReason = "generateMipmaps cannot be called on an integer Texture."; + return false; + } + + // This should be linear | rt because that's what metal needs, but the above + // code handles textures can't be used as RTs in metal. + auto gfx = Module::getInstance(Module::M_GRAPHICS); + if (gfx != nullptr && !gfx->isPixelFormatSupported(format, PIXELFORMATUSAGEFLAGS_LINEAR)) + { + outReason = "generateMipmaps cannot be called on textures with formats that don't support linear filtering on this system."; + return false; + } + + return true; +} + +void Texture::generateMipmaps() +{ + const char *err = nullptr; + if (!supportsGenerateMipmaps(err)) + throw love::Exception("%s", err); generateMipmapsInternal(); } diff --git a/src/modules/graphics/Texture.h b/src/modules/graphics/Texture.h index e79ad0528..415e4ace7 100644 --- a/src/modules/graphics/Texture.h +++ b/src/modules/graphics/Texture.h @@ -311,6 +311,7 @@ protected: void uploadImageData(love::image::ImageDataBase *d, int level, int slice, int x, int y); virtual void uploadByteData(PixelFormat pixelformat, const void *data, size_t size, int level, int slice, const Rect &r) = 0; + bool supportsGenerateMipmaps(const char *&outReason) const; virtual void generateMipmapsInternal() = 0; virtual void readbackImageData(love::image::ImageData *imagedata, int slice, int mipmap, const Rect &rect) = 0; diff --git a/src/modules/graphics/metal/Texture.mm b/src/modules/graphics/metal/Texture.mm index fcd01885c..0c2adf944 100644 --- a/src/modules/graphics/metal/Texture.mm +++ b/src/modules/graphics/metal/Texture.mm @@ -269,8 +269,6 @@ void Texture::uploadByteData(PixelFormat pixelformat, const void *data, size_t s void Texture::generateMipmapsInternal() { @autoreleasepool { - // TODO: alternate method for non-color-renderable and non-filterable - // pixel formats. id encoder = Graphics::getInstance()->useBlitEncoder(); [encoder generateMipmapsForTexture:texture]; }} From 0fa00e0bdc2e873f1ed4269dd866bce7d2d90995 Mon Sep 17 00:00:00 2001 From: Alex Szpakowski Date: Sun, 20 Feb 2022 12:23:41 -0400 Subject: [PATCH 14/55] love.graphics.newShader: add a compile options table parameter. The only field currently read from the table is 'defines', which can contain either an array of define names, or name-value pairs. For example: newShader(file, {defines={"MYFEATURE_ENABLED", MYSETTING=1}}) Fixes #1577 --- src/modules/graphics/Graphics.cpp | 35 +++++++++----- src/modules/graphics/Graphics.h | 8 ++-- src/modules/graphics/Shader.cpp | 6 ++- src/modules/graphics/Shader.h | 7 ++- src/modules/graphics/metal/Graphics.mm | 3 +- src/modules/graphics/opengl/Graphics.cpp | 3 +- src/modules/graphics/wrap_Graphics.cpp | 60 ++++++++++++++++++++---- 7 files changed, 94 insertions(+), 28 deletions(-) diff --git a/src/modules/graphics/Graphics.cpp b/src/modules/graphics/Graphics.cpp index 14f839c83..94adee465 100644 --- a/src/modules/graphics/Graphics.cpp +++ b/src/modules/graphics/Graphics.cpp @@ -307,12 +307,18 @@ love::graphics::ParticleSystem *Graphics::newParticleSystem(Texture *texture, in return new ParticleSystem(texture, size); } -ShaderStage *Graphics::newShaderStage(ShaderStageType stage, const std::string &source, const Shader::SourceInfo &info) +ShaderStage *Graphics::newShaderStage(ShaderStageType stage, const std::string &source, const Shader::CompileOptions &options, const Shader::SourceInfo &info, bool cache) { ShaderStage *s = nullptr; std::string cachekey; - if (!source.empty()) + // Never cache if there are custom defines set... because hashing would get + // more complicated/expensive, and there shouldn't be a lot of duplicate + // shader stages with custom defines anyway. + if (!options.defines.empty()) + cache = false; + + if (cache && !source.empty()) { data::HashFunction::Value hashvalue; data::hash(data::HashFunction::FUNCTION_SHA1, source.c_str(), source.size(), hashvalue); @@ -330,16 +336,16 @@ ShaderStage *Graphics::newShaderStage(ShaderStageType stage, const std::string & if (s == nullptr) { bool glsles = usesGLSLES(); - std::string glsl = Shader::createShaderStageCode(this, stage, source, info, glsles, true); + std::string glsl = Shader::createShaderStageCode(this, stage, source, options, info, glsles, true); s = newShaderStageInternal(stage, cachekey, glsl, glsles); - if (!cachekey.empty()) + if (cache && !cachekey.empty()) cachedShaderStages[stage][cachekey] = s; } return s; } -Shader *Graphics::newShader(const std::vector &stagessource) +Shader *Graphics::newShader(const std::vector &stagessource, const Shader::CompileOptions &options) { StrongRef stages[SHADERSTAGE_MAX_ENUM] = {}; @@ -360,12 +366,12 @@ Shader *Graphics::newShader(const std::vector &stagessource) if (info.stages[i] != Shader::ENTRYPOINT_NONE) { isanystage = true; - stages[i].set(newShaderStage((ShaderStageType) i, source, info), Acquire::NORETAIN); + stages[i].set(newShaderStage((ShaderStageType) i, source, options, info, true), Acquire::NORETAIN); } } if (!isanystage) - throw love::Exception("Could not parse shader code (missing 'position' or 'effect' function?)"); + throw love::Exception("Could not parse shader code (missing shader entry point function such as 'position' or 'effect')"); } for (int i = 0; i < SHADERSTAGE_MAX_ENUM; i++) @@ -375,7 +381,8 @@ Shader *Graphics::newShader(const std::vector &stagessource) { const std::string &source = Shader::getDefaultCode(Shader::STANDARD_DEFAULT, stype); Shader::SourceInfo info = Shader::getSourceInfo(source); - stages[i].set(newShaderStage(stype, source, info), Acquire::NORETAIN); + Shader::CompileOptions opts; + stages[i].set(newShaderStage(stype, source, opts, info, true), Acquire::NORETAIN); } } @@ -383,7 +390,7 @@ Shader *Graphics::newShader(const std::vector &stagessource) return newShaderInternal(stages); } -Shader *Graphics::newComputeShader(const std::string &source) +Shader *Graphics::newComputeShader(const std::string &source, const Shader::CompileOptions &options) { Shader::SourceInfo info = Shader::getSourceInfo(source); @@ -391,7 +398,11 @@ Shader *Graphics::newComputeShader(const std::string &source) throw love::Exception("Could not parse compute shader code (missing 'computemain' function?)"); StrongRef stages[SHADERSTAGE_MAX_ENUM]; - stages[SHADERSTAGE_COMPUTE].set(newShaderStage(SHADERSTAGE_COMPUTE, source, info)); + + // Don't bother caching compute shader intermediate source, since there + // shouldn't be much reuse. + stages[SHADERSTAGE_COMPUTE].set(newShaderStage(SHADERSTAGE_COMPUTE, source, options, info, false)); + return newShaderInternal(stages); } @@ -426,7 +437,7 @@ void Graphics::cleanupCachedShaderStage(ShaderStageType type, const std::string cachedShaderStages[type].erase(hashkey); } -bool Graphics::validateShader(bool gles, const std::vector &stagessource, std::string &err) +bool Graphics::validateShader(bool gles, const std::vector &stagessource, const Shader::CompileOptions &options, std::string &err) { StrongRef stages[SHADERSTAGE_MAX_ENUM] = {}; @@ -452,7 +463,7 @@ bool Graphics::validateShader(bool gles, const std::vector &stagess if (info.stages[i] != Shader::ENTRYPOINT_NONE) { isanystage = true; - std::string glsl = Shader::createShaderStageCode(this, stype, source, info, gles, false); + std::string glsl = Shader::createShaderStageCode(this, stype, source, options, info, gles, false); stages[i].set(new ShaderStageForValidation(this, stype, glsl, gles), Acquire::NORETAIN); } } diff --git a/src/modules/graphics/Graphics.h b/src/modules/graphics/Graphics.h index d5709f642..b00d959f1 100644 --- a/src/modules/graphics/Graphics.h +++ b/src/modules/graphics/Graphics.h @@ -448,8 +448,8 @@ public: SpriteBatch *newSpriteBatch(Texture *texture, int size, BufferDataUsage usage); ParticleSystem *newParticleSystem(Texture *texture, int size); - Shader *newShader(const std::vector &stagessource); - Shader *newComputeShader(const std::string &source); + Shader *newShader(const std::vector &stagessource, const Shader::CompileOptions &options); + Shader *newComputeShader(const std::string &source, const Shader::CompileOptions &options); virtual Buffer *newBuffer(const Buffer::Settings &settings, const std::vector &format, const void *data, size_t size, size_t arraylength) = 0; virtual Buffer *newBuffer(const Buffer::Settings &settings, DataFormat format, const void *data, size_t size, size_t arraylength); @@ -460,7 +460,7 @@ public: Text *newText(Font *font, const std::vector &text = {}); - bool validateShader(bool gles, const std::vector &stages, std::string &err); + bool validateShader(bool gles, const std::vector &stages, const Shader::CompileOptions &options, std::string &err); /** * Resets the current color, background color, line style, and so forth. @@ -965,7 +965,7 @@ protected: {} }; - ShaderStage *newShaderStage(ShaderStageType stage, const std::string &source, const Shader::SourceInfo &info); + ShaderStage *newShaderStage(ShaderStageType stage, const std::string &source, const Shader::CompileOptions &options, const Shader::SourceInfo &info, bool cache); virtual ShaderStage *newShaderStageInternal(ShaderStageType stage, const std::string &cachekey, const std::string &source, bool gles) = 0; virtual Shader *newShaderInternal(StrongRef stages[SHADERSTAGE_MAX_ENUM]) = 0; virtual StreamBuffer *newStreamBuffer(BufferUsage type, size_t size) = 0; diff --git a/src/modules/graphics/Shader.cpp b/src/modules/graphics/Shader.cpp index 4d9f562bb..17a7bb463 100644 --- a/src/modules/graphics/Shader.cpp +++ b/src/modules/graphics/Shader.cpp @@ -526,7 +526,7 @@ Shader::SourceInfo Shader::getSourceInfo(const std::string &src) return info; } -std::string Shader::createShaderStageCode(Graphics *gfx, ShaderStageType stage, const std::string &code, const Shader::SourceInfo &info, bool gles, bool checksystemfeatures) +std::string Shader::createShaderStageCode(Graphics *gfx, ShaderStageType stage, const std::string &code, const CompileOptions &options, const Shader::SourceInfo &info, bool gles, bool checksystemfeatures) { if (info.language == Shader::LANGUAGE_MAX_ENUM) throw love::Exception("Invalid shader language"); @@ -574,6 +574,10 @@ std::string Shader::createShaderStageCode(Graphics *gfx, ShaderStageType stage, ss << "#define LOVE_GAMMA_CORRECT 1\n"; if (info.usesMRT) ss << "#define LOVE_MULTI_RENDER_TARGETS 1\n"; + + for (const auto &def : options.defines) + ss << "#define " + def.first + " " + def.second + "\n"; + ss << glsl::global_syntax; ss << stageinfo.header; ss << stageinfo.uniforms; diff --git a/src/modules/graphics/Shader.h b/src/modules/graphics/Shader.h index c8d0ed838..862e9f268 100644 --- a/src/modules/graphics/Shader.h +++ b/src/modules/graphics/Shader.h @@ -107,6 +107,11 @@ public: ACCESS_WRITE = (1 << 1), }; + struct CompileOptions + { + std::map defines; + }; + struct SourceInfo { Language language; @@ -236,7 +241,7 @@ public: void getLocalThreadgroupSize(int *x, int *y, int *z); static SourceInfo getSourceInfo(const std::string &src); - static std::string createShaderStageCode(Graphics *gfx, ShaderStageType stage, const std::string &code, const SourceInfo &info, bool gles, bool checksystemfeatures); + static std::string createShaderStageCode(Graphics *gfx, ShaderStageType stage, const std::string &code, const CompileOptions &options, const SourceInfo &info, bool gles, bool checksystemfeatures); static bool validate(StrongRef stages[], std::string &err); diff --git a/src/modules/graphics/metal/Graphics.mm b/src/modules/graphics/metal/Graphics.mm index baf263867..7c1d97351 100644 --- a/src/modules/graphics/metal/Graphics.mm +++ b/src/modules/graphics/metal/Graphics.mm @@ -343,9 +343,10 @@ Graphics::Graphics() if (!Shader::standardShaders[i]) { std::vector stages; + Shader::CompileOptions opts; stages.push_back(Shader::getDefaultCode(stype, SHADERSTAGE_VERTEX)); stages.push_back(Shader::getDefaultCode(stype, SHADERSTAGE_PIXEL)); - Shader::standardShaders[i] = newShader(stages); + Shader::standardShaders[i] = newShader(stages, opts); } } diff --git a/src/modules/graphics/opengl/Graphics.cpp b/src/modules/graphics/opengl/Graphics.cpp index cff45294d..ae9a23eb3 100644 --- a/src/modules/graphics/opengl/Graphics.cpp +++ b/src/modules/graphics/opengl/Graphics.cpp @@ -425,9 +425,10 @@ bool Graphics::setMode(void */*context*/, int width, int height, int pixelwidth, if (!Shader::standardShaders[i]) { std::vector stages; + Shader::CompileOptions opts; stages.push_back(Shader::getDefaultCode(stype, SHADERSTAGE_VERTEX)); stages.push_back(Shader::getDefaultCode(stype, SHADERSTAGE_PIXEL)); - Shader::standardShaders[i] = newShader(stages); + Shader::standardShaders[i] = newShader(stages, opts); } } catch (love::Exception &) diff --git a/src/modules/graphics/wrap_Graphics.cpp b/src/modules/graphics/wrap_Graphics.cpp index 70b1de307..d919c4130 100644 --- a/src/modules/graphics/wrap_Graphics.cpp +++ b/src/modules/graphics/wrap_Graphics.cpp @@ -1347,7 +1347,7 @@ int w_newParticleSystem(lua_State *L) return 1; } -static int w_getShaderSource(lua_State *L, int startidx, std::vector &stages) +static int w_getShaderSource(lua_State *L, int startidx, std::vector &stages, Shader::CompileOptions &options) { using namespace love::filesystem; @@ -1369,7 +1369,6 @@ static int w_getShaderSource(lua_State *L, int startidx, std::vector stages; - w_getShaderSource(L, 1, stages); + Shader::CompileOptions options; + w_getShaderSource(L, 1, stages, options); bool should_error = false; try { - Shader *shader = instance()->newShader(stages); + Shader *shader = instance()->newShader(stages, options); luax_pushtype(L, shader); shader->release(); } @@ -1446,12 +1488,13 @@ int w_newShader(lua_State *L) int w_newComputeShader(lua_State* L) { std::vector stages; - w_getShaderSource(L, 1, stages); + Shader::CompileOptions options; + w_getShaderSource(L, 1, stages, options); bool should_error = false; try { - Shader *shader = instance()->newComputeShader(stages[0]); + Shader *shader = instance()->newComputeShader(stages[0], options); luax_pushtype(L, shader); shader->release(); } @@ -1476,13 +1519,14 @@ int w_validateShader(lua_State *L) bool gles = luax_checkboolean(L, 1); std::vector stages; - w_getShaderSource(L, 2, stages); + Shader::CompileOptions options; + w_getShaderSource(L, 2, stages, options); bool success = true; std::string err; try { - success = instance()->validateShader(gles, stages, err); + success = instance()->validateShader(gles, stages, options, err); } catch (love::Exception &e) { From 7b3e2182aa3dc8b2658951cab6b9db90e19dab3e Mon Sep 17 00:00:00 2001 From: Miku AuahDark Date: Tue, 1 Mar 2022 22:33:33 +0800 Subject: [PATCH 15/55] Use std::stringstream when building deprecation text. --- src/common/deprecation.cpp | 25 +++++++++++++------------ 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/src/common/deprecation.cpp b/src/common/deprecation.cpp index d17109e5c..b5928da01 100644 --- a/src/common/deprecation.cpp +++ b/src/common/deprecation.cpp @@ -24,6 +24,7 @@ #include #include +#include namespace love { @@ -97,32 +98,32 @@ bool isDeprecationOutputEnabled() std::string getDeprecationNotice(const DeprecationInfo &info, bool usewhere) { - std::string notice; + std::stringstream notice; if (usewhere) - notice += info.where; + notice << info.where; - notice += "Using deprecated "; + notice << "Using deprecated "; if (info.apiType == API_FUNCTION) - notice += "function "; + notice << "function "; else if (info.apiType == API_METHOD) - notice += "method "; + notice << "method "; else if (info.apiType == API_CALLBACK) - notice += "callback "; + notice << "callback "; else if (info.apiType == API_FIELD) - notice += "field "; + notice << "field "; else if (info.apiType == API_CONSTANT) - notice += "constant "; + notice << "constant "; - notice += info.name; + notice << info.name; if (info.type == DEPRECATED_REPLACED && !info.replacement.empty()) - notice += " (replaced by " + info.replacement + ")"; + notice << " (replaced by " << info.replacement << ")"; else if (info.type == DEPRECATED_RENAMED && !info.replacement.empty()) - notice += " (renamed to " + info.replacement + ")"; + notice << " (renamed to " << info.replacement << ")"; - return notice; + return notice.str(); } GetDeprecated::GetDeprecated() From 00e4ec1e04ad62928334e139be02384dedacf35f Mon Sep 17 00:00:00 2001 From: Miku AuahDark Date: Tue, 1 Mar 2022 22:34:14 +0800 Subject: [PATCH 16/55] Show deprecation notice when slashes is passed to require. Closes #1773 --- src/modules/filesystem/wrap_Filesystem.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/modules/filesystem/wrap_Filesystem.cpp b/src/modules/filesystem/wrap_Filesystem.cpp index d30566e47..d7fd00e7f 100644 --- a/src/modules/filesystem/wrap_Filesystem.cpp +++ b/src/modules/filesystem/wrap_Filesystem.cpp @@ -806,6 +806,9 @@ int loader(lua_State *L) { std::string modulename = luax_checkstring(L, 1); + if (modulename.find('/') != std::string::npos) + luax_markdeprecated(L, 2, "character in require string (forward slashes), use dots instead.", API_CUSTOM); + for (char &c : modulename) { if (c == '.') From 405e33a689ee0fd165a3ec689152399735fd75b2 Mon Sep 17 00:00:00 2001 From: Miku AuahDark Date: Sat, 5 Mar 2022 13:42:20 +0800 Subject: [PATCH 17/55] Use int instead of PixelFormatUsageFlags for isPixelFormatSupported. --- src/modules/graphics/Graphics.h | 2 +- src/modules/graphics/metal/Graphics.h | 2 +- src/modules/graphics/metal/Graphics.mm | 2 +- src/modules/graphics/opengl/Graphics.cpp | 2 +- src/modules/graphics/opengl/Graphics.h | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/modules/graphics/Graphics.h b/src/modules/graphics/Graphics.h index b00d959f1..4172ce7f4 100644 --- a/src/modules/graphics/Graphics.h +++ b/src/modules/graphics/Graphics.h @@ -797,7 +797,7 @@ public: /** * Gets whether the specified pixel format usage is supported. **/ - virtual bool isPixelFormatSupported(PixelFormat format, PixelFormatUsageFlags usage, bool sRGB = false) = 0; + virtual bool isPixelFormatSupported(PixelFormat format, int usage, bool sRGB = false) = 0; /** * Gets the renderer used by love.graphics. diff --git a/src/modules/graphics/metal/Graphics.h b/src/modules/graphics/metal/Graphics.h index 7591cff12..124a7ccdf 100644 --- a/src/modules/graphics/metal/Graphics.h +++ b/src/modules/graphics/metal/Graphics.h @@ -110,7 +110,7 @@ public: void setWireframe(bool enable) override; PixelFormat getSizedFormat(PixelFormat format, bool rendertarget, bool readable) const override; - bool isPixelFormatSupported(PixelFormat format, PixelFormatUsageFlags usage, bool sRGB = false) override; + bool isPixelFormatSupported(PixelFormat format, int usage, bool sRGB = false) override; Renderer getRenderer() const override; bool usesGLSLES() const override; RendererInfo getRendererInfo() const override; diff --git a/src/modules/graphics/metal/Graphics.mm b/src/modules/graphics/metal/Graphics.mm index 7c1d97351..e377223ab 100644 --- a/src/modules/graphics/metal/Graphics.mm +++ b/src/modules/graphics/metal/Graphics.mm @@ -1778,7 +1778,7 @@ PixelFormat Graphics::getSizedFormat(PixelFormat format, bool /*rendertarget*/, } } -bool Graphics::isPixelFormatSupported(PixelFormat format, PixelFormatUsageFlags usage, bool sRGB) +bool Graphics::isPixelFormatSupported(PixelFormat format, int usage, bool sRGB) { bool rendertarget = (usage & PIXELFORMATUSAGEFLAGS_RENDERTARGET) != 0; bool readable = (usage & PIXELFORMATUSAGEFLAGS_SAMPLE) != 0; diff --git a/src/modules/graphics/opengl/Graphics.cpp b/src/modules/graphics/opengl/Graphics.cpp index ae9a23eb3..c9705d29e 100644 --- a/src/modules/graphics/opengl/Graphics.cpp +++ b/src/modules/graphics/opengl/Graphics.cpp @@ -1697,7 +1697,7 @@ PixelFormat Graphics::getSizedFormat(PixelFormat format, bool rendertarget, bool } } -bool Graphics::isPixelFormatSupported(PixelFormat format, PixelFormatUsageFlags usage, bool sRGB) +bool Graphics::isPixelFormatSupported(PixelFormat format, int usage, bool sRGB) { if (sRGB) { diff --git a/src/modules/graphics/opengl/Graphics.h b/src/modules/graphics/opengl/Graphics.h index b3d559bdd..9c65ce6fd 100644 --- a/src/modules/graphics/opengl/Graphics.h +++ b/src/modules/graphics/opengl/Graphics.h @@ -106,7 +106,7 @@ public: void setWireframe(bool enable) override; PixelFormat getSizedFormat(PixelFormat format, bool rendertarget, bool readable) const override; - bool isPixelFormatSupported(PixelFormat format, PixelFormatUsageFlags usage, bool sRGB = false) override; + bool isPixelFormatSupported(PixelFormat format, int usage, bool sRGB = false) override; Renderer getRenderer() const override; bool usesGLSLES() const override; RendererInfo getRendererInfo() const override; From 767edc5f84681542d2348df5e57a1bb30a76967a Mon Sep 17 00:00:00 2001 From: Miku AuahDark Date: Sat, 5 Mar 2022 15:27:06 +0800 Subject: [PATCH 18/55] Add HAS_SOCKLEN_T to Android config.h --- src/common/config.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/common/config.h b/src/common/config.h index 34aa3f2ed..997bc6d34 100644 --- a/src/common/config.h +++ b/src/common/config.h @@ -38,6 +38,8 @@ #endif #if defined(__ANDROID__) # define LOVE_ANDROID 1 +// Needed for ENet +# define HAS_SOCKLEN_T 1 #endif #if defined(__APPLE__) # include From 6031a6c636c691544d80168d3c0ff6a98a1dccee Mon Sep 17 00:00:00 2001 From: Miku AuahDark Date: Sat, 5 Mar 2022 21:46:33 +0800 Subject: [PATCH 19/55] Fix Android compile error. --- CMakeLists.txt | 3 +++ src/common/android.cpp | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 6688e3bc5..c0f2fbae7 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -235,6 +235,8 @@ endfunction() # set(LOVE_SRC_COMMON + src/common/android.cpp + src/common/android.h src/common/b64.cpp src/common/b64.h src/common/Color.h @@ -1770,6 +1772,7 @@ if(ANDROID) # as shared library, so change the library name and add love.cpp set(LOVE_LIB_NAME ${LOVE_EXE_NAME}) set(LOVE_LIB_SRC ${LOVE_LIB_SRC} src/love.cpp) + set(LOVE_LINK_LIBRARIES ${LOVE_LINK_LIBRARIES} android) endif() add_library(${LOVE_LIB_NAME} SHARED ${LOVE_LIB_SRC} ${LOVE_RC}) diff --git a/src/common/android.cpp b/src/common/android.cpp index 9de92d243..a5118f25c 100644 --- a/src/common/android.cpp +++ b/src/common/android.cpp @@ -35,7 +35,7 @@ #include #include -#include "physfs.h" +#include "libraries/physfs/physfs.h" namespace love { From 9c75f816fe6ccc840ac7504cd4cee42d94ae2b93 Mon Sep 17 00:00:00 2001 From: Miku AuahDark Date: Sat, 5 Mar 2022 21:46:52 +0800 Subject: [PATCH 20/55] Remove Android.mk We no longer using ndk-build in 12.0. --- Android.mk | 106 ----------------------------------------------------- 1 file changed, 106 deletions(-) delete mode 100644 Android.mk diff --git a/Android.mk b/Android.mk deleted file mode 100644 index ec867bad5..000000000 --- a/Android.mk +++ /dev/null @@ -1,106 +0,0 @@ -LOCAL_PATH:= $(call my-dir) -include $(CLEAR_VARS) - -LOCAL_MODULE := liblove -LOCAL_CFLAGS := -g -DGL_GLEXT_PROTOTYPES -DAL_ALEXT_PROTOTYPES - -LOCAL_CPPFLAGS := ${LOCAL_CFLAGS} - -# I don't think there's armeabi-v7a device without NEON instructions in 2018 -LOCAL_ARM_NEON := true - -ifeq ($(IS_ANDROID_21),yes) - # API21 defines socklen_t - LOCAL_CFLAGS += -DHAS_SOCKLEN_T=1 -endif - -LOCAL_C_INCLUDES := \ - ${LOCAL_PATH}/src \ - ${LOCAL_PATH}/src/modules \ - ${LOCAL_PATH}/src/libraries/ \ - ${LOCAL_PATH}/src/libraries/enet/libenet/include \ - ${LOCAL_PATH}/src/libraries/physfs \ - ${LOCAL_PATH}/src/libraries/glslang/glslang/Include - -LOCAL_SRC_FILES := \ - $(filter-out \ - src/libraries/luasocket/libluasocket/wsocket.c \ - ,$(subst $(LOCAL_PATH)/,,\ - $(wildcard ${LOCAL_PATH}/src/love.cpp) \ - $(wildcard ${LOCAL_PATH}/src/common/*.cpp) \ - $(wildcard ${LOCAL_PATH}/src/modules/audio/*.cpp) \ - $(wildcard ${LOCAL_PATH}/src/modules/audio/null/*.cpp) \ - $(wildcard ${LOCAL_PATH}/src/modules/audio/openal/*.cpp) \ - $(wildcard ${LOCAL_PATH}/src/modules/data/*.cpp) \ - $(wildcard ${LOCAL_PATH}/src/modules/event/*.cpp) \ - $(wildcard ${LOCAL_PATH}/src/modules/event/sdl/*.cpp) \ - $(wildcard ${LOCAL_PATH}/src/modules/filesystem/*.cpp) \ - $(wildcard ${LOCAL_PATH}/src/modules/filesystem/physfs/*.cpp) \ - $(wildcard ${LOCAL_PATH}/src/modules/font/*.cpp) \ - $(wildcard ${LOCAL_PATH}/src/modules/font/freetype/*.cpp) \ - $(wildcard ${LOCAL_PATH}/src/modules/graphics/*.cpp) \ - $(wildcard ${LOCAL_PATH}/src/modules/graphics/opengl/*.cpp) \ - $(wildcard ${LOCAL_PATH}/src/modules/image/*.cpp) \ - $(wildcard ${LOCAL_PATH}/src/modules/image/magpie/*.cpp) \ - $(wildcard ${LOCAL_PATH}/src/modules/joystick/*.cpp) \ - $(wildcard ${LOCAL_PATH}/src/modules/joystick/sdl/*.cpp) \ - $(wildcard ${LOCAL_PATH}/src/modules/keyboard/*.cpp) \ - $(wildcard ${LOCAL_PATH}/src/modules/keyboard/sdl/*.cpp) \ - $(wildcard ${LOCAL_PATH}/src/modules/love/*.cpp) \ - $(wildcard ${LOCAL_PATH}/src/modules/math/*.cpp) \ - $(wildcard ${LOCAL_PATH}/src/modules/mouse/*.cpp) \ - $(wildcard ${LOCAL_PATH}/src/modules/mouse/sdl/*.cpp) \ - $(wildcard ${LOCAL_PATH}/src/modules/physics/*.cpp) \ - $(wildcard ${LOCAL_PATH}/src/modules/physics/box2d/*.cpp) \ - $(wildcard ${LOCAL_PATH}/src/modules/sound/*.cpp) \ - $(wildcard ${LOCAL_PATH}/src/modules/sound/lullaby/*.cpp) \ - $(wildcard ${LOCAL_PATH}/src/modules/system/*.cpp) \ - $(wildcard ${LOCAL_PATH}/src/modules/system/sdl/*.cpp) \ - $(wildcard ${LOCAL_PATH}/src/modules/thread/*.cpp) \ - $(wildcard ${LOCAL_PATH}/src/modules/thread/sdl/*.cpp) \ - $(wildcard ${LOCAL_PATH}/src/modules/touch/*.cpp) \ - $(wildcard ${LOCAL_PATH}/src/modules/touch/sdl/*.cpp) \ - $(wildcard ${LOCAL_PATH}/src/modules/timer/*.cpp) \ - $(wildcard ${LOCAL_PATH}/src/modules/timer/sdl/*.cpp) \ - $(wildcard ${LOCAL_PATH}/src/modules/video/*.cpp) \ - $(wildcard ${LOCAL_PATH}/src/modules/video/theora/*.cpp) \ - $(wildcard ${LOCAL_PATH}/src/modules/window/*.cpp) \ - $(wildcard ${LOCAL_PATH}/src/modules/window/sdl/*.cpp) \ - $(wildcard ${LOCAL_PATH}/src/libraries/ddsparse/*.cpp) \ - $(wildcard ${LOCAL_PATH}/src/libraries/box2d/*.cpp) \ - $(wildcard ${LOCAL_PATH}/src/libraries/box2d/Collision/*.cpp) \ - $(wildcard ${LOCAL_PATH}/src/libraries/box2d/Common/*.cpp) \ - $(wildcard ${LOCAL_PATH}/src/libraries/box2d/Dynamics/*.cpp) \ - $(wildcard ${LOCAL_PATH}/src/libraries/box2d/Rope/*.cpp) \ - $(wildcard ${LOCAL_PATH}/src/libraries/glad/*.cpp) \ - $(wildcard ${LOCAL_PATH}/src/libraries/glslang/glslang/GenericCodeGen/*.cpp) \ - $(wildcard ${LOCAL_PATH}/src/libraries/glslang/glslang/MachineIndependent/*.cpp) \ - $(wildcard ${LOCAL_PATH}/src/libraries/glslang/glslang/MachineIndependent/preprocessor/*.cpp) \ - $(wildcard ${LOCAL_PATH}/src/libraries/glslang/glslang/OSDependent/Unix/*.cpp) \ - $(wildcard ${LOCAL_PATH}/src/libraries/glslang/OGLCompilersDLL/*.cpp) \ - $(wildcard ${LOCAL_PATH}/src/libraries/glslang/glslang//*.cpp) \ - $(wildcard ${LOCAL_PATH}/src/libraries/enet/*.cpp) \ - $(wildcard ${LOCAL_PATH}/src/libraries/enet/libenet/*.c) \ - $(wildcard ${LOCAL_PATH}/src/libraries/lua53/*.c) \ - $(wildcard ${LOCAL_PATH}/src/libraries/luasocket/*.cpp) \ - $(wildcard ${LOCAL_PATH}/src/libraries/luautf8/*.c) \ - $(wildcard ${LOCAL_PATH}/src/libraries/luasocket/libluasocket/*.c) \ - $(wildcard ${LOCAL_PATH}/src/libraries/lodepng/*.cpp) \ - $(wildcard ${LOCAL_PATH}/src/libraries/lz4/*.c) \ - $(wildcard ${LOCAL_PATH}/src/libraries/noise1234/*.cpp) \ - $(wildcard ${LOCAL_PATH}/src/libraries/physfs/*.c) \ - $(wildcard ${LOCAL_PATH}/src/libraries/Wuff/*.c) \ - $(wildcard ${LOCAL_PATH}/src/libraries/xxHash/*.c) \ - )) - -LOCAL_CXXFLAGS := -std=c++11 -LOCAL_SHARED_LIBRARIES := libopenal -LOCAL_STATIC_LIBRARIES := libvorbis libogg libtheora libmodplug libfreetype libluajit SDL2_static - -# $(info liblove: include dirs $(LOCAL_C_INCLUDES)) -# $(info liblove: src files $(LOCAL_SRC_FILES)) - -LOCAL_LDLIBS := -lz -lGLESv1_CM -lGLESv2 -ldl -landroid -LOCAL_LDFLAGS := -Wl,--allow-multiple-definition - -include $(BUILD_SHARED_LIBRARY) From 09725dd3b94d0eb12deabbdd2b65c099dcebd602 Mon Sep 17 00:00:00 2001 From: Yan Date: Sun, 13 Mar 2022 00:26:04 +0800 Subject: [PATCH 21/55] Windows: Fix compile error: cast from 'HINSTANCE' to 'int' loses precision --- src/modules/system/System.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/modules/system/System.cpp b/src/modules/system/System.cpp index 981dcff41..e1de16d5d 100644 --- a/src/modules/system/System.cpp +++ b/src/modules/system/System.cpp @@ -164,7 +164,7 @@ bool System::openURL(const std::string &url) const #endif - return (int) result > 32; + return (ptrdiff_t) result > 32; #endif } From c2104f896a05ccf934440ff4f18450ccec5e028e Mon Sep 17 00:00:00 2001 From: Miku AuahDark Date: Thu, 17 Mar 2022 23:38:25 +0800 Subject: [PATCH 22/55] Add love.system.getPreferredLocales() --- src/modules/system/System.h | 13 +++++++++++++ src/modules/system/sdl/System.cpp | 26 ++++++++++++++++++++++++++ src/modules/system/sdl/System.h | 1 + src/modules/system/wrap_System.cpp | 17 +++++++++++++++++ 4 files changed, 57 insertions(+) diff --git a/src/modules/system/System.h b/src/modules/system/System.h index 7b2e6c7b5..8a40f59e9 100644 --- a/src/modules/system/System.h +++ b/src/modules/system/System.h @@ -114,6 +114,19 @@ public: **/ bool hasBackgroundMusic() const; + /** + * Gets the list of locales in order of user preference. + * + * The returned string from this function has format of + * xx_YY where 'xx' is ISO-639 language code and 'YY' is + * the ISO-3166 country code if available. If country + * code is unavailable, then it simply returns the language. + * + * @return List user preferred locales or empty if the current + * platform does not support this function. + */ + virtual std::vector getPreferredLocales() const = 0; + static bool getConstant(const char *in, PowerState &out); static bool getConstant(PowerState in, const char *&out); diff --git a/src/modules/system/sdl/System.cpp b/src/modules/system/sdl/System.cpp index acfaf316b..623289c04 100644 --- a/src/modules/system/sdl/System.cpp +++ b/src/modules/system/sdl/System.cpp @@ -25,6 +25,8 @@ // SDL #include #include +#include +#include namespace love { @@ -88,6 +90,30 @@ love::system::System::PowerState System::getPowerInfo(int &seconds, int &percent powerStates.find(sdlstate, state); return state; +} + +std::vector System::getPreferredLocales() const +{ + std::vector result; + +#if SDL_VERSION_ATLEAST(2, 0, 14) + SDL_Locale *locales = SDL_GetPreferredLocales(); + + if (locales) + { + for (SDL_Locale* locale = locales; locale->language != nullptr; locale++) + { + if (locale->country) + result.push_back(std::string(locale->language) + "_" + std::string(locale->country)); + else + result.push_back(locale->language); + } + + SDL_free(locales); + } +#endif + + return result; } EnumMap::Entry System::powerEntries[] = diff --git a/src/modules/system/sdl/System.h b/src/modules/system/sdl/System.h index 3f74b085c..0f0258dd4 100644 --- a/src/modules/system/sdl/System.h +++ b/src/modules/system/sdl/System.h @@ -51,6 +51,7 @@ public: std::string getClipboardText() const; PowerState getPowerInfo(int &seconds, int &percent) const; + std::vector getPreferredLocales() const override; private: diff --git a/src/modules/system/wrap_System.cpp b/src/modules/system/wrap_System.cpp index ac4df081b..d7d3209bd 100644 --- a/src/modules/system/wrap_System.cpp +++ b/src/modules/system/wrap_System.cpp @@ -101,6 +101,22 @@ int w_hasBackgroundMusic(lua_State *L) return 1; } +int w_getPreferredLocales(lua_State* L) +{ + int i = 1; + std::vector locales = instance()->getPreferredLocales(); + + lua_createtable(L, locales.size(), 0); + + for (const std::string& str: locales) + { + luax_pushstring(L, str); + lua_rawseti(L, -2, i++); + } + + return 1; +} + static const luaL_Reg functions[] = { { "getOS", w_getOS }, @@ -111,6 +127,7 @@ static const luaL_Reg functions[] = { "openURL", w_openURL }, { "vibrate", w_vibrate }, { "hasBackgroundMusic", w_hasBackgroundMusic }, + { "getPreferredLocales", w_getPreferredLocales }, { 0, 0 } }; From 75b1098572ee2bb2754099933d471c8e643cb0d1 Mon Sep 17 00:00:00 2001 From: Miku AuahDark Date: Thu, 17 Mar 2022 23:39:54 +0800 Subject: [PATCH 23/55] Add love.localechanged callback. --- src/modules/event/sdl/Event.cpp | 3 +++ src/modules/love/callbacks.lua | 3 +++ 2 files changed, 6 insertions(+) diff --git a/src/modules/event/sdl/Event.cpp b/src/modules/event/sdl/Event.cpp index d8d8deddf..60e68a36f 100644 --- a/src/modules/event/sdl/Event.cpp +++ b/src/modules/event/sdl/Event.cpp @@ -551,6 +551,9 @@ Message *Event::convertJoystickEvent(const SDL_Event &e) const msg = new Message("joystickremoved", vargs); } break; + case SDL_LOCALECHANGED: + msg = new Message("localechanged"); + break; default: break; } diff --git a/src/modules/love/callbacks.lua b/src/modules/love/callbacks.lua index 66735695c..200999169 100644 --- a/src/modules/love/callbacks.lua +++ b/src/modules/love/callbacks.lua @@ -120,6 +120,9 @@ function love.createhandlers() displayrotated = function (display, orient) if love.displayrotated then return love.displayrotated(display, orient) end end, + localechanged = function () + if love.localechanged then return love.localechanged() end + end, }, { __index = function(self, name) error("Unknown event: " .. name) From 5603c8915b1ec39b0fa484f6457a3229c2d6a2a0 Mon Sep 17 00:00:00 2001 From: Miku AuahDark Date: Thu, 17 Mar 2022 23:41:16 +0800 Subject: [PATCH 24/55] Update changelog for getPreferredLocales and localechanged. --- changes.txt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/changes.txt b/changes.txt index 733d3a298..a30a5f322 100644 --- a/changes.txt +++ b/changes.txt @@ -42,6 +42,8 @@ Released: N/A * Added a variant of love.graphics.setColorMask which accepts a single boolean. * Added new 'clampone' wrap mode. * Added a variant of Font:getWidth which takes a codepoint number argument. +* Added love.system.getPreferredLocales. +* Added love.localechanged callback. * Changed the default font from Vera size 12 to Noto Sans size 13. * Changed the Texture class and implementation to no longer have separate Canvas and Image subclasses. From 730cb08acf1c46e3e94f8ac725f7ee2606ac6959 Mon Sep 17 00:00:00 2001 From: Miku AuahDark Date: Fri, 18 Mar 2022 10:30:31 +0800 Subject: [PATCH 25/55] Minor fix to love.localechanged callback. Wrong callback placement. --- src/modules/event/sdl/Event.cpp | 10 +++++-- src/modules/system/sdl/System.cpp | 48 +++++++++++++++---------------- 2 files changed, 31 insertions(+), 27 deletions(-) diff --git a/src/modules/event/sdl/Event.cpp b/src/modules/event/sdl/Event.cpp index 60e68a36f..dae96456e 100644 --- a/src/modules/event/sdl/Event.cpp +++ b/src/modules/event/sdl/Event.cpp @@ -35,6 +35,8 @@ #include +#include + namespace love { namespace event @@ -433,6 +435,11 @@ Message *Event::convert(const SDL_Event &e) case SDL_APP_LOWMEMORY: msg = new Message("lowmemory"); break; +#if SDL_VERSION_ATLEAST(2, 0, 14) + case SDL_LOCALECHANGED: + msg = new Message("localechanged"); + break; +#endif default: break; } @@ -551,9 +558,6 @@ Message *Event::convertJoystickEvent(const SDL_Event &e) const msg = new Message("joystickremoved", vargs); } break; - case SDL_LOCALECHANGED: - msg = new Message("localechanged"); - break; default: break; } diff --git a/src/modules/system/sdl/System.cpp b/src/modules/system/sdl/System.cpp index 623289c04..df0c476cf 100644 --- a/src/modules/system/sdl/System.cpp +++ b/src/modules/system/sdl/System.cpp @@ -90,30 +90,30 @@ love::system::System::PowerState System::getPowerInfo(int &seconds, int &percent powerStates.find(sdlstate, state); return state; -} - -std::vector System::getPreferredLocales() const -{ - std::vector result; - -#if SDL_VERSION_ATLEAST(2, 0, 14) - SDL_Locale *locales = SDL_GetPreferredLocales(); - - if (locales) - { - for (SDL_Locale* locale = locales; locale->language != nullptr; locale++) - { - if (locale->country) - result.push_back(std::string(locale->language) + "_" + std::string(locale->country)); - else - result.push_back(locale->language); - } - - SDL_free(locales); - } -#endif - - return result; +} + +std::vector System::getPreferredLocales() const +{ + std::vector result; + +#if SDL_VERSION_ATLEAST(2, 0, 14) + SDL_Locale *locales = SDL_GetPreferredLocales(); + + if (locales) + { + for (SDL_Locale* locale = locales; locale->language != nullptr; locale++) + { + if (locale->country) + result.push_back(std::string(locale->language) + "_" + std::string(locale->country)); + else + result.push_back(locale->language); + } + + SDL_free(locales); + } +#endif + + return result; } EnumMap::Entry System::powerEntries[] = From 9682c7a5ada3823391b7a9361d793d7d78da72ca Mon Sep 17 00:00:00 2001 From: Alex Szpakowski Date: Mon, 11 Apr 2022 20:50:22 -0300 Subject: [PATCH 26/55] Fix lua https library when building with autotools --- platform/unix/configure.ac | 1 + 1 file changed, 1 insertion(+) diff --git a/platform/unix/configure.ac b/platform/unix/configure.ac index e431f5744..7968f8c0f 100644 --- a/platform/unix/configure.ac +++ b/platform/unix/configure.ac @@ -78,6 +78,7 @@ AS_VAR_IF([enable_gme], [yes], [ACLOVE_DEP_GME], []) AC_ARG_ENABLE([library-enet], [ --disable-library-enet Turn off library enet], [], [enable_library_enet=yes]) AC_ARG_ENABLE([library-luasocket], [ --disable-library-luasocket Turn off library luasocket], [], [enable_library_luasocket=yes]) AC_ARG_ENABLE([library-lua53], [ --disable-library-lua53 Turn off library lua53 (lua 5.3 backports, required by love.data)], [], [enable_library_lua53=yes]) +AC_ARG_ENABLE([library-luahttps], [ --disable-library-luahttps Turn off library luahttps], [], [enable_library_luahttps=yes]) # Select the libraries we need to build, based on the selected modules AS_VAR_IF([enable_module_filesystem], [yes], [enable_library_physfs=yes], [enable_library_physfs=no]) From 34f036f2e1032c77d023a51036910b4138724872 Mon Sep 17 00:00:00 2001 From: Alex Szpakowski Date: Fri, 15 Apr 2022 23:16:13 -0300 Subject: [PATCH 27/55] Rename 'display' field to 'displayindex' in setMode and love.conf. Fixes #1711. --- src/modules/love/boot.lua | 2 +- src/modules/window/Window.cpp | 1 + src/modules/window/Window.h | 5 +++-- src/modules/window/sdl/Window.cpp | 20 ++++++++++---------- src/modules/window/wrap_Window.cpp | 14 +++++++++++--- 5 files changed, 26 insertions(+), 16 deletions(-) diff --git a/src/modules/love/boot.lua b/src/modules/love/boot.lua index d06b4e4c3..cf1d2fc8f 100644 --- a/src/modules/love/boot.lua +++ b/src/modules/love/boot.lua @@ -145,7 +145,7 @@ function love.init() minheight = 1, fullscreen = false, fullscreentype = "desktop", - display = 1, + displayindex = 1, vsync = 1, msaa = 0, borderless = false, diff --git a/src/modules/window/Window.cpp b/src/modules/window/Window.cpp index 5133a29d3..c2a69df2d 100644 --- a/src/modules/window/Window.cpp +++ b/src/modules/window/Window.cpp @@ -114,6 +114,7 @@ StringMap::Entry Window::settingEntri {"minheight", SETTING_MIN_HEIGHT}, {"borderless", SETTING_BORDERLESS}, {"centered", SETTING_CENTERED}, + {"displayindex", SETTING_DISPLAYINDEX}, {"display", SETTING_DISPLAY}, {"highdpi", SETTING_HIGHDPI}, {"usedpiscale", SETTING_USE_DPISCALE}, diff --git a/src/modules/window/Window.h b/src/modules/window/Window.h index 333bafeee..01b468f6f 100644 --- a/src/modules/window/Window.h +++ b/src/modules/window/Window.h @@ -69,7 +69,8 @@ public: SETTING_MIN_HEIGHT, SETTING_BORDERLESS, SETTING_CENTERED, - SETTING_DISPLAY, + SETTING_DISPLAYINDEX, + SETTING_DISPLAY, // Deprecated SETTING_HIGHDPI, // Deprecated SETTING_USE_DPISCALE, SETTING_REFRESHRATE, @@ -264,7 +265,7 @@ struct WindowSettings int minheight = 1; bool borderless = false; bool centered = true; - int display = 0; + int displayindex = 0; bool usedpiscale = true; double refreshrate = 0.0; bool useposition = false; diff --git a/src/modules/window/sdl/Window.cpp b/src/modules/window/sdl/Window.cpp index 234e95887..02fa7db55 100644 --- a/src/modules/window/sdl/Window.cpp +++ b/src/modules/window/sdl/Window.cpp @@ -478,13 +478,13 @@ bool Window::setWindow(int width, int height, WindowSettings *settings) f.minwidth = std::max(f.minwidth, 1); f.minheight = std::max(f.minheight, 1); - f.display = std::min(std::max(f.display, 0), getDisplayCount() - 1); + f.displayindex = std::min(std::max(f.displayindex, 0), getDisplayCount() - 1); // Use the desktop resolution if a width or height of 0 is specified. if (width == 0 || height == 0) { SDL_DisplayMode mode = {}; - SDL_GetDesktopDisplayMode(f.display, &mode); + SDL_GetDesktopDisplayMode(f.displayindex, &mode); width = mode.w; height = mode.h; } @@ -509,16 +509,16 @@ bool Window::setWindow(int width, int height, WindowSettings *settings) { // The position needs to be in the global coordinate space. SDL_Rect displaybounds = {}; - SDL_GetDisplayBounds(f.display, &displaybounds); + SDL_GetDisplayBounds(f.displayindex, &displaybounds); x += displaybounds.x; y += displaybounds.y; } else { if (f.centered) - x = y = SDL_WINDOWPOS_CENTERED_DISPLAY(f.display); + x = y = SDL_WINDOWPOS_CENTERED_DISPLAY(f.displayindex); else - x = y = SDL_WINDOWPOS_UNDEFINED_DISPLAY(f.display); + x = y = SDL_WINDOWPOS_UNDEFINED_DISPLAY(f.displayindex); } SDL_DisplayMode fsmode = {0, width, height, 0, nullptr}; @@ -526,12 +526,12 @@ bool Window::setWindow(int width, int height, WindowSettings *settings) if (f.fullscreen && f.fstype == FULLSCREEN_EXCLUSIVE) { // Fullscreen window creation will bug out if no mode can be used. - if (SDL_GetClosestDisplayMode(f.display, &fsmode, &fsmode) == nullptr) + if (SDL_GetClosestDisplayMode(f.displayindex, &fsmode, &fsmode) == nullptr) { // GetClosestDisplayMode will fail if we request a size larger // than the largest available display mode, so we'll try to use // the largest (first) mode in that case. - if (SDL_GetDisplayMode(f.display, 0, &fsmode) < 0) + if (SDL_GetDisplayMode(f.displayindex, 0, &fsmode) < 0) return false; } } @@ -607,7 +607,7 @@ bool Window::setWindow(int width, int height, WindowSettings *settings) // Enforce minimum window dimensions. SDL_SetWindowMinimumSize(window, f.minwidth, f.minheight); - if (this->settings.display != f.display || f.useposition || f.centered) + if (this->settings.displayindex != f.displayindex || f.useposition || f.centered) SDL_SetWindowPosition(window, x, y); SDL_RaiseWindow(window); @@ -727,7 +727,7 @@ void Window::updateSettings(const WindowSettings &newsettings, bool updateGraphi settings.borderless = (wflags & SDL_WINDOW_BORDERLESS) != 0; settings.centered = newsettings.centered; - getPosition(settings.x, settings.y, settings.display); + getPosition(settings.x, settings.y, settings.displayindex); setHighDPIAllowed((wflags & SDL_WINDOW_ALLOW_HIGHDPI) != 0); @@ -745,7 +745,7 @@ void Window::updateSettings(const WindowSettings &newsettings, bool updateGraphi settings.depth = newsettings.depth; SDL_DisplayMode dmode = {}; - SDL_GetCurrentDisplayMode(settings.display, &dmode); + SDL_GetCurrentDisplayMode(settings.displayindex, &dmode); // May be 0 if the refresh rate can't be determined. settings.refreshrate = (double) dmode.refresh_rate; diff --git a/src/modules/window/wrap_Window.cpp b/src/modules/window/wrap_Window.cpp index a1720e6c2..59b20fee5 100644 --- a/src/modules/window/wrap_Window.cpp +++ b/src/modules/window/wrap_Window.cpp @@ -74,9 +74,17 @@ static int readWindowSettings(lua_State *L, int idx, WindowSettings &settings) settings.minheight = luax_intflag(L, idx, settingName(Window::SETTING_MIN_HEIGHT), settings.minheight); settings.borderless = luax_boolflag(L, idx, settingName(Window::SETTING_BORDERLESS), settings.borderless); settings.centered = luax_boolflag(L, idx, settingName(Window::SETTING_CENTERED), settings.centered); - settings.display = luax_intflag(L, idx, settingName(Window::SETTING_DISPLAY), settings.display+1) - 1; settings.usedpiscale = luax_boolflag(L, idx, settingName(Window::SETTING_USE_DPISCALE), settings.usedpiscale); + settings.displayindex = luax_intflag(L, idx, settingName(Window::SETTING_DISPLAYINDEX), settings.displayindex + 1) - 1; + lua_getfield(L, idx, settingName(Window::SETTING_DISPLAY)); + if (!lua_isnoneornil(L, -1)) + { + luax_markdeprecated(L, 1, "window.display", API_FIELD, DEPRECATED_REPLACED, "displayindex field"); + settings.displayindex = (int) luaL_checkinteger(L, -1) - 1; + } + lua_pop(L, 1); + lua_getfield(L, idx, settingName(Window::SETTING_HIGHDPI)); if (!lua_isnoneornil(L, -1)) { @@ -208,8 +216,8 @@ int w_getMode(lua_State *L) lua_setfield(L, -2, settingName(Window::SETTING_CENTERED)); // Display index is 0-based internally and 1-based in Lua. - lua_pushinteger(L, settings.display + 1); - lua_setfield(L, -2, settingName(Window::SETTING_DISPLAY)); + lua_pushinteger(L, settings.displayindex + 1); + lua_setfield(L, -2, settingName(Window::SETTING_DISPLAYINDEX)); luax_pushboolean(L, settings.usedpiscale); lua_setfield(L, -2, settingName(Window::SETTING_USE_DPISCALE)); From e512eff37b3e7bdf5a6444a4fc10b6f591a5edce Mon Sep 17 00:00:00 2001 From: Alex Szpakowski Date: Fri, 15 Apr 2022 23:31:57 -0300 Subject: [PATCH 28/55] Fix love.threaderror when the error message is empty. Fixes #1775 --- src/modules/thread/LuaThread.cpp | 19 +++++++++++++++---- src/modules/thread/LuaThread.h | 2 ++ src/modules/thread/wrap_LuaThread.cpp | 7 +++---- 3 files changed, 20 insertions(+), 8 deletions(-) diff --git a/src/modules/thread/LuaThread.cpp b/src/modules/thread/LuaThread.cpp index 05e6c2770..d052d31c5 100644 --- a/src/modules/thread/LuaThread.cpp +++ b/src/modules/thread/LuaThread.cpp @@ -36,6 +36,7 @@ love::Type LuaThread::type("Thread", &Threadable::type); LuaThread::LuaThread(const std::string &name, love::Data *code) : code(code) , name(name) + , haserror(false) { threadName = name; } @@ -47,6 +48,7 @@ LuaThread::~LuaThread() void LuaThread::threadFunction() { error.clear(); + haserror = false; lua_State *L = luaL_newstate(); luaL_openlibs(L); @@ -70,7 +72,10 @@ void LuaThread::threadFunction() int tracebackidx = lua_gettop(L); if (luaL_loadbuffer(L, (const char *) code->getData(), code->getSize(), name.c_str()) != 0) + { error = luax_tostring(L, -1); + haserror = true; + } else { int pushedargs = (int) args.size(); @@ -81,18 +86,27 @@ void LuaThread::threadFunction() args.clear(); if (lua_pcall(L, pushedargs, 0, tracebackidx) != 0) + { error = luax_tostring(L, -1); + haserror = true; + } } lua_close(L); - if (!error.empty()) + if (haserror) onError(); } bool LuaThread::start(const std::vector &args) { + if (isRunning()) + return false; + this->args = args; + error.clear(); + haserror = false; + return Threadable::start(); } @@ -103,9 +117,6 @@ const std::string &LuaThread::getError() const void LuaThread::onError() { - if (error.empty()) - return; - auto eventmodule = Module::getInstance(Module::M_EVENT); if (!eventmodule) return; diff --git a/src/modules/thread/LuaThread.h b/src/modules/thread/LuaThread.h index e8d625cb7..9e707b784 100644 --- a/src/modules/thread/LuaThread.h +++ b/src/modules/thread/LuaThread.h @@ -46,6 +46,7 @@ public: virtual ~LuaThread(); void threadFunction(); const std::string &getError() const; + bool hasError() const { return haserror; } bool start(const std::vector &args); @@ -56,6 +57,7 @@ private: StrongRef code; std::string name; std::string error; + bool haserror; std::vector args; diff --git a/src/modules/thread/wrap_LuaThread.cpp b/src/modules/thread/wrap_LuaThread.cpp index db71f9ca1..5d569710d 100644 --- a/src/modules/thread/wrap_LuaThread.cpp +++ b/src/modules/thread/wrap_LuaThread.cpp @@ -63,11 +63,10 @@ int w_Thread_wait(lua_State *L) int w_Thread_getError(lua_State *L) { LuaThread *t = luax_checkthread(L, 1); - std::string err = t->getError(); - if (err.empty()) - lua_pushnil(L); + if (t->hasError()) + luax_pushstring(L, t->getError()); else - luax_pushstring(L, err); + lua_pushnil(L); return 1; } From b3b1317f6184b2fb6c7d78cb7338f9c83ebb4abd Mon Sep 17 00:00:00 2001 From: Alex Szpakowski Date: Fri, 15 Apr 2022 23:43:30 -0300 Subject: [PATCH 29/55] linux: fix issues if certain codepaths are hit while signal handlers are temporarily off. --- src/modules/audio/openal/Audio.cpp | 38 ++++++++++++++---------------- src/modules/thread/sdl/Thread.cpp | 5 +--- src/modules/thread/threads.h | 6 +++++ 3 files changed, 25 insertions(+), 24 deletions(-) diff --git a/src/modules/audio/openal/Audio.cpp b/src/modules/audio/openal/Audio.cpp index 3f8193f2c..68645e351 100644 --- a/src/modules/audio/openal/Audio.cpp +++ b/src/modules/audio/openal/Audio.cpp @@ -100,11 +100,6 @@ Audio::Audio() , poolThread(nullptr) , distanceModel(DISTANCE_INVERSE_CLAMPED) { -#if defined(LOVE_LINUX) - // Temporarly block signals, as the thread inherits this mask - love::thread::disableSignals(); -#endif - // Before opening new device, check if recording // is requested. if (getRequestRecordingPermission()) @@ -114,29 +109,32 @@ Audio::Audio() requestRecordingPermission(); } - // Passing null for default device. - device = alcOpenDevice(nullptr); + { +#if defined(LOVE_LINUX) + // Temporarly block signals, as the thread inherits this mask + love::thread::ScopedDisableSignals disableSignals; +#endif - if (device == nullptr) - throw love::Exception("Could not open device."); + // Passing null for default device. + device = alcOpenDevice(nullptr); + + if (device == nullptr) + throw love::Exception("Could not open device."); #ifdef ALC_EXT_EFX - ALint attribs[4] = { ALC_MAX_AUXILIARY_SENDS, MAX_SOURCE_EFFECTS, 0, 0 }; + ALint attribs[4] = { ALC_MAX_AUXILIARY_SENDS, MAX_SOURCE_EFFECTS, 0, 0 }; #else - ALint *attribs = nullptr; + ALint *attribs = nullptr; #endif - context = alcCreateContext(device, attribs); + context = alcCreateContext(device, attribs); - if (context == nullptr) - throw love::Exception("Could not create context."); + if (context == nullptr) + throw love::Exception("Could not create context."); - if (!alcMakeContextCurrent(context) || alcGetError(device) != ALC_NO_ERROR) - throw love::Exception("Could not make context current."); - -#if defined(LOVE_LINUX) - love::thread::reenableSignals(); -#endif + if (!alcMakeContextCurrent(context) || alcGetError(device) != ALC_NO_ERROR) + throw love::Exception("Could not make context current."); + } #ifdef ALC_EXT_EFX initializeEFX(); diff --git a/src/modules/thread/sdl/Thread.cpp b/src/modules/thread/sdl/Thread.cpp index cfc63e31f..71f28e4ac 100644 --- a/src/modules/thread/sdl/Thread.cpp +++ b/src/modules/thread/sdl/Thread.cpp @@ -44,7 +44,7 @@ bool Thread::start() { #if defined(LOVE_LINUX) // Temporarly block signals, as the thread inherits this mask - love::thread::disableSignals(); + love::thread::ScopedDisableSignals disableSignals; #endif Lock l(mutex); @@ -55,9 +55,6 @@ bool Thread::start() thread = SDL_CreateThread(thread_runner, t->getThreadName(), this); running = (thread != nullptr); -#if defined(LOVE_LINUX) - love::thread::reenableSignals(); -#endif return running; } diff --git a/src/modules/thread/threads.h b/src/modules/thread/threads.h index 5f607bade..827ca8586 100644 --- a/src/modules/thread/threads.h +++ b/src/modules/thread/threads.h @@ -132,6 +132,12 @@ Thread *newThread(Threadable *t); #if defined(LOVE_LINUX) void disableSignals(); void reenableSignals(); + +struct ScopedDisableSignals +{ + ScopedDisableSignals() { disableSignals(); } + ~ScopedDisableSignals() { reenableSignals(); } +}; #endif } // thread From 321397d1dd86b2a2a308221609dd93308f2bd6cd Mon Sep 17 00:00:00 2001 From: Miku AuahDark Date: Sat, 16 Apr 2022 11:12:48 +0800 Subject: [PATCH 30/55] Add love.keyboard.isModifierActive. --- changes.txt | 1 + src/modules/keyboard/Keyboard.cpp | 20 ++++++++++++++++++++ src/modules/keyboard/Keyboard.h | 25 +++++++++++++++++++++++++ src/modules/keyboard/sdl/Keyboard.cpp | 26 ++++++++++++++++++++++++++ src/modules/keyboard/sdl/Keyboard.h | 1 + src/modules/keyboard/wrap_Keyboard.cpp | 12 ++++++++++++ 6 files changed, 85 insertions(+) diff --git a/changes.txt b/changes.txt index a30a5f322..2b98cbc6e 100644 --- a/changes.txt +++ b/changes.txt @@ -42,6 +42,7 @@ Released: N/A * Added a variant of love.graphics.setColorMask which accepts a single boolean. * Added new 'clampone' wrap mode. * Added a variant of Font:getWidth which takes a codepoint number argument. +* Added love.keyboard.isModifierActive. * Added love.system.getPreferredLocales. * Added love.localechanged callback. diff --git a/src/modules/keyboard/Keyboard.cpp b/src/modules/keyboard/Keyboard.cpp index 47f5baf69..05c4bed70 100644 --- a/src/modules/keyboard/Keyboard.cpp +++ b/src/modules/keyboard/Keyboard.cpp @@ -47,6 +47,16 @@ bool Keyboard::getConstant(Scancode in, const char *&out) return scancodes.find(in, out); } +bool Keyboard::getConstant(const char *in, ModifierKey &out) +{ + return modifiers.find(in, out); +} + +bool Keyboard::getConstant(ModifierKey in, const char *&out) +{ + return modifiers.find(in, out); +} + StringMap::Entry Keyboard::keyEntries[] = { {"unknown", Keyboard::KEY_UNKNOWN}, @@ -521,5 +531,15 @@ StringMap::Entry Keyboard::scan StringMap Keyboard::scancodes(Keyboard::scancodeEntries, sizeof(Keyboard::scancodeEntries)); +StringMap::Entry Keyboard::modifierEntries[] = +{ + {"numlock", MODKEY_NUMLOCK}, + {"capslock", MODKEY_CAPSLOCK}, + {"scrolllock", MODKEY_SCROLLLOCK}, + {"mode", MODKEY_MODE}, +}; + +StringMap Keyboard::modifiers(Keyboard::modifierEntries, sizeof(Keyboard::modifierEntries)); + } // keyboard } // love diff --git a/src/modules/keyboard/Keyboard.h b/src/modules/keyboard/Keyboard.h index d28c561e8..73ea3faeb 100644 --- a/src/modules/keyboard/Keyboard.h +++ b/src/modules/keyboard/Keyboard.h @@ -519,6 +519,18 @@ public: SCANCODE_MAX_ENUM }; + /** + * Modifier keys. These are special keys that temporarily modifies the normal function of a button when active. + **/ + enum ModifierKey { + MODKEY_NUMLOCK, + MODKEY_CAPSLOCK, + MODKEY_SCROLLLOCK, + MODKEY_MODE, + + MODKEY_MAX_ENUM + }; + virtual ~Keyboard() {} // Implements Module. @@ -550,6 +562,13 @@ public: **/ virtual bool isScancodeDown(const std::vector &scancodelist) const = 0; + /** + * Checks whether specific modifier key is active or not. + * @param key Modifier key to check. + * @return Whether the specified modifier key is active. + **/ + virtual bool isModifierActive(ModifierKey key) const = 0; + /** * Gets the key corresponding to the specified scancode according to the * current keyboard layout. @@ -592,6 +611,9 @@ public: static bool getConstant(const char *in, Scancode &out); static bool getConstant(Scancode in, const char *&out); + static bool getConstant(const char *in, ModifierKey &out); + static bool getConstant(ModifierKey in, const char *&out); + private: static StringMap::Entry keyEntries[]; @@ -600,6 +622,9 @@ private: static StringMap::Entry scancodeEntries[]; static StringMap scancodes; + static StringMap::Entry modifierEntries[]; + static StringMap modifiers; + }; // Keyboard } // keyboard diff --git a/src/modules/keyboard/sdl/Keyboard.cpp b/src/modules/keyboard/sdl/Keyboard.cpp index 51150e7fe..1abb0858d 100644 --- a/src/modules/keyboard/sdl/Keyboard.cpp +++ b/src/modules/keyboard/sdl/Keyboard.cpp @@ -18,9 +18,16 @@ * 3. This notice may not be removed or altered from any source distribution. **/ +#include + #include "Keyboard.h" #include "window/Window.h" +// SDL before 2.0.18 lack KMOD_SCROLL. Use KMOD_RESERVED instead. +#if !SDL_VERSION_ATLEAST(2, 0, 18) +#define KMOD_SCROLL KMOD_RESERVED +#endif // !SDL_VERSION_ATLEAST(2, 0, 18) + namespace love { namespace keyboard @@ -78,6 +85,25 @@ bool Keyboard::isScancodeDown(const std::vector &scancodelist) const return false; } +bool Keyboard::isModifierActive(ModifierKey key) const +{ + int modstate = SDL_GetModState(); + + switch (key) + { + case MODKEY_NUMLOCK: + return (modstate & KMOD_NUM) != 0; + case MODKEY_CAPSLOCK: + return (modstate & KMOD_CAPS) != 0; + case MODKEY_SCROLLLOCK: + return (modstate & KMOD_SCROLL) != 0; + case MODKEY_MODE: + return (modstate & KMOD_MODE) != 0; + } + + return false; +} + Keyboard::Key Keyboard::getKeyFromScancode(Scancode scancode) const { SDL_Scancode sdlscancode = SDL_SCANCODE_UNKNOWN; diff --git a/src/modules/keyboard/sdl/Keyboard.h b/src/modules/keyboard/sdl/Keyboard.h index e3e889f86..776b9789e 100644 --- a/src/modules/keyboard/sdl/Keyboard.h +++ b/src/modules/keyboard/sdl/Keyboard.h @@ -48,6 +48,7 @@ public: bool hasKeyRepeat() const; bool isDown(const std::vector &keylist) const; bool isScancodeDown(const std::vector &scancodelist) const; + bool isModifierActive(ModifierKey key) const; Key getKeyFromScancode(Scancode scancode) const; Scancode getScancodeFromKey(Key key) const; diff --git a/src/modules/keyboard/wrap_Keyboard.cpp b/src/modules/keyboard/wrap_Keyboard.cpp index 1e0def63c..64c9728b1 100644 --- a/src/modules/keyboard/wrap_Keyboard.cpp +++ b/src/modules/keyboard/wrap_Keyboard.cpp @@ -187,6 +187,17 @@ int w_hasScreenKeyboard(lua_State *L) return 1; } +int w_isModifierActive(lua_State* L) +{ + const char *keystr = luaL_checkstring(L, 1); + Keyboard::ModifierKey key; + if (!Keyboard::getConstant(keystr, key)) + return luax_enumerror(L, "modifier keys", keystr); + + luax_pushboolean(L, instance()->isModifierActive(key)); + return 1; +} + // List of functions to wrap. static const luaL_Reg functions[] = { @@ -199,6 +210,7 @@ static const luaL_Reg functions[] = { "isScancodeDown", w_isScancodeDown }, { "getScancodeFromKey", w_getScancodeFromKey }, { "getKeyFromScancode", w_getKeyFromScancode }, + { "isModifierActive", w_isModifierActive }, { 0, 0 } }; From 8d3730361ba5648d42fd53e4f7108e5c17f713b6 Mon Sep 17 00:00:00 2001 From: Alex Szpakowski Date: Sat, 16 Apr 2022 16:47:31 -0300 Subject: [PATCH 31/55] Add love.dropbegan and love.dropcompleted callbacks. Resolves #1346 --- src/modules/event/sdl/Event.cpp | 6 ++++++ src/modules/love/callbacks.lua | 6 ++++++ 2 files changed, 12 insertions(+) diff --git a/src/modules/event/sdl/Event.cpp b/src/modules/event/sdl/Event.cpp index dae96456e..40abe8ca4 100644 --- a/src/modules/event/sdl/Event.cpp +++ b/src/modules/event/sdl/Event.cpp @@ -428,6 +428,12 @@ Message *Event::convert(const SDL_Event &e) } SDL_free(e.drop.file); break; + case SDL_DROPBEGIN: + msg = new Message("dropbegan"); + break; + case SDL_DROPCOMPLETE: + msg = new Message("dropcompleted"); + break; case SDL_QUIT: case SDL_APP_TERMINATING: msg = new Message("quit"); diff --git a/src/modules/love/callbacks.lua b/src/modules/love/callbacks.lua index 200999169..79b1b3e97 100644 --- a/src/modules/love/callbacks.lua +++ b/src/modules/love/callbacks.lua @@ -112,6 +112,12 @@ function love.createhandlers() directorydropped = function (dir) if love.directorydropped then return love.directorydropped(dir) end end, + dropbegan = function () + if love.dropbegan then return love.dropbegan() end + end, + dropcompleted = function () + if love.dropcompleted then return love.dropcompleted() end + end, lowmemory = function () if love.lowmemory then love.lowmemory() end collectgarbage() From e61a664a8b7d18f21f911b81b0722be054ab1496 Mon Sep 17 00:00:00 2001 From: Alex Szpakowski Date: Mon, 18 Apr 2022 10:46:02 -0300 Subject: [PATCH 32/55] ImageData: limited support for integer pixel formats. For now this doesn't support conversions (get/set/mapPixel, etc). Just the ability for ImageData to contain integer pixel contents, for example via Canvas:newImageData. --- src/common/pixelformat.cpp | 12 ++++++ src/common/pixelformat.h | 10 +++++ src/modules/image/ImageData.cpp | 60 +++++----------------------- src/modules/image/ImageData.h | 1 - src/modules/image/wrap_ImageData.lua | 12 ++++-- 5 files changed, 42 insertions(+), 53 deletions(-) diff --git a/src/common/pixelformat.cpp b/src/common/pixelformat.cpp index e9b8f7df6..a26f7f1ad 100644 --- a/src/common/pixelformat.cpp +++ b/src/common/pixelformat.cpp @@ -240,11 +240,23 @@ const PixelFormatInfo &getPixelFormatInfo(PixelFormat format) return formatInfo[format]; } +const char *getPixelFormatName(PixelFormat format) +{ + const char *name = "unknown"; + getConstant(format, name); + return name; +} + bool isPixelFormatCompressed(PixelFormat format) { return formatInfo[format].compressed; } +bool isPixelFormatColor(PixelFormat format) +{ + return formatInfo[format].color; +} + bool isPixelFormatDepthStencil(PixelFormat format) { const PixelFormatInfo &info = formatInfo[format]; diff --git a/src/common/pixelformat.h b/src/common/pixelformat.h index 355acc383..bb3687f49 100644 --- a/src/common/pixelformat.h +++ b/src/common/pixelformat.h @@ -157,11 +157,21 @@ bool getConstant(const char *in, PixelFormat &out); const PixelFormatInfo &getPixelFormatInfo(PixelFormat format); +/** + * Gets the name of the specified pixel format. + **/ +const char *getPixelFormatName(PixelFormat format); + /** * Gets whether the specified pixel format is a compressed type. **/ bool isPixelFormatCompressed(PixelFormat format); +/** + * Gets whether the specified pixel format is a color type. + **/ +bool isPixelFormatColor(PixelFormat format); + /** * Gets whether the specified pixel format is a depth or stencil type. **/ diff --git a/src/modules/image/ImageData.cpp b/src/modules/image/ImageData.cpp index ec24d87f1..2a5be2819 100644 --- a/src/modules/image/ImageData.cpp +++ b/src/modules/image/ImageData.cpp @@ -43,7 +43,7 @@ ImageData::ImageData(int width, int height, PixelFormat format) : ImageDataBase(format, width, height) { if (!validPixelFormat(format)) - throw love::Exception("Unsupported pixel format for ImageData"); + throw love::Exception("ImageData does not support the %s pixel format.", getPixelFormatName(format)); create(width, height, format); @@ -55,7 +55,7 @@ ImageData::ImageData(int width, int height, PixelFormat format, void *data, bool : ImageDataBase(format, width, height) { if (!validPixelFormat(format)) - throw love::Exception("Unsupported pixel format for ImageData"); + throw love::Exception("ImageData does not support the %s pixel format.", getPixelFormatName(format)); if (own) this->data = (unsigned char *) data; @@ -196,11 +196,7 @@ love::filesystem::FileData *ImageData::encode(FormatHandler::EncodedFormat encod } if (encoder == nullptr || encodedimage.data == nullptr) - { - const char *fname = "unknown"; - love::getConstant(format, fname); - throw love::Exception("No suitable image encoder for %s format.", fname); - } + throw love::Exception("No suitable image encoder for the %s pixel format.", getPixelFormatName(format)); love::filesystem::FileData *filedata = nullptr; @@ -540,7 +536,7 @@ void ImageData::setPixel(int x, int y, const Colorf &c) Pixel *p = (Pixel *) (data + ((y * width + x) * pixelsize)); if (pixelSetFunction == nullptr) - throw love::Exception("Unhandled pixel format %d in ImageData::setPixel", format); + throw love::Exception("ImageData:setPixel does not currently support the %s pixel format.", getPixelFormatName(format)); Lock lock(mutex); @@ -556,7 +552,7 @@ void ImageData::getPixel(int x, int y, Colorf &c) const const Pixel *p = (const Pixel *) (data + ((y * width + x) * pixelsize)); if (pixelGetFunction == nullptr) - throw love::Exception("Unhandled pixel format %d in ImageData::setPixel", format); + throw love::Exception("ImageData:getPixel does not currently support the %s pixel format.", getPixelFormatName(format)); Lock lock(mutex); @@ -759,7 +755,7 @@ void ImageData::paste(ImageData *src, int dx, int dy, int sx, int sy, int sw, in else if (srcformat == PIXELFORMAT_RGBA32_FLOAT && dstformat == PIXELFORMAT_RGBA16_FLOAT) pasteRGBA32FtoRGBA16F(rowsrc, rowdst, sw); - else + else if (getfunction != nullptr && setfunction != nullptr) { // Slow path: convert src -> Colorf -> dst. Colorf c; @@ -771,6 +767,10 @@ void ImageData::paste(ImageData *src, int dx, int dy, int sx, int sy, int sw, in setfunction(c, dstp); } } + else if (getfunction == nullptr) + throw love::Exception("ImageData:paste does not currently support converting from the %s pixel format.", getPixelFormatName(srcformat)); + else + throw love::Exception("ImageData:paste does not currently support converting to the %s pixel format.", getPixelFormatName(dstformat)); } } } @@ -787,45 +787,7 @@ size_t ImageData::getPixelSize() const bool ImageData::validPixelFormat(PixelFormat format) { - switch (format) - { - case PIXELFORMAT_R8_UNORM: - case PIXELFORMAT_RG8_UNORM: - case PIXELFORMAT_RGBA8_UNORM: - case PIXELFORMAT_R16_UNORM: - case PIXELFORMAT_RG16_UNORM: - case PIXELFORMAT_RGBA16_UNORM: - case PIXELFORMAT_R16_FLOAT: - case PIXELFORMAT_RG16_FLOAT: - case PIXELFORMAT_RGBA16_FLOAT: - case PIXELFORMAT_R32_FLOAT: - case PIXELFORMAT_RG32_FLOAT: - case PIXELFORMAT_RGBA32_FLOAT: - case PIXELFORMAT_RGBA4_UNORM: - case PIXELFORMAT_RGB5A1_UNORM: - case PIXELFORMAT_RGB565_UNORM: - case PIXELFORMAT_RGB10A2_UNORM: - case PIXELFORMAT_RG11B10_FLOAT: - return true; - default: - return false; - } -} - -bool ImageData::canPaste(PixelFormat src, PixelFormat dst) -{ - if (src == dst) - return true; - - if (!(src == PIXELFORMAT_RGBA8_UNORM || src == PIXELFORMAT_RGBA16_UNORM - || src == PIXELFORMAT_RGBA16_FLOAT || src == PIXELFORMAT_RGBA32_FLOAT)) - return false; - - if (!(dst == PIXELFORMAT_RGBA8_UNORM || dst == PIXELFORMAT_RGBA16_UNORM - || dst == PIXELFORMAT_RGBA16_FLOAT || dst == PIXELFORMAT_RGBA32_FLOAT)) - return false; - - return true; + return isPixelFormatColor(format) && !isPixelFormatCompressed(format); } ImageData::PixelSetFunction ImageData::getPixelSetFunction(PixelFormat format) diff --git a/src/modules/image/ImageData.h b/src/modules/image/ImageData.h index e3e04c395..83769e797 100644 --- a/src/modules/image/ImageData.h +++ b/src/modules/image/ImageData.h @@ -124,7 +124,6 @@ public: PixelGetFunction getPixelGetFunction() const { return pixelGetFunction; } static bool validPixelFormat(PixelFormat format); - static bool canPaste(PixelFormat src, PixelFormat dst); static PixelSetFunction getPixelSetFunction(PixelFormat format); static PixelGetFunction getPixelGetFunction(PixelFormat format); diff --git a/src/modules/image/wrap_ImageData.lua b/src/modules/image/wrap_ImageData.lua index 5d5cac8a9..5092944da 100644 --- a/src/modules/image/wrap_ImageData.lua +++ b/src/modules/image/wrap_ImageData.lua @@ -368,9 +368,9 @@ local objectcache = setmetatable({}, { width = width, height = height, format = format, - pointer = ffi.cast(conv.pointer, imagedata:getFFIPointer()), - tolua = conv.tolua, - fromlua = conv.fromlua, + pointer = conv == nil and nil or ffi.cast(conv.pointer, imagedata:getFFIPointer()), + tolua = conv == nil and nil or conv.tolua, + fromlua = conv == nil and nil or conv.fromlua, } self[imagedata] = p @@ -395,6 +395,8 @@ function ImageData:_mapPixelUnsafe(func, ix, iy, iw, ih) local p = objectcache[self] local idw, idh = p.width, p.height + if p.pointer == nil then error("ImageData:mapPixel does not currently support the "..p.format.." pixel format.", 2) end + ix = floor(ix) iy = floor(iy) iw = floor(iw) @@ -423,6 +425,8 @@ function ImageData:getPixel(x, y) local p = objectcache[self] if not inside(x, y, p.width, p.height) then error("Attempt to get out-of-range pixel!", 2) end + if p.pointer == nil then error("ImageData:getPixel does not currently support the "..p.format.." pixel format.", 2) end + ffifuncs.lockMutex(self) local pixel = p.pointer[y * p.width + x] local r, g, b, a = p.tolua(pixel) @@ -451,6 +455,8 @@ function ImageData:setPixel(x, y, r, g, b, a) local p = objectcache[self] if not inside(x, y, p.width, p.height) then error("Attempt to set out-of-range pixel!", 2) end + if p.pointer == nil then error("ImageData:setPixel does not currently support the "..p.format.." pixel format.", 2) end + ffifuncs.lockMutex(self) p.fromlua(p.pointer[y * p.width + x], r, g, b, a) ffifuncs.unlockMutex(self) From 71a7aed4412f6ab45a4988c68c7d62c81c3dfebe Mon Sep 17 00:00:00 2001 From: Alex Szpakowski Date: Mon, 18 Apr 2022 20:12:08 -0300 Subject: [PATCH 33/55] minor cleanup of some audio decoder code. --- src/modules/sound/lullaby/FLACDecoder.cpp | 6 +++--- src/modules/sound/lullaby/GmeDecoder.cpp | 6 +++--- src/modules/sound/lullaby/MP3Decoder.cpp | 6 +++--- src/modules/sound/lullaby/ModPlugDecoder.cpp | 5 ++--- src/modules/sound/lullaby/VorbisDecoder.cpp | 6 +++--- src/modules/sound/lullaby/WaveDecoder.cpp | 6 +++--- 6 files changed, 17 insertions(+), 18 deletions(-) diff --git a/src/modules/sound/lullaby/FLACDecoder.cpp b/src/modules/sound/lullaby/FLACDecoder.cpp index b416f30a9..71f4cf315 100644 --- a/src/modules/sound/lullaby/FLACDecoder.cpp +++ b/src/modules/sound/lullaby/FLACDecoder.cpp @@ -52,12 +52,12 @@ bool FLACDecoder::accepts(const std::string &ext) // Please remove once it's no longer the case. static const std::string supported[] = { - "flac", "ogg", "" + "flac", "ogg" }; - for (int i = 0; !(supported[i].empty()); i++) + for (const auto& s : supported) { - if (supported[i].compare(ext) == 0) + if (s.compare(ext) == 0) return true; } diff --git a/src/modules/sound/lullaby/GmeDecoder.cpp b/src/modules/sound/lullaby/GmeDecoder.cpp index fbf4b5bfa..3638eebd9 100644 --- a/src/modules/sound/lullaby/GmeDecoder.cpp +++ b/src/modules/sound/lullaby/GmeDecoder.cpp @@ -72,12 +72,12 @@ bool GmeDecoder::accepts(const std::string &ext) static const std::string supported[] = { "ay", "gbs", "gym", "hes", "kss", "nsf", - "nsfe", "sap", "spc", "vgm", "vgz", "" + "nsfe", "sap", "spc", "vgm", "vgz", }; - for (int i = 0; !(supported[i].empty()); i++) + for (const auto& s : supported) { - if (supported[i].compare(ext) == 0) + if (s.compare(ext) == 0) return true; } diff --git a/src/modules/sound/lullaby/MP3Decoder.cpp b/src/modules/sound/lullaby/MP3Decoder.cpp index 76b2228fb..00c4740e1 100644 --- a/src/modules/sound/lullaby/MP3Decoder.cpp +++ b/src/modules/sound/lullaby/MP3Decoder.cpp @@ -74,12 +74,12 @@ bool MP3Decoder::accepts(const std::string &ext) { static const std::string supported[] = { - "mp3", "" + "mp3" }; - for (int i = 0; !(supported[i].empty()); i++) + for (const auto& s : supported) { - if (supported[i].compare(ext) == 0) + if (s.compare(ext) == 0) return true; } diff --git a/src/modules/sound/lullaby/ModPlugDecoder.cpp b/src/modules/sound/lullaby/ModPlugDecoder.cpp index 269e934c9..f38df31b0 100644 --- a/src/modules/sound/lullaby/ModPlugDecoder.cpp +++ b/src/modules/sound/lullaby/ModPlugDecoder.cpp @@ -84,12 +84,11 @@ bool ModPlugDecoder::accepts(const std::string &ext) "dsm", "far", "it", "j2b", "mdl", "med", "mid", "mod", "mt2", "mtm", "okt", "pat", "psm", "s3m", "stm", "ult", "umx", "xm", - "" }; - for (int i = 0; !(supported[i].empty()); i++) + for (const auto &s : supported) { - if (supported[i].compare(ext) == 0) + if (s.compare(ext) == 0) return true; } diff --git a/src/modules/sound/lullaby/VorbisDecoder.cpp b/src/modules/sound/lullaby/VorbisDecoder.cpp index a955bc474..b9de52347 100644 --- a/src/modules/sound/lullaby/VorbisDecoder.cpp +++ b/src/modules/sound/lullaby/VorbisDecoder.cpp @@ -168,12 +168,12 @@ bool VorbisDecoder::accepts(const std::string &ext) { static const std::string supported[] = { - "ogg", "oga", "ogv", "" + "ogg", "oga", "ogv" }; - for (int i = 0; !(supported[i].empty()); i++) + for (const auto& s : supported) { - if (supported[i].compare(ext) == 0) + if (s.compare(ext) == 0) return true; } diff --git a/src/modules/sound/lullaby/WaveDecoder.cpp b/src/modules/sound/lullaby/WaveDecoder.cpp index c7b680409..95dbfa2b4 100644 --- a/src/modules/sound/lullaby/WaveDecoder.cpp +++ b/src/modules/sound/lullaby/WaveDecoder.cpp @@ -103,12 +103,12 @@ bool WaveDecoder::accepts(const std::string &ext) { static const std::string supported[] = { - "wav", "" + "wav" }; - for (int i = 0; !(supported[i].empty()); i++) + for (const auto& s : supported) { - if (supported[i].compare(ext) == 0) + if (s.compare(ext) == 0) return true; } From bb9c34297215cb31fbf11a52fb6c7e4425b48542 Mon Sep 17 00:00:00 2001 From: Alex Szpakowski Date: Mon, 18 Apr 2022 20:41:55 -0300 Subject: [PATCH 34/55] Remove unused GME sound decoder backend. --- CMakeLists.txt | 2 - platform/unix/configure.ac | 4 - platform/unix/deps.m4 | 5 - .../xcode/liblove.xcodeproj/project.pbxproj | 10 -- src/modules/sound/lullaby/GmeDecoder.cpp | 154 ------------------ src/modules/sound/lullaby/GmeDecoder.h | 73 --------- 6 files changed, 248 deletions(-) delete mode 100644 src/modules/sound/lullaby/GmeDecoder.cpp delete mode 100644 src/modules/sound/lullaby/GmeDecoder.h diff --git a/CMakeLists.txt b/CMakeLists.txt index d6ea93bd6..a14d0045e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -893,8 +893,6 @@ set(LOVE_SRC_MODULE_SOUND_ROOT set(LOVE_SRC_MODULE_SOUND_LULLABY src/modules/sound/lullaby/FLACDecoder.cpp src/modules/sound/lullaby/FLACDecoder.h - src/modules/sound/lullaby/GmeDecoder.cpp - src/modules/sound/lullaby/GmeDecoder.h src/modules/sound/lullaby/ModPlugDecoder.cpp src/modules/sound/lullaby/ModPlugDecoder.h src/modules/sound/lullaby/MP3Decoder.h diff --git a/platform/unix/configure.ac b/platform/unix/configure.ac index 7968f8c0f..42c85fe20 100644 --- a/platform/unix/configure.ac +++ b/platform/unix/configure.ac @@ -54,9 +54,6 @@ with_clean_luaversion=`printf ${with_luaversion} | sed 's/\.//g'` # Generated sources for enabling/disabling modules m4_include([configure-modules-pre.ac]) -# Other features that can be enabled/disabled -AC_ARG_ENABLE([gme], AC_HELP_STRING([--enable-gme], [Enable GME support, for more chiptuney goodness]), [], [enable_gme=no]) - # Dependencies we always use ACLOVE_DEP_LUA ACLOVE_DEP_SDL2 @@ -72,7 +69,6 @@ AS_VAR_IF([enable_module_sound], [yes], [ ACLOVE_DEP_VORBISFILE ], []) AS_VAR_IF([enable_module_video], [yes], [ACLOVE_DEP_THEORA], []) -AS_VAR_IF([enable_gme], [yes], [ACLOVE_DEP_GME], []) # Add flags for optional libraries AC_ARG_ENABLE([library-enet], [ --disable-library-enet Turn off library enet], [], [enable_library_enet=yes]) diff --git a/platform/unix/deps.m4 b/platform/unix/deps.m4 index fbfb1c9a7..d84f01ac1 100644 --- a/platform/unix/deps.m4 +++ b/platform/unix/deps.m4 @@ -35,11 +35,6 @@ AC_DEFUN([ACLOVE_DEP_MPG123], [ AC_SUBST([FILE_OFFSET],[-D_FILE_OFFSET_BITS=64]), AC_SUBST([FILE_OFFSET],[]))]) -AC_DEFUN([ACLOVE_DEP_GME], [ - AC_SEARCH_LIBS([gme_open_data], [gme], [], [LOVE_MSG_ERROR([gme])]) - AC_DEFINE([LOVE_SUPPORT_GME], [], [Enable gme]) - AC_CHECK_HEADER([gme/gme.h], [includes="$includes -I/usr/include/gme"], [])]) - # For enet AC_DEFUN([ACLOVE_SOCKLEN_T], [ AC_CHECK_TYPE([socklen_t], [AC_DEFINE([HAS_SOCKLEN_T], [1], [Define if socklen_t exists.] )], , diff --git a/platform/xcode/liblove.xcodeproj/project.pbxproj b/platform/xcode/liblove.xcodeproj/project.pbxproj index 989a3c73f..c00d8c7cd 100644 --- a/platform/xcode/liblove.xcodeproj/project.pbxproj +++ b/platform/xcode/liblove.xcodeproj/project.pbxproj @@ -526,9 +526,6 @@ FA0B7E8B1A95902C000E1D17 /* FLACDecoder.cpp in Sources */ = {isa = PBXBuildFile; fileRef = FA0B7C821A95902C000E1D17 /* FLACDecoder.cpp */; }; FA0B7E8C1A95902C000E1D17 /* FLACDecoder.cpp in Sources */ = {isa = PBXBuildFile; fileRef = FA0B7C821A95902C000E1D17 /* FLACDecoder.cpp */; }; FA0B7E8D1A95902C000E1D17 /* FLACDecoder.h in Headers */ = {isa = PBXBuildFile; fileRef = FA0B7C831A95902C000E1D17 /* FLACDecoder.h */; }; - FA0B7E8E1A95902C000E1D17 /* GmeDecoder.cpp in Sources */ = {isa = PBXBuildFile; fileRef = FA0B7C841A95902C000E1D17 /* GmeDecoder.cpp */; }; - FA0B7E8F1A95902C000E1D17 /* GmeDecoder.cpp in Sources */ = {isa = PBXBuildFile; fileRef = FA0B7C841A95902C000E1D17 /* GmeDecoder.cpp */; }; - FA0B7E901A95902C000E1D17 /* GmeDecoder.h in Headers */ = {isa = PBXBuildFile; fileRef = FA0B7C851A95902C000E1D17 /* GmeDecoder.h */; }; FA0B7E911A95902C000E1D17 /* ModPlugDecoder.cpp in Sources */ = {isa = PBXBuildFile; fileRef = FA0B7C861A95902C000E1D17 /* ModPlugDecoder.cpp */; }; FA0B7E921A95902C000E1D17 /* ModPlugDecoder.cpp in Sources */ = {isa = PBXBuildFile; fileRef = FA0B7C861A95902C000E1D17 /* ModPlugDecoder.cpp */; }; FA0B7E931A95902C000E1D17 /* ModPlugDecoder.h in Headers */ = {isa = PBXBuildFile; fileRef = FA0B7C871A95902C000E1D17 /* ModPlugDecoder.h */; }; @@ -1699,8 +1696,6 @@ FA0B7C801A95902C000E1D17 /* Decoder.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Decoder.cpp; sourceTree = ""; }; FA0B7C821A95902C000E1D17 /* FLACDecoder.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FLACDecoder.cpp; sourceTree = ""; }; FA0B7C831A95902C000E1D17 /* FLACDecoder.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FLACDecoder.h; sourceTree = ""; }; - FA0B7C841A95902C000E1D17 /* GmeDecoder.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = GmeDecoder.cpp; sourceTree = ""; }; - FA0B7C851A95902C000E1D17 /* GmeDecoder.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GmeDecoder.h; sourceTree = ""; }; FA0B7C861A95902C000E1D17 /* ModPlugDecoder.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ModPlugDecoder.cpp; sourceTree = ""; }; FA0B7C871A95902C000E1D17 /* ModPlugDecoder.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ModPlugDecoder.h; sourceTree = ""; }; FA0B7C8A1A95902C000E1D17 /* Sound.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Sound.cpp; sourceTree = ""; }; @@ -3199,8 +3194,6 @@ FA0B7C7F1A95902C000E1D17 /* CoreAudioDecoder.h */, FA0B7C821A95902C000E1D17 /* FLACDecoder.cpp */, FA0B7C831A95902C000E1D17 /* FLACDecoder.h */, - FA0B7C841A95902C000E1D17 /* GmeDecoder.cpp */, - FA0B7C851A95902C000E1D17 /* GmeDecoder.h */, FA0B7C861A95902C000E1D17 /* ModPlugDecoder.cpp */, FA0B7C871A95902C000E1D17 /* ModPlugDecoder.h */, FA522D4B23F9FE370059EE3C /* MP3Decoder.cpp */, @@ -4023,7 +4016,6 @@ FAF140871E20934C00F898D2 /* parseVersions.h in Headers */, FA0B7AC61A958EA3000E1D17 /* types.h in Headers */, FA0B7DBD1A95902C000E1D17 /* Joystick.h in Headers */, - FA0B7E901A95902C000E1D17 /* GmeDecoder.h in Headers */, FA0B7D0E1A95902C000E1D17 /* wrap_Filesystem.h in Headers */, FA0B7EE41A95902D000E1D17 /* Window.h in Headers */, FA0B7CFC1A95902C000E1D17 /* Filesystem.h in Headers */, @@ -4517,7 +4509,6 @@ FADF54081E3D78F700012CC0 /* Video.cpp in Sources */, FA9D8DD81DEF8411002CD881 /* Data.cpp in Sources */, FABDA9992552448300B5C523 /* b2_contact_manager.cpp in Sources */, - FA0B7E8F1A95902C000E1D17 /* GmeDecoder.cpp in Sources */, FADF542B1E3DAADA00012CC0 /* wrap_Mesh.cpp in Sources */, FA0B7CD71A95902C000E1D17 /* Audio.cpp in Sources */, FA0B7AC01A958EA3000E1D17 /* host.c in Sources */, @@ -4940,7 +4931,6 @@ FABDA9982552448300B5C523 /* b2_contact_manager.cpp in Sources */, FADF542A1E3DAADA00012CC0 /* wrap_Mesh.cpp in Sources */, FA0B7D2B1A95902C000E1D17 /* wrap_Rasterizer.cpp in Sources */, - FA0B7E8E1A95902C000E1D17 /* GmeDecoder.cpp in Sources */, FA0B7CD61A95902C000E1D17 /* Audio.cpp in Sources */, FA3C5E421F8C368C0003C579 /* ShaderStage.cpp in Sources */, FA0B7EAF1A95902C000E1D17 /* System.cpp in Sources */, diff --git a/src/modules/sound/lullaby/GmeDecoder.cpp b/src/modules/sound/lullaby/GmeDecoder.cpp deleted file mode 100644 index 3638eebd9..000000000 --- a/src/modules/sound/lullaby/GmeDecoder.cpp +++ /dev/null @@ -1,154 +0,0 @@ -/** - * Copyright (c) 2006-2022 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 "common/config.h" - -#ifdef LOVE_SUPPORT_GME - -#include "common/Exception.h" -#include "GmeDecoder.h" - -namespace love -{ -namespace sound -{ -namespace lullaby -{ - -GmeDecoder::GmeDecoder(Data *data, int bufferSize) - : Decoder(data, bufferSize) - , emu(0) - , num_tracks(0) - , cur_track(0) -{ - void *d = data->getData(); - int s = data->getSize(); - - if (gme_open_data(d, s, &emu, sampleRate) != 0) - throw love::Exception("Could not open game music file"); - - num_tracks = gme_track_count(emu); - - try - { - if (num_tracks <= 0) - throw love::Exception("Game music file has no tracks"); - - if (gme_start_track(emu, cur_track) != 0) - throw love::Exception("Could not start game music playback"); - } - catch (love::Exception &) - { - gme_delete(emu); - throw; - } -} - -GmeDecoder::~GmeDecoder() -{ - if (emu) - gme_delete(emu); -} - -bool GmeDecoder::accepts(const std::string &ext) -{ - static const std::string supported[] = - { - "ay", "gbs", "gym", "hes", "kss", "nsf", - "nsfe", "sap", "spc", "vgm", "vgz", - }; - - for (const auto& s : supported) - { - if (s.compare(ext) == 0) - return true; - } - - return false; -} - -love::sound::Decoder *GmeDecoder::clone() -{ - return new GmeDecoder(data.get(), bufferSize); -} - -int GmeDecoder::decode() -{ - short *sbuf = static_cast(buffer); - int size = bufferSize / sizeof(short); - - if (gme_play(emu, size, sbuf) != 0) - throw love::Exception("Error while decoding game music"); - - if (!eof && gme_track_ended(emu)) - { - // Start the next track if this one ended. - if (cur_track < num_tracks - 1) - gme_start_track(emu, ++cur_track); - else - eof = true; - } - - return bufferSize; -} - -bool GmeDecoder::seek(double s) -{ - return gme_seek(emu, static_cast(s * 1000.0)) != 0; -} - -bool GmeDecoder::rewind() -{ - // If we're in the first track, rewind. - if (cur_track == 0) - return gme_seek(emu, 0) == 0; - else - { - // Otherwise, start from the first track again. - cur_track = 0; - return gme_start_track(emu, cur_track) == 0; - } -} - -bool GmeDecoder::isSeekable() -{ - return true; -} - -int GmeDecoder::getChannelCount() const -{ - return 2; -} - -int GmeDecoder::getBitDepth() const -{ - return 16; -} - -double GmeDecoder::getDuration() -{ - return -1; -} - -} // lullaby -} // sound -} // love - -#endif // LOVE_SUPPORT_GME diff --git a/src/modules/sound/lullaby/GmeDecoder.h b/src/modules/sound/lullaby/GmeDecoder.h deleted file mode 100644 index 61d072f43..000000000 --- a/src/modules/sound/lullaby/GmeDecoder.h +++ /dev/null @@ -1,73 +0,0 @@ -/** - * Copyright (c) 2006-2022 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_SOUND_LULLABY_GME_DECODER_H -#define LOVE_SOUND_LULLABY_GME_DECODER_H - -#ifdef LOVE_SUPPORT_GME - -// LOVE -#include "common/Data.h" -#include "sound/Decoder.h" - -#ifdef LOVE_APPLE_USE_FRAMEWORKS -#include -#else -#include -#endif - -namespace love -{ -namespace sound -{ -namespace lullaby -{ - -class GmeDecoder : public Decoder -{ -public: - - GmeDecoder(Data *data, int bufferSize); - virtual ~GmeDecoder(); - - static bool accepts(const std::string &ext); - - love::sound::Decoder *clone(); - int decode(); - bool seek(double s); - bool rewind(); - bool isSeekable(); - int getChannelCount() const; - int getBitDepth() const; - double getDuration(); - -private: - Music_Emu *emu; - int num_tracks; - int cur_track; -}; // Decoder - -} // lullaby -} // sound -} // love - -#endif // LOVE_SUPPORT_GME - -#endif // LOVE_SOUND_LULLABY_GME_DECODER_H From 76193899b3f622716370c241493b9b67a793610f Mon Sep 17 00:00:00 2001 From: Alex Szpakowski Date: Mon, 18 Apr 2022 20:53:46 -0300 Subject: [PATCH 35/55] Fix compilation... --- src/modules/sound/lullaby/Sound.cpp | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/modules/sound/lullaby/Sound.cpp b/src/modules/sound/lullaby/Sound.cpp index 8ab449d45..c20c90f99 100644 --- a/src/modules/sound/lullaby/Sound.cpp +++ b/src/modules/sound/lullaby/Sound.cpp @@ -27,7 +27,6 @@ #include "ModPlugDecoder.h" #include "VorbisDecoder.h" -#include "GmeDecoder.h" #include "WaveDecoder.h" #include "FLACDecoder.h" #include "MP3Decoder.h" @@ -88,9 +87,6 @@ sound::Decoder *Sound::newDecoder(love::filesystem::FileData *data, int bufferSi #endif // LOVE_NO_MODPLUG DecoderImplFor(), DecoderImplFor(), -#ifdef LOVE_SUPPORT_GME - DecoderImplFor(), -#endif // LOVE_SUPPORT_GME #ifdef LOVE_SUPPORT_COREAUDIO DecoderImplFor(), #endif From ceb23eee48fcb833074e7897c621c7f501c065a6 Mon Sep 17 00:00:00 2001 From: Alex Szpakowski Date: Thu, 21 Apr 2022 19:52:04 -0300 Subject: [PATCH 36/55] Add new Stream base class, internal-only for now. --- src/common/Stream.cpp | 51 +++++++++++++++++++- src/common/Stream.h | 77 +++++++++++++++++++++++++------ src/modules/data/ByteData.cpp | 5 +- src/modules/data/ByteData.h | 2 +- src/modules/video/Video.h | 1 - src/modules/video/VideoStream.cpp | 2 +- src/modules/video/VideoStream.h | 26 ++++++++++- 7 files changed, 143 insertions(+), 21 deletions(-) diff --git a/src/common/Stream.cpp b/src/common/Stream.cpp index d9139e355..01d4c593d 100644 --- a/src/common/Stream.cpp +++ b/src/common/Stream.cpp @@ -1,5 +1,5 @@ /** - * Copyright (c) 2006-2015 LOVE Development Team + * Copyright (c) 2006-2022 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 @@ -20,10 +20,59 @@ // LOVE #include "Stream.h" +#include "Data.h" +#include "data/ByteData.h" +#include "Exception.h" namespace love { love::Type Stream::type("Stream", &Object::type); +Data *Stream::read(int64 size) +{ + int64 max = LOVE_INT64_MAX; + int64 cur = 0; + + if (isSeekable()) + { + max = getSize(); + cur = tell(); + } + + if (cur < 0) + cur = 0; + else if (cur > max) + cur = max; + + if (cur + size > max) + size = max - cur; + + StrongRef dst(new data::ByteData(size, false), Acquire::NORETAIN); + + int64 bytesRead = read(dst->getData(), size); + + if (bytesRead < 0 || (bytesRead == 0 && bytesRead != size)) + throw love::Exception("Could not read from stream."); + + if (bytesRead < size) + dst.set(new data::ByteData(dst->getData(), (size_t) bytesRead), Acquire::NORETAIN); + + dst->retain(); + return dst; +} + +bool Stream::write(Data *src) +{ + return write(src, 0, src->getSize()); +} + +bool Stream::write(Data *src, int64 offset, int64 size) +{ + if (offset < 0 || size < 0 || offset + size > src->getSize()) + throw love::Exception("Offset and size parameters do not fit within the given Data's size."); + + return write((const uint8 *) src->getData() + offset, size); +} + } // love diff --git a/src/common/Stream.h b/src/common/Stream.h index 2b69144e3..6d02dcd1a 100644 --- a/src/common/Stream.h +++ b/src/common/Stream.h @@ -1,5 +1,5 @@ /** - * Copyright (c) 2006-2015 LOVE Development Team + * Copyright (c) 2006-2022 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 @@ -24,41 +24,92 @@ // LOVE #include #include "Object.h" +#include "int.h" namespace love { +class Data; + class Stream : public Object { public: + + enum SeekOrigin + { + SEEKORIGIN_BEGIN, + SEEKORIGIN_CURRENT, + SEEKORIGIN_END, + SEEKORIGIN_MAX_ENUM + }; + static love::Type type; virtual ~Stream() {} - // getData and getSize are assumed to talk about - // the buffer + /** + * Creates a new copy of the Stream, with the same settings as the original. + * The seek position will be reset in the copy. + **/ + virtual Stream *clone() = 0; /** - * A callback, gets called when some Stream consumer exhausts the data + * Gets whether read() is supported for this Stream. **/ - virtual void fillBackBuffer() {} + virtual bool isReadable() const = 0; /** - * Get the front buffer, Streams are supposed to be (at least) double-buffered + * Gets whether write() is supported for this Stream. **/ - virtual const void *getFrontBuffer() const = 0; + virtual bool isWritable() const = 0; /** - * Get the size of any (and in particular the front) buffer + * Gets whether seek(), tell(), and getSize() are supported for this Stream. **/ - virtual size_t getSize() const = 0; + virtual bool isSeekable() const = 0; /** - * Swap buffers. Returns true if there is new data in the front buffer, - * false otherwise. - * NOTE: If there is no back buffer ready, this call must be ignored + * Reads data into the destination buffer, and returns the number of bytes + * actually read. **/ - virtual bool swapBuffers() = 0; + virtual int64 read(void *dst, int64 size) = 0; + + /** + * Reads data into a new Data object. + **/ + virtual Data *read(int64 size); + + /** + * Writes data from the source buffer into the Stream. + **/ + virtual bool write(const void *src, int64 size) = 0; + + /** + * Writes data from the source Data object into the Stream. + **/ + virtual bool write(Data *src, int64 offset, int64 size); + bool write(Data *src); + + /** + * Flushes all data written to the Stream. + **/ + virtual bool flush() = 0; + + /** + * Gets the total size of the Stream, if supported. + **/ + virtual int64 getSize() = 0; + + /** + * Sets the current position in the Stream, if supported. + **/ + virtual bool seek(int64 pos, SeekOrigin origin = SEEKORIGIN_BEGIN) = 0; + + /** + * Gets the current position in the Stream, if supported. + **/ + virtual int64 tell() = 0; + }; // Stream } // love diff --git a/src/modules/data/ByteData.cpp b/src/modules/data/ByteData.cpp index 734af92c8..045f9f0e1 100644 --- a/src/modules/data/ByteData.cpp +++ b/src/modules/data/ByteData.cpp @@ -31,11 +31,12 @@ namespace data love::Type ByteData::type("ByteData", &Data::type); -ByteData::ByteData(size_t size) +ByteData::ByteData(size_t size, bool clear) : size(size) { create(); - memset(data, 0, size); + if (clear) + memset(data, 0, size); } ByteData::ByteData(const void *d, size_t size) diff --git a/src/modules/data/ByteData.h b/src/modules/data/ByteData.h index befbe079e..2e101df34 100644 --- a/src/modules/data/ByteData.h +++ b/src/modules/data/ByteData.h @@ -35,7 +35,7 @@ public: static love::Type type; - ByteData(size_t size); + ByteData(size_t size, bool clear = true); ByteData(const void *d, size_t size); ByteData(void *d, size_t size, bool own); ByteData(const ByteData &d); diff --git a/src/modules/video/Video.h b/src/modules/video/Video.h index 3de49f0d8..11d158980 100644 --- a/src/modules/video/Video.h +++ b/src/modules/video/Video.h @@ -23,7 +23,6 @@ // LOVE #include "common/Module.h" -#include "common/Stream.h" #include "filesystem/File.h" #include "VideoStream.h" diff --git a/src/modules/video/VideoStream.cpp b/src/modules/video/VideoStream.cpp index 2edb8707c..bb2fae705 100644 --- a/src/modules/video/VideoStream.cpp +++ b/src/modules/video/VideoStream.cpp @@ -27,7 +27,7 @@ namespace love namespace video { -love::Type VideoStream::type("VideoStream", &Stream::type); +love::Type VideoStream::type("VideoStream", &Object::type); void VideoStream::setSync(VideoStream::FrameSync *frameSync) { diff --git a/src/modules/video/VideoStream.h b/src/modules/video/VideoStream.h index fe507a6d0..10d1fa3ae 100644 --- a/src/modules/video/VideoStream.h +++ b/src/modules/video/VideoStream.h @@ -22,7 +22,7 @@ #define LOVE_VIDEO_VIDEOSTREAM_H // LOVE -#include "common/Stream.h" +#include "common/Object.h" #include "audio/Source.h" #include "thread/threads.h" @@ -31,7 +31,7 @@ namespace love namespace video { -class VideoStream : public Stream +class VideoStream : public love::Object { public: @@ -39,6 +39,28 @@ public: virtual ~VideoStream() {} + /** + * A callback, gets called when some Stream consumer exhausts the data + **/ + virtual void fillBackBuffer() {} + + /** + * Get the front buffer, Streams are supposed to be (at least) double-buffered + **/ + virtual const void* getFrontBuffer() const = 0; + + /** + * Get the size of any (and in particular the front) buffer + **/ + virtual size_t getSize() const = 0; + + /** + * Swap buffers. Returns true if there is new data in the front buffer, + * false otherwise. + * NOTE: If there is no back buffer ready, this call must be ignored + **/ + virtual bool swapBuffers() = 0; + virtual int getWidth() const = 0; virtual int getHeight() const = 0; virtual const std::string &getFilename() const = 0; From 1e85893abb93b8673340f7844439fe4deaeda2d8 Mon Sep 17 00:00:00 2001 From: Alex Szpakowski Date: Thu, 21 Apr 2022 19:52:46 -0300 Subject: [PATCH 37/55] Add new DataStream subclass of Stream, internal-only for now. --- CMakeLists.txt | 2 + src/modules/data/DataStream.cpp | 139 ++++++++++++++++++++++++++++++++ src/modules/data/DataStream.h | 69 ++++++++++++++++ 3 files changed, 210 insertions(+) create mode 100644 src/modules/data/DataStream.cpp create mode 100644 src/modules/data/DataStream.h diff --git a/CMakeLists.txt b/CMakeLists.txt index a14d0045e..399676783 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -375,6 +375,8 @@ set(LOVE_SRC_MODULE_DATA src/modules/data/Compressor.h src/modules/data/DataModule.cpp src/modules/data/DataModule.h + src/modules/data/DataStream.cpp + src/modules/data/DataStream.h src/modules/data/DataView.cpp src/modules/data/DataView.h src/modules/data/HashFunction.cpp diff --git a/src/modules/data/DataStream.cpp b/src/modules/data/DataStream.cpp new file mode 100644 index 000000000..54962845b --- /dev/null +++ b/src/modules/data/DataStream.cpp @@ -0,0 +1,139 @@ +/** + * Copyright (c) 2006-2022 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 "DataStream.h" +#include "common/Exception.h" +#include "common/int.h" +#include "common/Data.h" + +#include + +namespace love +{ +namespace data +{ + +love::Type DataStream::type("DataStream", &Stream::type); + +DataStream::DataStream(Data *data) + : data(data) + , offset(0) + , size(data->getSize()) + , memory((const uint8 *) data->getData()) + , writableMemory((uint8 *) data->getData()) // TODO: disallow writing sometimes? +{ +} + +DataStream::DataStream(const DataStream &other) + : data(other.data) + , offset(0) + , size(other.size) + , memory(other.memory) + , writableMemory(other.writableMemory) +{ +} + +DataStream::~DataStream() +{ +} + +DataStream *DataStream::clone() +{ + return new DataStream(*this); +} + +bool DataStream::isReadable() const +{ + return true; +} + +bool DataStream::isWritable() const +{ + return writableMemory != nullptr; +} + +bool DataStream::isSeekable() const +{ + return true; +} + +int64 DataStream::read(void* data, int64 size) +{ + if (size <= 0) + return 0; + + if (offset >= getSize()) + return 0; + + int64 readsize = std::min(size, getSize() - offset); + + memcpy(data, memory + offset, readsize); + + offset += readsize; + return readsize; +} + +bool DataStream::write(const void* data, int64 size) +{ + if (size <= 0 || writableMemory == nullptr) + return false; + + if (offset >= getSize()) + return false; + + int64 writesize = std::min(size, getSize() - offset); + + memcpy(writableMemory + offset, data, writesize); + + offset += writesize; + return true; +} + +bool DataStream::flush() +{ + return true; +} + +int64 DataStream::getSize() +{ + return size; +} + +bool DataStream::seek(int64 pos, SeekOrigin origin) +{ + if (origin == SEEKORIGIN_CURRENT) + pos += offset; + else if (origin == SEEKORIGIN_END) + pos += size; + + if (pos < 0 || pos > size) + return false; + + offset = pos; + return true; +} + +int64 DataStream::tell() +{ + return offset; +} + +} // data +} // love diff --git a/src/modules/data/DataStream.h b/src/modules/data/DataStream.h new file mode 100644 index 000000000..f9247c7e3 --- /dev/null +++ b/src/modules/data/DataStream.h @@ -0,0 +1,69 @@ +/** + * Copyright (c) 2006-2022 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. + **/ + +#pragma once + +#include "common/Stream.h" + +namespace love +{ +namespace data +{ + +class DataStream : public love::Stream +{ +public: + + static love::Type type; + + DataStream(Data *data); + virtual ~DataStream(); + + // Implements Stream. + DataStream *clone() override; + + bool isReadable() const override; + bool isWritable() const override; + bool isSeekable() const override; + + int64 read(void* data, int64 size) override; + bool write(const void* data, int64 size) override; + + bool flush() override; + + int64 getSize() override; + + bool seek(int64 pos, SeekOrigin origin = SEEKORIGIN_BEGIN) override; + int64 tell() override; + +private: + + DataStream(const DataStream &other); + + StrongRef data; + const uint8 *memory; + uint8 *writableMemory; + size_t offset; + size_t size; + +}; // DataStream + +} // data +} // love From d5865e160c1d268e17891b07cc5b66e34f451d93 Mon Sep 17 00:00:00 2001 From: Alex Szpakowski Date: Thu, 21 Apr 2022 19:53:27 -0300 Subject: [PATCH 38/55] File objects inherit from Stream. --- src/modules/event/sdl/Event.cpp | 2 +- src/modules/filesystem/File.cpp | 73 +++++---------- src/modules/filesystem/File.h | 93 ++++---------------- src/modules/filesystem/Filesystem.h | 5 +- src/modules/filesystem/NativeFile.cpp | 34 ++++++- src/modules/filesystem/NativeFile.h | 19 ++-- src/modules/filesystem/physfs/File.cpp | 38 ++++++-- src/modules/filesystem/physfs/File.h | 19 ++-- src/modules/filesystem/physfs/Filesystem.cpp | 24 ++--- src/modules/filesystem/physfs/Filesystem.h | 5 +- src/modules/filesystem/wrap_File.cpp | 4 +- src/modules/filesystem/wrap_Filesystem.cpp | 12 ++- src/modules/filesystem/wrap_Filesystem.h | 2 + 13 files changed, 156 insertions(+), 174 deletions(-) diff --git a/src/modules/event/sdl/Event.cpp b/src/modules/event/sdl/Event.cpp index 40abe8ca4..bde9d0bfb 100644 --- a/src/modules/event/sdl/Event.cpp +++ b/src/modules/event/sdl/Event.cpp @@ -420,7 +420,7 @@ Message *Event::convert(const SDL_Event &e) } else { - auto *file = new love::filesystem::NativeFile(e.drop.file); + auto *file = new love::filesystem::NativeFile(e.drop.file, love::filesystem::File::MODE_CLOSED); vargs.emplace_back(&love::filesystem::NativeFile::type, file); msg = new Message("filedropped", vargs); file->release(); diff --git a/src/modules/filesystem/File.cpp b/src/modules/filesystem/File.cpp index c3fc539f6..6538c8f85 100644 --- a/src/modules/filesystem/File.cpp +++ b/src/modules/filesystem/File.cpp @@ -25,12 +25,17 @@ namespace love namespace filesystem { -love::Type File::type("File", &Object::type); +love::Type File::type("File", &Stream::type); File::~File() { } +FileData *File::read() +{ + return read(getSize()); +} + FileData *File::read(int64 size) { bool isopen = isOpen(); @@ -40,7 +45,6 @@ FileData *File::read(int64 size) int64 max = getSize(); int64 cur = tell(); - size = (size == ALL) ? max : size; if (size < 0) throw love::Exception("Invalid read size."); @@ -54,7 +58,7 @@ FileData *File::read(int64 size) if (cur + size > max) size = max - cur; - FileData *fileData = new FileData(size, getFilename()); + StrongRef fileData(new FileData(size, getFilename()), Acquire::NORETAIN); int64 bytesRead = read(fileData->getData(), size); if (bytesRead < 0 || (bytesRead == 0 && bytesRead != size)) @@ -65,23 +69,18 @@ FileData *File::read(int64 size) if (bytesRead < size) { - FileData *tmpFileData = new FileData(bytesRead, getFilename()); + StrongRef tmpFileData(new FileData(bytesRead, getFilename()), Acquire::NORETAIN); memcpy(tmpFileData->getData(), fileData->getData(), (size_t) bytesRead); - fileData->release(); fileData = tmpFileData; } if (!isopen) close(); + fileData->retain(); return fileData; } -bool File::write(const Data *data, int64 size) -{ - return write(data->getData(), (size == ALL) ? data->getSize() : size); -} - std::string File::getExtension() const { const std::string &filename = getFilename(); @@ -93,54 +92,22 @@ std::string File::getExtension() const return std::string(); } -bool File::getConstant(const char *in, Mode &out) +STRINGMAP_CLASS_BEGIN(File, File::Mode, File::MODE_MAX_ENUM, mode) { - return modes.find(in, out); + { "c", File::MODE_CLOSED }, + { "r", File::MODE_READ }, + { "w", File::MODE_WRITE }, + { "a", File::MODE_APPEND }, } +STRINGMAP_CLASS_END(File, File::Mode, File::MODE_MAX_ENUM, mode) -bool File::getConstant(Mode in, const char *&out) +STRINGMAP_CLASS_BEGIN(File, File::BufferMode, File::BUFFER_MAX_ENUM, bufferMode) { - return modes.find(in, out); + { "none", File::BUFFER_NONE }, + { "line", File::BUFFER_LINE }, + { "full", File::BUFFER_FULL }, } - -std::vector File::getConstants(Mode) -{ - return modes.getNames(); -} - -bool File::getConstant(const char *in, BufferMode &out) -{ - return bufferModes.find(in, out); -} - -bool File::getConstant(BufferMode in, const char *&out) -{ - return bufferModes.find(in, out); -} - -std::vector File::getConstants(BufferMode) -{ - return bufferModes.getNames(); -} - -StringMap::Entry File::modeEntries[] = -{ - { "c", MODE_CLOSED }, - { "r", MODE_READ }, - { "w", MODE_WRITE }, - { "a", MODE_APPEND }, -}; - -StringMap File::modes(File::modeEntries, sizeof(File::modeEntries)); - -StringMap::Entry File::bufferModeEntries[] = -{ - { "none", BUFFER_NONE }, - { "line", BUFFER_LINE }, - { "full", BUFFER_FULL }, -}; - -StringMap File::bufferModes(File::bufferModeEntries, sizeof(File::bufferModeEntries)); +STRINGMAP_CLASS_END(File, File::BufferMode, File::BUFFER_MAX_ENUM, bufferMode) } // filesystem } // love diff --git a/src/modules/filesystem/File.h b/src/modules/filesystem/File.h index 902730470..4b7a74cd5 100644 --- a/src/modules/filesystem/File.h +++ b/src/modules/filesystem/File.h @@ -27,6 +27,7 @@ // LOVE #include "common/Data.h" #include "common/Object.h" +#include "common/Stream.h" #include "common/StringMap.h" #include "common/int.h" #include "FileData.h" @@ -40,7 +41,7 @@ namespace filesystem * A File interface, providing generic means of reading from and * writing to files. **/ -class File : public Object +class File : public Stream { public: @@ -66,16 +67,19 @@ public: BUFFER_MAX_ENUM }; - /** - * Used to indicate ALL data in a file. - **/ - static const int64 ALL = -1; - /** * Destructor. **/ virtual ~File(); + // Implements Stream. + bool isReadable() const override { return getMode() == MODE_READ; } + bool isWritable() const override { return getMode() == MODE_WRITE || getMode() == MODE_APPEND; } + bool isSeekable() const override { return isOpen(); } + + using Stream::read; + using Stream::write; + /** * Opens the file in a certain mode. * @@ -96,53 +100,14 @@ public: **/ virtual bool isOpen() const = 0; - /** - * Gets the size of the file. - * - * @return The size of the file. - **/ - virtual int64 getSize() = 0; - /** * Reads data from the file and allocates a Data object. * - * @param size The number of bytes to attempt reading, or -1 for EOF. + * @param size The number of bytes to attempt reading. * @return A newly allocated Data object. **/ - virtual FileData *read(int64 size = ALL); - - /** - * Reads data into the destination buffer. - * - * @param dst The destination buffer. - * @param size The number of bytes to attempt reading. - * @return The number of bytes actually read. - **/ - virtual int64 read(void *dst, int64 size) = 0; - - /** - * Writes data into the File. - * - * @param data The source buffer. - * @param size The size of the buffer. - * @return True of success, false otherwise. - **/ - virtual bool write(const void *data, int64 size) = 0; - - /** - * Writes a Data object into the File. - * - * @param data The data object to write into the file. - * @param size The number of bytes to attempt writing, or -1 for everything. - * @return True of success, false otherwise. - **/ - virtual bool write(const Data *data, int64 size = ALL); - - /** - * Flushes the currently buffered file data to disk. Only applicable in - * write mode. - **/ - virtual bool flush() = 0; + FileData *read(int64 size) override; + FileData *read(); /** * Checks whether we are currently at end-of-file. @@ -151,21 +116,6 @@ public: **/ virtual bool isEOF() = 0; - /** - * Gets the current position in the File. - * - * @return The current byte position in the File. - **/ - virtual int64 tell() = 0; - - /** - * Seeks to a certain position in the File. - * - * @param pos The byte position in the file. - * @return True on success, false otherwise. - **/ - virtual bool seek(uint64 pos) = 0; - /** * Sets the buffering mode for the file. When buffering is enabled, the file * will not write to disk (or will pre-load data if in read mode) until the @@ -202,21 +152,8 @@ public: **/ virtual std::string getExtension() const; - static bool getConstant(const char *in, Mode &out); - static bool getConstant(Mode in, const char *&out); - static std::vector getConstants(Mode); - - static bool getConstant(const char *in, BufferMode &out); - static bool getConstant(BufferMode in, const char *&out); - static std::vector getConstants(BufferMode); - -private: - - static StringMap::Entry modeEntries[]; - static StringMap modes; - - static StringMap::Entry bufferModeEntries[]; - static StringMap bufferModes; + STRINGMAP_CLASS_DECLARE(Mode); + STRINGMAP_CLASS_DECLARE(BufferMode); }; // File diff --git a/src/modules/filesystem/Filesystem.h b/src/modules/filesystem/Filesystem.h index 4a381c75c..53ca5cdbb 100644 --- a/src/modules/filesystem/Filesystem.h +++ b/src/modules/filesystem/Filesystem.h @@ -167,7 +167,7 @@ public: /** * Creates a new file. **/ - virtual File *newFile(const char *filename) const = 0; + virtual File *newFile(const char *filename, File::Mode = File::MODE_CLOSED) const = 0; /** * Creates a new FileData object. Data will be copied. @@ -239,7 +239,8 @@ public: * @param filename The name of the file to read from. * @param size The size in bytes of the data to read. **/ - virtual FileData *read(const char *filename, int64 size = File::ALL) const = 0; + virtual FileData *read(const char *filename, int64 size) const = 0; + virtual FileData *read(const char *filename) const = 0; /** * Write data to a file. diff --git a/src/modules/filesystem/NativeFile.cpp b/src/modules/filesystem/NativeFile.cpp index fad7432fb..0fd5d0f1d 100644 --- a/src/modules/filesystem/NativeFile.cpp +++ b/src/modules/filesystem/NativeFile.cpp @@ -39,13 +39,14 @@ namespace filesystem love::Type NativeFile::type("NativeFile", &File::type); -NativeFile::NativeFile(const std::string &filename) +NativeFile::NativeFile(const std::string &filename, Mode mode) : filename(filename) , file(nullptr) , mode(MODE_CLOSED) , bufferMode(BUFFER_NONE) , bufferSize(0) { + open(mode); } NativeFile::~NativeFile() @@ -54,10 +55,28 @@ NativeFile::~NativeFile() close(); } +NativeFile::NativeFile(const NativeFile &other) + : filename(other.filename) + , file(nullptr) + , mode(MODE_CLOSED) + , bufferMode(other.bufferMode) + , bufferSize(other.bufferSize) +{ + open(other.mode); +} + +NativeFile *NativeFile::clone() +{ + return new NativeFile(*this); +} + bool NativeFile::open(Mode newmode) { if (newmode == MODE_CLOSED) + { + close(); return true; + } // File already open? if (file != nullptr) @@ -197,15 +216,22 @@ int64 NativeFile::tell() #endif } -bool NativeFile::seek(uint64 pos) +bool NativeFile::seek(int64 pos, SeekOrigin origin) { if (file == nullptr) return false; + int forigin = SEEK_SET; + if (origin == SEEKORIGIN_CURRENT) + forigin = SEEK_CUR; + else if (origin == SEEKORIGIN_END) + forigin = SEEK_END; + + // TODO #ifdef LOVE_WINDOWS - return _fseeki64(file, (int64) pos, SEEK_SET) == 0; + return _fseeki64(file, pos, forigin) == 0; #else - return fseeko(file, (off_t) pos, SEEK_SET) == 0; + return fseeko(file, (off_t) pos, forigin) == 0; #endif } diff --git a/src/modules/filesystem/NativeFile.h b/src/modules/filesystem/NativeFile.h index 4a3e23e5c..9809784e8 100644 --- a/src/modules/filesystem/NativeFile.h +++ b/src/modules/filesystem/NativeFile.h @@ -39,22 +39,25 @@ public: static love::Type type; - NativeFile(const std::string &filename); + NativeFile(const std::string &filename, Mode mode); virtual ~NativeFile(); + // Implements Stream. + NativeFile *clone() override; + int64 read(void* dst, int64 size) override; + bool write(const void* data, int64 size) override; + bool flush() override; + int64 getSize() override; + int64 tell() override; + bool seek(int64 pos, SeekOrigin origin) override; + // Implements File. using File::read; using File::write; bool open(Mode mode) override; bool close() override; bool isOpen() const override; - int64 getSize() override; - int64 read(void *dst, int64 size) override; - bool write(const void *data, int64 size) override; - bool flush() override; bool isEOF() override; - int64 tell() override; - bool seek(uint64 pos) override; bool setBuffer(BufferMode bufmode, int64 size) override; BufferMode getBuffer(int64 &size) const override; Mode getMode() const override; @@ -62,6 +65,8 @@ public: private: + NativeFile(const NativeFile &other); + static const char *getModeString(Mode mode); std::string filename; diff --git a/src/modules/filesystem/physfs/File.cpp b/src/modules/filesystem/physfs/File.cpp index 2caa2fc93..f56455fc5 100644 --- a/src/modules/filesystem/physfs/File.cpp +++ b/src/modules/filesystem/physfs/File.cpp @@ -41,13 +41,24 @@ static bool setupWriteDirectory() return fs != nullptr && fs->setupWriteDirectory(); } -File::File(const std::string &filename) +File::File(const std::string &filename, Mode mode) : filename(filename) , file(nullptr) , mode(MODE_CLOSED) , bufferMode(BUFFER_NONE) , bufferSize(0) { + open(mode); +} + +File::File(const File &other) + : filename(other.filename) + , file(nullptr) + , mode(MODE_CLOSED) + , bufferMode(other.bufferMode) + , bufferSize(other.bufferSize) +{ + open(other.mode); } File::~File() @@ -56,10 +67,18 @@ File::~File() close(); } +File *File::clone() +{ + return new File(*this); +} + bool File::open(Mode mode) { if (mode == MODE_CLOSED) + { + close(); return true; + } if (!PHYSFS_isInit()) throw love::Exception("PhysFS is not initialized."); @@ -151,10 +170,6 @@ int64 File::read(void *dst, int64 size) if (!file || mode != MODE_READ) throw love::Exception("File is not opened for reading."); - int64 max = (int64)PHYSFS_fileLength(file); - size = (size == ALL) ? max : size; - size = (size > max) ? max : size; - if (size < 0) throw love::Exception("Invalid read size."); @@ -207,8 +222,19 @@ int64 File::tell() return (int64) PHYSFS_tell(file); } -bool File::seek(uint64 pos) +bool File::seek(int64 pos, SeekOrigin origin) { + if (file != nullptr) + { + if (origin == SEEKORIGIN_CURRENT) + pos += tell(); + else if (origin == SEEKORIGIN_END) + pos += getSize(); + } + + if (pos < 0) + return false; + return file != nullptr && PHYSFS_seek(file, (PHYSFS_uint64) pos) != 0; } diff --git a/src/modules/filesystem/physfs/File.h b/src/modules/filesystem/physfs/File.h index 13dc543ca..40bad82a6 100644 --- a/src/modules/filesystem/physfs/File.h +++ b/src/modules/filesystem/physfs/File.h @@ -46,23 +46,26 @@ public: * Constructs an File with the given ilename. * @param filename The relative filepath of the file to load. **/ - File(const std::string &filename); + File(const std::string &filename, Mode mode); virtual ~File(); + // Implements Stream. + File *clone() override; + int64 read(void* dst, int64 size) override; + bool write(const void* data, int64 size) override; + bool flush() override; + int64 getSize() override; + bool seek(int64 pos, SeekOrigin origin) override; + int64 tell() override; + // Implements love::filesystem::File. using love::filesystem::File::read; using love::filesystem::File::write; bool open(Mode mode) override; bool close() override; bool isOpen() const override; - int64 getSize() override; - virtual int64 read(void *dst, int64 size) override; - bool write(const void *data, int64 size) override; - bool flush() override; bool isEOF() override; - int64 tell() override; - bool seek(uint64 pos) override; bool setBuffer(BufferMode bufmode, int64 size) override; BufferMode getBuffer(int64 &size) const override; Mode getMode() const override; @@ -70,6 +73,8 @@ public: private: + File(const File &other); + // filename std::string filename; diff --git a/src/modules/filesystem/physfs/Filesystem.cpp b/src/modules/filesystem/physfs/Filesystem.cpp index a56b0d527..4a2752d5e 100644 --- a/src/modules/filesystem/physfs/Filesystem.cpp +++ b/src/modules/filesystem/physfs/Filesystem.cpp @@ -496,9 +496,9 @@ bool Filesystem::unmount(Data *data) return false; } -love::filesystem::File *Filesystem::newFile(const char *filename) const +love::filesystem::File *Filesystem::newFile(const char *filename, File::Mode mode) const { - return new File(filename); + return new File(filename, mode); } std::string Filesystem::getFullCommonPath(CommonPath path) @@ -793,19 +793,23 @@ bool Filesystem::remove(const char *file) FileData *Filesystem::read(const char *filename, int64 size) const { - File file(filename); - - file.open(File::MODE_READ); + File file(filename, File::MODE_READ); // close() is called in the File destructor. return file.read(size); } +FileData* Filesystem::read(const char* filename) const +{ + File file(filename, File::MODE_READ); + + // close() is called in the File destructor. + return file.read(); +} + void Filesystem::write(const char *filename, const void *data, int64 size) const { - File file(filename); - - file.open(File::MODE_WRITE); + File file(filename, File::MODE_WRITE); // close() is called in the File destructor. if (!file.write(data, size)) @@ -814,9 +818,7 @@ void Filesystem::write(const char *filename, const void *data, int64 size) const void Filesystem::append(const char *filename, const void *data, int64 size) const { - File file(filename); - - file.open(File::MODE_APPEND); + File file(filename, File::MODE_APPEND); // close() is called in the File destructor. if (!file.write(data, size)) diff --git a/src/modules/filesystem/physfs/Filesystem.h b/src/modules/filesystem/physfs/Filesystem.h index 1e7854029..9b31c5f9f 100644 --- a/src/modules/filesystem/physfs/Filesystem.h +++ b/src/modules/filesystem/physfs/Filesystem.h @@ -71,7 +71,7 @@ public: bool unmount(CommonPath path) override; bool unmountFullPath(const char *fullpath) override; - love::filesystem::File *newFile(const char *filename) const override; + love::filesystem::File *newFile(const char *filename, File::Mode mode = File::MODE_CLOSED) const override; std::string getFullCommonPath(CommonPath path) override; const char *getWorkingDirectory() override; @@ -88,7 +88,8 @@ public: bool remove(const char *file) override; - FileData *read(const char *filename, int64 size = File::ALL) const override; + FileData *read(const char *filename, int64 size) const override; + FileData *read(const char *filename) const override; void write(const char *filename, const void *data, int64 size) const override; void append(const char *filename, const void *data, int64 size) const override; diff --git a/src/modules/filesystem/wrap_File.cpp b/src/modules/filesystem/wrap_File.cpp index 6300ac183..4581010d8 100644 --- a/src/modules/filesystem/wrap_File.cpp +++ b/src/modules/filesystem/wrap_File.cpp @@ -121,10 +121,12 @@ int w_File_read(lua_State *L) startidx = 3; } - int64 size = (int64) luaL_optnumber(L, startidx, (lua_Number) File::ALL); + int64 size = (int64) luaL_optnumber(L, startidx, -1); try { + if (size < 0) + size = file->getSize(); d.set(file->read(size), Acquire::NORETAIN); } catch (love::Exception &e) diff --git a/src/modules/filesystem/wrap_Filesystem.cpp b/src/modules/filesystem/wrap_Filesystem.cpp index d7fd00e7f..fb239915d 100644 --- a/src/modules/filesystem/wrap_Filesystem.cpp +++ b/src/modules/filesystem/wrap_Filesystem.cpp @@ -343,6 +343,11 @@ Data *luax_getdata(lua_State *L, int idx) return data; } +bool luax_cangetfile(lua_State *L, int idx) +{ + return lua_isstring(L, idx) || luax_istype(L, idx, File::type); +} + bool luax_cangetfiledata(lua_State *L, int idx) { return lua_isstring(L, idx) || luax_istype(L, idx, File::type) || luax_istype(L, idx, FileData::type); @@ -557,12 +562,15 @@ int w_read(lua_State *L) } const char *filename = luaL_checkstring(L, startidx + 0); - int64 len = (int64) luaL_optinteger(L, startidx + 1, File::ALL); + int64 len = (int64) luaL_optinteger(L, startidx + 1, -1); FileData *data = nullptr; try { - data = instance()->read(filename, len); + if (len >= 0) + data = instance()->read(filename, len); + else + data = instance()->read(filename); } catch (love::Exception &e) { diff --git a/src/modules/filesystem/wrap_Filesystem.h b/src/modules/filesystem/wrap_Filesystem.h index 5474d3717..6d1667bf2 100644 --- a/src/modules/filesystem/wrap_Filesystem.h +++ b/src/modules/filesystem/wrap_Filesystem.h @@ -40,7 +40,9 @@ namespace filesystem **/ FileData *luax_getfiledata(lua_State *L, int idx); bool luax_cangetfiledata(lua_State *L, int idx); + File *luax_getfile(lua_State *L, int idx); +bool luax_cangetfile(lua_State *L, int idx); Data *luax_getdata(lua_State *L, int idx); bool luax_cangetdata(lua_State *L, int idx); From c09fef8e79c887aab96628f452bb142eebcddcf7 Mon Sep 17 00:00:00 2001 From: Alex Szpakowski Date: Thu, 21 Apr 2022 19:57:32 -0300 Subject: [PATCH 39/55] Audio source decoding improvements. - Streaming sources now also stream the file contents instead of loading it into memory, by default. Added a new stream type enum parameter to newSource ("file" or "memory"). - Audio file decoding now chooses the most appropriate decoder based on the file contents instead of the file extension. - Videos now stream audio from the file instead of loading all of the video file into memory for use with audio decoding. --- src/modules/audio/wrap_Audio.cpp | 41 +++-- src/modules/graphics/wrap_Graphics.lua | 2 +- src/modules/sound/Decoder.cpp | 14 +- src/modules/sound/Decoder.h | 19 ++- src/modules/sound/Sound.h | 6 +- .../sound/lullaby/CoreAudioDecoder.cpp | 99 +++--------- src/modules/sound/lullaby/CoreAudioDecoder.h | 22 ++- src/modules/sound/lullaby/FLACDecoder.cpp | 44 +++--- src/modules/sound/lullaby/FLACDecoder.h | 25 ++- src/modules/sound/lullaby/MP3Decoder.cpp | 46 +++--- src/modules/sound/lullaby/MP3Decoder.h | 21 ++- src/modules/sound/lullaby/ModPlugDecoder.cpp | 55 +++---- src/modules/sound/lullaby/ModPlugDecoder.h | 26 ++-- src/modules/sound/lullaby/Sound.cpp | 38 ++--- src/modules/sound/lullaby/Sound.h | 11 +- src/modules/sound/lullaby/VorbisDecoder.cpp | 142 ++++-------------- src/modules/sound/lullaby/VorbisDecoder.h | 42 ++---- src/modules/sound/lullaby/WaveDecoder.cpp | 53 ++----- src/modules/sound/lullaby/WaveDecoder.h | 33 ++-- src/modules/sound/wrap_Sound.cpp | 47 +++++- 20 files changed, 318 insertions(+), 468 deletions(-) diff --git a/src/modules/audio/wrap_Audio.cpp b/src/modules/audio/wrap_Audio.cpp index 6cfa2fac2..21c8429e3 100644 --- a/src/modules/audio/wrap_Audio.cpp +++ b/src/modules/audio/wrap_Audio.cpp @@ -20,6 +20,7 @@ // LOVE #include "wrap_Audio.h" +#include "filesystem/wrap_Filesystem.h" #include "openal/Audio.h" #include "null/Audio.h" @@ -57,8 +58,23 @@ int w_newSource(lua_State *L) return luaL_error(L, "Cannot create queueable sources using newSource. Use newQueueableSource instead."); } - if (lua_isstring(L, 1) || luax_istype(L, 1, love::filesystem::File::type) || luax_istype(L, 1, love::filesystem::FileData::type)) - luax_convobj(L, 1, "sound", "newDecoder"); + if (love::filesystem::luax_cangetdata(L, 1)) + { + // stream type + if (stype == Source::TYPE_STATIC) + lua_pushstring(L, "memory"); + else if (!lua_isnone(L, 3)) + lua_pushvalue(L, 3); + else + lua_pushnil(L); + + // buffer size + lua_pushnil(L); + + // (file, buffer size, stream type) + int idxs[] = { 1, lua_gettop(L), lua_gettop(L) - 1 }; + luax_convobj(L, idxs, 3, "sound", "newDecoder"); + } if (stype == Source::TYPE_STATIC && luax_istype(L, 1, love::sound::Decoder::type)) luax_convobj(L, 1, "sound", "newSoundData"); @@ -84,20 +100,17 @@ int w_newSource(lua_State *L) int w_newQueueableSource(lua_State *L) { + int samplerate = (int) luaL_checkinteger(L, 1); + int bitdepth = (int) luaL_checkinteger(L, 2); + int channels = (int) luaL_checkinteger(L, 3); + int buffers = (int) luaL_optinteger(L, 4, 0); + Source *t = nullptr; + luax_catchexcept(L, [&]() { t = instance()->newSource(samplerate, bitdepth, channels, buffers); }); - luax_catchexcept(L, [&]() { - t = instance()->newSource((int)luaL_checkinteger(L, 1), (int)luaL_checkinteger(L, 2), (int)luaL_checkinteger(L, 3), (int)luaL_optinteger(L, 4, 0)); - }); - - if (t != nullptr) - { - luax_pushtype(L, t); - t->release(); - return 1; - } - else - return 0; //all argument type errors are checked in above constructor + luax_pushtype(L, t); + t->release(); + return 1; } static std::vector readSourceList(lua_State *L, int n) diff --git a/src/modules/graphics/wrap_Graphics.lua b/src/modules/graphics/wrap_Graphics.lua index ea2b52579..8bb8a45f8 100644 --- a/src/modules/graphics/wrap_Graphics.lua +++ b/src/modules/graphics/wrap_Graphics.lua @@ -33,7 +33,7 @@ function love.graphics.newVideo(file, settings) local source, success if settings.audio ~= false and love.audio then - success, source = pcall(love.audio.newSource, video:getStream():getFilename(), "stream") + success, source = pcall(love.audio.newSource, video:getStream():getFilename(), "stream", "file") end if success then video:setSource(source) diff --git a/src/modules/sound/Decoder.cpp b/src/modules/sound/Decoder.cpp index 1182cb68a..355b783c9 100644 --- a/src/modules/sound/Decoder.cpp +++ b/src/modules/sound/Decoder.cpp @@ -29,13 +29,16 @@ namespace sound love::Type Decoder::type("Decoder", &Object::type); -Decoder::Decoder(Data *data, int bufferSize) - : data(data) +Decoder::Decoder(Stream *stream, int bufferSize) + : stream(stream) , bufferSize(bufferSize) , sampleRate(DEFAULT_SAMPLE_RATE) , buffer(0) , eof(false) { + if (!stream->isReadable() || !stream->isSeekable()) + throw love::Exception("Decoder input stream must be readable and seekable."); + buffer = new char[bufferSize]; } @@ -65,5 +68,12 @@ bool Decoder::isFinished() return eof; } +STRINGMAP_CLASS_BEGIN(Decoder, Decoder::StreamSource, Decoder::STREAM_MAX_ENUM, streamSource) +{ + { "memory", Decoder::STREAM_MEMORY }, + { "file", Decoder::STREAM_FILE }, +} +STRINGMAP_CLASS_END(Decoder, Decoder::StreamSource, Decoder::STREAM_MAX_ENUM, streamSource) + } // sound } // love diff --git a/src/modules/sound/Decoder.h b/src/modules/sound/Decoder.h index f3452567f..1c91be281 100644 --- a/src/modules/sound/Decoder.h +++ b/src/modules/sound/Decoder.h @@ -23,7 +23,8 @@ // LOVE #include "common/Object.h" -#include "filesystem/File.h" +#include "common/Stream.h" +#include "common/StringMap.h" #include @@ -39,9 +40,16 @@ class Decoder : public Object { public: + enum StreamSource + { + STREAM_MEMORY, + STREAM_FILE, + STREAM_MAX_ENUM + }; + static love::Type type; - Decoder(Data *data, int bufferSize); + Decoder(Stream *stream, int bufferSize); virtual ~Decoder(); /** @@ -143,11 +151,12 @@ public: **/ virtual double getDuration() = 0; + STRINGMAP_CLASS_DECLARE(StreamSource); + protected: - // The encoded data. This should be replaced with buffered file - // reads in the future. - StrongRef data; + // A readable stream containing the encoded data. + StrongRef stream; // When the decoder decodes data incrementally, it writes // this many bytes at a time (at most). diff --git a/src/modules/sound/Sound.h b/src/modules/sound/Sound.h index 30ec0cb9d..8cfe378a6 100644 --- a/src/modules/sound/Sound.h +++ b/src/modules/sound/Sound.h @@ -23,7 +23,7 @@ // LOVE #include "common/Module.h" -#include "filesystem/File.h" +#include "common/Stream.h" #include "SoundData.h" #include "Decoder.h" @@ -83,11 +83,11 @@ public: /** * Attempts to find a decoder for the encoded sound data in the * specified file. - * @param file The file with encoded sound data. + * @param stream The readable Stream with encoded sound data. * @param bufferSize The size of each decoded chunk. * @return A Decoder object on success, or zero if no decoder could be found. **/ - virtual Decoder *newDecoder(filesystem::FileData *file, int bufferSize) = 0; + virtual Decoder *newDecoder(Stream *stream, int bufferSize) = 0; }; // Sound diff --git a/src/modules/sound/lullaby/CoreAudioDecoder.cpp b/src/modules/sound/lullaby/CoreAudioDecoder.cpp index d1b117dd0..2365a2e91 100644 --- a/src/modules/sound/lullaby/CoreAudioDecoder.cpp +++ b/src/modules/sound/lullaby/CoreAudioDecoder.cpp @@ -34,37 +34,23 @@ namespace lullaby { // Callbacks -namespace +static OSStatus readFunc(void *inClientData, SInt64 inPosition, UInt32 requestCount, void *buffer, UInt32 *actualCount) { -OSStatus readFunc(void *inClientData, SInt64 inPosition, UInt32 requestCount, void *buffer, UInt32 *actualCount) -{ - Data *data = (Data *) inClientData; - SInt64 bytesLeft = data->getSize() - inPosition; + auto stream = (Stream *) inClientData; + int64 readbytes = stream->read(buffer, requestCount); - if (bytesLeft > 0) - { - UInt32 actualSize = bytesLeft >= requestCount ? requestCount : (UInt32) bytesLeft; - memcpy(buffer, (char *) data->getData() + inPosition, actualSize); - *actualCount = actualSize; - } - else - { - *actualCount = 0; - return kAudioFilePositionError; - } - - return noErr; + *actualCount = (UInt32) readbytes; + return readbytes > 0 ? noErr : kAudioFilePositionError; } -SInt64 getSizeFunc(void *inClientData) +static SInt64 getSizeFunc(void *inClientData) { - Data *data = (Data *) inClientData; - return data->getSize(); + auto stream = (Stream *) inClientData; + return stream->getSize(); } -} // anonymous namespace -CoreAudioDecoder::CoreAudioDecoder(Data *data, int bufferSize) - : Decoder(data, bufferSize) +CoreAudioDecoder::CoreAudioDecoder(Stream *stream, int bufferSize) + : Decoder(stream, bufferSize) , audioFile(nullptr) , extAudioFile(nullptr) , inputInfo() @@ -75,23 +61,23 @@ CoreAudioDecoder::CoreAudioDecoder(Data *data, int bufferSize) { OSStatus err = noErr; - // Open the file represented by the Data. - err = AudioFileOpenWithCallbacks(data, readFunc, nullptr, getSizeFunc, nullptr, kAudioFileMP3Type, &audioFile); + // Open the file represented by the Stream. + err = AudioFileOpenWithCallbacks(stream, readFunc, nullptr, getSizeFunc, nullptr, kAudioFileMP3Type, &audioFile); if (err != noErr) - throw love::Exception("Could open audio file for decoding."); + throw love::Exception("Could open audio file for decoding with CoreAudio."); // We want to use the Extended AudioFile API. err = ExtAudioFileWrapAudioFileID(audioFile, false, &extAudioFile); if (err != noErr) - throw love::Exception("Could open audio file for decoding."); + throw love::Exception("Could open audio file for decoding with CoreAudio."); // Get the format of the audio data. UInt32 propertySize = sizeof(inputInfo); err = ExtAudioFileGetProperty(extAudioFile, kExtAudioFileProperty_FileDataFormat, &propertySize, &inputInfo); if (err != noErr) - throw love::Exception("Could not determine file format."); + throw love::Exception("Could not determine CoreAudio file format."); // Set the output format to 16 bit signed integer (native-endian) data. // Keep the channel count and sample rate of the source format. @@ -116,7 +102,7 @@ CoreAudioDecoder::CoreAudioDecoder(Data *data, int bufferSize) err = ExtAudioFileSetProperty(extAudioFile, kExtAudioFileProperty_ClientDataFormat, propertySize, &outputInfo); if (err != noErr) - throw love::Exception("Could not set decoder properties."); + throw love::Exception("Could not set CoreAudio decoder properties."); } catch (love::Exception &) { @@ -143,59 +129,10 @@ void CoreAudioDecoder::closeAudioFile() audioFile = nullptr; } -bool CoreAudioDecoder::accepts(const std::string &ext) -{ - UInt32 size = 0; - std::vector types; - - // Get the size in bytes of the type array we're about to get. - OSStatus err = AudioFileGetGlobalInfoSize(kAudioFileGlobalInfo_ReadableTypes, sizeof(UInt32), nullptr, &size); - if (err != noErr) - return false; - - types.resize(size / sizeof(UInt32)); - - // Get an array of supported types. - err = AudioFileGetGlobalInfo(kAudioFileGlobalInfo_ReadableTypes, 0, nullptr, &size, &types[0]); - if (err != noErr) - return false; - - // Turn the extension string into a CFStringRef. - CFStringRef extstr = CFStringCreateWithCString(nullptr, ext.c_str(), kCFStringEncodingUTF8); - - CFArrayRef exts = nullptr; - size = sizeof(CFArrayRef); - - for (UInt32 type : types) - { - // Get the extension strings for the type. - err = AudioFileGetGlobalInfo(kAudioFileGlobalInfo_ExtensionsForType, sizeof(UInt32), &type, &size, &exts); - if (err != noErr) - continue; - - // A type can have more than one extension string. - for (CFIndex i = 0; i < CFArrayGetCount(exts); i++) - { - CFStringRef value = (CFStringRef) CFArrayGetValueAtIndex(exts, i); - - if (CFStringCompare(extstr, value, 0) == kCFCompareEqualTo) - { - CFRelease(extstr); - CFRelease(exts); - return true; - } - } - - CFRelease(exts); - } - - CFRelease(extstr); - return false; -} - love::sound::Decoder *CoreAudioDecoder::clone() { - return new CoreAudioDecoder(data.get(), bufferSize); + StrongRef s(stream->clone(), Acquire::NORETAIN); + return new CoreAudioDecoder(s, bufferSize); } int CoreAudioDecoder::decode() diff --git a/src/modules/sound/lullaby/CoreAudioDecoder.h b/src/modules/sound/lullaby/CoreAudioDecoder.h index d0380f136..9153dd2c4 100644 --- a/src/modules/sound/lullaby/CoreAudioDecoder.h +++ b/src/modules/sound/lullaby/CoreAudioDecoder.h @@ -26,7 +26,7 @@ #ifdef LOVE_SUPPORT_COREAUDIO // LOVE -#include "common/Data.h" +#include "common/Stream.h" #include "sound/Decoder.h" // Core Audio @@ -47,19 +47,17 @@ class CoreAudioDecoder : public Decoder { public: - CoreAudioDecoder(Data *data, int bufferSize); + CoreAudioDecoder(Stream *stream, int bufferSize); virtual ~CoreAudioDecoder(); - static bool accepts(const std::string &ext); - - love::sound::Decoder *clone(); - int decode(); - bool seek(double s); - bool rewind(); - bool isSeekable(); - int getChannelCount() const; - int getBitDepth() const; - double getDuration(); + love::sound::Decoder *clone() override; + int decode() override; + bool seek(double s) override; + bool rewind() override; + bool isSeekable() override; + int getChannelCount() const override; + int getBitDepth() const override; + double getDuration() override; private: diff --git a/src/modules/sound/lullaby/FLACDecoder.cpp b/src/modules/sound/lullaby/FLACDecoder.cpp index 71f4cf315..d45386490 100644 --- a/src/modules/sound/lullaby/FLACDecoder.cpp +++ b/src/modules/sound/lullaby/FLACDecoder.cpp @@ -22,6 +22,7 @@ #include "FLACDecoder.h" #include +#include #include "common/Exception.h" namespace love @@ -31,10 +32,24 @@ namespace sound namespace lullaby { -FLACDecoder::FLACDecoder(Data *data, int nbufferSize) -: Decoder(data, nbufferSize) +static size_t onRead(void *pUserData, void *pBufferOut, size_t bytesToRead) { - flac = drflac_open_memory(data->getData(), data->getSize(), nullptr); + auto stream = (Stream *) pUserData; + int64 read = stream->read(pBufferOut, bytesToRead); + return std::max(0, read); +} + +static drflac_bool32 onSeek(void* pUserData, int offset, drflac_seek_origin origin) +{ + auto stream = (Stream *) pUserData; + auto seekorigin = origin == drflac_seek_origin_current ? Stream::SEEKORIGIN_CURRENT : Stream::SEEKORIGIN_BEGIN; + return stream->seek(offset, seekorigin) ? DRFLAC_TRUE : DRFLAC_FALSE; +} + +FLACDecoder::FLACDecoder(Stream *stream, int nbufferSize) + : Decoder(stream, nbufferSize) +{ + flac = drflac_open(onRead, onSeek, stream, nullptr); if (flac == nullptr) throw love::Exception("Could not load FLAC file"); } @@ -44,29 +59,10 @@ FLACDecoder::~FLACDecoder() drflac_close(flac); } -bool FLACDecoder::accepts(const std::string &ext) -{ - // dr_flac supports FLAC encapsulated in Ogg, but unfortunately - // LOVE detects .ogg extension as Vorbis. It would be a good idea - // to always probe in the future (see #1487 and commit ccf9e63). - // Please remove once it's no longer the case. - static const std::string supported[] = - { - "flac", "ogg" - }; - - for (const auto& s : supported) - { - if (s.compare(ext) == 0) - return true; - } - - return false; -} - love::sound::Decoder *FLACDecoder::clone() { - return new FLACDecoder(data.get(), bufferSize); + StrongRef s(stream->clone(), Acquire::NORETAIN); + return new FLACDecoder(s, bufferSize); } int FLACDecoder::decode() diff --git a/src/modules/sound/lullaby/FLACDecoder.h b/src/modules/sound/lullaby/FLACDecoder.h index 4ddae3191..7e5fcdc30 100644 --- a/src/modules/sound/lullaby/FLACDecoder.h +++ b/src/modules/sound/lullaby/FLACDecoder.h @@ -22,7 +22,7 @@ #define LOVE_SOUND_LULLABY_FLAC_DECODER_H // LOVE -#include "common/Data.h" +#include "common/Stream.h" #include "sound/Decoder.h" #include "dr/dr_flac.h" @@ -38,23 +38,22 @@ namespace lullaby class FLACDecoder : public Decoder { public: - FLACDecoder(Data *data, int bufferSize); + FLACDecoder(Stream *stream, int bufferSize); ~FLACDecoder(); - static bool accepts(const std::string &ext); - love::sound::Decoder *clone(); - int decode(); - bool seek(double s); - bool rewind(); - bool isSeekable(); - int getChannelCount() const; - int getBitDepth() const; - int getSampleRate() const; - double getDuration(); + love::sound::Decoder *clone() override; + int decode() override; + bool seek(double s) override; + bool rewind() override; + bool isSeekable() override; + int getChannelCount() const override; + int getBitDepth() const override; + int getSampleRate() const override; + double getDuration() override; private: drflac *flac; -}; // Decoder +}; // FLACDecoder } // lullaby } // sound diff --git a/src/modules/sound/lullaby/MP3Decoder.cpp b/src/modules/sound/lullaby/MP3Decoder.cpp index 00c4740e1..2a1215639 100644 --- a/src/modules/sound/lullaby/MP3Decoder.cpp +++ b/src/modules/sound/lullaby/MP3Decoder.cpp @@ -21,6 +21,7 @@ #define DR_MP3_IMPLEMENTATION #define DR_MP3_NO_STDIO #include "MP3Decoder.h" +#include "common/Exception.h" namespace love { @@ -29,11 +30,25 @@ namespace sound namespace lullaby { -MP3Decoder::MP3Decoder(Data *data, int bufferSize) -: Decoder(data, bufferSize) +static size_t onRead(void *pUserData, void *pBufferOut, size_t bytesToRead) +{ + auto stream = (Stream *) pUserData; + int64 read = stream->read(pBufferOut, bytesToRead); + return std::max(0, read); +} + +static drmp3_bool32 onSeek(void *pUserData, int offset, drmp3_seek_origin origin) +{ + auto stream = (Stream *) pUserData; + auto seekorigin = origin == drmp3_seek_origin_current ? Stream::SEEKORIGIN_CURRENT : Stream::SEEKORIGIN_BEGIN; + return stream->seek(offset, seekorigin) ? DRMP3_TRUE : DRMP3_FALSE; +} + +MP3Decoder::MP3Decoder(Stream *stream, int bufferSize) + : Decoder(stream, bufferSize) { // initialize mp3 handle - if(drmp3_init_memory(&mp3, data->getData(), data->getSize(), nullptr, nullptr) == 0) + if (!drmp3_init(&mp3, onRead, onSeek, stream, nullptr, nullptr)) throw love::Exception("Could not read mp3 data."); sampleRate = mp3.sampleRate; @@ -43,7 +58,7 @@ MP3Decoder::MP3Decoder(Data *data, int bufferSize) if (!drmp3_get_mp3_and_pcm_frame_count(&mp3, &mp3FrameCount, &pcmCount)) { drmp3_uninit(&mp3); - throw love::Exception("Could not calculate duration."); + throw love::Exception("Could not calculate mp3 duration."); } duration = ((double) pcmCount) / ((double) mp3.sampleRate); @@ -53,7 +68,7 @@ MP3Decoder::MP3Decoder(Data *data, int bufferSize) if (!drmp3_calculate_seek_points(&mp3, &mp3FrameInt, seekTable.data())) { drmp3_uninit(&mp3); - throw love::Exception("Could not calculate seek table"); + throw love::Exception("Could not calculate mp3 seek table"); } mp3FrameInt = mp3FrameInt > mp3FrameCount ? mp3FrameCount : mp3FrameInt; @@ -61,7 +76,7 @@ MP3Decoder::MP3Decoder(Data *data, int bufferSize) if (!drmp3_bind_seek_table(&mp3, mp3FrameInt, seekTable.data())) { drmp3_uninit(&mp3); - throw love::Exception("Could not bind seek table"); + throw love::Exception("Could not bind mp3 seek table"); } } @@ -70,25 +85,10 @@ MP3Decoder::~MP3Decoder() drmp3_uninit(&mp3); } -bool MP3Decoder::accepts(const std::string &ext) -{ - static const std::string supported[] = - { - "mp3" - }; - - for (const auto& s : supported) - { - if (s.compare(ext) == 0) - return true; - } - - return false; -} - love::sound::Decoder *MP3Decoder::clone() { - return new MP3Decoder(data, bufferSize); + StrongRef s(stream->clone(), Acquire::NORETAIN); + return new MP3Decoder(s, bufferSize); } int MP3Decoder::decode() diff --git a/src/modules/sound/lullaby/MP3Decoder.h b/src/modules/sound/lullaby/MP3Decoder.h index 7a95564df..d8589210e 100644 --- a/src/modules/sound/lullaby/MP3Decoder.h +++ b/src/modules/sound/lullaby/MP3Decoder.h @@ -22,7 +22,7 @@ #define LOVE_SOUND_LULLABY_MP3_DECODER_H // LOVE -#include "common/Data.h" +#include "common/Stream.h" #include "sound/Decoder.h" // dr_mp3 @@ -41,18 +41,17 @@ class MP3Decoder: public love::sound::Decoder { public: - MP3Decoder(Data *data, int bufsize); + MP3Decoder(Stream *stream, int bufsize); virtual ~MP3Decoder(); - static bool accepts(const std::string &ext); - love::sound::Decoder *clone(); - int decode(); - bool seek(double s); - bool rewind(); - bool isSeekable(); - int getChannelCount() const; - int getBitDepth() const; - double getDuration(); + love::sound::Decoder *clone() override; + int decode() override; + bool seek(double s) override; + bool rewind() override; + bool isSeekable() override; + int getChannelCount() const override; + int getBitDepth() const override; + double getDuration() override; private: diff --git a/src/modules/sound/lullaby/ModPlugDecoder.cpp b/src/modules/sound/lullaby/ModPlugDecoder.cpp index f38df31b0..440aa44d8 100644 --- a/src/modules/sound/lullaby/ModPlugDecoder.cpp +++ b/src/modules/sound/lullaby/ModPlugDecoder.cpp @@ -23,6 +23,7 @@ #ifndef LOVE_NO_MODPLUG #include "common/Exception.h" +#include "common/Data.h" namespace love { @@ -31,12 +32,11 @@ namespace sound namespace lullaby { -ModPlugDecoder::ModPlugDecoder(Data *data, int bufferSize) - : Decoder(data, bufferSize) +ModPlugDecoder::ModPlugDecoder(Stream *stream, int bufferSize) + : Decoder(stream, bufferSize) , plug(0) , duration(-2.0) { - // Set some ModPlug settings. settings.mFlags = MODPLUG_ENABLE_OVERSAMPLING | MODPLUG_ENABLE_NOISE_REDUCTION; settings.mChannels = 2; @@ -60,10 +60,29 @@ ModPlugDecoder::ModPlugDecoder(Data *data, int bufferSize) ModPlug_SetSettings(&settings); - // Load the module. - plug = ModPlug_Load(data->getData(), (int) data->getSize()); + // ModPlug has no streaming API. Miserable. + // We don't want to load the entire stream immediately if it's big, because + // it might not be compatible with ModPlug. So we just try to load 4MB and + // see if that works, and then load the whole thing if it does. + if (stream->getSize() > 1024 * 1024 * 4) + { + data.set(stream->read(1024 * 1024 * 4), Acquire::NORETAIN); - if (plug == 0) + plug = ModPlug_Load(data->getData(), (int)data->getSize()); + + if (plug == nullptr) + throw love::Exception("Could not load file with ModPlug."); + + stream->seek(0); + ModPlug_Unload(plug); + } + + data.set(stream->read(stream->getSize()), Acquire::NORETAIN); + + // Load the module. + plug = ModPlug_Load(data->getData(), (int)data->getSize()); + + if (plug == nullptr) throw love::Exception("Could not load file with ModPlug."); // set master volume for delicate ears @@ -72,32 +91,14 @@ ModPlugDecoder::ModPlugDecoder(Data *data, int bufferSize) ModPlugDecoder::~ModPlugDecoder() { - if (plug != 0) + if (plug != nullptr) ModPlug_Unload(plug); } -bool ModPlugDecoder::accepts(const std::string &ext) -{ - static const std::string supported[] = - { - "699", "abc", "amf", "ams", "dbm", "dmf", - "dsm", "far", "it", "j2b", "mdl", "med", - "mid", "mod", "mt2", "mtm", "okt", "pat", - "psm", "s3m", "stm", "ult", "umx", "xm", - }; - - for (const auto &s : supported) - { - if (s.compare(ext) == 0) - return true; - } - - return false; -} - love::sound::Decoder *ModPlugDecoder::clone() { - return new ModPlugDecoder(data.get(), bufferSize); + StrongRef s(stream->clone(), Acquire::NORETAIN); + return new ModPlugDecoder(s, bufferSize); } int ModPlugDecoder::decode() diff --git a/src/modules/sound/lullaby/ModPlugDecoder.h b/src/modules/sound/lullaby/ModPlugDecoder.h index ec45d47b3..b99a2d0e6 100644 --- a/src/modules/sound/lullaby/ModPlugDecoder.h +++ b/src/modules/sound/lullaby/ModPlugDecoder.h @@ -26,7 +26,7 @@ #ifndef LOVE_NO_MODPLUG // LOVE -#include "common/Data.h" +#include "common/Stream.h" #include "sound/Decoder.h" // libmodplug @@ -47,28 +47,28 @@ class ModPlugDecoder : public Decoder { public: - ModPlugDecoder(Data *data, int bufferSize); + ModPlugDecoder(Stream *stream, int bufferSize); virtual ~ModPlugDecoder(); - static bool accepts(const std::string &ext); - - love::sound::Decoder *clone(); - int decode(); - bool seek(double s); - bool rewind(); - bool isSeekable(); - int getChannelCount() const; - int getBitDepth() const; - double getDuration(); + love::sound::Decoder *clone() override; + int decode() override; + bool seek(double s) override; + bool rewind() override; + bool isSeekable() override; + int getChannelCount() const override; + int getBitDepth() const override; + double getDuration() override; private: + StrongRef data; + ModPlugFile *plug; ModPlug_Settings settings; double duration; -}; // Decoder +}; // ModPlugDecoder } // lullaby } // sound diff --git a/src/modules/sound/lullaby/Sound.cpp b/src/modules/sound/lullaby/Sound.cpp index c20c90f99..63fe642d6 100644 --- a/src/modules/sound/lullaby/Sound.cpp +++ b/src/modules/sound/lullaby/Sound.cpp @@ -37,21 +37,16 @@ struct DecoderImpl { - love::sound::Decoder *(*create)(love::filesystem::FileData *data, int bufferSize); - bool (*accepts)(const std::string& ext); + love::sound::Decoder *(*create)(love::Stream *stream, int bufferSize); }; template DecoderImpl DecoderImplFor() { DecoderImpl decoderImpl; - decoderImpl.create = [](love::filesystem::FileData *data, int bufferSize) -> love::sound::Decoder* + decoderImpl.create = [](love::Stream *stream, int bufferSize) -> love::sound::Decoder* { - return new DecoderType(data, bufferSize); - }; - decoderImpl.accepts = [](const std::string& ext) -> bool - { - return DecoderType::accepts(ext); + return new DecoderType(stream, bufferSize); }; return decoderImpl; } @@ -76,15 +71,9 @@ const char *Sound::getName() const return "love.sound.lullaby"; } -sound::Decoder *Sound::newDecoder(love::filesystem::FileData *data, int bufferSize) +sound::Decoder *Sound::newDecoder(Stream *stream, int bufferSize) { - std::string ext = data->getExtension(); - std::transform(ext.begin(), ext.end(), ext.begin(), tolower); - std::vector possibleDecoders = { -#ifndef LOVE_NO_MODPLUG - DecoderImplFor(), -#endif // LOVE_NO_MODPLUG DecoderImplFor(), DecoderImplFor(), #ifdef LOVE_SUPPORT_COREAUDIO @@ -92,24 +81,19 @@ sound::Decoder *Sound::newDecoder(love::filesystem::FileData *data, int bufferSi #endif DecoderImplFor(), DecoderImplFor(), - // DecoderImplFor(), +#ifndef LOVE_NO_MODPLUG + DecoderImplFor(), // Last because it doesn't work well with Streams. +#endif }; - // First find a matching decoder based on extension - for (DecoderImpl &possibleDecoder : possibleDecoders) - { - if (possibleDecoder.accepts(ext)) - return possibleDecoder.create(data, bufferSize); - } - - // If that fails, start probing instead std::stringstream decodingErrors; decodingErrors << "Failed to determine file type:\n"; for (DecoderImpl &possibleDecoder : possibleDecoders) { try { - sound::Decoder *decoder = possibleDecoder.create(data, bufferSize); + stream->seek(0); + sound::Decoder *decoder = possibleDecoder.create(stream, bufferSize); return decoder; } catch (love::Exception &e) @@ -118,8 +102,8 @@ sound::Decoder *Sound::newDecoder(love::filesystem::FileData *data, int bufferSi } } - // Probing failed too, bail with the accumulated errors - throw love::Exception(decodingErrors.str().c_str()); + std::string errors = decodingErrors.str(); + throw love::Exception("No suitable audio decoders found.\n%s", errors.c_str()); // Unreachable, but here to prevent (possible) warnings return nullptr; diff --git a/src/modules/sound/lullaby/Sound.h b/src/modules/sound/lullaby/Sound.h index a79392b84..028740020 100644 --- a/src/modules/sound/lullaby/Sound.h +++ b/src/modules/sound/lullaby/Sound.h @@ -45,21 +45,14 @@ class Sound : public love::sound::Sound { public: - /** - * Constructor. Initializes relevant libraries. - **/ Sound(); - - /** - * Destructor. Deinitializes relevant libraries. - **/ virtual ~Sound(); /// @copydoc love::Module::getName - const char *getName() const; + const char *getName() const override; /// @copydoc love::sound::Sound::newDecoder - sound::Decoder *newDecoder(love::filesystem::FileData *file, int bufferSize); + sound::Decoder *newDecoder(Stream *stream, int bufferSize) override; }; // Sound diff --git a/src/modules/sound/lullaby/VorbisDecoder.cpp b/src/modules/sound/lullaby/VorbisDecoder.cpp index b9de52347..6aeac42b9 100644 --- a/src/modules/sound/lullaby/VorbisDecoder.cpp +++ b/src/modules/sound/lullaby/VorbisDecoder.cpp @@ -31,132 +31,65 @@ namespace sound namespace lullaby { -/** - * CALLBACK FUNCTIONS - **/ -static int vorbisClose(void * /* ptr to the data that the vorbis files need*/) +static int vorbisClose(void *) { // Does nothing (handled elsewhere) return 1; } -static size_t vorbisRead(void *ptr /* ptr to the data that the vorbis files need*/, - size_t byteSize /* how big a byte is*/, - size_t sizeToRead /* How much we can read*/, - void *datasource /* this is a pointer to the data we passed into ov_open_callbacks (our SOggFile struct*/) +static size_t vorbisRead(void *ptr, size_t byteSize, size_t sizeToRead, void *datasource) { - size_t spaceToEOF; // How much more we can read till we hit the EOF marker - size_t actualSizeToRead; // How much data we are actually going to read from memory - SOggFile *vorbisData; // Our vorbis data, for the typecast - - // Get the data in the right format - vorbisData = (SOggFile *)datasource; - - // Calculate how much we need to read. This can be sizeToRead*byteSize or less depending on how near the EOF marker we are - spaceToEOF = vorbisData->dataSize - vorbisData->dataRead; - if ((sizeToRead*byteSize) < spaceToEOF) - actualSizeToRead = (sizeToRead*byteSize); - else - actualSizeToRead = spaceToEOF; - - // A simple copy of the data from memory to the datastruct that the vorbis libs will use - if (actualSizeToRead) - { - // Copy the data from the start of the file PLUS how much we have already read in - memcpy(ptr, (const char *)vorbisData->dataPtr + vorbisData->dataRead, actualSizeToRead); - // Increase by how much we have read by - vorbisData->dataRead += (actualSizeToRead); - } - - // Return how much we read (in the same way fread would) - return actualSizeToRead; + auto stream = (Stream *) datasource; + return stream->read(ptr, byteSize * sizeToRead); } -static int vorbisSeek(void *datasource /* ptr to the data that the vorbis files need*/, - ogg_int64_t offset /*offset from the point we wish to seek to*/, - int whence /*where we want to seek to*/) +static int vorbisSeek(void *datasource, ogg_int64_t offset, int whence) { - int64 spaceToEOF; // How much more we can read till we hit the EOF marker - int64 actualOffset; // How much we can actually offset it by - SOggFile *vorbisData; // The data we passed in (for the typecast) + auto stream = (Stream *) datasource; + auto origin = Stream::SEEKORIGIN_BEGIN; - // Get the data in the right format - vorbisData = (SOggFile *) datasource; - - // Goto where we wish to seek to switch (whence) { - case SEEK_SET: // Seek to the start of the data file - // Make sure we are not going to the end of the file - if (vorbisData->dataSize >= offset) - actualOffset = offset; - else - actualOffset = vorbisData->dataSize; - // Set where we now are - vorbisData->dataRead = (int)actualOffset; + case SEEK_SET: + origin = Stream::SEEKORIGIN_BEGIN; break; - case SEEK_CUR: // Seek from where we are - // Make sure we dont go past the end - spaceToEOF = vorbisData->dataSize - vorbisData->dataRead; - if (offset < spaceToEOF) - actualOffset = (offset); - else - actualOffset = spaceToEOF; - // Seek from our currrent location - vorbisData->dataRead += actualOffset; + case SEEK_CUR: + origin = Stream::SEEKORIGIN_CURRENT; break; - case SEEK_END: // Seek from the end of the file - if (offset < 0) - vorbisData->dataRead = vorbisData->dataSize + offset; - else - vorbisData->dataRead = vorbisData->dataSize; + case SEEK_END: + origin = Stream::SEEKORIGIN_END; break; default: break; }; - return 0; + return stream->seek(offset, origin) ? 0 : -1; } -static long vorbisTell(void *datasource /* ptr to the data that the vorbis files need*/) +static long vorbisTell(void *datasource) { - SOggFile *vorbisData; - vorbisData = (SOggFile *) datasource; - return vorbisData->dataRead; + auto stream = (Stream *) datasource; + return (long) stream->tell(); } /** * END CALLBACK FUNCTIONS **/ -VorbisDecoder::VorbisDecoder(Data *data, int bufferSize) - : Decoder(data, bufferSize) +VorbisDecoder::VorbisDecoder(Stream *stream, int bufferSize) + : Decoder(stream, bufferSize) , duration(-2.0) { - // Initialize callbacks - vorbisCallbacks.close_func = vorbisClose; - vorbisCallbacks.seek_func = vorbisSeek; - vorbisCallbacks.read_func = vorbisRead; - vorbisCallbacks.tell_func = vorbisTell; - - // Check endianness -#ifdef LOVE_BIG_ENDIAN - endian = 1; -#else - endian = 0; -#endif - - // Initialize OGG file - oggFile.dataPtr = (const char *) data->getData(); - oggFile.dataSize = data->getSize(); - oggFile.dataRead = 0; + ov_callbacks callbacks = {}; + callbacks.close_func = vorbisClose; + callbacks.seek_func = vorbisSeek; + callbacks.read_func = vorbisRead; + callbacks.tell_func = vorbisTell; // Open Vorbis handle - if (ov_open_callbacks(&oggFile, &handle, NULL, 0, vorbisCallbacks) < 0) + if (ov_open_callbacks(stream, &handle, nullptr, 0, callbacks) < 0) throw love::Exception("Could not read Ogg bitstream"); - // Get info and comments vorbisInfo = ov_info(&handle, -1); - vorbisComment = ov_comment(&handle, -1); } VorbisDecoder::~VorbisDecoder() @@ -164,31 +97,22 @@ VorbisDecoder::~VorbisDecoder() ov_clear(&handle); } -bool VorbisDecoder::accepts(const std::string &ext) -{ - static const std::string supported[] = - { - "ogg", "oga", "ogv" - }; - - for (const auto& s : supported) - { - if (s.compare(ext) == 0) - return true; - } - - return false; -} - love::sound::Decoder *VorbisDecoder::clone() { - return new VorbisDecoder(data.get(), bufferSize); + StrongRef s(stream->clone(), Acquire::NORETAIN); + return new VorbisDecoder(s, bufferSize); } int VorbisDecoder::decode() { int size = 0; +#ifdef LOVE_BIG_ENDIAN + int endian = 1; +#else + int endian = 0; +#endif + while (size < bufferSize) { long result = ov_read(&handle, (char *) buffer + size, bufferSize - size, endian, (getBitDepth() == 16 ? 2 : 1), 1, 0); diff --git a/src/modules/sound/lullaby/VorbisDecoder.h b/src/modules/sound/lullaby/VorbisDecoder.h index dd58097ce..88f21e489 100644 --- a/src/modules/sound/lullaby/VorbisDecoder.h +++ b/src/modules/sound/lullaby/VorbisDecoder.h @@ -22,7 +22,7 @@ #define LOVE_SOUND_LULLABY_VORBIS_DECODER_H // LOVE -#include "common/Data.h" +#include "common/Stream.h" #include "common/int.h" #include "sound/Decoder.h" @@ -38,41 +38,29 @@ namespace sound namespace lullaby { -// Struct for handling data -struct SOggFile -{ - const char *dataPtr; // Pointer to the data in memory - int64 dataSize; // Size of the data - int64 dataRead; // How much we've read so far -}; - class VorbisDecoder : public Decoder { public: - VorbisDecoder(Data *data, int bufferSize); + VorbisDecoder(Stream *stream, int bufferSize); virtual ~VorbisDecoder(); - static bool accepts(const std::string &ext); - - love::sound::Decoder *clone(); - int decode(); - bool seek(double s); - bool rewind(); - bool isSeekable(); - int getChannelCount() const; - int getBitDepth() const; - int getSampleRate() const; - double getDuration(); + love::sound::Decoder *clone() override; + int decode() override; + bool seek(double s) override; + bool rewind() override; + bool isSeekable() override; + int getChannelCount() const override; + int getBitDepth() const override; + int getSampleRate() const override; + double getDuration() override; private: - SOggFile oggFile; // (see struct) - ov_callbacks vorbisCallbacks; // Callbacks used to read the file from mem - OggVorbis_File handle; // Handle to the file - vorbis_info *vorbisInfo; // Info - vorbis_comment *vorbisComment; // Comments - int endian; // Endianness + + OggVorbis_File handle; + vorbis_info *vorbisInfo; double duration; + }; // VorbisDecoder } // lullaby diff --git a/src/modules/sound/lullaby/WaveDecoder.cpp b/src/modules/sound/lullaby/WaveDecoder.cpp index 95dbfa2b4..012e4d42d 100644 --- a/src/modules/sound/lullaby/WaveDecoder.cpp +++ b/src/modules/sound/lullaby/WaveDecoder.cpp @@ -34,40 +34,32 @@ namespace lullaby // Callbacks static wuff_sint32 read_callback(void *userdata, wuff_uint8 *buffer, size_t *size) { - WaveFile *input = (WaveFile *) userdata; - size_t bytes_left = input->size - input->offset; - size_t target_size = *size < bytes_left ? *size : bytes_left; - memcpy(buffer, input->data + input->offset, target_size); - input->offset += target_size; - *size = target_size; + auto stream = (Stream *) userdata; + size_t readsize = stream->read(buffer, *size); + *size = readsize; return WUFF_SUCCESS; } static wuff_sint32 seek_callback(void *userdata, wuff_uint64 offset) { - WaveFile *input = (WaveFile *)userdata; - input->offset = (size_t) (offset < input->size ? offset : input->size); + auto stream = (Stream *) userdata; + stream->seek(offset, Stream::SEEKORIGIN_BEGIN); return WUFF_SUCCESS; } static wuff_sint32 tell_callback(void *userdata, wuff_uint64 *offset) { - WaveFile *input = (WaveFile *)userdata; - *offset = input->offset; + auto stream = (Stream *) userdata; + *offset = stream->tell(); return WUFF_SUCCESS; } -wuff_callback WaveDecoderCallbacks = {read_callback, seek_callback, tell_callback}; +static wuff_callback WaveDecoderCallbacks = {read_callback, seek_callback, tell_callback}; - -WaveDecoder::WaveDecoder(Data *data, int bufferSize) - : Decoder(data, bufferSize) +WaveDecoder::WaveDecoder(Stream *stream, int bufferSize) + : Decoder(stream, bufferSize) { - dataFile.data = (char *) data->getData(); - dataFile.size = data->getSize(); - dataFile.offset = 0; - - int wuff_status = wuff_open(&handle, &WaveDecoderCallbacks, &dataFile); + int wuff_status = wuff_open(&handle, &WaveDecoderCallbacks, stream); if (wuff_status < 0) throw love::Exception("Could not open WAVE"); @@ -78,13 +70,13 @@ WaveDecoder::WaveDecoder(Data *data, int bufferSize) throw love::Exception("Could not retrieve WAVE stream info"); if (info.channels > 2) - throw love::Exception("Multichannel audio not supported"); + throw love::Exception("WAVE Multichannel audio not supported"); if (info.format != WUFF_FORMAT_PCM_U8 && info.format != WUFF_FORMAT_PCM_S16) { wuff_status = wuff_format(handle, WUFF_FORMAT_PCM_S16); if (wuff_status < 0) - throw love::Exception("Could not set output format"); + throw love::Exception("Could not set WAVE output format"); } } catch (love::Exception &) @@ -99,25 +91,10 @@ WaveDecoder::~WaveDecoder() wuff_close(handle); } -bool WaveDecoder::accepts(const std::string &ext) -{ - static const std::string supported[] = - { - "wav" - }; - - for (const auto& s : supported) - { - if (s.compare(ext) == 0) - return true; - } - - return false; -} - love::sound::Decoder *WaveDecoder::clone() { - return new WaveDecoder(data.get(), bufferSize); + StrongRef s(stream->clone(), Acquire::NORETAIN); + return new WaveDecoder(s, bufferSize); } int WaveDecoder::decode() diff --git a/src/modules/sound/lullaby/WaveDecoder.h b/src/modules/sound/lullaby/WaveDecoder.h index 90832214a..9f7ddbeb4 100644 --- a/src/modules/sound/lullaby/WaveDecoder.h +++ b/src/modules/sound/lullaby/WaveDecoder.h @@ -22,7 +22,7 @@ #define LOVE_SOUND_LULLABY_WAVE_DECODER_H // LOVE -#include "common/Data.h" +#include "common/Stream.h" #include "sound/Decoder.h" #include "libraries/Wuff/wuff.h" @@ -34,36 +34,25 @@ namespace sound namespace lullaby { -// Struct for handling data -struct WaveFile -{ - char *data; - size_t size; - size_t offset; -}; - class WaveDecoder : public Decoder { public: - WaveDecoder(Data *data, int bufferSize); + WaveDecoder(Stream *stream, int bufferSize); virtual ~WaveDecoder(); - static bool accepts(const std::string &ext); - - love::sound::Decoder *clone(); - int decode(); - bool seek(double s); - bool rewind(); - bool isSeekable(); - int getChannelCount() const; - int getBitDepth() const; - int getSampleRate() const; - double getDuration(); + love::sound::Decoder *clone() override; + int decode() override; + bool seek(double s) override; + bool rewind() override; + bool isSeekable() override; + int getChannelCount() const override; + int getBitDepth() const override; + int getSampleRate() const override; + double getDuration() override; private: - WaveFile dataFile; wuff_handle *handle; wuff_info info; diff --git a/src/modules/sound/wrap_Sound.cpp b/src/modules/sound/wrap_Sound.cpp index 8c229c9a3..364374880 100644 --- a/src/modules/sound/wrap_Sound.cpp +++ b/src/modules/sound/wrap_Sound.cpp @@ -21,6 +21,7 @@ #include "wrap_Sound.h" #include "filesystem/wrap_Filesystem.h" +#include "data/DataStream.h" // Implementations. #include "lullaby/Sound.h" @@ -34,18 +35,50 @@ namespace sound int w_newDecoder(lua_State *L) { - love::filesystem::FileData *data = love::filesystem::luax_getfiledata(L, 1); - int bufferSize = (int) luaL_optinteger(L, 2, Decoder::DEFAULT_BUFFER_SIZE); + int bufferSize = (int)luaL_optinteger(L, 2, Decoder::DEFAULT_BUFFER_SIZE); + love::Stream *stream = nullptr; + + if (love::filesystem::luax_cangetfile(L, 1)) + { + Decoder::StreamSource source = Decoder::STREAM_FILE; + + const char* sourcestr = lua_isnoneornil(L, 3) ? nullptr : luaL_checkstring(L, 3); + if (sourcestr != nullptr && !Decoder::getConstant(sourcestr, source)) + return luax_enumerror(L, "stream type", Decoder::getConstants(source), sourcestr); + + if (source == Decoder::STREAM_FILE) + { + auto file = love::filesystem::luax_getfile(L, 1); + luax_catchexcept(L, [&]() { file->open(love::filesystem::File::MODE_READ); }); + stream = file; + } + else + { + luax_catchexcept(L, [&]() + { + StrongRef data(love::filesystem::luax_getfiledata(L, 1), Acquire::NORETAIN); + stream = new data::DataStream(data); + }); + } + + } + else if (luax_istype(L, 1, Data::type)) + { + Data *data = luax_checktype(L, 1); + luax_catchexcept(L, [&]() { stream = new data::DataStream(data); }); + } + else + { + stream = luax_checktype(L, 1); + stream->retain(); + } Decoder *t = nullptr; luax_catchexcept(L, - [&]() { t = instance()->newDecoder(data, bufferSize); }, - [&](bool) { data->release(); } + [&]() { t = instance()->newDecoder(stream, bufferSize); }, + [&](bool) { stream->release(); } ); - if (t == nullptr) - return luaL_error(L, "Extension \"%s\" not supported.", data->getExtension().c_str()); - luax_pushtype(L, t); t->release(); return 1; From ce18340312531ffcec3f5cc6cbb8d8f0ab28e92d Mon Sep 17 00:00:00 2001 From: Alex Szpakowski Date: Thu, 21 Apr 2022 20:02:48 -0300 Subject: [PATCH 40/55] Update xcode project with new files. --- 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 c00d8c7cd..a4b8df827 100644 --- a/platform/xcode/liblove.xcodeproj/project.pbxproj +++ b/platform/xcode/liblove.xcodeproj/project.pbxproj @@ -823,6 +823,9 @@ FA6A2B7A1F60B8250074C308 /* wrap_ByteData.cpp in Sources */ = {isa = PBXBuildFile; fileRef = FA6A2B781F60B8250074C308 /* wrap_ByteData.cpp */; }; FA6A2B7B1F60B8250074C308 /* wrap_ByteData.cpp in Sources */ = {isa = PBXBuildFile; fileRef = FA6A2B781F60B8250074C308 /* wrap_ByteData.cpp */; }; FA6BDE5C1F31725300786805 /* Color.h in Headers */ = {isa = PBXBuildFile; fileRef = FA6BDE5B1F31725300786805 /* Color.h */; }; + FA6BDF8E281219E900240F2A /* DataStream.cpp in Sources */ = {isa = PBXBuildFile; fileRef = FA6BDF8C281219E900240F2A /* DataStream.cpp */; }; + FA6BDF8F281219E900240F2A /* DataStream.cpp in Sources */ = {isa = PBXBuildFile; fileRef = FA6BDF8C281219E900240F2A /* DataStream.cpp */; }; + FA6BDF90281219E900240F2A /* DataStream.h in Headers */ = {isa = PBXBuildFile; fileRef = FA6BDF8D281219E900240F2A /* DataStream.h */; }; FA76344A1E28722A0066EF9E /* StreamBuffer.cpp in Sources */ = {isa = PBXBuildFile; fileRef = FA7634481E28722A0066EF9E /* StreamBuffer.cpp */; }; FA76344B1E28722A0066EF9E /* StreamBuffer.cpp in Sources */ = {isa = PBXBuildFile; fileRef = FA7634481E28722A0066EF9E /* StreamBuffer.cpp */; }; FA76344C1E28722A0066EF9E /* StreamBuffer.h in Headers */ = {isa = PBXBuildFile; fileRef = FA7634491E28722A0066EF9E /* StreamBuffer.h */; }; @@ -1904,6 +1907,8 @@ FA6A2B771F60B8250074C308 /* wrap_ByteData.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = wrap_ByteData.h; sourceTree = ""; }; FA6A2B781F60B8250074C308 /* wrap_ByteData.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = wrap_ByteData.cpp; sourceTree = ""; }; FA6BDE5B1F31725300786805 /* Color.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Color.h; sourceTree = ""; }; + FA6BDF8C281219E900240F2A /* DataStream.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = DataStream.cpp; sourceTree = ""; }; + FA6BDF8D281219E900240F2A /* DataStream.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DataStream.h; sourceTree = ""; }; FA7634481E28722A0066EF9E /* StreamBuffer.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = StreamBuffer.cpp; sourceTree = ""; }; FA7634491E28722A0066EF9E /* StreamBuffer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = StreamBuffer.h; sourceTree = ""; }; FA7DA04C1C16874A0056B200 /* wrap_Math.lua */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = wrap_Math.lua; sourceTree = ""; }; @@ -3755,6 +3760,8 @@ FACA02E31F5E396B0084B28F /* Compressor.h */, FACA02E41F5E396B0084B28F /* DataModule.cpp */, FACA02E51F5E396B0084B28F /* DataModule.h */, + FA6BDF8C281219E900240F2A /* DataStream.cpp */, + FA6BDF8D281219E900240F2A /* DataStream.h */, FA6A2B681F5F7F560074C308 /* DataView.cpp */, FA6A2B691F5F7F560074C308 /* DataView.h */, FACA02E61F5E396B0084B28F /* HashFunction.cpp */, @@ -4362,6 +4369,7 @@ 217DFC121D9F6D490055D849 /* usocket.h in Headers */, 217DFC081D9F6D490055D849 /* udp.h in Headers */, FA0B7DCF1A95902C000E1D17 /* wrap_Keyboard.h in Headers */, + FA6BDF90281219E900240F2A /* DataStream.h in Headers */, FAF6C9DB23C2DE2900D7B5BC /* SpvBuilder.h in Headers */, FA0B7EA21A95902C000E1D17 /* Sound.h in Headers */, FA0B7B331A958EA3000E1D17 /* wuff_config.h in Headers */, @@ -4693,6 +4701,7 @@ FAE64A922071364B00BC7981 /* physfs_platform_winrt.cpp in Sources */, FABDA97A2552448200B5C523 /* b2_joint.cpp in Sources */, FA4F2C0F1DE936FE00CA37D7 /* timeout.c in Sources */, + FA6BDF8F281219E900240F2A /* DataStream.cpp in Sources */, FA59A2D31C06481400328DBA /* ParticleSystem.cpp in Sources */, FA0B7E131A95902C000E1D17 /* GearJoint.cpp in Sources */, FABDA99B2552448300B5C523 /* b2_polygon_contact.cpp in Sources */, @@ -5112,6 +5121,7 @@ FABDA9932552448300B5C523 /* b2_polygon_circle_contact.cpp in Sources */, FAC7CD851FE35E95006A60C7 /* physfs_unicode.c in Sources */, FA6A2B7A1F60B8250074C308 /* wrap_ByteData.cpp in Sources */, + FA6BDF8E281219E900240F2A /* DataStream.cpp in Sources */, FAF140551E20934C00F898D2 /* Link.cpp in Sources */, FABDA9792552448200B5C523 /* b2_joint.cpp in Sources */, FAF140841E20934C00F898D2 /* ParseHelper.cpp in Sources */, From e167a7799401dcb08a9527fdd0114ce57b2b549b Mon Sep 17 00:00:00 2001 From: Alex Szpakowski Date: Fri, 22 Apr 2022 19:38:17 -0300 Subject: [PATCH 41/55] Fix a couple error message typos --- src/modules/sound/lullaby/CoreAudioDecoder.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/modules/sound/lullaby/CoreAudioDecoder.cpp b/src/modules/sound/lullaby/CoreAudioDecoder.cpp index 2365a2e91..499ecc43e 100644 --- a/src/modules/sound/lullaby/CoreAudioDecoder.cpp +++ b/src/modules/sound/lullaby/CoreAudioDecoder.cpp @@ -64,13 +64,13 @@ CoreAudioDecoder::CoreAudioDecoder(Stream *stream, int bufferSize) // Open the file represented by the Stream. err = AudioFileOpenWithCallbacks(stream, readFunc, nullptr, getSizeFunc, nullptr, kAudioFileMP3Type, &audioFile); if (err != noErr) - throw love::Exception("Could open audio file for decoding with CoreAudio."); + throw love::Exception("Could not open audio file for decoding with CoreAudio."); // We want to use the Extended AudioFile API. err = ExtAudioFileWrapAudioFileID(audioFile, false, &extAudioFile); if (err != noErr) - throw love::Exception("Could open audio file for decoding with CoreAudio."); + throw love::Exception("Could not open audio file for decoding with CoreAudio."); // Get the format of the audio data. UInt32 propertySize = sizeof(inputInfo); From b8ff64e9e35b1f7cd7467828d9a0f56612d21b85 Mon Sep 17 00:00:00 2001 From: Alex Szpakowski Date: Fri, 22 Apr 2022 21:05:20 -0300 Subject: [PATCH 42/55] Add love.filesystem.openFile. Deprecate love.filesystem.newFile. --- src/modules/filesystem/Filesystem.h | 4 +- src/modules/filesystem/NativeFile.cpp | 18 +++-- src/modules/filesystem/physfs/File.cpp | 6 +- src/modules/filesystem/physfs/Filesystem.cpp | 2 +- src/modules/filesystem/physfs/Filesystem.h | 2 +- src/modules/filesystem/wrap_Filesystem.cpp | 81 ++++++++++++-------- 6 files changed, 69 insertions(+), 44 deletions(-) diff --git a/src/modules/filesystem/Filesystem.h b/src/modules/filesystem/Filesystem.h index 53ca5cdbb..daa735f40 100644 --- a/src/modules/filesystem/Filesystem.h +++ b/src/modules/filesystem/Filesystem.h @@ -165,9 +165,9 @@ public: virtual bool unmountFullPath(const char *fullpath) = 0; /** - * Creates a new file. + * Opens a new File object from the specified path, using the given mode. **/ - virtual File *newFile(const char *filename, File::Mode = File::MODE_CLOSED) const = 0; + virtual File *openFile(const char *filename, File::Mode mode) const = 0; /** * Creates a new FileData object. Data will be copied. diff --git a/src/modules/filesystem/NativeFile.cpp b/src/modules/filesystem/NativeFile.cpp index 0fd5d0f1d..58753cfcf 100644 --- a/src/modules/filesystem/NativeFile.cpp +++ b/src/modules/filesystem/NativeFile.cpp @@ -46,13 +46,8 @@ NativeFile::NativeFile(const std::string &filename, Mode mode) , bufferMode(BUFFER_NONE) , bufferSize(0) { - open(mode); -} - -NativeFile::~NativeFile() -{ - if (mode != MODE_CLOSED) - close(); + if (!open(mode)) + throw love::Exception("Could not open file at path %s", filename.c_str()); } NativeFile::NativeFile(const NativeFile &other) @@ -62,7 +57,14 @@ NativeFile::NativeFile(const NativeFile &other) , bufferMode(other.bufferMode) , bufferSize(other.bufferSize) { - open(other.mode); + if (!open(other.mode)) + throw love::Exception("Could not open file at path %s", filename.c_str()); +} + +NativeFile::~NativeFile() +{ + if (mode != MODE_CLOSED) + close(); } NativeFile *NativeFile::clone() diff --git a/src/modules/filesystem/physfs/File.cpp b/src/modules/filesystem/physfs/File.cpp index f56455fc5..181153d3e 100644 --- a/src/modules/filesystem/physfs/File.cpp +++ b/src/modules/filesystem/physfs/File.cpp @@ -48,7 +48,8 @@ File::File(const std::string &filename, Mode mode) , bufferMode(BUFFER_NONE) , bufferSize(0) { - open(mode); + if (!open(mode)) + throw love::Exception("Could not open file at path %s", filename.c_str()); } File::File(const File &other) @@ -58,7 +59,8 @@ File::File(const File &other) , bufferMode(other.bufferMode) , bufferSize(other.bufferSize) { - open(other.mode); + if (!open(other.mode)) + throw love::Exception("Could not open file at path %s", filename.c_str()); } File::~File() diff --git a/src/modules/filesystem/physfs/Filesystem.cpp b/src/modules/filesystem/physfs/Filesystem.cpp index 4a2752d5e..02b7d0f78 100644 --- a/src/modules/filesystem/physfs/Filesystem.cpp +++ b/src/modules/filesystem/physfs/Filesystem.cpp @@ -496,7 +496,7 @@ bool Filesystem::unmount(Data *data) return false; } -love::filesystem::File *Filesystem::newFile(const char *filename, File::Mode mode) const +love::filesystem::File *Filesystem::openFile(const char *filename, File::Mode mode) const { return new File(filename, mode); } diff --git a/src/modules/filesystem/physfs/Filesystem.h b/src/modules/filesystem/physfs/Filesystem.h index 9b31c5f9f..3641e5ded 100644 --- a/src/modules/filesystem/physfs/Filesystem.h +++ b/src/modules/filesystem/physfs/Filesystem.h @@ -71,7 +71,7 @@ public: bool unmount(CommonPath path) override; bool unmountFullPath(const char *fullpath) override; - love::filesystem::File *newFile(const char *filename, File::Mode mode = File::MODE_CLOSED) const override; + love::filesystem::File *openFile(const char *filename, File::Mode mode) const override; std::string getFullCommonPath(CommonPath path) override; const char *getWorkingDirectory() override; diff --git a/src/modules/filesystem/wrap_Filesystem.cpp b/src/modules/filesystem/wrap_Filesystem.cpp index fb239915d..7ca9a7c4f 100644 --- a/src/modules/filesystem/wrap_Filesystem.cpp +++ b/src/modules/filesystem/wrap_Filesystem.cpp @@ -227,34 +227,53 @@ int w_unmountCommonPath(lua_State *L) return 1; } -int w_newFile(lua_State *L) +int w_openFile(lua_State *L) { const char *filename = luaL_checkstring(L, 1); + const char *modestr = luaL_checkstring(L, 2); - const char *str = 0; File::Mode mode = File::MODE_CLOSED; + if (!File::getConstant(modestr, mode)) + return luax_enumerror(L, "file open mode", File::getConstants(mode), modestr); - if (lua_isstring(L, 2)) + File *t = nullptr; + try { - str = luaL_checkstring(L, 2); - if (!File::getConstant(str, mode)) - return luax_enumerror(L, "file open mode", File::getConstants(mode), str); + t = instance()->openFile(filename, mode); + } + catch (love::Exception &e) + { + return luax_ioError(L, "%s", e.what()); } - File *t = instance()->newFile(filename); + luax_pushtype(L, t); + t->release(); + return 1; +} - if (mode != File::MODE_CLOSED) +int w_newFile(lua_State* L) +{ + luax_markdeprecated(L, 1, "love.filesystem.newFile", API_FUNCTION, DEPRECATED_RENAMED, "love.filesystem.openFile"); + + const char* filename = luaL_checkstring(L, 1); + + File::Mode mode = File::MODE_CLOSED; + + if (!lua_isnoneornil(L, 2)) { - try - { - if (!t->open(mode)) - throw love::Exception("Could not open file."); - } - catch (love::Exception &e) - { - t->release(); - return luax_ioError(L, "%s", e.what()); - } + const char* modestr = luaL_checkstring(L, 2); + if (!File::getConstant(modestr, mode)) + return luax_enumerror(L, "file open mode", File::getConstants(mode), modestr); + } + + File* t = nullptr; + try + { + t = instance()->openFile(filename, mode); + } + catch (love::Exception& e) + { + return luax_ioError(L, "%s", e.what()); } luax_pushtype(L, t); @@ -268,7 +287,14 @@ File *luax_getfile(lua_State *L, int idx) if (lua_isstring(L, idx)) { const char *filename = luaL_checkstring(L, idx); - file = instance()->newFile(filename); + try + { + file = instance()->openFile(filename, File::MODE_CLOSED); + } + catch (love::Exception &e) + { + luax_ioError(L, "%s", e.what()); + } } else { @@ -663,16 +689,8 @@ int w_lines(lua_State *L) { if (lua_isstring(L, 1)) { - File *file = instance()->newFile(lua_tostring(L, 1)); - bool success = false; - - luax_catchexcept(L, [&](){ success = file->open(File::MODE_READ); }); - - if (!success) - { - file->release(); - return luaL_error(L, "Could not open file."); - } + File *file = nullptr; + luax_catchexcept(L, [&]() { file = instance()->openFile(lua_tostring(L, 1), File::MODE_READ); }); luax_pushtype(L, file); file->release(); @@ -965,7 +983,7 @@ static const luaL_Reg functions[] = { "unmount", w_unmount }, { "unmountFullPath", w_unmountFullPath }, { "unmountCommonPath", w_unmountCommonPath }, - { "newFile", w_newFile }, + { "openFile", w_openFile }, { "getFullCommonPath", w_getFullCommonPath }, { "getWorkingDirectory", w_getWorkingDirectory }, { "getUserDirectory", w_getUserDirectory }, @@ -991,6 +1009,9 @@ static const luaL_Reg functions[] = { "getCRequirePath", w_getCRequirePath }, { "setCRequirePath", w_setCRequirePath }, + // Deprecated + { "newFile", w_newFile }, + { 0, 0 } }; From 4e051c6faa46be3c48707ddb9e87453c847a4ec1 Mon Sep 17 00:00:00 2001 From: Alex Szpakowski Date: Fri, 22 Apr 2022 21:11:39 -0300 Subject: [PATCH 43/55] Re-add love.filesystem.exists. It's still useful sometimes because it's a bit cheaper than getInfo. --- src/modules/filesystem/Filesystem.h | 5 +++++ src/modules/filesystem/physfs/Filesystem.cpp | 8 ++++++++ src/modules/filesystem/physfs/Filesystem.h | 1 + src/modules/filesystem/wrap_Filesystem.cpp | 8 ++++++++ 4 files changed, 22 insertions(+) diff --git a/src/modules/filesystem/Filesystem.h b/src/modules/filesystem/Filesystem.h index daa735f40..e2c78b7dc 100644 --- a/src/modules/filesystem/Filesystem.h +++ b/src/modules/filesystem/Filesystem.h @@ -216,6 +216,11 @@ public: **/ virtual std::string getRealDirectory(const char *filename) const = 0; + /** + * Gets whether anything exists at the specified path. + **/ + virtual bool exists(const char *filepath) const = 0; + /** * Gets information about the item at the specified filepath. Returns false * if nothing exists at the path. diff --git a/src/modules/filesystem/physfs/Filesystem.cpp b/src/modules/filesystem/physfs/Filesystem.cpp index 02b7d0f78..27f58e826 100644 --- a/src/modules/filesystem/physfs/Filesystem.cpp +++ b/src/modules/filesystem/physfs/Filesystem.cpp @@ -738,6 +738,14 @@ std::string Filesystem::getRealDirectory(const char *filename) const return std::string(dir); } +bool Filesystem::exists(const char *filepath) const +{ + if (!PHYSFS_isInit()) + return false; + + return PHYSFS_exists(filepath) != 0; +} + bool Filesystem::getInfo(const char *filepath, Info &info) const { if (!PHYSFS_isInit()) diff --git a/src/modules/filesystem/physfs/Filesystem.h b/src/modules/filesystem/physfs/Filesystem.h index 3641e5ded..a1ec62657 100644 --- a/src/modules/filesystem/physfs/Filesystem.h +++ b/src/modules/filesystem/physfs/Filesystem.h @@ -82,6 +82,7 @@ public: std::string getRealDirectory(const char *filename) const override; + bool exists(const char *filepath) const override; bool getInfo(const char *filepath, Info &info) const override; bool createDirectory(const char *dir) override; diff --git a/src/modules/filesystem/wrap_Filesystem.cpp b/src/modules/filesystem/wrap_Filesystem.cpp index 7ca9a7c4f..4b53a86c7 100644 --- a/src/modules/filesystem/wrap_Filesystem.cpp +++ b/src/modules/filesystem/wrap_Filesystem.cpp @@ -502,6 +502,13 @@ int w_getExecutablePath(lua_State *L) return 1; } +int w_exists(lua_State *L) +{ + const char *path = luaL_checkstring(L, 1); + luax_pushboolean(L, instance()->exists(path)); + return 1; +} + int w_getInfo(lua_State *L) { const char *filepath = luaL_checkstring(L, 1); @@ -1000,6 +1007,7 @@ static const luaL_Reg functions[] = { "getDirectoryItems", w_getDirectoryItems }, { "lines", w_lines }, { "load", w_load }, + { "exists", w_exists }, { "getInfo", w_getInfo }, { "setSymlinksEnabled", w_setSymlinksEnabled }, { "areSymlinksEnabled", w_areSymlinksEnabled }, From a9cca65a14e46702aad74b561d88b89327f33c39 Mon Sep 17 00:00:00 2001 From: Alex Szpakowski Date: Fri, 22 Apr 2022 22:55:48 -0300 Subject: [PATCH 44/55] Clean up some compressed texture loading code. --- src/modules/image/CompressedImageData.cpp | 9 ++++----- src/modules/image/CompressedImageData.h | 2 +- src/modules/image/CompressedSlice.cpp | 23 +++-------------------- src/modules/image/CompressedSlice.h | 20 +++++--------------- src/modules/image/FormatHandler.cpp | 2 +- src/modules/image/FormatHandler.h | 2 +- src/modules/image/magpie/ASTCHandler.cpp | 8 ++++---- src/modules/image/magpie/ASTCHandler.h | 2 +- src/modules/image/magpie/KTXHandler.cpp | 7 +++---- src/modules/image/magpie/KTXHandler.h | 2 +- src/modules/image/magpie/PKMHandler.cpp | 7 +++---- src/modules/image/magpie/PKMHandler.h | 2 +- src/modules/image/magpie/PVRHandler.cpp | 8 ++++---- src/modules/image/magpie/PVRHandler.h | 2 +- src/modules/image/magpie/ddsHandler.cpp | 7 +++---- src/modules/image/magpie/ddsHandler.h | 2 +- 16 files changed, 37 insertions(+), 68 deletions(-) diff --git a/src/modules/image/CompressedImageData.cpp b/src/modules/image/CompressedImageData.cpp index 79c7b78f5..387632056 100644 --- a/src/modules/image/CompressedImageData.cpp +++ b/src/modules/image/CompressedImageData.cpp @@ -53,7 +53,7 @@ CompressedImageData::CompressedImageData(const std::list &forma if (format == PIXELFORMAT_UNKNOWN) throw love::Exception("Could not parse compressed data: Unknown format."); - if (dataImages.size() == 0 || memory->size == 0) + if (dataImages.size() == 0 || memory->getSize() == 0) throw love::Exception("Could not parse compressed data: No valid data?"); } @@ -61,8 +61,7 @@ CompressedImageData::CompressedImageData(const CompressedImageData &c) : format(c.format) , sRGB(c.sRGB) { - memory.set(new CompressedMemory(c.memory->size), Acquire::NORETAIN); - memcpy(memory->data, c.memory->data, memory->size); + memory.set(c.memory->clone(), Acquire::NORETAIN); for (const auto &i : c.dataImages) { @@ -83,12 +82,12 @@ CompressedImageData::~CompressedImageData() size_t CompressedImageData::getSize() const { - return memory->size; + return memory->getSize(); } void *CompressedImageData::getData() const { - return memory->data; + return memory->getData(); } int CompressedImageData::getMipmapCount() const diff --git a/src/modules/image/CompressedImageData.h b/src/modules/image/CompressedImageData.h index 8234d81aa..924afb740 100644 --- a/src/modules/image/CompressedImageData.h +++ b/src/modules/image/CompressedImageData.h @@ -103,7 +103,7 @@ protected: bool sRGB; // Single block of memory containing all of the sub-images. - StrongRef memory; + StrongRef memory; // Texture info for each mipmap level. std::vector> dataImages; diff --git a/src/modules/image/CompressedSlice.cpp b/src/modules/image/CompressedSlice.cpp index f185da994..fb1dd29c9 100644 --- a/src/modules/image/CompressedSlice.cpp +++ b/src/modules/image/CompressedSlice.cpp @@ -26,30 +26,12 @@ namespace love namespace image { -CompressedMemory::CompressedMemory(size_t size) - : data(nullptr) - , size(size) -{ - try - { - data = new uint8[size]; - } - catch (std::exception &) - { - throw love::Exception("Out of memory."); - } -} - -CompressedMemory::~CompressedMemory() -{ - delete[] data; -} - -CompressedSlice::CompressedSlice(PixelFormat format, int width, int height, CompressedMemory *memory, size_t offset, size_t size) +CompressedSlice::CompressedSlice(PixelFormat format, int width, int height, ByteData *memory, size_t offset, size_t size) : ImageDataBase(format, width, height) , memory(memory) , offset(offset) , dataSize(size) + , sRGB(false) { } @@ -58,6 +40,7 @@ CompressedSlice::CompressedSlice(const CompressedSlice &s) , memory(s.memory) , offset(s.offset) , dataSize(s.dataSize) + , sRGB(s.sRGB) { } diff --git a/src/modules/image/CompressedSlice.h b/src/modules/image/CompressedSlice.h index f322dd5eb..63004836b 100644 --- a/src/modules/image/CompressedSlice.h +++ b/src/modules/image/CompressedSlice.h @@ -23,7 +23,7 @@ // LOVE #include "common/int.h" #include "common/pixelformat.h" -#include "common/Object.h" +#include "data/ByteData.h" #include "ImageDataBase.h" namespace love @@ -31,17 +31,7 @@ namespace love namespace image { -class CompressedMemory : public Object -{ -public: - - CompressedMemory(size_t size); - virtual ~CompressedMemory(); - - uint8 *data; - size_t size; - -}; // CompressedMemory +using ByteData = love::data::ByteData; // Compressed image data can have multiple mipmap levels, each represented by a // sub-image. @@ -49,19 +39,19 @@ class CompressedSlice : public ImageDataBase { public: - CompressedSlice(PixelFormat format, int width, int height, CompressedMemory *memory, size_t offset, size_t size); + CompressedSlice(PixelFormat format, int width, int height, ByteData *memory, size_t offset, size_t size); CompressedSlice(const CompressedSlice &slice); virtual ~CompressedSlice(); CompressedSlice *clone() const override; - void *getData() const override { return memory->data + offset; } + void *getData() const override { return (uint8 *) memory->getData() + offset; } size_t getSize() const override { return dataSize; } bool isSRGB() const override { return sRGB; } size_t getOffset() const { return offset; } private: - StrongRef memory; + StrongRef memory; size_t offset; size_t dataSize; bool sRGB; diff --git a/src/modules/image/FormatHandler.cpp b/src/modules/image/FormatHandler.cpp index 1d37eabf5..9b4fc0050 100644 --- a/src/modules/image/FormatHandler.cpp +++ b/src/modules/image/FormatHandler.cpp @@ -60,7 +60,7 @@ bool FormatHandler::canParseCompressed(Data* /*data*/) return false; } -StrongRef FormatHandler::parseCompressed(Data* /*filedata*/, std::vector>& /*images*/, PixelFormat& /*format*/, bool& /*sRGB*/) +StrongRef FormatHandler::parseCompressed(Data* /*filedata*/, std::vector>& /*images*/, PixelFormat& /*format*/, bool& /*sRGB*/) { throw love::Exception("Compressed image parsing is not implemented for this format backend."); } diff --git a/src/modules/image/FormatHandler.h b/src/modules/image/FormatHandler.h index dd41d6568..2f3f63180 100644 --- a/src/modules/image/FormatHandler.h +++ b/src/modules/image/FormatHandler.h @@ -107,7 +107,7 @@ public: * * @return The single block of memory containing the parsed images. **/ - virtual StrongRef parseCompressed(Data *filedata, + virtual StrongRef parseCompressed(Data *filedata, std::vector> &images, PixelFormat &format, bool &sRGB); diff --git a/src/modules/image/magpie/ASTCHandler.cpp b/src/modules/image/magpie/ASTCHandler.cpp index 6a5c1d12e..817025759 100644 --- a/src/modules/image/magpie/ASTCHandler.cpp +++ b/src/modules/image/magpie/ASTCHandler.cpp @@ -105,7 +105,7 @@ bool ASTCHandler::canParseCompressed(Data *data) return true; } -StrongRef ASTCHandler::parseCompressed(Data *filedata, std::vector> &images, PixelFormat &format, bool &sRGB) +StrongRef ASTCHandler::parseCompressed(Data *filedata, std::vector> &images, PixelFormat &format, bool &sRGB) { if (!canParseCompressed(filedata)) throw love::Exception("Could not decode compressed data (not an .astc file?)"); @@ -125,15 +125,15 @@ StrongRef ASTCHandler::parseCompressed(Data *filedata, std::ve uint32 blocksY = (sizeY + header.blockdimY - 1) / header.blockdimY; uint32 blocksZ = (sizeZ + header.blockdimZ - 1) / header.blockdimZ; - size_t totalsize = blocksX * blocksY * blocksZ * 16; + size_t totalsize = (size_t) blocksX * blocksY * blocksZ * 16; if (totalsize + sizeof(header) > filedata->getSize()) throw love::Exception("Could not parse .astc file: file is too small."); - StrongRef memory(new CompressedMemory(totalsize), Acquire::NORETAIN); + StrongRef memory(new ByteData(totalsize, false), Acquire::NORETAIN); // .astc files only store a single mipmap level. - memcpy(memory->data, (uint8 *) filedata->getData() + sizeof(ASTCHeader), totalsize); + memcpy(memory->getData(), (uint8 *) filedata->getData() + sizeof(ASTCHeader), totalsize); images.emplace_back(new CompressedSlice(cformat, sizeX, sizeY, memory, 0, totalsize), Acquire::NORETAIN); diff --git a/src/modules/image/magpie/ASTCHandler.h b/src/modules/image/magpie/ASTCHandler.h index 6bd797d93..c6daba32f 100644 --- a/src/modules/image/magpie/ASTCHandler.h +++ b/src/modules/image/magpie/ASTCHandler.h @@ -43,7 +43,7 @@ public: // Implements FormatHandler. bool canParseCompressed(Data *data) override; - StrongRef parseCompressed(Data *filedata, + StrongRef parseCompressed(Data *filedata, std::vector> &images, PixelFormat &format, bool &sRGB) override; diff --git a/src/modules/image/magpie/KTXHandler.cpp b/src/modules/image/magpie/KTXHandler.cpp index 177742d7f..8fc2e52c6 100644 --- a/src/modules/image/magpie/KTXHandler.cpp +++ b/src/modules/image/magpie/KTXHandler.cpp @@ -298,7 +298,7 @@ bool KTXHandler::canParseCompressed(Data *data) return true; } -StrongRef KTXHandler::parseCompressed(Data *filedata, std::vector> &images, PixelFormat &format, bool &sRGB) +StrongRef KTXHandler::parseCompressed(Data *filedata, std::vector> &images, PixelFormat &format, bool &sRGB) { if (!canParseCompressed(filedata)) throw love::Exception("Could not decode compressed data (not a KTX file?)"); @@ -354,8 +354,7 @@ StrongRef KTXHandler::parseCompressed(Data *filedata, std::vec fileoffset += mipsizepadded; } - StrongRef memory; - memory.set(new CompressedMemory(totalsize), Acquire::NORETAIN); + StrongRef memory(new ByteData(totalsize, false), Acquire::NORETAIN); // Reset the file offset to the start of the file's image data. fileoffset = sizeof(KTXHeader) + header.bytesOfKeyValueData; @@ -376,7 +375,7 @@ StrongRef KTXHandler::parseCompressed(Data *filedata, std::vec int width = (int) std::max(header.pixelWidth >> i, 1u); int height = (int) std::max(header.pixelHeight >> i, 1u); - memcpy(memory->data + dataoffset, filebytes + fileoffset, mipsize); + memcpy((uint8 *) memory->getData() + dataoffset, filebytes + fileoffset, mipsize); auto slice = new CompressedSlice(cformat, width, height, memory, dataoffset, mipsize); images.push_back(slice); diff --git a/src/modules/image/magpie/KTXHandler.h b/src/modules/image/magpie/KTXHandler.h index 599d3a3a5..4a7bede72 100644 --- a/src/modules/image/magpie/KTXHandler.h +++ b/src/modules/image/magpie/KTXHandler.h @@ -42,7 +42,7 @@ public: // Implements FormatHandler. bool canParseCompressed(Data *data) override; - StrongRef parseCompressed(Data *filedata, + StrongRef parseCompressed(Data *filedata, std::vector> &images, PixelFormat &format, bool &sRGB) override; diff --git a/src/modules/image/magpie/PKMHandler.cpp b/src/modules/image/magpie/PKMHandler.cpp index e513599bc..8f276d257 100644 --- a/src/modules/image/magpie/PKMHandler.cpp +++ b/src/modules/image/magpie/PKMHandler.cpp @@ -114,7 +114,7 @@ bool PKMHandler::canParseCompressed(Data *data) return true; } -StrongRef PKMHandler::parseCompressed(Data *filedata, std::vector> &images, PixelFormat &format, bool &sRGB) +StrongRef PKMHandler::parseCompressed(Data *filedata, std::vector> &images, PixelFormat &format, bool &sRGB) { if (!canParseCompressed(filedata)) throw love::Exception("Could not decode compressed data (not a PKM file?)"); @@ -135,11 +135,10 @@ StrongRef PKMHandler::parseCompressed(Data *filedata, std::vec // The rest of the file after the header is all texture data. size_t totalsize = filedata->getSize() - sizeof(PKMHeader); - StrongRef memory; - memory.set(new CompressedMemory(totalsize), Acquire::NORETAIN); + StrongRef memory(new ByteData(totalsize, false), Acquire::NORETAIN); // PKM files only store a single mipmap level. - memcpy(memory->data, (uint8 *) filedata->getData() + sizeof(PKMHeader), totalsize); + memcpy(memory->getData(), (uint8 *) filedata->getData() + sizeof(PKMHeader), totalsize); // TODO: verify whether glCompressedTexImage works properly with the unpadded // width and height values (extended == padded.) diff --git a/src/modules/image/magpie/PKMHandler.h b/src/modules/image/magpie/PKMHandler.h index 76c1e89e5..0752340ac 100644 --- a/src/modules/image/magpie/PKMHandler.h +++ b/src/modules/image/magpie/PKMHandler.h @@ -42,7 +42,7 @@ public: // Implements FormatHandler. bool canParseCompressed(Data *data) override; - StrongRef parseCompressed(Data *filedata, + StrongRef parseCompressed(Data *filedata, std::vector> &images, PixelFormat &format, bool &sRGB) override; diff --git a/src/modules/image/magpie/PVRHandler.cpp b/src/modules/image/magpie/PVRHandler.cpp index aa5d089a6..b752bec5f 100644 --- a/src/modules/image/magpie/PVRHandler.cpp +++ b/src/modules/image/magpie/PVRHandler.cpp @@ -475,7 +475,7 @@ bool PVRHandler::canParseCompressed(Data *data) return false; } -StrongRef PVRHandler::parseCompressed(Data *filedata, std::vector> &images, PixelFormat &format, bool &sRGB) +StrongRef PVRHandler::parseCompressed(Data *filedata, std::vector> &images, PixelFormat &format, bool &sRGB) { if (!canParseCompressed(filedata)) throw love::Exception("Could not decode compressed data (not a PVR file?)"); @@ -525,8 +525,8 @@ StrongRef PVRHandler::parseCompressed(Data *filedata, std::vec if (filedata->getSize() < fileoffset + totalsize) throw love::Exception("Could not parse PVR file: invalid size calculation."); - StrongRef memory; - memory.set(new CompressedMemory(totalsize), Acquire::NORETAIN); + ; + StrongRef memory(new ByteData(totalsize, false), Acquire::NORETAIN); size_t curoffset = 0; const uint8 *filebytes = (uint8 *) filedata->getData() + fileoffset; @@ -541,7 +541,7 @@ StrongRef PVRHandler::parseCompressed(Data *filedata, std::vec int width = std::max((int) header3.width >> i, 1); int height = std::max((int) header3.height >> i, 1); - memcpy(memory->data + curoffset, filebytes + curoffset, mipsize); + memcpy((uint8 *) memory->getData() + curoffset, filebytes + curoffset, mipsize); auto slice = new CompressedSlice(cformat, width, height, memory, curoffset, mipsize); images.push_back(slice); diff --git a/src/modules/image/magpie/PVRHandler.h b/src/modules/image/magpie/PVRHandler.h index 8db4db322..11f612bb2 100644 --- a/src/modules/image/magpie/PVRHandler.h +++ b/src/modules/image/magpie/PVRHandler.h @@ -40,7 +40,7 @@ public: // Implements FormatHandler. bool canParseCompressed(Data *data) override; - StrongRef parseCompressed(Data *filedata, + StrongRef parseCompressed(Data *filedata, std::vector> &images, PixelFormat &format, bool &sRGB) override; diff --git a/src/modules/image/magpie/ddsHandler.cpp b/src/modules/image/magpie/ddsHandler.cpp index c0701e6a4..dd19e7c46 100644 --- a/src/modules/image/magpie/ddsHandler.cpp +++ b/src/modules/image/magpie/ddsHandler.cpp @@ -229,7 +229,7 @@ bool DDSHandler::canParseCompressed(Data *data) return dds::isCompressedDDS(data->getData(), data->getSize()); } -StrongRef DDSHandler::parseCompressed(Data *filedata, std::vector> &images, PixelFormat &format, bool &sRGB) +StrongRef DDSHandler::parseCompressed(Data *filedata, std::vector> &images, PixelFormat &format, bool &sRGB) { if (!dds::isCompressedDDS(filedata->getData(), filedata->getSize())) throw love::Exception("Could not decode compressed data (not a DDS file?)"); @@ -238,7 +238,6 @@ StrongRef DDSHandler::parseCompressed(Data *filedata, std::vec bool isSRGB = false; bool bgra = false; - StrongRef memory; size_t dataSize = 0; images.clear(); @@ -261,7 +260,7 @@ StrongRef DDSHandler::parseCompressed(Data *filedata, std::vec dataSize += img->dataSize; } - memory.set(new CompressedMemory(dataSize), Acquire::NORETAIN); + StrongRef memory(new ByteData(dataSize, false), Acquire::NORETAIN); size_t dataOffset = 0; @@ -272,7 +271,7 @@ StrongRef DDSHandler::parseCompressed(Data *filedata, std::vec const dds::Image *img = parser.getImageData(i); // Copy the mipmap image from the FileData to our block of memory. - memcpy(memory->data + dataOffset, img->data, img->dataSize); + memcpy((uint8 *) memory->getData() + dataOffset, img->data, img->dataSize); auto slice = new CompressedSlice(texformat, img->width, img->height, memory, dataOffset, img->dataSize); images.emplace_back(slice, Acquire::NORETAIN); diff --git a/src/modules/image/magpie/ddsHandler.h b/src/modules/image/magpie/ddsHandler.h index 0af73b9de..3d957911a 100644 --- a/src/modules/image/magpie/ddsHandler.h +++ b/src/modules/image/magpie/ddsHandler.h @@ -46,7 +46,7 @@ public: bool canDecode(Data *data) override; DecodedImage decode(Data *data) override; bool canParseCompressed(Data *data) override; - StrongRef parseCompressed(Data *filedata, + StrongRef parseCompressed(Data *filedata, std::vector> &images, PixelFormat &format, bool &sRGB) override; From da1271e9c86484274f28f5102ffb92eb15d4a27e Mon Sep 17 00:00:00 2001 From: Alex Szpakowski Date: Sat, 23 Apr 2022 02:04:19 -0300 Subject: [PATCH 45/55] Allow launching a specific/custom .lua file as the main file. Previously you could pass in a folder or .zip to launch a game, now you can also pass in a .lua file directly. It will use the directory containing the lua file as the game source directory. Resolves #1591 --- src/modules/love/boot.lua | 25 ++++++++++++++++++++----- 1 file changed, 20 insertions(+), 5 deletions(-) diff --git a/src/modules/love/boot.lua b/src/modules/love/boot.lua index cf1d2fc8f..beae7cc6f 100644 --- a/src/modules/love/boot.lua +++ b/src/modules/love/boot.lua @@ -37,6 +37,7 @@ end local no_game_code = false local invalid_game_path = nil +local main_file = "main.lua" -- This can't be overridden. function love.boot() @@ -80,6 +81,9 @@ function love.boot() love.setDeprecationOutput(not love.filesystem.isFused()) + main_file = "main.lua" + local custom_main_file = false + local identity = "" if not can_has_game and o.game.set and o.game.arg[1] then local nouri = o.game.arg[1] @@ -89,6 +93,14 @@ function love.boot() end local full_source = love.path.getFull(nouri) + local source_leaf = love.path.leaf(full_source) + + if source_leaf:match("%.lua$") then + main_file = source_leaf + custom_main_file = true + full_source = love.path.getFull(full_source:sub(1, -(#source_leaf + 1))) + end + can_has_game = pcall(love.filesystem.setSource, full_source) if not can_has_game then @@ -104,7 +116,7 @@ function love.boot() -- Try to use the archive containing main.lua as the identity name. It -- might not be available, in which case the fallbacks above are used. - local realdir = love.filesystem.getRealDirectory("main.lua") + local realdir = love.filesystem.getRealDirectory(main_file) if realdir then identity = love.path.leaf(realdir) end @@ -118,7 +130,7 @@ function love.boot() -- before the save directory (the identity should be appended.) pcall(love.filesystem.setIdentity, identity, true) - if can_has_game and not (love.filesystem.getInfo("main.lua") or love.filesystem.getInfo("conf.lua")) then + if can_has_game and not (love.filesystem.getInfo(main_file) or (not custom_main_file and love.filesystem.getInfo("conf.lua"))) then no_game_code = true end @@ -362,13 +374,16 @@ function love.init() if love.filesystem then love.filesystem._setAndroidSaveExternal(c.externalstorage) love.filesystem.setIdentity(c.identity or love.filesystem.getIdentity(), c.appendidentity) - if love.filesystem.getInfo("main.lua") then - require("main") + if love.filesystem.getInfo(main_file) then + require(main_file:gsub("%.lua$", "")) end end if no_game_code then - error("No code to run\nYour game might be packaged incorrectly.\nMake sure main.lua is at the top level of the zip.") + local opts = love.arg.options + local gamepath = opts.game.set and opts.game.arg[1] or "" + local gamestr = gamepath == "" and "" or "at "..gamepath + error("No code to run at "..gamestr.."\nYour game might be packaged incorrectly.\nMake sure "..main_file.." is at the top level of the zip or folder.") elseif invalid_game_path then error("Cannot load game at path '" .. invalid_game_path .. "'.\nMake sure a folder exists at the specified path.") end From b2ba63df18a63a029eadc9f8bcfa5f0344068028 Mon Sep 17 00:00:00 2001 From: Alex Szpakowski Date: Sat, 23 Apr 2022 09:40:02 -0300 Subject: [PATCH 46/55] Fix typo in error message --- src/modules/love/boot.lua | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/modules/love/boot.lua b/src/modules/love/boot.lua index beae7cc6f..736950476 100644 --- a/src/modules/love/boot.lua +++ b/src/modules/love/boot.lua @@ -382,8 +382,8 @@ function love.init() if no_game_code then local opts = love.arg.options local gamepath = opts.game.set and opts.game.arg[1] or "" - local gamestr = gamepath == "" and "" or "at "..gamepath - error("No code to run at "..gamestr.."\nYour game might be packaged incorrectly.\nMake sure "..main_file.." is at the top level of the zip or folder.") + local gamestr = gamepath == "" and "" or " at "..gamepath + error("No code to run"..gamestr.."\nYour game might be packaged incorrectly.\nMake sure "..main_file.." is at the top level of the zip or folder.") elseif invalid_game_path then error("Cannot load game at path '" .. invalid_game_path .. "'.\nMake sure a folder exists at the specified path.") end From 28a64c2b29fb7e0359d12e99b492e52e0015166f Mon Sep 17 00:00:00 2001 From: Alex Szpakowski Date: Sat, 23 Apr 2022 09:41:29 -0300 Subject: [PATCH 47/55] Use uint32 instead of int for some internal bit-flags. --- src/modules/graphics/Graphics.h | 2 +- src/modules/graphics/metal/Graphics.h | 2 +- src/modules/graphics/metal/Graphics.mm | 2 +- src/modules/graphics/opengl/Graphics.cpp | 2 +- src/modules/graphics/opengl/Graphics.h | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/modules/graphics/Graphics.h b/src/modules/graphics/Graphics.h index 4172ce7f4..61b6cbdbb 100644 --- a/src/modules/graphics/Graphics.h +++ b/src/modules/graphics/Graphics.h @@ -797,7 +797,7 @@ public: /** * Gets whether the specified pixel format usage is supported. **/ - virtual bool isPixelFormatSupported(PixelFormat format, int usage, bool sRGB = false) = 0; + virtual bool isPixelFormatSupported(PixelFormat format, uint32 usage, bool sRGB = false) = 0; /** * Gets the renderer used by love.graphics. diff --git a/src/modules/graphics/metal/Graphics.h b/src/modules/graphics/metal/Graphics.h index 124a7ccdf..9274a0d6e 100644 --- a/src/modules/graphics/metal/Graphics.h +++ b/src/modules/graphics/metal/Graphics.h @@ -110,7 +110,7 @@ public: void setWireframe(bool enable) override; PixelFormat getSizedFormat(PixelFormat format, bool rendertarget, bool readable) const override; - bool isPixelFormatSupported(PixelFormat format, int usage, bool sRGB = false) override; + bool isPixelFormatSupported(PixelFormat format, uint32 usage, bool sRGB = false) override; Renderer getRenderer() const override; bool usesGLSLES() const override; RendererInfo getRendererInfo() const override; diff --git a/src/modules/graphics/metal/Graphics.mm b/src/modules/graphics/metal/Graphics.mm index e377223ab..2704118d0 100644 --- a/src/modules/graphics/metal/Graphics.mm +++ b/src/modules/graphics/metal/Graphics.mm @@ -1778,7 +1778,7 @@ PixelFormat Graphics::getSizedFormat(PixelFormat format, bool /*rendertarget*/, } } -bool Graphics::isPixelFormatSupported(PixelFormat format, int usage, bool sRGB) +bool Graphics::isPixelFormatSupported(PixelFormat format, uint32 usage, bool sRGB) { bool rendertarget = (usage & PIXELFORMATUSAGEFLAGS_RENDERTARGET) != 0; bool readable = (usage & PIXELFORMATUSAGEFLAGS_SAMPLE) != 0; diff --git a/src/modules/graphics/opengl/Graphics.cpp b/src/modules/graphics/opengl/Graphics.cpp index c9705d29e..d7d34fa2b 100644 --- a/src/modules/graphics/opengl/Graphics.cpp +++ b/src/modules/graphics/opengl/Graphics.cpp @@ -1697,7 +1697,7 @@ PixelFormat Graphics::getSizedFormat(PixelFormat format, bool rendertarget, bool } } -bool Graphics::isPixelFormatSupported(PixelFormat format, int usage, bool sRGB) +bool Graphics::isPixelFormatSupported(PixelFormat format, uint32 usage, bool sRGB) { if (sRGB) { diff --git a/src/modules/graphics/opengl/Graphics.h b/src/modules/graphics/opengl/Graphics.h index 9c65ce6fd..fab31e637 100644 --- a/src/modules/graphics/opengl/Graphics.h +++ b/src/modules/graphics/opengl/Graphics.h @@ -106,7 +106,7 @@ public: void setWireframe(bool enable) override; PixelFormat getSizedFormat(PixelFormat format, bool rendertarget, bool readable) const override; - bool isPixelFormatSupported(PixelFormat format, int usage, bool sRGB = false) override; + bool isPixelFormatSupported(PixelFormat format, uint32 usage, bool sRGB = false) override; Renderer getRenderer() const override; bool usesGLSLES() const override; RendererInfo getRendererInfo() const override; From ee336e53934c0f62a7f76686165a77e6f09260d5 Mon Sep 17 00:00:00 2001 From: Alex Szpakowski Date: Sat, 23 Apr 2022 13:15:58 -0300 Subject: [PATCH 48/55] opengl: restructure code for computing format support. --- src/modules/graphics/opengl/Graphics.cpp | 153 +++++++++++------------ src/modules/graphics/opengl/Graphics.h | 6 +- 2 files changed, 77 insertions(+), 82 deletions(-) diff --git a/src/modules/graphics/opengl/Graphics.cpp b/src/modules/graphics/opengl/Graphics.cpp index d7d34fa2b..df1b911fc 100644 --- a/src/modules/graphics/opengl/Graphics.cpp +++ b/src/modules/graphics/opengl/Graphics.cpp @@ -112,7 +112,7 @@ Graphics::Graphics() , bufferMapMemory(nullptr) , bufferMapMemorySize(2 * 1024 * 1024) , defaultBuffers() - , supportedFormats() + , pixelFormatUsage() { gl = OpenGL(); @@ -1670,6 +1670,13 @@ void Graphics::initCapabilities() for (int i = 0; i < TEXTURE_MAX_ENUM; i++) capabilities.textureTypes[i] = gl.isTextureTypeSupported((TextureType) i); + + for (int i = 0; i < PIXELFORMAT_MAX_ENUM; i++) + { + auto format = (PixelFormat) i; + pixelFormatUsage[i][0] = computePixelFormatUsage(format, false); + pixelFormatUsage[i][1] = computePixelFormatUsage(format, true); + } } PixelFormat Graphics::getSizedFormat(PixelFormat format, bool rendertarget, bool readable) const @@ -1697,111 +1704,97 @@ PixelFormat Graphics::getSizedFormat(PixelFormat format, bool rendertarget, bool } } -bool Graphics::isPixelFormatSupported(PixelFormat format, uint32 usage, bool sRGB) +uint32 Graphics::computePixelFormatUsage(PixelFormat format, bool readable) { - if (sRGB) - { - format = getSRGBPixelFormat(format); - sRGB = false; - } + uint32 usage = OpenGL::getPixelFormatUsageFlags(format); - bool rendertarget = (usage & PIXELFORMATUSAGEFLAGS_RENDERTARGET) != 0; - bool readable = (usage & PIXELFORMATUSAGEFLAGS_SAMPLE) != 0; - bool computewrite = (usage & PIXELFORMATUSAGEFLAGS_COMPUTEWRITE) != 0; - - format = getSizedFormat(format, rendertarget, readable); - - OptionalBool &supported = supportedFormats[format][rendertarget ? 1 : 0][readable ? 1 : 0][computewrite ? 1 : 0][sRGB ? 1 : 0]; - - if (supported.hasValue) - return supported.value; - - uint32 supportedflags = OpenGL::getPixelFormatUsageFlags(format); - - if ((usage & supportedflags) != usage) - { - supported.set(false); - return supported.value; - } - - if (!rendertarget) - { - supported.set(true); - return supported.value; - } + if (readable && (usage & PIXELFORMATUSAGEFLAGS_SAMPLE) == 0) + return 0; // Even though we might have the necessary OpenGL version or extension, // drivers are still allowed to throw FRAMEBUFFER_UNSUPPORTED when attaching // a texture to a FBO whose format the driver doesn't like. So we should // test with an actual FBO. - GLuint texture = 0; - GLuint renderbuffer = 0; - // Avoid the test for depth/stencil formats - not every GL version // guarantees support for depth/stencil-only render targets (which we would // need for the test below to work), and we already do some finagling in // convertPixelFormat to try to use the best-supported internal // depth/stencil format for a particular driver. - if (isPixelFormatDepthStencil(format)) + if ((usage & PIXELFORMATUSAGEFLAGS_RENDERTARGET) != 0 && !isPixelFormatDepthStencil(format)) { - supported.set(true); - return true; - } + GLuint texture = 0; + GLuint renderbuffer = 0; + bool sRGB = isPixelFormatSRGB(format); - OpenGL::TextureFormat fmt = OpenGL::convertPixelFormat(format, !readable, sRGB); + OpenGL::TextureFormat fmt = OpenGL::convertPixelFormat(format, !readable, sRGB); - GLuint current_fbo = gl.getFramebuffer(OpenGL::FRAMEBUFFER_ALL); + GLuint current_fbo = gl.getFramebuffer(OpenGL::FRAMEBUFFER_ALL); - GLuint fbo = 0; - glGenFramebuffers(1, &fbo); - gl.bindFramebuffer(OpenGL::FRAMEBUFFER_ALL, fbo); + GLuint fbo = 0; + glGenFramebuffers(1, &fbo); + gl.bindFramebuffer(OpenGL::FRAMEBUFFER_ALL, fbo); - // Make sure at least something is bound to a color attachment. I believe - // this is required on ES2 but I'm not positive. - if (isPixelFormatDepthStencil(format)) - gl.framebufferTexture(GL_COLOR_ATTACHMENT0, TEXTURE_2D, gl.getDefaultTexture(TEXTURE_2D, DATA_BASETYPE_FLOAT), 0, 0, 0); - - if (readable) - { - glGenTextures(1, &texture); - gl.bindTextureToUnit(TEXTURE_2D, texture, 0, false); - - SamplerState s; - s.minFilter = s.magFilter = SamplerState::FILTER_NEAREST; - gl.setSamplerState(TEXTURE_2D, s); - - gl.rawTexStorage(TEXTURE_2D, 1, format, sRGB, 1, 1); - } - else - { - glGenRenderbuffers(1, &renderbuffer); - glBindRenderbuffer(GL_RENDERBUFFER, renderbuffer); - glRenderbufferStorage(GL_RENDERBUFFER, fmt.internalformat, 1, 1); - } - - for (GLenum attachment : fmt.framebufferAttachments) - { - if (attachment == GL_NONE) - continue; + // Make sure at least something is bound to a color attachment. I believe + // this is required on ES2 but I'm not positive. + if (isPixelFormatDepthStencil(format)) + gl.framebufferTexture(GL_COLOR_ATTACHMENT0, TEXTURE_2D, gl.getDefaultTexture(TEXTURE_2D, DATA_BASETYPE_FLOAT), 0, 0, 0); if (readable) - gl.framebufferTexture(attachment, TEXTURE_2D, texture, 0, 0, 0); + { + glGenTextures(1, &texture); + gl.bindTextureToUnit(TEXTURE_2D, texture, 0, false); + + SamplerState s; + s.minFilter = s.magFilter = SamplerState::FILTER_NEAREST; + gl.setSamplerState(TEXTURE_2D, s); + + gl.rawTexStorage(TEXTURE_2D, 1, format, sRGB, 1, 1); + } else - glFramebufferRenderbuffer(GL_FRAMEBUFFER, attachment, GL_RENDERBUFFER, renderbuffer); + { + glGenRenderbuffers(1, &renderbuffer); + glBindRenderbuffer(GL_RENDERBUFFER, renderbuffer); + glRenderbufferStorage(GL_RENDERBUFFER, fmt.internalformat, 1, 1); + } + + for (GLenum attachment : fmt.framebufferAttachments) + { + if (attachment == GL_NONE) + continue; + + if (readable) + gl.framebufferTexture(attachment, TEXTURE_2D, texture, 0, 0, 0); + else + glFramebufferRenderbuffer(GL_FRAMEBUFFER, attachment, GL_RENDERBUFFER, renderbuffer); + } + + if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) + usage &= ~PIXELFORMATUSAGEFLAGS_RENDERTARGET; + + gl.bindFramebuffer(OpenGL::FRAMEBUFFER_ALL, current_fbo); + gl.deleteFramebuffer(fbo); + + if (texture != 0) + gl.deleteTexture(texture); + + if (renderbuffer != 0) + glDeleteRenderbuffers(1, &renderbuffer); } - supported.set(glCheckFramebufferStatus(GL_FRAMEBUFFER) == GL_FRAMEBUFFER_COMPLETE); + return usage; +} - gl.bindFramebuffer(OpenGL::FRAMEBUFFER_ALL, current_fbo); - gl.deleteFramebuffer(fbo); +bool Graphics::isPixelFormatSupported(PixelFormat format, uint32 usage, bool sRGB) +{ + if (sRGB) + format = getSRGBPixelFormat(format); - if (texture != 0) - gl.deleteTexture(texture); + bool rendertarget = (usage & PIXELFORMATUSAGEFLAGS_RENDERTARGET) != 0; + bool readable = (usage & PIXELFORMATUSAGEFLAGS_SAMPLE) != 0; - if (renderbuffer != 0) - glDeleteRenderbuffers(1, &renderbuffer); + format = getSizedFormat(format, rendertarget, readable); - return supported.value; + return (usage & pixelFormatUsage[format][readable ? 1 : 0]) == usage; } } // opengl diff --git a/src/modules/graphics/opengl/Graphics.h b/src/modules/graphics/opengl/Graphics.h index fab31e637..f9bcb6523 100644 --- a/src/modules/graphics/opengl/Graphics.h +++ b/src/modules/graphics/opengl/Graphics.h @@ -155,6 +155,8 @@ private: void setDebug(bool enable); + uint32 computePixelFormatUsage(PixelFormat format, bool readable); + std::unordered_map framebufferObjects; bool windowHasStencil; GLuint mainVAO; @@ -170,8 +172,8 @@ private: // Only needed for buffer types that can be bound to shaders. StrongRef defaultBuffers[BUFFERUSAGE_MAX_ENUM]; - // [rendertarget][readable][computewrite][srgb] - OptionalBool supportedFormats[PIXELFORMAT_MAX_ENUM][2][2][2][2]; + // [non-readable, readable] + uint32 pixelFormatUsage[PIXELFORMAT_MAX_ENUM][2]; }; // Graphics From 5f386cfa87016f6861efb2d59e56dc397c6a0300 Mon Sep 17 00:00:00 2001 From: Miku AuahDark Date: Sun, 1 May 2022 17:01:50 +0800 Subject: [PATCH 49/55] Implement audio probing to reduce audio false positive. --- src/modules/sound/Decoder.cpp | 5 ++ src/modules/sound/Decoder.h | 1 + .../sound/lullaby/CoreAudioDecoder.cpp | 15 +++++ src/modules/sound/lullaby/CoreAudioDecoder.h | 1 + src/modules/sound/lullaby/FLACDecoder.cpp | 16 +++++ src/modules/sound/lullaby/FLACDecoder.h | 1 + src/modules/sound/lullaby/MP3Decoder.cpp | 60 +++++++++++++++++++ src/modules/sound/lullaby/MP3Decoder.h | 1 + src/modules/sound/lullaby/ModPlugDecoder.cpp | 13 ++++ src/modules/sound/lullaby/ModPlugDecoder.h | 1 + src/modules/sound/lullaby/Sound.cpp | 38 +++++++++++- src/modules/sound/lullaby/VorbisDecoder.cpp | 13 ++++ src/modules/sound/lullaby/VorbisDecoder.h | 1 + src/modules/sound/lullaby/WaveDecoder.cpp | 21 +++++++ src/modules/sound/lullaby/WaveDecoder.h | 1 + 15 files changed, 185 insertions(+), 3 deletions(-) diff --git a/src/modules/sound/Decoder.cpp b/src/modules/sound/Decoder.cpp index 355b783c9..7c2a71c18 100644 --- a/src/modules/sound/Decoder.cpp +++ b/src/modules/sound/Decoder.cpp @@ -48,6 +48,11 @@ Decoder::~Decoder() delete [](char *) buffer; } +int Decoder::probe(Stream */* stream */) +{ + return 0; +} + void *Decoder::getBuffer() const { return buffer; diff --git a/src/modules/sound/Decoder.h b/src/modules/sound/Decoder.h index 1c91be281..6b6efe8aa 100644 --- a/src/modules/sound/Decoder.h +++ b/src/modules/sound/Decoder.h @@ -51,6 +51,7 @@ public: Decoder(Stream *stream, int bufferSize); virtual ~Decoder(); + static int probe(Stream *stream); /** * Indicates how many bytes of raw data should be generated at each diff --git a/src/modules/sound/lullaby/CoreAudioDecoder.cpp b/src/modules/sound/lullaby/CoreAudioDecoder.cpp index 499ecc43e..6839e2c43 100644 --- a/src/modules/sound/lullaby/CoreAudioDecoder.cpp +++ b/src/modules/sound/lullaby/CoreAudioDecoder.cpp @@ -118,6 +118,21 @@ CoreAudioDecoder::~CoreAudioDecoder() closeAudioFile(); } +int CoreAudioDecoder::probe(Stream* stream) +{ + AudioFileID audioFile; + + // I think this is sufficient + err = AudioFileOpenWithCallbacks(stream, readFunc, nullptr, getSizeFunc, nullptr, kAudioFileMP3Type, &audioFile); + if (err == noErr) + { + AudioFileClose(audioFile); + return 80; + } + + return 0; +} + void CoreAudioDecoder::closeAudioFile() { if (extAudioFile != nullptr) diff --git a/src/modules/sound/lullaby/CoreAudioDecoder.h b/src/modules/sound/lullaby/CoreAudioDecoder.h index 9153dd2c4..34ad19372 100644 --- a/src/modules/sound/lullaby/CoreAudioDecoder.h +++ b/src/modules/sound/lullaby/CoreAudioDecoder.h @@ -49,6 +49,7 @@ public: CoreAudioDecoder(Stream *stream, int bufferSize); virtual ~CoreAudioDecoder(); + static int probe(Stream *stream); love::sound::Decoder *clone() override; int decode() override; diff --git a/src/modules/sound/lullaby/FLACDecoder.cpp b/src/modules/sound/lullaby/FLACDecoder.cpp index d45386490..29403de49 100644 --- a/src/modules/sound/lullaby/FLACDecoder.cpp +++ b/src/modules/sound/lullaby/FLACDecoder.cpp @@ -59,6 +59,22 @@ FLACDecoder::~FLACDecoder() drflac_close(flac); } +int FLACDecoder::probe(Stream *stream) +{ + char header[4]; + + if (stream->read(header, 4) >= 4) + { + if (memcmp(header, "fLaC", 4) == 0) + return 100; + else if (memcmp(header, "OggS", 4) == 0) + // Vorbis has higher priority + return 40; + } + + return 0; +} + love::sound::Decoder *FLACDecoder::clone() { StrongRef s(stream->clone(), Acquire::NORETAIN); diff --git a/src/modules/sound/lullaby/FLACDecoder.h b/src/modules/sound/lullaby/FLACDecoder.h index 7e5fcdc30..b0f6c62d5 100644 --- a/src/modules/sound/lullaby/FLACDecoder.h +++ b/src/modules/sound/lullaby/FLACDecoder.h @@ -40,6 +40,7 @@ class FLACDecoder : public Decoder public: FLACDecoder(Stream *stream, int bufferSize); ~FLACDecoder(); + static int probe(Stream *stream); love::sound::Decoder *clone() override; int decode() override; diff --git a/src/modules/sound/lullaby/MP3Decoder.cpp b/src/modules/sound/lullaby/MP3Decoder.cpp index 2a1215639..028849bcb 100644 --- a/src/modules/sound/lullaby/MP3Decoder.cpp +++ b/src/modules/sound/lullaby/MP3Decoder.cpp @@ -85,6 +85,66 @@ MP3Decoder::~MP3Decoder() drmp3_uninit(&mp3); } +int MP3Decoder::probe(Stream* stream) +{ + // Header size of ID3v2 + unsigned char header[10]; + + if (stream->read(header, 10) >= 10) + { + if (memcmp(header, "TAG", 3) == 0) + { + // ID3v1 size is 128 bytes. https://id3.org/ID3v1 + stream->seek(128, Stream::SEEKORIGIN_BEGIN); + // We just need 4 bytes actually + if (stream->read(header, 4) < 4) + return 0; + } + else if (memcmp(header, "ID3", 3) == 0) + { + // ID3v2 size is variable + size_t id3Size = + size_t(header[9] & 0x7F) | + (size_t(header[8] & 0x7F) << 7) | + (size_t(header[7] & 0x7F) << 14) | + (size_t(header[6] & 0x7F) << 21); + stream->seek(3 /* "ID3" */ + 2 /* version */ + 1 /* flags */ + 4 /* size */ + id3Size, Stream::SEEKORIGIN_BEGIN); + // We just need 4 bytes actually + if (stream->read(header, 4) < 4) + return 0; + } + } + else + return 0; + + // According to http://www.mp3-tech.org/programmer/frame_header.html + // Check sync bits + if ((header[0] == 0xFF) && (((header[1] >> 5) & 0x7) == 0x7)) + { + if (((header[1] >> 3) & 3) == 1) + // Reserved version + return 0; + if (((header[1] >> 1) & 3) == 0) + // Reserved layer + return 0; + if (((header[2] >> 4) & 0xF) == 0xF) + // Bad bitrate + return 0; + if (((header[2] >> 2) & 0x3) == 0x3) + // Reserved sample rate + return 0; + if ((header[3] & 0x3) == 2) + // Reserved emphasis + return 0; + + // Likely MP3. + return 75; + } + + // Sync bits probably elsewhere + return 1; +} + love::sound::Decoder *MP3Decoder::clone() { StrongRef s(stream->clone(), Acquire::NORETAIN); diff --git a/src/modules/sound/lullaby/MP3Decoder.h b/src/modules/sound/lullaby/MP3Decoder.h index d8589210e..baf84005e 100644 --- a/src/modules/sound/lullaby/MP3Decoder.h +++ b/src/modules/sound/lullaby/MP3Decoder.h @@ -43,6 +43,7 @@ public: MP3Decoder(Stream *stream, int bufsize); virtual ~MP3Decoder(); + static int probe(Stream *stream); love::sound::Decoder *clone() override; int decode() override; diff --git a/src/modules/sound/lullaby/ModPlugDecoder.cpp b/src/modules/sound/lullaby/ModPlugDecoder.cpp index 440aa44d8..438a254a4 100644 --- a/src/modules/sound/lullaby/ModPlugDecoder.cpp +++ b/src/modules/sound/lullaby/ModPlugDecoder.cpp @@ -95,6 +95,19 @@ ModPlugDecoder::~ModPlugDecoder() ModPlug_Unload(plug); } +int ModPlugDecoder::probe(Stream* stream) +{ + // Ideally we want to probe every single format that ModPlug supports. + Data *data = stream->read(1024 * 1024 * 4); + ModPlugFile *plug = ModPlug_Load(data->getData(), (int)data->getSize()); + + if (plug) + ModPlug_Unload(plug); + + data->release(); + return plug ? 80 : 0; +} + love::sound::Decoder *ModPlugDecoder::clone() { StrongRef s(stream->clone(), Acquire::NORETAIN); diff --git a/src/modules/sound/lullaby/ModPlugDecoder.h b/src/modules/sound/lullaby/ModPlugDecoder.h index b99a2d0e6..59372d77f 100644 --- a/src/modules/sound/lullaby/ModPlugDecoder.h +++ b/src/modules/sound/lullaby/ModPlugDecoder.h @@ -49,6 +49,7 @@ public: ModPlugDecoder(Stream *stream, int bufferSize); virtual ~ModPlugDecoder(); + static int probe(Stream *stream); love::sound::Decoder *clone() override; int decode() override; diff --git a/src/modules/sound/lullaby/Sound.cpp b/src/modules/sound/lullaby/Sound.cpp index 63fe642d6..66fe1991f 100644 --- a/src/modules/sound/lullaby/Sound.cpp +++ b/src/modules/sound/lullaby/Sound.cpp @@ -38,8 +38,15 @@ struct DecoderImpl { love::sound::Decoder *(*create)(love::Stream *stream, int bufferSize); + int (*probe)(love::Stream *stream); + int probeScore; }; +static bool compareProbeScore(const DecoderImpl& a, const DecoderImpl& b) +{ + return a.probeScore > b.probeScore; +} + template DecoderImpl DecoderImplFor() { @@ -48,6 +55,20 @@ DecoderImpl DecoderImplFor() { return new DecoderType(stream, bufferSize); }; + decoderImpl.probe = [](love::Stream* stream) + { + return DecoderType::probe(stream); + }; + // Short description of probe score: + // Probe score indicates how likely is a file is in certain format. If the + // score is 0 then this particular decoder factory is skipped. Otherwise, + // decoder with highest probe score is used first then decoder with lower + // score. + // There's no standarized value for the probe value (except 0) but if a file + // is "very likely" on certain format, it's safe to return 100. If the file + // is "unlikely" to be certain format but determining such thing requires + // more complicated parsing, it's better to return 1. + decoderImpl.probeScore = 0; return decoderImpl; } @@ -73,7 +94,7 @@ const char *Sound::getName() const sound::Decoder *Sound::newDecoder(Stream *stream, int bufferSize) { - std::vector possibleDecoders = { + std::vector possibleActiveDecoders, possibleDecoders = { DecoderImplFor(), DecoderImplFor(), #ifdef LOVE_SUPPORT_COREAUDIO @@ -86,9 +107,20 @@ sound::Decoder *Sound::newDecoder(Stream *stream, int bufferSize) #endif }; + // Probe decoders + for (DecoderImpl& possibleDecoder : possibleDecoders) + { + stream->seek(0); + possibleDecoder.probeScore = possibleDecoder.probe(stream); + + if (possibleDecoder.probeScore > 0) + possibleActiveDecoders.push_back(possibleDecoder); + } + std::sort(possibleActiveDecoders.begin(), possibleActiveDecoders.end(), compareProbeScore); + + // Load std::stringstream decodingErrors; - decodingErrors << "Failed to determine file type:\n"; - for (DecoderImpl &possibleDecoder : possibleDecoders) + for (DecoderImpl &possibleDecoder : possibleActiveDecoders) { try { diff --git a/src/modules/sound/lullaby/VorbisDecoder.cpp b/src/modules/sound/lullaby/VorbisDecoder.cpp index 6aeac42b9..fbe1f4f28 100644 --- a/src/modules/sound/lullaby/VorbisDecoder.cpp +++ b/src/modules/sound/lullaby/VorbisDecoder.cpp @@ -97,6 +97,19 @@ VorbisDecoder::~VorbisDecoder() ov_clear(&handle); } +int VorbisDecoder::probe(Stream *stream) +{ + char header[4]; + + if (stream->read(header, 4) >= 4) + { + if (memcmp(header, "OggS", 4) == 0) + return 60; + } + + return 1; +} + love::sound::Decoder *VorbisDecoder::clone() { StrongRef s(stream->clone(), Acquire::NORETAIN); diff --git a/src/modules/sound/lullaby/VorbisDecoder.h b/src/modules/sound/lullaby/VorbisDecoder.h index 88f21e489..b1c0f262d 100644 --- a/src/modules/sound/lullaby/VorbisDecoder.h +++ b/src/modules/sound/lullaby/VorbisDecoder.h @@ -44,6 +44,7 @@ public: VorbisDecoder(Stream *stream, int bufferSize); virtual ~VorbisDecoder(); + static int probe(Stream *stream); love::sound::Decoder *clone() override; int decode() override; diff --git a/src/modules/sound/lullaby/WaveDecoder.cpp b/src/modules/sound/lullaby/WaveDecoder.cpp index 012e4d42d..1e9cd3085 100644 --- a/src/modules/sound/lullaby/WaveDecoder.cpp +++ b/src/modules/sound/lullaby/WaveDecoder.cpp @@ -91,6 +91,27 @@ WaveDecoder::~WaveDecoder() wuff_close(handle); } +int WaveDecoder::probe(Stream* stream) +{ + char header[8]; + + if (stream->read(header, 4) < 4) + return 0; + if (memcmp(header, "RIFF", 4) != 0) + return 0; + + // Ignore size + stream->seek(4, Stream::SEEKORIGIN_CURRENT); + + if (stream->read(header, 8) < 8) + return 0; + if (memcmp(header, "WAVEfmt ", 8) != 0) + return 0; + + // WAV file + return 100; +} + love::sound::Decoder *WaveDecoder::clone() { StrongRef s(stream->clone(), Acquire::NORETAIN); diff --git a/src/modules/sound/lullaby/WaveDecoder.h b/src/modules/sound/lullaby/WaveDecoder.h index 9f7ddbeb4..9997744f9 100644 --- a/src/modules/sound/lullaby/WaveDecoder.h +++ b/src/modules/sound/lullaby/WaveDecoder.h @@ -40,6 +40,7 @@ public: WaveDecoder(Stream *stream, int bufferSize); virtual ~WaveDecoder(); + static int probe(Stream *stream); love::sound::Decoder *clone() override; int decode() override; From 17800813b99f58bc9fbed48e432e49cc0f83afb8 Mon Sep 17 00:00:00 2001 From: Miku AuahDark Date: Sun, 1 May 2022 17:15:07 +0800 Subject: [PATCH 50/55] Fix iOS compile error. --- src/modules/sound/lullaby/CoreAudioDecoder.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/modules/sound/lullaby/CoreAudioDecoder.cpp b/src/modules/sound/lullaby/CoreAudioDecoder.cpp index 6839e2c43..fcdca95e6 100644 --- a/src/modules/sound/lullaby/CoreAudioDecoder.cpp +++ b/src/modules/sound/lullaby/CoreAudioDecoder.cpp @@ -121,6 +121,7 @@ CoreAudioDecoder::~CoreAudioDecoder() int CoreAudioDecoder::probe(Stream* stream) { AudioFileID audioFile; + OSStatus err = noErr; // I think this is sufficient err = AudioFileOpenWithCallbacks(stream, readFunc, nullptr, getSizeFunc, nullptr, kAudioFileMP3Type, &audioFile); From 2620225c031fa295f2a5532671b61fb0b96451b6 Mon Sep 17 00:00:00 2001 From: Miku AuahDark Date: Sun, 1 May 2022 21:51:31 +0800 Subject: [PATCH 51/55] Revert "Implement audio probing to reduce audio false positive." This reverts commit 5f386cfa87016f6861efb2d59e56dc397c6a0300 and 17800813b99f58bc9fbed48e432e49cc0f83afb8. --- src/modules/sound/Decoder.cpp | 5 -- src/modules/sound/Decoder.h | 1 - .../sound/lullaby/CoreAudioDecoder.cpp | 16 ----- src/modules/sound/lullaby/CoreAudioDecoder.h | 1 - src/modules/sound/lullaby/FLACDecoder.cpp | 16 ----- src/modules/sound/lullaby/FLACDecoder.h | 1 - src/modules/sound/lullaby/MP3Decoder.cpp | 60 ------------------- src/modules/sound/lullaby/MP3Decoder.h | 1 - src/modules/sound/lullaby/ModPlugDecoder.cpp | 13 ---- src/modules/sound/lullaby/ModPlugDecoder.h | 1 - src/modules/sound/lullaby/Sound.cpp | 38 +----------- src/modules/sound/lullaby/VorbisDecoder.cpp | 13 ---- src/modules/sound/lullaby/VorbisDecoder.h | 1 - src/modules/sound/lullaby/WaveDecoder.cpp | 21 ------- src/modules/sound/lullaby/WaveDecoder.h | 1 - 15 files changed, 3 insertions(+), 186 deletions(-) diff --git a/src/modules/sound/Decoder.cpp b/src/modules/sound/Decoder.cpp index 7c2a71c18..355b783c9 100644 --- a/src/modules/sound/Decoder.cpp +++ b/src/modules/sound/Decoder.cpp @@ -48,11 +48,6 @@ Decoder::~Decoder() delete [](char *) buffer; } -int Decoder::probe(Stream */* stream */) -{ - return 0; -} - void *Decoder::getBuffer() const { return buffer; diff --git a/src/modules/sound/Decoder.h b/src/modules/sound/Decoder.h index 6b6efe8aa..1c91be281 100644 --- a/src/modules/sound/Decoder.h +++ b/src/modules/sound/Decoder.h @@ -51,7 +51,6 @@ public: Decoder(Stream *stream, int bufferSize); virtual ~Decoder(); - static int probe(Stream *stream); /** * Indicates how many bytes of raw data should be generated at each diff --git a/src/modules/sound/lullaby/CoreAudioDecoder.cpp b/src/modules/sound/lullaby/CoreAudioDecoder.cpp index fcdca95e6..499ecc43e 100644 --- a/src/modules/sound/lullaby/CoreAudioDecoder.cpp +++ b/src/modules/sound/lullaby/CoreAudioDecoder.cpp @@ -118,22 +118,6 @@ CoreAudioDecoder::~CoreAudioDecoder() closeAudioFile(); } -int CoreAudioDecoder::probe(Stream* stream) -{ - AudioFileID audioFile; - OSStatus err = noErr; - - // I think this is sufficient - err = AudioFileOpenWithCallbacks(stream, readFunc, nullptr, getSizeFunc, nullptr, kAudioFileMP3Type, &audioFile); - if (err == noErr) - { - AudioFileClose(audioFile); - return 80; - } - - return 0; -} - void CoreAudioDecoder::closeAudioFile() { if (extAudioFile != nullptr) diff --git a/src/modules/sound/lullaby/CoreAudioDecoder.h b/src/modules/sound/lullaby/CoreAudioDecoder.h index 34ad19372..9153dd2c4 100644 --- a/src/modules/sound/lullaby/CoreAudioDecoder.h +++ b/src/modules/sound/lullaby/CoreAudioDecoder.h @@ -49,7 +49,6 @@ public: CoreAudioDecoder(Stream *stream, int bufferSize); virtual ~CoreAudioDecoder(); - static int probe(Stream *stream); love::sound::Decoder *clone() override; int decode() override; diff --git a/src/modules/sound/lullaby/FLACDecoder.cpp b/src/modules/sound/lullaby/FLACDecoder.cpp index 29403de49..d45386490 100644 --- a/src/modules/sound/lullaby/FLACDecoder.cpp +++ b/src/modules/sound/lullaby/FLACDecoder.cpp @@ -59,22 +59,6 @@ FLACDecoder::~FLACDecoder() drflac_close(flac); } -int FLACDecoder::probe(Stream *stream) -{ - char header[4]; - - if (stream->read(header, 4) >= 4) - { - if (memcmp(header, "fLaC", 4) == 0) - return 100; - else if (memcmp(header, "OggS", 4) == 0) - // Vorbis has higher priority - return 40; - } - - return 0; -} - love::sound::Decoder *FLACDecoder::clone() { StrongRef s(stream->clone(), Acquire::NORETAIN); diff --git a/src/modules/sound/lullaby/FLACDecoder.h b/src/modules/sound/lullaby/FLACDecoder.h index b0f6c62d5..7e5fcdc30 100644 --- a/src/modules/sound/lullaby/FLACDecoder.h +++ b/src/modules/sound/lullaby/FLACDecoder.h @@ -40,7 +40,6 @@ class FLACDecoder : public Decoder public: FLACDecoder(Stream *stream, int bufferSize); ~FLACDecoder(); - static int probe(Stream *stream); love::sound::Decoder *clone() override; int decode() override; diff --git a/src/modules/sound/lullaby/MP3Decoder.cpp b/src/modules/sound/lullaby/MP3Decoder.cpp index 028849bcb..2a1215639 100644 --- a/src/modules/sound/lullaby/MP3Decoder.cpp +++ b/src/modules/sound/lullaby/MP3Decoder.cpp @@ -85,66 +85,6 @@ MP3Decoder::~MP3Decoder() drmp3_uninit(&mp3); } -int MP3Decoder::probe(Stream* stream) -{ - // Header size of ID3v2 - unsigned char header[10]; - - if (stream->read(header, 10) >= 10) - { - if (memcmp(header, "TAG", 3) == 0) - { - // ID3v1 size is 128 bytes. https://id3.org/ID3v1 - stream->seek(128, Stream::SEEKORIGIN_BEGIN); - // We just need 4 bytes actually - if (stream->read(header, 4) < 4) - return 0; - } - else if (memcmp(header, "ID3", 3) == 0) - { - // ID3v2 size is variable - size_t id3Size = - size_t(header[9] & 0x7F) | - (size_t(header[8] & 0x7F) << 7) | - (size_t(header[7] & 0x7F) << 14) | - (size_t(header[6] & 0x7F) << 21); - stream->seek(3 /* "ID3" */ + 2 /* version */ + 1 /* flags */ + 4 /* size */ + id3Size, Stream::SEEKORIGIN_BEGIN); - // We just need 4 bytes actually - if (stream->read(header, 4) < 4) - return 0; - } - } - else - return 0; - - // According to http://www.mp3-tech.org/programmer/frame_header.html - // Check sync bits - if ((header[0] == 0xFF) && (((header[1] >> 5) & 0x7) == 0x7)) - { - if (((header[1] >> 3) & 3) == 1) - // Reserved version - return 0; - if (((header[1] >> 1) & 3) == 0) - // Reserved layer - return 0; - if (((header[2] >> 4) & 0xF) == 0xF) - // Bad bitrate - return 0; - if (((header[2] >> 2) & 0x3) == 0x3) - // Reserved sample rate - return 0; - if ((header[3] & 0x3) == 2) - // Reserved emphasis - return 0; - - // Likely MP3. - return 75; - } - - // Sync bits probably elsewhere - return 1; -} - love::sound::Decoder *MP3Decoder::clone() { StrongRef s(stream->clone(), Acquire::NORETAIN); diff --git a/src/modules/sound/lullaby/MP3Decoder.h b/src/modules/sound/lullaby/MP3Decoder.h index baf84005e..d8589210e 100644 --- a/src/modules/sound/lullaby/MP3Decoder.h +++ b/src/modules/sound/lullaby/MP3Decoder.h @@ -43,7 +43,6 @@ public: MP3Decoder(Stream *stream, int bufsize); virtual ~MP3Decoder(); - static int probe(Stream *stream); love::sound::Decoder *clone() override; int decode() override; diff --git a/src/modules/sound/lullaby/ModPlugDecoder.cpp b/src/modules/sound/lullaby/ModPlugDecoder.cpp index 438a254a4..440aa44d8 100644 --- a/src/modules/sound/lullaby/ModPlugDecoder.cpp +++ b/src/modules/sound/lullaby/ModPlugDecoder.cpp @@ -95,19 +95,6 @@ ModPlugDecoder::~ModPlugDecoder() ModPlug_Unload(plug); } -int ModPlugDecoder::probe(Stream* stream) -{ - // Ideally we want to probe every single format that ModPlug supports. - Data *data = stream->read(1024 * 1024 * 4); - ModPlugFile *plug = ModPlug_Load(data->getData(), (int)data->getSize()); - - if (plug) - ModPlug_Unload(plug); - - data->release(); - return plug ? 80 : 0; -} - love::sound::Decoder *ModPlugDecoder::clone() { StrongRef s(stream->clone(), Acquire::NORETAIN); diff --git a/src/modules/sound/lullaby/ModPlugDecoder.h b/src/modules/sound/lullaby/ModPlugDecoder.h index 59372d77f..b99a2d0e6 100644 --- a/src/modules/sound/lullaby/ModPlugDecoder.h +++ b/src/modules/sound/lullaby/ModPlugDecoder.h @@ -49,7 +49,6 @@ public: ModPlugDecoder(Stream *stream, int bufferSize); virtual ~ModPlugDecoder(); - static int probe(Stream *stream); love::sound::Decoder *clone() override; int decode() override; diff --git a/src/modules/sound/lullaby/Sound.cpp b/src/modules/sound/lullaby/Sound.cpp index 66fe1991f..63fe642d6 100644 --- a/src/modules/sound/lullaby/Sound.cpp +++ b/src/modules/sound/lullaby/Sound.cpp @@ -38,15 +38,8 @@ struct DecoderImpl { love::sound::Decoder *(*create)(love::Stream *stream, int bufferSize); - int (*probe)(love::Stream *stream); - int probeScore; }; -static bool compareProbeScore(const DecoderImpl& a, const DecoderImpl& b) -{ - return a.probeScore > b.probeScore; -} - template DecoderImpl DecoderImplFor() { @@ -55,20 +48,6 @@ DecoderImpl DecoderImplFor() { return new DecoderType(stream, bufferSize); }; - decoderImpl.probe = [](love::Stream* stream) - { - return DecoderType::probe(stream); - }; - // Short description of probe score: - // Probe score indicates how likely is a file is in certain format. If the - // score is 0 then this particular decoder factory is skipped. Otherwise, - // decoder with highest probe score is used first then decoder with lower - // score. - // There's no standarized value for the probe value (except 0) but if a file - // is "very likely" on certain format, it's safe to return 100. If the file - // is "unlikely" to be certain format but determining such thing requires - // more complicated parsing, it's better to return 1. - decoderImpl.probeScore = 0; return decoderImpl; } @@ -94,7 +73,7 @@ const char *Sound::getName() const sound::Decoder *Sound::newDecoder(Stream *stream, int bufferSize) { - std::vector possibleActiveDecoders, possibleDecoders = { + std::vector possibleDecoders = { DecoderImplFor(), DecoderImplFor(), #ifdef LOVE_SUPPORT_COREAUDIO @@ -107,20 +86,9 @@ sound::Decoder *Sound::newDecoder(Stream *stream, int bufferSize) #endif }; - // Probe decoders - for (DecoderImpl& possibleDecoder : possibleDecoders) - { - stream->seek(0); - possibleDecoder.probeScore = possibleDecoder.probe(stream); - - if (possibleDecoder.probeScore > 0) - possibleActiveDecoders.push_back(possibleDecoder); - } - std::sort(possibleActiveDecoders.begin(), possibleActiveDecoders.end(), compareProbeScore); - - // Load std::stringstream decodingErrors; - for (DecoderImpl &possibleDecoder : possibleActiveDecoders) + decodingErrors << "Failed to determine file type:\n"; + for (DecoderImpl &possibleDecoder : possibleDecoders) { try { diff --git a/src/modules/sound/lullaby/VorbisDecoder.cpp b/src/modules/sound/lullaby/VorbisDecoder.cpp index fbe1f4f28..6aeac42b9 100644 --- a/src/modules/sound/lullaby/VorbisDecoder.cpp +++ b/src/modules/sound/lullaby/VorbisDecoder.cpp @@ -97,19 +97,6 @@ VorbisDecoder::~VorbisDecoder() ov_clear(&handle); } -int VorbisDecoder::probe(Stream *stream) -{ - char header[4]; - - if (stream->read(header, 4) >= 4) - { - if (memcmp(header, "OggS", 4) == 0) - return 60; - } - - return 1; -} - love::sound::Decoder *VorbisDecoder::clone() { StrongRef s(stream->clone(), Acquire::NORETAIN); diff --git a/src/modules/sound/lullaby/VorbisDecoder.h b/src/modules/sound/lullaby/VorbisDecoder.h index b1c0f262d..88f21e489 100644 --- a/src/modules/sound/lullaby/VorbisDecoder.h +++ b/src/modules/sound/lullaby/VorbisDecoder.h @@ -44,7 +44,6 @@ public: VorbisDecoder(Stream *stream, int bufferSize); virtual ~VorbisDecoder(); - static int probe(Stream *stream); love::sound::Decoder *clone() override; int decode() override; diff --git a/src/modules/sound/lullaby/WaveDecoder.cpp b/src/modules/sound/lullaby/WaveDecoder.cpp index 1e9cd3085..012e4d42d 100644 --- a/src/modules/sound/lullaby/WaveDecoder.cpp +++ b/src/modules/sound/lullaby/WaveDecoder.cpp @@ -91,27 +91,6 @@ WaveDecoder::~WaveDecoder() wuff_close(handle); } -int WaveDecoder::probe(Stream* stream) -{ - char header[8]; - - if (stream->read(header, 4) < 4) - return 0; - if (memcmp(header, "RIFF", 4) != 0) - return 0; - - // Ignore size - stream->seek(4, Stream::SEEKORIGIN_CURRENT); - - if (stream->read(header, 8) < 8) - return 0; - if (memcmp(header, "WAVEfmt ", 8) != 0) - return 0; - - // WAV file - return 100; -} - love::sound::Decoder *WaveDecoder::clone() { StrongRef s(stream->clone(), Acquire::NORETAIN); diff --git a/src/modules/sound/lullaby/WaveDecoder.h b/src/modules/sound/lullaby/WaveDecoder.h index 9997744f9..9f7ddbeb4 100644 --- a/src/modules/sound/lullaby/WaveDecoder.h +++ b/src/modules/sound/lullaby/WaveDecoder.h @@ -40,7 +40,6 @@ public: WaveDecoder(Stream *stream, int bufferSize); virtual ~WaveDecoder(); - static int probe(Stream *stream); love::sound::Decoder *clone() override; int decode() override; From c19d7cab1ec27d5f3faaefda3f7098120785449d Mon Sep 17 00:00:00 2001 From: Miku AuahDark Date: Mon, 2 May 2022 07:40:48 +0800 Subject: [PATCH 52/55] Reorder sound decoder factory preference. --- src/modules/sound/lullaby/Sound.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/modules/sound/lullaby/Sound.cpp b/src/modules/sound/lullaby/Sound.cpp index 63fe642d6..84aaa08ce 100644 --- a/src/modules/sound/lullaby/Sound.cpp +++ b/src/modules/sound/lullaby/Sound.cpp @@ -74,13 +74,13 @@ const char *Sound::getName() const sound::Decoder *Sound::newDecoder(Stream *stream, int bufferSize) { std::vector possibleDecoders = { - DecoderImplFor(), + DecoderImplFor(), + DecoderImplFor(), DecoderImplFor(), #ifdef LOVE_SUPPORT_COREAUDIO DecoderImplFor(), #endif - DecoderImplFor(), - DecoderImplFor(), + DecoderImplFor(), #ifndef LOVE_NO_MODPLUG DecoderImplFor(), // Last because it doesn't work well with Streams. #endif From c9ab08a2d3c447907a680e783b2f8ec532c1ca5a Mon Sep 17 00:00:00 2001 From: Miku AuahDark Date: Mon, 2 May 2022 09:20:04 +0800 Subject: [PATCH 53/55] Improve MP3 detection to reduce false positive. --- src/modules/sound/lullaby/MP3Decoder.cpp | 117 ++++++++++++++++++++--- src/modules/sound/lullaby/MP3Decoder.h | 4 + 2 files changed, 110 insertions(+), 11 deletions(-) diff --git a/src/modules/sound/lullaby/MP3Decoder.cpp b/src/modules/sound/lullaby/MP3Decoder.cpp index 2a1215639..979c9cd6e 100644 --- a/src/modules/sound/lullaby/MP3Decoder.cpp +++ b/src/modules/sound/lullaby/MP3Decoder.cpp @@ -32,23 +32,119 @@ namespace lullaby static size_t onRead(void *pUserData, void *pBufferOut, size_t bytesToRead) { - auto stream = (Stream *) pUserData; - int64 read = stream->read(pBufferOut, bytesToRead); + auto decoder = (MP3Decoder *) pUserData; + int64 read = decoder->stream->read(pBufferOut, bytesToRead); return std::max(0, read); } static drmp3_bool32 onSeek(void *pUserData, int offset, drmp3_seek_origin origin) { - auto stream = (Stream *) pUserData; - auto seekorigin = origin == drmp3_seek_origin_current ? Stream::SEEKORIGIN_CURRENT : Stream::SEEKORIGIN_BEGIN; - return stream->seek(offset, seekorigin) ? DRMP3_TRUE : DRMP3_FALSE; + auto decoder = (MP3Decoder *) pUserData; + int64 pos = decoder->offset; + + // Due to possible offsets, we have to calculate the position ourself. + switch (origin) + { + case drmp3_seek_origin_start: + pos += offset; + break; + case drmp3_seek_origin_current: + pos = decoder->stream->tell() + offset; + break; + default: + return false; + } + + if (pos < decoder->offset) + return false; + + return decoder->stream->seek(pos, Stream::SEEKORIGIN_BEGIN) ? DRMP3_TRUE : DRMP3_FALSE; +} + +// Copied from dr_mp3 function drmp3_hdr_valid() +static bool isMP3HeaderValid(const uint8 *h) +{ + return + // Sync bits + h[0] == 0xff && + ((h[1] & 0xF0) == 0xf0 || (h[1] & 0xFE) == 0xe2) && + // Check layer + (DRMP3_HDR_GET_LAYER(h) != 0) && + // Check bitrate + (DRMP3_HDR_GET_BITRATE(h) != 15) && + // Check sample rate + (DRMP3_HDR_GET_SAMPLE_RATE(h) != 3); +} + +static int64 findFirstValidHeader(Stream* stream) +{ + constexpr size_t LOOKUP_SIZE = 16384; + + std::vector data(LOOKUP_SIZE); + uint8 header[10]; + uint8 *dataPtr = data.data(); + int64 buffer = 0; + int64 offset = 0; + + if (stream->read(header, 10) < 10) + return -1; + + if (memcmp(header, "TAG", 3) == 0) + { + // ID3v1 tag is always 128 bytes long + if (!stream->seek(128, Stream::SEEKORIGIN_BEGIN)) + return -1; + + buffer = stream->read(dataPtr, LOOKUP_SIZE); + offset = 128; + } + else if (memcmp(header, "ID3", 3) == 0) + { + // ID3v2 tag header is 10 bytes long, but we're + // only interested on how much we should skip. + int64 off = + header[9] | + ((int64) header[8] << 7) | + ((int64) header[7] << 14) | + ((int64) header[6] << 21); + + if (!stream->seek(off, Stream::SEEKORIGIN_CURRENT)) + return -1; + + buffer = stream->read(dataPtr, LOOKUP_SIZE); + offset = off + 10; + } + else + { + // Copy the rest to data buffer + memcpy(dataPtr, header, 10); + buffer = 10 + stream->read(dataPtr + 10, LOOKUP_SIZE - 10); + } + + // Look for mp3 data + for (int i = 0; i < buffer - 4; i++, offset++) + { + if (isMP3HeaderValid(dataPtr++)) + { + stream->seek(offset, Stream::SEEKORIGIN_BEGIN); + return offset; + } + } + + // No valid MP3 frame found in first 16KB data + return -1; } MP3Decoder::MP3Decoder(Stream *stream, int bufferSize) - : Decoder(stream, bufferSize) +: Decoder(stream, bufferSize) { + // Check for possible ID3 tag and skip it if necessary. + offset = findFirstValidHeader(stream); + if (offset == -1) + throw love::Exception("Could not find first valid mp3 header."); + // initialize mp3 handle - if (!drmp3_init(&mp3, onRead, onSeek, stream, nullptr, nullptr)) + if (!drmp3_init(&mp3, onRead, onSeek, this, nullptr, nullptr)) throw love::Exception("Could not read mp3 data."); sampleRate = mp3.sampleRate; @@ -63,14 +159,13 @@ MP3Decoder::MP3Decoder(Stream *stream, int bufferSize) duration = ((double) pcmCount) / ((double) mp3.sampleRate); // create seek table - uint32_t mp3FrameInt = mp3FrameCount; - seekTable.resize(mp3FrameCount, {0ULL, 0ULL, 0, 0}); + drmp3_uint32 mp3FrameInt = (drmp3_uint32) mp3FrameCount; + seekTable.resize((size_t) mp3FrameCount, {0ULL, 0ULL, 0, 0}); if (!drmp3_calculate_seek_points(&mp3, &mp3FrameInt, seekTable.data())) { drmp3_uninit(&mp3); throw love::Exception("Could not calculate mp3 seek table"); } - mp3FrameInt = mp3FrameInt > mp3FrameCount ? mp3FrameCount : mp3FrameInt; // bind seek table if (!drmp3_bind_seek_table(&mp3, mp3FrameInt, seekTable.data())) @@ -105,7 +200,7 @@ int MP3Decoder::decode() bool MP3Decoder::seek(double s) { - drmp3_uint64 targetSample = s * mp3.sampleRate; + drmp3_uint64 targetSample = (drmp3_uint64) (s * mp3.sampleRate); drmp3_bool32 success = drmp3_seek_to_pcm_frame(&mp3, targetSample); if (success) diff --git a/src/modules/sound/lullaby/MP3Decoder.h b/src/modules/sound/lullaby/MP3Decoder.h index d8589210e..bd1116b15 100644 --- a/src/modules/sound/lullaby/MP3Decoder.h +++ b/src/modules/sound/lullaby/MP3Decoder.h @@ -54,11 +54,15 @@ public: double getDuration() override; private: + friend size_t onRead(void *pUserData, void *pBufferOut, size_t bytesToRead); + friend drmp3_bool32 onSeek(void *pUserData, int offset, drmp3_seek_origin origin); // MP3 handle drmp3 mp3; // Used for fast seeking std::vector seekTable; + // Position of first MP3 frame found + int64 offset; double duration; }; // MP3Decoder From 5c3f8a8950ab03df81be32fc0a7286b658672f38 Mon Sep 17 00:00:00 2001 From: Miku AuahDark Date: Mon, 2 May 2022 09:43:08 +0800 Subject: [PATCH 54/55] Fix compile error. --- src/modules/sound/lullaby/MP3Decoder.cpp | 62 ++++++++++++------------ src/modules/sound/lullaby/MP3Decoder.h | 4 +- 2 files changed, 33 insertions(+), 33 deletions(-) diff --git a/src/modules/sound/lullaby/MP3Decoder.cpp b/src/modules/sound/lullaby/MP3Decoder.cpp index 979c9cd6e..4c73da29c 100644 --- a/src/modules/sound/lullaby/MP3Decoder.cpp +++ b/src/modules/sound/lullaby/MP3Decoder.cpp @@ -30,37 +30,6 @@ namespace sound namespace lullaby { -static size_t onRead(void *pUserData, void *pBufferOut, size_t bytesToRead) -{ - auto decoder = (MP3Decoder *) pUserData; - int64 read = decoder->stream->read(pBufferOut, bytesToRead); - return std::max(0, read); -} - -static drmp3_bool32 onSeek(void *pUserData, int offset, drmp3_seek_origin origin) -{ - auto decoder = (MP3Decoder *) pUserData; - int64 pos = decoder->offset; - - // Due to possible offsets, we have to calculate the position ourself. - switch (origin) - { - case drmp3_seek_origin_start: - pos += offset; - break; - case drmp3_seek_origin_current: - pos = decoder->stream->tell() + offset; - break; - default: - return false; - } - - if (pos < decoder->offset) - return false; - - return decoder->stream->seek(pos, Stream::SEEKORIGIN_BEGIN) ? DRMP3_TRUE : DRMP3_FALSE; -} - // Copied from dr_mp3 function drmp3_hdr_valid() static bool isMP3HeaderValid(const uint8 *h) { @@ -135,6 +104,37 @@ static int64 findFirstValidHeader(Stream* stream) return -1; } +size_t MP3Decoder::onRead(void *pUserData, void *pBufferOut, size_t bytesToRead) +{ + auto decoder = (MP3Decoder *) pUserData; + int64 read = decoder->stream->read(pBufferOut, bytesToRead); + return std::max(0, read); +} + +drmp3_bool32 MP3Decoder::onSeek(void *pUserData, int offset, drmp3_seek_origin origin) +{ + auto decoder = (MP3Decoder *) pUserData; + int64 pos = decoder->offset; + + // Due to possible offsets, we have to calculate the position ourself. + switch (origin) + { + case drmp3_seek_origin_start: + pos += offset; + break; + case drmp3_seek_origin_current: + pos = decoder->stream->tell() + offset; + break; + default: + return false; + } + + if (pos < decoder->offset) + return false; + + return decoder->stream->seek(pos, Stream::SEEKORIGIN_BEGIN) ? DRMP3_TRUE : DRMP3_FALSE; +} + MP3Decoder::MP3Decoder(Stream *stream, int bufferSize) : Decoder(stream, bufferSize) { diff --git a/src/modules/sound/lullaby/MP3Decoder.h b/src/modules/sound/lullaby/MP3Decoder.h index bd1116b15..84c9444e6 100644 --- a/src/modules/sound/lullaby/MP3Decoder.h +++ b/src/modules/sound/lullaby/MP3Decoder.h @@ -54,8 +54,8 @@ public: double getDuration() override; private: - friend size_t onRead(void *pUserData, void *pBufferOut, size_t bytesToRead); - friend drmp3_bool32 onSeek(void *pUserData, int offset, drmp3_seek_origin origin); + static size_t onRead(void *pUserData, void *pBufferOut, size_t bytesToRead); + static drmp3_bool32 onSeek(void *pUserData, int offset, drmp3_seek_origin origin); // MP3 handle drmp3 mp3; From 5dd7902175bc8348c3d812a9698aed6644aa00c6 Mon Sep 17 00:00:00 2001 From: Miku AuahDark Date: Mon, 2 May 2022 09:44:56 +0800 Subject: [PATCH 55/55] Use DRMP3_FALSE instead of "false" in MP3 seeking. --- src/modules/sound/lullaby/MP3Decoder.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/modules/sound/lullaby/MP3Decoder.cpp b/src/modules/sound/lullaby/MP3Decoder.cpp index 4c73da29c..1c73aa10b 100644 --- a/src/modules/sound/lullaby/MP3Decoder.cpp +++ b/src/modules/sound/lullaby/MP3Decoder.cpp @@ -126,11 +126,11 @@ drmp3_bool32 MP3Decoder::onSeek(void *pUserData, int offset, drmp3_seek_origin o pos = decoder->stream->tell() + offset; break; default: - return false; + return DRMP3_FALSE; } if (pos < decoder->offset) - return false; + return DRMP3_FALSE; return decoder->stream->seek(pos, Stream::SEEKORIGIN_BEGIN) ? DRMP3_TRUE : DRMP3_FALSE; }