Hard reset app (#3182)

This commit is contained in:
Pezsma
2026-05-20 18:38:13 +02:00
committed by GitHub
parent 95b2e9fbc3
commit ec6c7eff86
5 changed files with 408 additions and 1 deletions
+6 -1
View File
@@ -357,7 +357,11 @@ set(EXTCPPSRC
#two_tone_rx
external/two_tone_rx/main.cpp
external/two_tone_rx/ui_two_tone_rx.cpp
external/two_tone_rx/ui_two_tone_rx.cpp
#hard_reset
external/hard_reset/main.cpp
external/hard_reset/ui_hard_reset.cpp
)
set(EXTAPPLIST
@@ -446,6 +450,7 @@ set(EXTAPPLIST
p25_tx
two_tone_pager
two_tone_rx
hard_reset
)
# sdusb has type conflicts with PRALINE (HackRF Pro) - add only for non-PRALINE builds
+7
View File
@@ -109,6 +109,7 @@ MEMORY
ram_external_app_two_tone_pager (rwx) : org = 0xAE040000, len = 32k
ram_external_app_two_tone_rx (rwx) : org = 0xAE050000, len = 32k
ram_external_app_flex_tx (rwx) : org = 0xAE060000, len = 32k
ram_external_app_hard_reset (rwx) : org = 0xAE070000, len = 32k
}
SECTIONS
@@ -628,4 +629,10 @@ SECTIONS
KEEP(*(.external_app.app_flex_tx.application_information));
*(*ui*external_app*flex_tx*);
} > ram_external_app_flex_tx
.external_app_hard_reset : ALIGN(4) SUBALIGN(4)
{
KEEP(*(.external_app.app_hard_reset.application_information));
*(*ui*external_app*hard_reset*);
} > ram_external_app_hard_reset
}
+87
View File
@@ -0,0 +1,87 @@
/*
* Copyright (C) 2026 Pezsma
*
* This file is part of PortaPack.
*
* This program 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 2, or (at your option)
* any later version.
*
* This program 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; see the file COPYING. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street,
* Boston, MA 02110-1301, USA.
*/
#include "ui.hpp"
#include "ui_hard_reset.hpp"
#include "ui_navigation.hpp"
#include "external_app.hpp"
namespace ui::external_app::hard_reset {
void initialize_app(NavigationView& nav) {
nav.push<HardResetView>();
}
} // namespace ui::external_app::hard_reset
extern "C" {
__attribute__((section(".external_app.app_hard_reset.application_information"), used))
application_information_t _application_information_hard_reset = {
/*.memory_location = */ (uint8_t*)0x00000000,
/*.externalAppEntry = */ ui::external_app::hard_reset::initialize_app,
/*.header_version = */ CURRENT_HEADER_VERSION,
/*.app_version = */ VERSION_MD5,
/*.app_name = */ "Hard Reset",
/*.bitmap_data = */ {
0x00,
0x00,
0x00,
0x00,
0xC0,
0x01,
0xFF,
0x7F,
0xFF,
0x7F,
0x56,
0x35,
0x56,
0x35,
0x56,
0x35,
0x56,
0x35,
0x56,
0x35,
0x56,
0x35,
0x56,
0x35,
0x56,
0x35,
0xFE,
0x3F,
0xFE,
0x3F,
0x00,
0x00,
},
/*.icon_color = */ ui::Color::red().v,
/*.menu_location = */ app_location_t::SETTINGS,
/*.desired_menu_position = */ -1,
/*.m4_app_tag = portapack::spi_flash::image_tag_none */ {0, 0, 0, 0},
/*.m4_app_offset = */ 0x00000000, // will be filled at compile time
};
} // extern "C"
@@ -0,0 +1,239 @@
/*
* Copyright (C) 2026 Pezsma
*
* This file is part of PortaPack.
*
* This program 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 2, or (at your option)
* any later version.
*
* This program 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; see the file COPYING. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street,
* Boston, MA 02110-1301, USA.
*/
#include "ui_hard_reset.hpp"
#include "portapack.hpp"
#include "file.hpp"
#include "file_path.hpp"
#include "ui_touch_calibration.hpp"
#include "ui_external_items_menu_loader.hpp"
#include "app_settings.hpp"
using namespace portapack;
namespace pmem = portapack::persistent_memory;
namespace ui::external_app::hard_reset {
HardResetView::HardResetView(ui::NavigationView& nav)
: nav_(nav) {
add_children({&chk_settings_file,
&chk_bad_apps,
&chk_pmem,
&txt_settings_file_count,
&txt_bad_apps_count,
&btn_yes,
&btn_no,
&text,
&txt_wait});
txt_wait.set_style(Theme::getInstance()->warning_dark);
btn_yes.on_select = [this](Button&) {
{ // to free painter after draw
Painter painter;
txt_wait.hidden(false);
text.hidden(true);
painter.fill_rectangle(text.screen_rect(), this->style().background);
txt_wait.paint(painter);
}
if (chk_settings_file.value()) {
clear_settings_folder();
}
if (chk_bad_apps.value()) {
delete_bad_apps(apps_dir);
}
if (chk_pmem.value()) {
pmem::cache::defaults();
StatusRefreshMessage message{};
EventDispatcher::send_message(message);
nav_.replace<TouchCalibrationView>();
} else {
nav_.pop();
}
};
btn_no.on_select = [this](Button&) {
nav_.pop();
};
txt_settings_file_count.set_style(Theme::getInstance()->fg_magenta);
txt_bad_apps_count.set_style(Theme::getInstance()->fg_magenta);
signal_token_tick_second = rtc_time::signal_tick_second += [this]() {
this->calculate();
rtc_time::signal_tick_second -= signal_token_tick_second;
};
}
HardResetView::~HardResetView() {
rtc_time::signal_tick_second -= signal_token_tick_second;
}
void HardResetView::calculate() {
settings_file_count = count_ini_files_in_directory(settings_dir);
txt_settings_file_count.set(to_string_dec_uint(settings_file_count));
bad_apps_count = count_bad_apps(apps_dir);
txt_bad_apps_count.set(to_string_dec_uint(bad_apps_count));
if (settings_file_count > 0) {
chk_settings_file.set_value(true);
}
if (bad_apps_count > 0) {
chk_bad_apps.set_value(true);
}
chk_pmem.set_value(true);
txt_wait.hidden(true);
text.set("Warning! Checked items will beerased (bad apps, P.Mem, settings). Uncheck to keep. Touch calibration is required only if P.Mem is reset.");
}
void HardResetView::focus() {
btn_no.focus();
}
uint16_t HardResetView::count_ini_files_in_directory(const std::filesystem::path& dir_path) {
int count = 0;
for (const auto& entry : std::filesystem::directory_iterator(dir_path, u"*.ini")) {
if (std::filesystem::is_regular_file(entry.status())) {
count++;
}
}
return count;
}
void HardResetView::delete_ini_files_in_directory(const std::filesystem::path& dir_path) {
bool found_and_deleted;
do {
found_and_deleted = false;
for (const auto& entry : std::filesystem::directory_iterator(dir_path, u"*.ini")) {
if (std::filesystem::is_regular_file(entry.status())) {
auto full_path = dir_path / entry.path().filename();
delete_file(full_path);
found_and_deleted = true;
break; // iterator can become invalid if the dir is modified, so break and restart the loop after deletion
}
}
} while (found_and_deleted);
}
void HardResetView::clear_settings_folder() {
delete_ini_files_in_directory(settings_dir);
}
bool HardResetView::is_bad_app(const std::filesystem::path& file_path, bool is_ppma) {
File app;
if (app.open(file_path)) {
return true;
}
bool is_bad = false;
if (is_ppma) {
application_information_t app_info = {};
auto readResult = app.read(&app_info, sizeof(application_information_t));
if (!readResult || readResult.value() != sizeof(application_information_t)) {
is_bad = true;
} else if (app_info.header_version != CURRENT_HEADER_VERSION) {
is_bad = true;
} else if (VERSION_MD5 != app_info.app_version) {
is_bad = true;
} else {
// --- CHECKSUM VALIDATION ---
// Similar to run_external_app: read through the file and calculate the checksum
app.seek(0);
uint32_t checksum = 0;
uint8_t buffer[512]; // 512-byte buffer to conserve RAM
while (true) {
auto res = app.read(buffer, sizeof(buffer));
if (!res) break;
checksum += simple_checksum((uint32_t)buffer, res.value());
if (res.value() < sizeof(buffer)) break; // End of file
}
if (checksum != EXT_APP_EXPECTED_CHECKSUM) {
is_bad = true; // The file body is corrupted / truncated!
}
}
} else {
// PPMP validation
standalone_application_information_t app_info = {};
auto readResult = app.read(&app_info, sizeof(standalone_application_information_t));
if (!readResult || readResult.value() != sizeof(standalone_application_information_t)) {
is_bad = true;
} else if (app_info.header_version > CURRENT_STANDALONE_APPLICATION_API_VERSION) {
is_bad = true;
}
}
app.close();
return is_bad;
}
uint16_t HardResetView::count_bad_apps(const std::filesystem::path& apps_dir) {
int bad_count = 0;
// Check .ppma files
for (const auto& entry : std::filesystem::directory_iterator(apps_dir, u"*.ppma")) {
auto file_path = apps_dir / entry.path();
if (is_bad_app(file_path, true)) {
bad_count++;
}
}
// Check .ppmp files
for (const auto& entry : std::filesystem::directory_iterator(apps_dir, u"*.ppmp")) {
auto file_path = apps_dir / entry.path();
if (is_bad_app(file_path, false)) {
bad_count++;
}
}
return bad_count;
}
void HardResetView::delete_bad_apps(const std::filesystem::path& apps_dir) {
// Delete bad .ppma files
bool deleted_something;
do {
deleted_something = false;
for (const auto& entry : std::filesystem::directory_iterator(apps_dir, u"*.ppma")) {
auto file_path = apps_dir / entry.path();
if (is_bad_app(file_path, true)) {
delete_file(file_path);
deleted_something = true;
break;
}
}
} while (deleted_something);
// Delete bad .ppmp files
do {
deleted_something = false;
for (const auto& entry : std::filesystem::directory_iterator(apps_dir, u"*.ppmp")) {
auto file_path = apps_dir / entry.path();
if (is_bad_app(file_path, false)) {
delete_file(file_path);
deleted_something = true;
break;
}
}
} while (deleted_something);
}
} // namespace ui::external_app::hard_reset
@@ -0,0 +1,69 @@
/*
* Copyright (C) 2026 Pezsma
*
* This file is part of PortaPack.
*
* This program 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 2, or (at your option)
* any later version.
*
* This program 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; see the file COPYING. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street,
* Boston, MA 02110-1301, USA.
*/
#ifndef __UI_HARD_RESET_H__
#define __UI_HARD_RESET_H__
#include "ui_navigation.hpp"
#include "signal.hpp"
using namespace ui;
namespace ui::external_app::hard_reset {
class HardResetView : public ui::View {
public:
HardResetView(ui::NavigationView& nav);
~HardResetView();
std::string title() const override { return "Hard Reset"; }
void focus() override;
private:
ui::NavigationView& nav_;
SignalToken signal_token_tick_second{};
ui::Button btn_yes{{UI_POS_X(1), UI_POS_Y_BOTTOM(4), UI_POS_WIDTH(11), UI_POS_HEIGHT(2)}, "Erase sel."};
ui::Button btn_no{{UI_POS_X_RIGHT(11), UI_POS_Y_BOTTOM(4), UI_POS_WIDTH(10), UI_POS_HEIGHT(2)}, "No"};
ui::Text text{{UI_POS_X(0), UI_POS_Y(6), UI_POS_MAXWIDTH, UI_POS_HEIGHT(5)}, ""};
Checkbox chk_settings_file{{UI_POS_X(1), UI_POS_Y(1)}, 14, "Settings file:", true};
Checkbox chk_bad_apps{{UI_POS_X(1), UI_POS_Y(2.5)}, 9, "Bad apps:", true};
Checkbox chk_pmem{{UI_POS_X(1), UI_POS_Y(4)}, 11, "P.mem reset", true};
Text txt_settings_file_count{{UI_POS_X(19), UI_POS_Y(1), UI_POS_WIDTH(5), UI_POS_HEIGHT(1)}, "?"};
Text txt_bad_apps_count{{UI_POS_X(14), UI_POS_Y(2.5), UI_POS_WIDTH(5), UI_POS_HEIGHT(1)}, "?"};
Text txt_wait{{UI_POS_X_CENTER(14), UI_POS_Y(8), UI_POS_WIDTH(14), UI_POS_HEIGHT(1)}, "Please wait..."};
uint16_t count_ini_files_in_directory(const std::filesystem::path& dir_path);
void delete_ini_files_in_directory(const std::filesystem::path& dir_path);
void clear_settings_folder();
void calculate();
bool is_bad_app(const std::filesystem::path& file_path, bool is_ppma);
uint16_t count_bad_apps(const std::filesystem::path& apps_dir);
void delete_bad_apps(const std::filesystem::path& apps_dir);
uint16_t settings_file_count = 0;
uint16_t bad_apps_count = 0;
};
} // namespace ui::external_app::hard_reset
#endif // __UI_HARD_RESET_H__