Skip to content
Closed
9 changes: 9 additions & 0 deletions Source/Core/Common/VariantUtil.h
Original file line number Diff line number Diff line change
Expand Up @@ -26,3 +26,12 @@ auto VariantCast(const std::variant<From...>& v)
{
return detail::VariantCastProxy<From...>{v};
}

template <class... Ts>
struct overloaded : Ts...
{
using Ts::operator()...;
};

template <class... Ts>
overloaded(Ts...) -> overloaded<Ts...>;
180 changes: 141 additions & 39 deletions Source/Core/InputCommon/DynamicInputTextures/DITConfiguration.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
#include "Common/FileUtil.h"
#include "Common/Logging/Log.h"
#include "Common/StringUtil.h"
#include "Common/VariantUtil.h"
#include "Core/ConfigManager.h"
#include "InputCommon/ControllerEmu/ControllerEmu.h"
#include "InputCommon/DynamicInputTextures/DITSpecification.h"
Expand Down Expand Up @@ -73,16 +74,22 @@ Configuration::Configuration(const std::string& json_file)
specification = static_cast<u8>(spec_from_json);
}

if (specification != 1)
if (specification == 1)
{
m_valid = ProcessSpecificationV1(root, m_dynamic_input_textures, m_base_path, json_file);
}
else if (specification == 2)
{
m_valid = ProcessSpecificationV2(root, m_dynamic_input_textures, m_base_path, json_file);
}
else
{
ERROR_LOG_FMT(VIDEO,
"Failed to load dynamic input json file '{}', specification '{}' is invalid",
json_file, specification);
m_valid = false;
return;
}

m_valid = ProcessSpecificationV1(root, m_dynamic_input_textures, m_base_path, json_file);
}

