Skip to content

Commit 2306807

Browse files
committed
littlefs-do: unzip in memory and copy listed resources to SPI raw file
Add a new command `littlefs-do res load resource.zip` which loads resources from a zip file to the SPI raw file. Below an example `resource.zip` is loaded: ```sh $ ./littlefs-do res load infinitime-resources-1.10.0.zip --verbose Calling FS::Init() running 'res' running 'res load' loading resource file: "infinitime-resources-1.10.0.zip" zip: num of files in zip: 8 copy file teko.bin from zip to SPI path '/teko.bin' copy file lv_font_dots_40.bin from zip to SPI path '/lv_font_dots_40.bin' copy file 7segments_40.bin from zip to SPI path '/7segments_40.bin' copy file bebas.bin from zip to SPI path '/bebas.bin' copy file 7segments_115.bin from zip to SPI path '/7segments_115.bin' copy file matrix.bin from zip to SPI path '/matrix.bin' copy file infineat-1.bin from zip to SPI path '/infineat-1.bin' finished: zip file fully loaded into SPI memory: infinitime-resources-1.10.0.zip ``` Afterwards the files are listed in the SPI raw file: ```sh $ ./littlefs-do ls type: DIR name: / type: DIR name: . type: DIR name: .. type: REG size: 4928 name: 7segments\_115.bin type: REG size: 760 name: 7segments\_40.bin type: REG size: 4420 name: bebas.bin type: REG size: 1430 name: infineat-1.bin type: REG size: 1840 name: lv\_font\_dots\_40.bin type: REG size: 115204 name: matrix.bin type: REG size: 440 name: teko.bin ``` Fixes: #55
1 parent 4dea638 commit 2306807

File tree

3 files changed

+184
-0
lines changed

3 files changed

+184
-0
lines changed

CMakeLists.txt

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -336,3 +336,8 @@ target_link_libraries(littlefs-do PUBLIC littlefs)
336336

337337
target_link_libraries(littlefs-do PRIVATE SDL2::SDL2)
338338
target_link_libraries(littlefs-do PRIVATE infinitime_fonts)
339+
340+
add_subdirectory(external/miniz)
341+
add_subdirectory(external/nlohmann_json)
342+
target_link_libraries(littlefs-do PRIVATE miniz)
343+
target_link_libraries(littlefs-do PRIVATE nlohmann_json::nlohmann_json)

