diff --git a/README.md b/README.md
index fede712..d9f9c09 100644
--- a/README.md
+++ b/README.md
@@ -20,6 +20,8 @@ Matz's Ruby Interpreter, also called CRuby, is the most widely deployed version
 For a list of differences, see:
 http://stackoverflow.com/questions/21574/what-is-the-difference-between-ruby-1-8-and-ruby-1-9
 
+This binding supports RGSS1, RGSS2 and RGSS3.
+
 ### mruby (Lightweight Ruby)
 Website: https://github.com/mruby/mruby
 
@@ -29,6 +31,8 @@ Due to heavy differences between mruby and MRI as well as lacking modules, runni
 
 Some extensions to the standard classes/modules are provided, taking the RPG Maker XP helpfile as a quasi "reference". These include Marshal, File, FileTest and Time.
 
+This binding only supports RGSS1.
+
 **Important:** If you decide to use [mattn's oniguruma regexp gem](https://github.com/mattn/mruby-onig-regexp), don't forget to add `-lonig` to the linker flags to avoid ugly symbol overlaps with libc.
 
 ### null
diff --git a/binding-mri/filesystem-binding.cpp b/binding-mri/filesystem-binding.cpp
index 17cf50d..4bc5e03 100644
--- a/binding-mri/filesystem-binding.cpp
+++ b/binding-mri/filesystem-binding.cpp
@@ -46,7 +46,7 @@ fileIntForPath(const char *path, bool rubyExc)
 
 	try
 	{
-		shState->fileSystem().openRead(*ops, path);
+		shState->fileSystem().openReadRaw(*ops, path);
 	}
 	catch (const Exception &e)
 	{
diff --git a/binding-mri/font-binding.cpp b/binding-mri/font-binding.cpp
index 2a8856a..9a6e264 100644
--- a/binding-mri/font-binding.cpp
+++ b/binding-mri/font-binding.cpp
@@ -27,6 +27,29 @@
 
 #include <string.h>
 
+
+static void
+collectStrings(VALUE obj, std::vector<std::string> &out)
+{
+	if (RB_TYPE_P(obj, RUBY_T_STRING))
+	{
+		out.push_back(RSTRING_PTR(obj));
+	}
+	else if (RB_TYPE_P(obj, RUBY_T_ARRAY))
+	{
+		for (long i = 0; i < RARRAY_LEN(obj); ++i)
+		{
+			VALUE str = rb_ary_entry(obj, i);
+
+			/* Non-string objects are tolerated (ignored) */
+			if (!RB_TYPE_P(str, RUBY_T_STRING))
+				continue;
+
+			out.push_back(RSTRING_PTR(str));
+		}
+	}
+}
+
 DEF_TYPE(Font);
 
 RB_METHOD(fontDoesExist)
@@ -48,12 +71,30 @@ RB_METHOD(FontSetName);
 
 RB_METHOD(fontInitialize)
 {
-	VALUE name = Qnil;
+	VALUE namesObj = Qnil;
 	int size = 0;
 
-	rb_get_args(argc, argv, "|oi", &name, &size RB_ARG_END);
+	rb_get_args(argc, argv, "|oi", &namesObj, &size RB_ARG_END);
 
-	Font *f = new Font(0, size);
+	Font *f;
+
+	if (NIL_P(namesObj))
+	{
+		namesObj = rb_iv_get(rb_obj_class(self), "default_name");
+		f = new Font(0, size);
+	}
+	else
+	{
+		std::vector<std::string> names;
+		collectStrings(namesObj, names);
+
+		f = new Font(&names, size);
+	}
+
+	/* This is semantically wrong; the new Font object should take
+	 * a dup'ed object here in case of an array. Ditto for the setters.
+	 * However the same bug/behavior exists in all RM versions. */
+	rb_iv_set(self, "name", namesObj);
 
 	setPrivateData(self, f);
 
@@ -65,13 +106,6 @@ RB_METHOD(fontInitialize)
 	if (rgssVer >= 3)
 		wrapProperty(self, &f->getOutColor(), "out_color", ColorType);
 
-	if (NIL_P(name))
-		name = rb_iv_get(rb_obj_class(self), "default_name");
-
-	/* Going over the 'name=' function automatically causes
-	 * a possbile name array to be re-verified for existing fonts */
-	FontSetName(1, &name, self);
-
 	return self;
 }
 
@@ -105,57 +139,17 @@ RB_METHOD(FontGetName)
 	return rb_iv_get(self, "name");
 }
 
-static void
-fontSetNameHelper(VALUE self, int argc, VALUE *argv,
-                  const char *nameIv, char *outBuf, size_t outLen)
-{
-	rb_check_argc(argc, 1);
-
-	VALUE arg = argv[0];
-
-	// Fixme: in RGSS3, specifying "" (and only that) as font name results in
-	// no text being drawn (everything else is substituted with Arial I think)
-	strncpy(outBuf, "", outLen);
-
-	if (RB_TYPE_P(arg, RUBY_T_STRING))
-	{
-		strncpy(outBuf, RSTRING_PTR(arg), outLen);
-	}
-	else if (RB_TYPE_P(arg, RUBY_T_ARRAY))
-	{
-		for (long i = 0; i < RARRAY_LEN(arg); ++i)
-		{
-			VALUE str = rb_ary_entry(arg, i);
-
-			/* Non-string objects are tolerated (ignored) */
-			if (!RB_TYPE_P(str, RUBY_T_STRING))
-				continue;
-
-			const char *family = RSTRING_PTR(str);
-
-			/* We only set the core Font object's name attribute
-			 * to the actually existing font name */
-			if (!shState->fontState().fontPresent(family))
-				continue;
-
-			strncpy(outBuf, family, outLen);
-		}
-	}
-
-	/* RMXP doesn't even care if the argument type is
-	 * something other than string/array. Whatever... */
-	rb_iv_set(self, nameIv, arg);
-}
-
 RB_METHOD(FontSetName)
 {
 	Font *f = getPrivateData<Font>(self);
 
-	char result[256];
-	fontSetNameHelper(self, argc, argv, "name",
-	                  result, sizeof(result));
+	rb_check_argc(argc, 1);
 
-	f->setName(result);
+	std::vector<std::string> namesObj;
+	collectStrings(argv[0], namesObj);
+
+	f->setName(namesObj);
+	rb_iv_set(self, "name", argv[0]);
 
 	return argv[0];
 }
@@ -223,11 +217,15 @@ RB_METHOD(FontGetDefaultName)
 
 RB_METHOD(FontSetDefaultName)
 {
-	char result[256];
-	fontSetNameHelper(self, argc, argv, "default_name",
-	                  result, sizeof(result));
+	RB_UNUSED_PARAM;
 
-	Font::setDefaultName(result);
+	rb_check_argc(argc, 1);
+
+	std::vector<std::string> namesObj;
+	collectStrings(argv[0], namesObj);
+
+	Font::setDefaultName(namesObj, shState->fontState());
+	rb_iv_set(self, "default_name", argv[0]);
 
 	return argv[0];
 }
@@ -267,7 +265,24 @@ fontBindingInit()
 
 	Font::initDefaultDynAttribs();
 	wrapProperty(klass, &Font::getDefaultColor(), "default_color", ColorType);
-	rb_iv_set(klass, "default_name", rb_str_new_cstr(Font::getDefaultName()));
+
+	/* Initialize default names */
+	const std::vector<std::string> &defNames = Font::getInitialDefaultNames();
+	VALUE defNamesObj;
+
+	if (defNames.size() == 1)
+	{
+		defNamesObj = rb_str_new_cstr(defNames[0].c_str());
+	}
+	else
+	{
+		defNamesObj = rb_ary_new2(defNames.size());
+
+		for (size_t i = 0; i < defNames.size(); ++i)
+			rb_ary_push(defNamesObj, rb_str_new_cstr(defNames[i].c_str()));
+	}
+
+	rb_iv_set(klass, "default_name", defNamesObj);
 
 	if (rgssVer >= 3)
 		wrapProperty(klass, &Font::getDefaultOutColor(), "default_out_color", ColorType);
@@ -310,14 +325,4 @@ fontBindingInit()
 	INIT_PROP_BIND(Font, Outline, "outline");
 	INIT_PROP_BIND(Font, OutColor, "out_color");
 	}
-
-	if (rgssVer >= 2)
-	{
-		VALUE defNames = rb_ary_new2(3);
-		rb_ary_push(defNames, rb_str_new2("Verdana"));
-		rb_ary_push(defNames, rb_str_new2("Arial"));
-		rb_ary_push(defNames, rb_str_new2("Courier New"));
-
-		FontSetDefaultName(1, &defNames, klass);
-	}
 }
diff --git a/binding-mri/graphics-binding.cpp b/binding-mri/graphics-binding.cpp
index a585e17..6b39697 100644
--- a/binding-mri/graphics-binding.cpp
+++ b/binding-mri/graphics-binding.cpp
@@ -183,6 +183,18 @@ RB_METHOD(graphicsReset)
 	return Qnil;
 }
 
+RB_METHOD(graphicsPlayMovie)
+{
+	RB_UNUSED_PARAM;
+
+	const char *filename;
+	rb_get_args(argc, argv, "z", &filename RB_ARG_END);
+
+	shState->graphics().playMovie(filename);
+
+	return Qnil;
+}
+
 DEF_GRA_PROP_I(FrameRate)
 DEF_GRA_PROP_I(FrameCount)
 DEF_GRA_PROP_I(Brightness)
@@ -223,6 +235,11 @@ void graphicsBindingInit()
 	INIT_GRA_PROP_BIND( Brightness, "brightness" );
 	}
 
+	if (rgssVer >= 3)
+	{
+	_rb_define_module_function(module, "play_movie", graphicsPlayMovie);
+	}
+
 	INIT_GRA_PROP_BIND( Fullscreen, "fullscreen"  );
 	INIT_GRA_PROP_BIND( ShowCursor, "show_cursor" );
 }
diff --git a/binding-mri/sprite-binding.cpp b/binding-mri/sprite-binding.cpp
index 2404e4a..dc7f902 100644
--- a/binding-mri/sprite-binding.cpp
+++ b/binding-mri/sprite-binding.cpp
@@ -56,6 +56,7 @@ DEF_PROP_I(Sprite, Y)
 DEF_PROP_I(Sprite, OX)
 DEF_PROP_I(Sprite, OY)
 DEF_PROP_I(Sprite, BushDepth)
+DEF_PROP_I(Sprite, BushOpacity)
 DEF_PROP_I(Sprite, Opacity)
 DEF_PROP_I(Sprite, BlendType)
 DEF_PROP_I(Sprite, WaveAmp)
@@ -126,6 +127,8 @@ spriteBindingInit()
 	_rb_define_method(klass, "width", spriteWidth);
 	_rb_define_method(klass, "height", spriteHeight);
 
+	INIT_PROP_BIND( Sprite, BushOpacity, "bush_opacity" );
+
 	INIT_PROP_BIND( Sprite, WaveAmp,    "wave_amp"    );
 	INIT_PROP_BIND( Sprite, WaveLength, "wave_length" );
 	INIT_PROP_BIND( Sprite, WaveSpeed,  "wave_speed"  );
diff --git a/binding-mruby/binding-mruby.cpp b/binding-mruby/binding-mruby.cpp
index f895de3..867d2d6 100644
--- a/binding-mruby/binding-mruby.cpp
+++ b/binding-mruby/binding-mruby.cpp
@@ -266,7 +266,7 @@ runRMXPScripts(mrb_state *mrb, mrbc_context *ctx)
 	mrb_state *scriptMrb = mrb_open();
 	SDL_RWops ops;
 
-	shState->fileSystem().openRead(ops, scriptPack.c_str());
+	shState->fileSystem().openReadRaw(ops, scriptPack.c_str());
 
 	mrb_value scriptArray = mrb_nil_value();
 	std::string readError;
diff --git a/mkxp.conf.sample b/mkxp.conf.sample
index e128755..b88f216 100644
--- a/mkxp.conf.sample
+++ b/mkxp.conf.sample
@@ -57,9 +57,9 @@
 
 # Apply linear interpolation when game screen
 # is upscaled
-# (default: disabled)
+# (default: enabled)
 #
