Add ability to load add ons at configuration and game start (#98)
This commit is contained in:
parent
ed7acc5f99
commit
9270b8f9c3
|
|
@ -102,11 +102,13 @@ PageNav.page_tabs page.active {
|
|||
color: #eff0f1;
|
||||
}
|
||||
|
||||
#gameDirectoryList {
|
||||
#gameDirectoryList,
|
||||
#addOnDirectoryList {
|
||||
width: 100%%;
|
||||
}
|
||||
|
||||
#gameDirectoryList select {
|
||||
#gameDirectoryList select,
|
||||
#addOnDirectoryList select {
|
||||
background-color: #1e1e1e;
|
||||
border: 1px solid #404040;
|
||||
border-radius: 4px;
|
||||
|
|
@ -119,7 +121,8 @@ PageNav.page_tabs page.active {
|
|||
width: 100%%;
|
||||
}
|
||||
|
||||
#gameDirectoryList option
|
||||
#gameDirectoryList option,
|
||||
#addOnDirectoryList option
|
||||
{
|
||||
padding: 0.2rem 0.3rem;
|
||||
margin: 0.2rem 0.3rem;
|
||||
|
|
@ -127,7 +130,8 @@ PageNav.page_tabs page.active {
|
|||
color: white;
|
||||
}
|
||||
|
||||
#gameDirectoryList option:current
|
||||
#gameDirectoryList option:current,
|
||||
#addOnDirectoryList option:current
|
||||
{
|
||||
color: #1e1e1e;
|
||||
}
|
||||
|
|
@ -144,6 +148,7 @@ PageNav.page_tabs page.active {
|
|||
<div class="ConfigPage" tooltip-host>
|
||||
<PageNav target="ConfigContents" id="MainTabNav">
|
||||
<Page contents="system_config_general.html" label="General" Name="General" />
|
||||
<Page contents="system_config_game_browser.html" label="Game Browser" Name="GameBrowser" />
|
||||
<Page contents="system_config_graphics.html" label="Graphics" Name="Graphics" />
|
||||
<Page contents="system_config_audio.html" label="Audio" Name="Audio" />
|
||||
<Page contents="system_config_profiles.html" label="Profiles" Name="Profiles" />
|
||||
|
|
|
|||
|
|
@ -0,0 +1,30 @@
|
|||
<PageNav target="GameBrowserContents" id="GameBrowserTabNav" class="page_tabs">
|
||||
<Page label="Games" Name="Games">
|
||||
<div class="input-group">
|
||||
<div class="group-title">Game Directories</div>
|
||||
<ListBox id="gameDirectoryList" />
|
||||
<div class="buttons-container">
|
||||
<button id="gameDirectoryAdd">Add</button>
|
||||
<button id="gameDirectoryRemove">Remove</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="input-group">
|
||||
<div class="group-title">Display</div>
|
||||
<div class="form-row slider-row">
|
||||
<div class="left"><label for="myGamesIconSize">My Games Icon Size:</label></div>
|
||||
<div class="right"><input type="hslider" min="1" max="4" value="2" id="myGamesIconSize" style="width: 19em" /></div>
|
||||
</div>
|
||||
</div>
|
||||
</Page>
|
||||
<Page label="Add-Ons" Name="AddOns">
|
||||
<div class="input-group">
|
||||
<div class="group-title" tooltip="Folders of updates and DLC that load automatically when playing a game. Individual add-ons can be disabled in Configure Game.">Add-On Directories</div>
|
||||
<ListBox id="addOnDirectoryList" />
|
||||
<div class="buttons-container">
|
||||
<button id="addOnDirectoryAdd">Add</button>
|
||||
<button id="addOnDirectoryRemove">Remove</button>
|
||||
</div>
|
||||
</div>
|
||||
</Page>
|
||||
</PageNav>
|
||||
<div id="GameBrowserContents" class="page_content" />
|
||||
|
|
@ -1,21 +1,4 @@
|
|||
<PageNav target="GeneralContents" id="GeneralTabNav" class="page_tabs">
|
||||
<Page label="Game Browser" Name="GameBrowser">
|
||||
<div class="input-group">
|
||||
<div class="group-title">Game Directories</div>
|
||||
<ListBox id="gameDirectoryList" />
|
||||
<div class="buttons-container">
|
||||
<button id="gameDirectoryAdd">Add</button>
|
||||
<button id="gameDirectoryRemove">Remove</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="input-group">
|
||||
<div class="group-title">Game Browser</div>
|
||||
<div class="form-row slider-row">
|
||||
<div class="left"><label for="myGamesIconSize">My Games Icon Size:</label></div>
|
||||
<div class="right"><input type="hslider" min="1" max="4" value="2" id="myGamesIconSize" style="width: 19em" /></div>
|
||||
</div>
|
||||
</div>
|
||||
</Page>
|
||||
<Page label="System" Name="System">
|
||||
<div class="input-group">
|
||||
<div class="form-row">
|
||||
|
|
|
|||
|
|
@ -66,6 +66,7 @@ LoaderFileType GuessFromFilename(const std::string & name)
|
|||
{
|
||||
return LoaderFileType::NSP;
|
||||
}
|
||||
UNIMPLEMENTED();
|
||||
return LoaderFileType::Unknown;
|
||||
}
|
||||
|
||||
|
|
@ -80,6 +81,7 @@ std::string GetFileTypeString(LoaderFileType type)
|
|||
case LoaderFileType::Unknown:
|
||||
break;
|
||||
}
|
||||
UNIMPLEMENTED();
|
||||
return "unknown";
|
||||
}
|
||||
|
||||
|
|
@ -107,6 +109,7 @@ static std::shared_ptr<AppLoader> GetFileLoader(Systemloader & loader, FileSys::
|
|||
case LoaderFileType::NSP:
|
||||
return std::make_shared<AppLoader_NSP>(std::move(file), loader.GetFileSystemController(), loader.GetContentProvider(), program_id, program_index);
|
||||
}
|
||||
UNIMPLEMENTED();
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@ namespace
|
|||
{
|
||||
BooleanValue,
|
||||
IntegerValue,
|
||||
StringListValue,
|
||||
TitleIdStringListMapValue,
|
||||
};
|
||||
|
||||
|
|
@ -24,6 +25,7 @@ namespace
|
|||
public:
|
||||
LoaderSetting(const char * id, const char * path, bool * val, bool defaultValue);
|
||||
LoaderSetting(const char * id, const char * path, int * val, int defaultValue);
|
||||
LoaderSetting(const char * id, const char * path, StringList * val);
|
||||
LoaderSetting(const char * path, TitleIdStringListMap * val);
|
||||
|
||||
const char * identifier;
|
||||
|
|
@ -33,6 +35,7 @@ namespace
|
|||
{
|
||||
bool * boolValue;
|
||||
int * intValue;
|
||||
StringList * stringList;
|
||||
TitleIdStringListMap * titleIdStringListMap;
|
||||
} setting;
|
||||
union
|
||||
|
|
@ -46,6 +49,7 @@ namespace
|
|||
{NXLoaderSetting::CheckForUpdatedFirmware, "CheckForUpdatedFirmware", &loaderSettings.checkForUpdatedFirmware, true},
|
||||
{NXLoaderSetting::FirmwareInstallCurrent, nullptr, nullptr, 0},
|
||||
{NXLoaderSetting::FirmwareInstallTotal, nullptr, nullptr, 0},
|
||||
{NXLoaderSetting::AddOnDirectories, "AddOnDirectories", &loaderSettings.addOnDirectories},
|
||||
{"DisabledAddOns", &loaderSettings.disabled_addons},
|
||||
};
|
||||
|
||||
|
|
@ -124,6 +128,50 @@ namespace
|
|||
}
|
||||
return object;
|
||||
}
|
||||
|
||||
void LoadStringList(StringList & out, const JsonValue & value)
|
||||
{
|
||||
out.clear();
|
||||
if (!value.isArray())
|
||||
{
|
||||
return;
|
||||
}
|
||||
out.reserve(value.size());
|
||||
for (uint32_t i = 0, n = value.size(); i < n; ++i)
|
||||
{
|
||||
if (value[i].isString())
|
||||
{
|
||||
out.push_back(value[i].asString());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::string SerializeStringList(const StringList & list)
|
||||
{
|
||||
JsonValue jsonArray(JsonValueType::Array);
|
||||
for (const std::string & item : list)
|
||||
{
|
||||
jsonArray.Append(JsonValue(item));
|
||||
}
|
||||
return JsonStyledWriter().write(jsonArray);
|
||||
}
|
||||
|
||||
void ParseStringListFromStore(StringList & out, const char * setting)
|
||||
{
|
||||
out.clear();
|
||||
const std::string json = g_settings->GetString(setting);
|
||||
if (json.empty())
|
||||
{
|
||||
return;
|
||||
}
|
||||
JsonValue root;
|
||||
JsonReader reader;
|
||||
if (!reader.Parse(json.data(), json.data() + json.size(), root) || !root.isArray())
|
||||
{
|
||||
return;
|
||||
}
|
||||
LoadStringList(out, root);
|
||||
}
|
||||
};
|
||||
|
||||
void LoaderSettingChanged(const char * setting, void * /*userData*/)
|
||||
|
|
@ -150,6 +198,12 @@ void LoaderSettingChanged(const char * setting, void * /*userData*/)
|
|||
*loaderSetting.setting.intValue = g_settings->GetInt(setting);
|
||||
}
|
||||
break;
|
||||
case SettingType::StringListValue:
|
||||
if (loaderSetting.setting.stringList != nullptr)
|
||||
{
|
||||
ParseStringListFromStore(*loaderSetting.setting.stringList, setting);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
UNIMPLEMENTED();
|
||||
}
|
||||
|
|
@ -171,6 +225,9 @@ void SetupLoaderSetting(void)
|
|||
*loaderSetting.setting.intValue = loaderSetting.defaultValue.intValue;
|
||||
}
|
||||
break;
|
||||
case SettingType::StringListValue:
|
||||
loaderSetting.setting.stringList->clear();
|
||||
break;
|
||||
case SettingType::TitleIdStringListMapValue:
|
||||
loaderSetting.setting.titleIdStringListMap->clear();
|
||||
break;
|
||||
|
|
@ -207,6 +264,9 @@ void SetupLoaderSetting(void)
|
|||
*loaderSetting.setting.intValue = static_cast<int>(value.asInt64());
|
||||
}
|
||||
break;
|
||||
case SettingType::StringListValue:
|
||||
LoadStringList(*loaderSetting.setting.stringList, value);
|
||||
break;
|
||||
case SettingType::TitleIdStringListMapValue:
|
||||
LoadTitleIdStringListMap(*loaderSetting.setting.titleIdStringListMap, value);
|
||||
break;
|
||||
|
|
@ -233,6 +293,10 @@ void SetupLoaderSetting(void)
|
|||
g_settings->SetDefaultInt(loaderSetting.identifier, loaderSetting.defaultValue.intValue);
|
||||
g_settings->SetInt(loaderSetting.identifier, loaderSetting.setting.intValue != nullptr ? *loaderSetting.setting.intValue : loaderSetting.defaultValue.intValue);
|
||||
break;
|
||||
case SettingType::StringListValue:
|
||||
g_settings->SetDefaultString(loaderSetting.identifier, "[]");
|
||||
g_settings->SetString(loaderSetting.identifier, SerializeStringList(*loaderSetting.setting.stringList).c_str());
|
||||
break;
|
||||
default:
|
||||
UNIMPLEMENTED();
|
||||
}
|
||||
|
|
@ -283,6 +347,17 @@ void SaveLoaderSettings(void)
|
|||
JsonSetNestedValue(root, loaderSetting.json_path, *loaderSetting.setting.intValue);
|
||||
}
|
||||
break;
|
||||
case SettingType::StringListValue:
|
||||
if (loaderSetting.json_path != nullptr && !loaderSetting.setting.stringList->empty())
|
||||
{
|
||||
JsonValue jsonList(JsonValueType::Array);
|
||||
for (const std::string & item : *loaderSetting.setting.stringList)
|
||||
{
|
||||
jsonList.Append(JsonValue(item));
|
||||
}
|
||||
JsonSetNestedValue(root, loaderSetting.json_path, std::move(jsonList));
|
||||
}
|
||||
break;
|
||||
case SettingType::TitleIdStringListMapValue:
|
||||
{
|
||||
JsonValue value = SaveTitleIdStringListMap(*loaderSetting.setting.titleIdStringListMap);
|
||||
|
|
@ -330,6 +405,14 @@ LoaderSetting::LoaderSetting(const char * id, const char * path, int * val, int
|
|||
defaultValue.intValue = defaultValue_;
|
||||
}
|
||||
|
||||
LoaderSetting::LoaderSetting(const char * id, const char * path, StringList * val) :
|
||||
identifier(id),
|
||||
json_path(path),
|
||||
settingType(SettingType::StringListValue)
|
||||
{
|
||||
setting.stringList = val;
|
||||
}
|
||||
|
||||
LoaderSetting::LoaderSetting(const char * path, TitleIdStringListMap * val) :
|
||||
identifier(nullptr),
|
||||
json_path(path),
|
||||
|
|
|
|||
|
|
@ -6,12 +6,14 @@
|
|||
#include <vector>
|
||||
|
||||
using TitleIdStringListMap = std::map<uint64_t, std::vector<std::string>>;
|
||||
using StringList = std::vector<std::string>;
|
||||
|
||||
struct LoaderSettings
|
||||
{
|
||||
bool checkForUpdatedFirmware;
|
||||
|
||||
// Add-Ons
|
||||
StringList addOnDirectories;
|
||||
TitleIdStringListMap disabled_addons;
|
||||
};
|
||||
|
||||
|
|
@ -20,5 +22,6 @@ extern LoaderSettings loaderSettings;
|
|||
void SetupLoaderSetting(void);
|
||||
void CleanupLoaderSetting(void);
|
||||
void SaveLoaderSettings(void);
|
||||
void RefreshConfiguredAddOnDirectories(void);
|
||||
|
||||
void SetLoaderDisabledAddons(uint64_t program_id, std::vector<std::string> names);
|
||||
|
|
|
|||
|
|
@ -6,4 +6,5 @@ constexpr const char * CheckForUpdatedFirmware = "nxloader:CheckForUpdatedFirmwa
|
|||
constexpr const char * FirmwareInstallCurrent = "nxloader:FirmwareInstallCurrent";
|
||||
constexpr const char * FirmwareInstallTotal = "nxloader:FirmwareInstallTotal";
|
||||
constexpr const char * Has39BitAddressSpace = "nxloader:Has39BitAddressSpace";
|
||||
constexpr const char * AddOnDirectories = "nxloader:AddOnDirectories";
|
||||
} // namespace NXLoaderSetting
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
#include "system_loader.h"
|
||||
#include "core/core.h"
|
||||
#include "core/file_sys/card_image.h"
|
||||
#include "core/file_sys/common_funcs.h"
|
||||
#include "core/file_sys/content_archive.h"
|
||||
#include "core/file_sys/filesystem.h"
|
||||
#include "core/file_sys/control_metadata.h"
|
||||
|
|
@ -30,6 +31,7 @@
|
|||
#include <nxemu-core/settings/identifiers.h>
|
||||
#include <random>
|
||||
#include <yuzu_common/fs/fs.h>
|
||||
#include <yuzu_common/fs/filesystem_interfaces.h>
|
||||
|
||||
extern IModuleSettings * g_settings;
|
||||
extern IModuleNotification * g_notify;
|
||||
|
|
@ -78,7 +80,7 @@ std::string GetRegisteredNcaFilename(const std::string & source_name)
|
|||
if (dot != std::string::npos)
|
||||
{
|
||||
ext = ext.substr(dot);
|
||||
std::transform(ext.begin(), ext.end(), ext.begin(), [](unsigned char c) { return static_cast<char>(std::tolower(c)); });
|
||||
std::transform(ext.begin(), ext.end(), ext.begin(), [](unsigned char c) { return (char)std::tolower(c); });
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
@ -98,8 +100,7 @@ std::string GetRegisteredNcaFilename(const std::string & source_name)
|
|||
|
||||
std::string ToLowerAscii(std::string value)
|
||||
{
|
||||
std::transform(value.begin(), value.end(), value.begin(),
|
||||
[](unsigned char c) { return static_cast<char>(std::tolower(c)); });
|
||||
std::transform(value.begin(), value.end(), value.begin(), [](unsigned char c) { return (char)std::tolower(c); });
|
||||
return value;
|
||||
}
|
||||
|
||||
|
|
@ -386,7 +387,7 @@ bool InstallFirmwareFilesToRegistered(::FileSystemController & fs_controller, co
|
|||
}
|
||||
|
||||
int32_t copied = 0;
|
||||
g_settings->SetInt(NXLoaderSetting::FirmwareInstallTotal, static_cast<int32_t>(sources.size()));
|
||||
g_settings->SetInt(NXLoaderSetting::FirmwareInstallTotal, (int32_t)sources.size());
|
||||
g_settings->SetInt(NXLoaderSetting::FirmwareInstallCurrent, 0);
|
||||
|
||||
for (const FirmwareInstallSource & source : sources)
|
||||
|
|
@ -434,6 +435,57 @@ bool HasSupportedFileExtension(const char * fileName)
|
|||
return false;
|
||||
}
|
||||
|
||||
bool IsAddOnContentExtension(const char * fileName)
|
||||
{
|
||||
static const char * supported_extensions[] = {
|
||||
"nca",
|
||||
"dxci",
|
||||
"dnsp",
|
||||
};
|
||||
|
||||
std::string ext = Path(fileName).GetExtension();
|
||||
std::transform(ext.begin(), ext.end(), ext.begin(), ::tolower);
|
||||
for (const char * supported : supported_extensions)
|
||||
{
|
||||
if (ext == supported)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void RegisterMatchingAddOnEntriesFromNsp(const FileSys::NSP & nsp, uint64_t base_program_id, IManualContentProvider & provider)
|
||||
{
|
||||
if (nsp.GetStatus() != LoaderResultStatus::Success)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
for (const auto & title : nsp.GetNCAs())
|
||||
{
|
||||
if (FileSys::GetBaseTitleID(title.first) != base_program_id)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
for (const auto & entry : title.second)
|
||||
{
|
||||
if (entry.first.first != LoaderTitleType::Update && entry.first.first != LoaderTitleType::AOC)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
FileSys::VirtualFile file = entry.second->BaseFile();
|
||||
if (!file)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
provider.AddEntry(entry.first.first, entry.first.second, title.first, std::make_unique<VirtualFileImpl>(file).release());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // Anonymous namespace
|
||||
|
||||
class HostFilesystemImpl final :
|
||||
|
|
@ -576,6 +628,7 @@ bool Systemloader::LoadRom(const char * fileName)
|
|||
}
|
||||
uint64_t program_id = 0;
|
||||
const LoaderResultStatus res = app_loader->ReadProgramId(program_id);
|
||||
RegisterConfiguredAddOnDirectories(res == LoaderResultStatus::Success ? program_id : 0);
|
||||
if (res == LoaderResultStatus::Success && file_type == LoaderFileType::NCA)
|
||||
{
|
||||
UNIMPLEMENTED();
|
||||
|
|
@ -583,10 +636,22 @@ bool Systemloader::LoadRom(const char * fileName)
|
|||
else if (res == LoaderResultStatus::Success && (file_type == LoaderFileType::XCI || file_type == LoaderFileType::NSP))
|
||||
{
|
||||
const auto nsp = file_type == LoaderFileType::NSP ? std::make_shared<FileSys::NSP>(impl->m_file) : FileSys::XCI{impl->m_file}.GetSecurePartitionNSP();
|
||||
FileSys::ManualContentProvider & manual = impl->m_manualContentProvider->Provider();
|
||||
for (const auto & title : nsp->GetNCAs())
|
||||
{
|
||||
for (const auto & entry : title.second)
|
||||
{
|
||||
if (entry.first.first == LoaderTitleType::Update &&
|
||||
manual.HasEntry(title.first, LoaderContentRecordType::Program))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
if (entry.first.first == LoaderTitleType::AOC &&
|
||||
manual.HasEntry(title.first, entry.first.second))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
FileSys::VirtualFile file = entry.second->BaseFile();
|
||||
impl->m_manualContentProvider->AddEntry(entry.first.first, entry.first.second, title.first, std::make_unique<VirtualFileImpl>(file).release());
|
||||
}
|
||||
|
|
@ -833,16 +898,96 @@ IManualContentProvider & Systemloader::ManualContentProvider()
|
|||
return *(impl->m_manualContentProvider.get());
|
||||
}
|
||||
|
||||
void Systemloader::RegisterConfiguredAddOnDirectories(uint64_t program_id)
|
||||
{
|
||||
IManualContentProvider & provider = ManualContentProvider();
|
||||
provider.ClearAllEntries();
|
||||
|
||||
if (program_id == 0 || loaderSettings.addOnDirectories.empty())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
const uint64_t base_program_id = FileSys::GetBaseTitleID(program_id);
|
||||
FileSys::VirtualFilesystem & vfs = impl->m_virtualFilesystem;
|
||||
|
||||
for (const std::string & dir : loaderSettings.addOnDirectories)
|
||||
{
|
||||
if (dir.empty())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
const auto callback = [&](const std::filesystem::directory_entry & entry) -> bool {
|
||||
const std::filesystem::path & path = entry.path();
|
||||
if (!Common::FS::IsFile(path))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
const std::string physical_name = Common::FS::PathToUTF8String(path);
|
||||
if (!IsAddOnContentExtension(physical_name.c_str()))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
FileSys::VirtualFile file = vfs->OpenFile(physical_name, VirtualFileOpenMode::Read);
|
||||
if (!file)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
const std::string ext = ToLowerAscii(std::string(Path(physical_name).GetExtension()));
|
||||
if (ext == "dnsp")
|
||||
{
|
||||
RegisterMatchingAddOnEntriesFromNsp(FileSys::NSP(file), base_program_id, provider);
|
||||
}
|
||||
else if (ext == "dxci")
|
||||
{
|
||||
const FileSys::XCI xci(file);
|
||||
if (xci.GetStatus() == LoaderResultStatus::Success)
|
||||
{
|
||||
const std::shared_ptr<FileSys::NSP> nsp = xci.GetSecurePartitionNSP();
|
||||
if (nsp)
|
||||
{
|
||||
RegisterMatchingAddOnEntriesFromNsp(*nsp, base_program_id, provider);
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (ext == "nca")
|
||||
{
|
||||
const FileSys::NCA nca(file);
|
||||
if (nca.GetStatus() != LoaderResultStatus::Success)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
const uint64_t title_id = nca.GetTitleId();
|
||||
if (FileSys::GetBaseTitleID(title_id) != base_program_id || title_id == base_program_id)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
const LoaderTitleType title_type = (title_id & 0x800) != 0 ? LoaderTitleType::Update : LoaderTitleType::AOC;
|
||||
provider.AddEntry(title_type, FileSys::GetCRTypeFromNCAType(nca.GetType()), title_id, std::make_unique<VirtualFileImpl>(file).release());
|
||||
}
|
||||
return true;
|
||||
};
|
||||
|
||||
Common::FS::IterateDirEntriesRecursively(dir, callback, Common::FS::DirEntryFilter::File);
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t Systemloader::GetGamePatches(uint64_t program_id, GamePatchInfo * patches, uint32_t maxCount)
|
||||
{
|
||||
RegisterConfiguredAddOnDirectories(program_id);
|
||||
const FileSys::PatchManager pm(program_id, GetFileSystemController(), GetContentProvider());
|
||||
const std::vector<FileSys::Patch> list = pm.GetPatches();
|
||||
if (patches == nullptr || maxCount == 0)
|
||||
{
|
||||
return static_cast<uint32_t>(list.size());
|
||||
return (uint32_t)list.size();
|
||||
}
|
||||
|
||||
const uint32_t count = std::min(maxCount, static_cast<uint32_t>(list.size()));
|
||||
const uint32_t count = std::min(maxCount, (uint32_t)list.size());
|
||||
for (uint32_t i = 0; i < count; ++i)
|
||||
{
|
||||
patches[i].enabled = list[i].enabled;
|
||||
|
|
@ -907,7 +1052,7 @@ bool Systemloader::Impl::InstallFirmwareFromFolder(const char * utf8_folder_path
|
|||
continue;
|
||||
}
|
||||
std::string ext = entry.path().extension().string();
|
||||
std::transform(ext.begin(), ext.end(), ext.begin(), [](unsigned char c) { return static_cast<char>(std::tolower(c)); });
|
||||
std::transform(ext.begin(), ext.end(), ext.begin(), [](unsigned char c) { return (char)std::tolower(c); });
|
||||
if (ext == ".dnca")
|
||||
{
|
||||
nca_paths.push_back(entry.path());
|
||||
|
|
@ -1115,7 +1260,7 @@ bool Systemloader::Impl::QueryFirmwarePackage(const char * utf8_path, FirmwareVe
|
|||
continue;
|
||||
}
|
||||
std::string ext = entry.path().extension().string();
|
||||
std::transform(ext.begin(), ext.end(), ext.begin(), [](unsigned char c) { return static_cast<char>(std::tolower(c)); });
|
||||
std::transform(ext.begin(), ext.end(), ext.begin(), [](unsigned char c) { return (char)std::tolower(c); });
|
||||
if (ext == ".dnca")
|
||||
{
|
||||
nca_paths.push_back(entry.path());
|
||||
|
|
@ -1240,7 +1385,7 @@ uint32_t Systemloader::GetInstalledFirmwareDisplayVersion(char * buffer, uint32_
|
|||
return 0;
|
||||
}
|
||||
|
||||
const uint32_t copy_len = static_cast<uint32_t>(std::min(version.size(), static_cast<size_t>(buffer_size - 1)));
|
||||
const uint32_t copy_len = (uint32_t)(std::min(version.size(), (size_t)(buffer_size - 1)));
|
||||
std::memcpy(buffer, version.data(), copy_len);
|
||||
buffer[copy_len] = '\0';
|
||||
return copy_len;
|
||||
|
|
|
|||
|
|
@ -50,6 +50,7 @@ public:
|
|||
uint32_t GetGamePatches(uint64_t program_id, GamePatchInfo * patches, uint32_t maxCount) override;
|
||||
void SetDisabledAddons(uint64_t program_id, const char * const * names, uint32_t count) override;
|
||||
bool IsAddonDisabled(uint64_t program_id, const char * name) const override;
|
||||
void RegisterConfiguredAddOnDirectories(uint64_t program_id);
|
||||
|
||||
private:
|
||||
Systemloader() = delete;
|
||||
|
|
|
|||
|
|
@ -2144,6 +2144,7 @@ bool SciterMainWindow::OnTimer(SCITER_ELEMENT /*element*/, uint32_t * timerId)
|
|||
m_gameConfig.reset(new GameConfig(m_sciterUI, m_modules));
|
||||
m_gameConfig->Display((void *)m_window->GetHandle(), path.c_str());
|
||||
}
|
||||
return false;
|
||||
}
|
||||
else if (timerId == (uint32_t *)TIMER_DEFERRED_STOP_GAME)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@
|
|||
#include "settings/ui_settings.h"
|
||||
#include "system_config.h"
|
||||
#include "system_config_audio.h"
|
||||
#include "system_config_game_browser.h"
|
||||
#include "system_config_general.h"
|
||||
#include "system_config_graphics.h"
|
||||
#include "system_config_profiles.h"
|
||||
|
|
@ -73,6 +74,10 @@ void SystemConfig::Display(void * parentWindow, const char * startPage)
|
|||
{
|
||||
m_systemConfigGeneral->SetInitialPage(subPage.c_str());
|
||||
}
|
||||
else if (initialPage == "GameBrowser" && m_systemConfigGameBrowser)
|
||||
{
|
||||
m_systemConfigGameBrowser->SetInitialPage(subPage.c_str());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -331,6 +336,10 @@ void SystemConfig::PageNavCreatedPage(const std::string & pageName, SCITER_ELEME
|
|||
{
|
||||
m_systemConfigGeneral.reset(new SystemConfigGeneral(m_sciterUI, *this, *m_window, page));
|
||||
}
|
||||
else if (pageName == "GameBrowser")
|
||||
{
|
||||
m_systemConfigGameBrowser.reset(new SystemConfigGameBrowser(m_sciterUI, *this, *m_window, page));
|
||||
}
|
||||
else if (pageName == "Profiles")
|
||||
{
|
||||
m_systemConfigProfiles.reset(new SystemConfigProfiles(m_sciterUI, *this, m_modules, *m_window, page));
|
||||
|
|
@ -358,6 +367,10 @@ bool SystemConfig::OnClick(SCITER_ELEMENT element, SCITER_ELEMENT /*source*/, ui
|
|||
{
|
||||
m_systemConfigGeneral->SaveSetting();
|
||||
}
|
||||
if (m_systemConfigGameBrowser)
|
||||
{
|
||||
m_systemConfigGameBrowser->SaveSetting();
|
||||
}
|
||||
if (m_systemConfigProfiles)
|
||||
{
|
||||
m_systemConfigProfiles->SaveSetting();
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@ class SciterElement;
|
|||
class SystemConfigAudio;
|
||||
class SystemConfigGraphics;
|
||||
class SystemConfigGeneral;
|
||||
class SystemConfigGameBrowser;
|
||||
class SystemConfigProfiles;
|
||||
class SystemConfigSystem;
|
||||
class ConfigSetting;
|
||||
|
|
@ -87,5 +88,6 @@ private:
|
|||
std::unique_ptr<SystemConfigGraphics> m_systemConfigGraphics;
|
||||
std::unique_ptr<SystemConfigAudio> m_systemConfigAudio;
|
||||
std::unique_ptr<SystemConfigGeneral> m_systemConfigGeneral;
|
||||
std::unique_ptr<SystemConfigGameBrowser> m_systemConfigGameBrowser;
|
||||
std::unique_ptr<SystemConfigProfiles> m_systemConfigProfiles;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,19 +1,21 @@
|
|||
#include "config_setting.h"
|
||||
#include "settings/ui_identifiers.h"
|
||||
#include "settings/ui_settings.h"
|
||||
#include "system_config.h"
|
||||
#include "system_config_game_browser.h"
|
||||
#include <common/path.h>
|
||||
#include <nxemu-core/settings/identifiers.h>
|
||||
#include <nxemu-loader/loader_settings_identifiers.h>
|
||||
#include <widgets/list_box.h>
|
||||
|
||||
namespace
|
||||
{
|
||||
static ConfigSetting gameBrowserSettings[] = {
|
||||
static ConfigSetting gamesSettings[] = {
|
||||
ConfigSetting(ConfigSetting::Slider, "myGamesIconSize", true, NXUISetting::MyGameIconSize),
|
||||
ConfigSetting(ConfigSetting::CheckBox, "ConfirmBeforeStopping", true, NXUISetting::ConfirmBeforeStopping),
|
||||
ConfigSetting(ConfigSetting::ListBox, "gameDirectoryList", true, NXUISetting::GameDirectories),
|
||||
};
|
||||
|
||||
static ConfigSetting addOnsSettings[] = {
|
||||
ConfigSetting(ConfigSetting::ListBox, "addOnDirectoryList", true, NXLoaderSetting::AddOnDirectories),
|
||||
};
|
||||
}
|
||||
|
||||
SystemConfigGameBrowser::SystemConfigGameBrowser(ISciterUI & sciterUI, SystemConfig & config, ISciterWindow & window, SciterElement page) :
|
||||
|
|
@ -22,28 +24,76 @@ SystemConfigGameBrowser::SystemConfigGameBrowser(ISciterUI & sciterUI, SystemCon
|
|||
m_window(window),
|
||||
m_page(page)
|
||||
{
|
||||
m_config.SetupPage(page, gameBrowserSettings, sizeof(gameBrowserSettings) / sizeof(gameBrowserSettings[0]));
|
||||
|
||||
SciterElement root(m_window.GetRootElement());
|
||||
if (root.IsValid())
|
||||
SciterElement pageNav = page.GetElementByID("GameBrowserTabNav");
|
||||
std::shared_ptr<void> interfacePtr = pageNav.IsValid() ? m_sciterUI.GetElementInterface(pageNav, IID_IPAGENAV) : nullptr;
|
||||
if (interfacePtr)
|
||||
{
|
||||
SciterElement addButton = root.GetElementByID("gameDirectoryAdd");
|
||||
m_sciterUI.AttachHandler(addButton, IID_ICLICKSINK, (IClickSink *)this);
|
||||
SciterElement removeButton = root.GetElementByID("gameDirectoryRemove");
|
||||
m_sciterUI.AttachHandler(removeButton, IID_ICLICKSINK, (IClickSink *)this);
|
||||
m_pageNav = std::static_pointer_cast<IPageNav>(interfacePtr);
|
||||
m_pageNav->AddSink(this);
|
||||
}
|
||||
}
|
||||
|
||||
void SystemConfigGameBrowser::SetInitialPage(const char * path)
|
||||
{
|
||||
if (path == nullptr || path[0] == '\0' || !m_pageNav)
|
||||
{
|
||||
return;
|
||||
}
|
||||
m_pageNav->SetCurrentPage(path);
|
||||
}
|
||||
|
||||
void SystemConfigGameBrowser::SetupGamesPage(SciterElement page)
|
||||
{
|
||||
m_gamesPage = page;
|
||||
m_config.SetupPage(page, gamesSettings, sizeof(gamesSettings) / sizeof(gamesSettings[0]));
|
||||
m_sciterUI.AttachHandler(page.GetElementByID("gameDirectoryAdd"), IID_ICLICKSINK, (IClickSink *)this);
|
||||
m_sciterUI.AttachHandler(page.GetElementByID("gameDirectoryRemove"), IID_ICLICKSINK, (IClickSink *)this);
|
||||
}
|
||||
|
||||
void SystemConfigGameBrowser::SetupAddOnsPage(SciterElement page)
|
||||
{
|
||||
m_addOnsPage = page;
|
||||
m_config.SetupPage(page, addOnsSettings, sizeof(addOnsSettings) / sizeof(addOnsSettings[0]));
|
||||
m_sciterUI.AttachHandler(page.GetElementByID("addOnDirectoryAdd"), IID_ICLICKSINK, (IClickSink *)this);
|
||||
m_sciterUI.AttachHandler(page.GetElementByID("addOnDirectoryRemove"), IID_ICLICKSINK, (IClickSink *)this);
|
||||
}
|
||||
|
||||
bool SystemConfigGameBrowser::PageNavChangeFrom(const std::string & /*pageName*/, SCITER_ELEMENT /*pageNav*/)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SystemConfigGameBrowser::PageNavChangeTo(const std::string & /*pageName*/, SCITER_ELEMENT /*pageNav*/)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
void SystemConfigGameBrowser::PageNavCreatedPage(const std::string & pageName, SCITER_ELEMENT page)
|
||||
{
|
||||
if (pageName == "Games")
|
||||
{
|
||||
SetupGamesPage(page);
|
||||
}
|
||||
else if (pageName == "AddOns")
|
||||
{
|
||||
SetupAddOnsPage(page);
|
||||
}
|
||||
}
|
||||
|
||||
void SystemConfigGameBrowser::PageNavPageChanged(const std::string & /*pageName*/, SCITER_ELEMENT /*pageNav*/)
|
||||
{
|
||||
}
|
||||
|
||||
bool SystemConfigGameBrowser::OnClick(SCITER_ELEMENT element, SCITER_ELEMENT /*source*/, uint32_t /*reason*/)
|
||||
{
|
||||
SciterElement clickElem(element);
|
||||
std::string elementID = clickElem.GetAttributeByName("id");
|
||||
const std::string elementID = clickElem.GetAttributeByName("id");
|
||||
if (elementID == "gameDirectoryAdd")
|
||||
{
|
||||
Path newDirectory = Path().BrowseForDirectory((void*)m_window.GetHandle(), "Select Game Directory");
|
||||
if (newDirectory.DirectoryExists())
|
||||
Path newDirectory = Path().BrowseForDirectory((void *)m_window.GetHandle(), "Select Game Directory");
|
||||
if (newDirectory.DirectoryExists() && m_gamesPage.IsValid())
|
||||
{
|
||||
std::shared_ptr<void> interfacePtr = m_sciterUI.GetElementInterface(m_page.GetElementByID("gameDirectoryList"), IID_ILISTBOX);
|
||||
std::shared_ptr<void> interfacePtr = m_sciterUI.GetElementInterface(m_gamesPage.GetElementByID("gameDirectoryList"), IID_ILISTBOX);
|
||||
if (interfacePtr)
|
||||
{
|
||||
std::shared_ptr<IListBox> listBox = std::static_pointer_cast<IListBox>(interfacePtr);
|
||||
|
|
@ -53,47 +103,40 @@ bool SystemConfigGameBrowser::OnClick(SCITER_ELEMENT element, SCITER_ELEMENT /*s
|
|||
}
|
||||
else if (elementID == "gameDirectoryRemove")
|
||||
{
|
||||
RemoveSelectedDirectory();
|
||||
RemoveSelectedDirectory(m_gamesPage, "gameDirectoryList");
|
||||
}
|
||||
else if (elementID == "addOnDirectoryAdd")
|
||||
{
|
||||
Path newDirectory = Path().BrowseForDirectory((void *)m_window.GetHandle(), "Select Add-On Directory");
|
||||
if (newDirectory.DirectoryExists() && m_addOnsPage.IsValid())
|
||||
{
|
||||
std::shared_ptr<void> interfacePtr = m_sciterUI.GetElementInterface(m_addOnsPage.GetElementByID("addOnDirectoryList"), IID_ILISTBOX);
|
||||
if (interfacePtr)
|
||||
{
|
||||
std::shared_ptr<IListBox> listBox = std::static_pointer_cast<IListBox>(interfacePtr);
|
||||
listBox->AddItem(newDirectory, newDirectory);
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (elementID == "addOnDirectoryRemove")
|
||||
{
|
||||
RemoveSelectedDirectory(m_addOnsPage, "addOnDirectoryList");
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void SystemConfigGameBrowser::SelectDirectoryItem(const SciterElement & gameDirectoryList, SCITER_ELEMENT source)
|
||||
void SystemConfigGameBrowser::RemoveSelectedDirectory(const SciterElement & page, const char * listId)
|
||||
{
|
||||
SciterElement target = source, directoryItem;
|
||||
while (target.IsValid() && target != gameDirectoryList)
|
||||
{
|
||||
std::string className = target.GetAttribute("class");
|
||||
if (!className.empty())
|
||||
{
|
||||
std::string searchIn = " " + className + " ";
|
||||
if (searchIn.find(" directory-item ") != std::string::npos)
|
||||
{
|
||||
directoryItem = target;
|
||||
break;
|
||||
}
|
||||
}
|
||||
target = target.GetParent();
|
||||
}
|
||||
|
||||
if (target.IsValid())
|
||||
{
|
||||
for (uint32_t i = 0, n = gameDirectoryList.GetChildCount(); i < n; i++)
|
||||
{
|
||||
SciterElement(gameDirectoryList.GetChild(i)).SetState(0, SciterElement::STATE_CHECKED, true);
|
||||
}
|
||||
target.SetState(SciterElement::STATE_CHECKED, 0, true);
|
||||
}
|
||||
}
|
||||
|
||||
void SystemConfigGameBrowser::RemoveSelectedDirectory()
|
||||
{
|
||||
SciterElement gameDirectoryList = m_page.GetElementByID("gameDirectoryList");
|
||||
if (!gameDirectoryList.IsValid())
|
||||
if (!page.IsValid())
|
||||
{
|
||||
return;
|
||||
}
|
||||
std::shared_ptr<void> interfacePtr = m_sciterUI.GetElementInterface(m_page.GetElementByID("gameDirectoryList"), IID_ILISTBOX);
|
||||
SciterElement directoryList = page.GetElementByID(listId);
|
||||
if (!directoryList.IsValid())
|
||||
{
|
||||
return;
|
||||
}
|
||||
std::shared_ptr<void> interfacePtr = m_sciterUI.GetElementInterface(directoryList, IID_ILISTBOX);
|
||||
if (!interfacePtr)
|
||||
{
|
||||
return;
|
||||
|
|
@ -104,5 +147,12 @@ void SystemConfigGameBrowser::RemoveSelectedDirectory()
|
|||
|
||||
void SystemConfigGameBrowser::SaveSetting(void)
|
||||
{
|
||||
m_config.SavePage(m_page, gameBrowserSettings, sizeof(gameBrowserSettings) / sizeof(gameBrowserSettings[0]));
|
||||
if (m_gamesPage.IsValid())
|
||||
{
|
||||
m_config.SavePage(m_gamesPage, gamesSettings, sizeof(gamesSettings) / sizeof(gamesSettings[0]));
|
||||
}
|
||||
if (m_addOnsPage.IsValid())
|
||||
{
|
||||
m_config.SavePage(m_addOnsPage, addOnsSettings, sizeof(addOnsSettings) / sizeof(addOnsSettings[0]));
|
||||
}
|
||||
}
|
||||
|
|
@ -7,6 +7,7 @@
|
|||
class SystemConfig;
|
||||
|
||||
class SystemConfigGameBrowser :
|
||||
public IPagesSink,
|
||||
public IClickSink
|
||||
{
|
||||
public:
|
||||
|
|
@ -14,20 +15,31 @@ public:
|
|||
~SystemConfigGameBrowser() = default;
|
||||
|
||||
void SaveSetting(void);
|
||||
void SetInitialPage(const char * path);
|
||||
|
||||
// IPagesSink
|
||||
bool PageNavChangeFrom(const std::string & pageName, SCITER_ELEMENT pageNav) override;
|
||||
bool PageNavChangeTo(const std::string & pageName, SCITER_ELEMENT pageNav) override;
|
||||
void PageNavCreatedPage(const std::string & pageName, SCITER_ELEMENT page) override;
|
||||
void PageNavPageChanged(const std::string & pageName, SCITER_ELEMENT pageNav) override;
|
||||
|
||||
// IClickSink
|
||||
bool OnClick(SCITER_ELEMENT element, SCITER_ELEMENT source, uint32_t reason) override;
|
||||
|
||||
private:
|
||||
SystemConfigGameBrowser() = delete;
|
||||
SystemConfigGameBrowser(const SystemConfigGameBrowser&) = delete;
|
||||
SystemConfigGameBrowser& operator=(const SystemConfigGameBrowser&) = delete;
|
||||
SystemConfigGameBrowser(const SystemConfigGameBrowser &) = delete;
|
||||
SystemConfigGameBrowser & operator=(const SystemConfigGameBrowser &) = delete;
|
||||
|
||||
void SelectDirectoryItem(const SciterElement & gameDirectoryList, SCITER_ELEMENT source);
|
||||
void RemoveSelectedDirectory();
|
||||
void SetupGamesPage(SciterElement page);
|
||||
void SetupAddOnsPage(SciterElement page);
|
||||
void RemoveSelectedDirectory(const SciterElement & page, const char * listId);
|
||||
|
||||
ISciterUI & m_sciterUI;
|
||||
SystemConfig & m_config;
|
||||
ISciterWindow & m_window;
|
||||
SciterElement m_page;
|
||||
SciterElement m_gamesPage;
|
||||
SciterElement m_addOnsPage;
|
||||
std::shared_ptr<IPageNav> m_pageNav;
|
||||
};
|
||||
|
|
@ -1,4 +1,3 @@
|
|||
#include "system_config_game_browser.h"
|
||||
#include "system_config_general.h"
|
||||
#include "system_config_hotkeys.h"
|
||||
#include "system_config_logging.h"
|
||||
|
|
@ -34,10 +33,6 @@ void SystemConfigGeneral::SetInitialPage(const char * path)
|
|||
|
||||
void SystemConfigGeneral::SaveSetting(void)
|
||||
{
|
||||
if (m_systemConfigGameBrowser)
|
||||
{
|
||||
m_systemConfigGameBrowser->SaveSetting();
|
||||
}
|
||||
if (m_systemConfigLogging)
|
||||
{
|
||||
m_systemConfigLogging->SaveSetting();
|
||||
|
|
@ -64,11 +59,7 @@ bool SystemConfigGeneral::PageNavChangeTo(const std::string & /*pageName*/, SCIT
|
|||
|
||||
void SystemConfigGeneral::PageNavCreatedPage(const std::string & pageName, SCITER_ELEMENT page)
|
||||
{
|
||||
if (pageName == "GameBrowser")
|
||||
{
|
||||
m_systemConfigGameBrowser.reset(new SystemConfigGameBrowser(m_sciterUI, m_config, m_window, page));
|
||||
}
|
||||
else if (pageName == "Logging")
|
||||
if (pageName == "Logging")
|
||||
{
|
||||
m_systemConfigLogging.reset(new SystemConfigLogging(m_sciterUI, m_config, m_window, page));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,7 +4,6 @@
|
|||
#include <widgets/page_nav.h>
|
||||
|
||||
class SystemConfig;
|
||||
class SystemConfigGameBrowser;
|
||||
class SystemConfigHotkeys;
|
||||
class SystemConfigLogging;
|
||||
class SystemConfigSystem;
|
||||
|
|
@ -36,7 +35,6 @@ private:
|
|||
SciterElement m_page;
|
||||
std::shared_ptr<IPageNav> m_pageNav;
|
||||
std::unique_ptr<SystemConfigLogging> m_systemConfigLogging;
|
||||
std::unique_ptr<SystemConfigGameBrowser> m_systemConfigGameBrowser;
|
||||
std::unique_ptr<SystemConfigHotkeys> m_systemConfigHotkeys;
|
||||
std::unique_ptr<SystemConfigSystem> m_systemConfigSystem;
|
||||
};
|
||||
|
|
@ -249,12 +249,6 @@ bool WidgetRomBrowser::OnEvent(SCITER_ELEMENT element, SCITER_ELEMENT source, ui
|
|||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
if (event_code == (uint32_t)SciterBehaviorEvent::MenuItemClick)
|
||||
{
|
||||
__debugbreak();
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
@ -273,7 +267,7 @@ bool WidgetRomBrowser::OnClick(SCITER_ELEMENT element, SCITER_ELEMENT source, ui
|
|||
std::string className = el.GetAttribute("class");
|
||||
if (strcmp(className.c_str(), "config-button") == 0 && m_window != nullptr)
|
||||
{
|
||||
m_window->ShowConfig("General:GameBrowser");
|
||||
m_window->ShowConfig("GameBrowser");
|
||||
return true;
|
||||
}
|
||||
else if (strcmp(className.c_str(), "rom-card") == 0)
|
||||
|
|
|
|||
Loading…
Reference in New Issue