(svn r12743) [NewGRF_ports] -Sync: with trunk r12705:12741. NewGRF_ports
authorrichk
Wed, 16 Apr 2008 22:34:14 +0000
branchNewGRF_ports
changeset 10211 c1391c8ed5c6
parent 10210 a2131f7a315d
child 10242 52b4a9006029
(svn r12743) [NewGRF_ports] -Sync: with trunk r12705:12741.
configure
projects/generate
projects/generate.vbs
projects/openttd_vs80.vcproj
projects/openttd_vs90.vcproj
source.list
src/fiber.hpp
src/fiber_thread.cpp
src/fiber_win32.cpp
src/genworld.cpp
src/genworld.h
src/group_cmd.cpp
src/lang/english.txt
src/misc/smallvec.h
src/network/network_gamelist.cpp
src/network/network_gui.cpp
src/newgrf_town.cpp
src/order_cmd.cpp
src/saveload.cpp
src/settings.cpp
src/sound/win32_s.cpp
src/thread.cpp
src/thread.h
src/thread_none.cpp
src/thread_os2.cpp
src/thread_pthread.cpp
src/thread_win32.cpp
src/town.h
src/town_cmd.cpp
src/town_gui.cpp
src/train_cmd.cpp
src/vehicle.cpp
src/vehicle_base.h
src/viewport.cpp
src/viewport_func.h
src/window.cpp
--- a/configure	Wed Apr 16 22:16:04 2008 +0000
+++ b/configure	Wed Apr 16 22:34:14 2008 +0000
@@ -96,7 +96,7 @@
 		if ($0 == "MSVC"        && "'$os'" != "MSVC")              { next; }
 		if ($0 == "DIRECTMUSIC" && "'$with_direct_music'" == "0")  { next; }
 		if ($0 == "LIBTIMIDITY" && "'$libtimidity'" == "" )        { next; }
-		if ($0 == "NO_THREADS"  && "'$with_threads'" == "0")       { next; }
+		if ($0 == "HAVE_THREAD" && "'$with_threads'" == "0")       { next; }
 
 		skip += 1;
 
--- a/projects/generate	Wed Apr 16 22:16:04 2008 +0000
+++ b/projects/generate	Wed Apr 16 22:34:14 2008 +0000
@@ -83,7 +83,7 @@
 			if ($0 == "MSVC"        && "'$os'" != "MSVC")              { next; }
 			if ($0 == "DIRECTMUSIC" && "'$enable_directmusic'" != "1") { next; }
 			if ($0 == "LIBTIMIDITY" && "'$libtimidity'" == "" )        { next; }
-			if ($0 == "NO_THREADS"  && "'$with_threads'" == "0")       { next; }
+			if ($0 == "HAVE_THREAD" && "'$with_threads'" == "0")       { next; }
 
 			skip += 1;
 
--- a/projects/generate.vbs	Wed Apr 16 22:16:04 2008 +0000
+++ b/projects/generate.vbs	Wed Apr 16 22:34:14 2008 +0000
@@ -72,7 +72,7 @@
 						line = "WIN32" Or _
 						line = "MSVC" Or _
 						line = "DIRECTMUSIC" Or _
-						line = "NO_THREADS" _
+						line = "HAVE_THREAD" _
 					) Then skip = skip + 1
 					deep = deep + 1
 				Case "#"
--- a/projects/openttd_vs80.vcproj	Wed Apr 16 22:16:04 2008 +0000
+++ b/projects/openttd_vs80.vcproj	Wed Apr 16 22:34:14 2008 +0000
@@ -744,7 +744,11 @@
 				>
 			</File>
 			<File
-				RelativePath=".\..\src\thread.cpp"
+				RelativePath=".\..\src\thread_win32.cpp"
+				>
+			</File>
+			<File
+				RelativePath=".\..\src\fiber_win32.cpp"
 				>
 			</File>
 			<File
@@ -796,6 +800,10 @@
 				>
 			</File>
 			<File
+				RelativePath=".\..\src\core\alloc_type.hpp"
+				>
+			</File>
+			<File
 				RelativePath=".\..\src\articulated_vehicles.h"
 				>
 			</File>
@@ -948,6 +956,10 @@
 				>
 			</File>
 			<File
+				RelativePath=".\..\src\fiber.hpp"
+				>
+			</File>
+			<File
 				RelativePath=".\..\src\fileio.h"
 				>
 			</File>
@@ -2252,6 +2264,10 @@
 				>
 			</File>
 			<File
+				RelativePath=".\..\src\misc\countedobj.cpp"
+				>
+			</File>
+			<File
 				RelativePath=".\..\src\misc\countedptr.hpp"
 				>
 			</File>
@@ -2276,6 +2292,10 @@
 				>
 			</File>
 			<File
+				RelativePath=".\..\src\misc\smallveh.h"
+				>
+			</File>
+			<File
 				RelativePath=".\..\src\misc\str.hpp"
 				>
 			</File>
--- a/projects/openttd_vs90.vcproj	Wed Apr 16 22:16:04 2008 +0000
+++ b/projects/openttd_vs90.vcproj	Wed Apr 16 22:34:14 2008 +0000
@@ -733,7 +733,11 @@
 				>
 			</File>
 			<File
-				RelativePath=".\..\src\thread.cpp"
+				RelativePath=".\..\src\thread_win32.cpp"
+				>
+			</File>
+			<File
+				RelativePath=".\..\src\fiber_win32.cpp"
 				>
 			</File>
 			<File
@@ -789,6 +793,10 @@
 				>
 			</File>
 			<File
+				RelativePath=".\..\src\core\alloc_type.hpp"
+				>
+			</File>
+			<File
 				RelativePath=".\..\src\articulated_vehicles.h"
 				>
 			</File>
@@ -941,6 +949,10 @@
 				>
 			</File>
 			<File
+				RelativePath=".\..\src\fiber.hpp"
+				>
+			</File>
+			<File
 				RelativePath=".\..\src\fileio.h"
 				>
 			</File>
@@ -2229,6 +2241,10 @@
 				>
 			</File>
 			<File
+				RelativePath=".\..\src\misc\countedobj.cpp"
+				>
+			</File>
+			<File
 				RelativePath=".\..\src\misc\countedptr.hpp"
 				>
 			</File>
@@ -2253,6 +2269,10 @@
 				>
 			</File>
 			<File
+				RelativePath=".\..\src\misc\smallveh.h"
+				>
+			</File>
+			<File
 				RelativePath=".\..\src\misc\str.hpp"
 				>
 			</File>
--- a/source.list	Wed Apr 16 22:16:04 2008 +0000
+++ b/source.list	Wed Apr 16 22:34:14 2008 +0000
@@ -79,7 +79,21 @@
 strings.cpp
 texteff.cpp
 tgp.cpp
-thread.cpp
+#if HAVE_THREAD
+	#if WIN32
+		thread_win32.cpp
+		fiber_win32.cpp
+	#else
+		#if OS2
+			thread_os2.cpp
+		#else
+			thread_pthread.cpp
+		#end
+		fiber_thread.cpp
+	#end
+#else
+	thread_none.cpp
+#end
 tile_map.cpp
 #if WIN32
 #else
@@ -146,6 +160,7 @@
 engine_func.h
 engine_type.h
 core/enum_type.hpp
+fiber.hpp
 fileio.h
 fios.h
 fontcache.h
@@ -498,12 +513,14 @@
 misc/autoptr.hpp
 misc/binaryheap.hpp
 misc/blob.hpp
+misc/countedobj.cpp
 misc/countedptr.hpp
 misc/crc32.hpp
 misc/dbg_helpers.cpp
 misc/dbg_helpers.h
 misc/fixedsizearray.hpp
 misc/hashtable.hpp
+misc/smallveh.h
 misc/str.hpp
 misc/strapi.hpp
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/fiber.hpp	Wed Apr 16 22:34:14 2008 +0000
@@ -0,0 +1,53 @@
+/* $Id$ */
+
+/** @file fiber.hpp */
+
+#ifndef FIBER_HPP
+#define FIBER_HPP
+
+typedef void (CDECL *FiberFunc)(void *);
+
+class Fiber {
+public:
+	/**
+	 * Switch to this fiber.
+	 */
+	virtual void SwitchToFiber() = 0;
+
+	/**
+	 * Exit a fiber.
+	 */
+	virtual void Exit() = 0;
+
+	/**
+	 * Check if a fiber is running.
+	 */
+	virtual bool IsRunning() = 0;
+
+	/**
+	 * Get the 'param' data of the Fiber.
+	 */
+	virtual void *GetFiberData() = 0;
+
+	/**
+	 * Virtual Destructor to mute warnings.
+	 */
+	virtual ~Fiber() {};
+
+	/**
+	 * Create a new fiber, calling proc(param) when running.
+	 */
+	static Fiber *New(FiberFunc proc, void *param);
+
+	/**
+	 * Attach a current thread to a new fiber.
+	 */
+	static Fiber *AttachCurrent(void *param);
+
+	/**
+	 * Get the 'param' data of the current active Fiber.
+	 */
+	static void *GetCurrentFiberData();
+};
+
+#endif /* FIBER_HPP */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/fiber_thread.cpp	Wed Apr 16 22:34:14 2008 +0000
@@ -0,0 +1,153 @@
+/* $Id$ */
+
+/** @file fiber_thread.cpp ThreadObject implementation of Fiber. */
+
+#include "stdafx.h"
+#include "fiber.hpp"
+#include "thread.h"
+#include <stdlib.h>
+
+class Fiber_Thread : public Fiber {
+private:
+	ThreadObject *m_thread;
+	FiberFunc m_proc;
+	void *m_param;
+	bool m_attached;
+	ThreadSemaphore *m_sem;
+	bool m_kill;
+
+	static Fiber_Thread *s_current;
+	static Fiber_Thread *s_main;
+
+public:
+	/**
+	 * Create a ThreadObject fiber and start it, calling proc(param).
+	 */
+	Fiber_Thread(FiberFunc proc, void *param) :
+		m_thread(NULL),
+		m_proc(proc),
+		m_param(param),
+		m_attached(false),
+		m_kill(false)
+	{
+		this->m_sem = ThreadSemaphore::New();
+		/* Create a thread and start stFiberProc */
+		this->m_thread = ThreadObject::New(&stFiberProc, this);
+	}
+
+	/**
+	 * Create a ThreadObject fiber and attach current thread to it.
+	 */
+	Fiber_Thread(void *param) :
+		m_thread(NULL),
+		m_proc(NULL),
+		m_param(param),
+		m_attached(true),
+		m_kill(false)
+	{
+		this->m_sem = ThreadSemaphore::New();
+		/* Attach the current thread to this Fiber */
+		this->m_thread = ThreadObject::AttachCurrent();
+		/* We are the current thread */
+		if (s_current == NULL) s_current = this;
+		if (s_main == NULL) s_main = this;
+	}
+
+	~Fiber_Thread()
+	{
+		/* Remove the thread if needed */
+		if (this->m_thread != NULL) {
+			assert(this->m_attached || !this->m_thread->IsRunning());
+			delete this->m_thread;
+		}
+		/* Remove the semaphore */
+		delete this->m_sem;
+	}
+
+	/* virtual */ void SwitchToFiber()
+	{
+		/* You can't switch to yourself */
+		assert(s_current != this);
+		Fiber_Thread *cur = s_current;
+
+		/* Continue the execution of 'this' Fiber */
+		this->m_sem->Set();
+		/* Hold the execution of the current Fiber */
+		cur->m_sem->Wait();
+		if (this->m_kill) {
+			/* If the thread we switched too was killed, join it so it can finish quiting */
+			this->m_thread->Join();
+		}
+		/* If we continue, we are the current thread */
+		s_current = cur;
+	}
+
+	/* virtual */ void Exit()
+	{
+		/* Kill off our thread */
+		this->m_kill = true;
+		this->m_thread->Exit();
+	}
+
+	/* virtual */ bool IsRunning()
+	{
+		if (this->m_thread == NULL) return false;
+		return this->m_thread->IsRunning();
+	}
+
+	/* virtual */ void *GetFiberData()
+	{
+		return this->m_param;
+	}
+
+	static Fiber_Thread *GetCurrentFiber()
+	{
+		return s_current;
+	}
+
+private:
+	/**
+	 * First function which is called within the fiber.
+	 */
+	static void * CDECL stFiberProc(void *fiber)
+	{
+		Fiber_Thread *cur = (Fiber_Thread *)fiber;
+		/* Now suspend the thread until we get SwitchToFiber() for the first time */
+		cur->m_sem->Wait();
+		/* If we continue, we are the current thread */
+		s_current = cur;
+
+		try {
+			cur->m_proc(cur->m_param);
+		} catch (...) {
+			/* Unlock the main thread */
+			s_main->m_sem->Set();
+			throw;
+		}
+
+		return NULL;
+	}
+};
+
+/* Initialize the static member of Fiber_Thread */
+/* static */ Fiber_Thread *Fiber_Thread::s_current = NULL;
+/* static */ Fiber_Thread *Fiber_Thread::s_main = NULL;
+
+#ifndef WIN32
+
+/* static */ Fiber *Fiber::New(FiberFunc proc, void *param)
+{
+	return new Fiber_Thread(proc, param);
+}
+
+/* static */ Fiber *Fiber::AttachCurrent(void *param)
+{
+	return new Fiber_Thread(param);
+}
+
+/* static */ void *Fiber::GetCurrentFiberData()
+{
+	return Fiber_Thread::GetCurrentFiber()->GetFiberData();
+}
+
+#endif /* WIN32 */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/fiber_win32.cpp	Wed Apr 16 22:34:14 2008 +0000
@@ -0,0 +1,206 @@
+/* $Id$ */
+
+/** @file fiber_win32.cpp Win32 implementation of Fiber. */
+
+#include "stdafx.h"
+#include "fiber.hpp"
+#include <stdlib.h>
+#include <windows.h>
+#include <process.h>
+
+class Fiber_Win32 : public Fiber {
+private:
+	LPVOID m_fiber;
+	FiberFunc m_proc;
+	void *m_param;
+	bool m_attached;
+
+	static Fiber_Win32 *s_main;
+
+public:
+	/**
+	 * Create a win32 fiber and start it, calling proc(param).
+	 */
+	Fiber_Win32(FiberFunc proc, void *param) :
+		m_fiber(NULL),
+		m_proc(proc),
+		m_param(param),
+		m_attached(false)
+	{
+		CreateFiber();
+	}
+
+	/**
+	 * Create a win32 fiber and attach current thread to it.
+	 */
+	Fiber_Win32(void *param) :
+		m_fiber(NULL),
+		m_proc(NULL),
+		m_param(param),
+		m_attached(true)
+	{
+		ConvertThreadToFiber();
+		if (s_main == NULL) s_main = this;
+	}
+
+	/* virtual */ ~Fiber_Win32()
+	{
+		if (this->m_fiber != NULL) {
+			if (this->m_attached) {
+				this->ConvertFiberToThread();
+			} else {
+				this->DeleteFiber();
+			}
+		}
+	}
+
+	/* virtual */ void SwitchToFiber()
+	{
+		typedef VOID (WINAPI *FnSwitchToFiber)(LPVOID fiber);
+
+		static FnSwitchToFiber fnSwitchToFiber = (FnSwitchToFiber)stGetProcAddr("SwitchToFiber");
+		assert(fnSwitchToFiber != NULL);
+
+		fnSwitchToFiber(this->m_fiber);
+	}
+
+	/* virtual */ void Exit()
+	{
+		/* Simply switch back to the main fiber, we kill the fiber sooner or later */
+		s_main->SwitchToFiber();
+	}
+
+	/* virtual */ bool IsRunning()
+	{
+		return this->m_fiber != NULL;
+	}
+
+	/* virtual */ void *GetFiberData()
+	{
+		return this->m_param;
+	}
+
+	/**
+	 * Win95 doesn't have Fiber support. So check if we have Fiber support,
+	 *  and else fall back on Fiber_Thread.
+	 */
+	static bool IsSupported()
+	{
+		static bool first_run = true;
+		static bool is_supported = false;
+
+		if (first_run) {
+			first_run = false;
+			static const char *names[] = {
+				"ConvertThreadToFiber",
+				"CreateFiber",
+				"DeleteFiber",
+				"ConvertFiberToThread",
+				"SwitchToFiber"};
+			for (size_t i = 0; i < lengthof(names); i++) {
+				if (stGetProcAddr(names[i]) == NULL) return false;
+			}
+			is_supported = true;
+		}
+		return is_supported;
+	}
+
+private:
+	/**
+	 * Get a function from kernel32.dll.
+	 * @param name Function to get.
+	 * @return Proc to the function, or NULL when not found.
+	 */
+	static FARPROC stGetProcAddr(const char *name)
+	{
+		static HMODULE hKernel = LoadLibraryA("kernel32.dll");
+		return GetProcAddress(hKernel, name);
+	}
+
+	/**
+	 * First function which is called within the fiber.
+	 */
+	static VOID CALLBACK stFiberProc(LPVOID fiber)
+	{
+		Fiber_Win32 *cur = (Fiber_Win32 *)fiber;
+		cur->m_proc(cur->m_param);
+	}
+
+	/**
+	 * Delete a fiber.
+	 */
+	void DeleteFiber()
+	{
+		typedef VOID (WINAPI *FnDeleteFiber)(LPVOID lpFiber);
+
+		static FnDeleteFiber fnDeleteFiber = (FnDeleteFiber)stGetProcAddr("DeleteFiber");
+		assert(fnDeleteFiber != NULL);
+
+		fnDeleteFiber(this->m_fiber);
+		this->m_fiber = NULL;
+	}
+
+	/**
+	 * Convert a current thread to a fiber.
+	 */
+	void ConvertThreadToFiber()
+	{
+		typedef LPVOID (WINAPI *FnConvertThreadToFiber)(LPVOID lpParameter);
+
+		static FnConvertThreadToFiber fnConvertThreadToFiber = (FnConvertThreadToFiber)stGetProcAddr("ConvertThreadToFiber");
+		assert(fnConvertThreadToFiber != NULL);
+
+		this->m_fiber = fnConvertThreadToFiber(this);
+	}
+
+	/**
+	 * Create a new fiber.
+	 */
+	void CreateFiber()
+	{
+		typedef LPVOID (WINAPI *FnCreateFiber)(SIZE_T dwStackSize, LPFIBER_START_ROUTINE lpStartAddress, LPVOID lpParameter);
+
+		static FnCreateFiber fnCreateFiber = (FnCreateFiber)stGetProcAddr("CreateFiber");
+		assert(fnCreateFiber != NULL);
+
+		this->m_fiber = fnCreateFiber(0, &stFiberProc, this);
+	}
+
+	/**
+	 * Convert a fiber back to a thread.
+	 */
+	void ConvertFiberToThread()
+	{
+		typedef BOOL (WINAPI *FnConvertFiberToThread)();
+
+		static FnConvertFiberToThread fnConvertFiberToThread = (FnConvertFiberToThread)stGetProcAddr("ConvertFiberToThread");
+		assert(fnConvertFiberToThread != NULL);
+
+		fnConvertFiberToThread();
+		this->m_fiber = NULL;
+	}
+};
+
+/* Initialize the static member of Fiber_Win32 */
+/* static */ Fiber_Win32 *Fiber_Win32::s_main = NULL;
+
+/* Include Fiber_Thread, as Win95 needs it */
+#include "fiber_thread.cpp"
+
+/* static */ Fiber *Fiber::New(FiberFunc proc, void *param)
+{
+	if (Fiber_Win32::IsSupported()) return new Fiber_Win32(proc, param);
+	return new Fiber_Thread(proc, param);
+}
+
+/* static */ Fiber *Fiber::AttachCurrent(void *param)
+{
+	if (Fiber_Win32::IsSupported()) return new Fiber_Win32(param);
+	return new Fiber_Thread(param);
+}
+
+/* static */ void *Fiber::GetCurrentFiberData()
+{
+	if (Fiber_Win32::IsSupported()) return ((Fiber *)::GetFiberData())->GetFiberData();
+	return Fiber_Thread::GetCurrentFiber()->GetFiberData();
+}
--- a/src/genworld.cpp	Wed Apr 16 22:16:04 2008 +0000
+++ b/src/genworld.cpp	Wed Apr 16 22:34:14 2008 +0000
@@ -84,7 +84,7 @@
 /**
  * The internal, real, generate function.
  */