Configuration::~Configuration() = default;
Expand Down Expand Up @@ -110,7 +117,6 @@ bool Configuration::GenerateTexture(const IniFile& file,
auto image_to_write = original_image;

bool dirty = false;

for (const auto& controller_name : controller_names)
{
auto* sec = file.GetSection(controller_name);
Expand Down Expand Up @@ -146,7 +152,7 @@ bool Configuration::GenerateTexture(const IniFile& file,
}
}

for (auto& [emulated_key, rects] : emulated_controls_iter->second)
for (auto& emulated_entry : emulated_controls_iter->second)
{
if (!device_found)
{
Expand All @@ -156,43 +162,11 @@ bool Configuration::GenerateTexture(const IniFile& file,
continue;
}

std::string host_key;
sec->Get(emulated_key, &host_key);

const auto input_image_iter = host_devices_iter->second.find(host_key);
if (input_image_iter == host_devices_iter->second.end())
if (ApplyEmulatedEntry(host_devices_iter->second, emulated_entry, sec, *image_to_write,
texture_data.m_preserve_aspect_ratio))
{
dirty = true;
}
else
{
const auto host_key_image = LoadImage(m_base_path + input_image_iter->second);

for (const auto& rect : rects)
{
InputCommon::ImagePixelData pixel_data;
if (host_key_image->width == rect.GetWidth() &&
host_key_image->height == rect.GetHeight())
{
pixel_data = *host_key_image;
}
else if (texture_data.m_preserve_aspect_ratio)
{
pixel_data =
ResizeKeepAspectRatio(ResizeMode::Nearest, *host_key_image, rect.GetWidth(),
rect.GetHeight(), Pixel{0, 0, 0, 0});
}
else
{
pixel_data =
Resize(ResizeMode::Nearest, *host_key_image, rect.GetWidth(), rect.GetHeight());
}

CopyImageRegion(pixel_data, *image_to_write,
Rect{0, 0, rect.GetWidth(), rect.GetHeight()}, rect);
dirty = true;
}
}
}
}

Expand All @@ -219,4 +193,132 @@ bool Configuration::GenerateTexture(const IniFile& file,

return false;
}

bool Configuration::ApplyEmulatedEntry(const Configuration::HostEntries& host_entries,
const Data::EmulatedEntry& emulated_entry,
const IniFile::Section* section,
ImagePixelData& image_to_write,
bool preserve_aspect_ratio) const
{
return std::visit(overloaded{
[&, this](const Data::EmulatedSingleEntry& entry) {
std::string host_key;
section->Get(entry.m_key, &host_key);
return ApplyEmulatedSingleEntry(
host_entries, std::vector<std::string>{host_key}, entry.m_tag,
entry.m_region, image_to_write, preserve_aspect_ratio);
},
[&, this](const Data::EmulatedMultiEntry& entry) {
return ApplyEmulatedMultiEntry(host_entries, entry, section,
image_to_write, preserve_aspect_ratio);
},
},
emulated_entry);
}

bool Configuration::ApplyEmulatedSingleEntry(const Configuration::HostEntries& host_entries,
const std::vector<std::string> keys,
const std::optional<std::string> tag,
const Rect& region, ImagePixelData& image_to_write,
bool preserve_aspect_ratio) const
{
for (auto& host_entry : host_entries)
{
if (keys == host_entry.m_keys && tag == host_entry.m_tag)
{
const auto host_key_image = LoadImage(m_base_path + host_entry.m_path);
ImagePixelData pixel_data;
if (host_key_image->width == region.GetWidth() &&
host_key_image->height == region.GetHeight())
{
pixel_data = *host_key_image;
}
else if (preserve_aspect_ratio)
{
pixel_data = ResizeKeepAspectRatio(ResizeMode::Nearest, *host_key_image, region.GetWidth(),
region.GetHeight(), Pixel{0, 0, 0, 0});
}
else
{
pixel_data =
Resize(ResizeMode::Nearest, *host_key_image, region.GetWidth(), region.GetHeight());
}

CopyImageRegion(pixel_data, image_to_write, Rect{0, 0, region.GetWidth(), region.GetHeight()},
region);

return true;
}
}

return false;
}

bool Configuration::ApplyEmulatedMultiEntry(const Configuration::HostEntries& host_entries,
const Data::EmulatedMultiEntry& emulated_entry,
const IniFile::Section* section,
InputCommon::ImagePixelData& image_to_write,
bool preserve_aspect_ratio) const
{
// Try to apply our group'd region first
const auto emulated_keys = GetKeysFrom(emulated_entry);
std::vector<std::string> host_keys;
host_keys.reserve(emulated_keys.size());
for (const auto& emulated_key : emulated_keys)
{
std::string host_key;
section->Get(emulated_key, &host_key);
host_keys.push_back(host_key);
}
if (ApplyEmulatedSingleEntry(host_entries, host_keys, emulated_entry.m_combined_tag,
emulated_entry.m_combined_region, image_to_write,
preserve_aspect_ratio))
{
return true;
}

ImagePixelData temporary_pixel_data(emulated_entry.m_combined_region.GetWidth(),
emulated_entry.m_combined_region.GetHeight());
bool apply = false;
for (const auto& sub_entry : emulated_entry.m_sub_entries)
{
apply |= ApplyEmulatedEntry(host_entries, sub_entry, section, temporary_pixel_data,
preserve_aspect_ratio);
}

if (apply)
{
CopyImageRegion(temporary_pixel_data, image_to_write,
Rect{0, 0, emulated_entry.m_combined_region.GetWidth(),
emulated_entry.m_combined_region.GetHeight()},
emulated_entry.m_combined_region);
}

return apply;
}

std::vector<std::string> Configuration::GetKeysFrom(const Data::EmulatedEntry& emulated_entry) const
{
return std::visit(
overloaded{
[&, this](const Data::EmulatedSingleEntry& entry) {
return std::vector<std::string>{entry.m_key};
},
[&, this](const Data::EmulatedMultiEntry& entry) { return GetKeysFrom(entry); },
},
emulated_entry);
}

std::vector<std::string>
Configuration::GetKeysFrom(const Data::EmulatedMultiEntry& emulated_entry) const
{
std::vector<std::string> result;
for (const auto& sub_entry : emulated_entry.m_sub_entries)
{
const auto sub_entry_keys = GetKeysFrom(sub_entry);
result.reserve(result.size() + sub_entry_keys.size());
result.insert(result.end(), sub_entry_keys.begin(), sub_entry_keys.end());
}
return result;
}
} // namespace InputCommon::DynamicInputTextures
20 changes: 20 additions & 0 deletions Source/Core/InputCommon/DynamicInputTextures/DITConfiguration.h
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,13 @@

