Changes, add IPC code, start with themes

This commit is contained in:
XorTroll 2019-10-11 17:43:17 +02:00
parent 0340098246
commit 5eaf42fe5e
21 changed files with 317 additions and 216 deletions

View file

@ -20,4 +20,5 @@ namespace am
Result ApplicationStart(u64 app_id, bool system, u128 user_id);
bool ApplicationHasForeground();
Result ApplicationSetForeground();
u64 ApplicationGetId();
}

View file

@ -25,7 +25,7 @@ namespace am
LaunchApplication,
ResumeApplication,
TerminateApplication,
GetLatestMessage,
GetSuspendedApplicationId
};
Result QDaemon_LaunchQMenu(QMenuStartMode mode);

View file

@ -14,6 +14,8 @@ namespace fs
void DeleteDirectory(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);
void ForEachFileIn(std::string dir, std::function<void(std::string name, std::string path)> fn);

View file

@ -22,7 +22,8 @@ using JSON = nlohmann::json;
#define Q_BASE_DIR "reqwrite"
#define Q_BASE_SD_DIR "sdmc:/" Q_BASE_DIR
#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"

View file

@ -4,7 +4,7 @@ namespace am
{
extern bool home_focus;
AppletApplication app_holder;
bool launched;
u64 latest_appid;
bool ApplicationIsActive()
{
@ -45,6 +45,8 @@ namespace am
R_TRY(appletApplicationStart(&app_holder));
R_TRY(ApplicationSetForeground());
latest_appid = app_id;
return 0;
}
@ -59,4 +61,9 @@ namespace am
if(R_SUCCEEDED(rc)) home_focus = false;
return rc;
}
u64 ApplicationGetId()
{
return latest_appid;
}
}

View file

@ -3,6 +3,7 @@
#include <util/util_JSON.hpp>
#include <util/util_String.hpp>
#include <fs/fs_Stdio.hpp>
#include <db/db_Save.hpp>
#include <util/util_Convert.hpp>
namespace cfg
@ -160,13 +161,7 @@ namespace cfg
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);
}
fs::WriteFile(nroimg, iconbuf, ahdr.icon.size, true);
}
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);
}

View file

@ -33,13 +33,7 @@ namespace db
auto filename = GetUserPasswordFilePath(user_id);
if(fs::ExistsFile(filename))
{
FILE *f = fopen(filename.c_str(), "rb");
if(f)
{
fread(&pb, 1, sizeof(PassBlock), f);
fclose(f);
return SuccessResultWith(pb);
}
if(fs::ReadFile(filename, &pb, sizeof(pb))) return SuccessResultWith(pb);
}
return MakeResultWith(0xdead, pb);
}
@ -54,30 +48,16 @@ namespace db
{
std::string pwd;
auto filename = GetUserPasswordFilePath(user_id);
if(fs::ExistsFile(filename))
{
FILE *f = fopen(filename.c_str(), "rb");
if(f)
{
fclose(f);
return 0xdead;
}
}
fs::DeleteFile(filename);
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;
if(fs::ExistsFile(filename)) return 0xdead;
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);
if(!fs::WriteFile(filename, &pb, sizeof(pb), true)) return 0xdead2;
return 0;
}

View file

@ -1,7 +1,11 @@
#include <fs/fs_Stdio.hpp>
#include <util/util_String.hpp>
#include <db/db_Save.hpp>
namespace fs
{
#define FS_COMMIT_IF_DB_PATH(path) db::Commit();
static bool ExistsImpl(size_t st_mode, std::string path)
{
struct stat st;
@ -21,26 +25,56 @@ namespace fs
void CreateDirectory(std::string path)
{
mkdir(path.c_str(), 777);
FS_COMMIT_IF_DB_PATH(path)
}
void CreateFile(std::string path)
{
fsdevCreateFile(path.c_str(), 0, 0);
FS_COMMIT_IF_DB_PATH(path)
}
void CreateConcatenationFile(std::string path)
{
fsdevCreateFile(path.c_str(), 0, FS_CREATE_BIG_FILE);
FS_COMMIT_IF_DB_PATH(path)
}
void DeleteDirectory(std::string path)
{
fsdevDeleteDirectoryRecursively(path.c_str());
FS_COMMIT_IF_DB_PATH(path)
}
void DeleteFile(std::string path)
{
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)
@ -86,6 +120,8 @@ namespace fs
void MoveFile(std::string p1, std::string p2)
{
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)
@ -111,6 +147,7 @@ namespace fs
}
delete[] tmp;
fclose(outf);
FS_COMMIT_IF_DB_PATH(np)
}
fclose(inf);
}

