mirror of
				https://git.zaroz.cloud/nintendo-back-up/yuzu/yuzu-mainline.git
				synced 2025-03-21 01:53:15 +00:00 
			
		
		
		
	Merge pull request #850 from DarkLordZach/icon-meta
Add Icons and Metadata Support
This commit is contained in:
		
						commit
						fd9da4232b
					
				@ -4,8 +4,10 @@ function(copy_yuzu_Qt5_deps target_dir)
 | 
			
		||||
    set(Qt5_DLL_DIR "${Qt5_DIR}/../../../bin")
 | 
			
		||||
    set(Qt5_PLATFORMS_DIR "${Qt5_DIR}/../../../plugins/platforms/")
 | 
			
		||||
    set(Qt5_STYLES_DIR "${Qt5_DIR}/../../../plugins/styles/")
 | 
			
		||||
    set(Qt5_IMAGEFORMATS_DIR "${Qt5_DIR}/../../../plugins/imageformats/")
 | 
			
		||||
    set(PLATFORMS ${DLL_DEST}platforms/)
 | 
			
		||||
    set(STYLES ${DLL_DEST}styles/)
 | 
			
		||||
    set(IMAGEFORMATS ${DLL_DEST}imageformats/)
 | 
			
		||||
    windows_copy_files(${target_dir} ${Qt5_DLL_DIR} ${DLL_DEST}
 | 
			
		||||
        icudt*.dll
 | 
			
		||||
        icuin*.dll
 | 
			
		||||
@ -17,4 +19,5 @@ function(copy_yuzu_Qt5_deps target_dir)
 | 
			
		||||
    )
 | 
			
		||||
    windows_copy_files(yuzu ${Qt5_PLATFORMS_DIR} ${PLATFORMS} qwindows$<$<CONFIG:Debug>:d>.*)
 | 
			
		||||
    windows_copy_files(yuzu ${Qt5_STYLES_DIR} ${STYLES} qwindowsvistastyle$<$<CONFIG:Debug>:d>.*)
 | 
			
		||||
    windows_copy_files(yuzu ${Qt5_IMAGEFORMATS_DIR} ${IMAGEFORMATS} qjpeg$<$<CONFIG:Debug>:d>.*)
 | 
			
		||||
endfunction(copy_yuzu_Qt5_deps)
 | 
			
		||||
 | 
			
		||||
@ -117,6 +117,7 @@ after_build:
 | 
			
		||||
          mkdir $RELEASE_DIST
 | 
			
		||||
          mkdir $RELEASE_DIST/platforms
 | 
			
		||||
          mkdir $RELEASE_DIST/styles
 | 
			
		||||
          mkdir $RELEASE_DIST/imageformats
 | 
			
		||||
 | 
			
		||||
          # copy the compiled binaries and other release files to the release folder
 | 
			
		||||
          Get-ChildItem "$CMAKE_BINARY_DIR" -Filter "yuzu*.exe" | Copy-Item -destination $RELEASE_DIST
 | 
			
		||||
@ -140,6 +141,9 @@ after_build:
 | 
			
		||||
          # copy the qt windows vista style dll to platforms
 | 
			
		||||
          Copy-Item -path "C:/msys64/mingw64/share/qt5/plugins/styles/qwindowsvistastyle.dll" -force -destination "$RELEASE_DIST/styles"
 | 
			
		||||
 | 
			
		||||
          # copy the qt jpeg imageformat dll to platforms
 | 
			
		||||
          Copy-Item -path "C:/msys64/mingw64/share/qt5/plugins/imageformats/qjpeg.dll" -force -destination "$RELEASE_DIST/imageformats"
 | 
			
		||||
 | 
			
		||||
          7z a -tzip $MINGW_BUILD_ZIP $RELEASE_DIST\*
 | 
			
		||||
          7z a $MINGW_SEVENZIP $RELEASE_DIST
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
@ -5,6 +5,7 @@
 | 
			
		||||
#include <array>
 | 
			
		||||
#include <string>
 | 
			
		||||
#include <core/loader/loader.h>
 | 
			
		||||
#include "common/logging/log.h"
 | 
			
		||||
#include "core/file_sys/card_image.h"
 | 
			
		||||
#include "core/file_sys/partition_filesystem.h"
 | 
			
		||||
#include "core/file_sys/vfs_offset.h"
 | 
			
		||||
 | 
			
		||||
