Skip to content

Commit

Permalink
Raw fs (#2)
Browse files Browse the repository at this point in the history
* -allow removing of flags
-use raw fs (implement directory iterator)
-restructure stuff

* add version string as define

* UI improvements

Co-authored-by: WerWolv <werwolv98@gmail.com>
  • Loading branch information
HookedBehemoth and WerWolv committed Mar 30, 2020
1 parent 193fc25 commit a925cde
Show file tree
Hide file tree
Showing 8 changed files with 286 additions and 122 deletions.
6 changes: 4 additions & 2 deletions .vscode/c_cpp_properties.json
Expand Up @@ -14,7 +14,8 @@
"defines": [
"SWITCH",
"__SWITCH__",
"__aarch64__"
"__aarch64__",
"VERSION=\"\""
],
"compilerPath": "C:/devkitPro/devkitA64/bin/aarch64-none-elf-g++",
"cStandard": "c11",
Expand All @@ -36,7 +37,8 @@
"defines": [
"SWITCH",
"__SWITCH__",
"__aarch64__"
"__aarch64__",
"VERSION=\"\""
],
"compilerPath": "/opt/devkitpro/devkitA64/bin/aarch64-none-elf-g++",
"cStandard": "c11",
Expand Down
9 changes: 7 additions & 2 deletions Makefile
Expand Up @@ -38,13 +38,18 @@ include $(DEVKITPRO)/libnx/switch_rules
# NACP building is skipped as well.
#---------------------------------------------------------------------------------
APP_TITLE := Sysmodules
APP_VERSION := 1.2.0

TARGET := $(notdir $(CURDIR))
TARGET := ovlSysmodules
BUILD := build
SOURCES := source
DATA := data
INCLUDES := include libs/libtesla/include

ifeq ($(RELEASE),)
APP_VERSION := $(APP_VERSION)-$(shell git describe --dirty --always)
endif

NO_ICON := 1

#---------------------------------------------------------------------------------
Expand All @@ -55,7 +60,7 @@ ARCH := -march=armv8-a+crc+crypto -mtune=cortex-a57 -mtp=soft -fPIE
CFLAGS := -g -Wall -O2 -ffunction-sections \
$(ARCH) $(DEFINES)

CFLAGS += $(INCLUDE) -D__SWITCH__
CFLAGS += $(INCLUDE) -D__SWITCH__ -DVERSION=\"v$(APP_VERSION)\"

CXXFLAGS := $(CFLAGS) -fno-exceptions -std=c++17

Expand Down
26 changes: 26 additions & 0 deletions include/dir_iterator.hpp
@@ -0,0 +1,26 @@
#pragma once

#include <switch.h>

class FsDirIterator {
private:
FsDir m_dir;
FsDirectoryEntry entry;
s64 count;

public:
FsDirIterator() = default;
FsDirIterator(FsDir dir);

~FsDirIterator() = default;

const FsDirectoryEntry &operator*() const;
const FsDirectoryEntry *operator->() const;
FsDirIterator &operator++();

bool operator!=(const FsDirIterator &rhs);
};

inline FsDirIterator begin(FsDirIterator iter) noexcept { return iter; }

inline FsDirIterator end(FsDirIterator) noexcept { return FsDirIterator(); }
29 changes: 29 additions & 0 deletions include/gui_main.hpp
@@ -0,0 +1,29 @@
#pragma once

#include <list>
#include <tesla.hpp>

struct SystemModule {
tsl::elm::ListItem *listItem;
u64 programId;
bool needReboot;
};

class GuiMain : public tsl::Gui {
private:
FsFileSystem m_fs;
std::list<SystemModule> m_sysmoduleListItems;
bool m_scanned;

public:
GuiMain();
~GuiMain();

virtual tsl::elm::Element *createUI();
virtual void update() override;

private:
void updateStatus(const SystemModule &module);
bool hasFlag(const SystemModule &module);
bool isRunning(const SystemModule &module);
};
24 changes: 24 additions & 0 deletions source/dir_iterator.cpp
@@ -0,0 +1,24 @@
#include "dir_iterator.hpp"

FsDirIterator::FsDirIterator(FsDir dir) : m_dir(dir) {
if (R_FAILED(fsDirRead(&this->m_dir, &this->count, 1, &this->entry)))
this->count = 0;
}

const FsDirectoryEntry &FsDirIterator::operator*() const {
return this->entry;
}

const FsDirectoryEntry *FsDirIterator::operator->() const {
return &**this;
}

FsDirIterator &FsDirIterator::operator++() {
if (R_FAILED(fsDirRead(&this->m_dir, &this->count, 1, &this->entry)))
this->count = 0;
return *this;
}

bool FsDirIterator::operator!=(const FsDirIterator &__rhs) {
return this->count > 0;
}
188 changes: 188 additions & 0 deletions source/gui_main.cpp
@@ -0,0 +1,188 @@
#include "gui_main.hpp"

#include "dir_iterator.hpp"

#include <json.hpp>
using json = nlohmann::json;

constexpr const char *const amsContentsPath = "/atmosphere/contents";
constexpr const char *const boot2FlagFormat = "/atmosphere/contents/%016lX/flags/boot2.flag";

static char pathBuffer[FS_MAX_PATH];

constexpr const char *const descriptions[2][2] = {
[0] = {
[0] = "Off | \uE098",
[1] = "Off | \uE0F4",
},
[1] = {
[0] = "On | \uE098",
[1] = "On | \uE0F4",
},
};

GuiMain::GuiMain() {
Result rc = fsOpenSdCardFileSystem(&this->m_fs);
if (R_FAILED(rc))
return;

FsDir contentDir;
std::strcpy(pathBuffer, amsContentsPath);
rc = fsFsOpenDirectory(&this->m_fs, pathBuffer, FsDirOpenMode_ReadDirs, &contentDir);
if (R_FAILED(rc))
return;
tsl::hlp::ScopeGuard dirGuard([&] { fsDirClose(&contentDir); });

/* Iterate over contents folder. */
for (const auto &entry : FsDirIterator(contentDir)) {
FsFile toolboxFile;
std::snprintf(pathBuffer, FS_MAX_PATH, "/atmosphere/contents/%s/toolbox.json", entry.name);
rc = fsFsOpenFile(&this->m_fs, pathBuffer, FsOpenMode_Read, &toolboxFile);
if (R_FAILED(rc))
continue;
tsl::hlp::ScopeGuard fileGuard([&] { fsFileClose(&toolboxFile); });

/* Get toolbox file size. */
s64 size;
rc = fsFileGetSize(&toolboxFile, &size);
if (R_FAILED(rc))
continue;

/* Read toolbox file. */
std::string toolBoxData(size, '\0');
u64 bytesRead;
rc = fsFileRead(&toolboxFile, 0, toolBoxData.data(), size, FsReadOption_None, &bytesRead);
if (R_FAILED(rc))
continue;

/* Parse toolbox file data. */
json toolboxFileContent = json::parse(toolBoxData);

const std::string &sysmoduleProgramIdString = toolboxFileContent["tid"];
u64 sysmoduleProgramId = std::strtoul(sysmoduleProgramIdString.c_str(), nullptr, 16);

/* Let's not allow Tesla to be killed with this. */
if (sysmoduleProgramId == 0x010000000007E51AULL)
continue;

SystemModule module = {
.listItem = new tsl::elm::ListItem(toolboxFileContent["name"]),
.programId = sysmoduleProgramId,
.needReboot = toolboxFileContent["requires_reboot"],
};

module.listItem->setClickListener([this, module](u64 click) -> bool {
if (click & KEY_A && !module.needReboot) {
if (this->isRunning(module)) {
/* Kill process. */
pmshellTerminateProgram(module.programId);
} else {
/* Start process. */
const NcmProgramLocation programLocation{
.program_id = module.programId,
.storageID = NcmStorageId_None,
};
u64 pid = 0;
pmshellLaunchProgram(0, &programLocation, &pid);
}
return true;
}

if (click & KEY_Y) {
std::snprintf(pathBuffer, FS_MAX_PATH, boot2FlagFormat, module.programId);
if (this->hasFlag(module)) {
/* Remove boot2 flag file. */
fsFsDeleteFile(&this->m_fs, pathBuffer);
} else {
/* Create boot2 flag file. */
fsFsCreateFile(&this->m_fs, pathBuffer, 0, FsCreateOption(0));
}
return true;
}

return false;
});
this->m_sysmoduleListItems.push_back(std::move(module));
}
this->m_scanned = true;
}

GuiMain::~GuiMain() {
fsFsClose(&this->m_fs);
}

tsl::elm::Element *GuiMain::createUI() {
tsl::elm::OverlayFrame *rootFrame = new tsl::elm::OverlayFrame("Sysmodules", VERSION);

if (this->m_sysmoduleListItems.size() == 0) {
const char *description = this->m_scanned ? "No sysmodules found!" : "Scan failed!";

auto *warning = new tsl::elm::CustomDrawer([description](tsl::gfx::Renderer *renderer, s32 x, s32 y, s32 w, s32 h) {
renderer->drawString("\uE150", false, 180, 250, 90, renderer->a(0xFFFF));
renderer->drawString(description, false, 110, 340, 25, renderer->a(0xFFFF));
});

rootFrame->setContent(warning);
} else {
tsl::elm::List *sysmoduleList = new tsl::elm::List();
sysmoduleList->addItem(new tsl::elm::CategoryHeader("Dynamic | \uE0E0 Toggle | \uE0E3 Toggle auto start", true));
sysmoduleList->addItem(new tsl::elm::CustomDrawer([](tsl::gfx::Renderer *renderer, s32 x, s32 y, s32 w, s32 h) {
renderer->drawString("\uE016 These sysmodules can be toggled at any time.", false, x + 5, y + 20, 15, renderer->a(tsl::style::color::ColorDescription));
}), 30);
for (const auto &module : this->m_sysmoduleListItems) {
if (!module.needReboot)
sysmoduleList->addItem(module.listItem);
}

sysmoduleList->addItem(new tsl::elm::CategoryHeader("Static | \uE0E3 Toggle auto start", true));
sysmoduleList->addItem(new tsl::elm::CustomDrawer([](tsl::gfx::Renderer *renderer, s32 x, s32 y, s32 w, s32 h) {
renderer->drawString("\uE016 These sysmodules need a reboot to work.", false, x + 5, y + 20, 15, renderer->a(tsl::style::color::ColorDescription));
}), 30);
for (const auto &module : this->m_sysmoduleListItems) {
if (module.needReboot)
sysmoduleList->addItem(module.listItem);
}
rootFrame->setContent(sysmoduleList);
}

return rootFrame;
}

void GuiMain::update() {
static u32 counter = 0;

if (counter++ % 20 != 0)
return;

for (const auto &module : this->m_sysmoduleListItems) {
this->updateStatus(module);
}
}

void GuiMain::updateStatus(const SystemModule &module) {
bool running = this->isRunning(module);
bool hasFlag = this->hasFlag(module);

const char *desc = descriptions[running][hasFlag];
module.listItem->setValue(desc);
}

bool GuiMain::hasFlag(const SystemModule &module) {
FsFile flagFile;
std::snprintf(pathBuffer, FS_MAX_PATH, boot2FlagFormat, module.programId);
Result rc = fsFsOpenFile(&this->m_fs, pathBuffer, FsOpenMode_Read, &flagFile);
if (R_SUCCEEDED(rc)) {
fsFileClose(&flagFile);
return true;
} else {
return false;
}
}

bool GuiMain::isRunning(const SystemModule &module) {
u64 pid = 0;
if (R_FAILED(pmdmntGetProcessId(&pid, module.programId)))
return false;

return pid > 0;
}

0 comments on commit a925cde

Please sign in to comment.