From a819cb54a6a7c15bc42ab8334e32b78754f33f12 Mon Sep 17 00:00:00 2001
From: Jonas Kulla <Nyocurio@gmail.com>
Date: Fri, 3 Mar 2017 19:37:19 +0100
Subject: [PATCH 01/13] Config: Use set for preloadScripts

Would probably make sense for all other string vectors too.
---
 binding-mri/binding-mri.cpp | 5 +++--
 src/config.cpp              | 8 +++++++-
 src/config.h                | 3 ++-
 3 files changed, 12 insertions(+), 4 deletions(-)

diff --git a/binding-mri/binding-mri.cpp b/binding-mri/binding-mri.cpp
index 5cb518e..c28cdaa 100644
--- a/binding-mri/binding-mri.cpp
+++ b/binding-mri/binding-mri.cpp
@@ -452,8 +452,9 @@ static void runRMXPScripts(BacktraceData &btData)
 	}
 
 	/* Execute preloaded scripts */
-	for (size_t i = 0; i < conf.preloadScripts.size(); ++i)
-		runCustomScript(conf.preloadScripts[i]);
+	for (std::set<std::string>::iterator i = conf.preloadScripts.begin();
+	     i != conf.preloadScripts.end(); ++i)
+		runCustomScript(*i);
 
 	VALUE exc = rb_gv_get("$!");
 	if (exc != Qnil)
diff --git a/src/config.cpp b/src/config.cpp
index 9173acc..a58ddab 100644
--- a/src/config.cpp
+++ b/src/config.cpp
@@ -134,6 +134,12 @@ static std::string prefPath(const char *org, const char *app)
 	return str;
 }
 
+template<typename T>
+std::set<T> setFromVec(const std::vector<T> &vec)
+{
+	return std::set<T>(vec.begin(), vec.end());
+}
+
 typedef std::vector<std::string> StringVec;
 namespace po = boost::program_options;
 
@@ -226,7 +232,7 @@ void Config::read(int argc, char *argv[])
 
 	PO_DESC_ALL;
 
-	GUARD_ALL( preloadScripts = vm["preloadScript"].as<StringVec>(); );
+	GUARD_ALL( preloadScripts = setFromVec(vm["preloadScript"].as<StringVec>()); );
 
 	GUARD_ALL( rtps = vm["RTP"].as<StringVec>(); );
 
diff --git a/src/config.h b/src/config.h
index fc9bbda..d82698d 100644
--- a/src/config.h
+++ b/src/config.h
@@ -24,6 +24,7 @@
 
 #include <string>
 #include <vector>
+#include <set>
 
 struct Config
 {
@@ -77,7 +78,7 @@ struct Config
 	bool useScriptNames;
 
 	std::string customScript;
-	std::vector<std::string> preloadScripts;
+	std::set<std::string> preloadScripts;
 	std::vector<std::string> rtps;
 
 	std::vector<std::string> fontSubs;

From 3b5f2608ec28f4faaca183d73197504037d82e52 Mon Sep 17 00:00:00 2001
From: Jonas Kulla <Nyocurio@gmail.com>
Date: Sat, 4 Mar 2017 11:04:02 +0100
Subject: [PATCH 02/13] main: Only set window icon on Linux

OSX carries high-resolution icons in its bundles, and windows uses
windres to embed .ico files, so don't interfere with those.
---
 src/main.cpp | 40 +++++++++++++++++++++++++---------------
 1 file changed, 25 insertions(+), 15 deletions(-)

diff --git a/src/main.cpp b/src/main.cpp
index eb07970..4b5e234 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -166,6 +166,24 @@ static void showInitError(const std::string &msg)
 	SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "mkxp", msg.c_str(), 0);
 }
 
+static void setupWindowIcon(const Config &conf, SDL_Window *win)
+{
+	SDL_RWops *iconSrc;
+
+	if (conf.iconPath.empty())
+		iconSrc = SDL_RWFromConstMem(assets_icon_png, assets_icon_png_len);
+	else
+		iconSrc = SDL_RWFromFile(conf.iconPath.c_str(), "rb");
+
+	SDL_Surface *iconImg = IMG_Load_RW(iconSrc, SDL_TRUE);
+
+	if (iconImg)
+	{
+		SDL_SetWindowIcon(win, iconImg);
+		SDL_FreeSurface(iconImg);
+	}
+}
+
 int main(int argc, char *argv[])
 {
 	SDL_SetHint(SDL_HINT_VIDEO_MINIMIZE_ON_FOCUS_LOSS, "0");
@@ -239,16 +257,6 @@ int main(int argc, char *argv[])
 		return 0;
 	}
 
