diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index fa5948ded3ae5e250dfc09fc2c1a41176f56a3c9..a1f752ed7d540d72ac280aeff28dd4f9c5c0210e 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -232,7 +232,7 @@ set(SRB2_CONFIG_HAVE_GME ON CACHE BOOL
 set(SRB2_CONFIG_HAVE_OPENMPT ON CACHE BOOL
 	"Enable OpenMPT support.")
 set(SRB2_CONFIG_HAVE_CURL ON CACHE BOOL
-	"Enable curl support, used for downloading files via HTTP.")
+	"Enable curl support.")
 set(SRB2_CONFIG_HAVE_THREADS ON CACHE BOOL
 	"Enable multithreading support.")
 if(${CMAKE_SYSTEM} MATCHES Windows)
@@ -458,7 +458,7 @@ endif()
 if(${SRB2_CONFIG_HAVE_CURL})
 	if(${SRB2_CONFIG_USE_INTERNAL_LIBRARIES})
 		set(CURL_FOUND ON)
-		set(CURL_INCLUDE_DIRS ${CMAKE_SOURCE_DIR}/libs/curl)
+		set(CURL_INCLUDE_DIRS ${CMAKE_SOURCE_DIR}/libs/curl/include)
 		if(${SRB2_SYSTEM_BITS} EQUAL 64)
 			set(CURL_LIBRARIES "-L${CMAKE_SOURCE_DIR}/libs/curl/lib64 -lcurl")
 		else() # 32-bit
@@ -552,6 +552,7 @@ if(${SRB2_CONFIG_USEASM})
 	endif()
 	set(SRB2_USEASM ON)
 	add_definitions(-DUSEASM)
+	set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -msse3 -mfpmath=sse")
 else()
 	set(SRB2_USEASM OFF)
 	add_definitions(-DNONX86 -DNORUSEASM)
diff --git a/src/Makefile b/src/Makefile
index 8ad7ecf5a53de9868877a03d9d50b6985621bdb8..da918e2057146f2031fce4eec742e85d46e79c3f 100644
--- a/src/Makefile
+++ b/src/Makefile
@@ -277,7 +277,7 @@ OPTS += -DCOMPVERSION
 
 ifndef NONX86
 ifndef GCC29
-	ARCHOPTS?=-march=pentium
+	ARCHOPTS?=-msse3 -mfpmath=sse
 else
 	ARCHOPTS?=-mpentium
 endif
diff --git a/src/dehacked.c b/src/dehacked.c
index ae22e19d517d31e4327f59fcbb806657b2aa7d05..df0fe6f5053807bc388b502cfa244f4829ca75da 100644
--- a/src/dehacked.c
+++ b/src/dehacked.c
@@ -39,6 +39,7 @@
 #include "lua_script.h"
 #include "lua_hook.h"
 #include "d_clisrv.h"
+#include "g_state.h" // gamestate_t (for lua)
 
 #include "m_cond.h"
 
