mirror of
https://github.com/XorTroll/uLaunch
synced 2024-11-10 14:34:13 +00:00
Changes, add IPC code, start with themes
This commit is contained in:
parent
0340098246
commit
5eaf42fe5e
21 changed files with 317 additions and 216 deletions
|
@ -20,4 +20,5 @@ namespace am
|
||||||
Result ApplicationStart(u64 app_id, bool system, u128 user_id);
|
Result ApplicationStart(u64 app_id, bool system, u128 user_id);
|
||||||
bool ApplicationHasForeground();
|
bool ApplicationHasForeground();
|
||||||
Result ApplicationSetForeground();
|
Result ApplicationSetForeground();
|
||||||
|
u64 ApplicationGetId();
|
||||||
}
|
}
|
|
@ -25,7 +25,7 @@ namespace am
|
||||||
LaunchApplication,
|
LaunchApplication,
|
||||||
ResumeApplication,
|
ResumeApplication,
|
||||||
TerminateApplication,
|
TerminateApplication,
|
||||||
GetLatestMessage,
|
GetSuspendedApplicationId
|
||||||
};
|
};
|
||||||
|
|
||||||
Result QDaemon_LaunchQMenu(QMenuStartMode mode);
|
Result QDaemon_LaunchQMenu(QMenuStartMode mode);
|
||||||
|
|
|
@ -14,6 +14,8 @@ namespace fs
|
||||||
void DeleteDirectory(std::string path);
|
void DeleteDirectory(std::string path);
|
||||||
void DeleteFile(std::string path);
|
void DeleteFile(std::string path);
|
||||||
|
|
||||||
|
bool WriteFile(std::string path, void *data, size_t size, bool overwrite);
|
||||||
|
bool ReadFile(std::string path, void *data, size_t size);
|
||||||
size_t GetFileSize(std::string path);
|
size_t GetFileSize(std::string path);
|
||||||
|
|
||||||
void ForEachFileIn(std::string dir, std::function<void(std::string name, std::string path)> fn);
|
void ForEachFileIn(std::string dir, std::function<void(std::string name, std::string path)> fn);
|
||||||
|
|
|
@ -22,7 +22,8 @@ using JSON = nlohmann::json;
|
||||||
#define Q_BASE_DIR "reqwrite"
|
#define Q_BASE_DIR "reqwrite"
|
||||||
#define Q_BASE_SD_DIR "sdmc:/" Q_BASE_DIR
|
#define Q_BASE_SD_DIR "sdmc:/" Q_BASE_DIR
|
||||||
#define Q_DB_MOUNT_NAME "qsave"
|
#define Q_DB_MOUNT_NAME "qsave"
|
||||||
#define Q_BASE_DB_DIR Q_DB_MOUNT_NAME ":/" Q_BASE_DIR
|
#define Q_DB_MOUNT_PATH Q_DB_MOUNT_NAME ":/"
|
||||||
|
#define Q_BASE_DB_DIR Q_DB_MOUNT_PATH Q_BASE_DIR
|
||||||
|
|
||||||
#define Q_MENU_JSON Q_BASE_SD_DIR "/menu.json"
|
#define Q_MENU_JSON Q_BASE_SD_DIR "/menu.json"
|
||||||
|
|
||||||
|
|
|
@ -4,7 +4,7 @@ namespace am
|
||||||
{
|
{
|
||||||
extern bool home_focus;
|
extern bool home_focus;
|
||||||
AppletApplication app_holder;
|
AppletApplication app_holder;
|
||||||
bool launched;
|
u64 latest_appid;
|
||||||
|
|
||||||
bool ApplicationIsActive()
|
bool ApplicationIsActive()
|
||||||
{
|
{
|
||||||
|
@ -45,6 +45,8 @@ namespace am
|
||||||
R_TRY(appletApplicationStart(&app_holder));
|
R_TRY(appletApplicationStart(&app_holder));
|
||||||
R_TRY(ApplicationSetForeground());
|
R_TRY(ApplicationSetForeground());
|
||||||
|
|
||||||
|
latest_appid = app_id;
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -59,4 +61,9 @@ namespace am
|
||||||
if(R_SUCCEEDED(rc)) home_focus = false;
|
if(R_SUCCEEDED(rc)) home_focus = false;
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
u64 ApplicationGetId()
|
||||||
|
{
|
||||||
|
return latest_appid;
|
||||||
|
}
|
||||||
}
|
}
|
|
@ -3,6 +3,7 @@
|
||||||
#include <util/util_JSON.hpp>
|
#include <util/util_JSON.hpp>
|
||||||
#include <util/util_String.hpp>
|
#include <util/util_String.hpp>
|
||||||
#include <fs/fs_Stdio.hpp>
|
#include <fs/fs_Stdio.hpp>
|
||||||
|
#include <db/db_Save.hpp>
|
||||||
#include <util/util_Convert.hpp>
|
#include <util/util_Convert.hpp>
|
||||||
|
|
||||||
namespace cfg
|
namespace cfg
|
||||||
|
@ -160,13 +161,7 @@ namespace cfg
|
||||||
fseek(f, hdr.size + ahdr.icon.size, SEEK_SET);
|
fseek(f, hdr.size + ahdr.icon.size, SEEK_SET);
|
||||||
if(fread(iconbuf, ahdr.icon.size, 1, f) == 1)
|
if(fread(iconbuf, ahdr.icon.size, 1, f) == 1)
|
||||||
{
|
{
|
||||||
fs::DeleteFile(nroimg);
|
fs::WriteFile(nroimg, iconbuf, ahdr.icon.size, true);
|
||||||
FILE *iconf = fopen(nroimg.c_str(), "wb");
|
|
||||||
if(iconf)
|
|
||||||
{
|
|
||||||
fwrite(iconbuf, 1, ahdr.icon.size, iconf);
|
|
||||||
fclose(iconf);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
delete[] iconbuf;
|
delete[] iconbuf;
|
||||||
}
|
}
|
||||||
|
@ -200,108 +195,6 @@ namespace cfg
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// Search SD card for installed title extensions or homebrew accessors
|
|
||||||
fs::ForEachFileIn(Q_BASE_SD_DIR "/entries", [&](std::string name, std::string path)
|
|
||||||
{
|
|
||||||
if(util::StringEndsWith(name, ".json"))
|
|
||||||
{
|
|
||||||
auto [rc, json] = util::LoadJSONFromFile(path);
|
|
||||||
if(R_SUCCEEDED(rc))
|
|
||||||
{
|
|
||||||
TitleType type = (TitleType)json.value("type", 0u);
|
|
||||||
if(type == TitleType::Installed)
|
|
||||||
{
|
|
||||||
std::string appidstr = json.value("application_id", "");
|
|
||||||
if(!appidstr.empty())
|
|
||||||
{
|
|
||||||
std::string folder = json.value("folder", "");
|
|
||||||
u64 appid = util::Get64FromString(appidstr);
|
|
||||||
if(appid > 0)
|
|
||||||
{
|
|
||||||
if(!folder.empty()) MoveTitleToDirectory(list, appid, folder);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if(type == TitleType::Homebrew)
|
|
||||||
{
|
|
||||||
std::string nropath = json.value("nro_path", "");
|
|
||||||
if(!nropath.empty())
|
|
||||||
{
|
|
||||||
TitleRecord rec = {};
|
|
||||||
rec.title_type = (u32)type;
|
|
||||||
rec.nro_path = "sdmc:";
|
|
||||||
if(nropath.front() != '/') rec.nro_path += "/";
|
|
||||||
rec.nro_path += nropath;
|
|
||||||
std::string folder = json.value("folder", "");
|
|
||||||
rec.sub_folder = folder;
|
|
||||||
if(cache)
|
|
||||||
{
|
|
||||||
auto nroimg = GetNROCacheIconPath(rec.nro_path);
|
|
||||||
FILE *f = fopen(rec.nro_path.c_str(), "rb");
|
|
||||||
if(f)
|
|
||||||
{
|
|
||||||
NroStart st = {};
|
|
||||||
if(fread(&st, sizeof(NroStart), 1, f) == 1)
|
|
||||||
{
|
|
||||||
NroHeader hdr = {};
|
|
||||||
if(fread(&hdr, sizeof(NroHeader), 1, f) == 1)
|
|
||||||
{
|
|
||||||
fseek(f, hdr.size, SEEK_SET);
|
|
||||||
NroAssetHeader ahdr = {};
|
|
||||||
if(fread(&ahdr, sizeof(NroAssetHeader), 1, f) == 1)
|
|
||||||
{
|
|
||||||
if(ahdr.magic == NROASSETHEADER_MAGIC)
|
|
||||||
{
|
|
||||||
if(ahdr.icon.size > 0)
|
|
||||||
{
|
|
||||||
u8 *iconbuf = new u8[ahdr.icon.size]();
|
|
||||||
fseek(f, hdr.size + ahdr.icon.size, SEEK_SET);
|
|
||||||
if(fread(iconbuf, ahdr.icon.size, 1, f) == 1)
|
|
||||||
{
|
|
||||||
fs::DeleteFile(nroimg);
|
|
||||||
FILE *iconf = fopen(nroimg.c_str(), "wb");
|
|
||||||
if(iconf)
|
|
||||||
{
|
|
||||||
fwrite(iconbuf, 1, ahdr.icon.size, iconf);
|
|
||||||
fclose(iconf);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
delete[] iconbuf;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
fclose(f);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
TitleFolder fld = {};
|
|
||||||
fld.name = folder;
|
|
||||||
fld.titles.push_back(rec);
|
|
||||||
list.folders.push_back(fld);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
return SuccessResultWith(list);
|
return SuccessResultWith(list);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -33,13 +33,7 @@ namespace db
|
||||||
auto filename = GetUserPasswordFilePath(user_id);
|
auto filename = GetUserPasswordFilePath(user_id);
|
||||||
if(fs::ExistsFile(filename))
|
if(fs::ExistsFile(filename))
|
||||||
{
|
{
|
||||||
FILE *f = fopen(filename.c_str(), "rb");
|
if(fs::ReadFile(filename, &pb, sizeof(pb))) return SuccessResultWith(pb);
|
||||||
if(f)
|
|
||||||
{
|
|
||||||
fread(&pb, 1, sizeof(PassBlock), f);
|
|
||||||
fclose(f);
|
|
||||||
return SuccessResultWith(pb);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return MakeResultWith(0xdead, pb);
|
return MakeResultWith(0xdead, pb);
|
||||||
}
|
}
|
||||||
|
@ -54,30 +48,16 @@ namespace db
|
||||||
{
|
{
|
||||||
std::string pwd;
|
std::string pwd;
|
||||||
auto filename = GetUserPasswordFilePath(user_id);
|
auto filename = GetUserPasswordFilePath(user_id);
|
||||||
if(fs::ExistsFile(filename))
|
if(fs::ExistsFile(filename)) return 0xdead;
|
||||||
{
|
|
||||||
FILE *f = fopen(filename.c_str(), "rb");
|
if((password.length() > 15) || (password.empty())) return 0xdead1;
|
||||||
if(f)
|
PassBlock pb = {};
|
||||||
{
|
memcpy(&pb.uid, &user_id, sizeof(u128));
|
||||||
fclose(f);
|
char tmppass[0x10] = {0};
|
||||||
return 0xdead;
|
strcpy(tmppass, password.c_str());
|
||||||
}
|
sha256CalculateHash(pb.pass_sha, tmppass, 0x10);
|
||||||
}
|
|
||||||
fs::DeleteFile(filename);
|
if(!fs::WriteFile(filename, &pb, sizeof(pb), true)) return 0xdead2;
|
||||||
FILE *f = fopen(filename.c_str(), "wb");
|
|
||||||
if(f)
|
|
||||||
{
|
|
||||||
if((password.length() > 15) || (password.empty())) return 0xdead1;
|
|
||||||
PassBlock pb = {};
|
|
||||||
memcpy(&pb.uid, &user_id, sizeof(u128));
|
|
||||||
char tmppass[0x10] = {0};
|
|
||||||
strcpy(tmppass, password.c_str());
|
|
||||||
sha256CalculateHash(pb.pass_sha, tmppass, 0x10);
|
|
||||||
fwrite(&pb, 1, sizeof(PassBlock), f);
|
|
||||||
fclose(f);
|
|
||||||
Commit();
|
|
||||||
}
|
|
||||||
else return 0xdead2;
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,11 @@
|
||||||
#include <fs/fs_Stdio.hpp>
|
#include <fs/fs_Stdio.hpp>
|
||||||
|
#include <util/util_String.hpp>
|
||||||
|
#include <db/db_Save.hpp>
|
||||||
|
|
||||||
namespace fs
|
namespace fs
|
||||||
{
|
{
|
||||||
|
#define FS_COMMIT_IF_DB_PATH(path) db::Commit();
|
||||||
|
|
||||||
static bool ExistsImpl(size_t st_mode, std::string path)
|
static bool ExistsImpl(size_t st_mode, std::string path)
|
||||||
{
|
{
|
||||||
struct stat st;
|
struct stat st;
|
||||||
|
@ -21,26 +25,56 @@ namespace fs
|
||||||
void CreateDirectory(std::string path)
|
void CreateDirectory(std::string path)
|
||||||
{
|
{
|
||||||
mkdir(path.c_str(), 777);
|
mkdir(path.c_str(), 777);
|
||||||
|
FS_COMMIT_IF_DB_PATH(path)
|
||||||
}
|
}
|
||||||
|
|
||||||
void CreateFile(std::string path)
|
void CreateFile(std::string path)
|
||||||
{
|
{
|
||||||
fsdevCreateFile(path.c_str(), 0, 0);
|
fsdevCreateFile(path.c_str(), 0, 0);
|
||||||
|
FS_COMMIT_IF_DB_PATH(path)
|
||||||
}
|
}
|
||||||
|
|
||||||
void CreateConcatenationFile(std::string path)
|
void CreateConcatenationFile(std::string path)
|
||||||
{
|
{
|
||||||
fsdevCreateFile(path.c_str(), 0, FS_CREATE_BIG_FILE);
|
fsdevCreateFile(path.c_str(), 0, FS_CREATE_BIG_FILE);
|
||||||
|
FS_COMMIT_IF_DB_PATH(path)
|
||||||
}
|
}
|
||||||
|
|
||||||
void DeleteDirectory(std::string path)
|
void DeleteDirectory(std::string path)
|
||||||
{
|
{
|
||||||
fsdevDeleteDirectoryRecursively(path.c_str());
|
fsdevDeleteDirectoryRecursively(path.c_str());
|
||||||
|
FS_COMMIT_IF_DB_PATH(path)
|
||||||
}
|
}
|
||||||
|
|
||||||
void DeleteFile(std::string path)
|
void DeleteFile(std::string path)
|
||||||
{
|
{
|
||||||
remove(path.c_str());
|
remove(path.c_str());
|
||||||
|
FS_COMMIT_IF_DB_PATH(path)
|
||||||
|
}
|
||||||
|
|
||||||
|
bool WriteFile(std::string path, void *data, size_t size, bool overwrite)
|
||||||
|
{
|
||||||
|
FILE *f = fopen(path.c_str(), overwrite ? "wb" : "ab+");
|
||||||
|
if(f)
|
||||||
|
{
|
||||||
|
fwrite(data, 1, size, f);
|
||||||
|
fclose(f);
|
||||||
|
FS_COMMIT_IF_DB_PATH(path)
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ReadFile(std::string path, void *data, size_t size)
|
||||||
|
{
|
||||||
|
FILE *f = fopen(path.c_str(), "rb");
|
||||||
|
if(f)
|
||||||
|
{
|
||||||
|
fread(data, 1, size, f);
|
||||||
|
fclose(f);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t GetFileSize(std::string path)
|
size_t GetFileSize(std::string path)
|
||||||
|
@ -86,6 +120,8 @@ namespace fs
|
||||||
void MoveFile(std::string p1, std::string p2)
|
void MoveFile(std::string p1, std::string p2)
|
||||||
{
|
{
|
||||||
rename(p1.c_str(), p2.c_str());
|
rename(p1.c_str(), p2.c_str());
|
||||||
|
FS_COMMIT_IF_DB_PATH(p1)
|
||||||
|
FS_COMMIT_IF_DB_PATH(p2)
|
||||||
}
|
}
|
||||||
|
|
||||||
void CopyFile(std::string p, std::string np)
|
void CopyFile(std::string p, std::string np)
|
||||||
|
@ -111,6 +147,7 @@ namespace fs
|
||||||
}
|
}
|
||||||
delete[] tmp;
|
delete[] tmp;
|
||||||
fclose(outf);
|
fclose(outf);
|
||||||
|
FS_COMMIT_IF_DB_PATH(np)
|
||||||
}
|
}
|
||||||
fclose(inf);
|
fclose(inf);
|
||||||
}
|
}
|
||||||
|
|
|
@ -38,15 +38,10 @@ namespace os
|
||||||
if(R_SUCCEEDED(rc))
|
if(R_SUCCEEDED(rc))
|
||||||
{
|
{
|
||||||
auto iconcache = GetIconCacheImagePath(uidbuf[i]);
|
auto iconcache = GetIconCacheImagePath(uidbuf[i]);
|
||||||
fs::DeleteFile(iconcache);
|
fs::WriteFile(iconcache, imgbuf, imgsz, true);
|
||||||
FILE *f = fopen(iconcache.c_str(), "wb");
|
|
||||||
if(f)
|
|
||||||
{
|
|
||||||
fwrite(imgbuf, 1, imgsz, f);
|
|
||||||
fclose(f);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
accountProfileClose(&prof);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -25,12 +25,8 @@ namespace os
|
||||||
{
|
{
|
||||||
auto fname = cfg::GetTitleCacheIconPath(rec.app_id);
|
auto fname = cfg::GetTitleCacheIconPath(rec.app_id);
|
||||||
fs::DeleteFile(fname);
|
fs::DeleteFile(fname);
|
||||||
FILE *f = fopen(fname.c_str(), "wb");
|
db::Commit();
|
||||||
if(f)
|
fs::WriteFile(fname, control.icon, sizeof(control.icon), true);
|
||||||
{
|
|
||||||
fwrite(control.icon, 1, sizeof(control.icon), f);
|
|
||||||
fclose(f);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
titles.push_back(rec);
|
titles.push_back(rec);
|
||||||
|
|
BIN
LibraryAppletQMenu/RomFs/default/ui/AllTitles.png
Normal file
BIN
LibraryAppletQMenu/RomFs/default/ui/AllTitles.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 6.8 KiB |
BIN
LibraryAppletQMenu/RomFs/default/ui/Folder.png
Normal file
BIN
LibraryAppletQMenu/RomFs/default/ui/Folder.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 6.8 KiB |
|
@ -24,6 +24,12 @@ namespace qmenu
|
||||||
accountInitialize();
|
accountInitialize();
|
||||||
nsInitialize();
|
nsInitialize();
|
||||||
db::Mount();
|
db::Mount();
|
||||||
|
fs::CreateDirectory(Q_BASE_DB_DIR);
|
||||||
|
fs::CreateDirectory(Q_BASE_SD_DIR);
|
||||||
|
fs::CreateDirectory(Q_BASE_SD_DIR "/title");
|
||||||
|
fs::CreateDirectory(Q_BASE_SD_DIR "/user");
|
||||||
|
fs::CreateDirectory(Q_BASE_SD_DIR "/nro");
|
||||||
|
db::Commit();
|
||||||
am::QMenu_InitializeDaemonService();
|
am::QMenu_InitializeDaemonService();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -38,25 +44,10 @@ namespace qmenu
|
||||||
|
|
||||||
u8 *app_buf;
|
u8 *app_buf;
|
||||||
|
|
||||||
extern "C"
|
|
||||||
{
|
|
||||||
void userAppInit(void)
|
|
||||||
{
|
|
||||||
qmenu::Initialize();
|
|
||||||
}
|
|
||||||
|
|
||||||
void userAppExit(void)
|
|
||||||
{
|
|
||||||
delete[] app_buf;
|
|
||||||
qmenu::Exit();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
int main()
|
int main()
|
||||||
{
|
{
|
||||||
app_buf = new u8[1280 * 720 * 4]();
|
app_buf = new u8[1280 * 720 * 4]();
|
||||||
fs::CreateDirectory(Q_BASE_DB_DIR);
|
qmenu::Initialize();
|
||||||
fs::CreateDirectory(Q_BASE_DB_DIR "/user");
|
|
||||||
auto [_rc, menulist] = cfg::LoadTitleList(true);
|
auto [_rc, menulist] = cfg::LoadTitleList(true);
|
||||||
list = menulist;
|
list = menulist;
|
||||||
|
|
||||||
|
@ -70,10 +61,11 @@ int main()
|
||||||
|
|
||||||
if(smode == am::QMenuStartMode::MenuApplicationSuspended)
|
if(smode == am::QMenuStartMode::MenuApplicationSuspended)
|
||||||
{
|
{
|
||||||
/*
|
am::QMenuCommandWriter writer(am::QDaemonMessage::GetSuspendedApplicationId);
|
||||||
auto app_id = reader.Read<u64>();
|
writer.FinishWrite();
|
||||||
qapp->SetSuspendedApplicationId(app_id);
|
am::QMenuCommandResultReader reader;
|
||||||
*/
|
if(reader) qapp->SetSuspendedApplicationId(reader.Read<u64>());
|
||||||
|
reader.FinishRead();
|
||||||
|
|
||||||
FILE *f = fopen(Q_BASE_SD_DIR "/temp-suspended.rgba", "rb");
|
FILE *f = fopen(Q_BASE_SD_DIR "/temp-suspended.rgba", "rb");
|
||||||
if(f)
|
if(f)
|
||||||
|
@ -91,5 +83,8 @@ int main()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
delete[] app_buf;
|
||||||
|
qmenu::Exit();
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
|
@ -20,6 +20,10 @@ namespace ui
|
||||||
this->Add(this->bgSuspendedRaw);
|
this->Add(this->bgSuspendedRaw);
|
||||||
|
|
||||||
this->itemsMenu = SideMenu::New(pu::ui::Color(0, 120, 255, 255), pu::ui::Color());
|
this->itemsMenu = SideMenu::New(pu::ui::Color(0, 120, 255, 255), pu::ui::Color());
|
||||||
|
|
||||||
|
// Add first item for all titles menu
|
||||||
|
this->itemsMenu->AddItem("romfs:/default/ui/AllTitles.png");
|
||||||
|
|
||||||
for(auto itm: list.root.titles)
|
for(auto itm: list.root.titles)
|
||||||
{
|
{
|
||||||
std::string iconpth;
|
std::string iconpth;
|
||||||
|
@ -29,7 +33,8 @@ namespace ui
|
||||||
}
|
}
|
||||||
for(auto folder: list.folders)
|
for(auto folder: list.folders)
|
||||||
{
|
{
|
||||||
this->itemsMenu->AddItem("romfs:/Test.jpg");
|
// TODO: folder logic
|
||||||
|
this->itemsMenu->AddItem("romfs:/default/Folder.png");
|
||||||
}
|
}
|
||||||
this->itemsMenu->SetOnItemSelected(std::bind(&MenuLayout::menu_Click, this, std::placeholders::_1));
|
this->itemsMenu->SetOnItemSelected(std::bind(&MenuLayout::menu_Click, this, std::placeholders::_1));
|
||||||
this->Add(this->itemsMenu);
|
this->Add(this->itemsMenu);
|
||||||
|
@ -39,34 +44,42 @@ namespace ui
|
||||||
|
|
||||||
void MenuLayout::menu_Click(u32 index)
|
void MenuLayout::menu_Click(u32 index)
|
||||||
{
|
{
|
||||||
if(index < list.root.titles.size())
|
if(index == 0)
|
||||||
{
|
{
|
||||||
auto title = list.root.titles[index];
|
qapp->CreateShowDialog("A", "All titles", {"Ok"}, true);
|
||||||
if(!qapp->IsTitleSuspended())
|
|
||||||
{
|
|
||||||
am::QMenuCommandWriter writer(am::QDaemonMessage::LaunchApplication);
|
|
||||||
writer.Write<u64>(title.app_id);
|
|
||||||
writer.Write<bool>(false);
|
|
||||||
writer.FinishWrite();
|
|
||||||
|
|
||||||
am::QMenuCommandResultReader reader;
|
|
||||||
if(reader && R_SUCCEEDED(reader.GetReadResult()))
|
|
||||||
{
|
|
||||||
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
|
else
|
||||||
{
|
{
|
||||||
auto folder = list.folders[index - list.root.titles.size()];
|
u32 realidx = index - 1;
|
||||||
qapp->CreateShowDialog("Folder", folder.name, {"Ok"}, true);
|
if(realidx < list.root.titles.size())
|
||||||
|
{
|
||||||
|
auto title = list.root.titles[realidx];
|
||||||
|
if(!qapp->IsTitleSuspended())
|
||||||
|
{
|
||||||
|
am::QMenuCommandWriter writer(am::QDaemonMessage::LaunchApplication);
|
||||||
|
writer.Write<u64>(title.app_id);
|
||||||
|
writer.Write<bool>(false);
|
||||||
|
writer.FinishWrite();
|
||||||
|
|
||||||
|
am::QMenuCommandResultReader reader;
|
||||||
|
if(reader && R_SUCCEEDED(reader.GetReadResult()))
|
||||||
|
{
|
||||||
|
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
|
||||||
|
{
|
||||||
|
auto folder = list.folders[realidx - list.root.titles.size()];
|
||||||
|
qapp->CreateShowDialog("Folder", folder.name, {"Ok"}, true);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -88,19 +101,6 @@ namespace ui
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
if(down & KEY_X)
|
|
||||||
{
|
|
||||||
auto [rc, msg] = am::QMenu_GetLatestQMenuMessage();
|
|
||||||
qapp->CreateShowDialog("HOME SWEET HOME", "0x" + util::FormatApplicationId((u32)msg), {"Ok"}, true);
|
|
||||||
if(qapp->IsTitleSuspended())
|
|
||||||
{
|
|
||||||
if(this->mode == 1) this->mode = 2;
|
|
||||||
}
|
|
||||||
else while(this->itemsMenu->GetSelectedItem() > 0) this->itemsMenu->HandleMoveLeft();
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|
||||||
if(this->susptr != NULL)
|
if(this->susptr != NULL)
|
||||||
{
|
{
|
||||||
|
|
||||||
|
|
|
@ -6,7 +6,7 @@ namespace ui
|
||||||
{
|
{
|
||||||
void QMenuApplication::OnLoad()
|
void QMenuApplication::OnLoad()
|
||||||
{
|
{
|
||||||
pu::ui::render::SetDefaultFont("romfs:/Gilroy-Bold.ttf");
|
pu::ui::render::SetDefaultFont("romfs:/default/ui/Font.ttf");
|
||||||
|
|
||||||
this->startupLayout = StartupLayout::New(pu::ui::Color(10, 120, 255, 255));
|
this->startupLayout = StartupLayout::New(pu::ui::Color(10, 120, 255, 255));
|
||||||
this->menuLayout = MenuLayout::New(app_buf);
|
this->menuLayout = MenuLayout::New(app_buf);
|
||||||
|
|
28
SystemAppletQDaemon/Include/ipc/ipc_IDaemonService.hpp
Normal file
28
SystemAppletQDaemon/Include/ipc/ipc_IDaemonService.hpp
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
#include <q_Include.hpp>
|
||||||
|
#include <stratosphere.hpp>
|
||||||
|
|
||||||
|
namespace ipc
|
||||||
|
{
|
||||||
|
class IDaemonService : public IServiceObject
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
|
||||||
|
enum class CommandId
|
||||||
|
{
|
||||||
|
GetLatestMessage = 0
|
||||||
|
};
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
Result GetLatestMessage(Out<u32> msg);
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
DEFINE_SERVICE_DISPATCH_TABLE
|
||||||
|
{
|
||||||
|
MAKE_SERVICE_COMMAND_META(IDaemonService, GetLatestMessage)
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
|
@ -13,7 +13,7 @@
|
||||||
extern "C"
|
extern "C"
|
||||||
{
|
{
|
||||||
u32 __nx_applet_type = AppletType_SystemApplet;
|
u32 __nx_applet_type = AppletType_SystemApplet;
|
||||||
size_t __nx_heap_size = 0x1000000;
|
size_t __nx_heap_size = 0x3000000;//0x1000000;
|
||||||
}
|
}
|
||||||
|
|
||||||
void CommonSleepHandle()
|
void CommonSleepHandle()
|
||||||
|
@ -187,6 +187,23 @@ void HandleQMenuMessage()
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
case am::QDaemonMessage::GetSuspendedApplicationId:
|
||||||
|
{
|
||||||
|
reader.FinishRead();
|
||||||
|
|
||||||
|
if(am::ApplicationIsActive())
|
||||||
|
{
|
||||||
|
am::QDaemonCommandResultWriter res(0);
|
||||||
|
res.Write<u64>(am::ApplicationGetId());
|
||||||
|
res.FinishWrite();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
am::QDaemonCommandResultWriter res(0xDEAD1);
|
||||||
|
res.FinishWrite();
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -201,6 +218,53 @@ namespace qdaemon
|
||||||
app_buf = new u8[1280 * 720 * 4]();
|
app_buf = new u8[1280 * 720 * 4]();
|
||||||
fs::CreateDirectory(Q_BASE_SD_DIR);
|
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("")
|
||||||
|
|
||||||
|
while(true)
|
||||||
|
{
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
consoleExit(NULL);
|
||||||
|
|
||||||
svcSleepThread(100'000'000); // Wait for proper moment
|
svcSleepThread(100'000'000); // Wait for proper moment
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
16
SystemAppletQDaemon/Source/ipc/ipc_IDaemonService.cpp
Normal file
16
SystemAppletQDaemon/Source/ipc/ipc_IDaemonService.cpp
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
#include <ipc/ipc_IDaemonService.hpp>
|
||||||
|
#include <am/am_QCommunications.hpp>
|
||||||
|
|
||||||
|
extern HosMutex latestqlock;
|
||||||
|
extern am::QMenuMessage latestqmenumsg;
|
||||||
|
|
||||||
|
namespace ipc
|
||||||
|
{
|
||||||
|
Result IDaemonService::GetLatestMessage(Out<u32> msg)
|
||||||
|
{
|
||||||
|
std::scoped_lock lck(latestqlock);
|
||||||
|
msg.SetValue((u32)latestqmenumsg);
|
||||||
|
latestqmenumsg = am::QMenuMessage::Invalid;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
85
Themes.md
Normal file
85
Themes.md
Normal file
|
@ -0,0 +1,85 @@
|
||||||
|
# Theme system
|
||||||
|
|
||||||
|
## **IMPORTANT!** Don't use this system yet. Wait until this warning is removed (when the system is finally decided)
|
||||||
|
|
||||||
|
> Current theme format version: none (unreleased yet)
|
||||||
|
|
||||||
|
Themes consist on plain directories replacing RomFs content.
|
||||||
|
|
||||||
|
A valid theme must, at least, contain a `/theme` subdirectory and a `/theme/Manifest.json` within it (icon or actual modifications aren't neccessary)
|
||||||
|
|
||||||
|
**Important note:** any content (sound or UI) not found in the theme will be loaded from the default config, thus there is no need to provide every file (for instance, when making UI-only or sound-only changes)
|
||||||
|
|
||||||
|
## Metadata
|
||||||
|
|
||||||
|
Metadata is stored inside `/theme` directory. It is required for the theme to be recognized as a valid one.
|
||||||
|
|
||||||
|
- `theme/Manifest.json` -> JSON containing theme information
|
||||||
|
|
||||||
|
Demo JSON:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"name": "My awesome theme",
|
||||||
|
"version": 0,
|
||||||
|
"release": "0.1",
|
||||||
|
"description": "This is a really cool theme, check it out!",
|
||||||
|
"author": "XorTroll"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Properties:
|
||||||
|
|
||||||
|
- **name**: Theme name
|
||||||
|
|
||||||
|
- **version**: Theme format version (qlaunch updates might introduce changes to themes, thus a new format version would be out).
|
||||||
|
|
||||||
|
- **release**: Theme version string
|
||||||
|
|
||||||
|
- **description**: Theme description
|
||||||
|
|
||||||
|
- **author**: Theme author name(s)
|
||||||
|
|
||||||
|
- `theme/Icon(.png?)` -> Icon (which size and format shall we use?)
|
||||||
|
|
||||||
|
## Sound
|
||||||
|
|
||||||
|
Sound consists on custom *background music* and *sound effects* via files inside `/sound`.
|
||||||
|
|
||||||
|
- `sound/BGM.mp3` -> MP3 file to replace with custom music
|
||||||
|
|
||||||
|
- `sound/BGM.json` -> JSON file with BGM settings
|
||||||
|
|
||||||
|
Demo JSON:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"loop": true,
|
||||||
|
"fade_in": true,
|
||||||
|
"fade_out": false
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Properties:
|
||||||
|
|
||||||
|
- **loop**: Whether to replay the MP3 file again, after it finishes
|
||||||
|
|
||||||
|
- **fade_in**: Whether starting the music should apply a fade-in effect.
|
||||||
|
|
||||||
|
- **fade_out**: Whether stopping the music should apply a fade-out effect.
|
||||||
|
|
||||||
|
Note: returning to/launching a title/applet and returning back to HOME menu will restart the music.
|
||||||
|
|
||||||
|
> *TODO: Sound effects*
|
||||||
|
|
||||||
|
## UI
|
||||||
|
|
||||||
|
Can be customized via files in `/ui`.
|
||||||
|
|
||||||
|
- `ui/Font.ttf` -> TTF font used for all the UI.
|
||||||
|
|
||||||
|
- `ui/AllTitles.png` -> 256x256 icon for the "all titles" entry in the main menu.
|
||||||
|
|
||||||
|
- `ui/Folder.png` -> 256x256 icon for folders in the main menu.
|
||||||
|
|
||||||
|
> *TODO: more customizable stuff*
|
1
libstratosphere
Submodule
1
libstratosphere
Submodule
|
@ -0,0 +1 @@
|
||||||
|
Subproject commit 8bae7b4a78f8fe5061596f354e38ecd0238cdaed
|
Loading…
Reference in a new issue