-# smoothScaling=false
+# smoothScaling=true
 
 
 # Sync screen redraws to the monitor refresh rate
diff --git a/src/al-util.h b/src/al-util.h
index 78ae615..51801e7 100644
--- a/src/al-util.h
+++ b/src/al-util.h
@@ -238,6 +238,6 @@ inline ALenum chooseALFormat(int sampleSize, int channelCount)
 
 #define AUDIO_SLEEP 10
 #define STREAM_BUF_SIZE 32768
-#define GLOBAL_VOLUME 0.8
+#define GLOBAL_VOLUME 0.8f
 
 #endif // ALUTIL_H
diff --git a/src/alstream.cpp b/src/alstream.cpp
index 18799ff..487f09f 100644
--- a/src/alstream.cpp
+++ b/src/alstream.cpp
@@ -42,12 +42,12 @@ ALStream::ALStream(LoopMode loopMode,
 	  source(0),
 	  thread(0),
 	  preemptPause(false),
-      pitch(1.0)
+      pitch(1.0f)
 {
 	alSrc = AL::Source::gen();
 
-	AL::Source::setVolume(alSrc, 1.0);
-	AL::Source::setPitch(alSrc, 1.0);
+	AL::Source::setVolume(alSrc, 1.0f);
+	AL::Source::setPitch(alSrc, 1.0f);
 	AL::Source::detachBuffer(alSrc);
 
 	for (int i = 0; i < STREAM_BUFS; ++i)
@@ -172,7 +172,7 @@ void ALStream::setPitch(float value)
 	/* If the source supports setting pitch natively,
 	 * we don't have to do it via OpenAL */
 	if (source && source->setPitch(value))
-		AL::Source::setPitch(alSrc, 1.0);
+		AL::Source::setPitch(alSrc, 1.0f);
 	else
 		AL::Source::setPitch(alSrc, value);
 }
@@ -199,43 +199,73 @@ void ALStream::closeSource()
 	delete source;
 }
 