-static void *_GenerateWorld(void *arg)
+static void * CDECL _GenerateWorld(void *arg)
 {
 	_generating_world = true;
 	if (_network_dedicated) DEBUG(net, 0, "Generating map, please wait...");
@@ -194,7 +194,7 @@
 {
 	if (_gw.thread == NULL) return;
 	_gw.quit_thread = true;
-	OTTDJoinThread((OTTDThread*)_gw.thread);
+	_gw.thread->Join();
 	_gw.thread   = NULL;
 	_gw.threaded = false;
 }
@@ -228,6 +228,8 @@
 	if (_cursor.sprite == SPR_CURSOR_ZZZ) SetMouseCursor(SPR_CURSOR_MOUSE, PAL_NONE);
 	/* Show all vital windows again, because we have hidden them */
 	if (_gw.threaded && _game_mode != GM_MENU) ShowVitalWindows();
+
+	ThreadObject *thread = _gw.thread;
 	_gw.active   = false;
 	_gw.thread   = NULL;
 	_gw.proc     = NULL;
@@ -237,7 +239,7 @@
 	DeleteWindowById(WC_GENERATE_PROGRESS_WINDOW, 0);
 	MarkWholeScreenDirty();
 
-	OTTDExitThread();
+	thread->Exit();
 }
 
 /**
@@ -282,7 +284,7 @@
 	SetupColorsAndInitialWindow();
 
 	if (_network_dedicated ||
-	    (_gw.thread = OTTDCreateThread(&_GenerateWorld, NULL)) == NULL) {
+	    (_gw.thread = ThreadObject::New(&_GenerateWorld, NULL)) == NULL) {
 		DEBUG(misc, 1, "Cannot create genworld thread, reverting to single-threaded mode");
 		_gw.threaded = false;
 		_GenerateWorld(NULL);
--- a/src/genworld.h	Wed Apr 16 22:16:04 2008 +0000
+++ b/src/genworld.h	Wed Apr 16 22:34:14 2008 +0000
@@ -5,16 +5,6 @@
 #ifndef GENWORLD_H
 #define GENWORLD_H
 
-/* If OTTDThread isn't defined, define it to a void, but make sure to undefine
- *  it after this include. This makes including genworld.h easier, as you
- *  don't need to include thread.h before it, while it stays possible to
- *  include it after it, and still work.
- */
-#ifndef OTTDThread
-#define TEMPORARY_OTTDTHREAD_DEFINITION
-#define OTTDThread void
-#endif
-
 #include "player_type.h"
 
 /*
@@ -43,14 +33,9 @@
 	uint size_y;           ///< Y-size of the map
 	gw_done_proc *proc;    ///< Proc that is called when done (can be NULL)
 	gw_abort_proc *abortp; ///< Proc that is called when aborting (can be NULL)
-	OTTDThread *thread;    ///< The thread we are in (can be NULL)
+	class ThreadObject *thread; ///< The thread we are in (can be NULL)
 };
 
-#ifdef TEMPORARY_OTTDTHREAD_DEFINITION
-#undef OTTDThread
-#undef TEMPORARY_OTTDTHREAD_DEFINITION
-#endif
-
 enum gwp_class {
 	GWP_MAP_INIT,    ///< Initialize/allocate the map, start economy
 	GWP_LANDSCAPE,   ///< Create the landscape
--- a/src/group_cmd.cpp	Wed Apr 16 22:16:04 2008 +0000
+++ b/src/group_cmd.cpp	Wed Apr 16 22:34:14 2008 +0000
@@ -39,7 +39,7 @@
 		/* Decrease the num engines of EngineID i of the old group if it's not the default one */
 		if (!IsDefaultGroupID(old_g) && IsValidGroupID(old_g)) GetGroup(old_g)->num_engines[i]--;
 
-		/* Increase the num engines of EngineID i of the new group if it's not the new one */
+		/* Increase the num engines of EngineID i of the new group if it's not the default one */
 		if (!IsDefaultGroupID(new_g) && IsValidGroupID(new_g)) GetGroup(new_g)->num_engines[i]++;
 	}
 }
--- a/src/lang/english.txt	Wed Apr 16 22:16:04 2008 +0000
+++ b/src/lang/english.txt	Wed Apr 16 22:34:14 2008 +0000
@@ -1363,6 +1363,8 @@
 STR_NETWORK_GAME_NAME_TIP                                       :{BLACK}Name of the game
 STR_NETWORK_INFO_ICONS_TIP                                      :{BLACK}Language, server version, etc.
 STR_NETWORK_CLICK_GAME_TO_SELECT                                :{BLACK}Click a game from the list to select it
+STR_NETWORK_LAST_JOINED_SERVER                                  :{BLACK}The server you joined last time:
+STR_NETWORK_CLICK_TO_SELECT_LAST                                :{BLACK}Click to select the server you played last time
 
 STR_NETWORK_FIND_SERVER                                         :{BLACK}Find server
 STR_NETWORK_FIND_SERVER_TIP                                     :{BLACK}Search network for a server
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/misc/smallvec.h	Wed Apr 16 22:34:14 2008 +0000
@@ -0,0 +1,64 @@
+/* $Id$ */
+
+/* @file smallvec.h */
+
+#ifndef SMALLVEC_H
+#define SMALLVEC_H
+
+template <typename T, uint S> struct SmallVector {
+	T *data;
+	uint items;
+	uint capacity;
+
+	SmallVector() : data(NULL), items(0), capacity(0) { }
+
+	~SmallVector()
+	{
+		free(data);
+	}
+
+	/**
+	 * Append an item and return it.
+	 */
+	T *Append()
+	{
+		if (items == capacity) {
+			capacity += S;
+			data = ReallocT(data, capacity);
+		}
+
+		return &data[items++];
+	}
+
+	const T *Begin() const
+	{
+		return data;
+	}
+
+	T *Begin()
+	{
+		return data;
+	}
+
+	const T *End() const
+	{
+		return &data[items];
+	}
+
+	T *End()
+	{
+		return &data[items];
+	}
+
+	const T *Get(size_t index) const
+	{
+		return &data[index];
+	}
+
+	T *Get(size_t index)
+	{
+		return &data[index];
+	}
+};
+
+#endif /* SMALLVEC_H */
--- a/src/network/network_gamelist.cpp	Wed Apr 16 22:16:04 2008 +0000
+++ b/src/network/network_gamelist.cpp	Wed Apr 16 22:34:14 2008 +0000
@@ -25,6 +25,8 @@
  * @return a point to the newly added or already existing item */
 NetworkGameList *NetworkGameListAddItem(uint32 ip, uint16 port)
 {
+	if (ip == 0) return NULL;
+
 	NetworkGameList *item, *prev_item;
 
 	prev_item = NULL;
--- a/src/network/network_gui.cpp	Wed Apr 16 22:16:04 2008 +0000
+++ b/src/network/network_gui.cpp	Wed Apr 16 22:34:14 2008 +0000
@@ -233,6 +233,9 @@
 	NGWW_MATRIX,        ///< Panel with list of games
 	NGWW_SCROLLBAR,     ///< Scrollbar of matrix
 
+	NGWW_LASTJOINED_LABEL, ///< Label "Last joined server:"
+	NGWW_LASTJOINED,    ///< Info about the last joined server
+
 	NGWW_DETAILS,       ///< Panel with game details
 	NGWW_JOIN,          ///< 'Join game' button
 	NGWW_REFRESH,       ///< 'Refresh server' button
@@ -245,6 +248,39 @@
 };
 
 /**
+ * Draw a single server line.
+ * @param cur_item  the server to draw.
+ * @param y         from where to draw?
+ * @param highlight does the line need to be highlighted?
+ */
+static void DrawServerLine(const Window *w, const NetworkGameList *cur_item, uint y, bool highlight)
+{
+	/* show highlighted item with a different colour */
+	if (highlight) GfxFillRect(w->widget[NGWW_NAME].left + 1, y - 2, w->widget[NGWW_INFO].right - 1, y + 9, 10);
+
+	SetDParamStr(0, cur_item->info.server_name);
+	DrawStringTruncated(w->widget[NGWW_NAME].left + 5, y, STR_02BD, TC_BLACK, w->widget[NGWW_NAME].right - w->widget[NGWW_NAME].left - 5);
+
+	SetDParam(0, cur_item->info.clients_on);
+	SetDParam(1, cur_item->info.clients_max);
+	SetDParam(2, cur_item->info.companies_on);
+	SetDParam(3, cur_item->info.companies_max);
+	DrawStringCentered(w->widget[NGWW_CLIENTS].left + 39, y, STR_NETWORK_GENERAL_ONLINE, TC_GOLD);
+
+	/* only draw icons if the server is online */
+	if (cur_item->online) {
+		/* draw a lock if the server is password protected */
+		if (cur_item->info.use_password) DrawSprite(SPR_LOCK, PAL_NONE, w->widget[NGWW_INFO].left + 5, y - 1);
+
+		/* draw red or green icon, depending on compatibility with server */
+		DrawSprite(SPR_BLOT, (cur_item->info.compatible ? PALETTE_TO_GREEN : (cur_item->info.version_compatible ? PALETTE_TO_YELLOW : PALETTE_TO_RED)), w->widget[NGWW_INFO].left + 15, y);
+
+		/* draw flag according to server language */
+		DrawSprite(SPR_FLAGS_BASE + cur_item->info.server_lang, PAL_NONE, w->widget[NGWW_INFO].left + 25, y);
+	}
+}
+
+/**
  * Handler of actions done in the NetworkStartServer window
  *
  * @param w pointer to the Window structure
@@ -261,7 +297,7 @@
 
 	switch (e->event) {
 		case WE_CREATE: // Focus input box
-			w->vscroll.cap = 13;
+			w->vscroll.cap = 11;
 			w->resize.step_height = NET_PRC__SIZE_OF_ROW;
 
 			nd->field = NGWW_PLAYER;
@@ -314,7 +350,6 @@
 			uint16 y = NET_PRC__OFFSET_TOP_WIDGET + 3;
 			int32 n = 0;
 			int32 pos = w->vscroll.pos;
-			uint max_name_width = w->widget[NGWW_NAME].right - w->widget[NGWW_NAME].left - 5;
 			const NetworkGameList *cur_item = _network_game_list;
 
 			while (pos > 0 && cur_item != NULL) {
@@ -323,35 +358,17 @@
 			}
 
 			while (cur_item != NULL) {
-				/* show highlighted item with a different colour */
-				if (cur_item == sel) GfxFillRect(w->widget[NGWW_NAME].left + 1, y - 2, w->widget[NGWW_INFO].right - 1, y + 9, 10);
-
-				SetDParamStr(0, cur_item->info.server_name);
-				DrawStringTruncated(w->widget[NGWW_NAME].left + 5, y, STR_02BD, TC_BLACK, max_name_width);
-
-				SetDParam(0, cur_item->info.clients_on);
-				SetDParam(1, cur_item->info.clients_max);
-				SetDParam(2, cur_item->info.companies_on);
-				SetDParam(3, cur_item->info.companies_max);
-				DrawStringCentered(w->widget[NGWW_CLIENTS].left + 39, y, STR_NETWORK_GENERAL_ONLINE, TC_GOLD);
-
-				/* only draw icons if the server is online */
-				if (cur_item->online) {
-					/* draw a lock if the server is password protected */
-					if (cur_item->info.use_password) DrawSprite(SPR_LOCK, PAL_NONE, w->widget[NGWW_INFO].left + 5, y - 1);
-
-					/* draw red or green icon, depending on compatibility with server */
-					DrawSprite(SPR_BLOT, (cur_item->info.compatible ? PALETTE_TO_GREEN : (cur_item->info.version_compatible ? PALETTE_TO_YELLOW : PALETTE_TO_RED)), w->widget[NGWW_INFO].left + 15, y);
-
-					/* draw flag according to server language */
-					DrawSprite(SPR_FLAGS_BASE + cur_item->info.server_lang, PAL_NONE, w->widget[NGWW_INFO].left + 25, y);
-				}
+				DrawServerLine(w, cur_item, y, cur_item == sel);
 
 				cur_item = cur_item->next;
 				y += NET_PRC__SIZE_OF_ROW;
 				if (++n == w->vscroll.cap) break; // max number of games in the window
 			}
 
