diff --git a/CMakeLists.txt b/CMakeLists.txt
index 2e320de..d9b4e51 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -205,6 +205,11 @@ set(MAIN_SOURCE
 	src/fluid-fun.cpp
 )
 
+if(WIN32)
+	list(APPEND MAIN_HEADERS windows/resource.h)
+	list(APPEND MAIN_SOURCE windows/resource.rc)
+endif()
+
 source_group("MKXP Source" FILES ${MAIN_SOURCE} ${MAIN_HEADERS})
 
 ## Setup embedded source ##
@@ -415,6 +420,7 @@ target_compile_definitions(${PROJECT_NAME} PRIVATE
 )
 target_include_directories(${PROJECT_NAME} PRIVATE
 	src
+	windows
 	${SIGCXX_INCLUDE_DIRS}
 	${PIXMAN_INCLUDE_DIRS}
 	${PHYSFS_INCLUDE_DIRS}
diff --git a/binding-mri/binding-util.h b/binding-mri/binding-util.h
index 83589f7..1b758be 100644
--- a/binding-mri/binding-util.h
+++ b/binding-mri/binding-util.h
@@ -67,7 +67,7 @@ raiseRbExc(const Exception &exc);
 
 /* 2.1 has added a new field (flags) to rb_data_type_t */
 #include <ruby/version.h>
-#if RUBY_API_VERSION_MINOR > 0
+#if RUBY_API_VERSION_MAJOR >= 2 && RUBY_API_VERSION_MINOR >= 1
 /* TODO: can mkxp use RUBY_TYPED_FREE_IMMEDIATELY here? */
 #define DEF_TYPE_FLAGS 0
 #else
@@ -90,7 +90,12 @@ raiseRbExc(const Exception &exc);
 template<rb_data_type_t *rbType>
 static VALUE classAllocate(VALUE klass)
 {
+/* 2.3 has changed the name of this function */
+#if RUBY_API_VERSION_MAJOR >= 2 && RUBY_API_VERSION_MINOR >= 3
+	return rb_data_typed_object_wrap(klass, 0, rbType);
+#else
 	return rb_data_typed_object_alloc(klass, 0, rbType);
+#endif
 }
 
 template<class C>
diff --git a/mkxp.conf.sample b/mkxp.conf.sample
index 500dcc6..9863eaf 100644
--- a/mkxp.conf.sample
+++ b/mkxp.conf.sample
@@ -86,6 +86,12 @@
 # defScreenH=480
 
 
+# Override the game window title
+# (default: none)
+#
+# windowTitle=Custom Title
+
+
 # Enforce a static frame rate
 # (0 = disabled)
 #
diff --git a/src/config.cpp b/src/config.cpp
index 4396877..fdc0033 100644
--- a/src/config.cpp
+++ b/src/config.cpp
@@ -161,6 +161,7 @@ void Config::read(int argc, char *argv[])
 	PO_DESC(vsync, bool, false) \
 	PO_DESC(defScreenW, int, 0) \
 	PO_DESC(defScreenH, int, 0) \
+	PO_DESC(windowTitle, std::string, "") \
 	PO_DESC(fixedFramerate, int, 0) \
 	PO_DESC(frameSkip, bool, true) \
 	PO_DESC(syncToRefreshrate, bool, false) \
diff --git a/src/config.h b/src/config.h
index 2108413..5060e60 100644
--- a/src/config.h
+++ b/src/config.h
@@ -47,6 +47,7 @@ struct Config
 
 	int defScreenW;
 	int defScreenH;
+	std::string windowTitle;
 
 	int fixedFramerate;
 	bool frameSkip;
diff --git a/src/filesystem.cpp b/src/filesystem.cpp
index d7fd36b..f27b3bd 100644
--- a/src/filesystem.cpp
+++ b/src/filesystem.cpp
@@ -398,8 +398,8 @@ struct CacheEnumData
 	}
 };
 
