diff --git a/src/social_integration.cpp b/src/social_integration.cpp
new file mode 100644
--- /dev/null
+++ b/src/social_integration.cpp
@@ -0,0 +1,246 @@
+/*
+ * This file is part of OpenTTD.
+ * OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
+ * OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see .
+ */
+
+/** @file social_integration.cpp Base implementation of social integration support. */
+
+#include "stdafx.h"
+
+#include "social_integration.h"
+#include "3rdparty/openttd_social_integration_api/openttd_social_integration_api.h"
+
+#include "debug.h"
+#include "fileio_func.h"
+#include "library_loader.h"
+#include "rev.h"
+#include "string_func.h"
+#include "signature.h"
+
+#include "safeguards.h"
+
+/**
+ * Container to track information per plugin.
+ */
+class InternalSocialIntegrationPlugin {
+public:
+ InternalSocialIntegrationPlugin(const std::string &filename, const std::string &basepath) : library(nullptr), external(basepath)
+ {
+ openttd_info.openttd_version = _openttd_revision;
+
+ if (!ValidateSignatureFile(fmt::format("{}.sig", filename))) {
+ external.state = SocialIntegrationPlugin::INVALID_SIGNATURE;
+ return;
+ }
+
+ this->library = std::make_unique(filename);
+ }
+
+ OpenTTD_SocialIntegration_v1_PluginInfo plugin_info = {}; ///< Information supplied by plugin.
+ OpenTTD_SocialIntegration_v1_PluginApi plugin_api = {}; ///< API supplied by plugin.
+ OpenTTD_SocialIntegration_v1_OpenTTDInfo openttd_info = {}; ///< Information supplied by OpenTTD.
+
+ std::unique_ptr library = nullptr; ///< Library handle.
+
+ SocialIntegrationPlugin external; ///< Information of the plugin to be used by other parts of our codebase.
+};
+
+static std::vector> _plugins; ///< List of loaded plugins.
+static std::set _loaded_social_platform; ///< List of Social Platform plugins already loaded. Used to prevent loading a plugin for the same Social Platform twice.
+
+/** Helper for scanning for files with SocialIntegration as extension */
+class SocialIntegrationFileScanner : FileScanner {
+public:
+ void Scan()
+ {
+#ifdef _WIN32
+ std::string extension = "-social.dll";
+#elif defined(__APPLE__)
+ std::string extension = "-social.dylib";
+#else
+ std::string extension = "-social.so";
+#endif
+
+ this->FileScanner::Scan(extension.c_str(), SOCIAL_INTEGRATION_DIR, false);
+ }
+
+ bool AddFile(const std::string &filename, size_t basepath_length, const std::string &) override
+ {
+ std::string basepath = filename.substr(basepath_length);
+ Debug(misc, 1, "[Social Integration: {}] Loading ...", basepath);
+
+ auto &plugin = _plugins.emplace_back(std::make_unique(filename, basepath));
+
+ /* Validation failed, so no library was loaded. */
+ if (plugin->library == nullptr) {
+ return false;
+ }
+
+ if (plugin->library->HasError()) {
+ plugin->external.state = SocialIntegrationPlugin::FAILED;
+
+ Debug(misc, 0, "[Social Integration: {}] Failed to load library: {}", basepath, plugin->library->GetLastError());
+ return false;
+ }
+
+ OpenTTD_SocialIntegration_v1_GetInfo getinfo_func = plugin->library->GetFunction("SocialIntegration_v1_GetInfo");
+ if (plugin->library->HasError()) {
+ plugin->external.state = SocialIntegrationPlugin::UNSUPPORTED_API;
+
+ Debug(misc, 0, "[Social Integration: {}] Failed to find symbol SocialPlugin_v1_GetInfo: {}", basepath, plugin->library->GetLastError());
+ return false;
+ }
+
+ OpenTTD_SocialIntegration_v1_Init init_func = plugin->library->GetFunction("SocialIntegration_v1_Init");
+ if (plugin->library->HasError()) {
+ plugin->external.state = SocialIntegrationPlugin::UNSUPPORTED_API;
+
+ Debug(misc, 0, "[Social Integration: {}] Failed to find symbol SocialPlugin_v1_Init: {}", basepath, plugin->library->GetLastError());
+ return false;
+ }
+
+ getinfo_func(&plugin->plugin_info);
+ /* Setup the information for the outside world to see. */
+ plugin->external.social_platform = plugin->plugin_info.social_platform;
+ plugin->external.name = plugin->plugin_info.name;
+ plugin->external.version = plugin->plugin_info.version;
+
+ /* Lowercase the string for comparison. */
+ std::string lc_social_platform = plugin->plugin_info.social_platform;
+ strtolower(lc_social_platform);
+
+ /* Prevent more than one plugin for a certain Social Platform to be loaded, as that never ends well. */
+ if (_loaded_social_platform.find(lc_social_platform) != _loaded_social_platform.end()) {
+ plugin->external.state = SocialIntegrationPlugin::DUPLICATE;
+
+ Debug(misc, 0, "[Social Integration: {}] Another plugin for {} is already loaded", basepath, plugin->plugin_info.social_platform);
+ return false;
+ }
+ _loaded_social_platform.insert(lc_social_platform);
+
+ auto state = init_func(&plugin->plugin_api, &plugin->openttd_info);
+ switch (state) {
+ case OTTD_SOCIAL_INTEGRATION_V1_INIT_SUCCESS:
+ plugin->external.state = SocialIntegrationPlugin::RUNNING;
+
+ Debug(misc, 1, "[Social Integration: {}] Loaded for {}: {} ({})", basepath, plugin->plugin_info.social_platform, plugin->plugin_info.name, plugin->plugin_info.version);
+ return true;
+
+ case OTTD_SOCIAL_INTEGRATION_V1_INIT_FAILED:
+ plugin->external.state = SocialIntegrationPlugin::FAILED;
+
+ Debug(misc, 0, "[Social Integration: {}] Failed to initialize", basepath);
+ return false;
+
+ case OTTD_SOCIAL_INTEGRATION_V1_INIT_PLATFORM_NOT_RUNNING:
+ plugin->external.state = SocialIntegrationPlugin::PLATFORM_NOT_RUNNING;
+
+ Debug(misc, 1, "[Social Integration: {}] Failed to initialize: {} is not running", basepath, plugin->plugin_info.social_platform);
+ return false;
+
+ default:
+ NOT_REACHED();
+ }
+ }
+};
+
+std::vector SocialIntegration::GetPlugins()
+{
+ std::vector plugins;
+
+ for (auto &plugin : _plugins) {
+ plugins.push_back(&plugin->external);
+ }
+
+ return plugins;
+}
+
+void SocialIntegration::Initialize()
+{
+ SocialIntegrationFileScanner fs;
+ fs.Scan();
+}
+
+/**
+ * Wrapper to call a function pointer of a plugin if it isn't a nullptr.
+ *
+ * @param plugin Plugin to call the function pointer on.
+ * @param func Function pointer to call.
+ */
+template
+static void PluginCall(std::unique_ptr &plugin, T func, Ts... args)
+{
+ if (plugin->external.state != SocialIntegrationPlugin::RUNNING) {
+ return;
+ }
+
+ if (func != nullptr) {
+ func(args...);
+ }
+}
+
+void SocialIntegration::Shutdown()
+{
+ for (auto &plugin : _plugins) {
+ PluginCall(plugin, plugin->plugin_api.shutdown);
+ }
+
+ _plugins.clear();
+ _loaded_social_platform.clear();
+}
+
+void SocialIntegration::RunCallbacks()
+{
+ for (auto &plugin : _plugins) {
+ if (plugin->external.state != SocialIntegrationPlugin::RUNNING) {
+ continue;
+ }
+
+ if (plugin->plugin_api.run_callbacks != nullptr) {
+ if (!plugin->plugin_api.run_callbacks()) {
+ Debug(misc, 1, "[Social Plugin: {}] Requested to be unloaded", plugin->external.basepath);
+
+ _loaded_social_platform.erase(plugin->plugin_info.social_platform);
+ plugin->external.state = SocialIntegrationPlugin::UNLOADED;
+ PluginCall(plugin, plugin->plugin_api.shutdown);
+ }
+ }
+ }
+}
+
+void SocialIntegration::EventEnterMainMenu()
+{
+ for (auto &plugin : _plugins) {
+ PluginCall(plugin, plugin->plugin_api.event_enter_main_menu);
+ }
+}
+
+void SocialIntegration::EventEnterScenarioEditor(uint map_width, uint map_height)
+{
+ for (auto &plugin : _plugins) {
+ PluginCall(plugin, plugin->plugin_api.event_enter_scenario_editor, map_width, map_height);
+ }
+}
+
+void SocialIntegration::EventEnterSingleplayer(uint map_width, uint map_height)
+{
+ for (auto &plugin : _plugins) {
+ PluginCall(plugin, plugin->plugin_api.event_enter_singleplayer, map_width, map_height);
+ }
+}
+
+void SocialIntegration::EventEnterMultiplayer(uint map_width, uint map_height)
+{
+ for (auto &plugin : _plugins) {
+ PluginCall(plugin, plugin->plugin_api.event_enter_multiplayer, map_width, map_height);
+ }
+}
+
+void SocialIntegration::EventJoiningMultiplayer()
+{
+ for (auto &plugin : _plugins) {
+ PluginCall(plugin, plugin->plugin_api.event_joining_multiplayer);
+ }
+}