This commit is contained in:
summermorningdream 2024-05-16 02:15:42 +00:00 committed by GitHub
commit 132a18d58a
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
44 changed files with 717 additions and 213 deletions

6
.gitignore vendored
View File

@ -9,10 +9,16 @@
/firmware/application/portapack_cpld_data.cpp
/firmware/application/hackrf_cpld_data.cpp
/firmware/application/hackrf_cpld_data.hpp
/sdcard/SYS/ADSB/world_map.bin
/sdcard/SYS/FREQMAN/BHT*
/sdcard/SYS/FREQMAN/R.TXT
/sdcard/SYS/FREQMAN/XXX.TXT
/sdcard/ADSB/world_map.bin
/sdcard/FREQMAN/BHT*
/sdcard/FREQMAN/R.TXT
/sdcard/FREQMAN/XXX.TXT
# Toolchain binaries
/armbin
# Compiled Object files

View File

@ -33,6 +33,7 @@
#include "app_settings.hpp"
#include "radio_state.hpp"
#include "tone_key.hpp"
#include "file_path.hpp"
namespace ui {
@ -226,7 +227,7 @@ class AnalogAudioView : public View {
RecordView record_view{
{0 * 8, 2 * 16, 30 * 8, 1 * 16},
u"AUD",
u"AUDIO",
audio_dir_user,
RecordView::FileType::WAV,
4096,
4};

View File

@ -470,7 +470,7 @@ BLERxView::BLERxView(NavigationView& nav)
logging = v;
if (logger && logging)
logger->append(blerx_dir.string() + "/Logs/BLELOG_" + to_string_timestamp(rtc_time::now()) + ".TXT");
logger->append(blerx_dir_user.string() + "/Logs/BLELOG_" + to_string_timestamp(rtc_time::now()) + ".TXT");
};
check_log.set_value(logging);

View File

@ -134,7 +134,7 @@ class BleRecentEntryDetailView : public View {
void on_save_file(const std::string value, BLETxPacket packetToSave);
bool saveFile(const std::filesystem::path& path, BLETxPacket packetToSave);
std::string packetFileBuffer{};
std::filesystem::path packet_save_path{blerx_dir / u"Lists/????.csv"};
std::filesystem::path packet_save_path{blerx_dir_user / u"Lists/????.csv"};
static constexpr uint8_t total_data_lines{5};
@ -251,9 +251,9 @@ class BLERxView : public View {
uint64_t total_count = 0;
std::vector<std::string> searchList{};
std::filesystem::path find_packet_path{blerx_dir / u"Find/????.TXT"};
std::filesystem::path log_packets_path{blerx_dir / u"Logs/????.TXT"};
std::filesystem::path packet_save_path{blerx_dir / u"Lists/????.csv"};
std::filesystem::path find_packet_path{blerx_dir_user / u"Find/????.TXT"};
std::filesystem::path log_packets_path{blerx_dir_user / u"Logs/????.TXT"};
std::filesystem::path packet_save_path{blerx_dir_user / u"Lists/????.csv"};
static constexpr auto header_height = 4 * 16;
static constexpr auto switch_button_height = 3 * 16;

View File

@ -139,7 +139,7 @@ class BLETxView : public View {
uint32_t prev_value{0};
std::filesystem::path file_path{};
std::filesystem::path packet_save_path{bletx_dir / u"BLETX_????.TXT"};
std::filesystem::path packet_save_path{bletx_dir_user / u"BLETX_????.TXT"};
uint8_t channel_number = 37;
bool auto_channel = false;
@ -166,7 +166,7 @@ class BLETxView : public View {
std::unique_ptr<FileWrapper> dataFileWrapper{};
File dataFile{};
std::filesystem::path dataTempFilePath{bletx_dir / u"dataFileTemp.TXT"};
std::filesystem::path dataTempFilePath{bletx_dir_resources / u"dataFileTemp.TXT"};
std::vector<uint16_t> markedBytes{};
CursorPos cursor_pos{};
uint8_t marked_counter = 0;

View File

@ -102,7 +102,7 @@ class CaptureAppView : public View {
RecordView record_view{
{0 * 8, 2 * 16, 30 * 8, 1 * 16},
u"BBD_????.*",
captures_dir,
captures_dir_user,
RecordView::FileType::RawS16,
16384,
3};

View File

@ -95,9 +95,11 @@ void SoundBoardView::start_tx(const uint32_t id) {
stop();
if (!reader->open(u"/WAV/" + file_list[id].native())) {
file_error();
return;
if (!reader->open(wav_dir_resources + u"/" + file_list[id].native())) {
if (!reader->open(wav_dir_user + u"/" + file_list[id].native())) {
file_error();
return;
}
}
playing_id = id;
@ -164,28 +166,34 @@ void SoundBoardView::refresh_list() {
c_page = page;
// List directories and files, put directories up top
std::filesystem::path file_list_index[2];
file_list_index[0] = wav_dir_user;
file_list_index[1] = wav_dir_resources;
uint32_t count = 0;
for (const auto& entry : std::filesystem::directory_iterator(wav_dir, u"*")) {
if (std::filesystem::is_regular_file(entry.status())) {
if (entry.path().string().length()) {
auto entry_extension = entry.path().extension().string();
// tempnewnote: need test
for (const auto& now_path : file_list_index) {
for (const auto& entry : std::filesystem::directory_iterator(now_path, u"*")) {
if (std::filesystem::is_regular_file(entry.status())) {
if (entry.path().string().length()) {
auto entry_extension = entry.path().extension().string();
for (auto& c : entry_extension)
c = toupper(c);
for (auto& c : entry_extension)
c = toupper(c);
if (entry_extension == ".WAV") {
if (reader->open(wav_dir / entry.path())) {
if ((reader->channels() == 1) && ((reader->bits_per_sample() == 8) || (reader->bits_per_sample() == 16))) {
// sounds[c].ms_duration = reader->ms_duration();
// sounds[c].path = u"WAV/" + entry.path().native();
if (count >= (page - 1) * 100 && count < page * 100) {
file_list.push_back(entry.path());
if (file_list.size() == 100) {
page++;
break;
if (entry_extension == ".WAV") {
if (reader->open(now_path / entry.path().native())) {
if ((reader->channels() == 1) && ((reader->bits_per_sample() == 8) || (reader->bits_per_sample() == 16))) {
// sounds[c].ms_duration = reader->ms_duration();
// sounds[c].path = u"/SYS/WAV/" + entry.path().native();
if (count >= (page - 1) * 100 && count < page * 100) {
file_list.push_back(entry.path());
if (file_list.size() == 100) {
page++;
break;
}
}
count++;
}
count++;
}
}
}

View File

@ -233,7 +233,7 @@ class APRSRxView : public View {
RecordView record_view{
{0 * 8, 1 * 16, 30 * 8, 1 * 16},
u"AFS_????.WAV",
aprs_dir,
aprs_dir_user,
RecordView::FileType::WAV,
4096,
4};

View File

@ -1,6 +1,7 @@
/*
* Copyright (C) 2015 Jared Boone, ShareBrained Technology, Inc.
* Copyright (C) 2017 Furrtek
* Copyleft (ɔ) 2024 zxkmm with the GPL license
*
* This file is part of PortaPack.
*
@ -49,6 +50,7 @@ static const fs::path cxx_ext{u".C*"};
static const fs::path png_ext{u".PNG"};
static const fs::path bmp_ext{u".BMP"};
static const fs::path rem_ext{u".REM"};
} // namespace ui
namespace {
@ -227,7 +229,7 @@ void FileManBaseView::load_directory_contents(const fs::path& dir_path) {
auto filtering = !extension_filter.empty();
bool cxx_file = path_iequal(cxx_ext, extension_filter);
text_current.set(dir_path.empty() ? "(sd root)" : truncate(dir_path, 24));
text_current.set(current_path.empty() ? "/" : truncate(dir_path, 24));
for (const auto& entry : fs::directory_iterator(dir_path, u"*")) {
// Hide files starting with '.' (hidden / tmp).
@ -237,6 +239,7 @@ void FileManBaseView::load_directory_contents(const fs::path& dir_path) {
if (fs::is_regular_file(entry.status())) {
if (!filtering || path_iequal(entry.path().extension(), extension_filter) || (cxx_file && is_cxx_capture_file(entry.path())))
insert_sorted(entry_list, {entry.path().string(), (uint32_t)entry.size(), false});
} else if (fs::is_directory(entry.status())) {
insert_sorted(entry_list, {entry.path().string(), 0, true});
}
@ -291,6 +294,8 @@ FileManBaseView::FileManBaseView(
extension_filter{filter} {
add_children({&labels,
&text_current,
&option_profile_switch,
&label_profile,
&button_exit});
button_exit.on_select = [this](Button&) {
@ -313,6 +318,24 @@ FileManBaseView::FileManBaseView(
pop_dir();
};
}
option_profile_switch.on_change = [this](size_t, uint8_t profiles) {
switch (static_cast<DirProfiles>(profiles)) {
case DirProfiles::User:
jumping_between_profiles(current_path, DirProfiles::User);
option_profile_switch.set_style(nullptr);
break;
case DirProfiles::System:
jumping_between_profiles(current_path, DirProfiles::System);
option_profile_switch.set_style(&Styles::red);
break;
}
reload_current();
};
}
void FileManBaseView::focus() {
@ -324,8 +347,29 @@ void FileManBaseView::focus() {
}
void FileManBaseView::push_dir(const fs::path& path) {
// if you want it freely jump between profiles when picking files in your app, don't use this
// , you should use push_fake_dir, which handle and call back the dir automatically
if (path == parent_dir_path) {
pop_dir();
} else if (path == system_dir || path == apps_dir || path == firmware_dir) {
nav_.push<ModalMessageView>(
"Warning",
"It is not recommended to\n"
"modify system files,\n"
"so you can easily\n"
"update sdcard content.\n"
"You can add all the custom\n"
"files in root folders.\n"
"Continue anyway?",
YESNO,
[this, path](bool choice) {
if (choice) {
current_path /= path;
saved_index_stack.push_back(menu_view.highlighted_index());
menu_view.set_highlighted(0);
reload_current();
}
});
} else {
current_path /= path;
saved_index_stack.push_back(menu_view.highlighted_index());
@ -334,14 +378,33 @@ void FileManBaseView::push_dir(const fs::path& path) {
}
}
void FileManBaseView::pop_dir() {
if (saved_index_stack.empty())
return;
void FileManBaseView::push_fake_dir(const fs::path& path) {
// the one this accepted is just a flag (e.g. CAPTURE, instead of /SYS/CAPTURE nor /CAPTURE), not real dir
// after passing the flag here, this func will handle it automatically and make callback automatically
fs::path first_level = path.extract_first_level();
const fs::path default_mother_dir = user_dir;
current_path = current_path.parent_path();
reload_current(true);
menu_view.set_highlighted(saved_index_stack.back());
saved_index_stack.pop_back();
if (first_level != user_dir && first_level != system_dir) {
current_path = default_mother_dir / path;
saved_index_stack.push_back(
menu_view.highlighted_index()); // TODO: do we really want to allow user to redirect to other dir tho?
menu_view.set_highlighted(0);
reload_current();
}
}
void FileManBaseView::pop_dir() {
if (saved_index_stack.empty() && current_path == u"/") {
return;
} else if (saved_index_stack.empty()) {
current_path = u"";
reload_current();
} else {
current_path = current_path.parent_path();
reload_current();
menu_view.set_highlighted(saved_index_stack.back());
saved_index_stack.pop_back();
}
}
std::string get_extension(std::string t) {
@ -388,8 +451,10 @@ void FileManBaseView::refresh_list() {
}
menu_view.add_item(
{entry_name.substr(0, max_filename_length) + std::string(21 - entry_name.length(), ' ') + size_str,
ui::Color::yellow(),
{entry_name + std::string(21 - entry_name.length(), ' ') + size_str,
(entry.path == system_dir || entry.path == apps_dir || entry.path == firmware_dir) ? Color::red() : Color::yellow(),
&bitmap_icon_dir,
[this](KeyEvent key) {
if (on_select_entry)
@ -420,6 +485,31 @@ void FileManBaseView::reload_current(bool reset_pagination) {
refresh_list();
}
fs::path FileManBaseView::jumping_between_profiles(fs::path& path, DirProfiles profile) {
fs::path first_level = path.extract_first_level();
const fs::path null_path = u"";
if (profile == DirProfiles::User) {
if (first_level == system_dir) { // /SYS/abcdef
path = path.remove_first_level();
} else if (first_level == null_path) { // /abcdef
// this is for: after user redirected to other dir and then switch to user profile
path = user_dir;
}
} else if (profile == DirProfiles::System) {
if (first_level == null_path && path != system_dir) { // /abcdef
// the second argument is for preventing circuling enter system dir
path = system_dir / path;
} else if (first_level == system_dir) { // /SYS/abcdef
// this is for: after user redirected to other dir and then switch to system profile
path = system_dir;
}
}
return path;
}
const FileManBaseView::file_assoc_t& FileManBaseView::get_assoc(
const fs::path& ext) const {
size_t index = 0;
@ -547,7 +637,8 @@ void FileManagerView::refresh_widgets(const bool v) {
button_paste.hidden(v);
button_new_dir.hidden(v);
button_new_file.hidden(v);
option_profile_switch.hidden(!v);
label_profile.hidden(!v);
set_dirty();
}
@ -752,7 +843,8 @@ FileManagerView::FileManagerView(
} else {
text_date.set_style(&Styles::grey);
if (selected_is_valid())
text_date.set((is_directory(get_selected_full_path()) ? "Created " : "Modified ") + to_string_FAT_timestamp(file_created_date(get_selected_full_path())));
text_date.set((is_directory(get_selected_full_path()) ? "Created " : "Modified ") +
to_string_FAT_timestamp(file_created_date(get_selected_full_path())));
else
text_date.set("");
}
@ -862,5 +954,4 @@ FileManagerView::FileManagerView(
reload_current();
};
}
} // namespace ui

View File

@ -48,6 +48,11 @@ enum class ClipboardMode : uint8_t {
Copy
};
enum class DirProfiles : uint8_t {
User = 1,
System = 2
};
class FileManBaseView : public View {
public:
FileManBaseView(
@ -59,6 +64,7 @@ class FileManBaseView : public View {
void focus() override;
std::string title() const override { return "Fileman"; };
void push_dir(const std::filesystem::path& path);
void push_fake_dir(const std::filesystem::path& path);
protected:
uint32_t prev_highlight = 0;
@ -95,6 +101,9 @@ class FileManBaseView : public View {
void reload_current(bool reset_pagination = false);
void load_directory_contents(const std::filesystem::path& dir_path);
void load_directory_contents_unordered(const std::filesystem::path& dir_path, size_t file_cnt);
std::filesystem::path jumping_between_profiles(std::filesystem::path& path, DirProfiles profile);
const file_assoc_t& get_assoc(const std::filesystem::path& ext) const;
NavigationView& nav_;
@ -106,6 +115,7 @@ class FileManBaseView : public View {
const std::string str_back{"<--"};
const std::string str_next{"-->"};
const std::string str_full{"Can't load more.."};
const std::filesystem::path parent_dir_path{u".."};
std::filesystem::path current_path{u""};
std::filesystem::path extension_filter{u""};
@ -116,10 +126,10 @@ class FileManBaseView : public View {
bool show_hidden_files{false};
Labels labels{
{{0, 0}, "Path:", Color::light_grey()}};
{{0, 0}, "\u007F", Color::white()}};
Text text_current{
{6 * 8, 0 * 8, 24 * 8, 16},
{1 * 8, 0 * 8, 20 * 8, 16},
"",
};
@ -127,6 +137,16 @@ class FileManBaseView : public View {
{0, 2 * 8, 240, 26 * 8},
true};
OptionsField option_profile_switch{
{11 * 8, 32 * 8},
16,
{{"User Directory ", 1},
{"System Directory", 2}}};
Labels label_profile{
{{0 * 8, 32 * 8}, "Profile: ", Color::light_grey()},
{{9 * 8, 32 * 8}, "\u007F/", Color::white()}};
Button button_exit{
{22 * 8, 34 * 8, 8 * 8, 32},
"Exit"};

View File

@ -2,6 +2,7 @@
* Copyright (C) 2015 Jared Boone, ShareBrained Technology, Inc.
* Copyright (C) 2016 Furrtek
* Copyright (C) 2023 Kyle Reed
* copyleft (ɔ) 2024 zxkmm with the GPL license
*
* This file is part of PortaPack.
*
@ -96,7 +97,11 @@ void FreqManBaseView::focus() {
if (error_ == ERROR_ACCESS) {
nav_.display_modal("Error", "File access error", ABORT);
} else if (error_ == ERROR_NOFILES) {
nav_.display_modal("Error", "No database files\nin /FREQMAN", ABORT);
nav_.display_modal("Error",
"No database files\nin either" +
freqman_dir_user.string() +
"\nor" + freqman_dir_resources.string(),
ABORT);
} else {
options_category.focus();
}
@ -107,7 +112,15 @@ void FreqManBaseView::change_category(size_t new_index) {
return;
current_category_index = new_index;
if (!db_.open(get_freqman_path(current_category()))) {
current_is_system_item = false;
if (!db_.open(get_freqman_path(current_category(), dir_profile::ProfileUser))) {
current_is_system_item = true;
}
if (current_is_system_item && // <<< this means that entered previus condition, it's a work around to save 1 byte of ram
(!db_.open(
get_freqman_path(current_category(), dir_profile::ProfileSystem)))) {
error_ = ERROR_ACCESS;
}
@ -117,15 +130,20 @@ void FreqManBaseView::change_category(size_t new_index) {
void FreqManBaseView::refresh_categories() {
OptionsField::options_t new_categories;
scan_root_files(
freqman_dir, u"*.TXT", [&new_categories](const fs::path& path) {
// Skip temp/hidden files.
if (path.empty() || path.native()[0] == u'.')
return;
auto load_files = [&new_categories](const fs::path& dir) {
scan_root_files(
dir, u"*.TXT", [&new_categories](const fs::path& path) {
// Skip temp/hidden files.
if (path.empty() || path.native()[0] == u'.')
return;
// The UI layer will truncate long file names when displaying.
new_categories.emplace_back(path.stem().string(), new_categories.size());
});
// The UI layer will truncate long file names when displaying.
new_categories.emplace_back(path.stem().string(), new_categories.size());
});
};
load_files(freqman_dir_resources);
load_files(freqman_dir_user);
// Alphabetically sort the categories.
std::sort(new_categories.begin(), new_categories.end(), [](auto& left, auto& right) {
@ -163,9 +181,17 @@ FrequencySaveView::FrequencySaveView(
bind(field_description, entry_.description, nav);
button_save.on_select = [this, &nav](Button&) {
db_.insert_entry(db_.entry_count(), entry_);
nav_.pop();
button_save.on_select = [this, &nav](Button&) { // TODO: don't list system category here
if (current_is_system_item) {
nav.display_modal("Forbid",
"Can't save to\n"
"system category.\n"
"Please save to \n"
"a user category.");
} else {
db_.insert_entry(db_.entry_count(), entry_);
nav_.pop();
}
};
}
@ -238,6 +264,7 @@ void FrequencyManagerView::on_edit_desc() {
}
void FrequencyManagerView::on_add_category() {
ensure_directory(freqman_dir_user);
temp_buffer_.clear();
text_prompt(nav_, temp_buffer_, 20, [this](std::string& new_name) {
if (!new_name.empty()) {
@ -252,8 +279,8 @@ void FrequencyManagerView::on_del_category() {
"Delete", "Delete " + current_category() + "\nAre you sure?", YESNO,
[this](bool choice) {
if (choice) {
db_.close(); // Ensure file is closed.
auto path = get_freqman_path(current_category());
db_.close(); // Ensure file is closed.
auto path = get_freqman_path(current_category(), dir_profile::ProfileUser); // only allow del user's
delete_file(path);
refresh_categories();
}
@ -285,6 +312,19 @@ void FrequencyManagerView::on_del_entry() {
}
});
}
bool FrequencyManagerView::forbid_delete_system_item_helper(NavigationView& nav) {
// this is just a modal, however, it's been forbidden in those handler.
if (current_is_system_item) {
nav.display_modal("Forbid",
"Can't do that to \n"
"system item.\n"
"If you have to,\n"
"do it with File Manager");
}
return current_is_system_item;
}
FrequencyManagerView::FrequencyManagerView(
NavigationView& nav)
@ -309,31 +349,44 @@ FrequencyManagerView::FrequencyManagerView(
};
button_add_category.on_select = [this]() {
// this one will only directly add to /FREQMAN
on_add_category();
};
button_del_category.on_select = [this]() {
on_del_category();
button_del_category.on_select = [this, &nav]() {
if (!forbid_delete_system_item_helper(nav)) {
on_del_category();
}
};
button_edit_entry.on_select = [this](Button&) {
on_edit_entry();
button_edit_entry.on_select = [this, &nav](Button&) {
if (!forbid_delete_system_item_helper(nav)) {
on_edit_entry();
}
};
button_edit_freq.on_select = [this](Button&) {
on_edit_freq();
button_edit_freq.on_select = [this, &nav](Button&) {
if (!forbid_delete_system_item_helper(nav)) {
on_edit_freq();
}
};
button_edit_desc.on_select = [this](Button&) {
on_edit_desc();
button_edit_desc.on_select = [this, &nav](Button&) {
if (!forbid_delete_system_item_helper(nav)) {
on_edit_desc();
}
};
button_add_entry.on_select = [this]() {
on_add_entry();
button_add_entry.on_select = [this, &nav]() {
if (!forbid_delete_system_item_helper(nav)) {
on_add_entry();
}
};
button_del_entry.on_select = [this]() {
on_del_entry();
button_del_entry.on_select = [this, &nav]() {
if (!forbid_delete_system_item_helper(nav)) {
on_del_entry();
}
};
}

View File

@ -2,6 +2,7 @@
* Copyright (C) 2015 Jared Boone, ShareBrained Technology, Inc.
* Copyright (C) 2016 Furrtek
* Copyright (C) 2023 Kyle Reed
* copyleft (ɔ) 2024 zxkmm with the GPL license
*
* This file is part of PortaPack.
*
@ -123,6 +124,7 @@ class FrequencyManagerView : public FreqManBaseView {
std::string title() const override { return "Freqman"; }
private:
bool forbid_delete_system_item_helper(NavigationView& nav);
std::string temp_buffer_{};
void on_edit_entry();

View File

@ -38,6 +38,8 @@
#include "radio_state.hpp"
#include "pocsag_app.hpp"
#include "file_path.hpp"
#include <functional>
class FskRxLogger {
@ -160,7 +162,7 @@ class FskxRxMainView : public View {
RecordView record_view{
{0 * 8, 4 * 16, 30 * 8, 1 * 16},
u"FSKRX_????.C16",
u"FSKRX",
fskrx_dir_user,
RecordView::FileType::RawS16,
16384,
3};

View File

@ -48,7 +48,7 @@ IQTrimView::IQTrimView(NavigationView& nav)
field_path.on_select = [this](TextField&) {
auto open_view = nav_.push<FileLoadView>(".C*");
open_view->push_dir(captures_dir);
open_view->push_fake_dir(captures_dir);
open_view->on_changed = [this](fs::path path) {
open_file(path);
};

View File

@ -573,6 +573,8 @@ void GlassView::set_spec_iq_phase_calibration_value(uint8_t cal_value) { // def
void GlassView::load_presets() {
File presets_file;
auto error = presets_file.open(looking_glass_dir / u"PRESETS.TXT");
// TODO: this app originally ain't support user load list.
// should support, but ain't have funtionality loss in sdcard refactor PR.
presets_db.clear();
// Add the "Manual" entry.

View File

@ -131,7 +131,7 @@ void PlaylistView::open_file(bool prompt_save) {
}
auto open_view = nav_.push<FileLoadView>(".PPL");
open_view->push_dir(playlist_dir);
open_view->push_fake_dir(playlist_dir);
open_view->on_changed = [this](fs::path new_file_path) {
on_file_changed(new_file_path);
};
@ -170,7 +170,7 @@ void PlaylistView::save_file(bool show_dialogs) {
void PlaylistView::add_entry(fs::path&& path) {
if (playlist_path_.empty()) {
playlist_path_ = next_filename_matching_pattern(playlist_dir / u"PLAY_????.PPL");
playlist_path_ = next_filename_matching_pattern(playlist_dir_user / u"PLAY_????.PPL");
// Hack around focus getting called by ctor before parent is set.
if (parent())
@ -388,7 +388,7 @@ PlaylistView::PlaylistView(
&waterfall,
});
ensure_directory(playlist_dir);
ensure_fake_directories(playlist_dir);
waterfall.show_audio_spectrum_view(false);
field_frequency.set_value(transmitter_model.target_frequency());
@ -411,7 +411,7 @@ PlaylistView::PlaylistView(
if (is_active())
return;
auto open_view = nav_.push<FileLoadView>(".C*");
open_view->push_dir(captures_dir);
open_view->push_fake_dir(captures_dir);
open_view->on_changed = [this](fs::path path) {
add_entry(std::move(path));
};

View File

@ -337,7 +337,7 @@ ReconView::ReconView(NavigationView& nav)
// set record View
record_view = std::make_unique<RecordView>(Rect{0, 0, 30 * 8, 1 * 16},
u"AUTO_AUDIO", audio_dir,
u"AUTO_AUDIO", audio_dir_user,
RecordView::FileType::WAV, 4096, 4);
record_view->set_filename_date_frequency(true);
record_view->set_auto_trim(false);
@ -680,7 +680,7 @@ ReconView::ReconView(NavigationView& nav)
open_view->on_changed = [this](std::vector<std::string> result) {
input_file = result[0];
output_file = result[1];
freq_file_path = get_freqman_path(output_file).string();
freq_file_path = get_freqman_path(output_file, dir_profile::ProfileUser).string();
load_persisted_settings();
ui_settings.save();
@ -731,7 +731,7 @@ ReconView::ReconView(NavigationView& nav)
file_name.set("=>");
// Loading input and output file from settings
freq_file_path = get_freqman_path(output_file).string();
freq_file_path = get_freqman_path(output_file, dir_profile::ProfileUser).string();
field_recon_match_mode.set_selected_index(recon_match_mode);
field_squelch.set_value(squelch);
@ -790,7 +790,10 @@ void ReconView::frequency_file_load() {
.load_ranges = load_ranges,
.load_hamradios = load_hamradios,
.load_repeaters = load_repeaters};
if (!load_freqman_file(file_input, frequency_list, options) || frequency_list.empty()) {
if (
(!load_freqman_file(file_input, frequency_list, options, dir_profile::ProfileSystem) &&
!load_freqman_file(file_input, frequency_list, options, dir_profile::ProfileUser)) ||
frequency_list.empty()) {
file_name.set_style(&Styles::red);
desc_cycle.set("...empty file...");
frequency_list.clear();
@ -1170,18 +1173,18 @@ size_t ReconView::change_mode(freqman_index_t new_mod) {
if (new_mod == SPEC_MODULATION) {
if (persistent_memory::recon_repeat_recorded()) {
record_view = std::make_unique<RecordView>(Rect{0, 0, 30 * 8, 1 * 16},
u"RECON_REPEAT.C16", captures_dir,
u"RECON_REPEAT.C16", captures_dir_user,
RecordView::FileType::RawS16, 16384, 3);
record_view->set_filename_as_is(true);
} else {
record_view = std::make_unique<RecordView>(Rect{0, 0, 30 * 8, 1 * 16},
u"AUTO_RAW", captures_dir,
u"AUTO_RAW", captures_dir_user,
RecordView::FileType::RawS16, 16384, 3);
record_view->set_filename_date_frequency(true);
}
} else {
record_view = std::make_unique<RecordView>(Rect{0, 0, 30 * 8, 1 * 16},
u"AUTO_AUDIO", audio_dir,
u"AUTO_AUDIO", audio_dir_user,
RecordView::FileType::WAV, 4096, 4);
record_view->set_filename_date_frequency(true);
}

View File

@ -182,6 +182,7 @@ class ReconView : public View {
const std::filesystem::path repeat_rec_file = u"RECON_REPEAT.C16";
const std::filesystem::path repeat_rec_meta = u"RECON_REPEAT.TXT";
// tempnote: path const var removed
const size_t repeat_read_size{16384};
const size_t repeat_buffer_count{3};
int8_t repeat_cur_rep = 0;

View File

@ -2,6 +2,7 @@
* Copyright (C) 2015 Jared Boone, ShareBrained Technology, Inc.
* Copyright (C) 2016 Furrtek
* Copyright (C) 2023 gullradriel, Nilorea Studio Inc.
* Copyleft (ɔ) 2024 zxkmm with the GPL license
*
* This file is part of PortaPack.
*
@ -57,26 +58,30 @@ ReconSetupViewMain::ReconSetupViewMain(NavigationView& nav, Rect parent_rect, st
button_input_file.on_select = [this, &nav](Button&) {
auto open_view = nav.push<FileLoadView>(".TXT");
open_view->push_dir(freqman_dir);
open_view->push_fake_dir(freqman_dir); // the argu that push fake dir accepted is just a flag, so can safely hard coded
open_view->on_changed = [this, &nav](std::filesystem::path new_file_path) {
if (new_file_path.native().find((u"/" / freqman_dir).native()) == 0) {
if ((new_file_path.native().find((u"/" / freqman_dir_resources).native()) == 0) || new_file_path.native().find((freqman_dir_user).native()) == 0) {
_input_file = new_file_path.stem().string();
text_input_file.set(_input_file);
} else {
nav.display_modal("LOAD ERROR", "A valid file from\nFREQMAN directory is\nrequired.");
nav.display_modal("LOAD ERROR", "A valid file from\n" +
freqman_dir_user.string() +
"\nor\n" +
freqman_dir_resources.string() +
"\ndirectories is\nrequired.");
}
};
};
button_choose_output_file.on_select = [this, &nav](Button&) {
auto open_view = nav.push<FileLoadView>(".TXT");
open_view->push_dir(freqman_dir);
open_view->push_fake_dir(freqman_dir); // the argu that push fake dir accepted is just a flag, so can safely hard coded
open_view->on_changed = [this, &nav](std::filesystem::path new_file_path) {
if (new_file_path.native().find((u"/" / freqman_dir).native()) == 0) {
if (new_file_path.native().find((freqman_dir_user).native()) == 0) {
_output_file = new_file_path.stem().string();
button_choose_output_name.set_text(_output_file);
} else {
nav.display_modal("SAVE ERROR", "A valid file from\nFREQMAN directory is\nrequired.");
nav.display_modal("SAVE ERROR", "A valid file from\n" + freqman_dir_user.string() + "\ndirectories is\nrequired.");
}
};
};

View File

@ -60,7 +60,7 @@
// screen size helper
#define SCREEN_W 240
//#define SCREEN_H 320
// #define SCREEN_H 320
// recon settings nb params
#define RECON_SETTINGS_NB_PARAMS 7

View File

@ -247,7 +247,7 @@ RemoteEntryEditView::RemoteEntryEditView(
field_path.on_select = [this, &nav](TextField&) {
auto open_view = nav.push<FileLoadView>(".C*");
open_view->push_dir(captures_dir);
open_view->push_fake_dir(captures_dir);
open_view->on_changed = [this](fs::path path) {
load_path(std::move(path));
refresh_ui();
@ -356,7 +356,7 @@ RemoteView::RemoteView(
Dim waterfall_height = waterfall_bottom - waterfall_top;
waterfall.set_parent_rect({0, waterfall_top, screen_width, waterfall_height});
ensure_directory(remotes_dir);
ensure_fake_directories(remotes_dir);
// Load the previously loaded remote if exists.
if (!load_remote(settings_.remote_path))
@ -528,7 +528,7 @@ void RemoteView::new_remote() {
void RemoteView::open_remote() {
auto open_view = nav_.push<FileLoadView>(".REM");
open_view->push_dir(remotes_dir);
open_view->push_fake_dir(remotes_dir);
open_view->on_changed = [this](fs::path path) {
save_remote();
load_remote(std::move(path));
@ -539,7 +539,7 @@ void RemoteView::open_remote() {
void RemoteView::init_remote() {
model_ = {"<Unnamed Remote>", {}};
reset_buttons();
set_remote_path(next_filename_matching_pattern(remotes_dir / u"REMOTE_????.REM"));
set_remote_path(next_filename_matching_pattern(remotes_dir_user / u"REMOTE_????.REM"));
set_needs_save(false);
if (remote_path_.empty())

View File

@ -2,6 +2,7 @@
* Copyright (C) 2015 Jared Boone, ShareBrained Technology, Inc.
* Copyright (C) 2018 Furrtek
* Copyright (C) 2023 Mark Thompson
* Copyleft (ɔ) 2024 zxkmm with the GPL license
*
* This file is part of PortaPack.
*
@ -336,13 +337,13 @@ ScannerView::ScannerView(
// Button to load a Freqman file.
button_load.on_select = [this, &nav](Button&) {
auto open_view = nav.push<FileLoadView>(".TXT");
open_view->push_dir(freqman_dir);
open_view->push_fake_dir(freqman_dir); // the argu that push fake dir accepted is just a flag, so can safely hard coded
open_view->on_changed = [this, &nav](std::filesystem::path new_file_path) {
if (new_file_path.native().find((u"/" / freqman_dir).native()) == 0) {
if ((new_file_path.native().find((u"/" / freqman_dir_resources).native()) == 0) || new_file_path.native().find((freqman_dir_user).native()) == 0) {
scan_pause();
frequency_file_load(new_file_path);
} else {
nav.display_modal("LOAD ERROR", "A valid file from\nFREQMAN directory is\nrequired.");
nav.display_modal("LOAD ERROR", "A valid file from\nFREQMAN directories is\nrequired.");
}
};
};
@ -491,7 +492,7 @@ ScannerView::ScannerView(
// Button to add current frequency (found during Search) to the Scan Frequency List
button_add.on_select = [this](Button&) {
FreqmanDB db;
if (db.open(get_freqman_path(freqman_file), /*create*/ true)) {
if (db.open(get_freqman_path(freqman_file, dir_profile::ProfileUser), /*create*/ true)) {
freqman_entry entry{
.frequency_a = current_frequency,
.type = freqman_type::Single,
@ -537,7 +538,7 @@ ScannerView::ScannerView(
receiver_model.set_squelch_level(0);
// LOAD FREQUENCIES
frequency_file_load(get_freqman_path(freqman_file));
frequency_file_load(get_freqman_path(freqman_file, dir_profile::ProfileUser));
}
void ScannerView::frequency_file_load(const fs::path& path) {

View File

@ -38,7 +38,7 @@
#define SCANNER_SLEEP_MS 50 // ms that Scanner Thread sleeps per loop
#define STATISTICS_UPDATES_PER_SEC 10
#define MAX_FREQ_LOCK 10 //# of 50ms cycles scanner locks into freq when signal detected, to verify signal is not spurious
#define MAX_FREQ_LOCK 10 // # of 50ms cycles scanner locks into freq when signal detected, to verify signal is not spurious
namespace ui {

View File

@ -5,7 +5,7 @@
* Copyright (C) 2023 Kyle Reed
* Copyright (C) 2024 Mark Thompson
* Copyright (C) 2024 u-foka
* Copyleft (ɔ) 2024 zxkmm under GPL license
* Copyleft (ɔ) 2024 zxkmm with the GPL license
*
* This file is part of PortaPack.
*

View File

@ -5,7 +5,7 @@
* Copyright (C) 2023 Kyle Reed
* Copyright (C) 2024 Mark Thompson
* Copyright (C) 2024 u-foka
* Copyleft (ɔ) 2024 zxkmm under GPL license
* Copyleft (ɔ) 2024 zxkmm with the GPL license
*
* This file is part of PortaPack.
*

View File

@ -27,7 +27,7 @@ namespace fs = std::filesystem;
namespace ui {
const std::filesystem::path splash_dot_bmp{u"/splash.bmp"};
const std::filesystem::path current_using_splash_image{u"/splash.bmp"};
ScreenshotViewer::ScreenshotViewer(
NavigationView& nav,
@ -108,8 +108,8 @@ SplashViewer::SplashViewer(
bool SplashViewer::on_key(const KeyEvent key) {
if (valid_image && key == KeyEvent::Right) {
delete_file(splash_dot_bmp);
copy_file(path_, splash_dot_bmp);
delete_file(current_using_splash_image);
copy_file(path_, current_using_splash_image);
}
nav_.pop();
@ -125,7 +125,7 @@ void SplashViewer::paint(Painter& painter) {
}
// Show option to set splash screen if it's not already the splash screen
if (!path_iequal(path_, splash_dot_bmp)) {
if (!path_iequal(path_, current_using_splash_image)) {
painter.draw_string({0, 0}, Styles::white, "*RIGHT BUTTON UPDATES SPLASH*");
valid_image = true;
}

View File

@ -32,7 +32,7 @@
namespace ui {
extern const std::filesystem::path splash_dot_bmp;
extern const std::filesystem::path current_using_splash_image;
class ScreenshotViewer : public View {
public:

View File

@ -25,6 +25,7 @@
#include "portapack.hpp"
#include "hackrf_hal.hpp"
#include "file_path.hpp"
#include <cstring>
#include <stdio.h>
@ -35,7 +36,13 @@ namespace ui {
void SSTVTXView::focus() {
if (file_error)
nav_.display_modal("No files", "No valid bitmaps\nin /sstv directory.", ABORT);
nav_.display_modal("No files",
"No valid bitmaps\nin either" +
sstv_dir.string() +
"\nor\n" +
sstv_dir_resources.string() +
"directory.",
ABORT);
else
options_bitmaps.focus();
}
@ -172,7 +179,10 @@ void SSTVTXView::start_tx() {
}
void SSTVTXView::on_bitmap_changed(const size_t index) {
bmp_file.open("/sstv/" + bitmaps[index].string());
auto open_system_dir = bmp_file.open(sstv_dir_user + u"/" + bitmaps[index].string());
if (!open_system_dir->ok()) {
bmp_file.open(sstv_dir_resources + u"/" + bitmaps[index].string());
}
bmp_file.read(&bmp_header, sizeof(bmp_header));
set_dirty();
}
@ -200,31 +210,47 @@ SSTVTXView::SSTVTXView(
NavigationView& nav)
: nav_(nav) {
std::vector<std::filesystem::path> file_list;
std::filesystem::path file_list_index[2];
using option_t = std::pair<std::string, int32_t>;
using options_t = std::vector<option_t>;
options_t bitmap_options;
options_t mode_options;
uint32_t c;
// Search for valid bitmaps
file_list = scan_root_files(u"/sstv", u"*.bmp");
if (!file_list.size()) {
file_error = true;
return;
}
for (const auto& file_name : file_list) {
if (!bmp_file.open("/sstv/" + file_name.string()).is_valid()) {
bmp_file.read(&bmp_header, sizeof(bmp_header));
if ((bmp_header.signature == 0x4D42) && // "BM"
(bmp_header.width == 320) && // Must be exactly 320x256 pixels for now
(bmp_header.height == 256) &&
(bmp_header.planes == 1) &&
(bmp_header.bpp == 24) && // 24 bpp only
(bmp_header.compression == 0)) { // No compression
bitmaps.push_back(file_name);
file_list_index[0] = sstv_dir_user;
file_list_index[1] = u"/" + sstv_dir_resources;
bool found_files = false;
for (const auto& now_path : file_list_index) {
file_list = scan_root_files(now_path, u"*.bmp");
if (!file_list.size()) {
continue; // Skip to the next path if no files found
}
found_files = true; // Mark that we found some files
for (const auto& file_name : file_list) {
if (!bmp_file.open(now_path / file_name).is_valid()) {
bmp_file.read(&bmp_header, sizeof(bmp_header));
if ((bmp_header.signature == 0x4D42) && // "BM"
(bmp_header.width == 320) && // Must be exactly 320x256 pixels for now
(bmp_header.height == 256) &&
(bmp_header.planes == 1) &&
(bmp_header.bpp == 24) && // 24 bpp only
(bmp_header.compression == 0)) { // No compression
bitmaps.push_back(file_name);
}
}
}
}
if (!found_files) {
file_error = true;
return;
}
if (!bitmaps.size()) {
file_error = true;
return;

View File

@ -80,8 +80,14 @@ class SSTVTXView : public View {
void prepare_scanline();
Labels labels{
{{1 * 8, 1 * 8}, "File:", Color::light_grey()},
{{1 * 8, 3 * 8}, "Mode:", Color::light_grey()}};
{{1 * 8, 1 * 8}, "Profile:", Color::light_grey()},
{{1 * 8, 3 * 8}, "File:", Color::light_grey()},
{{1 * 8, 5 * 8}, "Mode:", Color::light_grey()}};
OptionsField options_profile{
{6 * 8, 1 * 8},
16,
{}};
OptionsField options_bitmaps{
{6 * 8, 1 * 8},

View File

@ -144,7 +144,6 @@ WhipCalcView::WhipCalcView(NavigationView& nav)
void WhipCalcView::load_antenna_db() {
File antennas_file;
auto error = antennas_file.open(whipcalc_dir / u"ANTENNAS.TXT");
if (error)
return;

View File

@ -42,6 +42,7 @@ SpectrumInputImageView::SpectrumInputImageView(NavigationView& nav) {
ensure_directory(spectrum_dir);
open_view->push_dir(spectrum_dir);
// tempnewnote: const var removed
open_view->on_changed = [this](std::filesystem::path new_file_path) {
this->file = new_file_path.string();

View File

@ -1,6 +1,7 @@
/*
* Copyright (C) 2015 Jared Boone, ShareBrained Technology, Inc.
* Copyright (C) 2016 Furrtek
* Copyleft (ɔ) 2024 zxkmm with the GPL license
*
* This file is part of PortaPack.
*
@ -22,6 +23,7 @@
#include "file.hpp"
#include "complex.hpp"
#include "file_path.hpp"
#include <algorithm>
#include <codecvt>
@ -368,6 +370,15 @@ std::filesystem::filesystem_error ensure_directory(
return make_new_directory(dir_path);
}
void ensure_fake_directories(
const std::filesystem::path& dir_path) {
std::filesystem::path combined_user_dir = user_dir / dir_path;
std::filesystem::path combimed_system_dir = system_dir / dir_path;
ensure_directory(combimed_system_dir);
ensure_directory(combined_user_dir);
}
namespace std {
namespace filesystem {
@ -454,6 +465,49 @@ path path::filename() const {
}
}
path path::remove_first_level() const { // /abc/def/ghi/hjk.q to /def/ghi/hjk.q
std::size_t start_index = _s.find(preferred_separator);
if (start_index == _s.npos) {
return _s;
} else {
start_index += 1; // To move past the first slash
std::size_t end_index = _s.find(preferred_separator, start_index);
if (end_index == _s.npos) {
end_index = _s.length(); // If there's no more slashes, end at the end of the string
}
return _s.substr(end_index);
}
}
path path::extract_first_level() const { // /abc/def/ghi/hjk.q to /abc
const auto first_separator = _s.find_first_of(preferred_separator);
if (first_separator == _s.npos) {
return _s;
} else {
const auto second_separator = _s.find_first_of(preferred_separator, first_separator + 1);
if (second_separator == _s.npos) {
return _s.substr(0, first_separator);
} else {
return _s.substr(0, second_separator);
}
}
}
path path::absolute_trimmed_path() const {
basic_string<char16_t> path_str = _s.substr();
if (path_str.front() == '/') {
path_str.erase(0, 1);
}
if (path_str.back() == '/') {
path_str.pop_back();
}
return path{path_str};
}
path path::stem() const {
const auto t = filename().native();
const auto index = t.find_last_of(u'.');

View File

@ -1,6 +1,7 @@
/*
* Copyright (C) 2015 Jared Boone, ShareBrained Technology, Inc.
* Copyright (C) 2016 Furrtek
* Copyleft (ɔ) 2024 zxkmm with the GPL license
*
* This file is part of PortaPack.
*
@ -116,6 +117,9 @@ struct path {
path parent_path() const;
path extension() const;
path filename() const;
path remove_first_level() const;
path extract_first_level() const;
path absolute_trimmed_path() const;
path stem() const;
bool empty() const {
@ -276,6 +280,7 @@ std::filesystem::filesystem_error file_update_date(const std::filesystem::path&
std::filesystem::filesystem_error make_new_file(const std::filesystem::path& file_path);
std::filesystem::filesystem_error make_new_directory(const std::filesystem::path& dir_path);
std::filesystem::filesystem_error ensure_directory(const std::filesystem::path& dir_path);
void ensure_fake_directories(const std::filesystem::path& dir_path);
template <typename TCallback>
void scan_root_files(const std::filesystem::path& directory, const std::filesystem::path& extension, const TCallback& fn) {

View File

@ -1,5 +1,6 @@
/*
* Copyright (C) 2024 Mark Thompson
* copyleft (ɔ) 2024 zxkmm with the GPL license
*
* This file is part of PortaPack.
*
@ -22,28 +23,104 @@
#include "file_path.hpp"
#include "file.hpp"
const std::filesystem::path adsb_dir = u"ADSB";
const std::filesystem::path ais_dir = u"AIS";
const std::filesystem::path apps_dir = u"APPS";
const std::filesystem::path aprs_dir = u"APRS";
const std::filesystem::path audio_dir = u"AUDIO";
const std::filesystem::path blerx_dir = u"BLERX";
const std::filesystem::path bletx_dir = u"BLETX";
const std::filesystem::path captures_dir = u"CAPTURES";
const std::filesystem::path debug_dir = u"DEBUG";
const std::filesystem::path firmware_dir = u"FIRMWARE";
const std::filesystem::path freqman_dir = u"FREQMAN";
const std::filesystem::path gps_dir = u"GPS";
const std::filesystem::path logs_dir = u"LOGS";
const std::filesystem::path looking_glass_dir = u"LOOKINGGLASS";
const std::filesystem::path playlist_dir = u"PLAYLIST";
const std::filesystem::path remotes_dir = u"REMOTES";
const std::filesystem::path repeat_rec_path = u"CAPTURES";
const std::filesystem::path samples_dir = u"SAMPLES";
const std::filesystem::path screenshots_dir = u"SCREENSHOTS";
const std::filesystem::path settings_dir = u"SETTINGS";
const std::filesystem::path spectrum_dir = u"SPECTRUM";
const std::filesystem::path splash_dir = u"SPLASH";
const std::filesystem::path sstv_dir = u"SSTV";
const std::filesystem::path wav_dir = u"WAV";
const std::filesystem::path whipcalc_dir = u"WHIPCALC";
const std::filesystem::path adsb_dir = u"SYS/ADSB"; // this dir no need to seperate profiles since all are resources
const std::filesystem::path ais_dir = u"SYS/AIS"; // this dir no need to seperate profiles since all are resources
const std::filesystem::path apps_dir = u"APPS"; // currently in root
// APRS
const std::filesystem::path aprs_dir = u"APRS"; // fake path
const std::filesystem::path aprs_dir_user = u"/APRS";
const std::filesystem::path aprs_dir_resources = u"SYS/APRS";
// AUDIO
const std::filesystem::path audio_dir = u"AUDIO"; // fake path
const std::filesystem::path audio_dir_user = u"/AUDIO";
const std::filesystem::path audio_dir_resources = u"SYS/AUDIO";
// BLERX
const std::filesystem::path blerx_dir = u"BLERX"; // fake path
const std::filesystem::path blerx_dir_user = u"/BLERX";
const std::filesystem::path blerx_dir_resources = u"SYS/BLERX";
// BLETX
const std::filesystem::path bletx_dir = u"BLETX"; // fake path
const std::filesystem::path bletx_dir_user = u"/BLETX";
const std::filesystem::path bletx_dir_resources = u"SYS/BLETX";
// CAPTURES
const std::filesystem::path captures_dir = u"CAPTURES"; // fake path
const std::filesystem::path captures_dir_user = u"/CAPTURES"; // used by recon and capture apps to save capture C16 into USR
const std::filesystem::path captures_dir_resources = u"SYS/CAPTURES";
const std::filesystem::path debug_dir = u"DEBUG"; // in root
const std::filesystem::path firmware_dir = u"FIRMWARE"; // in root
// FREQMAN
const std::filesystem::path freqman_dir = u"FREQMAN"; // fake path
const std::filesystem::path freqman_dir_user = u"/FREQMAN";
const std::filesystem::path freqman_dir_resources = u"SYS/FREQMAN";
const std::filesystem::path fskrx_dir_user = u"/FSKRX";
const std::filesystem::path gps_dir = u"/GPS";
const std::filesystem::path logs_dir = u"/LOGS";
const std::filesystem::path looking_glass_dir = u"SYS/LOOKINGGLASS";
// PLAYLIST
const std::filesystem::path playlist_dir = u"PLAYLIST"; // fake dir
const std::filesystem::path playlist_dir_user = u"/PLAYLIST"; // used by playlist aka replay app, to save nre created PPL files into USR
const std::filesystem::path playlist_dir_resources = u"SYS/PLAYLIST"; //
// REMOTE
const std::filesystem::path remotes_dir = u"REMOTES"; // fake dir
const std::filesystem::path remotes_dir_user = u"/REMOTES";
const std::filesystem::path remotes_dir_resources = u"SYS/REMOTES";
// recon app
const std::filesystem::path repeat_rec_path = u"/CAPTURES"; // this is for recon captures files
const std::filesystem::path samples_dir = u"SYS/CAPTURES"; // TODO: rename var name
const std::filesystem::path screenshots_dir = u"/SCREENSHOTS"; // TODO: rename var name to screenshots_dir
const std::filesystem::path settings_dir = u"/SETTINGS";
const std::filesystem::path spectrum_dir = u"SPECTRUM"; // fake dir
const std::filesystem::path splash_dir = u"/SPLASH";
const std::filesystem::path sstv_dir = u"SSTV"; // fake path
const std::filesystem::path sstv_dir_user = u"/SSTV";
const std::filesystem::path sstv_dir_resources = u"SYS/SSTV";
const std::filesystem::path system_dir = u"/SYS"; // in root
const std::filesystem::path user_dir = u"/"; // root
// WAV
const std::filesystem::path wav_dir = u"WAV"; // fake path
const std::filesystem::path wav_dir_user = u"/WAV";
const std::filesystem::path wav_dir_resources = u"SYS/WAV";
const std::filesystem::path whipcalc_dir = u"SYS/WHIPCALC";
// vector
const std::vector<std::string> allow_dirs = {
"ADSB",
"AIS",
"BLETX",
"GPS",
"LOOKINGGLASS",
"PLAYLIST",
"REMOTES",
"SPLASH",
"SSTV",
"WAV",
"WHIPCALC",
"SAMPLES",
};

View File

@ -1,5 +1,6 @@
/*
* Copyright (C) 2024 Mark Thompson
* copyleft (ɔ) 2024 zxkmm with the GPL license
*
* This file is part of PortaPack.
*
@ -25,29 +26,83 @@
#include "file.hpp"
extern const std::filesystem::path adsb_dir;
extern const std::filesystem::path ais_dir;
extern const std::filesystem::path apps_dir;
extern const std::filesystem::path aprs_dir;
extern const std::filesystem::path aprs_dir_user;
extern const std::filesystem::path aprs_dir_resources;
extern const std::filesystem::path audio_dir;
extern const std::filesystem::path audio_dir_user;
extern const std::filesystem::path audio_dir_resources;
extern const std::filesystem::path blerx_dir;
extern const std::filesystem::path blerx_dir_user;
extern const std::filesystem::path blerx_dir_resources;
extern const std::filesystem::path bletx_dir;
extern const std::filesystem::path bletx_dir_user;
extern const std::filesystem::path bletx_dir_resources;
extern const std::filesystem::path captures_dir;
extern const std::filesystem::path captures_dir_user;
extern const std::filesystem::path captures_dir_resources;
extern const std::filesystem::path debug_dir;
extern const std::filesystem::path firmware_dir;
extern const std::filesystem::path freqman_dir;
extern const std::filesystem::path freqman_dir_user;
extern const std::filesystem::path freqman_dir_resources;
extern const std::filesystem::path fskrx_dir_user;
extern const std::filesystem::path gps_dir;
extern const std::filesystem::path logs_dir;
extern const std::filesystem::path looking_glass_dir;
extern const std::filesystem::path playlist_dir;
extern const std::filesystem::path playlist_dir_user;
extern const std::filesystem::path playlist_dir_resources;
extern const std::filesystem::path remotes_dir;
extern const std::filesystem::path remotes_dir_user;
extern const std::filesystem::path remotes_dir_resources;
extern const std::filesystem::path repeat_rec_path;
extern const std::filesystem::path samples_dir;
extern const std::filesystem::path screenshots_dir;
extern const std::filesystem::path settings_dir;
extern const std::filesystem::path spectrum_dir;
extern const std::filesystem::path splash_dir;
extern const std::filesystem::path sstv_dir;
extern const std::filesystem::path sstv_dir_user;
extern const std::filesystem::path sstv_dir_resources;
extern const std::filesystem::path system_dir;
extern const std::filesystem::path user_dir;
extern const std::filesystem::path wav_dir;
extern const std::filesystem::path wav_dir_user;
extern const std::filesystem::path wav_dir_resources;
extern const std::filesystem::path whipcalc_dir;
extern const std::vector<std::string> allow_dirs;
#endif /* __FILE_PATH_H__ */

View File

@ -3,6 +3,8 @@
* Copyright (C) 2016 Furrtek
* Copyright (C) 2023 gullradriel, Nilorea Studio Inc.
* Copyright (C) 2023 Kyle Reed
* copyleft (ɔ) 2024 zxkmm with the GPL license
*
*
* This file is part of PortaPack.
*
@ -39,6 +41,7 @@
namespace fs = std::filesystem;
const std::filesystem::path freqman_extension{u".TXT"};
bool current_is_system_item{true};
// NB: Don't include UI headers to keep this code unit testable.
using option_t = std::pair<std::string, int32_t>;
@ -218,21 +221,32 @@ std::string freqman_entry_get_step_string_short(freqman_index_t step) {
return {};
}
const std::filesystem::path get_freqman_path(const std::string& stem) {
return freqman_dir / stem + freqman_extension;
const std::filesystem::path get_freqman_path(const std::string& stem, dir_profile profile) {
switch (profile) {
case dir_profile::ProfileSystem:
return freqman_dir_resources / stem + freqman_extension;
break;
case dir_profile::ProfileUser:
return freqman_dir_user / stem + freqman_extension;
break;
default:
// throw ?
return freqman_dir_user / stem + freqman_extension;
}
}
bool create_freqman_file(const std::string& file_stem) {
auto fs_error = make_new_file(get_freqman_path(file_stem));
// always create in user dir, so no judgement here
auto fs_error = make_new_file(get_freqman_path(file_stem, dir_profile::ProfileUser)); // only allow create on usr dir
return fs_error.ok();
}
bool load_freqman_file(const std::string& file_stem, freqman_db& db, freqman_load_options options) {
return parse_freqman_file(get_freqman_path(file_stem), db, options);
bool load_freqman_file(const std::string& file_stem, freqman_db& db, freqman_load_options options, dir_profile profile) {
return parse_freqman_file(get_freqman_path(file_stem, profile), db, options);
}
void delete_freqman_file(const std::string& file_stem) {
delete_file(get_freqman_path(file_stem));
delete_file(get_freqman_path(file_stem, dir_profile::ProfileUser)); // don't allow to delete sys files
}
std::string pretty_string(const freqman_entry& entry, size_t max_length) {

View File

@ -3,6 +3,7 @@
* Copyright (C) 2016 Furrtek
* Copyright (C) 2023 gullradriel, Nilorea Studio Inc.
* Copyright (C) 2023 Kyle Reed
* copyleft (ɔ) 2024 zxkmm with the GPL license
*
* This file is part of PortaPack.
*
@ -37,6 +38,7 @@
/* Defined in freqman_db.cpp */
extern const std::filesystem::path freqman_extension;
extern bool current_is_system_item;
using freqman_index_t = uint8_t;
constexpr freqman_index_t freqman_invalid_index = static_cast<freqman_index_t>(-1);
@ -60,6 +62,11 @@ enum class freqman_type : uint8_t {
Unknown,
};
enum class dir_profile : uint8_t {
ProfileUser = 1,
ProfileSystem = 2
};
/* Tables for next round of changes.
* // TODO: attempt to consolidate all of these values
* // and settings into a single location.
@ -177,9 +184,9 @@ using freqman_entry_ptr = std::unique_ptr<freqman_entry>;
using freqman_db = std::vector<freqman_entry_ptr>;
/* Gets the full path for a given file stem (no extension). */
const std::filesystem::path get_freqman_path(const std::string& stem);
const std::filesystem::path get_freqman_path(const std::string& stem, dir_profile profile);
bool create_freqman_file(const std::string& file_stem);
bool load_freqman_file(const std::string& file_stem, freqman_db& db, freqman_load_options options);
bool load_freqman_file(const std::string& file_stem, freqman_db& db, freqman_load_options options, dir_profile profile);
void delete_freqman_file(const std::string& file_stem);
/* Gets a pretty string representation for an entry. */

View File

@ -1,6 +1,6 @@
/*
* Copyright (C) 2015 Jared Boone, ShareBrained Technology, Inc.
* copyleft (ɔ) 2023 zxkmm under GPL license
* copyleft (ɔ) 2023 zxkmm with the GPL license
* Copyright (C) 2023 u-foka
*
* This file is part of PortaPack.

View File

@ -1,6 +1,6 @@
/*
* Copyright (C) 2015 Jared Boone, ShareBrained Technology, Inc.
* copyleft (ɔ) 2023 zxkmm under GPL license
* copyleft (ɔ) 2023 zxkmm with the GPL license
* Copyright (C) 2023 u-foka
*
* This file is part of PortaPack.

View File

@ -2,7 +2,7 @@
* Copyright (C) 2015 Jared Boone, ShareBrained Technology, Inc.
* Copyright (C) 2016 Furrtek
* Copyright (C) 2024 u-foka
* Copyleft (ɔ) 2024 zxkmm under GPL license
* Copyleft (ɔ) 2024 zxkmm with the GPL license
*
* This file is part of PortaPack.
*
@ -465,6 +465,11 @@ void SystemStatusView::set_back_hidden(bool new_value) {
void SystemStatusView::set_title_image_enabled(bool new_value) {
if (new_value) {
add_child(&button_title);
if (!already_shown_sdcard_warning_in_this_boot) {
// the modal warning can't be called from constructor since the nav isn't valid at that time, so putting here as a work around
new_sdcard_structure_checker();
already_shown_sdcard_warning_in_this_boot = true;
}
} else {
remove_child(&button_title);
}
@ -599,6 +604,93 @@ void SystemStatusView::rtc_battery_workaround() {
}
}
void SystemStatusView::new_sdcard_structure_checker() {
const std::filesystem::path root_dir = u"/";
const std::filesystem::path resources_dir = u"SYS";
std::vector<std::filesystem::path> directories = scan_root_directories(root_dir);
auto scan_result = std::find(directories.begin(), directories.end(), resources_dir);
auto sd_status = sd_card::status();
if (
nav_.is_valid() &&
(scan_result == directories.end()) &&
(sd_status == sd_card::Status::Mounted)) {
nav_.display_modal(
"Warning",
" WARNING!!!\n"
"SD Card content changed\n"
"in this update,\n"
"Press YES to auto move.\n"
"After finished,\n"
"you should move your own\n"
"files back into\n"
"original folders,\n"
"which are now only for users.\n"
"You can also choose NO,\n"
"and then manually merge\n"
"following the Wiki.\n"
"Details:\n"
"Mayhem wiki - Updating",
YESNO,
[this](bool choice) {
if (choice) {
new_sdcard_structure_worker();
}
},
true);
}
}
void SystemStatusView::new_sdcard_structure_worker() {
const std::filesystem::path root_dir = u"/";
const std::filesystem::path system_dir = u"SYS";
/// worker log
std::filesystem::path filename{};
File automove_dump_file{};
ensure_directory(logs_dir);
filename = next_filename_matching_pattern(logs_dir + "/AUTOMOVE_LOG_????.TXT");
automove_dump_file.create(filename);
std::vector<std::filesystem::path> root_dirs = scan_root_directories(root_dir);
std::vector<std::string> scan_result_string_vec;
for (const auto& e : root_dirs) {
scan_result_string_vec.push_back(e.string());
}
std::string scan_result_string = "";
for (const auto& e : scan_result_string_vec) {
scan_result_string += e + "\n";
}
automove_dump_file.write_line(scan_result_string + "\n\n");
/// worker moving folders
ensure_directory(system_dir);
auto directories = scan_root_directories(root_dir);
for (const auto& e : root_dirs) {
for (const auto& d : allow_dirs) {
if (e.absolute_trimmed_path().string() == d) {
std::filesystem::path new_path = system_dir / d;
rename_file(e, new_path);
automove_dump_file.write_line("Moved: " + e.string() + " to " + new_path.string() + "\n");
break;
}
}
}
/// worker renaming
rename_file(u"/SYS/SAMPLES", u"/SYS/CAPTURES"); // it's because, make it easier to use the sample folder.
/// worker check adding flag
make_new_file(U"/AUTO_MOVED"); // add a flag to check in case if need something as flag to check in the future
}
/* Information View *****************************************************/
InformationView::InformationView(
@ -1006,7 +1098,7 @@ BMPView::BMPView(NavigationView& nav) {
}
void BMPView::paint(Painter&) {
if (!portapack::display.drawBMP2({0, 0}, splash_dot_bmp))
if (!portapack::display.drawBMP2({0, 0}, current_using_splash_image))
portapack::display.drawBMP({(240 - 230) / 2, (320 - 50) / 2 - 10}, splash_bmp, false);
}
@ -1079,15 +1171,17 @@ void ModalMessageView::paint(Painter& painter) {
auto lines = split_string(message_, '\n');
for (size_t i = 0; i < lines.size(); ++i) {
painter.draw_string(
{1 * 8, (Coord)(((compact) ? 8 * 3 : 120) + (i * 16))},
style(),
lines[i]);
}
}
void ModalMessageView::focus() {
if ((type_ == YESNO)) {
button_yes.focus();
if (type_ == YESNO) {
button_no.focus(); // it should default to NO like all the operating system
} else {
button_ok.focus();
}

View File

@ -2,7 +2,7 @@
* Copyright (C) 2015 Jared Boone, ShareBrained Technology, Inc.
* Copyright (C) 2016 Furrtek
* Copyright (C) 2024 u-foka
* Copyleft (ɔ) 2024 zxkmm under GPL license
* Copyleft (ɔ) 2024 zxkmm with the GPL license
*
* This file is part of PortaPack.
*
@ -191,6 +191,7 @@ class SystemStatusView : public View {
void set_title(const std::string new_value);
private:
bool already_shown_sdcard_warning_in_this_boot{false};
static constexpr auto default_title = "";
bool batt_info_up = false; // to prevent show multiple batt info dialog
NavigationView& nav_;
@ -289,6 +290,10 @@ class SystemStatusView : public View {
void refresh();
void on_clk();
void rtc_battery_workaround();
void new_sdcard_structure_checker();
void new_sdcard_structure_worker();
void on_battery_data(const BatteryStateMessage* msg);
void on_battery_details();
@ -447,17 +452,17 @@ class ModalMessageView : public View {
const bool compact;
Button button_ok{
{10 * 8, 14 * 16, 10 * 8, 48},
{0, screen_height - 8 * 8, screen_width, 48},
"OK",
};
Button button_yes{
{5 * 8, 14 * 16, 8 * 8, 48},
{0, screen_height - 8 * 8, screen_width / 2, 48},
"YES",
};
Button button_no{
{17 * 8, 14 * 16, 8 * 8, 48},
{screen_width / 2, screen_height - 8 * 8, screen_width / 2, 48},
"NO",
};
};

View File

@ -1,6 +1,8 @@
/*
* Copyright (C) 2014 Jared Boone, ShareBrained Technology, Inc.
* Copyleft (ɔ) 2024 zxkmm under GPL license
* Copyleft (ɔ) 2024 zxkmm with the GPL license
* Copyright (C) 2024 u-foka
* Copyright (C) 2024 Mark Thompson
*
* This file is part of PortaPack.
*

View File

@ -1,6 +1,6 @@
/*
* Copyright (C) 2014 Jared Boone, ShareBrained Technology, Inc.
* Copyleft (ɔ) 2024 zxkmm under GPL license
* Copyleft (ɔ) 2024 zxkmm with the GPL license
* Copyright (C) 2024 u-foka
* Copyright (C) 2024 Mark Thompson
*
@ -347,42 +347,6 @@ class IO {
addr(1); /* Set up for data phase (most likely after a command) */
}
// void high_contrast(ui::Color& pixel, size_t contrast_level_shift) { // TODO
// uint16_t r = (pixel.v >> 11) & 0x1F;
// uint16_t g = (pixel.v >> 5) & 0x3F;
// uint16_t b = pixel.v & 0x1F;
//
// if ((r << contrast_level_shift) > 0x1F) { // should be slightly smaller, need more obverse...
// r = 0x1F;
// } else {
// r = r << contrast_level_shift;
// }
//
// if ((g << contrast_level_shift) > 0x3F) { // same as above
// g = 0x3F;
// } else {
// g = g << contrast_level_shift;
// }
//
// if ((b << contrast_level_shift) > 0x1F) { // same as above
// b = 0x1F;
// } else {
// b = b << contrast_level_shift;
// }
//
// pixel.v = (r << 11) | (g << 5) | b;
// }
//
// void gray_scale(ui::Color& pixel) { // TODO: the blackwhite not looks right....
// uint16_t r = (pixel.v >> 11) & 0x1F;
// uint16_t g = (pixel.v >> 5) & 0x3F;
// uint16_t b = pixel.v & 0x1F;
//
// uint16_t average = (r + g + b) / 3;
//
// pixel.v = (average << 11) | (average << 5) | average;
// }
void lcd_write_data(const uint32_t value) __attribute__((always_inline)) {
// NOTE: Assumes and DIR=0 and ADDR=1 from command phase.
data_write_high(value); /* Drive high byte */

View File

@ -23,11 +23,11 @@ import sys
import struct
from PIL import Image
outfile = open('../../sdcard/ADSB/world_map.bin', 'wb')
outfile = open('../../sdcard/SYS/ADSB/world_map.bin', 'wb')
# Allow for bigger images
Image.MAX_IMAGE_PIXELS = None
im = Image.open("../../sdcard/ADSB/world_map.jpg")
im = Image.open("../../sdcard/SYS/ADSB/world_map.jpg")
pix = im.load()
# Write as unsigned short (2 bytes) as little endian
outfile.write(struct.pack('<H', im.size[0]))