Browse Source

Merge branch 'main' into 12.0-development

Alex Szpakowski 3 years ago
parent
commit
c8038f22df
37 changed files with 487 additions and 158 deletions
  1. 18 25
      .github/workflows/main.yml
  2. 9 0
      changes.txt
  3. 3 1
      platform/xcode/liblove.xcodeproj/project.pbxproj
  4. 1 1
      platform/xcode/love.xcodeproj/project.pbxproj
  5. 4 4
      readme.md
  6. 154 55
      src/common/android.cpp
  7. 2 0
      src/common/android.h
  8. 5 1
      src/common/runtime.cpp
  9. 40 14
      src/libraries/enet/enet.cpp
  10. 1 1
      src/libraries/luasocket/libluasocket/ftp.lua.h
  11. 1 1
      src/libraries/luasocket/libluasocket/headers.lua.h
  12. 1 1
      src/libraries/luasocket/libluasocket/http.lua.h
  13. 1 1
      src/libraries/luasocket/libluasocket/ltn12.lua.h
  14. 1 1
      src/libraries/luasocket/libluasocket/mbox.lua.h
  15. 1 1
      src/libraries/luasocket/libluasocket/mime.lua.h
  16. 1 1
      src/libraries/luasocket/libluasocket/smtp.lua.h
  17. 1 1
      src/libraries/luasocket/libluasocket/socket.lua.h
  18. 1 1
      src/libraries/luasocket/libluasocket/tp.lua.h
  19. 1 1
      src/libraries/luasocket/libluasocket/url.lua.h
  20. 16 8
      src/love.cpp
  21. 11 0
      src/modules/data/wrap_ByteData.cpp
  22. 12 1
      src/modules/data/wrap_DataView.cpp
  23. 1 1
      src/modules/event/wrap_Event.cpp
  24. 7 5
      src/modules/filesystem/physfs/Filesystem.cpp
  25. 38 2
      src/modules/filesystem/wrap_Filesystem.cpp
  26. 4 7
      src/modules/graphics/Font.cpp
  27. 1 1
      src/modules/graphics/ParticleSystem.cpp
  28. 1 1
      src/modules/graphics/wrap_Graphics.cpp
  29. 1 1
      src/modules/graphics/wrap_Video.cpp
  30. 27 4
      src/modules/joystick/sdl/Joystick.cpp
  31. 1 1
      src/modules/love/callbacks.lua
  32. 71 0
      src/modules/love/jitsetup.lua
  33. 27 8
      src/modules/love/love.cpp
  34. 3 0
      src/modules/love/love.h
  35. 1 1
      src/modules/math/wrap_Math.cpp
  36. 18 5
      src/modules/video/theora/OggDemuxer.cpp
  37. 1 1
      src/modules/video/theora/OggDemuxer.h

+ 18 - 25
.github/workflows/main.yml

@@ -9,37 +9,30 @@ jobs:
       run: sudo apt-get update
     - name: Install Dependencies
       run: |
-        sudo apt-get install --assume-yes build-essential autotools-dev automake libtool pkg-config \
-                                          libfreetype6-dev libluajit-5.1-dev libsdl2-dev libopenal-dev \
-                                          libogg-dev libvorbis-dev libmodplug-dev libmpg123-dev libtheora-dev
-    - name: Checkout
+        sudo apt-get install --assume-yes build-essential git make cmake autoconf automake \
+                                          libtool pkg-config libasound2-dev libpulse-dev libaudio-dev \
+                                          libjack-dev libx11-dev libxext-dev libxrandr-dev libxcursor-dev \
+                                          libxfixes-dev libxi-dev libxinerama-dev libxxf86vm-dev libxss-dev \
+                                          libgl1-mesa-dev libdbus-1-dev libudev-dev libgles2-mesa-dev \
+                                          libegl1-mesa-dev libibus-1.0-dev fcitx-libs-dev libsamplerate0-dev \
+                                          libsndio-dev libwayland-dev libxkbcommon-dev libdrm-dev libgbm-dev
+    - name: Checkout love-appimage-source
       uses: actions/checkout@v2
-    - name: Pre-Configure
-      run: $PWD/platform/unix/automagic
-    - name: Configure
-      run: mkdir build && cd build && ../configure
-    - name: Build
-      run: cd build && make -j2
-    - name: Prepare appimagetool
-      run: |
-        cd build &&
-        wget https://github.com/AppImage/AppImageKit/releases/download/continuous/appimagetool-x86_64.AppImage -O appimagetool &&
-        chmod +x appimagetool &&
-        sudo apt install -y appstream
-    - name: Clone love-appimages
+      with:
+        repository: MikuAuahDark/love-appimage-source
+    - name: Checkout LÖVE
       uses: actions/checkout@v2
       with:
-        path: build/love-appimages
-        repository: pfirsich/love-appimages
+        path: love2d-${{ github.sha }}
     - name: Build AppImage
-      run: |
-        cd build &&
-        python3 love-appimages/build.py .. AppDir --builddir build --appimage love.AppImage
+      run: make LOVE_BRANCH=${{ github.sha }}
+    - name: Print LuaJIT branch
+      run: git -C LuaJIT-v2.1 branch -v
     - name: Artifact
-      uses: actions/upload-artifact@v2-preview
+      uses: actions/upload-artifact@v2
       with:
-        name: love.AppImage
-        path: build/love.AppImage
+        name: love-x86_64.AppImage
+        path: love-${{ github.sha }}.AppImage
   windows-os:
     runs-on: windows-latest
     strategy:

+ 9 - 0
changes.txt

@@ -38,12 +38,14 @@ LOVE 11.4 [Mysterious Mysteries]
 
 Released: N/A
 
+* Added native arm64 support on macOS.
 * Added Body:getLocalPoints.
 * Added Font:getKerning.
 * Added support for r16, rg16, and rgba16 pixel formats in Canvases.
 * Added Shader:send(name, matrixlayout, data, ...) variant, whose argument order is more consistent than Shader:send(name, data, matrixlayout, ...).
 
 * Changed love.timer.getTime to start at 0 when the module is first loaded.
+* Changed certain out-of-Lua-memory situations to show a message box instead of instantly crashing.
 
 * Fixed build-time compatibility with Lua 5.4.
 * Fixed code compatibility with math.mod and string.gfind when LuaJIT 2.1 is used.
@@ -54,21 +56,28 @@ Released: N/A
 * Fixed support for > 2GB dropped files on desktops.
 * Fixed love.physics meter scale value persisting after love.event.quit("restart").
 * Fixed audio to resume properly after interruption on iOS.
+* Fixed love.graphics.newVideo to error instead of crash when an invalid video file is given.
 * Fixed initial window creation to set the window's title during creation instead of after.
 * Fixed the window's screen position when exiting fullscreen via love.window.setFullscreen.