-	/* Setup application icon */
-	SDL_RWops *iconSrc;
-
-	if (conf.iconPath.empty())
-		iconSrc = SDL_RWFromConstMem(assets_icon_png, assets_icon_png_len);
-	else
-		iconSrc = SDL_RWFromFile(conf.iconPath.c_str(), "rb");
-
-	SDL_Surface *iconImg = IMG_Load_RW(iconSrc, SDL_TRUE);
-
 	SDL_Window *win;
 	Uint32 winFlags = SDL_WINDOW_OPENGL | SDL_WINDOW_INPUT_FOCUS;
 
@@ -267,11 +275,13 @@ int main(int argc, char *argv[])
 		return 0;
 	}
 
-	if (iconImg)
-	{
-		SDL_SetWindowIcon(win, iconImg);
-		SDL_FreeSurface(iconImg);
-	}
+	/* OSX and Windows have their own native ways of
+	 * dealing with icons; don't interfere with them */
+#ifdef __LINUX__
+	setupWindowIcon(conf, win);
+#else
+	(void) setupWindowIcon;
+#endif
 
 	ALCdevice *alcDev = alcOpenDevice(0);
 

From a5a648ac861e3c8cac8576749e64262c31c04e60 Mon Sep 17 00:00:00 2001
From: Marty Plummer <ntzrmtthihu777@gmail.com>
Date: Fri, 3 Mar 2017 22:45:31 -0600
Subject: [PATCH 03/13] Add icon and resource files for windows

Signed-off-by: Marty Plummer <ntzrmtthihu777@gmail.com>
---
 assets/icon.ico    | Bin 0 -> 16958 bytes
 assets/resource.h  |   1 +
 assets/resource.rc |   4 ++++
 src/main.cpp       |   4 ++++
 4 files changed, 9 insertions(+)
 create mode 100644 assets/icon.ico
 create mode 100644 assets/resource.h
 create mode 100644 assets/resource.rc

diff --git a/assets/icon.ico b/assets/icon.ico
new file mode 100644
index 0000000000000000000000000000000000000000..2a10780dd1c609077bcc4a2cbd42ddfe422d5aac
GIT binary patch
literal 16958
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

literal 0
HcmV?d00001

diff --git a/assets/resource.h b/assets/resource.h
new file mode 100644
index 0000000..32015bd
--- /dev/null
+++ b/assets/resource.h
@@ -0,0 +1 @@
+#define IDI_APPICON 101
diff --git a/assets/resource.rc b/assets/resource.rc
new file mode 100644
index 0000000..293e38d
--- /dev/null
+++ b/assets/resource.rc
@@ -0,0 +1,4 @@
+#include <windows.h>
+#include "resource.h"
+
+IDI_APPICON ICON "icon.ico"
diff --git a/src/main.cpp b/src/main.cpp
index 4b5e234..1ad3ee2 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -40,7 +40,11 @@
 
 #include "binding.h"
 
+#ifdef __WINDOWS__
+#include "resource.h"
+#elif __LINUX__
 #include "icon.png.xxd"
+#endif
 
 static void
 rgssThreadError(RGSSThreadData *rtData, const std::string &msg)

From fbfee3ed9b6a9486ebdbaee1598ece88ae6eef68 Mon Sep 17 00:00:00 2001
From: Jonas Kulla <Nyocurio@gmail.com>
Date: Wed, 8 Mar 2017 16:30:07 +0100
Subject: [PATCH 04/13] Filesystem: Search for "Fonts/" with case-insensitivity

---
 src/filesystem.cpp | 24 +++++++++++++++++++++---
 1 file changed, 21 insertions(+), 3 deletions(-)

diff --git a/src/filesystem.cpp b/src/filesystem.cpp
index fe5d403..d7fd36b 100644
--- a/src/filesystem.cpp
+++ b/src/filesystem.cpp
@@ -458,7 +458,7 @@ struct FontSetsCBData
 	SharedFontState *sfs;
 };
 
