mirror of
https://github.com/XorTroll/uLaunch
synced 2024-11-22 11:53:06 +00:00
Fully fix menu logic and UI issues
This commit is contained in:
parent
79de9de4a1
commit
bb0f03fbc8
16 changed files with 334 additions and 252 deletions
|
@ -24,4 +24,5 @@ namespace am
|
|||
Result ApplicationSetForeground();
|
||||
Result ApplicationSend(void *data, size_t size, AppletLaunchParameterKind kind = AppletLaunchParameterKind_UserChannel);
|
||||
u64 ApplicationGetId();
|
||||
Result ApplicationGetResult();
|
||||
}
|
|
@ -106,6 +106,7 @@ namespace cfg
|
|||
void SaveConfig(Config &cfg);
|
||||
|
||||
void SaveRecord(TitleRecord &record);
|
||||
void RemoveRecord(TitleRecord &record);
|
||||
bool MoveRecordTo(TitleList &list, TitleRecord record, std::string folder);
|
||||
TitleFolder &FindFolderByName(TitleList &list, std::string name);
|
||||
bool ExistsRecord(TitleList &list, TitleRecord record);
|
||||
|
|
|
@ -6,4 +6,5 @@ namespace os
|
|||
{
|
||||
u32 GetBatteryLevel();
|
||||
bool IsConsoleCharging();
|
||||
std::string GetFirmwareVersion();
|
||||
}
|
|
@ -50,9 +50,10 @@ static constexpr size_t RawRGBAScreenBufferSize = 1280 * 720 * 4;
|
|||
} \
|
||||
})
|
||||
|
||||
#define STLITER_FINDWITHCONDITION(stl_item, var_name, cond) std::find_if(stl_item.begin(), stl_item.end(), [&](auto &var_name){ return (cond); });
|
||||
#define STLITER_ISFOUND(stl_item, find_ret) (find_ret != stl_item.end())
|
||||
#define STLITER_UNWRAP(find_ret) (*find_ret)
|
||||
#define STL_FIND_IF(stl_item, var_name, cond) std::find_if(stl_item.begin(), stl_item.end(), [&](const auto &var_name){ return (cond); });
|
||||
#define STL_FOUND(stl_item, find_ret) (find_ret != stl_item.end())
|
||||
#define STL_UNWRAP(find_ret) (*find_ret)
|
||||
#define STL_REMOVE_IF(stl_item, var_name, cond) stl_item.erase(std::remove_if(stl_item.begin(), stl_item.end(), [&](const auto &var_name){ return (cond); }), stl_item.end());
|
||||
|
||||
template<typename ...Args>
|
||||
using ResultWith = std::tuple<Result, Args...>;
|
||||
|
|
|
@ -257,7 +257,10 @@ namespace cfg
|
|||
if(record.json_name.empty()) record.json_name = jsonname;
|
||||
json += "/" + jsonname;
|
||||
entry["nro_path"] = record.nro_target.nro_path;
|
||||
if(strcmp(record.nro_target.nro_path, record.nro_target.argv) != 0) entry["nro_argv"] = record.nro_target.argv;
|
||||
if(strlen(record.nro_target.argv))
|
||||
{
|
||||
if(strcmp(record.nro_target.nro_path, record.nro_target.argv) != 0) entry["nro_argv"] = record.nro_target.argv;
|
||||
}
|
||||
if(!record.icon.empty()) entry["icon"] = record.icon;
|
||||
}
|
||||
else if((TitleType)record.title_type == TitleType::Installed)
|
||||
|
@ -277,6 +280,28 @@ namespace cfg
|
|||
ofs.close();
|
||||
}
|
||||
|
||||
void RemoveRecord(TitleRecord &record)
|
||||
{
|
||||
// Prepare JSON path
|
||||
std::string basepath = Q_ENTRIES_PATH;
|
||||
std::string json = basepath;
|
||||
if((TitleType)record.title_type == TitleType::Homebrew)
|
||||
{
|
||||
auto jsonname = std::to_string(fs::GetFileSize(record.nro_target.nro_path)) + ".json";
|
||||
if(record.json_name.empty()) record.json_name = jsonname;
|
||||
json += "/" + jsonname;
|
||||
}
|
||||
else if((TitleType)record.title_type == TitleType::Installed)
|
||||
{
|
||||
auto strappid = util::FormatApplicationId(record.app_id);
|
||||
auto jsonname = strappid + ".json";
|
||||
if(record.json_name.empty()) record.json_name = jsonname;
|
||||
json += "/" + jsonname;
|
||||
}
|
||||
if(!record.json_name.empty()) json = basepath + "/" + record.json_name;
|
||||
fs::DeleteFile(json);
|
||||
}
|
||||
|
||||
bool MoveTitleToDirectory(TitleList &list, u64 app_id, std::string folder)
|
||||
{
|
||||
bool title_found = false;
|
||||
|
@ -284,11 +309,11 @@ namespace cfg
|
|||
std::string recjson;
|
||||
|
||||
// Search in root first
|
||||
auto find = STLITER_FINDWITHCONDITION(list.root.titles, tit, (tit.app_id == app_id));
|
||||
if(STLITER_ISFOUND(list.root.titles, find))
|
||||
auto find = STL_FIND_IF(list.root.titles, tit, (tit.app_id == app_id));
|
||||
if(STL_FOUND(list.root.titles, find))
|
||||
{
|
||||
if(folder.empty()) return true; // It is already on root...?
|
||||
recjson = STLITER_UNWRAP(find).json_name;
|
||||
recjson = STL_UNWRAP(find).json_name;
|
||||
|
||||
list.root.titles.erase(find);
|
||||
title_found = true;
|
||||
|
@ -298,11 +323,11 @@ namespace cfg
|
|||
{
|
||||
for(auto &fld: list.folders)
|
||||
{
|
||||
auto find = STLITER_FINDWITHCONDITION(fld.titles, entry, (entry.app_id == app_id));
|
||||
if(STLITER_ISFOUND(fld.titles, find))
|
||||
auto find = STL_FIND_IF(fld.titles, entry, (entry.app_id == app_id));
|
||||
if(STL_FOUND(fld.titles, find))
|
||||
{
|
||||
if(fld.name == folder) return true; // It is already on that folder...?
|
||||
recjson = STLITER_UNWRAP(find).json_name;
|
||||
recjson = STL_UNWRAP(find).json_name;
|
||||
|
||||
fld.titles.erase(find);
|
||||
title_found = true;
|
||||
|
@ -325,10 +350,10 @@ namespace cfg
|
|||
}
|
||||
else // Add it to the new folder
|
||||
{
|
||||
auto find2 = STLITER_FINDWITHCONDITION(list.folders, fld, (fld.name == folder));
|
||||
if(STLITER_ISFOUND(list.folders, find2))
|
||||
auto find2 = STL_FIND_IF(list.folders, fld, (fld.name == folder));
|
||||
if(STL_FOUND(list.folders, find2))
|
||||
{
|
||||
STLITER_UNWRAP(find2).titles.push_back(title);
|
||||
STL_UNWRAP(find2).titles.push_back(title);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -354,11 +379,11 @@ namespace cfg
|
|||
// Search in root first
|
||||
if((TitleType)record.title_type == TitleType::Installed)
|
||||
{
|
||||
auto find = STLITER_FINDWITHCONDITION(list.root.titles, tit, (tit.title_type == record.title_type) && (tit.app_id == record.app_id));
|
||||
if(STLITER_ISFOUND(list.root.titles, find))
|
||||
auto find = STL_FIND_IF(list.root.titles, tit, (tit.title_type == record.title_type) && (tit.app_id == record.app_id));
|
||||
if(STL_FOUND(list.root.titles, find))
|
||||
{
|
||||
if(folder.empty()) return true; // It is already on root...?
|
||||
recjson = STLITER_UNWRAP(find).json_name;
|
||||
recjson = STL_UNWRAP(find).json_name;
|
||||
|
||||
list.root.titles.erase(find);
|
||||
title_found = true;
|
||||
|
@ -366,11 +391,11 @@ namespace cfg
|
|||
}
|
||||
else
|
||||
{
|
||||
auto find = STLITER_FINDWITHCONDITION(list.root.titles, tit, (tit.title_type == record.title_type) && (strcmp(tit.nro_target.nro_path, record.nro_target.nro_path) == 0));
|
||||
if(STLITER_ISFOUND(list.root.titles, find))
|
||||
auto find = STL_FIND_IF(list.root.titles, tit, (tit.title_type == record.title_type) && (strcmp(tit.nro_target.nro_path, record.nro_target.nro_path) == 0));
|
||||
if(STL_FOUND(list.root.titles, find))
|
||||
{
|
||||
if(folder.empty()) return true; // It is already on root...?
|
||||
recjson = STLITER_UNWRAP(find).json_name;
|
||||
recjson = STL_UNWRAP(find).json_name;
|
||||
|
||||
list.root.titles.erase(find);
|
||||
title_found = true;
|
||||
|
@ -383,11 +408,11 @@ namespace cfg
|
|||
{
|
||||
if((TitleType)record.title_type == TitleType::Installed)
|
||||
{
|
||||
auto find = STLITER_FINDWITHCONDITION(fld.titles, tit, (tit.title_type == record.title_type) && (tit.app_id == record.app_id));
|
||||
if(STLITER_ISFOUND(fld.titles, find))
|
||||
auto find = STL_FIND_IF(fld.titles, tit, (tit.title_type == record.title_type) && (tit.app_id == record.app_id));
|
||||
if(STL_FOUND(fld.titles, find))
|
||||
{
|
||||
if(fld.name == folder) return true; // It is already on that folder...?
|
||||
recjson = STLITER_UNWRAP(find).json_name;
|
||||
recjson = STL_UNWRAP(find).json_name;
|
||||
|
||||
fld.titles.erase(find);
|
||||
title_found = true;
|
||||
|
@ -396,11 +421,11 @@ namespace cfg
|
|||
}
|
||||
else
|
||||
{
|
||||
auto find = STLITER_FINDWITHCONDITION(fld.titles, tit, (tit.title_type == record.title_type) && (strcmp(tit.nro_target.nro_path, record.nro_target.nro_path) == 0));
|
||||
if(STLITER_ISFOUND(fld.titles, find))
|
||||
auto find = STL_FIND_IF(fld.titles, tit, (tit.title_type == record.title_type) && (strcmp(tit.nro_target.nro_path, record.nro_target.nro_path) == 0));
|
||||
if(STL_FOUND(fld.titles, find))
|
||||
{
|
||||
if(fld.name == folder) return true; // It is already on that folder...?
|
||||
recjson = STLITER_UNWRAP(find).json_name;
|
||||
recjson = STL_UNWRAP(find).json_name;
|
||||
|
||||
fld.titles.erase(find);
|
||||
title_found = true;
|
||||
|
@ -422,10 +447,10 @@ namespace cfg
|
|||
}
|
||||
else // Add it to the new folder
|
||||
{
|
||||
auto find2 = STLITER_FINDWITHCONDITION(list.folders, fld, (fld.name == folder));
|
||||
if(STLITER_ISFOUND(list.folders, find2))
|
||||
auto find2 = STL_FIND_IF(list.folders, fld, (fld.name == folder));
|
||||
if(STL_FOUND(list.folders, find2))
|
||||
{
|
||||
STLITER_UNWRAP(find2).titles.push_back(title);
|
||||
STL_UNWRAP(find2).titles.push_back(title);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -446,10 +471,10 @@ namespace cfg
|
|||
{
|
||||
if(!name.empty())
|
||||
{
|
||||
auto f = STLITER_FINDWITHCONDITION(list.folders, fld, (fld.name == name));
|
||||
if(STLITER_ISFOUND(list.folders, f))
|
||||
auto f = STL_FIND_IF(list.folders, fld, (fld.name == name));
|
||||
if(STL_FOUND(list.folders, f))
|
||||
{
|
||||
return STLITER_UNWRAP(f);
|
||||
return STL_UNWRAP(f);
|
||||
}
|
||||
}
|
||||
return list.root;
|
||||
|
@ -464,18 +489,18 @@ namespace cfg
|
|||
// Search in root first
|
||||
if((TitleType)record.title_type == TitleType::Installed)
|
||||
{
|
||||
auto find = STLITER_FINDWITHCONDITION(list.root.titles, tit, (tit.title_type == record.title_type) && (tit.app_id == record.app_id));
|
||||
if(STLITER_ISFOUND(list.root.titles, find))
|
||||
auto find = STL_FIND_IF(list.root.titles, tit, (tit.title_type == record.title_type) && (tit.app_id == record.app_id));
|
||||
if(STL_FOUND(list.root.titles, find))
|
||||
{
|
||||
if(!STLITER_UNWRAP(find).json_name.empty()) title_found = true;
|
||||
if(!STL_UNWRAP(find).json_name.empty()) title_found = true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
auto find = STLITER_FINDWITHCONDITION(list.root.titles, tit, (tit.title_type == record.title_type) && (strcmp(tit.nro_target.nro_path, record.nro_target.nro_path) == 0));
|
||||
if(STLITER_ISFOUND(list.root.titles, find))
|
||||
auto find = STL_FIND_IF(list.root.titles, tit, (tit.title_type == record.title_type) && (strcmp(tit.nro_target.nro_path, record.nro_target.nro_path) == 0));
|
||||
if(STL_FOUND(list.root.titles, find))
|
||||
{
|
||||
if(!STLITER_UNWRAP(find).json_name.empty()) title_found = true;
|
||||
if(!STL_UNWRAP(find).json_name.empty()) title_found = true;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -485,10 +510,10 @@ namespace cfg
|
|||
{
|
||||
if((TitleType)record.title_type == TitleType::Installed)
|
||||
{
|
||||
auto find = STLITER_FINDWITHCONDITION(fld.titles, tit, (tit.title_type == record.title_type) && (tit.app_id == record.app_id));
|
||||
if(STLITER_ISFOUND(fld.titles, find))
|
||||
auto find = STL_FIND_IF(fld.titles, tit, (tit.title_type == record.title_type) && (tit.app_id == record.app_id));
|
||||
if(STL_FOUND(fld.titles, find))
|
||||
{
|
||||
if(!STLITER_UNWRAP(find).json_name.empty())
|
||||
if(!STL_UNWRAP(find).json_name.empty())
|
||||
{
|
||||
title_found = true;
|
||||
break;
|
||||
|
@ -497,10 +522,10 @@ namespace cfg
|
|||
}
|
||||
else
|
||||
{
|
||||
auto find = STLITER_FINDWITHCONDITION(fld.titles, tit, (tit.title_type == record.title_type) && (strcmp(tit.nro_target.nro_path, record.nro_target.nro_path) == 0));
|
||||
if(STLITER_ISFOUND(fld.titles, find))
|
||||
auto find = STL_FIND_IF(fld.titles, tit, (tit.title_type == record.title_type) && (strcmp(tit.nro_target.nro_path, record.nro_target.nro_path) == 0));
|
||||
if(STL_FOUND(fld.titles, find))
|
||||
{
|
||||
if(!STLITER_UNWRAP(find).json_name.empty())
|
||||
if(!STL_UNWRAP(find).json_name.empty())
|
||||
{
|
||||
title_found = true;
|
||||
break;
|
||||
|
@ -550,16 +575,16 @@ namespace cfg
|
|||
rec.app_id = appid;
|
||||
rec.title_type = (u32)TitleType::Installed;
|
||||
|
||||
auto find = STLITER_FINDWITHCONDITION(list.root.titles, tit, (tit.app_id == appid));
|
||||
if(STLITER_ISFOUND(list.root.titles, find))
|
||||
auto find = STL_FIND_IF(list.root.titles, tit, (tit.app_id == appid));
|
||||
if(STL_FOUND(list.root.titles, find))
|
||||
{
|
||||
list.root.titles.erase(find);
|
||||
}
|
||||
|
||||
auto find2 = STLITER_FINDWITHCONDITION(list.folders, fld, (fld.name == folder));
|
||||
if(STLITER_ISFOUND(list.folders, find2))
|
||||
auto find2 = STL_FIND_IF(list.folders, fld, (fld.name == folder));
|
||||
if(STL_FOUND(list.folders, find2))
|
||||
{
|
||||
STLITER_UNWRAP(find2).titles.push_back(rec);
|
||||
STL_UNWRAP(find2).titles.push_back(rec);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -590,8 +615,8 @@ namespace cfg
|
|||
if(folder.empty()) list.root.titles.push_back(rec);
|
||||
else
|
||||
{
|
||||
auto find = STLITER_FINDWITHCONDITION(list.folders, fld, (fld.name == folder));
|
||||
if(STLITER_ISFOUND(list.folders, find)) STLITER_UNWRAP(find).titles.push_back(rec);
|
||||
auto find = STL_FIND_IF(list.folders, fld, (fld.name == folder));
|
||||
if(STL_FOUND(list.folders, find)) STL_UNWRAP(find).titles.push_back(rec);
|
||||
else
|
||||
{
|
||||
TitleFolder fld = {};
|
||||
|
|
|
@ -15,4 +15,11 @@ namespace os
|
|||
psmGetChargerType(&cht);
|
||||
return (cht > ChargerType_None);
|
||||
}
|
||||
|
||||
std::string GetFirmwareVersion()
|
||||
{
|
||||
SetSysFirmwareVersion fwver;
|
||||
setsysGetFirmwareVersion(&fwver);
|
||||
return std::string(fwver.display_version);
|
||||
}
|
||||
}
|
|
@ -61,8 +61,6 @@ namespace ui
|
|||
bool warnshown;
|
||||
bool homebrew_mode;
|
||||
u8 minalpha;
|
||||
u32 root_idx;
|
||||
u32 root_baseidx;
|
||||
u32 mode;
|
||||
s32 rawalpha;
|
||||
pu::audio::Sfx sfxTitleLaunch;
|
||||
|
|
Binary file not shown.
Before Width: | Height: | Size: 2 KiB After Width: | Height: | Size: 2 KiB |
|
@ -14,20 +14,25 @@ extern "C"
|
|||
size_t __nx_heap_size = 0x10000000; // 256MB heap - now we can use as much as we want from the applet pool ;)
|
||||
}
|
||||
|
||||
// Some global vars
|
||||
|
||||
ui::QMenuApplication::Ref qapp;
|
||||
cfg::TitleList list;
|
||||
std::vector<cfg::TitleRecord> homebrew;
|
||||
cfg::Config config;
|
||||
cfg::ProcessedTheme theme;
|
||||
u8 *app_buf;
|
||||
|
||||
namespace qmenu
|
||||
{
|
||||
void Initialize(bool cache_homebrew)
|
||||
void Initialize()
|
||||
{
|
||||
accountInitialize();
|
||||
nsInitialize();
|
||||
nifmInitialize();
|
||||
psmInitialize();
|
||||
setsysInitialize();
|
||||
|
||||
db::Mount();
|
||||
fs::CreateDirectory(Q_BASE_DB_DIR);
|
||||
fs::CreateDirectory(Q_BASE_SD_DIR);
|
||||
|
@ -37,10 +42,11 @@ namespace qmenu
|
|||
fs::CreateDirectory(Q_BASE_SD_DIR "/user");
|
||||
fs::CreateDirectory(Q_BASE_SD_DIR "/nro");
|
||||
db::Commit();
|
||||
|
||||
am::QMenu_InitializeDaemonService();
|
||||
|
||||
// Cache all homebrew (is this too slow...?)
|
||||
homebrew = cfg::QueryAllHomebrew(cache_homebrew);
|
||||
homebrew = cfg::QueryAllHomebrew(true);
|
||||
|
||||
// Load menu config
|
||||
config = cfg::EnsureConfig();
|
||||
|
@ -53,7 +59,10 @@ namespace qmenu
|
|||
void Exit()
|
||||
{
|
||||
am::QMenu_FinalizeDaemonService();
|
||||
|
||||
db::Unmount();
|
||||
|
||||
setsysExit();
|
||||
psmExit();
|
||||
nifmExit();
|
||||
nsExit();
|
||||
|
@ -61,15 +70,13 @@ namespace qmenu
|
|||
}
|
||||
}
|
||||
|
||||
u8 *app_buf;
|
||||
|
||||
int main()
|
||||
{
|
||||
auto [rc, smode] = am::QMenu_ProcessInput();
|
||||
if(R_SUCCEEDED(rc))
|
||||
{
|
||||
app_buf = new u8[RawRGBAScreenBufferSize]();
|
||||
qmenu::Initialize(smode == am::QMenuStartMode::StartupScreen); // Cache homebrew only on first launch
|
||||
qmenu::Initialize();
|
||||
auto [_rc, menulist] = cfg::LoadTitleList(true);
|
||||
list = menulist;
|
||||
|
||||
|
|
|
@ -20,8 +20,6 @@ namespace ui
|
|||
this->susptr = raw;
|
||||
this->mode = 0;
|
||||
this->rawalpha = 255;
|
||||
this->root_idx = 0;
|
||||
this->root_baseidx = 0;
|
||||
this->last_hasconn = false;
|
||||
this->last_batterylvl = 0;
|
||||
this->last_charge = false;
|
||||
|
@ -111,131 +109,166 @@ namespace ui
|
|||
|
||||
void MenuLayout::menu_Click(u64 down, u32 index)
|
||||
{
|
||||
if(index == 0)
|
||||
if((down & KEY_A) || (down & KEY_X) || (down & KEY_Y))
|
||||
{
|
||||
if(down & KEY_A)
|
||||
if(index == 0)
|
||||
{
|
||||
if(this->homebrew_mode)
|
||||
if(down & KEY_A)
|
||||
{
|
||||
am::QMenuCommandWriter writer(am::QDaemonMessage::LaunchHomebrewLibApplet);
|
||||
hb::TargetInput ipt = {};
|
||||
strcpy(ipt.nro_path, "sdmc:/hbmenu.nro"); // Launch normal hbmenu
|
||||
strcpy(ipt.argv, "sdmc:/hbmenu.nro");
|
||||
writer.Write<hb::TargetInput>(ipt);
|
||||
writer.FinishWrite();
|
||||
|
||||
pu::audio::Play(this->sfxTitleLaunch);
|
||||
qapp->StopPlayBGM();
|
||||
qapp->CloseWithFadeOut();
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
qapp->CreateShowDialog("All", "All titles...", {"Ok"}, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
u32 realidx = index - 1;
|
||||
if(this->homebrew_mode)
|
||||
{
|
||||
auto hb = homebrew[realidx];
|
||||
if(down & KEY_A) this->HandleHomebrewLaunch(hb);
|
||||
else if(down & KEY_X)
|
||||
{
|
||||
auto sopt = qapp->CreateShowDialog("Add to menu", "Would you like to add this homebrew to the main menu?", { "Yes", "Cancel" }, true);
|
||||
if(sopt == 0)
|
||||
if(this->homebrew_mode)
|
||||
{
|
||||
if(cfg::ExistsRecord(list, hb)) qapp->CreateShowDialog("Add to menu", "The homebrew is alredy in the main menu.\nNothing was added nor removed.", { "Ok" }, true);
|
||||
else
|
||||
{
|
||||
cfg::SaveRecord(hb);
|
||||
list.root.titles.push_back(hb);
|
||||
qapp->CreateShowDialog("Add to menu", "The homebrew was successfully added to the main menu.", { "Ok" }, true);
|
||||
}
|
||||
am::QMenuCommandWriter writer(am::QDaemonMessage::LaunchHomebrewLibApplet);
|
||||
hb::TargetInput ipt = {};
|
||||
strcpy(ipt.nro_path, "sdmc:/hbmenu.nro"); // Launch normal hbmenu
|
||||
strcpy(ipt.argv, "sdmc:/hbmenu.nro");
|
||||
writer.Write<hb::TargetInput>(ipt);
|
||||
writer.FinishWrite();
|
||||
|
||||
pu::audio::Play(this->sfxTitleLaunch);
|
||||
qapp->StopPlayBGM();
|
||||
qapp->CloseWithFadeOut();
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
qapp->CreateShowDialog("All", "All titles...", {"Ok"}, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
auto &folder = cfg::FindFolderByName(list, this->curfolder);
|
||||
if(realidx < folder.titles.size())
|
||||
u32 realidx = index - 1;
|
||||
if(this->homebrew_mode)
|
||||
{
|
||||
auto title = folder.titles[realidx];
|
||||
if(down & KEY_A)
|
||||
{
|
||||
if(!qapp->IsSuspended())
|
||||
{
|
||||
if((cfg::TitleType)title.title_type == cfg::TitleType::Homebrew) this->HandleHomebrewLaunch(title);
|
||||
else
|
||||
{
|
||||
am::QMenuCommandWriter writer(am::QDaemonMessage::LaunchApplication);
|
||||
writer.Write<u64>(title.app_id);
|
||||
writer.FinishWrite();
|
||||
|
||||
am::QMenuCommandResultReader reader;
|
||||
if(reader && R_SUCCEEDED(reader.GetReadResult()))
|
||||
{
|
||||
pu::audio::Play(this->sfxTitleLaunch);
|
||||
qapp->StopPlayBGM();
|
||||
qapp->CloseWithFadeOut();
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
auto rc = reader.GetReadResult();
|
||||
qapp->CreateShowDialog("Title launch", "An error ocurred attempting to launch the title:\n" + util::FormatResultDisplay(rc) + " (" + util::FormatResultHex(rc) + ")", { "Ok" }, true);
|
||||
}
|
||||
reader.FinishRead();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if((cfg::TitleType)title.title_type == cfg::TitleType::Homebrew)
|
||||
{
|
||||
if(std::string(title.nro_target.nro_path) == qapp->GetSuspendedHomebrewPath())
|
||||
{
|
||||
if(this->mode == 1) this->mode = 2;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if(title.app_id == qapp->GetSuspendedApplicationId())
|
||||
{
|
||||
if(this->mode == 1) this->mode = 2;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
auto hb = homebrew[realidx];
|
||||
if(down & KEY_A) this->HandleHomebrewLaunch(hb);
|
||||
else if(down & KEY_X)
|
||||
{
|
||||
if(qapp->IsSuspended())
|
||||
{
|
||||
if((cfg::TitleType)title.title_type == cfg::TitleType::Homebrew)
|
||||
{
|
||||
if(std::string(title.nro_target.nro_path) == qapp->GetSuspendedHomebrewPath()) this->HandleCloseSuspended();
|
||||
}
|
||||
else
|
||||
{
|
||||
if(title.app_id == qapp->GetSuspendedApplicationId()) this->HandleCloseSuspended();
|
||||
}
|
||||
if(std::string(hb.nro_target.nro_path) == qapp->GetSuspendedHomebrewPath()) this->HandleCloseSuspended();
|
||||
}
|
||||
}
|
||||
else if(down & KEY_Y)
|
||||
{
|
||||
if(this->HandleFolderChange(title))
|
||||
auto sopt = qapp->CreateShowDialog("Add to menu", "Would you like to add this homebrew to the main menu?", { "Yes", "Cancel" }, true);
|
||||
if(sopt == 0)
|
||||
{
|
||||
this->MoveFolder(this->curfolder, true);
|
||||
if(cfg::ExistsRecord(list, hb)) qapp->CreateShowDialog("Add to menu", "The homebrew is already in the main menu.\nNothing was added nor removed.\n\nYou can remove it from main menu itself.", { "Ok" }, true);
|
||||
else
|
||||
{
|
||||
cfg::SaveRecord(hb);
|
||||
list.root.titles.push_back(hb);
|
||||
qapp->CreateShowDialog("Add to menu", "The homebrew was successfully added to the main menu.", { "Ok" }, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
auto foldr = list.folders[realidx - folder.titles.size()];
|
||||
if(down & KEY_A)
|
||||
auto &folder = cfg::FindFolderByName(list, this->curfolder);
|
||||
if(realidx < folder.titles.size())
|
||||
{
|
||||
this->MoveFolder(foldr.name, true);
|
||||
auto title = folder.titles[realidx];
|
||||
if(down & KEY_A)
|
||||
{
|
||||
if(!qapp->IsSuspended())
|
||||
{
|
||||
if((cfg::TitleType)title.title_type == cfg::TitleType::Homebrew) this->HandleHomebrewLaunch(title);
|
||||
else
|
||||
{
|
||||
am::QMenuCommandWriter writer(am::QDaemonMessage::LaunchApplication);
|
||||
writer.Write<u64>(title.app_id);
|
||||
writer.FinishWrite();
|
||||
|
||||
am::QMenuCommandResultReader reader;
|
||||
if(reader && R_SUCCEEDED(reader.GetReadResult()))
|
||||
{
|
||||
pu::audio::Play(this->sfxTitleLaunch);
|
||||
qapp->StopPlayBGM();
|
||||
qapp->CloseWithFadeOut();
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
auto rc = reader.GetReadResult();
|
||||
qapp->CreateShowDialog("Title launch", "An error ocurred attempting to launch the title:\n" + util::FormatResultDisplay(rc) + " (" + util::FormatResultHex(rc) + ")", { "Ok" }, true);
|
||||
}
|
||||
reader.FinishRead();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if((cfg::TitleType)title.title_type == cfg::TitleType::Homebrew)
|
||||
{
|
||||
if(std::string(title.nro_target.nro_path) == qapp->GetSuspendedHomebrewPath())
|
||||
{
|
||||
if(this->mode == 1) this->mode = 2;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if(title.app_id == qapp->GetSuspendedApplicationId())
|
||||
{
|
||||
if(this->mode == 1) this->mode = 2;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else if(down & KEY_X)
|
||||
{
|
||||
if(qapp->IsSuspended())
|
||||
{
|
||||
if((cfg::TitleType)title.title_type == cfg::TitleType::Homebrew)
|
||||
{
|
||||
if(std::string(title.nro_target.nro_path) == qapp->GetSuspendedHomebrewPath()) this->HandleCloseSuspended();
|
||||
}
|
||||
else
|
||||
{
|
||||
if(title.app_id == qapp->GetSuspendedApplicationId()) this->HandleCloseSuspended();
|
||||
}
|
||||
}
|
||||
}
|
||||
else if(down & KEY_Y)
|
||||
{
|
||||
if((cfg::TitleType)title.title_type == cfg::TitleType::Homebrew)
|
||||
{
|
||||
auto sopt = qapp->CreateShowDialog("Entry options", "What would you like to do with the selected entry?", { "Move to/from folder", "Remove", "Cancel" }, true);
|
||||
if(sopt == 0)
|
||||
{
|
||||
if(this->HandleFolderChange(title))
|
||||
{
|
||||
this->MoveFolder(this->curfolder, true);
|
||||
}
|
||||
}
|
||||
else if(sopt == 1)
|
||||
{
|
||||
auto sopt2 = qapp->CreateShowDialog("Remove entry", "Would you like to remove this entry from main menu?\nThis homebrew will still be launchable from the homebrew menu.", { "Yes", "No" }, true);
|
||||
if(sopt2 == 0)
|
||||
{
|
||||
cfg::RemoveRecord(title);
|
||||
folder.titles.erase(folder.titles.begin() + realidx);
|
||||
qapp->CreateShowDialog("Remove entry", "The entry was successfully removed.", { "Ok" }, true);
|
||||
this->MoveFolder(this->curfolder, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if(this->HandleFolderChange(title))
|
||||
{
|
||||
this->MoveFolder(this->curfolder, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
auto foldr = list.folders[realidx - folder.titles.size()];
|
||||
if(down & KEY_A)
|
||||
{
|
||||
this->MoveFolder(foldr.name, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -326,62 +359,45 @@ namespace ui
|
|||
{
|
||||
if(fade) qapp->FadeOut();
|
||||
|
||||
if(this->homebrew_mode)
|
||||
auto itm_list = homebrew;
|
||||
if(!this->homebrew_mode)
|
||||
{
|
||||
this->itemsMenu->ClearItems();
|
||||
this->itemsMenu->AddItem(cfg::ProcessedThemeResource(theme, "ui/Hbmenu.png"));
|
||||
for(auto itm: homebrew)
|
||||
{
|
||||
this->itemsMenu->AddItem(cfg::GetRecordIconPath(itm));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if(this->curfolder.empty())
|
||||
{
|
||||
// Moving from root to a folder, let's save the indexes we were on
|
||||
this->root_idx = itemsMenu->GetSelectedItem();
|
||||
this->root_baseidx = itemsMenu->GetBaseItemIndex();
|
||||
}
|
||||
|
||||
auto &folder = cfg::FindFolderByName(list, name);
|
||||
this->itemsMenu->ClearItems();
|
||||
itm_list = folder.titles;
|
||||
}
|
||||
|
||||
// Add first item for all titles menu
|
||||
this->itemsMenu->AddItem(cfg::ProcessedThemeResource(theme, "ui/AllTitles.png"));
|
||||
u32 tmpidx = 0;
|
||||
for(auto itm: folder.titles)
|
||||
this->itemsMenu->ClearItems();
|
||||
auto initial_itm = cfg::ProcessedThemeResource(theme, "ui/AllTitles.png");
|
||||
if(this->homebrew_mode) initial_itm = cfg::ProcessedThemeResource(theme, "ui/Hbmenu.png");
|
||||
this->itemsMenu->AddItem(initial_itm);
|
||||
|
||||
u32 tmpidx = 0;
|
||||
for(auto itm: itm_list)
|
||||
{
|
||||
if((cfg::TitleType)itm.title_type == cfg::TitleType::Installed)
|
||||
{
|
||||
if((cfg::TitleType)itm.title_type == cfg::TitleType::Installed)
|
||||
{
|
||||
if(qapp->IsTitleSuspended()) if(qapp->GetSuspendedApplicationId() == itm.app_id) this->itemsMenu->SetSuspendedItem(tmpidx + 1); // 1st item is always "all titles"!
|
||||
}
|
||||
else
|
||||
{
|
||||
if(qapp->IsHomebrewSuspended()) if(qapp->GetSuspendedHomebrewPath() == std::string(itm.nro_target.nro_path)) this->itemsMenu->SetSuspendedItem(tmpidx + 1); // 1st item is always "all titles"!
|
||||
}
|
||||
this->itemsMenu->AddItem(cfg::GetRecordIconPath(itm));
|
||||
tmpidx++;
|
||||
if(qapp->IsTitleSuspended()) if(qapp->GetSuspendedApplicationId() == itm.app_id) this->itemsMenu->SetSuspendedItem(tmpidx + 1); // Skip initial item
|
||||
}
|
||||
else
|
||||
{
|
||||
if(qapp->IsHomebrewSuspended()) if(qapp->GetSuspendedHomebrewPath() == std::string(itm.nro_target.nro_path)) this->itemsMenu->SetSuspendedItem(tmpidx + 1); // Skip initial item
|
||||
}
|
||||
this->itemsMenu->AddItem(cfg::GetRecordIconPath(itm));
|
||||
tmpidx++;
|
||||
}
|
||||
|
||||
if(!this->homebrew_mode) // Only normal menu has folders
|
||||
{
|
||||
if(name.empty())
|
||||
{
|
||||
std::vector<cfg::TitleFolder> folders;
|
||||
for(auto folder: list.folders)
|
||||
{
|
||||
if(!folder.titles.empty())
|
||||
{
|
||||
folders.push_back(folder);
|
||||
this->itemsMenu->AddItem(cfg::ProcessedThemeResource(theme, "ui/Folder.png"));
|
||||
}
|
||||
}
|
||||
list.folders = folders;
|
||||
this->itemsMenu->SetBasePositions(this->root_idx, this->root_baseidx);
|
||||
STL_REMOVE_IF(list.folders, fldr, (fldr.titles.empty())) // Remove empty folders
|
||||
for(auto folder: list.folders) this->itemsMenu->AddItem(cfg::ProcessedThemeResource(theme, "ui/Folder.png"));
|
||||
}
|
||||
this->itemsMenu->UpdateBorderIcons();
|
||||
|
||||
this->curfolder = name;
|
||||
}
|
||||
|
||||
this->itemsMenu->UpdateBorderIcons();
|
||||
if(!this->homebrew_mode) this->curfolder = name;
|
||||
|
||||
if(fade) qapp->FadeIn();
|
||||
}
|
||||
|
||||
|
@ -568,6 +584,7 @@ namespace ui
|
|||
swkbdConfigSetHeaderText(&swkbd, "Enter directory name");
|
||||
char dir[500] = {0};
|
||||
auto rc = swkbdShow(&swkbd, dir, 500);
|
||||
swkbdClose(&swkbd);
|
||||
if(R_SUCCEEDED(rc))
|
||||
{
|
||||
changedone = cfg::MoveRecordTo(list, rec, std::string(dir));
|
||||
|
@ -575,7 +592,7 @@ namespace ui
|
|||
}
|
||||
else
|
||||
{
|
||||
auto sopt = qapp->CreateShowDialog("Title move", "Would you like to move this entry outside the folder?", { "Yes", "Cancel" }, true);
|
||||
auto sopt = qapp->CreateShowDialog("Entry move", "Would you like to move this entry outside the folder?", { "Yes", "Cancel" }, true);
|
||||
if(sopt == 0)
|
||||
{
|
||||
changedone = cfg::MoveRecordTo(list, rec, "");
|
||||
|
@ -652,6 +669,7 @@ namespace ui
|
|||
swkbdConfigSetHeaderText(&swkbd, "Enter web page URL");
|
||||
char url[500] = {0};
|
||||
auto rc = swkbdShow(&swkbd, url, 500);
|
||||
swkbdClose(&swkbd);
|
||||
if(R_SUCCEEDED(rc))
|
||||
{
|
||||
WebCommonConfig web = {};
|
||||
|
|
|
@ -139,7 +139,10 @@ namespace ui
|
|||
void SideMenu::ClearItems()
|
||||
{
|
||||
this->icons.clear();
|
||||
for(auto &ricon: this->ricons) pu::ui::render::DeleteTexture(ricon);
|
||||
for(auto ricon: this->ricons)
|
||||
{
|
||||
if(ricon != NULL) pu::ui::render::DeleteTexture(ricon);
|
||||
}
|
||||
this->ricons.clear();
|
||||
this->selitm = 0;
|
||||
this->baseiconidx = 0;
|
||||
|
|
|
@ -85,13 +85,13 @@ namespace ui
|
|||
swkbdConfigSetHeaderText(&swkbd, "Input user password");
|
||||
char inpass[0x10] = {0};
|
||||
auto rc = swkbdShow(&swkbd, inpass, 0x10);
|
||||
swkbdClose(&swkbd);
|
||||
if(R_SUCCEEDED(rc))
|
||||
{
|
||||
auto rc = db::TryLogUser(uid, std::string(inpass));
|
||||
if(R_FAILED(rc)) qapp->CreateShowDialog("Login", "Invalid password. Please try again.", {"Ok"}, true);
|
||||
else login_ok = true;
|
||||
}
|
||||
swkbdClose(&swkbd);
|
||||
}
|
||||
else login_ok = true;
|
||||
if(login_ok)
|
||||
|
|
10
Makefile
10
Makefile
|
@ -1,7 +1,7 @@
|
|||
|
||||
export Q_VERSION := dev
|
||||
|
||||
.PHONY: all clean
|
||||
.PHONY: all dev clean
|
||||
|
||||
all:
|
||||
@$(MAKE) -C SystemAppletQDaemon/
|
||||
|
@ -15,6 +15,14 @@ all:
|
|||
@cp -r $(CURDIR)/LibraryAppletQHbTarget/Out $(CURDIR)/SdOut/titles/0100000000001009
|
||||
@cp -r $(CURDIR)/SystemApplicationQHbTarget/Out $(CURDIR)/SdOut/titles/01008BB00013C000
|
||||
|
||||
setdev:
|
||||
$(eval export Q_DEV := 1)
|
||||
@echo
|
||||
@echo IMPORTANT! Building in development mode - do not treat this build as release...
|
||||
@echo
|
||||
|
||||
dev: setdev all
|
||||
|
||||
clean:
|
||||
@rm -rf $(CURDIR)/SdOut
|
||||
@$(MAKE) clean -C SystemAppletQDaemon/
|
||||
|
|
|
@ -4,6 +4,8 @@
|
|||
|
||||
uLaunch is a very ambicious project, consisting on two custom library applets, a custom system application and a custom system applet, in order to replace the console's **HOME menu** with a custom, homebrew-orienteed one.
|
||||
|
||||
No, this isn't any kind of HOME menu extension, injection, patch, etc. uLaunch is a **complete** reimplementation, 100% open-source, which also takes over eShop and Mii applets and flog system title, for its extended functionality.
|
||||
|
||||
## **NOTE:** the project is still a work-in-progress. Check what's left to do before release [here](TODO.md)!
|
||||
|
||||
- The project is licensed as **GPLv2**.
|
||||
|
|
|
@ -34,6 +34,10 @@ CFLAGS := -g -Wall -O2 -ffunction-sections \
|
|||
|
||||
CFLAGS += $(INCLUDE) -D__SWITCH__ -DQ_VERSION=\"$(Q_VERSION)\"
|
||||
|
||||
ifeq ($(Q_DEV),1)
|
||||
CFLAGS += -DQ_DEV
|
||||
endif
|
||||
|
||||
CXXFLAGS := $(CFLAGS) -fno-rtti -fno-exceptions -std=gnu++17
|
||||
|
||||
ASFLAGS := -g $(ARCH)
|
||||
|
|
|
@ -13,7 +13,11 @@
|
|||
extern "C"
|
||||
{
|
||||
u32 __nx_applet_type = AppletType_SystemApplet;
|
||||
size_t __nx_heap_size = 0x3000000;//0x1000000;
|
||||
#ifdef Q_DEV
|
||||
size_t __nx_heap_size = 0x3000000; // Dev uses 3x heap (48MB, still pretty low) for debug console
|
||||
#else
|
||||
size_t __nx_heap_size = 0x1000000;
|
||||
#endif
|
||||
}
|
||||
|
||||
u8 *app_buf;
|
||||
|
@ -286,52 +290,54 @@ namespace qdaemon
|
|||
app_buf = new u8[RawRGBAScreenBufferSize]();
|
||||
fs::CreateDirectory(Q_BASE_SD_DIR);
|
||||
|
||||
// Debug testing mode
|
||||
consoleInit(NULL);
|
||||
CONSOLE_FMT("Welcome to QDaemon's debug mode!")
|
||||
CONSOLE_FMT("")
|
||||
CONSOLE_FMT("(A) -> Dump system save data to sd:/<q>/save_dump")
|
||||
CONSOLE_FMT("(B) -> Delete everything in save data (except official HOME menu's content)")
|
||||
CONSOLE_FMT("(X) -> Reboot system")
|
||||
CONSOLE_FMT("(Y) -> Continue to QMenu (proceed launch)")
|
||||
CONSOLE_FMT("")
|
||||
#ifdef Q_DEV
|
||||
// Debug testing mode
|
||||
consoleInit(NULL);
|
||||
CONSOLE_FMT("Welcome to QDaemon's debug mode!")
|
||||
CONSOLE_FMT("")
|
||||
CONSOLE_FMT("(A) -> Dump system save data to sd:/<q>/save_dump")
|
||||
CONSOLE_FMT("(B) -> Delete everything in save data (except official HOME menu's content)")
|
||||
CONSOLE_FMT("(X) -> Reboot system")
|
||||
CONSOLE_FMT("(Y) -> Continue to QMenu (proceed launch)")
|
||||
CONSOLE_FMT("")
|
||||
|
||||
while(true)
|
||||
{
|
||||
hidScanInput();
|
||||
auto k = hidKeysDown(CONTROLLER_P1_AUTO);
|
||||
if(k & KEY_A)
|
||||
while(true)
|
||||
{
|
||||
db::Mount();
|
||||
fs::CopyDirectory(Q_DB_MOUNT_NAME ":/", Q_BASE_SD_DIR "/save_dump");
|
||||
db::Unmount();
|
||||
CONSOLE_FMT(" - Dump done.")
|
||||
hidScanInput();
|
||||
auto k = hidKeysDown(CONTROLLER_P1_AUTO);
|
||||
if(k & KEY_A)
|
||||
{
|
||||
db::Mount();
|
||||
fs::CopyDirectory(Q_DB_MOUNT_NAME ":/", Q_BASE_SD_DIR "/save_dump");
|
||||
db::Unmount();
|
||||
CONSOLE_FMT(" - Dump done.")
|
||||
}
|
||||
else if(k & KEY_B)
|
||||
{
|
||||
db::Mount();
|
||||
fs::DeleteDirectory(Q_BASE_DB_DIR);
|
||||
fs::CreateDirectory(Q_BASE_DB_DIR);
|
||||
db::Commit();
|
||||
db::Unmount();
|
||||
CONSOLE_FMT(" - Cleanup done.")
|
||||
}
|
||||
else if(k & KEY_X)
|
||||
{
|
||||
CONSOLE_FMT(" - Rebooting...")
|
||||
svcSleepThread(200'000'000);
|
||||
appletStartRebootSequence();
|
||||
}
|
||||
else if(k & KEY_Y)
|
||||
{
|
||||
CONSOLE_FMT(" - Proceeding with launch...")
|
||||
svcSleepThread(500'000'000);
|
||||
break;
|
||||
}
|
||||
svcSleepThread(10'000'000);
|
||||
}
|
||||
else if(k & KEY_B)
|
||||
{
|
||||
db::Mount();
|
||||
fs::DeleteDirectory(Q_BASE_DB_DIR);
|
||||
fs::CreateDirectory(Q_BASE_DB_DIR);
|
||||
db::Commit();
|
||||
db::Unmount();
|
||||
CONSOLE_FMT(" - Cleanup done.")
|
||||
}
|
||||
else if(k & KEY_X)
|
||||
{
|
||||
CONSOLE_FMT(" - Rebooting...")
|
||||
svcSleepThread(200'000'000);
|
||||
appletStartRebootSequence();
|
||||
}
|
||||
else if(k & KEY_Y)
|
||||
{
|
||||
CONSOLE_FMT(" - Proceeding with launch...")
|
||||
svcSleepThread(500'000'000);
|
||||
break;
|
||||
}
|
||||
svcSleepThread(10'000'000);
|
||||
}
|
||||
|
||||
consoleExit(NULL);
|
||||
consoleExit(NULL);
|
||||
#endif
|
||||
|
||||
svcSleepThread(100'000'000); // Wait for proper moment
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue