mirror of
https://github.com/portapack-mayhem/mayhem-firmware.git
synced 2025-05-08 02:26:50 +00:00
Added support for extracting firmware from TAR file (with apps) (#1704)
* Added support for extracting firmware from TAR file (with apps) * Added tar to usb cdc firmware command. * Serial flash tar * Show error on bad tar * Check tar for valid filenames
This commit is contained in:
parent
d122a8fc3b
commit
fbe7954f2e
@ -50,6 +50,17 @@ FlashUtilityView::FlashUtilityView(NavigationView& nav)
|
|||||||
this->firmware_selected(path);
|
this->firmware_selected(path);
|
||||||
}});
|
}});
|
||||||
}
|
}
|
||||||
|
for (const auto& entry : std::filesystem::directory_iterator(firmware_folder, u"*.ppfw.tar")) {
|
||||||
|
auto filename = entry.path().filename();
|
||||||
|
auto path = entry.path().native();
|
||||||
|
|
||||||
|
menu_view.add_item({filename.string().substr(0, max_filename_length),
|
||||||
|
ui::Color::purple(),
|
||||||
|
&bitmap_icon_temperature,
|
||||||
|
[this, path](KeyEvent) {
|
||||||
|
this->firmware_selected(path);
|
||||||
|
}});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void FlashUtilityView::firmware_selected(std::filesystem::path::string_type path) {
|
void FlashUtilityView::firmware_selected(std::filesystem::path::string_type path) {
|
||||||
@ -65,7 +76,43 @@ void FlashUtilityView::firmware_selected(std::filesystem::path::string_type path
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool FlashUtilityView::endsWith(const std::u16string& str, const std::u16string& suffix) {
|
||||||
|
if (str.length() >= suffix.length()) {
|
||||||
|
std::u16string endOfString = str.substr(str.length() - suffix.length());
|
||||||
|
return endOfString == suffix;
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::filesystem::path FlashUtilityView::extract_tar(std::filesystem::path::string_type path) {
|
||||||
|
//
|
||||||
|
ui::Painter painter;
|
||||||
|
painter.fill_rectangle(
|
||||||
|
{0, 0, portapack::display.width(), portapack::display.height()},
|
||||||
|
ui::Color::black());
|
||||||
|
painter.draw_string({12, 24}, this->nav_.style(), "Unpacking TAR file...");
|
||||||
|
|
||||||
|
auto res = UnTar::untar(path, [this](const std::string fileName) {
|
||||||
|
ui::Painter painter;
|
||||||
|
painter.fill_rectangle({0, 50, portapack::display.width(), 90}, ui::Color::black());
|
||||||
|
painter.draw_string({0, 60}, this->nav_.style(), fileName);
|
||||||
|
});
|
||||||
|
if (res.string().empty()) {
|
||||||
|
ui::Painter painter;
|
||||||
|
painter.fill_rectangle({0, 50, portapack::display.width(), 90}, ui::Color::black());
|
||||||
|
painter.draw_string({0, 60}, this->nav_.style(), "BAD TAR FILE");
|
||||||
|
chThdSleepMilliseconds(5000);
|
||||||
|
}
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
void FlashUtilityView::flash_firmware(std::filesystem::path::string_type path) {
|
void FlashUtilityView::flash_firmware(std::filesystem::path::string_type path) {
|
||||||
|
if (endsWith(path, u".ppfw.tar")) {
|
||||||
|
// extract, then update
|
||||||
|
path = extract_tar(u'/' + path).native();
|
||||||
|
if (path.empty()) return;
|
||||||
|
}
|
||||||
ui::Painter painter;
|
ui::Painter painter;
|
||||||
painter.fill_rectangle(
|
painter.fill_rectangle(
|
||||||
{0, 0, portapack::display.width(), portapack::display.height()},
|
{0, 0, portapack::display.width(), portapack::display.height()},
|
||||||
|
@ -29,7 +29,7 @@
|
|||||||
#include "ff.h"
|
#include "ff.h"
|
||||||
#include "baseband_api.hpp"
|
#include "baseband_api.hpp"
|
||||||
#include "core_control.hpp"
|
#include "core_control.hpp"
|
||||||
|
#include "untar.hpp"
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
|
|
||||||
namespace ui {
|
namespace ui {
|
||||||
@ -55,8 +55,10 @@ class FlashUtilityView : public View {
|
|||||||
{0, 2 * 8, 240, 26 * 8},
|
{0, 2 * 8, 240, 26 * 8},
|
||||||
true};
|
true};
|
||||||
|
|
||||||
|
std::filesystem::path extract_tar(std::filesystem::path::string_type path); // extracts the tar file, and returns the firmware.bin path from it. empty string if no fw
|
||||||
void firmware_selected(std::filesystem::path::string_type path);
|
void firmware_selected(std::filesystem::path::string_type path);
|
||||||
void flash_firmware(std::filesystem::path::string_type path);
|
void flash_firmware(std::filesystem::path::string_type path);
|
||||||
|
bool endsWith(const std::u16string& str, const std::u16string& suffix);
|
||||||
};
|
};
|
||||||
|
|
||||||
} /* namespace ui */
|
} /* namespace ui */
|
||||||
|
@ -40,6 +40,7 @@
|
|||||||
#include "ff.h"
|
#include "ff.h"
|
||||||
#include "chprintf.h"
|
#include "chprintf.h"
|
||||||
#include "chqueues.h"
|
#include "chqueues.h"
|
||||||
|
#include "untar.hpp"
|
||||||
|
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <codecvt>
|
#include <codecvt>
|
||||||
@ -156,6 +157,15 @@ std::filesystem::path path_from_string8(char* path) {
|
|||||||
return conv.from_bytes(path);
|
return conv.from_bytes(path);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool strEndsWith(const std::u16string& str, const std::u16string& suffix) {
|
||||||
|
if (str.length() >= suffix.length()) {
|
||||||
|
std::u16string endOfString = str.substr(str.length() - suffix.length());
|
||||||
|
return endOfString == suffix;
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static void cmd_flash(BaseSequentialStream* chp, int argc, char* argv[]) {
|
static void cmd_flash(BaseSequentialStream* chp, int argc, char* argv[]) {
|
||||||
if (argc != 1) {
|
if (argc != 1) {
|
||||||
chprintf(chp, "Usage: flash /FIRMWARE/portapack-h1_h2-mayhem.bin\r\n");
|
chprintf(chp, "Usage: flash /FIRMWARE/portapack-h1_h2-mayhem.bin\r\n");
|
||||||
@ -163,15 +173,37 @@ static void cmd_flash(BaseSequentialStream* chp, int argc, char* argv[]) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
auto path = path_from_string8(argv[0]);
|
auto path = path_from_string8(argv[0]);
|
||||||
size_t filename_length = strlen(argv[0]);
|
|
||||||
|
|
||||||
if (!std::filesystem::file_exists(path)) {
|
if (!std::filesystem::file_exists(path)) {
|
||||||
chprintf(chp, "file not found.\r\n");
|
chprintf(chp, "file not found.\r\n");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::memcpy(&shared_memory.bb_data.data[0], path.c_str(), (filename_length + 1) * 2);
|
// check file extensions
|
||||||
|
if (strEndsWith(path.native(), u".ppfw.tar")) {
|
||||||
|
// extract tar
|
||||||
|
chprintf(chp, "Extracting TAR file.\r\n");
|
||||||
|
auto res = UnTar::untar(
|
||||||
|
path.native(), [chp](const std::string fileName) {
|
||||||
|
chprintf(chp, fileName.c_str());
|
||||||
|
chprintf(chp, "\r\n");
|
||||||
|
});
|
||||||
|
if (res.empty()) {
|
||||||
|
chprintf(chp, "error bad TAR file.\r\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
path = res; // it will contain the last bin file in tar
|
||||||
|
} else if (strEndsWith(path.native(), u".bin")) {
|
||||||
|
// nothing to do for this case yet.
|
||||||
|
} else {
|
||||||
|
chprintf(chp, "error only .bin or .ppfw.tar files canbe flashed.\r\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
chprintf(chp, "Flashing: ");
|
||||||
|
chprintf(chp, path.string().c_str());
|
||||||
|
chprintf(chp, "\r\n");
|
||||||
|
chThdSleepMilliseconds(50);
|
||||||
|
std::memcpy(&shared_memory.bb_data.data[0], path.native().c_str(), (path.native().length() + 1) * 2);
|
||||||
m4_request_shutdown();
|
m4_request_shutdown();
|
||||||
chThdSleepMilliseconds(50);
|
chThdSleepMilliseconds(50);
|
||||||
m4_init(portapack::spi_flash::image_tag_flash_utility, portapack::memory::map::m4_code, false);
|
m4_init(portapack::spi_flash::image_tag_flash_utility, portapack::memory::map::m4_code, false);
|
||||||
|
166
firmware/common/untar.hpp
Normal file
166
firmware/common/untar.hpp
Normal file
@ -0,0 +1,166 @@
|
|||||||
|
#ifndef __UNTAR
|
||||||
|
#define __UNTAR
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include "string_format.hpp"
|
||||||
|
#include "file.hpp"
|
||||||
|
|
||||||
|
class UnTar {
|
||||||
|
public:
|
||||||
|
static std::filesystem::path untar(std::u16string tar, std::function<void(const std::string)> cb = NULL) {
|
||||||
|
File tf;
|
||||||
|
auto result = tf.open(tar, true, false);
|
||||||
|
if (!result.value().ok()) return "";
|
||||||
|
return untar_int(&tf, cb);
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
static int parseoct(const char* p, size_t n) {
|
||||||
|
int i = 0;
|
||||||
|
while (*p < '0' || *p > '7') {
|
||||||
|
++p;
|
||||||
|
--n;
|
||||||
|
}
|
||||||
|
while (*p >= '0' && *p <= '7' && n > 0) {
|
||||||
|
i *= 8;
|
||||||
|
i += *p - '0';
|
||||||
|
++p;
|
||||||
|
--n;
|
||||||
|
}
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool is_end_of_archive(const char* p) {
|
||||||
|
for (int n = 511; n >= 0; --n)
|
||||||
|
if (p[n] != '\0') return false;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static size_t strnlen(const char* s, size_t maxlen) {
|
||||||
|
for (size_t i = 0; i < maxlen; i++) {
|
||||||
|
if (s[i] == '\0')
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
return maxlen;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool isValidName(char* name) {
|
||||||
|
size_t pathStrLen = strnlen(name, 100);
|
||||||
|
if (pathStrLen == 0 || pathStrLen >= 100) return false;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool create_dir(char* pathname) {
|
||||||
|
char* p;
|
||||||
|
std::filesystem::filesystem_error r;
|
||||||
|
|
||||||
|
if (!isValidName(pathname)) return false;
|
||||||
|
/* Strip trailing '/' */
|
||||||
|
if (pathname[strlen(pathname) - 1] == '/')
|
||||||
|
pathname[strlen(pathname) - 1] = '\0';
|
||||||
|
|
||||||
|
/* Try creating the directory. */
|
||||||
|
std::string dirnameStr = u'/' + pathname;
|
||||||
|
std::filesystem::path dirname = dirnameStr;
|
||||||
|
|
||||||
|
r = make_new_directory(dirname);
|
||||||
|
|
||||||
|
if (!r.ok()) {
|
||||||
|
/* On failure, try creating parent directory. */
|
||||||
|
p = strrchr(pathname, '/');
|
||||||
|
if (p != NULL) {
|
||||||
|
*p = '\0';
|
||||||
|
create_dir(pathname);
|
||||||
|
*p = '/';
|
||||||
|
r = make_new_directory(dirname);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return (r.ok());
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool verify_checksum(const char* p) {
|
||||||
|
int n, u = 0;
|
||||||
|
for (n = 0; n < 512; ++n) {
|
||||||
|
if (n < 148 || n > 155)
|
||||||
|
/* Standard tar checksum adds unsigned bytes. */
|
||||||
|
u += ((unsigned char*)p)[n];
|
||||||
|
else
|
||||||
|
u += 0x20;
|
||||||
|
}
|
||||||
|
return (u == parseoct(p + 148, 8));
|
||||||
|
}
|
||||||
|
|
||||||
|
static std::filesystem::path untar_int(File* a, std::function<void(const std::string)> cb = NULL) {
|
||||||
|
char buff[512];
|
||||||
|
UINT bytes_read;
|
||||||
|
std::string binfile = "";
|
||||||
|
std::string fn = "";
|
||||||
|
int filesize;
|
||||||
|
for (;;) {
|
||||||
|
auto readres = a->read(buff, 512);
|
||||||
|
if (!readres.is_ok()) return "";
|
||||||
|
bytes_read = readres.value();
|
||||||
|
if (bytes_read < 512) {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
if (is_end_of_archive(buff)) {
|
||||||
|
return binfile;
|
||||||
|
}
|
||||||
|
if (!verify_checksum(buff)) {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
filesize = parseoct(buff + 124, 12);
|
||||||
|
switch (buff[156]) {
|
||||||
|
case '1':
|
||||||
|
break;
|
||||||
|
case '2':
|
||||||
|
break;
|
||||||
|
case '3':
|
||||||
|
break;
|
||||||
|
case '4':
|
||||||
|
break;
|
||||||
|
case '5':
|
||||||
|
create_dir(buff);
|
||||||
|
filesize = 0;
|
||||||
|
break;
|
||||||
|
case '6':
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
if (isValidName(buff)) {
|
||||||
|
fn = buff;
|
||||||
|
if (fn.length() > 5 && fn.substr(fn.length() - 4) == ".bin") {
|
||||||
|
binfile = fn;
|
||||||
|
}
|
||||||
|
if (cb != NULL) cb(fn);
|
||||||
|
} else {
|
||||||
|
return ""; // bad tar
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
File f;
|
||||||
|
if (filesize > 0) {
|
||||||
|
delete_file(fn);
|
||||||
|
auto fres = f.open(fn, false, true);
|
||||||
|
if (!fres.value().ok()) return "";
|
||||||
|
}
|
||||||
|
while (filesize > 0) {
|
||||||
|
readres = a->read(buff, 512);
|
||||||
|
if (!readres.is_ok()) return "";
|
||||||
|
bytes_read = readres.value();
|
||||||
|
if (bytes_read < 512) {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
if (filesize < 512)
|
||||||
|
bytes_read = filesize;
|
||||||
|
auto fwres = f.write(buff, bytes_read);
|
||||||
|
if (!fwres.is_ok()) return "";
|
||||||
|
filesize -= bytes_read;
|
||||||
|
f.sync();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return binfile;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
#endif
|
Loading…
x
Reference in New Issue
Block a user