-static void cacheEnumCB(void *d, const char *origdir,
-                        const char *fname)
+static PHYSFS_EnumerateCallbackResult
+cacheEnumCB(void *d, const char *origdir, const char *fname)
 {
 	CacheEnumData &data = *static_cast<CacheEnumData*>(d);
 	char fullPath[512];
@@ -426,7 +426,7 @@ static void cacheEnumCB(void *d, const char *origdir,
 
 		/* Iterate over its contents */
 		data.fileLists.push(&list);
-		PHYSFS_enumerateFilesCallback(fullPath, cacheEnumCB, d);
+		PHYSFS_enumerate(fullPath, cacheEnumCB, d);
 		data.fileLists.pop();
 	}
 	else
@@ -441,13 +441,15 @@ static void cacheEnumCB(void *d, const char *origdir,
 		/* Add the lower -> mixed mapping of the file's full path */
 		data.p->pathCache.insert(lowerCase, mixedCase);
 	}
+
+	return PHYSFS_ENUM_OK;
 }
 
 void FileSystem::createPathCache()
 {
 	CacheEnumData data(p);
 	data.fileLists.push(&p->fileLists[""]);
-	PHYSFS_enumerateFilesCallback("", cacheEnumCB, &data);
+	PHYSFS_enumerate("", cacheEnumCB, &data);
 
 	p->havePathCache = true;
 }
@@ -458,8 +460,8 @@ struct FontSetsCBData
 	SharedFontState *sfs;
 };
 