@@ -5179,6 +5180,7 @@ static const char *const STATE_LIST[] = { // array length left dynamic for sanit
 	"S_TAILSOVERLAY_PAIN",
 	"S_TAILSOVERLAY_GASP",
 	"S_TAILSOVERLAY_EDGE",
+	"S_TAILSOVERLAY_DASH",
 
 	// [:
 	"S_JETFUMEFLASH",
@@ -10122,6 +10124,22 @@ struct {
 	{"MU_MOD_EX", MU_MOD_EX},
 	{"MU_MID_EX", MU_MID_EX},
 
+	// gamestates
+	{"GS_NULL",GS_NULL},
+	{"GS_LEVEL",GS_LEVEL},
+	{"GS_INTERMISSION",GS_INTERMISSION},
+	{"GS_CONTINUING",GS_CONTINUING},
+	{"GS_TITLESCREEN",GS_TITLESCREEN},
+	{"GS_TIMEATTACK",GS_TIMEATTACK},
+	{"GS_CREDITS",GS_CREDITS},
+	{"GS_EVALUATION",GS_EVALUATION},
+	{"GS_GAMEEND",GS_GAMEEND},
+	{"GS_INTRO",GS_INTRO},
+	{"GS_ENDING",GS_ENDING},
+	{"GS_CUTSCENE",GS_CUTSCENE},
+	{"GS_DEDICATEDSERVER",GS_DEDICATEDSERVER},
+	{"GS_WAITINGPLAYERS",GS_WAITINGPLAYERS},
+
 	{NULL,0}
 };
 
diff --git a/src/hardware/hw_batching.c b/src/hardware/hw_batching.c
index a63be3a729b00d042f2cd20eb7058721bda590b7..5ea9f55d4c9ba88cc36ffa6ba2e44f72ce79ff60 100644
--- a/src/hardware/hw_batching.c
+++ b/src/hardware/hw_batching.c
@@ -1,7 +1,6 @@
 // SONIC ROBO BLAST 2
 //-----------------------------------------------------------------------------
-// Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 1999-2020 by Sonic Team Junior.
+// Copyright (C) 2020 by Sonic Team Junior.
 //
 // This program is free software distributed under the
 // terms of the GNU General Public License, version 2.
diff --git a/src/hardware/hw_batching.h b/src/hardware/hw_batching.h
index 7c108a4bd2a18e2cee24487afe8c79b4b856c28b..3d22324ac8aaef8baf7bae73ccd89e92dcd78385 100644
--- a/src/hardware/hw_batching.h
+++ b/src/hardware/hw_batching.h
@@ -1,7 +1,6 @@
 // SONIC ROBO BLAST 2
 //-----------------------------------------------------------------------------
-// Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 1999-2020 by Sonic Team Junior.
+// Copyright (C) 2020 by Sonic Team Junior.
 //
 // This program is free software distributed under the
 // terms of the GNU General Public License, version 2.
diff --git a/src/info.c b/src/info.c
index cb5fb0889e2830313f1e54bb0350b95da3c52f8a..29a79b1d6e28ab0e9b1352b0338d9dacc2a06d32 100644
--- a/src/info.c
+++ b/src/info.c
@@ -584,6 +584,7 @@ char spr2names[NUMPLAYERSPRITES][5] =
 	"TAL9",
 	"TALA",
 	"TALB",
+	"TALC",
 
 	"CNT1",
 	"CNT2",
@@ -661,6 +662,7 @@ playersprite_t spr2defaults[NUMPLAYERSPRITES] = {
 	SPR2_TAL0, // SPR2_TAL9,
 	SPR2_TAL9, // SPR2_TALA,
 	SPR2_TAL0, // SPR2_TALB,
+	SPR2_TAL6, // SPR2_TALC,
 
 	SPR2_WAIT, // SPR2_CNT1,
 	SPR2_FALL, // SPR2_CNT2,
@@ -801,6 +803,7 @@ state_t states[NUMSTATES] =
 	{SPR_PLAY, SPR2_TAL9|FF_SPR2MIDSTART, 35, {NULL}, 0, 0, S_TAILSOVERLAY_PAIN}, // S_TAILSOVERLAY_PAIN
 	{SPR_PLAY, SPR2_TALA|FF_SPR2MIDSTART, 35, {NULL}, 0, 0, S_TAILSOVERLAY_GASP}, // S_TAILSOVERLAY_GASP
 	{SPR_PLAY, SPR2_TALB                , 35, {NULL}, 0, 0, S_TAILSOVERLAY_EDGE}, // S_TAILSOVERLAY_EDGE
+	{SPR_PLAY, SPR2_TALC|FF_SPR2MIDSTART, 35, {NULL}, 0, 0, S_TAILSOVERLAY_DASH}, // S_TAILSOVERLAY_DASH
 
 	// [:
 	{SPR_JETF, 3|FF_ANIMATE|FF_FULLBRIGHT, 2, {NULL}, 1, 1, S_JETFUME1}, // S_JETFUMEFLASH
diff --git a/src/info.h b/src/info.h
index 721ebf7f269c8d575a5b717a3bc7d76def2b5730..d84461617c41ec2cc93a774da39e1dc8c09cc376 100644
--- a/src/info.h
+++ b/src/info.h
@@ -856,6 +856,7 @@ typedef enum playersprite
 	SPR2_TAL9,
 	SPR2_TALA,
 	SPR2_TALB,
+	SPR2_TALC,
 
 	SPR2_CNT1, // continue disappointment
 	SPR2_CNT2, // continue lift
@@ -997,6 +998,7 @@ typedef enum state
 	S_TAILSOVERLAY_PAIN,
 	S_TAILSOVERLAY_GASP,
 	S_TAILSOVERLAY_EDGE,
+	S_TAILSOVERLAY_DASH,
 
 	// [:
 	S_JETFUMEFLASH,
diff --git a/src/lua_script.c b/src/lua_script.c
index ae7f479f6da07931dd68215c23bf272dfc6a2261..6e40cb785444d15b39f7aacdd571e6d1cb393ab4 100644
--- a/src/lua_script.c
+++ b/src/lua_script.c
@@ -34,6 +34,7 @@
 #include "lua_hook.h"
 
 #include "doomstat.h"
+#include "g_state.h"
 
 lua_State *gL = NULL;
 
@@ -361,6 +362,9 @@ int LUA_PushGlobals(lua_State *L, const char *word)
 	} else if (fastcmp(word, "token")) {
 		lua_pushinteger(L, token);
 		return 1;
+	} else if (fastcmp(word, "gamestate")) {
+		lua_pushinteger(L, gamestate);
+		return 1;
 	}
 	return 0;
 }
