Add rom browser
This commit is contained in:
parent
ff9dbd8179
commit
0919cd79ed
|
|
@ -37,12 +37,144 @@
|
|||
border: 1px solid #434242;
|
||||
padding: 1em;
|
||||
}
|
||||
|
||||
|
||||
#RomBrowserPanel,
|
||||
#LoadingPanel
|
||||
{
|
||||
display: none;
|
||||
width: 100%%;
|
||||
height: 100%%;
|
||||
display: none;
|
||||
}
|
||||
|
||||
rombrowser
|
||||
{
|
||||
display: block;
|
||||
width: 100%%;
|
||||
height: 100%%;
|
||||
padding: 0.5rem 1rem;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
rombrowser .games-title
|
||||
{
|
||||
flow: horizontal;
|
||||
}
|
||||
|
||||
rombrowser .buttons-container
|
||||
{
|
||||
padding: 0.2rem 0 0;
|
||||
}
|
||||
|
||||
rombrowser .config-button svg
|
||||
{
|
||||
width: 1.5rem;
|
||||
height: 1.5rem;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
rombrowser .config-button .outline
|
||||
{
|
||||
fill: #eff0f1;
|
||||
}
|
||||
|
||||
rombrowser .config-button:hover .outline
|
||||
{
|
||||
fill: #232629;
|
||||
}
|
||||
|
||||
rombrowser .config-button
|
||||
{
|
||||
padding: 0.2rem;
|
||||
}
|
||||
|
||||
rombrowser .config-button:hover
|
||||
{
|
||||
background-color: #b3d9f6;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
rombrowser .games-title h1
|
||||
{
|
||||
font-family: 'Segoe UI', Arial, sans-serif;
|
||||
font-size: 1.5rem;
|
||||
font-weight: 400;
|
||||
color: #eff0f1;
|
||||
padding: 0 0 0.6rem;
|
||||
margin: 0;
|
||||
width:*
|
||||
}
|
||||
|
||||
rombrowser .games-grid
|
||||
{
|
||||
height: 100%%;
|
||||
flow:horizontal-wrap;
|
||||
border-spacing: 1rem;
|
||||
overflow-y: auto;
|
||||
overflow-x: hidden;
|
||||
}
|
||||
|
||||
rombrowser .games-grid[data-size="1"]
|
||||
{
|
||||
border-spacing: 0.5rem;
|
||||
}
|
||||
|
||||
rombrowser .rom-icon
|
||||
{
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
display: block;
|
||||
border-radius: 5px;
|
||||
}
|
||||
|
||||
rombrowser .rom-card-content
|
||||
{
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
rombrowser .rom-card
|
||||
{
|
||||
cursor: pointer;
|
||||
border-radius: 8px;
|
||||
border: 3px solid transparent;
|
||||
}
|
||||
|
||||
rombrowser .rom-card:visited
|
||||
{
|
||||
border: 3px solid #76B9ED;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
rombrowser .games-grid[data-size="1"] .rom-card
|
||||
{
|
||||
width: 6rem;
|
||||
height: 6rem;
|
||||
}
|
||||
|
||||
rombrowser .games-grid[data-size="2"] .rom-card
|
||||
{
|
||||
width: 8rem;
|
||||
height: 8rem;
|
||||
}
|
||||
|
||||
rombrowser .games-grid[data-size="3"] .rom-card
|
||||
{
|
||||
width: 10rem;
|
||||
height: 10rem;
|
||||
}
|
||||
|
||||
rombrowser .games-grid[data-size="4"] .rom-card
|
||||
{
|
||||
width: 13rem;
|
||||
height: 13rem;
|
||||
}
|
||||
|
||||
#LoadingPanel .loading-container
|
||||
{
|
||||
width: 100%%;
|
||||
height: 100%%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
background-color: black;
|
||||
|
|
|
|||
|
|
@ -14,14 +14,18 @@
|
|||
</header>
|
||||
<mainmenu id="MainMenu" />
|
||||
<div id="MainContents">
|
||||
<div id="RomBrowserPanel">
|
||||
<rombrowser />
|
||||
</div>
|
||||
<div id="LoadingPanel">
|
||||
<div class="loading-text">
|
||||
<span class="text">Loading</span>
|
||||
<div class="dot-pulse-container">
|
||||
<div class="dot-pulse"></div>
|
||||
<div class="dot-pulse"></div>
|
||||
<div class="dot-pulse"></div>
|
||||
</div>
|
||||
<div class="loading-container">
|
||||
<div class="loading-text"><span class="text">Loading</span>
|
||||
<div class="dot-pulse-container">
|
||||
<div class="dot-pulse"></div>
|
||||
<div class="dot-pulse"></div>
|
||||
<div class="dot-pulse"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -3259,9 +3259,6 @@
|
|||
<ClCompile Include="core\reporter.cpp">
|
||||
<Filter>Source Files\core</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="core\arm\debug.cpp">
|
||||
<Filter>Source Files\core\arm</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="emu_thread.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@
|
|||
#include <widgets/menubar.h>
|
||||
#include <widgets/page_nav.h>
|
||||
#include <widgets/tooltip_host.h>
|
||||
#include "user_interface/widgets/rom_browser.h"
|
||||
#include <windows.h>
|
||||
|
||||
void RegisterWidgets(ISciterUI & sciterUI)
|
||||
|
|
@ -21,6 +22,7 @@ void RegisterWidgets(ISciterUI & sciterUI)
|
|||
Register_WidgetMenuBar(sciterUI);
|
||||
Register_WidgetPageNav(sciterUI);
|
||||
Register_WidgetToolTipHost(sciterUI);
|
||||
Register_WidgetRomBrowser(sciterUI);
|
||||
}
|
||||
|
||||
extern "C" {
|
||||
|
|
|
|||
|
|
@ -40,6 +40,7 @@
|
|||
<ClCompile Include="main.cpp" />
|
||||
<ClCompile Include="settings\ui_settings.cpp" />
|
||||
<ClCompile Include="startup_checks.cpp" />
|
||||
<ClCompile Include="user_interface\game_list_worker.cpp" />
|
||||
<ClCompile Include="user_interface\key_mappings.cpp" />
|
||||
<ClCompile Include="user_interface\notification.cpp" />
|
||||
<ClCompile Include="user_interface\sciter_main_window.cpp" />
|
||||
|
|
@ -51,12 +52,14 @@
|
|||
<ClCompile Include="user_interface\settings\system_config_debug.cpp" />
|
||||
<ClCompile Include="user_interface\settings\system_config_game_browser.cpp" />
|
||||
<ClCompile Include="user_interface\settings\system_config_graphics.cpp" />
|
||||
<ClCompile Include="user_interface\widgets\rom_browser.cpp" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="settings\ui_identifiers.h" />
|
||||
<ClInclude Include="settings\ui_settings.h" />
|
||||
<ClInclude Include="settings\ui_settings_identifiers.h" />
|
||||
<ClInclude Include="startup_checks.h" />
|
||||
<ClInclude Include="user_interface\game_list_worker.h" />
|
||||
<ClInclude Include="user_interface\key_mappings.h" />
|
||||
<ClInclude Include="user_interface\notification.h" />
|
||||
<ClInclude Include="user_interface\resource.h" />
|
||||
|
|
@ -69,6 +72,7 @@
|
|||
<ClInclude Include="user_interface\settings\system_config_debug.h" />
|
||||
<ClInclude Include="user_interface\settings\system_config_game_browser.h" />
|
||||
<ClInclude Include="user_interface\settings\system_config_graphics.h" />
|
||||
<ClInclude Include="user_interface\widgets\rom_browser.h" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\external\sciterui\src\sciterui\sciterui.vcxproj">
|
||||
|
|
|
|||
|
|
@ -31,6 +31,12 @@
|
|||
<Filter Include="Source Files\settings">
|
||||
<UniqueIdentifier>{3bf11314-5055-4185-95ae-653ebe2f8e8e}</UniqueIdentifier>
|
||||
</Filter>
|
||||
<Filter Include="Header Files\user_interface\widgets">
|
||||
<UniqueIdentifier>{465d5a27-da20-4e68-b138-2d8b5b359780}</UniqueIdentifier>
|
||||
</Filter>
|
||||
<Filter Include="Source Files\user_interface\widgets">
|
||||
<UniqueIdentifier>{a4091b11-2249-4f6f-8fd2-49ae4e48ea92}</UniqueIdentifier>
|
||||
</Filter>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="main.cpp">
|
||||
|
|
@ -75,6 +81,12 @@
|
|||
<ClCompile Include="user_interface\key_mappings.cpp">
|
||||
<Filter>Source Files\user_interface</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="user_interface\game_list_worker.cpp">
|
||||
<Filter>Source Files\user_interface</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="user_interface\widgets\rom_browser.cpp">
|
||||
<Filter>Source Files\user_interface\widgets</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="user_interface\resource.h">
|
||||
|
|
@ -122,6 +134,12 @@
|
|||
<ClInclude Include="user_interface\key_mappings.h">
|
||||
<Filter>Header Files\user_interface</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="user_interface\game_list_worker.h">
|
||||
<Filter>Header Files\user_interface</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="user_interface\widgets\rom_browser.h">
|
||||
<Filter>Header Files\user_interface\widgets</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="settings\ui_identifiers.h">
|
||||
<Filter>Header Files\settings</Filter>
|
||||
</ClInclude>
|
||||
|
|
|
|||
|
|
@ -0,0 +1,232 @@
|
|||
#include "game_list_worker.h"
|
||||
#include "settings/ui_settings.h"
|
||||
#include <yuzu_common/fs/fs.h>
|
||||
#include <common/path.h>
|
||||
#include <common/std_string.h>
|
||||
#include <nxemu-module-spec/system_loader.h>
|
||||
#include <nxemu-core/modules/system_modules.h>
|
||||
#include <yuzu_common/interface_pointer.h>
|
||||
#include <yuzu_common/interface_pointer_def.h>
|
||||
#include <unordered_set>
|
||||
|
||||
namespace
|
||||
{
|
||||
bool HasSupportedFileExtension(const std::string & file_name)
|
||||
{
|
||||
const std::unordered_set<std::string> supported_extensions = {
|
||||
"nso", "nro", "nca",
|
||||
"dxci", "dnsp", "kip"
|
||||
};
|
||||
return supported_extensions.contains(stdstr(Path(file_name).GetExtension()).ToLower());
|
||||
}
|
||||
|
||||
bool IsExtractedNCAMain(const std::string & file_name)
|
||||
{
|
||||
return (stdstr(Path(file_name).GetNameExtension()).ToLower() == "main");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
GameListWorker::GameListWorker(IManualContentProvider & provider_, SystemModules & modules) :
|
||||
m_provider(provider_),
|
||||
m_modules(modules),
|
||||
m_stop(false)
|
||||
{
|
||||
}
|
||||
|
||||
GameListWorker::~GameListWorker()
|
||||
{
|
||||
Stop();
|
||||
}
|
||||
|
||||
void GameListWorker::Start()
|
||||
{
|
||||
m_thread = std::thread(&GameListWorker::Run, this);
|
||||
}
|
||||
|
||||
void GameListWorker::Stop()
|
||||
{
|
||||
m_stop = true;
|
||||
if (m_thread.joinable())
|
||||
{
|
||||
m_thread.join();
|
||||
}
|
||||
}
|
||||
|
||||
void GameListWorker::Run()
|
||||
{
|
||||
#ifdef tofix
|
||||
watch_list.clear();
|
||||
provider.ClearAllEntries();
|
||||
|
||||
const auto DirEntryReady = [&](GameListDir * game_list_dir) {
|
||||
RecordEvent([=](GameList * game_list) { game_list->AddDirEntry(game_list_dir); });
|
||||
};
|
||||
#endif
|
||||
for (const std::string & game_dir : uiSettings.gameDirectories)
|
||||
{
|
||||
if (m_stop)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
#ifdef tofix
|
||||
if (game_dir.path == std::string("SDMC"))
|
||||
{
|
||||
auto * const game_list_dir = new GameListDir(game_dir, GameListItemType::SdmcDir);
|
||||
DirEntryReady(game_list_dir);
|
||||
AddTitlesToGameList(game_list_dir);
|
||||
}
|
||||
else if (game_dir.path == std::string("UserNAND"))
|
||||
{
|
||||
auto * const game_list_dir = new GameListDir(game_dir, GameListItemType::UserNandDir);
|
||||
DirEntryReady(game_list_dir);
|
||||
AddTitlesToGameList(game_list_dir);
|
||||
}
|
||||
else if (game_dir.path == std::string("SysNAND"))
|
||||
{
|
||||
auto * const game_list_dir = new GameListDir(game_dir, GameListItemType::SysNandDir);
|
||||
DirEntryReady(game_list_dir);
|
||||
AddTitlesToGameList(game_list_dir);
|
||||
}
|
||||
else
|
||||
{
|
||||
watch_list.append(QString::fromStdString(game_dir.path));
|
||||
auto * const game_list_dir = new GameListDir(game_dir);
|
||||
DirEntryReady(game_list_dir);
|
||||
#endif
|
||||
ScanFileSystem(ScanTarget::FillManualContentProvider, game_dir, false /*game_dir.deep_scan, game_list_dir*/);
|
||||
ScanFileSystem(ScanTarget::PopulateGameList, game_dir, false /*game_dir.deep_scan, game_list_dir*/);
|
||||
#ifdef tofix
|
||||
}
|
||||
#endif
|
||||
}
|
||||
#ifdef tofix
|
||||
RecordEvent([this](GameList * game_list) { game_list->DonePopulating(watch_list); });
|
||||
processing_completed.Set();
|
||||
#endif
|
||||
}
|
||||
|
||||
void GameListWorker::ScanFileSystem(ScanTarget target, const std::string & dir_path, bool deep_scan/*, GameListDir * parent_dir*/)
|
||||
{
|
||||
using IRomInfoPtr = InterfacePtr<IRomInfo>;
|
||||
|
||||
const auto callback = [this, target/*, parent_dir*/](const std::filesystem::path & path) -> bool
|
||||
{
|
||||
if (m_stop)
|
||||
{
|
||||
// Breaks the callback loop.
|
||||
return false;
|
||||
}
|
||||
const std::string physical_name = Common::FS::PathToUTF8String(path);
|
||||
const bool is_dir = Common::FS::IsDir(path);
|
||||
if (!is_dir && (HasSupportedFileExtension(physical_name) || IsExtractedNCAMain(physical_name)))
|
||||
{
|
||||
ISystemloader & systemloader = m_modules.Modules().Systemloader();
|
||||
IRomInfoPtr info(systemloader.RomInfo(physical_name.c_str(), 0 ,0));
|
||||
if (!info)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
const LoaderFileType file_type = info->GetFileType();
|
||||
if (file_type == LoaderFileType::Unknown || file_type == LoaderFileType::Error)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
u64 program_id = 0;
|
||||
const LoaderResultStatus res2 = info->ReadProgramId(program_id);
|
||||
|
||||
if (target == ScanTarget::FillManualContentProvider)
|
||||
{
|
||||
if (res2 == LoaderResultStatus::Success)
|
||||
{
|
||||
info->AddToManualContentProvider(m_provider);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
uint32_t count = 0;
|
||||
std::vector<uint64_t> program_ids;
|
||||
|
||||
if (info->ReadProgramIds(nullptr, &count) == LoaderResultStatus::Success)
|
||||
{
|
||||
program_ids.resize(count);
|
||||
info->ReadProgramIds(program_ids.data(), &count);
|
||||
}
|
||||
|
||||
if (res2 == LoaderResultStatus::Success && program_ids.size() > 1 && (file_type == LoaderFileType::XCI || file_type == LoaderFileType::NSP))
|
||||
{
|
||||
__debugbreak();
|
||||
#ifdef tofix
|
||||
for (const auto id : program_ids)
|
||||
{
|
||||
loader = Loader::GetLoader(systemModules.GetSystemloader(), file, id);
|
||||
if (!loader)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
std::vector<u8> icon;
|
||||
[[maybe_unused]] const auto res1 = info->ReadIcon(icon);
|
||||
|
||||
std::string name = " ";
|
||||
[[maybe_unused]] const auto res3 = info->ReadTitle(name);
|
||||
|
||||
const FileSys::PatchManager patch{id, system.GetFileSystemController(), system.GetContentProvider()};
|
||||
|
||||
auto entry = MakeGameListEntry(physical_name, name, Common::FS::GetSize(physical_name), icon, *loader, id, compatibility_list, play_time_manager, patch);
|
||||
|
||||
RecordEvent(
|
||||
[=](GameList * game_list) { game_list->AddEntry(entry, parent_dir); });
|
||||
}
|
||||
#endif
|
||||
}
|
||||
else
|
||||
{
|
||||
uint32_t size = 0;
|
||||
std::vector<u8> icon;
|
||||
LoaderResultStatus res1 = info->ReadIcon(nullptr, &size);
|
||||
if (res1 == LoaderResultStatus::Success)
|
||||
{
|
||||
icon.resize(size);
|
||||
res1 = info->ReadIcon(icon.data(), &size);
|
||||
}
|
||||
|
||||
std::string name = " ";
|
||||
LoaderResultStatus res3 = info->ReadTitle(nullptr, &size);
|
||||
if (res3 == LoaderResultStatus::Success)
|
||||
{
|
||||
name.resize(size);
|
||||
res3 = info->ReadTitle(name.data(), &size);
|
||||
}
|
||||
|
||||
//__debugbreak();
|
||||
#ifdef tofix
|
||||
const FileSys::PatchManager patch{program_id, system.GetFileSystemController(), system.GetContentProvider()};
|
||||
auto entry = MakeGameListEntry(physical_name, name, Common::FS::GetSize(physical_name), icon, *info, program_id, compatibility_list, play_time_manager, patch);
|
||||
RecordEvent([=](GameList * game_list) { game_list->AddEntry(entry, parent_dir); });
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (is_dir)
|
||||
{
|
||||
__debugbreak();
|
||||
#ifdef tofix
|
||||
watch_list.append(QString::fromStdString(physical_name));
|
||||
#endif
|
||||
}
|
||||
return true;
|
||||
};
|
||||
|
||||
if (deep_scan)
|
||||
{
|
||||
Common::FS::IterateDirEntriesRecursively(dir_path, callback, Common::FS::DirEntryFilter::All);
|
||||
}
|
||||
else
|
||||
{
|
||||
Common::FS::IterateDirEntries(dir_path, callback, Common::FS::DirEntryFilter::File);
|
||||
}
|
||||
}
|
||||
|
||||
template class InterfacePtr<IRomInfo>;
|
||||
|
|
@ -0,0 +1,31 @@
|
|||
#pragma once
|
||||
#include <thread>
|
||||
#include <string>
|
||||
|
||||
class SystemModules;
|
||||
__interface IManualContentProvider;
|
||||
|
||||
class GameListWorker
|
||||
{
|
||||
enum class ScanTarget
|
||||
{
|
||||
FillManualContentProvider,
|
||||
PopulateGameList,
|
||||
};
|
||||
|
||||
public:
|
||||
GameListWorker(IManualContentProvider & provider_, SystemModules & modules);
|
||||
~GameListWorker();
|
||||
|
||||
void Start();
|
||||
void Stop();
|
||||
|
||||
private:
|
||||
void Run();
|
||||
void ScanFileSystem(ScanTarget target, const std::string & game_dir, bool deep_scan);
|
||||
|
||||
IManualContentProvider & m_provider;
|
||||
SystemModules & m_modules;
|
||||
std::thread m_thread;
|
||||
bool m_stop;
|
||||
};
|
||||
|
|
@ -59,6 +59,10 @@ SciterMainWindow::~SciterMainWindow()
|
|||
settings.UnregisterCallback(NXOsSetting::AudioVolume, SciterMainWindow::SettingChanged, this);
|
||||
|
||||
settings.SetBool(NXCoreSetting::ShuttingDown, true);
|
||||
if (m_romBrowser)
|
||||
{
|
||||
m_romBrowser->ClearItems();
|
||||
}
|
||||
m_modules.ShutDown();
|
||||
}
|
||||
|
||||
|
|
@ -153,6 +157,21 @@ bool SciterMainWindow::Show()
|
|||
m_sciterUI.AttachHandler(m_rootElement, IID_ITIMERSINK, (ITimerSink *)this);
|
||||
m_rootElement.SetTimer(25, (uint32_t *)TIMER_UPDATE_INPUT);
|
||||
|
||||
SciterElement romBrowserPanel = m_rootElement.GetElementByID("RomBrowserPanel");
|
||||
if (romBrowserPanel.IsValid())
|
||||
{
|
||||
romBrowserPanel.SetStyleAttribute("display", "block");
|
||||
}
|
||||
|
||||
SciterElement rombrowser = m_rootElement.FindFirst("rombrowser");
|
||||
interfacePtr = rombrowser.IsValid() ? m_sciterUI.GetElementInterface(rombrowser, IID_ROMBROWSER) : nullptr;
|
||||
if (interfacePtr)
|
||||
{
|
||||
m_romBrowser = std::static_pointer_cast<IRomBrowser>(interfacePtr);
|
||||
m_romBrowser->SetMainWindow(this, &m_modules.Modules());
|
||||
m_romBrowser->PopulateAsync();
|
||||
}
|
||||
|
||||
if (!uiSettings.hasBrokenVulkan)
|
||||
{
|
||||
PopulateVulkanRecords(m_vkDeviceRecords, RenderSurface());
|
||||
|
|
@ -565,7 +584,17 @@ void SciterMainWindow::SettingChanged(const char * setting, void * userData)
|
|||
|
||||
bool SciterMainWindow::OnTimer(SCITER_ELEMENT /*element*/, uint32_t * timerId)
|
||||
{
|
||||
if (timerId == (uint32_t*)TIMER_UPDATE_INPUT)
|
||||
if (timerId == (uint32_t *)TIMER_UPDATE_UI)
|
||||
{
|
||||
SciterElement mainContents(m_rootElement.GetElementByID("MainContents"));
|
||||
if (mainContents.IsValid())
|
||||
{
|
||||
mainContents.Update(false);
|
||||
m_sciterUI.UpdateWindow(mainContents.GetElementHwnd(true));
|
||||
}
|
||||
return false;
|
||||
}
|
||||
else if (timerId == (uint32_t *)TIMER_UPDATE_INPUT)
|
||||
{
|
||||
UpdateInputDrivers();
|
||||
}
|
||||
|
|
@ -576,6 +605,12 @@ bool SciterMainWindow::OnEvent(SCITER_ELEMENT /*element*/, SCITER_ELEMENT /*sour
|
|||
{
|
||||
if (event_code == EVENT_EMULATION_LOADING)
|
||||
{
|
||||
SciterElement romBrowserPanel(m_rootElement.GetElementByID("RomBrowserPanel"));
|
||||
if (romBrowserPanel.IsValid())
|
||||
{
|
||||
romBrowserPanel.SetStyleAttribute("display", "none");
|
||||
}
|
||||
|
||||
SciterElement LoadingPanel(m_rootElement.GetElementByID("LoadingPanel"));
|
||||
if (LoadingPanel.IsValid())
|
||||
{
|
||||
|
|
@ -594,6 +629,12 @@ bool SciterMainWindow::OnEvent(SCITER_ELEMENT /*element*/, SCITER_ELEMENT /*sour
|
|||
{
|
||||
LoadingPanel.SetStyleAttribute("display", "none");
|
||||
}
|
||||
|
||||
SciterElement romBrowserPanel(m_rootElement.GetElementByID("RomBrowserPanel"));
|
||||
if (romBrowserPanel.IsValid())
|
||||
{
|
||||
romBrowserPanel.SetStyleAttribute("display", "block");
|
||||
}
|
||||
}
|
||||
else if (event_code == EVENT_EMULATION_FIRST_FRAME)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@
|
|||
#include <sciter_element.h>
|
||||
#include <widgets/menubar.h>
|
||||
#include "startup_checks.h"
|
||||
#include "user_interface/widgets/rom_browser.h"
|
||||
|
||||
class SystemConfig;
|
||||
class InputConfig;
|
||||
|
|
@ -43,7 +44,8 @@ class SciterMainWindow :
|
|||
|
||||
enum
|
||||
{
|
||||
TIMER_UPDATE_INPUT = 5000,
|
||||
TIMER_UPDATE_UI = 5000,
|
||||
TIMER_UPDATE_INPUT = 5001,
|
||||
};
|
||||
|
||||
public:
|
||||
|
|
@ -122,6 +124,7 @@ private:
|
|||
SystemModules m_modules;
|
||||
std::vector<VkDeviceRecord> m_vkDeviceRecords;
|
||||
std::shared_ptr<IMenuBar> m_menuBar;
|
||||
std::shared_ptr<IRomBrowser> m_romBrowser;
|
||||
void * m_renderWindow;
|
||||
std::string m_windowTitle;
|
||||
std::unique_ptr<SystemConfig> m_systemConfig;
|
||||
|
|
|
|||
|
|
@ -1581,7 +1581,7 @@ void InputConfigPlayer::UpdateControllerButtonNames()
|
|||
}
|
||||
NpadStyleIndex layout = GetControllerTypeFromIndex(m_comboControllerType->CurrentIndex());
|
||||
SciterElement el;
|
||||
|
||||
|
||||
switch (layout) {
|
||||
case NpadStyleIndex::Fullkey:
|
||||
case NpadStyleIndex::JoyconDual:
|
||||
|
|
|
|||
|
|
@ -0,0 +1,557 @@
|
|||
#include "rom_browser.h"
|
||||
#include "user_interface/sciter_main_window.h"
|
||||
#include "settings/ui_settings.h"
|
||||
#include <map>
|
||||
#include <common/path.h>
|
||||
#include <common/std_string.h>
|
||||
#include <nxemu-module-spec/system_loader.h>
|
||||
#include <nxemu-core/modules/system_modules.h>
|
||||
#include <nxemu-core/settings/settings.h>
|
||||
#include <nxemu/settings/ui_identifiers.h>
|
||||
#include <sciter_handler.h>
|
||||
#include <sciter_element.h>
|
||||
#include <yuzu_common/fs/fs.h>
|
||||
#include <yuzu_common/interface_pointer.h>
|
||||
#include <yuzu_common/interface_pointer_def.h>
|
||||
#include <mutex>
|
||||
#include <thread>
|
||||
#include <unordered_set>
|
||||
#include <unordered_map>
|
||||
|
||||
namespace
|
||||
{
|
||||
enum
|
||||
{
|
||||
EVENT_UPDATE_LIST = 0x1000,
|
||||
TIMER_UPDATE_UI = 1,
|
||||
};
|
||||
|
||||
std::string Base64Encode(const uint8_t * data, size_t len)
|
||||
{
|
||||
static const char * base64_chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
|
||||
|
||||
std::string ret;
|
||||
int32_t i = 0;
|
||||
int32_t j = 0;
|
||||
uint8_t char_array_3[3];
|
||||
uint8_t char_array_4[4];
|
||||
|
||||
while (len--)
|
||||
{
|
||||
char_array_3[i++] = *(data++);
|
||||
if (i == 3)
|
||||
{
|
||||
char_array_4[0] = (char_array_3[0] & 0xfc) >> 2;
|
||||
char_array_4[1] = ((char_array_3[0] & 0x03) << 4) + ((char_array_3[1] & 0xf0) >> 4);
|
||||
char_array_4[2] = ((char_array_3[1] & 0x0f) << 2) + ((char_array_3[2] & 0xc0) >> 6);
|
||||
char_array_4[3] = char_array_3[2] & 0x3f;
|
||||
|
||||
for (i = 0; i < 4; i++)
|
||||
{
|
||||
ret += base64_chars[char_array_4[i]];
|
||||
}
|
||||
i = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (i)
|
||||
{
|
||||
for (j = i; j < 3; j++)
|
||||
{
|
||||
char_array_3[j] = '\0';
|
||||
}
|
||||
|
||||
char_array_4[0] = (char_array_3[0] & 0xfc) >> 2;
|
||||
char_array_4[1] = ((char_array_3[0] & 0x03) << 4) + ((char_array_3[1] & 0xf0) >> 4);
|
||||
char_array_4[2] = ((char_array_3[1] & 0x0f) << 2) + ((char_array_3[2] & 0xc0) >> 6);
|
||||
|
||||
for (j = 0; j < i + 1; j++)
|
||||
{
|
||||
ret += base64_chars[char_array_4[j]];
|
||||
}
|
||||
|
||||
while ((i++ < 3))
|
||||
{
|
||||
ret += '=';
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
using IRomInfoPtr = InterfacePtr<IRomInfo>;
|
||||
|
||||
class WidgetRomBrowser;
|
||||
|
||||
struct RomEntry
|
||||
{
|
||||
SciterElement romCard;
|
||||
std::string title;
|
||||
std::vector<uint8_t> icon;
|
||||
bool newItem;
|
||||
bool found;
|
||||
};
|
||||
using RomEntrys = std::unordered_map<std::string, RomEntry>;
|
||||
|
||||
class RomListWorker
|
||||
{
|
||||
public:
|
||||
RomListWorker(WidgetRomBrowser & romBrowser, ISystemModules & modules);
|
||||
~RomListWorker();
|
||||
|
||||
void Start();
|
||||
void Stop();
|
||||
|
||||
private:
|
||||
void Run();
|
||||
void ScanFileSystem(const std::string & dir_path);
|
||||
bool HasSupportedFileExtension(const std::string & file_name);
|
||||
|
||||
ISystemModules & m_modules;
|
||||
WidgetRomBrowser & m_romBrowser;
|
||||
std::mutex & m_romsMutex;
|
||||
RomEntrys & m_roms;
|
||||
SciterElement & m_rootElement;
|
||||
std::thread m_thread;
|
||||
bool m_stop;
|
||||
};
|
||||
|
||||
class WidgetRomBrowser :
|
||||
public std::enable_shared_from_this<WidgetRomBrowser>,
|
||||
public IWidget,
|
||||
public IEventSink,
|
||||
public ITimerSink,
|
||||
public IClickSink,
|
||||
public IDoubleClickSink,
|
||||
public IRomBrowser
|
||||
{
|
||||
friend class RomListWorker;
|
||||
|
||||
typedef std::map<IWidget *, std::shared_ptr<WidgetRomBrowser>> RomBrowsers;
|
||||
|
||||
public:
|
||||
static bool Register(ISciterUI & sciterUI);
|
||||
|
||||
// IEventSink
|
||||
bool OnEvent(SCITER_ELEMENT element, SCITER_ELEMENT source, uint32_t event_code, uint64_t reason) override;
|
||||
|
||||
// ITimerSink
|
||||
bool OnTimer(SCITER_ELEMENT Element, uint32_t * TimerId) override;
|
||||
|
||||
// IClickSink
|
||||
bool OnClick(SCITER_ELEMENT element, SCITER_ELEMENT source, uint32_t reason) override;
|
||||
|
||||
// IDoubleClickSink
|
||||
bool OnDoubleClick(SCITER_ELEMENT element, SCITER_ELEMENT source) override;
|
||||
|
||||
private:
|
||||
WidgetRomBrowser() = delete;
|
||||
WidgetRomBrowser(const WidgetRomBrowser &) = delete;
|
||||
WidgetRomBrowser & operator=(const WidgetRomBrowser &) = delete;
|
||||
|
||||
WidgetRomBrowser(ISciterUI & sciterUI);
|
||||
bool RenderUI();
|
||||
|
||||
// IWidget
|
||||
void Attached(SCITER_ELEMENT element, IBaseElement * baseElement) override;
|
||||
void Detached(SCITER_ELEMENT element) override;
|
||||
std::shared_ptr<void> GetInterface(const char * riid) override;
|
||||
|
||||
// IRomBrowser
|
||||
void PopulateAsync() override;
|
||||
void SetMainWindow(SciterMainWindow * window, ISystemModules * modules) override;
|
||||
void ClearItems() override;
|
||||
|
||||
static void SettingChanged(const char * setting, void * userData);
|
||||
static IWidget * __stdcall CreateWidget(ISciterUI & sciterUI);
|
||||
static void __stdcall ReleaseWidget(IWidget * widget);
|
||||
|
||||
ISciterUI & m_sciterUI;
|
||||
IBaseElement * m_baseElement;
|
||||
SciterMainWindow * m_window;
|
||||
ISystemModules * m_modules;
|
||||
SciterElement m_rootElement;
|
||||
SciterElement m_gameTitle;
|
||||
SciterElement m_gameGrid;
|
||||
SciterElement m_configButton;
|
||||
SciterElement m_currentGame;
|
||||
std::mutex m_romsMutex;
|
||||
RomEntrys m_roms;
|
||||
std::unique_ptr<RomListWorker> m_currentWorker;
|
||||
bool m_updatingUI;
|
||||
static RomBrowsers m_instances;
|
||||
};
|
||||
|
||||
WidgetRomBrowser::RomBrowsers WidgetRomBrowser::m_instances;
|
||||
|
||||
WidgetRomBrowser::WidgetRomBrowser(ISciterUI & sciterUI) :
|
||||
m_sciterUI(sciterUI),
|
||||
m_updatingUI(false),
|
||||
m_baseElement(nullptr),
|
||||
m_window(nullptr),
|
||||
m_modules(nullptr)
|
||||
{
|
||||
}
|
||||
|
||||
bool WidgetRomBrowser::Register(ISciterUI & sciterUI)
|
||||
{
|
||||
const char * WidgetCss =
|
||||
"rombrowser {"
|
||||
" display: block;"
|
||||
" behavior: rombrowser;"
|
||||
"}";
|
||||
return sciterUI.RegisterWidgetType("rombrowser", WidgetRomBrowser::CreateWidget, WidgetRomBrowser::ReleaseWidget, WidgetCss);
|
||||
}
|
||||
|
||||
bool WidgetRomBrowser::RenderUI()
|
||||
{
|
||||
if (!m_gameTitle.IsValid())
|
||||
{
|
||||
m_gameTitle.Create("div", "");
|
||||
m_gameTitle.SetAttribute("class", "games-title");
|
||||
SciterElement title;
|
||||
title.Create("h1", "My Games");
|
||||
m_gameTitle.Insert(title, m_gameTitle.GetChildCount());
|
||||
|
||||
SciterElement buttonsContainer;
|
||||
buttonsContainer.Create("div", "");
|
||||
buttonsContainer.SetAttribute("class", "buttons-container");
|
||||
|
||||
m_configButton.Create("div", "");
|
||||
m_configButton.SetAttribute("class", "config-button");
|
||||
buttonsContainer.Insert(m_configButton, buttonsContainer.GetChildCount());
|
||||
m_gameTitle.Insert(buttonsContainer, m_gameTitle.GetChildCount());
|
||||
m_rootElement.Insert(m_gameTitle, m_rootElement.GetChildCount());
|
||||
|
||||
std::string ConfigSvg = R"(<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 36 36">
|
||||
<path d="M18.1 11c-3.9 0-7 3.1-7 7s3.1 7 7 7 7-3.1 7-7-3.1-7-7-7zm0 12c-2.8 0-5-2.2-5-5s2.2-5 5-5 5 2.2 5 5-2.2 5-5 5z" class="outline"/>
|
||||
<path d="m32.8 14.7-2.8-.9-.6-1.5 1.4-2.6c.3-.6.2-1.4-.3-1.9l-2.4-2.4c-.5-.5-1.3-.6-1.9-.3l-2.6 1.4-1.5-.6-.9-2.8C21 2.5 20.4 2 19.7 2h-3.4c-.7 0-1.3.5-1.4 1.2L14 6c-.6.1-1.1.3-1.6.6L9.8 5.2c-.6-.3-1.4-.2-1.9.3L5.5 7.9c-.5.5-.6 1.3-.3 1.9l1.3 2.5c-.2.5-.4 1.1-.6 1.6l-2.8.9c-.6.2-1.1.8-1.1 1.5v3.4c0 .7.5 1.3 1.2 1.5l2.8.9.6 1.5-1.4 2.6c-.3.6-.2 1.4.3 1.9l2.4 2.4c.5.5 1.3.6 1.9.3l2.6-1.4 1.5.6.9 2.9c.2.6.8 1.1 1.5 1.1h3.4c.7 0 1.3-.5 1.5-1.1l.9-2.9 1.5-.6 2.6 1.4c.6.3 1.4.2 1.9-.3l2.4-2.4c.5-.5.6-1.3.3-1.9l-1.4-2.6.6-1.5 2.9-.9c.6-.2 1.1-.8 1.1-1.5v-3.4c0-.7-.5-1.4-1.2-1.6zm-.8 4.7-3.6 1.1-.1.5-.9 2.1-.3.5 1.8 3.3-2 2-3.3-1.8-.5.3c-.7.4-1.4.7-2.1.9l-.5.1-1.1 3.6h-2.8l-1.1-3.6-.5-.1-2.1-.9-.5-.3-3.3 1.8-2-2 1.8-3.3-.3-.5c-.4-.7-.7-1.4-.9-2.1l-.1-.5L4 19.4v-2.8l3.4-1 .2-.5c.2-.8.5-1.5.9-2.2l.3-.5-1.7-3.3 2-2 3.2 1.8.5-.3c.7-.4 1.4-.7 2.2-.9l.5-.2L16.6 4h2.8l1.1 3.5.5.2c.7.2 1.4.5 2.1.9l.5.3 3.3-1.8 2 2-1.8 3.3.3.5c.4.7.7 1.4.9 2.1l.1.5 3.6 1.1v2.8z" class="outline"/>
|
||||
</svg>)";
|
||||
m_configButton.SetHTML((uint8_t *)ConfigSvg.c_str(), ConfigSvg.size());
|
||||
m_sciterUI.AttachHandler(m_configButton, IID_ICLICKSINK, (IClickSink *)this);
|
||||
m_sciterUI.AttachHandler(m_configButton, IID_IDBLCLICKSINK, (IDoubleClickSink *)this);
|
||||
}
|
||||
if (!m_gameGrid.IsValid())
|
||||
{
|
||||
SettingsStore & settings = SettingsStore::GetInstance();
|
||||
m_gameGrid.Create("div", "");
|
||||
m_gameGrid.SetAttribute("class", "games-grid");
|
||||
m_gameGrid.SetAttribute("data-size", stdstr_f("%d", settings.GetInt(NXUISetting::MyGameIconSize)).c_str());
|
||||
m_rootElement.Insert(m_gameGrid, m_rootElement.GetChildCount());
|
||||
}
|
||||
|
||||
if (m_roms.size() > 0)
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(m_romsMutex);
|
||||
for (std::pair<const std::string, RomEntry> & entry : m_roms)
|
||||
{
|
||||
RomEntry & romEntry = entry.second;
|
||||
SciterElement & romCard = romEntry.romCard;
|
||||
if (romCard.IsValid())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
romCard.Create("div", "");
|
||||
romCard.SetAttribute("class", "rom-card");
|
||||
romCard.SetAttribute("data-path", entry.first.c_str());
|
||||
m_gameGrid.Insert(romCard, m_gameGrid.GetChildCount());
|
||||
m_sciterUI.AttachHandler(romCard, IID_ICLICKSINK, (IClickSink *)this);
|
||||
m_sciterUI.AttachHandler(romCard, IID_IDBLCLICKSINK, (IDoubleClickSink *)this);
|
||||
|
||||
SciterElement romCardContents;
|
||||
romCardContents.Create("div", "");
|
||||
romCardContents.SetAttribute("class", "rom-card-content");
|
||||
romCard.Insert(romCardContents, romCard.GetChildCount());
|
||||
|
||||
if (romEntry.icon.size() > 0)
|
||||
{
|
||||
SciterElement iconElement;
|
||||
iconElement.Create("img", "");
|
||||
iconElement.SetAttribute("class", "rom-icon");
|
||||
iconElement.SetAttribute("src", stdstr_f("data:image/jpeg;base64,%s", Base64Encode(romEntry.icon.data(), romEntry.icon.size()).c_str()).c_str());
|
||||
romCardContents.Insert(iconElement, romCardContents.GetChildCount());
|
||||
romEntry.icon.clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
m_updatingUI = false;
|
||||
return false;
|
||||
}
|
||||
|
||||
bool WidgetRomBrowser::OnEvent(SCITER_ELEMENT /*element*/, SCITER_ELEMENT /*source*/, uint32_t event_code, uint64_t /*reason*/)
|
||||
{
|
||||
if (event_code == EVENT_UPDATE_LIST && !m_updatingUI)
|
||||
{
|
||||
m_updatingUI = true;
|
||||
m_rootElement.SetTimer(250, (uint32_t *)TIMER_UPDATE_UI);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool WidgetRomBrowser::OnTimer(SCITER_ELEMENT /*element*/, uint32_t * timerId)
|
||||
{
|
||||
if (timerId == (uint32_t *)TIMER_UPDATE_UI)
|
||||
{
|
||||
return RenderUI();
|
||||
};
|
||||
return true;
|
||||
}
|
||||
|
||||
bool WidgetRomBrowser::OnClick(SCITER_ELEMENT element, SCITER_ELEMENT /*source*/, uint32_t /*reason*/)
|
||||
{
|
||||
SciterElement el(element);
|
||||
std::string className = el.GetAttribute("class");
|
||||
if (strcmp(className.c_str(), "config-button") == 0 && m_window != nullptr)
|
||||
{
|
||||
m_window->ShowConfig("GameBrowser");
|
||||
return true;
|
||||
}
|
||||
else if (strcmp(className.c_str(), "rom-card") == 0)
|
||||
{
|
||||
if (m_currentGame.IsValid())
|
||||
{
|
||||
m_currentGame.SetState(0, SciterElement::STATE_CURRENT | SciterElement::STATE_VISITED, true);
|
||||
}
|
||||
m_currentGame = el;
|
||||
m_currentGame.SetState(SciterElement::STATE_CURRENT | SciterElement::STATE_VISITED, 0, true);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool WidgetRomBrowser::OnDoubleClick(SCITER_ELEMENT element, SCITER_ELEMENT /*source*/)
|
||||
{
|
||||
SciterElement el(element);
|
||||
if (strcmp(el.GetAttribute("class").c_str(), "rom-card") == 0 && m_window != nullptr)
|
||||
{
|
||||
std::string path = el.GetAttribute("data-path");
|
||||
m_window->LoadGame(path.c_str());
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void WidgetRomBrowser::SettingChanged(const char * setting, void * userData)
|
||||
{
|
||||
WidgetRomBrowser * impl = (WidgetRomBrowser *)userData;
|
||||
SettingsStore & settings = SettingsStore::GetInstance();
|
||||
if (strcmp(setting, NXUISetting::MyGameIconSize) == 0 && impl->m_gameGrid)
|
||||
{
|
||||
impl->m_gameGrid.SetAttribute("data-size", stdstr_f("%d", settings.GetInt(NXUISetting::MyGameIconSize)).c_str());
|
||||
}
|
||||
else if (strcmp(setting, NXUISetting::GameDirectories) == 0)
|
||||
{
|
||||
impl->PopulateAsync();
|
||||
}
|
||||
}
|
||||
|
||||
IWidget * __stdcall WidgetRomBrowser::CreateWidget(ISciterUI & sciterUI)
|
||||
{
|
||||
std::shared_ptr<WidgetRomBrowser> instance(new WidgetRomBrowser(sciterUI));
|
||||
IWidget * widget = (IWidget *)instance.get();
|
||||
WidgetRomBrowser * rombrowser = instance.get();
|
||||
m_instances.insert(RomBrowsers::value_type(rombrowser, std::move(instance)));
|
||||
return widget;
|
||||
}
|
||||
|
||||
void __stdcall WidgetRomBrowser::ReleaseWidget(IWidget * widget)
|
||||
{
|
||||
RomBrowsers::iterator it = m_instances.find(widget);
|
||||
if (it != m_instances.end())
|
||||
{
|
||||
m_instances.erase(it);
|
||||
}
|
||||
}
|
||||
|
||||
void WidgetRomBrowser::Attached(SCITER_ELEMENT element, IBaseElement * baseElement)
|
||||
{
|
||||
m_baseElement = baseElement;
|
||||
m_rootElement = element;
|
||||
m_sciterUI.AttachHandler(m_rootElement, IID_EVENTSINK, (IEventSink *)this);
|
||||
m_sciterUI.AttachHandler(m_rootElement, IID_ITIMERSINK, (ITimerSink *)this);
|
||||
|
||||
SettingsStore & settings = SettingsStore::GetInstance();
|
||||
settings.RegisterCallback(NXUISetting::MyGameIconSize, WidgetRomBrowser::SettingChanged, this);
|
||||
settings.RegisterCallback(NXUISetting::GameDirectories, WidgetRomBrowser::SettingChanged, this);
|
||||
|
||||
RenderUI();
|
||||
}
|
||||
|
||||
void WidgetRomBrowser::Detached(SCITER_ELEMENT /*element*/)
|
||||
{
|
||||
m_baseElement = nullptr;
|
||||
m_rootElement = nullptr;
|
||||
}
|
||||
|
||||
std::shared_ptr<void> WidgetRomBrowser::GetInterface(const char * riid)
|
||||
{
|
||||
if (strcmp(riid, IID_ROMBROWSER) == 0)
|
||||
{
|
||||
return std::static_pointer_cast<IRomBrowser>(shared_from_this());
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void WidgetRomBrowser::PopulateAsync()
|
||||
{
|
||||
if (m_modules != nullptr)
|
||||
{
|
||||
m_currentWorker.reset();
|
||||
m_currentWorker = std::make_unique<RomListWorker>(*this, *m_modules);
|
||||
m_currentWorker->Start();
|
||||
}
|
||||
}
|
||||
|
||||
void WidgetRomBrowser::SetMainWindow(SciterMainWindow * window, ISystemModules * modules)
|
||||
{
|
||||
m_modules = modules;
|
||||
m_window = window;
|
||||
}
|
||||
|
||||
void WidgetRomBrowser::ClearItems()
|
||||
{
|
||||
m_currentWorker->Stop();
|
||||
std::lock_guard<std::mutex> lock(m_romsMutex);
|
||||
m_roms.clear();
|
||||
}
|
||||
|
||||
RomListWorker::RomListWorker(WidgetRomBrowser & romBrowser, ISystemModules & modules) :
|
||||
m_modules(modules),
|
||||
m_romBrowser(romBrowser),
|
||||
m_romsMutex(romBrowser.m_romsMutex),
|
||||
m_roms(romBrowser.m_roms),
|
||||
m_rootElement(romBrowser.m_rootElement),
|
||||
m_stop(false)
|
||||
{
|
||||
}
|
||||
|
||||
RomListWorker::~RomListWorker()
|
||||
{
|
||||
Stop();
|
||||
}
|
||||
|
||||
void RomListWorker::Start()
|
||||
{
|
||||
m_thread = std::thread(&RomListWorker::Run, this);
|
||||
}
|
||||
|
||||
void RomListWorker::Stop()
|
||||
{
|
||||
m_stop = true;
|
||||
if (m_thread.joinable())
|
||||
{
|
||||
m_thread.join();
|
||||
}
|
||||
}
|
||||
|
||||
void RomListWorker::Run()
|
||||
{
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(m_romsMutex);
|
||||
for (std::pair<const std::string, RomEntry> & entry : m_roms)
|
||||
{
|
||||
RomEntry & romEntry = entry.second;
|
||||
romEntry.found = false;
|
||||
}
|
||||
}
|
||||
|
||||
for (const std::string & game_dir : uiSettings.gameDirectories)
|
||||
{
|
||||
if (m_stop)
|
||||
{
|
||||
return;
|
||||
}
|
||||
ScanFileSystem(game_dir);
|
||||
}
|
||||
|
||||
if (!m_stop)
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(m_romsMutex);
|
||||
|
||||
for (RomEntrys::iterator itr = m_roms.begin(); itr != m_roms.end();)
|
||||
{
|
||||
if (!itr->second.found)
|
||||
{
|
||||
SciterElement & romCard = itr->second.romCard;
|
||||
if (romCard.IsValid())
|
||||
{
|
||||
romCard.Destroy();
|
||||
}
|
||||
itr = m_roms.erase(itr);
|
||||
}
|
||||
else
|
||||
{
|
||||
itr++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void RomListWorker::ScanFileSystem(const std::string & dir_path)
|
||||
{
|
||||
const auto callback = [this](const std::filesystem::path & path) -> bool {
|
||||
if (m_stop)
|
||||
{
|
||||
// Breaks the callback loop.
|
||||
return false;
|
||||
}
|
||||
const std::string physical_name = Common::FS::PathToUTF8String(path);
|
||||
const bool is_dir = Common::FS::IsDir(path);
|
||||
if (!is_dir && (HasSupportedFileExtension(physical_name)))
|
||||
{
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(m_romsMutex);
|
||||
RomEntrys::iterator iter = m_roms.find(physical_name);
|
||||
if (iter != m_roms.end())
|
||||
{
|
||||
iter->second.found = true;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
ISystemloader & systemloader = m_modules.Systemloader();
|
||||
RomEntry entry;
|
||||
IRomInfoPtr romInfo = systemloader.RomInfo(physical_name.c_str(), 0, 0);
|
||||
if (!romInfo)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
entry.newItem = true;
|
||||
entry.found = true;
|
||||
|
||||
uint32_t size = 0;
|
||||
LoaderResultStatus res = romInfo->ReadIcon(nullptr, &size);
|
||||
if (res == LoaderResultStatus::Success && size > 0)
|
||||
{
|
||||
entry.icon.resize(size);
|
||||
romInfo->ReadIcon(entry.icon.data(), &size);
|
||||
}
|
||||
|
||||
res = romInfo->ReadTitle(nullptr, &size);
|
||||
if (res == LoaderResultStatus::Success)
|
||||
{
|
||||
entry.title.resize(size);
|
||||
res = romInfo->ReadTitle(entry.title.data(), &size);
|
||||
}
|
||||
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(m_romsMutex);
|
||||
m_roms.emplace(physical_name, std::move(entry));
|
||||
}
|
||||
m_rootElement.PostEvent(EVENT_UPDATE_LIST);
|
||||
}
|
||||
return true;
|
||||
};
|
||||
Common::FS::IterateDirEntries(dir_path, callback, Common::FS::DirEntryFilter::File);
|
||||
}
|
||||
|
||||
bool RomListWorker::HasSupportedFileExtension(const std::string & file_name)
|
||||
{
|
||||
static const std::unordered_set<std::string> supported_extensions = {
|
||||
"nso", "nro", "nca",
|
||||
"dxci", "dnsp", "kip"};
|
||||
return supported_extensions.contains(stdstr(Path(file_name).GetExtension()).ToLower());
|
||||
}
|
||||
} // namespace
|
||||
|
||||
bool Register_WidgetRomBrowser(ISciterUI & sciterUI)
|
||||
{
|
||||
return WidgetRomBrowser::Register(sciterUI);
|
||||
}
|
||||
|
|
@ -0,0 +1,17 @@
|
|||
#pragma once
|
||||
|
||||
__interface ISciterUI;
|
||||
__interface ISystemModules;
|
||||
class SciterMainWindow;
|
||||
class SystemModules;
|
||||
|
||||
static const char * IID_ROMBROWSER = "F68DFC0D-C86D-4810-97C6-48289FA650ED";
|
||||
|
||||
__interface IRomBrowser
|
||||
{
|
||||
void PopulateAsync() = 0;
|
||||
void SetMainWindow(SciterMainWindow * window, ISystemModules * modules) = 0;
|
||||
void ClearItems() = 0;
|
||||
};
|
||||
|
||||
bool Register_WidgetRomBrowser(ISciterUI & SciterUI);
|
||||
Loading…
Reference in New Issue