-static void fontSetEnumCB(void *data, const char *,
+static void fontSetEnumCB(void *data, const char *dir,
                           const char *fname)
 {
 	FontSetsCBData *d = static_cast<FontSetsCBData*>(data);
@@ -480,7 +480,7 @@ static void fontSetEnumCB(void *data, const char *,
 		return;
 
 	char filename[512];
-	snprintf(filename, sizeof(filename), "Fonts/%s", fname);
+	snprintf(filename, sizeof(filename), "%s/%s", dir, fname);
 
 	PHYSFS_File *handle = PHYSFS_openRead(filename);
 
@@ -495,11 +495,29 @@ static void fontSetEnumCB(void *data, const char *,
 	SDL_RWclose(&ops);
 }
 
+/* Basically just a case-insensitive search
+ * for the folder "Fonts"... */
+static void findFontsFolderCB(void *data, const char *,
+                              const char *fname)
+{
+	size_t i = 0;
+	char buffer[512];
+	const char *s = fname;
+
+	while (s && i < sizeof(buffer))
+		buffer[i++] = tolower(*s++);
+
+	buffer[i] = '\0';
+
+	if (strcmp(buffer, "fonts") == 0)
+		PHYSFS_enumerateFilesCallback(fname, fontSetEnumCB, data);
+}
+
 void FileSystem::initFontSets(SharedFontState &sfs)
 {
 	FontSetsCBData d = { p, &sfs };
 
-	PHYSFS_enumerateFilesCallback("Fonts", fontSetEnumCB, &d);
+	PHYSFS_enumerateFilesCallback(".", findFontsFolderCB, &d);
 }
 
 struct OpenReadEnumData

From 2f273bade8d943d699ff69ed3fbd87d5837b087f Mon Sep 17 00:00:00 2001
From: Jonas Kulla <Nyocurio@gmail.com>
Date: Sat, 8 Apr 2017 16:16:36 +0200
Subject: [PATCH 05/13] EventThread: Make system cursor visible over black
 aspect ratio bars

Should be less confusing for the player.
---
 src/eventthread.cpp | 48 ++++++++++++++++++++++++++++++++++++---------
 src/eventthread.h   |  6 +++++-
 src/graphics.cpp    |  3 +++
 3 files changed, 47 insertions(+), 10 deletions(-)

diff --git a/src/eventthread.cpp b/src/eventthread.cpp
index 00c1b3e..42dab49 100644
--- a/src/eventthread.cpp
+++ b/src/eventthread.cpp
@@ -83,6 +83,7 @@ enum
 	REQUEST_SETCURSORVISIBLE,
 
 	UPDATE_FPS,
+	UPDATE_SCREEN_RECT,
 
 	EVENT_COUNT
 };
@@ -131,6 +132,8 @@ void EventThread::process(RGSSThreadData &rtData)
 	bool displayingFPS = false;
 
 	bool cursorInWindow = false;
+	/* Will be updated eventually */
+	SDL_Rect gameScreen = { 0, 0, 0, 0 };
 
 	/* SDL doesn't send an initial FOCUS_GAINED event */
 	bool windowFocused = true;
@@ -170,7 +173,7 @@ void EventThread::process(RGSSThreadData &rtData)
 				delete sMenu;
 				sMenu = 0;
 
-				updateCursorState(cursorInWindow && windowFocused);
+				updateCursorState(cursorInWindow && windowFocused, gameScreen);
 			}
 
 			continue;
@@ -211,14 +214,14 @@ void EventThread::process(RGSSThreadData &rtData)
 			case SDL_WINDOWEVENT_ENTER :
 				cursorInWindow = true;
 				mouseState.inWindow = true;
-				updateCursorState(cursorInWindow && windowFocused && !sMenu);
+				updateCursorState(cursorInWindow && windowFocused && !sMenu, gameScreen);
 
 				break;
 
 			case SDL_WINDOWEVENT_LEAVE :
 				cursorInWindow = false;
 				mouseState.inWindow = false;
-				updateCursorState(cursorInWindow && windowFocused && !sMenu);
+				updateCursorState(cursorInWindow && windowFocused && !sMenu, gameScreen);
 
 				break;
 
@@ -229,13 +232,13 @@ void EventThread::process(RGSSThreadData &rtData)
 
 			case SDL_WINDOWEVENT_FOCUS_GAINED :
 				windowFocused = true;
-				updateCursorState(cursorInWindow && windowFocused && !sMenu);
+				updateCursorState(cursorInWindow && windowFocused && !sMenu, gameScreen);
 
 				break;
 
 			case SDL_WINDOWEVENT_FOCUS_LOST :
 				windowFocused = false;
-				updateCursorState(cursorInWindow && windowFocused && !sMenu);
+				updateCursorState(cursorInWindow && windowFocused && !sMenu, gameScreen);
 				resetInputStates();
 
 				break;
@@ -268,7 +271,7 @@ void EventThread::process(RGSSThreadData &rtData)
 				if (!sMenu)
 				{
 					sMenu = new SettingsMenu(rtData);
-					updateCursorState(false);
+					updateCursorState(false, gameScreen);
 				}
 
 				sMenu->raise();
@@ -374,6 +377,7 @@ void EventThread::process(RGSSThreadData &rtData)
 		case SDL_MOUSEMOTION :
 			mouseState.x = event.motion.x;
 			mouseState.y = event.motion.y;
+			updateCursorState(cursorInWindow, gameScreen);
 			break;
 
 		case SDL_FINGERDOWN :
@@ -413,7 +417,7 @@ void EventThread::process(RGSSThreadData &rtData)
 
 			case REQUEST_SETCURSORVISIBLE :
 				showCursor = event.user.code;
-				updateCursorState(cursorInWindow);
+				updateCursorState(cursorInWindow, gameScreen);
 				break;
 
 			case UPDATE_FPS :
@@ -438,6 +442,15 @@ void EventThread::process(RGSSThreadData &rtData)
 
 				SDL_SetWindowTitle(win, buffer);
 				break;
+
+			case UPDATE_SCREEN_RECT :
+				gameScreen.x = event.user.windowID;
+				gameScreen.y = event.user.code;
+				gameScreen.w = reinterpret_cast<intptr_t>(event.user.data1);
+				gameScreen.h = reinterpret_cast<intptr_t>(event.user.data2);
+				updateCursorState(cursorInWindow, gameScreen);
+
+				break;
 			}
 		}
 
