Index: binary-improvements2/MapRendering/API.cs
===================================================================
--- binary-improvements2/MapRendering/API.cs	(revision 390)
+++ 	(revision )
@@ -1,39 +1,0 @@
-using AllocsFixes.NetConnections.Servers.Web;
-using AllocsFixes.NetConnections.Servers.Web.Handlers;
-
-namespace AllocsFixes {
-	public class API : IModApi {
-		private Web webInstance;
-		private Mod modInstance;
-		
-		public void InitMod (Mod _modInstance) {
-			ModEvents.GameStartDone.RegisterHandler (GameStartDone);
-			ModEvents.GameShutdown.RegisterHandler (GameShutdown);
-			ModEvents.CalcChunkColorsDone.RegisterHandler (CalcChunkColorsDone);
-			modInstance = _modInstance;
-		}
-
-		private void GameStartDone () {
-			// ReSharper disable once ObjectCreationAsStatement
-			if (!ConnectionManager.Instance.IsServer) {
-				return;
-			}
-			
-			webInstance = new Web (modInstance.Path);
-			LogBuffer.Init ();
-
-			if (ItemIconHandler.Instance != null) {
-				ItemIconHandler.Instance.LoadIcons ();
-			}
-		}
-
-		private void GameShutdown () {
-			webInstance?.Shutdown ();
-			MapRendering.MapRendering.Shutdown ();
-		}
-
-		private void CalcChunkColorsDone (Chunk _chunk) {
-			MapRendering.MapRendering.RenderSingleChunk (_chunk);
-		}
-	}
-}
Index: binary-improvements2/MapRendering/AssemblyInfo.cs
===================================================================
--- binary-improvements2/MapRendering/AssemblyInfo.cs	(revision 390)
+++ 	(revision )
@@ -1,25 +1,0 @@
-using System.Reflection;
-
-// Information about this assembly is defined by the following attributes. 
-// Change them to the values specific to your project.
-
-[assembly: AssemblyTitle ("MapRendering")]
-[assembly: AssemblyDescription ("")]
-[assembly: AssemblyConfiguration ("")]
-[assembly: AssemblyCompany ("")]
-[assembly: AssemblyProduct ("")]
-[assembly: AssemblyCopyright ("ci")]
-[assembly: AssemblyTrademark ("")]
-[assembly: AssemblyCulture ("")]
-
-// The assembly version has the format "{Major}.{Minor}.{Build}.{Revision}".
-// The form "{Major}.{Minor}.*" will automatically update the build and revision,
-// and "{Major}.{Minor}.{Build}.*" will update just the revision.
-
-[assembly: AssemblyVersion ("0.0.0.0")]
-
-// The following attributes are used to specify the signing key for the assembly, 
-// if desired. See the Mono documentation for more information about signing.
-
-//[assembly: AssemblyDelaySign(false)]
-//[assembly: AssemblyKeyFile("")]
Index: binary-improvements2/MapRendering/Commands/EnableOpenIDDebug.cs
===================================================================
--- binary-improvements2/MapRendering/Commands/EnableOpenIDDebug.cs	(revision 390)
+++ 	(revision )
@@ -1,24 +1,0 @@
-using System.Collections.Generic;
-using AllocsFixes.NetConnections.Servers.Web;
-
-namespace AllocsFixes.CustomCommands {
-	public class EnableOpenIDDebug : ConsoleCmdAbstract {
-		public override string GetDescription () {
-			return "enable/disable OpenID debugging";
-		}
-
-		public override string[] GetCommands () {
-			return new[] {"openiddebug"};
-		}
-
-		public override void Execute (List<string> _params, CommandSenderInfo _senderInfo) {
-			if (_params.Count != 1) {
-				SdtdConsole.Instance.Output ("Current state: " + OpenID.debugOpenId);
-				return;
-			}
-
-			OpenID.debugOpenId = _params [0].Equals ("1");
-			SdtdConsole.Instance.Output ("Set OpenID debugging to " + _params [0].Equals ("1"));
-		}
-	}
-}
Index: binary-improvements2/MapRendering/Commands/EnableRendering.cs
===================================================================
--- binary-improvements2/MapRendering/Commands/EnableRendering.cs	(revision 390)
+++ 	(revision )
@@ -1,23 +1,0 @@
-using System.Collections.Generic;
-
-namespace AllocsFixes.CustomCommands {
-	public class EnableRendering : ConsoleCmdAbstract {
-		public override string GetDescription () {
-			return "enable/disable live map rendering";
-		}
-
-		public override string[] GetCommands () {
-			return new[] {"enablerendering"};
-		}
-
-		public override void Execute (List<string> _params, CommandSenderInfo _senderInfo) {
-			if (_params.Count != 1) {
-				SdtdConsole.Instance.Output ("Current state: " + MapRendering.MapRendering.renderingEnabled);
-				return;
-			}
-
-			MapRendering.MapRendering.renderingEnabled = _params [0].Equals ("1");
-			SdtdConsole.Instance.Output ("Set live map rendering to " + _params [0].Equals ("1"));
-		}
-	}
-}
Index: binary-improvements2/MapRendering/Commands/Exception.cs
===================================================================
--- binary-improvements2/MapRendering/Commands/Exception.cs	(revision 390)
+++ 	(revision )
@@ -1,23 +1,0 @@
-using System;
-using System.Collections.Generic;
-
-namespace AllocsFixes.CustomCommands {
-	public class ConsoleCmdException : ConsoleCmdAbstract {
-		public override string[] GetCommands () {
-			return new[] { "exception" };
-		}
-
-		public override bool AllowedInMainMenu => true;
-
-		public override void Execute (List<string> _params, CommandSenderInfo _senderInfo) {
-			Log.Out ("Test info");
-			Log.Warning ("Test warning");
-			Log.Error ("Test error");
-			throw new Exception ("Test exception");
-		}
-
-		public override string GetDescription () {
-			return "Throw an exception / log messages";
-		}
-	}
-}
Index: binary-improvements2/MapRendering/Commands/ReloadWebPermissions.cs
===================================================================
--- binary-improvements2/MapRendering/Commands/ReloadWebPermissions.cs	(revision 390)
+++ 	(revision )
@@ -1,19 +1,0 @@
-using System.Collections.Generic;
-using AllocsFixes.NetConnections.Servers.Web;
-
-namespace AllocsFixes.CustomCommands {
-	public class ReloadWebPermissions : ConsoleCmdAbstract {
-		public override string GetDescription () {
-			return "force reload of web permissions file";
-		}
-
-		public override string[] GetCommands () {
-			return new[] {"reloadwebpermissions"};
-		}
-
-		public override void Execute (List<string> _params, CommandSenderInfo _senderInfo) {
-			WebPermissions.Instance.Load ();
-			SdtdConsole.Instance.Output ("Web permissions file reloaded");
-		}
-	}
-}
Index: binary-improvements2/MapRendering/Commands/RenderMap.cs
===================================================================
--- binary-improvements2/MapRendering/Commands/RenderMap.cs	(revision 390)
+++ 	(revision )
@@ -1,19 +1,0 @@
-using System.Collections.Generic;
-
-namespace AllocsFixes.CustomCommands {
-	public class RenderMap : ConsoleCmdAbstract {
-		public override string GetDescription () {
-			return "render the current map to a file";
-		}
-
-		public override string[] GetCommands () {
-			return new[] {"rendermap"};
-		}
-
-		public override void Execute (List<string> _params, CommandSenderInfo _senderInfo) {
-			MapRendering.MapRendering.Instance.RenderFullMap ();
-
-			SdtdConsole.Instance.Output ("Render map done");
-		}
-	}
-}
Index: binary-improvements2/MapRendering/Commands/WebPermissionsCmd.cs
===================================================================
--- binary-improvements2/MapRendering/Commands/WebPermissionsCmd.cs	(revision 390)
+++ 	(revision )
@@ -1,83 +1,0 @@
-using System.Collections.Generic;
-using AllocsFixes.NetConnections.Servers.Web;
-
-namespace AllocsFixes.CustomCommands {
-	public class WebPermissionsCmd : ConsoleCmdAbstract {
-		public override string[] GetCommands () {
-			return new[] {"webpermission"};
-		}
-
-		public override string GetDescription () {
-			return "Manage web permission levels";
-		}
-
-		public override string GetHelp () {
-			return "Set/get permission levels required to access a given web functionality. Default\n" +
-			       "level required for functions that are not explicitly specified is 0.\n" +
-			       "Usage:\n" +
-			       "   webpermission add <webfunction> <level>\n" +
-			       "   webpermission remove <webfunction>\n" +
-			       "   webpermission list";
-		}
-
-		public override void Execute (List<string> _params, CommandSenderInfo _senderInfo) {
-			if (_params.Count >= 1) {
-				if (_params [0].EqualsCaseInsensitive ("add")) {
-					ExecuteAdd (_params);
-				} else if (_params [0].EqualsCaseInsensitive ("remove")) {
-					ExecuteRemove (_params);
-				} else if (_params [0].EqualsCaseInsensitive ("list")) {
-					ExecuteList ();
-				} else {
-					SdtdConsole.Instance.Output ($"Invalid sub command \"{_params [0]}\".");
-				}
-			} else {
-				SdtdConsole.Instance.Output ("No sub command given.");
-			}
-		}
-
-		private void ExecuteAdd (List<string> _params) {
-			if (_params.Count != 3) {
-				SdtdConsole.Instance.Output ($"Wrong number of arguments, expected 3, found {_params.Count}.");
-				return;
-			}
-
-			if (!WebPermissions.Instance.IsKnownModule (_params [1])) {
-				SdtdConsole.Instance.Output ($"\"{_params [1]}\" is not a valid web function.");
-				return;
-			}
-
-			int level;
-			if (!int.TryParse (_params [2], out level)) {
-				SdtdConsole.Instance.Output ($"\"{_params [2]}\" is not a valid integer.");
-				return;
-			}
-
-			WebPermissions.Instance.AddModulePermission (_params [1], level);
-			SdtdConsole.Instance.Output ($"{_params [1]} added with permission level of {level}.");
-		}
-
-		private void ExecuteRemove (List<string> _params) {
-			if (_params.Count != 2) {
-				SdtdConsole.Instance.Output ($"Wrong number of arguments, expected 2, found {_params.Count}.");
-				return;
-			}
-
-			if (!WebPermissions.Instance.IsKnownModule (_params [1])) {
-				SdtdConsole.Instance.Output ($"\"{_params [1]}\" is not a valid web function.");
-				return;
-			}
-
-			WebPermissions.Instance.RemoveModulePermission (_params [1]);
-			SdtdConsole.Instance.Output ($"{_params [1]} removed from permissions list.");
-		}
-
-		private void ExecuteList () {
-			SdtdConsole.Instance.Output ("Defined web function permissions:");
-			SdtdConsole.Instance.Output ("  Level: Web function");
-			foreach (WebPermissions.WebModulePermission wmp in WebPermissions.Instance.GetModules ()) {
-				SdtdConsole.Instance.Output ($"  {wmp.permissionLevel,5}: {wmp.module}");
-			}
-		}
-	}
-}
Index: binary-improvements2/MapRendering/Commands/WebTokens.cs
===================================================================
--- binary-improvements2/MapRendering/Commands/WebTokens.cs	(revision 390)
+++ 	(revision )
@@ -1,111 +1,0 @@
-using System.Collections.Generic;
-using System.Text.RegularExpressions;
-using AllocsFixes.NetConnections.Servers.Web;
-
-namespace AllocsFixes.CustomCommands {
-	public class WebTokens : ConsoleCmdAbstract {
-		private static readonly Regex validNameTokenMatcher = new Regex (@"^\w+$");
-
-		public override string[] GetCommands () {
-			return new[] {"webtokens"};
-		}
-
-		public override string GetDescription () {
-			return "Manage web tokens";
-		}
-
-		public override string GetHelp () {
-			return "Set/get webtoken permission levels. A level of 0 is maximum permission.\n" +
-			       "Usage:\n" +
-			       "   webtokens add <username> <usertoken> <level>\n" +
-			       "   webtokens remove <username>\n" +
-			       "   webtokens list";
-		}
-
-		public override void Execute (List<string> _params, CommandSenderInfo _senderInfo) {
-			if (_params.Count >= 1) {
-				if (_params [0].EqualsCaseInsensitive ("add")) {
-					ExecuteAdd (_params);
-				} else if (_params [0].EqualsCaseInsensitive ("remove")) {
-					ExecuteRemove (_params);
-				} else if (_params [0].EqualsCaseInsensitive ("list")) {
-					ExecuteList ();
-				} else {
-					SdtdConsole.Instance.Output ("Invalid sub command \"" + _params [0] + "\".");
-				}
-			} else {
-				SdtdConsole.Instance.Output ("No sub command given.");
-			}
-		}
-
-		private void ExecuteAdd (List<string> _params) {
-			if (_params.Count != 4) {
-				SdtdConsole.Instance.Output ("Wrong number of arguments, expected 4, found " + _params.Count + ".");
-				return;
-			}
-
-			if (string.IsNullOrEmpty (_params [1])) {
-				SdtdConsole.Instance.Output ("Argument 'username' is empty.");
-				return;
-			}
-
-			if (!validNameTokenMatcher.IsMatch (_params [1])) {
-				SdtdConsole.Instance.Output (
-					"Argument 'username' may only contain characters (A-Z, a-z), digits (0-9) and underscores (_).");
-				return;
-			}
-
-			if (string.IsNullOrEmpty (_params [2])) {
-				SdtdConsole.Instance.Output ("Argument 'usertoken' is empty.");
-				return;
-			}
-
-			if (!validNameTokenMatcher.IsMatch (_params [2])) {
-				SdtdConsole.Instance.Output (
-					"Argument 'usertoken' may only contain characters (A-Z, a-z), digits (0-9) and underscores (_).");
-				return;
-			}
-
-			int level;
-			if (!int.TryParse (_params [3], out level)) {
-				SdtdConsole.Instance.Output ("Argument 'level' is not a valid integer.");
-				return;
-			}
-
-			WebPermissions.Instance.AddAdmin (_params [1], _params [2], level);
-			SdtdConsole.Instance.Output (string.Format (
-				"Web user with name={0} and password={1} added with permission level of {2}.", _params [1], _params [2],
-				level));
-		}
-
-		private void ExecuteRemove (List<string> _params) {
-			if (_params.Count != 2) {
-				SdtdConsole.Instance.Output ("Wrong number of arguments, expected 2, found " + _params.Count + ".");
-				return;
-			}
-
-			if (string.IsNullOrEmpty (_params [1])) {
-				SdtdConsole.Instance.Output ("Argument 'username' is empty.");
-				return;
-			}
-
-			if (!validNameTokenMatcher.IsMatch (_params [1])) {
-				SdtdConsole.Instance.Output (
-					"Argument 'username' may only contain characters (A-Z, a-z), digits (0-9) and underscores (_).");
-				return;
-			}
-
-			WebPermissions.Instance.RemoveAdmin (_params [1]);
-			SdtdConsole.Instance.Output (string.Format ("{0} removed from web user permissions list.", _params [1]));
-		}
-
-		private void ExecuteList () {
-			SdtdConsole.Instance.Output ("Defined webuser permissions:");
-			SdtdConsole.Instance.Output ("  Level: Name / Token");
-			foreach (WebPermissions.AdminToken at in WebPermissions.Instance.GetAdmins ()) {
-				SdtdConsole.Instance.Output (
-					string.Format ("  {0,5}: {1} / {2}", at.permissionLevel, at.name, at.token));
-			}
-		}
-	}
-}
Index: binary-improvements2/MapRendering/Commands/webstat.cs
===================================================================
--- binary-improvements2/MapRendering/Commands/webstat.cs	(revision 390)
+++ 	(revision )
@@ -1,33 +1,0 @@
-using System.Collections.Generic;
-using AllocsFixes.NetConnections.Servers.Web;
-
-namespace AllocsFixes.CustomCommands {
-	public class webstat : ConsoleCmdAbstract {
-		public override string GetDescription () {
-			return "DEBUG PURPOSES ONLY";
-		}
-
-		public override string[] GetCommands () {
-			return new[] {"webstat"};
-		}
-
-		public override void Execute (List<string> _params, CommandSenderInfo _senderInfo) {
-			int curHandlers = Web.currentHandlers;
-			int totalHandlers = Web.handlingCount;
-			long totalTime = Web.totalHandlingTime;
-			SdtdConsole.Instance.Output ("Current Web handlers: " + curHandlers + " - total: " + totalHandlers);
-			SdtdConsole.Instance.Output (" - Total time: " + totalTime + " µs - average time: " +
-			                             totalTime / totalHandlers + " µs");
-
-			curHandlers = WebCommandResult.currentHandlers;
-			totalHandlers = WebCommandResult.handlingCount;
-			totalTime = WebCommandResult.totalHandlingTime;
-			SdtdConsole.Instance.Output ("Current Web command handlers: " + curHandlers + " - total: " +
-			                             totalHandlers);
-			SdtdConsole.Instance.Output (" - Total time: " + totalTime + " µs" +
-			                             (totalHandlers > 0
-				                             ? " - average time: " + totalTime / totalHandlers + " µs"
-				                             : ""));
-		}
-	}
-}
Index: binary-improvements2/MapRendering/MapRendering.csproj
===================================================================
--- binary-improvements2/MapRendering/MapRendering.csproj	(revision 391)
+++ binary-improvements2/MapRendering/MapRendering.csproj	(revision 391)
@@ -0,0 +1,102 @@
+﻿<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+  <PropertyGroup>
+    <Configuration Condition=" '$(Configuration)' == '' ">Release</Configuration>
+    <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
+    <ProductVersion>8.0.30703</ProductVersion>
+    <SchemaVersion>2.0</SchemaVersion>
+    <ProjectGuid>{A1847B5F-7BFC-4BCD-94AA-A6C9FB7E7C54}</ProjectGuid>
+    <OutputType>Library</OutputType>
+    <RootNamespace>MapRendering</RootNamespace>
+    <AssemblyName>MapRendering</AssemblyName>
+    <TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
+    <LangVersion>8</LangVersion>
+  </PropertyGroup>
+  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
+    <DebugType>none</DebugType>
+    <Optimize>true</Optimize>
+    <OutputPath>..\bin\Mods\TFP_MapRendering\</OutputPath>
+    <ErrorReport>prompt</ErrorReport>
+    <WarningLevel>4</WarningLevel>
+    <ConsolePause>false</ConsolePause>
+    <NoStdLib>true</NoStdLib>
+  </PropertyGroup>
+  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release_Profiler|AnyCPU' ">
+    <OutputPath>..\bin\Mods\TFP_MapRendering\</OutputPath>
+    <DefineConstants>ENABLE_PROFILER</DefineConstants>
+    <Optimize>true</Optimize>
+    <DebugSymbols>true</DebugSymbols>
+    <DebugType>full</DebugType>
+    <WarningLevel>4</WarningLevel>
+    <NoStdLib>true</NoStdLib>
+  </PropertyGroup>
+  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
+    <OutputPath>..\bin\Mods\TFP_MapRendering\</OutputPath>
+    <DebugType>full</DebugType>
+    <DebugSymbols>true</DebugSymbols>
+  </PropertyGroup>
+  <ItemGroup>
+    <Reference Include="Assembly-CSharp-firstpass, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null">
+      <HintPath>..\7dtd-binaries\Assembly-CSharp-firstpass.dll</HintPath>
+      <Private>False</Private>
+    </Reference>
+    <Reference Include="LogLibrary">
+      <HintPath>..\7dtd-binaries\LogLibrary.dll</HintPath>
+      <Private>False</Private>
+    </Reference>
+    <Reference Include="mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
+      <HintPath>..\7dtd-binaries\mscorlib.dll</HintPath>
+      <Private>False</Private>
+    </Reference>
+    <Reference Include="System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
+      <HintPath>..\7dtd-binaries\System.dll</HintPath>
+      <Private>False</Private>
+    </Reference>
+    <Reference Include="System.Xml, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
+      <HintPath>..\7dtd-binaries\System.Xml.dll</HintPath>
+      <Private>False</Private>
+    </Reference>
+    <Reference Include="UnityEngine">
+      <HintPath>..\7dtd-binaries\UnityEngine.dll</HintPath>
+      <Private>False</Private>
+    </Reference>
+    <Reference Include="Assembly-CSharp">
+      <HintPath>..\7dtd-binaries\Assembly-CSharp.dll</HintPath>
+      <Private>False</Private>
+    </Reference>
+    <Reference Include="mscorlib">
+      <HintPath>..\7dtd-binaries\mscorlib.dll</HintPath>
+      <Private>False</Private>
+    </Reference>
+    <Reference Include="UnityEngine.CoreModule, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null">
+      <HintPath>..\7dtd-binaries\UnityEngine.CoreModule.dll</HintPath>
+      <Private>False</Private>
+    </Reference>
+    <Reference Include="UnityEngine.ImageConversionModule, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null">
+      <HintPath>..\7dtd-binaries\UnityEngine.ImageConversionModule.dll</HintPath>
+      <Private>False</Private>
+    </Reference>
+  </ItemGroup>
+  <ItemGroup>
+    <Compile Include="src\Constants.cs" />
+    <Compile Include="src\MapRenderBlockBuffer.cs" />
+    <Compile Include="src\MapRenderer.cs" />
+    <Compile Include="src\ModApi.cs" />
+    <Compile Include="src\AssemblyInfo.cs" />
+    <Compile Include="src\Commands\EnableRendering.cs" />
+    <Compile Include="src\Commands\RenderMap.cs" />
+  </ItemGroup>
+  <Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
+  <ItemGroup>
+    <ProjectReference Include="..\7dtd-server-fixes\7dtd-server-fixes.csproj">
+      <Project>{81DA7F87-1A66-4920-AADA-6EAF1971F8D0}</Project>
+      <Name>7dtd-server-fixes</Name>
+      <Private>False</Private>
+    </ProjectReference>
+  </ItemGroup>
+  <ItemGroup>
+    <None Include="ModInfo.xml">
+      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
+    </None>
+  </ItemGroup>
+</Project>
Index: binary-improvements2/MapRendering/MapRendering/Constants.cs
===================================================================
--- binary-improvements2/MapRendering/MapRendering/Constants.cs	(revision 390)
+++ 	(revision )
@@ -1,20 +1,0 @@
-using UnityEngine;
-
-namespace AllocsFixes.MapRendering {
-	public class Constants {
-		public static readonly TextureFormat DEFAULT_TEX_FORMAT = TextureFormat.ARGB32;
-		public static int MAP_BLOCK_SIZE = 128;
-		public const int MAP_CHUNK_SIZE = 16;
-		public const int MAP_REGION_SIZE = 512;
-		public static int ZOOMLEVELS = 5;
-		public static string MAP_DIRECTORY = string.Empty;
-
-		public static int MAP_BLOCK_TO_CHUNK_DIV {
-			get { return MAP_BLOCK_SIZE / MAP_CHUNK_SIZE; }
-		}
-
-		public static int MAP_REGION_TO_CHUNK_DIV {
-			get { return MAP_REGION_SIZE / MAP_CHUNK_SIZE; }
-		}
-	}
-}
Index: binary-improvements2/MapRendering/MapRendering/MapRenderBlockBuffer.cs
===================================================================
--- binary-improvements2/MapRendering/MapRendering/MapRenderBlockBuffer.cs	(revision 390)
+++ 	(revision )
@@ -1,232 +1,0 @@
-using System;
-using System.IO;
-using AllocsFixes.FileCache;
-using Unity.Collections;
-using UnityEngine;
-using UnityEngine.Profiling;
-
-namespace AllocsFixes.MapRendering {
-	public class MapRenderBlockBuffer {
-		private readonly Texture2D blockMap = new Texture2D (Constants.MAP_BLOCK_SIZE, Constants.MAP_BLOCK_SIZE, Constants.DEFAULT_TEX_FORMAT, false);
-		private readonly MapTileCache cache;
-		private readonly NativeArray<int> emptyImageData;
-		private readonly Texture2D zoomBuffer = new Texture2D (Constants.MAP_BLOCK_SIZE / 2, Constants.MAP_BLOCK_SIZE / 2, Constants.DEFAULT_TEX_FORMAT, false);
-		private readonly int zoomLevel;
-		private readonly string folderBase;
-		
-		private Vector2i currentBlockMapPos = new Vector2i (Int32.MinValue, Int32.MinValue);
-		private string currentBlockMapFolder = string.Empty;
-
-		public MapRenderBlockBuffer (int _level, MapTileCache _cache) {
-			zoomLevel = _level;
-			cache = _cache;
-			folderBase = Constants.MAP_DIRECTORY + "/" + zoomLevel + "/";
-
-			{
-				// Initialize empty tile data
-				Color nullColor = new Color (0, 0, 0, 0);
-				for (int x = 0; x < Constants.MAP_BLOCK_SIZE; x++) {
-					for (int y = 0; y < Constants.MAP_BLOCK_SIZE; y++) {
-						blockMap.SetPixel (x, y, nullColor);
-					}
-				}
-
-				NativeArray<int> blockMapData = blockMap.GetRawTextureData<int> ();
-				emptyImageData = new NativeArray<int> (blockMapData.Length, Allocator.Persistent,
-					NativeArrayOptions.UninitializedMemory);
-				blockMapData.CopyTo (emptyImageData);
-			}
-		}
-
-		public TextureFormat FormatSelf {
-			get { return blockMap.format; }
-		}
-
-		public void ResetBlock () {
-			currentBlockMapFolder = string.Empty;
-			currentBlockMapPos = new Vector2i (Int32.MinValue, Int32.MinValue);
-			cache.ResetTile (zoomLevel);
-		}
-
-		public void SaveBlock () {
-			Profiler.BeginSample ("SaveBlock");
-			try {
-				saveTextureToFile ();
-			} catch (Exception e) {
-				Log.Warning ("Exception in MapRenderBlockBuffer.SaveBlock(): " + e);
-			}
-			Profiler.EndSample ();
-		}
-
-		public bool LoadBlock (Vector2i _block) {
-			Profiler.BeginSample ("LoadBlock");
-			lock (blockMap) {
-				if (currentBlockMapPos != _block) {
-					Profiler.BeginSample ("LoadBlock.Strings");
-					string folder;
-					if (currentBlockMapPos.x != _block.x) {
-						folder = folderBase + _block.x + '/';
-
-						Profiler.BeginSample ("LoadBlock.Directory");
-						Directory.CreateDirectory (folder);
-						Profiler.EndSample ();
-					} else {
-						folder = currentBlockMapFolder;
-					}
-
-					string fileName = folder + _block.y + ".png";
-					Profiler.EndSample ();
-					
-					SaveBlock ();
-					loadTextureFromFile (fileName);
-
-					currentBlockMapFolder = folder;
-					currentBlockMapPos = _block;
-
-					Profiler.EndSample ();
-					return true;
-				}
-			}
-
-			Profiler.EndSample ();
-			return false;
-		}
-
-		public void SetPart (Vector2i _offset, int _partSize, Color32[] _pixels) {
-			if (_offset.x + _partSize > Constants.MAP_BLOCK_SIZE || _offset.y + _partSize > Constants.MAP_BLOCK_SIZE) {
-				Log.Error (string.Format ("MapBlockBuffer[{0}].SetPart ({1}, {2}, {3}) has blockMap.size ({4}/{5})",
-					zoomLevel, _offset, _partSize, _pixels.Length, Constants.MAP_BLOCK_SIZE, Constants.MAP_BLOCK_SIZE));
-				return;
-			}
-
-			Profiler.BeginSample ("SetPart");
-			blockMap.SetPixels32 (_offset.x, _offset.y, _partSize, _partSize, _pixels);
-			Profiler.EndSample ();
-		}
-
-		public Color32[] GetHalfScaled () {
-			Profiler.BeginSample ("HalfScaled.ResizeBuffer");
-			zoomBuffer.Resize (Constants.MAP_BLOCK_SIZE, Constants.MAP_BLOCK_SIZE);
-			Profiler.EndSample ();
-
-			Profiler.BeginSample ("HalfScaled.CopyPixels");
-			if (blockMap.format == zoomBuffer.format) {
-				Profiler.BeginSample ("Native");
-				NativeArray<byte> dataSrc = blockMap.GetRawTextureData<byte> ();
-				NativeArray<byte> dataZoom = zoomBuffer.GetRawTextureData<byte> ();
-				dataSrc.CopyTo (dataZoom);
-				Profiler.EndSample ();
-			} else {
-				Profiler.BeginSample ("GetSetPixels");
-				zoomBuffer.SetPixels32 (blockMap.GetPixels32 ());
-				Profiler.EndSample ();
-			}
-			Profiler.EndSample ();
-
-			Profiler.BeginSample ("HalfScaled.Scale");
-			TextureScale.Point (zoomBuffer, Constants.MAP_BLOCK_SIZE / 2, Constants.MAP_BLOCK_SIZE / 2);
-			Profiler.EndSample ();
-
-			Profiler.BeginSample ("HalfScaled.Return");
-			Color32[] result = zoomBuffer.GetPixels32 ();
-			Profiler.EndSample ();
-
-			return result;
-		}
-
-		public void SetPartNative (Vector2i _offset, int _partSize, NativeArray<int> _pixels) {
-			if (_offset.x + _partSize > Constants.MAP_BLOCK_SIZE || _offset.y + _partSize > Constants.MAP_BLOCK_SIZE) {
-				Log.Error (string.Format ("MapBlockBuffer[{0}].SetPart ({1}, {2}, {3}) has blockMap.size ({4}/{5})",
-					zoomLevel, _offset, _partSize, _pixels.Length, Constants.MAP_BLOCK_SIZE, Constants.MAP_BLOCK_SIZE));
-				return;
-			}
-
-			Profiler.BeginSample ("SetPartNative");
-			NativeArray<int> destData = blockMap.GetRawTextureData<int> ();
-			
-			for (int y = 0; y < _partSize; y++) {
-				int srcLineStartIdx = _partSize * y;
-				int destLineStartIdx = blockMap.width * (_offset.y + y) + _offset.x;
-				for (int x = 0; x < _partSize; x++) {
-					destData [destLineStartIdx + x] = _pixels [srcLineStartIdx + x];
-				}
-			}
-			Profiler.EndSample ();
-		}
-
-		public NativeArray<int> GetHalfScaledNative () {
-			Profiler.BeginSample ("HalfScaledNative.ResizeBuffer");
-			if (zoomBuffer.format != blockMap.format || zoomBuffer.height != Constants.MAP_BLOCK_SIZE / 2 || zoomBuffer.width != Constants.MAP_BLOCK_SIZE / 2) {
-				zoomBuffer.Resize (Constants.MAP_BLOCK_SIZE / 2, Constants.MAP_BLOCK_SIZE / 2, blockMap.format, false);
-			}
-			Profiler.EndSample ();
-
-			Profiler.BeginSample ("HalfScaledNative.Scale");
-			ScaleNative (blockMap, zoomBuffer);
-			Profiler.EndSample ();
-
-			return zoomBuffer.GetRawTextureData<int> ();
-		}
-		
-		private static void ScaleNative (Texture2D _sourceTex, Texture2D _targetTex) {
-			NativeArray<int> srcData = _sourceTex.GetRawTextureData<int> ();
-			NativeArray<int> targetData = _targetTex.GetRawTextureData<int> ();
-			
-			int oldWidth = _sourceTex.width;
-			int oldHeight = _sourceTex.height;
-			int newWidth = _targetTex.width;
-			int newHeight = _targetTex.height;
-			
-			float ratioX = ((float) oldWidth) / newWidth;
-			float ratioY = ((float) oldHeight) / newHeight;
-
-			for (var y = 0; y < newHeight; y++) {
-				var oldLineStart = (int) (ratioY * y) * oldWidth;
-				var newLineStart = y * newWidth;
-				for (var x = 0; x < newWidth; x++) {
-					targetData [newLineStart + x] = srcData [(int) (oldLineStart + ratioX * x)];
-				}
-			}
-		}
-
-		private void loadTextureFromFile (string _fileName) {
-			Profiler.BeginSample ("LoadTexture");
-
-			Profiler.BeginSample ("LoadFile");
-			byte[] array = cache.LoadTile (zoomLevel, _fileName);
-			Profiler.EndSample ();
-
-			Profiler.BeginSample ("LoadImage");
-			if (array != null && blockMap.LoadImage (array) && blockMap.height == Constants.MAP_BLOCK_SIZE &&
-			    blockMap.width == Constants.MAP_BLOCK_SIZE) {
-				Profiler.EndSample ();
-
-				Profiler.EndSample ();
-				return;
-			}
-			Profiler.EndSample ();
-
-			if (array != null) {
-				Log.Error ("Map image tile " + _fileName + " has been corrupted, recreating tile");
-			}
-
-			if (blockMap.format != Constants.DEFAULT_TEX_FORMAT || blockMap.height != Constants.MAP_BLOCK_SIZE ||
-			    blockMap.width != Constants.MAP_BLOCK_SIZE) {
-				blockMap.Resize (Constants.MAP_BLOCK_SIZE, Constants.MAP_BLOCK_SIZE, Constants.DEFAULT_TEX_FORMAT,
-					false);
-			}
-
-			blockMap.LoadRawTextureData (emptyImageData);
-
-			Profiler.EndSample ();
-		}
-
-		private void saveTextureToFile () {
-			Profiler.BeginSample ("EncodePNG");
-			byte[] array = blockMap.EncodeToPNG ();
-			Profiler.EndSample ();
-
-			cache.SaveTile (zoomLevel, array);
-		}
-	}
-}
Index: binary-improvements2/MapRendering/MapRendering/MapRendering.cs
===================================================================
--- binary-improvements2/MapRendering/MapRendering/MapRendering.cs	(revision 390)
+++ 	(revision )
@@ -1,420 +1,0 @@
-using System;
-using System.Collections;
-using System.Collections.Generic;
-using System.IO;
-using System.Text;
-using System.Threading;
-using AllocsFixes.FileCache;
-using AllocsFixes.JSON;
-using UnityEngine;
-using UnityEngine.Profiling;
-using Object = UnityEngine.Object;
-
-namespace AllocsFixes.MapRendering {
-	public class MapRendering {
-		private static MapRendering instance;
-
-		private static readonly object lockObject = new object ();
-		public static bool renderingEnabled = true;
-		private readonly MapTileCache cache = new MapTileCache (Constants.MAP_BLOCK_SIZE);
-		private readonly Dictionary<Vector2i, Color32[]> dirtyChunks = new Dictionary<Vector2i, Color32[]> ();
-		private readonly MicroStopwatch msw = new MicroStopwatch ();
-		private readonly MapRenderBlockBuffer[] zoomLevelBuffers;
-		private Coroutine renderCoroutineRef;
-		private bool renderingFullMap;
-		private float renderTimeout = float.MaxValue;
-		private bool shutdown;
-
-		private MapRendering () {
-			Constants.MAP_DIRECTORY = GameIO.GetSaveGameDir () + "/map";
-
-			lock (lockObject) {
-				if (!LoadMapInfo ()) {
-					WriteMapInfo ();
-				}
-			}
-
-			cache.SetZoomCount (Constants.ZOOMLEVELS);
-
-			zoomLevelBuffers = new MapRenderBlockBuffer[Constants.ZOOMLEVELS];
-			for (int i = 0; i < Constants.ZOOMLEVELS; i++) {
-				zoomLevelBuffers [i] = new MapRenderBlockBuffer (i, cache);
-			}
-
-			renderCoroutineRef = ThreadManager.StartCoroutine (renderCoroutine ());
-		}
-
-		public static MapRendering Instance {
-			get {
-				if (instance == null) {
-					instance = new MapRendering ();
-				}
-
-				return instance;
-			}
-		}
-
-		public static MapTileCache GetTileCache () {
-			return Instance.cache;
-		}
-
-		public static void Shutdown () {
-			if (Instance == null) {
-				return;
-			}
-
-			Instance.shutdown = true;
-			
-			if (Instance.renderCoroutineRef != null) {
-				ThreadManager.StopCoroutine (Instance.renderCoroutineRef);
-				Instance.renderCoroutineRef = null;
-			}
-		}
-
-		public static void RenderSingleChunk (Chunk _chunk) {
-			if (renderingEnabled && Instance != null) {
-				// TODO: Replace with regular thread and a blocking queue / set
-				ThreadPool.UnsafeQueueUserWorkItem (_o => {
-					try {
-						if (!Instance.renderingFullMap) {
-							lock (lockObject) {
-								Chunk c = (Chunk) _o;
-								Vector3i cPos = c.GetWorldPos ();
-								Vector2i cPos2 = new Vector2i (cPos.x / Constants.MAP_CHUNK_SIZE,
-									cPos.z / Constants.MAP_CHUNK_SIZE);
-
-								ushort[] mapColors = c.GetMapColors ();
-								if (mapColors != null) {
-									Color32[] realColors =
-										new Color32[Constants.MAP_CHUNK_SIZE * Constants.MAP_CHUNK_SIZE];
-									for (int i_colors = 0; i_colors < mapColors.Length; i_colors++) {
-										realColors [i_colors] = shortColorToColor32 (mapColors [i_colors]);
-									}
-
-									Instance.dirtyChunks [cPos2] = realColors;
-
-									//Log.Out ("Add Dirty: " + cPos2);
-								}
-							}
-						}
-					} catch (Exception e) {
-						Log.Out ("Exception in MapRendering.RenderSingleChunk(): " + e);
-					}
-				}, _chunk);
-			}
-		}
-
-		public void RenderFullMap () {
-			MicroStopwatch microStopwatch = new MicroStopwatch ();
-
-			string regionSaveDir = GameIO.GetSaveGameRegionDir ();
-			RegionFileManager rfm = new RegionFileManager (regionSaveDir, regionSaveDir, 0, false);
-			Texture2D fullMapTexture = null;
-
-			Vector2i minChunk, maxChunk;
-			Vector2i minPos, maxPos;
-			int widthChunks, heightChunks, widthPix, heightPix;
-			getWorldExtent (rfm, out minChunk, out maxChunk, out minPos, out maxPos, out widthChunks, out heightChunks,
-				out widthPix, out heightPix);
-
-			Log.Out (string.Format (
-				"RenderMap: min: {0}, max: {1}, minPos: {2}, maxPos: {3}, w/h: {4}/{5}, wP/hP: {6}/{7}",
-				minChunk.ToString (), maxChunk.ToString (),
-				minPos.ToString (), maxPos.ToString (),
-				widthChunks, heightChunks,
-				widthPix, heightPix)
-			);
-
-			lock (lockObject) {
-				for (int i = 0; i < Constants.ZOOMLEVELS; i++) {
-					zoomLevelBuffers [i].ResetBlock ();
-				}
-
-				if (Directory.Exists (Constants.MAP_DIRECTORY)) {
-					Directory.Delete (Constants.MAP_DIRECTORY, true);
-				}
-
-				WriteMapInfo ();
-
-				renderingFullMap = true;
-
-				if (widthPix <= 8192 && heightPix <= 8192) {
-					fullMapTexture = new Texture2D (widthPix, heightPix);
-				}
-
-				Vector2i curFullMapPos = default (Vector2i);
-				Vector2i curChunkPos = default (Vector2i);
-				for (curFullMapPos.x = 0; curFullMapPos.x < widthPix; curFullMapPos.x += Constants.MAP_CHUNK_SIZE) {
-					for (curFullMapPos.y = 0;
-						curFullMapPos.y < heightPix;
-						curFullMapPos.y += Constants.MAP_CHUNK_SIZE) {
-						curChunkPos.x = curFullMapPos.x / Constants.MAP_CHUNK_SIZE + minChunk.x;
-						curChunkPos.y = curFullMapPos.y / Constants.MAP_CHUNK_SIZE + minChunk.y;
-
-						try {
-							long chunkKey = WorldChunkCache.MakeChunkKey (curChunkPos.x, curChunkPos.y);
-							if (rfm.ContainsChunkSync (chunkKey)) {
-								Chunk c = rfm.GetChunkSync (chunkKey);
-								ushort[] mapColors = c.GetMapColors ();
-								if (mapColors != null) {
-									Color32[] realColors =
-										new Color32[Constants.MAP_CHUNK_SIZE * Constants.MAP_CHUNK_SIZE];
-									for (int i_colors = 0; i_colors < mapColors.Length; i_colors++) {
-										realColors [i_colors] = shortColorToColor32 (mapColors [i_colors]);
-									}
-
-									dirtyChunks [curChunkPos] = realColors;
-									if (fullMapTexture != null) {
-										fullMapTexture.SetPixels32 (curFullMapPos.x, curFullMapPos.y,
-											Constants.MAP_CHUNK_SIZE, Constants.MAP_CHUNK_SIZE, realColors);
-									}
-								}
-							}
-						} catch (Exception e) {
-							Log.Out ("Exception: " + e);
-						}
-					}
-
-					while (dirtyChunks.Count > 0) {
-						RenderDirtyChunks ();
-					}
-
-					Log.Out (string.Format ("RenderMap: {0}/{1} ({2}%)", curFullMapPos.x, widthPix,
-						(int) ((float) curFullMapPos.x / widthPix * 100)));
-				}
-			}
-			
-			rfm.Cleanup ();
-
-			if (fullMapTexture != null) {
-				byte[] array = fullMapTexture.EncodeToPNG ();
-				File.WriteAllBytes (Constants.MAP_DIRECTORY + "/map.png", array);
-				Object.Destroy (fullMapTexture);
-			}
-
-			renderingFullMap = false;
-
-			Log.Out ("Generating map took: " + microStopwatch.ElapsedMilliseconds + " ms");
-			Log.Out ("World extent: " + minPos + " - " + maxPos);
-		}
-
-		private void SaveAllBlockMaps () {
-			for (int i = 0; i < Constants.ZOOMLEVELS; i++) {
-				zoomLevelBuffers [i].SaveBlock ();
-			}
-		}
-		
-		private readonly WaitForSeconds coroutineDelay = new WaitForSeconds (0.2f);
-
-		private IEnumerator renderCoroutine () {
-			while (!shutdown) {
-				lock (lockObject) {
-					if (dirtyChunks.Count > 0 && renderTimeout >= float.MaxValue / 2) {
-						renderTimeout = Time.time + 0.5f;
-					}
-
-					if (Time.time > renderTimeout || dirtyChunks.Count > 200) {
-						Profiler.BeginSample ("RenderDirtyChunks");
-						RenderDirtyChunks ();
-						Profiler.EndSample ();
-					}
-				}
-
-				yield return coroutineDelay;
-			}
-		}
-
-		private readonly List<Vector2i> chunksToRender = new List<Vector2i> ();
-		private readonly List<Vector2i> chunksRendered = new List<Vector2i> ();
-
-		private void RenderDirtyChunks () {
-			msw.ResetAndRestart ();
-
-			if (dirtyChunks.Count <= 0) {
-				return;
-			}
-
-			Profiler.BeginSample ("RenderDirtyChunks.Prepare");
-			chunksToRender.Clear ();
-			chunksRendered.Clear ();
-
-			dirtyChunks.CopyKeysTo (chunksToRender);
-
-			Vector2i chunkPos = chunksToRender [0];
-			chunksRendered.Add (chunkPos);
-
-			//Log.Out ("Start Dirty: " + chunkPos);
-
-			getBlockNumber (chunkPos, out Vector2i block, out _, Constants.MAP_BLOCK_TO_CHUNK_DIV, Constants.MAP_CHUNK_SIZE);
-
-			zoomLevelBuffers [Constants.ZOOMLEVELS - 1].LoadBlock (block);
-			Profiler.EndSample ();
-
-			Profiler.BeginSample ("RenderDirtyChunks.Work");
-			// Write all chunks that are in the same image tile of the highest zoom level 
-			Vector2i v_block, v_blockOffset;
-			foreach (Vector2i v in chunksToRender) {
-				getBlockNumber (v, out v_block, out v_blockOffset, Constants.MAP_BLOCK_TO_CHUNK_DIV,
-					Constants.MAP_CHUNK_SIZE);
-				if (v_block.Equals (block)) {
-					//Log.Out ("Dirty: " + v + " render: true");
-					chunksRendered.Add (v);
-					if (dirtyChunks [v].Length != Constants.MAP_CHUNK_SIZE * Constants.MAP_CHUNK_SIZE) {
-						Log.Error (string.Format ("Rendering chunk has incorrect data size of {0} instead of {1}",
-							dirtyChunks [v].Length, Constants.MAP_CHUNK_SIZE * Constants.MAP_CHUNK_SIZE));
-					}
-
-					zoomLevelBuffers [Constants.ZOOMLEVELS - 1]
-						.SetPart (v_blockOffset, Constants.MAP_CHUNK_SIZE, dirtyChunks [v]);
-				}
-			}
-			Profiler.EndSample ();
-
-			foreach (Vector2i v in chunksRendered) {
-				dirtyChunks.Remove (v);
-			}
-
-			// Update lower zoom levels affected by the change of the highest one
-			RenderZoomLevel (block);
-
-			Profiler.BeginSample ("RenderDirtyChunks.SaveAll");
-			SaveAllBlockMaps ();
-			Profiler.EndSample ();
-		}
-
-		private void RenderZoomLevel (Vector2i _innerBlock) {
-			Profiler.BeginSample ("RenderZoomLevel");
-			int level = Constants.ZOOMLEVELS - 1;
-			while (level > 0) {
-				Vector2i block, blockOffset;
-				getBlockNumber (_innerBlock, out block, out blockOffset, 2, Constants.MAP_BLOCK_SIZE / 2);
-
-				zoomLevelBuffers [level - 1].LoadBlock (block);
-
-				Profiler.BeginSample ("RenderZoomLevel.Transfer");
-				if ((zoomLevelBuffers [level].FormatSelf == TextureFormat.ARGB32 ||
-				     zoomLevelBuffers [level].FormatSelf == TextureFormat.RGBA32) &&
-				    zoomLevelBuffers [level].FormatSelf == zoomLevelBuffers [level - 1].FormatSelf) {
-					zoomLevelBuffers [level - 1].SetPartNative (blockOffset, Constants.MAP_BLOCK_SIZE / 2, zoomLevelBuffers [level].GetHalfScaledNative ());
-				} else {
-					zoomLevelBuffers [level - 1].SetPart (blockOffset, Constants.MAP_BLOCK_SIZE / 2, zoomLevelBuffers [level].GetHalfScaled ());
-				}
-				Profiler.EndSample ();
-
-				level--;
-				_innerBlock = block;
-			}
-			Profiler.EndSample ();
-		}
-
-		private void getBlockNumber (Vector2i _innerPos, out Vector2i _block, out Vector2i _blockOffset, int _scaleFactor,
-			int _offsetSize) {
-			_block = default (Vector2i);
-			_blockOffset = default (Vector2i);
-			_block.x = (_innerPos.x + 16777216) / _scaleFactor - 16777216 / _scaleFactor;
-			_block.y = (_innerPos.y + 16777216) / _scaleFactor - 16777216 / _scaleFactor;
-			_blockOffset.x = (_innerPos.x + 16777216) % _scaleFactor * _offsetSize;
-			_blockOffset.y = (_innerPos.y + 16777216) % _scaleFactor * _offsetSize;
-		}
-
-		private void WriteMapInfo () {
-			JSONObject mapInfo = new JSONObject ();
-			mapInfo.Add ("blockSize", new JSONNumber (Constants.MAP_BLOCK_SIZE));
-			mapInfo.Add ("maxZoom", new JSONNumber (Constants.ZOOMLEVELS - 1));
-
-			Directory.CreateDirectory (Constants.MAP_DIRECTORY);
-			File.WriteAllText (Constants.MAP_DIRECTORY + "/mapinfo.json", mapInfo.ToString (), Encoding.UTF8);
-		}
-
-		private bool LoadMapInfo () {
-			if (!File.Exists (Constants.MAP_DIRECTORY + "/mapinfo.json")) {
-				return false;
-			}
-
-			string json = File.ReadAllText (Constants.MAP_DIRECTORY + "/mapinfo.json", Encoding.UTF8);
-			try {
-				JSONNode node = Parser.Parse (json);
-				if (node is JSONObject) {
-					JSONObject jo = (JSONObject) node;
-					if (jo.ContainsKey ("blockSize")) {
-						Constants.MAP_BLOCK_SIZE = ((JSONNumber) jo ["blockSize"]).GetInt ();
-					}
-
-					if (jo.ContainsKey ("maxZoom")) {
-						Constants.ZOOMLEVELS = ((JSONNumber) jo ["maxZoom"]).GetInt () + 1;
-					}
-
-					return true;
-				}
-			} catch (MalformedJSONException e) {
-				Log.Out ("Exception in LoadMapInfo: " + e);
-			} catch (InvalidCastException e) {
-				Log.Out ("Exception in LoadMapInfo: " + e);
-			}
-
-			return false;
-		}
-
-		private void getWorldExtent (RegionFileManager _rfm, out Vector2i _minChunk, out Vector2i _maxChunk,
-			out Vector2i _minPos, out Vector2i _maxPos,
-			out int _widthChunks, out int _heightChunks,
-			out int _widthPix, out int _heightPix) {
-			_minChunk = default (Vector2i);
-			_maxChunk = default (Vector2i);
-			_minPos = default (Vector2i);
-			_maxPos = default (Vector2i);
-
-			long[] keys = _rfm.GetAllChunkKeys ();
-			int minX = int.MaxValue;
-			int minY = int.MaxValue;
-			int maxX = int.MinValue;
-			int maxY = int.MinValue;
-			foreach (long key in keys) {
-				int x = WorldChunkCache.extractX (key);
-				int y = WorldChunkCache.extractZ (key);
-
-				if (x < minX) {
-					minX = x;
-				}
-
-				if (x > maxX) {
-					maxX = x;
-				}
-
-				if (y < minY) {
-					minY = y;
-				}
-
-				if (y > maxY) {
-					maxY = y;
-				}
-			}
-
-			_minChunk.x = minX;
-			_minChunk.y = minY;
-
-			_maxChunk.x = maxX;
-			_maxChunk.y = maxY;
-
-			_minPos.x = minX * Constants.MAP_CHUNK_SIZE;
-			_minPos.y = minY * Constants.MAP_CHUNK_SIZE;
-
-			_maxPos.x = maxX * Constants.MAP_CHUNK_SIZE;
-			_maxPos.y = maxY * Constants.MAP_CHUNK_SIZE;
-
-			_widthChunks = maxX - minX + 1;
-			_heightChunks = maxY - minY + 1;
-
-			_widthPix = _widthChunks * Constants.MAP_CHUNK_SIZE;
-			_heightPix = _heightChunks * Constants.MAP_CHUNK_SIZE;
-		}
-
-		private static Color32 shortColorToColor32 (ushort _col) {
-			byte r = (byte) (256 * ((_col >> 10) & 31) / 32);
-			byte g = (byte) (256 * ((_col >> 5) & 31) / 32);
-			byte b = (byte) (256 * (_col & 31) / 32);
-			const byte a = 255;
-			return new Color32 (r, g, b, a);
-		}
-	}
-}
Index: binary-improvements2/MapRendering/ModInfo.xml
===================================================================
--- binary-improvements2/MapRendering/ModInfo.xml	(revision 390)
+++ binary-improvements2/MapRendering/ModInfo.xml	(revision 391)
@@ -2,9 +2,9 @@
 <xml>
 	<ModInfo>