README.md

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -133,6 +133,20 @@ Commands:
133133
rm remove directory or file
134134
cp copy files into or out of flash file
135135
settings list settings from 'settings.h'
136+
res resource.zip handling
137+
```
138+
139+
### Resource loading
140+
141+
To load resource zip files into the SPI raw file for the simulator to use the `res load` command can be used.
142+
143+
```sh
144+
$ ./littlefs-do res --help
145+
Usage: ./littlefs-do res <action> [options]
146+
actions:
147+
load res.zip load zip file into SPI memory
148+
Options:
149+
-h, --help show this help message for the selected command and exit
136150
```
137151

138152
## Licenses

littlefs-do-main.cpp

Lines changed: 165 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313

1414
#include <array>
1515
#include <iostream>
16+
#include <iomanip> // std::left, std::setw
1617
#include <typeinfo>
1718
#include <algorithm>
1819
#include <filesystem>
@@ -23,6 +24,9 @@
2324
#include "components/settings/Settings.h"
2425
#include "drivers/SpiNorFlash.h"
2526

27+
#include "nlohmann/json.hpp"
28+
#include "miniz.h"
29+
2630
/*********************
2731
* DEFINES
2832
*********************/
@@ -104,6 +108,7 @@ void print_help_generic(const std::string &program_name)
104108
std::cout << " rm remove directory or file" << std::endl;
105109
std::cout << " cp copy files into or out of flash file" << std::endl;
106110
std::cout << " settings list settings from 'settings.h'" << std::endl;
111+
std::cout << " res resource.zip handling" << std::endl;
107112
}
108113
void print_help_stat(const std::string &program_name)
109114
{
@@ -156,6 +161,14 @@ void print_help_settings(const std::string &program_name)
156161
std::cout << "Options:" << std::endl;
157162
std::cout << " -h, --help show this help message for the selected command and exit" << std::endl;
158163
}
164+
void print_help_res(const std::string &program_name)
165+
{
166+
std::cout << "Usage: " << program_name << " res <action> [options]" << std::endl;
167+
std::cout << "actions:" << std::endl;
168+
std::cout << " load res.zip load zip file into SPI memory" << std::endl;
169+
std::cout << "Options:" << std::endl;
170+
std::cout << " -h, --help show this help message for the selected command and exit" << std::endl;
171+
}
159172

160173
int command_stat(const std::string &program_name, const std::vector<std::string> &args, bool verbose)
161174
{
@@ -602,6 +615,156 @@ int command_settings(const std::string &program_name, const std::vector<std::str
602615
return 0;
603616
}
604617

618+
void mkdir_path(const std::filesystem::path &path) {
619+
if (!path.is_absolute()) {
620+
// for absolute paths parent path converges at '/', then parent_path == path
621+
mkdir_path(std::filesystem::path{"/"} / path);
622+
return;
623+
}
624+
std::filesystem::path parent = path.parent_path();
625+
if (path == parent) {
626+
return;
627+
}
628+
lfs_info info;
629+
int ret = fs.Stat(path.generic_string().c_str(), &info);
630+
if (ret == 0) {
631+
// directory exists, nothing to do
632+
return;
633+
}
634+
// try to create parent dir first
635+
mkdir_path(parent);
636+
// then create current dir
637+
ret = fs.DirCreate(path.generic_string().c_str());
638+
if (ret < 0) {
639+
std::cout << "mkdir_path: fs.DirCreate returned error code: " << ret << " " << lfs_error_to_string(ret) << std::endl;
640+
assert(false);
641+
return;
642+
}
643+
}
644+
int command_res(const std::string &program_name, const std::vector<std::string> &args, bool verbose)
645+
{
646+
if (verbose) {
647+
std::cout << "running 'res'" << std::endl;
648+
}
649+
for (const std::string &arg : args)
650+
{
651+
if (arg == "-h" || arg == "--help")
652+
{
653+
print_help_res(program_name);
654+
return 0;
655+
}
656+
}
657+
if (args.size() < 1) {
658+
std::cout << "error: no action specified" << std::endl;
659+
print_help_res(program_name);
660+
return 1;
661+
}
662+
if (args.at(0) == "load") {
663+
if (verbose) {
664+
std::cout << "running 'res load'" << std::endl;
665+
}
666+
if (args.size() < 2) {
667+
std::cout << "error: res load needs at least one path to a ressource bundle to load" << std::endl;
668+
print_help_res(program_name);
669+
return 1;
670+
}
671+
for (size_t i=1; i<args.size(); i++) {
672+
const std::filesystem::path path = args.at(i);
673+
if (verbose) {
674+
std::cout << "loading resource file: " << path << std::endl;
675+
}
676+
if (path.extension() == ".zip") {
677+
const std::string &zip_filename = args.at(i);
678+
const size_t f_size = std::filesystem::file_size(path);
679+
std::vector<uint8_t> buffer_compressed(f_size);
680+
std::ifstream ifs(path, std::ios::binary);
681+
ifs.read((char*)(buffer_compressed.data()), f_size);
682+
683+
mz_zip_archive zip_archive {};
684+
int mz_status = mz_zip_reader_init_file(&zip_archive, zip_filename.c_str(), 0);
685+
if (!mz_status) {
686+
std::cout << "error: mz_zip_reader_init_file() failed!" << std::endl;
687+
return 1;
688+
}
689+
690+
mz_uint zip_num_files = mz_zip_reader_get_num_files(&zip_archive);
691+
if (verbose) {
692+
std::cout << "zip: num of files in zip: " << zip_num_files << std::endl;
693+
}
694+
695+
size_t uncomp_size = 0;
696+
void *p = nullptr;
697+
// extract resources.json file to heap, create a string and parse it
698+
p = mz_zip_reader_extract_file_to_heap(&zip_archive, "resources.json", &uncomp_size, 0);
699+
if (!p)
700+
{
701+
std::cout << "mz_zip_reader_extract_file_to_heap() failed to extract resources.json file" << std::endl;
702+
mz_zip_reader_end(&zip_archive);
703+
return 1;
704+
}
705+
std::string_view json_data(static_cast<const char *>(p), uncomp_size);
706+
nlohmann::json doc = nlohmann::json::parse(json_data);
707+
mz_free(p); // free json data, already converted into json document
708+
if (!doc.contains("resources")) {
709+
std::cout << "resources.json is missing 'resources' entry" << std::endl;
710+
mz_zip_reader_end(&zip_archive);
711+
return 1;
712+
}
713+
// copy all listed resources to SPI raw file
714+
for (const auto &res : doc["resources"]) {
715+
const auto filename = res["filename"].get<std::string>();
716+
const auto dest_path = res["path"].get<std::string>();
717+
if (verbose) {
718+
std::cout << "copy file " << std::left << std::setw(25) << filename
719+
<< " from zip to SPI path '" << dest_path << "'" << std::endl;
720+
}
721+
// make sure destination directory exists before copy
722+
const std::filesystem::path dest_dir = std::filesystem::path{dest_path}.parent_path();
723+
mkdir_path(dest_dir);
724+
// extract from zip to heap to then copy to SPI raw file
725+
void *p = mz_zip_reader_extract_file_to_heap(&zip_archive, filename.c_str(), &uncomp_size, 0);
726+
if (!p) {
727+
std::cout << "mz_zip_reader_extract_file_to_heap() failed to extract file: " << filename << std::endl;
728+
mz_zip_reader_end(&zip_archive);
729+
return 1;
730+
}
731+
lfs_file_t file_p;
732+
int ret = fs.FileOpen(&file_p, dest_path.c_str(), LFS_O_WRONLY | LFS_O_CREAT);
733+
if (ret) {
734+
std::cout << "fs.FileOpen returned error code: " << ret << " " << lfs_error_to_string(ret) << std::endl;
735+
return ret;
736+
}
737+
ret = fs.FileWrite(&file_p, static_cast<uint8_t *>(p), uncomp_size);
738+
if (ret < 0) {
739+
std::cout << "fs.FileWrite returned error code: " << ret << " " << lfs_error_to_string(ret) << std::endl;
740+
fs.FileClose(&file_p);
741+
return ret;
742+
}
743+
// file copy complete, close destination file and free data
744+
fs.FileClose(&file_p);
745+
mz_free(p);
746+
}
747+
// all done, close archive
748+
mz_zip_reader_end(&zip_archive);
749+
if (verbose) {
750+
std::cout << "finished: zip file fully loaded into SPI memory: " << zip_filename << std::endl;
751+
}
752+
753+
} else {
754+
std::cout << "error: resource has unknown extension: " << args.at(i) << std::endl;
755+
print_help_res(program_name);
756+
return 1;
757+
}
758+
}
759+
} else {
760+
std::cout << "error: unknown res action '" << args.at(0) << "'" << std::endl;
761+
print_help_res(program_name);
762+
return 1;
763+
}
764+
return 0;
765+
}
766+
767+
605768
int main(int argc, char **argv)
606769
{
607770
// parse arguments
@@ -649,6 +812,8 @@ int main(int argc, char **argv)
649812
return command_cp(argv[0], args, verbose);
650813
} else if (command == "settings") {
651814
return command_settings(argv[0], args, verbose);
815+
} else if (command == "res") {
816+
return command_res(argv[0], args, verbose);
652817
} else
653818
{
654819
std::cout << "unknown argument '" << command << "'" << std::endl;

0 commit comments

Comments
 (0)