5 Commits
0.4.0 ... 0.4.1

Author SHA1 Message Date
ITotalJustice
0570c14343 bump version for release 2024-12-20 11:17:15 +00:00
ITotalJustice
66f2171995 use localtime instead of gmtime
fixes #23
2024-12-18 17:51:08 +00:00
J0hnTR
3178f11596 Update ru.json (#29)
Native RU-speaker here, just verified autotranslation a bit.
2024-12-18 14:36:50 +00:00
ITotalJustice
e2d9db8928 fix appstore sort strings not being rendered 2024-12-18 00:28:59 +00:00
ITotalJustice
945d1f3ae6 add updater, remove white theme (it was unfished), remove more dead code, bump version for release 2024-12-18 00:04:27 +00:00
12 changed files with 254 additions and 165 deletions

View File

@@ -1,89 +1,89 @@
{ {
"Launch": "Запуск", "Launch": "Запуск",
"Options": "Параметры", "Options": "Параметры",
"Homebrew Options": "Варианты домашнего пивоварения", "Homebrew Options": "Параметры Homebrew",
"Sort By": "Сортировать по", "Sort By": "Сортировать по",
"Sort Options": "Параметры сортировки", "Sort Options": "Параметры сортировки",
"Updated": "Обновлено", "Updated": "Обновлено",
"Size": "Размер", "Size": "Размер",
"Alphabetical": "Алфавитный", "Alphabetical": "По наименованию",
"Decending": "по убыванию", "Decending": "По убыванию",
"Ascending": "восходящий", "Ascending": "По возрастанию",
"Sort": "Сортировать", "Sort": "Сортировать",
"Order": "Заказ", "Order": "Порядок",
"Info": "Информация", "Info": "Информация",
"Delete": "Удалить", "Delete": "Удалить",
"Hide Sphaira": "Скрыть Сфаиру", "Hide Sphaira": "Скрыть Sphaira",
"Are you sure you want to delete ": "Вы уверены, что хотите удалить ", "Are you sure you want to delete ": "Вы уверены, что хотите удалить ",
"Install Forwarder": "Установить переадресатор", "Install Forwarder": "Установить форвардер",
"WARNING: Installing forwarders will lead to a ban!": "ВНИМАНИЕ: Установка форвардеров приведет к бану!", "WARNING: Installing forwarders will lead to a ban!": "ВНИМАНИЕ: Установка форвардеров приведет к бану!",
"Back": "Назад", "Back": "Назад",
"Install": "Установить", "Install": "Установить",
"Fs": "Фс", "Fs": "Фс",
"App": "Приложение", "App": "Приложение",
"Menu": "Меню", "Menu": "Меню",
"Homebrew": "Домашнее пиво", "Homebrew": "Homebrew",
"FileBrowser": "ФайлБраузер", "FileBrowser": "Файловый менеджер",
"Open": "Открыть", "Open": "Открыть",
"Theme Options": "Параметры темы", "Theme Options": "Параметры темы",
"Select Theme": "Выберите тему", "Select Theme": "Выберите тему",
"Shuffle": "Перетасовать", "Shuffle": "Перетасовать",
"Music": "Музыка", "Music": "Музыка",
"Show Hidden": "Показать скрытое", "Show Hidden": "Показать скрытые",
"Folders First": "Папки в первую очередь", "Folders First": "Папки в первую очередь",
"Hidden Last": "Скрытый последний", "Hidden Last": "Скрытые в последнюю очередь",
"Yes": "Да", "Yes": "Да",
"No": "Нет", "No": "Нет",
"Network Options": "Параметры сети", "Network Options": "Параметры сети",
"Nxlink": "Нкслинк", "Nxlink": "Nxlink",
"Check for update": "Проверить наличие обновлений", "Check for update": "Проверить наличие обновлений",
"File Options": "Параметры файла", "File Options": "Параметры файла",
"Cut": "Резать", "Cut": "Вырезать",
"Copy": "Копировать", "Copy": "Копировать",
"Rename": "Переименовать", "Rename": "Переименовать",
"Advanced Options": "Создать файл", "Advanced Options": "Расширенные параметры",
"Create File": "Создать файл", "Create File": "Создать файл",
"Create Folder": "Создать папку", "Create Folder": "Создать папку",
"View as text": "Посмотреть как текст", "View as text": "Посмотреть как текст",
"View as text (unfinished)": "Посмотреть как текст (незакончено)", "View as text (unfinished)": "Посмотреть как текст (незакончено)",
"Set Archive Bit": "Установить бит архива", "Set Archive Bit": "Установить Archive Bit",
"AppStore Options": "Параметры магазина приложений", "AppStore Options": "Параметры магазина приложений",
"All": "Все", "All": "Все",
"Games": "Игры", "Games": "Игры",
"Emulators": "Эмуляторы", "Emulators": "Эмуляторы",
"Tools": "Инструменты", "Tools": "Инструменты",
"Advanced": ередовой", "Advanced": родвинутые",
"Themes": "Темы", "Themes": "Темы",
"Legacy": "Наследие", "Legacy": "Легаси",
"Misc": "Разное", "Misc": "Прочее",
"Downloads": "Загрузки", "Downloads": "Загрузки",
"Filter": "Фильтр", "Filter": "Фильтр",
"Search": "Поиск", "Search": "Поиск",
"Menu Options": "Опции меню", "Menu Options": "Параметры меню",
"Header": "Заголовок", "Header": "Заголовок",
"Theme": "Тема", "Theme": "Тема",
"Network": "Сеть", "Network": "Сеть",
"Logging": "Ведение журнала", "Logging": "Журналирование",
"Enabled": "Включено", "Enabled": "Включено",
"Disabled": "Неполноценный", "Disabled": "Отключено",
"Replace hbmenu on exit": "Заменить hbmenu при выходе", "Replace hbmenu on exit": "Заменить hbmenu при выходе",
"Misc Options": "Разные параметры", "Misc Options": "Прочие параметры",
"Themezer": "Темезер", "Themezer": "Themezer",
"Irs": "IRS", "Irs": "Irs",
"Web": "Интернет", "Web": "Интернет",
"Download": "Скачать", "Download": "Скачать",
"Next Page": "Следующая страница", "Next Page": "Следующая страница",
"Prev Page": "Предыдущая страница", "Prev Page": "Предыдущая страница",
"Pad ": "Подушка ", "Pad ": "Pad ",
" (Unconnected)": " (Не подключено)", " (Unconnected)": " (Не подключено)",
"HandHeld": "Ручной", "HandHeld": "Портативный",
" (Available)": " (Доступный)", " (Available)": " (Доступно)",
"0 (Sideways)": "0 (вбок)", "0 (Sideways)": "0 (набок)",
"90 (Flat)": "90 (квартира)", "90 (Flat)": "90 (ровно)",
"180 (-Sideways)": "180 (-вбок)", "180 (-Sideways)": "180 (-вбок)",
"270 (Upside down)": "270 (перевернутый)", "270 (Upside down)": "270 (перевернуто)",
"Grey": "Серый", "Grey": "Серый",
"Ironbow": "Железный лук", "Ironbow": "Стальной",
"Green": "Зеленый", "Green": "Зеленый",
"Red": "Красный", "Red": "Красный",
"Blue": "Синий", "Blue": "Синий",
@@ -92,7 +92,7 @@
"Dim group": "Тусклая группа", "Dim group": "Тусклая группа",
"None": "Никто", "None": "Никто",
"Normal image": "Обычное изображение", "Normal image": "Обычное изображение",
"Negative image": "Негативный имидж", "Negative image": "Негативное изображение",
"320x240": "320x240", "320x240": "320x240",
"160x120": "160x120", "160x120": "160x120",
"80x60": "80х60", "80x60": "80х60",
@@ -101,13 +101,13 @@
"Controller": "Контроллер", "Controller": "Контроллер",
"Rotation": "Вращение", "Rotation": "Вращение",
"Colour": "Цвет", "Colour": "Цвет",
"Light Target": "Легкая мишень", "Light Target": "Световая мишень",
"Gain": "Прирост", "Gain": "Прирост",
"Negative Image": "Негативное изображение", "Negative Image": "Негативное изображение",
"Format": "Формат", "Format": "Формат",
"Trimming Format": "Формат обрезки", "Trimming Format": "Формат обрезки",
"External Light Filter": "Внешний светофильтр", "External Light Filter": "Внешний светофильтр",
"Load Default": "Загрузить по умолчанию", "Load Default": "Загрузить умолчания",
"No Internet": "Нет Интернета", "No Internet": "Нет Интернета",
"[Applet Mode]": "[Режим апплета]", "[Applet Mode]": "[Режим апплета]",
"Language": "Язык" "Language": "Язык"

View File

@@ -1,23 +0,0 @@
[meta]
name="White not finished"
author=TotalJustice
version=1.0.0
preview=romfs:/theme/preview.jpg
[theme]
background=0xEBEBEBff
cursor=romfs:/theme/cursor.png
cursor_drag=romfs:/theme/cursor_drag.png
grid=0x46464630
selected=0x464646ff
selected_overlay=0x00ffc8ff
text=0x2D2D2Dff
text_selected=0x3A50F0ff
icon_audio=romfs:/theme/icon_audio.png
icon_video=romfs:/theme/icon_video.png
icon_image=romfs:/theme/icon_image.png
icon_file=romfs:/theme/icon_file.png
icon_folder=romfs:/theme/icon_folder.png
icon_zip=romfs:/theme/icon_zip.png
icon_nro=romfs:/theme/icon_nro.png

View File

@@ -1,6 +1,6 @@
cmake_minimum_required(VERSION 3.13) cmake_minimum_required(VERSION 3.13)
set(sphaira_VERSION 0.3.0) set(sphaira_VERSION 0.4.1)
project(sphaira project(sphaira
VERSION ${sphaira_VERSION} VERSION ${sphaira_VERSION}

View File

@@ -7,6 +7,8 @@ namespace sphaira::i18n {
bool init(long index); bool init(long index);
void exit(); void exit();
std::string get(const char* str);
} // namespace sphaira::i18n } // namespace sphaira::i18n
inline namespace literals { inline namespace literals {

View File

@@ -7,6 +7,15 @@
namespace sphaira::ui::menu::main { namespace sphaira::ui::menu::main {
enum class UpdateState {
// still downloading json from github
Pending,
// no update available.
None,
// update available!
Update,
};
// this holds 2 menus and allows for switching between them // this holds 2 menus and allows for switching between them
struct MainMenu final : Widget { struct MainMenu final : Widget {
MainMenu(); MainMenu();
@@ -31,7 +40,7 @@ private:
std::string m_update_url{}; std::string m_update_url{};
std::string m_update_version{}; std::string m_update_version{};
std::string m_update_description{}; std::string m_update_description{};
bool m_update_avaliable{}; UpdateState m_update_state{UpdateState::Pending};
}; };
} // namespace sphaira::ui::menu::main } // namespace sphaira::ui::menu::main

View File

@@ -1046,6 +1046,29 @@ App::~App() {
} else { } else {
log_write("success with copying over root file!\n"); log_write("success with copying over root file!\n");
} }
} else if (IsHbmenu()) {
// check we have a version that's newer than current.
fs::FsNativeSd fs;
NacpStruct sphaira_nacp;
fs::FsPath sphaira_path = "/switch/sphaira/sphaira.nro";
Result rc;
rc = nro_get_nacp(sphaira_path, sphaira_nacp);
if (R_FAILED(rc) || std::strcmp(sphaira_nacp.lang[0].name, "sphaira")) {
sphaira_path = "/switch/sphaira.nro";
rc = nro_get_nacp(sphaira_path, sphaira_nacp);
}
// found sphaira, now lets get compare version
if (R_SUCCEEDED(rc) && !std::strcmp(sphaira_nacp.lang[0].name, "sphaira")) {
if (std::strcmp(APP_VERSION, sphaira_nacp.display_version) < 0) {
if (R_FAILED(rc = fs.copy_entire_file(GetExePath(), sphaira_path, true))) {
log_write("failed to copy entire file: %s 0x%X module: %u desc: %u\n", sphaira_path, rc, R_MODULE(rc), R_DESCRIPTION(rc));
} else {
log_write("success with updating hbmenu!\n");
}
}
}
} }
if (App::GetNxlinkEnable()) { if (App::GetNxlinkEnable()) {

View File

@@ -11,7 +11,7 @@ std::vector<u8> g_i18n_data;
yyjson_doc* json; yyjson_doc* json;
yyjson_val* root; yyjson_val* root;
std::string get(const char* str, size_t len) { std::string get_internal(const char* str, size_t len) {
if (!json || !root) { if (!json || !root) {
log_write("no json or root\n"); log_write("no json or root\n");
return str; return str;
@@ -117,12 +117,16 @@ void exit() {
g_i18n_data.clear(); g_i18n_data.clear();
} }
std::string get(const char* str) {
return get_internal(str, std::strlen(str));
}
} // namespace sphaira::i18n } // namespace sphaira::i18n
namespace literals { namespace literals {
std::string operator"" _i18n(const char* str, size_t len) { std::string operator"" _i18n(const char* str, size_t len) {
return sphaira::i18n::get(str, len); return sphaira::i18n::get_internal(str, len);
} }
} // namespace literals } // namespace literals

View File

@@ -1087,21 +1087,14 @@ Menu::Menu(const std::vector<NroEntry>& nro_entries) : MenuBase{"AppStore"}, m_n
// check the date, if older than 1day, then fetch new file // check the date, if older than 1day, then fetch new file
// this relaxes the spam to their server, don't want to fetch repo // this relaxes the spam to their server, don't want to fetch repo
// every time the user opens the app! // every time the user opens the app!
const auto time_file = (time_t)time_stamp.created; const auto time_file = time_stamp.created;
const auto time_cur = (time_t)current_time; const auto time_cur = current_time;
const auto tm_file = *gmtime(&time_file); const auto day = 60 * 60 * 24;
const auto tm_cur = *gmtime(&time_cur); if (time_file > time_cur || time_cur - time_file >= day) {
if (tm_cur.tm_yday > tm_file.tm_yday || tm_cur.tm_year > tm_file.tm_year) { log_write("repo.json expired, downloading new! time_file: %zu time_cur: %zu\n", time_file, time_cur);
log_write("repo.json expired, downloading new! cur_yday: %d file_yday: %d | cur_year: %d file_year: %d\n", tm_cur.tm_yday, tm_file.tm_yday, tm_cur.tm_year, tm_file.tm_year);
download_file = true; download_file = true;
} else { } else {
log_write("repo.json not expired! cur_yday: %d file_yday: %d | cur_year: %d file_year: %d\n", tm_cur.tm_yday, tm_file.tm_yday, tm_cur.tm_year, tm_file.tm_year); log_write("repo.json not expired! time_file: %zu time_cur: %zu\n", time_file, time_cur);
// time_file = (time_t)time_stamp.modified;
// tm_file = *gmtime(&time_file);
// log_write("repo.json not expired! cur_yday: %d file_yday: %d | cur_year: %d file_year: %d\n", tm_cur.tm_yday, tm_file.tm_yday, tm_cur.tm_year, tm_file.tm_year);
// time_file = (time_t)time_stamp.accessed;
// tm_file = *gmtime(&time_file);
// log_write("repo.json not expired! cur_yday: %d file_yday: %d | cur_year: %d file_year: %d\n", tm_cur.tm_yday, tm_file.tm_yday, tm_cur.tm_year, tm_file.tm_year);
} }
} }
@@ -1438,7 +1431,7 @@ void Menu::Sort() {
char subheader[128]{}; char subheader[128]{};
std::snprintf(subheader, sizeof(subheader), "Sort: %s | Filter: %s | Order: %s", SORT_STR[m_sort], FILTER_STR[m_filter], ORDER_STR[m_order]); std::snprintf(subheader, sizeof(subheader), "Sort: %s | Filter: %s | Order: %s", i18n::get(SORT_STR[m_sort]).c_str(), i18n::get(FILTER_STR[m_filter]).c_str(), i18n::get(ORDER_STR[m_order]).c_str());
SetTitleSubHeading(subheader); SetTitleSubHeading(subheader);
std::sort(m_entries_current.begin(), m_entries_current.end(), sorter); std::sort(m_entries_current.begin(), m_entries_current.end(), sorter);

View File

@@ -791,8 +791,9 @@ void Menu::Draw(NVGcontext* vg, Theme* theme) {
} }
} }
const auto t = (time_t)(e.time_stamp.modified); const auto t = (time_t)(e.time_stamp.modified);
const auto tm = gmtime(&t); struct tm tm{};
gfx::drawTextArgs(vg, x + w - text_xoffset, y + (h / 2.f) + 3, 16.f, NVG_ALIGN_RIGHT | NVG_ALIGN_TOP, theme->elements[text_id].colour, "%02u/%02u/%u", tm->tm_mday, tm->tm_mon + 1, tm->tm_year + 1900); localtime_r(&t, &tm);
gfx::drawTextArgs(vg, x + w - text_xoffset, y + (h / 2.f) + 3, 16.f, NVG_ALIGN_RIGHT | NVG_ALIGN_TOP, theme->elements[text_id].colour, "%02u/%02u/%u", tm.tm_mday, tm.tm_mon + 1, tm.tm_year + 1900);
if ((double)e.file_size / 1024.0 / 1024.0 <= 0.009) { if ((double)e.file_size / 1024.0 / 1024.0 <= 0.009) {
gfx::drawTextArgs(vg, x + w - text_xoffset, y + (h / 2.f) - 3, 16.f, NVG_ALIGN_RIGHT | NVG_ALIGN_BOTTOM, theme->elements[text_id].colour, "%.2f KiB", (double)e.file_size / 1024.0); gfx::drawTextArgs(vg, x + w - text_xoffset, y + (h / 2.f) - 3, 16.f, NVG_ALIGN_RIGHT | NVG_ALIGN_BOTTOM, theme->elements[text_id].colour, "%.2f KiB", (double)e.file_size / 1024.0);
} else { } else {

View File

@@ -17,38 +17,6 @@
namespace sphaira::ui::menu::homebrew { namespace sphaira::ui::menu::homebrew {
namespace { namespace {
constexpr const char* SORT_STR[] = {
"Updated",
"Size",
"Alphabetical",
};
constexpr const char* ORDER_STR[] = {
"Desc",
"Asc",
};
// returns seconds as: hh:mm:ss
auto TimeFormat(u64 sec) -> std::string {
char buf[9];
const auto s = sec % 60;
const auto h = sec / 60 % 60;
const auto d = sec / 60 / 60 % 24;
if (sec < 60) {
if (!sec) {
return "00:00:00";
}
std::snprintf(buf, sizeof(buf), "00:00:%02lu", s);
} else if (sec < 3600) {
std::snprintf(buf, sizeof(buf), "00:%02lu:%02lu", h, s);
} else {
std::snprintf(buf, sizeof(buf), "%02lu:%02lu:%02lu", d, h, s);
}
return std::string{buf};
}
} // namespace } // namespace

View File

@@ -1,76 +1,187 @@
#include "ui/menus/main_menu.hpp" #include "ui/menus/main_menu.hpp"
#include "ui/menus/irs_menu.hpp"
#include "ui/menus/themezer.hpp"
#include "ui/sidebar.hpp" #include "ui/sidebar.hpp"
#include "ui/popup_list.hpp" #include "ui/popup_list.hpp"
#include "ui/option_box.hpp" #include "ui/option_box.hpp"
#include "ui/progress_box.hpp"
#include "ui/error_box.hpp"
#include "app.hpp" #include "app.hpp"
#include "log.hpp" #include "log.hpp"
#include "download.hpp" #include "download.hpp"
#include "defines.hpp" #include "defines.hpp"
#include "ui/menus/irs_menu.hpp"
#include "ui/menus/themezer.hpp"
#include "web.hpp" #include "web.hpp"
#include "i18n.hpp" #include "i18n.hpp"
#include <cstring> #include <cstring>
#include <minizip/unzip.h>
#include <yyjson.h>
namespace sphaira::ui::menu::main { namespace sphaira::ui::menu::main {
namespace { namespace {
#if 0 auto InstallUpdate(ProgressBox* pbox, const std::string url, const std::string version) -> bool {
bool parseSearch(const char *parse_string, const char *filter, char* new_string) { static fs::FsPath zip_out{"/switch/sphaira/cache/update.zip"};
char c; constexpr auto chunk_size = 1024 * 512; // 512KiB
u32 offset = 0;
const u32 filter_len = std::strlen(filter) - 1;
while ((c = parse_string[offset++]) != '\0') { fs::FsNativeSd fs;
if (c == *filter) { R_TRY_RESULT(fs.GetFsOpenResult(), false);
for (u32 i = 0; c == filter[i]; i++) {
c = parse_string[offset++]; // 1. download the zip
if (i == filter_len) { if (!pbox->ShouldExit()) {
for (u32 j = 0; c != '\"'; j++) { pbox->NewTransfer("Downloading "_i18n + version);
new_string[j] = c; log_write("starting download: %s\n", url.c_str());
new_string[j+1] = '\0';
c = parse_string[offset++]; DownloadClearCache(url);
if (!DownloadFile(url, zip_out, "", [pbox](u32 dltotal, u32 dlnow, u32 ultotal, u32 ulnow){
if (pbox->ShouldExit()) {
return false;
}
pbox->UpdateTransfer(dlnow, dltotal);
return true;
})) {
log_write("error with download\n");
// push popup error box
return false;
}
}
ON_SCOPE_EXIT(fs.DeleteFile(zip_out));
// 2. extract the zip
if (!pbox->ShouldExit()) {
auto zfile = unzOpen64(zip_out);
if (!zfile) {
log_write("failed to open zip: %s\n", zip_out);
return false;
}
ON_SCOPE_EXIT(unzClose(zfile));
unz_global_info64 pglobal_info;
if (UNZ_OK != unzGetGlobalInfo64(zfile, &pglobal_info)) {
return false;
}
for (int i = 0; i < pglobal_info.number_entry; i++) {
if (i > 0) {
if (UNZ_OK != unzGoToNextFile(zfile)) {
log_write("failed to unzGoToNextFile\n");
return false;
}
}
if (UNZ_OK != unzOpenCurrentFile(zfile)) {
log_write("failed to open current file\n");
return false;
}
ON_SCOPE_EXIT(unzCloseCurrentFile(zfile));
unz_file_info64 info;
fs::FsPath file_path;
if (UNZ_OK != unzGetCurrentFileInfo64(zfile, &info, file_path, sizeof(file_path), 0, 0, 0, 0)) {
log_write("failed to get current info\n");
return false;
}
if (file_path[0] != '/') {
file_path = fs::AppendPath("/", file_path);
}
Result rc;
if (file_path[strlen(file_path) -1] == '/') {
if (R_FAILED(rc = fs.CreateDirectoryRecursively(file_path)) && rc != FsError_ResultPathAlreadyExists) {
log_write("failed to create folder: %s 0x%04X\n", file_path, rc);
return false;
}
} else {
Result rc;
if (R_FAILED(rc = fs.CreateFile(file_path, info.uncompressed_size, 0)) && rc != FsError_ResultPathAlreadyExists) {
log_write("failed to create file: %s 0x%04X\n", file_path, rc);
return false;
}
FsFile f;
if (R_FAILED(rc = fs.OpenFile(file_path, FsOpenMode_Write, &f))) {
log_write("failed to open file: %s 0x%04X\n", file_path, rc);
return false;
}
ON_SCOPE_EXIT(fsFileClose(&f));
if (R_FAILED(rc = fsFileSetSize(&f, info.uncompressed_size))) {
log_write("failed to set file size: %s 0x%04X\n", file_path, rc);
return false;
}
std::vector<char> buf(chunk_size);
u64 offset{};
while (offset < info.uncompressed_size) {
if (pbox->ShouldExit()) {
return false;
} }
return true;
const auto bytes_read = unzReadCurrentFile(zfile, buf.data(), buf.size());
if (bytes_read <= 0) {
// log_write("failed to read zip file: %s\n", inzip.c_str());
return false;
}
if (R_FAILED(rc = fsFileWrite(&f, offset, buf.data(), bytes_read, FsWriteOption_None))) {
log_write("failed to write file: %s 0x%04X\n", file_path, rc);
return false;
}
pbox->UpdateTransfer(offset, info.uncompressed_size);
offset += bytes_read;
} }
} }
} }
} }
return false; log_write("finished update :)\n");
return true;
} }
#endif
} // namespace } // namespace
MainMenu::MainMenu() { MainMenu::MainMenu() {
#if 0 DownloadMemoryAsync("https://api.github.com/repos/ITotalJustice/sphaira/releases/latest", "", [this](std::vector<u8>& data, bool success){
DownloadMemoryAsync("https://api.github.com/repos/ITotalJustice/sys-patch/releases/latest", [this](std::vector<u8>& data, bool success){ m_update_state = UpdateState::None;
data.push_back('\0'); auto json = yyjson_read((const char*)data.data(), data.size(), 0);
auto raw_str = (const char*)data.data(); R_UNLESS(json, false);
char out_str[0x301]; ON_SCOPE_EXIT(yyjson_doc_free(json));
if (parseSearch(raw_str, "tag_name\":\"", out_str)) { auto root = yyjson_doc_get_root(json);
m_update_version = out_str; R_UNLESS(root, false);
if (strcasecmp("v1.5.0", m_update_version.c_str())) {
m_update_avaliable = true;
}
log_write("FOUND IT : %s\n", out_str);
}
if (parseSearch(raw_str, "browser_download_url\":\"", out_str)) { auto tag_key = yyjson_obj_get(root, "tag_name");
m_update_url = out_str; R_UNLESS(tag_key, false);
log_write("FOUND IT : %s\n", out_str);
}
if (parseSearch(raw_str, "body\":\"", out_str)) { const auto version = yyjson_get_str(tag_key);
m_update_description = out_str; R_UNLESS(version, false);
// m_update_description.replace("\r\n\r\n", "\n"); R_UNLESS(std::strcmp(APP_VERSION, version) < 0, false);
log_write("FOUND IT : %s\n", out_str);
} auto assets = yyjson_obj_get(root, "assets");
R_UNLESS(assets, false);
auto idx0 = yyjson_arr_get(assets, 0);
R_UNLESS(idx0, false);
auto url_key = yyjson_obj_get(idx0, "browser_download_url");
R_UNLESS(url_key, false);
const auto url = yyjson_get_str(url_key);
R_UNLESS(url, false);
m_update_version = version;
m_update_url = url;
m_update_state = UpdateState::Update;
log_write("found url: %s\n", url);
App::Notify("Update avaliable: "_i18n + m_update_version);
return true;
}); });
#endif
AddOnLPress(); AddOnLPress();
AddOnRPress(); AddOnRPress();
@@ -128,22 +239,26 @@ MainMenu::MainMenu() {
options->Add(std::make_shared<SidebarEntryBool>("Nxlink"_i18n, App::GetNxlinkEnable(), [this](bool& enable){ options->Add(std::make_shared<SidebarEntryBool>("Nxlink"_i18n, App::GetNxlinkEnable(), [this](bool& enable){
App::SetNxlinkEnable(enable); App::SetNxlinkEnable(enable);
}, "Enabled"_i18n, "Disabled"_i18n)); }, "Enabled"_i18n, "Disabled"_i18n));
options->Add(std::make_shared<SidebarEntryCallback>("Check for update"_i18n, [this](){
App::Notify("Not Implemented"_i18n); if (m_update_state == UpdateState::Update) {
})); options->Add(std::make_shared<SidebarEntryCallback>("Download update: "_i18n + m_update_version, [this](){
App::Push(std::make_shared<ProgressBox>("Downloading "_i18n + m_update_version, [this](auto pbox){
return InstallUpdate(pbox, m_update_url, m_update_version);
}, [this](bool success){
if (success) {
m_update_state = UpdateState::None;
} else {
App::Push(std::make_shared<ui::ErrorBox>(MAKERESULT(351, 1), "Failed to download update"));
}
}, 2));
}));
}
})); }));
options->Add(std::make_shared<SidebarEntryArray>("Language"_i18n, language_items, [this, language_items](std::size_t& index_out){ options->Add(std::make_shared<SidebarEntryArray>("Language"_i18n, language_items, [this, language_items](std::size_t& index_out){
App::SetLanguage(index_out); App::SetLanguage(index_out);
}, (std::size_t)App::GetLanguage())); }, (std::size_t)App::GetLanguage()));
if (m_update_avaliable) {
std::string str = "Update avaliable: "_i18n + m_update_version;
options->Add(std::make_shared<SidebarEntryCallback>(str, [this](){
App::Notify("Not Implemented"_i18n);
}));
}
options->Add(std::make_shared<SidebarEntryBool>("Logging"_i18n, App::GetLogEnable(), [this](bool& enable){ options->Add(std::make_shared<SidebarEntryBool>("Logging"_i18n, App::GetLogEnable(), [this](bool& enable){
App::SetLogEnable(enable); App::SetLogEnable(enable);
}, "Enabled"_i18n, "Disabled"_i18n)); }, "Enabled"_i18n, "Disabled"_i18n));

View File

@@ -32,12 +32,9 @@ void MenuBase::Draw(NVGcontext* vg, Theme* theme) {
u32 strength{}; u32 strength{};
u32 ip{}; u32 ip{};
const auto _time = time(NULL); const auto t = time(NULL);
struct tm tm{}; struct tm tm{};
const auto gmt = gmtime(&_time); localtime_r(&t, &tm);
if (gmt) {
tm = *gmt;
}
// todo: app thread poll every 1s and this query the result // todo: app thread poll every 1s and this query the result
psmGetBatteryChargePercentage(&battery_percetange); psmGetBatteryChargePercentage(&battery_percetange);