| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,167 @@ | ||
| { | ||
| "meta": | ||
| { | ||
| "title": "Bloom and HUD definitions", | ||
| "author": "iwubcode" | ||
| }, | ||
| "groups": [ | ||
| { | ||
| "name": "HUD", | ||
| "targets": [ | ||
| { | ||
| "type": "draw_started", | ||
| "texture_filename": "tex1_96x96_7b5b0f693c1200ad_5" | ||
| }, | ||
| { | ||
| "type": "draw_started", | ||
| "texture_filename": "tex1_40x48_b510b4434b7de70c_5" | ||
| }, | ||
| { | ||
| "type": "draw_started", | ||
| "texture_filename": "tex1_96x96_633c30835459df0f_5" | ||
| }, | ||
| { | ||
| "type": "draw_started", | ||
| "texture_filename": "tex1_24x68_715518a00c14e148_5" | ||
| }, | ||
| { | ||
| "type": "draw_started", | ||
| "texture_filename": "tex1_32x80_f310048c1139815d_14" | ||
| }, | ||
| { | ||
| "type": "draw_started", | ||
| "texture_filename": "tex1_12x16_1e9016c61dfffb7a_14" | ||
| }, | ||
| { | ||
| "type": "draw_started", | ||
| "texture_filename": "tex1_12x16_459c7d7576547909_14" | ||
| }, | ||
| { | ||
| "type": "draw_started", | ||
| "texture_filename": "tex1_12x16_d1b77f0000ff337a_14" | ||
| }, | ||
| { | ||
| "type": "draw_started", | ||
| "texture_filename": "tex1_20x16_798aee4dc7001432_14" | ||
| }, | ||
| { | ||
| "type": "draw_started", | ||
| "texture_filename": "tex1_108x122_911fa08f1554752c_5" | ||
| }, | ||
| { | ||
| "type": "draw_started", | ||
| "texture_filename": "tex1_64x64_96894941f5454ead_3" | ||
| }, | ||
| { | ||
| "type": "draw_started", | ||
| "texture_filename": "tex1_8x8_0017d44adaf2291b_14" | ||
| }, | ||
| { | ||
| "type": "draw_started", | ||
| "texture_filename": "tex1_10x8_7a6a869d5553c4a0_14" | ||
| }, | ||
| { | ||
| "type": "draw_started", | ||
| "texture_filename": "tex1_16x16_49c191eaf4314e9d_14" | ||
| }, | ||
| { | ||
| "type": "draw_started", | ||
| "texture_filename": "tex1_18x16_472b403cdcfc31a3_3" | ||
| }, | ||
| { | ||
| "type": "draw_started", | ||
| "texture_filename": "tex1_30x26_629956a45175e53a_14" | ||
| }, | ||
| { | ||
| "type": "projection", | ||
| "value": "2d", | ||
| "texture_filename": "tex1_96x96_7b5b0f693c1200ad_5" | ||
| }, | ||
| { | ||
| "type": "projection", | ||
| "value": "2d", | ||
| "texture_filename": "tex1_40x48_b510b4434b7de70c_5" | ||
| }, | ||
| { | ||
| "type": "projection", | ||
| "value": "2d", | ||
| "texture_filename": "tex1_96x96_633c30835459df0f_5" | ||
| }, | ||
| { | ||
| "type": "projection", | ||
| "value": "2d", | ||
| "texture_filename": "tex1_24x68_715518a00c14e148_5" | ||
| }, | ||
| { | ||
| "type": "projection", | ||
| "value": "2d", | ||
| "texture_filename": "tex1_32x80_f310048c1139815d_14" | ||
| }, | ||
| { | ||
| "type": "projection", | ||
| "value": "2d", | ||
| "texture_filename": "tex1_12x16_1e9016c61dfffb7a_14" | ||
| }, | ||
| { | ||
| "type": "projection", | ||
| "value": "2d", | ||
| "texture_filename": "tex1_12x16_459c7d7576547909_14" | ||
| }, | ||
| { | ||
| "type": "projection", | ||
| "value": "2d", | ||
| "texture_filename": "tex1_12x16_d1b77f0000ff337a_14" | ||
| }, | ||
| { | ||
| "type": "projection", | ||
| "value": "2d", | ||
| "texture_filename": "tex1_20x16_798aee4dc7001432_14" | ||
| }, | ||
| { | ||
| "type": "projection", | ||
| "value": "2d", | ||
| "texture_filename": "tex1_108x122_911fa08f1554752c_5" | ||
| }, | ||
| { | ||
| "type": "projection", | ||
| "value": "2d", | ||
| "texture_filename": "tex1_64x64_96894941f5454ead_3" | ||
| }, | ||
| { | ||
| "type": "projection", | ||
| "value": "2d", | ||
| "texture_filename": "tex1_8x8_0017d44adaf2291b_14" | ||
| }, | ||
| { | ||
| "type": "projection", | ||
| "value": "2d", | ||
| "texture_filename": "tex1_10x8_7a6a869d5553c4a0_14" | ||
| }, | ||
| { | ||
| "type": "projection", | ||
| "value": "2d", | ||
| "texture_filename": "tex1_16x16_49c191eaf4314e9d_14" | ||
| }, | ||
| { | ||
| "type": "projection", | ||
| "value": "2d", | ||
| "texture_filename": "tex1_18x16_472b403cdcfc31a3_3" | ||
| }, | ||
| { | ||
| "type": "projection", | ||
| "value": "2d", | ||
| "texture_filename": "tex1_30x26_629956a45175e53a_14" | ||
| } | ||
| ] | ||
| }, | ||
| { | ||
| "name": "Bloom", | ||
| "targets": [ | ||
| { | ||
| "type": "efb", | ||
| "texture_filename": "efb1_n1_320x240_6" | ||
| } | ||
| ] | ||
| } | ||
| ] | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,19 @@ | ||
| { | ||
| "meta": | ||
| { | ||
| "title": "Bloom Texture Definitions", | ||
| "author": "iwubcode" | ||
| }, | ||
| "groups": | ||
| [ | ||
| { | ||
| "name": "Bloom", | ||
| "targets": [ | ||
| { | ||
| "type": "efb", | ||
| "texture_filename": "efb1_n49_152x114_6" | ||
| } | ||
| ] | ||
| } | ||
| ] | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,27 @@ | ||
| { | ||
| "meta": | ||
| { | ||
| "title": "Bloom Texture Definitions", | ||
| "author": "iwubcode" | ||
| }, | ||
| "groups": | ||
| [ | ||
| { | ||
| "name": "Bloom", | ||
| "targets": [ | ||
| { | ||
| "type": "efb", | ||
| "texture_filename": "efb1_n3_80x56_6" | ||
| }, | ||
| { | ||
| "type": "efb", | ||
| "texture_filename": "efb1_n2_160x112_6" | ||
| }, | ||
| { | ||
| "type": "efb", | ||
| "texture_filename": "efb1_n6_320x224_6" | ||
| } | ||
| ] | ||
| } | ||
| ] | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,19 @@ | ||
| { | ||
| "meta": | ||
| { | ||
| "title": "Bloom Texture Definitions", | ||
| "author": "iwubcode" | ||
| }, | ||
| "groups": | ||
| [ | ||
| { | ||
| "name": "Bloom", | ||
| "targets": [ | ||
| { | ||
| "type": "efb", | ||
| "texture_filename": "efb1_n000019_128x128_4" | ||
| } | ||
| ] | ||
| } | ||
| ] | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,19 @@ | ||
| { | ||
| "meta": | ||
| { | ||
| "title": "Bloom Texture Definitions", | ||
| "author": "iwubcode" | ||
| }, | ||
| "groups": | ||
| [ | ||
| { | ||
| "name": "Bloom", | ||
| "targets": [ | ||
| { | ||
| "type": "efb", | ||
| "texture_filename": "efb1_n51_320x240_6" | ||
| } | ||
| ] | ||
| } | ||
| ] | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,103 @@ | ||
| { | ||
| "meta": | ||
| { | ||
| "title": "Bloom and HUD Texture Definitions", | ||
| "author": "iwubcode" | ||
| }, | ||
| "groups": | ||
| [ | ||
| { | ||
| "name": "HUD", | ||
| "targets": [ | ||
| { | ||
| "type": "draw_started", | ||
| "pretty_name": "hp_rp", | ||
| "texture_filename": "tex1_200x64_29bf40765535b389_fb4403f0539ecfc6_9" | ||
| }, | ||
| { | ||
| "type": "projection", | ||
| "value": "2d", | ||
| "pretty_name": "hp_rp", | ||
| "texture_filename": "tex1_200x64_29bf40765535b389_fb4403f0539ecfc6_9" | ||
| }, | ||
| { | ||
| "type": "draw_started", | ||
| "pretty_name": "hp_gradient", | ||
| "texture_filename": "tex1_96x8_491977b196c249d8_1feafe410943bfac_8" | ||
| }, | ||
| { | ||
| "type": "projection", | ||
| "value": "2d", | ||
| "pretty_name": "hp_gradient", | ||
| "texture_filename": "tex1_96x8_491977b196c249d8_1feafe410943bfac_8" | ||
| }, | ||
| { | ||
| "type": "draw_started", | ||
| "pretty_name": "rp_gradient", | ||
| "texture_filename": "tex1_96x8_cdcb5c030686767c_2c5a8138bfca228c_8" | ||
| }, | ||
| { | ||
| "type": "projection", | ||
| "value": "2d", | ||
| "pretty_name": "rp_gradient", | ||
| "texture_filename": "tex1_96x8_cdcb5c030686767c_2c5a8138bfca228c_8" | ||
| }, | ||
| { | ||
| "type": "draw_started", | ||
| "pretty_name": "spring_season", | ||
| "texture_filename": "tex1_256x40_30d99f26895bc811_02e626cce31a83ae_9" | ||
| }, | ||
| { | ||
| "type": "projection", | ||
| "value": "2d", | ||
| "pretty_name": "spring_season", | ||
| "texture_filename": "tex1_256x40_30d99f26895bc811_02e626cce31a83ae_9" | ||
| }, | ||
| { | ||
| "type": "draw_started", | ||
| "pretty_name": "quick_pick_box", | ||
| "texture_filename": "tex1_128x128_b87c102764a80c67_0488ebdbd87cfc9d_9" | ||
| }, | ||
| { | ||
| "type": "projection", | ||
| "value": "2d", | ||
| "pretty_name": "quick_pick_box", | ||
| "texture_filename": "tex1_128x128_b87c102764a80c67_0488ebdbd87cfc9d_9" | ||
| }, | ||
| { | ||
| "type": "draw_started", | ||
| "pretty_name": "face", | ||
| "texture_filename": "tex1_48x48_92405f9277895cd2_914f5a4762aa04ae_9" | ||
| }, | ||
| { | ||
| "type": "projection", | ||
| "value": "2d", | ||
| "pretty_name": "face", | ||
| "texture_filename": "tex1_48x48_92405f9277895cd2_914f5a4762aa04ae_9" | ||
| }, | ||
| { | ||
| "type": "draw_started", | ||
| "pretty_name": "sunny_icon", | ||
| "texture_filename": "tex1_24x24_3791555ba7e8186f_e82e2316ceba262d_9" | ||
| }, | ||
| { | ||
| "type": "projection", | ||
| "value": "2d", | ||
| "pretty_name": "sunny_icon", | ||
| "texture_filename": "tex1_24x24_3791555ba7e8186f_e82e2316ceba262d_9" | ||
| }, | ||
| { | ||
| "type": "draw_started", | ||
| "pretty_name": "text", | ||
| "texture_filename": "tex1_256x256_83aa16840fa69ffb_0" | ||
| }, | ||
| { | ||
| "type": "projection", | ||
| "value": "2d", | ||
| "pretty_name": "text", | ||
| "texture_filename": "tex1_256x256_83aa16840fa69ffb_0" | ||
| } | ||
| ] | ||
| } | ||
| ] | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,27 @@ | ||
| { | ||
| "meta": | ||
| { | ||
| "title": "Bloom Texture Definitions", | ||
| "author": "iwubcode" | ||
| }, | ||
| "groups": | ||
| [ | ||
| { | ||
| "name": "Bloom", | ||
| "targets": [ | ||
| { | ||
| "type": "efb", | ||
| "texture_filename": "efb1_n000031_80x57_4" | ||
| }, | ||
| { | ||
| "type": "efb", | ||
| "texture_filename": "efb1_n000033_160x114_4" | ||
| }, | ||
| { | ||
| "type": "efb", | ||
| "texture_filename": "efb1_n000038_320x228_4" | ||
| } | ||
| ] | ||
| } | ||
| ] | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,31 @@ | ||
| { | ||
| "meta": | ||
| { | ||
| "title": "Bloom Texture Definitions", | ||
| "author": "iwubcode" | ||
| }, | ||
| "groups": | ||
| [ | ||
| { | ||
| "name": "Bloom", | ||
| "targets": [ | ||
| { | ||
| "type": "efb", | ||
| "texture_filename": "efb1_n001461_40x28_1" | ||
| }, | ||
| { | ||
| "type": "efb", | ||
| "texture_filename": "efb1_n001460_80x56_1" | ||
| }, | ||
| { | ||
| "type": "efb", | ||
| "texture_filename": "efb1_n001459_160x112_1" | ||
| }, | ||
| { | ||
| "type": "efb", | ||
| "texture_filename": "efb1_n001458_320x224_1" | ||
| } | ||
| ] | ||
| } | ||
| ] | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,31 @@ | ||
| { | ||
| "meta": | ||
| { | ||
| "title": "Bloom Texture Definitions", | ||
| "author": "iwubcode" | ||
| }, | ||
| "groups": | ||
| [ | ||
| { | ||
| "name": "Bloom", | ||
| "targets": [ | ||
| { | ||
| "type": "efb", | ||
| "texture_filename": "efb1_n000022_40x28_6" | ||
| }, | ||
| { | ||
| "type": "efb", | ||
| "texture_filename": "efb1_n000021_80x56_6" | ||
| }, | ||
| { | ||
| "type": "efb", | ||
| "texture_filename": "efb1_n000020_160x112_6" | ||
| }, | ||
| { | ||
| "type": "efb", | ||
| "texture_filename": "efb1_n000025_320x224_6" | ||
| } | ||
| ] | ||
| } | ||
| ] | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,23 @@ | ||
| { | ||
| "meta": | ||
| { | ||
| "title": "Bloom Texture Definitions", | ||
| "author": "iwubcode" | ||
| }, | ||
| "groups": | ||
| [ | ||
| { | ||
| "name": "Bloom", | ||
| "targets": [ | ||
| { | ||
| "type": "efb", | ||
| "texture_filename": "efb1_n55_80x57_6" | ||
| }, | ||
| { | ||
| "type": "efb", | ||
| "texture_filename": "efb1_n54_160x114_6" | ||
| } | ||
| ] | ||
| } | ||
| ] | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,31 @@ | ||
| { | ||
| "meta": | ||
| { | ||
| "title": "Bloom Texture Definitions", | ||
| "author": "iwubcode" | ||
| }, | ||
| "groups": | ||
| [ | ||
| { | ||
| "name": "Bloom", | ||
| "targets": [ | ||
| { | ||
| "type": "efb", | ||
| "texture_filename": "efb1_n15_20x16_4" | ||
| }, | ||
| { | ||
| "type": "efb", | ||
| "texture_filename": "efb1_n9_40x30_4" | ||
| }, | ||
| { | ||
| "type": "efb", | ||
| "texture_filename": "efb1_n7_80x58_4" | ||
| }, | ||
| { | ||
| "type": "efb", | ||
| "texture_filename": "efb1_n1_320x228_4" | ||
| } | ||
| ] | ||
| } | ||
| ] | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,279 @@ | ||
| // Copyright 2022 Dolphin Emulator Project | ||
| // SPDX-License-Identifier: GPL-2.0-or-later | ||
|
|
||
| #include "DolphinQt/Config/GraphicsModListWidget.h" | ||
|
|
||
| #include <QCheckBox> | ||
| #include <QHBoxLayout> | ||
| #include <QLabel> | ||
| #include <QListWidget> | ||
| #include <QPushButton> | ||
| #include <QVBoxLayout> | ||
| #include <QWidget> | ||
|
|
||
| #include <set> | ||
|
|
||
| #include "Core/ConfigManager.h" | ||
| #include "Core/Core.h" | ||
| #include "DolphinQt/Config/GraphicsModWarningWidget.h" | ||
| #include "DolphinQt/Settings.h" | ||
| #include "UICommon/GameFile.h" | ||
| #include "VideoCommon/GraphicsModSystem/Config/GraphicsMod.h" | ||
| #include "VideoCommon/VideoConfig.h" | ||
|
|
||
| GraphicsModListWidget::GraphicsModListWidget(const UICommon::GameFile& game) | ||
| : m_game_id(game.GetGameID()), m_mod_group(m_game_id) | ||
| { | ||
| CalculateGameRunning(Core::GetState()); | ||
| if (m_loaded_game_is_running && g_Config.graphics_mod_config) | ||
| { | ||
| m_mod_group.SetChangeCount(g_Config.graphics_mod_config->GetChangeCount()); | ||
| } | ||
| CreateWidgets(); | ||
| ConnectWidgets(); | ||
|
|
||
| RefreshModList(); | ||
| OnModChanged(std::nullopt); | ||
| } | ||
|
|
||
| GraphicsModListWidget::~GraphicsModListWidget() | ||
| { | ||
| if (m_needs_save) | ||
| { | ||
| m_mod_group.Save(); | ||
| } | ||
| } | ||
|
|
||
| void GraphicsModListWidget::CreateWidgets() | ||
| { | ||
| auto* main_v_layout = new QVBoxLayout(this); | ||
|
|
||
| auto* main_layout = new QHBoxLayout; | ||
|
|
||
| auto* left_v_layout = new QVBoxLayout; | ||
|
|
||
| m_mod_list = new QListWidget; | ||
| m_mod_list->setSortingEnabled(false); | ||
| m_mod_list->setSelectionBehavior(QAbstractItemView::SelectionBehavior::SelectItems); | ||
| m_mod_list->setSelectionMode(QAbstractItemView::SelectionMode::SingleSelection); | ||
| m_mod_list->setSelectionRectVisible(true); | ||
| m_mod_list->setDragDropMode(QAbstractItemView::InternalMove); | ||
|
|
||
| m_refresh = new QPushButton(tr("&Refresh List")); | ||
| QHBoxLayout* hlayout = new QHBoxLayout; | ||
| hlayout->addStretch(); | ||
| hlayout->addWidget(m_refresh); | ||
|
|
||
| left_v_layout->addWidget(m_mod_list); | ||
| left_v_layout->addLayout(hlayout); | ||
|
|
||
| auto* right_v_layout = new QVBoxLayout; | ||
|
|
||
| m_selected_mod_name = new QLabel(); | ||
| right_v_layout->addWidget(m_selected_mod_name); | ||
|
|
||
| m_mod_meta_layout = new QVBoxLayout; | ||
| right_v_layout->addLayout(m_mod_meta_layout); | ||
| right_v_layout->addStretch(); | ||
|
|
||
| main_layout->addLayout(left_v_layout); | ||
| main_layout->addLayout(right_v_layout, 1); | ||
|
|
||
| m_warning = new GraphicsModWarningWidget(this); | ||
| main_v_layout->addWidget(m_warning); | ||
| main_v_layout->addLayout(main_layout); | ||
|
|
||
| setLayout(main_v_layout); | ||
| } | ||
|
|
||
| void GraphicsModListWidget::ConnectWidgets() | ||
| { | ||
| connect(m_warning, &GraphicsModWarningWidget::GraphicsModEnableSettings, this, | ||
| &GraphicsModListWidget::OpenGraphicsSettings); | ||
|
|
||
| connect(m_mod_list, &QListWidget::itemSelectionChanged, this, | ||
| &GraphicsModListWidget::ModSelectionChanged); | ||
|
|
||
| connect(m_mod_list, &QListWidget::itemChanged, this, &GraphicsModListWidget::ModItemChanged); | ||
|
|
||
| connect(m_mod_list->model(), &QAbstractItemModel::rowsMoved, this, | ||
| &GraphicsModListWidget::SaveModList); | ||
|
|
||
| connect(m_refresh, &QPushButton::clicked, this, &GraphicsModListWidget::RefreshModList); | ||
|
|
||
| connect(&Settings::Instance(), &Settings::EmulationStateChanged, this, | ||
| &GraphicsModListWidget::CalculateGameRunning); | ||
| } | ||
|
|
||
| void GraphicsModListWidget::RefreshModList() | ||
| { | ||
| m_mod_list->setCurrentItem(nullptr); | ||
| m_mod_list->clear(); | ||
|
|
||
| m_mod_group = GraphicsModGroupConfig(m_game_id); | ||
| m_mod_group.Load(); | ||
|
|
||
| std::set<std::string> groups; | ||
|
|
||
| for (const auto& mod : m_mod_group.GetMods()) | ||
| { | ||
| if (mod.m_groups.empty()) | ||
| continue; | ||
|
|
||
| for (const auto& group : mod.m_groups) | ||
| { | ||
| groups.insert(group.m_name); | ||
| } | ||
| } | ||
|
|
||
| for (const auto& mod : m_mod_group.GetMods()) | ||
| { | ||
| // Group only mods shouldn't be shown | ||
| if (mod.m_features.empty()) | ||
| continue; | ||
|
|
||
| // If the group doesn't exist in the available mod's features, skip | ||
| if (std::none_of(mod.m_features.begin(), mod.m_features.end(), | ||
| [&groups](const GraphicsModFeatureConfig& feature) { | ||
| return groups.count(feature.m_group) == 1; | ||
| })) | ||
| { | ||
| continue; | ||
| } | ||
|
|
||
| QListWidgetItem* item = new QListWidgetItem(QString::fromStdString(mod.m_title)); | ||
| item->setFlags(item->flags() | Qt::ItemIsUserCheckable); | ||
| item->setData(Qt::UserRole, QString::fromStdString(mod.GetAbsolutePath())); | ||
| item->setCheckState(mod.m_enabled ? Qt::Checked : Qt::Unchecked); | ||
|
|
||
| m_mod_list->addItem(item); | ||
| } | ||
| } | ||
|
|
||
| void GraphicsModListWidget::ModSelectionChanged() | ||
| { | ||
| if (m_mod_list->currentItem() == nullptr) | ||
| return; | ||
| if (m_mod_list->count() == 0) | ||
| return; | ||
| const auto absolute_path = m_mod_list->currentItem()->data(Qt::UserRole).toString().toStdString(); | ||
| OnModChanged(absolute_path); | ||
| } | ||
|
|
||
| void GraphicsModListWidget::ModItemChanged(QListWidgetItem* item) | ||
| { | ||
| const auto absolute_path = item->data(Qt::UserRole).toString(); | ||
| GraphicsModConfig* mod = m_mod_group.GetMod(absolute_path.toStdString()); | ||
| if (!mod) | ||
| return; | ||
|
|
||
| const bool was_enabled = mod->m_enabled; | ||
| const bool should_enable = item->checkState() == Qt::Checked; | ||
| mod->m_enabled = should_enable; | ||
| if (was_enabled == should_enable) | ||
| return; | ||
|
|
||
| m_mod_group.SetChangeCount(m_mod_group.GetChangeCount() + 1); | ||
| if (m_loaded_game_is_running) | ||
| { | ||
| g_Config.graphics_mod_config = m_mod_group; | ||
| } | ||
| m_needs_save = true; | ||
| } | ||
|
|
||
| void GraphicsModListWidget::OnModChanged(std::optional<std::string> absolute_path) | ||
| { | ||
| ClearLayoutRecursively(m_mod_meta_layout); | ||
|
|
||
| adjustSize(); | ||
|
|
||
| if (!absolute_path) | ||
| { | ||
| m_selected_mod_name->setText(QStringLiteral("No graphics mod selected")); | ||
| m_selected_mod_name->setAlignment(Qt::AlignCenter); | ||
| return; | ||
| } | ||
|
|
||
| GraphicsModConfig* mod = m_mod_group.GetMod(*absolute_path); | ||
| if (!mod) | ||
| return; | ||
|
|
||
| m_selected_mod_name->setText(QString::fromStdString(mod->m_title)); | ||
| m_selected_mod_name->setAlignment(Qt::AlignLeft); | ||
| QFont font = m_selected_mod_name->font(); | ||
| font.setWeight(QFont::Bold); | ||
| m_selected_mod_name->setFont(font); | ||
|
|
||
| if (!mod->m_author.empty()) | ||
| { | ||
| auto* author_label = new QLabel(tr("By: ") + QString::fromStdString(mod->m_author)); | ||
| m_mod_meta_layout->addWidget(author_label); | ||
| } | ||
|
|
||
| if (!mod->m_description.empty()) | ||
| { | ||
| auto* description_label = | ||
| new QLabel(tr("Description: ") + QString::fromStdString(mod->m_description)); | ||
| m_mod_meta_layout->addWidget(description_label); | ||
| } | ||
| } | ||
|
|
||
| void GraphicsModListWidget::SaveModList() | ||
| { | ||
| for (int i = 0; i < m_mod_list->count(); i++) | ||
| { | ||
| const auto absolute_path = m_mod_list->model() | ||
| ->data(m_mod_list->model()->index(i, 0), Qt::UserRole) | ||
| .toString() | ||
| .toStdString(); | ||
| m_mod_group.GetMod(absolute_path)->m_weight = i; | ||
| } | ||
|
|
||
| if (m_loaded_game_is_running) | ||
| { | ||
| g_Config.graphics_mod_config = m_mod_group; | ||
| } | ||
| m_needs_save = true; | ||
| } | ||
|
|
||
| void GraphicsModListWidget::ClearLayoutRecursively(QLayout* layout) | ||
| { | ||
| while (QLayoutItem* child = layout->takeAt(0)) | ||
| { | ||
| if (child == nullptr) | ||
| continue; | ||
|
|
||
| if (child->widget()) | ||
| { | ||
| layout->removeWidget(child->widget()); | ||
| delete child->widget(); | ||
| } | ||
| else if (child->layout()) | ||
| { | ||
| ClearLayoutRecursively(child->layout()); | ||
| layout->removeItem(child); | ||
| } | ||
| else | ||
| { | ||
| layout->removeItem(child); | ||
| } | ||
| delete child; | ||
| } | ||
| } | ||
|
|
||
| void GraphicsModListWidget::SaveToDisk() | ||
| { | ||
| m_needs_save = false; | ||
| m_mod_group.Save(); | ||
| } | ||
|
|
||
| const GraphicsModGroupConfig& GraphicsModListWidget::GetGraphicsModConfig() const | ||
| { | ||
| return m_mod_group; | ||
| } | ||
|
|
||
| void GraphicsModListWidget::CalculateGameRunning(Core::State state) | ||
| { | ||
| m_loaded_game_is_running = | ||
| state == Core::State::Running ? m_game_id == SConfig::GetInstance().GetGameID() : false; | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,75 @@ | ||
| // Copyright 2022 Dolphin Emulator Project | ||
| // SPDX-License-Identifier: GPL-2.0-or-later | ||
|
|
||
| #pragma once | ||
|
|
||
| #include <optional> | ||
| #include <string> | ||
|
|
||
| #include <QWidget> | ||
|
|
||
| #include "Common/CommonTypes.h" | ||
| #include "VideoCommon/GraphicsModSystem/Config/GraphicsModGroup.h" | ||
|
|
||
| class GraphicsModWarningWidget; | ||
| class QHBoxLayout; | ||
| class QLabel; | ||
| class QListWidget; | ||
| class QListWidgetItem; | ||
| class QModelIndex; | ||
| class QPushButton; | ||
| class QVBoxLayout; | ||
|
|
||
| namespace Core | ||
| { | ||
| enum class State; | ||
| } | ||
|
|
||
| namespace UICommon | ||
| { | ||
| class GameFile; | ||
| } | ||
|
|
||
| class GraphicsModListWidget : public QWidget | ||
| { | ||
| Q_OBJECT | ||
| public: | ||
| explicit GraphicsModListWidget(const UICommon::GameFile& game); | ||
| ~GraphicsModListWidget(); | ||
|
|
||
| void SaveToDisk(); | ||
|
|
||
| const GraphicsModGroupConfig& GetGraphicsModConfig() const; | ||
|
|
||
| signals: | ||
| void OpenGraphicsSettings(); | ||
|
|
||
| private: | ||
| void CreateWidgets(); | ||
| void ConnectWidgets(); | ||
|
|
||
| void RefreshModList(); | ||
| void ModSelectionChanged(); | ||
| void ModItemChanged(QListWidgetItem* item); | ||
|
|
||
| void OnModChanged(std::optional<std::string> absolute_path); | ||
|
|
||
| void SaveModList(); | ||
|
|
||
| void ClearLayoutRecursively(QLayout* layout); | ||
|
|
||
| void CalculateGameRunning(Core::State state); | ||
| bool m_loaded_game_is_running = false; | ||
| bool m_needs_save = false; | ||
|
|
||
| QListWidget* m_mod_list; | ||
|
|
||
| QLabel* m_selected_mod_name; | ||
| QVBoxLayout* m_mod_meta_layout; | ||
|
|
||
| QPushButton* m_refresh; | ||
| GraphicsModWarningWidget* m_warning; | ||
|
|
||
| std::string m_game_id; | ||
| GraphicsModGroupConfig m_mod_group; | ||
| }; |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,70 @@ | ||
| // Copyright 2022 Dolphin Emulator Project | ||
| // SPDX-License-Identifier: GPL-2.0-or-later | ||
|
|
||
| #include "DolphinQt/Config/GraphicsModWarningWidget.h" | ||
|
|
||
| #include <QHBoxLayout> | ||
| #include <QLabel> | ||
| #include <QPixmap> | ||
| #include <QPushButton> | ||
| #include <QStyle> | ||
|
|
||
| #include "DolphinQt/Settings.h" | ||
|
|
||
| GraphicsModWarningWidget::GraphicsModWarningWidget(QWidget* parent) : QWidget(parent) | ||
| { | ||
| CreateWidgets(); | ||
| ConnectWidgets(); | ||
|
|
||
| connect(&Settings::Instance(), &Settings::EnableGfxModsChanged, this, | ||
| &GraphicsModWarningWidget::Update); | ||
| Update(); | ||
| } | ||
|
|
||
| void GraphicsModWarningWidget::CreateWidgets() | ||
| { | ||
| auto* icon = new QLabel; | ||
|
|
||
| const auto size = 1.5 * QFontMetrics(font()).height(); | ||
|
|
||
| QPixmap warning_icon = style()->standardIcon(QStyle::SP_MessageBoxWarning).pixmap(size, size); | ||
|
|
||
| icon->setPixmap(warning_icon); | ||
|
|
||
| m_text = new QLabel(); | ||
| m_config_button = new QPushButton(tr("Configure Dolphin")); | ||
|
|
||
| m_config_button->setHidden(true); | ||
|
|
||
| auto* layout = new QHBoxLayout; | ||
|
|
||
| layout->addWidget(icon); | ||
| layout->addWidget(m_text, 1); | ||
| layout->addWidget(m_config_button); | ||
|
|
||
| layout->setContentsMargins(0, 0, 0, 0); | ||
|
|
||
| setLayout(layout); | ||
| } | ||
|
|
||
| void GraphicsModWarningWidget::Update() | ||
| { | ||
| bool hide_widget = true; | ||
| bool hide_config_button = true; | ||
|
|
||
| if (!Settings::Instance().GetGraphicModsEnabled()) | ||
| { | ||
| hide_widget = false; | ||
| hide_config_button = false; | ||
| m_text->setText(tr("Graphics mods are currently disabled.")); | ||
| } | ||
|
|
||
| setHidden(hide_widget); | ||
| m_config_button->setHidden(hide_config_button); | ||
| } | ||
|
|
||
| void GraphicsModWarningWidget::ConnectWidgets() | ||
| { | ||
| connect(m_config_button, &QPushButton::clicked, this, | ||
| &GraphicsModWarningWidget::GraphicsModEnableSettings); | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,28 @@ | ||
| // Copyright 2022 Dolphin Emulator Project | ||
| // SPDX-License-Identifier: GPL-2.0-or-later | ||
|
|
||
| #pragma once | ||
|
|
||
| #include <QWidget> | ||
|
|
||
| class QLabel; | ||
| class QPushButton; | ||
|
|
||
| class GraphicsModWarningWidget final : public QWidget | ||
| { | ||
| Q_OBJECT | ||
| public: | ||
| explicit GraphicsModWarningWidget(QWidget* parent); | ||
|
|
||
| signals: | ||
| void GraphicsModEnableSettings(); | ||
|
|
||
| private: | ||
| void CreateWidgets(); | ||
| void ConnectWidgets(); | ||
|
|
||
| void Update(); | ||
|
|
||
| QLabel* m_text; | ||
| QPushButton* m_config_button; | ||
| }; |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,291 @@ | ||
| // Copyright 2022 Dolphin Emulator Project | ||
| // SPDX-License-Identifier: GPL-2.0-or-later | ||
|
|
||
| #include "VideoCommon/GraphicsModSystem/Config/GraphicsMod.h" | ||
|
|
||
| #include <fmt/format.h> | ||
|
|
||
| #include "Common/CommonPaths.h" | ||
| #include "Common/FileUtil.h" | ||
| #include "Common/Logging/Log.h" | ||
| #include "Common/StringUtil.h" | ||
|
|
||
| #include "VideoCommon/GraphicsModSystem/Constants.h" | ||
|
|
||
| std::optional<GraphicsModConfig> GraphicsModConfig::Create(const std::string& file_path, | ||
| Source source) | ||
| { | ||
| std::string json_data; | ||
| if (!File::ReadFileToString(file_path, json_data)) | ||
| { | ||
| ERROR_LOG_FMT(VIDEO, "Failed to load graphics mod json file '{}'", file_path); | ||
| return std::nullopt; | ||
| } | ||
|
|
||
| picojson::value root; | ||
| const auto error = picojson::parse(root, json_data); | ||
|
|
||
| if (!error.empty()) | ||
| { | ||
| ERROR_LOG_FMT(VIDEO, "Failed to load graphics mod json file '{}' due to parse error: {}", | ||
| file_path, error); | ||
| return std::nullopt; | ||
| } | ||
|
|
||
| GraphicsModConfig result; | ||
| if (!result.DeserializeFromConfig(root)) | ||
| { | ||
| return std::nullopt; | ||
| } | ||
| result.m_source = source; | ||
| if (source == Source::User) | ||
| { | ||
| const std::string base_path = File::GetUserPath(D_GRAPHICSMOD_IDX); | ||
| if (base_path.size() > file_path.size()) | ||
| { | ||
| ERROR_LOG_FMT( | ||
| VIDEO, | ||
| "Failed to load graphics mod json file '{}' due to it not matching the base path: {}", | ||
| file_path, base_path); | ||
| return std::nullopt; | ||
| } | ||
| result.m_relative_path = file_path.substr(base_path.size()); | ||
| } | ||
| else | ||
| { | ||
| const std::string base_path = File::GetSysDirectory() + DOLPHIN_SYSTEM_GRAPHICS_MOD_DIR; | ||
| if (base_path.size() > file_path.size()) | ||
| { | ||
| ERROR_LOG_FMT( | ||
| VIDEO, | ||
| "Failed to load graphics mod json file '{}' due to it not matching the base path: {}", | ||
| file_path, base_path); | ||
| return std::nullopt; | ||
| } | ||
| result.m_relative_path = file_path.substr(base_path.size()); | ||
| } | ||
|
|
||
| return result; | ||
| } | ||
|
|
||
| std::optional<GraphicsModConfig> GraphicsModConfig::Create(const picojson::object* obj) | ||
| { | ||
| if (!obj) | ||
| return std::nullopt; | ||
|
|
||
| const auto source_it = obj->find("source"); | ||
| if (source_it == obj->end()) | ||
| { | ||
| return std::nullopt; | ||
| } | ||
| const std::string source_str = source_it->second.to_str(); | ||
|
|
||
| const auto path_it = obj->find("path"); | ||
| if (path_it == obj->end()) | ||
| { | ||
| return std::nullopt; | ||
| } | ||
| const std::string relative_path = path_it->second.to_str(); | ||
|
|
||
| if (source_str == "system") | ||
| { | ||
| return Create(fmt::format("{}{}{}", File::GetSysDirectory(), DOLPHIN_SYSTEM_GRAPHICS_MOD_DIR, | ||
| relative_path), | ||
| Source::System); | ||
| } | ||
| else | ||
| { | ||
| return Create(File::GetUserPath(D_GRAPHICSMOD_IDX) + relative_path, Source::User); | ||
| } | ||
| } | ||
|
|
||
| std::string GraphicsModConfig::GetAbsolutePath() const | ||
| { | ||
| if (m_source == Source::System) | ||
| { | ||
| return WithUnifiedPathSeparators(fmt::format("{}{}{}", File::GetSysDirectory(), | ||
| DOLPHIN_SYSTEM_GRAPHICS_MOD_DIR, m_relative_path)); | ||
| } | ||
| else | ||
| { | ||
| return WithUnifiedPathSeparators(File::GetUserPath(D_GRAPHICSMOD_IDX) + m_relative_path); | ||
| } | ||
| } | ||
|
|
||
| bool GraphicsModConfig::DeserializeFromConfig(const picojson::value& value) | ||
| { | ||
| const auto& meta = value.get("meta"); | ||
| if (meta.is<picojson::object>()) | ||
| { | ||
| const auto& title = meta.get("title"); | ||
| if (title.is<std::string>()) | ||
| { | ||
| m_title = title.to_str(); | ||
| } | ||
|
|
||
| const auto& author = meta.get("author"); | ||
| if (author.is<std::string>()) | ||
| { | ||
| m_author = author.to_str(); | ||
| } | ||
|
|
||
| const auto& description = meta.get("description"); | ||
| if (description.is<std::string>()) | ||
| { | ||
| m_description = description.to_str(); | ||
| } | ||
| } | ||
|
|
||
| const auto& groups = value.get("groups"); | ||
| if (groups.is<picojson::array>()) | ||
| { | ||
| for (const auto& group_val : groups.get<picojson::array>()) | ||
| { | ||
| if (!group_val.is<picojson::object>()) | ||
| { | ||
| ERROR_LOG_FMT( | ||
| VIDEO, "Failed to load mod configuration file, specified group is not a json object"); | ||
| return false; | ||
| } | ||
| GraphicsTargetGroupConfig group; | ||
| if (!group.DeserializeFromConfig(group_val.get<picojson::object>())) | ||
| { | ||
| return false; | ||
| } | ||
|
|
||
| m_groups.push_back(group); | ||
| } | ||
| } | ||
|
|
||
| const auto& features = value.get("features"); | ||
| if (features.is<picojson::array>()) | ||
| { | ||
| for (const auto& feature_val : features.get<picojson::array>()) | ||
| { | ||
| if (!feature_val.is<picojson::object>()) | ||
| { | ||
| ERROR_LOG_FMT( | ||
| VIDEO, "Failed to load mod configuration file, specified feature is not a json object"); | ||
| return false; | ||
| } | ||
| GraphicsModFeatureConfig feature; | ||
| if (!feature.DeserializeFromConfig(feature_val.get<picojson::object>())) | ||
| { | ||
| return false; | ||
| } | ||
|
|
||
| m_features.push_back(feature); | ||
| } | ||
| } | ||
|
|
||
| return true; | ||
| } | ||
|
|
||
| void GraphicsModConfig::SerializeToProfile(picojson::object* obj) const | ||
| { | ||
| if (!obj) | ||
| return; | ||
|
|
||
| auto& json_obj = *obj; | ||
| switch (m_source) | ||
| { | ||
| case Source::User: | ||
| { | ||
| json_obj["source"] = picojson::value{"user"}; | ||
| } | ||
| break; | ||
| case Source::System: | ||
| { | ||
| json_obj["source"] = picojson::value{"system"}; | ||
| } | ||
| break; | ||
| }; | ||
|
|
||
| json_obj["path"] = picojson::value{m_relative_path}; | ||
|
|
||
| picojson::array serialized_groups; | ||
| for (const auto& group : m_groups) | ||
| { | ||
| picojson::object serialized_group; | ||
| group.SerializeToProfile(&serialized_group); | ||
| serialized_groups.push_back(picojson::value{serialized_group}); | ||
| } | ||
| json_obj["groups"] = picojson::value{serialized_groups}; | ||
|
|
||
| picojson::array serialized_features; | ||
| for (const auto& feature : m_features) | ||
| { | ||
| picojson::object serialized_feature; | ||
| feature.SerializeToProfile(&serialized_feature); | ||
| serialized_features.push_back(picojson::value{serialized_feature}); | ||
| } | ||
| json_obj["features"] = picojson::value{serialized_features}; | ||
|
|
||
| json_obj["enabled"] = picojson::value{m_enabled}; | ||
|
|
||
| json_obj["weight"] = picojson::value{static_cast<double>(m_weight)}; | ||
| } | ||
|
|
||
| void GraphicsModConfig::DeserializeFromProfile(const picojson::object& obj) | ||
| { | ||
| if (const auto it = obj.find("groups"); it != obj.end()) | ||
| { | ||
| if (it->second.is<picojson::array>()) | ||
| { | ||
| auto serialized_groups = it->second.get<picojson::array>(); | ||
| if (serialized_groups.size() != m_groups.size()) | ||
| return; | ||
|
|
||
| for (std::size_t i = 0; i < serialized_groups.size(); i++) | ||
| { | ||
| const auto& serialized_group_val = serialized_groups[i]; | ||
| if (serialized_group_val.is<picojson::object>()) | ||
| { | ||
| const auto& serialized_group = serialized_group_val.get<picojson::object>(); | ||
| m_groups[i].DeserializeFromProfile(serialized_group); | ||
| } | ||
| } | ||
| } | ||
| } | ||
|
|
||
| if (const auto it = obj.find("features"); it != obj.end()) | ||
| { | ||
| if (it->second.is<picojson::array>()) | ||
| { | ||
| auto serialized_features = it->second.get<picojson::array>(); | ||
| if (serialized_features.size() != m_features.size()) | ||
| return; | ||
|
|
||
| for (std::size_t i = 0; i < serialized_features.size(); i++) | ||
| { | ||
| const auto& serialized_feature_val = serialized_features[i]; | ||
| if (serialized_feature_val.is<picojson::object>()) | ||
| { | ||
| const auto& serialized_feature = serialized_feature_val.get<picojson::object>(); | ||
| m_features[i].DeserializeFromProfile(serialized_feature); | ||
| } | ||
| } | ||
| } | ||
| } | ||
|
|
||
| if (const auto it = obj.find("enabled"); it != obj.end()) | ||
| { | ||
| if (it->second.is<bool>()) | ||
| { | ||
| m_enabled = it->second.get<bool>(); | ||
| } | ||
| } | ||
|
|
||
| if (const auto it = obj.find("weight"); it != obj.end()) | ||
| { | ||
| if (it->second.is<double>()) | ||
| { | ||
| m_weight = static_cast<u16>(it->second.get<double>()); | ||
| } | ||
| } | ||
| } | ||
|
|
||
| bool GraphicsModConfig::operator<(const GraphicsModConfig& other) const | ||
| { | ||
| return m_weight < other.m_weight; | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,45 @@ | ||
| // Copyright 2022 Dolphin Emulator Project | ||
| // SPDX-License-Identifier: GPL-2.0-or-later | ||
|
|
||
| #pragma once | ||
|
|
||
| #include <optional> | ||
| #include <string> | ||
| #include <vector> | ||
|
|
||
| #include <picojson.h> | ||
|
|
||
| #include "VideoCommon/GraphicsModSystem/Config/GraphicsModFeature.h" | ||
| #include "VideoCommon/GraphicsModSystem/Config/GraphicsTargetGroup.h" | ||
|
|
||
| struct GraphicsModConfig | ||
| { | ||
| std::string m_title; | ||
| std::string m_author; | ||
| std::string m_description; | ||
| bool m_enabled = false; | ||
| u16 m_weight = 0; | ||
| std::string m_relative_path; | ||
|
|
||
| enum class Source | ||
| { | ||
| User, | ||
| System | ||
| }; | ||
| Source m_source = Source::User; | ||
|
|
||
| std::vector<GraphicsTargetGroupConfig> m_groups; | ||
| std::vector<GraphicsModFeatureConfig> m_features; | ||
|
|
||
| static std::optional<GraphicsModConfig> Create(const std::string& file, Source source); | ||
| static std::optional<GraphicsModConfig> Create(const picojson::object* obj); | ||
|
|
||
| std::string GetAbsolutePath() const; | ||
|
|
||
| bool DeserializeFromConfig(const picojson::value& value); | ||
|
|
||
| void SerializeToProfile(picojson::object* value) const; | ||
| void DeserializeFromProfile(const picojson::object& value); | ||
|
|
||
| bool operator<(const GraphicsModConfig& other) const; | ||
| }; |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,48 @@ | ||
| // Copyright 2022 Dolphin Emulator Project | ||
| // SPDX-License-Identifier: GPL-2.0-or-later | ||
|
|
||
| #include "VideoCommon/GraphicsModSystem/Config/GraphicsModFeature.h" | ||
|
|
||
| #include "Common/Logging/Log.h" | ||
|
|
||
| bool GraphicsModFeatureConfig::DeserializeFromConfig(const picojson::object& obj) | ||
| { | ||
| if (auto group_iter = obj.find("group"); group_iter != obj.end()) | ||
| { | ||
| if (!group_iter->second.is<std::string>()) | ||
| { | ||
| ERROR_LOG_FMT( | ||
| VIDEO, | ||
| "Failed to load mod configuration file, specified feature's group is not a string"); | ||
| return false; | ||
| } | ||
| m_group = group_iter->second.get<std::string>(); | ||
| } | ||
|
|
||
| if (auto action_iter = obj.find("action"); action_iter != obj.end()) | ||
| { | ||
| if (!action_iter->second.is<std::string>()) | ||
| { | ||
| ERROR_LOG_FMT( | ||
| VIDEO, | ||
| "Failed to load mod configuration file, specified feature's action is not a string"); | ||
| return false; | ||
| } | ||
| m_action = action_iter->second.get<std::string>(); | ||
| } | ||
|
|
||
| if (auto action_data_iter = obj.find("action_data"); action_data_iter != obj.end()) | ||
| { | ||
| m_action_data = action_data_iter->second; | ||
| } | ||
|
|
||
| return true; | ||
| } | ||
|
|
||
| void GraphicsModFeatureConfig::SerializeToProfile(picojson::object*) const | ||
| { | ||
| } | ||
|
|
||
| void GraphicsModFeatureConfig::DeserializeFromProfile(const picojson::object&) | ||
| { | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,20 @@ | ||
| // Copyright 2022 Dolphin Emulator Project | ||
| // SPDX-License-Identifier: GPL-2.0-or-later | ||
|
|
||
| #pragma once | ||
|
|
||
| #include <string> | ||
|
|
||
| #include <picojson.h> | ||
|
|
||
| struct GraphicsModFeatureConfig | ||
| { | ||
| std::string m_group; | ||
| std::string m_action; | ||
| picojson::value m_action_data; | ||
|
|
||
| bool DeserializeFromConfig(const picojson::object& value); | ||
|
|
||
| void SerializeToProfile(picojson::object* value) const; | ||
| void DeserializeFromProfile(const picojson::object& value); | ||
| }; |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,191 @@ | ||
| // Copyright 2022 Dolphin Emulator Project | ||
| // SPDX-License-Identifier: GPL-2.0-or-later | ||
|
|
||
| #include "VideoCommon/GraphicsModSystem/Config/GraphicsModGroup.h" | ||
|
|
||
| #include <map> | ||
| #include <sstream> | ||
| #include <string> | ||
|
|
||
| #include "Common/CommonPaths.h" | ||
| #include "Common/FileSearch.h" | ||
| #include "Common/FileUtil.h" | ||
| #include "Common/Logging/Log.h" | ||
| #include "Common/StringUtil.h" | ||
| #include "Core/ConfigManager.h" | ||
|
|
||
| #include "VideoCommon/GraphicsModSystem/Config/GraphicsMod.h" | ||
| #include "VideoCommon/GraphicsModSystem/Constants.h" | ||
| #include "VideoCommon/HiresTextures.h" | ||
|
|
||
| GraphicsModGroupConfig::GraphicsModGroupConfig(const std::string& game_id) : m_game_id(game_id) | ||
| { | ||
| } | ||
|
|
||
| GraphicsModGroupConfig::~GraphicsModGroupConfig() = default; | ||
|
|
||
| GraphicsModGroupConfig::GraphicsModGroupConfig(const GraphicsModGroupConfig&) = default; | ||
|
|
||
| GraphicsModGroupConfig::GraphicsModGroupConfig(GraphicsModGroupConfig&&) = default; | ||
|
|
||
| GraphicsModGroupConfig& GraphicsModGroupConfig::operator=(const GraphicsModGroupConfig&) = default; | ||
|
|
||
| GraphicsModGroupConfig& GraphicsModGroupConfig::operator=(GraphicsModGroupConfig&&) = default; | ||
|
|
||
| void GraphicsModGroupConfig::Load() | ||
| { | ||
| const std::string file_path = GetPath(); | ||
|
|
||
| std::set<std::string> known_paths; | ||
| if (File::Exists(file_path)) | ||
| { | ||
| std::string json_data; | ||
| if (!File::ReadFileToString(file_path, json_data)) | ||
| { | ||
| ERROR_LOG_FMT(VIDEO, "Failed to load graphics mod group json file '{}'", file_path); | ||
| return; | ||
| } | ||
|
|
||
| picojson::value root; | ||
| const auto error = picojson::parse(root, json_data); | ||
|
|
||
| if (!error.empty()) | ||
| { | ||
| ERROR_LOG_FMT(VIDEO, | ||
| "Failed to load graphics mod group json file '{}' due to parse error: {}", | ||
| file_path, error); | ||
| return; | ||
| } | ||
| if (!root.is<picojson::object>()) | ||
| { | ||
| ERROR_LOG_FMT( | ||
| VIDEO, | ||
| "Failed to load graphics mod group json file '{}' due to root not being an object!", | ||
| file_path); | ||
| return; | ||
| } | ||
|
|
||
| const auto& mods = root.get("mods"); | ||
| if (mods.is<picojson::array>()) | ||
| { | ||
| for (const auto& mod_json : mods.get<picojson::array>()) | ||
| { | ||
| if (mod_json.is<picojson::object>()) | ||
| { | ||
| const auto& mod_json_obj = mod_json.get<picojson::object>(); | ||
| auto graphics_mod = GraphicsModConfig::Create(&mod_json_obj); | ||
| if (!graphics_mod) | ||
| { | ||
| continue; | ||
| } | ||
| graphics_mod->DeserializeFromProfile(mod_json_obj); | ||
|
|
||
| auto mod_full_path = graphics_mod->GetAbsolutePath(); | ||
| known_paths.insert(std::move(mod_full_path)); | ||
| m_graphics_mods.push_back(*graphics_mod); | ||
| } | ||
| } | ||
| } | ||
| } | ||
|
|
||
| const auto try_add_mod = [&known_paths, this](const std::string& dir, | ||
| GraphicsModConfig::Source source) { | ||
| auto file = dir + DIR_SEP + "metadata.json"; | ||
| UnifyPathSeparators(file); | ||
| if (known_paths.find(file) != known_paths.end()) | ||
| { | ||
| return; | ||
| } | ||
| const auto mod = GraphicsModConfig::Create(file, source); | ||
| if (mod) | ||
| { | ||
| m_graphics_mods.push_back(*mod); | ||
| } | ||
| }; | ||
|
|
||
| const std::set<std::string> graphics_mod_user_directories = | ||
| GetTextureDirectoriesWithGameId(File::GetUserPath(D_GRAPHICSMOD_IDX), m_game_id); | ||
|
|
||
| for (const auto& graphics_mod_directory : graphics_mod_user_directories) | ||
| { | ||
| try_add_mod(graphics_mod_directory, GraphicsModConfig::Source::User); | ||
| } | ||
|
|
||
| const std::set<std::string> graphics_mod_system_directories = GetTextureDirectoriesWithGameId( | ||
| File::GetSysDirectory() + DOLPHIN_SYSTEM_GRAPHICS_MOD_DIR, m_game_id); | ||
|
|
||
| for (const auto& graphics_mod_directory : graphics_mod_system_directories) | ||
| { | ||
| try_add_mod(graphics_mod_directory, GraphicsModConfig::Source::System); | ||
| } | ||
|
|
||
| std::sort(m_graphics_mods.begin(), m_graphics_mods.end()); | ||
| for (auto& mod : m_graphics_mods) | ||
| { | ||
| m_path_to_graphics_mod[mod.GetAbsolutePath()] = &mod; | ||
| } | ||
|
|
||
| m_change_count++; | ||
| } | ||
|
|
||
| void GraphicsModGroupConfig::Save() const | ||
| { | ||
| const std::string file_path = GetPath(); | ||
| std::ofstream json_stream; | ||
| File::OpenFStream(json_stream, file_path, std::ios_base::out); | ||
| if (!json_stream.is_open()) | ||
| { | ||
| ERROR_LOG_FMT(VIDEO, "Failed to open graphics mod group json file '{}' for writing", file_path); | ||
| return; | ||
| } | ||
|
|
||
| picojson::object serialized_root; | ||
| picojson::array serialized_mods; | ||
| for (const auto& mod : m_graphics_mods) | ||
| { | ||
| picojson::object serialized_mod; | ||
| mod.SerializeToProfile(&serialized_mod); | ||
| serialized_mods.push_back(picojson::value{serialized_mod}); | ||
| } | ||
| serialized_root["mods"] = picojson::value{serialized_mods}; | ||
|
|
||
| const auto output = picojson::value{serialized_root}.serialize(true); | ||
| json_stream << output; | ||
| } | ||
|
|
||
| void GraphicsModGroupConfig::SetChangeCount(u32 change_count) | ||
| { | ||
| m_change_count = change_count; | ||
| } | ||
|
|
||
| u32 GraphicsModGroupConfig::GetChangeCount() const | ||
| { | ||
| return m_change_count; | ||
| } | ||
|
|
||
| const std::vector<GraphicsModConfig>& GraphicsModGroupConfig::GetMods() const | ||
| { | ||
| return m_graphics_mods; | ||
| } | ||
|
|
||
| GraphicsModConfig* GraphicsModGroupConfig::GetMod(const std::string& absolute_path) const | ||
| { | ||
| if (const auto iter = m_path_to_graphics_mod.find(absolute_path); | ||
| iter != m_path_to_graphics_mod.end()) | ||
| { | ||
| return iter->second; | ||
| } | ||
|
|
||
| return nullptr; | ||
| } | ||
|
|
||
| const std::string& GraphicsModGroupConfig::GetGameID() const | ||
| { | ||
| return m_game_id; | ||
| } | ||
|
|
||
| std::string GraphicsModGroupConfig::GetPath() const | ||
| { | ||
| const std::string game_mod_root = File::GetUserPath(D_CONFIG_IDX) + GRAPHICSMOD_CONFIG_DIR; | ||
| return fmt::format("{}/{}.json", game_mod_root, m_game_id); | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,46 @@ | ||
| // Copyright 2022 Dolphin Emulator Project | ||
| // SPDX-License-Identifier: GPL-2.0-or-later | ||
|
|
||
| #pragma once | ||
|
|
||
| #include <map> | ||
| #include <string> | ||
| #include <vector> | ||
|
|
||
| #include <picojson.h> | ||
|
|
||
| #include "Common/CommonTypes.h" | ||
|
|
||
| struct GraphicsModConfig; | ||
|
|
||
| class GraphicsModGroupConfig | ||
| { | ||
| public: | ||
| explicit GraphicsModGroupConfig(const std::string& game_id); | ||
| ~GraphicsModGroupConfig(); | ||
|
|
||
| GraphicsModGroupConfig(const GraphicsModGroupConfig&); | ||
| GraphicsModGroupConfig(GraphicsModGroupConfig&&); | ||
|
|
||
| GraphicsModGroupConfig& operator=(const GraphicsModGroupConfig&); | ||
| GraphicsModGroupConfig& operator=(GraphicsModGroupConfig&&); | ||
|
|
||
| void Load(); | ||
| void Save() const; | ||
|
|
||
| void SetChangeCount(u32 change_count); | ||
| u32 GetChangeCount() const; | ||
|
|
||
| const std::vector<GraphicsModConfig>& GetMods() const; | ||
|
|
||
| GraphicsModConfig* GetMod(const std::string& absolute_path) const; | ||
|
|
||
| const std::string& GetGameID() const; | ||
|
|
||
| private: | ||
| std::string GetPath() const; | ||
| std::string m_game_id; | ||
| std::vector<GraphicsModConfig> m_graphics_mods; | ||
| std::map<std::string, GraphicsModConfig*> m_path_to_graphics_mod; | ||
| u32 m_change_count = 0; | ||
| }; |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,254 @@ | ||
| // Copyright 2022 Dolphin Emulator Project | ||
| // SPDX-License-Identifier: GPL-2.0-or-later | ||
|
|
||
| #include "VideoCommon/GraphicsModSystem/Config/GraphicsTarget.h" | ||
|
|
||
| #include "Common/Logging/Log.h" | ||
| #include "Common/StringUtil.h" | ||
| #include "VideoCommon/TextureCacheBase.h" | ||
|
|
||
| namespace | ||
| { | ||
| template <typename T, std::enable_if_t<std::is_base_of_v<FBTarget, T>, int> = 0> | ||
| std::optional<T> DeserializeFBTargetFromConfig(const picojson::object& obj, std::string_view prefix) | ||
| { | ||
| T fb; | ||
| const auto texture_filename_iter = obj.find("texture_filename"); | ||
| if (texture_filename_iter == obj.end()) | ||
| { | ||
| ERROR_LOG_FMT(VIDEO, | ||
| "Failed to load mod configuration file, option 'texture_filename' not found"); | ||
| return std::nullopt; | ||
| } | ||
| if (!texture_filename_iter->second.is<std::string>()) | ||
| { | ||
| ERROR_LOG_FMT( | ||
| VIDEO, | ||
| "Failed to load mod configuration file, option 'texture_filename' is not a string type"); | ||
| return std::nullopt; | ||
| } | ||
| const auto texture_filename = texture_filename_iter->second.get<std::string>(); | ||
| const auto texture_filename_without_prefix = texture_filename.substr(prefix.size() + 1); | ||
| const auto split_str_values = SplitString(texture_filename_without_prefix, '_'); | ||
| if (split_str_values.size() == 1) | ||
| { | ||
| ERROR_LOG_FMT( | ||
| VIDEO, "Failed to load mod configuration file, value in 'texture_filename' is not valid"); | ||
| return std::nullopt; | ||
| } | ||
| const auto split_width_height_values = SplitString(texture_filename_without_prefix, 'x'); | ||
| if (split_width_height_values.size() != 2) | ||
| { | ||
| ERROR_LOG_FMT(VIDEO, "Failed to load mod configuration file, value in 'texture_filename' is " | ||
| "not valid, width and height separator found more matches than expected"); | ||
| return std::nullopt; | ||
| } | ||
|
|
||
| const std::size_t width_underscore_pos = split_width_height_values[0].find_last_of('_'); | ||
| std::string width_str; | ||
| if (width_underscore_pos == std::string::npos) | ||
| { | ||
| width_str = split_width_height_values[0]; | ||
| } | ||
| else | ||
| { | ||
| width_str = split_width_height_values[0].substr(width_underscore_pos + 1); | ||
| } | ||
| if (!TryParse(width_str, &fb.m_width)) | ||
| { | ||
| ERROR_LOG_FMT(VIDEO, "Failed to load mod configuration file, value in 'texture_filename' is " | ||
| "not valid, width not a number"); | ||
| return std::nullopt; | ||
| } | ||
|
|
||
| const std::size_t height_underscore_pos = split_width_height_values[1].find_first_of('_'); | ||
| if (height_underscore_pos == std::string::npos || | ||
| height_underscore_pos == split_width_height_values[1].size() - 1) | ||
| { | ||
| ERROR_LOG_FMT(VIDEO, "Failed to load mod configuration file, value in 'texture_filename' is " | ||
| "not valid, underscore after height is missing or incomplete"); | ||
| return std::nullopt; | ||
| } | ||
| const std::string height_str = split_width_height_values[1].substr(0, height_underscore_pos); | ||
| if (!TryParse(height_str, &fb.m_height)) | ||
| { | ||
| ERROR_LOG_FMT(VIDEO, "Failed to load mod configuration file, value in 'texture_filename' is " | ||
| "not valid, height not a number"); | ||
| return std::nullopt; | ||
| } | ||
|
|
||
| const std::size_t format_underscore_pos = | ||
| split_width_height_values[1].find_first_of('_', height_underscore_pos + 1); | ||
|
|
||
| std::string format_str; | ||
| if (format_underscore_pos == std::string::npos) | ||
| { | ||
| format_str = split_width_height_values[1].substr(height_underscore_pos + 1); | ||
| } | ||
| else | ||
| { | ||
| format_str = split_width_height_values[1].substr( | ||
| height_underscore_pos + 1, (format_underscore_pos - height_underscore_pos) - 1); | ||
| } | ||
| u32 format; | ||
| if (!TryParse(format_str, &format)) | ||
| { | ||
| ERROR_LOG_FMT(VIDEO, "Failed to load mod configuration file, value in 'texture_filename' is " | ||
| "not valid, texture format is not a number"); | ||
| return std::nullopt; | ||
| } | ||
| if (!IsValidTextureFormat(static_cast<TextureFormat>(format))) | ||
| { | ||
| ERROR_LOG_FMT(VIDEO, "Failed to load mod configuration file, value in 'texture_filename' is " | ||
| "not valid, texture format is not valid"); | ||
| return std::nullopt; | ||
| } | ||
| fb.m_texture_format = static_cast<TextureFormat>(format); | ||
|
|
||
| return fb; | ||
| } | ||
| std::optional<std::string> ExtractTextureFilenameForConfig(const picojson::object& obj) | ||
| { | ||
| const auto texture_filename_iter = obj.find("texture_filename"); | ||
| if (texture_filename_iter == obj.end()) | ||
| { | ||
| ERROR_LOG_FMT(VIDEO, | ||
| "Failed to load mod configuration file, option 'texture_filename' not found"); | ||
| return std::nullopt; | ||
| } | ||
| if (!texture_filename_iter->second.is<std::string>()) | ||
| { | ||
| ERROR_LOG_FMT( | ||
| VIDEO, | ||
| "Failed to load mod configuration file, option 'texture_filename' is not a string type"); | ||
| return std::nullopt; | ||
| } | ||
| std::string texture_info = texture_filename_iter->second.get<std::string>(); | ||
| if (texture_info.find(EFB_DUMP_PREFIX) != std::string::npos) | ||
| { | ||
| const auto letter_c_pos = texture_info.find_first_of('n'); | ||
| if (letter_c_pos == std::string::npos) | ||
| { | ||
| ERROR_LOG_FMT(VIDEO, "Failed to load mod configuration file, value in 'texture_filename' " | ||
| "is an efb without a count"); | ||
| return std::nullopt; | ||
| } | ||
| texture_info = | ||
| texture_info.substr(letter_c_pos - 1, texture_info.find_first_of("_", letter_c_pos)); | ||
| } | ||
| else if (texture_info.find(XFB_DUMP_PREFIX) != std::string::npos) | ||
| { | ||
| const auto letter_c_pos = texture_info.find_first_of('n'); | ||
| if (letter_c_pos == std::string::npos) | ||
| { | ||
| ERROR_LOG_FMT(VIDEO, "Failed to load mod configuration file, value in 'texture_filename' " | ||
| "is an xfb without a count"); | ||
| return std::nullopt; | ||
| } | ||
| texture_info = | ||
| texture_info.substr(letter_c_pos - 1, texture_info.find_first_of("_", letter_c_pos)); | ||
| } | ||
| return texture_info; | ||
| } | ||
| } // namespace | ||
|
|
||
| std::optional<GraphicsTargetConfig> DeserializeTargetFromConfig(const picojson::object& obj) | ||
| { | ||
| const auto type_iter = obj.find("type"); | ||
| if (type_iter == obj.end()) | ||
| { | ||
| ERROR_LOG_FMT(VIDEO, "Failed to load mod configuration file, option 'type' not found"); | ||
| return std::nullopt; | ||
| } | ||
| if (!type_iter->second.is<std::string>()) | ||
| { | ||
| ERROR_LOG_FMT(VIDEO, | ||
| "Failed to load mod configuration file, option 'type' is not a string type"); | ||
| return std::nullopt; | ||
| } | ||
| const std::string& type = type_iter->second.get<std::string>(); | ||
| if (type == "draw_started") | ||
| { | ||
| std::optional<std::string> texture_info = ExtractTextureFilenameForConfig(obj); | ||
| if (!texture_info.has_value()) | ||
| return std::nullopt; | ||
|
|
||
| DrawStartedTextureTarget target; | ||
| target.m_texture_info_string = texture_info.value(); | ||
| return target; | ||
| } | ||
| else if (type == "load_texture") | ||
| { | ||
| std::optional<std::string> texture_info = ExtractTextureFilenameForConfig(obj); | ||
| if (!texture_info.has_value()) | ||
| return std::nullopt; | ||
|
|
||
| LoadTextureTarget target; | ||
| target.m_texture_info_string = texture_info.value(); | ||
| return target; | ||
| } | ||
| else if (type == "efb") | ||
| { | ||
| return DeserializeFBTargetFromConfig<EFBTarget>(obj, EFB_DUMP_PREFIX); | ||
| } | ||
| else if (type == "xfb") | ||
| { | ||
| return DeserializeFBTargetFromConfig<XFBTarget>(obj, EFB_DUMP_PREFIX); | ||
| } | ||
| else if (type == "projection") | ||
| { | ||
| ProjectionTarget target; | ||
| const auto texture_iter = obj.find("texture_filename"); | ||
| if (texture_iter != obj.end()) | ||
| { | ||
| std::optional<std::string> texture_info = ExtractTextureFilenameForConfig(obj); | ||
| if (!texture_info.has_value()) | ||
| return std::nullopt; | ||
| target.m_texture_info_string = texture_info; | ||
| } | ||
| const auto value_iter = obj.find("value"); | ||
| if (value_iter == obj.end()) | ||
| { | ||
| ERROR_LOG_FMT(VIDEO, "Failed to load mod configuration file, option 'value' not found"); | ||
| return std::nullopt; | ||
| } | ||
| if (!value_iter->second.is<std::string>()) | ||
| { | ||
| ERROR_LOG_FMT(VIDEO, | ||
| "Failed to load mod configuration file, option 'value' is not a string type"); | ||
| return std::nullopt; | ||
| } | ||
| const auto& value_str = value_iter->second.get<std::string>(); | ||
| if (value_str == "2d") | ||
| { | ||
| target.m_projection_type = ProjectionType::Orthographic; | ||
| } | ||
| else if (value_str == "3d") | ||
| { | ||
| target.m_projection_type = ProjectionType::Perspective; | ||
| } | ||
| else | ||
| { | ||
| ERROR_LOG_FMT(VIDEO, "Failed to load mod configuration file, option 'value' is not a valid " | ||
| "value, valid values are: 2d, 3d"); | ||
| return std::nullopt; | ||
| } | ||
| return target; | ||
| } | ||
| else | ||
| { | ||
| ERROR_LOG_FMT(VIDEO, | ||
| "Failed to load mod configuration file, option 'type' is not a valid value"); | ||
| } | ||
| return std::nullopt; | ||
| } | ||
|
|
||
| void SerializeTargetToProfile(picojson::object*, const GraphicsTargetConfig&) | ||
| { | ||
| // Added for consistency, no functionality as of now | ||
| } | ||
|
|
||
| void DeserializeTargetFromProfile(const picojson::object&, GraphicsTargetConfig*) | ||
| { | ||
| // Added for consistency, no functionality as of now | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,56 @@ | ||
| // Copyright 2022 Dolphin Emulator Project | ||
| // SPDX-License-Identifier: GPL-2.0-or-later | ||
|
|
||
| #pragma once | ||
|
|
||
| #include <optional> | ||
| #include <string> | ||
| #include <variant> | ||
|
|
||
| #include <picojson.h> | ||
|
|
||
| #include "Common/CommonTypes.h" | ||
| #include "VideoCommon/TextureDecoder.h" | ||
| #include "VideoCommon/XFMemory.h" | ||
|
|
||
| struct TextureTarget | ||
| { | ||
| std::string m_texture_info_string; | ||
| }; | ||
|
|
||
| struct DrawStartedTextureTarget final : public TextureTarget | ||
| { | ||
| }; | ||
|
|
||
| struct LoadTextureTarget final : public TextureTarget | ||
| { | ||
| }; | ||
|
|
||
| struct FBTarget | ||
| { | ||
| u32 m_height = 0; | ||
| u32 m_width = 0; | ||
| TextureFormat m_texture_format = TextureFormat::I4; | ||
| }; | ||
|
|
||
| struct EFBTarget final : public FBTarget | ||
| { | ||
| }; | ||
|
|
||
| struct XFBTarget final : public FBTarget | ||
| { | ||
| }; | ||
|
|
||
| struct ProjectionTarget | ||
| { | ||
| std::optional<std::string> m_texture_info_string; | ||
| ProjectionType m_projection_type = ProjectionType::Perspective; | ||
| }; | ||
|
|
||
| using GraphicsTargetConfig = std::variant<DrawStartedTextureTarget, LoadTextureTarget, EFBTarget, | ||
| XFBTarget, ProjectionTarget>; | ||
|
|
||
| std::optional<GraphicsTargetConfig> DeserializeTargetFromConfig(const picojson::object& obj); | ||
|
|
||
| void SerializeTargetToProfile(picojson::object* obj, const GraphicsTargetConfig& target); | ||
| void DeserializeTargetFromProfile(const picojson::object& obj, GraphicsTargetConfig* target); |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,88 @@ | ||
| // Copyright 2022 Dolphin Emulator Project | ||
| // SPDX-License-Identifier: GPL-2.0-or-later | ||
|
|
||
| #include "VideoCommon/GraphicsModSystem/Config/GraphicsTargetGroup.h" | ||
|
|
||
| #include "Common/Logging/Log.h" | ||
|
|
||
| bool GraphicsTargetGroupConfig::DeserializeFromConfig(const picojson::object& obj) | ||
| { | ||
| if (auto name_iter = obj.find("name"); name_iter != obj.end()) | ||
| { | ||
| if (!name_iter->second.is<std::string>()) | ||
| { | ||
| ERROR_LOG_FMT( | ||
| VIDEO, "Failed to load mod configuration file, specified group's name is not a string"); | ||
| return false; | ||
| } | ||
| m_name = name_iter->second.get<std::string>(); | ||
| } | ||
|
|
||
| if (auto targets_iter = obj.find("targets"); targets_iter != obj.end()) | ||
| { | ||
| if (!targets_iter->second.is<picojson::array>()) | ||
| { | ||
| ERROR_LOG_FMT( | ||
| VIDEO, | ||
| "Failed to load mod configuration file, specified group's targets is not an array"); | ||
| return false; | ||
| } | ||
| for (const auto& target_val : targets_iter->second.get<picojson::array>()) | ||
| { | ||
| if (!target_val.is<picojson::object>()) | ||
| { | ||
| ERROR_LOG_FMT( | ||
| VIDEO, | ||
| "Failed to load shader configuration file, specified target is not a json object"); | ||
| return false; | ||
| } | ||
| const auto target = DeserializeTargetFromConfig(target_val.get<picojson::object>()); | ||
| if (!target) | ||
| { | ||
| return false; | ||
| } | ||
|
|
||
| m_targets.push_back(*target); | ||
| } | ||
| } | ||
|
|
||
| return true; | ||
| } | ||
|
|
||
| void GraphicsTargetGroupConfig::SerializeToProfile(picojson::object* obj) const | ||
| { | ||
| if (!obj) | ||
| return; | ||
| auto& json_obj = *obj; | ||
| picojson::array serialized_targets; | ||
| for (const auto& target : m_targets) | ||
| { | ||
| picojson::object serialized_target; | ||
| SerializeTargetToProfile(&serialized_target, target); | ||
| serialized_targets.push_back(picojson::value{serialized_target}); | ||
| } | ||
| json_obj["targets"] = picojson::value{serialized_targets}; | ||
| } | ||
|
|
||
| void GraphicsTargetGroupConfig::DeserializeFromProfile(const picojson::object& obj) | ||
| { | ||
| if (const auto it = obj.find("targets"); it != obj.end()) | ||
| { | ||
| if (it->second.is<picojson::array>()) | ||
| { | ||
| auto serialized_targets = it->second.get<picojson::array>(); | ||
| if (serialized_targets.size() != m_targets.size()) | ||
| return; | ||
|
|
||
| for (std::size_t i = 0; i < serialized_targets.size(); i++) | ||
| { | ||
| const auto& serialized_target_val = serialized_targets[i]; | ||
| if (serialized_target_val.is<picojson::object>()) | ||
| { | ||
| const auto& serialized_target = serialized_target_val.get<picojson::object>(); | ||
| DeserializeTargetFromProfile(serialized_target, &m_targets[i]); | ||
| } | ||
| } | ||
| } | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,22 @@ | ||
| // Copyright 2022 Dolphin Emulator Project | ||
| // SPDX-License-Identifier: GPL-2.0-or-later | ||
|
|
||
| #pragma once | ||
|
|
||
| #include <string> | ||
| #include <vector> | ||
|
|
||
| #include <picojson.h> | ||
|
|
||
| #include "VideoCommon/GraphicsModSystem/Config/GraphicsTarget.h" | ||
|
|
||
| struct GraphicsTargetGroupConfig | ||
| { | ||
| std::string m_name; | ||
| std::vector<GraphicsTargetConfig> m_targets; | ||
|
|
||
| bool DeserializeFromConfig(const picojson::object& obj); | ||
|
|
||
| void SerializeToProfile(picojson::object* obj) const; | ||
| void DeserializeFromProfile(const picojson::object& obj); | ||
| }; |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,11 @@ | ||
| // Copyright 2022 Dolphin Emulator Project | ||
| // SPDX-License-Identifier: GPL-2.0-or-later | ||
|
|
||
| #pragma once | ||
|
|
||
| #include <string> | ||
|
|
||
| #include "Common/CommonPaths.h" | ||
|
|
||
| static const inline std::string DOLPHIN_SYSTEM_GRAPHICS_MOD_DIR = | ||
| LOAD_DIR DIR_SEP GRAPHICSMOD_DIR DIR_SEP; |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,47 @@ | ||
| // Copyright 2022 Dolphin Emulator Project | ||
| // SPDX-License-Identifier: GPL-2.0-or-later | ||
|
|
||
| #include "VideoCommon/GraphicsModSystem/Runtime/Actions/MoveAction.h" | ||
|
|
||
| std::unique_ptr<MoveAction> MoveAction::Create(const picojson::value& json_data) | ||
| { | ||
| Common::Vec3 position_offset; | ||
| const auto& x = json_data.get("X"); | ||
| if (x.is<double>()) | ||
| { | ||
| position_offset.x = static_cast<float>(x.get<double>()); | ||
| } | ||
|
|
||
| const auto& y = json_data.get("Y"); | ||
| if (y.is<double>()) | ||
| { | ||
| position_offset.y = static_cast<float>(y.get<double>()); | ||
| } | ||
|
|
||
| const auto& z = json_data.get("Z"); | ||
| if (z.is<double>()) | ||
| { | ||
| position_offset.z = static_cast<float>(z.get<double>()); | ||
| } | ||
| return std::make_unique<MoveAction>(position_offset); | ||
| } | ||
|
|
||
| MoveAction::MoveAction(Common::Vec3 position_offset) : m_position_offset(position_offset) | ||
| { | ||
| } | ||
|
|
||
| void MoveAction::OnProjection(Common::Matrix44* matrix) | ||
| { | ||
| if (!matrix) | ||
| return; | ||
|
|
||
| *matrix *= Common::Matrix44::Translate(m_position_offset); | ||
| } | ||
|
|
||
| void MoveAction::OnProjectionAndTexture(Common::Matrix44* matrix) | ||
| { | ||
| if (!matrix) | ||
| return; | ||
|
|
||
| *matrix *= Common::Matrix44::Translate(m_position_offset); | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,22 @@ | ||
| // Copyright 2022 Dolphin Emulator Project | ||
| // SPDX-License-Identifier: GPL-2.0-or-later | ||
|
|
||
| #pragma once | ||
|
|
||
| #include <memory> | ||
|
|
||
| #include <picojson.h> | ||
|
|
||
| #include "VideoCommon/GraphicsModSystem/Runtime/GraphicsModAction.h" | ||
|
|
||
| class MoveAction final : public GraphicsModAction | ||
| { | ||
| public: | ||
| static std::unique_ptr<MoveAction> Create(const picojson::value& json_data); | ||
| explicit MoveAction(Common::Vec3 position_offset); | ||
| void OnProjection(Common::Matrix44* matrix) override; | ||
| void OnProjectionAndTexture(Common::Matrix44* matrix) override; | ||
|
|
||
| private: | ||
| Common::Vec3 m_position_offset; | ||
| }; |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,36 @@ | ||
| // Copyright 2022 Dolphin Emulator Project | ||
| // SPDX-License-Identifier: GPL-2.0-or-later | ||
|
|
||
| #include "VideoCommon/GraphicsModSystem/Runtime/Actions/PrintAction.h" | ||
|
|
||
| #include "Common/Logging/Log.h" | ||
|
|
||
| void PrintAction::OnDrawStarted(bool*) | ||
| { | ||
| INFO_LOG_FMT(VIDEO, "OnDrawStarted Called"); | ||
| } | ||
|
|
||
| void PrintAction::OnEFB(bool*, u32 texture_width, u32 texture_height, u32* scaled_width, | ||
| u32* scaled_height) | ||
| { | ||
| if (!scaled_width || !scaled_height) | ||
| return; | ||
|
|
||
| INFO_LOG_FMT(VIDEO, "OnEFB Called. Original [{}, {}], Scaled [{}, {}]", texture_width, | ||
| texture_height, *scaled_width, *scaled_height); | ||
| } | ||
|
|
||
| void PrintAction::OnProjection(Common::Matrix44*) | ||
| { | ||
| INFO_LOG_FMT(VIDEO, "OnProjection Called"); | ||
| } | ||
|
|
||
| void PrintAction::OnProjectionAndTexture(Common::Matrix44*) | ||
| { | ||
| INFO_LOG_FMT(VIDEO, "OnProjectionAndTexture Called"); | ||
| } | ||
|
|
||
| void PrintAction::OnTextureLoad() | ||
| { | ||
| INFO_LOG_FMT(VIDEO, "OnTextureLoad Called"); | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,17 @@ | ||
| // Copyright 2022 Dolphin Emulator Project | ||
| // SPDX-License-Identifier: GPL-2.0-or-later | ||
|
|
||
| #pragma once | ||
|
|
||
| #include "VideoCommon/GraphicsModSystem/Runtime/GraphicsModAction.h" | ||
|
|
||
| class PrintAction final : public GraphicsModAction | ||
| { | ||
| public: | ||
| void OnDrawStarted(bool* skip) override; | ||
| void OnEFB(bool* skip, u32 texture_width, u32 texture_height, u32* scaled_width, | ||
| u32* scaled_height) override; | ||
| void OnProjection(Common::Matrix44* matrix) override; | ||
| void OnProjectionAndTexture(Common::Matrix44* matrix) override; | ||
| void OnTextureLoad() override; | ||
| }; |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,59 @@ | ||
| // Copyright 2022 Dolphin Emulator Project | ||
| // SPDX-License-Identifier: GPL-2.0-or-later | ||
|
|
||
| #include "VideoCommon/GraphicsModSystem/Runtime/Actions/ScaleAction.h" | ||
|
|
||
| std::unique_ptr<ScaleAction> ScaleAction::Create(const picojson::value& json_data) | ||
| { | ||
| Common::Vec3 scale; | ||
| const auto& x = json_data.get("X"); | ||
| if (x.is<double>()) | ||
| { | ||
| scale.x = static_cast<float>(x.get<double>()); | ||
| } | ||
|
|
||
| const auto& y = json_data.get("Y"); | ||
| if (y.is<double>()) | ||
| { | ||
| scale.y = static_cast<float>(y.get<double>()); | ||
| } | ||
|
|
||
| const auto& z = json_data.get("Z"); | ||
| if (z.is<double>()) | ||
| { | ||
| scale.z = static_cast<float>(z.get<double>()); | ||
| } | ||
| return std::make_unique<ScaleAction>(scale); | ||
| } | ||
|
|
||
| ScaleAction::ScaleAction(Common::Vec3 scale) : m_scale(scale) | ||
| { | ||
| } | ||
|
|
||
| void ScaleAction::OnEFB(bool*, u32 texture_width, u32 texture_height, u32* scaled_width, | ||
| u32* scaled_height) | ||
| { | ||
| if (scaled_width && m_scale.x > 0) | ||
| *scaled_width = texture_width * m_scale.x; | ||
|
|
||
| if (scaled_height && m_scale.y > 0) | ||
| *scaled_height = texture_height * m_scale.y; | ||
| } | ||
|
|
||
| void ScaleAction::OnProjection(Common::Matrix44* matrix) | ||
| { | ||
| if (!matrix) | ||
| return; | ||
| auto& the_matrix = *matrix; | ||
| the_matrix.data[0] = the_matrix.data[0] * m_scale.x; | ||
| the_matrix.data[5] = the_matrix.data[5] * m_scale.y; | ||
| } | ||
|
|
||
| void ScaleAction::OnProjectionAndTexture(Common::Matrix44* matrix) | ||
| { | ||
| if (!matrix) | ||
| return; | ||
| auto& the_matrix = *matrix; | ||
| the_matrix.data[0] = the_matrix.data[0] * m_scale.x; | ||
| the_matrix.data[5] = the_matrix.data[5] * m_scale.y; | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,24 @@ | ||
| // Copyright 2022 Dolphin Emulator Project | ||
| // SPDX-License-Identifier: GPL-2.0-or-later | ||
|
|
||
| #pragma once | ||
|
|
||
| #include <memory> | ||
|
|
||
| #include <picojson.h> | ||
|
|
||
| #include "VideoCommon/GraphicsModSystem/Runtime/GraphicsModAction.h" | ||
|
|
||
| class ScaleAction final : public GraphicsModAction | ||
| { | ||
| public: | ||
| static std::unique_ptr<ScaleAction> Create(const picojson::value& json_data); | ||
| explicit ScaleAction(Common::Vec3 scale); | ||
| void OnEFB(bool* skip, u32 texture_width, u32 texture_height, u32* scaled_width, | ||
| u32* scaled_height) override; | ||
| void OnProjection(Common::Matrix44* matrix) override; | ||
| void OnProjectionAndTexture(Common::Matrix44* matrix) override; | ||
|
|
||
| private: | ||
| Common::Vec3 m_scale; | ||
| }; |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,20 @@ | ||
| // Copyright 2022 Dolphin Emulator Project | ||
| // SPDX-License-Identifier: GPL-2.0-or-later | ||
|
|
||
| #include "VideoCommon/GraphicsModSystem/Runtime/Actions/SkipAction.h" | ||
|
|
||
| void SkipAction::OnDrawStarted(bool* skip) | ||
| { | ||
| if (!skip) | ||
| return; | ||
|
|
||
| *skip = true; | ||
| } | ||
|
|
||
| void SkipAction::OnEFB(bool* skip, u32, u32, u32*, u32*) | ||
| { | ||
| if (!skip) | ||
| return; | ||
|
|
||
| *skip = true; | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,14 @@ | ||
| // Copyright 2022 Dolphin Emulator Project | ||
| // SPDX-License-Identifier: GPL-2.0-or-later | ||
|
|
||
| #pragma once | ||
|
|
||
| #include "VideoCommon/GraphicsModSystem/Runtime/GraphicsModAction.h" | ||
|
|
||
| class SkipAction final : public GraphicsModAction | ||
| { | ||
| public: | ||
| void OnDrawStarted(bool* skip) override; | ||
| void OnEFB(bool* skip, u32 texture_width, u32 texture_height, u32* scaled_width, | ||
| u32* scaled_height) override; | ||
| }; |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,22 @@ | ||
| // Copyright 2022 Dolphin Emulator Project | ||
| // SPDX-License-Identifier: GPL-2.0-or-later | ||
|
|
||
| #include "VideoCommon/GraphicsModSystem/Runtime/FBInfo.h" | ||
|
|
||
| #include "Common/Hash.h" | ||
|
|
||
| u32 FBInfo::CalculateHash() const | ||
| { | ||
| return Common::HashAdler32(reinterpret_cast<const u8*>(this), sizeof(FBInfo)); | ||
| } | ||
|
|
||
| bool FBInfo::operator==(const FBInfo& other) const | ||
| { | ||
| return m_height == other.m_height && m_width == other.m_width && | ||
| m_texture_format == other.m_texture_format; | ||
| } | ||
|
|
||
| bool FBInfo::operator!=(const FBInfo& other) const | ||
| { | ||
| return !(*this == other); | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,25 @@ | ||
| // Copyright 2022 Dolphin Emulator Project | ||
| // SPDX-License-Identifier: GPL-2.0-or-later | ||
|
|
||
| #pragma once | ||
|
|
||
| #include "Common/CommonTypes.h" | ||
| #include "VideoCommon/TextureDecoder.h" | ||
|
|
||
| struct FBInfo | ||
| { | ||
| u32 m_height = 0; | ||
| u32 m_width = 0; | ||
| TextureFormat m_texture_format = TextureFormat::I4; | ||
| u32 CalculateHash() const; | ||
| bool operator==(const FBInfo& other) const; | ||
| bool operator!=(const FBInfo& other) const; | ||
| }; | ||
|
|
||
| struct FBInfoHasher | ||
| { | ||
| std::size_t operator()(const FBInfo& fb_info) const noexcept | ||
| { | ||
| return static_cast<std::size_t>(fb_info.CalculateHash()); | ||
| } | ||
| }; |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,29 @@ | ||
| // Copyright 2022 Dolphin Emulator Project | ||
| // SPDX-License-Identifier: GPL-2.0-or-later | ||
|
|
||
| #pragma once | ||
|
|
||
| #include "Common/CommonTypes.h" | ||
| #include "Common/Matrix.h" | ||
|
|
||
| class GraphicsModAction | ||
| { | ||
| public: | ||
| GraphicsModAction() = default; | ||
| virtual ~GraphicsModAction() = default; | ||
| GraphicsModAction(const GraphicsModAction&) = default; | ||
| GraphicsModAction(GraphicsModAction&&) = default; | ||
| GraphicsModAction& operator=(const GraphicsModAction&) = default; | ||
| GraphicsModAction& operator=(GraphicsModAction&&) = default; | ||
|
|
||
| virtual void OnDrawStarted(bool* skip) {} | ||
| virtual void OnEFB(bool* skip, u32 texture_width, u32 texture_height, u32* scaled_width, | ||
| u32* scaled_height) | ||
| { | ||
| } | ||
| virtual void OnXFB() {} | ||
| virtual void OnProjection(Common::Matrix44* matrix) {} | ||
| virtual void OnProjectionAndTexture(Common::Matrix44* matrix) {} | ||
| virtual void OnTextureLoad() {} | ||
| virtual void OnFrameEnd() {} | ||
| }; |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,34 @@ | ||
| // Copyright 2022 Dolphin Emulator Project | ||
| // SPDX-License-Identifier: GPL-2.0-or-later | ||
|
|
||
| #include "VideoCommon/GraphicsModSystem/Runtime/GraphicsModActionFactory.h" | ||
|
|
||
| #include "VideoCommon/GraphicsModSystem/Runtime/Actions/MoveAction.h" | ||
| #include "VideoCommon/GraphicsModSystem/Runtime/Actions/PrintAction.h" | ||
| #include "VideoCommon/GraphicsModSystem/Runtime/Actions/ScaleAction.h" | ||
| #include "VideoCommon/GraphicsModSystem/Runtime/Actions/SkipAction.h" | ||
|
|
||
| namespace GraphicsModActionFactory | ||
| { | ||
| std::unique_ptr<GraphicsModAction> Create(std::string_view name, const picojson::value& json_data) | ||
| { | ||
| if (name == "print") | ||
| { | ||
| return std::make_unique<PrintAction>(); | ||
| } | ||
| else if (name == "skip") | ||
| { | ||
| return std::make_unique<SkipAction>(); | ||
| } | ||
| else if (name == "move") | ||
| { | ||
| return MoveAction::Create(json_data); | ||
| } | ||
| else if (name == "scale") | ||
| { | ||
| return ScaleAction::Create(json_data); | ||
| } | ||
|
|
||
| return nullptr; | ||
| } | ||
| } // namespace GraphicsModActionFactory |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,16 @@ | ||
| // Copyright 2022 Dolphin Emulator Project | ||
| // SPDX-License-Identifier: GPL-2.0-or-later | ||
|
|
||
| #pragma once | ||
|
|
||
| #include <memory> | ||
| #include <string_view> | ||
|
|
||
| #include <picojson.h> | ||
|
|
||
| #include "VideoCommon/GraphicsModSystem/Runtime/GraphicsModAction.h" | ||
|
|
||
| namespace GraphicsModActionFactory | ||
| { | ||
| std::unique_ptr<GraphicsModAction> Create(std::string_view name, const picojson::value& json_data); | ||
| } |