-		<Name value="Allocs MapRendering and Webinterface" />
+		<Name value="TFP_MapRendering" />
 		<Description value="Render the game map to image map tiles as it is uncovered" />
-		<Author value="Christian 'Alloc' Illy" />
-		<Version value="39" />
-		<Website value="http://7dtd.illy.bz" />
+		<Author value="The Fun Pimps LLC" />
+		<Version value="1" />
+		<Website value="" />
 	</ModInfo>
 </xml>
Index: binary-improvements2/MapRendering/WebAndMapRendering.csproj
===================================================================
--- binary-improvements2/MapRendering/WebAndMapRendering.csproj	(revision 390)
+++ 	(revision )
@@ -1,158 +1,0 @@
-﻿<?xml version="1.0" encoding="utf-8"?>
-<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
-  <PropertyGroup>
-    <Configuration Condition=" '$(Configuration)' == '' ">Release</Configuration>
-    <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
-    <ProductVersion>8.0.30703</ProductVersion>
-    <SchemaVersion>2.0</SchemaVersion>
-    <ProjectGuid>{A1847B5F-7BFC-4BCD-94AA-A6C9FB7E7C54}</ProjectGuid>
-    <OutputType>Library</OutputType>
-    <RootNamespace>MapRendering</RootNamespace>
-    <AssemblyName>MapRendering</AssemblyName>
-    <TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
-    <LangVersion>8</LangVersion>
-  </PropertyGroup>
-  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
-    <DebugType>none</DebugType>
-    <Optimize>true</Optimize>
-    <OutputPath>..\bin\Mods\Allocs_WebAndMapRendering\</OutputPath>
-    <ErrorReport>prompt</ErrorReport>
-    <WarningLevel>4</WarningLevel>
-    <ConsolePause>false</ConsolePause>
-    <NoStdLib>true</NoStdLib>
-  </PropertyGroup>
-  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release_Profiler|AnyCPU' ">
-    <OutputPath>..\bin\Mods\Allocs_WebAndMapRendering\</OutputPath>
-    <DefineConstants>ENABLE_PROFILER</DefineConstants>
-    <Optimize>true</Optimize>
-    <DebugSymbols>true</DebugSymbols>
-    <DebugType>full</DebugType>
-    <WarningLevel>4</WarningLevel>
-    <NoStdLib>true</NoStdLib>
-  </PropertyGroup>
-  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
-    <OutputPath>..\bin\Mods\Allocs_WebAndMapRendering\</OutputPath>
-    <DebugType>full</DebugType>
-    <DebugSymbols>true</DebugSymbols>
-  </PropertyGroup>
-  <ItemGroup>
-    <Reference Include="Assembly-CSharp-firstpass, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null">
-      <HintPath>..\7dtd-binaries\Assembly-CSharp-firstpass.dll</HintPath>
-      <Private>False</Private>
-    </Reference>
-    <Reference Include="LogLibrary">
-      <HintPath>..\7dtd-binaries\LogLibrary.dll</HintPath>
-      <Private>False</Private>
-    </Reference>
-    <Reference Include="mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
-      <HintPath>..\7dtd-binaries\mscorlib.dll</HintPath>
-      <Private>False</Private>
-    </Reference>
-    <Reference Include="System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
-      <HintPath>..\7dtd-binaries\System.dll</HintPath>
-      <Private>False</Private>
-    </Reference>
-    <Reference Include="System.Xml, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
-      <HintPath>..\7dtd-binaries\System.Xml.dll</HintPath>
-      <Private>False</Private>
-    </Reference>
-    <Reference Include="UnityEngine">
-      <HintPath>..\7dtd-binaries\UnityEngine.dll</HintPath>
-      <Private>False</Private>
-    </Reference>
-    <Reference Include="Assembly-CSharp">
-      <HintPath>..\7dtd-binaries\Assembly-CSharp.dll</HintPath>
-      <Private>False</Private>
-    </Reference>
-    <Reference Include="mscorlib">
-      <HintPath>..\7dtd-binaries\mscorlib.dll</HintPath>
-      <Private>False</Private>
-    </Reference>
-    <Reference Include="UnityEngine.CoreModule, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null">
-      <HintPath>..\7dtd-binaries\UnityEngine.CoreModule.dll</HintPath>
-      <Private>False</Private>
-    </Reference>
-    <Reference Include="UnityEngine.ImageConversionModule, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null">
-      <HintPath>..\7dtd-binaries\UnityEngine.ImageConversionModule.dll</HintPath>
-      <Private>False</Private>
-    </Reference>
-  </ItemGroup>
-  <ItemGroup>
-    <Compile Include="AssemblyInfo.cs" />
-    <Compile Include="Commands\Exception.cs" />
-    <Compile Include="MapRendering\MapRendering.cs" />
-    <Compile Include="MapRendering\MapRenderBlockBuffer.cs" />
-    <Compile Include="MapRendering\Constants.cs" />
-    <Compile Include="Commands\RenderMap.cs" />
-    <Compile Include="Commands\EnableRendering.cs" />
-    <Compile Include="API.cs" />
-    <Compile Include="Web\API\AbsRestApi.cs" />
-    <Compile Include="Web\API\GetAnimalsLocation.cs" />
-    <Compile Include="Web\API\GetHostileLocation.cs" />
-    <Compile Include="Web\API\GetWebMods.cs" />
-    <Compile Include="Web\API\Null.cs" />
-    <Compile Include="Web\Handlers\RewriteHandler.cs" />
-    <Compile Include="Web\RequestContext.cs" />
-    <Compile Include="Web\SSE\EventLog.cs" />
-    <Compile Include="Web\SSE\SseHandler.cs" />
-    <Compile Include="Web\SSE\EventBase.cs" />
-    <Compile Include="Web\Web.cs" />
-    <Compile Include="Web\MimeType.cs" />
-    <Compile Include="Web\API\GetPlayersOnline.cs" />
-    <Compile Include="Web\API\AbsWebAPI.cs" />
-    <Compile Include="Web\API\GetPlayersLocation.cs" />
-    <Compile Include="Web\API\GetPlayerInventory.cs" />
-    <Compile Include="Web\API\GetLandClaims.cs" />
-    <Compile Include="Commands\webstat.cs" />
-    <Compile Include="Web\API\GetStats.cs" />
-    <Compile Include="Web\WebConnection.cs" />
-    <Compile Include="Web\OpenID.cs" />
-    <Compile Include="Web\ConnectionHandler.cs" />
-    <Compile Include="Web\WebMod.cs" />
-    <Compile Include="Web\WebPermissions.cs" />
-    <Compile Include="Web\Handlers\ApiHandler.cs" />
-    <Compile Include="Web\Handlers\ItemIconHandler.cs" />
-    <Compile Include="Web\Handlers\AbsHandler.cs" />
-    <Compile Include="Web\Handlers\SimpleRedirectHandler.cs" />
-    <Compile Include="Web\Handlers\StaticHandler.cs" />
-    <Compile Include="Web\Handlers\SessionHandler.cs" />
-    <Compile Include="Web\API\ExecuteConsoleCommand.cs" />
-    <Compile Include="Commands\ReloadWebPermissions.cs" />
-    <Compile Include="Web\Handlers\UserStatusHandler.cs" />
-    <Compile Include="Commands\WebTokens.cs" />
-    <Compile Include="Commands\WebPermissionsCmd.cs" />
-    <Compile Include="Web\LogBuffer.cs" />
-    <Compile Include="Web\API\GetLog.cs" />
-    <Compile Include="Web\API\GetWebUIUpdates.cs" />
-    <Compile Include="Web\API\GetServerInfo.cs" />
-    <Compile Include="Web\API\GetPlayerList.cs" />
-    <Compile Include="Web\WebCommandResult.cs" />
-    <Compile Include="Web\API\GetAllowedCommands.cs" />
-    <Compile Include="Commands\EnableOpenIDDebug.cs" />
-    <Compile Include="Web\API\GetPlayerInventories.cs" />
-    <Compile Include="Web\WebUtils.cs" />
-  </ItemGroup>
-  <Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
-  <ItemGroup>
-    <ProjectReference Include="..\7dtd-server-fixes\7dtd-server-fixes.csproj">
-      <Project>{81DA7F87-1A66-4920-AADA-6EAF1971F8D0}</Project>
-      <Name>7dtd-server-fixes</Name>
-      <Private>False</Private>
-    </ProjectReference>
-    <ProjectReference Include="..\SpaceWizards.HttpListener\SpaceWizards.HttpListener.csproj">
-      <Project>{e273d042-57f9-4e2e-8268-5053527e5287}</Project>
-      <Name>SpaceWizards.HttpListener</Name>
-    </ProjectReference>
-  </ItemGroup>
-  <ItemGroup>
-    <None Include="ModInfo.xml">
-      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
-    </None>
-    <None Include="steam-intermediate.cer">
-      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
-    </None>
-    <None Include="steam-rootca.cer">
-      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
-    </None>
-  </ItemGroup>
-</Project>
Index: binary-improvements2/MapRendering/src/AssemblyInfo.cs
===================================================================
--- binary-improvements2/MapRendering/src/AssemblyInfo.cs	(revision 391)
+++ binary-improvements2/MapRendering/src/AssemblyInfo.cs	(revision 391)
@@ -0,0 +1,25 @@
+using System.Reflection;
+
+// Information about this assembly is defined by the following attributes. 
+// Change them to the values specific to your project.
+
+[assembly: AssemblyTitle ("MapRendering")]
+[assembly: AssemblyDescription ("")]
+[assembly: AssemblyConfiguration ("")]
+[assembly: AssemblyCompany ("The Fun Pimps LLC")]
+[assembly: AssemblyProduct ("")]
+[assembly: AssemblyCopyright ("The Fun Pimps LLC")]
+[assembly: AssemblyTrademark ("")]
+[assembly: AssemblyCulture ("")]
+
+// The assembly version has the format "{Major}.{Minor}.{Build}.{Revision}".
+// The form "{Major}.{Minor}.*" will automatically update the build and revision,
+// and "{Major}.{Minor}.{Build}.*" will update just the revision.
+
+[assembly: AssemblyVersion ("0.0.0.0")]
+
+// The following attributes are used to specify the signing key for the assembly, 
+// if desired. See the Mono documentation for more information about signing.
+
+//[assembly: AssemblyDelaySign(false)]
+//[assembly: AssemblyKeyFile("")]
Index: binary-improvements2/MapRendering/src/Commands/EnableRendering.cs
===================================================================
--- binary-improvements2/MapRendering/src/Commands/EnableRendering.cs	(revision 391)
+++ binary-improvements2/MapRendering/src/Commands/EnableRendering.cs	(revision 391)
@@ -0,0 +1,25 @@
+using System.Collections.Generic;
+using JetBrains.Annotations;
+
+namespace MapRendering.Commands {
+	[UsedImplicitly]
+	public class EnableRendering : ConsoleCmdAbstract {
+		public override string GetDescription () {
+			return "enable/disable live map rendering";
+		}
+
+		public override string[] GetCommands () {
+			return new[] {"enablerendering"};
+		}
+
+		public override void Execute (List<string> _params, CommandSenderInfo _senderInfo) {
+			if (_params.Count != 1) {
+				SdtdConsole.Instance.Output ("Current state: " + MapRenderer.renderingEnabled);
+				return;
+			}
+
+			MapRenderer.renderingEnabled = _params [0].Equals ("1");
+			SdtdConsole.Instance.Output ("Set live map rendering to " + _params [0].Equals ("1"));
+		}
+	}
+}
Index: binary-improvements2/MapRendering/src/Commands/RenderMap.cs
===================================================================
--- binary-improvements2/MapRendering/src/Commands/RenderMap.cs	(revision 391)
+++ binary-improvements2/MapRendering/src/Commands/RenderMap.cs	(revision 391)
@@ -0,0 +1,21 @@
+using System.Collections.Generic;
+using JetBrains.Annotations;
+
+namespace MapRendering.Commands {
+	[UsedImplicitly]
+	public class RenderMap : ConsoleCmdAbstract {
+		public override string GetDescription () {
+			return "render the current map to a file";
+		}
+
+		public override string[] GetCommands () {
+			return new[] {"rendermap"};
+		}
+
+		public override void Execute (List<string> _params, CommandSenderInfo _senderInfo) {
+			MapRenderer.Instance.RenderFullMap ();
+
+			SdtdConsole.Instance.Output ("Render map done");
+		}
+	}
+}
Index: binary-improvements2/MapRendering/src/Constants.cs
===================================================================
--- binary-improvements2/MapRendering/src/Constants.cs	(revision 391)
+++ binary-improvements2/MapRendering/src/Constants.cs	(revision 391)
@@ -0,0 +1,16 @@
+using UnityEngine;
+
+namespace MapRendering {
+	public static class Constants {
+		public static readonly TextureFormat DefaultTextureFormat = TextureFormat.ARGB32;
+		public static int MapBlockSize = 128;
+		public const int MapChunkSize = 16;
+		public const int MapRegionSize = 512;
+		public static int Zoomlevels = 5;
+		public static string MapDirectory = string.Empty;
+
+		public static int MAP_BLOCK_TO_CHUNK_DIV => MapBlockSize / MapChunkSize;
+
+		public static int MAP_REGION_TO_CHUNK_DIV => MapRegionSize / MapChunkSize;
+	}
+}
Index: binary-improvements2/MapRendering/src/MapRenderBlockBuffer.cs
===================================================================
--- binary-improvements2/MapRendering/src/MapRenderBlockBuffer.cs	(revision 391)
+++ binary-improvements2/MapRendering/src/MapRenderBlockBuffer.cs	(revision 391)
@@ -0,0 +1,230 @@
+using System;
+using System.IO;
+using AllocsFixes.FileCache;
+using Unity.Collections;
+using UnityEngine;
+using UnityEngine.Profiling;
+
+namespace MapRendering {
+	public class MapRenderBlockBuffer {
+		private readonly Texture2D blockMap = new Texture2D (Constants.MapBlockSize, Constants.MapBlockSize, Constants.DefaultTextureFormat, false);
+		private readonly MapTileCache cache;
+		private readonly NativeArray<int> emptyImageData;
+		private readonly Texture2D zoomBuffer = new Texture2D (Constants.MapBlockSize / 2, Constants.MapBlockSize / 2, Constants.DefaultTextureFormat, false);
+		private readonly int zoomLevel;
+		private readonly string folderBase;
+		
+		private Vector2i currentBlockMapPos = new Vector2i (int.MinValue, int.MinValue);
+		private string currentBlockMapFolder = string.Empty;
+
+		public MapRenderBlockBuffer (int _level, MapTileCache _cache) {
+			zoomLevel = _level;
+			cache = _cache;
+			folderBase = Constants.MapDirectory + "/" + zoomLevel + "/";
+
+			{
+				// Initialize empty tile data
+				Color nullColor = new Color (0, 0, 0, 0);
+				for (int x = 0; x < Constants.MapBlockSize; x++) {
+					for (int y = 0; y < Constants.MapBlockSize; y++) {
+						blockMap.SetPixel (x, y, nullColor);
+					}
+				}
+
+				NativeArray<int> blockMapData = blockMap.GetRawTextureData<int> ();
+				emptyImageData = new NativeArray<int> (blockMapData.Length, Allocator.Persistent,
+					NativeArrayOptions.UninitializedMemory);
+				blockMapData.CopyTo (emptyImageData);
+			}
+		}
+
+		public TextureFormat FormatSelf => blockMap.format;
+
+		public void ResetBlock () {
+			currentBlockMapFolder = string.Empty;
+			currentBlockMapPos = new Vector2i (int.MinValue, int.MinValue);
+			cache.ResetTile (zoomLevel);
+		}
+
+		public void SaveBlock () {
+			Profiler.BeginSample ("SaveBlock");
+			try {
+				saveTextureToFile ();
+			} catch (Exception e) {
+				Log.Warning ("Exception in MapRenderBlockBuffer.SaveBlock(): " + e);
+			}
+			Profiler.EndSample ();
+		}
+
+		public bool LoadBlock (Vector2i _block) {
+			Profiler.BeginSample ("LoadBlock");
+			lock (blockMap) {
+				if (currentBlockMapPos != _block) {
+					Profiler.BeginSample ("LoadBlock.Strings");
+					string folder;
+					if (currentBlockMapPos.x != _block.x) {
+						folder = folderBase + _block.x + '/';
+
+						Profiler.BeginSample ("LoadBlock.Directory");
+						Directory.CreateDirectory (folder);
+						Profiler.EndSample ();
+					} else {
+						folder = currentBlockMapFolder;
+					}
+
+					string fileName = folder + _block.y + ".png";
+					Profiler.EndSample ();
+					
+					SaveBlock ();
+					loadTextureFromFile (fileName);
+
+					currentBlockMapFolder = folder;
+					currentBlockMapPos = _block;
+
+					Profiler.EndSample ();
+					return true;
+				}
+			}
+
+			Profiler.EndSample ();
+			return false;
+		}
+
+		public void SetPart (Vector2i _offset, int _partSize, Color32[] _pixels) {
+			if (_offset.x + _partSize > Constants.MapBlockSize || _offset.y + _partSize > Constants.MapBlockSize) {
+				Log.Error (
+					$"MapBlockBuffer[{zoomLevel}].SetPart ({_offset}, {_partSize}, {_pixels.Length}) has blockMap.size ({Constants.MapBlockSize}/{Constants.MapBlockSize})");
+				return;
+			}
+
+			Profiler.BeginSample ("SetPart");
+			blockMap.SetPixels32 (_offset.x, _offset.y, _partSize, _partSize, _pixels);
+			Profiler.EndSample ();
+		}
+
+		public Color32[] GetHalfScaled () {
+			Profiler.BeginSample ("HalfScaled.ResizeBuffer");
+			zoomBuffer.Resize (Constants.MapBlockSize, Constants.MapBlockSize);
+			Profiler.EndSample ();
+
+			Profiler.BeginSample ("HalfScaled.CopyPixels");
+			if (blockMap.format == zoomBuffer.format) {
+				Profiler.BeginSample ("Native");
+				NativeArray<byte> dataSrc = blockMap.GetRawTextureData<byte> ();
+				NativeArray<byte> dataZoom = zoomBuffer.GetRawTextureData<byte> ();
+				dataSrc.CopyTo (dataZoom);
+				Profiler.EndSample ();
+			} else {
+				Profiler.BeginSample ("GetSetPixels");
+				zoomBuffer.SetPixels32 (blockMap.GetPixels32 ());
+				Profiler.EndSample ();
+			}
+			Profiler.EndSample ();
+
+			Profiler.BeginSample ("HalfScaled.Scale");
+			TextureScale.Point (zoomBuffer, Constants.MapBlockSize / 2, Constants.MapBlockSize / 2);
+			Profiler.EndSample ();
+
+			Profiler.BeginSample ("HalfScaled.Return");
+			Color32[] result = zoomBuffer.GetPixels32 ();
+			Profiler.EndSample ();
+
+			return result;
+		}
+
+		public void SetPartNative (Vector2i _offset, int _partSize, NativeArray<int> _pixels) {
+			if (_offset.x + _partSize > Constants.MapBlockSize || _offset.y + _partSize > Constants.MapBlockSize) {
+				Log.Error (
+					$"MapBlockBuffer[{zoomLevel}].SetPart ({_offset}, {_partSize}, {_pixels.Length}) has blockMap.size ({Constants.MapBlockSize}/{Constants.MapBlockSize})");
+				return;
+			}
+
+			Profiler.BeginSample ("SetPartNative");
+			NativeArray<int> destData = blockMap.GetRawTextureData<int> ();
+			
+			for (int y = 0; y < _partSize; y++) {
+				int srcLineStartIdx = _partSize * y;
+				int destLineStartIdx = blockMap.width * (_offset.y + y) + _offset.x;
+				for (int x = 0; x < _partSize; x++) {
+					destData [destLineStartIdx + x] = _pixels [srcLineStartIdx + x];
+				}
+			}
+			Profiler.EndSample ();
+		}
+
+		public NativeArray<int> GetHalfScaledNative () {
+			Profiler.BeginSample ("HalfScaledNative.ResizeBuffer");
+			if (zoomBuffer.format != blockMap.format || zoomBuffer.height != Constants.MapBlockSize / 2 || zoomBuffer.width != Constants.MapBlockSize / 2) {
+				zoomBuffer.Resize (Constants.MapBlockSize / 2, Constants.MapBlockSize / 2, blockMap.format, false);
+			}
+			Profiler.EndSample ();
+
+			Profiler.BeginSample ("HalfScaledNative.Scale");
+			ScaleNative (blockMap, zoomBuffer);
+			Profiler.EndSample ();
+
+			return zoomBuffer.GetRawTextureData<int> ();
+		}
+		
+		private static void ScaleNative (Texture2D _sourceTex, Texture2D _targetTex) {
+			NativeArray<int> srcData = _sourceTex.GetRawTextureData<int> ();
+			NativeArray<int> targetData = _targetTex.GetRawTextureData<int> ();
+			
+			int oldWidth = _sourceTex.width;
+			int oldHeight = _sourceTex.height;
+			int newWidth = _targetTex.width;
+			int newHeight = _targetTex.height;
+			
+			float ratioX = (float) oldWidth / newWidth;
+			float ratioY = (float) oldHeight / newHeight;
+
+			for (int y = 0; y < newHeight; y++) {
+				int oldLineStart = (int) (ratioY * y) * oldWidth;
+				int newLineStart = y * newWidth;
+				for (int x = 0; x < newWidth; x++) {
+					targetData [newLineStart + x] = srcData [(int) (oldLineStart + ratioX * x)];
+				}
+			}
+		}
+
+		private void loadTextureFromFile (string _fileName) {
+			Profiler.BeginSample ("LoadTexture");
+
+			Profiler.BeginSample ("LoadFile");
+			byte[] array = cache.LoadTile (zoomLevel, _fileName);
+			Profiler.EndSample ();
+
+			Profiler.BeginSample ("LoadImage");
+			if (array != null && blockMap.LoadImage (array) && blockMap.height == Constants.MapBlockSize &&
+			    blockMap.width == Constants.MapBlockSize) {
+				Profiler.EndSample ();
+
+				Profiler.EndSample ();
+				return;
+			}
+			Profiler.EndSample ();
+
+			if (array != null) {
+				Log.Error ("Map image tile " + _fileName + " has been corrupted, recreating tile");
+			}
+
+			if (blockMap.format != Constants.DefaultTextureFormat || blockMap.height != Constants.MapBlockSize ||
+			    blockMap.width != Constants.MapBlockSize) {
+				blockMap.Resize (Constants.MapBlockSize, Constants.MapBlockSize, Constants.DefaultTextureFormat,
+					false);
+			}
+
+			blockMap.LoadRawTextureData (emptyImageData);
+
+			Profiler.EndSample ();
+		}
+
+		private void saveTextureToFile () {
+			Profiler.BeginSample ("EncodePNG");
+			byte[] array = blockMap.EncodeToPNG ();
+			Profiler.EndSample ();
+
+			cache.SaveTile (zoomLevel, array);
+		}
+	}
+}
Index: binary-improvements2/MapRendering/src/MapRenderer.cs
===================================================================
--- binary-improvements2/MapRendering/src/MapRenderer.cs	(revision 391)
+++ binary-improvements2/MapRendering/src/MapRenderer.cs	(revision 391)
@@ -0,0 +1,407 @@
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using System.IO;
+using System.Text;
+using System.Threading;
+using AllocsFixes.FileCache;
+using AllocsFixes.JSON;
+using UnityEngine;
+using UnityEngine.Profiling;
+using Object = UnityEngine.Object;
+
+namespace MapRendering {
+	public class MapRenderer {
+		private static MapRenderer instance;
+
+		private static readonly object lockObject = new object ();
+		public static bool renderingEnabled = true;
+		private readonly MapTileCache cache = new MapTileCache (Constants.MapBlockSize);
+		private readonly Dictionary<Vector2i, Color32[]> dirtyChunks = new Dictionary<Vector2i, Color32[]> ();
+		private readonly MicroStopwatch msw = new MicroStopwatch ();
+		private readonly MapRenderBlockBuffer[] zoomLevelBuffers;
+		private Coroutine renderCoroutineRef;
+		private bool renderingFullMap;
+		private float renderTimeout = float.MaxValue;
+		private bool shutdown;
+
+		private MapRenderer () {
+			Constants.MapDirectory = GameIO.GetSaveGameDir () + "/map";
+
+			lock (lockObject) {
+				if (!LoadMapInfo ()) {
+					WriteMapInfo ();
+				}
+			}
+
+			cache.SetZoomCount (Constants.Zoomlevels);
+
+			zoomLevelBuffers = new MapRenderBlockBuffer[Constants.Zoomlevels];
+			for (int i = 0; i < Constants.Zoomlevels; i++) {
+				zoomLevelBuffers [i] = new MapRenderBlockBuffer (i, cache);
+			}
+
+			renderCoroutineRef = ThreadManager.StartCoroutine (renderCoroutine ());
+		}
+
+		public static MapRenderer Instance => instance ??= new MapRenderer ();
+
+		public static MapTileCache GetTileCache () {
+			return Instance.cache;
+		}
+
+		public static void Shutdown () {
+			if (Instance == null) {
+				return;
+			}
+
+			Instance.shutdown = true;
+			
+			if (Instance.renderCoroutineRef != null) {
+				ThreadManager.StopCoroutine (Instance.renderCoroutineRef);
+				Instance.renderCoroutineRef = null;
+			}
+		}
+
+		public static void RenderSingleChunk (Chunk _chunk) {
+			if (renderingEnabled && Instance != null) {
+				// TODO: Replace with regular thread and a blocking queue / set
+				ThreadPool.UnsafeQueueUserWorkItem (_o => {
+					try {
+						if (Instance.renderingFullMap) {
+							return;
+						}
+
+						lock (lockObject) {
+							Chunk c = (Chunk) _o;
+							Vector3i cPos = c.GetWorldPos ();
+							Vector2i cPos2 = new Vector2i (cPos.x / Constants.MapChunkSize,
+								cPos.z / Constants.MapChunkSize);
+
+							ushort[] mapColors = c.GetMapColors ();
+							if (mapColors == null) {
+								return;
+							}
+
+							Color32[] realColors =
+								new Color32[Constants.MapChunkSize * Constants.MapChunkSize];
+							for (int iColors = 0; iColors < mapColors.Length; iColors++) {
+								realColors [iColors] = shortColorToColor32 (mapColors [iColors]);
+							}
+
+							Instance.dirtyChunks [cPos2] = realColors;
+
+							//Log.Out ("Add Dirty: " + cPos2);
+						}
+					} catch (Exception e) {
+						Log.Out ("Exception in MapRendering.RenderSingleChunk(): " + e);
+					}
+				}, _chunk);
+			}
+		}
+
+		public void RenderFullMap () {
+			MicroStopwatch microStopwatch = new MicroStopwatch ();
+
+			string regionSaveDir = GameIO.GetSaveGameRegionDir ();
+			RegionFileManager rfm = new RegionFileManager (regionSaveDir, regionSaveDir, 0, false);
+			Texture2D fullMapTexture = null;
+
+			getWorldExtent (rfm, out Vector2i minChunk, out Vector2i maxChunk, out Vector2i minPos, out Vector2i maxPos, out int widthChunks, out int heightChunks,
+				out int widthPix, out int heightPix);
+
+			Log.Out (
+				$"RenderMap: min: {minChunk.ToString ()}, max: {maxChunk.ToString ()}, minPos: {minPos.ToString ()}, maxPos: {maxPos.ToString ()}, w/h: {widthChunks}/{heightChunks}, wP/hP: {widthPix}/{heightPix}"
+			);
+
+			lock (lockObject) {
+				for (int i = 0; i < Constants.Zoomlevels; i++) {
+					zoomLevelBuffers [i].ResetBlock ();
+				}
+
+				if (Directory.Exists (Constants.MapDirectory)) {
+					Directory.Delete (Constants.MapDirectory, true);
+				}
+
+				WriteMapInfo ();
+
+				renderingFullMap = true;
+
+				if (widthPix <= 8192 && heightPix <= 8192) {
+					fullMapTexture = new Texture2D (widthPix, heightPix);
+				}
+
+				Vector2i curFullMapPos = default;
+				Vector2i curChunkPos = default;
+				for (curFullMapPos.x = 0; curFullMapPos.x < widthPix; curFullMapPos.x += Constants.MapChunkSize) {
+					for (curFullMapPos.y = 0;
+						curFullMapPos.y < heightPix;
+						curFullMapPos.y += Constants.MapChunkSize) {
+						curChunkPos.x = curFullMapPos.x / Constants.MapChunkSize + minChunk.x;
+						curChunkPos.y = curFullMapPos.y / Constants.MapChunkSize + minChunk.y;
+
+						try {
+							long chunkKey = WorldChunkCache.MakeChunkKey (curChunkPos.x, curChunkPos.y);
+							if (rfm.ContainsChunkSync (chunkKey)) {
+								Chunk c = rfm.GetChunkSync (chunkKey);
+								ushort[] mapColors = c.GetMapColors ();
+								if (mapColors != null) {
+									Color32[] realColors =
+										new Color32[Constants.MapChunkSize * Constants.MapChunkSize];
+									for (int iColors = 0; iColors < mapColors.Length; iColors++) {
+										realColors [iColors] = shortColorToColor32 (mapColors [iColors]);
+									}
+
+									dirtyChunks [curChunkPos] = realColors;
+									if (fullMapTexture != null) {
+										fullMapTexture.SetPixels32 (curFullMapPos.x, curFullMapPos.y,
+											Constants.MapChunkSize, Constants.MapChunkSize, realColors);
+									}
+								}
+							}
+						} catch (Exception e) {
+							Log.Out ("Exception: " + e);
+						}
+					}
+
+					while (dirtyChunks.Count > 0) {
+						RenderDirtyChunks ();
+					}
+
+					Log.Out ($"RenderMap: {curFullMapPos.x}/{widthPix} ({(int)((float)curFullMapPos.x / widthPix * 100)}%)");
+				}
+			}
+			
+			rfm.Cleanup ();
+
+			if (fullMapTexture != null) {
+				byte[] array = fullMapTexture.EncodeToPNG ();
+				File.WriteAllBytes (Constants.MapDirectory + "/map.png", array);
+				Object.Destroy (fullMapTexture);
+			}
+
+			renderingFullMap = false;
+
+			Log.Out ("Generating map took: " + microStopwatch.ElapsedMilliseconds + " ms");
+			Log.Out ("World extent: " + minPos + " - " + maxPos);
+		}
+
+		private void SaveAllBlockMaps () {
+			for (int i = 0; i < Constants.Zoomlevels; i++) {
+				zoomLevelBuffers [i].SaveBlock ();
+			}
+		}
+		
+		private readonly WaitForSeconds coroutineDelay = new WaitForSeconds (0.2f);
+
+		private IEnumerator renderCoroutine () {
+			while (!shutdown) {
+				lock (lockObject) {
+					if (dirtyChunks.Count > 0 && renderTimeout >= float.MaxValue / 2) {
+						renderTimeout = Time.time + 0.5f;
+					}
+
+					if (Time.time > renderTimeout || dirtyChunks.Count > 200) {
+						Profiler.BeginSample ("RenderDirtyChunks");
+						RenderDirtyChunks ();
+						Profiler.EndSample ();
+					}
+				}
+
+				yield return coroutineDelay;
+			}
+		}
+
+		private readonly List<Vector2i> chunksToRender = new List<Vector2i> ();
+		private readonly List<Vector2i> chunksRendered = new List<Vector2i> ();
+
+		private void RenderDirtyChunks () {
+			msw.ResetAndRestart ();
+
+			if (dirtyChunks.Count <= 0) {
+				return;
+			}
+
+			Profiler.BeginSample ("RenderDirtyChunks.Prepare");
+			chunksToRender.Clear ();
+			chunksRendered.Clear ();
+
+			dirtyChunks.CopyKeysTo (chunksToRender);
+
+			Vector2i chunkPos = chunksToRender [0];
+			chunksRendered.Add (chunkPos);
+
+			//Log.Out ("Start Dirty: " + chunkPos);
+
+			getBlockNumber (chunkPos, out Vector2i block, out _, Constants.MAP_BLOCK_TO_CHUNK_DIV, Constants.MapChunkSize);
+
+			zoomLevelBuffers [Constants.Zoomlevels - 1].LoadBlock (block);
+			Profiler.EndSample ();
+
+			Profiler.BeginSample ("RenderDirtyChunks.Work");
+			// Write all chunks that are in the same image tile of the highest zoom level 
+			foreach (Vector2i v in chunksToRender) {
+				getBlockNumber (v, out Vector2i vBlock, out Vector2i vBlockOffset, Constants.MAP_BLOCK_TO_CHUNK_DIV,
+					Constants.MapChunkSize);
+				if (!vBlock.Equals (block)) {
+					continue;
+				}
+
+				//Log.Out ("Dirty: " + v + " render: true");
+				chunksRendered.Add (v);
+				if (dirtyChunks [v].Length != Constants.MapChunkSize * Constants.MapChunkSize) {
+					Log.Error (
+						$"Rendering chunk has incorrect data size of {dirtyChunks [v].Length} instead of {Constants.MapChunkSize * Constants.MapChunkSize}");
+				}
+
+				zoomLevelBuffers [Constants.Zoomlevels - 1]
+					.SetPart (vBlockOffset, Constants.MapChunkSize, dirtyChunks [v]);
+			}
+			Profiler.EndSample ();
+
+			foreach (Vector2i v in chunksRendered) {
+				dirtyChunks.Remove (v);
+			}
+
+			// Update lower zoom levels affected by the change of the highest one
+			RenderZoomLevel (block);
+
+			Profiler.BeginSample ("RenderDirtyChunks.SaveAll");
+			SaveAllBlockMaps ();
+			Profiler.EndSample ();
+		}
+
+		private void RenderZoomLevel (Vector2i _innerBlock) {
+			Profiler.BeginSample ("RenderZoomLevel");
+			int level = Constants.Zoomlevels - 1;
+			while (level > 0) {
+				getBlockNumber (_innerBlock, out Vector2i block, out Vector2i blockOffset, 2, Constants.MapBlockSize / 2);
+
+				zoomLevelBuffers [level - 1].LoadBlock (block);
+
+				Profiler.BeginSample ("RenderZoomLevel.Transfer");
+				if ((zoomLevelBuffers [level].FormatSelf == TextureFormat.ARGB32 ||
+				     zoomLevelBuffers [level].FormatSelf == TextureFormat.RGBA32) &&
+				    zoomLevelBuffers [level].FormatSelf == zoomLevelBuffers [level - 1].FormatSelf) {
+					zoomLevelBuffers [level - 1].SetPartNative (blockOffset, Constants.MapBlockSize / 2, zoomLevelBuffers [level].GetHalfScaledNative ());
+				} else {
+					zoomLevelBuffers [level - 1].SetPart (blockOffset, Constants.MapBlockSize / 2, zoomLevelBuffers [level].GetHalfScaled ());
+				}
+				Profiler.EndSample ();
+
+				level--;
+				_innerBlock = block;
+			}
+			Profiler.EndSample ();
+		}
+
+		private void getBlockNumber (Vector2i _innerPos, out Vector2i _block, out Vector2i _blockOffset, int _scaleFactor,
+			int _offsetSize) {
+			_block = default;
+			_blockOffset = default;
+			_block.x = (_innerPos.x + 16777216) / _scaleFactor - 16777216 / _scaleFactor;
+			_block.y = (_innerPos.y + 16777216) / _scaleFactor - 16777216 / _scaleFactor;
+			_blockOffset.x = (_innerPos.x + 16777216) % _scaleFactor * _offsetSize;
+			_blockOffset.y = (_innerPos.y + 16777216) % _scaleFactor * _offsetSize;
+		}
+
+		private void WriteMapInfo () {
+			JsonObject mapInfo = new JsonObject ();
+			mapInfo.Add ("blockSize", new JsonNumber (Constants.MapBlockSize));
+			mapInfo.Add ("maxZoom", new JsonNumber (Constants.Zoomlevels - 1));
+
+			Directory.CreateDirectory (Constants.MapDirectory);
+			File.WriteAllText (Constants.MapDirectory + "/mapinfo.json", mapInfo.ToString (), Encoding.UTF8);
+		}
+
+		private bool LoadMapInfo () {
+			if (!File.Exists (Constants.MapDirectory + "/mapinfo.json")) {
+				return false;
+			}
+
+			string json = File.ReadAllText (Constants.MapDirectory + "/mapinfo.json", Encoding.UTF8);
+			try {
+				JsonNode node = Parser.Parse (json);
+				if (node is JsonObject jo) {
+					if (jo.ContainsKey ("blockSize")) {
+						Constants.MapBlockSize = ((JsonNumber) jo ["blockSize"]).GetInt ();
+					}
+
+					if (jo.ContainsKey ("maxZoom")) {
+						Constants.Zoomlevels = ((JsonNumber) jo ["maxZoom"]).GetInt () + 1;
+					}
+
+					return true;
+				}
+			} catch (MalformedJsonException e) {
+				Log.Out ("Exception in LoadMapInfo: " + e);
+			} catch (InvalidCastException e) {
+				Log.Out ("Exception in LoadMapInfo: " + e);
+			}
+
+			return false;
+		}
+
+		private void getWorldExtent (RegionFileManager _rfm, out Vector2i _minChunk, out Vector2i _maxChunk,
+			out Vector2i _minPos, out Vector2i _maxPos,
+			out int _widthChunks, out int _heightChunks,
+			out int _widthPix, out int _heightPix) {
+			_minChunk = default;
+			_maxChunk = default;
+			_minPos = default;
+			_maxPos = default;
+
+			long[] keys = _rfm.GetAllChunkKeys ();
+			int minX = int.MaxValue;
+			int minY = int.MaxValue;
+			int maxX = int.MinValue;
+			int maxY = int.MinValue;
+			foreach (long key in keys) {
+				int x = WorldChunkCache.extractX (key);
+				int y = WorldChunkCache.extractZ (key);
+
+				if (x < minX) {
+					minX = x;
+				}
+
+				if (x > maxX) {
+					maxX = x;
+				}
+
+				if (y < minY) {
+					minY = y;
+				}
+
+				if (y > maxY) {
+					maxY = y;
+				}
+			}
+
+			_minChunk.x = minX;
+			_minChunk.y = minY;
+
+			_maxChunk.x = maxX;
+			_maxChunk.y = maxY;
+
+			_minPos.x = minX * Constants.MapChunkSize;
+			_minPos.y = minY * Constants.MapChunkSize;
+
+			_maxPos.x = maxX * Constants.MapChunkSize;
+			_maxPos.y = maxY * Constants.MapChunkSize;
+
+			_widthChunks = maxX - minX + 1;
+			_heightChunks = maxY - minY + 1;
+
+			_widthPix = _widthChunks * Constants.MapChunkSize;
+			_heightPix = _heightChunks * Constants.MapChunkSize;
+		}
+
+		private static Color32 shortColorToColor32 (ushort _col) {
+			byte r = (byte) (256 * ((_col >> 10) & 31) / 32);
+			byte g = (byte) (256 * ((_col >> 5) & 31) / 32);
+			byte b = (byte) (256 * (_col & 31) / 32);
+			const byte a = 255;
+			return new Color32 (r, g, b, a);
+		}
+	}
+}
Index: binary-improvements2/MapRendering/src/ModApi.cs
===================================================================
--- binary-improvements2/MapRendering/src/ModApi.cs	(revision 391)
+++ binary-improvements2/MapRendering/src/ModApi.cs	(revision 391)
@@ -0,0 +1,19 @@
+using JetBrains.Annotations;
+
+namespace MapRendering {
+	[UsedImplicitly]
+	public class ModApi : IModApi {
+		public void InitMod (Mod _modInstance) {
+			ModEvents.GameShutdown.RegisterHandler (GameShutdown);
+			ModEvents.CalcChunkColorsDone.RegisterHandler (CalcChunkColorsDone);
+		}
+
+		private void GameShutdown () {
+			MapRenderer.Shutdown ();
+		}
+
+		private void CalcChunkColorsDone (Chunk _chunk) {
+			MapRenderer.RenderSingleChunk (_chunk);
+		}
+	}
+}
