portapack-mayhem/firmware/application/ui_external_items_menu_loader.cpp
Bernd Herzog 7fdb1af69d
External apps (#1469)
* implemented external app build

* added some ui stuff for testing

* added pacman game

* wired key to pacman game

* fixed pacman drawing issue

* changed afsk rx app to be external

* fixed ui::NavigationView initialization for external apps

* refactoring

* refactoring

* moved m4 image to external app

* added script for external app deployment

* refactoring

* implemented dynamic app listing

* added color to app icon

* improved app loading

* added external apps to sd card content

* refactoring

* review findings

* typo

* review findings

* improved memory management of bitmaps
2023-10-02 20:19:22 +02:00

140 lines
4.9 KiB
C++

#include "ui_external_items_menu_loader.hpp"
#include "sd_card.hpp"
namespace ui {
/* static */ std::vector<DynamicBitmap<16, 16>> ExternalItemsMenuLoader::bitmaps;
/* static */ std::vector<GridItem> ExternalItemsMenuLoader::load_external_items(app_location_t app_location, NavigationView& nav) {
bitmaps.clear();
std::vector<GridItem> external_apps;
if (sd_card::status() != sd_card::Status::Mounted)
return external_apps;
for (const auto& entry : std::filesystem::directory_iterator(u"APPS", u"*.ppma")) {
auto filePath = u"/APPS/" + entry.path();
File app;
auto openError = app.open(filePath);
if (openError)
continue;
application_information_t application_information = {};
auto readResult = app.read(&application_information, sizeof(application_information_t));
if (!readResult)
continue;
if (application_information.menu_location != app_location)
continue;
if (application_information.header_version != CURRENT_HEADER_VERSION)
continue;
bool versionMatches = VERSION_MD5 == application_information.app_version;
GridItem gridItem = {};
gridItem.text = reinterpret_cast<char*>(&application_information.app_name[0]);
if (versionMatches) {
gridItem.color = Color((uint16_t)application_information.icon_color);
auto dyn_bmp = DynamicBitmap<16, 16>{application_information.bitmap_data};
gridItem.bitmap = dyn_bmp.bitmap();
bitmaps.push_back(std::move(dyn_bmp));
gridItem.on_select = [&nav, app_location, filePath]() {
run_external_app(nav, filePath);
};
} else {
gridItem.color = Color::light_grey();
gridItem.bitmap = &bitmap_sd_card_error;
gridItem.on_select = [&nav]() {
nav.display_modal("Error", "The .ppma file in your APPS\nfolder is outdated. Please\nupdate your SD Card content.");
};
}
external_apps.push_back(gridItem);
}
return external_apps;
}
/* static */ void ExternalItemsMenuLoader::run_external_app(ui::NavigationView& nav, std::filesystem::path filePath) {
File app;
auto openError = app.open(filePath);
if (openError)
chDbgPanic("file gone");
application_information_t application_information = {};
auto readResult = app.read(&application_information, sizeof(application_information_t));
if (!readResult)
chDbgPanic("no data");
app.seek(0);
if (application_information.m4_app_offset != 0) {
// copy application image
for (size_t file_read_index = 0; file_read_index < application_information.m4_app_offset; file_read_index += std::filesystem::max_file_block_size) {
auto bytes_to_read = std::filesystem::max_file_block_size;
if (file_read_index + std::filesystem::max_file_block_size > application_information.m4_app_offset)
bytes_to_read = application_information.m4_app_offset - file_read_index;
if (bytes_to_read == 0)
break;
readResult = app.read(&application_information.memory_location[file_read_index], bytes_to_read);
if (!readResult)
chDbgPanic("read error");
if (readResult.value() < std::filesystem::max_file_block_size)
break;
}
// copy baseband image
for (size_t file_read_index = application_information.m4_app_offset;; file_read_index += readResult.value()) {
size_t bytes_to_read = std::filesystem::max_file_block_size;
// not aligned
if ((file_read_index % std::filesystem::max_file_block_size) != 0)
bytes_to_read = std::filesystem::max_file_block_size - (file_read_index % std::filesystem::max_file_block_size);
if (bytes_to_read == 0)
break;
auto target_memory = reinterpret_cast<void*>(portapack::memory::map::m4_code.base() + file_read_index - application_information.m4_app_offset);
readResult = app.read(target_memory, bytes_to_read);
if (!readResult)
chDbgPanic("read error #2");
if (readResult.value() != bytes_to_read)
break;
}
} else {
// copy application image
for (size_t file_read_index = 0; file_read_index < 80 * std::filesystem::max_file_block_size; file_read_index += std::filesystem::max_file_block_size) {
auto bytes_to_read = std::filesystem::max_file_block_size;
readResult = app.read(&application_information.memory_location[file_read_index], bytes_to_read);
if (!readResult)
chDbgPanic("read error #3");
if (readResult.value() < std::filesystem::max_file_block_size)
break;
}
}
application_information.externalAppEntry(nav);
}
} // namespace ui