@ -170,6 +170,10 @@ VirtualFile NCA::Decrypt(NCASectionHeader s_header, VirtualFile in, u64 starting
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
NCA::NCA(VirtualFile file_) : file(std::move(file_)) {
 | 
			
		||||
    if (file == nullptr) {
 | 
			
		||||
        status = Loader::ResultStatus::ErrorInvalidFormat;
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
    if (sizeof(NCAHeader) != file->ReadObject(&header))
 | 
			
		||||
        LOG_ERROR(Loader, "File reader errored out during header read.");
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -12,6 +12,7 @@
 | 
			
		||||
#include "common/common_funcs.h"
 | 
			
		||||
#include "common/common_types.h"
 | 
			
		||||
#include "common/swap.h"
 | 
			
		||||
#include "control_metadata.h"
 | 
			
		||||
#include "core/crypto/key_manager.h"
 | 
			
		||||
#include "core/file_sys/partition_filesystem.h"
 | 
			
		||||
#include "core/loader/loader.h"
 | 
			
		||||
 | 
			
		||||
@ -62,6 +62,13 @@ enum class Language : u8 {
 | 
			
		||||
    Chinese = 14,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static constexpr std::array<const char*, 15> LANGUAGE_NAMES = {
 | 
			
		||||
    "AmericanEnglish", "BritishEnglish", "Japanese",
 | 
			
		||||
    "French",          "German",         "LatinAmericanSpanish",
 | 
			
		||||
    "Spanish",         "Italian",        "Dutch",
 | 
			
		||||
    "CanadianFrench",  "Portugese",      "Russian",
 | 
			
		||||
    "Korean",          "Taiwanese",      "Chinese"};
 | 
			
		||||
 | 
			
		||||
// A class representing the format used by NX metadata files, typically named Control.nacp.
 | 
			
		||||
// These store application name, dev name, title id, and other miscellaneous data.
 | 
			
		||||
class NACP {
 | 
			
		||||
 | 
			
		||||
@ -7,6 +7,7 @@
 | 
			
		||||
#include "common/file_util.h"
 | 
			
		||||
#include "common/logging/log.h"
 | 
			
		||||
#include "core/file_sys/content_archive.h"
 | 
			
		||||
#include "core/file_sys/control_metadata.h"
 | 
			
		||||
#include "core/gdbstub/gdbstub.h"
 | 
			
		||||
#include "core/hle/kernel/process.h"
 | 
			
		||||
#include "core/hle/kernel/resource_limit.h"
 | 
			
		||||
@ -17,8 +18,50 @@
 | 
			
		||||
 | 
			
		||||
namespace Loader {
 | 
			
		||||
 | 
			
		||||
AppLoader_DeconstructedRomDirectory::AppLoader_DeconstructedRomDirectory(FileSys::VirtualFile file)
 | 
			
		||||
    : AppLoader(std::move(file)) {}
 | 
			
		||||
AppLoader_DeconstructedRomDirectory::AppLoader_DeconstructedRomDirectory(FileSys::VirtualFile file_)
 | 
			
		||||
    : AppLoader(std::move(file_)) {
 | 
			
		||||
    const auto dir = file->GetContainingDirectory();
 | 
			
		||||
 | 
			
		||||
    // Icon
 | 
			
		||||
    FileSys::VirtualFile icon_file = nullptr;
 | 
			
		||||
    for (const auto& language : FileSys::LANGUAGE_NAMES) {
 | 
			
		||||
        icon_file = dir->GetFile("icon_" + std::string(language) + ".dat");
 | 
			
		||||
        if (icon_file != nullptr) {
 | 
			
		||||
            icon_data = icon_file->ReadAllBytes();
 | 
			
		||||
            break;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (icon_data.empty()) {
 | 
			
		||||
        // Any png, jpeg, or bmp file
 | 
			
		||||
        const auto& files = dir->GetFiles();
 | 
			
		||||
        const auto icon_iter =
 | 
			
		||||
            std::find_if(files.begin(), files.end(), [](const FileSys::VirtualFile& file) {
 | 
			
		||||
                return file->GetExtension() == "png" || file->GetExtension() == "jpg" ||
 | 
			
		||||
                       file->GetExtension() == "bmp" || file->GetExtension() == "jpeg";
 | 
			
		||||
            });
 | 
			
		||||
        if (icon_iter != files.end())
 | 
			
		||||
            icon_data = (*icon_iter)->ReadAllBytes();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Metadata
 | 
			
		||||
    FileSys::VirtualFile nacp_file = dir->GetFile("control.nacp");
 | 
			
		||||
    if (nacp_file == nullptr) {
 | 
			
		||||
        const auto& files = dir->GetFiles();
 | 
			
		||||
        const auto nacp_iter =
 | 
			
		||||
            std::find_if(files.begin(), files.end(), [](const FileSys::VirtualFile& file) {
 | 
			
		||||
                return file->GetExtension() == "nacp";
 | 
			
		||||
            });
 | 
			
		||||
        if (nacp_iter != files.end())
 | 
			
		||||
            nacp_file = *nacp_iter;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (nacp_file != nullptr) {
 | 
			
		||||
        FileSys::NACP nacp(nacp_file);
 | 
			
		||||
        title_id = nacp.GetTitleId();
 | 
			
		||||
        name = nacp.GetApplicationName();
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
AppLoader_DeconstructedRomDirectory::AppLoader_DeconstructedRomDirectory(
 | 
			
		||||
    FileSys::VirtualDir directory)
 | 
			
		||||
@ -105,4 +148,25 @@ ResultStatus AppLoader_DeconstructedRomDirectory::ReadRomFS(FileSys::VirtualFile
 | 
			
		||||
    return ResultStatus::Success;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
ResultStatus AppLoader_DeconstructedRomDirectory::ReadIcon(std::vector<u8>& buffer) {
 | 
			
		||||
    if (icon_data.empty())
 | 
			
		||||
        return ResultStatus::ErrorNotUsed;
 | 
			
		||||
    buffer = icon_data;
 | 
			
		||||
    return ResultStatus::Success;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
ResultStatus AppLoader_DeconstructedRomDirectory::ReadProgramId(u64& out_program_id) {
 | 
			
		||||
    if (name.empty())
 | 
			
		||||
        return ResultStatus::ErrorNotUsed;
 | 
			
		||||
    out_program_id = title_id;
 | 
			
		||||
    return ResultStatus::Success;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
ResultStatus AppLoader_DeconstructedRomDirectory::ReadTitle(std::string& title) {
 | 
			
		||||
    if (name.empty())
 | 
			
		||||
        return ResultStatus::ErrorNotUsed;
 | 
			
		||||
    title = name;
 | 
			
		||||
    return ResultStatus::Success;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
} // namespace Loader
 | 
			
		||||
 | 
			
		||||
@ -39,11 +39,18 @@ public:
 | 
			
		||||
    ResultStatus Load(Kernel::SharedPtr<Kernel::Process>& process) override;
 | 
			
		||||
 | 
			
		||||
    ResultStatus ReadRomFS(FileSys::VirtualFile& dir) override;
 | 
			
		||||
    ResultStatus ReadIcon(std::vector<u8>& buffer) override;
 | 
			
		||||
    ResultStatus ReadProgramId(u64& out_program_id) override;
 | 
			
		||||
    ResultStatus ReadTitle(std::string& title) override;
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
    FileSys::ProgramMetadata metadata;
 | 
			
		||||
    FileSys::VirtualFile romfs;
 | 
			
		||||
    FileSys::VirtualDir dir;
 | 
			
		||||
 | 
			
		||||
    std::vector<u8> icon_data;
 | 
			
		||||
    std::string name;
 | 
			
		||||
    u64 title_id{};
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
} // namespace Loader
 | 
			
		||||
 | 
			
		||||
@ -68,7 +68,7 @@ FileType GuessFromFilename(const std::string& name) {
 | 
			
		||||
    return FileType::Unknown;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const char* GetFileTypeString(FileType type) {
 | 
			
		||||
std::string GetFileTypeString(FileType type) {
 | 
			
		||||
    switch (type) {
 | 
			
		||||
    case FileType::ELF:
 | 
			
		||||
        return "ELF";
 | 
			
		||||
 | 
			
		||||
@ -61,7 +61,7 @@ FileType GuessFromFilename(const std::string& name);
 | 
			
		||||
/**
 | 
			
		||||
 * Convert a FileType into a string which can be displayed to the user.
 | 
			
		||||
 */
 | 
			
		||||
const char* GetFileTypeString(FileType type);
 | 
			
		||||
std::string GetFileTypeString(FileType type);
 | 
			
		||||
 | 
			
		||||
/// Return type for functions in Loader namespace
 | 
			
		||||
enum class ResultStatus {
 | 
			
		||||
 | 
			
		||||
@ -77,8 +77,8 @@ ResultStatus AppLoader_NCA::ReadRomFS(FileSys::VirtualFile& dir) {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
ResultStatus AppLoader_NCA::ReadProgramId(u64& out_program_id) {
 | 
			
		||||
    if (nca == nullptr)
 | 
			
		||||
        return ResultStatus::ErrorNotLoaded;
 | 
			
		||||
    if (nca == nullptr || nca->GetStatus() != ResultStatus::Success)
 | 
			
		||||
        return ResultStatus::ErrorInvalidFormat;
 | 
			
		||||
    out_program_id = nca->GetTitleId();
 | 
			
		||||
    return ResultStatus::Success;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -33,7 +33,6 @@ public:
 | 
			
		||||
    ResultStatus Load(Kernel::SharedPtr<Kernel::Process>& process) override;
 | 
			
		||||
 | 
			
		||||
    ResultStatus ReadRomFS(FileSys::VirtualFile& dir) override;
 | 
			
		||||
 | 
			
		||||
    ResultStatus ReadProgramId(u64& out_program_id) override;
 | 
			
		||||
 | 
			
		||||
    ~AppLoader_NCA();
 | 
			
		||||
@ -41,6 +40,7 @@ public:
 | 
			
		||||
private:
 | 
			
		||||
    FileSys::ProgramMetadata metadata;
 | 
			
		||||
 | 
			
		||||
    FileSys::NCAHeader header;
 | 
			
		||||
    std::unique_ptr<FileSys::NCA> nca;
 | 
			
		||||
    std::unique_ptr<AppLoader_DeconstructedRomDirectory> directory_loader;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
@ -26,7 +26,25 @@ namespace Loader {
 | 
			
		||||
AppLoader_XCI::AppLoader_XCI(FileSys::VirtualFile file)
 | 
			
		||||
    : AppLoader(file), xci(std::make_unique<FileSys::XCI>(file)),
 | 
			
		||||
      nca_loader(std::make_unique<AppLoader_NCA>(
 | 
			
		||||
          xci->GetNCAFileByType(FileSys::NCAContentType::Program))) {}
 | 
			
		||||
          xci->GetNCAFileByType(FileSys::NCAContentType::Program))) {
 | 
			
		||||
    if (xci->GetStatus() != ResultStatus::Success)
 | 
			
		||||
        return;
 | 
			
		||||
    const auto control_nca = xci->GetNCAByType(FileSys::NCAContentType::Control);
 | 
			
		||||
    if (control_nca == nullptr || control_nca->GetStatus() != ResultStatus::Success)
 | 
			
		||||
        return;
 | 
			
		||||
    const auto romfs = FileSys::ExtractRomFS(control_nca->GetRomFS());
 | 
			
		||||
    if (romfs == nullptr)
 | 
			
		||||
        return;
 | 
			
		||||
    for (const auto& language : FileSys::LANGUAGE_NAMES) {
 | 
			
		||||
        icon_file = romfs->GetFile("icon_" + std::string(language) + ".dat");
 | 
			
		||||
        if (icon_file != nullptr)
 | 
			
		||||
            break;
 | 
			
		||||
    }
 | 
			
		||||
    const auto nacp_raw = romfs->GetFile("control.nacp");
 | 
			
		||||
    if (nacp_raw == nullptr)
 | 
			
		||||
        return;
 | 
			
		||||
    nacp_file = std::make_shared<FileSys::NACP>(nacp_raw);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
AppLoader_XCI::~AppLoader_XCI() = default;
 | 
			
		||||
 | 
			
		||||
@ -71,4 +89,17 @@ ResultStatus AppLoader_XCI::ReadProgramId(u64& out_program_id) {
 | 
			
		||||
    return nca_loader->ReadProgramId(out_program_id);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
ResultStatus AppLoader_XCI::ReadIcon(std::vector<u8>& buffer) {
 | 
			
		||||
    if (icon_file == nullptr)
 | 
			
		||||
        return ResultStatus::ErrorInvalidFormat;
 | 
			
		||||
    buffer = icon_file->ReadAllBytes();
 | 
			
		||||
    return ResultStatus::Success;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
ResultStatus AppLoader_XCI::ReadTitle(std::string& title) {
 | 
			
		||||
    if (nacp_file == nullptr)
 | 
			
		||||
        return ResultStatus::ErrorInvalidFormat;
 | 
			
		||||
    title = nacp_file->GetApplicationName();
 | 
			
		||||
    return ResultStatus::Success;
 | 
			
		||||
}
 | 
			
		||||
} // namespace Loader
 | 
			
		||||
 | 
			
		||||
@ -33,12 +33,17 @@ public:
 | 
			
		||||
 | 
			
		||||
    ResultStatus ReadRomFS(FileSys::VirtualFile& dir) override;
 | 
			
		||||
    ResultStatus ReadProgramId(u64& out_program_id) override;
 | 
			
		||||
    ResultStatus ReadIcon(std::vector<u8>& buffer) override;
 | 
			
		||||
    ResultStatus ReadTitle(std::string& title) override;
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
    FileSys::ProgramMetadata metadata;
 | 
			
		||||
 | 
			
		||||
    std::unique_ptr<FileSys::XCI> xci;
 | 
			
		||||
    std::unique_ptr<AppLoader_NCA> nca_loader;
 | 
			
		||||
 | 
			
		||||
    FileSys::VirtualFile icon_file;
 | 
			
		||||
    std::shared_ptr<FileSys::NACP> nacp_file;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
} // namespace Loader
 | 
			
		||||
 | 
			
		||||
@ -17,6 +17,8 @@ add_executable(yuzu
 | 
			
		||||
    configuration/configure_debug.h
 | 
			
		||||
    configuration/configure_dialog.cpp
 | 
			
		||||
    configuration/configure_dialog.h
 | 
			
		||||
    configuration/configure_gamelist.cpp
 | 
			
		||||
    configuration/configure_gamelist.h
 | 
			
		||||
    configuration/configure_general.cpp
 | 
			
		||||
    configuration/configure_general.h
 | 
			
		||||
    configuration/configure_graphics.cpp
 | 
			
		||||
@ -59,6 +61,7 @@ set(UIS
 | 
			
		||||
    configuration/configure.ui
 | 
			
		||||
    configuration/configure_audio.ui
 | 
			
		||||
    configuration/configure_debug.ui
 | 
			
		||||
    configuration/configure_gamelist.ui
 | 
			
		||||
    configuration/configure_general.ui
 | 
			
		||||
    configuration/configure_graphics.ui
 | 
			
		||||
    configuration/configure_input.ui
 | 
			
		||||
 | 
			
		||||
@ -122,6 +122,13 @@ void Config::ReadValues() {
 | 
			
		||||
    qt_config->beginGroup("UI");
 | 
			
		||||
    UISettings::values.theme = qt_config->value("theme", UISettings::themes[0].second).toString();
 | 
			
		||||
 | 
			
		||||
    qt_config->beginGroup("UIGameList");
 | 
			
		||||
    UISettings::values.show_unknown = qt_config->value("show_unknown", true).toBool();
 | 
			
		||||
    UISettings::values.icon_size = qt_config->value("icon_size", 48).toUInt();
 | 
			
		||||
    UISettings::values.row_1_text_id = qt_config->value("row_1_text_id", 0).toUInt();
 | 
			
		||||
    UISettings::values.row_2_text_id = qt_config->value("row_2_text_id", 3).toUInt();
 | 
			
		||||
    qt_config->endGroup();
 | 
			
		||||
 | 
			
		||||
    qt_config->beginGroup("UILayout");
 | 
			
		||||
    UISettings::values.geometry = qt_config->value("geometry").toByteArray();
 | 
			
		||||
    UISettings::values.state = qt_config->value("state").toByteArray();
 | 
			
		||||
@ -234,6 +241,13 @@ void Config::SaveValues() {
 | 
			
		||||
    qt_config->beginGroup("UI");
 | 
			
		||||
    qt_config->setValue("theme", UISettings::values.theme);
 | 
			
		||||
 | 
			
		||||
    qt_config->beginGroup("UIGameList");
 | 
			
		||||
    qt_config->setValue("show_unknown", UISettings::values.show_unknown);
 | 
			
		||||
    qt_config->setValue("icon_size", UISettings::values.icon_size);
 | 
			
		||||
    qt_config->setValue("row_1_text_id", UISettings::values.row_1_text_id);
 | 
			
		||||
    qt_config->setValue("row_2_text_id", UISettings::values.row_2_text_id);
 | 
			
		||||
    qt_config->endGroup();
 | 
			
		||||
 | 
			
		||||
    qt_config->beginGroup("UILayout");
 | 
			
		||||
    qt_config->setValue("geometry", UISettings::values.geometry);
 | 
			
		||||
    qt_config->setValue("state", UISettings::values.state);
 | 
			
		||||
 | 
			
		||||
@ -24,6 +24,11 @@
 | 
			
		||||
       <string>General</string>
 | 
			
		||||
      </attribute>
 | 
			
		||||
     </widget>
 | 
			
		||||
      <widget class="ConfigureGameList" name="gameListTab">
 | 
			
		||||
        <attribute name="title">
 | 
			
		||||
          <string>Game List</string>
 | 
			
		||||
        </attribute>
 | 
			
		||||
      </widget>
 | 
			
		||||
     <widget class="ConfigureSystem" name="systemTab">
 | 
			
		||||
      <attribute name="title">
 | 
			
		||||
       <string>System</string>
 | 
			
		||||
@ -67,6 +72,12 @@
 | 
			
		||||
   <header>configuration/configure_general.h</header>
 | 
			
		||||
   <container>1</container>
 | 
			
		||||
  </customwidget>
 | 
			
		||||
   <customwidget>
 | 
			
		||||
     <class>ConfigureGameList</class>
 | 
			
		||||
     <extends>QWidget</extends>
 | 
			
		||||
     <header>configuration/configure_gamelist.h</header>
 | 
			
		||||
     <container>1</container>
 | 
			
		||||
   </customwidget>
 | 
			
		||||
  <customwidget>
 | 
			
		||||
   <class>ConfigureSystem</class>
 | 
			
		||||
   <extends>QWidget</extends>
 | 
			
		||||
 | 
			
		||||
@ -21,6 +21,7 @@ void ConfigureDialog::setConfiguration() {}
 | 
			
		||||
 | 
			
		||||
void ConfigureDialog::applyConfiguration() {
 | 
			
		||||
    ui->generalTab->applyConfiguration();
 | 
			
		||||
    ui->gameListTab->applyConfiguration();
 | 
			
		||||
    ui->systemTab->applyConfiguration();
 | 
			
		||||
    ui->inputTab->applyConfiguration();
 | 
			
		||||
    ui->graphicsTab->applyConfiguration();
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										63
									
								
								src/yuzu/configuration/configure_gamelist.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										63
									
								
								src/yuzu/configuration/configure_gamelist.cpp
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,63 @@
 | 
			
		||||
// Copyright 2016 Citra Emulator Project
 | 
			
		||||
// Licensed under GPLv2 or any later version
 | 
			
		||||
// Refer to the license.txt file included.
 | 
			
		||||
 | 
			
		||||
#include "core/core.h"
 | 
			
		||||
#include "core/settings.h"
 | 
			
		||||
#include "ui_configure_gamelist.h"
 | 
			
		||||
#include "ui_settings.h"
 | 
			
		||||
#include "yuzu/configuration/configure_gamelist.h"
 | 
			
		||||
 | 
			
		||||
ConfigureGameList::ConfigureGameList(QWidget* parent)
 | 
			
		||||
    : QWidget(parent), ui(new Ui::ConfigureGameList) {
 | 
			
		||||
    ui->setupUi(this);
 | 
			
		||||
 | 
			
		||||
    static const std::vector<std::pair<u32, std::string>> default_icon_sizes{
 | 
			
		||||
        std::make_pair(0, "None"),        std::make_pair(32, "Small"),
 | 
			
		||||
        std::make_pair(64, "Standard"),   std::make_pair(128, "Large"),
 | 
			
		||||
        std::make_pair(256, "Full Size"),
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    for (const auto& size : default_icon_sizes) {
 | 
			
		||||
        ui->icon_size_combobox->addItem(QString::fromStdString(size.second + " (" +
 | 
			
		||||
                                                               std::to_string(size.first) + "x" +
 | 
			
		||||
                                                               std::to_string(size.first) + ")"),
 | 
			
		||||
                                        size.first);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    static const std::vector<std::string> row_text_names{
 | 
			
		||||
        "Filename",
 | 
			
		||||
        "Filetype",
 | 
			
		||||
        "Title ID",
 | 
			
		||||
        "Title Name",
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    for (size_t i = 0; i < row_text_names.size(); ++i) {
 | 
			
		||||
        ui->row_1_text_combobox->addItem(QString::fromStdString(row_text_names[i]),
 | 
			
		||||
                                         QVariant::fromValue(i));
 | 
			
		||||
        ui->row_2_text_combobox->addItem(QString::fromStdString(row_text_names[i]),
 | 
			
		||||
                                         QVariant::fromValue(i));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    this->setConfiguration();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
ConfigureGameList::~ConfigureGameList() {}
 | 
			
		||||
 | 
			
		||||
void ConfigureGameList::setConfiguration() {
 | 
			
		||||
    ui->show_unknown->setChecked(UISettings::values.show_unknown);
 | 
			
		||||
    ui->icon_size_combobox->setCurrentIndex(
 | 
			
		||||
        ui->icon_size_combobox->findData(UISettings::values.icon_size));
 | 
			
		||||
    ui->row_1_text_combobox->setCurrentIndex(
 | 
			
		||||
        ui->row_1_text_combobox->findData(UISettings::values.row_1_text_id));
 | 
			
		||||
    ui->row_2_text_combobox->setCurrentIndex(
 | 
			
		||||
        ui->row_2_text_combobox->findData(UISettings::values.row_2_text_id));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void ConfigureGameList::applyConfiguration() {
 | 
			
		||||
    UISettings::values.show_unknown = ui->show_unknown->isChecked();
 | 
			
		||||
    UISettings::values.icon_size = ui->icon_size_combobox->currentData().toUInt();
 | 
			
		||||
    UISettings::values.row_1_text_id = ui->row_1_text_combobox->currentData().toUInt();
 | 
			
		||||
    UISettings::values.row_2_text_id = ui->row_2_text_combobox->currentData().toUInt();
 | 
			
		||||
    Settings::Apply();
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										28
									
								
								src/yuzu/configuration/configure_gamelist.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										28
									
								
								src/yuzu/configuration/configure_gamelist.h
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,28 @@
 | 
			
		||||
// Copyright 2016 Citra Emulator Project
 | 
			
		||||
// Licensed under GPLv2 or any later version
 | 
			
		||||
// Refer to the license.txt file included.
 | 
			
		||||
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include <memory>
 | 
			
		||||
#include <QWidget>
 | 
			
		||||
 | 
			
		||||
namespace Ui {
 | 
			
		||||
class ConfigureGameList;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
class ConfigureGameList : public QWidget {
 | 
			
		||||
    Q_OBJECT
 | 
			
		||||
 | 
			
		||||
public:
 | 
			
		||||
    explicit ConfigureGameList(QWidget* parent = nullptr);
 | 
			
		||||
    ~ConfigureGameList();
 | 
			
		||||
 | 
			
		||||
    void applyConfiguration();
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
    void setConfiguration();
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
    std::unique_ptr<Ui::ConfigureGameList> ui;
 | 
			
		||||
};
 | 
			
		||||
							
								
								
									
										126
									
								
								src/yuzu/configuration/configure_gamelist.ui
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										126
									
								
								src/yuzu/configuration/configure_gamelist.ui
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,126 @@
 | 
			
		||||
<?xml version="1.0" encoding="UTF-8"?>
 | 
			
		||||
<ui version="4.0">
 | 
			
		||||
 <class>ConfigureGameList</class>
 | 
			
		||||
  <widget class="QWidget" name="ConfigureGeneral">
 | 
			
		||||
    <property name="geometry">
 | 
			
		||||
      <rect>
 | 
			
		||||
        <x>0</x>
 | 
			
		||||
        <y>0</y>
 | 
			
		||||
        <width>300</width>
 | 
			
		||||
        <height>377</height>
 | 
			
		||||
      </rect>
 | 
			
		||||
    </property>
 | 
			
		||||
    <property name="windowTitle">
 | 
			
		||||
      <string>Form</string>
 | 
			
		||||
    </property>
 | 
			
		||||
    <layout class="QHBoxLayout" name="HorizontalLayout">
 | 
			
		||||
      <item>
 | 
			
		||||
        <layout class="QVBoxLayout" name="VerticalLayout">
 | 
			
		||||
          <item>
 | 
			
		||||
            <widget class="QGroupBox" name="GeneralGroupBox">
 | 
			
		||||
              <property name="title">
 | 
			
		||||
                <string>General</string>
 | 
			
		||||
              </property>
 | 
			
		||||
              <layout class="QHBoxLayout" name="GeneralHorizontalLayout">
 | 
			
		||||
                <item>
 | 
			
		||||
                  <layout class="QVBoxLayout" name="GeneralVerticalLayout">
 | 
			
		||||
                    <item>
 | 
			
		||||
                      <widget class="QCheckBox" name="show_unknown">
 | 
			
		||||
                        <property name="text">
 | 
			
		||||
                          <string>Show files with type 'Unknown'</string>
 | 
			
		||||
                        </property>
 | 
			
		||||
                      </widget>
 | 
			
		||||
                    </item>
 | 
			
		||||
                  </layout>
 | 
			
		||||
                </item>
 | 
			
		||||
              </layout>
 | 
			
		||||
            </widget>
 | 
			
		||||
          </item>
 | 
			
		||||
          <item>
 | 
			
		||||
            <widget class="QGroupBox" name="IconSizeGroupBox">
 | 
			
		||||
              <property name="title">
 | 
			
		||||
                <string>Icon Size</string>
 | 
			
		||||
              </property>
 | 
			
		||||
              <layout class="QHBoxLayout" name="icon_size_qhbox_layout">
 | 
			
		||||
                <item>
 | 
			
		||||
                  <layout class="QVBoxLayout" name="icon_size_qvbox_layout">
 | 
			
		||||
                    <item>
 | 
			
		||||
                      <layout class="QHBoxLayout" name="icon_size_qhbox_layout_2">
 | 
			
		||||
                        <item>
 | 
			
		||||
                          <widget class="QLabel" name="icon_size_label">
 | 
			
		||||
                            <property name="text">
 | 
			
		||||
                              <string>Icon Size:</string>
 | 
			
		||||
                            </property>
 | 
			
		||||
                          </widget>
 | 
			
		||||
                        </item>
 | 
			
		||||
                        <item>
 | 
			
		||||
                          <widget class="QComboBox" name="icon_size_combobox"/>
 | 
			
		||||
                        </item>
 | 
			
		||||
                      </layout>
 | 
			
		||||
                    </item>
 | 
			
		||||
                  </layout>
 | 
			
		||||
                </item>
 | 
			
		||||
              </layout>
 | 
			
		||||
            </widget>
 | 
			
		||||
          </item>
 | 
			
		||||
          <item>
 | 
			
		||||
            <widget class="QGroupBox" name="RowGroupBox">
 | 
			
		||||
              <property name="title">
 | 
			
		||||
                <string>Row Text</string>
 | 
			
		||||
              </property>
 | 
			
		||||
              <layout class="QHBoxLayout" name="RowHorizontalLayout">
 | 
			
		||||
                <item>
 | 
			
		||||
                  <layout class="QVBoxLayout" name="RowVerticalLayout">
 | 
			
		||||
                    <item>
 | 
			
		||||
                      <layout class="QHBoxLayout" name="row_1_qhbox_layout">
 | 
			
		||||
                        <item>
 | 
			
		||||
                          <widget class="QLabel" name="row_1_label">
 | 
			
		||||
                            <property name="text">
 | 
			
		||||
                              <string>Row 1 Text:</string>
 | 
			
		||||
                            </property>
 | 
			
		||||
                          </widget>
 | 
			
		||||
                        </item>
 | 
			
		||||
                        <item>
 | 
			
		||||
                          <widget class="QComboBox" name="row_1_text_combobox"/>
 | 
			
		||||
                        </item>
 | 
			
		||||
                      </layout>
 | 
			
		||||
                    </item>
 | 
			
		||||
                    <item>
 | 
			
		||||
                      <layout class="QHBoxLayout" name="row_2_qhbox_layout">
 | 
			
		||||
                        <item>
 | 
			
		||||
                          <widget class="QLabel" name="row_2_label">
 | 
			
		||||
                            <property name="text">
 | 
			
		||||
                              <string>Row 2 Text:</string>
 | 
			
		||||
                            </property>
 | 
			
		||||
                          </widget>
 | 
			
		||||
                        </item>
 | 
			
		||||
                        <item>
 | 
			
		||||
                          <widget class="QComboBox" name="row_2_text_combobox"/>
 | 
			
		||||
                        </item>
 | 
			
		||||
                      </layout>
 | 
			
		||||
                    </item>
 | 
			
		||||
                  </layout>
 | 
			
		||||
                </item>
 | 
			
		||||
              </layout>
 | 
			
		||||
            </widget>
 | 
			
		||||
          </item>
 | 
			
		||||
          <item>
 | 
			
		||||
            <spacer name="verticalSpacer">
 | 
			
		||||
              <property name="orientation">
 | 
			
		||||
                <enum>Qt::Vertical</enum>
 | 
			
		||||
              </property>
 | 
			
		||||
              <property name="sizeHint" stdset="0">
 | 
			
		||||
                <size>
 | 
			
		||||
                  <width>20</width>
 | 
			
		||||
                  <height>40</height>
 | 
			
		||||
                </size>
 | 
			
		||||
              </property>
 | 
			
		||||
            </spacer>
 | 
			
		||||
          </item>
 | 
			
		||||
        </layout>
 | 
			
		||||
      </item>
 | 
			
		||||
    </layout>
 | 
			
		||||
  </widget>
 | 
			
		||||
 <resources/>
 | 
			
		||||
 <connections/>
 | 
			
		||||
</ui>
 | 
			
		||||
@ -9,9 +9,12 @@
 | 
			
		||||
#include <QKeyEvent>
 | 
			
		||||
#include <QMenu>
 | 
			
		||||
#include <QThreadPool>
 | 
			
		||||
#include <boost/container/flat_map.hpp>
 | 
			
		||||
#include "common/common_paths.h"
 | 
			
		||||
#include "common/logging/log.h"
 | 
			
		||||
#include "common/string_util.h"
 | 
			
		||||
#include "core/file_sys/content_archive.h"
 | 
			
		||||
#include "core/file_sys/control_metadata.h"
 | 
			
		||||
#include "core/file_sys/vfs_real.h"
 | 
			
		||||
#include "core/loader/loader.h"
 | 
			
		||||
#include "game_list.h"
 | 
			
		||||
@ -398,7 +401,31 @@ void GameList::RefreshGameDirectory() {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void GameListWorker::AddFstEntriesToGameList(const std::string& dir_path, unsigned int recursion) {
 | 
			
		||||
    const auto callback = [this, recursion](u64* num_entries_out, const std::string& directory,
 | 
			
		||||
    boost::container::flat_map<u64, std::shared_ptr<FileSys::NCA>> nca_control_map;
 | 
			
		||||
 | 
			
		||||
    const auto nca_control_callback =
 | 
			
		||||
        [this, &nca_control_map](u64* num_entries_out, const std::string& directory,
 | 
			
		||||
                                 const std::string& virtual_name) -> bool {
 | 
			
		||||
        std::string physical_name = directory + DIR_SEP + virtual_name;
 | 
			
		||||
 | 
			
		||||
        if (stop_processing)
 | 
			
		||||
            return false; // Breaks the callback loop.
 | 
			
		||||
 | 
			
		||||
        bool is_dir = FileUtil::IsDirectory(physical_name);
 | 
			
		||||
        QFileInfo file_info(physical_name.c_str());
 | 
			
		||||
        if (!is_dir && file_info.suffix().toStdString() == "nca") {
 | 
			
		||||
            auto nca = std::make_shared<FileSys::NCA>(
 | 
			
		||||
                std::make_shared<FileSys::RealVfsFile>(physical_name));
 | 
			
		||||
            if (nca->GetType() == FileSys::NCAContentType::Control)
 | 
			
		||||
                nca_control_map.insert_or_assign(nca->GetTitleId(), nca);
 | 
			
		||||
        }
 | 
			
		||||
        return true;
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    FileUtil::ForeachDirectoryEntry(nullptr, dir_path, nca_control_callback);
 | 
			
		||||
 | 
			
		||||
    const auto callback = [this, recursion,
 | 
			
		||||
                           &nca_control_map](u64* num_entries_out, const std::string& directory,
 | 
			
		||||
                                             const std::string& virtual_name) -> bool {
 | 
			
		||||
        std::string physical_name = directory + DIR_SEP + virtual_name;
 | 
			
		||||
 | 
			
		||||
@ -410,17 +437,50 @@ void GameListWorker::AddFstEntriesToGameList(const std::string& dir_path, unsign
 | 
			
		||||
            (HasSupportedFileExtension(physical_name) || IsExtractedNCAMain(physical_name))) {
 | 
			
		||||
            std::unique_ptr<Loader::AppLoader> loader =
 | 
			
		||||
                Loader::GetLoader(std::make_shared<FileSys::RealVfsFile>(physical_name));
 | 
			
		||||
            if (!loader)
 | 
			
		||||
            if (!loader || ((loader->GetFileType() == Loader::FileType::Unknown ||
 | 
			
		||||
                             loader->GetFileType() == Loader::FileType::Error) &&
 | 
			
		||||
                            !UISettings::values.show_unknown))
 | 
			
		||||
                return true;
 | 
			
		||||
 | 
			
		||||
            std::vector<u8> smdh;
 | 
			
		||||
            loader->ReadIcon(smdh);
 | 
			
		||||
            std::vector<u8> icon;
 | 
			
		||||
            const auto res1 = loader->ReadIcon(icon);
 | 
			
		||||
 | 
			
		||||
            u64 program_id = 0;
 | 
			
		||||
            loader->ReadProgramId(program_id);
 | 
			
		||||
            u64 program_id;
 | 
			
		||||
            const auto res2 = loader->ReadProgramId(program_id);
 | 
			
		||||
 | 
			
		||||
            std::string name = " ";
 | 
			
		||||
            const auto res3 = loader->ReadTitle(name);
 | 
			
		||||
 | 
			
		||||
            if ((res1 == Loader::ResultStatus::ErrorNotUsed ||
 | 
			
		||||
                 res1 == Loader::ResultStatus::ErrorNotImplemented) &&
 | 
			
		||||
                (res3 == Loader::ResultStatus::ErrorNotUsed ||
 | 
			
		||||
                 res3 == Loader::ResultStatus::ErrorNotImplemented) &&
 | 
			
		||||
                res2 == Loader::ResultStatus::Success) {
 | 
			
		||||
                // Use from metadata pool.
 | 
			
		||||
                if (nca_control_map.find(program_id) != nca_control_map.end()) {
 | 
			
		||||
                    const auto nca = nca_control_map[program_id];
 | 
			
		||||
                    const auto control_dir = nca->GetSubdirectories()[0];
 | 
			
		||||
 | 
			
		||||
                    const auto nacp_file = control_dir->GetFile("control.nacp");
 | 
			
		||||
                    FileSys::NACP nacp(nacp_file);
 | 
			
		||||
                    name = nacp.GetApplicationName();
 | 
			
		||||
 | 
			
		||||
                    FileSys::VirtualFile icon_file = nullptr;
 | 
			
		||||
                    for (const auto& language : FileSys::LANGUAGE_NAMES) {
 | 
			
		||||
                        icon_file = control_dir->GetFile("icon_" + std::string(language) + ".dat");
 | 
			
		||||
                        if (icon_file != nullptr) {
 | 
			
		||||
                            icon = icon_file->ReadAllBytes();
 | 
			
		||||
                            break;
 | 
			
		||||
                        }
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            emit EntryReady({
 | 
			
		||||
                new GameListItemPath(FormatGameName(physical_name), smdh, program_id),
 | 
			
		||||
                new GameListItemPath(
 | 
			
		||||
                    FormatGameName(physical_name), icon, QString::fromStdString(name),
 | 
			
		||||
                    QString::fromStdString(Loader::GetFileTypeString(loader->GetFileType())),
 | 
			
		||||
                    program_id),
 | 
			
		||||
                new GameListItem(
 | 
			
		||||
                    QString::fromStdString(Loader::GetFileTypeString(loader->GetFileType()))),
 | 
			
		||||
                new GameListItemSize(FileUtil::GetSize(physical_name)),
 | 
			
		||||
 | 
			
		||||
@ -11,6 +11,7 @@
 | 
			
		||||
#include <QStandardItem>
 | 
			
		||||
#include <QString>
 | 
			
		||||
#include "common/string_util.h"
 | 
			
		||||
#include "ui_settings.h"
 | 
			
		||||
#include "yuzu/util/util.h"
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
@ -18,8 +19,7 @@
 | 
			
		||||
 * @param large If true, returns large icon (48x48), otherwise returns small icon (24x24)
 | 
			
		||||
 * @return QPixmap default icon
 | 
			
		||||
 */
 | 
			
		||||
static QPixmap GetDefaultIcon(bool large) {
 | 
			
		||||
    int size = large ? 48 : 24;
 | 
			
		||||
static QPixmap GetDefaultIcon(u32 size) {
 | 
			
		||||
    QPixmap icon(size, size);
 | 
			
		||||
    icon.fill(Qt::transparent);
 | 
			
		||||
    return icon;
 | 
			
		||||
@ -44,11 +44,25 @@ public:
 | 
			
		||||
    static const int FullPathRole = Qt::UserRole + 1;
 | 
			
		||||
    static const int TitleRole = Qt::UserRole + 2;
 | 
			
		||||
    static const int ProgramIdRole = Qt::UserRole + 3;
 | 
			
		||||
    static const int FileTypeRole = Qt::UserRole + 4;
 | 
			
		||||
 | 
			
		||||
    GameListItemPath() = default;
 | 
			
		||||
    GameListItemPath(const QString& game_path, const std::vector<u8>& smdh_data, u64 program_id) {
 | 
			
		||||
    GameListItemPath(const QString& game_path, const std::vector<u8>& picture_data,
 | 
			
		||||
                     const QString& game_name, const QString& game_type, u64 program_id)
 | 
			
		||||
        : GameListItem() {
 | 
			
		||||
        setData(game_path, FullPathRole);
 | 
			
		||||
        setData(game_name, TitleRole);
 | 
			
		||||
        setData(qulonglong(program_id), ProgramIdRole);
 | 
			
		||||
        setData(game_type, FileTypeRole);
 | 
			
		||||
 | 
			
		||||
        QPixmap picture;
 | 
			
		||||
        u32 size = UISettings::values.icon_size;
 | 
			
		||||
        if (!picture.loadFromData(picture_data.data(), picture_data.size()))
 | 
			
		||||
            picture = GetDefaultIcon(size);
 | 
			
		||||
 | 
			
		||||
        picture = picture.scaled(size, size);
 | 
			
		||||
 | 
			
		||||
        setData(picture, Qt::DecorationRole);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    QVariant data(int role) const override {
 | 
			
		||||
@ -57,7 +71,23 @@ public:
 | 
			
		||||
            Common::SplitPath(data(FullPathRole).toString().toStdString(), nullptr, &filename,
 | 
			
		||||
                              nullptr);
 | 
			
		||||
            QString title = data(TitleRole).toString();
 | 
			
		||||
            return QString::fromStdString(filename) + (title.isEmpty() ? "" : "\n    " + title);
 | 
			
		||||
 | 
			
		||||
            std::vector<QString> row_data{
 | 
			
		||||
                QString::fromStdString(filename),
 | 
			
		||||
                data(FileTypeRole).toString(),
 | 
			
		||||
                QString::fromStdString(fmt::format("0x{:016X}", data(ProgramIdRole).toULongLong())),
 | 
			
		||||
                data(TitleRole).toString(),
 | 
			
		||||
            };
 | 
			
		||||
 | 
			
		||||
            auto row1 = row_data.at(UISettings::values.row_1_text_id);
 | 
			
		||||
            auto row2 = row_data.at(UISettings::values.row_2_text_id);
 | 
			
		||||
 | 
			
		||||
            if (row1.isEmpty() || row1 == row2)
 | 
			
		||||
                return row2;
 | 
			
		||||
            if (row2.isEmpty())
 | 
			
		||||
                return row1;
 | 
			
		||||
 | 
			
		||||
            return row1 + "\n    " + row2;
 | 
			
		||||
        } else {
 | 
			
		||||
            return GameListItem::data(role);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
@ -768,6 +768,7 @@ void GMainWindow::OnConfigure() {
 | 
			
		||||
        configureDialog.applyConfiguration();
 | 
			
		||||
        if (UISettings::values.theme != old_theme)
 | 
			
		||||
            UpdateUITheme();
 | 
			
		||||
        game_list->PopulateAsync(UISettings::values.gamedir, UISettings::values.gamedir_deepscan);
 | 
			
		||||
        config->Save();
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -54,6 +54,12 @@ struct Values {
 | 
			
		||||
 | 
			
		||||
    // logging
 | 
			
		||||
    bool show_console;
 | 
			
		||||
 | 
			
		||||
    // Game List
 | 
			
		||||
    bool show_unknown;
 | 
			
		||||
    uint32_t icon_size;
 | 
			
		||||
    uint8_t row_1_text_id;
 | 
			
		||||
    uint8_t row_2_text_id;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
extern Values values;
 | 
			
		||||
 | 
			
		||||
		Loading…
	
		Reference in New Issue
	
	Block a user