diff --git a/src/m_cheat.c b/src/m_cheat.c
index 349f00c4858722a984bdb48bacd13af168245651..8e9cd9f51d9020e63b47f7f2ac66274c22cc9d13 100644
--- a/src/m_cheat.c
+++ b/src/m_cheat.c
@@ -1440,14 +1440,21 @@ void Command_Writethings_f(void)
 
 void Command_ObjectPlace_f(void)
 {
+	size_t thingarg;
+	size_t silent;
+
 	REQUIRE_INLEVEL;
 	REQUIRE_SINGLEPLAYER;
 	REQUIRE_NOULTIMATE;
 
 	G_SetGameModified(multiplayer);
 
+	silent = COM_CheckParm("-silent");
+
+	thingarg = 2 - ( silent != 1 );
+
 	// Entering objectplace?
-	if (!objectplacing || COM_Argc() > 1)
+	if (!objectplacing || thingarg < COM_Argc())
 	{
 		if (!objectplacing)
 		{
@@ -1456,7 +1463,7 @@ void Command_ObjectPlace_f(void)
 			if (players[0].powers[pw_carry] == CR_NIGHTSMODE)
 				return;
 
-			if (!COM_CheckParm("-silent"))
+			if (! silent)
 			{
 				HU_SetCEchoFlags(V_RETURN8|V_MONOSPACE|V_AUTOFADEOUT);
 				HU_SetCEchoDuration(10);
@@ -1507,9 +1514,9 @@ void Command_ObjectPlace_f(void)
 				op_oldstate = (statenum_t)(players[0].mo->state-states);
 		}
 
-		if (COM_Argc() > 1)
+		if (thingarg < COM_Argc())
 		{
-			UINT16 mapthingnum = atoi(COM_Argv(1));
+			UINT16 mapthingnum = atoi(COM_Argv(thingarg));
 			mobjtype_t type = P_GetMobjtype(mapthingnum);
 			if (type == MT_UNKNOWN)
 				CONS_Printf(M_GetText("No mobj type delegated to thing type %d.\n"), mapthingnum);
diff --git a/src/m_menu.c b/src/m_menu.c
index 6e0d520aea0c998c0b07f0ce29b598c6d757bc08..5860f00ca1a1f4dc5399982f06ccebc21c3d469b 100644
--- a/src/m_menu.c
+++ b/src/m_menu.c
@@ -8246,7 +8246,7 @@ static void M_CacheLoadGameData(void)
 
 static void M_DrawLoadGameData(void)
 {
-	INT32 i, savetodraw, x, y, hsep = 90;
+	INT32 i, prev_i = 1, savetodraw, x, y, hsep = 90;
 	skin_t *charskin = NULL;
 
 	if (vid.width != BASEVIDWIDTH*vid.dupx)
@@ -8255,8 +8255,9 @@ static void M_DrawLoadGameData(void)
 	if (needpatchrecache)
 		M_CacheLoadGameData();
 
-	for (i = -2; i <= 2; i++)
+	for (i = 2; prev_i; i = -(i + ((UINT32)i >> 31))) // draws from outwards in; 2, -2, 1, -1, 0
 	{
+		prev_i = i;
 		savetodraw = (saveSlotSelected + i + numsaves)%numsaves;
 		x = (BASEVIDWIDTH/2 - 42 + loadgamescroll) + (i*hsep);
 		y = 33 + 9;
diff --git a/src/m_perfstats.c b/src/m_perfstats.c
index df1e31b5e20ab2770f20ec00d2f1679caf00c140..085adda80dac925917e2f86ec3a14ec4ec5c0590 100644
--- a/src/m_perfstats.c
+++ b/src/m_perfstats.c
@@ -1,7 +1,6 @@
 // SONIC ROBO BLAST 2
 //-----------------------------------------------------------------------------
-// Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 1999-2020 by Sonic Team Junior.
+// Copyright (C) 2020 by Sonic Team Junior.
 //
 // This program is free software distributed under the
 // terms of the GNU General Public License, version 2.
diff --git a/src/m_perfstats.h b/src/m_perfstats.h
index 1db46025e43c2a1baea59ef176d8682ae91e02f0..01a818c1c360c3cdc6231069686edcb7bf3544ee 100644
--- a/src/m_perfstats.h
+++ b/src/m_perfstats.h
@@ -1,7 +1,6 @@
 // SONIC ROBO BLAST 2
 //-----------------------------------------------------------------------------
-// Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 1999-2020 by Sonic Team Junior.
+// Copyright (C) 2020 by Sonic Team Junior.
 //
 // This program is free software distributed under the
 // terms of the GNU General Public License, version 2.
diff --git a/src/p_setup.c b/src/p_setup.c
index 7747f6462b976bc06c4cee699f7e6c84650fc05d..8e09c34df9fa97fda3dea95b99b6abf05440d761 100644
--- a/src/p_setup.c
+++ b/src/p_setup.c
@@ -2401,6 +2401,10 @@ static boolean P_LoadExtendedSubsectorsAndSegs(UINT8 **data, nodetype_t nodetype
 		seg->angle = R_PointToAngle2(v1->x, v1->y, v2->x, v2->y);
 		if (seg->linedef)
 			segs[i].offset = FixedHypot(v1->x - seg->linedef->v1->x, v1->y - seg->linedef->v1->y);
+		seg->length = P_SegLength(seg);
+#ifdef HWRENDER
+		seg->flength = (rendermode == render_opengl) ? P_SegLengthFloat(seg) : 0;
+#endif
 	}
 
 	return true;
diff --git a/src/p_user.c b/src/p_user.c
index 0d73312931c1b926daf8e8959fdf2b51c8fa4d8a..f9593158f3b269e2dd03c3bd64fec9ec4f8ebcc8 100644
--- a/src/p_user.c
+++ b/src/p_user.c
@@ -2029,6 +2029,7 @@ mobj_t *P_SpawnGhostMobj(mobj_t *mobj)
 	ghost->colorized = mobj->colorized; // alternatively, "true" for sonic advance style colourisation
 
 	ghost->angle = (mobj->player ? mobj->player->drawangle : mobj->angle);
+	ghost->rollangle = mobj->rollangle;
 	ghost->sprite = mobj->sprite;
 	ghost->sprite2 = mobj->sprite2;
 	ghost->frame = mobj->frame;
@@ -11206,6 +11207,8 @@ static void P_DoTailsOverlay(player_t *player, mobj_t *tails)
 		chosenstate = S_TAILSOVERLAY_GASP;
 	else if (player->mo->state-states == S_PLAY_EDGE)
 		chosenstate = S_TAILSOVERLAY_EDGE;
+	else if (player->panim == PA_DASH)
+		chosenstate = S_TAILSOVERLAY_DASH;
 	else if (player->panim == PA_RUN)
 		chosenstate = S_TAILSOVERLAY_RUN;
 	else if (player->panim == PA_WALK)
diff --git a/src/sdl/CMakeLists.txt b/src/sdl/CMakeLists.txt
index bb5edf8170e486974c03e9a0e13437348d8eebd1..a7f015c869c783537dccfff26bad85de9c68f5f5 100644
--- a/src/sdl/CMakeLists.txt
+++ b/src/sdl/CMakeLists.txt
@@ -272,7 +272,7 @@ if(${SDL2_FOUND})
 	endif()
 
 	target_compile_definitions(SRB2SDL2 PRIVATE
-		-DDDIRECTFULLSCREEN -DHAVE_SDL
+		-DDIRECTFULLSCREEN -DHAVE_SDL
 	)
 
 	## strip debug symbols into separate file when using gcc.