@@ -532,9 +545,13 @@ void EventThread::setFullscreen(SDL_Window *win, bool mode)
 	fullscreen = mode;
 }
 
-void EventThread::updateCursorState(bool inWindow)
+void EventThread::updateCursorState(bool inWindow,
+                                    const SDL_Rect &screen)
 {
-	if (inWindow)
+	SDL_Point pos = { mouseState.x, mouseState.y };
+	bool inScreen = inWindow && SDL_PointInRect(&pos, &screen);
+
+	if (inScreen)
 		SDL_ShowCursor(showCursor ? SDL_TRUE : SDL_FALSE);
 	else
 		SDL_ShowCursor(SDL_TRUE);
@@ -640,6 +657,19 @@ void EventThread::notifyFrame()
 	SDL_PushEvent(&event);
 }
 
+void EventThread::notifyGameScreenChange(const SDL_Rect &screen)
+{
+	/* We have to get a bit hacky here to fit the rectangle
+	 * data into the user event struct */
+	SDL_Event event;
+	event.type = usrIdStart + UPDATE_SCREEN_RECT;
+	event.user.windowID = screen.x;
+	event.user.code = screen.y;
+	event.user.data1 = reinterpret_cast<void*>(screen.w);
+	event.user.data2 = reinterpret_cast<void*>(screen.h);
+	SDL_PushEvent(&event);
+}
+
 void SyncPoint::haltThreads()
 {
 	if (mainSync.locked)
diff --git a/src/eventthread.h b/src/eventthread.h
index 46051f1..02a9ea1 100644
--- a/src/eventthread.h
+++ b/src/eventthread.h
@@ -98,12 +98,16 @@ public:
 	/* RGSS thread calls this once per frame */
 	void notifyFrame();
 
+	/* Called on game screen (size / offset) changes */
+	void notifyGameScreenChange(const SDL_Rect &screen);
+
 private:
 	static int eventFilter(void *, SDL_Event*);
 
 	void resetInputStates();
 	void setFullscreen(SDL_Window *, bool mode);
-	void updateCursorState(bool inWindow);
+	void updateCursorState(bool inWindow,
+	                       const SDL_Rect &screen);
 
 	bool fullscreen;
 	bool showCursor;
diff --git a/src/graphics.cpp b/src/graphics.cpp
index fe51f74..d205697 100644
--- a/src/graphics.cpp
+++ b/src/graphics.cpp
@@ -566,6 +566,9 @@ struct GraphicsPrivate
 			glState.viewport.refresh();
 			recalculateScreenSize(threadData);
 			updateScreenResoRatio(threadData);
+
+			SDL_Rect screen = { scOffset.x, scOffset.y, scSize.x, scSize.y };
+			threadData->ethread->notifyGameScreenChange(screen);
 		}
 	}
 

From d25551e2666397dfb9321bef064d5c0f96198622 Mon Sep 17 00:00:00 2001
From: Jonas Kulla <Nyocurio@gmail.com>
Date: Sat, 8 Apr 2017 18:41:56 +0200
Subject: [PATCH 06/13] Input: Remove ugly [-20,-20] mouse position hack

This was supposed to disappear shortly after To the Moon's release,
but it unfortunately survived a bit longer :)

The status of the mouse cursor being inside / outside the game window
is now properly exposed (in MRI) via MKXP.mouse_in_window.
---
 binding-mri/binding-mri.cpp | 9 +++++++++
 src/input.cpp               | 6 ------
 2 files changed, 9 insertions(+), 6 deletions(-)

diff --git a/binding-mri/binding-mri.cpp b/binding-mri/binding-mri.cpp
index c28cdaa..f0a4bb4 100644
--- a/binding-mri/binding-mri.cpp
+++ b/binding-mri/binding-mri.cpp
@@ -80,6 +80,7 @@ RB_METHOD(mriP);
 RB_METHOD(mkxpDataDirectory);
 RB_METHOD(mkxpPuts);
 RB_METHOD(mkxpRawKeyStates);
+RB_METHOD(mkxpMouseInWindow);
 
 RB_METHOD(mriRgssMain);
 RB_METHOD(mriRgssStop);