View file

@ -38,15 +38,10 @@ namespace os
if(R_SUCCEEDED(rc))
{
auto iconcache = GetIconCacheImagePath(uidbuf[i]);
fs::DeleteFile(iconcache);
FILE *f = fopen(iconcache.c_str(), "wb");
if(f)
{
fwrite(imgbuf, 1, imgsz, f);
fclose(f);
}
fs::WriteFile(iconcache, imgbuf, imgsz, true);
}
}
accountProfileClose(&prof);
}
}
}

View file

@ -25,12 +25,8 @@ namespace os
{
auto fname = cfg::GetTitleCacheIconPath(rec.app_id);
fs::DeleteFile(fname);
FILE *f = fopen(fname.c_str(), "wb");
if(f)
{
fwrite(control.icon, 1, sizeof(control.icon), f);
fclose(f);
}
db::Commit();
fs::WriteFile(fname, control.icon, sizeof(control.icon), true);
}
}
titles.push_back(rec);

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.8 KiB

View file

@ -24,6 +24,12 @@ namespace qmenu
accountInitialize();
nsInitialize();
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();
}
@ -38,25 +44,10 @@ namespace qmenu
u8 *app_buf;
extern "C"
{
void userAppInit(void)
{
qmenu::Initialize();
}
void userAppExit(void)
{
delete[] app_buf;
qmenu::Exit();
}
}
int main()
{
app_buf = new u8[1280 * 720 * 4]();
fs::CreateDirectory(Q_BASE_DB_DIR);
fs::CreateDirectory(Q_BASE_DB_DIR "/user");
qmenu::Initialize();
auto [_rc, menulist] = cfg::LoadTitleList(true);
list = menulist;
@ -70,10 +61,11 @@ int main()
if(smode == am::QMenuStartMode::MenuApplicationSuspended)
{
/*
auto app_id = reader.Read<u64>();
qapp->SetSuspendedApplicationId(app_id);
*/
am::QMenuCommandWriter writer(am::QDaemonMessage::GetSuspendedApplicationId);
writer.FinishWrite();
am::QMenuCommandResultReader reader;
if(reader) qapp->SetSuspendedApplicationId(reader.Read<u64>());
reader.FinishRead();
FILE *f = fopen(Q_BASE_SD_DIR "/temp-suspended.rgba", "rb");
if(f)
@ -91,5 +83,8 @@ int main()
}
}
delete[] app_buf;
qmenu::Exit();
return 0;
}

View file

@ -20,6 +20,10 @@ namespace ui
this->Add(this->bgSuspendedRaw);
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)
{
std::string iconpth;
@ -29,7 +33,8 @@ namespace ui
}
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->Add(this->itemsMenu);
@ -39,34 +44,42 @@ namespace ui
void MenuLayout::menu_Click(u32 index)
{
if(index < list.root.titles.size())
if(index == 0)
{
auto title = list.root.titles[index];
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();
}
qapp->CreateShowDialog("A", "All titles", {"Ok"}, true);
}
else
{
auto folder = list.folders[index - list.root.titles.size()];
qapp->CreateShowDialog("Folder", folder.name, {"Ok"}, true);
u32 realidx = index - 1;
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;
}
/*
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)
{

View file

@ -6,7 +6,7 @@ namespace ui
{
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->menuLayout = MenuLayout::New(app_buf);

View 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)
};
};
}

View file

@ -13,7 +13,7 @@
extern "C"
{
u32 __nx_applet_type = AppletType_SystemApplet;
size_t __nx_heap_size = 0x1000000;
size_t __nx_heap_size = 0x3000000;//0x1000000;
}
void CommonSleepHandle()
@ -187,6 +187,23 @@ void HandleQMenuMessage()
}
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:
break;
}
@ -201,6 +218,53 @@ namespace qdaemon
app_buf = new u8[1280 * 720 * 4]();
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
}

View 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
View 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

@ -0,0 +1 @@
Subproject commit 8bae7b4a78f8fe5061596f354e38ecd0238cdaed