+* Fixed love.displayrotated being given a boolean instead of an enum string.
 * Fixed memory corruption and a crash when drawing smooth lines.
 * Fixed a crash in Canvas:newImageData when the pixel format's pixel byte size multiplied by its width isn't a multiple of 4.
 * Fixed love.graphics.newVolumeImage when explicit mipmaps are provided.
 * Fixed freezes and crashes in automatic batching when an AMD GPU is used.
 * Fixed love.graphics.print and Image:replacePixels on more AMD/ATI GPUs.
 * Fixed Font:setFallbacks to account for different DPI scales in each fallback font.
+* Fixed Font:getWrap to not remove trailing newlines.
 * Fixed Text:getWidth when the Text's string only contains spaces.
 * Fixed a crash with some Intel graphics drivers on Linux.
 * Fixed a hang with some Intel graphics drivers on Windows, by preventing gamma correct rendering on affected systems.
 * Fixed a crash with some Intel graphics drivers on Windows when mipmapped Canvases are used.
 * Fixed texture memory reported by love.graphics.getStats when a volume or array Canvas is created.
+* Fixed DXT1 textures which use 1 bit alpha-cutout.
 * Fixed rare issues where textures were not sent to shaders correctly.
 * Fixed Shader:send(name, data, matrixlayout, ...).
+* Fixed quad offsets in ParticleSystems when ParticleSystem:setOffset is not used.
+* Fixed rounded rectangles breaking if the rx or ry parameters are negative.
+* Fixed rounded rectangle automatic points calculation when rx or ry are more than half the rectangle's size.
 * Fixed source code compilation on Xcode 12+.
 * Fixed source code compilation on Linux systems that don't provide posix_spawn APIs.
 

+ 3 - 1
platform/xcode/liblove.xcodeproj/project.pbxproj

@@ -1769,6 +1769,7 @@
 		FA620A301AA2F8DB005DB4C2 /* wrap_Texture.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = wrap_Texture.cpp; sourceTree = "<group>"; };
 		FA620A311AA2F8DB005DB4C2 /* wrap_Texture.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = wrap_Texture.h; sourceTree = "<group>"; };
 		FA620A391AA305F6005DB4C2 /* types.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = types.cpp; sourceTree = "<group>"; };
+		FA69B918273828DD00CDC2E7 /* jitsetup.lua */ = {isa = PBXFileReference; lastKnownFileType = text; path = jitsetup.lua; sourceTree = "<group>"; };
 		FA6A2B641F5F7B6B0074C308 /* wrap_Data.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = wrap_Data.h; sourceTree = "<group>"; };
 		FA6A2B651F5F7B6B0074C308 /* wrap_Data.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = wrap_Data.cpp; sourceTree = "<group>"; };
 		FA6A2B681F5F7F560074C308 /* DataView.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = DataView.cpp; sourceTree = "<group>"; };
@@ -2850,6 +2851,7 @@
 				FA1E95B4271F932B0044CF08 /* arg.lua */,
 				FA577A8D16C71D3600860150 /* boot.lua */,
 				FA1E95B5271F932B0044CF08 /* callbacks.lua */,
+				FA69B918273828DD00CDC2E7 /* jitsetup.lua */,
 				FA0B7BFE1A95902C000E1D17 /* love.cpp */,
 				FA0B7BFF1A95902C000E1D17 /* love.h */,
 			);