@@ -144,6 +145,7 @@ static void mriBindingInit()
 	_rb_define_module_function(mod, "data_directory", mkxpDataDirectory);
 	_rb_define_module_function(mod, "puts", mkxpPuts);
 	_rb_define_module_function(mod, "raw_key_states", mkxpRawKeyStates);
+	_rb_define_module_function(mod, "mouse_in_window", mkxpMouseInWindow);
 
 	rb_gv_set("MKXP", Qtrue);
 }
@@ -222,6 +224,13 @@ RB_METHOD(mkxpRawKeyStates)
 	return str;
 }
 
+RB_METHOD(mkxpMouseInWindow)
+{
+	RB_UNUSED_PARAM;
+
+	return rb_bool_new(EventThread::mouseState.inWindow);
+}
+
 static VALUE rgssMainCb(VALUE block)
 {
 	rb_funcall2(block, rb_intern("call"), 0, 0);
diff --git a/src/input.cpp b/src/input.cpp
index d7978bd..22dab5d 100644
--- a/src/input.cpp
+++ b/src/input.cpp
@@ -665,9 +665,6 @@ int Input::mouseX()
 {
 	RGSSThreadData &rtData = shState->rtData();
 
-	if (!EventThread::mouseState.inWindow)
-		return -20;
-
 	return (EventThread::mouseState.x - rtData.screenOffset.x) * rtData.sizeResoRatio.x;
 }
 
@@ -675,9 +672,6 @@ int Input::mouseY()
 {
 	RGSSThreadData &rtData = shState->rtData();
 
-	if (!EventThread::mouseState.inWindow)
-		return -20;
-
 	return (EventThread::mouseState.y - rtData.screenOffset.y) * rtData.sizeResoRatio.y;
 }
 

From 2e240d9fbec83cc431202de905201bdfbda4a0f9 Mon Sep 17 00:00:00 2001
From: Jonas Kulla <Nyocurio@gmail.com>
Date: Sat, 8 Apr 2017 19:13:31 +0200
Subject: [PATCH 07/13] Fix build on OSX after
 60e967e3b781ce170b30ffd744e132145ef37bd7

---
 src/main.cpp | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/src/main.cpp b/src/main.cpp
index 1ad3ee2..a87ae48 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -42,10 +42,10 @@
 
 #ifdef __WINDOWS__
 #include "resource.h"
-#elif __LINUX__
-#include "icon.png.xxd"
 #endif
 
+#include "icon.png.xxd"
+
 static void
 rgssThreadError(RGSSThreadData *rtData, const std::string &msg)
 {

From 1bc27177443776dfb835f7f7ff980d369f34f7ac Mon Sep 17 00:00:00 2001
From: Jonas Kulla <Nyocurio@gmail.com>
Date: Sat, 8 Apr 2017 20:06:12 +0200
Subject: [PATCH 08/13] Add missing include

---
 src/eventthread.cpp | 1 +
 1 file changed, 1 insertion(+)

diff --git a/src/eventthread.cpp b/src/eventthread.cpp
index 42dab49..08b6c29 100644
--- a/src/eventthread.cpp
+++ b/src/eventthread.cpp
@@ -27,6 +27,7 @@
 #include <SDL_timer.h>
 #include <SDL_thread.h>
 #include <SDL_touch.h>
+#include <SDL_rect.h>
 
 #include <al.h>
 #include <alc.h>

From 9fec876e3240ac3503db748e0f7d9094e86a5602 Mon Sep 17 00:00:00 2001
From: Jonas Kulla <Nyocurio@gmail.com>
Date: Sun, 23 Apr 2017 12:28:34 +0200
Subject: [PATCH 09/13] Config: Add "maxTextureSize" entry to artificially
 limit texture sizes

---
 mkxp.conf.sample    | 11 +++++++++++
 src/config.cpp      |  1 +
 src/config.h        |  1 +
 src/glstate.cpp     |  6 +++++-
 src/glstate.h       |  4 +++-
 src/sharedstate.cpp |  1 +
 6 files changed, 22 insertions(+), 2 deletions(-)

diff --git a/mkxp.conf.sample b/mkxp.conf.sample
index b88f216..0062451 100644
--- a/mkxp.conf.sample
+++ b/mkxp.conf.sample
@@ -124,6 +124,17 @@
 # subImageFix=false
 
 
+# Limit the maximum size (width, height) of
+# most textures mkxp will create (exceptions are
+# rendering backbuffers and similar).
+# If set to 0, the hardware maximum is used.
+# This is useful for recording traces that can
+# be played back on machines with lower specs.
+# (default: 0)
+#
+# maxTextureSize=0
+
+
 # Set the base path of the game to '/path/to/game'
 # (default: executable directory)
 #
diff --git a/src/config.cpp b/src/config.cpp
index a58ddab..0fab180 100644
--- a/src/config.cpp
+++ b/src/config.cpp
@@ -166,6 +166,7 @@ void Config::read(int argc, char *argv[])
 	PO_DESC(syncToRefreshrate, bool, false) \
 	PO_DESC(solidFonts, bool, false) \
 	PO_DESC(subImageFix, bool, false) \
+	PO_DESC(maxTextureSize, int, 0) \
 	PO_DESC(gameFolder, std::string, ".") \
 	PO_DESC(anyAltToggleFS, bool, false) \
 	PO_DESC(enableReset, bool, true) \
diff --git a/src/config.h b/src/config.h
index d82698d..f6dc2c4 100644
--- a/src/config.h
+++ b/src/config.h
@@ -49,6 +49,7 @@ struct Config
 	bool solidFonts;
 
 	bool subImageFix;
+	int maxTextureSize;
 
 	std::string gameFolder;
 	bool anyAltToggleFS;
diff --git a/src/glstate.cpp b/src/glstate.cpp
index 73aec31..ff88de7 100644
--- a/src/glstate.cpp
+++ b/src/glstate.cpp
@@ -23,6 +23,7 @@
 #include "shader.h"
 #include "etc.h"
 #include "gl-fun.h"
+#include "config.h"
 
 #include <SDL_rect.h>
 
@@ -111,7 +112,7 @@ GLState::Caps::Caps()
 	gl.GetIntegerv(GL_MAX_TEXTURE_SIZE, &maxTexSize);
 }
 
-GLState::GLState()
+GLState::GLState(const Config &conf)
 {
 	gl.Disable(GL_DEPTH_TEST);
 
@@ -121,4 +122,7 @@ GLState::GLState()
 	scissorTest.init(false);
 	scissorBox.init(IntRect(0, 0, 640, 480));
 	program.init(0);
+
+	if (conf.maxTextureSize > 0)
+		caps.maxTexSize = conf.maxTextureSize;
 }
diff --git a/src/glstate.h b/src/glstate.h
index 63b0bcb..02830e6 100644
--- a/src/glstate.h
+++ b/src/glstate.h
@@ -27,6 +27,8 @@
 #include <stack>
 #include <assert.h>
 
+struct Config;
+
 template<typename T>
 struct GLProperty
 {
@@ -130,7 +132,7 @@ public:
 
 	} caps;
 
-	GLState();
+	GLState(const Config &conf);
 };
 
 #endif // GLSTATE_H