+			const NetworkGameList *last_joined = NetworkGameListAddItem(inet_addr(_network_last_host), _network_last_port);
+			/* Draw the last joined server, if any */
+			if (last_joined != NULL) DrawServerLine(w, last_joined, y = w->widget[NGWW_LASTJOINED].top + 3, last_joined == sel);
+
 			/* Draw the right menu */
 			GfxFillRect(w->widget[NGWW_DETAILS].left + 1, 43, w->widget[NGWW_DETAILS].right - 1, 92, 157);
 			if (sel == NULL) {
@@ -467,6 +484,14 @@
 					SetWindowDirty(w);
 				} break;
 
+				case NGWW_LASTJOINED: {
+					NetworkGameList *last_joined = NetworkGameListAddItem(inet_addr(_network_last_host), _network_last_port);
+					if (last_joined != NULL) {
+						nd->server = last_joined;
+						SetWindowDirty(w);
+					}
+				} break;
+
 				case NGWW_FIND: // Find server automatically
 					switch (_network_lan_internet) {
 						case 0: NetworkUDPSearchGame(); break;
@@ -597,8 +622,10 @@
 { WWT_PUSHTXTBTN,   RESIZE_LR,     BTC,    71,   150,    42,    53, STR_NETWORK_CLIENTS_CAPTION,      STR_NETWORK_CLIENTS_CAPTION_TIP},  // NGWW_CLIENTS
 { WWT_PUSHTXTBTN,   RESIZE_LR,     BTC,   151,   190,    42,    53, STR_EMPTY,                        STR_NETWORK_INFO_ICONS_TIP},       // NGWW_INFO
 
-{     WWT_MATRIX,   RESIZE_RB,     BGC,    10,   190,    54,   236, (11 << 8) + 1,                    STR_NETWORK_CLICK_GAME_TO_SELECT}, // NGWW_MATRIX
-{  WWT_SCROLLBAR,   RESIZE_LRB,    BGC,   191,   202,    42,   236, 0x0,                              STR_0190_SCROLL_BAR_SCROLLS_LIST}, // NGWW_SCROLLBAR
+{     WWT_MATRIX,   RESIZE_RB,     BGC,    10,   190,    54,   208, (11 << 8) + 1,                    STR_NETWORK_CLICK_GAME_TO_SELECT}, // NGWW_MATRIX
+{  WWT_SCROLLBAR,   RESIZE_LRB,    BGC,   191,   202,    42,   208, 0x0,                              STR_0190_SCROLL_BAR_SCROLLS_LIST}, // NGWW_SCROLLBAR
+{       WWT_TEXT,   RESIZE_RTB,    BGC,    10,   190,   211,   222, STR_NETWORK_LAST_JOINED_SERVER,   STR_NULL},                         // NGWW_LASTJOINED_LABEL
+{      WWT_PANEL,   RESIZE_RTB,    BGC,    10,   190,   223,   236, 0x0,                              STR_NETWORK_CLICK_TO_SELECT_LAST}, // NGWW_LASTJOINED
 
 /* RIGHT SIDE */
 {      WWT_PANEL,   RESIZE_LRB,    BGC,   210,   440,    42,   236, 0x0,                              STR_NULL},                         // NGWW_DETAILS
--- a/src/newgrf_town.cpp	Wed Apr 16 22:16:04 2008 +0000
+++ b/src/newgrf_town.cpp	Wed Apr 16 22:34:14 2008 +0000
@@ -36,16 +36,16 @@
 		case 0x8A: return t->grow_counter;
 		case 0x92: return t->flags12;  // In original game, 0x92 and 0x93 are really one word. Since flags12 is a byte, this is to adjust
 		case 0x93: return 0;
-		case 0x94: return t->radius[0];
-		case 0x95: return GB(t->radius[0], 8, 8);
-		case 0x96: return t->radius[1];
-		case 0x97: return GB(t->radius[1], 8, 8);
-		case 0x98: return t->radius[2];
-		case 0x99: return GB(t->radius[2], 8, 8);
-		case 0x9A: return t->radius[3];
-		case 0x9B: return GB(t->radius[3], 8, 8);
-		case 0x9C: return t->radius[4];
-		case 0x9D: return GB(t->radius[4], 8, 8);
+		case 0x94: return ClampToU16(t->squared_town_zone_radius[0]);
+		case 0x95: return GB(ClampToU16(t->squared_town_zone_radius[0]), 8, 8);
+		case 0x96: return ClampToU16(t->squared_town_zone_radius[1]);
+		case 0x97: return GB(ClampToU16(t->squared_town_zone_radius[1]), 8, 8);
+		case 0x98: return ClampToU16(t->squared_town_zone_radius[2]);
+		case 0x99: return GB(ClampToU16(t->squared_town_zone_radius[2]), 8, 8);
+		case 0x9A: return ClampToU16(t->squared_town_zone_radius[3]);
+		case 0x9B: return GB(ClampToU16(t->squared_town_zone_radius[3]), 8, 8);
+		case 0x9C: return ClampToU16(t->squared_town_zone_radius[4]);
+		case 0x9D: return GB(ClampToU16(t->squared_town_zone_radius[4]), 8, 8);
 		case 0x9E: return t->ratings[0];
 		case 0x9F: return GB(t->ratings[0], 8, 8);
 		case 0xA0: return t->ratings[1];
--- a/src/order_cmd.cpp	Wed Apr 16 22:16:04 2008 +0000
+++ b/src/order_cmd.cpp	Wed Apr 16 22:34:14 2008 +0000
@@ -1614,7 +1614,7 @@
 	}
 
 	/* Check if we've reached a non-stop station.. */