#include <string>
#include <unordered_map>
#include <variant>
#include <vector>

#include "Common/CommonTypes.h"
#include "Common/IniFile.h"
#include "InputCommon/DynamicInputTextures/DITData.h"
#include "InputCommon/ImageOperations.h"

namespace InputCommon::DynamicInputTextures
{
Expand All @@ -26,6 +28,24 @@ class Configuration
bool GenerateTexture(const IniFile& file, const std::vector<std::string>& controller_names,
const Data& texture_data) const;

using HostEntries = std::vector<Data::HostEntry>;
bool ApplyEmulatedEntry(const HostEntries& host_entries,
const Data::EmulatedEntry& emulated_entry,
const IniFile::Section* section, ImagePixelData& image_to_write,
bool preserve_aspect_ratio) const;
bool ApplyEmulatedSingleEntry(const HostEntries& host_entries,
const std::vector<std::string> keys,
const std::optional<std::string> tag, const Rect& region,
ImagePixelData& image_to_write, bool preserve_aspect_ratio) const;
bool ApplyEmulatedMultiEntry(const HostEntries& host_entries,
const Data::EmulatedMultiEntry& emulated_entry,
const IniFile::Section* section, ImagePixelData& image_to_write,
bool preserve_aspect_ratio) const;

std::vector<std::string> GetKeysFrom(const Data::EmulatedEntry& emulated_entry) const;

std::vector<std::string> GetKeysFrom(const Data::EmulatedMultiEntry& emulated_entry) const;

std::vector<Data> m_dynamic_input_textures;
std::string m_base_path;
bool m_valid = true;
Expand Down
38 changes: 30 additions & 8 deletions Source/Core/InputCommon/DynamicInputTextures/DITData.h
Original file line number Diff line number Diff line change
@@ -1,11 +1,9 @@
// Copyright 2021 Dolphin Emulator Project
// Licensed under GPLv2+
// Refer to the license.txt file included.

#pragma once

#include <optional>
#include <string>
#include <unordered_map>
#include <variant>
#include <vector>

#include "InputCommon/ImageOperations.h"
Expand All @@ -18,11 +16,35 @@ struct Data
std::string m_hires_texture_name;
std::string m_generated_folder_name;

using EmulatedKeyToRegionsMap = std::unordered_map<std::string, std::vector<Rect>>;
std::unordered_map<std::string, EmulatedKeyToRegionsMap> m_emulated_controllers;
struct EmulatedSingleEntry;
struct EmulatedMultiEntry;
using EmulatedEntry = std::variant<EmulatedSingleEntry, EmulatedMultiEntry>;

struct EmulatedSingleEntry
{
std::string m_key;
std::optional<std::string> m_tag;
Rect m_region;
};

struct EmulatedMultiEntry
{
std::string m_combined_tag;
Rect m_combined_region;

std::vector<EmulatedEntry> m_sub_entries;
};

std::unordered_map<std::string, std::vector<EmulatedEntry>> m_emulated_controllers;

struct HostEntry
{
std::vector<std::string> m_keys;
std::optional<std::string> m_tag;
std::string m_path;
};

using HostKeyToImagePath = std::unordered_map<std::string, std::string>;
std::unordered_map<std::string, HostKeyToImagePath> m_host_devices;
std::unordered_map<std::string, std::vector<HostEntry>> m_host_devices;
bool m_preserve_aspect_ratio = true;
};
} // namespace InputCommon::DynamicInputTextures
Loading