diff --git a/src/sharedstate.cpp b/src/sharedstate.cpp
index 9872af7..0778da9 100644
--- a/src/sharedstate.cpp
+++ b/src/sharedstate.cpp
@@ -109,6 +109,7 @@ struct SharedStatePrivate
 	      graphics(threadData),
 	      input(*threadData),
 	      audio(*threadData),
+	      _glState(threadData->config),
 	      fontState(threadData->config),
 	      stampCounter(0)
 	{

From 04634ed8fe3670c4366d74fe46c414006ff9568f Mon Sep 17 00:00:00 2001
From: Jonas Kulla <Nyocurio@gmail.com>
Date: Sun, 23 Apr 2017 14:32:11 +0200
Subject: [PATCH 10/13] Config: Add "enableBlitting" entry to toggle
 GL_EXT_framebuffer_blit

---
 mkxp.conf.sample | 9 +++++++++
 src/config.cpp   | 1 +
 src/config.h     | 1 +
 src/main.cpp     | 3 +++
 4 files changed, 14 insertions(+)

diff --git a/mkxp.conf.sample b/mkxp.conf.sample
index 0062451..500dcc6 100644
--- a/mkxp.conf.sample
+++ b/mkxp.conf.sample
@@ -124,6 +124,15 @@
 # subImageFix=false
 
 
+# Enable framebuffer blitting if the driver is
+# capable of it. Some drivers carry buggy
+# implementations of this functionality, so
+# disabling it can be used as a workaround
+# (default: enabled)
+#
+# enableBlitting=true
+
+
 # Limit the maximum size (width, height) of
 # most textures mkxp will create (exceptions are
 # rendering backbuffers and similar).
diff --git a/src/config.cpp b/src/config.cpp
index 0fab180..33d8160 100644
--- a/src/config.cpp
+++ b/src/config.cpp
@@ -166,6 +166,7 @@ void Config::read(int argc, char *argv[])
 	PO_DESC(syncToRefreshrate, bool, false) \
 	PO_DESC(solidFonts, bool, false) \
 	PO_DESC(subImageFix, bool, false) \
+	PO_DESC(enableBlitting, bool, true) \
 	PO_DESC(maxTextureSize, int, 0) \
 	PO_DESC(gameFolder, std::string, ".") \
 	PO_DESC(anyAltToggleFS, bool, false) \
diff --git a/src/config.h b/src/config.h
index f6dc2c4..d2d4650 100644
--- a/src/config.h
+++ b/src/config.h
@@ -49,6 +49,7 @@ struct Config
 	bool solidFonts;
 
 	bool subImageFix;
+	bool enableBlitting;
 	int maxTextureSize;
 
 	std::string gameFolder;
diff --git a/src/main.cpp b/src/main.cpp
index a87ae48..1a3fc4e 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -102,6 +102,9 @@ int rgssThreadFun(void *userdata)
 		return 0;
 	}
 