-static void fontSetEnumCB(void *data, const char *dir,
-                          const char *fname)
+static PHYSFS_EnumerateCallbackResult
+fontSetEnumCB (void *data, const char *dir, const char *fname)
 {
 	FontSetsCBData *d = static_cast<FontSetsCBData*>(data);
 
@@ -467,7 +469,7 @@ static void fontSetEnumCB(void *data, const char *dir,
 	const char *ext = findExt(fname);
 
 	if (!ext)
-		return;
+		return PHYSFS_ENUM_STOP;
 
 	char lowExt[8];
 	size_t i;
@@ -477,7 +479,7 @@ static void fontSetEnumCB(void *data, const char *dir,
 	lowExt[i] = '\0';
 
 	if (strcmp(lowExt, "ttf") && strcmp(lowExt, "otf"))
-		return;
+		return PHYSFS_ENUM_STOP;
 
 	char filename[512];
 	snprintf(filename, sizeof(filename), "%s/%s", dir, fname);
@@ -485,7 +487,7 @@ static void fontSetEnumCB(void *data, const char *dir,
 	PHYSFS_File *handle = PHYSFS_openRead(filename);
 
 	if (!handle)
-		return;
+		return PHYSFS_ENUM_ERROR;
 
 	SDL_RWops ops;
 	initReadOps(handle, ops, false);
@@ -493,12 +495,14 @@ static void fontSetEnumCB(void *data, const char *dir,
 	d->sfs->initFontSetCB(ops, filename);
 
 	SDL_RWclose(&ops);
+
+	return PHYSFS_ENUM_OK;
 }
 
 /* Basically just a case-insensitive search
  * for the folder "Fonts"... */
-static void findFontsFolderCB(void *data, const char *,
-                              const char *fname)
+static PHYSFS_EnumerateCallbackResult
+findFontsFolderCB(void *data, const char *, const char *fname)
 {
 	size_t i = 0;
 	char buffer[512];
@@ -510,14 +514,16 @@ static void findFontsFolderCB(void *data, const char *,
 	buffer[i] = '\0';
 
 	if (strcmp(buffer, "fonts") == 0)
-		PHYSFS_enumerateFilesCallback(fname, fontSetEnumCB, data);
+		PHYSFS_enumerate(fname, fontSetEnumCB, data);
+
+	return PHYSFS_ENUM_OK;
 }
 
 void FileSystem::initFontSets(SharedFontState &sfs)
 {
 	FontSetsCBData d = { p, &sfs };
 
-	PHYSFS_enumerateFilesCallback(".", findFontsFolderCB, &d);
+	PHYSFS_enumerate(".", findFontsFolderCB, &d);
 }
 
 struct OpenReadEnumData
@@ -550,19 +556,19 @@ struct OpenReadEnumData
 	{}
 };
 
-static void openReadEnumCB(void *d, const char *dirpath,
-                           const char *filename)
+static PHYSFS_EnumerateCallbackResult
+openReadEnumCB(void *d, const char *dirpath, const char *filename)
 {
 	OpenReadEnumData &data = *static_cast<OpenReadEnumData*>(d);
 	char buffer[512];
 	const char *fullPath;
 
 	if (data.stopSearching)
-		return;
+		return PHYSFS_ENUM_STOP;
 
 	/* If there's not even a partial match, continue searching */
 	if (strncmp(filename, data.filename, data.filenameN) != 0)
-		return;
+		return PHYSFS_ENUM_OK;
 
 	if (!*dirpath)
 	{
@@ -580,7 +586,7 @@ static void openReadEnumCB(void *d, const char *dirpath,
 	 * of the extension), or up to a following '\0' (full match), we've
 	 * found our file */
 	if (last != '.' && last != '\0')
-		return;
+		return PHYSFS_ENUM_STOP;
 
 	/* If the path cache is active, translate from lower case
 	 * to mixed case path */
@@ -595,9 +601,9 @@ static void openReadEnumCB(void *d, const char *dirpath,
 		 * be a deeper rooted problem somewhere within PhysFS.
 		 * Just abort alltogether. */
 		data.stopSearching = true;
-		data.physfsError = PHYSFS_getLastError();
+		data.physfsError = PHYSFS_getErrorByCode(PHYSFS_getLastErrorCode());
 
-		return;
+		return PHYSFS_ENUM_ERROR;
 	}
 
 	initReadOps(phys, data.ops, false);
@@ -608,6 +614,7 @@ static void openReadEnumCB(void *d, const char *dirpath,
 		data.stopSearching = true;
 
 	++data.matchCount;
+	return PHYSFS_ENUM_OK;
 }
 
 void FileSystem::openRead(OpenHandler &handler, const char *filename)
@@ -653,7 +660,7 @@ void FileSystem::openRead(OpenHandler &handler, const char *filename)
 	}
 	else
 	{
-		PHYSFS_enumerateFilesCallback(dir, openReadEnumCB, &data);
+		PHYSFS_enumerate(dir, openReadEnumCB, &data);
 	}
 
 	if (data.physfsError)
diff --git a/src/main.cpp b/src/main.cpp
index 358f836..bed78e5 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -233,6 +233,9 @@ int main(int argc, char *argv[])
 
 	conf.readGameINI();
 
+	if (conf.windowTitle.empty())
+		conf.windowTitle = conf.game.title;
+
 	assert(conf.rgssVersion >= 1 && conf.rgssVersion <= 3);
 	printRgssVersion(conf.rgssVersion);
 
@@ -272,7 +275,7 @@ int main(int argc, char *argv[])
 	if (conf.fullscreen)
 		winFlags |= SDL_WINDOW_FULLSCREEN_DESKTOP;
 
-	win = SDL_CreateWindow(conf.game.title.c_str(),
+	win = SDL_CreateWindow(conf.windowTitle.c_str(),
 	                       SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED,
 	                       conf.defScreenW, conf.defScreenH, winFlags);
 
diff --git a/src/rgssad.cpp b/src/rgssad.cpp
index 17e8417..4b51472 100644
--- a/src/rgssad.cpp
+++ b/src/rgssad.cpp
@@ -331,14 +331,16 @@ verifyHeader(PHYSFS_Io *io, char version)
 }
 
 static void*
-RGSS_openArchive(PHYSFS_Io *io, const char *, int forWrite)
+RGSS_openArchive(PHYSFS_Io *io, const char *, int forWrite, int *claimed)
 {
 	if (forWrite)
-		return 0;
+		return NULL;
 
 	/* Version 1 */
 	if (!verifyHeader(io, 1))
-		return 0;
+		return NULL;
+	else
+		*claimed = 1;
 
 	RGSS_archiveData *data = new RGSS_archiveData;
 	data->archiveIo = io;
@@ -389,9 +391,9 @@ RGSS_openArchive(PHYSFS_Io *io, const char *, int forWrite)
 	return data;
 }
 
-static void
+static PHYSFS_EnumerateCallbackResult
 RGSS_enumerateFiles(void *opaque, const char *dirname,
-                    PHYSFS_EnumFilesCallback cb,
+                    PHYSFS_EnumerateCallback cb,
                     const char *origdir, void *callbackdata)
 {
 	RGSS_archiveData *data = static_cast<RGSS_archiveData*>(opaque);
@@ -399,13 +401,15 @@ RGSS_enumerateFiles(void *opaque, const char *dirname,
 	std::string _dirname(dirname);
 
 	if (!data->dirHash.contains(_dirname))
-		return;
+		return PHYSFS_ENUM_STOP;
 
 	const BoostSet<std::string> &entries = data->dirHash[_dirname];
 
 	BoostSet<std::string>::const_iterator iter;
 	for (iter = entries.cbegin(); iter != entries.cend(); ++iter)
 		cb(callbackdata, origdir, iter->c_str());
+
+	return PHYSFS_ENUM_OK;
 }
 
 static PHYSFS_Io*
@@ -536,19 +540,21 @@ readUint32AndXor(PHYSFS_Io *io, uint32_t &result, uint32_t key)
 }
 
 static void*
-RGSS3_openArchive(PHYSFS_Io *io, const char *, int forWrite)
+RGSS3_openArchive(PHYSFS_Io *io, const char *, int forWrite, int *claimed)
 {
 	if (forWrite)
-		return 0;
+		return NULL;
 
 	/* Version 3 */
 	if (!verifyHeader(io, 3))
-		return 0;
+		return NULL;
+	else
+		*claimed = 1;
 
 	uint32_t baseMagic;
 
 	if (!readUint32(io, baseMagic))
-		return 0;
+		return NULL;
 
 	baseMagic = (baseMagic * 9) + 3;
 
@@ -605,7 +611,7 @@ RGSS3_openArchive(PHYSFS_Io *io, const char *, int forWrite)
 
 	error:
 		delete data;
-		return 0;
+		return NULL;
 	}
 
 	return data;
diff --git a/src/sprite.cpp b/src/sprite.cpp
index 7999546..26eb297 100644
--- a/src/sprite.cpp
+++ b/src/sprite.cpp
@@ -121,7 +121,7 @@ struct SpritePrivate
 
 	void recomputeBushDepth()
 	{
-		if (!bitmap)
+		if (nullOrDisposed(bitmap))
 			return;
 
 		/* Calculate effective (normalized) bush depth */
@@ -137,18 +137,15 @@ struct SpritePrivate
 		FloatRect rect = srcRect->toFloatRect();
 		Vec2i bmSize;
 
-		if (bitmap)
+		if (!nullOrDisposed(bitmap))
 			bmSize = Vec2i(bitmap->width(), bitmap->height());
 
-		if (mirrored)
-			rect = rect.hFlipped();
-
 		/* Clamp the rectangle so it doesn't reach outside
 		 * the bitmap bounds */
 		rect.w = clamp<int>(rect.w, 0, bmSize.x-rect.x);
 		rect.h = clamp<int>(rect.h, 0, bmSize.y-rect.y);
 
-		quad.setTexRect(rect);
+		quad.setTexRect(mirrored ? rect.hFlipped() : rect);
 
 		quad.setPosRect(FloatRect(0, 0, rect.w, rect.h));
 		recomputeBushDepth();
diff --git a/assets/icon.ico b/windows/icon.ico
similarity index 100%
rename from assets/icon.ico
rename to windows/icon.ico
diff --git a/assets/resource.h b/windows/resource.h
similarity index 100%
rename from assets/resource.h
rename to windows/resource.h
diff --git a/assets/resource.rc b/windows/resource.rc
similarity index 100%
rename from assets/resource.rc
rename to windows/resource.rc