@@ -4208,7 +4210,7 @@
 		08FB7793FE84155DC02AAC07 /* Project object */ = {
 			isa = PBXProject;
 			attributes = {
-				LastUpgradeCheck = 1250;
+				LastUpgradeCheck = 1310;
 				TargetAttributes = {
 					FA0B78DC1A958B90000E1D17 = {
 						CreatedOnToolsVersion = 6.1.1;

+ 1 - 1
platform/xcode/love.xcodeproj/project.pbxproj

@@ -324,7 +324,7 @@
 		29B97313FDCFA39411CA2CEA /* Project object */ = {
 			isa = PBXProject;
 			attributes = {
-				LastUpgradeCheck = 1250;
+				LastUpgradeCheck = 1310;
 				TargetAttributes = {
 					FA0B7F051A95AAF3000E1D17 = {
 						CreatedOnToolsVersion = 6.1.1;

+ 4 - 4
readme.md

@@ -83,16 +83,16 @@ Dependencies
 - Vorbisfile
 - Theora
 
-[site]: http://love2d.org
-[wiki]: http://love2d.org/wiki
-[forums]: http://love2d.org/forums
+[site]: https://love2d.org
+[wiki]: https://love2d.org/wiki
+[forums]: https://love2d.org/forums
 [discord]: https://discord.gg/rhUets9
 [irc]: irc://irc.oftc.net/love
 [dependencies-macos]: https://github.com/slime73/love-apple-dependencies
 [dependencies-ios]: https://github.com/love2d/love/releases
 [megasource]: https://github.com/love2d/megasource
 [unstableppa]: https://launchpad.net/~bartbes/+archive/love-unstable
-[aur]: http://aur.archlinux.org/packages/love-git
+[aur]: https://aur.archlinux.org/packages/love-git
 [love-experiments]: https://github.com/slime73/love-experiments
 [codestyle]: https://love2d.org/wiki/Code_Style
 [android-repository]: https://github.com/love2d/love-android

+ 154 - 55
src/common/android.cpp

@@ -333,24 +333,59 @@ void showRecordingPermissionMissingDialog()
 	env->DeleteLocalRef(activity);
 }
 
+/* A container for AssetManager Java object */
+class AssetManagerObject
+{
+public:
+	AssetManagerObject()
+	{
+		JNIEnv *env = (JNIEnv *) SDL_AndroidGetJNIEnv();
+		jobject am = getLocalAssetManager(env);
+
+		assetManager = env->NewGlobalRef(am);
+		env->DeleteLocalRef(am);
+	}
+
+	~AssetManagerObject()
+	{
+		JNIEnv *env = (JNIEnv *) SDL_AndroidGetJNIEnv();
+		env->DeleteGlobalRef(assetManager);
+	}
+
+	static jobject getLocalAssetManager(JNIEnv *env) {
+		jobject self = (jobject) SDL_AndroidGetActivity();
+		jclass activity = env->GetObjectClass(self);
+		jmethodID method = env->GetMethodID(activity, "getAssets", "()Landroid/content/res/AssetManager;");
+		jobject am = env->CallObjectMethod(self, method);
+
+		env->DeleteLocalRef(self);
+		env->DeleteLocalRef(activity);
+		return am;
+	}
+
+	explicit operator jobject()
+	{
+		return assetManager;
+	};
+private:
+	jobject assetManager;
+};
+
 /*
  * Helper functions to aid new fusing method
  */
+
+// This returns *global* reference, no need to free it.
+static jobject getJavaAssetManager()
+{
+	static AssetManagerObject assetManager;
+	return (jobject) assetManager;
+}
+
 static AAssetManager *getAssetManager()
 {
 	JNIEnv *env = (JNIEnv*) SDL_AndroidGetJNIEnv();
-	jobject self = (jobject) SDL_AndroidGetActivity();
-	jclass activity = env->GetObjectClass(self);
-	jmethodID method = env->GetMethodID(activity, "getAssetManager", "()Landroid/content/res/AssetManager;");
-
-	jobject assetManager = env->CallObjectMethod(self, method);
-	AAssetManager *assetManagerObject = AAssetManager_fromJava(env, assetManager);
-
-	env->DeleteLocalRef(assetManager);
-	env->DeleteLocalRef(activity);
-	env->DeleteLocalRef(self);
-
-	return assetManagerObject;
+	return AAssetManager_fromJava(env, (jobject) getJavaAssetManager());
 }
 
 namespace aasset
@@ -368,35 +403,6 @@ struct AssetInfo
 
 static std::unordered_map<std::string, PHYSFS_FileType> fileTree;
 
-// Workaround AAsset can't detect whetever something is a directory or doesn't exist
-static void buildFileLookup(
-	AAssetManager *assetManager,
-	std::unordered_map<std::string, PHYSFS_FileType> &out,
-	const std::string &path = ""
-)
-{
-	if (!path.empty())
-	{
-		AAsset *test = AAssetManager_open(assetManager, path.c_str(), AASSET_MODE_STREAMING);
-		if (test)
-		{
-			AAsset_close(test);
-			out[path] = PHYSFS_FILETYPE_REGULAR;
-			return;
-		}
-
-		out[path] = PHYSFS_FILETYPE_DIRECTORY;
-	}
-
-	AAssetDir *dir = AAssetManager_openDir(assetManager, path.c_str());
-	const char *file;
-
-	while ((file = AAssetDir_getNextFileName(dir)) != nullptr)
-		buildFileLookup(assetManager, out, file);
-
-	AAssetDir_close(dir);
-}
-
 PHYSFS_sint64 read(PHYSFS_Io *io, void *buf, PHYSFS_uint64 len)
 {
 	AAsset *asset = ((AssetInfo *) io->opaque)->asset;
@@ -505,7 +511,31 @@ void *openArchive(PHYSFS_Io *io, const char *name, int forWrite, int *claimed)
 	AAssetManager *assetManager = getAssetManager();
 
 	if (io::fileTree.empty())
-		io::buildFileLookup(assetManager, io::fileTree);
+	{
+		// AAssetDir_getNextFileName intentionally excludes directories, so
+		// we have to use JNI that calls AssetManager.list() recursively.
+		JNIEnv *env = (JNIEnv *) SDL_AndroidGetJNIEnv();
+		jobject activity = (jobject) SDL_AndroidGetActivity();
+		jclass clazz = env->GetObjectClass(activity);
+
+		jmethodID method = env->GetMethodID(clazz, "buildFileTree", "()[Ljava/lang/String;");
+		jobjectArray list = (jobjectArray) env->CallObjectMethod(activity, method);
+
+		for (jsize i = 0; i < env->GetArrayLength(list); i++)
+		{
+			jstring jstr = (jstring) env->GetObjectArrayElement(list, i);
+			const char *str = env->GetStringUTFChars(jstr, nullptr);
+
+			io::fileTree[str + 1] = str[0] == 'd' ? PHYSFS_FILETYPE_DIRECTORY : PHYSFS_FILETYPE_REGULAR;
+
+			env->ReleaseStringUTFChars(jstr, str);
+			env->DeleteLocalRef(jstr);
+		}
+
+		env->DeleteLocalRef(list);
+		env->DeleteLocalRef(clazz);
+		env->DeleteLocalRef(activity);
+	}
 
 	return assetManager;
 }
@@ -518,33 +548,60 @@ PHYSFS_EnumerateCallbackResult enumerate(
 	void *callbackdata
 )
 {
+	using FileTreeIterator = std::unordered_map<std::string, PHYSFS_FileType>::iterator;
+	LOVE_UNUSED(opaque);
+
 	const char *path = dirname;
 	if (path == nullptr || (path[0] == '/' && path[1] == 0))
 		path = "";
 
-	AAssetManager *assetManager = (AAssetManager *) opaque;
-	AAssetDir *dir = AAssetManager_openDir(assetManager, path);
-
-	if (dir == nullptr)
+	if (path[0] != 0)
 	{
-		PHYSFS_setErrorCode(PHYSFS_ERR_NOT_FOUND);
-		return PHYSFS_ENUM_ERROR;
+		FileTreeIterator result = io::fileTree.find(path);
+
+		if (result == io::fileTree.end() || result->second != PHYSFS_FILETYPE_DIRECTORY)
+		{
+			PHYSFS_setErrorCode(PHYSFS_ERR_NOT_FOUND);
+			return PHYSFS_ENUM_ERROR;
+		}
 	}
 
+	JNIEnv *env = (JNIEnv *) SDL_AndroidGetJNIEnv();
+	jobject assetManager = getJavaAssetManager();
+	jclass clazz = env->GetObjectClass(assetManager);
+	jmethodID method = env->GetMethodID(clazz, "list", "(Ljava/lang/String;)[Ljava/lang/String;");
+
+	jstring jstringDir = env->NewStringUTF(path);
+	jobjectArray dir = (jobjectArray) env->CallObjectMethod(assetManager, method, jstringDir);
+
 	PHYSFS_EnumerateCallbackResult ret = PHYSFS_ENUM_OK;
 
-	while (ret == PHYSFS_ENUM_OK)
+	if (env->ExceptionCheck())
+	{
+		// IOException occured
+		ret = PHYSFS_ENUM_ERROR;
+		env->ExceptionClear();
+	}
+	else
 	{
-		const char *name = AAssetDir_getNextFileName(dir);
+		jsize i = 0;
+		jsize len = env->GetArrayLength(dir);
+
+		while (ret == PHYSFS_ENUM_OK && i < len) {
+			jstring jstr = (jstring) env->GetObjectArrayElement(dir, i++);
+			const char *name = env->GetStringUTFChars(jstr, nullptr);
 
-		// No more files?
-		if (name == nullptr)
-			break;
+			ret = cb(callbackdata, origdir, name);
+
+			env->ReleaseStringUTFChars(jstr, name);
+			env->DeleteLocalRef(jstr);
+		}
 
-		ret = cb(callbackdata, origdir, name);
+		env->DeleteLocalRef(dir);
 	}
 
-	AAssetDir_close(dir);
+	env->DeleteLocalRef(jstringDir);
+	env->DeleteLocalRef(clazz);
 	return ret;
 }
 
@@ -724,6 +781,48 @@ bool checkFusedGame(void **physfsIO_Out)
 	return false;
 }
 
+const char *getCRequirePath()
+{
+	static bool initialized = false;
+	static const char *path = nullptr;
+
+	if (!initialized)
+	{
+		JNIEnv *env = (JNIEnv*) SDL_AndroidGetJNIEnv();
+		jobject activity = (jobject) SDL_AndroidGetActivity();
+
+		jclass clazz(env->GetObjectClass(activity));
+		jmethodID method_id = env->GetMethodID(clazz, "getCRequirePath", "()Ljava/lang/String;");
+
+		path = "";
+		initialized = true;
+
+		if (method_id)
+		{
+			jstring cpath = (jstring) env->CallObjectMethod(activity, method_id);
+			const char *utf = env->GetStringUTFChars(cpath, nullptr);
+			if (utf)
+			{
+				path = SDL_strdup(utf);
+				env->ReleaseStringUTFChars(cpath, utf);
+			}
+
+			env->DeleteLocalRef(cpath);
+		}
+		else
+		{
+			// NoSuchMethodException is thrown in case methodID is null
+			env->ExceptionClear();
+			return "";
+		}
+
+		env->DeleteLocalRef(activity);
+		env->DeleteLocalRef(clazz);
+	}
+
+	return path;
+}
+
 } // android
 } // love
 

+ 2 - 0
src/common/android.h

@@ -101,6 +101,8 @@ void deinitializeVirtualArchive();
  */
 bool checkFusedGame(void **physfsIO_Out);
 
+const char *getCRequirePath();
+
 } // android
 } // love
 

+ 5 - 1
src/common/runtime.cpp

@@ -1156,7 +1156,9 @@ void luax_runwrapper(lua_State *L, const char *filedata, size_t datalen, const c
 	// functions struct pointer as arguments.
 	if (lua_istable(L, -1))
 	{
-		luaL_loadbuffer(L, filedata, datalen, filename);
+		std::string chunkname = std::string("=[love \"") + std::string(filename) + std::string("\"]");
+
+		luaL_loadbuffer(L, filedata, datalen, chunkname.c_str());
 		lua_pushvalue(L, -2);
 		if (ffifuncs != nullptr)
 			luax_pushpointerasstring(L, ffifuncs);
@@ -1179,8 +1181,10 @@ int luax_resume(lua_State *L, int nargs, int* nres)
 #if LUA_VERSION_NUM >= 504
 	return lua_resume(L, nullptr, nargs, nres);
 #elif LUA_VERSION_NUM >= 502
+	LOVE_UNUSED(nres);
 	return lua_resume(L, nullptr, nargs);
 #else
+	LOVE_UNUSED(nres);
 	return lua_resume(L, nargs);
 #endif
 }

+ 40 - 14
src/libraries/enet/enet.cpp

@@ -118,11 +118,30 @@ static size_t find_peer_index(lua_State *l, ENetHost *enet_host, ENetPeer *peer)
 #define ENET_ALIGNOF(x) alignof(x)
 #endif
 
-// For use with the enet_peers registry.
-// Using the pointer directly via lightuserdata would be ideal, but LuaJIT
-// cannot use lightuserdata with more than 47 bits whereas some newer arm64
-// architectures allow pointers which use more than that.
-static lua_Number compute_peer_key(lua_State *L, ENetPeer *peer)
+static bool supports_full_lightuserdata(lua_State *L)
+{
+	static bool checked = false;
+	static bool supported = false;
+
+	if (!checked)
+	{
+		lua_pushcclosure(L, [](lua_State* L) -> int
+		{
+			// Try to push pointer with all bits set.
+			lua_pushlightuserdata(L, (void*)(~((size_t)0)));
+			return 1;
+		}, 0);
+
+		supported = lua_pcall(L, 0, 1, 0) == 0;
+		checked = true;
+
+		lua_pop(L, 1);
+	}
+
+	return supported;
+}
+
+static uintptr_t compute_peer_key(lua_State *L, ENetPeer *peer)
 {
 	// ENet peers are be allocated on the heap in an array. Lua numbers
 	// (doubles) can store all possible integers up to 2^53. We can store
@@ -140,21 +159,28 @@ static lua_Number compute_peer_key(lua_State *L, ENetPeer *peer)
 
 	static const size_t shift = (size_t) log2((double) minalign);
 
-	key >>= shift;
-
-	// Make sure our key isn't larger than 2^53.
-	if (key > 0x20000000000000ULL)
-		luaL_error(L, "Cannot push enet peer to Lua: pointer value %p is too large", peer);
+	return key >> shift;
+}
 
-	return (lua_Number) key;
+static void push_peer_key(lua_State *L, uintptr_t key)
+{
+	// If full 64-bit lightuserdata is supported, always use that. Otherwise,
+	// if the key is smaller than 2^53 (which is integer precision for double
+	// datatype), then push number. Otherwise, throw error.
+	if (supports_full_lightuserdata(L))
+		lua_pushlightuserdata(L, (void*) key);
+	else if (key > 0x20000000000000ULL) // 2^53
+		luaL_error(L, "Cannot push enet peer to Lua: pointer value %p is too large", key);
+	else
+		lua_pushnumber(L, (lua_Number) key);
 }
 
 static void push_peer(lua_State *l, ENetPeer *peer) {
-	lua_Number key = compute_peer_key(l, peer);
+	uintptr_t key = compute_peer_key(l, peer);
 
 	// try to find in peer table
 	lua_getfield(l, LUA_REGISTRYINDEX, "enet_peers");
-	lua_pushnumber(l, key);
+	push_peer_key(l, key);
 	lua_gettable(l, -2);
 
 	if (lua_isnil(l, -1)) {
@@ -165,7 +191,7 @@ static void push_peer(lua_State *l, ENetPeer *peer) {
 		luaL_getmetatable(l, "enet_peer");
 		lua_setmetatable(l, -2);
 
-		lua_pushnumber(l, key);
+		push_peer_key(l, key);
 		lua_pushvalue(l, -2);
 
 		lua_settable(l, -4);

+ 1 - 1
src/libraries/luasocket/libluasocket/ftp.lua.h

@@ -540,5 +540,5 @@ static const unsigned char B1[]={
 
 };
 
- if (luaL_loadbuffer(L,(const char*)B1,sizeof(B1),"ftp.lua")==0) lua_call(L, 0, LUA_MULTRET);
+ if (luaL_loadbuffer(L,(const char*)B1,sizeof(B1),"=[socket \"ftp.lua\"]")==0) lua_call(L, 0, LUA_MULTRET);
 }

+ 1 - 1
src/libraries/luasocket/libluasocket/headers.lua.h

@@ -192,5 +192,5 @@ static const unsigned char B1[]={
 108,101,114, 34, 44, 10,125, 10, 10,114,101,116,117,114,110, 32, 95, 77,
 };
 
- if (luaL_loadbuffer(L,(const char*)B1,sizeof(B1),"headers.lua")==0) lua_call(L, 0, LUA_MULTRET);
+ if (luaL_loadbuffer(L,(const char*)B1,sizeof(B1),"=[socket \"headers.lua\"]")==0) lua_call(L, 0, LUA_MULTRET);
 }

+ 1 - 1
src/libraries/luasocket/libluasocket/http.lua.h

@@ -668,5 +668,5 @@ static const unsigned char B1[]={
 101,110,100, 41, 10, 10,114,101,116,117,114,110, 32, 95, 77, 10,
 };
 
- if (luaL_loadbuffer(L,(const char*)B1,sizeof(B1),"http.lua")==0) lua_call(L, 0, LUA_MULTRET);
+ if (luaL_loadbuffer(L,(const char*)B1,sizeof(B1),"=[socket \"http.lua\"]")==0) lua_call(L, 0, LUA_MULTRET);
 }

+ 1 - 1
src/libraries/luasocket/libluasocket/ltn12.lua.h

@@ -442,5 +442,5 @@ static const unsigned char B1[]={
 110,100, 10,101,110,100, 10, 10,114,101,116,117,114,110, 32, 95, 77, 10,
 };
 
- if (luaL_loadbuffer(L,(const char*)B1,sizeof(B1),"ltn12.lua")==0) lua_call(L, 0, LUA_MULTRET);
+ if (luaL_loadbuffer(L,(const char*)B1,sizeof(B1),"=[socket \"ltn12.lua\"]")==0) lua_call(L, 0, LUA_MULTRET);
 }

+ 1 - 1
src/libraries/luasocket/libluasocket/mbox.lua.h

@@ -140,5 +140,5 @@ static const unsigned char B1[]={
 110,100, 10, 10,114,101,116,117,114,110, 32, 95, 77, 10,
 };
 
- if (luaL_loadbuffer(L,(const char*)B1,sizeof(B1),"mbox.lua")==0) lua_call(L, 0, LUA_MULTRET);
+ if (luaL_loadbuffer(L,(const char*)B1,sizeof(B1),"=[socket \"mbox.lua\"]")==0) lua_call(L, 0, LUA_MULTRET);
 }

+ 1 - 1
src/libraries/luasocket/libluasocket/mime.lua.h

@@ -132,5 +132,5 @@ static const unsigned char B1[]={
 116,117,114,110, 32, 95, 77,
 };
 
- if (luaL_loadbuffer(L,(const char*)B1,sizeof(B1),"mime.lua")==0) lua_call(L, 0, LUA_MULTRET);
+ if (luaL_loadbuffer(L,(const char*)B1,sizeof(B1),"=[socket \"mime.lua\"]")==0) lua_call(L, 0, LUA_MULTRET);
 }