+	if (!conf.enableBlitting)
+		gl.BlitFramebuffer = 0;
+
 	gl.ClearColor(0, 0, 0, 1);
 	gl.Clear(GL_COLOR_BUFFER_BIT);
 	SDL_GL_SwapWindow(win);

From 4063e7aa9d052c973658cd78f7db549a224bd155 Mon Sep 17 00:00:00 2001
From: Jonas Kulla <Nyocurio@gmail.com>
Date: Thu, 11 May 2017 12:20:08 +0200
Subject: [PATCH 11/13] Graphics: Use proper resizing function for TEXFBOs

Manually resizing the contained TEX objects skips updating the
width/height TEXFBO properties, which GLMeta::blit relies on.
---
 src/graphics.cpp | 15 +++++----------
 1 file changed, 5 insertions(+), 10 deletions(-)

diff --git a/src/graphics.cpp b/src/graphics.cpp
index d205697..8a6301a 100644
--- a/src/graphics.cpp
+++ b/src/graphics.cpp
@@ -92,11 +92,9 @@ struct PingPong
 	{
 		screenW = width;
 		screenH = height;
+
 		for (int i = 0; i < 2; ++i)
-		{
-			TEX::bind(rt[i].tex);
-			TEX::allocEmpty(width, height);
-		}
+			TEXFBO::allocEmpty(rt[i], width, height);
 	}
 
 	void startRender()
@@ -946,16 +944,13 @@ void Graphics::resizeScreen(int width, int height)
 
 	p->screen.setResolution(width, height);
 
-	TEX::bind(p->frozenScene.tex);
-	TEX::allocEmpty(width, height);
-	TEX::bind(p->currentScene.tex);
-	TEX::allocEmpty(width, height);
+	TEXFBO::allocEmpty(p->frozenScene, width, height);
+	TEXFBO::allocEmpty(p->currentScene, width, height);
 
 	FloatRect screenRect(0, 0, width, height);
 	p->screenQuad.setTexPosRect(screenRect, screenRect);
 
-	TEX::bind(p->transBuffer.tex);
-	TEX::allocEmpty(width, height);
+	TEXFBO::allocEmpty(p->transBuffer, width, height);
 
 	shState->eThread().requestWindowResize(width, height);
 }

From 128cc08e89bc39df931dae827693b7503092c530 Mon Sep 17 00:00:00 2001
From: Jonas Kulla <Nyocurio@gmail.com>
Date: Thu, 11 May 2017 12:37:15 +0200
Subject: [PATCH 12/13] Graphics: Remove superfluous TEXFBOs while reusing
 existing ones

While the PingPong buffers were always texture-backed, currentScene
and transBuffer used to be backed by renderbuffers, which might have
been more optimized as render targets on older hardware; but since
all buffers in Graphics got switched to being texture backed to allow
blitting via rendering (when hardware blitting isn't available or broken,
eg. on mobile platforms), their reason to exist vanished.

For transBuffer, we can reuse the backbuffer of the PingPong structure,
while currentScene might have been useless from the start.
---
 src/graphics.cpp | 33 ++++++++++++---------------------
 1 file changed, 12 insertions(+), 21 deletions(-)

diff --git a/src/graphics.cpp b/src/graphics.cpp
index 8a6301a..6a12cb3 100644
--- a/src/graphics.cpp
+++ b/src/graphics.cpp
@@ -474,9 +474,7 @@ struct GraphicsPrivate
 
 	bool frozen;
 	TEXFBO frozenScene;
-	TEXFBO currentScene;
 	Quad screenQuad;
-	TEXFBO transBuffer;
 
 	/* Global list of all live Disposables
 	 * (disposed on reset) */
@@ -502,26 +500,15 @@ struct GraphicsPrivate
 		TEXFBO::allocEmpty(frozenScene, scRes.x, scRes.y);
 		TEXFBO::linkFBO(frozenScene);
 
-		TEXFBO::init(currentScene);
-		TEXFBO::allocEmpty(currentScene, scRes.x, scRes.y);
-		TEXFBO::linkFBO(currentScene);
-
 		FloatRect screenRect(0, 0, scRes.x, scRes.y);
 		screenQuad.setTexPosRect(screenRect, screenRect);
 