+struct ALStreamOpenHandler : FileSystem::OpenHandler
+{
+	SDL_RWops *srcOps;
+	bool looped;
+	ALDataSource *source;
+	std::string errorMsg;
+
+	ALStreamOpenHandler(SDL_RWops &srcOps, bool looped)
+	    : srcOps(&srcOps), looped(looped), source(0)
+	{}
+
+	bool tryRead(SDL_RWops &ops, const char *ext)
+	{
+		/* Copy this because we need to keep it around,
+		 * as we will continue reading data from it later */
+		*srcOps = ops;
+
+		/* Try to read ogg file signature */
+		char sig[5] = { 0 };
+		SDL_RWread(srcOps, sig, 1, 4);
+		SDL_RWseek(srcOps, 0, RW_SEEK_SET);
+
+		try
+		{
+			if (!strcmp(sig, "OggS"))
+			{
+				source = createVorbisSource(*srcOps, looped);
+				return true;
+			}
+
+			if (!strcmp(sig, "MThd"))
+			{
+				shState->midiState().initIfNeeded(shState->config());
+
+				if (HAVE_FLUID)
+				{
+					source = createMidiSource(*srcOps, looped);
+					return true;
+				}
+			}
+
+			source = createSDLSource(*srcOps, ext, STREAM_BUF_SIZE, looped);
+		}
+		catch (const Exception &e)
+		{
+			/* All source constructors will close the passed ops
+			 * before throwing errors */
+			errorMsg = e.msg;
+			return false;
+		}
+
+		return true;
+	}
+};
+
 void ALStream::openSource(const std::string &filename)
 {
-	char ext[8];
-	shState->fileSystem().openRead(srcOps, filename.c_str(), false, ext, sizeof(ext));
+	ALStreamOpenHandler handler(srcOps, looped);
+	shState->fileSystem().openRead(handler, filename.c_str());
+	source = handler.source;
 	needsRewind.clear();
 
-	/* Try to read ogg file signature */
-	char sig[5] = { 0 };
-	SDL_RWread(&srcOps, sig, 1, 4);
-	SDL_RWseek(&srcOps, 0, RW_SEEK_SET);
-
-	try
-	{
-		if (!strcmp(sig, "OggS"))
-		{
-			source = createVorbisSource(srcOps, looped);
-			return;
-		}
-
-		if (!strcmp(sig, "MThd"))
-		{
-			shState->midiState().initIfNeeded(shState->config());
-
-			if (HAVE_FLUID)
-			{
-				source = createMidiSource(srcOps, looped);
-				return;
-			}
-		}
-
-		source = createSDLSource(srcOps, ext, STREAM_BUF_SIZE, looped);
-	}
-	catch (const Exception &e)
+	if (!source)
 	{
 		char buf[512];
-		snprintf(buf, sizeof(buf), "Unable to decode audio stream: %s.%s: %s",
-		         filename.c_str(), ext, e.msg.c_str());
+		snprintf(buf, sizeof(buf), "Unable to decode audio stream: %s: %s",
+		         filename.c_str(), handler.errorMsg.c_str());
 
 		Debug() << buf;
 	}
diff --git a/src/audio.cpp b/src/audio.cpp
index 945aed0..d536c70 100644
--- a/src/audio.cpp
+++ b/src/audio.cpp
@@ -170,7 +170,7 @@ struct AudioPrivate
 					else
 					{
 						/* BGM is stopped. -> MeNotPlaying */
-						bgm.setVolume(AudioStream::External, 1.0);
+						bgm.setVolume(AudioStream::External, 1.0f);
 
 						if (!bgm.noResumeStop)
 							bgm.stream.play();
@@ -193,7 +193,7 @@ struct AudioPrivate
 				if (bgm.stream.queryState() == ALStream::Stopped)
 				{
 					/* BGM stopped midway fade in. -> MeNotPlaying */
-					bgm.setVolume(AudioStream::External, 1.0);
+					bgm.setVolume(AudioStream::External, 1.0f);
 					meWatch.state = MeNotPlaying;
 					bgm.unlockStream();
 
@@ -219,7 +219,7 @@ struct AudioPrivate
 				if (vol >= 1)
 				{
 					/* BGM fully faded in. -> MeNotPlaying */
-					vol = 1.0;
+					vol = 1.0f;
 					meWatch.state = MeNotPlaying;
 				}
 
diff --git a/src/audiostream.cpp b/src/audiostream.cpp
index ae572ef..1d96553 100644
--- a/src/audiostream.cpp
+++ b/src/audiostream.cpp
@@ -34,11 +34,11 @@ AudioStream::AudioStream(ALStream::LoopMode loopMode,
 	  noResumeStop(false),
 	  stream(loopMode, threadId)
 {
-	current.volume = 1.0;
-	current.pitch = 1.0;
+	current.volume = 1.0f;
+	current.pitch = 1.0f;
 
 	for (size_t i = 0; i < VolumeTypeCount; ++i)
-		volumes[i] = 1.0;
+		volumes[i] = 1.0f;
 
 	fade.thread = 0;
 	fade.threadName = std::string("audio_fadeout (") + threadId + ")";
@@ -82,8 +82,8 @@ void AudioStream::play(const std::string &filename,
 
 	lockStream();
 
-	float _volume = clamp<int>(volume, 0, 100) / 100.f;
-	float _pitch  = clamp<int>(pitch, 50, 150) / 100.f;
+	float _volume = clamp<int>(volume, 0, 100) / 100.0f;
+	float _pitch  = clamp<int>(pitch, 50, 150) / 100.0f;
 
 	ALStream::State sState = stream.queryState();
 
@@ -211,7 +211,7 @@ void AudioStream::fadeOut(int duration)
 	}
 
 	fade.active.set();
-	fade.msStep = (1.0) / duration;
+	fade.msStep = 1.0f / duration;
 	fade.reqFini.clear();
 	fade.reqTerm.clear();
 	fade.startTicks = SDL_GetTicks();
@@ -302,7 +302,7 @@ void AudioStream::fadeOutThread()
 		lockStream();
 
 		uint32_t curDur = SDL_GetTicks() - fade.startTicks;
-		float resVol = 1.0 - (curDur*fade.msStep);
+		float resVol = 1.0f - (curDur*fade.msStep);
 
 		ALStream::State state = stream.queryState();
 
@@ -313,7 +313,7 @@ void AudioStream::fadeOutThread()
 			if (state != ALStream::Paused)
 				stream.stop();
 
-			setVolume(FadeOut, 1.0);
+			setVolume(FadeOut, 1.0f);
 			unlockStream();
 
 			break;
@@ -340,15 +340,15 @@ void AudioStream::fadeInThread()
 
 		/* Fade in duration is always 1 second */
 		uint32_t cur = SDL_GetTicks() - fadeIn.startTicks;
-		float prog = cur / 1000.0;
+		float prog = cur / 1000.0f;
 
 		ALStream::State state = stream.queryState();
 
 		if (state != ALStream::Playing
-		||  prog >= 1.0
+		||  prog >= 1.0f
 		||  fadeIn.rqFini)
 		{
-			setVolume(FadeIn, 1.0);
+			setVolume(FadeIn, 1.0f);
 			unlockStream();
 
 			break;
diff --git a/src/bitmap.cpp b/src/bitmap.cpp
index 48f3337..28b0d7e 100644
--- a/src/bitmap.cpp
+++ b/src/bitmap.cpp
@@ -247,13 +247,26 @@ struct BitmapPrivate
 	}
 };
 
+struct BitmapOpenHandler : FileSystem::OpenHandler
+{
+	SDL_Surface *surf;
+
+	BitmapOpenHandler()
+	    : surf(0)
+	{}
+
+	bool tryRead(SDL_RWops &ops, const char *ext)
+	{
+		surf = IMG_LoadTyped_RW(&ops, 1, ext);
+		return surf != 0;
+	}
+};
+
 Bitmap::Bitmap(const char *filename)
 {
-	SDL_RWops ops;
-	char ext[8];
-
-	shState->fileSystem().openRead(ops, filename, false, ext, sizeof(ext));
-	SDL_Surface *imgSurf = IMG_LoadTyped_RW(&ops, 1, ext);
+	BitmapOpenHandler handler;
+	shState->fileSystem().openRead(handler, filename);
+	SDL_Surface *imgSurf = handler.surf;
 
 	if (!imgSurf)
 		throw Exception(Exception::SDLError, "Error loading image '%s': %s",
@@ -992,9 +1005,9 @@ static void applyShadow(SDL_Surface *&in, const SDL_PixelFormat &fm, const SDL_C
 	SDL_Surface *out = SDL_CreateRGBSurface
 		(0, in->w+1, in->h+1, fm.BitsPerPixel, fm.Rmask, fm.Gmask, fm.Bmask, fm.Amask);
 
-	float fr = c.r / 255.0;
-	float fg = c.g / 255.0;
-	float fb = c.b / 255.0;
+	float fr = c.r / 255.0f;
+	float fg = c.g / 255.0f;
+	float fb = c.b / 255.0f;
 
 	/* We allocate an output surface one pixel wider and higher than the input,
 	 * (implicitly) blit a copy of the input with RGB values set to black into
@@ -1048,11 +1061,11 @@ static void applyShadow(SDL_Surface *&in, const SDL_PixelFormat &fm, const SDL_C
 				continue;
 			}
 
-			float fSrcA = srcA / 255.0;
-			float fShdA = shdA / 255.0;
+			float fSrcA = srcA / 255.0f;
+			float fShdA = shdA / 255.0f;
 
 			/* Because opacity == 1, co1 == fSrcA */
-			float co2 = fShdA * (1.0 - fSrcA);
+			float co2 = fShdA * (1.0f - fSrcA);
 			/* Result alpha */
 			float fa = fSrcA + co2;
 			/* Temp value to simplify arithmetic below */
@@ -1061,10 +1074,10 @@ static void applyShadow(SDL_Surface *&in, const SDL_PixelFormat &fm, const SDL_C
 			/* Result colors */
 			uint8_t r, g, b, a;
 
-			r = clamp<float>(fr * co3, 0, 1) * 255;
-			g = clamp<float>(fg * co3, 0, 1) * 255;
-			b = clamp<float>(fb * co3, 0, 1) * 255;
-			a = clamp<float>(fa, 0, 1) * 255;
+			r = clamp<float>(fr * co3, 0, 1) * 255.0f;
+			g = clamp<float>(fg * co3, 0, 1) * 255.0f;
+			b = clamp<float>(fb * co3, 0, 1) * 255.0f;
+			a = clamp<float>(fa, 0, 1) * 255.0f;
 
 			*outP = SDL_MapRGBA(&fm, r, g, b, a);
 		}
@@ -1169,11 +1182,11 @@ void Bitmap::drawText(const IntRect &rect, const char *str, int align)
 	Vec2i gpTexSize;
 	shState->ensureTexSize(txtSurf->w, txtSurf->h, gpTexSize);
 
-	bool fastBlit = !p->touchesTaintedArea(posRect) && txtAlpha == 1.0;
+	bool fastBlit = !p->touchesTaintedArea(posRect) && txtAlpha == 1.0f;
 
 	if (fastBlit)
 	{
-		if (squeeze == 1.0 && !shState->config().subImageFix)
+		if (squeeze == 1.0f && !shState->config().subImageFix)
 		{
 			/* Even faster: upload directly to bitmap texture.
 			 * We have to make sure the posRect lies within the texture
diff --git a/src/config.cpp b/src/config.cpp
index 6166da3..248c2c2 100644
--- a/src/config.cpp
+++ b/src/config.cpp
@@ -151,7 +151,7 @@ void Config::read(int argc, char *argv[])
 	PO_DESC(winResizable, bool, false) \
 	PO_DESC(fullscreen, bool, false) \
 	PO_DESC(fixedAspectRatio, bool, true) \
-	PO_DESC(smoothScaling, bool, false) \
+	PO_DESC(smoothScaling, bool, true) \
 	PO_DESC(vsync, bool, false) \
 	PO_DESC(defScreenW, int, 0) \
 	PO_DESC(defScreenH, int, 0) \
diff --git a/src/config.h b/src/config.h
index e3c7573..9e43faf 100644
--- a/src/config.h
+++ b/src/config.h
@@ -31,49 +31,6 @@ struct CropTexture
 	int w, h;
 };
 
-struct TouchOverlay
-{
-	std::string image;
-
-	struct Button
-	{
-		enum Shape
-		{
-			Rectangle,
-			Circle,
-			Triangle
-		};
-
-		std::string id;
-		std::string target;
-		Shape shape;
-
-		int x;
-		int y;
-
-		union
-		{
-			struct
-			{
-				int width;
-				int height;
-			} r;
-
-			struct
-			{
-				int radius;
-			} c;
-			struct
-			{
-				int x1, y1;
-				int x2, y2;
-			} t;
-		} u;
-	};
-
-	std::vector<Button> buttons;
-};
-
 struct Config
 {
 	int rgssVersion;
diff --git a/src/etc-internal.h b/src/etc-internal.h
index c9bd520..4cbc704 100644
--- a/src/etc-internal.h
+++ b/src/etc-internal.h
@@ -61,9 +61,9 @@ struct Vec4
 		return (x == other.x && y == other.y && z == other.z && w == other.w);
 	}
 
-	bool xyzHasEffect() const
+	bool xyzNotNull() const
 	{
-		return (x != 0.0 || y != 0.0 || z != 0.0);
+		return (x != 0.0f || y != 0.0f || z != 0.0f);
 	}
 };
 
@@ -88,6 +88,11 @@ struct Vec2i
 		return x == other.x && y == other.y;
 	}
 
+	bool operator!=(const Vec2i &other) const
+	{
+		return !(*this == other);
+	}
+
 	Vec2i &operator+=(const Vec2i &value)
 	{
 		x += value.x;
@@ -131,6 +136,11 @@ struct Vec2i
 		return Vec2i(x % value, y % value);
 	}
 
+	Vec2i operator&(unsigned value) const
+	{
+		return Vec2i(x & value, y & value);
+	}
+
 	Vec2i operator-() const
 	{
 		return Vec2i(-x, -y);
@@ -176,6 +186,11 @@ struct IntRect : SDL_Rect
 		        w == other.w && h == other.h);
 	}
 
+	bool operator!=(const IntRect &other) const
+	{
+		return !(*this == other);
+	}
+
 	Vec2i pos() const
 	{
 		return Vec2i(x, y);
@@ -259,13 +274,13 @@ struct NormValue
 
 	NormValue(int unNorm)
 	    : unNorm(unNorm),
-	      norm(unNorm / 255.0)
+	      norm(unNorm / 255.0f)
 	{}
 
 	void operator =(int value)
 	{
 		unNorm = clamp(value, 0, 255);
-		norm = unNorm / 255.0;
+		norm = unNorm / 255.0f;
 	}
 
 	bool operator ==(int value) const
diff --git a/src/eventthread.cpp b/src/eventthread.cpp
index a3de5cf..00c1b3e 100644
--- a/src/eventthread.cpp
+++ b/src/eventthread.cpp
@@ -111,7 +111,11 @@ void EventThread::process(RGSSThreadData &rtData)
 	UnidirMessage<Vec2i> &windowSizeMsg = rtData.windowSizeMsg;
 
 	initALCFunctions(rtData.alcDev);
+
+	// XXX this function breaks input focus on OSX
+#ifndef __MACOSX__
 	SDL_SetEventFilter(eventFilter, &rtData);
+#endif
 
 	fullscreen = rtData.config.fullscreen;
 	int toggleFSMod = rtData.config.anyAltToggleFS ? KMOD_ALT : KMOD_LALT;
@@ -127,7 +131,9 @@ void EventThread::process(RGSSThreadData &rtData)
 	bool displayingFPS = false;
 
 	bool cursorInWindow = false;
-	bool windowFocused = false;
+
+	/* SDL doesn't send an initial FOCUS_GAINED event */
+	bool windowFocused = true;
 
 	bool terminate = false;
 
@@ -490,9 +496,9 @@ int EventThread::eventFilter(void *data, SDL_Event *event)
 		Debug() << "SDL_APP_LOWMEMORY";
 		return 0;
 
-	case SDL_RENDER_TARGETS_RESET :
-		Debug() << "****** SDL_RENDER_TARGETS_RESET";
-		return 0;
+//	case SDL_RENDER_TARGETS_RESET :
+//		Debug() << "****** SDL_RENDER_TARGETS_RESET";
+//		return 0;
 
 //	case SDL_RENDER_DEVICE_RESET :
 //		Debug() << "****** SDL_RENDER_DEVICE_RESET";
diff --git a/src/filesystem.cpp b/src/filesystem.cpp
index 6642d3b..fe5d403 100644
--- a/src/filesystem.cpp
+++ b/src/filesystem.cpp
@@ -37,6 +37,7 @@
 #include <string.h>
 #include <algorithm>
 #include <vector>
+#include <stack>
 
 #ifdef __APPLE__
 #include <iconv.h>
@@ -249,201 +250,65 @@ strcpySafe(char *dst, const char *src,
 	return cpyMax;
 }
 
+/* Attempt to locate an extension string in a filename.
+ * Either a pointer into the input string pointing at the
+ * extension, or null is returned */
+static const char *
+findExt(const char *filename)
+{
+	size_t len;
+
+	for (len = strlen(filename); len > 0; --len)
+	{
+		if (filename[len] == '/')
+			return 0;
+
+		if (filename[len] == '.')
+			return &filename[len+1];
+	}
+
+	return 0;
+}
+
+static void
+initReadOps(PHYSFS_File *handle,
+            SDL_RWops &ops,
+            bool freeOnClose)
+{
+	ops.size  = SDL_RWopsSize;
+	ops.seek  = SDL_RWopsSeek;
+	ops.read  = SDL_RWopsRead;
+	ops.write = SDL_RWopsWrite;
+
+	if (freeOnClose)
+		ops.close = SDL_RWopsCloseFree;
+	else
+		ops.close = SDL_RWopsClose;
+
+	ops.type = SDL_RWOPS_PHYSFS;
+	ops.hidden.unknown.data1 = handle;
+}
+
+static void strTolower(std::string &str)
+{
+	for (size_t i = 0; i < str.size(); ++i)
+		str[i] = tolower(str[i]);
+}
+
 const Uint32 SDL_RWOPS_PHYSFS = SDL_RWOPS_UNKNOWN+10;
 
 struct FileSystemPrivate
 {
-	/* Maps: lower case filepath without extension,
-	 * To:   mixed case full filepath
-	 * This is for compatibility with games that take Windows'
-	 * case insensitivity for granted */
+	/* Maps: lower case full filepath,
+	 * To:   mixed case full filepath */
 	BoostHash<std::string, std::string> pathCache;
+	/* Maps: lower case directory path,
+	 * To:   list of lower case filenames */
+	BoostHash<std::string, std::vector<std::string> > fileLists;
+
+	/* This is for compatibility with games that take Windows'
+	 * case insensitivity for granted */
 	bool havePathCache;
-
-	/* Attempt to locate an extension string in a filename.
-	 * Either a pointer into the input string pointing at the
-	 * extension, or null is returned */
-	const char *findExt(const char *filename)
-	{
-		size_t len;
-
-		for (len = strlen(filename); len > 0; --len)
-		{
-			if (filename[len] == '/')
-				return 0;
-
-			if (filename[len] == '.')
-				return &filename[len+1];
-		}
-
-		return 0;
-	}
-
-	struct CompleteFilenameData
-	{
-		bool found;
-		/* Contains the incomplete filename we're looking for;
-		 * when found, we write the complete filename into this
-		 * same buffer */
-		char *outBuf;
-		/* Length of incomplete file name */
-		size_t filenameLen;
-		/* Maximum we can write into outBuf */
-		size_t outBufN;
-	};
-
-	static void completeFilenameRegCB(void *data, const char *,
-	                                  const char *fname)
-	{
-		CompleteFilenameData &d = *static_cast<CompleteFilenameData*>(data);
-
-		if (d.found)
-			return;
-
-		if (strncmp(d.outBuf, fname, d.filenameLen) != 0)
-			return;
-
-		/* If fname matches up to a following '.' (meaning the rest is part
-		 * of the extension), or up to a following '\0' (full match), we've
-		 * found our file */
-		switch (fname[d.filenameLen])
-		{
-		case '.' :
-			/* Overwrite the incomplete file name we looked for with
-			 * the full version containing any extensions */
-			strcpySafe(d.outBuf, fname, d.outBufN, -1);
-		case '\0' :
-			d.found = true;
-		}
-	}
-
-	bool completeFilenameReg(const char *filepath,
-	                         char *outBuffer,
-	                         size_t outN)
-	{
-		strcpySafe(outBuffer, filepath, outN, -1);
-
-		size_t len = strlen(outBuffer);
-		char *delim;
-
-		/* Find the deliminator separating directory and file name */
-		for (delim = outBuffer + len; delim > outBuffer; --delim)
-			if (*delim == '/')
-				break;
-
-		bool root = (delim == outBuffer);
-		CompleteFilenameData d;
-
-		if (!root)
-		{
-			/* If we have such a deliminator, we set it to '\0' so we
-			 * can pass the first half to PhysFS as the directory name,
-			 * and compare all filenames against the second half */
-			d.outBuf = delim+1;
-			d.filenameLen = len - (delim - outBuffer + 1);
-
-			*delim = '\0';
-		}
-		else
-		{
-			/* Otherwise the file is in the root directory */
-			d.outBuf = outBuffer;
-			d.filenameLen = len - (delim - outBuffer);
-		}
-
-		d.found = false;
-		d.outBufN = outN - (d.outBuf - outBuffer);
-
-		PHYSFS_enumerateFilesCallback(root ? "" : outBuffer, completeFilenameRegCB, &d);
-
-		if (!d.found)
-			return false;
-
-		/* Now we put the deliminator back in to form the completed
-		 * file path (if required) */
-		if (delim != outBuffer)
-			*delim = '/';
-
-		return true;
-	}
-
-	bool completeFilenamePC(const char *filepath,
-	                        char *outBuffer,
-	                        size_t outN)
-	{
-		std::string lowCase(filepath);
-
-		for (size_t i = 0; i < lowCase.size(); ++i)
-			lowCase[i] = tolower(lowCase[i]);
-
-		if (!pathCache.contains(lowCase))
-			return false;
-
-		const std::string &fullPath = pathCache[lowCase];
-		strcpySafe(outBuffer, fullPath.c_str(), outN, fullPath.size());
-
-		return true;
-	}
-
-	bool completeFilename(const char *filepath,
-	                      char *outBuffer,
-	                      size_t outN)
-	{
-		if (havePathCache)
-			return completeFilenamePC(filepath, outBuffer, outN);
-		else
-			return completeFilenameReg(filepath, outBuffer, outN);
-	}
-
-	PHYSFS_File *openReadHandle(const char *filename,
-	                            char *extBuf,
-	                            size_t extBufN)
-	{
-		char found[512];
-
-		if (!completeFilename(filename, found, sizeof(found)))
-			throw Exception(Exception::NoFileError, "%s", filename);
-
-		PHYSFS_File *handle = PHYSFS_openRead(found);
-
-		if (!handle)
-			throw Exception(Exception::PHYSFSError, "PhysFS: %s", PHYSFS_getLastError());
-
-		if (!extBuf)
-			return handle;
-
-		for (char *q = found+strlen(found); q > found; --q)
-		{
-			if (*q == '/')
-				break;
-
-			if (*q != '.')
-				continue;
-
-			strcpySafe(extBuf, q+1, extBufN, -1);
-			break;
-		}
-
-		return handle;
-	}
-
-	void initReadOps(PHYSFS_File *handle,
-	                 SDL_RWops &ops,
-	                 bool freeOnClose)
-	{
-		ops.size  = SDL_RWopsSize;
-		ops.seek  = SDL_RWopsSeek;
-		ops.read  = SDL_RWopsRead;
-		ops.write = SDL_RWopsWrite;
-
-		if (freeOnClose)
-			ops.close = SDL_RWopsCloseFree;
-		else
-			ops.close = SDL_RWopsClose;
-
-		ops.type = SDL_RWOPS_PHYSFS;
-		ops.hidden.unknown.data1 = handle;
-	}
 };
 
 FileSystem::FileSystem(const char *argv0,
@@ -484,99 +349,105 @@ void FileSystem::addPath(const char *path)
 	}
 }
 
-#ifdef __APPLE__
-struct CacheEnumCBData
+struct CacheEnumData
 {
 	FileSystemPrivate *p;
+	std::stack<std::vector<std::string>*> fileLists;
+
+#ifdef __APPLE__
 	iconv_t nfd2nfc;
+	char buf[512];
+#endif
 
-	CacheEnumCBData(FileSystemPrivate *fsp)
+	CacheEnumData(FileSystemPrivate *p)
+	    : p(p)
 	{
-		p = fsp;
+#ifdef __APPLE__
 		nfd2nfc = iconv_open("utf-8", "utf-8-mac");
+#endif
 	}
 
-	~CacheEnumCBData()
+	~CacheEnumData()
 	{
+#ifdef __APPLE__
 		iconv_close(nfd2nfc);
+#endif
 	}
 
-	void nfcFromNfd(char *dst, const char *src, size_t dstSize)
+	/* Converts in-place */
+	void toNFC(char *inout)
 	{
-		size_t srcSize = strlen(src);
+#ifdef __APPLE__
+		size_t srcSize = strlen(inout);
+		size_t bufSize = sizeof(buf);
+		char *bufPtr = buf;
+		char *inoutPtr = inout;
+
 		/* Reserve room for null terminator */
-		--dstSize;
-		/* iconv takes a char** instead of a const char**, even though
-		 * the string data isn't written to. */
+		--bufSize;
+
 		iconv(nfd2nfc,
-			  const_cast<char**>(&src), &srcSize,
-			  &dst, &dstSize);
+			  &inoutPtr, &srcSize,
+			  &bufPtr, &bufSize);
 		/* Null-terminate */
-		*dst = 0;
+		*bufPtr = 0;
+		strcpy(inout, buf);
+#else
+		(void) inout;
+#endif
 	}
 };
-#endif
 
 static void cacheEnumCB(void *d, const char *origdir,
                         const char *fname)
 {
-#ifdef __APPLE__
-	CacheEnumCBData *data = static_cast<CacheEnumCBData*>(d);
-	FileSystemPrivate *p = data->p;
-#else
-	FileSystemPrivate *p = static_cast<FileSystemPrivate*>(d);
-#endif
+	CacheEnumData &data = *static_cast<CacheEnumData*>(d);
+	char fullPath[512];
 
-	char buf[512];
-
-	if (*origdir == '\0')
-		strncpy(buf, fname, sizeof(buf));
+	if (!*origdir)
+		snprintf(fullPath, sizeof(fullPath), "%s", fname);
 	else
-		snprintf(buf, sizeof(buf), "%s/%s", origdir, fname);
+		snprintf(fullPath, sizeof(fullPath), "%s/%s", origdir, fname);
 
-#ifdef __APPLE__
-	char bufNfc[sizeof(buf)];
-	data->nfcFromNfd(bufNfc, buf, sizeof(bufNfc));
-#else
-	char *const bufNfc = buf;
-#endif
+	/* Deal with OSX' weird UTF-8 standards */
+	data.toNFC(fullPath);
 
-	char *ptr = bufNfc;
+	std::string mixedCase(fullPath);
+	std::string lowerCase = mixedCase;
+	strTolower(lowerCase);
 
-	/* Trim leading slash */
-	if (*ptr == '/')
-		++ptr;
+	PHYSFS_Stat stat;
+	PHYSFS_stat(fullPath, &stat);
 
-	std::string mixedCase(ptr);
-
-	for (char *q = bufNfc; *q; ++q)
-		*q = tolower(*q);
-
-	p->pathCache.insert(std::string(ptr), mixedCase);
-
-	for (char *q = ptr+strlen(ptr); q > ptr; --q)
+	if (stat.filetype == PHYSFS_FILETYPE_DIRECTORY)
 	{
-		if (*q == '/')
-			break;
+		/* Create a new list for this directory */
+		std::vector<std::string> &list = data.p->fileLists[lowerCase];
 
-		if (*q != '.')
-			continue;
-
-		*q = '\0';
-		p->pathCache.insert(std::string(ptr), mixedCase);
+		/* Iterate over its contents */
+		data.fileLists.push(&list);
+		PHYSFS_enumerateFilesCallback(fullPath, cacheEnumCB, d);
+		data.fileLists.pop();
 	}
+	else
+	{
+		/* Get the file list for the directory we're currently
+		 * traversing and append this filename to it */
+		std::vector<std::string> &list = *data.fileLists.top();
+		std::string lowerFilename(fname);
+		strTolower(lowerFilename);
+		list.push_back(lowerFilename);
 
-	PHYSFS_enumerateFilesCallback(mixedCase.c_str(), cacheEnumCB, d);
+		/* Add the lower -> mixed mapping of the file's full path */
+		data.p->pathCache.insert(lowerCase, mixedCase);
+	}
 }
 
 void FileSystem::createPathCache()
 {
-#ifdef __APPLE__
-	CacheEnumCBData data(p);
+	CacheEnumData data(p);
+	data.fileLists.push(&p->fileLists[""]);
 	PHYSFS_enumerateFilesCallback("", cacheEnumCB, &data);
-#else
-	PHYSFS_enumerateFilesCallback("", cacheEnumCB, p);
-#endif
 
 	p->havePathCache = true;
 }
@@ -591,10 +462,9 @@ static void fontSetEnumCB(void *data, const char *,
                           const char *fname)
 {
 	FontSetsCBData *d = static_cast<FontSetsCBData*>(data);
-	FileSystemPrivate *p = d->p;
 
 	/* Only consider filenames with font extensions */
-	const char *ext = p->findExt(fname);
+	const char *ext = findExt(fname);
 
 	if (!ext)
 		return;
@@ -618,7 +488,7 @@ static void fontSetEnumCB(void *data, const char *,
 		return;
 
 	SDL_RWops ops;
-	p->initReadOps(handle, ops, false);
+	initReadOps(handle, ops, false);
 
 	d->sfs->initFontSetCB(ops, filename);
 
@@ -632,15 +502,147 @@ void FileSystem::initFontSets(SharedFontState &sfs)
 	PHYSFS_enumerateFilesCallback("Fonts", fontSetEnumCB, &d);
 }
 
-void FileSystem::openRead(SDL_RWops &ops,
-                          const char *filename,
-                          bool freeOnClose,
-                          char *extBuf,
-                          size_t extBufN)
+struct OpenReadEnumData
 {
- 	PHYSFS_File *handle = p->openReadHandle(filename, extBuf, extBufN);
+	FileSystem::OpenHandler &handler;
+	SDL_RWops ops;
 
-	p->initReadOps(handle, ops, freeOnClose);
+	/* The filename (without directory) we're looking for */
+	const char *filename;
+	size_t filenameN;
+
+	/* Optional hash to translate full filepaths
+	 * (used with path cache) */
+	BoostHash<std::string, std::string> *pathTrans;
+
+	/* Number of files we've attempted to read and parse */
+	size_t matchCount;
+	bool stopSearching;
+
+	/* In case of a PhysFS error, save it here so it
+	 * doesn't get changed before we get back into our code */
+	const char *physfsError;
+
+	OpenReadEnumData(FileSystem::OpenHandler &handler,
+	                 const char *filename, size_t filenameN,
+	                 BoostHash<std::string, std::string> *pathTrans)
+	    : handler(handler), filename(filename), filenameN(filenameN),
+	      pathTrans(pathTrans), matchCount(0), stopSearching(false),
+	      physfsError(0)
+	{}
+};
+
+static void 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;
+
+	/* If there's not even a partial match, continue searching */
+	if (strncmp(filename, data.filename, data.filenameN) != 0)
+		return;
+
+	if (!*dirpath)
+	{
+		fullPath = filename;
+	}
+	else
+	{
+		snprintf(buffer, sizeof(buffer), "%s/%s", dirpath, filename);
+		fullPath = buffer;
+	}
+
+	char last = filename[data.filenameN];
+
+	/* If fname matches up to a following '.' (meaning the rest is part
+	 * of the extension), or up to a following '\0' (full match), we've
+	 * found our file */
+	if (last != '.' && last != '\0')
+		return;
+
+	/* If the path cache is active, translate from lower case
+	 * to mixed case path */
+	if (data.pathTrans)
+		fullPath = (*data.pathTrans)[fullPath].c_str();
+
+	PHYSFS_File *phys = PHYSFS_openRead(fullPath);
+
+	if (!phys)
+	{
+		/* Failing to open this file here means there must
+		 * be a deeper rooted problem somewhere within PhysFS.
+		 * Just abort alltogether. */
+		data.stopSearching = true;
+		data.physfsError = PHYSFS_getLastError();
+
+		return;
+	}
+
+	initReadOps(phys, data.ops, false);
+
+	const char *ext = findExt(filename);
+
+	if (data.handler.tryRead(data.ops, ext))
+		data.stopSearching = true;
+
+	++data.matchCount;
+}
+
+void FileSystem::openRead(OpenHandler &handler, const char *filename)
+{
+	char buffer[512];
+	size_t len = strcpySafe(buffer, filename, sizeof(buffer), -1);
+	char *delim;
+
+	if (p->havePathCache)
+		for (size_t i = 0; i < len; ++i)
+			buffer[i] = tolower(buffer[i]);
+
+	/* Find the deliminator separating directory and file name */
+	for (delim = buffer + len; delim > buffer; --delim)
+		if (*delim == '/')
+			break;
+
+	const bool root = (delim == buffer);
+
+	const char *file = buffer;
+	const char *dir = "";
+
+	if (!root)
+	{
+		/* Cut the buffer in half so we can use it
+		 * for both filename and directory path */
+		*delim = '\0';
+		file = delim+1;
+		dir = buffer;
+	}
+
+	OpenReadEnumData data(handler, file, len + buffer - delim - !root,
+	                      p->havePathCache ? &p->pathCache : 0);
+
+	if (p->havePathCache)
+	{
+		/* Get the list of files contained in this directory
+		 * and manually iterate over them */
+		const std::vector<std::string> &fileList = p->fileLists[dir];
+
+		for (size_t i = 0; i < fileList.size(); ++i)
+			openReadEnumCB(&data, dir, fileList[i].c_str());
+	}
+	else
+	{
+		PHYSFS_enumerateFilesCallback(dir, openReadEnumCB, &data);
+	}
+
+	if (data.physfsError)
+		throw Exception(Exception::PHYSFSError, "PhysFS: %s", data.physfsError);
+
+	if (data.matchCount == 0)
+		throw Exception(Exception::NoFileError, "%s", filename);
 }
 
 void FileSystem::openReadRaw(SDL_RWops &ops,
@@ -650,12 +652,10 @@ void FileSystem::openReadRaw(SDL_RWops &ops,
 	PHYSFS_File *handle = PHYSFS_openRead(filename);
 	assert(handle);
 
-	p->initReadOps(handle, ops, freeOnClose);
+	initReadOps(handle, ops, freeOnClose);
 }
 
 bool FileSystem::exists(const char *filename)
 {
-	char found[512];
-
-	return p->completeFilename(filename, found, sizeof(found));
+	return PHYSFS_exists(filename);
 }
diff --git a/src/filesystem.h b/src/filesystem.h
index a98d96d..1f9f79f 100644
--- a/src/filesystem.h
+++ b/src/filesystem.h
@@ -43,17 +43,28 @@ public:
 	 * available font assets */
 	void initFontSets(SharedFontState &sfs);
 
-	void openRead(SDL_RWops &ops,
-	              const char *filename,
-	              bool freeOnClose = false,
-	              char *extBuf = 0,
-	              size_t extBufN = 0);
+	struct OpenHandler
+	{
+		/* Try to read and interpret data provided from ops.
+		 * If data cannot be parsed, return false, otherwise true.
+		 * Can be called multiple times until a parseable file is found.
+		 * It's the handler's responsibility to close every passed
+		 * ops structure, even when data could not be parsed.
+		 * After this function returns, ops becomes invalid, so don't take
+		 * references to it. Instead, copy the structure without closing
+		 * if you need to further read from it later. */
+		virtual bool tryRead(SDL_RWops &ops, const char *ext) = 0;
+	};
+
+	void openRead(OpenHandler &handler,
+	              const char *filename);
 
 	/* Circumvents extension supplementing */
 	void openReadRaw(SDL_RWops &ops,
 	                 const char *filename,
 	                 bool freeOnClose = false);
 
+	/* Does not perform extension supplementing */
 	bool exists(const char *filename);
 
 private:
diff --git a/src/font.cpp b/src/font.cpp
index 058dd21..62ab3e8 100644
--- a/src/font.cpp
+++ b/src/font.cpp
@@ -174,7 +174,7 @@ _TTF_Font *SharedFontState::getFont(std::string family,
 	// FIXME 0.9 is guesswork at this point
 //	float gamma = (96.0/45.0)*(5.0/14.0)*(size-5);
 //	font = TTF_OpenFontRW(ops, 1, gamma /** .90*/);
-	font = TTF_OpenFontRW(ops, 1, size* .90);
+	font = TTF_OpenFontRW(ops, 1, size* 0.90f);
 
 	if (!font)
 		throw Exception(Exception::SDLError, "%s", SDL_GetError());
@@ -184,7 +184,7 @@ _TTF_Font *SharedFontState::getFont(std::string family,
 	return font;
 }
 
-bool SharedFontState::fontPresent(std::string family)
+bool SharedFontState::fontPresent(std::string family) const
 {
 	/* Check for substitutions */
 	if (p->subs.contains(family))
@@ -202,6 +202,26 @@ _TTF_Font *SharedFontState::openBundled(int size)
 	return TTF_OpenFontRW(ops, 1, size);
 }
 
+void pickExistingFontName(const std::vector<std::string> &names,
+                          std::string &out,
+                          const SharedFontState &sfs)
+{
+	/* Note: In RMXP, a names array with no existing entry
+	 * results in no text being drawn at all (same for "" and []);
+	 * we can't replicate this in mkxp due to the default substitute. */
+
+	for (size_t i = 0; i < names.size(); ++i)
+	{
+		if (sfs.fontPresent(names[i]))
+		{
+			out = names[i];
+			return;
+		}
+	}
+
+	out = "";
+}
+
 
 struct FontPrivate
 {
@@ -229,15 +249,15 @@ struct FontPrivate
 	static Color defaultColorTmp;
 	static Color defaultOutColorTmp;
 
+	static std::vector<std::string> initialDefaultNames;
+
 	/* The actual font is opened as late as possible
 	 * (when it is queried by a Bitmap), prior it is
 	 * set to null */
 	TTF_Font *sdlFont;
 
-	FontPrivate(const char *name = 0,
-	            int size = 0)
-	    : name(name ? std::string(name) : defaultName),
-	      size(size ? size : defaultSize),
+	FontPrivate(int size)
+	    : size(size),
 	      bold(defaultBold),
 	      italic(defaultItalic),
 	      outline(defaultOutline),
@@ -290,6 +310,8 @@ Color      *FontPrivate::defaultOutColor = &FontPrivate::defaultOutColorTmp;
 Color FontPrivate::defaultColorTmp(255, 255, 255, 255);
 Color FontPrivate::defaultOutColorTmp(0, 0, 0, 128);
 
+std::vector<std::string> FontPrivate::initialDefaultNames;
+
 bool Font::doesExist(const char *name)
 {
 	if (!name)
@@ -298,10 +320,15 @@ bool Font::doesExist(const char *name)
 	return shState->fontState().fontPresent(name);
 }
 
-Font::Font(const char *name,
+Font::Font(const std::vector<std::string> *names,
            int size)
 {
-	p = new FontPrivate(name, size);
+	p = new FontPrivate(size ? size : FontPrivate::defaultSize);
+
+	if (names)
+		setName(*names);
+	else
+		p->name = FontPrivate::defaultName;
 }
 
 Font::Font(const Font &other)
@@ -321,17 +348,9 @@ const Font &Font::operator=(const Font &o)
 	return o;
 }
 
-const char *Font::getName() const
+void Font::setName(const std::vector<std::string> &names)
 {
-	return p->name.c_str();
-}
-
-void Font::setName(const char *value)
-{
-	if (p->name == value)
-		return;
-
-	p->name = value;
+	pickExistingFontName(names, p->name, shState->fontState());
 	p->sdlFont = 0;
 }
 
@@ -367,14 +386,15 @@ DEF_ATTR_SIMPLE_STATIC(Font, DefaultOutline,  bool,    FontPrivate::defaultOutli
 DEF_ATTR_SIMPLE_STATIC(Font, DefaultColor,    Color&, *FontPrivate::defaultColor)
 DEF_ATTR_SIMPLE_STATIC(Font, DefaultOutColor, Color&, *FontPrivate::defaultOutColor)
 
-const char *Font::getDefaultName()
+void Font::setDefaultName(const std::vector<std::string> &names,
+                          const SharedFontState &sfs)
 {
-	return FontPrivate::defaultName.c_str();
+	pickExistingFontName(names, FontPrivate::defaultName, sfs);
 }
 
-void Font::setDefaultName(const char *value)
+const std::vector<std::string> &Font::getInitialDefaultNames()
 {
-	FontPrivate::defaultName = value;
+	return FontPrivate::initialDefaultNames;
 }
 
 void Font::initDynAttribs()
@@ -393,8 +413,30 @@ void Font::initDefaultDynAttribs()
 		FontPrivate::defaultOutColor = new Color(FontPrivate::defaultOutColorTmp);
 }
 
-void Font::initDefaults()
+void Font::initDefaults(const SharedFontState &sfs)
 {
+	std::vector<std::string> &names = FontPrivate::initialDefaultNames;
+
+	switch (rgssVer)
+	{
+	case 1 :
+		// FIXME: Japanese version has "MS PGothic" instead
+		names.push_back("Arial");
+		break;
+
+	case 2 :
+		names.push_back("UmePlus Gothic");
+		names.push_back("MS Gothic");
+		names.push_back("Courier New");
+		break;
+
+	default:
+	case 3 :
+		names.push_back("VL Gothic");
+	}
+
+	setDefaultName(names, sfs);
+
 	FontPrivate::defaultOutline = (rgssVer >= 3 ? true : false);
 	FontPrivate::defaultShadow  = (rgssVer == 2 ? true : false);
 }
diff --git a/src/font.h b/src/font.h
index 73bb83f..6024007 100644
--- a/src/font.h
+++ b/src/font.h
@@ -25,6 +25,9 @@
 #include "etc.h"
 #include "util.h"
 
+#include <vector>
+#include <string>
+
 struct SDL_RWops;
 struct _TTF_Font;
 struct Config;
@@ -47,7 +50,7 @@ public:
 	_TTF_Font *getFont(std::string family,
 	                   int size);
 
-	bool fontPresent(std::string family);
+	bool fontPresent(std::string family) const;
 
 	static _TTF_Font *openBundled(int size);
 
@@ -55,22 +58,6 @@ private:
 	SharedFontStatePrivate *p;
 };
 
-/* Concerning Font::name/defaultName :
- * In RGSS, this is not actually a string; any type of
- * object is accepted, however anything but strings and
- * arrays is ignored (and text drawing turns blank).
- * Single strings are interpreted as font family names,
- * and directly passed to the underlying C++ object;
- * arrays however are searched for the first string
- * object corresponding to a valid font family name,
- * and rendering is done with that. In mkxp, we pass
- * this first valid font family as the 'name' attribute
- * back to the C++ object on assignment and object
- * creation (in case Font.default_name is also an array).
- * Invalid parameters (things other than strings or
- * arrays not containing any valid family name) are
- * passed back as "". */
-
 struct FontPrivate;
 
 class Font
@@ -78,31 +65,43 @@ class Font
 public:
 	static bool doesExist(const char *name);
 
-	Font(const char *name = 0,
+	Font(const std::vector<std::string> *names = 0,
 	     int size = 0);
+
 	/* Clone constructor */
 	Font(const Font &other);
+
 	~Font();
 
 	const Font &operator=(const Font &o);
 
-	DECL_ATTR( Name,     const char * )
-	DECL_ATTR( Size,     int          )
-	DECL_ATTR( Bold,     bool         )
-	DECL_ATTR( Italic,   bool         )
-	DECL_ATTR( Color,    Color&       )
-	DECL_ATTR( Shadow,   bool         )
-	DECL_ATTR( Outline,  bool         )
-	DECL_ATTR( OutColor, Color&       )
+	DECL_ATTR( Size,     int    )
+	DECL_ATTR( Bold,     bool   )
+	DECL_ATTR( Italic,   bool   )
+	DECL_ATTR( Color,    Color& )
+	DECL_ATTR( Shadow,   bool   )
+	DECL_ATTR( Outline,  bool   )
+	DECL_ATTR( OutColor, Color& )
 
-	DECL_ATTR_STATIC( DefaultName,     const char* )
-	DECL_ATTR_STATIC( DefaultSize,     int         )
-	DECL_ATTR_STATIC( DefaultBold,     bool        )
-	DECL_ATTR_STATIC( DefaultItalic,   bool        )
-	DECL_ATTR_STATIC( DefaultColor,    Color&      )
-	DECL_ATTR_STATIC( DefaultShadow,   bool        )
-	DECL_ATTR_STATIC( DefaultOutline,  bool        )
-	DECL_ATTR_STATIC( DefaultOutColor, Color&      )
+	DECL_ATTR_STATIC( DefaultSize,     int    )
+	DECL_ATTR_STATIC( DefaultBold,     bool   )
+	DECL_ATTR_STATIC( DefaultItalic,   bool   )
+	DECL_ATTR_STATIC( DefaultColor,    Color& )
+	DECL_ATTR_STATIC( DefaultShadow,   bool   )
+	DECL_ATTR_STATIC( DefaultOutline,  bool   )
+	DECL_ATTR_STATIC( DefaultOutColor, Color& )
+
+	/* There is no point in providing getters for these,
+	 * as the bindings will always return the stored native
+	 * string/array object anyway. It's impossible to mirror
+	 * in the C++ core.
+	 * The core object picks the first existing name from the
+	 * passed array and stores it internally (same for default). */
+	void setName(const std::vector<std::string> &names);
+	static void setDefaultName(const std::vector<std::string> &names,
+	                           const SharedFontState &sfs);
+
+	static const std::vector<std::string> &getInitialDefaultNames();
 
 	/* Assigns heap allocated objects to object properties;
 	 * using this in pure C++ will cause memory leaks
@@ -110,7 +109,7 @@ public:
 	void initDynAttribs();
 	static void initDefaultDynAttribs();
 
-	static void initDefaults();
+	static void initDefaults(const SharedFontState &sfs);
 
 	/* internal */
 	_TTF_Font *getSdlFont();
diff --git a/src/graphics.cpp b/src/graphics.cpp
index 79de27d..687cb3e 100644
--- a/src/graphics.cpp
+++ b/src/graphics.cpp
@@ -169,15 +169,15 @@ public:
 		}
 	}
 
-	void requestViewportRender(Vec4 &c, Vec4 &f, Vec4 &t)
+	void requestViewportRender(const Vec4 &c, const Vec4 &f, const Vec4 &t)
 	{
 		const IntRect &viewpRect = glState.scissorBox.get();
 		const IntRect &screenRect = geometry.rect;
 
-		bool toneRGBEffect  = t.xyzHasEffect();
-		bool toneGrayEffect = t.w != 0;
-		bool colorEffect    = c.w > 0;
-		bool flashEffect    = f.w > 0;
+		const bool toneRGBEffect  = t.xyzNotNull();
+		const bool toneGrayEffect = t.w != 0;
+		const bool colorEffect    = c.w > 0;
+		const bool flashEffect    = f.w > 0;
 
 		if (toneGrayEffect)
 		{
@@ -240,7 +240,7 @@ public:
 			/* Then apply them using hardware blending */
 			gl.BlendFuncSeparate(GL_ONE, GL_ONE, GL_ZERO, GL_ONE);
 
-			if (add.xyzHasEffect())
+			if (add.xyzNotNull())
 			{
 				gl.BlendEquation(GL_FUNC_ADD);
 				shader.setColor(add);
@@ -248,7 +248,7 @@ public:
 				screenQuad.draw();
 			}
 
-			if (sub.xyzHasEffect())
+			if (sub.xyzNotNull())
 			{
 				gl.BlendEquation(GL_FUNC_REVERSE_SUBTRACT);
 				shader.setColor(sub);
@@ -281,9 +281,9 @@ public:
 
 	void setBrightness(float norm)
 	{
-		brightnessQuad.setColor(Vec4(0, 0, 0, 1.0 - norm));
+		brightnessQuad.setColor(Vec4(0, 0, 0, 1.0f - norm));
 
-		brightEffect = norm < 1.0;
+		brightEffect = norm < 1.0f;
 	}
 
 	void updateReso(int width, int height)
@@ -742,7 +742,7 @@ void Graphics::transition(int duration,
 		shader.setFrozenScene(p->frozenScene.tex);
 		shader.setCurrentScene(p->currentScene.tex);
 		shader.setTransMap(transMap->getGLTypes().tex);
-		shader.setVague(vague / 256.0);
+		shader.setVague(vague / 256.0f);
 		shader.setTexSize(p->scRes);
 	}
 	else
@@ -780,7 +780,7 @@ void Graphics::transition(int duration,
 
 		p->checkSyncLock();
 
-		const float prog = i * (1.0 / duration);
+		const float prog = i * (1.0f / duration);
 
 		if (transMap)
 		{
@@ -858,7 +858,7 @@ void Graphics::fadeout(int duration)
 	FBO::unbind();
 
 	float curr = p->brightness;
-	float diff = 255.0 - curr;
+	float diff = 255.0f - curr;
 
 	for (int i = duration-1; i > -1; --i)
 	{
@@ -888,7 +888,7 @@ void Graphics::fadein(int duration)
 	FBO::unbind();
 
 	float curr = p->brightness;
-	float diff = 255.0 - curr;
+	float diff = 255.0f - curr;
 
 	for (int i = 1; i <= duration; ++i)
 	{
@@ -963,6 +963,11 @@ void Graphics::resizeScreen(int width, int height)
 	shState->eThread().requestWindowResize(width, height);
 }
 
+void Graphics::playMovie(const char *filename)
+{
+	Debug() << "Graphics.playMovie(" << filename << ") not implemented";
+}
+
 DEF_ATTR_RD_SIMPLE(Graphics, Brightness, int, p->brightness)
 
 void Graphics::setBrightness(int value)
diff --git a/src/graphics.h b/src/graphics.h
index e914178..aa30189 100644
--- a/src/graphics.h
+++ b/src/graphics.h
@@ -54,6 +54,7 @@ public:
 	int width() const;
 	int height() const;
 	void resizeScreen(int width, int height);
+	void playMovie(const char *filename);
 
 	void reset();
 
diff --git a/src/midisource.cpp b/src/midisource.cpp
index fdca2e4..3d569a7 100644
--- a/src/midisource.cpp
+++ b/src/midisource.cpp
@@ -320,7 +320,7 @@ readEvent(MidiReadHandler *handler, MemChunk &chunk,
 			              | (data[2] << 0x00);
 
 			e.type = Tempo;
-			e.e.tempo.bpm = 60000000.0 / mpqn;
+			e.e.tempo.bpm = 60000000 / mpqn;
 		}
 		else if (metaType == 0x2F)
 		{
@@ -626,9 +626,20 @@ struct MidiSource : ALDataSource, MidiReadHandler
 		std::vector<uint8_t> data(dataLen);
 
 		if (SDL_RWread(&ops, &data[0], 1, dataLen) < dataLen)
+		{
+			SDL_RWclose(&ops);
 			throw Exception(Exception::MKXPError, "Reading midi data failed");
+		}
 
-		readMidi(this, data);
+		try
+		{
+			readMidi(this, data);
+		}
+		catch (const Exception &)
+		{
+			SDL_RWclose(&ops);
+			throw;
+		}
 
 		synth = shState->midiState().allocateSynth();
 
@@ -688,7 +699,7 @@ struct MidiSource : ALDataSource, MidiReadHandler
 
 	void updatePlaybackSpeed(uint32_t bpm)
 	{
-		float deltaLength = 60.0 / (dpb * bpm);
+		float deltaLength = 60.0f / (dpb * bpm);
 		playbackSpeed = TICK_FRAMES / (deltaLength * freq);
 	}
 
@@ -899,7 +910,7 @@ struct MidiSource : ALDataSource, MidiReadHandler
 	bool setPitch(float value)
 	{
 		// not completely correct, but close
-		pitchShift = round((value > 1.0 ? 14 : 24) * (value - 1.0));
+		pitchShift = round((value > 1.0f ? 14 : 24) * (value - 1.0f));
 
 		return true;
 	}
diff --git a/src/plane.cpp b/src/plane.cpp
index f275e85..e3de3a0 100644
--- a/src/plane.cpp
+++ b/src/plane.cpp
@@ -105,8 +105,8 @@ struct PlanePrivate
 			return;
 
 		/* Scaled (zoomed) bitmap dimensions */
-		double sw = bitmap->width()  * zoomX;
-		double sh = bitmap->height() * zoomY;
+		float sw = bitmap->width()  * zoomX;
+		float sh = bitmap->height() * zoomY;
 
 		/* Plane offset wrapped by scaled bitmap dims */
 		float wox = fwrap(ox, sw);
diff --git a/src/rgssad.cpp b/src/rgssad.cpp
index e8606b9..17e8417 100644
--- a/src/rgssad.cpp
+++ b/src/rgssad.cpp
@@ -34,7 +34,7 @@ struct RGSS_entryData
 
 struct RGSS_entryHandle
 {
-	RGSS_entryData data;
+	const RGSS_entryData data;
 	uint32_t currentMagic;
 	uint64_t currentOffset;
 	PHYSFS_Io *io;
@@ -229,7 +229,7 @@ RGSS_ioSeek(PHYSFS_Io *self, PHYSFS_uint64 offset)
 static PHYSFS_sint64
 RGSS_ioTell(PHYSFS_Io *self)
 {
-	RGSS_entryHandle *entry = static_cast<RGSS_entryHandle*>(self->opaque);
+	const RGSS_entryHandle *entry = static_cast<RGSS_entryHandle*>(self->opaque);
 
 	return entry->currentOffset;
 }
@@ -237,7 +237,7 @@ RGSS_ioTell(PHYSFS_Io *self)
 static PHYSFS_sint64
 RGSS_ioLength(PHYSFS_Io *self)
 {
-	RGSS_entryHandle *entry = static_cast<RGSS_entryHandle*>(self->opaque);
+	const RGSS_entryHandle *entry = static_cast<RGSS_entryHandle*>(self->opaque);
 
 	return entry->data.size;
 }
@@ -245,7 +245,7 @@ RGSS_ioLength(PHYSFS_Io *self)
 static PHYSFS_Io*
 RGSS_ioDuplicate(PHYSFS_Io *self)
 {
-	RGSS_entryHandle *entry = static_cast<RGSS_entryHandle*>(self->opaque);
+	const RGSS_entryHandle *entry = static_cast<RGSS_entryHandle*>(self->opaque);
 	RGSS_entryHandle *entryDup = new RGSS_entryHandle(*entry);
 
 	PHYSFS_Io *dup = PHYSFS_ALLOC(PHYSFS_Io);
@@ -448,7 +448,7 @@ RGSS_stat(void *opaque, const char *filename, PHYSFS_Stat *stat)
 
 	if (hasFile)
 	{
-		RGSS_entryData &entry = data->entryHash[filename];
+		const RGSS_entryData &entry = data->entryHash[filename];
 
 		stat->filesize = entry.size;
 		stat->filetype = PHYSFS_FILETYPE_REGULAR;
diff --git a/src/scene.h b/src/scene.h
index edee331..7685407 100644
--- a/src/scene.h
+++ b/src/scene.h
@@ -55,7 +55,9 @@ public:
 	virtual ~Scene();
 
 	virtual void composite();
-	virtual void requestViewportRender(Vec4& /*color*/, Vec4& /*flash*/, Vec4& /*tone*/) {}
+	virtual void requestViewportRender(const Vec4& /* color */,
+	                                   const Vec4& /* flash */,
+	                                   const Vec4& /* tone */) {}
 
 	const Geometry &getGeometry() const { return geometry; }
 
diff --git a/src/settingsmenu.cpp b/src/settingsmenu.cpp
index a14b488..6518d7f 100644
--- a/src/settingsmenu.cpp
+++ b/src/settingsmenu.cpp
@@ -764,13 +764,13 @@ void Widget::click(int x, int y, uint8_t button)
 }
 
 /* Ratio of cell area to total widget width */
-#define BW_CELL_R 0.75
+#define BW_CELL_R 0.75f
 
 void BindingWidget::drawHandler(SDL_Surface *surf)
 {
 	const int cellW = (rect.w*BW_CELL_R) / 2;
 	const int cellH = rect.h / 2;
-	const int cellOffX = (1.0-BW_CELL_R) * rect.w;
+	const int cellOffX = (1.0f-BW_CELL_R) * rect.w;
 
 	const int cellOff[] =
 	{
@@ -860,7 +860,7 @@ int BindingWidget::cellIndex(int x, int y) const
 {
 	const int cellW = (rect.w*BW_CELL_R) / 2;
 	const int cellH = rect.h / 2;
-	const int cellOff = (1.0-BW_CELL_R) * rect.w;
+	const int cellOff = (1.0f-BW_CELL_R) * rect.w;
 
 	if (x < cellOff)
 		return -1;
diff --git a/src/sharedmidistate.h b/src/sharedmidistate.h
index 00de6dc..1692848 100644
--- a/src/sharedmidistate.h
+++ b/src/sharedmidistate.h
@@ -80,7 +80,7 @@ struct SharedMidiState
 			return;
 
 		flSettings = fluid.new_settings();
-		fluid.settings_setnum(flSettings, "synth.gain", 1.0);
+		fluid.settings_setnum(flSettings, "synth.gain", 1.0f);
 		fluid.settings_setnum(flSettings, "synth.sample-rate", SYNTH_SAMPLERATE);
 		fluid.settings_setstr(flSettings, "synth.chorus.active", conf.midi.chorus ? "yes" : "no");
 		fluid.settings_setstr(flSettings, "synth.reverb.active", conf.midi.reverb ? "yes" : "no");
diff --git a/src/sharedstate.cpp b/src/sharedstate.cpp
index 023ff6b..9872af7 100644
--- a/src/sharedstate.cpp
+++ b/src/sharedstate.cpp
@@ -172,7 +172,6 @@ void SharedState::initInstance(RGSSThreadData *threadData)
 	 * Font depends on SharedState existing */
 
 	rgssVersion = threadData->config.rgssVersion;
-	Font::initDefaults();
 
 	_globalIBO = new GlobalIBO();
 	_globalIBO->ensureSize(1);
@@ -183,6 +182,7 @@ void SharedState::initInstance(RGSSThreadData *threadData)
 	try
 	{
 		SharedState::instance = new SharedState(threadData);
+		Font::initDefaults(instance->p->fontState);
 		defaultFont = new Font();
 	}
 	catch (const Exception &exc)
diff --git a/src/soundemitter.cpp b/src/soundemitter.cpp
index 3850c0b..74eaf03 100644
--- a/src/soundemitter.cpp
+++ b/src/soundemitter.cpp
@@ -123,8 +123,8 @@ void SoundEmitter::play(const std::string &filename,
                         int volume,
                         int pitch)
 {
-	float _volume = clamp<int>(volume, 0, 100) / 100.f;
-	float _pitch  = clamp<int>(pitch, 50, 150) / 100.f;
+	float _volume = clamp<int>(volume, 0, 100) / 100.0f;
+	float _pitch  = clamp<int>(pitch, 50, 150) / 100.0f;
 
 	SoundBuffer *buffer = allocateBuffer(filename);
 
@@ -184,6 +184,44 @@ void SoundEmitter::stop()
 		AL::Source::stop(alSrcs[i]);
 }
 
+struct SoundOpenHandler : FileSystem::OpenHandler
+{
+	SoundBuffer *buffer;
+
+	SoundOpenHandler()
+	    : buffer(0)
+	{}
+
+	bool tryRead(SDL_RWops &ops, const char *ext)
+	{
+		Sound_Sample *sample = Sound_NewSample(&ops, ext, 0, STREAM_BUF_SIZE);
+
+		if (!sample)
+		{
+			SDL_RWclose(&ops);
+			return false;
+		}
+
+		/* Do all of the decoding in the handler so we don't have
+		 * to keep the source ops around */
+		uint32_t decBytes = Sound_DecodeAll(sample);
+		uint8_t sampleSize = formatSampleSize(sample->actual.format);
+		uint32_t sampleCount = decBytes / sampleSize;
+
+		buffer = new SoundBuffer;
+		buffer->bytes = sampleSize * sampleCount;
+
+		ALenum alFormat = chooseALFormat(sampleSize, sample->actual.channels);
+
+		AL::Buffer::uploadData(buffer->alBuffer, alFormat, sample->buffer,
+							   buffer->bytes, sample->actual.rate);
+
+		Sound_FreeSample(sample);
+
+		return true;
+	}
+};
+
 SoundBuffer *SoundEmitter::allocateBuffer(const std::string &filename)
 {
 	SoundBuffer *buffer = bufferHash.value(filename, 0);
@@ -199,40 +237,22 @@ SoundBuffer *SoundEmitter::allocateBuffer(const std::string &filename)
 	}
 	else
 	{
-		/* Buffer not in cashe, needs to be loaded */
-		SDL_RWops dataSource;
-		char ext[8];
+		/* Buffer not in cache, needs to be loaded */
+		SoundOpenHandler handler;
+		shState->fileSystem().openRead(handler, filename.c_str());
+		buffer = handler.buffer;
 
-		shState->fileSystem().openRead(dataSource, filename.c_str(),
-		                               false, ext, sizeof(ext));
-
-		Sound_Sample *sampleHandle = Sound_NewSample(&dataSource, ext, 0, STREAM_BUF_SIZE);
-
-		if (!sampleHandle)
+		if (!buffer)
 		{
 			char buf[512];
-			snprintf(buf, sizeof(buf), "Unable to decode sound: %s.%s: %s",
-			         filename.c_str(), ext, Sound_GetError());
+			snprintf(buf, sizeof(buf), "Unable to decode sound: %s: %s",
+			         filename.c_str(), Sound_GetError());
 			Debug() << buf;
 
 			return 0;
 		}
 
-		uint32_t decBytes = Sound_DecodeAll(sampleHandle);
-		uint8_t sampleSize = formatSampleSize(sampleHandle->actual.format);
-		uint32_t sampleCount = decBytes / sampleSize;
-
-		buffer = new SoundBuffer;
 		buffer->key = filename;
-		buffer->bytes = sampleSize * sampleCount;
-
-		ALenum alFormat = chooseALFormat(sampleSize, sampleHandle->actual.channels);
-
-		AL::Buffer::uploadData(buffer->alBuffer, alFormat, sampleHandle->buffer,
-							   buffer->bytes, sampleHandle->actual.rate);
-
-		Sound_FreeSample(sampleHandle);
-
 		uint32_t wouldBeBytes = bufferBytes + buffer->bytes;
 
 		/* If memory limit is reached, delete lowest priority buffer
diff --git a/src/sprite.cpp b/src/sprite.cpp
index 0313444..760bb0b 100644
--- a/src/sprite.cpp
+++ b/src/sprite.cpp
@@ -109,7 +109,7 @@ struct SpritePrivate
 		wave.amp = 0;
 		wave.length = 180;
 		wave.speed = 360;
-		wave.phase = 0.0;
+		wave.phase = 0.0f;
 		wave.dirty = false;
 	}
 
@@ -129,7 +129,7 @@ struct SpritePrivate
 		                     (srcRect->y + srcRect->height) +
 		                     bitmap->height();
 
-		efBushDepth = 1.0 - texBushDepth / bitmap->height();
+		efBushDepth = 1.0f - texBushDepth / bitmap->height();
 	}
 
 	void onSrcRectChange()
@@ -194,7 +194,7 @@ struct SpritePrivate
 	void emitWaveChunk(SVertex *&vert, float phase, int width,
 	                   float zoomY, int chunkY, int chunkLength)
 	{
-		float wavePos = phase + (chunkY / (float) wave.length) * M_PI * 2;
+		float wavePos = phase + (chunkY / (float) wave.length) * (float) (M_PI * 2);
 		float chunkX = sin(wavePos) * wave.amp;
 
 		FloatRect tex(0, chunkY / zoomY, width, chunkLength / zoomY);
@@ -261,7 +261,7 @@ struct SpritePrivate
 		wave.qArray.resize(!!firstLength + chunks + !!lastLength);
 		SVertex *vert = &wave.qArray.vertices[0];
 
-		float phase = (wave.phase * M_PI) / 180.f;
+		float phase = (wave.phase * (float) M_PI) / 180.0f;
 
 		if (firstLength > 0)
 			emitWaveChunk(vert, phase, width, zoomY, 0, firstLength);
diff --git a/src/tileatlasvx.cpp b/src/tileatlasvx.cpp
index 8f3ec47..663eca3 100644
--- a/src/tileatlasvx.cpp
+++ b/src/tileatlasvx.cpp
@@ -51,14 +51,14 @@ extern const int autotileVXRectsBN;
 /* Waterfall (C) autotile patterns */
 static const StaticRect autotileVXRectsC[] =
 {
-	{ 32.5, 0.5, 15, 31 },
-	{ 16.5, 0.5, 15, 31 },
-	{  0.0, 0.5, 15, 31 },
-	{ 16.5, 0.5, 15, 31 },
-	{ 32.5, 0.5, 15, 31 },
-	{ 48.5, 0.5, 15, 31 },
-	{  0.0, 0.5, 15, 31 },
-	{ 48.5, 0.5, 15, 31 }
+	{ 32.5f, 0.5f, 15.0f, 31.0f },
+	{ 16.5f, 0.5f, 15.0f, 31.0f },
+	{  0.0f, 0.5f, 15.0f, 31.0f },
+	{ 16.5f, 0.5f, 15.0f, 31.0f },
+	{ 32.5f, 0.5f, 15.0f, 31.0f },
+	{ 48.5f, 0.5f, 15.0f, 31.0f },
+	{  0.0f, 0.5f, 15.0f, 31.0f },
+	{ 48.5f, 0.5f, 15.0f, 31.0f }
 };
 
 static elementsN(autotileVXRectsC);
@@ -500,7 +500,7 @@ onTileA4(Reader &reader, int16_t tileID,
 	Vec2i orig = blitsA4[0].dst;
 	tileID -= 0x1700;
 
-	const int offY[] = { 0, 3, 5, 8, 10, 13 };
+	static const int offY[] = { 0, 3, 5, 8, 10, 13 };
 
 	int patternID  = tileID % 0x30;
 	int autotileID = tileID / 0x30;
diff --git a/src/tilemap-common.h b/src/tilemap-common.h
index 45ee91c..028dfd7 100644
--- a/src/tilemap-common.h
+++ b/src/tilemap-common.h
@@ -46,6 +46,13 @@ wrap(int value, int range)
 	return res < 0 ? res + range : res;
 }
 
+static inline Vec2i
+wrap(const Vec2i &value, int range)
+{
+	return Vec2i(wrap(value.x, range),
+	             wrap(value.y, range));
+}
+
 static inline int16_t
 tableGetWrapped(const Table &t, int x, int y, int z = 0)
 {
@@ -54,6 +61,16 @@ tableGetWrapped(const Table &t, int x, int y, int z = 0)
 	             z);
 }
 
+/* Calculate the tile x/y on which this pixel x/y lies */
+static inline Vec2i
+getTilePos(const Vec2i &pixelPos)
+{
+	/* Round the pixel position down to the nearest top left
+	 * tile boundary, by masking off the lower 5 bits (2^5 = 32).
+	 * Then divide by 32 to convert into tile units. */
+	return (pixelPos & ~(32-1)) / 32;
+}
+
 enum AtSubPos
 {
 	TopLeft          = 0,
diff --git a/src/tilemap.cpp b/src/tilemap.cpp
index d3cf437..8b61ea7 100644
--- a/src/tilemap.cpp
+++ b/src/tilemap.cpp
@@ -236,7 +236,7 @@ struct TilemapPrivate
 	Table *mapData;
 	Table *priorities;
 	bool visible;
-	Vec2i offset;
+	Vec2i origin;
 
 	Vec2i dispPos;
 
@@ -294,7 +294,6 @@ struct TilemapPrivate
 		/* Used layers out of 'zlayers' (rest is hidden) */
 		size_t activeLayers;
 		Scene::Geometry sceneGeo;
-		Vec2i sceneOffset;
 	} elem;
 
 	/* Affected by: autotiles, tileset */
@@ -394,7 +393,7 @@ struct TilemapPrivate
 
 	void updateFlashMapViewport()
 	{
-		flashMap.setViewport(IntRect(viewpPos.x, viewpPos.y, viewpW, viewpH));
+		flashMap.setViewport(IntRect(viewpPos, Vec2i(viewpW, viewpH)));
 	}
 
 	void updateAtlasInfo()
@@ -442,13 +441,8 @@ struct TilemapPrivate
 
 	void updateSceneGeometry(const Scene::Geometry &geo)
 	{
-		elem.sceneOffset = geo.offset();
 		elem.sceneGeo = geo;
-	}
-
-	void updatePosition()
-	{
-		dispPos = -(offset - viewpPos * 32) + elem.sceneOffset;
+		mapViewportDirty = true;
 	}
 
 	void invalidateAtlasSize()
@@ -698,7 +692,7 @@ struct TilemapPrivate
 		int tileY = tsInd / 8;
 
 		Vec2i texPos = TileAtlas::tileToAtlasCoor(tileX, tileY, atlas.efTilesetH, atlas.size.y);
-		FloatRect texRect((float) texPos.x+.5, (float) texPos.y+.5, 31, 31);
+		FloatRect texRect((float) texPos.x+0.5f, (float) texPos.y+0.5f, 31, 31);
 		FloatRect posRect(x*32, y*32, 32, 32);
 
 		SVertex v[4];
@@ -898,38 +892,17 @@ struct TilemapPrivate
 
 	void updateMapViewport()
 	{
-		int tileOX, tileOY;
+		const Vec2i combOrigin = origin + elem.sceneGeo.orig;
+		const Vec2i mvpPos = getTilePos(combOrigin);
 
-		if (offset.x >= 0)
-			tileOX = offset.x / 32;
-		else
-			tileOX = -(-(offset.x-31) / 32);
-
-		if (offset.y >= 0)
-			tileOY = offset.y / 32;
-		else
-			tileOY = -(-(offset.y-31) / 32);
-
-		bool dirty = false;
-
-		if (tileOX < viewpPos.x || tileOX + 21 > viewpPos.x + viewpW)
-		{
-			viewpPos.x = tileOX;
-			dirty = true;
-		}
-
-		if (tileOY < viewpPos.y || tileOY + 16 > viewpPos.y + viewpH)
-		{
-			viewpPos.y = tileOY;
-			dirty = true;
-		}
-
-		if (dirty)
+		if (mvpPos != viewpPos)
 		{
+			viewpPos = mvpPos;
 			buffersDirty = true;
 			updateFlashMapViewport();
-			updatePosition();
 		}
+
+		dispPos = elem.sceneGeo.rect.pos() - wrap(combOrigin, 32);
 	}
 
 	void prepare()
@@ -1024,7 +997,6 @@ void GroundLayer::drawInt()
 void GroundLayer::onGeometryChange(const Scene::Geometry &geo)
 {
 	p->updateSceneGeometry(geo);
-	p->updatePosition();
 }
 
 ZLayer::ZLayer(TilemapPrivate *p, Viewport *viewport)
@@ -1072,7 +1044,7 @@ void ZLayer::drawInt()
 
 int ZLayer::calculateZ(TilemapPrivate *p, int index)
 {
-	return 32 * (index + p->viewpPos.y + 1) - p->offset.y;
+	return 32 * (index + p->viewpPos.y + 1) - p->origin.y;
 }
 
 void ZLayer::initUpdateZ()
@@ -1172,8 +1144,8 @@ DEF_ATTR_RD_SIMPLE(Tilemap, MapData, Table*, p->mapData)
 DEF_ATTR_RD_SIMPLE(Tilemap, FlashData, Table*, p->flashMap.getData())
 DEF_ATTR_RD_SIMPLE(Tilemap, Priorities, Table*, p->priorities)
 DEF_ATTR_RD_SIMPLE(Tilemap, Visible, bool, p->visible)
-DEF_ATTR_RD_SIMPLE(Tilemap, OX, int, p->offset.x)
-DEF_ATTR_RD_SIMPLE(Tilemap, OY, int, p->offset.y)
+DEF_ATTR_RD_SIMPLE(Tilemap, OX, int, p->origin.x)
+DEF_ATTR_RD_SIMPLE(Tilemap, OY, int, p->origin.y)
 
 void Tilemap::setTileset(Bitmap *value)
 {
@@ -1259,11 +1231,10 @@ void Tilemap::setOX(int value)
 {
 	guardDisposed();
 
-	if (p->offset.x == value)
+	if (p->origin.x == value)
 		return;
 
-	p->offset.x = value;
-	p->updatePosition();
+	p->origin.x = value;
 	p->mapViewportDirty = true;
 }
 
@@ -1271,11 +1242,10 @@ void Tilemap::setOY(int value)
 {
 	guardDisposed();
 
-	if (p->offset.y == value)
+	if (p->origin.y == value)
 		return;
 
-	p->offset.y = value;
-	p->updatePosition();
+	p->origin.y = value;
 	p->zOrderDirty = true;
 	p->mapViewportDirty = true;
 }
diff --git a/src/tilemapvx.cpp b/src/tilemapvx.cpp
index 6291fd9..50d8df0 100644
--- a/src/tilemapvx.cpp
+++ b/src/tilemapvx.cpp
@@ -57,12 +57,12 @@ struct TilemapVXPrivate : public ViewportElement, TileAtlasVX::Reader
 
 	Table *mapData;
 	Table *flags;
-	Vec2i offset;
+	Vec2i origin;
 
-	Vec2i dispPos;
-	/* Map viewport position */
+	/* Subregion of the map that is drawn to screen (map viewport) */
 	IntRect mapViewp;
-	Vec2i sceneOffset;
+	/* Position on screen the map subregion is drawn at */
+	Vec2i dispPos;
 	Scene::Geometry sceneGeo;
 
 	std::vector<SVertex> groundVert;
@@ -179,49 +179,31 @@ struct TilemapVXPrivate : public ViewportElement, TileAtlasVX::Reader
 		TileAtlasVX::build(atlas, bitmaps);
 	}
 
-	void updatePosition()
-	{
-		dispPos.x = -(offset.x - mapViewp.x * 32) + sceneOffset.x;
-		dispPos.y = -(offset.y - mapViewp.y * 32) + sceneOffset.y;
-	}
-
 	void updateMapViewport()
 	{
-		int tileOX, tileOY;
+		/* Note: We include one extra row at the top above
+		 * the normal map viewport to ensure the legs of table
+		 * tiles off screen are properly drawn */
 
-		Vec2i offs(offset.x-sceneOffset.x, offset.y-sceneOffset.y);
+		IntRect newMvp;
 
-		if (offs.x >= 0)
-			tileOX = offs.x / 32;
-		else
-			tileOX = -(-(offs.x-31) / 32);
+		const Vec2i combOrigin = origin + sceneGeo.orig;
+		const Vec2i geoSize = sceneGeo.rect.size();
 
-		if (offs.y >= 0)
-			tileOY = offs.y / 32;
-		else
-			tileOY = -(-(offs.y-31) / 32);
+		newMvp.setPos(getTilePos(combOrigin) - Vec2i(0, 1));
 
-		bool dirty = false;
+		/* Ensure that the size is big enough to cover the whole viewport,
+		 * and add one tile row/column as a buffer for scrolling */
+		newMvp.setSize((geoSize / 32) + !!(geoSize % 32) + Vec2i(1, 2));
 
-		if (tileOX < mapViewp.x || tileOX > mapViewp.x)
-		{
-			mapViewp.x = tileOX;
-			dirty = true;
-		}
-
-		if (tileOY < mapViewp.y || tileOY > mapViewp.y)
-		{
-			mapViewp.y = tileOY;
-			dirty = true;
-		}
-
-		if (dirty)
+		if (newMvp != mapViewp)
 		{
+			mapViewp = newMvp;
+			flashMap.setViewport(newMvp);
 			buffersDirty = true;
 		}
 
-		updatePosition();
-		flashMap.setViewport(mapViewp);
+		dispPos = sceneGeo.rect.pos() - wrap(combOrigin, 32) - Vec2i(0, 32);
 	}
 
 	static size_t quadBytes(size_t quads)
@@ -366,10 +348,6 @@ struct TilemapVXPrivate : public ViewportElement, TileAtlasVX::Reader
 
 	void onGeometryChange(const Scene::Geometry &geo)
 	{
-		const Vec2i geoSize = geo.rect.size();
-		mapViewp.setSize((geoSize / 32) + !!(geoSize % 32) + Vec2i(1));
-
-		sceneOffset = geo.offset();
 		sceneGeo = geo;
 
 		buffersDirty = true;
@@ -380,7 +358,7 @@ struct TilemapVXPrivate : public ViewportElement, TileAtlasVX::Reader
 
 	/* TileAtlasVX::Reader */
 	void onQuads(const FloatRect *t, const FloatRect *p,
-	              size_t n, bool overPlayer)
+	             size_t n, bool overPlayer)
 	{
 		SVertex *vert = allocVert(overPlayer ? aboveVert : groundVert, n*4);
 
@@ -467,8 +445,8 @@ TilemapVX::BitmapArray &TilemapVX::getBitmapArray()
 DEF_ATTR_RD_SIMPLE(TilemapVX, MapData, Table*, p->mapData)
 DEF_ATTR_RD_SIMPLE(TilemapVX, FlashData, Table*, p->flashMap.getData())
 DEF_ATTR_RD_SIMPLE(TilemapVX, Flags, Table*, p->flags)
-DEF_ATTR_RD_SIMPLE(TilemapVX, OX, int, p->offset.x)
-DEF_ATTR_RD_SIMPLE(TilemapVX, OY, int, p->offset.y)
+DEF_ATTR_RD_SIMPLE(TilemapVX, OX, int, p->origin.x)
+DEF_ATTR_RD_SIMPLE(TilemapVX, OY, int, p->origin.y)
 
 Viewport *TilemapVX::getViewport() const
 {
@@ -541,10 +519,10 @@ void TilemapVX::setOX(int value)
 {
 	guardDisposed();
 
-	if (p->offset.x == value)
+	if (p->origin.x == value)
 		return;
 
-	p->offset.x = value;
+	p->origin.x = value;
 	p->mapViewportDirty = true;
 }
 
@@ -552,10 +530,10 @@ void TilemapVX::setOY(int value)
 {
 	guardDisposed();
 
-	if (p->offset.y == value)
+	if (p->origin.y == value)
 		return;
 
-	p->offset.y = value;
+	p->origin.y = value;
 	p->mapViewportDirty = true;
 }
 
diff --git a/src/transform.h b/src/transform.h
index cefd09f..7a129f9 100644
--- a/src/transform.h
+++ b/src/transform.h
@@ -130,7 +130,7 @@ private:
 		if (rotation < 0)
 			rotation += 360;
 
-		float angle  = rotation * 3.141592654f / 180.f;
+		float angle  = rotation * 3.141592654f / 180.0f;
 		float cosine = (float) cos(angle);
 		float sine   = (float) sin(angle);
 		float sxc    = scale.x * cosine;
diff --git a/src/window.cpp b/src/window.cpp
index a32481d..71a8cc9 100644
--- a/src/window.cpp
+++ b/src/window.cpp
@@ -642,7 +642,7 @@ struct WindowPrivate
 
 		if (active && cursorVert.vert)
 		{
-			float alpha = cursorAniAlpha[cursorAniAlphaIdx] / 255.0;
+			float alpha = cursorAniAlpha[cursorAniAlphaIdx] / 255.0f;
 
 			cursorVert.setAlpha(alpha);
 
@@ -651,7 +651,7 @@ struct WindowPrivate
 
 		if (pause && pauseAniVert.vert)
 		{
-			float alpha = pauseAniAlpha[pauseAniAlphaIdx] / 255.0;
+			float alpha = pauseAniAlpha[pauseAniAlphaIdx] / 255.0f;
 			FloatRect frameRect = pauseAniSrc[pauseAniQuad[pauseAniQuadIdx]];
 
 			pauseAniVert.setAlpha(alpha);
diff --git a/src/windowvx.cpp b/src/windowvx.cpp
index 0bafb5b..02a70d9 100644
--- a/src/windowvx.cpp
+++ b/src/windowvx.cpp
@@ -482,7 +482,7 @@ struct WindowVXPrivate
 	void updateBaseQuad()
 	{
 		const FloatRect tex(0, 0, geo.w, geo.h);
-		const FloatRect pos(0, (geo.h / 2.0) * (1 - openness.norm),
+		const FloatRect pos(0, (geo.h / 2.0f) * (1.0f - openness.norm),
 		                    geo.w, geo.h * openness.norm);
 
 		base.quad.setTexPosRect(tex, pos);
@@ -633,7 +633,7 @@ struct WindowVXPrivate
 		Quad::setTexRect(pauseVert, pauseSrc[pauseQuad[pauseQuadIdx]]);
 
 		/* Set opacity */
-		Quad::setColor(pauseVert, Vec4(1, 1, 1, pauseAlpha[pauseAlphaIdx] / 255.0));
+		Quad::setColor(pauseVert, Vec4(1, 1, 1, pauseAlpha[pauseAlphaIdx] / 255.0f));
 
 		ctrlVertArrayDirty = true;
 	}
@@ -643,7 +643,7 @@ struct WindowVXPrivate
 		if (cursorVert.count() == 0)
 			return;
 
-		Vec4 color(1, 1, 1, cursorAlpha[cursorAlphaIdx] / 255.0);
+		Vec4 color(1, 1, 1, cursorAlpha[cursorAlphaIdx] / 255.0f);
 
 		for (size_t i = 0; i < cursorVert.count(); ++i)
 			Quad::setColor(&cursorVert.vertices[i*4], color);