-	if ((v->current_order.GetNonStopType() & ONSF_NO_STOP_AT_DESTINATION_STATION) &&
+	if (v->current_order.IsType(OT_GOTO_STATION) && (v->current_order.GetNonStopType() & ONSF_NO_STOP_AT_DESTINATION_STATION) &&
 			IsTileType(v->tile, MP_STATION) &&
 			v->current_order.GetDestination() == GetStationIndex(v->tile)) {
 		v->last_station_visited = v->current_order.GetDestination();
@@ -1742,10 +1742,11 @@
  */
 bool Order::ShouldStopAtStation(const Vehicle *v, StationID station) const
 {
+	bool is_dest_station = this->IsType(OT_GOTO_STATION) && this->dest == station;
 	return
 			v->last_station_visited != station && // Do stop only when we've not just been there
 			/* Finally do stop when there is no non-stop flag set for this type of station. */
-			!(this->GetNonStopType() & ((this->dest == station) ? ONSF_NO_STOP_AT_DESTINATION_STATION : ONSF_NO_STOP_AT_INTERMEDIATE_STATIONS));
+			!(this->GetNonStopType() & (is_dest_station ? ONSF_NO_STOP_AT_DESTINATION_STATION : ONSF_NO_STOP_AT_INTERMEDIATE_STATIONS));
 }
 
 void InitializeOrders()
--- a/src/saveload.cpp	Wed Apr 16 22:16:04 2008 +0000
+++ b/src/saveload.cpp	Wed Apr 16 22:34:14 2008 +0000
@@ -76,13 +76,24 @@
 
 enum NeedLengthValues {NL_NONE = 0, NL_WANTLENGTH = 1, NL_CALCLENGTH = 2};
 
+/** Error handler, calls longjmp to simulate an exception.
+ * @todo this was used to have a central place to handle errors, but it is
+ * pretty ugly, and seriously interferes with any multithreaded approaches */
+static void NORETURN SlError(StringID string, const char *extra_msg = NULL)
+{
+	_sl.error_str = string;
+	free(_sl.extra_msg);
+	_sl.extra_msg = (extra_msg == NULL) ? NULL : strdup(extra_msg);
+	throw std::exception();
+}
+
 /**
  * Fill the input buffer by reading from the file with the given reader
  */
 static void SlReadFill()
 {
 	uint len = _sl.read_bytes();
-	assert(len != 0);
+	if (len == 0) SlError(STR_GAME_SAVELOAD_ERROR_BROKEN_SAVEGAME, "Unexpected end of chunk");
 
 	_sl.bufp = _sl.buf;
 	_sl.bufe = _sl.buf + len;
@@ -137,17 +148,6 @@
 	_sl.bufe = _sl.buf + _sl.bufsize;
 }
 
-/** Error handler, calls longjmp to simulate an exception.
- * @todo this was used to have a central place to handle errors, but it is
- * pretty ugly, and seriously interferes with any multithreaded approaches */
-static void NORETURN SlError(StringID string, const char *extra_msg = NULL)
-{
-	_sl.error_str = string;
-	free(_sl.extra_msg);
-	_sl.extra_msg = (extra_msg == NULL) ? NULL : strdup(extra_msg);
-	throw std::exception();
-}
-
 /** Read in a single byte from file. If the temporary buffer is full,
  * flush it to its final destination
  * @return return the read byte from file
@@ -1499,7 +1499,7 @@
 	SaveFileDone();
 }
 
-static OTTDThread* save_thread;
+static ThreadObject *save_thread;
 
 /** We have written the whole game into memory, _Savegame_pool, now find
  * and appropiate compressor and start writing to file.
@@ -1524,7 +1524,7 @@
 			uint i;
 			uint count = 1 << Savegame_POOL_BLOCK_SIZE_BITS;
 
-			assert(_ts.count == _sl.offs_base);
+			if (_ts.count != _sl.offs_base) SlError(STR_GAME_SAVELOAD_ERROR_BROKEN_SAVEGAME, "Unexpected size of chunk");
 			for (i = 0; i != _Savegame_pool.GetBlockCount() - 1; i++) {
 				_sl.buf = _Savegame_pool.blocks[i];
 				fmt->writer(count);
@@ -1537,7 +1537,7 @@
 		}
 
 		fmt->uninit_write();
-		assert(_ts.count == _sl.offs_base);
+		if (_ts.count != _sl.offs_base) SlError(STR_GAME_SAVELOAD_ERROR_BROKEN_SAVEGAME, "Unexpected size of chunk");
 		GetSavegameFormat("memory")->uninit_write(); // clean the memorypool
 		fclose(_sl.fh);
 
@@ -1561,7 +1561,7 @@
 	}
 }
 
-static void* SaveFileToDiskThread(void *arg)
+static void * CDECL SaveFileToDiskThread(void *arg)
 {
 	SaveFileToDisk(true);
 	return NULL;
@@ -1569,7 +1569,9 @@
 
 void WaitTillSaved()
 {
-	OTTDJoinThread(save_thread);
+	if (save_thread == NULL) return;
+
+	save_thread->Join();
 	save_thread = NULL;
 }
 
@@ -1641,7 +1643,7 @@
 
 			SaveFileStart();
 			if (_network_server ||
-						(save_thread = OTTDCreateThread(&SaveFileToDiskThread, NULL)) == NULL) {
+						(save_thread = ThreadObject::New(&SaveFileToDiskThread, NULL)) == NULL) {
 				if (!_network_server) DEBUG(sl, 1, "Cannot create savegame thread, reverting to single-threaded mode...");
 
 				SaveOrLoadResult result = SaveFileToDisk(false);
--- a/src/settings.cpp	Wed Apr 16 22:16:04 2008 +0000
+++ b/src/settings.cpp	Wed Apr 16 22:34:14 2008 +0000
@@ -1339,6 +1339,8 @@
 	  SDTG_VAR("min_players",          SLE_UINT8, S, 0, _network_min_players,               0, 0, 10,   0, STR_NULL, NULL),
 	SDTG_OMANY("server_lang",          SLE_UINT8, S, 0, _network_game_info.server_lang,     0, 35, "ANY|ENGLISH|GERMAN|FRENCH|BRAZILIAN|BULGARIAN|CHINESE|CZECH|DANISH|DUTCH|ESPERANTO|FINNISH|HUNGARIAN|ICELANDIC|ITALIAN|JAPANESE|KOREAN|LITHUANIAN|NORWEGIAN|POLISH|PORTUGUESE|ROMANIAN|RUSSIAN|SLOVAK|SLOVENIAN|SPANISH|SWEDISH|TURKISH|UKRAINIAN|AFRIKAANS|CROATIAN|CATALAN|ESTONIAN|GALICIAN|GREEK|LATVIAN", STR_NULL, NULL),
 	 SDTG_BOOL("reload_cfg",                      S, 0, _network_reload_cfg,           false,              STR_NULL, NULL),
+	  SDTG_STR("last_host",             SLE_STRB, S, 0, _network_last_host,            "0.0.0.0",          STR_NULL, NULL),
+	  SDTG_VAR("last_port",           SLE_UINT16, S, 0, _network_last_port,            0, 0, UINT16_MAX, 0, STR_NULL ,NULL),
 	  SDTG_END()
 };
 #endif /* ENABLE_NETWORK */
--- a/src/sound/win32_s.cpp	Wed Apr 16 22:16:04 2008 +0000
+++ b/src/sound/win32_s.cpp	Wed Apr 16 22:34:14 2008 +0000
@@ -59,7 +59,7 @@
 	wfex.nBlockAlign = (wfex.nChannels * wfex.wBitsPerSample) / 8;
 	wfex.nAvgBytesPerSec = wfex.nSamplesPerSec * wfex.nBlockAlign;
 
-	_bufsize = GetDriverParamInt(parm, "bufsize", 2048);
+	_bufsize = GetDriverParamInt(parm, "bufsize", (GB(GetVersion(), 0, 8) > 5) ? 2048 : 1024);
 
 	if (waveOutOpen(&_waveout, WAVE_MAPPER, &wfex, (DWORD_PTR)&waveOutProc, 0, CALLBACK_FUNCTION) != MMSYSERR_NOERROR)
 		return "waveOutOpen failed";
--- a/src/thread.cpp	Wed Apr 16 22:16:04 2008 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,313 +0,0 @@
-/* $Id$ */
-
-/** @file thread.cpp */
-
-#include "stdafx.h"
-#include "thread.h"
-#include "core/alloc_func.hpp"
-#include <stdlib.h>
-
-#if defined(__AMIGA__) || defined(PSP) || defined(NO_THREADS)
-OTTDThread *OTTDCreateThread(OTTDThreadFunc function, void *arg) { return NULL; }
-void *OTTDJoinThread(OTTDThread *t) { return NULL; }
-void OTTDExitThread() { NOT_REACHED(); };
-
-#elif defined(__OS2__)
-
-#define INCL_DOS
-#include <os2.h>
-#include <process.h>
-
-struct OTTDThread {
-	TID thread;
-	OTTDThreadFunc func;
-	void* arg;
-	void* ret;
-};
-
-static void Proxy(void* arg)
-{
-	OTTDThread* t = (OTTDThread*)arg;
-	t->ret = t->func(t->arg);
-}
-
-OTTDThread* OTTDCreateThread(OTTDThreadFunc function, void* arg)
-{
-	OTTDThread* t = MallocT<OTTDThread>(1);
-
-	if (t == NULL) return NULL;
-
-	t->func = function;
-	t->arg  = arg;
-	t->thread = _beginthread(Proxy, NULL, 32768, t);
-	if (t->thread != (TID)-1) {
-		return t;
-	} else {
-		free(t);
-		return NULL;
-	}
-}
-
-void* OTTDJoinThread(OTTDThread* t)
-{
-	void* ret;
-
-	if (t == NULL) return NULL;
-
-	DosWaitThread(&t->thread, DCWW_WAIT);
-	ret = t->ret;
-	free(t);
-	return ret;
-}
-
-void OTTDExitThread()
-{
-	_endthread();
-}
-
-#elif defined(UNIX) && !defined(MORPHOS)
-
-#include <pthread.h>
-
-struct OTTDThread {
-	pthread_t thread;
-};
-
-OTTDThread* OTTDCreateThread(OTTDThreadFunc function, void* arg)
-{
-	OTTDThread* t = MallocT<OTTDThread>(1);
-
-	if (t == NULL) return NULL;
-
-	if (pthread_create(&t->thread, NULL, function, arg) == 0) {
-		return t;
-	} else {
-		free(t);
-		return NULL;
-	}
-}
-
-void* OTTDJoinThread(OTTDThread* t)
-{
-	void* ret;
-
-	if (t == NULL) return NULL;
-
-	pthread_join(t->thread, &ret);
-	free(t);
-	return ret;
-}
-
-void OTTDExitThread()
-{
-	pthread_exit(NULL);
-}
-
-#elif defined(WIN32)
-
-#include <windows.h>
-
-struct OTTDThread {
-	HANDLE thread;
-	OTTDThreadFunc func;
-	void* arg;
-	void* ret;
-};
-
-static DWORD WINAPI Proxy(LPVOID arg)
-{
-	OTTDThread* t = (OTTDThread*)arg;
-	t->ret = t->func(t->arg);
-	return 0;
-}
-
-OTTDThread* OTTDCreateThread(OTTDThreadFunc function, void* arg)
-{
-	OTTDThread* t = MallocT<OTTDThread>(1);
-	DWORD dwThreadId;
-
-	if (t == NULL) return NULL;
-
-	t->func = function;
-	t->arg  = arg;
-	t->thread = CreateThread(NULL, 0, Proxy, t, 0, &dwThreadId);
-
-	if (t->thread != NULL) {
-		return t;
-	} else {
-		free(t);
-		return NULL;
-	}
-}
-
-void* OTTDJoinThread(OTTDThread* t)
-{
-	void* ret;
-
-	if (t == NULL) return NULL;
-
-	WaitForSingleObject(t->thread, INFINITE);
-	CloseHandle(t->thread);
-	ret = t->ret;
-	free(t);
-	return ret;
-}
-
-void OTTDExitThread()
-{
-	ExitThread(0);
-}
-
-
-#elif defined(MORPHOS)
-
-#include <exec/types.h>
-#include <exec/rawfmt.h>
-#include <dos/dostags.h>
-
-#include <proto/dos.h>
-#include <proto/exec.h>
-
-#include <setjmp.h>
-
-/* NOTE: this code heavily depends on latest libnix updates. So make
- *        sure you link with new stuff which supports semaphore locking of
- *        the IO resources, else it will just go foobar. */
-
-struct OTTDThreadStartupMessage {
-	struct Message msg;  ///< standard exec.library message (MUST be the first thing in the message struct!)
-	OTTDThreadFunc func; ///< function the thread will execute
-	void *arg;           ///< functions arguments for the thread function
-	void *ret;           ///< return value of the thread function
-	jmp_buf jumpstore;   ///< storage for the setjump state
-};
-
-struct OTTDThread {
-	struct MsgPort *replyport;
-	struct OTTDThreadStartupMessage msg;
-};
-
-
-/**
- *  Default OpenTTD STDIO/ERR debug output is not very useful for this, so we
- *  utilize serial/ramdebug instead.
- */
-#ifndef NO_DEBUG_MESSAGES
-void KPutStr(CONST_STRPTR format)
-{
-	RawDoFmt(format, NULL, (void (*)())RAWFMTFUNC_SERIAL, NULL);
-}
-#else
-#define KPutStr(x)
-#endif
-
-static void Proxy(void)
-{
-	struct Task *child = FindTask(NULL);
-	struct OTTDThreadStartupMessage *msg;
-
-	/* Make sure, we don't block the parent. */
-	SetTaskPri(child, -5);
-
-	KPutStr("[Child] Progressing...\n");
-
-	if (NewGetTaskAttrs(NULL, &msg, sizeof(struct OTTDThreadStartupMessage *), TASKINFOTYPE_STARTUPMSG, TAG_DONE) && msg != NULL) {
-		/* Make use of setjmp() here, so this point can be reached again from inside
-		 *  OTTDExitThread() which can be called from anythere inside msg->func.
-		 *  It's a bit ugly and in worst case it leaks some memory. */
-		if (setjmp(msg->jumpstore) == 0) {
-			msg->ret = msg->func(msg->arg);
-		} else {
-			KPutStr("[Child] Returned to main()\n");
-		}
-	}
-
-	/*  Quit the child, exec.library will reply the startup msg internally. */
-	KPutStr("[Child] Done.\n");
-}
-
-OTTDThread* OTTDCreateThread(OTTDThreadFunc function, void *arg)
-{
-	OTTDThread *t;
-	struct Task *parent;
-
-	KPutStr("[OpenTTD] Create thread...\n");
-
-	t = (struct OTTDThread *)AllocVecTaskPooled(sizeof(struct OTTDThread));
-	if (t == NULL) return NULL;
-
-	parent = FindTask(NULL);
-
-	/* Make sure main thread runs with sane priority */
-	SetTaskPri(parent, 0);
-
-	/* Things we'll pass down to the child by utilizing NP_StartupMsg */
-	t->msg.func = function;
-	t->msg.arg  = arg;
-	t->msg.ret  = NULL;
-
-	t->replyport = CreateMsgPort();
-
-	if (t->replyport != NULL) {
-		struct Process *child;
-
-		t->msg.msg.mn_Node.ln_Type = NT_MESSAGE;
-		t->msg.msg.mn_ReplyPort    = t->replyport;
-		t->msg.msg.mn_Length       = sizeof(struct OTTDThreadStartupMessage);
-
-		child = CreateNewProcTags(
-			NP_CodeType,     CODETYPE_PPC,
-			NP_Entry,        Proxy,
-			NP_StartupMsg,   (ULONG)&t->msg,
-			NP_Priority,     5UL,
-			NP_Name,         (ULONG)"OpenTTD Thread",
-			NP_PPCStackSize, 131072UL,
-			TAG_DONE);
-
-		if (child != NULL) {
-			KPutStr("[OpenTTD] Child process launched.\n");
-			return t;
-		}
-		DeleteMsgPort(t->replyport);
-	}
-	FreeVecTaskPooled(t);
-
-	return NULL;
-}
-
-void* OTTDJoinThread(OTTDThread *t)
-{
-	struct OTTDThreadStartupMessage *reply;
-	void *ret;
-
-	KPutStr("[OpenTTD] Join threads...\n");
-
-	if (t == NULL) return NULL;
-
-	KPutStr("[OpenTTD] Wait for child to quit...\n");
-	WaitPort(t->replyport);
-
-	reply = (struct OTTDThreadStartupMessage *)GetMsg(t->replyport);
-	ret   = reply->ret;
-
-	DeleteMsgPort(t->replyport);
-	FreeVecTaskPooled(t);
-
-	return ret;
-}
-
-void OTTDExitThread()
-{
-	struct OTTDThreadStartupMessage *msg;
-
-	KPutStr("[Child] Aborting...\n");
-
-	if (NewGetTaskAttrs(NULL, &msg, sizeof(struct OTTDThreadStartupMessage *), TASKINFOTYPE_STARTUPMSG, TAG_DONE) && msg != NULL) {
-		KPutStr("[Child] Jumping back...\n");
-		longjmp(msg->jumpstore, 0xBEAFCAFE);
-	}
-
-	NOT_REACHED();
-}
-
-#endif
--- a/src/thread.h	Wed Apr 16 22:16:04 2008 +0000
+++ b/src/thread.h	Wed Apr 16 22:34:14 2008 +0000
@@ -5,12 +5,95 @@
 #ifndef THREAD_H
 #define THREAD_H
 
-struct OTTDThread;
+typedef void * (CDECL *OTTDThreadFunc)(void *);
 
-typedef void * (*OTTDThreadFunc)(void*);
+/**
+ * A Thread Object which works on all our supported OSes.
+ */
+class ThreadObject {
+public:
+	/**
+	 * Virtual destructor to allow 'delete' operator to work properly.
+	 */
+	virtual ~ThreadObject() {};
 
-OTTDThread *OTTDCreateThread(OTTDThreadFunc, void*);
-void       *OTTDJoinThread(OTTDThread*);
-void        OTTDExitThread();
+	/**
+	 * Check if the thread is currently running.
+	 * @return True if the thread is running.
+	 */
+	virtual bool IsRunning() = 0;
+
+	/**
+	 * Waits for the thread to exit.
+	 * @return True if the thread has exited.
+	 */
+	virtual bool WaitForStop() = 0;
+
+	/**
+	 * Exit this thread.
+	 */
+	virtual bool Exit() = 0;
+
+	/**
+	 * Join this thread.
+	 */
+	virtual void *Join() = 0;
+
+	/**
+	 * Check if this thread is the current active thread.
+	 * @return True if it is the current active thread.
+	 */
+	virtual bool IsCurrent() = 0;
+
+	/**
+	 * Get the unique ID of this thread.
+	 * @return A value unique to each thread.
+	 */
+	virtual uint GetId() = 0;
+
+	/**
+	 * Create a thread; proc will be called as first function inside the thread,
+	 *  with optinal params.
+	 * @param proc The procedure to call inside the thread.
+	 * @param param The params to give with 'proc'.
+	 * @return True if the thread was started correctly.
+	 */
+	static ThreadObject *New(OTTDThreadFunc proc, void *param);
+
+	/**
+	 * Convert the current thread to a new ThreadObject.
+	 * @return A new ThreadObject with the current thread attached to it.
+	 */
+	static ThreadObject* AttachCurrent();
+
+	/**
+	 * Find the Id of the current running thread.
+	 * @return The thread ID of the current active thread.
+	 */
+	static uint CurrentId();
+};
+
+/**
+ * Cross-platform Thread Semaphore. Wait() waits for a Set() of someone else.
+ */
+class ThreadSemaphore {
+public:
+	static ThreadSemaphore *New();
+
+	/**
+	 * Virtual Destructor to avoid compiler warnings.
+	 */
+	virtual ~ThreadSemaphore() {};
+
+	/**
+	 * Signal all threads that are in Wait() to continue.
+	 */
+	virtual void Set() = 0;
+
+	/**
+	 * Wait until we are signaled by a call to Set().
+	 */
+	virtual void Wait() = 0;
+};
 
 #endif /* THREAD_H */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/thread_none.cpp	Wed Apr 16 22:34:14 2008 +0000
@@ -0,0 +1,42 @@
+/* $Id$ */
+
+/** @file thread_none.cpp No-Threads-Available implementation of Threads */
+
+#include "stdafx.h"
+#include "thread.h"
+#include "fiber.hpp"
+
+/* static */ ThreadObject *ThreadObject::New(OTTDThreadFunc proc, void *param)
+{
+	return NULL;
+}
+
+/* static */ ThreadObject *ThreadObject::AttachCurrent()
+{
+	return NULL;
+}
+
+/* static */ uint ThreadObject::CurrentId()
+{
+	return -1;
+}
+
+/* static */ ThreadSemaphore *ThreadSemaphore::New()
+{
+	return NULL;
+}
+
+/* static */ Fiber *Fiber::New(FiberFunc proc, void *param)
+{
+	return NULL;
+}
+
+/* static */ Fiber *Fiber::AttachCurrent(void *param)
+{
+	return NULL;
+}
+
+/* static */ void *Fiber::GetCurrentFiberData()
+{
+	return NULL;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/thread_os2.cpp	Wed Apr 16 22:34:14 2008 +0000
@@ -0,0 +1,80 @@
+/* $Id$ */
+
+/** @file thread_os2.cpp OS2 implementation of Threads. */
+
+#include "stdafx.h"
+#include "thread.h"
+
+#if 0
+#include "debug.h"
+#include "core/alloc_func.hpp"
+#include <stdlib.h>
+
+#define INCL_DOS
+#include <os2.h>
+#include <process.h>
+
+struct OTTDThread {
+	TID thread;
+	OTTDThreadFunc func;
+	void *arg;
+	void *ret;
+};
+
+static void Proxy(void *arg)
+{
+	OTTDThread *t = (OTTDThread *)arg;
+	t->ret = t->func(t->arg);
+}
+
+OTTDThread *OTTDCreateThread(OTTDThreadFunc function, void *arg)
+{
+	OTTDThread *t = MallocT<OTTDThread>(1);
+
+	t->func = function;
+	t->arg  = arg;
+	t->thread = _beginthread(Proxy, NULL, 32768, t);
+	if (t->thread != (TID)-1) {
+		return t;
+	} else {
+		free(t);
+		return NULL;
+	}
+}
+
+void *OTTDJoinThread(OTTDThread *t)
+{
+	if (t == NULL) return NULL;
+
+	DosWaitThread(&t->thread, DCWW_WAIT);
+	void *ret = t->ret;
+	free(t);
+	return ret;
+}
+
+void OTTDExitThread()
+{
+	_endthread();
+}
+
+#endif
+
+/* static */ ThreadObject *ThreadObject::New(OTTDThreadFunc proc, void *param)
+{
+	return NULL;
+}
+
+/* static */ ThreadObject *ThreadObject::AttachCurrent()
+{
+	return NULL;
+}
+
+/* static */ uint ThreadObject::CurrentId()
+{
+	return -1;
+}
+
+/* static */ ThreadSemaphore *ThreadSemaphore::New()
+{
+	return NULL;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/thread_pthread.cpp	Wed Apr 16 22:34:14 2008 +0000
@@ -0,0 +1,199 @@
+/* $Id$ */
+
+/** @file thread_pthread.cpp POSIX pthread implementation of Threads. */
+
+#include "stdafx.h"
+#include "thread.h"
+#include "debug.h"
+#include "core/alloc_func.hpp"
+#include <stdlib.h>
+#include <pthread.h>
+#include <semaphore.h>
+#include <unistd.h>
+
+/**
+ * POSIX pthread version for ThreadObject.
+ */
+class ThreadObject_pthread : public ThreadObject {
+private:
+	pthread_t m_thr;             ///< System thread identifier.
+	OTTDThreadFunc m_proc;       ///< External thread procedure.
+	void     *m_param;           ///< Parameter for the external thread procedure.
+	bool      m_attached;        ///< True if the ThreadObject was attached to an existing thread.
+	sem_t     m_sem_start;       ///< Here the new thread waits before it starts.
+	sem_t     m_sem_stop;        ///< Here the other thread can wait for this thread to end.
+
+public:
+	/**
+	 * Create a pthread and start it, calling proc(param).
+	 */
+	ThreadObject_pthread(OTTDThreadFunc proc, void *param) :
+		m_thr(0),
+		m_proc(proc),
+		m_param(param),
+		m_attached(false)
+	{
+		sem_init(&m_sem_start, 0, 0);
+		sem_init(&m_sem_stop, 0, 0);
+
+		pthread_create(&m_thr, NULL, &stThreadProc, this);
+		sem_post(&m_sem_start);
+	}
+
+	/**
+	 * Create a pthread and attach current thread to it.
+	 */
+	ThreadObject_pthread() :
+		m_thr(0),
+		m_proc(NULL),
+		m_param(0),
+		m_attached(true)
+	{
+		sem_init(&m_sem_start, 0, 0);
+		sem_init(&m_sem_stop, 0, 0);
+
+		m_thr = pthread_self();
+	}
+
+	/* virtual */ ~ThreadObject_pthread()
+	{
+		sem_destroy(&m_sem_stop);
+		sem_destroy(&m_sem_start);
+	};
+
+	/* virtual */ bool IsRunning()
+	{
+		return m_thr != 0;
+	}
+
+	/* virtual */ bool WaitForStop()
+	{
+		/* You can't wait on yourself */
+		assert(!IsCurrent());
+		/* If the thread is not running, waiting is over */
+		if (!IsRunning()) return true;
+
+		int ret = sem_wait(&m_sem_stop);
+		if (ret == 0) {
+			/* We have passed semaphore so increment it again */
+			sem_post(&m_sem_stop);
+			return true;
+		}
+		return false;
+	}
+
+	/* virtual */ bool Exit()
+	{
+		/* You can only exit yourself */
+		assert(IsCurrent());
+		/* If the thread is not running, we are already closed */
+		if (!IsRunning()) return false;
+
+		/* For now we terminate by throwing an error, gives much cleaner cleanup */
+		throw 0;
+	}
+
+	/* virtual */ void *Join()
+	{
+		/* You cannot join yourself */
+		assert(!IsCurrent());
+
+		void *ret;
+		pthread_join(m_thr, &ret);
+		m_thr = 0;
+
+		return ret;
+	}
+
+	/* virtual */ bool IsCurrent()
+	{
+		return pthread_self() == m_thr;
+	}
+
+	/* virtual */ uint GetId()
+	{
+		return (uint)m_thr;
+	}
+
+private:
+	/**
+	 * On thread creation, this function is called, which calls the real startup
+	 *  function. This to get back into the correct instance again.
+	 */
+	static void *stThreadProc(void *thr)
+	{
+		return ((ThreadObject_pthread *)thr)->ThreadProc();
+	}
+
+	/**
+	 * A new thread is created, and this function is called. Call the custom
+	 *  function of the creator of the thread.
+	 */
+	void *ThreadProc()
+	{
+		/* The new thread stops here so the calling thread can complete pthread_create() call */
+		sem_wait(&m_sem_start);
+
+		/* Call the proc of the creator to continue this thread */
+		try {
+			m_proc(m_param);
+		} catch (...) {
+		}
+
+		/* Notify threads waiting for our completion */
+		sem_post(&m_sem_stop);
+
+		return NULL;
+	}
+};
+
+/* static */ ThreadObject *ThreadObject::New(OTTDThreadFunc proc, void *param)
+{
+	return new ThreadObject_pthread(proc, param);
+}
+
+/* static */ ThreadObject *ThreadObject::AttachCurrent()
+{
+	return new ThreadObject_pthread();
+}
+
+/* static */ uint ThreadObject::CurrentId()
+{
+	return (uint)pthread_self();
+}
+
+
+/**
+ * POSIX pthread version of ThreadSemaphore.
+ */
+class ThreadSemaphore_pthread : public ThreadSemaphore {
+private:
+	sem_t m_sem;
+
+public:
+	ThreadSemaphore_pthread()
+	{
+		sem_init(&m_sem, 0, 0);
+	}
+
+	/* virtual */ ~ThreadSemaphore_pthread()
+	{
+		sem_destroy(&m_sem);
+	}
+
+	/* virtual */ void Set()
+	{
+		int val = 0;
+		if (sem_getvalue(&m_sem, &val) == 0 && val == 0) sem_post(&m_sem);
+	}
+
+	/* virtual */ void Wait()
+	{
+		sem_wait(&m_sem);
+	}
+};
+
+/* static */ ThreadSemaphore *ThreadSemaphore::New()
+{
+	return new ThreadSemaphore_pthread();
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/thread_win32.cpp	Wed Apr 16 22:34:14 2008 +0000
@@ -0,0 +1,188 @@
+/* $Id$ */
+
+/** @file thread_win32.cpp Win32 thread implementation of Threads. */
+
+#include "stdafx.h"
+#include "thread.h"
+#include "debug.h"
+#include "core/alloc_func.hpp"
+#include <stdlib.h>
+#include <windows.h>
+#include <process.h>
+
+/**
+ * Win32 thread version for ThreadObject.
+ */
+class ThreadObject_Win32 : public ThreadObject {
+private:
+	uint     m_id_thr;
+	HANDLE   m_h_thr;
+	OTTDThreadFunc m_proc;
+	void     *m_param;
+	bool     m_attached;
+	void     *ret;
+
+public:
+	/**
+	 * Create a win32 thread and start it, calling proc(param).
+	 */
+	ThreadObject_Win32(OTTDThreadFunc proc, void *param) :
+		m_id_thr(0),
+		m_h_thr(NULL),
+		m_proc(proc),
+		m_param(param),
+		m_attached(false)
+	{
+		m_h_thr = (HANDLE)_beginthreadex(NULL, 0, &stThreadProc, this, CREATE_SUSPENDED, &m_id_thr);
+		if (m_h_thr == NULL) return;
+		ResumeThread(m_h_thr);
+	}
+
+	/**
+	 * Create a win32 thread and attach current thread to it.
+	 */
+	ThreadObject_Win32() :
+		m_id_thr(0),
+		m_h_thr(NULL),
+		m_proc(NULL),
+		m_param(NULL),
+		m_attached(false)
+	{
+		BOOL ret = DuplicateHandle(GetCurrentProcess(), GetCurrentThread(), GetCurrentProcess(), &m_h_thr, 0, FALSE, DUPLICATE_SAME_ACCESS);
+		if (!ret) return;
+		m_id_thr = GetCurrentThreadId();
+	}
+
+	/* virtual */ ~ThreadObject_Win32()
+	{
+		if (m_h_thr != NULL) {
+			CloseHandle(m_h_thr);
+			m_h_thr = NULL;
+		}
+	}
+
+	/* virtual */ bool IsRunning()
+	{
+		if (m_h_thr == NULL) return false;
+		DWORD exit_code = 0;
+		if (!GetExitCodeThread(m_h_thr, &exit_code)) return false;
+		return (exit_code == STILL_ACTIVE);
+	}
+
+	/* virtual */ bool WaitForStop()
+	{
+		/* You can't wait on yourself */
+		assert(!IsCurrent());
+		/* If the thread is not running, waiting is over */
+		if (!IsRunning()) return true;
+
+		DWORD res = WaitForSingleObject(m_h_thr, INFINITE);
+		return res == WAIT_OBJECT_0;
+	}
+
+	/* virtual */ bool Exit()
+	{
+		/* You can only exit yourself */
+		assert(IsCurrent());
+		/* If the thread is not running, we are already closed */
+		if (!IsRunning()) return false;
+
+		/* For now we terminate by throwing an error, gives much cleaner cleanup */
+		throw 0;
+	}
+
+	/* virtual */ void *Join()
+	{
+		/* You cannot join yourself */
+		assert(!IsCurrent());
+
+		WaitForSingleObject(m_h_thr, INFINITE);
+
+		return this->ret;
+	}
+
+	/* virtual */ bool IsCurrent()
+	{
+		DWORD id_cur = GetCurrentThreadId();
+		return id_cur == m_id_thr;
+	}
+
+	/* virtual */ uint GetId()
+	{
+		return m_id_thr;
+	}
+
+private:
+	/**
+	 * On thread creation, this function is called, which calls the real startup
+	 *  function. This to get back into the correct instance again.
+	 */
+	static uint CALLBACK stThreadProc(void *thr)
+	{
+		return ((ThreadObject_Win32 *)thr)->ThreadProc();
+	}
+
+	/**
+	 * A new thread is created, and this function is called. Call the custom
+	 *  function of the creator of the thread.
+	 */
+	uint ThreadProc()
+	{
+		try {
+			this->ret = m_proc(m_param);
+		} catch (...) {
+		}
+
+		return 0;
+	}
+};
+
+/* static */ ThreadObject *ThreadObject::New(OTTDThreadFunc proc, void *param)
+{
+	return new ThreadObject_Win32(proc, param);
+}
+
+/* static */ ThreadObject* ThreadObject::AttachCurrent()
+{
+	return new ThreadObject_Win32();
+}
+
+/* static */ uint ThreadObject::CurrentId()
+{
+	return GetCurrentThreadId();
+}
+
+
+/**
+ * Win32 thread version of ThreadSemaphore.
+ */
+class ThreadSemaphore_Win32 : public ThreadSemaphore {
+private:
+	HANDLE m_handle;
+
+public:
+	ThreadSemaphore_Win32()
+	{
+		m_handle = ::CreateEvent(NULL, FALSE, FALSE, NULL);
+	}
+
+	/* virtual */ ~ThreadSemaphore_Win32()
+	{
+		::CloseHandle(m_handle);
+	}
+
+	/* virtual */ void Set()
+	{
+		::SetEvent(m_handle);
+	}
+
+	/* virtual */ void Wait()
+	{
+		::WaitForSingleObject(m_handle, INFINITE);
+	}
+};
+
+/* static */ ThreadSemaphore *ThreadSemaphore::New()
+{
+	return new ThreadSemaphore_Win32();
+}
--- a/src/town.h	Wed Apr 16 22:16:04 2008 +0000
+++ b/src/town.h	Wed Apr 16 22:34:14 2008 +0000
@@ -163,7 +163,7 @@
 	bool larger_town;
 
 	/* NOSAVE: UpdateTownRadius updates this given the house count. */
-	uint16 radius[HZB_END];
+	uint32 squared_town_zone_radius[HZB_END];
 
 	/* NOSAVE: The number of each type of building in the town. */
 	BuildingCounts building_counts;
--- a/src/town_cmd.cpp	Wed Apr 16 22:16:04 2008 +0000
+++ b/src/town_cmd.cpp	Wed Apr 16 22:34:14 2008 +0000
@@ -1301,7 +1301,7 @@
 
 void UpdateTownRadius(Town *t)
 {
-	static const uint16 _town_radius_data[23][5] = {
+	static const uint32 _town_squared_town_zone_radius_data[23][5] = {
 		{  4,  0,  0,  0,  0}, // 0
 		{ 16,  0,  0,  0,  0},
 		{ 25,  0,  0,  0,  0},
@@ -1328,19 +1328,17 @@
 	};
 
 	if (t->num_houses < 92) {
-		memcpy(t->radius, _town_radius_data[t->num_houses / 4], sizeof(t->radius));
+		memcpy(t->squared_town_zone_radius, _town_squared_town_zone_radius_data[t->num_houses / 4], sizeof(t->squared_town_zone_radius));
 	} else {
 		int mass = t->num_houses / 8;
-		/* At least very roughly extrapolate. Empirical numbers dancing between
-		 * overwhelming by cottages and skyscrapers outskirts. */
-		t->radius[0] = mass * mass;
-		/* Actually we are proportional to sqrt() but that's right because
-		 * we are covering an area. */
-		t->radius[1] = mass * 7;
-		t->radius[2] = 0;
-		t->radius[3] = mass * 4;
-		t->radius[4] = mass * 3;
-		//debug("%d (->%d): %d %d %d %d\n", t->num_houses, mass, t->radius[0], t->radius[1], t->radius[3], t->radius[4]);
+		/* Actually we are proportional to sqrt() but that's right because we are covering an area.
+		 * The offsets are to make sure the radii do not decrease in size when going from the table
+		 * to the calculated value.*/
+		t->squared_town_zone_radius[0] = mass * 15 - 40;
+		t->squared_town_zone_radius[1] = mass * 9 - 15;
+		t->squared_town_zone_radius[2] = 0;
+		t->squared_town_zone_radius[3] = mass * 5 - 5;
+		t->squared_town_zone_radius[4] = mass * 3 + 5;
 	}
 }
 
@@ -1603,8 +1601,8 @@
 
 
 /** Returns the bit corresponding to the town zone of the specified tile
- * @param t Town on which radius is to be found
- * @param tile TileIndex where radius needs to be found
+ * @param t Town on which town zone is to be found
+ * @param tile TileIndex where town zone needs to be found
  * @return the bit position of the given zone, as defined in HouseZones
  */
 HouseZonesBits GetTownRadiusGroup(const Town *t, TileIndex tile)
@@ -1615,7 +1613,7 @@
 
 	HouseZonesBits smallest = HZB_TOWN_EDGE;
 	for (HouseZonesBits i = HZB_BEGIN; i < HZB_END; i++) {
-		if (dist < t->radius[i]) smallest = i;
+		if (dist < t->squared_town_zone_radius[i]) smallest = i;
 	}
 
 	return smallest;
@@ -1992,6 +1990,33 @@
 	DeleteAnimatedTile(tile);
 }
 
+/**
+ * Determines if a given HouseID is part of a multitile house.
+ * The given ID is set to the ID of the north tile and the TileDiff to the north tile is returned.
+ *
+ * @param house Is changed to the HouseID of the north tile of the same house
+ * @return TileDiff from the tile of the given HouseID to the north tile
+ */
+static TileIndex GetHouseNorthPart(HouseID &house)
+{
+	if (house >= 3) { // house id 0,1,2 MUST be single tile houses, or this code breaks.
+		if (GetHouseSpecs(house - 1)->building_flags & TILE_SIZE_2x1) {
+			house--;
+			return TileDiffXY(-1, 0);
+		} else if (GetHouseSpecs(house - 1)->building_flags & BUILDING_2_TILES_Y) {
+			house--;
+			return TileDiffXY(0, -1);
+		} else if (GetHouseSpecs(house - 2)->building_flags & BUILDING_HAS_4_TILES) {
+			house -= 2;
+			return TileDiffXY(-1, 0);
+		} else if (GetHouseSpecs(house - 3)->building_flags & BUILDING_HAS_4_TILES) {
+			house -= 3;
+			return TileDiffXY(-1, -1);
+		}
+	}
+	return 0;
+}
+
 void ClearTownHouse(Town *t, TileIndex tile)
 {
 	assert(IsTileType(tile, MP_HOUSE));
@@ -1999,21 +2024,7 @@
 	HouseID house = GetHouseType(tile);
 
 	/* need to align the tile to point to the upper left corner of the house */
-	if (house >= 3) { // house id 0,1,2 MUST be single tile houses, or this code breaks.
-		if (GetHouseSpecs(house-1)->building_flags & TILE_SIZE_2x1) {
-			house--;
-			tile += TileDiffXY(-1, 0);
-		} else if (GetHouseSpecs(house-1)->building_flags & BUILDING_2_TILES_Y) {
-			house--;
-			tile += TileDiffXY(0, -1);
-		} else if (GetHouseSpecs(house-2)->building_flags & BUILDING_HAS_4_TILES) {
-			house-=2;
-			tile += TileDiffXY(-1, 0);
-		} else if (GetHouseSpecs(house-3)->building_flags & BUILDING_HAS_4_TILES) {
-			house-=3;
-			tile += TileDiffXY(-1, -1);
-		}
-	}
+	tile += GetHouseNorthPart(house); // modifies house to the ID of the north tile
 
 	const HouseSpec *hs = GetHouseSpecs(house);
 
@@ -2285,7 +2296,7 @@
 
 	const Station *st;
 	FOR_ALL_STATIONS(st) {
-		if (DistanceSquare(st->xy, t->xy) <= t->radius[0]) {
+		if (DistanceSquare(st->xy, t->xy) <= t->squared_town_zone_radius[0]) {
 			if (st->time_since_load <= 20 || st->time_since_unload <= 20) {
 				n++;
 				if (IsValidPlayer(st->owner)) {
@@ -2536,6 +2547,7 @@
 {
 	if (AutoslopeEnabled()) {
 		HouseID house = GetHouseType(tile);
+		GetHouseNorthPart(house); // modifies house to the ID of the north tile
 		const HouseSpec *hs = GetHouseSpecs(house);
 
 		/* Here we differ from TTDP by checking TILE_NOT_SLOPED */
--- a/src/town_gui.cpp	Wed Apr 16 22:16:04 2008 +0000
+++ b/src/town_gui.cpp	Wed Apr 16 22:34:14 2008 +0000
@@ -278,6 +278,7 @@
 
 enum TownViewWidget {
 	TVW_CAPTION = 1,
+	TVW_STICKY,
 	TVW_CENTERVIEW = 6,
 	TVW_SHOWAUTORITY,
 	TVW_CHANGENAME,
@@ -290,9 +291,22 @@
 	Town *t = GetTown(w->window_number);
 
 	switch (e->event) {
-		case WE_CREATE:
+		case WE_CREATE: {
+			bool ingame = _game_mode != GM_EDITOR;
 			if (t->larger_town) w->widget[TVW_CAPTION].data = STR_CITY;
-			break;
+			w->SetWidgetHiddenState(TVW_DELETE, ingame);  // hide delete button on game mode
+			w->SetWidgetHiddenState(TVW_EXPAND, ingame);  // hide expand button on game mode
+			w->SetWidgetHiddenState(TVW_SHOWAUTORITY, !ingame); // hide autority button on editor mode
+
+			if (ingame) {
+				/* resize caption bar */
+				w->widget[TVW_CAPTION].right = w->widget[TVW_STICKY].left -1;
+				/* move the rename from top on scenario to bottom in game */
+				w->widget[TVW_CHANGENAME].top = w->widget[TVW_EXPAND].top;
+				w->widget[TVW_CHANGENAME].bottom = w->widget[TVW_EXPAND].bottom;
+				w->widget[TVW_CHANGENAME].right = w->widget[TVW_STICKY].right;
+			}
+		} break;
 
 		case WE_PAINT:
 			/* disable renaming town in network games if you are not the server */
@@ -353,14 +367,16 @@
 
 static const Widget _town_view_widgets[] = {
 {   WWT_CLOSEBOX,   RESIZE_NONE,    13,     0,    10,     0,    13, STR_00C5,                 STR_018B_CLOSE_WINDOW},
-{    WWT_CAPTION,   RESIZE_NONE,    13,    11,   247,     0,    13, STR_2005,                 STR_018C_WINDOW_TITLE_DRAG_THIS},
+{    WWT_CAPTION,   RESIZE_NONE,    13,    11,   172,     0,    13, STR_2005,                 STR_018C_WINDOW_TITLE_DRAG_THIS},
 {  WWT_STICKYBOX,   RESIZE_NONE,    13,   248,   259,     0,    13, 0x0,                      STR_STICKY_BUTTON},
 {      WWT_PANEL,   RESIZE_NONE,    13,     0,   259,    14,   105, 0x0,                      STR_NULL},
 {      WWT_INSET,   RESIZE_NONE,    13,     2,   257,    16,   103, 0x0,                      STR_NULL},
 {      WWT_PANEL,   RESIZE_NONE,    13,     0,   259,   106,   137, 0x0,                      STR_NULL},
 { WWT_PUSHTXTBTN,   RESIZE_NONE,    13,     0,    85,   138,   149, STR_00E4_LOCATION,        STR_200B_CENTER_THE_MAIN_VIEW_ON},
 { WWT_PUSHTXTBTN,   RESIZE_NONE,    13,    86,   171,   138,   149, STR_2020_LOCAL_AUTHORITY, STR_2021_SHOW_INFORMATION_ON_LOCAL},
-{ WWT_PUSHTXTBTN,   RESIZE_NONE,    13,   172,   259,   138,   149, STR_0130_RENAME,          STR_200C_CHANGE_TOWN_NAME},
+{ WWT_PUSHTXTBTN,   RESIZE_NONE,    13,   172,   247,     0,    13, STR_0130_RENAME,          STR_200C_CHANGE_TOWN_NAME},
+{ WWT_PUSHTXTBTN,   RESIZE_NONE,    13,    86,   171,   138,   149, STR_023C_EXPAND,          STR_023B_INCREASE_SIZE_OF_TOWN},
+{ WWT_PUSHTXTBTN,   RESIZE_NONE,    13,   172,   259,   138,   149, STR_0290_DELETE,          STR_0291_DELETE_THIS_TOWN_COMPLETELY},
 {   WIDGETS_END},
 };
 
@@ -372,38 +388,11 @@
 	TownViewWndProc
 };
 
-static const Widget _town_view_scen_widgets[] = {
-{   WWT_CLOSEBOX,   RESIZE_NONE,    13,     0,    10,     0,    13, STR_00C5,          STR_018B_CLOSE_WINDOW},
-{    WWT_CAPTION,   RESIZE_NONE,    13,    11,   172,     0,    13, STR_2005,          STR_018C_WINDOW_TITLE_DRAG_THIS},
-{  WWT_STICKYBOX,   RESIZE_NONE,    13,   248,   259,     0,    13, 0x0,               STR_STICKY_BUTTON},
-{      WWT_PANEL,   RESIZE_NONE,    13,     0,   259,    14,   105, 0x0,               STR_NULL},
-{      WWT_INSET,   RESIZE_NONE,    13,     2,   257,    16,   103, 0x0,               STR_NULL},
-{      WWT_PANEL,   RESIZE_NONE,    13,     0,   259,   106,   137, 0x0,               STR_NULL},
-{ WWT_PUSHTXTBTN,   RESIZE_NONE,    13,     0,    85,   138,   149, STR_00E4_LOCATION, STR_200B_CENTER_THE_MAIN_VIEW_ON},
-{      WWT_EMPTY,   RESIZE_NONE,     0,     0,     0,     0,     0, 0x0,               STR_NULL},
-{ WWT_PUSHTXTBTN,   RESIZE_NONE,    13,   173,   247,     0,    13, STR_0130_RENAME,   STR_200C_CHANGE_TOWN_NAME},
-{ WWT_PUSHTXTBTN,   RESIZE_NONE,    13,    86,   171,   138,   149, STR_023C_EXPAND,   STR_023B_INCREASE_SIZE_OF_TOWN},
-{ WWT_PUSHTXTBTN,   RESIZE_NONE,    13,   172,   259,   138,   149, STR_0290_DELETE,   STR_0291_DELETE_THIS_TOWN_COMPLETELY},
-{   WIDGETS_END},
-};
-
-static const WindowDesc _town_view_scen_desc = {
-	WDP_AUTO, WDP_AUTO, 260, 150, 260, 150,
-	WC_TOWN_VIEW, WC_NONE,
-	WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET | WDF_UNCLICK_BUTTONS | WDF_STICKY_BUTTON,
-	_town_view_scen_widgets,
-	TownViewWndProc
-};
-
 void ShowTownViewWindow(TownID town)
 {
 	Window *w;
 
-	if (_game_mode != GM_EDITOR) {
-		w = AllocateWindowDescFront(&_town_view_desc, town);
-	} else {
-		w = AllocateWindowDescFront(&_town_view_scen_desc, town);
-	}
+	w = AllocateWindowDescFront(&_town_view_desc, town);
 
 	if (w != NULL) {
 		w->flags4 |= WF_DISABLE_VP_SCROLL;
--- a/src/train_cmd.cpp	Wed Apr 16 22:16:04 2008 +0000
+++ b/src/train_cmd.cpp	Wed Apr 16 22:34:14 2008 +0000
@@ -1811,8 +1811,7 @@
 		InvalidateWindowData(WC_VEHICLE_DEPOT, v->tile);
 	}
 
-	/* set reversed flag on all parts */
-	for (Vehicle *u = v; u != NULL; u = u->Next()) ToggleBit(u->u.rail.flags, VRF_TOGGLE_REVERSE);
+	ToggleBit(v->u.rail.flags, VRF_TOGGLE_REVERSE);
 
 	ClrBit(v->u.rail.flags, VRF_REVERSING);
 
--- a/src/vehicle.cpp	Wed Apr 16 22:16:04 2008 +0000
+++ b/src/vehicle.cpp	Wed Apr 16 22:34:14 2008 +0000
@@ -2188,8 +2188,7 @@
 			if (!IsFrontEngine(v)) v = v->First();
 			UpdateSignalsOnSegment(v->tile, INVALID_DIAGDIR, v->owner);
 			v->load_unload_time_rem = 0;
-			/* Reset reversed flag */
-			for (Vehicle *u = v; u != NULL; u = u->Next()) ClrBit(u->u.rail.flags, VRF_TOGGLE_REVERSE);
+			ClrBit(v->u.rail.flags, VRF_TOGGLE_REVERSE);
 			TrainConsistChanged(v);
 			break;
 
--- a/src/vehicle_base.h	Wed Apr 16 22:16:04 2008 +0000
+++ b/src/vehicle_base.h	Wed Apr 16 22:34:14 2008 +0000
@@ -140,7 +140,7 @@
 	/* used to mark that electric train engine is allowed to run on normal rail */
 	VRF_EL_ENGINE_ALLOWED_NORMAL_RAIL = 6,
 
-	/* used for vehicle var 0xFE bit 8 (toggled each time the train is reversed) */
+	/* used for vehicle var 0xFE bit 8 (toggled each time the train is reversed, accurate for first vehicle only) */
 	VRF_TOGGLE_REVERSE = 7,
 };
 
--- a/src/viewport.cpp	Wed Apr 16 22:16:04 2008 +0000
+++ b/src/viewport.cpp	Wed Apr 16 22:34:14 2008 +0000
@@ -29,15 +29,11 @@
 #include "settings_type.h"
 #include "station_func.h"
 #include "core/alloc_type.hpp"
+#include "misc/smallvec.h"
 
 #include "table/sprites.h"
 #include "table/strings.h"
 
-enum {
-	VIEWPORT_DRAW_MEM = (65536 * 2),
-	PARENT_LIST_SIZE  = 6144,
-};
-
 PlaceProc *_place_proc;
 Point _tile_fract_coords;
 ZoomLevel _saved_scrollpos_zoom;
@@ -78,7 +74,6 @@
 struct StringSpriteToDraw {
 	uint16 string;
 	uint16 color;
-	StringSpriteToDraw *next;
 	int32 x;
 	int32 y;
 	uint64 params[2];
@@ -89,7 +84,6 @@
 	SpriteID image;
 	SpriteID pal;
 	const SubSprite *sub;           ///< only draw a rectangular part of the sprite
-	TileSpriteToDraw *next;
 	int32 x;
 	int32 y;
 	byte z;
@@ -122,18 +116,11 @@
 	int zmin;                       ///< minimal world Z coordinate of bounding box
 	int zmax;                       ///< maximal world Z coordinate of bounding box
 
-	ChildScreenSpriteToDraw *child; ///< head of child list;
+	int first_child;                ///< the first child to draw.
+	int last_child;                 ///< the last sprite to draw.
 	bool comparison_done;           ///< Used during sprite sorting: true if sprite has been compared with all other sprites
 };
 
-/* Quick hack to know how much memory to reserve when allocating from the spritelist
- * to prevent a buffer overflow. */
-#define LARGEST_SPRITELIST_STRUCT ParentSpriteToDraw
-assert_compile(sizeof(LARGEST_SPRITELIST_STRUCT) >= sizeof(StringSpriteToDraw));
-assert_compile(sizeof(LARGEST_SPRITELIST_STRUCT) >= sizeof(TileSpriteToDraw));
-assert_compile(sizeof(LARGEST_SPRITELIST_STRUCT) >= sizeof(ChildScreenSpriteToDraw));
-assert_compile(sizeof(LARGEST_SPRITELIST_STRUCT) >= sizeof(ParentSpriteToDraw));
-
 /* Enumeration of multi-part foundations */
 enum FoundationPart {
 	FOUNDATION_PART_NONE     = 0xFF,  ///< Neither foundation nor groundsprite drawn yet.
@@ -142,29 +129,32 @@
 	FOUNDATION_PART_END
 };
 
+typedef SmallVector<TileSpriteToDraw, 64> TileSpriteToDrawVector;
+typedef SmallVector<StringSpriteToDraw, 4> StringSpriteToDrawVector;
+typedef SmallVector<ParentSpriteToDraw, 64> ParentSpriteToDrawVector;
+typedef SmallVector<ParentSpriteToDraw*, 64> ParentSpriteToSortVector;
+typedef SmallVector<ChildScreenSpriteToDraw, 16> ChildScreenSpriteToDrawVector;
+
 struct ViewportDrawer {
 	DrawPixelInfo dpi;
 
-	byte *spritelist_mem;
-	const byte *eof_spritelist_mem;
-
-	StringSpriteToDraw **last_string, *first_string;
-	TileSpriteToDraw **last_tile, *first_tile;
-
-	ChildScreenSpriteToDraw **last_child;
-
-	ParentSpriteToDraw **parent_list;
-	ParentSpriteToDraw * const *eof_parent_list;
+	StringSpriteToDrawVector string_sprites_to_draw;
+	TileSpriteToDrawVector tile_sprites_to_draw;
+	ParentSpriteToDrawVector parent_sprites_to_draw;
+	ParentSpriteToSortVector parent_sprites_to_sort;
+	ChildScreenSpriteToDrawVector child_screen_sprites_to_draw;
+
+	int *last_child;
 
 	byte combine_sprites;
 
-	ParentSpriteToDraw *foundation[FOUNDATION_PART_END];                   ///< Foundation sprites.
-	FoundationPart foundation_part;                                        ///< Currently active foundation for ground sprite drawing.
-	ChildScreenSpriteToDraw **last_foundation_child[FOUNDATION_PART_END];  ///< Tail of ChildSprite list of the foundations.
-	Point foundation_offset[FOUNDATION_PART_END];                          ///< Pixeloffset for ground sprites on the foundations.
+	int foundation[FOUNDATION_PART_END];             ///< Foundation sprites (index into parent_sprites_to_draw).
+	FoundationPart foundation_part;                  ///< Currently active foundation for ground sprite drawing.
+	int *last_foundation_child[FOUNDATION_PART_END]; ///< Tail of ChildSprite list of the foundations. (index into child_screen_sprites_to_draw)
+	Point foundation_offset[FOUNDATION_PART_END];    ///< Pixeloffset for ground sprites on the foundations.
 };
 
-static ViewportDrawer *_cur_vd;
+static ViewportDrawer _vd;
 
 TileHighlightData _thd;
 static TileInfo *_cur_ti;
@@ -486,28 +476,15 @@
  */
 void DrawGroundSpriteAt(SpriteID image, SpriteID pal, int32 x, int32 y, byte z, const SubSprite *sub)
 {
-	ViewportDrawer *vd = _cur_vd;
-	TileSpriteToDraw *ts;
-
 	assert((image & SPRITE_MASK) < MAX_SPRITES);
 
-	if (vd->spritelist_mem >= vd->eof_spritelist_mem) {
-		DEBUG(sprite, 0, "Out of sprite memory");
-		return;
-	}
-	ts = (TileSpriteToDraw*)vd->spritelist_mem;
-
-	vd->spritelist_mem += sizeof(TileSpriteToDraw);
-
+	TileSpriteToDraw *ts = _vd.tile_sprites_to_draw.Append();
 	ts->image = image;
 	ts->pal = pal;
 	ts->sub = sub;
-	ts->next = NULL;
 	ts->x = x;
 	ts->y = y;
 	ts->z = z;
-	*vd->last_tile = ts;
-	vd->last_tile = &ts->next;
 }
 
 /**
@@ -524,19 +501,18 @@
  */
 static void AddChildSpriteToFoundation(SpriteID image, SpriteID pal, const SubSprite *sub, FoundationPart foundation_part, int extra_offs_x, int extra_offs_y)
 {
-	ViewportDrawer *vd = _cur_vd;
 	assert(IsInsideMM(foundation_part, 0, FOUNDATION_PART_END));
-	assert(vd->foundation[foundation_part] != NULL);
-	Point offs = vd->foundation_offset[foundation_part];
+	assert(_vd.foundation[foundation_part] != -1);
+	Point offs = _vd.foundation_offset[foundation_part];
 
 	/* Change the active ChildSprite list to the one of the foundation */
-	ChildScreenSpriteToDraw **old_child = vd->last_child;
-	vd->last_child = vd->last_foundation_child[foundation_part];
+	int *old_child = _vd.last_child;
+	_vd.last_child = _vd.last_foundation_child[foundation_part];
 
 	AddChildSpriteScreen(image, pal, offs.x + extra_offs_x, offs.y + extra_offs_y, false, sub);
 
 	/* Switch back to last ChildSprite list */
-	vd->last_child = old_child;
+	_vd.last_child = old_child;
 }
 
 /**
@@ -549,12 +525,11 @@
  */
 void DrawGroundSprite(SpriteID image, SpriteID pal, const SubSprite *sub)
 {
-	ViewportDrawer *vd = _cur_vd;
 	/* Switch to first foundation part, if no foundation was drawn */
-	if (vd->foundation_part == FOUNDATION_PART_NONE) vd->foundation_part = FOUNDATION_PART_NORMAL;
-
-	if (vd->foundation[vd->foundation_part] != NULL) {
-		AddChildSpriteToFoundation(image, pal, sub, vd->foundation_part, 0, 0);
+	if (_vd.foundation_part == FOUNDATION_PART_NONE) _vd.foundation_part = FOUNDATION_PART_NORMAL;
+
+	if (_vd.foundation[_vd.foundation_part] != -1) {
+		AddChildSpriteToFoundation(image, pal, sub, _vd.foundation_part, 0, 0);
 	} else {
 		DrawGroundSpriteAt(image, pal, _cur_ti->x, _cur_ti->y, _cur_ti->z, sub);
 	}
@@ -570,24 +545,23 @@
  */
 void OffsetGroundSprite(int x, int y)
 {
-	ViewportDrawer *vd = _cur_vd;
 	/* Switch to next foundation part */
-	switch (vd->foundation_part) {
+	switch (_vd.foundation_part) {
 		case FOUNDATION_PART_NONE:
-			vd->foundation_part = FOUNDATION_PART_NORMAL;
+			_vd.foundation_part = FOUNDATION_PART_NORMAL;
 			break;
 		case FOUNDATION_PART_NORMAL:
-			vd->foundation_part = FOUNDATION_PART_HALFTILE;
+			_vd.foundation_part = FOUNDATION_PART_HALFTILE;
 			break;
 		default: NOT_REACHED();
 	}
 
-	/* vd->last_child == NULL if foundation sprite was clipped by the viewport bounds */
-	if (vd->last_child != NULL) vd->foundation[vd->foundation_part] = vd->parent_list[-1];
-
-	vd->foundation_offset[vd->foundation_part].x = x;
-	vd->foundation_offset[vd->foundation_part].y = y;
-	vd->last_foundation_child[vd->foundation_part] = vd->last_child;
+	/* _vd.last_child == NULL if foundation sprite was clipped by the viewport bounds */
+	if (_vd.last_child != NULL) _vd.foundation[_vd.foundation_part] = _vd.parent_sprites_to_draw.items - 1;
+
+	_vd.foundation_offset[_vd.foundation_part].x = x;
+	_vd.foundation_offset[_vd.foundation_part].y = y;
+	_vd.last_foundation_child[_vd.foundation_part] = _vd.last_child;
 }
 
 /**
@@ -603,17 +577,17 @@
  */
 static void AddCombinedSprite(SpriteID image, SpriteID pal, int x, int y, byte z, const SubSprite *sub)
 {
-	const ViewportDrawer *vd = _cur_vd;
 	Point pt = RemapCoords(x, y, z);
 	const Sprite* spr = GetSprite(image & SPRITE_MASK);
 
-	if (pt.x + spr->x_offs >= vd->dpi.left + vd->dpi.width ||
-			pt.x + spr->x_offs + spr->width <= vd->dpi.left ||
-			pt.y + spr->y_offs >= vd->dpi.top + vd->dpi.height ||
-			pt.y + spr->y_offs + spr->height <= vd->dpi.top)
+	if (pt.x + spr->x_offs >= _vd.dpi.left + _vd.dpi.width ||
+			pt.x + spr->x_offs + spr->width <= _vd.dpi.left ||
+			pt.y + spr->y_offs >= _vd.dpi.top + _vd.dpi.height ||
+			pt.y + spr->y_offs + spr->height <= _vd.dpi.top)
 		return;
 
-	AddChildSpriteScreen(image, pal, pt.x - vd->parent_list[-1]->left, pt.y - vd->parent_list[-1]->top, false, sub);
+	const ParentSpriteToDraw *pstd = _vd.parent_sprites_to_draw.End() - 1;
+	AddChildSpriteScreen(image, pal, pt.x - pstd->left, pt.y - pstd->top, false, sub);
 }
 
 /** Draw a (transparent) sprite at given coordinates with a given bounding box.
@@ -642,9 +616,6 @@
  */
 void AddSortableSpriteToDraw(SpriteID image, SpriteID pal, int x, int y, int w, int h, int dz, int z, bool transparent, int bb_offset_x, int bb_offset_y, int bb_offset_z, const SubSprite *sub)
 {
-	ViewportDrawer *vd = _cur_vd;
-	ParentSpriteToDraw *ps;
-	Point pt;
 	int32 left, right, top, bottom;
 
 	assert((image & SPRITE_MASK) < MAX_SPRITES);
@@ -655,45 +626,27 @@
 		pal = PALETTE_TO_TRANSPARENT;
 	}
 
-	if (vd->combine_sprites == 2) {
+	if (_vd.combine_sprites == 2) {
 		AddCombinedSprite(image, pal, x, y, z, sub);
 		return;
 	}
 
-	vd->last_child = NULL;
-
-	if (vd->spritelist_mem >= vd->eof_spritelist_mem) {
-		DEBUG(sprite, 0, "Out of sprite memory");
-		return;
-	}
-	ps = (ParentSpriteToDraw*)vd->spritelist_mem;
-
-	if (vd->parent_list >= vd->eof_parent_list) {
-		/* This can happen rarely, mostly when you zoom out completely
-		 *  and have a lot of stuff that moves (and is added to the
-		 *  sort-list, this function). To solve it, increase
-		 *  parent_list somewhere below to a higher number.
-		 * This can not really hurt you, it just gives some black
-		 *  spots on the screen ;) */
-		DEBUG(sprite, 0, "Out of sprite memory (parent_list)");
-		return;
-	}
-
-	pt = RemapCoords(x, y, z);
-	ps->x = pt.x;
-	ps->y = pt.y;
+	_vd.last_child = NULL;
+
+	Point pt = RemapCoords(x, y, z);
+	int tmp_left, tmp_top, tmp_x = pt.x, tmp_y = pt.y;
 
 	/* Compute screen extents of sprite */
 	if (image == SPR_EMPTY_BOUNDING_BOX) {
-		left = ps->left = RemapCoords(x + w          , y + bb_offset_y, z + bb_offset_z).x;
+		left = tmp_left = RemapCoords(x + w          , y + bb_offset_y, z + bb_offset_z).x;
 		right           = RemapCoords(x + bb_offset_x, y + h          , z + bb_offset_z).x + 1;
-		top  = ps->top  = RemapCoords(x + bb_offset_x, y + bb_offset_y, z + dz         ).y;
+		top  = tmp_top  = RemapCoords(x + bb_offset_x, y + bb_offset_y, z + dz         ).y;
 		bottom          = RemapCoords(x + w          , y + h          , z + bb_offset_z).y + 1;
 	} else {
 		const Sprite *spr = GetSprite(image & SPRITE_MASK);
-		left = ps->left = (pt.x += spr->x_offs);
+		left = tmp_left = (pt.x += spr->x_offs);
 		right           = (pt.x +  spr->width );
-		top  = ps->top  = (pt.y += spr->y_offs);
+		top  = tmp_top  = (pt.y += spr->y_offs);
 		bottom          = (pt.y +  spr->height);
 	}
 
@@ -706,14 +659,19 @@
 	}
 
 	/* Do not add the sprite to the viewport, if it is outside */
-	if (left   >= vd->dpi.left + vd->dpi.width ||
-	    right  <= vd->dpi.left                 ||
-	    top    >= vd->dpi.top + vd->dpi.height ||
-	    bottom <= vd->dpi.top) {
+	if (left   >= _vd.dpi.left + _vd.dpi.width ||
+	    right  <= _vd.dpi.left                 ||
+	    top    >= _vd.dpi.top + _vd.dpi.height ||
+	    bottom <= _vd.dpi.top) {
 		return;
 	}
 
-	vd->spritelist_mem += sizeof(ParentSpriteToDraw);
+	ParentSpriteToDraw *ps = _vd.parent_sprites_to_draw.Append();
+	ps->x = tmp_x;
+	ps->y = tmp_y;
+
+	ps->left = tmp_left;
+	ps->top  = tmp_top;
 
 	ps->image = image;
 	ps->pal = pal;
@@ -728,22 +686,22 @@
 	ps->zmax = z + max(bb_offset_z, dz) - 1;
 
 	ps->comparison_done = false;
-	ps->child = NULL;
-	vd->last_child = &ps->child;
-
-	*vd->parent_list++ = ps;
-
-	if (vd->combine_sprites == 1) vd->combine_sprites = 2;
+	ps->first_child = _vd.child_screen_sprites_to_draw.items;
+	ps->last_child  = _vd.child_screen_sprites_to_draw.items;
+
+	_vd.last_child = &ps->last_child;
+
+	if (_vd.combine_sprites == 1) _vd.combine_sprites = 2;
 }
 
 void StartSpriteCombine()
 {
-	_cur_vd->combine_sprites = 1;
+	_vd.combine_sprites = 1;
 }
 
 void EndSpriteCombine()
 {
-	_cur_vd->combine_sprites = 0;
+	_vd.combine_sprites = 0;
 }
 
 /**
@@ -758,69 +716,41 @@
  */
 void AddChildSpriteScreen(SpriteID image, SpriteID pal, int x, int y, bool transparent, const SubSprite *sub)
 {
-	ViewportDrawer *vd = _cur_vd;
-	ChildScreenSpriteToDraw *cs;
-
 	assert((image & SPRITE_MASK) < MAX_SPRITES);
 
+	/* If the ParentSprite was clipped by the viewport bounds, do not draw the ChildSprites either */
+	if (_vd.last_child == NULL) return;
+
 	/* make the sprites transparent with the right palette */
 	if (transparent) {
 		SetBit(image, PALETTE_MODIFIER_TRANSPARENT);
 		pal = PALETTE_TO_TRANSPARENT;
 	}
 
-	if (vd->spritelist_mem >= vd->eof_spritelist_mem) {
-		DEBUG(sprite, 0, "Out of sprite memory");
-		return;
-	}
-	cs = (ChildScreenSpriteToDraw*)vd->spritelist_mem;
-
-	/* If the ParentSprite was clipped by the viewport bounds, do not draw the ChildSprites either */
-	if (vd->last_child == NULL) return;
-
-	vd->spritelist_mem += sizeof(ChildScreenSpriteToDraw);
-
 	/* Append the sprite to the active ChildSprite list.
 	 * If the active ParentSprite is a foundation, update last_foundation_child as well. */
-	*vd->last_child = cs;
-	if (vd->last_foundation_child[0] == vd->last_child) vd->last_foundation_child[0] = &cs->next;
-	if (vd->last_foundation_child[1] == vd->last_child) vd->last_foundation_child[1] = &cs->next;
-	vd->last_child = &cs->next;
-
+	ChildScreenSpriteToDraw *cs = _vd.child_screen_sprites_to_draw.Append();
 	cs->image = image;
 	cs->pal = pal;
 	cs->sub = sub;
 	cs->x = x;
 	cs->y = y;
 	cs->next = NULL;
+
+	*_vd.last_child = _vd.child_screen_sprites_to_draw.items;
 }
 
 /* Returns a StringSpriteToDraw */
-void *AddStringToDraw(int x, int y, StringID string, uint64 params_1, uint64 params_2)
+void AddStringToDraw(int x, int y, StringID string, uint64 params_1, uint64 params_2, uint16 color, uint16 width)
 {
-	ViewportDrawer *vd = _cur_vd;
-	StringSpriteToDraw *ss;
-
-	if (vd->spritelist_mem >= vd->eof_spritelist_mem) {
-		DEBUG(sprite, 0, "Out of sprite memory");
-		return NULL;
-	}
-	ss = (StringSpriteToDraw*)vd->spritelist_mem;
-
-	vd->spritelist_mem += sizeof(StringSpriteToDraw);
-
+	StringSpriteToDraw *ss = _vd.string_sprites_to_draw.Append();
 	ss->string = string;
-	ss->next = NULL;
 	ss->x = x;
 	ss->y = y;
 	ss->params[0] = params_1;
 	ss->params[1] = params_2;
-	ss->width = 0;
-
-	*vd->last_string = ss;
-	vd->last_string = &ss->next;
-
-	return ss;
+	ss->width = width;
+	ss->color = color;
 }
 
 
@@ -838,7 +768,7 @@
 static void DrawSelectionSprite(SpriteID image, SpriteID pal, const TileInfo *ti, int z_offset, FoundationPart foundation_part)
 {
 	/* FIXME: This is not totally valid for some autorail highlights, that extent over the edges of the tile. */
-	if (_cur_vd->foundation[foundation_part] == NULL) {
+	if (_vd.foundation[foundation_part] == -1) {
 		/* draw on real ground */
 		DrawGroundSpriteAt(image, pal, ti->x, ti->y, ti->z + z_offset);
 	} else {
@@ -1036,7 +966,6 @@
 
 static void ViewportAddLandscape()
 {
-	ViewportDrawer *vd = _cur_vd;
 	int x, y, width, height;
 	TileInfo ti;
 	bool direction;
@@ -1044,14 +973,14 @@
 	_cur_ti = &ti;
 
 	/* Transform into tile coordinates and round to closest full tile */
-	x = ((vd->dpi.top >> 1) - (vd->dpi.left >> 2)) & ~0xF;
-	y = ((vd->dpi.top >> 1) + (vd->dpi.left >> 2) - 0x10) & ~0xF;
+	x = ((_vd.dpi.top >> 1) - (_vd.dpi.left >> 2)) & ~0xF;
+	y = ((_vd.dpi.top >> 1) + (_vd.dpi.left >> 2) - 0x10) & ~0xF;
 
 	/* determine size of area */
 	{
 		Point pt = RemapCoords(x, y, 241);
-		width = (vd->dpi.left + vd->dpi.width - pt.x + 95) >> 6;
-		height = (vd->dpi.top + vd->dpi.height - pt.y) >> 5 << 1;
+		width = (_vd.dpi.left + _vd.dpi.width - pt.x + 95) >> 6;
+		height = (_vd.dpi.top + _vd.dpi.height - pt.y) >> 5 << 1;
 	}
 
 	assert(width > 0);
@@ -1086,11 +1015,11 @@
 			y_cur += 0x10;
 			x_cur -= 0x10;
 
-			vd->foundation_part = FOUNDATION_PART_NONE;
-			vd->foundation[0] = NULL;
-			vd->foundation[1] = NULL;
-			vd->last_foundation_child[0] = NULL;
-			vd->last_foundation_child[1] = NULL;
+			_vd.foundation_part = FOUNDATION_PART_NONE;
+			_vd.foundation[0] = -1;
+			_vd.foundation[1] = -1;
+			_vd.last_foundation_child[0] = NULL;
+			_vd.last_foundation_child[1] = NULL;
 
 			_tile_type_procs[tt]->draw_tile_proc(&ti);
 			DrawTileSelection(&ti);
@@ -1171,13 +1100,7 @@
 
 static void AddStation(const Station *st, StringID str, uint16 width)
 {
-	StringSpriteToDraw *sstd;
-
-	sstd = (StringSpriteToDraw*)AddStringToDraw(st->sign.left + 1, st->sign.top + 1, str, st->index, st->facilities);
-	if (sstd != NULL) {
-		sstd->color = (st->owner == OWNER_NONE || st->facilities == 0) ? 0xE : _player_colors[st->owner];
-		sstd->width = width;
-	}
+	AddStringToDraw(st->sign.left + 1, st->sign.top + 1, str, st->index, st->facilities, (st->owner == OWNER_NONE || st->facilities == 0) ? 0xE : _player_colors[st->owner], width);
 }
 
 
@@ -1241,13 +1164,7 @@
 
 static void AddSign(const Sign *si, StringID str, uint16 width)
 {
-	StringSpriteToDraw *sstd;
-
-	sstd = (StringSpriteToDraw*)AddStringToDraw(si->sign.left + 1, si->sign.top + 1, str, si->index, 0);
-	if (sstd != NULL) {
-		sstd->color = (si->owner == OWNER_NONE) ? 14 : _player_colors[si->owner];
-		sstd->width = width;
-	}
+	AddStringToDraw(si->sign.left + 1, si->sign.top + 1, str, si->index, 0, (si->owner == OWNER_NONE) ? 14 : _player_colors[si->owner], width);
 }
 
 
@@ -1311,13 +1228,7 @@
 
 static void AddWaypoint(const Waypoint *wp, StringID str, uint16 width)
 {
-	StringSpriteToDraw *sstd;
-
-	sstd = (StringSpriteToDraw*)AddStringToDraw(wp->sign.left + 1, wp->sign.top + 1, str, wp->index, 0);
-	if (sstd != NULL) {
-		sstd->color = (wp->deleted ? 0xE : 11);
-		sstd->width = width;
-	}
+	AddStringToDraw(wp->sign.left + 1, wp->sign.top + 1, str, wp->index, 0, (wp->deleted ? 0xE : 11), width);
 }
 
 
@@ -1398,84 +1309,84 @@
 }
 
 
-static void ViewportDrawTileSprites(TileSpriteToDraw *ts)
+static void ViewportDrawTileSprites(const TileSpriteToDrawVector *tstdv)
 {
-	do {
+	const TileSpriteToDraw *tsend = tstdv->End();
+	for (const TileSpriteToDraw *ts = tstdv->Begin(); ts != tsend; ++ts) {
 		Point pt = RemapCoords(ts->x, ts->y, ts->z);
 		DrawSprite(ts->image, ts->pal, pt.x, pt.y, ts->sub);
-		ts = ts->next;
-	} while (ts != NULL);
+	}
 }
 
-static void ViewportSortParentSprites(ParentSpriteToDraw *psd[])
+static void ViewportSortParentSprites(ParentSpriteToSortVector *psdv)
 {
-	while (*psd != NULL) {
-		ParentSpriteToDraw* ps = *psd;
-
-		if (!ps->comparison_done) {
-			ParentSpriteToDraw** psd2 = psd;
-
-			ps->comparison_done = true;
-
-			while (*++psd2 != NULL) {
-				ParentSpriteToDraw* ps2 = *psd2;
-				ParentSpriteToDraw** psd3;
-
-				if (ps2->comparison_done) continue;
-
-				/* Decide which comparator to use, based on whether the bounding
-				 * boxes overlap
+	ParentSpriteToDraw **psdvend = psdv->End();
+	ParentSpriteToDraw **psd = psdv->Begin();
+	while (psd != psdvend) {
+		ParentSpriteToDraw *ps = *psd;
+
+		if (ps->comparison_done) {
+			psd++;
+			continue;
+		}
+
+		ps->comparison_done = true;
+
+		for (ParentSpriteToDraw **psd2 = psd + 1; psd2 != psdvend; psd2++) {
+			ParentSpriteToDraw *ps2 = *psd2;
+
+			if (ps2->comparison_done) continue;
+
+			/* Decide which comparator to use, based on whether the bounding
+			 * boxes overlap
+			 */
+			if (ps->xmax >= ps2->xmin && ps->xmin <= ps2->xmax && // overlap in X?
+					ps->ymax >= ps2->ymin && ps->ymin <= ps2->ymax && // overlap in Y?
+					ps->zmax >= ps2->zmin && ps->zmin <= ps2->zmax) { // overlap in Z?
+				/* Use X+Y+Z as the sorting order, so sprites closer to the bottom of
+				 * the screen and with higher Z elevation, are drawn in front.
+				 * Here X,Y,Z are the coordinates of the "center of mass" of the sprite,
+				 * i.e. X=(left+right)/2, etc.
+				 * However, since we only care about order, don't actually divide / 2
 				 */
-				if (ps->xmax >= ps2->xmin && ps->xmin <= ps2->xmax && // overlap in X?
-						ps->ymax >= ps2->ymin && ps->ymin <= ps2->ymax && // overlap in Y?
-						ps->zmax >= ps2->zmin && ps->zmin <= ps2->zmax) { // overlap in Z?
-					/* Use X+Y+Z as the sorting order, so sprites closer to the bottom of
-					 * the screen and with higher Z elevation, are drawn in front.
-					 * Here X,Y,Z are the coordinates of the "center of mass" of the sprite,
-					 * i.e. X=(left+right)/2, etc.
-					 * However, since we only care about order, don't actually divide / 2
-					 */
-					if (ps->xmin + ps->xmax + ps->ymin + ps->ymax + ps->zmin + ps->zmax <=
-							ps2->xmin + ps2->xmax + ps2->ymin + ps2->ymax + ps2->zmin + ps2->zmax) {
-						continue;
-					}
-				} else {
-					/* We only change the order, if it is definite.
-					 * I.e. every single order of X, Y, Z says ps2 is behind ps or they overlap.
-					 * That is: If one partial order says ps behind ps2, do not change the order.
-					 */
-					if (ps->xmax < ps2->xmin ||
-							ps->ymax < ps2->ymin ||
-							ps->zmax < ps2->zmin) {
-						continue;
-					}
+				if (ps->xmin + ps->xmax + ps->ymin + ps->ymax + ps->zmin + ps->zmax <=
+						ps2->xmin + ps2->xmax + ps2->ymin + ps2->ymax + ps2->zmin + ps2->zmax) {
+					continue;
 				}
-
-				/* Swap the two sprites ps and ps2 using bubble-sort algorithm. */
-				psd3 = psd;
-				do {
-					ParentSpriteToDraw* temp = *psd3;
-					*psd3 = ps2;
-					ps2 = temp;
-
-					psd3++;
-				} while (psd3 <= psd2);
+			} else {
+				/* We only change the order, if it is definite.
+				 * I.e. every single order of X, Y, Z says ps2 is behind ps or they overlap.
+				 * That is: If one partial order says ps behind ps2, do not change the order.
+				 */
+				if (ps->xmax < ps2->xmin ||
+						ps->ymax < ps2->ymin ||
+						ps->zmax < ps2->zmin) {
+					continue;
+				}
 			}
-		} else {
-			psd++;
+
+			/* Swap the two sprites ps and ps2 using bubble-sort algorithm. */
+			ParentSpriteToDraw **psd3 = psd;
+			do {
+				ParentSpriteToDraw *temp = *psd3;
+				*psd3 = ps2;
+				ps2 = temp;
+
+				psd3++;
+			} while (psd3 <= psd2);
 		}
 	}
 }
 
-static void ViewportDrawParentSprites(ParentSpriteToDraw *psd[])
+static void ViewportDrawParentSprites(const ParentSpriteToSortVector *psd, const ChildScreenSpriteToDrawVector *csstdv)
 {
-	for (; *psd != NULL; psd++) {
-		const ParentSpriteToDraw* ps = *psd;
-		const ChildScreenSpriteToDraw* cs;
-
+	const ParentSpriteToDraw * const *psd_end = psd->End();
+	for (const ParentSpriteToDraw * const *it = psd->Begin(); it != psd_end; it++) {
+		const ParentSpriteToDraw *ps = *it;
 		if (ps->image != SPR_EMPTY_BOUNDING_BOX) DrawSprite(ps->image, ps->pal, ps->x, ps->y, ps->sub);
 
-		for (cs = ps->child; cs != NULL; cs = cs->next) {
+		const ChildScreenSpriteToDraw *last = csstdv->Get(ps->last_child);
+		for (const ChildScreenSpriteToDraw *cs = csstdv->Get(ps->first_child); cs != last; cs++) {
 			DrawSprite(cs->image, cs->pal, ps->left + cs->x, ps->top + cs->y, cs->sub);
 		}
 	}
@@ -1485,10 +1396,11 @@
  * Draws the bounding boxes of all ParentSprites
  * @param psd Array of ParentSprites
  */
-static void ViewportDrawBoundingBoxes(ParentSpriteToDraw *psd[])
+static void ViewportDrawBoundingBoxes(const ParentSpriteToSortVector *psd)
 {
-	for (; *psd != NULL; psd++) {
-		const ParentSpriteToDraw* ps = *psd;
+	const ParentSpriteToDraw * const *psd_end = psd->End();
+	for (const ParentSpriteToDraw * const *it = psd->Begin(); it != psd_end; it++) {
+		const ParentSpriteToDraw *ps = *it;
 		Point pt1 = RemapCoords(ps->xmax + 1, ps->ymax + 1, ps->zmax + 1); // top front corner
 		Point pt2 = RemapCoords(ps->xmin    , ps->ymax + 1, ps->zmax + 1); // top left corner
 		Point pt3 = RemapCoords(ps->xmax + 1, ps->ymin    , ps->zmax + 1); // top right corner
@@ -1501,7 +1413,7 @@
 	}
 }
 
-static void ViewportDrawStrings(DrawPixelInfo *dpi, const StringSpriteToDraw *ss)
+static void ViewportDrawStrings(DrawPixelInfo *dpi, const StringSpriteToDrawVector *sstdv)
 {
 	DrawPixelInfo dp;
 	ZoomLevel zoom;
@@ -1517,15 +1429,13 @@
 	dp.width  = UnScaleByZoom(dp.width,  zoom);
 	dp.height = UnScaleByZoom(dp.height, zoom);
 
-	do {
+	const StringSpriteToDraw *ssend = sstdv->End();
+	for (const StringSpriteToDraw *ss = sstdv->Begin(); ss != ssend; ++ss) {
 		uint16 colour;
 
 		if (ss->width != 0) {
 			/* Do not draw signs nor station names if they are set invisible */
-			if (IsInvisibilitySet(TO_SIGNS) && ss->string != STR_2806) {
-				ss = ss->next;
-				continue;
-			}
+			if (IsInvisibilitySet(TO_SIGNS) && ss->string != STR_2806) continue;
 
 			int x = UnScaleByZoom(ss->x, zoom) - 1;
 			int y = UnScaleByZoom(ss->y, zoom) - 1;
@@ -1564,78 +1474,61 @@
 			UnScaleByZoom(ss->x, zoom), UnScaleByZoom(ss->y, zoom) - (ss->width & 0x8000 ? 2 : 0),
 			ss->string, colour
 		);
-
-		ss = ss->next;
-	} while (ss != NULL);
+	}
 }
 
 void ViewportDoDraw(const ViewPort *vp, int left, int top, int right, int bottom)
 {
-	ViewportDrawer vd;
-	int mask;
-	int x;
-	int y;
-	DrawPixelInfo *old_dpi;
-
-	SmallStackSafeStackAlloc<byte, VIEWPORT_DRAW_MEM> mem;
-	SmallStackSafeStackAlloc<ParentSpriteToDraw*, PARENT_LIST_SIZE> parent_list;
-
-	_cur_vd = &vd;
-
-	old_dpi = _cur_dpi;
-	_cur_dpi = &vd.dpi;
-
-	vd.dpi.zoom = vp->zoom;
-	mask = ScaleByZoom(-1, vp->zoom);
-
-	vd.combine_sprites = 0;
-
-	vd.dpi.width = (right - left) & mask;
-	vd.dpi.height = (bottom - top) & mask;
-	vd.dpi.left = left & mask;
-	vd.dpi.top = top & mask;
-	vd.dpi.pitch = old_dpi->pitch;
-
-	x = UnScaleByZoom(vd.dpi.left - (vp->virtual_left & mask), vp->zoom) + vp->left;
-	y = UnScaleByZoom(vd.dpi.top - (vp->virtual_top & mask), vp->zoom) + vp->top;
-
-	vd.dpi.dst_ptr = BlitterFactoryBase::GetCurrentBlitter()->MoveTo(old_dpi->dst_ptr, x - old_dpi->left, y - old_dpi->top);
-
-	vd.parent_list = parent_list;
-	vd.eof_parent_list = parent_list.EndOf();
-	vd.spritelist_mem = mem;
-	vd.eof_spritelist_mem = mem.EndOf() - sizeof(LARGEST_SPRITELIST_STRUCT);
-	vd.last_string = &vd.first_string;
-	vd.first_string = NULL;
-	vd.last_tile = &vd.first_tile;
-	vd.first_tile = NULL;
+	DrawPixelInfo *old_dpi = _cur_dpi;
+	_cur_dpi = &_vd.dpi;
+
+	_vd.dpi.zoom = vp->zoom;
+	int mask = ScaleByZoom(-1, vp->zoom);
+
+	_vd.combine_sprites = 0;
+
+	_vd.dpi.width = (right - left) & mask;
+	_vd.dpi.height = (bottom - top) & mask;
+	_vd.dpi.left = left & mask;
+	_vd.dpi.top = top & mask;
+	_vd.dpi.pitch = old_dpi->pitch;
+	_vd.last_child = NULL;
+
+	int x = UnScaleByZoom(_vd.dpi.left - (vp->virtual_left & mask), vp->zoom) + vp->left;
+	int y = UnScaleByZoom(_vd.dpi.top - (vp->virtual_top & mask), vp->zoom) + vp->top;
+
+	_vd.dpi.dst_ptr = BlitterFactoryBase::GetCurrentBlitter()->MoveTo(old_dpi->dst_ptr, x - old_dpi->left, y - old_dpi->top);
 
 	ViewportAddLandscape();
-	ViewportAddVehicles(&vd.dpi);
-	DrawTextEffects(&vd.dpi);
-
-	ViewportAddTownNames(&vd.dpi);
-	ViewportAddStationNames(&vd.dpi);
-	ViewportAddSigns(&vd.dpi);
-	ViewportAddWaypoints(&vd.dpi);
-
-	/* This assert should never happen (because the length of the parent_list
-	 *  is checked) */
-	assert(vd.parent_list <= endof(parent_list));
-
-	if (vd.first_tile != NULL) ViewportDrawTileSprites(vd.first_tile);
-
-	/* null terminate parent sprite list */
-	*vd.parent_list = NULL;
-
-	ViewportSortParentSprites(parent_list);
-	ViewportDrawParentSprites(parent_list);
-
-	if (_draw_bounding_boxes) ViewportDrawBoundingBoxes(parent_list);
-
-	if (vd.first_string != NULL) ViewportDrawStrings(&vd.dpi, vd.first_string);
+	ViewportAddVehicles(&_vd.dpi);
+	DrawTextEffects(&_vd.dpi);
+
+	ViewportAddTownNames(&_vd.dpi);
+	ViewportAddStationNames(&_vd.dpi);
+	ViewportAddSigns(&_vd.dpi);
+	ViewportAddWaypoints(&_vd.dpi);
+
+	if (_vd.tile_sprites_to_draw.items != 0) ViewportDrawTileSprites(&_vd.tile_sprites_to_draw);
+
+	ParentSpriteToDraw *psd_end = _vd.parent_sprites_to_draw.End();
+	for (ParentSpriteToDraw *it = _vd.parent_sprites_to_draw.Begin(); it != psd_end; it++) {
+		*_vd.parent_sprites_to_sort.Append() = it;
+	}
+
+	ViewportSortParentSprites(&_vd.parent_sprites_to_sort);
+	ViewportDrawParentSprites(&_vd.parent_sprites_to_sort, &_vd.child_screen_sprites_to_draw);
+
+	if (_draw_bounding_boxes) ViewportDrawBoundingBoxes(&_vd.parent_sprites_to_sort);
+
+	if (_vd.string_sprites_to_draw.items != 0) ViewportDrawStrings(&_vd.dpi, &_vd.string_sprites_to_draw);
 
 	_cur_dpi = old_dpi;
+
+	_vd.string_sprites_to_draw.items = 0;
+	_vd.tile_sprites_to_draw.items = 0;
+	_vd.parent_sprites_to_draw.items = 0;
+	_vd.parent_sprites_to_sort.items = 0;
+	_vd.child_screen_sprites_to_draw.items = 0;
 }
 
 /** Make sure we don't draw a too big area at a time.
--- a/src/viewport_func.h	Wed Apr 16 22:16:04 2008 +0000
+++ b/src/viewport_func.h	Wed Apr 16 22:34:14 2008 +0000
@@ -34,7 +34,7 @@
 void DrawGroundSprite(SpriteID image, SpriteID pal, const SubSprite *sub = NULL);
 void DrawGroundSpriteAt(SpriteID image, SpriteID pal, int32 x, int32 y, byte z, const SubSprite *sub = NULL);
 void AddSortableSpriteToDraw(SpriteID image, SpriteID pal, int x, int y, int w, int h, int dz, int z, bool transparent = false, int bb_offset_x = 0, int bb_offset_y = 0, int bb_offset_z = 0, const SubSprite *sub = NULL);
-void *AddStringToDraw(int x, int y, StringID string, uint64 params_1, uint64 params_2);
+void AddStringToDraw(int x, int y, StringID string, uint64 params_1, uint64 params_2, uint16 color = 0, uint16 width = 0);
 void AddChildSpriteScreen(SpriteID image, SpriteID pal, int x, int y, bool transparent = false, const SubSprite *sub = NULL);
 
 
--- a/src/window.cpp	Wed Apr 16 22:16:04 2008 +0000
+++ b/src/window.cpp	Wed Apr 16 22:34:14 2008 +0000
@@ -2316,7 +2316,7 @@
 
 				const Window *wt = FindWindowById(WC_MAIN_TOOLBAR, 0);
 				if (wt != NULL) {
-					if (top < wt->height) top = wt->height;
+					if (top < wt->height && wt->left < (w->left + w->width) && (wt->left + wt->width) > w->left) top = wt->height;
 					if (top >= newh) top = newh - 1;
 				} else {
 					if (top < 0) top = 0;