-		TEXFBO::init(transBuffer);
-		TEXFBO::allocEmpty(transBuffer, scRes.x, scRes.y);
-		TEXFBO::linkFBO(transBuffer);
-
 		fpsLimiter.resetFrameAdjust();
 	}
 
 	~GraphicsPrivate()
 	{
 		TEXFBO::fini(frozenScene);
-		TEXFBO::fini(currentScene);
-
-		TEXFBO::fini(transBuffer);
 	}
 
 	void updateScreenResoRatio(RGSSThreadData *rtData)
@@ -721,8 +708,15 @@ void Graphics::transition(int duration,
 
 	setBrightness(255);
 
+	/* The PP frontbuffer will hold the current scene after the
+	 * composition step. Since the backbuffer is unused during
+	 * the transition, we can reuse it as the target buffer for
+	 * the final rendered image. */
+	TEXFBO &currentScene = p->screen.getPP().frontBuffer();
+	TEXFBO &transBuffer  = p->screen.getPP().backBuffer();
+
 	/* Capture new scene */
-	p->compositeToBuffer(p->currentScene);
+	p->screen.composite();
 
 	/* If no transition bitmap is provided,
 	 * we can use a simplified shader */
@@ -735,7 +729,7 @@ void Graphics::transition(int duration,
 		shader.bind();
 		shader.applyViewportProj();
 		shader.setFrozenScene(p->frozenScene.tex);
-		shader.setCurrentScene(p->currentScene.tex);
+		shader.setCurrentScene(currentScene.tex);
 		shader.setTransMap(transMap->getGLTypes().tex);
 		shader.setVague(vague / 256.0f);
 		shader.setTexSize(p->scRes);
@@ -746,7 +740,7 @@ void Graphics::transition(int duration,
 		shader.bind();
 		shader.applyViewportProj();
 		shader.setFrozenScene(p->frozenScene.tex);
-		shader.setCurrentScene(p->currentScene.tex);
+		shader.setCurrentScene(currentScene.tex);
 		shader.setTexSize(p->scRes);
 	}
 
@@ -790,7 +784,7 @@ void Graphics::transition(int duration,
 
 		/* Draw the composed frame to a buffer first
 		 * (we need this because we're skipping PingPong) */
-		FBO::bind(p->transBuffer.fbo);
+		FBO::bind(transBuffer.fbo);
 		FBO::clear();
 		p->screenQuad.draw();
 
@@ -801,7 +795,7 @@ void Graphics::transition(int duration,
 		FBO::clear();
 
 		GLMeta::blitBeginScreen(Vec2i(p->winSize));
-		GLMeta::blitSource(p->transBuffer);
+		GLMeta::blitSource(transBuffer);
 		p->metaBlitBufferFlippedScaled();
 		GLMeta::blitEnd();
 
@@ -945,13 +939,10 @@ void Graphics::resizeScreen(int width, int height)
 	p->screen.setResolution(width, height);
 
 	TEXFBO::allocEmpty(p->frozenScene, width, height);
-	TEXFBO::allocEmpty(p->currentScene, width, height);
 
 	FloatRect screenRect(0, 0, width, height);
 	p->screenQuad.setTexPosRect(screenRect, screenRect);
 
-	TEXFBO::allocEmpty(p->transBuffer, width, height);
-
 	shState->eThread().requestWindowResize(width, height);
 }
 

From c09d24ff0a0678f46c595c396afc204bd9f19942 Mon Sep 17 00:00:00 2001
From: Marty Plummer <ntzrmtthihu777@gmail.com>
Date: Thu, 25 May 2017 04:17:02 -0500
Subject: [PATCH 13/13] Allows cross-compilation to windows targets.

Tested on gentoo with x86_64-w64-mingw32 toolchain and libraries.

Signed-off-by: Marty Plummer <ntzrmtthihu777@gmail.com>
---
 CMakeLists.txt | 6 ++++++
 1 file changed, 6 insertions(+)

diff --git a/CMakeLists.txt b/CMakeLists.txt
index d875826..7029f79 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -99,6 +99,7 @@ find_package(ZLIB REQUIRED)
 ## Setup main source ##
 
 set(MAIN_HEADERS
+	assets/resource.h
 	src/quadarray.h
 	src/audio.h
 	src/binding.h
@@ -205,6 +206,10 @@ set(MAIN_SOURCE
 	src/fluid-fun.cpp
 )
 
+if(WIN32)
+	list(APPEND MAIN_SOURCE assets/resource.rc)
+endif()
+
 source_group("MKXP Source" FILES ${MAIN_SOURCE} ${MAIN_HEADERS})
 
 ## Setup embedded source ##
@@ -404,6 +409,7 @@ target_compile_definitions(${PROJECT_NAME} PRIVATE
 	${DEFINES}
 )
 target_include_directories(${PROJECT_NAME} PRIVATE
+	assets
 	src
 	${SIGCXX_INCLUDE_DIRS}
 	${PIXMAN_INCLUDE_DIRS}