mirror of
https://github.com/DarkMatterCore/nxdumptool.git
synced 2024-11-22 10:16:39 +00:00
Implemented AsyncTask class.
Other changes include: * Updated borealis. * Updated Makefile. * Began implementation of a (very) simple, CURL-based HTTP handler. * OptionsTab: added a small disclaimer about dump options. * OptionsTab: added notifications for the update application item (running as NSO, app already updated). * config: improved boundary handling while validating integer entries. * utils: (de)initialize CURL (this will be moved to http.c eventually).
This commit is contained in:
parent
ff0a2a385c
commit
05dec93795
14 changed files with 474 additions and 22 deletions
3
Makefile
3
Makefile
|
@ -82,9 +82,6 @@ CFLAGS += -DVERSION_MAJOR=${VERSION_MAJOR} -DVERSION_MINOR=${VERSION_MINOR} -DV
|
|||
CFLAGS += -DAPP_TITLE=\"${APP_TITLE}\" -DAPP_AUTHOR=\"${APP_AUTHOR}\" -DAPP_VERSION=\"${APP_VERSION}\"
|
||||
CFLAGS += -DGIT_BRANCH=\"${GIT_BRANCH}\" -DGIT_COMMIT=\"${GIT_COMMIT}\" -DGIT_REV=\"${GIT_REV}\"
|
||||
CFLAGS += -DBOREALIS_RESOURCES="\"${BOREALIS_RESOURCES}\""
|
||||
CFLAGS += `aarch64-none-elf-pkg-config zlib --cflags`
|
||||
CFLAGS += `aarch64-none-elf-pkg-config libxml-2.0 --cflags`
|
||||
CFLAGS += `aarch64-none-elf-pkg-config json-c --cflags`
|
||||
|
||||
CXXFLAGS := $(CFLAGS) -std=c++20 -Wno-volatile -Wno-unused-parameter
|
||||
|
||||
|
|
23
http.c
Normal file
23
http.c
Normal file
|
@ -0,0 +1,23 @@
|
|||
/*
|
||||
* http.c
|
||||
*
|
||||
* Copyright (c) 2020-2021, DarkMatterCore <pabloacurielz@gmail.com>.
|
||||
*
|
||||
* This file is part of nxdumptool (https://github.com/DarkMatterCore/nxdumptool).
|
||||
*
|
||||
* nxdumptool is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* nxdumptool is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "nxdt_utils.h"
|
||||
#include "http.h"
|
307
include/async_task.hpp
Normal file
307
include/async_task.hpp
Normal file
|
@ -0,0 +1,307 @@
|
|||
/*
|
||||
* async_task.hpp
|
||||
*
|
||||
* Copyright (c) 2020-2021, DarkMatterCore <pabloacurielz@gmail.com>.
|
||||
*
|
||||
* Based on attcs' C++ implementation at:
|
||||
* https://github.com/attcs/AsyncTask/blob/master/asynctask.h.
|
||||
*
|
||||
* This file is part of nxdumptool (https://github.com/DarkMatterCore/nxdumptool).
|
||||
*
|
||||
* nxdumptool is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* nxdumptool is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#ifndef __ASYNC_TASK_HPP__
|
||||
#define __ASYNC_TASK_HPP__
|
||||
|
||||
#include <exception>
|
||||
#include <future>
|
||||
#include <atomic>
|
||||
|
||||
namespace nxdt::utils
|
||||
{
|
||||
/* Used by AsyncTask to throw exceptions whenever required. */
|
||||
class AsyncTaskException : std::exception
|
||||
{
|
||||
public:
|
||||
enum class eEx : int
|
||||
{
|
||||
TaskIsAlreadyRunning, ///< Task is already running.
|
||||
TaskIsAlreadyFinished, ///< Task is already finished.
|
||||
TaskIsPending, ///< Task hasn't been executed.
|
||||
TaskIsCancelled, ///< Task has been cancelled.
|
||||
TaskWaitTimeout ///< Timed out while waiting for the task to finish.
|
||||
};
|
||||
|
||||
eEx e;
|
||||
|
||||
AsyncTaskException() = default;
|
||||
AsyncTaskException(eEx e) : e(e) { }
|
||||
};
|
||||
|
||||
/* Used by AsyncTask to indicate the current status of the asynchronous task. */
|
||||
enum class AsyncTaskStatus : int
|
||||
{
|
||||
PENDING, ///< The task hasn't been executed yet.
|
||||
RUNNING, ///< The task is currently running.
|
||||
FINISHED ///< The task is finished.
|
||||
};
|
||||
|
||||
/* Asynchronous task handler class. */
|
||||
template<typename Progress, typename Result, typename... Params>
|
||||
class AsyncTask
|
||||
{
|
||||
private:
|
||||
AsyncTaskStatus m_status = AsyncTaskStatus::PENDING;
|
||||
Result m_result{};
|
||||
std::future<Result> m_future{};
|
||||
std::atomic<Progress> m_progress{};
|
||||
std::atomic_bool m_cancelled = false;
|
||||
std::atomic_bool m_rethrowException = false;
|
||||
std::exception_ptr m_exceptionPtr{};
|
||||
|
||||
/* Runs on the calling thread after doInBackground() finishes execution. */
|
||||
void finish(Result&& result)
|
||||
{
|
||||
/* Copy result. */
|
||||
this->m_result = result;
|
||||
|
||||
/* Call appropiate post-execution function. */
|
||||
if (this->isCancelled())
|
||||
{
|
||||
this->onCancelled(this->m_result);
|
||||
} else {
|
||||
this->onPostExecute(this->m_result);
|
||||
}
|
||||
|
||||
/* Update status. */
|
||||
this->m_status = AsyncTaskStatus::FINISHED;
|
||||
|
||||
/* Rethrow asynchronous task exception (if available). */
|
||||
if (this->m_rethrowException.load() && this->m_exceptionPtr) std::rethrow_exception(this->m_exceptionPtr);
|
||||
}
|
||||
|
||||
protected:
|
||||
/* Set class as non-copyable. */
|
||||
NON_COPYABLE(AsyncTask);
|
||||
|
||||
virtual ~AsyncTask(void) noexcept
|
||||
{
|
||||
/* Return right away if the task isn't running. */
|
||||
if (this->getStatus() != AsyncTaskStatus::RUNNING) return;
|
||||
|
||||
/* Cancel task. This won't do anything if it has already been cancelled. */
|
||||
this->cancel();
|
||||
|
||||
/* Return right away if the result was already retrieved. */
|
||||
if (!this->m_future.valid()) return;
|
||||
|
||||
/* Wait until a result is provided by the task thread. */
|
||||
/* Avoid rethrowing any exceptions here - program execution could end if another exception has already been rethrown. */
|
||||
m_future.wait();
|
||||
}
|
||||
|
||||
/* Asynchronous task function. */
|
||||
/* This function should periodically call isCancelled() to determine if it should end prematurely. */
|
||||
virtual Result doInBackground(const Params&... params) = 0;
|
||||
|
||||
/* Posts asynchronous task result. Runs on the asynchronous task thread. */
|
||||
virtual Result postResult(Result&& result)
|
||||
{
|
||||
return result;
|
||||
}
|
||||
|
||||
/* Cleanup function called if the task is cancelled. Runs on the calling thread. */
|
||||
virtual void onCancelled(const Result& result) { }
|
||||
|
||||
/* Post-execution function called right after the task finishes. Runs on the calling thread. */
|
||||
virtual void onPostExecute(const Result& result) { }
|
||||
|
||||
/* Pre-execution function called right before the task starts. Runs on the calling thread. */
|
||||
virtual void onPreExecute(void) { }
|
||||
|
||||
/* Progress update function. Runs on the calling thread. */
|
||||
virtual void onProgressUpdate(const Progress& progress) { }
|
||||
|
||||
/* Stores the current progress inside the class. Runs on the asynchronous task thread. */
|
||||
virtual void publishProgress(const Progress& progress)
|
||||
{
|
||||
/* Don't proceed if the task isn't running. */
|
||||
if (this->getStatus() != AsyncTaskStatus::RUNNING || this->isCancelled()) return;
|
||||
|
||||
/* Update progress. */
|
||||
this->m_progress.store(progress);
|
||||
}
|
||||
|
||||
public:
|
||||
AsyncTask(void) = default;
|
||||
|
||||
/* Cancels the task. Runs on the calling thread. */
|
||||
void cancel(void) noexcept
|
||||
{
|
||||
/* Return right away if the task has already completed, or if it has already been cancelled. */
|
||||
if (this->getStatus() == AsyncTaskStatus::FINISHED || this->isCancelled()) return;
|
||||
|
||||
/* Update cancel flag. */
|
||||
this->m_cancelled.store(true);
|
||||
}
|
||||
|
||||
/* Starts the asynchronous task. Runs on the calling thread. */
|
||||
AsyncTask<Progress, Result, Params...>& execute(const Params&... params)
|
||||
{
|
||||
/* Return right away if the task was cancelled before starting. */
|
||||
if (this->isCancelled()) return *this;
|
||||
|
||||
/* Verify task status. */
|
||||
switch(this->getStatus())
|
||||
{
|
||||
case AsyncTaskStatus::RUNNING:
|
||||
throw AsyncTaskException(AsyncTaskException::eEx::TaskIsAlreadyRunning);
|
||||
case AsyncTaskStatus::FINISHED:
|
||||
throw AsyncTaskException(AsyncTaskException::eEx::TaskIsAlreadyFinished);
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
/* Update task status. */
|
||||
this->m_status = AsyncTaskStatus::RUNNING;
|
||||
|
||||
/* Run onPreExecute() callback. */
|
||||
this->onPreExecute();
|
||||
|
||||
/* Start asynchronous task on a new thread. */
|
||||
this->m_future = std::async(std::launch::async, [this](const Params&... params) -> Result {
|
||||
/* Catch any exceptions thrown by the asynchronous task. */
|
||||
try {
|
||||
return this->postResult(this->doInBackground(params...));
|
||||
} catch(...) {
|
||||
this->cancel();
|
||||
this->m_rethrowException.store(true);
|
||||
this->m_exceptionPtr = std::current_exception();
|
||||
}
|
||||
|
||||
return {};
|
||||
}, params...);
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
/* Waits for the asynchronous task to complete, then returns its result. Runs on the calling thread. */
|
||||
/* If an exception is thrown by the asynchronous task, it will be rethrown by this function. */
|
||||
Result get(void)
|
||||
{
|
||||
auto status = this->getStatus();
|
||||
|
||||
/* Throw an exception if the asynchronous task hasn't been executed. */
|
||||
if (status == AsyncTaskStatus::PENDING) throw AsyncTaskException(AsyncTaskException::eEx::TaskIsPending);
|
||||
|
||||
/* If the task is still running, wait until it finishes. */
|
||||
/* get() calls wait() on its own if the result hasn't been retrieved. */
|
||||
/* finish() takes care of rethrowing any exceptions thrown by the asynchronous task. */
|
||||
if (status == AsyncTaskStatus::RUNNING) this->finish(this->m_future.get());
|
||||
|
||||
/* Throw an exception if the asynchronous task was cancelled. */
|
||||
if (this->isCancelled()) throw AsyncTaskException(AsyncTaskException::eEx::TaskIsCancelled);
|
||||
|
||||
/* Return result. */
|
||||
return this->m_result;
|
||||
}
|
||||
|
||||
/* Waits for at most the given time for the asynchronous task to complete, then returns its result. Runs on the calling thread. */
|
||||
/* If an exception is thrown by the asynchronous task, it will be rethrown by this function. */
|
||||
template<typename Rep, typename Period>
|
||||
Result get(const std::chrono::duration<Rep, Period>& timeout)
|
||||
{
|
||||
auto status = this->getStatus();
|
||||
|
||||
/* Throw an exception if the asynchronous task hasn't been executed. */
|
||||
if (status == AsyncTaskStatus::PENDING) throw AsyncTaskException(AsyncTaskException::eEx::TaskIsPending);
|
||||
|
||||
/* Check if the task is still running. */
|
||||
if (status == AsyncTaskStatus::RUNNING)
|
||||
{
|
||||
/* Wait for at most the given time for the asynchronous task to complete. */
|
||||
auto thread_status = this->m_future.wait_for(timeout);
|
||||
switch(thread_status)
|
||||
{
|
||||
case std::future_status::timeout:
|
||||
/* Throw an exception if we timed out while waiting for the task to finish. */
|
||||
throw AsyncTaskException(AsyncTaskException::eEx::TaskWaitTimeout);
|
||||
case std::future_status::ready:
|
||||
/* Retrieve the task result. */
|
||||
/* finish() takes care of rethrowing any exceptions thrown by the asynchronous task. */
|
||||
this->finish(this->m_future.get());
|
||||
|
||||
/* Throw an exception if the asynchronous task was cancelled. */
|
||||
if (this->isCancelled()) throw AsyncTaskException(AsyncTaskException::eEx::TaskIsCancelled);
|
||||
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* Return result. */
|
||||
return this->m_result;
|
||||
}
|
||||
|
||||
/* Returns the current task status. Runs on both threads. */
|
||||
AsyncTaskStatus getStatus(void) noexcept
|
||||
{
|
||||
return this->m_status;
|
||||
}
|
||||
|
||||
/* Returns true if the task was cancelled before it completed normally. Runs on both threads. */
|
||||
/* Can be used by the asynchronous task to return prematurely. */
|
||||
bool isCancelled(void) noexcept
|
||||
{
|
||||
return this->m_cancelled.load();
|
||||
}
|
||||
|
||||
/* Used by the calling thread to refresh the task progress, preferrably inside a loop. Returns true if the task finished. */
|
||||
/* If an exception is thrown by the asynchronous task, it will be rethrown by this function. */
|
||||
bool loopCallback(void)
|
||||
{
|
||||
auto status = this->getStatus();
|
||||
|
||||
/* Return immediately if the task already finished. */
|
||||
if (status == AsyncTaskStatus::FINISHED) return true;
|
||||
|
||||
/* Return immediately if the task hasn't started, or if its result was already retrieved. */
|
||||
if (status == AsyncTaskStatus::PENDING || !this->m_future.valid()) return false;
|
||||
|
||||
/* Get task thread status without waiting. */
|
||||
auto thread_status = this->m_future.wait_for(std::chrono::seconds(0));
|
||||
switch(thread_status)
|
||||
{
|
||||
case std::future_status::timeout:
|
||||
/* Update progress. */
|
||||
this->onProgressUpdate(this->m_progress.load());
|
||||
return false;
|
||||
case std::future_status::ready:
|
||||
/* Finish task. */
|
||||
this->finish(this->m_future.get());
|
||||
return true;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#endif /* __ASYNC_TASK_HPP__ */
|
|
@ -24,6 +24,8 @@
|
|||
#ifndef __CONFIG_H__
|
||||
#define __CONFIG_H__
|
||||
|
||||
#include <json-c/json.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
|
39
include/core/http.h
Normal file
39
include/core/http.h
Normal file
|
@ -0,0 +1,39 @@
|
|||
/*
|
||||
* http.h
|
||||
*
|
||||
* Copyright (c) 2020-2021, DarkMatterCore <pabloacurielz@gmail.com>.
|
||||
*
|
||||
* This file is part of nxdumptool (https://github.com/DarkMatterCore/nxdumptool).
|
||||
*
|
||||
* nxdumptool is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* nxdumptool is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#ifndef __HTTP_H__
|
||||
#define __HTTP_H__
|
||||
|
||||
#include <curl/curl.h>
|
||||
/*
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
*/
|
||||
#endif /* __HTTP_H__ */
|
|
@ -59,6 +59,9 @@
|
|||
/* Configuration handler. */
|
||||
#include "config.h"
|
||||
|
||||
/* HTTP requests handler. */
|
||||
#include "http.h"
|
||||
|
||||
/* USB Mass Storage support. */
|
||||
#include "ums.h"
|
||||
|
||||
|
|
|
@ -30,8 +30,15 @@ namespace nxdt::views
|
|||
{
|
||||
class OptionsTab: public brls::List
|
||||
{
|
||||
private:
|
||||
bool display_notification = true;
|
||||
brls::menu_timer_t notification_timer = 0.0f;
|
||||
brls::menu_timer_ctx_entry_t notification_timer_ctx = {0};
|
||||
|
||||
void DisplayNotification(std::string str);
|
||||
public:
|
||||
OptionsTab(void);
|
||||
~OptionsTab(void);
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -1 +1 @@
|
|||
Subproject commit bfc32dba9394409691a940896d2a10dfc65c10df
|
||||
Subproject commit 097693eb5264941f8697902ea88a89e2efd25c11
|
|
@ -1,4 +1,6 @@
|
|||
{
|
||||
"dump_options_info": "Dump options are displayed in their respective menus for convenience.",
|
||||
|
||||
"overclock": {
|
||||
"label": "Overclock",
|
||||
"description": "Overclocks both CPU and MEM to 1785 MHz and 1600 MHz, respectively, in order to speed up dump operations. This is considered a relatively safe action.\n\nIf the application is running under title override mode, and sys-clk is active, and a clock profile has been created for the overriden title, this setting has no effect at all.",
|
||||
|
@ -16,5 +18,10 @@
|
|||
"update_app": {
|
||||
"label": "Update application",
|
||||
"description": "Checks if an update is available in nxdumptool's GitHub repository. Requires Internet connectivity."
|
||||
},
|
||||
|
||||
"notifications": {
|
||||
"is_nso": "The application is running as a NSO. Unable to update.",
|
||||
"already_updated": "The application has already been updated. Please reload."
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
{
|
||||
"notifications": {
|
||||
"gamecard_status_updated": "Gamecard status updated",
|
||||
"gamecard_ejected": "Gamecard ejected",
|
||||
"user_titles": "User titles updated",
|
||||
"ums_device": "USB Mass Storage devices updated",
|
||||
"usb_host_connected": "USB host connected",
|
||||
"usb_host_disconnected": "USB host disconnected"
|
||||
"gamecard_status_updated": "Gamecard status updated.",
|
||||
"gamecard_ejected": "Gamecard ejected.",
|
||||
"user_titles": "User titles updated.",
|
||||
"ums_device": "USB Mass Storage devices updated.",
|
||||
"usb_host_connected": "USB host connected.",
|
||||
"usb_host_disconnected": "USB host disconnected."
|
||||
}
|
||||
}
|
||||
|
|
|
@ -23,8 +23,6 @@
|
|||
#include "config.h"
|
||||
#include "title.h"
|
||||
|
||||
#include <json-c/json.h>
|
||||
|
||||
#define JSON_VALIDATE_FIELD(type, name, ...) \
|
||||
if (!strcmp(key, #name)) { \
|
||||
if (name##_found || !configValidateJson##type(val, ##__VA_ARGS__)) goto end; \
|
||||
|
@ -241,8 +239,8 @@ static bool configValidateJsonRootObject(const struct json_object *obj)
|
|||
json_object_object_foreach(obj, key, val)
|
||||
{
|
||||
JSON_VALIDATE_FIELD(Boolean, overclock);
|
||||
JSON_VALIDATE_FIELD(Integer, naming_convention, TitleNamingConvention_Full, TitleNamingConvention_IdAndVersionOnly);
|
||||
JSON_VALIDATE_FIELD(Integer, dump_destination, ConfigDumpDestination_SdCard, ConfigDumpDestination_UsbHost);
|
||||
JSON_VALIDATE_FIELD(Integer, naming_convention, TitleNamingConvention_Full, TitleNamingConvention_Count - 1);
|
||||
JSON_VALIDATE_FIELD(Integer, dump_destination, ConfigDumpDestination_SdCard, ConfigDumpDestination_Count - 1);
|
||||
JSON_VALIDATE_OBJECT(GameCard, gamecard);
|
||||
JSON_VALIDATE_OBJECT(Nsp, nsp);
|
||||
JSON_VALIDATE_OBJECT(Ticket, ticket);
|
||||
|
@ -268,7 +266,7 @@ static bool configValidateJsonGameCardObject(const struct json_object *obj)
|
|||
JSON_VALIDATE_FIELD(Boolean, keep_certificate);
|
||||
JSON_VALIDATE_FIELD(Boolean, trim_dump);
|
||||
JSON_VALIDATE_FIELD(Boolean, calculate_checksum);
|
||||
JSON_VALIDATE_FIELD(Integer, checksum_lookup_method, ConfigChecksumLookupMethod_None, ConfigChecksumLookupMethod_NoIntro);
|
||||
JSON_VALIDATE_FIELD(Integer, checksum_lookup_method, ConfigChecksumLookupMethod_None, ConfigChecksumLookupMethod_Count - 1);
|
||||
goto end;
|
||||
}
|
||||
|
||||
|
|
|
@ -210,9 +210,19 @@ bool utilsInitializeResources(const int program_argc, const char **program_argv)
|
|||
/* TODO: only use this function while dealing with a dump process - make sure to handle power button presses as well. */
|
||||
appletSetMediaPlaybackState(true);
|
||||
|
||||
/* Redirect stdout and stderr over network to nxlink. */
|
||||
/* Initialize socket driver. */
|
||||
rc = socketInitializeDefault();
|
||||
if (R_SUCCEEDED(rc)) g_nxLinkSocketFd = nxlinkConnectToHost(true, true);
|
||||
if (R_FAILED(rc))
|
||||
{
|
||||
LOG_MSG("socketInitializeDefault failed! (0x%08X).", rc);
|
||||
break;
|
||||
}
|
||||
|
||||
/* Initialize CURL. */
|
||||
curl_global_init(CURL_GLOBAL_ALL);
|
||||
|
||||
/* Redirect stdout and stderr over network to nxlink. */
|
||||
g_nxLinkSocketFd = nxlinkConnectToHost(true, true);
|
||||
|
||||
/* Update flags. */
|
||||
ret = g_resourcesInit = true;
|
||||
|
@ -227,6 +237,9 @@ void utilsCloseResources(void)
|
|||
{
|
||||
SCOPED_LOCK(&g_resourcesMutex)
|
||||
{
|
||||
/* Cleanup CURL. */
|
||||
curl_global_cleanup();
|
||||
|
||||
/* Close nxlink socket. */
|
||||
if (g_nxLinkSocketFd >= 0)
|
||||
{
|
||||
|
@ -234,6 +247,7 @@ void utilsCloseResources(void)
|
|||
g_nxLinkSocketFd = -1;
|
||||
}
|
||||
|
||||
/* Deinitialize socket driver. */
|
||||
socketExit();
|
||||
|
||||
/* Enable screen dimming and auto sleep. */
|
||||
|
|
|
@ -33,15 +33,28 @@ namespace nxdt::views
|
|||
this->setSpacing(this->getSpacing() / 2);
|
||||
this->setMarginBottom(20);
|
||||
|
||||
/* Information about actual dump options. */
|
||||
brls::Label *dump_options_info = new brls::Label(brls::LabelStyle::DESCRIPTION, "options_tab/dump_options_info"_i18n, true);
|
||||
dump_options_info->setHorizontalAlign(NVG_ALIGN_CENTER);
|
||||
this->addView(dump_options_info);
|
||||
|
||||
/* Overclock. */
|
||||
brls::ToggleListItem *overclock = new brls::ToggleListItem("options_tab/overclock/label"_i18n, configGetBoolean("overclock"), \
|
||||
"options_tab/overclock/description"_i18n, "options_tab/overclock/value_enabled"_i18n, \
|
||||
"options_tab/overclock/value_disabled"_i18n);
|
||||
overclock->getClickEvent()->subscribe([](brls::View* view) {
|
||||
brls::ToggleListItem *item = static_cast<brls::ToggleListItem*>(view);
|
||||
|
||||
/* Get current value. */
|
||||
bool value = item->getToggleState();
|
||||
|
||||
/* Change hardware clocks based on the current value. */
|
||||
utilsOverclockSystem(value);
|
||||
|
||||
/* Update configuration. */
|
||||
configSetBoolean("overclock", value);
|
||||
|
||||
brls::Logger::debug("Overclock setting changed by user.");
|
||||
});
|
||||
this->addView(overclock);
|
||||
|
||||
|
@ -52,16 +65,57 @@ namespace nxdt::views
|
|||
}, static_cast<unsigned>(configGetInteger("naming_convention")),
|
||||
"options_tab/naming_convention/description"_i18n);
|
||||
naming_convention->getValueSelectedEvent()->subscribe([](int selected){
|
||||
/* Make sure the current value isn't out of bounds. */
|
||||
if (selected < 0 || selected > static_cast<int>(TitleNamingConvention_Count)) return;
|
||||
|
||||
/* Update configuration. */
|
||||
configSetInteger("naming_convention", selected);
|
||||
|
||||
brls::Logger::debug("Naming convention setting changed by user.");
|
||||
});
|
||||
this->addView(naming_convention);
|
||||
|
||||
/* Update application. */
|
||||
if (!envIsNso())
|
||||
{
|
||||
brls::ListItem *update_app = new brls::ListItem("options_tab/update_app/label"_i18n, "options_tab/update_app/description"_i18n);
|
||||
this->addView(update_app);
|
||||
}
|
||||
brls::ListItem *update_app = new brls::ListItem("options_tab/update_app/label"_i18n, "options_tab/update_app/description"_i18n);
|
||||
update_app->getClickEvent()->subscribe([this](brls::View* view) {
|
||||
if (envIsNso())
|
||||
{
|
||||
/* Display a notification if we're running as a NSO. */
|
||||
this->DisplayNotification("options_tab/notifications/is_nso"_i18n);
|
||||
return;
|
||||
} else
|
||||
if (false)
|
||||
{
|
||||
/* Display a notification if the application has already been updated. */
|
||||
this->DisplayNotification("options_tab/notifications/already_updated"_i18n);
|
||||
return;
|
||||
}
|
||||
|
||||
/*brls::StagedAppletFrame *staged_frame = new brls::StagedAppletFrame();
|
||||
staged_frame->setTitle("options_tab/update_app/label"_i18n);
|
||||
|
||||
brls::Application::pushView(staged_frame);*/
|
||||
});
|
||||
this->addView(update_app);
|
||||
}
|
||||
|
||||
OptionsTab::~OptionsTab(void)
|
||||
{
|
||||
brls::menu_timer_kill(&(this->notification_timer));
|
||||
}
|
||||
|
||||
void OptionsTab::DisplayNotification(std::string str)
|
||||
{
|
||||
if (str == "" || !this->display_notification) return;
|
||||
|
||||
brls::Application::notify(str);
|
||||
this->display_notification = false;
|
||||
|
||||
this->notification_timer_ctx.duration = brls::Application::getStyle()->AnimationDuration.notificationTimeout;
|
||||
this->notification_timer_ctx.cb = [this](void *userdata) { this->display_notification = true; };
|
||||
this->notification_timer_ctx.tick = [](void*){};
|
||||
this->notification_timer_ctx.userdata = nullptr;
|
||||
|
||||
brls::menu_timer_start(&(this->notification_timer), &(this->notification_timer_ctx));
|
||||
}
|
||||
}
|
||||
|
|
1
todo.txt
1
todo.txt
|
@ -23,6 +23,7 @@ todo:
|
|||
usb: improve abi (make it rest-like?)
|
||||
usb: improve cancel mechanism
|
||||
|
||||
others: move curl (de)initialization to http.c
|
||||
others: use hardcoded directories, move data to hardcoded directory if the launch path isn't the right one
|
||||
others: dump verification via nswdb / no-intro
|
||||
others: update application feature
|
||||
|
|
Loading…
Reference in a new issue