2023-10-02 14:19:22 -04:00
|
|
|
#include "ui_external_items_menu_loader.hpp"
|
|
|
|
|
|
|
|
#include "sd_card.hpp"
|
|
|
|
|
|
|
|
namespace ui {
|
|
|
|
|
|
|
|
/* static */ std::vector<DynamicBitmap<16, 16>> ExternalItemsMenuLoader::bitmaps;
|
|
|
|
|
2024-01-07 12:56:09 -05:00
|
|
|
// iterates over all ppma-s, and if it is runnable on the current system, it'll call the callback, and pass info.
|
|
|
|
/* static */ void ExternalItemsMenuLoader::load_all_external_items_callback(std::function<void(AppInfoConsole&)> callback) {
|
|
|
|
if (!callback) return;
|
|
|
|
if (sd_card::status() != sd_card::Status::Mounted)
|
|
|
|
return;
|
|
|
|
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.header_version != CURRENT_HEADER_VERSION)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
bool versionMatches = VERSION_MD5 == application_information.app_version;
|
|
|
|
if (!versionMatches) continue;
|
|
|
|
// here the app is startable and good.
|
|
|
|
std::string appshortname = filePath.filename().string();
|
|
|
|
if (appshortname.size() >= 5 && appshortname.substr(appshortname.size() - 5) == ".ppma") {
|
|
|
|
// Remove the ".ppma" suffix
|
|
|
|
appshortname = appshortname.substr(0, appshortname.size() - 5);
|
|
|
|
}
|
|
|
|
AppInfoConsole info{
|
|
|
|
.appCallName = appshortname.c_str(),
|
|
|
|
.appFriendlyName = reinterpret_cast<char*>(&application_information.app_name[0]),
|
|
|
|
.appLocation = application_information.menu_location};
|
|
|
|
callback(info);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-10-02 14:19:22 -04:00
|
|
|
/* 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]() {
|
2024-01-07 12:56:09 -05:00
|
|
|
if (!run_external_app(nav, filePath)) {
|
|
|
|
nav.display_modal("Error", "The .ppma file in your APPS\nfolder can't be read. Please\nupdate your SD Card content.");
|
|
|
|
}
|
2023-10-02 14:19:22 -04:00
|
|
|
};
|
|
|
|
} 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;
|
|
|
|
}
|
|
|
|
|
2024-01-07 12:56:09 -05:00
|
|
|
/* static */ bool ExternalItemsMenuLoader::run_external_app(ui::NavigationView& nav, std::filesystem::path filePath) {
|
2023-10-02 14:19:22 -04:00
|
|
|
File app;
|
2024-01-24 17:37:21 -05:00
|
|
|
uint32_t checksum{0};
|
2023-10-02 14:19:22 -04:00
|
|
|
|
|
|
|
auto openError = app.open(filePath);
|
|
|
|
if (openError)
|
2024-01-07 12:56:09 -05:00
|
|
|
return false;
|
2023-10-02 14:19:22 -04:00
|
|
|
|
|
|
|
application_information_t application_information = {};
|
|
|
|
|
|
|
|
auto readResult = app.read(&application_information, sizeof(application_information_t));
|
|
|
|
if (!readResult)
|
2024-01-07 12:56:09 -05:00
|
|
|
return false;
|
2023-10-02 14:19:22 -04:00
|
|
|
|
|
|
|
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)
|
2024-01-07 12:56:09 -05:00
|
|
|
return false;
|
2023-10-02 14:19:22 -04:00
|
|
|
|
2024-01-24 17:37:21 -05:00
|
|
|
checksum += simple_checksum((uint32_t)&application_information.memory_location[file_read_index], readResult.value());
|
|
|
|
|
2023-10-02 14:19:22 -04:00
|
|
|
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)
|
2024-01-07 12:56:09 -05:00
|
|
|
return false;
|
2023-10-02 14:19:22 -04:00
|
|
|
|
2024-01-24 17:37:21 -05:00
|
|
|
checksum += simple_checksum((uint32_t)target_memory, readResult.value());
|
|
|
|
|
2023-10-02 14:19:22 -04:00
|
|
|
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)
|
2024-01-07 12:56:09 -05:00
|
|
|
return false;
|
2023-10-02 14:19:22 -04:00
|
|
|
|
2024-01-24 17:37:21 -05:00
|
|
|
checksum += simple_checksum((uint32_t)&application_information.memory_location[file_read_index], readResult.value());
|
|
|
|
|
2023-10-02 14:19:22 -04:00
|
|
|
if (readResult.value() < std::filesystem::max_file_block_size)
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-01-24 17:37:21 -05:00
|
|
|
if (checksum != EXT_APP_EXPECTED_CHECKSUM)
|
|
|
|
return false;
|
|
|
|
|
2023-10-02 14:19:22 -04:00
|
|
|
application_information.externalAppEntry(nav);
|
2024-01-07 12:56:09 -05:00
|
|
|
return true;
|
2023-10-02 14:19:22 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
} // namespace ui
|