+ 1 - 1
src/libraries/luasocket/libluasocket/smtp.lua.h

@@ -411,5 +411,5 @@ static const unsigned char B1[]={
 110,100, 41, 10, 10,114,101,116,117,114,110, 32, 95, 77,
 };
 
- if (luaL_loadbuffer(L,(const char*)B1,sizeof(B1),"smtp.lua")==0) lua_call(L, 0, LUA_MULTRET);
+ if (luaL_loadbuffer(L,(const char*)B1,sizeof(B1),"=[socket \"smtp.lua\"]")==0) lua_call(L, 0, LUA_MULTRET);
 }

+ 1 - 1
src/libraries/luasocket/libluasocket/socket.lua.h

@@ -230,5 +230,5 @@ static const unsigned char B1[]={
 116,117,114,110, 32, 95, 77, 10,
 };
 
- if (luaL_loadbuffer(L,(const char*)B1,sizeof(B1),"socket.lua")==0) lua_call(L, 0, LUA_MULTRET);
+ if (luaL_loadbuffer(L,(const char*)B1,sizeof(B1),"=[socket \"socket.lua\"]")==0) lua_call(L, 0, LUA_MULTRET);
 }

+ 1 - 1
src/libraries/luasocket/libluasocket/tp.lua.h

@@ -196,5 +196,5 @@ static const unsigned char B1[]={
 114,110, 32, 95, 77, 10,
 };
 
