diff --git a/FCHost/CMakeLists.txt b/FCHost/CMakeLists.txt
index d54e523a94771a1177590ed273364744ccb63889..ab5d14bc26601b4d5dbd83876787536de3686895 100644
--- a/FCHost/CMakeLists.txt
+++ b/FCHost/CMakeLists.txt
@@ -97,6 +97,9 @@ set(CMAKE_CONFIGURATION_TYPES Debug Release)
 # Project name.
 project(fchost)
 
+set(CMAKE_CXX_STANDARD 17)
+set(CMAKE_CXX_STANDARD_REQUIRED ON)
+
 # Use folders in the resulting project files.
 set_property(GLOBAL PROPERTY OS_FOLDERS ON)
 
@@ -113,7 +116,7 @@ set_property(GLOBAL PROPERTY OS_FOLDERS ON)
 # Set the CEF_ROOT environment variable before executing CMake. For example:
 #    > set CEF_ROOT=c:\path\to\cef_binary_3.2704.xxxx.gyyyyyyy_windows32
 #
-set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_CURRENT_SOURCE_DIR}/cmake")
+# set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_CURRENT_SOURCE_DIR}/cmake")
 
 
 #
diff --git a/FCHost/fchost/fchost_app.cc b/FCHost/fchost/fchost_app.cc
index 13807a9207bf32495101cae16c4d39003b9a08fa..b15304c1594101e8fb2b807e417e1496d6b8ab90 100644
--- a/FCHost/fchost/fchost_app.cc
+++ b/FCHost/fchost/fchost_app.cc
@@ -4,8 +4,6 @@
 
 #include "fchost_app.h"
 
-#include <string>
-
 #include "include/cef_browser.h"
 #include "include/cef_command_line.h"
 #include "include/views/cef_browser_view.h"
@@ -14,6 +12,13 @@
 #include "fchost_handler.h"
 #include "fchost_storage_js.h"
 
+#include <filesystem>
+#include <string>
+
+#if defined(OS_LINUX)
+  #include <unistd.h>
+#endif
+
 namespace {
 
 // When using the Views framework this object provides the delegate
@@ -55,6 +60,36 @@ class SimpleWindowDelegate : public CefWindowDelegate {
   DISALLOW_COPY_AND_ASSIGN(SimpleWindowDelegate);
 };
 
+std::filesystem::path executablePath() {
+#if defined(OS_WIN)
+  wchar_t target_path[_MAX_PATH];
+  GetModuleFileNameW(NULL, target_path, _MAX_PATH);
+  return std::filesystem::path(target_path);
+#elif defined(OS_LINUX)
+  std::string buf;
+	buf.resize(32); // initial size estimate
+
+	for (;; ) {
+		ssize_t ret = readlink("/proc/self/exe", buf.data(), buf.size());
+
+		if (ret == -1) {
+			perror("getexename() failed");
+			break;
+		}
+
+		if (static_cast<std::size_t>(ret) >= buf.size()) { // >= because we need the terminating NUL too
+			buf.resize(buf.size() * 2);
+			continue;
+		}
+
+		return buf;
+	}
+
+	return {};
+#else
+  #error "Platform-specific code required"
+#endif
+}
 }  // namespace
 
 FCHostApp::FCHostApp() {}
