From 91b9b8aebfa634ee7a6f26284a5b782c72de0fb6 Mon Sep 17 00:00:00 2001
From: Enrico Turri <enricoturri@seznam.cz>
Date: Wed, 23 May 2018 12:49:56 +0200
Subject: [PATCH 01/17] Fixed wrong layer height texture updates when using
 multiple objects

---
 lib/Slic3r/GUI/3DScene.pm | 21 +++++++++++++++++++--
 xs/xsp/Print.xsp          |  3 +++
 2 files changed, 22 insertions(+), 2 deletions(-)

diff --git a/lib/Slic3r/GUI/3DScene.pm b/lib/Slic3r/GUI/3DScene.pm
index 33c0e8d37..fca22bedd 100644
--- a/lib/Slic3r/GUI/3DScene.pm
+++ b/lib/Slic3r/GUI/3DScene.pm
@@ -360,6 +360,7 @@ sub _variable_layer_thickness_action {
     my ($self, $mouse_event, $do_modification) = @_;
     # A volume is selected. Test, whether hovering over a layer thickness bar.
     return if $self->{layer_height_edit_last_object_id} == -1;
+    
     if (defined($mouse_event)) {
         my ($bar_left, $bar_top, $bar_right, $bar_bottom) = $self->_variable_layer_thickness_bar_rect_screen;
         $self->{layer_height_edit_last_z} = unscale($self->{print}->get_object($self->{layer_height_edit_last_object_id})->size->z)
@@ -374,9 +375,20 @@ sub _variable_layer_thickness_action {
         $self->{layer_height_edit_strength},
         $self->{layer_height_edit_band_width}, 
         $self->{layer_height_edit_last_action});
-    $self->volumes->[$self->{layer_height_edit_last_object_id}]->generate_layer_height_texture(
+        
+    # searches the id of the first volume of the selected object
+    my $volume_idx = 0;
+    for my $i (0..$self->{layer_height_edit_last_object_id} - 1) {
+        my $obj = $self->{print}->get_object($i);
+        for my $j (0..$obj->region_volumes_count - 1) {
+            $volume_idx += scalar @{$obj->get_region_volumes($j)};
+        }
+    }
+
+    $self->volumes->[$volume_idx]->generate_layer_height_texture(
         $self->{print}->get_object($self->{layer_height_edit_last_object_id}), 1);
-    $self->Refresh;
+
+        $self->Refresh;
     # Automatic action on mouse down with the same coordinate.
     $self->{layer_height_edit_timer}->Start(100, wxTIMER_CONTINUOUS);
 }
@@ -1623,6 +1635,11 @@ sub draw_active_object_annotations {
     $self->{layer_height_edit_shader}->set_uniform('z_cursor',                    $z_max * $z_cursor_relative);
     $self->{layer_height_edit_shader}->set_uniform('z_cursor_band_width',         $self->{layer_height_edit_band_width});
     glBindTexture(GL_TEXTURE_2D, $self->{layer_preview_z_texture_id});
+#=============================================================================================================================================    
+    print "texture id: ";
+    print $self->{layer_preview_z_texture_id};
+    print "\n";
+#=============================================================================================================================================    
     glTexImage2D_c(GL_TEXTURE_2D, 0, GL_RGBA8, $volume->layer_height_texture_width, $volume->layer_height_texture_height, 
         0, GL_RGBA, GL_UNSIGNED_BYTE, 0);
     glTexImage2D_c(GL_TEXTURE_2D, 1, GL_RGBA8, $volume->layer_height_texture_width / 2, $volume->layer_height_texture_height / 2,
diff --git a/xs/xsp/Print.xsp b/xs/xsp/Print.xsp
index ef9c5345f..702919514 100644
--- a/xs/xsp/Print.xsp
+++ b/xs/xsp/Print.xsp
@@ -52,6 +52,9 @@ _constant()
     int region_count()
         %code%{ RETVAL = THIS->print()->regions.size(); %};
 
+    int region_volumes_count()
+        %code%{ RETVAL = THIS->region_volumes.size(); %};
+        
     Ref<Print> print();
     Ref<ModelObject> model_object();
     Ref<StaticPrintConfig> config()

From 157a34bcd935fd3b40f26f1e634fb8e06f545b3c Mon Sep 17 00:00:00 2001
From: Enrico Turri <enricoturri@seznam.cz>
Date: Thu, 24 May 2018 09:57:12 +0200
Subject: [PATCH 02/17] AMF I/O - Automatic detection if open file is zip
 archive or xml format

---
 xs/src/libslic3r/Format/AMF.cpp | 20 +++++++++++++++++---
 1 file changed, 17 insertions(+), 3 deletions(-)

diff --git a/xs/src/libslic3r/Format/AMF.cpp b/xs/src/libslic3r/Format/AMF.cpp
index 83b50ec9e..263363756 100644
--- a/xs/src/libslic3r/Format/AMF.cpp
+++ b/xs/src/libslic3r/Format/AMF.cpp
@@ -13,6 +13,9 @@
 
 #include <boost/filesystem/operations.hpp>
 #include <boost/algorithm/string.hpp>
+//############################################################################################################################################
+#include <boost/nowide/fstream.hpp>
+//############################################################################################################################################
 #include <miniz/miniz_zip.h>
 
 #if 0
@@ -666,10 +669,21 @@ bool load_amf_archive(const char *path, PresetBundle* bundle, Model *model)
 // If bundle is not a null pointer, updates it if the amf file/archive contains config data
 bool load_amf(const char *path, PresetBundle* bundle, Model *model)
 {
-    if (boost::iends_with(path, ".zip.amf"))
-        return load_amf_archive(path, bundle, model);
-    else if (boost::iends_with(path, ".amf") || boost::iends_with(path, ".amf.xml"))
+    if (boost::iends_with(path, ".amf.xml"))
+        // backward compatibility with older slic3r output
         return load_amf_file(path, bundle, model);
+    else if (boost::iends_with(path, ".amf"))
+    {
+        boost::nowide::ifstream file(path, boost::nowide::ifstream::binary);
+        if (!file.good())
+            return false;
+
+        std::string zip_mask(2, '\0');
+        file.read(const_cast<char*>(zip_mask.data()), 2);
+        file.close();
+
+        return (zip_mask == "PK") ? load_amf_archive(path, bundle, model) : load_amf_file(path, bundle, model);
+    }
     else
         return false;
 }

From d5268fdc97adde08bbc5165cfbde4714efc583fa Mon Sep 17 00:00:00 2001
From: Enrico Turri <enricoturri@seznam.cz>
Date: Mon, 28 May 2018 13:04:01 +0200
Subject: [PATCH 03/17] Removed unneeded debug output

---
 lib/Slic3r/GUI/3DScene.pm | 5 -----
 1 file changed, 5 deletions(-)

diff --git a/lib/Slic3r/GUI/3DScene.pm b/lib/Slic3r/GUI/3DScene.pm
index fca22bedd..5fc1e0b18 100644
--- a/lib/Slic3r/GUI/3DScene.pm
+++ b/lib/Slic3r/GUI/3DScene.pm
@@ -1635,11 +1635,6 @@ sub draw_active_object_annotations {
     $self->{layer_height_edit_shader}->set_uniform('z_cursor',                    $z_max * $z_cursor_relative);
     $self->{layer_height_edit_shader}->set_uniform('z_cursor_band_width',         $self->{layer_height_edit_band_width});
     glBindTexture(GL_TEXTURE_2D, $self->{layer_preview_z_texture_id});
-#=============================================================================================================================================    
-    print "texture id: ";
-    print $self->{layer_preview_z_texture_id};
-    print "\n";
-#=============================================================================================================================================    
     glTexImage2D_c(GL_TEXTURE_2D, 0, GL_RGBA8, $volume->layer_height_texture_width, $volume->layer_height_texture_height, 
         0, GL_RGBA, GL_UNSIGNED_BYTE, 0);
     glTexImage2D_c(GL_TEXTURE_2D, 1, GL_RGBA8, $volume->layer_height_texture_width / 2, $volume->layer_height_texture_height / 2,

From e65fac5e8484b7737f90948d40dfd05a49a35969 Mon Sep 17 00:00:00 2001
From: bubnikv <bubnikv@gmail.com>
Date: Mon, 4 Jun 2018 17:27:33 +0200
Subject: [PATCH 04/17] Added initial implementation of fixing 3MFs through the
 Netfabb API provided through the Windows 10 Universal Windows Platform API.

---
 xs/CMakeLists.txt                       |   2 +
 xs/src/slic3r/Utils/FixModelByWin10.cpp | 287 ++++++++++++++++++++++++
 xs/src/slic3r/Utils/FixModelByWin10.hpp |  18 ++
 3 files changed, 307 insertions(+)
 create mode 100644 xs/src/slic3r/Utils/FixModelByWin10.cpp
 create mode 100644 xs/src/slic3r/Utils/FixModelByWin10.hpp

diff --git a/xs/CMakeLists.txt b/xs/CMakeLists.txt
index 77747cd07..64f4b9274 100644
--- a/xs/CMakeLists.txt
+++ b/xs/CMakeLists.txt
@@ -232,6 +232,8 @@ add_library(libslic3r_gui STATIC
     ${LIBDIR}/slic3r/GUI/FirmwareDialog.hpp
     ${LIBDIR}/slic3r/Utils/Http.cpp
     ${LIBDIR}/slic3r/Utils/Http.hpp
+    ${LIBDIR}/slic3r/Utils/FixModelByWin10.cpp
+    ${LIBDIR}/slic3r/Utils/FixModelByWin10.hpp
     ${LIBDIR}/slic3r/Utils/OctoPrint.cpp
     ${LIBDIR}/slic3r/Utils/OctoPrint.hpp
     ${LIBDIR}/slic3r/Utils/Bonjour.cpp
diff --git a/xs/src/slic3r/Utils/FixModelByWin10.cpp b/xs/src/slic3r/Utils/FixModelByWin10.cpp
new file mode 100644
index 000000000..e4b1952a4
--- /dev/null
+++ b/xs/src/slic3r/Utils/FixModelByWin10.cpp
@@ -0,0 +1,287 @@
+#ifdef HAS_WIN10SDK
+
+#include "FixModelByWin10.hpp"
+
+#include <roapi.h>
+#include <string>
+#include <cstdint>
+// for ComPtr
+#include <wrl/client.h>
+// from C:/Program Files (x86)/Windows Kits/10/Include/10.0.17134.0/
+#include <winrt/robuffer.h>
+#include <winrt/windows.storage.provider.h>
+#include <winrt/windows.graphics.printing3d.h>
+
+extern "C"{
+	// from rapi.h
+	typedef HRESULT (__stdcall* FunctionRoInitialize)(int);
+	typedef HRESULT (__stdcall* FunctionRoUninitialize)();
+	typedef HRESULT	(__stdcall* FunctionRoActivateInstance)(HSTRING activatableClassId, IInspectable **instance);
+	typedef HRESULT (__stdcall* FunctionRoGetActivationFactory)(HSTRING activatableClassId, REFIID iid, void **factory);
+	// from winstring.h
+	typedef HRESULT	(__stdcall* FunctionWindowsCreateString)(LPCWSTR sourceString, UINT32  length, HSTRING *string);
+	typedef HRESULT	(__stdcall* FunctionWindowsDelteString)(HSTRING string);
+}
+
+HMODULE							s_hRuntimeObjectLibrary  = nullptr;
+FunctionRoInitialize			s_RoInitialize			 = nullptr;
+FunctionRoUninitialize			s_RoUninitialize		 = nullptr;
+FunctionRoActivateInstance		s_RoActivateInstance     = nullptr;
+FunctionRoGetActivationFactory	s_RoGetActivationFactory = nullptr;
+FunctionWindowsCreateString		s_WindowsCreateString    = nullptr;
+FunctionWindowsDelteString		s_WindowsDeleteString    = nullptr;
+
+bool winrt_load_runtime_object_library()
+{
+	if (s_hRuntimeObjectLibrary == nullptr)
+		s_hRuntimeObjectLibrary = LoadLibrary(L"ComBase.dll");
+	if (s_hRuntimeObjectLibrary != nullptr) {
+		s_RoInitialize			 = (FunctionRoInitialize)			GetProcAddress(s_hRuntimeObjectLibrary, "RoInitialize");
+		s_RoUninitialize		 = (FunctionRoUninitialize)			GetProcAddress(s_hRuntimeObjectLibrary, "RoUninitialize");
+		s_RoActivateInstance	 = (FunctionRoActivateInstance)		GetProcAddress(s_hRuntimeObjectLibrary, "RoActivateInstance");
+		s_RoGetActivationFactory = (FunctionRoGetActivationFactory)	GetProcAddress(s_hRuntimeObjectLibrary, "RoGetActivationFactory");
+		s_WindowsCreateString	 = (FunctionWindowsCreateString)	GetProcAddress(s_hRuntimeObjectLibrary, "WindowsCreateString");
+		s_WindowsDeleteString	 = (FunctionWindowsDelteString)		GetProcAddress(s_hRuntimeObjectLibrary, "WindowsDeleteString");
+	}
+	return s_RoInitialize && s_RoUninitialize && s_RoActivateInstance && s_WindowsCreateString && s_WindowsDeleteString;
+}
+
+static HRESULT winrt_activate_instance(const std::wstring &class_name, IInspectable **pinst)
+{
+	HSTRING hClassName;
+	HRESULT hr = (*s_WindowsCreateString)(class_name.c_str(), class_name.size(), &hClassName);
+	if (S_OK != hr) 
+		return hr;
+	hr = (*s_RoActivateInstance)(hClassName, pinst);
+	(*s_WindowsDeleteString)(hClassName);
+	return hr;
+}
+
+template<typename TYPE>
+static HRESULT winrt_activate_instance(const std::wstring &class_name, TYPE **pinst)
+{
+	IInspectable *pinspectable = nullptr;
+	HRESULT hr = winrt_activate_instance(class_name, &pinspectable);
+	if (S_OK != hr)
+		return hr;
+	hr = pinspectable->QueryInterface(__uuidof(TYPE), (void**)pinst);
+	pinspectable->Release();
+	return hr;
+}
+
+static HRESULT winrt_get_activation_factory(const std::wstring &class_name, REFIID iid, void **pinst)
+{
+	HSTRING hClassName;
+	HRESULT hr = (*s_WindowsCreateString)(class_name.c_str(), class_name.size(), &hClassName);
+	if (S_OK != hr)
+		return hr;
+	hr = (*s_RoGetActivationFactory)(hClassName, iid, pinst);
+	(*s_WindowsDeleteString)(hClassName);
+	return hr;
+}
+
+template<typename TYPE>
+static HRESULT winrt_get_activation_factory(const std::wstring &class_name, TYPE **pinst)
+{
+	return winrt_get_activation_factory(class_name, __uuidof(TYPE), reinterpret_cast<void**>(pinst));
+}
+
+template<typename T>
+static AsyncStatus winrt_async_await(const Microsoft::WRL::ComPtr<T> &asyncAction, int blocking_tick_ms = 300)
+{
+	Microsoft::WRL::ComPtr<ABI::Windows::Foundation::IAsyncInfo> asyncInfo;
+	asyncAction.As(&asyncInfo);
+	AsyncStatus status;
+	// Ugly blocking loop until the RepairAsync call finishes.
+//FIXME replace with a callback.
+// https://social.msdn.microsoft.com/Forums/en-US/a5038fb4-b7b7-4504-969d-c102faa389fb/trying-to-block-an-async-operation-and-wait-for-a-particular-time?forum=vclanguage
+	for (;;) {
+		asyncInfo->get_Status(&status);
+		if (status != AsyncStatus::Started)
+			return status;
+		::Sleep(blocking_tick_ms);
+	}
+}
+
+static HRESULT winrt_open_file_stream(
+	const std::wstring									 &path,
+	ABI::Windows::Storage::FileAccessMode				  mode,
+	ABI::Windows::Storage::Streams::IRandomAccessStream **fileStream)
+{
+	// Get the file factory.
+	Microsoft::WRL::ComPtr<ABI::Windows::Storage::IStorageFileStatics> fileFactory;
+	HRESULT hr = winrt_get_activation_factory(L"Windows.Storage.StorageFile", fileFactory.GetAddressOf());
+	if (FAILED(hr)) return hr;
+
+	// Open the file asynchronously.
+	HSTRING hstr_path;
+	hr = (*s_WindowsCreateString)(path.c_str(), path.size(), &hstr_path);
+	if (FAILED(hr)) return hr;
+	Microsoft::WRL::ComPtr<ABI::Windows::Foundation::IAsyncOperation<ABI::Windows::Storage::StorageFile*>> fileOpenAsync;
+	hr = fileFactory->GetFileFromPathAsync(hstr_path, fileOpenAsync.GetAddressOf());
+	if (FAILED(hr)) return hr;
+	(*s_WindowsDeleteString)(hstr_path);
+
+	// Wait until the file gets open, get the actual file.
+	AsyncStatus status = winrt_async_await(fileOpenAsync);
+	Microsoft::WRL::ComPtr<ABI::Windows::Storage::IStorageFile> storageFile;
+	if (status == AsyncStatus::Completed) {
+		hr = fileOpenAsync->GetResults(storageFile.GetAddressOf());
+	} else {
+		Microsoft::WRL::ComPtr<ABI::Windows::Foundation::IAsyncInfo> asyncInfo;
+		hr = fileOpenAsync.As(&asyncInfo);
+		if (FAILED(hr)) return hr;
+		HRESULT err;
+		hr = asyncInfo->get_ErrorCode(&err);
+		return FAILED(hr) ? hr : err;
+	}
+
+	Microsoft::WRL::ComPtr<ABI::Windows::Foundation::IAsyncOperation<ABI::Windows::Storage::Streams::IRandomAccessStream*>> fileStreamAsync;
+	hr = storageFile->OpenAsync(mode, fileStreamAsync.GetAddressOf());
+	if (FAILED(hr)) return hr;
+
+	status = winrt_async_await(fileStreamAsync);
+	if (status == AsyncStatus::Completed) {
+		hr = fileStreamAsync->GetResults(fileStream);
+	} else {
+		Microsoft::WRL::ComPtr<ABI::Windows::Foundation::IAsyncInfo> asyncInfo;
+		hr = fileStreamAsync.As(&asyncInfo);
+		if (FAILED(hr)) return hr;
+		HRESULT err;
+		hr = asyncInfo->get_ErrorCode(&err);
+		if (!FAILED(hr))
+			hr = err;
+	}
+	return hr;
+}
+
+bool is_windows10()
+{
+	HKEY hKey;
+	LONG lRes = RegOpenKeyExW(HKEY_LOCAL_MACHINE, L"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion", 0, KEY_READ, &hKey);
+	if (lRes == ERROR_SUCCESS) {
+		WCHAR szBuffer[512];
+		DWORD dwBufferSize = sizeof(szBuffer);
+		lRes = RegQueryValueExW(hKey, L"ProductName", 0, nullptr, (LPBYTE)szBuffer, &dwBufferSize);
+		if (lRes == ERROR_SUCCESS)
+			return wcsncmp(szBuffer, L"Windows 10", 10) == 0;
+		RegCloseKey(hKey);
+	}
+	return false;
+}
+
+bool fix_model_by_win10_sdk(const std::string &path_src, const std::string &path_dst)
+{
+	if (! is_windows10()) {
+		return false;
+	}
+
+	if (! winrt_load_runtime_object_library()) {
+		printf("Failed to initialize the WinRT library. This should not happen on Windows 10. Exiting.\n");
+		return -1;
+	}
+
+	(*s_RoInitialize)(RO_INIT_MULTITHREADED);
+	{
+		Microsoft::WRL::ComPtr<ABI::Windows::Storage::Streams::IRandomAccessStream>       fileStream;
+		HRESULT hr = winrt_open_file_stream(L"D:\\3dprint\\bug.3mf", ABI::Windows::Storage::FileAccessMode::FileAccessMode_Read, fileStream.GetAddressOf());
+
+		Microsoft::WRL::ComPtr<ABI::Windows::Graphics::Printing3D::IPrinting3D3MFPackage> printing3d3mfpackage;
+		hr = winrt_activate_instance(L"Windows.Graphics.Printing3D.Printing3D3MFPackage", printing3d3mfpackage.GetAddressOf());
+
+		Microsoft::WRL::ComPtr<ABI::Windows::Foundation::IAsyncOperation<ABI::Windows::Graphics::Printing3D::Printing3DModel*>> modelAsync;
+		hr = printing3d3mfpackage->LoadModelFromPackageAsync(fileStream.Get(), modelAsync.GetAddressOf());
+
+		AsyncStatus status = winrt_async_await(modelAsync);
+		Microsoft::WRL::ComPtr<ABI::Windows::Graphics::Printing3D::IPrinting3DModel>	  model;
+		if (status == AsyncStatus::Completed) {
+			hr = modelAsync->GetResults(model.GetAddressOf());
+		} else {
+			printf("Failed loading the input model. Exiting.\n");
+			return -1;
+		}
+
+		Microsoft::WRL::ComPtr<ABI::Windows::Foundation::Collections::IVector<ABI::Windows::Graphics::Printing3D::Printing3DMesh*>> meshes;
+		hr = model->get_Meshes(meshes.GetAddressOf());
+		unsigned num_meshes = 0;
+		hr = meshes->get_Size(&num_meshes);
+		
+		Microsoft::WRL::ComPtr<ABI::Windows::Foundation::IAsyncAction>					  repairAsync;
+		hr = model->RepairAsync(repairAsync.GetAddressOf());
+		status = winrt_async_await(repairAsync);
+		if (status != AsyncStatus::Completed) {
+			printf("Mesh repair failed. Exiting.\n");
+			return -1;
+		}
+		printf("Mesh repair finished successfully.\n");
+		repairAsync->GetResults();
+
+		// Verify the number of meshes returned after the repair action.
+		meshes.Reset();
+		hr = model->get_Meshes(meshes.GetAddressOf());
+		hr = meshes->get_Size(&num_meshes);
+
+		// Save model to this class' Printing3D3MFPackage.
+		Microsoft::WRL::ComPtr<ABI::Windows::Foundation::IAsyncAction>					  saveToPackageAsync;
+		hr = printing3d3mfpackage->SaveModelToPackageAsync(model.Get(), saveToPackageAsync.GetAddressOf());
+		status = winrt_async_await(saveToPackageAsync);
+		if (status != AsyncStatus::Completed) {
+			printf("Saving mesh into the 3MF container failed. Exiting.\n");
+			return -1;
+		}
+		hr = saveToPackageAsync->GetResults();
+
+		Microsoft::WRL::ComPtr<ABI::Windows::Foundation::IAsyncOperation<ABI::Windows::Storage::Streams::IRandomAccessStream*>> generatorStreamAsync;
+		hr = printing3d3mfpackage->SaveAsync(generatorStreamAsync.GetAddressOf());
+		status = winrt_async_await(generatorStreamAsync);
+		if (status != AsyncStatus::Completed) {
+			printf("Saving mesh into the 3MF container failed. Exiting.\n");
+			return -1;
+		}
+		Microsoft::WRL::ComPtr<ABI::Windows::Storage::Streams::IRandomAccessStream> generatorStream;
+		hr = generatorStreamAsync->GetResults(generatorStream.GetAddressOf());
+
+		// Go to the beginning of the stream.
+		generatorStream->Seek(0);
+		Microsoft::WRL::ComPtr<ABI::Windows::Storage::Streams::IInputStream> inputStream;
+		hr = generatorStream.As(&inputStream);
+
+		// Get the buffer factory.
+		Microsoft::WRL::ComPtr<ABI::Windows::Storage::Streams::IBufferFactory> bufferFactory;
+		hr = winrt_get_activation_factory(L"Windows.Storage.Streams.Buffer", bufferFactory.GetAddressOf());
+
+		// Open the destination file.
+		FILE *fout = ::fopen("D:\\3dprint\\bug-repaired.3mf", "wb");
+
+		Microsoft::WRL::ComPtr<ABI::Windows::Storage::Streams::IBuffer> buffer;
+		byte														   *buffer_ptr;
+		bufferFactory->Create(65536 * 2048, buffer.GetAddressOf());
+		{
+			Microsoft::WRL::ComPtr<Windows::Storage::Streams::IBufferByteAccess> bufferByteAccess;
+			buffer.As(&bufferByteAccess);
+			hr = bufferByteAccess->Buffer(&buffer_ptr);
+		}
+		uint32_t length;
+		hr = buffer->get_Length(&length);
+
+		Microsoft::WRL::ComPtr<ABI::Windows::Foundation::IAsyncOperationWithProgress<ABI::Windows::Storage::Streams::IBuffer*, UINT32>> asyncRead;
+		for (;;) {
+			hr = inputStream->ReadAsync(buffer.Get(), 65536 * 2048, ABI::Windows::Storage::Streams::InputStreamOptions_ReadAhead, asyncRead.GetAddressOf());
+			status = winrt_async_await(asyncRead);
+			if (status != AsyncStatus::Completed) {
+				printf("Saving mesh into the 3MF container failed. Exiting.\n");
+				return -1;
+			}
+			hr = buffer->get_Length(&length);
+			if (length == 0)
+				break;
+			fwrite(buffer_ptr, length, 1, fout);
+		}
+		fclose(fout);
+		// Here all the COM objects will be released through the ComPtr destructors.
+	}
+	(*s_RoUninitialize)();
+	return 0;
+}
+
+#endif /* HAS_WIN10SDK */
diff --git a/xs/src/slic3r/Utils/FixModelByWin10.hpp b/xs/src/slic3r/Utils/FixModelByWin10.hpp
new file mode 100644
index 000000000..268b614ef
--- /dev/null
+++ b/xs/src/slic3r/Utils/FixModelByWin10.hpp
@@ -0,0 +1,18 @@
+#ifndef slic3r_GUI_Utils_FixModelByWin10_hpp_
+#define slic3r_GUI_Utils_FixModelByWin10_hpp_
+
+#include <string>
+
+#ifdef HAS_WIN10SDK
+
+extern bool is_windows10();
+extern bool fix_model_by_win10_sdk(const std::string &path_src, const std::string &path_dst);
+
+#else /* HAS_WIN10SDK */
+
+inline bool is_windows10() { return false; }
+inline bool fix_model_by_win10_sdk(const std::string &, const std::string &) { return false; }
+
+#endif /* HAS_WIN10SDK *
+
+#endif /* slic3r_GUI_Utils_FixModelByWin10_hpp_ */

From d05d3cb652de8dfc156b62e940aaf8a7422dee20 Mon Sep 17 00:00:00 2001
From: bubnikv <bubnikv@gmail.com>
Date: Mon, 4 Jun 2018 21:22:42 +0200
Subject: [PATCH 05/17] Initial working implementation of the "Fix by Netfabb"
 function.

---
 CMakeLists.txt                          | 16 ++++++
 lib/Slic3r/GUI/Plater.pm                | 33 ++++++++++++
 xs/CMakeLists.txt                       |  7 +++
 xs/src/slic3r/Utils/FixModelByWin10.cpp | 72 +++++++++++++++++++++----
 xs/src/slic3r/Utils/FixModelByWin10.hpp | 12 ++++-
 xs/xsp/GUI.xsp                          |  7 +++
 6 files changed, 136 insertions(+), 11 deletions(-)

diff --git a/CMakeLists.txt b/CMakeLists.txt
index e8b2a6faa..60d67553b 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -49,6 +49,22 @@ if(NOT DEFINED CMAKE_PREFIX_PATH)
 	endif()
 endif()
 
+# WIN10SDK_PATH is used to point CMake to the WIN10 SDK installation directory.
+# We pick it from environment if it is not defined in another way
+if(WIN32)
+	if(NOT DEFINED WIN10SDK_PATH)
+		if(DEFINED ENV{WIN10SDK_PATH})
+			set(WIN10SDK_PATH "$ENV{WIN10SDK_PATH}")
+		endif()
+	endif()
+	if(DEFINED WIN10SDK_PATH AND NOT EXISTS "${WIN10SDK_PATH}/include/winrt/windows.graphics.printing3d.h")
+		message("WIN10SDK_PATH is invalid: ${WIN10SDK_PATH}")
+		message("${WIN10SDK_PATH}/include/winrt/windows.graphics.printing3d.h was not found")
+		message("STL fixing by the Netfabb service will not be compiled")
+		unset(WIN10SDK_PATH)
+	endif()
+endif()
+
 add_subdirectory(xs)
 
 get_filename_component(PERL_BIN_PATH "${PERL_EXECUTABLE}" DIRECTORY)
diff --git a/lib/Slic3r/GUI/Plater.pm b/lib/Slic3r/GUI/Plater.pm
index d27be785c..40c2a1351 100644
--- a/lib/Slic3r/GUI/Plater.pm
+++ b/lib/Slic3r/GUI/Plater.pm
@@ -1620,6 +1620,34 @@ sub export_object_stl {
     $self->statusbar->SetStatusText(L("STL file exported to ").$output_file);
 }
 
+sub fix_through_netfabb {
+    my ($self) = @_;
+    my ($obj_idx, $object) = $self->selected_object;
+    return if !defined $obj_idx;
+    my $model_object = $self->{model}->objects->[$obj_idx];
+    my $model_fixed = Slic3r::Model->new;
+    Slic3r::GUI::fix_model_by_win10_sdk_gui($model_object, $self->{print}, $model_fixed);
+
+    my @new_obj_idx = $self->load_model_objects(@{$model_fixed->objects});
+    return if !@new_obj_idx;
+    
+    foreach my $new_obj_idx (@new_obj_idx) {
+        my $o = $self->{model}->objects->[$new_obj_idx];
+        $o->clear_instances;
+        $o->add_instance($_) for @{$model_object->instances};
+        #$o->invalidate_bounding_box;
+        
+        if ($o->volumes_count == $model_object->volumes_count) {
+            for my $i (0..($o->volumes_count-1)) {
+                $o->get_volume($i)->config->apply($model_object->get_volume($i)->config);
+            }
+        }
+        #FIXME restore volumes and their configs, layer_height_ranges, layer_height_profile, layer_height_profile_valid,
+    }
+    
+    $self->remove($obj_idx);
+}
+
 sub export_amf {
     my ($self) = @_;
     return if !@{$self->{objects}};
@@ -2178,6 +2206,11 @@ sub object_menu {
     $frame->_append_menu_item($menu, L("Export object as STL…"), L('Export this single object as STL file'), sub {
         $self->export_object_stl;
     }, undef, 'brick_go.png');
+    if (Slic3r::GUI::is_windows10) {
+        $frame->_append_menu_item($menu, L("Fix STL through Netfabb"), L('Fix the model by sending it to a Netfabb cloud service through Windows 10 API'), sub {
+            $self->fix_through_netfabb;
+        }, undef, 'brick_go.png');
+    }
     
     return $menu;
 }
diff --git a/xs/CMakeLists.txt b/xs/CMakeLists.txt
index 64f4b9274..866374584 100644
--- a/xs/CMakeLists.txt
+++ b/xs/CMakeLists.txt
@@ -27,6 +27,13 @@ if(WIN32)
     # BOOST_ALL_NO_LIB: Avoid the automatic linking of Boost libraries on Windows. Rather rely on explicit linking.
     add_definitions(-D_USE_MATH_DEFINES -D_WIN32 -DBOOST_ALL_NO_LIB)
     # -D_ITERATOR_DEBUG_LEVEL)
+    if(WIN10SDK_PATH)
+        message("Building with Win10 Netfabb STL fixing service support")
+        add_definitions(-DHAS_WIN10SDK)
+        include_directories("${WIN10SDK_PATH}/Include")
+    else()
+        message("Building without Win10 Netfabb STL fixing service support")
+    endif()
 endif()
 
 add_definitions(-DwxUSE_UNICODE -D_UNICODE -DUNICODE)
diff --git a/xs/src/slic3r/Utils/FixModelByWin10.cpp b/xs/src/slic3r/Utils/FixModelByWin10.cpp
index e4b1952a4..27a79f84e 100644
--- a/xs/src/slic3r/Utils/FixModelByWin10.cpp
+++ b/xs/src/slic3r/Utils/FixModelByWin10.cpp
@@ -1,10 +1,15 @@
 #ifdef HAS_WIN10SDK
 
+#ifndef NOMINMAX
+# define NOMINMAX
+#endif
+
 #include "FixModelByWin10.hpp"
 
 #include <roapi.h>
 #include <string>
 #include <cstdint>
+#include <thread>
 // for ComPtr
 #include <wrl/client.h>
 // from C:/Program Files (x86)/Windows Kits/10/Include/10.0.17134.0/
@@ -12,6 +17,18 @@
 #include <winrt/windows.storage.provider.h>
 #include <winrt/windows.graphics.printing3d.h>
 
+#include <boost/filesystem.hpp>
+#include <boost/nowide/convert.hpp>
+#include <boost/nowide/cstdio.hpp>
+
+#include "libslic3r/Model.hpp"
+#include "libslic3r/Print.hpp"
+#include "libslic3r/Format/3mf.hpp"
+#include "../GUI/GUI.hpp"
+#include "../GUI/PresetBundle.hpp"
+
+#include <wx/progdlg.h>
+
 extern "C"{
 	// from rapi.h
 	typedef HRESULT (__stdcall* FunctionRoInitialize)(int);
@@ -23,6 +40,8 @@ extern "C"{
 	typedef HRESULT	(__stdcall* FunctionWindowsDelteString)(HSTRING string);
 }
 
+namespace Slic3r {
+
 HMODULE							s_hRuntimeObjectLibrary  = nullptr;
 FunctionRoInitialize			s_RoInitialize			 = nullptr;
 FunctionRoUninitialize			s_RoUninitialize		 = nullptr;
@@ -178,13 +197,13 @@ bool fix_model_by_win10_sdk(const std::string &path_src, const std::string &path
 
 	if (! winrt_load_runtime_object_library()) {
 		printf("Failed to initialize the WinRT library. This should not happen on Windows 10. Exiting.\n");
-		return -1;
+		return false;
 	}
 
-	(*s_RoInitialize)(RO_INIT_MULTITHREADED);
+	HRESULT hr = (*s_RoInitialize)(RO_INIT_MULTITHREADED);
 	{
 		Microsoft::WRL::ComPtr<ABI::Windows::Storage::Streams::IRandomAccessStream>       fileStream;
-		HRESULT hr = winrt_open_file_stream(L"D:\\3dprint\\bug.3mf", ABI::Windows::Storage::FileAccessMode::FileAccessMode_Read, fileStream.GetAddressOf());
+		hr = winrt_open_file_stream(boost::nowide::widen(path_src), ABI::Windows::Storage::FileAccessMode::FileAccessMode_Read, fileStream.GetAddressOf());
 
 		Microsoft::WRL::ComPtr<ABI::Windows::Graphics::Printing3D::IPrinting3D3MFPackage> printing3d3mfpackage;
 		hr = winrt_activate_instance(L"Windows.Graphics.Printing3D.Printing3D3MFPackage", printing3d3mfpackage.GetAddressOf());
@@ -198,7 +217,7 @@ bool fix_model_by_win10_sdk(const std::string &path_src, const std::string &path
 			hr = modelAsync->GetResults(model.GetAddressOf());
 		} else {
 			printf("Failed loading the input model. Exiting.\n");
-			return -1;
+			return false;
 		}
 
 		Microsoft::WRL::ComPtr<ABI::Windows::Foundation::Collections::IVector<ABI::Windows::Graphics::Printing3D::Printing3DMesh*>> meshes;
@@ -211,7 +230,7 @@ bool fix_model_by_win10_sdk(const std::string &path_src, const std::string &path
 		status = winrt_async_await(repairAsync);
 		if (status != AsyncStatus::Completed) {
 			printf("Mesh repair failed. Exiting.\n");
-			return -1;
+			return false;
 		}
 		printf("Mesh repair finished successfully.\n");
 		repairAsync->GetResults();
@@ -227,7 +246,7 @@ bool fix_model_by_win10_sdk(const std::string &path_src, const std::string &path
 		status = winrt_async_await(saveToPackageAsync);
 		if (status != AsyncStatus::Completed) {
 			printf("Saving mesh into the 3MF container failed. Exiting.\n");
-			return -1;
+			return false;
 		}
 		hr = saveToPackageAsync->GetResults();
 
@@ -236,7 +255,7 @@ bool fix_model_by_win10_sdk(const std::string &path_src, const std::string &path
 		status = winrt_async_await(generatorStreamAsync);
 		if (status != AsyncStatus::Completed) {
 			printf("Saving mesh into the 3MF container failed. Exiting.\n");
-			return -1;
+			return false;
 		}
 		Microsoft::WRL::ComPtr<ABI::Windows::Storage::Streams::IRandomAccessStream> generatorStream;
 		hr = generatorStreamAsync->GetResults(generatorStream.GetAddressOf());
@@ -251,7 +270,7 @@ bool fix_model_by_win10_sdk(const std::string &path_src, const std::string &path
 		hr = winrt_get_activation_factory(L"Windows.Storage.Streams.Buffer", bufferFactory.GetAddressOf());
 
 		// Open the destination file.
-		FILE *fout = ::fopen("D:\\3dprint\\bug-repaired.3mf", "wb");
+		FILE *fout = boost::nowide::fopen(path_dst.c_str(), "wb");
 
 		Microsoft::WRL::ComPtr<ABI::Windows::Storage::Streams::IBuffer> buffer;
 		byte														   *buffer_ptr;
@@ -270,7 +289,7 @@ bool fix_model_by_win10_sdk(const std::string &path_src, const std::string &path
 			status = winrt_async_await(asyncRead);
 			if (status != AsyncStatus::Completed) {
 				printf("Saving mesh into the 3MF container failed. Exiting.\n");
-				return -1;
+				return false;
 			}
 			hr = buffer->get_Length(&length);
 			if (length == 0)
@@ -281,7 +300,40 @@ bool fix_model_by_win10_sdk(const std::string &path_src, const std::string &path
 		// Here all the COM objects will be released through the ComPtr destructors.
 	}
 	(*s_RoUninitialize)();
-	return 0;
+	return true;
 }
 
+void fix_model_by_win10_sdk_gui(const ModelObject &model_object, const Print &print, Model &result)
+{
+	enum { PROGRESS_RANGE = 1000 };
+	wxProgressDialog progress_dialog(
+		_(L("Model fixing")),
+		_(L("Exporting model...")),
+		PROGRESS_RANGE, nullptr, wxPD_AUTO_HIDE | wxPD_APP_MODAL | wxPD_CAN_ABORT);
+	progress_dialog.Pulse();
+
+	// Executing the calculation in a background thread, so that the COM context could be created with its own threading model.
+	// (It seems like wxWidgets initialize the COM contex as single threaded and we need a multi-threaded context).
+	auto worker = std::thread([&model_object, &print, &result, &progress_dialog](){
+		boost::filesystem::path path_src = boost::filesystem::temp_directory_path() / boost::filesystem::unique_path();
+		path_src += ".3mf";
+		Model model;
+		model.add_object(model_object);
+		bool res = Slic3r::store_3mf(path_src.string().c_str(), &model, const_cast<Print*>(&print), false);
+		model.clear_objects(); 
+		model.clear_materials();
+
+		boost::filesystem::path path_dst = boost::filesystem::temp_directory_path() / boost::filesystem::unique_path();
+		path_dst += ".3mf";
+		res = fix_model_by_win10_sdk(path_src.string().c_str(), path_dst.string());
+		boost::filesystem::remove(path_src);
+		PresetBundle bundle;
+	    res = Slic3r::load_3mf(path_dst.string().c_str(), &bundle, &result);
+		boost::filesystem::remove(path_dst);
+	});
+	worker.join();
+}
+
+} // namespace Slic3r
+
 #endif /* HAS_WIN10SDK */
diff --git a/xs/src/slic3r/Utils/FixModelByWin10.hpp b/xs/src/slic3r/Utils/FixModelByWin10.hpp
index 268b614ef..299c9b75b 100644
--- a/xs/src/slic3r/Utils/FixModelByWin10.hpp
+++ b/xs/src/slic3r/Utils/FixModelByWin10.hpp
@@ -3,16 +3,26 @@
 
 #include <string>
 
+namespace Slic3r {
+
+class Model;
+class ModelObject;
+class Print;
+
 #ifdef HAS_WIN10SDK
 
 extern bool is_windows10();
 extern bool fix_model_by_win10_sdk(const std::string &path_src, const std::string &path_dst);
+extern void fix_model_by_win10_sdk_gui(const ModelObject &model_object, const Print &print, Model &result);
 
 #else /* HAS_WIN10SDK */
 
 inline bool is_windows10() { return false; }
 inline bool fix_model_by_win10_sdk(const std::string &, const std::string &) { return false; }
+inline void fix_model_by_win10_sdk_gui(const ModelObject &, const Print &, Model &) {}
 
-#endif /* HAS_WIN10SDK *
+#endif /* HAS_WIN10SDK */
+
+} // namespace Slic3r
 
 #endif /* slic3r_GUI_Utils_FixModelByWin10_hpp_ */
diff --git a/xs/xsp/GUI.xsp b/xs/xsp/GUI.xsp
index 897e63693..1d860d284 100644
--- a/xs/xsp/GUI.xsp
+++ b/xs/xsp/GUI.xsp
@@ -4,6 +4,7 @@
 #include <xsinit.h>
 #include "slic3r/GUI/GUI.hpp"
 #include "slic3r/Utils/ASCIIFolding.hpp"
+#include "slic3r/Utils/FixModelByWin10.hpp"
 #include "slic3r/Utils/Serial.hpp"
 %}
 
@@ -28,6 +29,9 @@ bool debugged()
 void break_to_debugger()
     %code{% Slic3r::GUI::break_to_debugger(); %};
 
+bool is_windows10()
+    %code{% RETVAL=Slic3r::is_windows10(); %};
+
 void set_wxapp(SV *ui)
     %code%{ Slic3r::GUI::set_wxapp((wxApp*)wxPli_sv_2_object(aTHX_ ui, "Wx::App")); %};
 
@@ -98,3 +102,6 @@ int get_export_option(SV *ui)
     
 void desktop_open_datadir_folder()
     %code%{ Slic3r::GUI::desktop_open_datadir_folder(); %};
+
+void fix_model_by_win10_sdk_gui(ModelObject *model_object_src, Print *print, Model *model_dst)
+    %code%{ Slic3r::fix_model_by_win10_sdk_gui(*model_object_src, *print, *model_dst); %};

From 40bb0b6f55c5702da34b801eb23ee891a06c723d Mon Sep 17 00:00:00 2001
From: Enrico Turri <enricoturri@seznam.cz>
Date: Tue, 5 Jun 2018 16:07:09 +0200
Subject: [PATCH 06/17] Fixed overflow in Polygon::area()

---
 xs/src/libslic3r/Polygon.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/xs/src/libslic3r/Polygon.cpp b/xs/src/libslic3r/Polygon.cpp
index 27f9a2ca1..b5fd7e64f 100644
--- a/xs/src/libslic3r/Polygon.cpp
+++ b/xs/src/libslic3r/Polygon.cpp
@@ -103,7 +103,7 @@ double Polygon::area() const
 
     double a = 0.;
     for (size_t i = 0, j = n - 1; i < n; ++i) {
-        a += double(points[j].x + points[i].x) * double(points[i].y - points[j].y);
+        a += ((double)points[j].x + (double)points[i].x) * ((double)points[i].y - (double)points[j].y);
         j = i;
     }
     return 0.5 * a;

From ce6a23ef3bc0e0caefdc96efc002a9a98339d50f Mon Sep 17 00:00:00 2001
From: bubnikv <bubnikv@gmail.com>
Date: Wed, 6 Jun 2018 15:19:06 +0200
Subject: [PATCH 07/17] Repair by the netfabb service: Implemented progress
 dialog and cancelation.

---
 xs/src/slic3r/Utils/FixModelByWin10.cpp | 201 ++++++++++++++++--------
 xs/src/slic3r/Utils/FixModelByWin10.hpp |   2 -
 2 files changed, 132 insertions(+), 71 deletions(-)

diff --git a/xs/src/slic3r/Utils/FixModelByWin10.cpp b/xs/src/slic3r/Utils/FixModelByWin10.cpp
index 27a79f84e..556035a5b 100644
--- a/xs/src/slic3r/Utils/FixModelByWin10.cpp
+++ b/xs/src/slic3r/Utils/FixModelByWin10.cpp
@@ -6,10 +6,19 @@
 
 #include "FixModelByWin10.hpp"
 
-#include <roapi.h>
-#include <string>
+#include <atomic>
+#include <chrono>
 #include <cstdint>
+#include <condition_variable>
+#include <exception>
+#include <string>
 #include <thread>
+
+#include <boost/filesystem.hpp>
+#include <boost/nowide/convert.hpp>
+#include <boost/nowide/cstdio.hpp>
+
+#include <roapi.h>
 // for ComPtr
 #include <wrl/client.h>
 // from C:/Program Files (x86)/Windows Kits/10/Include/10.0.17134.0/
@@ -17,16 +26,13 @@
 #include <winrt/windows.storage.provider.h>
 #include <winrt/windows.graphics.printing3d.h>
 
-#include <boost/filesystem.hpp>
-#include <boost/nowide/convert.hpp>
-#include <boost/nowide/cstdio.hpp>
-
 #include "libslic3r/Model.hpp"
 #include "libslic3r/Print.hpp"
 #include "libslic3r/Format/3mf.hpp"
 #include "../GUI/GUI.hpp"
 #include "../GUI/PresetBundle.hpp"
 
+#include <wx/msgdlg.h>
 #include <wx/progdlg.h>
 
 extern "C"{
@@ -105,8 +111,11 @@ static HRESULT winrt_get_activation_factory(const std::wstring &class_name, TYPE
 	return winrt_get_activation_factory(class_name, __uuidof(TYPE), reinterpret_cast<void**>(pinst));
 }
 
+// To be called often to test whether to cancel the operation.
+typedef std::function<void ()> ThrowOnCancelFn;
+
 template<typename T>
-static AsyncStatus winrt_async_await(const Microsoft::WRL::ComPtr<T> &asyncAction, int blocking_tick_ms = 300)
+static AsyncStatus winrt_async_await(const Microsoft::WRL::ComPtr<T> &asyncAction, ThrowOnCancelFn throw_on_cancel, int blocking_tick_ms = 100)
 {
 	Microsoft::WRL::ComPtr<ABI::Windows::Foundation::IAsyncInfo> asyncInfo;
 	asyncAction.As(&asyncInfo);
@@ -118,6 +127,7 @@ static AsyncStatus winrt_async_await(const Microsoft::WRL::ComPtr<T> &asyncActio
 		asyncInfo->get_Status(&status);
 		if (status != AsyncStatus::Started)
 			return status;
+		throw_on_cancel();
 		::Sleep(blocking_tick_ms);
 	}
 }
@@ -125,7 +135,8 @@ static AsyncStatus winrt_async_await(const Microsoft::WRL::ComPtr<T> &asyncActio
 static HRESULT winrt_open_file_stream(
 	const std::wstring									 &path,
 	ABI::Windows::Storage::FileAccessMode				  mode,
-	ABI::Windows::Storage::Streams::IRandomAccessStream **fileStream)
+	ABI::Windows::Storage::Streams::IRandomAccessStream **fileStream,
+	ThrowOnCancelFn										  throw_on_cancel)
 {
 	// Get the file factory.
 	Microsoft::WRL::ComPtr<ABI::Windows::Storage::IStorageFileStatics> fileFactory;
@@ -142,7 +153,7 @@ static HRESULT winrt_open_file_stream(
 	(*s_WindowsDeleteString)(hstr_path);
 
 	// Wait until the file gets open, get the actual file.
-	AsyncStatus status = winrt_async_await(fileOpenAsync);
+	AsyncStatus status = winrt_async_await(fileOpenAsync, throw_on_cancel);
 	Microsoft::WRL::ComPtr<ABI::Windows::Storage::IStorageFile> storageFile;
 	if (status == AsyncStatus::Completed) {
 		hr = fileOpenAsync->GetResults(storageFile.GetAddressOf());
@@ -159,7 +170,7 @@ static HRESULT winrt_open_file_stream(
 	hr = storageFile->OpenAsync(mode, fileStreamAsync.GetAddressOf());
 	if (FAILED(hr)) return hr;
 
-	status = winrt_async_await(fileStreamAsync);
+	status = winrt_async_await(fileStreamAsync, throw_on_cancel);
 	if (status == AsyncStatus::Completed) {
 		hr = fileStreamAsync->GetResults(fileStream);
 	} else {
@@ -189,21 +200,23 @@ bool is_windows10()
 	return false;
 }
 
-bool fix_model_by_win10_sdk(const std::string &path_src, const std::string &path_dst)
-{
-	if (! is_windows10()) {
-		return false;
-	}
+// Progress function, to be called regularly to update the progress.
+typedef std::function<void (const char * /* message */, unsigned /* progress */)> ProgressFn;
 
-	if (! winrt_load_runtime_object_library()) {
-		printf("Failed to initialize the WinRT library. This should not happen on Windows 10. Exiting.\n");
-		return false;
-	}
+void fix_model_by_win10_sdk(const std::string &path_src, const std::string &path_dst, ProgressFn on_progress, ThrowOnCancelFn throw_on_cancel)
+{
+	if (! is_windows10())
+		throw std::runtime_error("fix_model_by_win10_sdk called on non Windows 10 system");
+
+	if (! winrt_load_runtime_object_library())
+		throw std::runtime_error("Failed to initialize the WinRT library.");
 
 	HRESULT hr = (*s_RoInitialize)(RO_INIT_MULTITHREADED);
 	{
+		on_progress(L("Exporting the source model"), 20);
+
 		Microsoft::WRL::ComPtr<ABI::Windows::Storage::Streams::IRandomAccessStream>       fileStream;
-		hr = winrt_open_file_stream(boost::nowide::widen(path_src), ABI::Windows::Storage::FileAccessMode::FileAccessMode_Read, fileStream.GetAddressOf());
+		hr = winrt_open_file_stream(boost::nowide::widen(path_src), ABI::Windows::Storage::FileAccessMode::FileAccessMode_Read, fileStream.GetAddressOf(), throw_on_cancel);
 
 		Microsoft::WRL::ComPtr<ABI::Windows::Graphics::Printing3D::IPrinting3D3MFPackage> printing3d3mfpackage;
 		hr = winrt_activate_instance(L"Windows.Graphics.Printing3D.Printing3D3MFPackage", printing3d3mfpackage.GetAddressOf());
@@ -211,30 +224,29 @@ bool fix_model_by_win10_sdk(const std::string &path_src, const std::string &path
 		Microsoft::WRL::ComPtr<ABI::Windows::Foundation::IAsyncOperation<ABI::Windows::Graphics::Printing3D::Printing3DModel*>> modelAsync;
 		hr = printing3d3mfpackage->LoadModelFromPackageAsync(fileStream.Get(), modelAsync.GetAddressOf());
 
-		AsyncStatus status = winrt_async_await(modelAsync);
+		AsyncStatus status = winrt_async_await(modelAsync, throw_on_cancel);
 		Microsoft::WRL::ComPtr<ABI::Windows::Graphics::Printing3D::IPrinting3DModel>	  model;
-		if (status == AsyncStatus::Completed) {
+		if (status == AsyncStatus::Completed)
 			hr = modelAsync->GetResults(model.GetAddressOf());
-		} else {
-			printf("Failed loading the input model. Exiting.\n");
-			return false;
-		}
+		else
+			throw std::runtime_error(L("Failed loading the input model."));
 
 		Microsoft::WRL::ComPtr<ABI::Windows::Foundation::Collections::IVector<ABI::Windows::Graphics::Printing3D::Printing3DMesh*>> meshes;
 		hr = model->get_Meshes(meshes.GetAddressOf());
 		unsigned num_meshes = 0;
 		hr = meshes->get_Size(&num_meshes);
 		
+		on_progress(L("Repairing the model by the Netfabb service"), 40);
+		
 		Microsoft::WRL::ComPtr<ABI::Windows::Foundation::IAsyncAction>					  repairAsync;
 		hr = model->RepairAsync(repairAsync.GetAddressOf());
-		status = winrt_async_await(repairAsync);
-		if (status != AsyncStatus::Completed) {
-			printf("Mesh repair failed. Exiting.\n");
-			return false;
-		}
-		printf("Mesh repair finished successfully.\n");
+		status = winrt_async_await(repairAsync, throw_on_cancel);
+		if (status != AsyncStatus::Completed)
+			throw std::runtime_error(L("Mesh repair failed."));
 		repairAsync->GetResults();
 
+		on_progress(L("Loading the repaired model"), 60);
+
 		// Verify the number of meshes returned after the repair action.
 		meshes.Reset();
 		hr = model->get_Meshes(meshes.GetAddressOf());
@@ -243,20 +255,16 @@ bool fix_model_by_win10_sdk(const std::string &path_src, const std::string &path
 		// Save model to this class' Printing3D3MFPackage.
 		Microsoft::WRL::ComPtr<ABI::Windows::Foundation::IAsyncAction>					  saveToPackageAsync;
 		hr = printing3d3mfpackage->SaveModelToPackageAsync(model.Get(), saveToPackageAsync.GetAddressOf());
-		status = winrt_async_await(saveToPackageAsync);
-		if (status != AsyncStatus::Completed) {
-			printf("Saving mesh into the 3MF container failed. Exiting.\n");
-			return false;
-		}
+		status = winrt_async_await(saveToPackageAsync, throw_on_cancel);
+		if (status != AsyncStatus::Completed)
+			throw std::runtime_error(L("Saving mesh into the 3MF container failed."));
 		hr = saveToPackageAsync->GetResults();
 
 		Microsoft::WRL::ComPtr<ABI::Windows::Foundation::IAsyncOperation<ABI::Windows::Storage::Streams::IRandomAccessStream*>> generatorStreamAsync;
 		hr = printing3d3mfpackage->SaveAsync(generatorStreamAsync.GetAddressOf());
-		status = winrt_async_await(generatorStreamAsync);
-		if (status != AsyncStatus::Completed) {
-			printf("Saving mesh into the 3MF container failed. Exiting.\n");
-			return false;
-		}
+		status = winrt_async_await(generatorStreamAsync, throw_on_cancel);
+		if (status != AsyncStatus::Completed)
+			throw std::runtime_error(L("Saving mesh into the 3MF container failed."));
 		Microsoft::WRL::ComPtr<ABI::Windows::Storage::Streams::IRandomAccessStream> generatorStream;
 		hr = generatorStreamAsync->GetResults(generatorStream.GetAddressOf());
 
@@ -286,11 +294,9 @@ bool fix_model_by_win10_sdk(const std::string &path_src, const std::string &path
 		Microsoft::WRL::ComPtr<ABI::Windows::Foundation::IAsyncOperationWithProgress<ABI::Windows::Storage::Streams::IBuffer*, UINT32>> asyncRead;
 		for (;;) {
 			hr = inputStream->ReadAsync(buffer.Get(), 65536 * 2048, ABI::Windows::Storage::Streams::InputStreamOptions_ReadAhead, asyncRead.GetAddressOf());
-			status = winrt_async_await(asyncRead);
-			if (status != AsyncStatus::Completed) {
-				printf("Saving mesh into the 3MF container failed. Exiting.\n");
-				return false;
-			}
+			status = winrt_async_await(asyncRead, throw_on_cancel);
+			if (status != AsyncStatus::Completed)
+				throw std::runtime_error(L("Saving mesh into the 3MF container failed."));
 			hr = buffer->get_Length(&length);
 			if (length == 0)
 				break;
@@ -300,38 +306,95 @@ bool fix_model_by_win10_sdk(const std::string &path_src, const std::string &path
 		// Here all the COM objects will be released through the ComPtr destructors.
 	}
 	(*s_RoUninitialize)();
-	return true;
 }
 
+class RepairCanceledException : public std::exception {
+public:
+   const char* what() const throw() { return "Model repair has been canceled"; }
+};
+
 void fix_model_by_win10_sdk_gui(const ModelObject &model_object, const Print &print, Model &result)
 {
-	enum { PROGRESS_RANGE = 1000 };
+	std::mutex 						mutex;
+	std::condition_variable			condition;
+	std::unique_lock<std::mutex>	lock(mutex);
+	struct Progress {
+		std::string 				message;
+		int 						percent  = 0;
+		bool						updated = false;
+	} progress;
+	std::atomic<bool>				canceled = false;
+	std::atomic<bool>				finished = false;
+
+	// Open a progress dialog.
 	wxProgressDialog progress_dialog(
 		_(L("Model fixing")),
 		_(L("Exporting model...")),
-		PROGRESS_RANGE, nullptr, wxPD_AUTO_HIDE | wxPD_APP_MODAL | wxPD_CAN_ABORT);
-	progress_dialog.Pulse();
-
+		100, nullptr, wxPD_AUTO_HIDE | wxPD_APP_MODAL | wxPD_CAN_ABORT);
 	// Executing the calculation in a background thread, so that the COM context could be created with its own threading model.
 	// (It seems like wxWidgets initialize the COM contex as single threaded and we need a multi-threaded context).
-	auto worker = std::thread([&model_object, &print, &result, &progress_dialog](){
-		boost::filesystem::path path_src = boost::filesystem::temp_directory_path() / boost::filesystem::unique_path();
-		path_src += ".3mf";
-		Model model;
-		model.add_object(model_object);
-		bool res = Slic3r::store_3mf(path_src.string().c_str(), &model, const_cast<Print*>(&print), false);
-		model.clear_objects(); 
-		model.clear_materials();
-
-		boost::filesystem::path path_dst = boost::filesystem::temp_directory_path() / boost::filesystem::unique_path();
-		path_dst += ".3mf";
-		res = fix_model_by_win10_sdk(path_src.string().c_str(), path_dst.string());
-		boost::filesystem::remove(path_src);
-		PresetBundle bundle;
-	    res = Slic3r::load_3mf(path_dst.string().c_str(), &bundle, &result);
-		boost::filesystem::remove(path_dst);
+	bool success  = false;
+	auto on_progress = [&mutex, &condition, &progress](const char *msg, unsigned prcnt) {
+        std::lock_guard<std::mutex> lk(mutex);
+		progress.message = msg;
+		progress.percent = prcnt;
+		progress.updated = true;
+	    condition.notify_all();
+	};
+	auto worker_thread = boost::thread([&model_object, &print, &result, on_progress, &success, &canceled, &finished]() {
+		try {
+			on_progress(L("Exporting the source model"), 0);
+			boost::filesystem::path path_src = boost::filesystem::temp_directory_path() / boost::filesystem::unique_path();
+			path_src += ".3mf";
+			Model model;
+			model.add_object(model_object);
+			if (! Slic3r::store_3mf(path_src.string().c_str(), &model, const_cast<Print*>(&print), false)) {
+				boost::filesystem::remove(path_src);
+				throw std::runtime_error(L("Export of a temporary 3mf file failed"));
+			}
+			model.clear_objects(); 
+			model.clear_materials();
+			boost::filesystem::path path_dst = boost::filesystem::temp_directory_path() / boost::filesystem::unique_path();
+			path_dst += ".3mf";
+			fix_model_by_win10_sdk(path_src.string().c_str(), path_dst.string(), on_progress, 
+				[&canceled]() { if (canceled) throw RepairCanceledException(); });
+			boost::filesystem::remove(path_src);
+			PresetBundle bundle;
+			on_progress(L("Loading the repaired model"), 80);
+		    bool loaded = Slic3r::load_3mf(path_dst.string().c_str(), &bundle, &result);
+			boost::filesystem::remove(path_dst);
+			if (! loaded)
+ 				throw std::runtime_error(L("Import of the repaired 3mf file failed"));
+			success  = true;
+			finished = true;
+			on_progress(L("Model repair finished"), 100);
+		} catch (RepairCanceledException &ex) {
+			canceled = true;
+			finished = true;
+			on_progress(L("Model repair canceled"), 100);
+		} catch (std::exception &ex) {
+			success = false;
+			finished = true;
+			on_progress(ex.what(), 100);
+		}
 	});
-	worker.join();
+    while (! finished) {
+		condition.wait_for(lock, std::chrono::milliseconds(500), [&progress]{ return progress.updated; });
+		if (! progress_dialog.Update(progress.percent, _(progress.message)))
+			canceled = true;
+		progress.updated = false;
+    }
+
+	if (canceled) {
+		// Nothing to show.
+	} else if (success) {
+		wxMessageDialog dlg(nullptr, _(L("Model repaired successfully")), _(L("Model Repair by the Netfabb service")), wxICON_INFORMATION | wxOK_DEFAULT);
+		dlg.ShowModal();
+	} else {
+		wxMessageDialog dlg(nullptr, _(L("Model repair failed: \n")) + _(progress.message), _(L("Model Repair by the Netfabb service")), wxICON_ERROR | wxOK_DEFAULT);
+		dlg.ShowModal();
+	}
+	worker_thread.join();
 }
 
 } // namespace Slic3r
diff --git a/xs/src/slic3r/Utils/FixModelByWin10.hpp b/xs/src/slic3r/Utils/FixModelByWin10.hpp
index 299c9b75b..c148a6970 100644
--- a/xs/src/slic3r/Utils/FixModelByWin10.hpp
+++ b/xs/src/slic3r/Utils/FixModelByWin10.hpp
@@ -12,13 +12,11 @@ class Print;
 #ifdef HAS_WIN10SDK
 
 extern bool is_windows10();
-extern bool fix_model_by_win10_sdk(const std::string &path_src, const std::string &path_dst);
 extern void fix_model_by_win10_sdk_gui(const ModelObject &model_object, const Print &print, Model &result);
 
 #else /* HAS_WIN10SDK */
 
 inline bool is_windows10() { return false; }
-inline bool fix_model_by_win10_sdk(const std::string &, const std::string &) { return false; }
 inline void fix_model_by_win10_sdk_gui(const ModelObject &, const Print &, Model &) {}
 
 #endif /* HAS_WIN10SDK */

From 766d1d52a9c602c61f9bec66edb05162244e9c0b Mon Sep 17 00:00:00 2001
From: Enrico Turri <enricoturri@seznam.cz>
Date: Thu, 7 Jun 2018 16:13:32 +0200
Subject: [PATCH 08/17] Fixed import of model rotated clockwise from 3mf

---
 xs/src/libslic3r/Format/3mf.cpp | 20 ++++++++++++++++++++
 1 file changed, 20 insertions(+)

diff --git a/xs/src/libslic3r/Format/3mf.cpp b/xs/src/libslic3r/Format/3mf.cpp
index 0467962c3..2c32db1a6 100644
--- a/xs/src/libslic3r/Format/3mf.cpp
+++ b/xs/src/libslic3r/Format/3mf.cpp
@@ -1271,6 +1271,7 @@ namespace Slic3r {
         if ((std::abs(sx - sy) > 0.00001) || (std::abs(sx - sz) > 0.00001))
             return;
 
+#if 0 // use quaternions
         // rotations (extracted using quaternion)
         double inv_sx = 1.0 / sx;
         double inv_sy = 1.0 / sy;
@@ -1331,6 +1332,25 @@ namespace Slic3r {
             if (angle_z < 0.0)
                 angle_z += 2.0 * PI;
         }
+#else // use eigen library
+        double inv_sx = 1.0 / sx;
+        double inv_sy = 1.0 / sy;
+        double inv_sz = 1.0 / sz;
+
+        Eigen::Matrix3d m3x3;
+        m3x3 << (double)matrix(0, 0) * inv_sx, (double)matrix(0, 1) * inv_sy, (double)matrix(0, 2) * inv_sz,
+                (double)matrix(1, 0) * inv_sx, (double)matrix(1, 1) * inv_sy, (double)matrix(1, 2) * inv_sz,
+                (double)matrix(2, 0) * inv_sx, (double)matrix(2, 1) * inv_sy, (double)matrix(2, 2) * inv_sz;
+
+        Eigen::AngleAxisd rotation;
+        rotation.fromRotationMatrix(m3x3);
+
+        // invalid rotation axis, we currently handle only rotations around Z axis
+        if ((rotation.angle() != 0.0) && (rotation.axis() != Eigen::Vector3d::UnitZ()) && (rotation.axis() != -Eigen::Vector3d::UnitZ()))
+            return;
+
+        double angle_z = (rotation.axis() == Eigen::Vector3d::UnitZ()) ? rotation.angle() : -rotation.angle();
+#endif 
 
         instance.offset.x = offset_x;
         instance.offset.y = offset_y;

From 7499a4dea438c0619cfaaa53dc713c0b2e52f809 Mon Sep 17 00:00:00 2001
From: bubnikv <bubnikv@gmail.com>
Date: Tue, 19 Jun 2018 16:14:10 +0200
Subject: [PATCH 09/17] Disabled the UI gizmos, they are not yet ready for the
 prime time

---
 lib/Slic3r/GUI/Plater.pm | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/lib/Slic3r/GUI/Plater.pm b/lib/Slic3r/GUI/Plater.pm
index 0ac24664c..e15b8c34c 100644
--- a/lib/Slic3r/GUI/Plater.pm
+++ b/lib/Slic3r/GUI/Plater.pm
@@ -151,7 +151,7 @@ sub new {
         Slic3r::GUI::_3DScene::register_on_instance_moved_callback($self->{canvas3D}, $on_instances_moved);
         Slic3r::GUI::_3DScene::register_on_enable_action_buttons_callback($self->{canvas3D}, $enable_action_buttons);
         Slic3r::GUI::_3DScene::register_on_gizmo_scale_uniformly_callback($self->{canvas3D}, $on_gizmo_scale_uniformly);
-        Slic3r::GUI::_3DScene::enable_gizmos($self->{canvas3D}, 1);
+#        Slic3r::GUI::_3DScene::enable_gizmos($self->{canvas3D}, 1);
         Slic3r::GUI::_3DScene::enable_shader($self->{canvas3D}, 1);
         Slic3r::GUI::_3DScene::enable_force_zoom_to_bed($self->{canvas3D}, 1);
 

From 478488972c7832c669f7ae7c47360555c95a7e7d Mon Sep 17 00:00:00 2001
From: Vojtech Kral <vojtech@kral.hk>
Date: Tue, 19 Jun 2018 18:26:38 +0200
Subject: [PATCH 10/17] Updating bugfixes (#973)

* ConfigWizard: Fix MM legacy profile detect

* Remove Perl BedShapeDialog

* PresetUpdater: Look for updates in resources as well

* ConfigWizard: Startup condition based on printer profiles only rather than all profiles
Previously wizard would not run if there was a leftover filament profile but no printer profiles

* ConfigWizard: Fix button labels

* ConfigWizard: Pick the very first printer variant by default
---
 lib/Slic3r/GUI.pm                     |   1 -
 lib/Slic3r/GUI/BedShapeDialog.pm      | 316 --------------------------
 xs/src/slic3r/GUI/ConfigWizard.cpp    |  17 +-
 xs/src/slic3r/GUI/GUI.cpp             |   2 +-
 xs/src/slic3r/Utils/PresetUpdater.cpp |  27 ++-
 5 files changed, 28 insertions(+), 335 deletions(-)
 delete mode 100644 lib/Slic3r/GUI/BedShapeDialog.pm

diff --git a/lib/Slic3r/GUI.pm b/lib/Slic3r/GUI.pm
index 52c482813..80130fefe 100644
--- a/lib/Slic3r/GUI.pm
+++ b/lib/Slic3r/GUI.pm
@@ -7,7 +7,6 @@ use File::Basename qw(basename);
 use FindBin;
 use List::Util qw(first);
 use Slic3r::GUI::2DBed;
-use Slic3r::GUI::BedShapeDialog;
 use Slic3r::GUI::Controller;
 use Slic3r::GUI::Controller::ManualControlDialog;
 use Slic3r::GUI::Controller::PrinterPanel;
diff --git a/lib/Slic3r/GUI/BedShapeDialog.pm b/lib/Slic3r/GUI/BedShapeDialog.pm
deleted file mode 100644
index 70c8e0256..000000000
--- a/lib/Slic3r/GUI/BedShapeDialog.pm
+++ /dev/null
@@ -1,316 +0,0 @@
-# The bed shape dialog.
-# The dialog opens from Print Settins tab -> Bed Shape: Set...
-
-package Slic3r::GUI::BedShapeDialog;
-use strict;
-use warnings;
-use utf8;
-
-use List::Util qw(min max);
-use Slic3r::Geometry qw(X Y unscale);
-use Wx qw(:dialog :id :misc :sizer :choicebook wxTAB_TRAVERSAL);
-use Wx::Event qw(EVT_CLOSE);
-use base 'Wx::Dialog';
-
-sub new {
-    my $class = shift;
-    my ($parent, $default) = @_;
-    my $self = $class->SUPER::new($parent, -1, "Bed Shape", wxDefaultPosition, [350,700], wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER);
-    
-    $self->{panel} = my $panel = Slic3r::GUI::BedShapePanel->new($self, $default);
-    
-    my $main_sizer = Wx::BoxSizer->new(wxVERTICAL);
-    $main_sizer->Add($panel, 1, wxEXPAND);
-    $main_sizer->Add($self->CreateButtonSizer(wxOK | wxCANCEL), 0, wxEXPAND);
-    
-    $self->SetSizer($main_sizer);
-    $self->SetMinSize($self->GetSize);
-    $main_sizer->SetSizeHints($self);
-    
-    # needed to actually free memory
-    EVT_CLOSE($self, sub {
-        $self->EndModal(wxID_OK);
-        $self->Destroy;
-    });
-    
-    return $self;
-}
-
-sub GetValue {
-    my ($self) = @_;
-    return $self->{panel}->GetValue;
-}
-
-package Slic3r::GUI::BedShapePanel;
-
-use List::Util qw(min max sum first);
-use Scalar::Util qw(looks_like_number);
-use Slic3r::Geometry qw(PI X Y unscale scaled_epsilon);
-use Wx qw(:font :id :misc :sizer :choicebook :filedialog :pen :brush wxTAB_TRAVERSAL);
-use Wx::Event qw(EVT_CLOSE EVT_CHOICEBOOK_PAGE_CHANGED EVT_BUTTON);
-use base 'Wx::Panel';
-
-use constant SHAPE_RECTANGULAR  => 0;
-use constant SHAPE_CIRCULAR     => 1;
-use constant SHAPE_CUSTOM       => 2;
-
-sub new {
-    my $class = shift;
-    my ($parent, $default) = @_;
-    my $self = $class->SUPER::new($parent, -1);
-    
-    $self->on_change(undef);
-    
-    my $box = Wx::StaticBox->new($self, -1, "Shape");
-    my $sbsizer = Wx::StaticBoxSizer->new($box, wxVERTICAL);
-    
-    # shape options
-    $self->{shape_options_book} = Wx::Choicebook->new($self, -1, wxDefaultPosition, [300,-1], wxCHB_TOP);
-    $sbsizer->Add($self->{shape_options_book});
-    
-    $self->{optgroups} = [];
-    {
-        my $optgroup = $self->_init_shape_options_page('Rectangular');
-        $optgroup->append_single_option_line(Slic3r::GUI::OptionsGroup::Option->new(
-            opt_id      => 'rect_size',
-            type        => 'point',
-            label       => 'Size',
-            tooltip     => 'Size in X and Y of the rectangular plate.',
-            default     => [200,200],
-        ));
-        $optgroup->append_single_option_line(Slic3r::GUI::OptionsGroup::Option->new(
-            opt_id      => 'rect_origin',
-            type        => 'point',
-            label       => 'Origin',
-            tooltip     => 'Distance of the 0,0 G-code coordinate from the front left corner of the rectangle.',
-            default     => [0,0],
-        ));
-    }
-    {
-        my $optgroup = $self->_init_shape_options_page('Circular');
-        $optgroup->append_single_option_line(Slic3r::GUI::OptionsGroup::Option->new(
-            opt_id      => 'diameter',
-            type        => 'f',
-            label       => 'Diameter',
-            tooltip     => 'Diameter of the print bed. It is assumed that origin (0,0) is located in the center.',
-            sidetext    => 'mm',
-            default     => 200,
-        ));
-    }
-    {
-        my $optgroup = $self->_init_shape_options_page('Custom');
-        $optgroup->append_line(Slic3r::GUI::OptionsGroup::Line->new(
-            full_width  => 1,
-            widget      => sub {
-                my ($parent) = @_;
-                
-                my $btn = Wx::Button->new($parent, -1, "Load shape from STL...", wxDefaultPosition, wxDefaultSize);
-                EVT_BUTTON($self, $btn, sub { $self->_load_stl });
-                return $btn;
-            }
-        ));
-    }
-    
-    EVT_CHOICEBOOK_PAGE_CHANGED($self, -1, sub {
-        $self->_update_shape;
-    });
-    
-    # right pane with preview canvas
-    my $canvas = $self->{canvas} = Slic3r::GUI::2DBed->new($self);
-    
-    # main sizer
-    my $top_sizer = Wx::BoxSizer->new(wxHORIZONTAL);
-    $top_sizer->Add($sbsizer, 0, wxEXPAND | wxTOP | wxBOTTOM, 10);
-    $top_sizer->Add($canvas, 1, wxEXPAND | wxALL, 10) if $canvas;
-    
-    $self->SetSizerAndFit($top_sizer);
-    
-    $self->_set_shape($default);
-    $self->_update_preview;
-    
-    return $self;
-}
-
-sub on_change {
-    my ($self, $cb) = @_;
-    $self->{on_change} = $cb // sub {};
-}
-
-# Called from the constructor.
-# Set the initial bed shape from a list of points.
-# Deduce the bed shape type (rect, circle, custom)
-# This routine shall be smart enough if the user messes up
-# with the list of points in the ini file directly.
-sub _set_shape {
-    my ($self, $points) = @_;
-    
-    # is this a rectangle?
-    if (@$points == 4) {
-        my $polygon = Slic3r::Polygon->new_scale(@$points);
-        my $lines = $polygon->lines;
-        if ($lines->[0]->parallel_to_line($lines->[2]) && $lines->[1]->parallel_to_line($lines->[3])) {
-            # okay, it's a rectangle
-            
-            # find origin
-            # the || 0 hack prevents "-0" which might confuse the user
-            my $x_min = min(map $_->[X], @$points) || 0;
-            my $x_max = max(map $_->[X], @$points) || 0;
-            my $y_min = min(map $_->[Y], @$points) || 0;
-            my $y_max = max(map $_->[Y], @$points) || 0;
-            my $origin = [-$x_min, -$y_min];
-            
-            $self->{shape_options_book}->SetSelection(SHAPE_RECTANGULAR);
-            my $optgroup = $self->{optgroups}[SHAPE_RECTANGULAR];
-            $optgroup->set_value('rect_size', [ $x_max-$x_min, $y_max-$y_min ]);
-            $optgroup->set_value('rect_origin', $origin);
-            $self->_update_shape;
-            return;
-        }
-    }
-    
-    # is this a circle?
-    {
-        # Analyze the array of points. Do they reside on a circle?
-        my $polygon = Slic3r::Polygon->new_scale(@$points);
-        my $center = $polygon->bounding_box->center;
-        my @vertex_distances = map $center->distance_to($_), @$polygon;
-        my $avg_dist = sum(@vertex_distances)/@vertex_distances;
-        if (!defined first { abs($_ - $avg_dist) > 10*scaled_epsilon } @vertex_distances) {
-            # all vertices are equidistant to center
-            $self->{shape_options_book}->SetSelection(SHAPE_CIRCULAR);
-            my $optgroup = $self->{optgroups}[SHAPE_CIRCULAR];
-            $optgroup->set_value('diameter', sprintf("%.0f", unscale($avg_dist*2)));
-            $self->_update_shape;
-            return;
-        }
-    }
-    
-    if (@$points < 3) {
-        # Invalid polygon. Revert to default bed dimensions.
-        $self->{shape_options_book}->SetSelection(SHAPE_RECTANGULAR);
-        my $optgroup = $self->{optgroups}[SHAPE_RECTANGULAR];
-        $optgroup->set_value('rect_size', [200, 200]);
-        $optgroup->set_value('rect_origin', [0, 0]);
-        $self->_update_shape;
-        return;
-    }
-
-    # This is a custom bed shape, use the polygon provided.
-    $self->{shape_options_book}->SetSelection(SHAPE_CUSTOM);
-    # Copy the polygon to the canvas, make a copy of the array.
-    $self->{canvas}->bed_shape([@$points]);
-    $self->_update_shape;
-}
-
-# Update the bed shape from the dialog fields.
-sub _update_shape {
-    my ($self) = @_;
-    
-    my $page_idx = $self->{shape_options_book}->GetSelection;
-    if ($page_idx == SHAPE_RECTANGULAR) {
-        my $rect_size = $self->{optgroups}[SHAPE_RECTANGULAR]->get_value('rect_size');
-        my $rect_origin = $self->{optgroups}[SHAPE_RECTANGULAR]->get_value('rect_origin');
-        my ($x, $y) = @$rect_size;
-        return if !looks_like_number($x) || !looks_like_number($y);  # empty strings or '-' or other things
-        return if !$x || !$y or $x == 0 or $y == 0;
-        my ($x0, $y0) = (0,0);
-        my ($x1, $y1) = ($x ,$y);
-        {
-            my ($dx, $dy) = @$rect_origin;
-            return if !looks_like_number($dx) || !looks_like_number($dy);  # empty strings or '-' or other things
-            $x0 -= $dx;
-            $x1 -= $dx;
-            $y0 -= $dy;
-            $y1 -= $dy;
-        }
-        $self->{canvas}->bed_shape([
-            [$x0,$y0],
-            [$x1,$y0],
-            [$x1,$y1],
-            [$x0,$y1],
-        ]);
-    } elsif ($page_idx == SHAPE_CIRCULAR) {
-        my $diameter = $self->{optgroups}[SHAPE_CIRCULAR]->get_value('diameter');
-        return if !$diameter or $diameter == 0;
-        my $r = $diameter/2;
-        my $twopi = 2*PI;
-        my $edges = 60;
-        my $polygon = Slic3r::Polygon->new_scale(
-            map [ $r * cos $_, $r * sin $_ ],
-                map { $twopi/$edges*$_ } 1..$edges
-        );
-        $self->{canvas}->bed_shape([
-            map [ unscale($_->x), unscale($_->y) ], @$polygon  #))
-        ]);
-    }
-    
-    $self->{on_change}->();
-    $self->_update_preview;
-}
-
-sub _update_preview {
-    my ($self) = @_;
-    $self->{canvas}->Refresh if $self->{canvas};
-    $self->Refresh;
-}
-
-# Called from the constructor.
-# Create a panel for a rectangular / circular / custom bed shape. 
-sub _init_shape_options_page {
-    my ($self, $title) = @_;
-    
-    my $panel = Wx::Panel->new($self->{shape_options_book});
-    my $optgroup;
-    push @{$self->{optgroups}}, $optgroup = Slic3r::GUI::OptionsGroup->new(
-        parent      => $panel,
-        title       => 'Settings',
-        label_width => 100,
-        on_change   => sub {
-            my ($opt_id) = @_;
-            #$self->{"_$opt_id"} = $optgroup->get_value($opt_id);
-            $self->_update_shape;
-        },
-    );
-    $panel->SetSizerAndFit($optgroup->sizer);
-    $self->{shape_options_book}->AddPage($panel, $title);
-    
-    return $optgroup;
-}
-
-# Loads an stl file, projects it to the XY plane and calculates a polygon.
-sub _load_stl {
-    my ($self) = @_;
-    
-    my $dialog = Wx::FileDialog->new($self, 'Choose a file to import bed shape from (STL/OBJ/AMF/PRUSA):', "", "", &Slic3r::GUI::MODEL_WILDCARD, wxFD_OPEN | wxFD_FILE_MUST_EXIST);
-    if ($dialog->ShowModal != wxID_OK) {
-        $dialog->Destroy;
-        return;
-    }
-    my $input_file = $dialog->GetPaths;
-    $dialog->Destroy;
-    
-    my $model = Slic3r::Model->read_from_file($input_file);
-    my $mesh = $model->mesh;
-    my $expolygons = $mesh->horizontal_projection;
-
-    if (@$expolygons == 0) {
-        Slic3r::GUI::show_error($self, "The selected file contains no geometry.");
-        return;
-    }
-    if (@$expolygons > 1) {
-        Slic3r::GUI::show_error($self, "The selected file contains several disjoint areas. This is not supported.");
-        return;
-    }
-    
-    my $polygon = $expolygons->[0]->contour;
-    $self->{canvas}->bed_shape([ map [ unscale($_->x), unscale($_->y) ], @$polygon ]);
-    $self->_update_preview();
-}
-
-# Returns the resulting bed shape polygon. This value will be stored to the ini file.
-sub GetValue {
-    my ($self) = @_;
-    return $self->{canvas}->bed_shape;
-}
-
-1;
diff --git a/xs/src/slic3r/GUI/ConfigWizard.cpp b/xs/src/slic3r/GUI/ConfigWizard.cpp
index aed0c3534..ce06da853 100644
--- a/xs/src/slic3r/GUI/ConfigWizard.cpp
+++ b/xs/src/slic3r/GUI/ConfigWizard.cpp
@@ -113,6 +113,11 @@ PrinterPicker::PrinterPicker(wxWindow *parent, const VendorProfile &vendor, cons
 	sizer->Add(all_none_sizer, 0, wxEXPAND);
 
 	SetSizer(sizer);
+
+	if (cboxes.size() > 0) {
+		cboxes[0]->SetValue(true);
+		on_checkbox(cboxes[0], true);
+	}
 }
 
 void PrinterPicker::select_all(bool select)
@@ -598,10 +603,10 @@ void ConfigWizardIndex::on_paint(wxPaintEvent & evt)
 
 static const std::unordered_map<std::string, std::pair<std::string, std::string>> legacy_preset_map {{
 	{ "Original Prusa i3 MK2.ini",                           std::make_pair("MK2S", "0.4") },
-	{ "Original Prusa i3 MK2 MM Single Mode.ini",            std::make_pair("MK2S", "0.4") },
-	{ "Original Prusa i3 MK2 MM Single Mode 0.6 nozzle.ini", std::make_pair("MK2S", "0.6") },
-	{ "Original Prusa i3 MK2 MultiMaterial.ini",             std::make_pair("MK2S", "0.4") },
-	{ "Original Prusa i3 MK2 MultiMaterial 0.6 nozzle.ini",  std::make_pair("MK2S", "0.6") },
+	{ "Original Prusa i3 MK2 MM Single Mode.ini",            std::make_pair("MK2SMM", "0.4") },
+	{ "Original Prusa i3 MK2 MM Single Mode 0.6 nozzle.ini", std::make_pair("MK2SMM", "0.6") },
+	{ "Original Prusa i3 MK2 MultiMaterial.ini",             std::make_pair("MK2SMM", "0.4") },
+	{ "Original Prusa i3 MK2 MultiMaterial 0.6 nozzle.ini",  std::make_pair("MK2SMM", "0.6") },
 	{ "Original Prusa i3 MK2 0.25 nozzle.ini",               std::make_pair("MK2S", "0.25") },
 	{ "Original Prusa i3 MK2 0.6 nozzle.ini",                std::make_pair("MK2S", "0.6") },
 	{ "Original Prusa i3 MK3.ini",                           std::make_pair("MK3",  "0.4") },
@@ -809,8 +814,8 @@ ConfigWizard::ConfigWizard(wxWindow *parent, RunReason reason) :
 	topsizer->AddSpacer(INDEX_MARGIN);
 	topsizer->Add(p->hscroll, 1, wxEXPAND);
 
-	p->btn_prev = new wxButton(this, wxID_BACKWARD);
-	p->btn_next = new wxButton(this, wxID_FORWARD);
+	p->btn_prev = new wxButton(this, wxID_NONE, _(L("< &Back")));
+	p->btn_next = new wxButton(this, wxID_NONE, _(L("&Next >")));
 	p->btn_finish = new wxButton(this, wxID_APPLY, _(L("&Finish")));
 	p->btn_cancel = new wxButton(this, wxID_CANCEL);
 	p->btnsizer->AddStretchSpacer();
diff --git a/xs/src/slic3r/GUI/GUI.cpp b/xs/src/slic3r/GUI/GUI.cpp
index 974c554b6..e2f3925fc 100644
--- a/xs/src/slic3r/GUI/GUI.cpp
+++ b/xs/src/slic3r/GUI/GUI.cpp
@@ -423,7 +423,7 @@ bool check_unsaved_changes()
 
 bool config_wizard_startup(bool app_config_exists)
 {
-	if (! app_config_exists || g_PresetBundle->has_defauls_only()) {
+	if (! app_config_exists || g_PresetBundle->printers.size() <= 1) {
 		config_wizard(ConfigWizard::RR_DATA_EMPTY);
 		return true;
 	} else if (g_AppConfig->legacy_datadir()) {
diff --git a/xs/src/slic3r/Utils/PresetUpdater.cpp b/xs/src/slic3r/Utils/PresetUpdater.cpp
index f34fc4c19..8159a75e2 100644
--- a/xs/src/slic3r/Utils/PresetUpdater.cpp
+++ b/xs/src/slic3r/Utils/PresetUpdater.cpp
@@ -259,7 +259,7 @@ void PresetUpdater::priv::sync_config(const std::set<VendorProfile> vendors) con
 		}
 		const auto recommended = recommended_it->config_version;
 
-		BOOST_LOG_TRIVIAL(debug) << boost::format("New index for vendor: %1%: current version: %2%, recommended version: %3%")
+		BOOST_LOG_TRIVIAL(debug) << boost::format("Got index for vendor: %1%: current version: %2%, recommended version: %3%")
 			% vendor.name
 			% vendor.config_version.to_string()
 			% recommended.to_string();
@@ -352,20 +352,25 @@ Updates PresetUpdater::priv::get_config_updates() const
 				continue;
 			}
 
-			auto path_in_cache = cache_path / (idx.vendor() + ".ini");
-			if (! fs::exists(path_in_cache)) {
-				BOOST_LOG_TRIVIAL(warning) << "Index indicates update, but new bundle not found in cache: " << path_in_cache.string();
-				continue;
+			auto path_src = cache_path / (idx.vendor() + ".ini");
+			if (! fs::exists(path_src)) {
+				auto path_in_rsrc = rsrc_path / (idx.vendor() + ".ini");
+				if (! fs::exists(path_in_rsrc)) {
+					BOOST_LOG_TRIVIAL(warning) << boost::format("Index for vendor %1% indicates update, but bundle found in neither cache nor resources")
+						% idx.vendor();;
+					continue;
+				} else {
+					path_src = std::move(path_in_rsrc);
+				}
 			}
 
-			const auto cached_vp = VendorProfile::from_ini(path_in_cache, false);
-			if (cached_vp.config_version == recommended->config_version) {
-				updates.updates.emplace_back(std::move(path_in_cache), std::move(bundle_path), *recommended);
+			const auto new_vp = VendorProfile::from_ini(path_src, false);
+			if (new_vp.config_version == recommended->config_version) {
+				updates.updates.emplace_back(std::move(path_src), std::move(bundle_path), *recommended);
 			} else {
-				BOOST_LOG_TRIVIAL(warning) << boost::format("Vendor: %1%: Index indicates update (%2%) but cached bundle has a different version: %3%")
+				BOOST_LOG_TRIVIAL(warning) << boost::format("Index for vendor %1% indicates update (%2%) but the new bundle was found neither in cache nor resources")
 					% idx.vendor()
-					% recommended->config_version.to_string()
-					% cached_vp.config_version.to_string();
+					% recommended->config_version.to_string();
 			}
 		}
 	}

From 1602ddd56cca7a77d3d893c49df7efca926d108b Mon Sep 17 00:00:00 2001
From: Vojtech Kral <vojtech@kral.hk>
Date: Tue, 22 May 2018 12:54:46 +0200
Subject: [PATCH 11/17] avrdude: Reduce retries to make timeout time more
 reasonable

---
 xs/src/avrdude/libavrdude.h |  2 +-
 xs/src/avrdude/stk500v2.c   | 17 ++++++-----------
 2 files changed, 7 insertions(+), 12 deletions(-)

diff --git a/xs/src/avrdude/libavrdude.h b/xs/src/avrdude/libavrdude.h
index e8197f9c2..238f59615 100644
--- a/xs/src/avrdude/libavrdude.h
+++ b/xs/src/avrdude/libavrdude.h
@@ -737,7 +737,7 @@ extern bool cancel_flag;
 #define RETURN_IF_CANCEL() \
   do { \
     if (cancel_flag) { \
-      avrdude_message(MSG_INFO, "%s(): Cancelled, exiting...\n", __func__); \
+      avrdude_message(MSG_INFO, "avrdude: %s(): Cancelled, exiting...\n", __func__); \
       return -99; \
     } \
   } while (0)
diff --git a/xs/src/avrdude/stk500v2.c b/xs/src/avrdude/stk500v2.c
index d3acb639c..4d62640c0 100644
--- a/xs/src/avrdude/stk500v2.c
+++ b/xs/src/avrdude/stk500v2.c
@@ -79,7 +79,7 @@
 #define SERIAL_TIMEOUT 2
 
 // Retry count
-#define RETRIES 5
+#define RETRIES 0
 
 #if 0
 #define DEBUG(...) avrdude_message(MSG_INFO, __VA_ARGS__)
@@ -745,7 +745,7 @@ static int stk500v2_recv(PROGRAMMER * pgm, unsigned char *msg, size_t maxsize) {
 
 
 
-static int stk500v2_getsync_internal(PROGRAMMER * pgm, int retries) {
+int stk500v2_getsync(PROGRAMMER * pgm) {
   int tries = 0;
   unsigned char buf[1], resp[32];
   int status;
@@ -804,7 +804,7 @@ retry:
                         progname, pgmname[PDATA(pgm)->pgmtype]);
       return 0;
     } else {
-      if (tries > retries) {
+      if (tries > RETRIES) {
         avrdude_message(MSG_INFO, "%s: stk500v2_getsync(): can't communicate with device: resp=0x%02x\n",
                         progname, resp[0]);
         return -6;
@@ -814,7 +814,7 @@ retry:
 
   // or if we got a timeout
   } else if (status == -1) {
-    if (tries > retries) {
+    if (tries > RETRIES) {
       avrdude_message(MSG_INFO, "%s: stk500v2_getsync(): timeout communicating with programmer\n",
               progname);
       return -1;
@@ -823,7 +823,7 @@ retry:
 
   // or any other error
   } else {
-    if (tries > retries) {
+    if (tries > RETRIES) {
       avrdude_message(MSG_INFO, "%s: stk500v2_getsync(): error communicating with programmer: (%d)\n",
               progname,status);
     } else
@@ -833,11 +833,6 @@ retry:
   return 0;
 }
 
-int stk500v2_getsync(PROGRAMMER * pgm) {
-  // This is to avoid applying RETRIES exponentially
-  return stk500v2_getsync_internal(pgm, RETRIES);
-}
-
 static int stk500v2_command(PROGRAMMER * pgm, unsigned char * buf,
                             size_t len, size_t maxlen) {
   int i;
@@ -947,7 +942,7 @@ retry:
   }
 
   // otherwise try to sync up again
-  status = stk500v2_getsync_internal(pgm, 1);
+  status = stk500v2_getsync(pgm);
   if (status != 0) {
     if (tries > RETRIES) {
       avrdude_message(MSG_INFO, "%s: stk500v2_command(): failed miserably to execute command 0x%02x\n",

From 2a07f3a0d58fc5785652bc3deee5e742dac16f05 Mon Sep 17 00:00:00 2001
From: Vojtech Kral <vojtech@kral.hk>
Date: Wed, 23 May 2018 17:21:01 +0200
Subject: [PATCH 12/17] Firmware updater: Fix filename encoding on Windows

---
 xs/src/avrdude/fileio.c              | 24 ++++++++++++++++++++++--
 xs/src/slic3r/GUI/FirmwareDialog.cpp |  6 ++++--
 2 files changed, 26 insertions(+), 4 deletions(-)

diff --git a/xs/src/avrdude/fileio.c b/xs/src/avrdude/fileio.c
index f2d617823..2ed19bd15 100644
--- a/xs/src/avrdude/fileio.c
+++ b/xs/src/avrdude/fileio.c
@@ -45,6 +45,8 @@
 
 #define MAX_LINE_LEN 256  /* max line length for ASCII format input files */
 
+#define MAX_MODE_LEN 32  // For fopen_utf8()
+
 
 struct ihexrec {
   unsigned char    reclen;
@@ -100,6 +102,23 @@ static int fmt_autodetect(char * fname);
 
 
 
+static FILE *fopen_utf8(const char *filename, const char *mode)
+{
+  // On Windows we need to convert the filename to UTF-16
+#if defined(WIN32NATIVE)
+  static wchar_t fname_buffer[PATH_MAX];
+  static wchar_t mode_buffer[MAX_MODE_LEN];
+
+  if (MultiByteToWideChar(CP_UTF8, 0, filename, -1, fname_buffer, PATH_MAX) == 0) { return NULL; }
+  if (MultiByteToWideChar(CP_ACP, 0, mode, -1, mode_buffer, MAX_MODE_LEN) == 0) { return NULL; }
+
+  return _wfopen(fname_buffer, mode_buffer);
+#else
+  return fopen(filename, mode);
+#endif
+}
+
+
 char * fmtstr(FILEFMT format)
 {
   switch (format) {
@@ -1368,10 +1387,11 @@ static int fmt_autodetect(char * fname)
   int first = 1;
 
 #if defined(WIN32NATIVE)
-  f = fopen(fname, "r");
+  f = fopen_utf8(fname, "r");
 #else
   f = fopen(fname, "rb");
 #endif
+
   if (f == NULL) {
     avrdude_message(MSG_INFO, "%s: error opening %s: %s\n",
             progname, fname, strerror(errno));
@@ -1533,7 +1553,7 @@ int fileio(int op, char * filename, FILEFMT format,
 
   if (format != FMT_IMM) {
     if (!using_stdio) {
-      f = fopen(fname, fio.mode);
+      f = fopen_utf8(fname, fio.mode);
       if (f == NULL) {
         avrdude_message(MSG_INFO, "%s: can't open %s file %s: %s\n",
                 progname, fio.iodesc, fname, strerror(errno));
diff --git a/xs/src/slic3r/GUI/FirmwareDialog.cpp b/xs/src/slic3r/GUI/FirmwareDialog.cpp
index 8ea9d2d6e..e57ec6326 100644
--- a/xs/src/slic3r/GUI/FirmwareDialog.cpp
+++ b/xs/src/slic3r/GUI/FirmwareDialog.cpp
@@ -163,6 +163,7 @@ void FirmwareDialog::priv::perform_upload()
 
 	flashing_status(true);
 
+	const auto filename_utf8 = filename.utf8_str();
 	std::vector<std::string> args {{
 		"-v",
 		"-p", "atmega2560",
@@ -170,7 +171,7 @@ void FirmwareDialog::priv::perform_upload()
 		"-P", port,
 		"-b", "115200",   // XXX: is this ok to hardcode?
 		"-D",
-		"-U", (boost::format("flash:w:%1%:i") % filename.ToStdString()).str()
+		"-U", (boost::format("flash:w:%1%:i") % filename_utf8.data()).str()
 	}};
 
 	BOOST_LOG_TRIVIAL(info) << "Invoking avrdude, arguments: "
@@ -187,8 +188,9 @@ void FirmwareDialog::priv::perform_upload()
 		.args(args)
 		.on_message(std::move([q](const char *msg, unsigned /* size */) {
 			auto evt = new wxCommandEvent(EVT_AVRDUDE, q->GetId());
+			auto wxmsg = wxString::FromUTF8(msg);
 			evt->SetExtraLong(AE_MESSAGE);
-			evt->SetString(msg);
+			evt->SetString(std::move(wxmsg));
 			wxQueueEvent(q, evt);
 		}))
 		.on_progress(std::move([q](const char * /* task */, unsigned progress) {

From 5414f7379d9ba0592f60a37a0c610a569787456c Mon Sep 17 00:00:00 2001
From: Vojtech Kral <vojtech@kral.hk>
Date: Tue, 5 Jun 2018 11:55:23 +0200
Subject: [PATCH 13/17] FirmwareDialog: Fix progress display

---
 xs/src/avrdude/avrdude-slic3r.cpp    | 23 ++++++++++++++++++-----
 xs/src/avrdude/avrdude-slic3r.hpp    |  6 ++++++
 xs/src/avrdude/ser_posix.c           |  4 ++++
 xs/src/slic3r/GUI/FirmwareDialog.cpp | 23 +++++++++++++++--------
 4 files changed, 43 insertions(+), 13 deletions(-)

diff --git a/xs/src/avrdude/avrdude-slic3r.cpp b/xs/src/avrdude/avrdude-slic3r.cpp
index a859200fb..cf4380fdb 100644
--- a/xs/src/avrdude/avrdude-slic3r.cpp
+++ b/xs/src/avrdude/avrdude-slic3r.cpp
@@ -34,6 +34,7 @@ struct AvrDude::priv
 {
 	std::string sys_config;
 	std::vector<std::string> args;
+	RunFn run_fn;
 	MessageFn message_fn;
 	ProgressFn progress_fn;
 	CompleteFn complete_fn;
@@ -94,6 +95,12 @@ AvrDude& AvrDude::args(std::vector<std::string> args)
 	return *this;
 }
 
+AvrDude& AvrDude::on_run(RunFn fn)
+{
+	if (p) { p->run_fn = std::move(fn); }
+	return *this;
+}
+
 AvrDude& AvrDude::on_message(MessageFn fn)
 {
 	if (p) { p->message_fn = std::move(fn); }
@@ -123,11 +130,17 @@ AvrDude::Ptr AvrDude::run()
 
 	if (self->p) {
 		auto avrdude_thread = std::thread([self]() {
-				auto res = self->p->run();
-				if (self->p->complete_fn) {
-					self->p->complete_fn(res);
-				}
-			});
+			if (self->p->run_fn) {
+				self->p->run_fn();
+			}
+
+			auto res = self->p->run();
+
+			if (self->p->complete_fn) {
+				self->p->complete_fn(res);
+			}
+		});
+
 		self->p->avrdude_thread = std::move(avrdude_thread);
 	}
 
diff --git a/xs/src/avrdude/avrdude-slic3r.hpp b/xs/src/avrdude/avrdude-slic3r.hpp
index 8d881b094..29d96f72d 100644
--- a/xs/src/avrdude/avrdude-slic3r.hpp
+++ b/xs/src/avrdude/avrdude-slic3r.hpp
@@ -12,6 +12,7 @@ class AvrDude
 {
 public:
 	typedef std::shared_ptr<AvrDude> Ptr;
+	typedef std::function<void()> RunFn;
 	typedef std::function<void(const char * /* msg */, unsigned /* size */)> MessageFn;
 	typedef std::function<void(const char * /* task */, unsigned /* progress */)> ProgressFn;
 	typedef std::function<void(int /* exit status */)> CompleteFn;
@@ -29,6 +30,11 @@ public:
 	// Set avrdude cli arguments
 	AvrDude& args(std::vector<std::string> args);
 
+	// Set a callback to be called just after run() before avrdude is ran
+	// This can be used to perform any needed setup tasks from the background thread.
+	// This has no effect when using run_sync().
+	AvrDude& on_run(RunFn fn);
+
 	// Set message output callback
 	AvrDude& on_message(MessageFn fn);
 
diff --git a/xs/src/avrdude/ser_posix.c b/xs/src/avrdude/ser_posix.c
index 91b18e945..cb0fc0385 100644
--- a/xs/src/avrdude/ser_posix.c
+++ b/xs/src/avrdude/ser_posix.c
@@ -376,6 +376,10 @@ static int ser_recv(union filedescriptor *fd, unsigned char * buf, size_t buflen
     FD_SET(fd->ifd, &rfds);
 
     nfds = select(fd->ifd + 1, &rfds, NULL, NULL, &to2);
+    // FIXME: The timeout has different behaviour on Linux vs other Unices
+    // On Linux, the timeout is modified by subtracting the time spent,
+    // on OS X (for example), it is not modified.
+    // POSIX recommends re-initializing it before selecting.
     if (nfds == 0) {
       avrdude_message(MSG_NOTICE2, "%s: ser_recv(): programmer is not responding\n",
                         progname);
diff --git a/xs/src/slic3r/GUI/FirmwareDialog.cpp b/xs/src/slic3r/GUI/FirmwareDialog.cpp
index e57ec6326..bbb00e445 100644
--- a/xs/src/slic3r/GUI/FirmwareDialog.cpp
+++ b/xs/src/slic3r/GUI/FirmwareDialog.cpp
@@ -10,6 +10,7 @@
 #include <wx/event.h>
 #include <wx/sizer.h>
 #include <wx/settings.h>
+#include <wx/timer.h>
 #include <wx/panel.h>
 #include <wx/button.h>
 #include <wx/filepicker.h>
@@ -36,7 +37,7 @@ namespace Slic3r {
 enum AvrdudeEvent
 {
 	AE_MESSAGE,
-	AE_PRORGESS,
+	AE_PROGRESS,
 	AE_EXIT,
 };
 
@@ -62,7 +63,6 @@ struct FirmwareDialog::priv
 	std::vector<Utils::SerialPortInfo> ports;
 	wxFilePickerCtrl *hex_picker;
 	wxStaticText *txt_status;
-	wxStaticText *txt_progress;
 	wxGauge *progressbar;
 	wxCollapsiblePane *spoiler;
 	wxTextCtrl *txt_stdout;
@@ -72,6 +72,8 @@ struct FirmwareDialog::priv
 	wxString btn_flash_label_ready;
 	wxString btn_flash_label_flashing;
 
+	wxTimer timer_pulse;
+
 	// This is a shared pointer holding the background AvrDude task
 	// also serves as a status indication (it is set _iff_ the background task is running, otherwise it is reset).
 	AvrDude::Ptr avrdude;
@@ -83,6 +85,7 @@ struct FirmwareDialog::priv
 		q(q),
 		btn_flash_label_ready(_(L("Flash!"))),
 		btn_flash_label_flashing(_(L("Cancel"))),
+		timer_pulse(q),
 		avrdude_config((fs::path(::Slic3r::resources_dir()) / "avrdude" / "avrdude.conf").string()),
 		progress_tasks_done(0),
 		cancelled(false)
@@ -131,6 +134,7 @@ void FirmwareDialog::priv::flashing_status(bool value, AvrDudeComplete complete)
 		progressbar->SetValue(0);
 		progress_tasks_done = 0;
 		cancelled = false;
+		timer_pulse.Start(50);
 	} else {
 		auto text_color = wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOWTEXT);
 		port_picker->Enable();
@@ -186,6 +190,7 @@ void FirmwareDialog::priv::perform_upload()
 	avrdude = AvrDude()
 		.sys_config(avrdude_config)
 		.args(args)
+		.on_run([]() { /* TODO: needed? */ })
 		.on_message(std::move([q](const char *msg, unsigned /* size */) {
 			auto evt = new wxCommandEvent(EVT_AVRDUDE, q->GetId());
 			auto wxmsg = wxString::FromUTF8(msg);
@@ -195,7 +200,7 @@ void FirmwareDialog::priv::perform_upload()
 		}))
 		.on_progress(std::move([q](const char * /* task */, unsigned progress) {
 			auto evt = new wxCommandEvent(EVT_AVRDUDE, q->GetId());
-			evt->SetExtraLong(AE_PRORGESS);
+			evt->SetExtraLong(AE_PROGRESS);
 			evt->SetInt(progress);
 			wxQueueEvent(q, evt);
 		}))
@@ -226,19 +231,19 @@ void FirmwareDialog::priv::on_avrdude(const wxCommandEvent &evt)
 		txt_stdout->AppendText(evt.GetString());
 		break;
 
-	case AE_PRORGESS:
+	case AE_PROGRESS:
 		// We try to track overall progress here.
 		// When uploading the firmware, avrdude first reads a littlebit of status data,
 		// then performs write, then reading (verification).
-		// We Pulse() during the first read and combine progress of the latter two tasks.
+		// We ignore the first task (which just let's the timer_pulse work)
+		// and then display overall progress during the latter two tasks.
 
-		if (progress_tasks_done == 0) {
-			progressbar->Pulse();
-		} else {
+		if (progress_tasks_done > 0) {
 			progressbar->SetValue(progress_tasks_done - 100 + evt.GetInt());
 		}
 
 		if (evt.GetInt() == 100) {
+			timer_pulse.Stop();
 			progress_tasks_done += 100;
 		}
 
@@ -376,6 +381,8 @@ FirmwareDialog::FirmwareDialog(wxWindow *parent) :
 		}
 	});
 
+	Bind(wxEVT_TIMER, [this](wxTimerEvent &evt) { this->p->progressbar->Pulse(); });
+
 	Bind(EVT_AVRDUDE, [this](wxCommandEvent &evt) { this->p->on_avrdude(evt); });
 
 	Bind(wxEVT_CLOSE_WINDOW, [this](wxCloseEvent &evt) {

From 7863412687376a5882aceb46085bfdaf0cadf2b0 Mon Sep 17 00:00:00 2001
From: bubnikv <bubnikv@gmail.com>
Date: Thu, 14 Jun 2018 15:03:16 +0200
Subject: [PATCH 14/17] Firwmare updater for the Einsy external flash memory,
 to be used as a storage for localization strings.

Hacked into the avrdude Arduino STK500 (not STK500v2) protocol.
---
 xs/CMakeLists.txt                    |  16 ++--
 xs/src/avrdude/arduino.c             |  51 +++++++++++
 xs/src/avrdude/stk500.c              | 125 +++++++++++++++++----------
 xs/src/slic3r/GUI/FirmwareDialog.cpp |  12 ++-
 4 files changed, 147 insertions(+), 57 deletions(-)

diff --git a/xs/CMakeLists.txt b/xs/CMakeLists.txt
index 117af1959..66c1cdd6a 100644
--- a/xs/CMakeLists.txt
+++ b/xs/CMakeLists.txt
@@ -353,8 +353,6 @@ add_library(semver STATIC
 )
 
 
-add_subdirectory(src/avrdude)
-
 # Generate the Slic3r Perl module (XS) typemap file.
 set(MyTypemap ${CMAKE_CURRENT_BINARY_DIR}/typemap)
 add_custom_command(
@@ -517,12 +515,12 @@ if (WIN32 AND ";${PerlEmbed_CCFLAGS};" MATCHES ";[-/]Od;")
     message("Old CMAKE_CXX_FLAGS_RELEASE: ${CMAKE_CXX_FLAGS_RELEASE}")
     message("Old CMAKE_CXX_FLAGS_RELWITHDEBINFO: ${CMAKE_CXX_FLAGS_RELEASE}")
     message("Old CMAKE_CXX_FLAGS: ${CMAKE_CXX_FLAGS_RELEASE}")
-    set(CMAKE_CXX_FLAGS_RELEASE "/MD /Od /Zi /EHsc /DNDEBUG")
-    set(CMAKE_C_FLAGS_RELEASE "/MD /Od /Zi /DNDEBUG")
-    set(CMAKE_CXX_FLAGS_RELWITHDEBINFO "/MD /Od /Zi /EHsc /DNDEBUG")
-    set(CMAKE_C_FLAGS_RELWITHDEBINFO "/MD /Od /Zi /DNDEBUG")
-    set(CMAKE_CXX_FLAGS "/MD /Od /Zi /EHsc /DNDEBUG")
-    set(CMAKE_C_FLAGS "/MD /Od /Zi /DNDEBUG")
+    set(CMAKE_CXX_FLAGS_RELEASE "/MD /Od /Zi /EHsc /DNDEBUG /DWIN32")
+    set(CMAKE_C_FLAGS_RELEASE "/MD /Od /Zi /DNDEBUG /DWIN32")
+    set(CMAKE_CXX_FLAGS_RELWITHDEBINFO "/MD /Od /Zi /EHsc /DNDEBUG /DWIN32")
+    set(CMAKE_C_FLAGS_RELWITHDEBINFO "/MD /Od /Zi /DNDEBUG /DWIN32")
+    set(CMAKE_CXX_FLAGS "/MD /Od /Zi /EHsc /DNDEBUG /DWIN32")
+    set(CMAKE_C_FLAGS "/MD /Od /Zi /DNDEBUG /DWIN32")
 endif()
 # The following line will add -fPIC on Linux to make the XS.so rellocable.
 add_definitions(${PerlEmbed_CCCDLFLAGS})
@@ -530,6 +528,8 @@ if (WIN32)
     target_link_libraries(XS ${PERL_LIBRARY})
 endif()
 
+add_subdirectory(src/avrdude)
+
 ## REQUIRED packages
 
 # Find and configure boost
diff --git a/xs/src/avrdude/arduino.c b/xs/src/avrdude/arduino.c
index 566f56abd..886a43f0b 100644
--- a/xs/src/avrdude/arduino.c
+++ b/xs/src/avrdude/arduino.c
@@ -102,6 +102,57 @@ static int arduino_open(PROGRAMMER * pgm, char * port)
    */
   stk500_drain(pgm, 0);
 
+{
+  //FIXME initialization sequence for programming the external FLASH.
+  const char entry_magic_send   [] = "start\n";
+  const char entry_magic_receive[] = "w25x20cl_enter\n";
+  const char entry_magic_cfm    [] = "w25x20cl_cfm\n";
+  const char *entry_magic_ptr = entry_magic_send;
+  struct timeval tv;
+  double tstart, tnow;
+  char c;
+  gettimeofday(&tv, NULL);
+  tstart = tv.tv_sec;
+  while (*entry_magic_ptr != 0) {
+     if (serial_recv(&pgm->fd, &c, 1) < 0)
+        goto timedout;
+     printf("Received: %c (%d)\n", c, (int)c);
+     if (c != *entry_magic_ptr ++) {
+       avrdude_message(MSG_INFO, "%s: stk500v2_recv(): MK3 printer emited incorrect start code\n", progname);
+       return -1;
+     }
+     gettimeofday(&tv, NULL);
+     tnow = tv.tv_sec;
+     if (tnow-tstart > 2.) {      // wuff - signed/unsigned/overflow
+     timedout:
+       avrdude_message(MSG_INFO, "%s: stk500v2_recv(): MK3 printer did not boot up on time\n", progname);
+       return -1;
+     }
+  }
+  if (serial_send(&pgm->fd, entry_magic_receive, strlen(entry_magic_receive)) < 0) {
+    avrdude_message(MSG_INFO, "%s: stk500v2_send(): failed to send command to serial port\n",progname);
+    return -1;
+  }
+
+  entry_magic_ptr = entry_magic_cfm;
+  while (*entry_magic_ptr != 0) {
+    if (serial_recv(&pgm->fd, &c, 1) < 0)
+      goto timedout2;
+    printf("Received: %c (%d)\n", c, (int)c);
+    if (c != *entry_magic_ptr++) {
+      avrdude_message(MSG_INFO, "%s: stk500v2_recv(): MK3 printer emited incorrect start code\n", progname);
+      return -1;
+    }
+    gettimeofday(&tv, NULL);
+    tnow = tv.tv_sec;
+    if (tnow - tstart > 2.) {      // wuff - signed/unsigned/overflow
+    timedout2:
+      avrdude_message(MSG_INFO, "%s: stk500v2_recv(): MK3 printer did not boot up on time\n", progname);
+      return -1;
+    }
+  }
+}
+
   if (stk500_getsync(pgm) < 0)
     return -1;
 
diff --git a/xs/src/avrdude/stk500.c b/xs/src/avrdude/stk500.c
index 5d2d3c1df..63deb228f 100644
--- a/xs/src/avrdude/stk500.c
+++ b/xs/src/avrdude/stk500.c
@@ -716,11 +716,14 @@ static int stk500_loadaddr(PROGRAMMER * pgm, AVRMEM * mem, unsigned int addr)
   }
 
   buf[0] = Cmnd_STK_LOAD_ADDRESS;
-  buf[1] = addr & 0xff;
-  buf[2] = (addr >> 8) & 0xff;
-  buf[3] = Sync_CRC_EOP;
-
-  stk500_send(pgm, buf, 4);
+  // Workaround for the infamous ';' bug in the Prusa3D usb to serial converter.
+  // Send the binary data by nibbles to avoid transmitting the ';' character.
+  buf[1] = addr & 0x0f;
+  buf[2] = addr & 0xf0;
+  buf[3] = (addr >> 8) & 0x0f;
+  buf[4] = (addr >> 8) & 0xf0;
+  buf[5] = Sync_CRC_EOP;
+  stk500_send(pgm, buf, 6);
 
   if (stk500_recv(pgm, buf, 1) < 0)
     return -1;
@@ -765,7 +768,9 @@ static int stk500_paged_write(PROGRAMMER * pgm, AVRPART * p, AVRMEM * m,
   int block_size;
   int tries;
   unsigned int n;
-  unsigned int i;
+  unsigned int i, j;
+  unsigned int prusa3d_semicolon_workaround_round = 0;
+  bool has_semicolon = false;
 
   if (strcmp(m->desc, "flash") == 0) {
     memtype = 'F';
@@ -806,44 +811,64 @@ static int stk500_paged_write(PROGRAMMER * pgm, AVRPART * p, AVRMEM * m,
     tries++;
     stk500_loadaddr(pgm, m, addr/a_div);
 
-    /* build command block and avoid multiple send commands as it leads to a crash
-        of the silabs usb serial driver on mac os x */
-    i = 0;
-    buf[i++] = Cmnd_STK_PROG_PAGE;
-    buf[i++] = (block_size >> 8) & 0xff;
-    buf[i++] = block_size & 0xff;
-    buf[i++] = memtype;
-    memcpy(&buf[i], &m->buf[addr], block_size);
-    i += block_size;
-    buf[i++] = Sync_CRC_EOP;
-    stk500_send( pgm, buf, i);
-
-    if (stk500_recv(pgm, buf, 1) < 0)
-      return -1;
-    if (buf[0] == Resp_STK_NOSYNC) {
-      if (tries > 33) {
-        avrdude_message(MSG_INFO, "\n%s: stk500_paged_write(): can't get into sync\n",
-                progname);
-        return -3;
+    for (i = 0; i < n_bytes; ++ i)
+      if (m->buf[addr + i] == ';') {
+        has_semicolon = true;
+        break;
+      }
+
+    for (prusa3d_semicolon_workaround_round = 0; prusa3d_semicolon_workaround_round < (has_semicolon ? 2 : 1); ++ prusa3d_semicolon_workaround_round) {
+      /* build command block and avoid multiple send commands as it leads to a crash
+          of the silabs usb serial driver on mac os x */
+      i = 0;
+      buf[i++] = Cmnd_STK_PROG_PAGE;
+      // Workaround for the infamous ';' bug in the Prusa3D usb to serial converter.
+      // Send the binary data by nibbles to avoid transmitting the ';' character.
+      buf[i++] = (block_size >> 8) & 0xf0;
+      buf[i++] = (block_size >> 8) & 0x0f;
+      buf[i++] = block_size & 0xf0;
+      buf[i++] = block_size & 0x0f;
+      buf[i++] = memtype;
+      if (has_semicolon) {
+        for (j = 0; j < block_size; ++i, ++ j) {
+          buf[i] = m->buf[addr + j];
+          if (buf[i] == ';')
+            buf[i] |= (prusa3d_semicolon_workaround_round ? 0xf0 : 0x0f);
+        }
+      } else {
+        memcpy(&buf[i], &m->buf[addr], block_size);
+        i += block_size;
+      }
+      buf[i++] = Sync_CRC_EOP;
+      stk500_send( pgm, buf, i);
+
+      if (stk500_recv(pgm, buf, 1) < 0)
+        return -1;
+      if (buf[0] == Resp_STK_NOSYNC) {
+        if (tries > 33) {
+          avrdude_message(MSG_INFO, "\n%s: stk500_paged_write(): can't get into sync\n",
+                  progname);
+          return -3;
+        }
+        if (stk500_getsync(pgm) < 0)
+          return -1;
+        goto retry;
+      }
+      else if (buf[0] != Resp_STK_INSYNC) {
+        avrdude_message(MSG_INFO, "\n%s: stk500_paged_write(): (a) protocol error, "
+                        "expect=0x%02x, resp=0x%02x\n",
+                        progname, Resp_STK_INSYNC, buf[0]);
+        return -4;
+      }
+      
+      if (stk500_recv(pgm, buf, 1) < 0)
+        return -1;
+      if (buf[0] != Resp_STK_OK) {
+        avrdude_message(MSG_INFO, "\n%s: stk500_paged_write(): (a) protocol error, "
+                        "expect=0x%02x, resp=0x%02x\n",
+                        progname, Resp_STK_INSYNC, buf[0]);
+        return -5;
       }
-      if (stk500_getsync(pgm) < 0)
-	return -1;
-      goto retry;
-    }
-    else if (buf[0] != Resp_STK_INSYNC) {
-      avrdude_message(MSG_INFO, "\n%s: stk500_paged_write(): (a) protocol error, "
-                      "expect=0x%02x, resp=0x%02x\n",
-                      progname, Resp_STK_INSYNC, buf[0]);
-      return -4;
-    }
-    
-    if (stk500_recv(pgm, buf, 1) < 0)
-      return -1;
-    if (buf[0] != Resp_STK_OK) {
-      avrdude_message(MSG_INFO, "\n%s: stk500_paged_write(): (a) protocol error, "
-                      "expect=0x%02x, resp=0x%02x\n",
-                      progname, Resp_STK_INSYNC, buf[0]);
-      return -5;
     }
   }
 
@@ -893,11 +918,15 @@ static int stk500_paged_load(PROGRAMMER * pgm, AVRPART * p, AVRMEM * m,
     tries++;
     stk500_loadaddr(pgm, m, addr/a_div);
     buf[0] = Cmnd_STK_READ_PAGE;
-    buf[1] = (block_size >> 8) & 0xff;
-    buf[2] = block_size & 0xff;
-    buf[3] = memtype;
-    buf[4] = Sync_CRC_EOP;
-    stk500_send(pgm, buf, 5);
+    // Workaround for the infamous ';' bug in the Prusa3D usb to serial converter.
+    // Send the binary data by nibbles to avoid transmitting the ';' character.
+    buf[1] = (block_size >> 8) & 0xf0;
+    buf[2] = (block_size >> 8) & 0x0f;
+    buf[3] = block_size & 0xf0;
+    buf[4] = block_size & 0x0f;
+    buf[5] = memtype;
+    buf[6] = Sync_CRC_EOP;
+    stk500_send(pgm, buf, 7);
 
     if (stk500_recv(pgm, buf, 1) < 0)
       return -1;
diff --git a/xs/src/slic3r/GUI/FirmwareDialog.cpp b/xs/src/slic3r/GUI/FirmwareDialog.cpp
index bbb00e445..136b17af6 100644
--- a/xs/src/slic3r/GUI/FirmwareDialog.cpp
+++ b/xs/src/slic3r/GUI/FirmwareDialog.cpp
@@ -171,11 +171,19 @@ void FirmwareDialog::priv::perform_upload()
 	std::vector<std::string> args {{
 		"-v",
 		"-p", "atmega2560",
-		"-c", "wiring",
+		// Using the "Wiring" mode to program Rambo or Einsy, using the STK500v2 protocol (not the STK500).
+		// The Prusa's avrdude is patched to never send semicolons inside the data packets, as the USB to serial chip
+		// is flashed with a buggy firmware.
+//		"-c", "wiring",
+		// Using the "Arduino" mode to program Einsy's external flash with languages, using the STK500 protocol (not the STK500v2).
+		// The Prusa's avrdude is patched again to never send semicolons inside the data packets.
+		"-c", "arduino",
 		"-P", port,
 		"-b", "115200",   // XXX: is this ok to hardcode?
 		"-D",
+		"-u", // disable safe mode
 		"-U", (boost::format("flash:w:%1%:i") % filename_utf8.data()).str()
+//		"-v", "-v", "-v", "-v", "-v", // enable super verbose mode, logging each serial line exchange
 	}};
 
 	BOOST_LOG_TRIVIAL(info) << "Invoking avrdude, arguments: "
@@ -192,6 +200,8 @@ void FirmwareDialog::priv::perform_upload()
 		.args(args)
 		.on_run([]() { /* TODO: needed? */ })
 		.on_message(std::move([q](const char *msg, unsigned /* size */) {
+			// Debugging output to console, useful when avrdude is executed in a super verbose mode (with -v -v -v).
+			// printf("%s", msg);
 			auto evt = new wxCommandEvent(EVT_AVRDUDE, q->GetId());
 			auto wxmsg = wxString::FromUTF8(msg);
 			evt->SetExtraLong(AE_MESSAGE);

From 15f943938b622a3f32bbb8847809acb53782df2a Mon Sep 17 00:00:00 2001
From: Vojtech Kral <vojtech@kral.hk>
Date: Mon, 18 Jun 2018 18:10:50 +0200
Subject: [PATCH 15/17] avrdude: add file offset to update operation spec,
 refactoring

---
 xs/src/avrdude/arduino.c             | 95 ++++++++++++++--------------
 xs/src/avrdude/fileio.c              | 57 +++++++++++------
 xs/src/avrdude/libavrdude.h          |  5 +-
 xs/src/avrdude/main.c                |  2 +-
 xs/src/avrdude/update.c              | 38 ++++++++---
 xs/src/slic3r/GUI/FirmwareDialog.cpp |  4 +-
 6 files changed, 118 insertions(+), 83 deletions(-)

diff --git a/xs/src/avrdude/arduino.c b/xs/src/avrdude/arduino.c
index 886a43f0b..5e0693e94 100644
--- a/xs/src/avrdude/arduino.c
+++ b/xs/src/avrdude/arduino.c
@@ -80,6 +80,49 @@ static int arduino_read_sig_bytes(PROGRAMMER * pgm, AVRPART * p, AVRMEM * m)
   return 3;
 }
 
+static int prusa_init_external_flash(PROGRAMMER * pgm)
+{
+  // Note: send/receive as in _the firmare_ send & receives
+  const char entry_magic_send   [] = "start\n";
+  const char entry_magic_receive[] = "w25x20cl_enter\n";
+  const char entry_magic_cfm    [] = "w25x20cl_cfm\n";
+  const size_t buffer_len = 32;     // Should be large enough for the above messages
+
+  int res;
+  size_t recv_size;
+  char *buffer = alloca(buffer_len);
+
+  // 1. receive the "start" command
+  recv_size = sizeof(entry_magic_send) - 1;
+  res = serial_recv(&pgm->fd, buffer, recv_size);
+  if (res < 0) {
+    avrdude_message(MSG_INFO, "%s: prusa_init_external_flash(): MK3 printer did not boot up on time or serial communication failed\n", progname);
+    return -1;
+  } else if (strncmp(buffer, entry_magic_send, recv_size) != 0) {
+    avrdude_message(MSG_INFO, "%s: prusa_init_external_flash(): MK3 printer emitted incorrect start code\n", progname);
+    return -1;
+  }
+
+  // 2. Send the external flash programmer enter command
+  if (serial_send(&pgm->fd, entry_magic_receive, sizeof(entry_magic_receive) - 1) < 0) {
+    avrdude_message(MSG_INFO, "%s: prusa_init_external_flash(): Failed to send command to the printer\n",progname);
+    return -1;
+  }
+
+  // 3. Receive the entry confirmation command
+  recv_size = sizeof(entry_magic_cfm) - 1;
+  res = serial_recv(&pgm->fd, buffer, recv_size);
+  if (res < 0) {
+    avrdude_message(MSG_INFO, "%s: prusa_init_external_flash(): MK3 printer did not boot up on time or serial communication failed\n", progname);
+    return -1;
+  } else if (strncmp(buffer, entry_magic_cfm, recv_size) != 0) {
+    avrdude_message(MSG_INFO, "%s: prusa_init_external_flash(): MK3 printer emitted incorrect start code\n", progname);
+    return -1;
+  }
+
+  return 0;
+}
+
 static int arduino_open(PROGRAMMER * pgm, char * port)
 {
   union pinfo pinfo;
@@ -102,56 +145,10 @@ static int arduino_open(PROGRAMMER * pgm, char * port)
    */
   stk500_drain(pgm, 0);
 
-{
-  //FIXME initialization sequence for programming the external FLASH.
-  const char entry_magic_send   [] = "start\n";
-  const char entry_magic_receive[] = "w25x20cl_enter\n";
-  const char entry_magic_cfm    [] = "w25x20cl_cfm\n";
-  const char *entry_magic_ptr = entry_magic_send;
-  struct timeval tv;
-  double tstart, tnow;
-  char c;
-  gettimeofday(&tv, NULL);
-  tstart = tv.tv_sec;
-  while (*entry_magic_ptr != 0) {
-     if (serial_recv(&pgm->fd, &c, 1) < 0)
-        goto timedout;
-     printf("Received: %c (%d)\n", c, (int)c);
-     if (c != *entry_magic_ptr ++) {
-       avrdude_message(MSG_INFO, "%s: stk500v2_recv(): MK3 printer emited incorrect start code\n", progname);
-       return -1;
-     }
-     gettimeofday(&tv, NULL);
-     tnow = tv.tv_sec;
-     if (tnow-tstart > 2.) {      // wuff - signed/unsigned/overflow
-     timedout:
-       avrdude_message(MSG_INFO, "%s: stk500v2_recv(): MK3 printer did not boot up on time\n", progname);
-       return -1;
-     }
+  // Initialization sequence for programming the external FLASH on the Prusa MK3
+  if (prusa_init_external_flash(pgm) < 0) {
+    avrdude_message(MSG_INFO, "%s: arduino_open(): Failed to initialize MK3 external flash programming mode\n", progname);
   }
-  if (serial_send(&pgm->fd, entry_magic_receive, strlen(entry_magic_receive)) < 0) {
-    avrdude_message(MSG_INFO, "%s: stk500v2_send(): failed to send command to serial port\n",progname);
-    return -1;
-  }
-
-  entry_magic_ptr = entry_magic_cfm;
-  while (*entry_magic_ptr != 0) {
-    if (serial_recv(&pgm->fd, &c, 1) < 0)
-      goto timedout2;
-    printf("Received: %c (%d)\n", c, (int)c);
-    if (c != *entry_magic_ptr++) {
-      avrdude_message(MSG_INFO, "%s: stk500v2_recv(): MK3 printer emited incorrect start code\n", progname);
-      return -1;
-    }
-    gettimeofday(&tv, NULL);
-    tnow = tv.tv_sec;
-    if (tnow - tstart > 2.) {      // wuff - signed/unsigned/overflow
-    timedout2:
-      avrdude_message(MSG_INFO, "%s: stk500v2_recv(): MK3 printer did not boot up on time\n", progname);
-      return -1;
-    }
-  }
-}
 
   if (stk500_getsync(pgm) < 0)
     return -1;
diff --git a/xs/src/avrdude/fileio.c b/xs/src/avrdude/fileio.c
index 2ed19bd15..aa57f5587 100644
--- a/xs/src/avrdude/fileio.c
+++ b/xs/src/avrdude/fileio.c
@@ -45,7 +45,7 @@
 
 #define MAX_LINE_LEN 256  /* max line length for ASCII format input files */
 
-#define MAX_MODE_LEN 32  // For fopen_utf8()
+#define MAX_MODE_LEN 32  // For fopen_and_seek()
 
 
 struct ihexrec {
@@ -98,12 +98,13 @@ static int fileio_num(struct fioparms * fio,
 		char * filename, FILE * f, AVRMEM * mem, int size,
 		FILEFMT fmt);
 
-static int fmt_autodetect(char * fname);
+static int fmt_autodetect(char * fname, size_t offset);
 
 
 
-static FILE *fopen_utf8(const char *filename, const char *mode)
+static FILE *fopen_and_seek(const char *filename, const char *mode, size_t offset)
 {
+  FILE *file;
   // On Windows we need to convert the filename to UTF-16
 #if defined(WIN32NATIVE)
   static wchar_t fname_buffer[PATH_MAX];
@@ -112,10 +113,24 @@ static FILE *fopen_utf8(const char *filename, const char *mode)
   if (MultiByteToWideChar(CP_UTF8, 0, filename, -1, fname_buffer, PATH_MAX) == 0) { return NULL; }
   if (MultiByteToWideChar(CP_ACP, 0, mode, -1, mode_buffer, MAX_MODE_LEN) == 0) { return NULL; }
 
-  return _wfopen(fname_buffer, mode_buffer);
+  file = _wfopen(fname_buffer, mode_buffer);
 #else
-  return fopen(filename, mode);
+  file = fopen(filename, mode);
 #endif
+
+  if (file != NULL) {
+    // Some systems allow seeking past the end of file, so we need check for that first and disallow
+    if (fseek(file, 0, SEEK_END) != 0
+      || offset >= ftell(file)
+      || fseek(file, offset, SEEK_SET) != 0
+      ) {
+      fclose(file);
+      file = NULL;
+      errno = EINVAL;
+    }
+  }
+
+  return file;
 }
 
 
@@ -1377,7 +1392,7 @@ int fileio_setparms(int op, struct fioparms * fp,
 
 
 
-static int fmt_autodetect(char * fname)
+static int fmt_autodetect(char * fname, size_t offset)
 {
   FILE * f;
   unsigned char buf[MAX_LINE_LEN];
@@ -1387,9 +1402,9 @@ static int fmt_autodetect(char * fname)
   int first = 1;
 
 #if defined(WIN32NATIVE)
-  f = fopen_utf8(fname, "r");
+  f = fopen_and_seek(fname, "r", offset);
 #else
-  f = fopen(fname, "rb");
+  f = fopen_and_seek(fname, "rb", offset);
 #endif
 
   if (f == NULL) {
@@ -1465,7 +1480,7 @@ static int fmt_autodetect(char * fname)
 
 
 int fileio(int op, char * filename, FILEFMT format, 
-             struct avrpart * p, char * memtype, int size)
+             struct avrpart * p, char * memtype, int size, size_t offset)
 {
   int rc;
   FILE * f;
@@ -1497,15 +1512,17 @@ int fileio(int op, char * filename, FILEFMT format,
   using_stdio = 0;
 
   if (strcmp(filename, "-")==0) {
-    if (fio.op == FIO_READ) {
-      fname = "<stdin>";
-      f = stdin;
-    }
-    else {
-      fname = "<stdout>";
-      f = stdout;
-    }
-    using_stdio = 1;
+    return -1;
+    // Note: we don't want to read stdin or write to stdout as part of Slic3r
+    // if (fio.op == FIO_READ) {
+    //   fname = "<stdin>";
+    //   f = stdin;
+    // }
+    // else {
+    //   fname = "<stdout>";
+    //   f = stdout;
+    // }
+    // using_stdio = 1;
   }
   else {
     fname = filename;
@@ -1522,7 +1539,7 @@ int fileio(int op, char * filename, FILEFMT format,
       return -1;
     }
 
-    format_detect = fmt_autodetect(fname);
+    format_detect = fmt_autodetect(fname, offset);
     if (format_detect < 0) {
       avrdude_message(MSG_INFO, "%s: can't determine file format for %s, specify explicitly\n",
                       progname, fname);
@@ -1553,7 +1570,7 @@ int fileio(int op, char * filename, FILEFMT format,
 
   if (format != FMT_IMM) {
     if (!using_stdio) {
-      f = fopen_utf8(fname, fio.mode);
+      f = fopen_and_seek(fname, fio.mode, offset);
       if (f == NULL) {
         avrdude_message(MSG_INFO, "%s: can't open %s file %s: %s\n",
                 progname, fio.iodesc, fname, strerror(errno));
diff --git a/xs/src/avrdude/libavrdude.h b/xs/src/avrdude/libavrdude.h
index 238f59615..536f1a2f7 100644
--- a/xs/src/avrdude/libavrdude.h
+++ b/xs/src/avrdude/libavrdude.h
@@ -821,7 +821,7 @@ extern "C" {
 char * fmtstr(FILEFMT format);
 
 int fileio(int op, char * filename, FILEFMT format,
-           struct avrpart * p, char * memtype, int size);
+           struct avrpart * p, char * memtype, int size, size_t offset);
 
 #ifdef __cplusplus
 }
@@ -870,6 +870,7 @@ enum updateflags {
 typedef struct update_t {
   char * memtype;
   int    op;
+  size_t offset;
   char * filename;
   int    format;
 } UPDATE;
@@ -881,7 +882,7 @@ extern "C" {
 extern UPDATE * parse_op(char * s);
 extern UPDATE * dup_update(UPDATE * upd);
 extern UPDATE * new_update(int op, char * memtype, int filefmt,
-			   char * filename);
+			   char * filename, size_t offset);
 extern void free_update(UPDATE * upd);
 extern int do_op(PROGRAMMER * pgm, struct avrpart * p, UPDATE * upd,
 		 enum updateflags flags);
diff --git a/xs/src/avrdude/main.c b/xs/src/avrdude/main.c
index 0550ceff1..91f2fc827 100644
--- a/xs/src/avrdude/main.c
+++ b/xs/src/avrdude/main.c
@@ -194,7 +194,7 @@ static void usage(void)
  "  -F                         Override invalid signature check.\n"
  "  -e                         Perform a chip erase.\n"
  "  -O                         Perform RC oscillator calibration (see AVR053). \n"
- "  -U <memtype>:r|w|v:<filename>[:format]\n"
+ "  -U <memtype>:r|w|v:<offset>:<filename>[:format]\n"
  "                             Memory operation specification.\n"
  "                             Multiple -U options are allowed, each request\n"
  "                             is performed in the order specified.\n"
diff --git a/xs/src/avrdude/update.c b/xs/src/avrdude/update.c
index a73461dfa..fa3372476 100644
--- a/xs/src/avrdude/update.c
+++ b/xs/src/avrdude/update.c
@@ -101,6 +101,25 @@ UPDATE * parse_op(char * s)
 
   p++;
 
+  // Extension: Parse file contents offset
+  size_t offset = 0;
+
+  for (; *p != ':'; p++) {
+    if (*p >= '0' && *p <= '9') {
+      offset *= 10;
+      offset += *p - 0x30;
+    } else {
+      avrdude_message(MSG_INFO, "%s: invalid update specification: offset is not a number\n", progname);
+      free(upd->memtype);
+      free(upd);
+      return NULL;
+    }
+  }
+
+  upd->offset = offset;
+  printf("parse_op: offset: %lu\n", offset);
+  p++;
+
   /*
    * Now, parse the filename component.  Instead of looking for the
    * leftmost possible colon delimiter, we look for the rightmost one.
@@ -176,7 +195,7 @@ UPDATE * dup_update(UPDATE * upd)
   return u;
 }
 
-UPDATE * new_update(int op, char * memtype, int filefmt, char * filename)
+UPDATE * new_update(int op, char * memtype, int filefmt, char * filename, size_t offset)
 {
   UPDATE * u;
 
@@ -190,6 +209,7 @@ UPDATE * new_update(int op, char * memtype, int filefmt, char * filename)
   u->filename = strdup(filename);
   u->op = op;
   u->format = filefmt;
+  u->offset = offset;
 
   return u;
 }
@@ -250,7 +270,7 @@ int do_op(PROGRAMMER * pgm, struct avrpart * p, UPDATE * upd, enum updateflags f
                       progname,
                       strcmp(upd->filename, "-")==0 ? "<stdout>" : upd->filename);
     }
-    rc = fileio(FIO_WRITE, upd->filename, upd->format, p, upd->memtype, size);
+    rc = fileio(FIO_WRITE, upd->filename, upd->format, p, upd->memtype, size, 0);
     if (rc < 0) {
       avrdude_message(MSG_INFO, "%s: write to file '%s' failed\n",
               progname, upd->filename);
@@ -267,7 +287,7 @@ int do_op(PROGRAMMER * pgm, struct avrpart * p, UPDATE * upd, enum updateflags f
                       progname,
                       strcmp(upd->filename, "-")==0 ? "<stdin>" : upd->filename);
     }
-    rc = fileio(FIO_READ, upd->filename, upd->format, p, upd->memtype, -1);
+    rc = fileio(FIO_READ, upd->filename, upd->format, p, upd->memtype, -1, upd->offset);
     if (rc < 0) {
       avrdude_message(MSG_INFO, "%s: read from file '%s' failed\n",
               progname, upd->filename);
@@ -296,11 +316,11 @@ int do_op(PROGRAMMER * pgm, struct avrpart * p, UPDATE * upd, enum updateflags f
       report_progress(1,1,NULL);
     }
     else {
-      /*
-       * test mode, don't actually write to the chip, output the buffer
-       * to stdout in intel hex instead
-       */
-      rc = fileio(FIO_WRITE, "-", FMT_IHEX, p, upd->memtype, size);
+      // /*
+      //  * test mode, don't actually write to the chip, output the buffer
+      //  * to stdout in intel hex instead
+      //  */
+      // rc = fileio(FIO_WRITE, "-", FMT_IHEX, p, upd->memtype, size, 0);
     }
 
     if (rc < 0) {
@@ -332,7 +352,7 @@ int do_op(PROGRAMMER * pgm, struct avrpart * p, UPDATE * upd, enum updateflags f
             progname, mem->desc, upd->filename);
     }
 
-    rc = fileio(FIO_READ, upd->filename, upd->format, p, upd->memtype, -1);
+    rc = fileio(FIO_READ, upd->filename, upd->format, p, upd->memtype, -1, upd->offset);
     if (rc < 0) {
       avrdude_message(MSG_INFO, "%s: read from file '%s' failed\n",
               progname, upd->filename);
diff --git a/xs/src/slic3r/GUI/FirmwareDialog.cpp b/xs/src/slic3r/GUI/FirmwareDialog.cpp
index 136b17af6..f9aabacc0 100644
--- a/xs/src/slic3r/GUI/FirmwareDialog.cpp
+++ b/xs/src/slic3r/GUI/FirmwareDialog.cpp
@@ -182,8 +182,8 @@ void FirmwareDialog::priv::perform_upload()
 		"-b", "115200",   // XXX: is this ok to hardcode?
 		"-D",
 		"-u", // disable safe mode
-		"-U", (boost::format("flash:w:%1%:i") % filename_utf8.data()).str()
-//		"-v", "-v", "-v", "-v", "-v", // enable super verbose mode, logging each serial line exchange
+		"-U", (boost::format("flash:w:0:%1%:i") % filename_utf8.data()).str(),    // FIXME
+		// "-vvvvv", //"-v", "-v", "-v", "-v", // enable super verbose mode, logging each serial line exchange
 	}};
 
 	BOOST_LOG_TRIVIAL(info) << "Invoking avrdude, arguments: "

From 635bb1e484e319b1d403faac39170cc4d0dfa6f7 Mon Sep 17 00:00:00 2001
From: Vojtech Kral <vojtech@kral.hk>
Date: Tue, 19 Jun 2018 11:16:56 +0200
Subject: [PATCH 16/17] Firmware updater: Add support for l10n firmware images

---
 xs/src/avrdude/avrdude-slic3r.cpp    |  35 ++++--
 xs/src/avrdude/avrdude-slic3r.hpp    |  19 +--
 xs/src/avrdude/main.c                |   2 +-
 xs/src/slic3r/GUI/FirmwareDialog.cpp | 178 ++++++++++++++++++---------
 4 files changed, 156 insertions(+), 78 deletions(-)

diff --git a/xs/src/avrdude/avrdude-slic3r.cpp b/xs/src/avrdude/avrdude-slic3r.cpp
index cf4380fdb..030353413 100644
--- a/xs/src/avrdude/avrdude-slic3r.cpp
+++ b/xs/src/avrdude/avrdude-slic3r.cpp
@@ -1,5 +1,6 @@
 #include "avrdude-slic3r.hpp"
 
+#include <deque>
 #include <thread>
 
 extern "C" {
@@ -33,7 +34,8 @@ static void avrdude_progress_handler_closure(const char *task, unsigned progress
 struct AvrDude::priv
 {
 	std::string sys_config;
-	std::vector<std::string> args;
+	std::deque<std::vector<std::string>> args;
+	size_t current_args_set = 0;
 	RunFn run_fn;
 	MessageFn message_fn;
 	ProgressFn progress_fn;
@@ -41,10 +43,13 @@ struct AvrDude::priv
 
 	std::thread avrdude_thread;
 
+	priv(std::string &&sys_config) : sys_config(sys_config) {}
+
+	int run_one(const std::vector<std::string> &args);
 	int run();
 };
 
-int AvrDude::priv::run() {
+int AvrDude::priv::run_one(const std::vector<std::string> &args) {
 	std::vector<char*> c_args {{ const_cast<char*>(PACKAGE_NAME) }};
 	for (const auto &arg : args) {
 		c_args.push_back(const_cast<char*>(arg.data()));
@@ -69,10 +74,22 @@ int AvrDude::priv::run() {
 	return res;
 }
 
+int AvrDude::priv::run() {
+	for (; args.size() > 0; current_args_set++) {
+		int res = run_one(args.front());
+		args.pop_front();
+		if (res != 0) {
+			return res;
+		}
+	}
+
+	return 0;
+}
+
 
 // Public
 
-AvrDude::AvrDude() : p(new priv()) {}
+AvrDude::AvrDude(std::string sys_config) : p(new priv(std::move(sys_config))) {}
 
 AvrDude::AvrDude(AvrDude &&other) : p(std::move(other.p)) {}
 
@@ -83,15 +100,9 @@ AvrDude::~AvrDude()
 	}
 }
 
-AvrDude& AvrDude::sys_config(std::string sys_config)
+AvrDude& AvrDude::push_args(std::vector<std::string> args)
 {
-	if (p) { p->sys_config = std::move(sys_config); }
-	return *this;
-}
-
-AvrDude& AvrDude::args(std::vector<std::string> args)
-{
-	if (p) { p->args = std::move(args); }
+	if (p) { p->args.push_back(std::move(args)); }
 	return *this;
 }
 
@@ -137,7 +148,7 @@ AvrDude::Ptr AvrDude::run()
 			auto res = self->p->run();
 
 			if (self->p->complete_fn) {
-				self->p->complete_fn(res);
+				self->p->complete_fn(res, self->p->current_args_set);
 			}
 		});
 
diff --git a/xs/src/avrdude/avrdude-slic3r.hpp b/xs/src/avrdude/avrdude-slic3r.hpp
index 29d96f72d..273aa2378 100644
--- a/xs/src/avrdude/avrdude-slic3r.hpp
+++ b/xs/src/avrdude/avrdude-slic3r.hpp
@@ -15,20 +15,20 @@ public:
 	typedef std::function<void()> RunFn;
 	typedef std::function<void(const char * /* msg */, unsigned /* size */)> MessageFn;
 	typedef std::function<void(const char * /* task */, unsigned /* progress */)> ProgressFn;
-	typedef std::function<void(int /* exit status */)> CompleteFn;
+	typedef std::function<void(int /* exit status */, size_t /* args_id */)> CompleteFn;
 
-	AvrDude();
+	// Main c-tor, sys_config is the location of avrdude's main configuration file
+	AvrDude(std::string sys_config);
 	AvrDude(AvrDude &&);
 	AvrDude(const AvrDude &) = delete;
 	AvrDude &operator=(AvrDude &&) = delete;
 	AvrDude &operator=(const AvrDude &) = delete;
 	~AvrDude();
 
-	// Set location of avrdude's main configuration file
-	AvrDude& sys_config(std::string sys_config);
-
-	// Set avrdude cli arguments
-	AvrDude& args(std::vector<std::string> args);
+	// Push a set of avrdude cli arguments
+	// Each set makes one avrdude invocation - use this method multiple times to push
+	// more than one avrdude invocations.
+	AvrDude& push_args(std::vector<std::string> args);
 
 	// Set a callback to be called just after run() before avrdude is ran
 	// This can be used to perform any needed setup tasks from the background thread.
@@ -42,7 +42,10 @@ public:
 	// Progress is reported per each task (reading / writing) in percents.
 	AvrDude& on_progress(ProgressFn fn);
 
-	// Called when avrdude's main function finishes
+	// Called when the last avrdude invocation finishes with the exit status of zero,
+	// or earlier, if one of the invocations return a non-zero status.
+	// The second argument contains the sequential id of the last avrdude invocation argument set.
+	// This has no effect when using run_sync().
 	AvrDude& on_complete(CompleteFn fn);
 
 	int run_sync();
diff --git a/xs/src/avrdude/main.c b/xs/src/avrdude/main.c
index 91f2fc827..d4c34fe44 100644
--- a/xs/src/avrdude/main.c
+++ b/xs/src/avrdude/main.c
@@ -374,7 +374,7 @@ static void list_parts(FILE * f, const char *prefix, LISTID avrparts)
 
 static int cleanup_main(int status)
 {
-    if (pgm_setup && pgm->teardown) {
+    if (pgm_setup && pgm != NULL && pgm->teardown) {
         pgm->teardown(pgm);
     }
 
diff --git a/xs/src/slic3r/GUI/FirmwareDialog.cpp b/xs/src/slic3r/GUI/FirmwareDialog.cpp
index f9aabacc0..d74743055 100644
--- a/xs/src/slic3r/GUI/FirmwareDialog.cpp
+++ b/xs/src/slic3r/GUI/FirmwareDialog.cpp
@@ -4,6 +4,7 @@
 #include <algorithm>
 #include <boost/format.hpp>
 #include <boost/filesystem/path.hpp>
+#include <boost/filesystem/fstream.hpp>
 #include <boost/log/trivial.hpp>
 
 #include <wx/app.h>
@@ -92,7 +93,9 @@ struct FirmwareDialog::priv
 	{}
 
 	void find_serial_ports();
-	void flashing_status(bool flashing, AvrDudeComplete complete = AC_NONE);
+	void flashing_start(bool flashing_l10n);
+	void flashing_done(AvrDudeComplete complete);
+	size_t hex_lang_offset(const wxString &path);
 	void perform_upload();
 	void cancel();
 	void on_avrdude(const wxCommandEvent &evt);
@@ -119,43 +122,76 @@ void FirmwareDialog::priv::find_serial_ports()
 	}
 }
 
-void FirmwareDialog::priv::flashing_status(bool value, AvrDudeComplete complete)
+void FirmwareDialog::priv::flashing_start(bool flashing_l10n)
 {
-	if (value) {
-		txt_stdout->Clear();
-		txt_status->SetLabel(_(L("Flashing in progress. Please do not disconnect the printer!")));
-		txt_status->SetForegroundColour(GUI::get_label_clr_modified());
-		port_picker->Disable();
-		btn_rescan->Disable();
-		hex_picker->Disable();
-		btn_close->Disable();
-		btn_flash->SetLabel(btn_flash_label_flashing);
-		progressbar->SetRange(200);   // See progress callback below
-		progressbar->SetValue(0);
-		progress_tasks_done = 0;
-		cancelled = false;
-		timer_pulse.Start(50);
-	} else {
-		auto text_color = wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOWTEXT);
-		port_picker->Enable();
-		btn_rescan->Enable();
-		hex_picker->Enable();
-		btn_close->Enable();
-		btn_flash->SetLabel(btn_flash_label_ready);
-		txt_status->SetForegroundColour(text_color);
-		progressbar->SetValue(200);
+	txt_stdout->Clear();
+	txt_status->SetLabel(_(L("Flashing in progress. Please do not disconnect the printer!")));
+	txt_status->SetForegroundColour(GUI::get_label_clr_modified());
+	port_picker->Disable();
+	btn_rescan->Disable();
+	hex_picker->Disable();
+	btn_close->Disable();
+	btn_flash->SetLabel(btn_flash_label_flashing);
+	progressbar->SetRange(flashing_l10n ? 500 : 200);   // See progress callback below
+	progressbar->SetValue(0);
+	progress_tasks_done = 0;
+	cancelled = false;
+	timer_pulse.Start(50);
+}
 
-		switch (complete) {
-		case AC_SUCCESS: txt_status->SetLabel(_(L("Flashing succeeded!"))); break;
-		case AC_FAILURE: txt_status->SetLabel(_(L("Flashing failed. Please see the avrdude log below."))); break;
-		case AC_CANCEL: txt_status->SetLabel(_(L("Flashing cancelled."))); break;
+void FirmwareDialog::priv::flashing_done(AvrDudeComplete complete)
+{
+	auto text_color = wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOWTEXT);
+	port_picker->Enable();
+	btn_rescan->Enable();
+	hex_picker->Enable();
+	btn_close->Enable();
+	btn_flash->SetLabel(btn_flash_label_ready);
+	txt_status->SetForegroundColour(text_color);
+	timer_pulse.Stop();
+	progressbar->SetValue(progressbar->GetRange());
+
+	switch (complete) {
+	case AC_SUCCESS: txt_status->SetLabel(_(L("Flashing succeeded!"))); break;
+	case AC_FAILURE: txt_status->SetLabel(_(L("Flashing failed. Please see the avrdude log below."))); break;
+	case AC_CANCEL: txt_status->SetLabel(_(L("Flashing cancelled."))); break;
+	}
+}
+
+size_t FirmwareDialog::priv::hex_lang_offset(const wxString &path)
+{
+	fs::ifstream file(fs::path(path.wx_str()));
+	if (! file.good()) {
+		return 0;
+	}
+
+	static const char *hex_terminator = ":00000001FF\r";
+	size_t res = 0;
+	std::string line;
+	while (getline(file, line, '\n').good()) {
+		// Account for LF vs CRLF
+		if (!line.empty() && line.back() != '\r') {
+			line.push_back('\r');
+		}
+
+		if (line == hex_terminator) {
+			if (res == 0) {
+				// This is the first terminator seen, save the position
+				res = file.tellg();
+			} else {
+				// We've found another terminator, return the offset just after the first one
+				// which is the start of the second 'section'.
+				return res;
+			}
 		}
 	}
+
+	return 0;
 }
 
 void FirmwareDialog::priv::perform_upload()
 {
-	auto filename  = hex_picker->GetPath();
+	auto filename = hex_picker->GetPath();
 	std::string port = port_picker->GetValue().ToStdString();
 	int  selection = port_picker->GetSelection();
 	if (selection != -1) {
@@ -165,25 +201,32 @@ void FirmwareDialog::priv::perform_upload()
 	}
 	if (filename.IsEmpty() || port.empty()) { return; }
 
-	flashing_status(true);
-
+	const bool extra_verbose = false;   // For debugging
+	const auto lang_offset = hex_lang_offset(filename);
 	const auto filename_utf8 = filename.utf8_str();
+
+	flashing_start(lang_offset > 0);
+
+	// It is ok here to use the q-pointer to the FirmwareDialog
+	// because the dialog ensures it doesn't exit before the background thread is done.
+	auto q = this->q;
+
+	// Init the avrdude object
+	AvrDude avrdude(avrdude_config);
+
+	// Build argument list(s)
 	std::vector<std::string> args {{
-		"-v",
+		extra_verbose ? "-vvvvv" : "-v",
 		"-p", "atmega2560",
 		// Using the "Wiring" mode to program Rambo or Einsy, using the STK500v2 protocol (not the STK500).
 		// The Prusa's avrdude is patched to never send semicolons inside the data packets, as the USB to serial chip
 		// is flashed with a buggy firmware.
-//		"-c", "wiring",
-		// Using the "Arduino" mode to program Einsy's external flash with languages, using the STK500 protocol (not the STK500v2).
-		// The Prusa's avrdude is patched again to never send semicolons inside the data packets.
-		"-c", "arduino",
+		"-c", "wiring",
 		"-P", port,
-		"-b", "115200",   // XXX: is this ok to hardcode?
+		"-b", "115200",   // TODO: Allow other rates? Ditto below.
 		"-D",
-		"-u", // disable safe mode
-		"-U", (boost::format("flash:w:0:%1%:i") % filename_utf8.data()).str(),    // FIXME
-		// "-vvvvv", //"-v", "-v", "-v", "-v", // enable super verbose mode, logging each serial line exchange
+		// XXX: Safe mode?
+		"-U", (boost::format("flash:w:0:%1%:i") % filename_utf8.data()).str(),
 	}};
 
 	BOOST_LOG_TRIVIAL(info) << "Invoking avrdude, arguments: "
@@ -191,17 +234,38 @@ void FirmwareDialog::priv::perform_upload()
 			return a + ' ' + b;
 		});
 
-	// It is ok here to use the q-pointer to the FirmwareDialog
-	// because the dialog ensures it doesn't exit before the background thread is done.
-	auto q = this->q;
+	avrdude.push_args(std::move(args));
+	
+	if (lang_offset > 0) {
+		// The hex file also contains another section with l10n data to be flashed into the external flash on MK3 (Einsy)
+		// This is done via another avrdude invocation, here we build arg list for that:
+		std::vector<std::string> args_l10n {{
+			extra_verbose ? "-vvvvv" : "-v",
+			"-p", "atmega2560",
+			// Using the "Arduino" mode to program Einsy's external flash with languages, using the STK500 protocol (not the STK500v2).
+			// The Prusa's avrdude is patched again to never send semicolons inside the data packets.
+			"-c", "arduino",
+			"-P", port,
+			"-b", "115200",
+			"-D",
+			"-u", // disable safe mode
+			"-U", (boost::format("flash:w:%1%:%2%:i") % lang_offset % filename_utf8.data()).str(),
+		}};
+
+		BOOST_LOG_TRIVIAL(info) << "Invoking avrdude for external flash flashing, arguments: "
+			<< std::accumulate(std::next(args_l10n.begin()), args_l10n.end(), args_l10n[0], [](std::string a, const std::string &b) {
+				return a + ' ' + b;
+			});
+
+		avrdude.push_args(std::move(args_l10n));
+	}
+	
+	this->avrdude = avrdude
+		.on_message(std::move([q, extra_verbose](const char *msg, unsigned /* size */) {
+			if (extra_verbose) {
+				BOOST_LOG_TRIVIAL(debug) << "avrdude: " << msg;
+			}
 
-	avrdude = AvrDude()
-		.sys_config(avrdude_config)
-		.args(args)
-		.on_run([]() { /* TODO: needed? */ })
-		.on_message(std::move([q](const char *msg, unsigned /* size */) {
-			// Debugging output to console, useful when avrdude is executed in a super verbose mode (with -v -v -v).
-			// printf("%s", msg);
 			auto evt = new wxCommandEvent(EVT_AVRDUDE, q->GetId());
 			auto wxmsg = wxString::FromUTF8(msg);
 			evt->SetExtraLong(AE_MESSAGE);
@@ -214,7 +278,7 @@ void FirmwareDialog::priv::perform_upload()
 			evt->SetInt(progress);
 			wxQueueEvent(q, evt);
 		}))
-		.on_complete(std::move([q](int status) {
+		.on_complete(std::move([q](int status, size_t /* args_id */) {
 			auto evt = new wxCommandEvent(EVT_AVRDUDE, q->GetId());
 			evt->SetExtraLong(AE_EXIT);
 			evt->SetInt(status);
@@ -243,10 +307,10 @@ void FirmwareDialog::priv::on_avrdude(const wxCommandEvent &evt)
 
 	case AE_PROGRESS:
 		// We try to track overall progress here.
-		// When uploading the firmware, avrdude first reads a littlebit of status data,
-		// then performs write, then reading (verification).
-		// We ignore the first task (which just let's the timer_pulse work)
-		// and then display overall progress during the latter two tasks.
+		// Avrdude performs 3 tasks per one memory operation ("-U" arg),
+		// first of which is reading of status data (very short).
+		// We use the timer_pulse during the very first task to indicate intialization
+		// and then display overall progress during the latter tasks.
 
 		if (progress_tasks_done > 0) {
 			progressbar->SetValue(progress_tasks_done - 100 + evt.GetInt());
@@ -263,7 +327,7 @@ void FirmwareDialog::priv::on_avrdude(const wxCommandEvent &evt)
 		BOOST_LOG_TRIVIAL(info) << "avrdude exit code: " << evt.GetInt();
 
 		complete_kind = cancelled ? AC_CANCEL : (evt.GetInt() == 0 ? AC_SUCCESS : AC_FAILURE);
-		flashing_status(false, complete_kind);
+		flashing_done(complete_kind);
 
 		// Make sure the background thread is collected and the AvrDude object reset
 		if (avrdude) { avrdude->join(); }

From 725b8524f2c922dcaa32b280d6553eaba81ae30f Mon Sep 17 00:00:00 2001
From: Vojtech Kral <vojtech@kral.hk>
Date: Tue, 19 Jun 2018 15:45:30 +0200
Subject: [PATCH 17/17] avrdude: Fix error handling in arduino, fix various
 outputs

---
 xs/src/avrdude/arduino.c |  1 +
 xs/src/avrdude/avrpart.c | 10 +++++-----
 xs/src/avrdude/update.c  |  1 -
 3 files changed, 6 insertions(+), 6 deletions(-)

diff --git a/xs/src/avrdude/arduino.c b/xs/src/avrdude/arduino.c
index 5e0693e94..fc9f4571f 100644
--- a/xs/src/avrdude/arduino.c
+++ b/xs/src/avrdude/arduino.c
@@ -148,6 +148,7 @@ static int arduino_open(PROGRAMMER * pgm, char * port)
   // Initialization sequence for programming the external FLASH on the Prusa MK3
   if (prusa_init_external_flash(pgm) < 0) {
     avrdude_message(MSG_INFO, "%s: arduino_open(): Failed to initialize MK3 external flash programming mode\n", progname);
+    return -1;
   }
 
   if (stk500_getsync(pgm) < 0)
diff --git a/xs/src/avrdude/avrpart.c b/xs/src/avrdude/avrpart.c
index 621a85b98..b04851ac1 100644
--- a/xs/src/avrdude/avrpart.c
+++ b/xs/src/avrdude/avrpart.c
@@ -378,7 +378,7 @@ void avr_mem_display(const char * prefix, FILE * f, AVRMEM * m, int type,
   char * optr;
 
   if (m == NULL) {
-      fprintf(f,
+      avrdude_message(MSG_INFO,
               "%s                       Block Poll               Page                       Polled\n"
               "%sMemory Type Mode Delay Size  Indx Paged  Size   Size #Pages MinW  MaxW   ReadBack\n"
               "%s----------- ---- ----- ----- ---- ------ ------ ---- ------ ----- ----- ---------\n",
@@ -386,13 +386,13 @@ void avr_mem_display(const char * prefix, FILE * f, AVRMEM * m, int type,
   }
   else {
     if (verbose > 2) {
-      fprintf(f,
+      avrdude_message(MSG_INFO,
               "%s                       Block Poll               Page                       Polled\n"
               "%sMemory Type Mode Delay Size  Indx Paged  Size   Size #Pages MinW  MaxW   ReadBack\n"
               "%s----------- ---- ----- ----- ---- ------ ------ ---- ------ ----- ----- ---------\n",
               prefix, prefix, prefix);
     }
-    fprintf(f,
+    avrdude_message(MSG_INFO,
             "%s%-11s %4d %5d %5d %4d %-6s %6d %4d %6d %5d %5d 0x%02x 0x%02x\n",
             prefix, m->desc, m->mode, m->delay, m->blocksize, m->pollindex,
             m->paged ? "yes" : "no",
@@ -415,7 +415,7 @@ void avr_mem_display(const char * prefix, FILE * f, AVRMEM * m, int type,
               optr = avr_op_str(i);
             else
               optr = " ";
-          fprintf(f,
+          avrdude_message(MSG_INFO,
                   "%s    %-11s  %8d  %8s  %5d  %5d\n",
                   prefix, optr, j,
                   bittype(m->op[i]->bit[j].type),
@@ -620,7 +620,7 @@ void avr_display(FILE * f, AVRPART * p, const char * prefix, int verbose)
   LNODEID ln;
   AVRMEM * m;
 
-  fprintf(f,
+  avrdude_message(MSG_INFO,
           "%sAVR Part                      : %s\n"
           "%sChip Erase delay              : %d us\n"
           "%sPAGEL                         : P%02X\n"
diff --git a/xs/src/avrdude/update.c b/xs/src/avrdude/update.c
index fa3372476..e9dd6e325 100644
--- a/xs/src/avrdude/update.c
+++ b/xs/src/avrdude/update.c
@@ -117,7 +117,6 @@ UPDATE * parse_op(char * s)
   }
 
   upd->offset = offset;
-  printf("parse_op: offset: %lu\n", offset);
   p++;
 
   /*