- if (luaL_loadbuffer(L,(const char*)B1,sizeof(B1),"tp.lua")==0) lua_call(L, 0, LUA_MULTRET);
+ if (luaL_loadbuffer(L,(const char*)B1,sizeof(B1),"=[socket \"tp.lua\"]")==0) lua_call(L, 0, LUA_MULTRET);
 }

+ 1 - 1
src/libraries/luasocket/libluasocket/url.lua.h

@@ -563,5 +563,5 @@ static const unsigned char B1[]={
 110, 32, 95, 77, 10,
 };
 
- if (luaL_loadbuffer(L,(const char*)B1,sizeof(B1),"url.lua")==0) lua_call(L, 0, LUA_MULTRET);
+ if (luaL_loadbuffer(L,(const char*)B1,sizeof(B1),"=[socket \"url.lua\"]")==0) lua_call(L, 0, LUA_MULTRET);
 }

+ 16 - 8
src/love.cpp

@@ -143,14 +143,6 @@ enum DoneAction
 
 static DoneAction runlove(int argc, char **argv, int &retval, love::Variant &restartvalue)
 {
-#ifdef LOVE_LEGENDARY_APP_ARGV_HACK
-	int hack_argc = 0;
-	char **hack_argv = 0;
-	get_app_arguments(argc, argv, hack_argc, hack_argv);
-	argc = hack_argc;
-	argv = hack_argv;
-#endif // LOVE_LEGENDARY_APP_ARGV_HACK
-
 	// Oh, you just want the version? Okay!
 	if (argc > 1 && strcmp(argv[1], "--version") == 0)
 	{
@@ -167,6 +159,22 @@ static DoneAction runlove(int argc, char **argv, int &retval, love::Variant &res
 	lua_State *L = luaL_newstate();
 	luaL_openlibs(L);
 
+	// LuaJIT-specific setup needs to be done as early as possible - before
+	// get_app_arguments because that loads external library code. This is also
+	// loaded inside require("love"). Note that it doesn't use the love table.
+	love_preload(L, luaopen_love_jitsetup, "love.jitsetup");
+	lua_getglobal(L, "require");
+	lua_pushstring(L, "love.jitsetup");
+	lua_call(L, 1, 0);
+
+#ifdef LOVE_LEGENDARY_APP_ARGV_HACK
+	int hack_argc = 0;
+	char **hack_argv = nullptr;
+	get_app_arguments(argc, argv, hack_argc, hack_argv);
+	argc = hack_argc;
+	argv = hack_argv;
+#endif // LOVE_LEGENDARY_APP_ARGV_HACK
+
 	// Add love to package.preload for easy requiring.
 	love_preload(L, luaopen_love, "love");
 

+ 11 - 0
src/modules/data/wrap_ByteData.cpp

@@ -31,8 +31,19 @@ ByteData *luax_checkbytedata(lua_State *L, int idx)
 	return luax_checktype<ByteData>(L, idx);
 }
 
+int w_ByteData_clone(lua_State *L)
+{
+	ByteData *t = luax_checkbytedata(L, 1);
+	ByteData *c = nullptr;
+	luax_catchexcept(L, [&](){ c = t->clone(); });
+	luax_pushtype(L, c);
+	c->release();
+	return 1;
+}
+
 static const luaL_Reg w_ByteData_functions[] =
 {
+	{ "clone", w_ByteData_clone },
 	{ 0, 0 }
 };
 

+ 12 - 1
src/modules/data/wrap_DataView.cpp

@@ -26,13 +26,24 @@ namespace love
 namespace data
 {
 
-DataView *luax_checkdataView(lua_State *L, int idx)
+DataView *luax_checkdataview(lua_State *L, int idx)
 {
 	return luax_checktype<DataView>(L, idx);
 }
 
+int w_DataView_clone(lua_State *L)
+{
+	DataView *t = luax_checkdataview(L, 1);
+	DataView *c = nullptr;
+	luax_catchexcept(L, [&](){ c = t->clone(); });
+	luax_pushtype(L, c);
+	c->release();
+	return 1;
+}
+
 static const luaL_Reg w_DataView_functions[] =
 {
+	{ "clone", w_DataView_clone },
 	{ 0, 0 }
 };
 

+ 1 - 1
src/modules/event/wrap_Event.cpp

@@ -180,7 +180,7 @@ extern "C" int luaopen_love_event(lua_State *L)
 
 	int ret = luax_register_module(L, w);
 
-	if (luaL_loadbuffer(L, (const char *)event_lua, sizeof(event_lua), "wrap_Event.lua") == 0)
+	if (luaL_loadbuffer(L, (const char *)event_lua, sizeof(event_lua), "=[love \"wrap_Event.lua\"]") == 0)
 		lua_call(L, 0, 0);
 	else
 		lua_error(L);

+ 7 - 5
src/modules/filesystem/physfs/Filesystem.cpp

@@ -240,15 +240,13 @@ bool Filesystem::setSource(const char *source)
 
 	PHYSFS_Io *gameLoveIO;
 	bool hasFusedGame = love::android::checkFusedGame((void **) &gameLoveIO);
+	bool isAAssetMounted = false;
 
 	if (hasFusedGame)
 	{
 		if (gameLoveIO)
-		{
 			// Actually we should just be able to mount gameLoveIO, but that's experimental.
 			gameLoveIO->destroy(gameLoveIO);
-			goto oldschool;
-		}
 		else
 		{
 			if (!love::android::initializeVirtualArchive())
@@ -256,11 +254,15 @@ bool Filesystem::setSource(const char *source)
 				SDL_Log("Unable to mount AAsset: %s", PHYSFS_getErrorByCode(PHYSFS_getLastErrorCode()));
 				return false;
 			}
+
+			// See love::android::initializeVirtualArchive()
+			new_search_path = "ASET.AASSET";
+			isAAssetMounted = true;
 		}
 	}
-	else
+
+	if (!isAAssetMounted)
 	{
-	oldschool:
 		new_search_path = love::android::getSelectedGameFile();
 
 		// try mounting first, if that fails, load to memory and mount

+ 38 - 2
src/modules/filesystem/wrap_Filesystem.cpp

@@ -29,6 +29,10 @@
 
 #include "physfs/Filesystem.h"
 
+#ifdef LOVE_ANDROID
+#include "common/android.h"
+#endif
+
 // SDL
 #include <SDL_loadso.h>
 
@@ -380,11 +384,22 @@ int w_newFileData(lua_State *L)
 	}
 
 	size_t length = 0;
-	const char *str = luaL_checklstring(L, 1, &length);
+	const void *ptr = nullptr;
+	if (luax_istype(L, 1, Data::type))
+	{
+		Data *data = data::luax_checkdata(L, 1);
+		ptr = data->getData();
+		length = data->getSize();
+	}
+	else if (lua_isstring(L, 1))
+		ptr = luaL_checklstring(L, 1, &length);
+	else
+		return luaL_argerror(L, 1, "string or Data expected");
+
 	const char *filename = luaL_checkstring(L, 2);
 
 	FileData *t = nullptr;
-	luax_catchexcept(L, [&](){ t = instance()->newFileData(str, length, filename); });
+	luax_catchexcept(L, [&](){ t = instance()->newFileData(ptr, length, filename); });
 
 	luax_pushtype(L, t);
 	t->release();
@@ -849,6 +864,23 @@ int extloader(lua_State *L)
 	void *handle = nullptr;
 	auto *inst = instance();
 
+#ifdef LOVE_ANDROID
+	// Specifically Android, look the library path based on getCRequirePath first
+	std::string androidPath(love::android::getCRequirePath());
+
+	if (!androidPath.empty())
+	{
+		// Replace ? with just the dotted filename (not tokenized_name)
+		replaceAll(androidPath, "?", filename);
+
+		// Load directly, don't check for existence.
+		handle = SDL_LoadObject(androidPath.c_str());
+	}
+
+	if (!handle)
+	{
+#endif // LOVE_ANDROID
+
 	for (const std::string &el : inst->getCRequirePath())
 	{
 		for (const char *ext : library_extensions)
@@ -878,6 +910,10 @@ int extloader(lua_State *L)
 			break;
 	}
 
+#ifdef LOVE_ANDROID
+	} // if (!handle)
+#endif
+
 	if (!handle)
 	{
 		lua_pushfstring(L, "\n\tno file '%s' in LOVE paths.", tokenized_name.c_str());

+ 4 - 7
src/modules/graphics/Font.cpp

@@ -901,14 +901,11 @@ void Font::getWrap(const ColoredCodepoints &codepoints, float wraplimit, std::ve
 	}
 
 	// Push the last line.
-	if (!wline.cps.empty())
-	{
-		lines.push_back(wline);
+	lines.push_back(wline);
 
-		// Ignore the width of any trailing spaces, for individual lines.
-		if (linewidths)
-			linewidths->push_back(width - widthoftrailingspace);
-	}
+	// Ignore the width of any trailing spaces, for individual lines.
+	if (linewidths)
+		linewidths->push_back(width - widthoftrailingspace);
 }
 
 void Font::getWrap(const std::vector<ColoredString> &text, float wraplimit, std::vector<std::string> &lines, std::vector<int> *linewidths)

+ 1 - 1
src/modules/graphics/ParticleSystem.cpp

@@ -177,7 +177,7 @@ void ParticleSystem::resetOffset()
 	else
 	{
 		Quad::Viewport v = quads[0]->getViewport();
-		offset = love::Vector2(v.x*0.5f, v.y*0.5f);
+		offset = love::Vector2(v.w*0.5f, v.h*0.5f);
 	}
 }
 

+ 1 - 1
src/modules/graphics/wrap_Graphics.cpp

@@ -3648,7 +3648,7 @@ extern "C" int luaopen_love_graphics(lua_State *L)
 
 	int n = luax_register_module(L, w);
 
-	if (luaL_loadbuffer(L, (const char *)graphics_lua, sizeof(graphics_lua), "wrap_Graphics.lua") == 0)
+	if (luaL_loadbuffer(L, (const char *)graphics_lua, sizeof(graphics_lua), "=[love \"wrap_Graphics.lua\"]") == 0)
 		lua_call(L, 0, 0);
 	else
 		lua_error(L);

+ 1 - 1
src/modules/graphics/wrap_Video.cpp

@@ -168,7 +168,7 @@ int luaopen_video(lua_State *L)
 {
 	int ret = luax_register_type(L, &Video::type, functions, nullptr);
 
-	luaL_loadbuffer(L, video_lua, sizeof(video_lua), "Video.lua");
+	luaL_loadbuffer(L, video_lua, sizeof(video_lua), "=[love \"Video.lua\"]");
 	luax_gettypemetatable(L, Video::type);
 	lua_call(L, 1, 0);
 

+ 27 - 4
src/modules/joystick/sdl/Joystick.cpp

@@ -440,6 +440,11 @@ bool Joystick::checkCreateHaptic()
 
 bool Joystick::isVibrationSupported()
 {
+#if SDL_VERSION_ATLEAST(2, 0, 18)
+	if (isConnected() && SDL_JoystickHasRumble(joyhandle) == SDL_TRUE)
+		return true;
+#endif
+
 	if (!checkCreateHaptic())
 		return false;
 
@@ -490,8 +495,12 @@ bool Joystick::setVibration(float left, float right, float duration)
 	if (left == 0.0f && right == 0.0f)
 		return setVibration();
 
-	if (!checkCreateHaptic())
+	if (!isConnected())
+	{
+		vibration.left = vibration.right = 0.0f;
+		vibration.endtime = SDL_HAPTIC_INFINITY;
 		return false;
+	}
 
 	Uint32 length = SDL_HAPTIC_INFINITY;
 	if (duration >= 0.0f)
@@ -501,10 +510,19 @@ bool Joystick::setVibration(float left, float right, float duration)
 	}
 
 	bool success = false;
+
+#if SDL_VERSION_ATLEAST(2, 0, 9)
+	if (SDL_JoystickRumble(joyhandle, (Uint16)(left * LOVE_UINT16_MAX), (Uint16)(right * LOVE_UINT16_MAX), length) == 0)
+		success = true;
+#endif
+
+	if (!success && !checkCreateHaptic())
+		return false;
+
 	unsigned int features = SDL_HapticQuery(haptic);
 	int axes = SDL_HapticNumAxes(haptic);
 
-	if ((features & SDL_HAPTIC_LEFTRIGHT) != 0)
+	if (!success && (features & SDL_HAPTIC_LEFTRIGHT) != 0)
 	{
 		memset(&vibration.effect, 0, sizeof(SDL_HapticEffect));
 		vibration.effect.type = SDL_HAPTIC_LEFTRIGHT;
@@ -576,9 +594,14 @@ bool Joystick::setVibration(float left, float right, float duration)
 
 bool Joystick::setVibration()
 {
-	bool success = true;
+	bool success = false;
+
+#if SDL_VERSION_ATLEAST(2, 0, 9)
+	if (!success)
+		success = isConnected() && SDL_JoystickRumble(joyhandle, 0, 0, 0) == 0;
+#endif
 
-	if (SDL_WasInit(SDL_INIT_HAPTIC) && haptic && SDL_HapticIndex(haptic) != -1)
+	if (!success && SDL_WasInit(SDL_INIT_HAPTIC) && haptic && SDL_HapticIndex(haptic) != -1)
 		success = (SDL_HapticStopEffect(haptic, vibration.id) == 0);
 
 	if (success)

+ 1 - 1
src/modules/love/callbacks.lua

@@ -256,6 +256,7 @@ function love.errhand(msg)
 	p = p:gsub("%[string \"(.-)\"%]", "%1")
 
 	local function draw()
+		if not love.graphics.isActive() then return end
 		local pos = 70
 		love.graphics.clear(89/255, 157/255, 220/255)
 		love.graphics.printf(p, pos, pos, love.graphics.getWidth() - pos)
@@ -267,7 +268,6 @@ function love.errhand(msg)
 		if not love.system then return end
 		love.system.setClipboardText(fullErrorText)
 		p = p .. "\nCopied to clipboard!"
-		draw()
 	end
 
 	if love.system then

+ 71 - 0
src/modules/love/jitsetup.lua

@@ -0,0 +1,71 @@
+R"luastring"--(
+-- DO NOT REMOVE THE ABOVE LINE. It is used to load this file as a C++ string.
+-- There is a matching delimiter at the bottom of the file.
+
+--[[
+Copyright (c) 2006-2021 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.
+--]]
+
+if type(jit) ~= "table" or not jit.status() then
+	return
+end
+
+-- Double the defaults.
+jit.opt.start("maxtrace=2000", "maxrecord=8000")
+
+-- Somewhat arbitrary value. Needs to be higher than the combined sizes below,
+-- and higher than the default (512) because that's already too low.
+jit.opt.start("maxmcode=16384")
+
+if jit.arch == "arm64" then
+	-- https://github.com/LuaJIT/LuaJIT/issues/285
+	-- LuaJIT 2.1 on arm64 currently (as of commit b4b2dce) can only use memory
+	-- for JIT compilation within a certain short range. Other libraries such as
+	-- SDL can take all the usable space in that range and cause attempts at JIT
+	-- compilation to both fail and take a long time.
+	-- This is a very hacky attempt at a workaround. LuaJIT allocates executable
+	-- code in pools. We'll try "reserving" pools before any external code is
+	-- executed, by causing JIT compilation via a small loop. We can't easily
+	-- tell if JIT compilation succeeded, so we do several successively smaller
+	-- pool allocations in case previous ones fail.
+	-- This is a really hacky hack and by no means foolproof - there are a lot of
+	-- potential situations (especially when threads are used) where previously
+	-- executed external code will still take up space that LuaJIT needed for itself.
+
+	jit.opt.start("sizemcode=2048")
+	for i=1, 100 do end
+	
+	jit.opt.start("sizemcode=1024")
+	for i=1, 100 do end
+	
+	jit.opt.start("sizemcode=512")
+	for i=1, 100 do end
+	
+	jit.opt.start("sizemcode=256")
+	for i=1, 100 do end
+	
+	jit.opt.start("sizemcode=128")
+	for i=1, 100 do end
+else
+	-- Somewhat arbitrary value (>= the default).
+	jit.opt.start("sizemcode=128")
+end
+
+-- DO NOT REMOVE THE NEXT LINE. It is used to load this file as a C++ string.
+--)luastring"--"

+ 27 - 8
src/modules/love/love.cpp

@@ -102,6 +102,10 @@ static const char boot_lua[] =
 #include "boot.lua"
 ;
 
+static const char jit_setup_lua[] =
+#include "jitsetup.lua"
+;
+
 // All modules define a c-accessible luaopen
 // so let's make use of those, instead
 // of addressing implementations directly.
@@ -165,6 +169,7 @@ extern "C"
 	extern int luaopen_love_window(lua_State*);
 #endif
 	extern int luaopen_love_nogame(lua_State*);
+	extern int luaopen_love_jitsetup(lua_State*);
 	extern int luaopen_love_arg(lua_State*);
 	extern int luaopen_love_callbacks(lua_State*);
 	extern int luaopen_love_boot(lua_State*);
@@ -229,6 +234,7 @@ static const luaL_Reg modules[] = {
 	{ "love.window", luaopen_love_window },
 #endif
 	{ "love.nogame", luaopen_love_nogame },
+	{ "love.jitsetup", luaopen_love_jitsetup },
 	{ "love.arg", luaopen_love_arg },
 	{ "love.callbacks", luaopen_love_callbacks },
 	{ "love.boot", luaopen_love_boot },
@@ -440,6 +446,15 @@ static void luax_addcompatibilityalias(lua_State *L, const char *module, const c
 
 int luaopen_love(lua_State *L)
 {
+	// Preload module loaders.
+	for (int i = 0; modules[i].name != nullptr; i++)
+		love::luax_preload(L, modules[i].func, modules[i].name);
+
+	// jitsetup is also loaded in the love executable runlove function. It's
+	// needed here too for threads. Note that it doesn't use the love table.
+	love::luax_require(L, "love.jitsetup");
+	lua_pop(L, 1);
+
 	love::luax_insistpinnedthread(L);
 
 	love::luax_insistglobal(L, "love");
@@ -543,10 +558,6 @@ int luaopen_love(lua_State *L)
 		lua_setfield(L, -2, "hasDeprecationOutput");
 	}
 
-	// Preload module loaders.
-	for (int i = 0; modules[i].name != nullptr; i++)
-		love::luax_preload(L, modules[i].func, modules[i].name);
-
 	// Necessary for Data-creating methods to work properly in Data subclasses.
 	love::luax_require(L, "love.data");
 	lua_pop(L, 1);
@@ -693,7 +704,15 @@ int w__setAccelerometerAsJoystick(lua_State *L)
 
 int luaopen_love_nogame(lua_State *L)
 {
-	if (luaL_loadbuffer(L, (const char *)love::nogame_lua, sizeof(love::nogame_lua), "nogame.lua") == 0)
+	if (luaL_loadbuffer(L, (const char *)love::nogame_lua, sizeof(love::nogame_lua), "=[love \"nogame.lua\"]") == 0)
+		lua_call(L, 0, 1);
+
+	return 1;
+}
+
+int luaopen_love_jitsetup(lua_State *L)
+{
+	if (luaL_loadbuffer(L, jit_setup_lua, sizeof(jit_setup_lua), "=[love \"jitsetup.lua\"]") == 0)
 		lua_call(L, 0, 1);
 
 	return 1;
@@ -701,7 +720,7 @@ int luaopen_love_nogame(lua_State *L)
 
 int luaopen_love_arg(lua_State *L)
 {
-	if (luaL_loadbuffer(L, arg_lua, sizeof(arg_lua), "arg.lua") == 0)
+	if (luaL_loadbuffer(L, arg_lua, sizeof(arg_lua), "=[love \"arg.lua\"]") == 0)
 		lua_call(L, 0, 1);
 
 	return 1;
@@ -709,7 +728,7 @@ int luaopen_love_arg(lua_State *L)
 
 int luaopen_love_callbacks(lua_State *L)
 {
-	if (luaL_loadbuffer(L, callbacks_lua, sizeof(callbacks_lua), "callbacks.lua") == 0)
+	if (luaL_loadbuffer(L, callbacks_lua, sizeof(callbacks_lua), "=[love \"callbacks.lua\"]") == 0)
 		lua_call(L, 0, 1);
 
 	return 1;
@@ -717,7 +736,7 @@ int luaopen_love_callbacks(lua_State *L)
 
 int luaopen_love_boot(lua_State *L)
 {
-	if (luaL_loadbuffer(L, boot_lua, sizeof(boot_lua), "boot.lua") == 0)
+	if (luaL_loadbuffer(L, boot_lua, sizeof(boot_lua), "=[love \"boot.lua\"]") == 0)
 		lua_call(L, 0, 1);
 
 	return 1;

+ 3 - 0
src/modules/love/love.h

@@ -36,6 +36,9 @@ LOVE_EXPORT const char *love_version();
 LOVE_EXPORT const char *love_codename();
 LOVE_EXPORT int luaopen_love(lua_State *L);
 LOVE_EXPORT int luaopen_love_nogame(lua_State *L);
+LOVE_EXPORT int luaopen_love_jitsetup(lua_State *L);
+LOVE_EXPORT int luaopen_love_arg(lua_State *L);
+LOVE_EXPORT int luaopen_love_callbacks(lua_State *L);
 LOVE_EXPORT int luaopen_love_boot(lua_State *L);
 
 #ifdef LOVE_LEGENDARY_CONSOLE_IO_HACK

+ 1 - 1
src/modules/math/wrap_Math.cpp

@@ -415,7 +415,7 @@ extern "C" int luaopen_love_math(lua_State *L)
 	int n = luax_register_module(L, w);
 
 	// Execute wrap_Math.lua, sending the math table and ffifuncs pointer as args.
-	luaL_loadbuffer(L, math_lua, sizeof(math_lua), "wrap_Math.lua");
+	luaL_loadbuffer(L, math_lua, sizeof(math_lua), "=[love \"wrap_Math.lua\"]");
 	lua_pushvalue(L, -2);
 	luax_pushpointerasstring(L, &ffifuncs);
 	lua_call(L, 2, 0);

+ 18 - 5
src/modules/video/theora/OggDemuxer.cpp

@@ -43,7 +43,7 @@ OggDemuxer::~OggDemuxer()
 	ogg_sync_clear(&sync);
 }
 
-void OggDemuxer::readPage()
+bool OggDemuxer::readPage(bool erroreof)
 {
 	char *syncBuffer = nullptr;
 	while (ogg_sync_pageout(&sync, &page) != 1)
@@ -53,8 +53,13 @@ void OggDemuxer::readPage()
 
 		syncBuffer = ogg_sync_buffer(&sync, 8192);
 		size_t read = file->read(syncBuffer, 8192);
+		if (read == 0 && erroreof)
+			return false;
+
 		ogg_sync_wrote(&sync, read);
 	}
+
+	return true;
 }
 
 bool OggDemuxer::readPacket(ogg_packet &packet, bool mustSucceed)
@@ -118,22 +123,25 @@ OggDemuxer::StreamType OggDemuxer::findStream()
 	if (streamInited)
 	{
 		eos = false;
+		streamInited = false;
 		file->seek(0);
 		ogg_stream_clear(&stream);
 		ogg_sync_reset(&sync);
 	}
 
-	streamInited = true;
 	while (true)
 	{
+		if (!readPage(true))
+			return TYPE_UNKNOWN;
+
 		// If this page isn't at the start of a stream, we've seen all streams
-		readPage();
 		if (!ogg_page_bos(&page))
 			break;
 
 		videoSerial = ogg_page_serialno(&page);
 		ogg_stream_init(&stream, videoSerial);
 		ogg_stream_pagein(&stream, &page);
+		streamInited = true;
 
 		StreamType type = determineType();
 		switch(type)
@@ -145,10 +153,15 @@ OggDemuxer::StreamType OggDemuxer::findStream()
 		}
 
 		ogg_stream_clear(&stream);
+		streamInited = false;
+	}
+
+	if (streamInited)
+	{
+		streamInited = false;
+		ogg_stream_clear(&stream);
 	}
 
-	streamInited = false;
-	ogg_stream_clear(&stream);
 	ogg_sync_reset(&sync);
 
 	return TYPE_UNKNOWN;

+ 1 - 1
src/modules/video/theora/OggDemuxer.h

@@ -67,7 +67,7 @@ private:
 	int videoSerial;
 	bool eos;
 
-	void readPage();
+	bool readPage(bool erroreof = false);
 	StreamType determineType();
 }; // OggDemuxer