@@ -86,15 +121,11 @@ void FCHostApp::OnContextInitialized() {
 
   // For now, read from external file.  Probably want to make this a resource
   // at least on Windows.
+  std::filesystem::path gameHTML = executablePath().parent_path() / "FC_pregmod.html";
 #if defined(OS_WIN)
-  wchar_t target_path[_MAX_PATH];
-  GetModuleFileNameW(NULL, target_path, _MAX_PATH);
-  std::wstring url = target_path;
-  size_t pos = url.find_last_of(L"\\/");
-  url = url.substr(0, pos);
-  url += L"/FC_pregmod.html";
+  std::wstring url = gameHTML.native();
 #else
-  #error "Platform-specific code needed"
+  std::string url = "file://" + gameHTML.string();
 #endif
 
   // Allow some access flexibility for our local file
@@ -127,4 +158,3 @@ void FCHostApp::OnContextInitialized() {
 void FCHostApp::OnContextCreated(CefRefPtr<CefBrowser> browser, CefRefPtr<CefFrame> frame, CefRefPtr<CefV8Context> context) {
 	FCHostStorageRegister(GetLocalStorePath().ToWString() + L"/FCHostPersistentStorage", context->GetGlobal());
 }
-
diff --git a/FCHost/fchost/fchost_handler.cc b/FCHost/fchost/fchost_handler.cc
index b3c8cd2808282618eb3a8a700324148139fb94c8..95eec1b6e56592ccd2c081c94b970ed189bfbef1 100644
--- a/FCHost/fchost/fchost_handler.cc
+++ b/FCHost/fchost/fchost_handler.cc
@@ -206,7 +206,13 @@ bool FCHostHandler::OnPreKeyEvent(CefRefPtr<CefBrowser> browser,
 			CefWindowInfo windowInfo;
 			CefBrowserSettings settings;
 			CefPoint point;
+  #if defined (OS_WIN)
 			windowInfo.SetAsPopup(browser->GetHost()->GetWindowHandle(), "DevTools");
+  #elif defined(OS_LINUX)
+      windowInfo.SetAsChild(browser->GetHost()->GetWindowHandle(), CefRect(100, 100, 800, 600));
+  #else
+    #error "Platform-specific code required"
+  #endif
 			browser->GetHost()->ShowDevTools(windowInfo, browser->GetHost()->GetClient(), settings, point);
 
 			return true;
diff --git a/FCHost/fchost/fchost_handler.h b/FCHost/fchost/fchost_handler.h
index 1756d701c91d7aab0b1ab092bb4713ff7234d785..19aeb67c102126d9160f6d7f2b896da85503bec9 100644
--- a/FCHost/fchost/fchost_handler.h
+++ b/FCHost/fchost/fchost_handler.h
@@ -13,7 +13,8 @@ class FCHostHandler : public CefClient,
                       public CefLifeSpanHandler,
                       public CefLoadHandler,
 					  public CefDownloadHandler,
-					  public CefKeyboardHandler {
+					  public CefKeyboardHandler,
+            private CefDialogHandler {
  public:
   explicit FCHostHandler(bool use_views);
   ~FCHostHandler();
@@ -32,6 +33,10 @@ class FCHostHandler : public CefClient,
   virtual CefRefPtr<CefDownloadHandler> GetDownloadHandler() OVERRIDE { return this; }
   virtual CefRefPtr<CefKeyboardHandler> GetKeyboardHandler() OVERRIDE { return this; }
 
+  CefRefPtr< CefDialogHandler > GetDialogHandler() override { return this; }
+  bool OnFileDialog(CefRefPtr<CefBrowser> browser, CefDialogHandler::FileDialogMode mode,
+    const CefString& title, const CefString& default_file_path, const std::vector<CefString>& accept_filters,
+    int selected_accept_filter, CefRefPtr<CefFileDialogCallback> callback) override;
 
   // CefDisplayHandler methods:
   virtual void OnTitleChange(CefRefPtr<CefBrowser> browser,
diff --git a/FCHost/fchost/fchost_handler_linux.cc b/FCHost/fchost/fchost_handler_linux.cc
new file mode 100644
index 0000000000000000000000000000000000000000..f960f287892e3759892627a28edfd9076d77931f
--- /dev/null
+++ b/FCHost/fchost/fchost_handler_linux.cc
@@ -0,0 +1,266 @@
+// Copyright (c) 2014 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+
+#include "fchost_handler.h"
+
+#if defined(CEF_X11)
+#include <X11/Xatom.h>
+#include <X11/Xlib.h>
+#endif
+
+#include "include/base/cef_logging.h"
+#include "include/cef_browser.h"
+
+#include <cstdlib>
+#include <cstdio>
+#include <filesystem>
+#include <iomanip>
+#include <memory>
+#include <sstream>
+#include <string>
+
+// #define TRACE
+
+#ifdef TRACE
+	#include <iostream>
+#endif
+
+namespace {
+
+	std::string executeProcessAndReadStdOut(const std::string& command)
+	{
+#ifdef TRACE
+		std::cerr << "Command: " << command << std::endl;
+#endif
+		std::unique_ptr<FILE, decltype(&::pclose)> f(::popen(command.c_str(), "r"), &::pclose);
+		std::ostringstream output;
+		const int BUF_SIZE = 1024;
+		char buf[BUF_SIZE];
+		while(!std::feof(f.get())){
+			std::size_t read = std::fread(buf, 1, BUF_SIZE, f.get());
+			output.write(buf, static_cast<std::streamsize>(read));
+		}
+		std::string res = output.str();
+		return res.substr(0, res.size() - 1); // to clear out CR
+	}
+
+	std::string runKDialog(CefDialogHandler::FileDialogMode mode, const CefString& title, const CefString& default_file_path,
+		const std::vector<CefString>& accept_filters)
+	{
+		std::ostringstream cmdLine;
+		cmdLine << "kdialog";
+		if (!title.empty()) {
+			cmdLine << " --title " <<  std::quoted(title.ToString());
+		}
+
+#ifdef TRACE
+		std::cerr << "mode: " << std::hex << mode << std::dec << std::endl
+			<< "default_file_path: " << default_file_path << std::endl
+			<< "accept_filters: " << std::endl;
+
+		for (const auto& f: accept_filters) {
+			std::cerr << f.ToString() << std::endl;
+		}
+#endif
+
+		// kdialog does not support these
+		mode = static_cast<CefDialogHandler::FileDialogMode>(mode & ~(CefDialogHandler::FileDialogMode::FILE_DIALOG_OVERWRITEPROMPT_FLAG));
+		mode = static_cast<CefDialogHandler::FileDialogMode>(mode & ~(CefDialogHandler::FileDialogMode::FILE_DIALOG_HIDEREADONLY_FLAG));
+
+		switch (mode) {
+			case CefDialogHandler::FileDialogMode::FILE_DIALOG_OPEN_MULTIPLE:
+				cmdLine << " --multiple"; [[fallthrough]]
+			case CefDialogHandler::FileDialogMode::FILE_DIALOG_OPEN:
+				cmdLine << " --getopenfilename";
+				break;
+			case CefDialogHandler::FileDialogMode::FILE_DIALOG_SAVE:
+				cmdLine << " --getsavefilename";
+				break;
+			case CefDialogHandler::FileDialogMode::FILE_DIALOG_OPEN_FOLDER:
+				cmdLine << " --getexistingdirectory";
+				break;
+			default:
+				break; // TODO
+		}
+
+		const std::string dsp = default_file_path.ToString();
+		if (dsp.find('/') != std::string::npos) {
+			cmdLine << ' ' << std::quoted(dsp);
+		} else {
+			// TODO save last used directory and put it here instead of $HOME
+			cmdLine << ' ' << std::quoted(std::string(getenv("HOME")) + '/' + dsp);
+		}
+
+		if (accept_filters.size()) {
+			cmdLine << " \'";
+			for (const auto& f: accept_filters) {
+				const auto s = f.ToString();
+				cmdLine << " |" << s << " file(*" << s << ')';
+			}
+			cmdLine << '\'';
+		}
+
+		return executeProcessAndReadStdOut(cmdLine.str());
+	}
+
+	std::string runZenity(CefDialogHandler::FileDialogMode mode, const CefString& title, const CefString& default_file_path,
+		const std::vector<CefString>& accept_filters)
+	{
+		std::ostringstream cmdLine;
+		cmdLine << "zenity --file-selection";
+
+		// zenity does not support these
+		mode = static_cast<CefDialogHandler::FileDialogMode>(mode & ~(CefDialogHandler::FileDialogMode::FILE_DIALOG_HIDEREADONLY_FLAG));
+
+		if (mode & CefDialogHandler::FileDialogMode::FILE_DIALOG_OVERWRITEPROMPT_FLAG) {
+			cmdLine << " --confirm-overwrite";
+		}
+		mode = static_cast<CefDialogHandler::FileDialogMode>(mode & ~(CefDialogHandler::FileDialogMode::FILE_DIALOG_OVERWRITEPROMPT_FLAG));
+
+		switch (mode) {
+			case CefDialogHandler::FileDialogMode::FILE_DIALOG_OPEN_MULTIPLE:
+				cmdLine << " --multiple";
+				break;
+			case CefDialogHandler::FileDialogMode::FILE_DIALOG_OPEN:
+				// this is the implicit default mode for zenity
+				break;
+			case CefDialogHandler::FileDialogMode::FILE_DIALOG_SAVE:
+				cmdLine << " --save";
+				break;
+			case CefDialogHandler::FileDialogMode::FILE_DIALOG_OPEN_FOLDER:
+				cmdLine << " --directory";
+				break;
+			default:
+				break; // TODO ?
+		}
+
+		const std::string dsp = default_file_path.ToString();
+		if (dsp.find('/') != std::string::npos) {
+			cmdLine << " --filename=" << std::quoted(dsp);
+		} else {
+			// TODO save last used directory and put it here instead of $HOME
+			cmdLine << " --filename=" << std::quoted(std::string(getenv("HOME")) + '/' + dsp);
+		}
+
+		for (const auto& f: accept_filters) {
+			const auto s = f.ToString();
+			cmdLine << " --file-filter=\"" << s << " file | *" << s << '"';
+		}
+
+		return executeProcessAndReadStdOut(cmdLine.str());
+	}
+
+	using DialogRunFunc = std::string (*)(CefDialogHandler::FileDialogMode mode, const CefString& title, const CefString& default_file_path,
+		const std::vector<CefString>& accept_filters);
+
+	class DialogHelper {
+		public:
+			DialogHelper();
+			std::string operator()(CefDialogHandler::FileDialogMode mode, const CefString& title, const CefString& default_file_path,
+		const std::vector<CefString>& accept_filters) const
+		{
+			return func_ ? func_(mode, title, default_file_path, accept_filters) : "";
+		}
+		private:
+			DialogRunFunc func_;
+	};
+
+	DialogHelper::DialogHelper()
+	{
+		// we will try to launch kdialog or zenity
+		std::string dialogExecutable;
+		// try to determine which environment we run inside
+		std::string desktop = getenv("XDG_CURRENT_DESKTOP");
+
+		const auto checkExeExists = [](const char* name) {
+			int ec = ::system((std::string(name) + " --help > /dev/null").c_str());
+			return ec >= 0 && ec < 127;
+		};
+
+		if (desktop == "KDE") {
+			dialogExecutable = "kdialog";
+		} else if (desktop == "GNOME") {
+			dialogExecutable = "zenity";
+		} else {
+			// well, let's check executables
+			if (checkExeExists("zenity")) {
+				dialogExecutable = "zenity";
+			} else if (checkExeExists("kdialog")){
+				dialogExecutable = "kdialog";
+			}
+		}
+
+#ifdef TRACE
+		std::cerr << "dialogExecutable: " << dialogExecutable << std::endl
+			<< "checkExeExists: " <<  checkExeExists(dialogExecutable.c_str()) << std::endl;
+#endif
+
+		if (!dialogExecutable.empty() && checkExeExists(dialogExecutable.c_str())) {
+			if (dialogExecutable == "kdialog") {
+				func_ = &runKDialog;
+			} else if (dialogExecutable == "zenity") {
+				func_ = &runZenity;
+			} else {
+				func_ = nullptr;
+			}
+		}
+	}
+
+
+}
+
+void FCHostHandler::PlatformTitleChange(CefRefPtr<CefBrowser> browser, const CefString& title) {
+	std::string titleStr(title);
+
+#if defined(CEF_X11)
+	// Retrieve the X11 display shared with Chromium.
+	::Display* display = cef_get_xdisplay();
+	DCHECK(display);
+
+	// Retrieve the X11 window handle for the browser.
+	::Window window = browser->GetHost()->GetWindowHandle();
+	if (window == kNullWindowHandle)
+		return;
+
+	// Retrieve the atoms required by the below XChangeProperty call.
+	const char* kAtoms[] = {"_NET_WM_NAME", "UTF8_STRING"};
+	Atom atoms[2];
+	int result =
+			XInternAtoms(display, const_cast<char**>(kAtoms), 2, false, atoms);
+	if (!result)
+		NOTREACHED();
+
+	// Set the window title.
+	XChangeProperty(display, window, atoms[0], atoms[1], 8, PropModeReplace,
+									reinterpret_cast<const unsigned char*>(titleStr.c_str()),
+									titleStr.size());
+
+	// TODO(erg): This is technically wrong. So XStoreName and friends expect
+	// this in Host Portable Character Encoding instead of UTF-8, which I believe
+	// is Compound Text. This shouldn't matter 90% of the time since this is the
+	// fallback to the UTF8 property above.
+	XStoreName(display, browser->GetHost()->GetWindowHandle(), titleStr.c_str());
+#endif	// defined(CEF_X11)
+}
+
+bool FCHostHandler::OnFileDialog(CefRefPtr<CefBrowser> /* browser */, CefDialogHandler::FileDialogMode mode,
+		const CefString& title, const CefString& default_file_path, const std::vector<CefString>& accept_filters,
+		int /* selected_accept_filter */, CefRefPtr<CefFileDialogCallback> callback)
+{
+	static DialogHelper helper;
+
+	std::string fn = helper(mode, title, default_file_path, accept_filters);
+#ifdef TRACE
+	std::cerr << "fn: " << fn << std::endl;
+#endif
+	if (fn.empty()) {
+		callback->Cancel();
+		return false;
+	}
+
+	std::vector<CefString> selected;
+	selected.push_back(fn);
+	callback->Continue(0, selected);
+	return  true;
+}
diff --git a/FCHost/fchost/fchost_handler_win.cc b/FCHost/fchost/fchost_handler_win.cc
index 106787eb5f89706a422d859b8024aeb3371e5374..483f69258c702e8f01a486ff6601767b3a631c67 100644
--- a/FCHost/fchost/fchost_handler_win.cc
+++ b/FCHost/fchost/fchost_handler_win.cc
@@ -26,3 +26,10 @@ void FCHostHandler::PlatformTitleChange(CefRefPtr<CefBrowser> browser,
 
   SetWindowText(hwnd, std::wstring(title).c_str());
 }
+
+bool FCHostHandler::OnFileDialog(CefRefPtr<CefBrowser> /* browser */, CefDialogHandler::FileDialogMode /* mode */,
+    const CefString& /* title */, const CefString& /* default_file_path */, const std::vector<CefString>& /* accept_filters */,
+    int /* selected_accept_filter */, CefRefPtr<CefFileDialogCallback> /* callback */)
+{
+  return false; // to display the default dialog
+}
diff --git a/FCHost/fchost/fchost_linux.cc b/FCHost/fchost/fchost_linux.cc
new file mode 100644
index 0000000000000000000000000000000000000000..837df28715be7c8b36e0aad937a9993c716c9b5a
--- /dev/null
+++ b/FCHost/fchost/fchost_linux.cc
@@ -0,0 +1,103 @@
+// Copyright (c) 2013 The Chromium Embedded Framework Authors. All rights
+// reserved. Use of this source code is governed by a BSD-style license that
+// can be found in the LICENSE file.
+
+#include "fchost_app.h"
+
+#if defined(CEF_X11)
+#include <X11/Xlib.h>
+#endif
+
+#include "include/base/cef_logging.h"
+#include "include/cef_command_line.h"
+
+#include <filesystem>
+
+CefString FCHostApp::GetLocalStorePath()
+{
+  std::filesystem::path ret = std::filesystem::path{getenv("HOME")} / ".local" / "share" / "FreeCities_Pregmod";
+
+	return CefString(ret.string());
+}
+
+#if defined(CEF_X11)
+namespace {
+
+int XErrorHandlerImpl(Display* display, XErrorEvent* event) {
+  LOG(WARNING) << "X error received: "
+               << "type " << event->type << ", "
+               << "serial " << event->serial << ", "
+               << "error_code " << static_cast<int>(event->error_code) << ", "
+               << "request_code " << static_cast<int>(event->request_code)
+               << ", "
+               << "minor_code " << static_cast<int>(event->minor_code);
+  return 0;
+}
+
+int XIOErrorHandlerImpl(Display* display) {
+  return 0;
+}
+
+}  // namespace
+#endif  // defined(CEF_X11)
+
+// Entry point function for all processes.
+int main(int argc, char* argv[]) {
+  // Provide CEF with command-line arguments.
+  CefMainArgs main_args(argc, argv);
+
+  // CEF applications have multiple sub-processes (render, plugin, GPU, etc)
+  // that share the same executable. This function checks the command-line and,
+  // if this is a sub-process, executes the appropriate logic.
+  int exit_code = CefExecuteProcess(main_args, nullptr, nullptr);
+  if (exit_code >= 0) {
+    // The sub-process has completed so return here.
+    return exit_code;
+  }
+
+#if defined(CEF_X11)
+  // Install xlib error handlers so that the application won't be terminated
+  // on non-fatal errors.
+  XSetErrorHandler(XErrorHandlerImpl);
+  XSetIOErrorHandler(XIOErrorHandlerImpl);
+#endif
+
+  // Parse command-line arguments for use in this method.
+  CefRefPtr<CefCommandLine> command_line = CefCommandLine::CreateCommandLine();
+  command_line->InitFromArgv(argc, argv);
+
+  // It will create the first browser instance in OnContextInitialized() after
+  // CEF has initialized.
+  CefRefPtr<FCHostApp> app(new FCHostApp);
+
+  // Specify CEF global settings here.
+  CefSettings settings;
+
+  if (command_line->HasSwitch("enable-chrome-runtime")) {
+    // Enable experimental Chrome runtime. See issue #2969 for details.
+    settings.chrome_runtime = true;
+  }
+
+  // Cache location is required for local storage
+  CefString local_storage = app->GetLocalStorePath();
+  cef_string_from_utf16(local_storage.c_str(), local_storage.length(), &settings.cache_path);
+
+// When generating projects with CMake the CEF_USE_SANDBOX value will be defined
+// automatically. Pass -DUSE_SANDBOX=OFF to the CMake command-line to disable
+// use of the sandbox.
+#if !defined(CEF_USE_SANDBOX)
+  settings.no_sandbox = true;
+#endif
+
+  // Initialize CEF for the browser process.
+  CefInitialize(main_args, settings, app.get(), nullptr);
+
+  // Run the CEF message loop. This will block until CefQuitMessageLoop() is
+  // called.
+  CefRunMessageLoop();
+
+  // Shut down CEF.
+  CefShutdown();
+
+  return 0;
+}
diff --git a/FCHost/fchost/fchost_storage.cc b/FCHost/fchost/fchost_storage.cc
index af13e4c8817c8d8490afceac1ec65083be6ed143..7326e838cfa1d654c867f9610da783ef4356ed53 100644
--- a/FCHost/fchost/fchost_storage.cc
+++ b/FCHost/fchost/fchost_storage.cc
@@ -1,9 +1,20 @@
 #include "fchost_storage.h"
 
 #include <algorithm>
-#if defined(OS_WIN)
-#include "shlwapi.h"
-#endif
+#include <cstdio>
+#include <memory>
+#include <vector>
+
+namespace fs = std::filesystem;
+
+namespace {
+	struct closeFile {
+		void operator()(std::FILE* f) const noexcept
+		{
+			std::fclose(f);
+		}
+	};
+}
 
 CefRefPtr<CefV8Value> FCHostSessionStorage::keys() const
 {
@@ -17,7 +28,6 @@ CefRefPtr<CefV8Value> FCHostSessionStorage::keys() const
 	return ret;
 }
 
-#if defined(OS_WIN)
 /* This shouldn't happen, so don't waste time on it. Sugarcube really only writes simple alphanumeric keys.
 static bool SanitizePath(std::wstring& inpath)
 {
@@ -31,96 +41,89 @@ static bool SanitizePath(std::wstring& inpath)
 }
 */
 
+FCHostPersistentStorage::FCHostPersistentStorage(const CefString& _path)
+#if defined(OS_WIN)
+	: path(_path.ToWString())
+#else
+	: path(_path.ToString())
+#endif
+{
+	ensure_folder_exists();
+	load();
+}
+
 void FCHostPersistentStorage::set(const CefString& key, CefRefPtr<CefV8Value> val)
 {
-	__super::set(key, val);
+	base::set(key, val);
 
 	// only strings get persisted (should be OK, Sugarcube will serialize first)
 	if (val->IsString())
 	{
 		// we should probably be doing this async but TBT Sugarcube is the slow part, not the file IO
-		DWORD written;
-		HANDLE fh = CreateFile(get_filename(key).c_str(), GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
+		std::unique_ptr<std::FILE, closeFile> fh;
+#if defined(OS_WIN)
+		fh.reset(_wfopen(get_filename(key).c_str(), L"w"));
+#else
+		fh.reset(std::fopen(get_filename(key).c_str(), "w"));
+#endif
 		CefString valStr = val->GetStringValue();
-		if (valStr.size() > 0)
-		{
-			WriteFile(fh, valStr.c_str(), static_cast<DWORD>(valStr.size() * sizeof(valStr.c_str()[0])), &written, NULL);
+		if (valStr.size() > 0) {
+			std::fwrite(valStr.c_str(), static_cast<size_t>(valStr.size() * sizeof(valStr.c_str()[0])), 1, fh.get());
 		}
-		CloseHandle(fh);
 	}
 }
 
 bool FCHostPersistentStorage::remove(const CefString& key)
 {
-	bool retval = __super::remove(key);
-
-	DeleteFile(get_filename(key).c_str());
-
-	return retval;
+	fs::remove(get_filename(key));
+	return base::remove(key);
 }
 
 void FCHostPersistentStorage::clear()
 {
-	__super::clear();
-
-	WIN32_FIND_DATAW w32fd;
-	HANDLE hFind = FindFirstFile((path + L"\\*").c_str(), &w32fd);
-	if (hFind != INVALID_HANDLE_VALUE)
-	{
-		do
-		{
-			if (w32fd.dwFileAttributes & ~(FILE_ATTRIBUTE_DIRECTORY | FILE_ATTRIBUTE_DEVICE))
-			{
-				DeleteFile(get_filename(w32fd.cFileName).c_str());
-			}
-		} while (FindNextFile(hFind, &w32fd));
-		FindClose(hFind);
+	for (const auto& entry: fs::directory_iterator(path)) {
+		if (fs::is_regular_file(entry.path())) {
+			fs::remove_all(entry.path());
+		}
 	}
+	base::clear();
 }
 
 void FCHostPersistentStorage::load()
 {
-	constexpr size_t bufsize = 1024 * 1024 * 1024; // 1gb should be enough
-	char* readbuf = new char[bufsize];
-	WIN32_FIND_DATAW w32fd;
-	HANDLE hFind = FindFirstFile((path + L"\\*").c_str(), &w32fd);
-	if (hFind != INVALID_HANDLE_VALUE)
-	{
-		do
-		{
-			if (w32fd.dwFileAttributes & ~(FILE_ATTRIBUTE_DIRECTORY | FILE_ATTRIBUTE_DEVICE))
-			{
-				DWORD bytesread = 0;
-				HANDLE fh = CreateFile(get_filename(w32fd.cFileName).c_str(), GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
-				if (fh)
-				{
-					BOOL success = ReadFile(fh, readbuf, static_cast<DWORD>(bufsize - 1), &bytesread, NULL);
-					if (success)
-					{
-						readbuf[bytesread] = L'\0';
-						readbuf[bytesread+1] = L'\0'; // null terminate
-						CefString val = reinterpret_cast<wchar_t*>(readbuf);
-						storage.emplace(w32fd.cFileName, CefV8Value::CreateString(val));
-					}
-					CloseHandle(fh);
-				}
+	std::vector<char> readbuf;
+
+	std::unique_ptr<std::FILE, closeFile> fh;
+	for (const auto& entry: fs::directory_iterator(path)) {
+		if (fs::is_regular_file(entry.path())) {
+			const auto entrySize = fs::file_size(entry.path());
+			readbuf.resize(static_cast<std::size_t>(entrySize + 2)); // +1 wchar_t
+
+#if defined(OS_WIN)
+			fh.reset(_wfopen(entry.path().c_str(), L"r"));
+#else
+			fh.reset(std::fopen(entry.path().c_str(), "r"));
+#endif
+			if (std::fread(&readbuf[0], entrySize, 1, fh.get())) {
+				readbuf[entrySize + 1] = readbuf[entrySize] = 0; // null terminate
+				CefString val = static_cast<const wchar_t*>(static_cast<const void*>(readbuf.data()));
+				storage.emplace(entry.path().filename().native(), CefV8Value::CreateString(val));
 			}
-		} while (FindNextFile(hFind, &w32fd));
-		FindClose(hFind);
+		}
 	}
-	delete[] readbuf;
 }
 
-std::wstring FCHostPersistentStorage::get_filename(const CefString& key) const
+fs::path FCHostPersistentStorage::get_filename(const CefString& key) const
 {
-	return path + L"\\" + key.c_str();
+#if defined (OS_WIN)
+	return path / key.ToWString();
+#else
+	return path / key.ToString();
+#endif
 }
 
 void FCHostPersistentStorage::ensure_folder_exists() const
 {
 	// ignore returned errors
-	CreateDirectory(path.c_str(), NULL);
+	fs::create_directories(path);
 }
-#else
-#error "Platform-specific persistent storage implementation required."
-#endif
diff --git a/FCHost/fchost/fchost_storage.h b/FCHost/fchost/fchost_storage.h
index bb7536dc83096f9e081bfc7c92730e98bb3f93a5..db036577572d4428add530e5e02b7426057c99ca 100644
--- a/FCHost/fchost/fchost_storage.h
+++ b/FCHost/fchost/fchost_storage.h
@@ -1,6 +1,8 @@
 #pragma once
 
 #include "include/cef_app.h" // probably excessive
+
+#include <filesystem>
 #include <map>
 #include <string>
 
@@ -30,8 +32,9 @@ protected:
 // memory-backed, disk-persistent local storage
 class FCHostPersistentStorage : public FCHostSessionStorage
 {
+	using base = FCHostSessionStorage;
 public:
-	FCHostPersistentStorage(const std::wstring& _path) : path(_path) { ensure_folder_exists(); load();  };
+	FCHostPersistentStorage(const CefString& _path);
 
 	virtual void set(const CefString& key, CefRefPtr<CefV8Value> val) override;
 	virtual bool remove(const CefString& key) override;
@@ -40,7 +43,7 @@ public:
 private:
 	void load();
 	void ensure_folder_exists() const;
-	std::wstring get_filename(const CefString& key) const;
+	std::filesystem::path get_filename(const CefString& key) const;
 
-	std::wstring path;
+	std::filesystem::path path;
 };