diff --git a/config_templates/system_settings.ini b/config_templates/system_settings.ini index e16fbcd2f..5adeb5355 100644 --- a/config_templates/system_settings.ini +++ b/config_templates/system_settings.ini @@ -67,6 +67,11 @@ ; Note that this setting is ignored (and treated as 1) when htc is enabled. ; 0 = Disabled, 1 = Enabled ; enable_log_manager = u8!0x0 +; Controls whether the bluetooth pairing database is redirected to sd card for synchronization between sysnand and/or multiple emummcs +; Note that in firmware 13.0.0 the database size was doubled to 20 entries. Booting to a pre-13.0.0 firmware will cause the +; database to be truncated to 10 entries. +; 0 = Disabled, 1 = Enabled +; enable_external_bluetooth_db = u8!0x0 [hbloader] ; Controls the size of the homebrew heap when running as applet. ; If set to zero, all available applet memory is used as heap. diff --git a/stratosphere/ams_mitm/source/set_mitm/setsys_mitm_service.cpp b/stratosphere/ams_mitm/source/set_mitm/setsys_mitm_service.cpp index eb504b3c9..183105c21 100644 --- a/stratosphere/ams_mitm/source/set_mitm/setsys_mitm_service.cpp +++ b/stratosphere/ams_mitm/source/set_mitm/setsys_mitm_service.cpp @@ -16,6 +16,7 @@ #include #include "setsys_mitm_service.hpp" #include "settings_sd_kvs.hpp" +#include "setsys_shim.h" namespace ams::mitm::settings { @@ -87,6 +88,87 @@ namespace ams::mitm::settings { R_SUCCEED(); } + const char *g_external_bluetooth_db_location = "@Sdcard:/atmosphere/bluetooth_devices.db"; + + bool ExternalBluetoothDatabaseEnabled(void) { + u8 en = 0; + settings::fwdbg::GetSettingsItemValue(std::addressof(en), sizeof(en), "atmosphere", "enable_external_bluetooth_db"); + return en; + } + + bool HasExternalBluetoothDatabase(void) { + bool file_exists; + R_ABORT_UNLESS(fs::HasFile(std::addressof(file_exists), g_external_bluetooth_db_location)); + return file_exists; + } + + Result ReadExternalBluetoothDatabase(SetSysBluetoothDevicesSettings *db, size_t db_max_size, size_t *entries_read) { + /* Open external database file. */ + fs::FileHandle file; + R_TRY(fs::OpenFile(std::addressof(file), g_external_bluetooth_db_location, fs::OpenMode_Read)); + ON_SCOPE_EXIT { fs::CloseFile(file); }; + + /* Read number of database entries stored in external database. */ + size_t total_entries; + R_TRY(fs::ReadFile(file, 0, std::addressof(total_entries), sizeof(total_entries))); + + u64 db_offset = sizeof(total_entries); + if (total_entries > db_max_size) { + /* Cap number of database entries read to size of database on this firmware */ + total_entries = db_max_size; + + /* Pairings are stored from least to most recent. Add offset to skip the older entries that won't fit */ + db_offset += (total_entries - db_max_size) * sizeof(SetSysBluetoothDevicesSettings); + } + + /* Read database entries. */ + R_TRY(fs::ReadFile(file, db_offset, db, total_entries * sizeof(SetSysBluetoothDevicesSettings))); + + /* Convert to old database format if running on a firmware below 13.0.0 */ + if (hos::GetVersion() < hos::Version_13_0_0) { + for (u32 i = 0; i < total_entries; ++i) { + util::SNPrintf(db[i].name.name, sizeof(db[i].name), "%s", db[i].name2); + //std::memcpy(std::addressof(db[i].name), db[i].name2, sizeof(db[i].name)); + std::memset(db[i].name2, 0, sizeof(db[i].name2)); + } + } + + *entries_read = total_entries; + + return ResultSuccess(); + } + + Result WriteExternalBluetoothDatabase(const SetSysBluetoothDevicesSettings *db, size_t total_entries) { + /* Open external database file. */ + fs::FileHandle file; + R_TRY(fs::OpenFile(std::addressof(file), g_external_bluetooth_db_location, fs::OpenMode_Write | fs::OpenMode_AllowAppend)); + ON_SCOPE_EXIT { fs::CloseFile(file); }; + + /* Write number of database entries. */ + R_TRY(fs::WriteFile(file, 0, std::addressof(total_entries), sizeof(total_entries), fs::WriteOption::None)); + + /* Write database entries. */ + u64 db_offset = sizeof(total_entries); + if (hos::GetVersion() < hos::Version_13_0_0) { + /* Convert to new database format if running on a firmware below 13.0.0 */ + auto tmp_entry = std::make_unique(); + for (u32 i = 0; i < total_entries; ++i) { + std::memcpy(tmp_entry.get(), std::addressof(db[i]), sizeof(SetSysBluetoothDevicesSettings)); + //std::memcpy(tmp_entry->name2, std::addressof(tmp_entry->name), sizeof(tmp_entry->name)); + util::SNPrintf(tmp_entry->name2, sizeof(tmp_entry->name2), "%s", db[i].name.name); + std::memset(std::addressof(tmp_entry->name), 0, sizeof(tmp_entry->name)); + R_TRY(fs::WriteFile(file, db_offset, tmp_entry.get(), sizeof(SetSysBluetoothDevicesSettings), fs::WriteOption::None)); + db_offset += sizeof(SetSysBluetoothDevicesSettings); + } + fs::FlushFile(file); + } + else { + R_TRY(fs::WriteFile(file, db_offset, db, total_entries * sizeof(SetSysBluetoothDevicesSettings), fs::WriteOption::Flush)); + } + + return ResultSuccess(); + } + } Result SetSysMitmService::GetFirmwareVersion(sf::Out out) { @@ -102,6 +184,44 @@ namespace ams::mitm::settings { R_RETURN(GetFirmwareVersionImpl(out.GetPointer(), m_client_info)); } + Result SetSysMitmService::SetBluetoothDevicesSettings(const sf::InMapAliasArray &settings) { + /* Forward to session unless external database setting enabled. */ + R_UNLESS(ExternalBluetoothDatabaseEnabled(), sm::mitm::ResultShouldForwardToSession()); + + /* Create the external database if it doesn't exist. */ + if (!HasExternalBluetoothDatabase()) { + R_TRY(fs::CreateFile(g_external_bluetooth_db_location, 0)); + } + + /* Backup local database to sd card. */ + R_TRY(WriteExternalBluetoothDatabase(settings.GetPointer(), settings.GetSize())); + + /* Also allow the updated database to be stored to system save as usual. */ + return sm::mitm::ResultShouldForwardToSession(); + } + + Result SetSysMitmService::GetBluetoothDevicesSettings(sf::Out out_count, const sf::OutMapAliasArray &out) { + /* Forward to session unless external database setting enabled. */ + R_UNLESS(ExternalBluetoothDatabaseEnabled(), sm::mitm::ResultShouldForwardToSession()); + + if (!HasExternalBluetoothDatabase()) { + /* Forward to the real command to fetch database stored in system save. */ + R_TRY(setsysGetBluetoothDevicesSettingsFwd(m_forward_service.get(), out_count.GetPointer(), out.GetPointer(), out.GetSize())); + + /* Create the external database file. */ + R_TRY(fs::CreateFile(g_external_bluetooth_db_location, 0)); + + /* Backup local database to sd card. */ + R_TRY(WriteExternalBluetoothDatabase(out.GetPointer(), out_count.GetValue())); + } + else { + /* Read the external database from sd card. */ + R_TRY(ReadExternalBluetoothDatabase(out.GetPointer(), out.GetSize(), reinterpret_cast(out_count.GetPointer()))); + } + + return ResultSuccess(); + } + Result SetSysMitmService::GetSettingsItemValueSize(sf::Out out_size, const settings::SettingsName &name, const settings::SettingsItemKey &key) { R_TRY_CATCH(settings::fwdbg::GetSdCardKeyValueStoreSettingsItemValueSize(out_size.GetPointer(), name.value, key.value)) { R_CATCH_RETHROW(sf::impl::ResultRequestContextChanged) diff --git a/stratosphere/ams_mitm/source/set_mitm/setsys_mitm_service.hpp b/stratosphere/ams_mitm/source/set_mitm/setsys_mitm_service.hpp index 5779438bb..fdbace1bc 100644 --- a/stratosphere/ams_mitm/source/set_mitm/setsys_mitm_service.hpp +++ b/stratosphere/ams_mitm/source/set_mitm/setsys_mitm_service.hpp @@ -16,12 +16,14 @@ #pragma once #include -#define AMS_SETTINGS_SYSTEM_MITM_INTERFACE_INFO(C, H) \ - AMS_SF_METHOD_INFO(C, H, 3, Result, GetFirmwareVersion, (sf::Out out), (out)) \ - AMS_SF_METHOD_INFO(C, H, 4, Result, GetFirmwareVersion2, (sf::Out out), (out)) \ - AMS_SF_METHOD_INFO(C, H, 37, Result, GetSettingsItemValueSize, (sf::Out out_size, const ams::settings::SettingsName &name, const ams::settings::SettingsItemKey &key), (out_size, name, key)) \ - AMS_SF_METHOD_INFO(C, H, 38, Result, GetSettingsItemValue, (sf::Out out_size, const sf::OutBuffer &out, const ams::settings::SettingsName &name, const ams::settings::SettingsItemKey &key), (out_size, out, name, key)) \ - AMS_SF_METHOD_INFO(C, H, 62, Result, GetDebugModeFlag, (sf::Out out), (out)) +#define AMS_SETTINGS_SYSTEM_MITM_INTERFACE_INFO(C, H) \ + AMS_SF_METHOD_INFO(C, H, 3, Result, GetFirmwareVersion, (sf::Out out), (out)) \ + AMS_SF_METHOD_INFO(C, H, 4, Result, GetFirmwareVersion2, (sf::Out out), (out)) \ + AMS_SF_METHOD_INFO(C, H, 11, Result, SetBluetoothDevicesSettings, (const sf::InMapAliasArray &settings), (settings)) \ + AMS_SF_METHOD_INFO(C, H, 12, Result, GetBluetoothDevicesSettings, (sf::Out out_count, const sf::OutMapAliasArray &out), (out_count, out)) \ + AMS_SF_METHOD_INFO(C, H, 37, Result, GetSettingsItemValueSize, (sf::Out out_size, const ams::settings::SettingsName &name, const ams::settings::SettingsItemKey &key), (out_size, name, key)) \ + AMS_SF_METHOD_INFO(C, H, 38, Result, GetSettingsItemValue, (sf::Out out_size, const sf::OutBuffer &out, const ams::settings::SettingsName &name, const ams::settings::SettingsItemKey &key), (out_size, out, name, key)) \ + AMS_SF_METHOD_INFO(C, H, 62, Result, GetDebugModeFlag, (sf::Out out), (out)) AMS_SF_DEFINE_MITM_INTERFACE(ams::mitm::settings, ISetSysMitmInterface, AMS_SETTINGS_SYSTEM_MITM_INTERFACE_INFO, 0x0E82ED13) @@ -41,6 +43,8 @@ namespace ams::mitm::settings { public: Result GetFirmwareVersion(sf::Out out); Result GetFirmwareVersion2(sf::Out out); + Result SetBluetoothDevicesSettings(const sf::InMapAliasArray &settings); + Result GetBluetoothDevicesSettings(sf::Out out_count, const sf::OutMapAliasArray &out); Result GetSettingsItemValueSize(sf::Out out_size, const ams::settings::SettingsName &name, const ams::settings::SettingsItemKey &key); Result GetSettingsItemValue(sf::Out out_size, const sf::OutBuffer &out, const ams::settings::SettingsName &name, const ams::settings::SettingsItemKey &key); Result GetDebugModeFlag(sf::Out out); diff --git a/stratosphere/ams_mitm/source/set_mitm/setsys_shim.c b/stratosphere/ams_mitm/source/set_mitm/setsys_shim.c new file mode 100644 index 000000000..22cbd2c99 --- /dev/null +++ b/stratosphere/ams_mitm/source/set_mitm/setsys_shim.c @@ -0,0 +1,23 @@ +/* + * Copyright (c) Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope 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 this program. If not, see . + */ +#include "setsys_shim.h" + +Result setsysGetBluetoothDevicesSettingsFwd(Service *s, s32 *total_out, SetSysBluetoothDevicesSettings *settings, s32 count) { + return serviceDispatchOut(s, 12, *total_out, + .buffer_attrs = { SfBufferAttr_HipcMapAlias | SfBufferAttr_Out }, + .buffers = { { settings, count*sizeof(SetSysBluetoothDevicesSettings) } }, + ); +} diff --git a/stratosphere/ams_mitm/source/set_mitm/setsys_shim.h b/stratosphere/ams_mitm/source/set_mitm/setsys_shim.h new file mode 100644 index 000000000..9b959c976 --- /dev/null +++ b/stratosphere/ams_mitm/source/set_mitm/setsys_shim.h @@ -0,0 +1,19 @@ +/** + * @file setsys_shim.h + * @brief Settings Services (fs) IPC wrapper for setsys.mitm. + * @author ndeadly + * @copyright libnx Authors + */ +#pragma once +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/* Forwarding shims. */ +Result setsysGetBluetoothDevicesSettingsFwd(Service *s, s32 *total_out, SetSysBluetoothDevicesSettings *settings, s32 count); + +#ifdef __cplusplus +} +#endif \ No newline at end of file diff --git a/stratosphere/ams_mitm/source/set_mitm/settings_sd_kvs.cpp b/stratosphere/ams_mitm/source/set_mitm/settings_sd_kvs.cpp index 1d0d9ce10..b5959c464 100644 --- a/stratosphere/ams_mitm/source/set_mitm/settings_sd_kvs.cpp +++ b/stratosphere/ams_mitm/source/set_mitm/settings_sd_kvs.cpp @@ -393,6 +393,12 @@ namespace ams::settings::fwdbg { /* 0 = Disabled, 1 = Enabled */ R_ABORT_UNLESS(ParseSettingsItemValue("atmosphere", "enable_log_manager", "u8!0x0")); + /* Controls whether the bluetooth pairing database is redirected to sd card for synchronization between sysnand and/or multiple emummcs. */ + /* Note that in firmware 13.0.0 the database size was doubled to 20 entries. Booting to a pre-13.0.0 firmware will cause the */ + /* database to be truncated to 10 entries. */ + /* 0 = Disabled, 1 = Enabled */ + R_ABORT_UNLESS(ParseSettingsItemValue("atmosphere", "enable_external_bluetooth_db", "u8!0x0")); + /* Hbloader custom settings. */ /* Controls the size of the homebrew heap when running as applet. */