2022-04-23 10:59:50 +02:00
|
|
|
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
|
|
|
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
2020-09-23 15:52:25 +02:00
|
|
|
|
|
|
|
#include <fmt/format.h>
|
|
|
|
|
2021-05-26 01:32:56 +02:00
|
|
|
#include "common/fs/fs.h"
|
|
|
|
#include "common/fs/path_util.h"
|
2020-09-23 15:52:25 +02:00
|
|
|
#include "yuzu/configuration/config.h"
|
|
|
|
#include "yuzu/configuration/input_profiles.h"
|
|
|
|
|
|
|
|
namespace FS = Common::FS;
|
|
|
|
|
|
|
|
namespace {
|
|
|
|
|
|
|
|
bool ProfileExistsInFilesystem(std::string_view profile_name) {
|
2021-05-26 01:32:56 +02:00
|
|
|
return FS::Exists(FS::GetYuzuPath(FS::YuzuPath::ConfigDir) / "input" /
|
|
|
|
fmt::format("{}.ini", profile_name));
|
2020-09-23 15:52:25 +02:00
|
|
|
}
|
|
|
|
|
2021-05-26 01:32:56 +02:00
|
|
|
bool IsINI(const std::filesystem::path& filename) {
|
|
|
|
return filename.extension() == ".ini";
|
2020-09-23 15:52:25 +02:00
|
|
|
}
|
|
|
|
|
2021-05-26 01:32:56 +02:00
|
|
|
std::filesystem::path GetNameWithoutExtension(std::filesystem::path filename) {
|
|
|
|
return filename.replace_extension();
|
2020-09-23 15:52:25 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
} // namespace
|
|
|
|
|
2022-07-10 17:29:10 +02:00
|
|
|
InputProfiles::InputProfiles() {
|
2021-05-26 01:32:56 +02:00
|
|
|
const auto input_profile_loc = FS::GetYuzuPath(FS::YuzuPath::ConfigDir) / "input";
|
|
|
|
|
|
|
|
if (!FS::IsDir(input_profile_loc)) {
|
|
|
|
return;
|
|
|
|
}
|
2020-09-23 15:52:25 +02:00
|
|
|
|
2021-05-26 01:32:56 +02:00
|
|
|
FS::IterateDirEntries(
|
|
|
|
input_profile_loc,
|
|
|
|
[this](const std::filesystem::path& full_path) {
|
|
|
|
const auto filename = full_path.filename();
|
|
|
|
const auto name_without_ext =
|
|
|
|
Common::FS::PathToUTF8String(GetNameWithoutExtension(filename));
|
|
|
|
|
|
|
|
if (IsINI(filename) && IsProfileNameValid(name_without_ext)) {
|
2020-09-23 15:52:25 +02:00
|
|
|
map_profiles.insert_or_assign(
|
2022-07-10 17:29:10 +02:00
|
|
|
name_without_ext,
|
|
|
|
std::make_unique<Config>(name_without_ext, Config::ConfigType::InputProfile));
|
2020-09-23 15:52:25 +02:00
|
|
|
}
|
2021-05-26 01:32:56 +02:00
|
|
|
|
2020-09-23 15:52:25 +02:00
|
|
|
return true;
|
2021-05-26 01:32:56 +02:00
|
|
|
},
|
|
|
|
FS::DirEntryFilter::File);
|
2020-09-23 15:52:25 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
InputProfiles::~InputProfiles() = default;
|
|
|
|
|
|
|
|
std::vector<std::string> InputProfiles::GetInputProfileNames() {
|
|
|
|
std::vector<std::string> profile_names;
|
|
|
|
profile_names.reserve(map_profiles.size());
|
|
|
|
|
2023-01-28 05:31:41 +01:00
|
|
|
auto it = map_profiles.cbegin();
|
|
|
|
while (it != map_profiles.cend()) {
|
|
|
|
const auto& [profile_name, config] = *it;
|
2020-09-23 15:52:25 +02:00
|
|
|
if (!ProfileExistsInFilesystem(profile_name)) {
|
2023-01-28 05:31:41 +01:00
|
|
|
it = map_profiles.erase(it);
|
2020-09-23 15:52:25 +02:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
profile_names.push_back(profile_name);
|
2023-01-28 05:31:41 +01:00
|
|
|
++it;
|
2020-09-23 15:52:25 +02:00
|
|
|
}
|
|
|
|
|
2022-09-23 19:31:35 +02:00
|
|
|
std::stable_sort(profile_names.begin(), profile_names.end());
|
|
|
|
|
2020-09-23 15:52:25 +02:00
|
|
|
return profile_names;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool InputProfiles::IsProfileNameValid(std::string_view profile_name) {
|
|
|
|
return profile_name.find_first_of("<>:;\"/\\|,.!?*") == std::string::npos;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool InputProfiles::CreateProfile(const std::string& profile_name, std::size_t player_index) {
|
|
|
|
if (ProfileExistsInMap(profile_name)) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
map_profiles.insert_or_assign(
|
2022-07-10 17:29:10 +02:00
|
|
|
profile_name, std::make_unique<Config>(profile_name, Config::ConfigType::InputProfile));
|
2020-09-23 15:52:25 +02:00
|
|
|
|
|
|
|
return SaveProfile(profile_name, player_index);
|
|
|
|
}
|
|
|
|
|
|
|
|
bool InputProfiles::DeleteProfile(const std::string& profile_name) {
|
|
|
|
if (!ProfileExistsInMap(profile_name)) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!ProfileExistsInFilesystem(profile_name) ||
|
2021-05-26 01:32:56 +02:00
|
|
|
FS::RemoveFile(map_profiles[profile_name]->GetConfigFilePath())) {
|
2020-09-23 15:52:25 +02:00
|
|
|
map_profiles.erase(profile_name);
|
|
|
|
}
|
|
|
|
|
|
|
|
return !ProfileExistsInMap(profile_name) && !ProfileExistsInFilesystem(profile_name);
|
|
|
|
}
|
|
|
|
|
|
|
|
bool InputProfiles::LoadProfile(const std::string& profile_name, std::size_t player_index) {
|
|
|
|
if (!ProfileExistsInMap(profile_name)) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!ProfileExistsInFilesystem(profile_name)) {
|
|
|
|
map_profiles.erase(profile_name);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
map_profiles[profile_name]->ReadControlPlayerValue(player_index);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool InputProfiles::SaveProfile(const std::string& profile_name, std::size_t player_index) {
|
|
|
|
if (!ProfileExistsInMap(profile_name)) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
map_profiles[profile_name]->SaveControlPlayerValue(player_index);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool InputProfiles::ProfileExistsInMap(const std::string& profile_name) const {
|
|
|
|
return map_profiles.find(profile_name) != map_profiles.end();
|
|
|
|
}
|