Merge remote-tracking branch 'origin/12.0-development' into vulkan

This commit is contained in:
niki
2022-05-06 16:46:39 +02:00
112 changed files with 1615 additions and 1368 deletions
-106
View File
@@ -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)
+21 -4
View File
@@ -36,6 +36,11 @@ set(CMAKE_POSITION_INDEPENDENT_CODE TRUE)
set (CMAKE_CXX_STANDARD 17)
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()
@@ -48,11 +53,15 @@ 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()
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()
@@ -238,6 +247,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
@@ -290,6 +301,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})
@@ -364,6 +378,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
@@ -901,8 +917,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
@@ -1644,6 +1658,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})
@@ -1764,6 +1779,7 @@ set(LOVE_LIB_SRC
)
include_directories(
BEFORE
src
src/libraries
src/libraries/box2D
@@ -1792,6 +1808,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})
+3
View File
@@ -42,6 +42,9 @@ 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.
* 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.
+1 -4
View File
@@ -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,12 +69,12 @@ 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])
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])
-5
View File
@@ -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.] )], ,
@@ -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 */; };
@@ -826,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 */; };
@@ -1699,8 +1699,6 @@
FA0B7C801A95902C000E1D17 /* Decoder.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Decoder.cpp; sourceTree = "<group>"; };
FA0B7C821A95902C000E1D17 /* FLACDecoder.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FLACDecoder.cpp; sourceTree = "<group>"; };
FA0B7C831A95902C000E1D17 /* FLACDecoder.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FLACDecoder.h; sourceTree = "<group>"; };
FA0B7C841A95902C000E1D17 /* GmeDecoder.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = GmeDecoder.cpp; sourceTree = "<group>"; };
FA0B7C851A95902C000E1D17 /* GmeDecoder.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GmeDecoder.h; sourceTree = "<group>"; };
FA0B7C861A95902C000E1D17 /* ModPlugDecoder.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ModPlugDecoder.cpp; sourceTree = "<group>"; };
FA0B7C871A95902C000E1D17 /* ModPlugDecoder.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ModPlugDecoder.h; sourceTree = "<group>"; };
FA0B7C8A1A95902C000E1D17 /* Sound.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Sound.cpp; sourceTree = "<group>"; };
@@ -1909,6 +1907,8 @@
FA6A2B771F60B8250074C308 /* wrap_ByteData.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = wrap_ByteData.h; sourceTree = "<group>"; };
FA6A2B781F60B8250074C308 /* wrap_ByteData.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = wrap_ByteData.cpp; sourceTree = "<group>"; };
FA6BDE5B1F31725300786805 /* Color.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Color.h; sourceTree = "<group>"; };
FA6BDF8C281219E900240F2A /* DataStream.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = DataStream.cpp; sourceTree = "<group>"; };
FA6BDF8D281219E900240F2A /* DataStream.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DataStream.h; sourceTree = "<group>"; };
FA7634481E28722A0066EF9E /* StreamBuffer.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = StreamBuffer.cpp; sourceTree = "<group>"; };
FA7634491E28722A0066EF9E /* StreamBuffer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = StreamBuffer.h; sourceTree = "<group>"; };
FA7DA04C1C16874A0056B200 /* wrap_Math.lua */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = wrap_Math.lua; sourceTree = "<group>"; };
@@ -3199,8 +3199,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 */,
@@ -3762,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 */,
@@ -4023,7 +4023,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 */,
@@ -4370,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 */,
@@ -4517,7 +4517,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 */,
@@ -4702,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 */,
@@ -4940,7 +4940,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 */,
@@ -5122,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 */,
@@ -5399,7 +5399,6 @@
"\"$(SRCROOT)/../../src/libraries/enet/libenet/include\"",
);
IPHONEOS_DEPLOYMENT_TARGET = 9.0;
LD_RUNPATH_SEARCH_PATHS = "@rpath";
LIBRARY_SEARCH_PATHS = "";
MACOSX_DEPLOYMENT_TARGET = 10.9;
ONLY_ACTIVE_ARCH = NO;
@@ -5465,7 +5464,6 @@
"\"$(SRCROOT)/../../src/libraries/enet/libenet/include\"",
);
IPHONEOS_DEPLOYMENT_TARGET = 9.0;
LD_RUNPATH_SEARCH_PATHS = "@rpath";
LIBRARY_SEARCH_PATHS = "";
MACOSX_DEPLOYMENT_TARGET = 10.9;
ONLY_ACTIVE_ARCH = YES;
@@ -5603,7 +5601,6 @@
"\"$(SRCROOT)/../../src/libraries/enet/libenet/include\"",
);
IPHONEOS_DEPLOYMENT_TARGET = 9.0;
LD_RUNPATH_SEARCH_PATHS = "@rpath";
LIBRARY_SEARCH_PATHS = "";
LLVM_LTO = YES;
MACOSX_DEPLOYMENT_TARGET = 10.9;
@@ -5637,6 +5634,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",
@@ -5675,6 +5673,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",
@@ -5714,6 +5713,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",
+50 -1
View File
@@ -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<data::ByteData> 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
+64 -13
View File
@@ -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 <stddef.h>
#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
+1 -1
View File
@@ -35,7 +35,7 @@
#include <sys/types.h>
#include <unistd.h>
#include "physfs.h"
#include "libraries/physfs/physfs.h"
namespace love
{
+2
View File
@@ -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 <TargetConditionals.h>
+13 -12
View File
@@ -24,6 +24,7 @@
#include <atomic>
#include <map>
#include <sstream>
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()
+12
View File
@@ -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];
+10
View File
@@ -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.
**/
+5 -1
View File
@@ -1,4 +1,8 @@
#include <lua.hpp>
extern "C"
{
#include <lua.h>
#include <lauxlib.h>
}
#include "../common/HTTPS.h"
#include "../common/config.h"
+18 -20
View File
@@ -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();
+27 -14
View File
@@ -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<Source*> readSourceList(lua_State *L, int n)
+3 -2
View File
@@ -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)
+1 -1
View File
@@ -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);
+139
View File
@@ -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 <algorithm>
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<int64>(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<int64>(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
@@ -18,56 +18,52 @@
* 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
#pragma once
#ifdef LOVE_SUPPORT_GME
// LOVE
#include "common/Data.h"
#include "sound/Decoder.h"
#ifdef LOVE_APPLE_USE_FRAMEWORKS
#include <Game_Music_Emu/gme.h>
#else
#include <gme.h>
#endif
#include "common/Stream.h"
namespace love
{
namespace sound
{
namespace lullaby
namespace data
{
class GmeDecoder : public Decoder
class DataStream : public love::Stream
{
public:
GmeDecoder(Data *data, int bufferSize);
virtual ~GmeDecoder();
static love::Type type;
static bool accepts(const std::string &ext);
DataStream(Data *data);
virtual ~DataStream();
love::sound::Decoder *clone();
int decode();
bool seek(double s);
bool rewind();
bool isSeekable();
int getChannelCount() const;
int getBitDepth() const;
double getDuration();
// 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:
Music_Emu *emu;
int num_tracks;
int cur_track;
}; // Decoder
} // lullaby
} // sound
DataStream(const DataStream &other);
StrongRef<Data> data;
const uint8 *memory;
uint8 *writableMemory;
size_t offset;
size_t size;
}; // DataStream
} // data
} // love
#endif // LOVE_SUPPORT_GME
#endif // LOVE_SOUND_LULLABY_GME_DECODER_H
+14 -1
View File
@@ -35,6 +35,8 @@
#include <cmath>
#include <SDL_version.h>
namespace love
{
namespace event
@@ -418,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();
@@ -426,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");
@@ -433,6 +441,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;
}
+20 -53
View File
@@ -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> 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<FileData> 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<std::string> 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<std::string> File::getConstants(BufferMode)
{
return bufferModes.getNames();
}
StringMap<File::Mode, File::MODE_MAX_ENUM>::Entry File::modeEntries[] =
{
{ "c", MODE_CLOSED },
{ "r", MODE_READ },
{ "w", MODE_WRITE },
{ "a", MODE_APPEND },
};
StringMap<File::Mode, File::MODE_MAX_ENUM> File::modes(File::modeEntries, sizeof(File::modeEntries));
StringMap<File::BufferMode, File::BUFFER_MAX_ENUM>::Entry File::bufferModeEntries[] =
{
{ "none", BUFFER_NONE },
{ "line", BUFFER_LINE },
{ "full", BUFFER_FULL },
};
StringMap<File::BufferMode, File::BUFFER_MAX_ENUM> File::bufferModes(File::bufferModeEntries, sizeof(File::bufferModeEntries));
STRINGMAP_CLASS_END(File, File::BufferMode, File::BUFFER_MAX_ENUM, bufferMode)
} // filesystem
} // love
+15 -78
View File
@@ -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<std::string> getConstants(Mode);
static bool getConstant(const char *in, BufferMode &out);
static bool getConstant(BufferMode in, const char *&out);
static std::vector<std::string> getConstants(BufferMode);
private:
static StringMap<Mode, MODE_MAX_ENUM>::Entry modeEntries[];
static StringMap<Mode, MODE_MAX_ENUM> modes;
static StringMap<BufferMode, BUFFER_MAX_ENUM>::Entry bufferModeEntries[];
static StringMap<BufferMode, BUFFER_MAX_ENUM> bufferModes;
STRINGMAP_CLASS_DECLARE(Mode);
STRINGMAP_CLASS_DECLARE(BufferMode);
}; // File
+9 -3
View File
@@ -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) const = 0;
virtual File *openFile(const char *filename, File::Mode mode) const = 0;
/**
* Creates a new FileData object. Data will be copied.
@@ -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.
@@ -239,7 +244,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.
+32 -4
View File
@@ -39,13 +39,26 @@ 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)
{
if (!open(mode))
throw love::Exception("Could not open file at path %s", filename.c_str());
}
NativeFile::NativeFile(const NativeFile &other)
: filename(other.filename)
, file(nullptr)
, mode(MODE_CLOSED)
, bufferMode(other.bufferMode)
, bufferSize(other.bufferSize)
{
if (!open(other.mode))
throw love::Exception("Could not open file at path %s", filename.c_str());
}
NativeFile::~NativeFile()
@@ -54,10 +67,18 @@ NativeFile::~NativeFile()
close();
}
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 +218,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
}
+12 -7
View File
@@ -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;
+34 -6
View File
@@ -41,13 +41,26 @@ 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)
{
if (!open(mode))
throw love::Exception("Could not open file at path %s", filename.c_str());
}
File::File(const File &other)
: filename(other.filename)
, file(nullptr)
, mode(MODE_CLOSED)
, bufferMode(other.bufferMode)
, bufferSize(other.bufferSize)
{
if (!open(other.mode))
throw love::Exception("Could not open file at path %s", filename.c_str());
}
File::~File()
@@ -56,10 +69,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 +172,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 +224,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;
}
+12 -7
View File
@@ -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;
+21 -11
View File
@@ -496,9 +496,9 @@ bool Filesystem::unmount(Data *data)
return false;
}
love::filesystem::File *Filesystem::newFile(const char *filename) const
love::filesystem::File *Filesystem::openFile(const char *filename, File::Mode mode) const
{
return new File(filename);
return new File(filename, mode);
}
std::string Filesystem::getFullCommonPath(CommonPath path)
@@ -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())
@@ -793,19 +801,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 +826,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))
+4 -2
View File
@@ -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 *openFile(const char *filename, File::Mode mode) const override;
std::string getFullCommonPath(CommonPath path) override;
const char *getWorkingDirectory() override;
@@ -82,13 +82,15 @@ 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;
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;
+3 -1
View File
@@ -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)
+72 -32
View File
@@ -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
{
@@ -343,6 +369,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);
@@ -471,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);
@@ -557,12 +595,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)
{
@@ -655,16 +696,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();
@@ -806,6 +839,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 == '.')
@@ -954,7 +990,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 },
@@ -971,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 },
@@ -980,6 +1017,9 @@ static const luaL_Reg functions[] =
{ "getCRequirePath", w_getCRequirePath },
{ "setCRequirePath", w_setCRequirePath },
// Deprecated
{ "newFile", w_newFile },
{ 0, 0 }
};
+2
View File
@@ -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);
+28 -15
View File
@@ -253,8 +253,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);
}
@@ -310,12 +312,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);
@@ -333,16 +341,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<std::string> &stagessource, bool vulkan)
Shader *Graphics::newShader(const std::vector<std::string> &stagessource, const Shader::CompileOptions &options)
{
StrongRef<ShaderStage> stages[SHADERSTAGE_MAX_ENUM] = {};
@@ -353,7 +361,7 @@ Shader *Graphics::newShader(const std::vector<std::string> &stagessource, bool v
for (const std::string &source : stagessource)
{
Shader::SourceInfo info = Shader::getSourceInfo(source);
info.vulkan = vulkan;
info.vulkan = options.defines.find("vulkan") != options.defines.end();
bool isanystage = false;
for (int i = 0; i < SHADERSTAGE_MAX_ENUM; i++)
@@ -364,12 +372,12 @@ Shader *Graphics::newShader(const std::vector<std::string> &stagessource, bool v
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++)
@@ -379,7 +387,8 @@ Shader *Graphics::newShader(const std::vector<std::string> &stagessource, bool v
{
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);
}
}
@@ -387,7 +396,7 @@ Shader *Graphics::newShader(const std::vector<std::string> &stagessource, bool v
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);
@@ -395,7 +404,11 @@ Shader *Graphics::newComputeShader(const std::string &source)
throw love::Exception("Could not parse compute shader code (missing 'computemain' function?)");
StrongRef<ShaderStage> 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);
}
@@ -430,7 +443,7 @@ void Graphics::cleanupCachedShaderStage(ShaderStageType type, const std::string
cachedShaderStages[type].erase(hashkey);
}
bool Graphics::validateShader(bool gles, const std::vector<std::string> &stagessource, std::string &err)
bool Graphics::validateShader(bool gles, const std::vector<std::string> &stagessource, const Shader::CompileOptions &options, std::string &err)
{
StrongRef<ShaderStage> stages[SHADERSTAGE_MAX_ENUM] = {};
@@ -456,7 +469,7 @@ bool Graphics::validateShader(bool gles, const std::vector<std::string> &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);
}
}
+5 -5
View File
@@ -449,8 +449,8 @@ public:
SpriteBatch *newSpriteBatch(Texture *texture, int size, BufferDataUsage usage);
ParticleSystem *newParticleSystem(Texture *texture, int size);
Shader *newShader(const std::vector<std::string> &stagessource, bool vulkan = false);
Shader *newComputeShader(const std::string &source);
Shader *newShader(const std::vector<std::string> &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<Buffer::DataDeclaration> &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);
@@ -461,7 +461,7 @@ public:
Text *newText(Font *font, const std::vector<Font::ColoredString> &text = {});
bool validateShader(bool gles, const std::vector<std::string> &stages, std::string &err);
bool validateShader(bool gles, const std::vector<std::string> &stages, const Shader::CompileOptions &options, std::string &err);
/**
* Resets the current color, background color, line style, and so forth.
@@ -798,7 +798,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, uint32 usage, bool sRGB = false) = 0;
/**
* Gets the renderer used by love.graphics.
@@ -966,7 +966,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<ShaderStage> stages[SHADERSTAGE_MAX_ENUM]) = 0;
virtual StreamBuffer *newStreamBuffer(BufferUsage type, size_t size) = 0;
+5 -3
View File
@@ -573,7 +573,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");
@@ -630,8 +630,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";
if (info.vulkan)
ss << "#define USE_VULKAN\n";
for (const auto &def : options.defines)
ss << "#define " + def.first + " " + def.second + "\n";
ss << glsl::global_syntax;
ss << stageinfo.header;
ss << stageinfo.uniforms;
+6 -1
View File
@@ -107,6 +107,11 @@ public:
ACCESS_WRITE = (1 << 1),
};
struct CompileOptions
{
std::map<std::string, std::string> defines;
};
struct SourceInfo
{
Language language;
@@ -237,7 +242,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<ShaderStage> stages[], std::string &err);
+43 -8
View File
@@ -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<Graphics>(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();
}
+1
View File
@@ -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;
+2 -2
View File
@@ -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();
+1 -1
View File
@@ -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, uint32 usage, bool sRGB = false) override;
Renderer getRenderer() const override;
bool usesGLSLES() const override;
RendererInfo getRendererInfo() const override;
+3 -7
View File
@@ -130,11 +130,6 @@ static inline id<MTLTexture> getMTLTexture(love::graphics::Texture *tex)
return tex ? (__bridge id<MTLTexture>)(void *) tex->getHandle() : nil;
}
static inline id<MTLSamplerState> getMTLSampler(love::graphics::Texture *tex)
{
return tex ? (__bridge id<MTLSamplerState>)(void *) tex->getSamplerHandle() : nil;
}
static inline id<MTLTexture> getMTLRenderTarget(love::graphics::Texture *tex)
{
return tex ? (__bridge id<MTLTexture>)(void *) tex->getRenderTargetHandle() : nil;
@@ -348,9 +343,10 @@ Graphics::Graphics()
if (!Shader::standardShaders[i])
{
std::vector<std::string> 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);
}
}
@@ -1782,7 +1778,7 @@ PixelFormat Graphics::getSizedFormat(PixelFormat format, bool /*rendertarget*/,
}
}
bool Graphics::isPixelFormatSupported(PixelFormat format, PixelFormatUsageFlags usage, bool sRGB)
bool Graphics::isPixelFormatSupported(PixelFormat format, uint32 usage, bool sRGB)
{
bool rendertarget = (usage & PIXELFORMATUSAGEFLAGS_RENDERTARGET) != 0;
bool readable = (usage & PIXELFORMATUSAGEFLAGS_SAMPLE) != 0;
+6 -5
View File
@@ -232,11 +232,6 @@ static inline id<MTLTexture> getMTLTexture(love::graphics::Buffer *buffer)
return buffer ? (__bridge id<MTLTexture>)(void *) buffer->getTexelBufferHandle() : nil;
}
static inline id<MTLSamplerState> getMTLSampler(love::graphics::Texture *tex)
{
return tex ? (__bridge id<MTLSamplerState>)(void *) tex->getSamplerHandle() : nil;
}
static inline id<MTLBuffer> getMTLBuffer(love::graphics::Buffer *buffer)
{
return buffer ? (__bridge id<MTLBuffer>)(void *) buffer->getHandle() : nil;
@@ -313,6 +308,12 @@ Shader::Shader(id<MTLDevice> device, StrongRef<love::graphics::ShaderStage> stag
bool forcedefault = false;
bool forwardcompat = true;
#ifdef LOVE_IOS
defaultversion = 320;
defaultprofile = EEsProfile;
forcedefault = true;
#endif
if (!tshader->parse(&defaultTBuiltInResource, defaultversion, defaultprofile, forcedefault, forwardcompat, EShMsgSuppressWarnings))
{
const char *stagename = "unknown";
-2
View File
@@ -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<MTLBlitCommandEncoder> encoder = Graphics::getInstance()->useBlitEncoder();
[encoder generateMipmapsForTexture:texture];
}}
+75 -81
View File
@@ -112,7 +112,7 @@ Graphics::Graphics()
, bufferMapMemory(nullptr)
, bufferMapMemorySize(2 * 1024 * 1024)
, defaultBuffers()
, supportedFormats()
, pixelFormatUsage()
{
gl = OpenGL();
@@ -425,9 +425,10 @@ bool Graphics::setMode(void */*context*/, int width, int height, int pixelwidth,
if (!Shader::standardShaders[i])
{
std::vector<std::string> 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 &)
@@ -1669,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
@@ -1696,111 +1704,97 @@ PixelFormat Graphics::getSizedFormat(PixelFormat format, bool rendertarget, bool
}
}
bool Graphics::isPixelFormatSupported(PixelFormat format, PixelFormatUsageFlags 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
+5 -3
View File
@@ -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, uint32 usage, bool sRGB = false) override;
Renderer getRenderer() const override;
bool usesGLSLES() const override;
RendererInfo getRendererInfo() const override;
@@ -155,6 +155,8 @@ private:
void setDebug(bool enable);
uint32 computePixelFormatUsage(PixelFormat format, bool readable);
std::unordered_map<RenderTargets, GLuint, CachedFBOHasher> framebufferObjects;
bool windowHasStencil;
GLuint mainVAO;
@@ -170,8 +172,8 @@ private:
// Only needed for buffer types that can be bound to shaders.
StrongRef<love::graphics::Buffer> 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
+1 -1
View File
@@ -823,7 +823,7 @@ namespace love {
std::vector<std::string> stages;
stages.push_back(Shader::getDefaultCode(stype, SHADERSTAGE_VERTEX));
stages.push_back(Shader::getDefaultCode(stype, SHADERSTAGE_PIXEL));
Shader::standardShaders[i] = newShader(stages, true);
Shader::standardShaders[i] = newShader(stages, { { {"vulkan", "1"} } });
}
}
}
+1 -1
View File
@@ -68,7 +68,7 @@ namespace love {
void setPointSize(float size) override { std::cout << "setPointSize "; }
void setWireframe(bool enable) override { std::cout << "setWireframe "; }
PixelFormat getSizedFormat(PixelFormat format, bool rendertarget, bool readable) const override { std::cout << "getSizedFormat "; return format; }
bool isPixelFormatSupported(PixelFormat format, PixelFormatUsageFlags usage, bool sRGB = false) override { std::cout << "isPixelFormatSupported "; return true; }
bool isPixelFormatSupported(PixelFormat format, uint32 usage, bool sRGB = false) override { std::cout << "isPixelFormatSupported "; return true; }
Renderer getRenderer() const override { std::cout << "getRenderer "; return RENDERER_VULKAN; }
bool usesGLSLES() const override { std::cout << "usesGLSES "; return false; }
RendererInfo getRendererInfo() const override { std::cout << "getRendererInfo "; return {}; }
+52 -8
View File
@@ -1347,7 +1347,7 @@ int w_newParticleSystem(lua_State *L)
return 1;
}
static int w_getShaderSource(lua_State *L, int startidx, std::vector<std::string> &stages)
static int w_getShaderSource(lua_State *L, int startidx, std::vector<std::string> &stages, Shader::CompileOptions &options)
{
using namespace love::filesystem;
@@ -1369,7 +1369,6 @@ static int w_getShaderSource(lua_State *L, int startidx, std::vector<std::string
lua_replace(L, i);
}
continue;
}
@@ -1412,18 +1411,61 @@ static int w_getShaderSource(lua_State *L, int startidx, std::vector<std::string
if (has_arg2)
stages.push_back(luax_checkstring(L, startidx + 1));
int optionsidx = has_arg2 ? startidx + 2 : startidx + 1;
if (!lua_isnoneornil(L, optionsidx))
{
luaL_checktype(L, optionsidx, LUA_TTABLE);
lua_getfield(L, optionsidx, "defines");
if (!lua_isnoneornil(L, -1))
{
if (!lua_istable(L, -1))
luaL_argerror(L, optionsidx, "expected 'defines' field to be a table");
lua_pushnil(L);
while (lua_next(L, -2))
{
std::string defname;
std::string defval;
if (lua_type(L, -2) == LUA_TNUMBER && lua_type(L, -1) == LUA_TSTRING)
defname = luaL_checkstring(L, -1);
else if (lua_type(L, -2) != LUA_TSTRING)
luaL_argerror(L, optionsidx, "all fields in the 'defines' table must use string keys.");
else
{
defname = luaL_checkstring(L, -2);
if (lua_type(L, -1) == LUA_TBOOLEAN)
defval = luax_toboolean(L, -1) ? "1" : "0";
else
{
const char *val = lua_tostring(L, -1);
if (val == nullptr)
luaL_argerror(L, optionsidx, "'defines' table values must be strings, numbers, or booleans.");
defval = val;
}
}
options.defines[defname] = defval;
lua_pop(L, 1);
}
}
lua_pop(L, 1);
}
return 0;
}
int w_newShader(lua_State *L)
{
std::vector<std::string> 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<std::string> 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<std::string> 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)
{
+1 -1
View File
@@ -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)
+4 -5
View File
@@ -53,7 +53,7 @@ CompressedImageData::CompressedImageData(const std::list<FormatHandler *> &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
+1 -1
View File
@@ -103,7 +103,7 @@ protected:
bool sRGB;
// Single block of memory containing all of the sub-images.
StrongRef<CompressedMemory> memory;
StrongRef<ByteData> memory;
// Texture info for each mipmap level.
std::vector<StrongRef<CompressedSlice>> dataImages;
+3 -20
View File
@@ -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)
{
}
+5 -15
View File
@@ -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<CompressedMemory> memory;
StrongRef<ByteData> memory;
size_t offset;
size_t dataSize;
bool sRGB;
+1 -1
View File
@@ -60,7 +60,7 @@ bool FormatHandler::canParseCompressed(Data* /*data*/)
return false;
}
StrongRef<CompressedMemory> FormatHandler::parseCompressed(Data* /*filedata*/, std::vector<StrongRef<CompressedSlice>>& /*images*/, PixelFormat& /*format*/, bool& /*sRGB*/)
StrongRef<ByteData> FormatHandler::parseCompressed(Data* /*filedata*/, std::vector<StrongRef<CompressedSlice>>& /*images*/, PixelFormat& /*format*/, bool& /*sRGB*/)
{
throw love::Exception("Compressed image parsing is not implemented for this format backend.");
}
+1 -1
View File
@@ -107,7 +107,7 @@ public:
*
* @return The single block of memory containing the parsed images.
**/
virtual StrongRef<CompressedMemory> parseCompressed(Data *filedata,
virtual StrongRef<ByteData> parseCompressed(Data *filedata,
std::vector<StrongRef<CompressedSlice>> &images,
PixelFormat &format, bool &sRGB);
+11 -49
View File
@@ -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)
-1
View File
@@ -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);
+4 -4
View File
@@ -105,7 +105,7 @@ bool ASTCHandler::canParseCompressed(Data *data)
return true;
}
StrongRef<CompressedMemory> ASTCHandler::parseCompressed(Data *filedata, std::vector<StrongRef<CompressedSlice>> &images, PixelFormat &format, bool &sRGB)
StrongRef<ByteData> ASTCHandler::parseCompressed(Data *filedata, std::vector<StrongRef<CompressedSlice>> &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<CompressedMemory> 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<CompressedMemory> memory(new CompressedMemory(totalsize), Acquire::NORETAIN);
StrongRef<ByteData> 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);
+1 -1
View File
@@ -43,7 +43,7 @@ public:
// Implements FormatHandler.
bool canParseCompressed(Data *data) override;
StrongRef<CompressedMemory> parseCompressed(Data *filedata,
StrongRef<ByteData> parseCompressed(Data *filedata,
std::vector<StrongRef<CompressedSlice>> &images,
PixelFormat &format, bool &sRGB) override;
+3 -4
View File
@@ -298,7 +298,7 @@ bool KTXHandler::canParseCompressed(Data *data)
return true;
}
StrongRef<CompressedMemory> KTXHandler::parseCompressed(Data *filedata, std::vector<StrongRef<CompressedSlice>> &images, PixelFormat &format, bool &sRGB)
StrongRef<ByteData> KTXHandler::parseCompressed(Data *filedata, std::vector<StrongRef<CompressedSlice>> &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<CompressedMemory> KTXHandler::parseCompressed(Data *filedata, std::vec
fileoffset += mipsizepadded;
}
StrongRef<CompressedMemory> memory;
memory.set(new CompressedMemory(totalsize), Acquire::NORETAIN);
StrongRef<ByteData> 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<CompressedMemory> 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);
+1 -1
View File
@@ -42,7 +42,7 @@ public:
// Implements FormatHandler.
bool canParseCompressed(Data *data) override;
StrongRef<CompressedMemory> parseCompressed(Data *filedata,
StrongRef<ByteData> parseCompressed(Data *filedata,
std::vector<StrongRef<CompressedSlice>> &images,
PixelFormat &format, bool &sRGB) override;
+3 -4
View File
@@ -114,7 +114,7 @@ bool PKMHandler::canParseCompressed(Data *data)
return true;
}
StrongRef<CompressedMemory> PKMHandler::parseCompressed(Data *filedata, std::vector<StrongRef<CompressedSlice>> &images, PixelFormat &format, bool &sRGB)
StrongRef<ByteData> PKMHandler::parseCompressed(Data *filedata, std::vector<StrongRef<CompressedSlice>> &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<CompressedMemory> 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<CompressedMemory> memory;
memory.set(new CompressedMemory(totalsize), Acquire::NORETAIN);
StrongRef<ByteData> 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.)
+1 -1
View File
@@ -42,7 +42,7 @@ public:
// Implements FormatHandler.
bool canParseCompressed(Data *data) override;
StrongRef<CompressedMemory> parseCompressed(Data *filedata,
StrongRef<ByteData> parseCompressed(Data *filedata,
std::vector<StrongRef<CompressedSlice>> &images,
PixelFormat &format, bool &sRGB) override;
+4 -4
View File
@@ -475,7 +475,7 @@ bool PVRHandler::canParseCompressed(Data *data)
return false;
}
StrongRef<CompressedMemory> PVRHandler::parseCompressed(Data *filedata, std::vector<StrongRef<CompressedSlice>> &images, PixelFormat &format, bool &sRGB)
StrongRef<ByteData> PVRHandler::parseCompressed(Data *filedata, std::vector<StrongRef<CompressedSlice>> &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<CompressedMemory> PVRHandler::parseCompressed(Data *filedata, std::vec
if (filedata->getSize() < fileoffset + totalsize)
throw love::Exception("Could not parse PVR file: invalid size calculation.");
StrongRef<CompressedMemory> memory;
memory.set(new CompressedMemory(totalsize), Acquire::NORETAIN);
;
StrongRef<ByteData> memory(new ByteData(totalsize, false), Acquire::NORETAIN);
size_t curoffset = 0;
const uint8 *filebytes = (uint8 *) filedata->getData() + fileoffset;
@@ -541,7 +541,7 @@ StrongRef<CompressedMemory> 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);
+1 -1
View File
@@ -40,7 +40,7 @@ public:
// Implements FormatHandler.
bool canParseCompressed(Data *data) override;
StrongRef<CompressedMemory> parseCompressed(Data *filedata,
StrongRef<ByteData> parseCompressed(Data *filedata,
std::vector<StrongRef<CompressedSlice>> &images,
PixelFormat &format, bool &sRGB) override;
+3 -4
View File
@@ -229,7 +229,7 @@ bool DDSHandler::canParseCompressed(Data *data)
return dds::isCompressedDDS(data->getData(), data->getSize());
}
StrongRef<CompressedMemory> DDSHandler::parseCompressed(Data *filedata, std::vector<StrongRef<CompressedSlice>> &images, PixelFormat &format, bool &sRGB)
StrongRef<ByteData> DDSHandler::parseCompressed(Data *filedata, std::vector<StrongRef<CompressedSlice>> &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<CompressedMemory> DDSHandler::parseCompressed(Data *filedata, std::vec
bool isSRGB = false;
bool bgra = false;
StrongRef<CompressedMemory> memory;
size_t dataSize = 0;
images.clear();
@@ -261,7 +260,7 @@ StrongRef<CompressedMemory> DDSHandler::parseCompressed(Data *filedata, std::vec
dataSize += img->dataSize;
}
memory.set(new CompressedMemory(dataSize), Acquire::NORETAIN);
StrongRef<ByteData> memory(new ByteData(dataSize, false), Acquire::NORETAIN);
size_t dataOffset = 0;
@@ -272,7 +271,7 @@ StrongRef<CompressedMemory> 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);
+1 -1
View File
@@ -46,7 +46,7 @@ public:
bool canDecode(Data *data) override;
DecodedImage decode(Data *data) override;
bool canParseCompressed(Data *data) override;
StrongRef<CompressedMemory> parseCompressed(Data *filedata,
StrongRef<ByteData> parseCompressed(Data *filedata,
std::vector<StrongRef<CompressedSlice>> &images,
PixelFormat &format, bool &sRGB) override;
+9 -3
View File
@@ -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)
+20
View File
@@ -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<Keyboard::Key, Keyboard::KEY_MAX_ENUM>::Entry Keyboard::keyEntries[] =
{
{"unknown", Keyboard::KEY_UNKNOWN},
@@ -521,5 +531,15 @@ StringMap<Keyboard::Scancode, Keyboard::SCANCODE_MAX_ENUM>::Entry Keyboard::scan
StringMap<Keyboard::Scancode, Keyboard::SCANCODE_MAX_ENUM> Keyboard::scancodes(Keyboard::scancodeEntries, sizeof(Keyboard::scancodeEntries));
StringMap<Keyboard::ModifierKey, Keyboard::MODKEY_MAX_ENUM>::Entry Keyboard::modifierEntries[] =
{
{"numlock", MODKEY_NUMLOCK},
{"capslock", MODKEY_CAPSLOCK},
{"scrolllock", MODKEY_SCROLLLOCK},
{"mode", MODKEY_MODE},
};
StringMap<Keyboard::ModifierKey, Keyboard::MODKEY_MAX_ENUM> Keyboard::modifiers(Keyboard::modifierEntries, sizeof(Keyboard::modifierEntries));
} // keyboard
} // love
+25
View File
@@ -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<Scancode> &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<Key, KEY_MAX_ENUM>::Entry keyEntries[];
@@ -600,6 +622,9 @@ private:
static StringMap<Scancode, SCANCODE_MAX_ENUM>::Entry scancodeEntries[];
static StringMap<Scancode, SCANCODE_MAX_ENUM> scancodes;
static StringMap<ModifierKey, MODKEY_MAX_ENUM>::Entry modifierEntries[];
static StringMap<ModifierKey, MODKEY_MAX_ENUM> modifiers;
}; // Keyboard
} // keyboard
+26
View File
@@ -18,9 +18,16 @@
* 3. This notice may not be removed or altered from any source distribution.
**/
#include <SDL_version.h>
#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<Scancode> &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;
+1
View File
@@ -48,6 +48,7 @@ public:
bool hasKeyRepeat() const;
bool isDown(const std::vector<Key> &keylist) const;
bool isScancodeDown(const std::vector<Scancode> &scancodelist) const;
bool isModifierActive(ModifierKey key) const;
Key getKeyFromScancode(Scancode scancode) const;
Scancode getScancodeFromKey(Key key) const;
+12
View File
@@ -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 }
};
+21 -6
View File
@@ -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
@@ -145,7 +157,7 @@ function love.init()
minheight = 1,
fullscreen = false,
fullscreentype = "desktop",
display = 1,
displayindex = 1,
vsync = 1,
msaa = 0,
borderless = false,
@@ -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"..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
+9
View File
@@ -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()
@@ -120,6 +126,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)
+12 -2
View File
@@ -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
+14 -5
View File
@@ -23,7 +23,8 @@
// LOVE
#include "common/Object.h"
#include "filesystem/File.h"
#include "common/Stream.h"
#include "common/StringMap.h"
#include <string>
@@ -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> data;
// A readable stream containing the encoded data.
StrongRef<Stream> stream;
// When the decoder decodes data incrementally, it writes
// this many bytes at a time (at most).
+3 -3
View File
@@ -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
+18 -81
View File
@@ -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 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.");
throw love::Exception("Could not 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<UInt32> 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<Stream> s(stream->clone(), Acquire::NORETAIN);
return new CoreAudioDecoder(s, bufferSize);
}
int CoreAudioDecoder::decode()
+10 -12
View File
@@ -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:
+20 -24
View File
@@ -22,6 +22,7 @@
#include "FLACDecoder.h"
#include <set>
#include <algorithm>
#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<int64>(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 (int i = 0; !(supported[i].empty()); i++)
{
if (supported[i].compare(ext) == 0)
return true;
}
return false;
}
love::sound::Decoder *FLACDecoder::clone()
{
return new FLACDecoder(data.get(), bufferSize);
StrongRef<Stream> s(stream->clone(), Acquire::NORETAIN);
return new FLACDecoder(s, bufferSize);
}
int FLACDecoder::decode()
+12 -13
View File
@@ -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
-154
View File
@@ -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 (int i = 0; !(supported[i].empty()); i++)
{
if (supported[i].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<short*>(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<long>(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
+122 -27
View File
@@ -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,121 @@ namespace sound
namespace lullaby
{
MP3Decoder::MP3Decoder(Data *data, int bufferSize)
: Decoder(data, bufferSize)
// 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<uint8> 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;
}
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<int64>(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 DRMP3_FALSE;
}
if (pos < decoder->offset)
return DRMP3_FALSE;
return decoder->stream->seek(pos, Stream::SEEKORIGIN_BEGIN) ? DRMP3_TRUE : DRMP3_FALSE;
}
MP3Decoder::MP3Decoder(Stream *stream, int 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_memory(&mp3, data->getData(), data->getSize(), nullptr, nullptr) == 0)
if (!drmp3_init(&mp3, onRead, onSeek, this, nullptr, nullptr))
throw love::Exception("Could not read mp3 data.");
sampleRate = mp3.sampleRate;
@@ -43,25 +154,24 @@ 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);
// 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 seek table");
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()))
{
drmp3_uninit(&mp3);
throw love::Exception("Could not bind seek table");
throw love::Exception("Could not bind mp3 seek table");
}
}
@@ -70,25 +180,10 @@ MP3Decoder::~MP3Decoder()
drmp3_uninit(&mp3);
}
bool MP3Decoder::accepts(const std::string &ext)
{
static const std::string supported[] =
{
"mp3", ""
};
for (int i = 0; !(supported[i].empty()); i++)
{
if (supported[i].compare(ext) == 0)
return true;
}
return false;
}
love::sound::Decoder *MP3Decoder::clone()
{
return new MP3Decoder(data, bufferSize);
StrongRef<Stream> s(stream->clone(), Acquire::NORETAIN);
return new MP3Decoder(s, bufferSize);
}
int MP3Decoder::decode()
@@ -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)
+14 -11
View File
@@ -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,25 +41,28 @@ 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:
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;
// Used for fast seeking
std::vector<drmp3_seek_point> seekTable;
// Position of first MP3 frame found
int64 offset;
double duration;
}; // MP3Decoder
+28 -28
View File
@@ -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,33 +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 (int i = 0; !(supported[i].empty()); i++)
{
if (supported[i].compare(ext) == 0)
return true;
}
return false;
}
love::sound::Decoder *ModPlugDecoder::clone()
{
return new ModPlugDecoder(data.get(), bufferSize);
StrongRef<Stream> s(stream->clone(), Acquire::NORETAIN);
return new ModPlugDecoder(s, bufferSize);
}
int ModPlugDecoder::decode()
+13 -13
View File
@@ -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> data;
ModPlugFile *plug;
ModPlug_Settings settings;
double duration;
}; // Decoder
}; // ModPlugDecoder
} // lullaby
} // sound
+14 -34
View File
@@ -27,7 +27,6 @@
#include "ModPlugDecoder.h"
#include "VorbisDecoder.h"
#include "GmeDecoder.h"
#include "WaveDecoder.h"
#include "FLACDecoder.h"
#include "MP3Decoder.h"
@@ -38,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<typename DecoderType>
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;
}
@@ -77,43 +71,29 @@ 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<DecoderImpl> possibleDecoders = {
#ifndef LOVE_NO_MODPLUG
DecoderImplFor<ModPlugDecoder>(),
#endif // LOVE_NO_MODPLUG
DecoderImplFor<MP3Decoder>(),
DecoderImplFor<WaveDecoder>(),
DecoderImplFor<FLACDecoder>(),
DecoderImplFor<VorbisDecoder>(),
#ifdef LOVE_SUPPORT_GME
DecoderImplFor<GmeDecoder>(),
#endif // LOVE_SUPPORT_GME
#ifdef LOVE_SUPPORT_COREAUDIO
DecoderImplFor<CoreAudioDecoder>(),
#endif
DecoderImplFor<WaveDecoder>(),
DecoderImplFor<FLACDecoder>(),
// DecoderImplFor<OtherDecoder>(),
DecoderImplFor<MP3Decoder>(),
#ifndef LOVE_NO_MODPLUG
DecoderImplFor<ModPlugDecoder>(), // 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)
@@ -122,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;
+2 -9
View File
@@ -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
+33 -109
View File
@@ -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 (int i = 0; !(supported[i].empty()); i++)
{
if (supported[i].compare(ext) == 0)
return true;
}
return false;
}
love::sound::Decoder *VorbisDecoder::clone()
{
return new VorbisDecoder(data.get(), bufferSize);
StrongRef<Stream> 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);
+15 -27
View File
@@ -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
+15 -38
View File
@@ -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 (int i = 0; !(supported[i].empty()); i++)
{
if (supported[i].compare(ext) == 0)
return true;
}
return false;
}
love::sound::Decoder *WaveDecoder::clone()
{
return new WaveDecoder(data.get(), bufferSize);
StrongRef<Stream> s(stream->clone(), Acquire::NORETAIN);
return new WaveDecoder(s, bufferSize);
}
int WaveDecoder::decode()
+11 -22
View File
@@ -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;
+40 -7
View File
@@ -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<love::filesystem::FileData> 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<Data>(L, 1);
luax_catchexcept(L, [&]() { stream = new data::DataStream(data); });
}
else
{
stream = luax_checktype<Stream>(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;
+1 -1
View File
@@ -164,7 +164,7 @@ bool System::openURL(const std::string &url) const
#endif
return (int) result > 32;
return (ptrdiff_t) result > 32;
#endif
}
+13
View File
@@ -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<std::string> getPreferredLocales() const = 0;
static bool getConstant(const char *in, PowerState &out);
static bool getConstant(PowerState in, const char *&out);
+26
View File
@@ -25,6 +25,8 @@
// SDL
#include <SDL_clipboard.h>
#include <SDL_cpuinfo.h>
#include <SDL_locale.h>
#include <SDL_version.h>
namespace love
{
@@ -90,6 +92,30 @@ love::system::System::PowerState System::getPowerInfo(int &seconds, int &percent
return state;
}
std::vector<std::string> System::getPreferredLocales() const
{
std::vector<std::string> 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<System::PowerState, SDL_PowerState, System::POWER_MAX_ENUM>::Entry System::powerEntries[] =
{
{System::POWER_UNKNOWN, SDL_POWERSTATE_UNKNOWN},
+1
View File
@@ -51,6 +51,7 @@ public:
std::string getClipboardText() const;
PowerState getPowerInfo(int &seconds, int &percent) const;
std::vector<std::string> getPreferredLocales() const override;
private:
+17
View File
@@ -101,6 +101,22 @@ int w_hasBackgroundMusic(lua_State *L)
return 1;
}
int w_getPreferredLocales(lua_State* L)
{
int i = 1;
std::vector<std::string> 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 }
};

Some files were not shown because too many files have changed in this diff Show More