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: https://github.com/InfiniTimeOrg/InfiniSim/issues/55
This commit is contained in:
		
							parent
							
								
									4dea63843e
								
							
						
					
					
						commit
						2306807a73
					
				@ -336,3 +336,8 @@ target_link_libraries(littlefs-do PUBLIC littlefs)
 | 
			
		||||
 | 
			
		||||
target_link_libraries(littlefs-do PRIVATE SDL2::SDL2)
 | 
			
		||||
target_link_libraries(littlefs-do PRIVATE infinitime_fonts)
 | 
			
		||||
 | 
			
		||||
add_subdirectory(external/miniz)
 | 
			
		||||
add_subdirectory(external/nlohmann_json)
 | 
			
		||||
target_link_libraries(littlefs-do PRIVATE miniz)
 | 
			
		||||
target_link_libraries(littlefs-do PRIVATE nlohmann_json::nlohmann_json)
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										14
									
								
								README.md
									
									
									
									
									
								
							
							
						
						
									
										14
									
								
								README.md
									
									
									
									
									
								
							@ -133,6 +133,20 @@ Commands:
 | 
			
		||||
  rm                   remove directory or file
 | 
			
		||||
  cp                   copy files into or out of flash file
 | 
			
		||||
  settings             list settings from 'settings.h'
 | 
			
		||||
  res                  resource.zip handling
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
### Resource loading
 | 
			
		||||
 | 
			
		||||
To load resource zip files into the SPI raw file for the simulator to use the `res load` command can be used.
 | 
			
		||||
 | 
			
		||||
```sh
 | 
			
		||||
$ ./littlefs-do res --help
 | 
			
		||||
Usage: ./littlefs-do res <action> [options]
 | 
			
		||||
actions:
 | 
			
		||||
  load res.zip         load zip file into SPI memory
 | 
			
		||||
Options:
 | 
			
		||||
  -h, --help           show this help message for the selected command and exit
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
## Licenses
 | 
			
		||||
 | 
			
		||||
@ -13,6 +13,7 @@
 | 
			
		||||
 | 
			
		||||
#include <array>
 | 
			
		||||
#include <iostream>
 | 
			
		||||
#include <iomanip> // std::left, std::setw
 | 
			
		||||
#include <typeinfo>
 | 
			
		||||
#include <algorithm>
 | 
			
		||||
#include <filesystem>
 | 
			
		||||
@ -23,6 +24,9 @@
 | 
			
		||||
#include "components/settings/Settings.h"
 | 
			
		||||
#include "drivers/SpiNorFlash.h"
 | 
			
		||||
 | 
			
		||||
#include "nlohmann/json.hpp"
 | 
			
		||||
#include "miniz.h"
 | 
			
		||||
 | 
			
		||||
/*********************
 | 
			
		||||
 *      DEFINES
 | 
			
		||||
 *********************/
 | 
			
		||||
@ -104,6 +108,7 @@ void print_help_generic(const std::string &program_name)
 | 
			
		||||
  std::cout << "  rm                   remove directory or file" << std::endl;
 | 
			
		||||
  std::cout << "  cp                   copy files into or out of flash file" << std::endl;
 | 
			
		||||
  std::cout << "  settings             list settings from 'settings.h'" << std::endl;
 | 
			
		||||
  std::cout << "  res                  resource.zip handling" << std::endl;
 | 
			
		||||
}
 | 
			
		||||
void print_help_stat(const std::string &program_name)
 | 
			
		||||
{
 | 
			
		||||
@ -156,6 +161,14 @@ void print_help_settings(const std::string &program_name)
 | 
			
		||||
  std::cout << "Options:" << std::endl;
 | 
			
		||||
  std::cout << "  -h, --help           show this help message for the selected command and exit" << std::endl;
 | 
			
		||||
}
 | 
			
		||||
void print_help_res(const std::string &program_name)
 | 
			
		||||
{
 | 
			
		||||
  std::cout << "Usage: " << program_name << " res <action> [options]" << std::endl;
 | 
			
		||||
  std::cout << "actions:" << std::endl;
 | 
			
		||||
  std::cout << "  load res.zip         load zip file into SPI memory" << std::endl;
 | 
			
		||||
  std::cout << "Options:" << std::endl;
 | 
			
		||||
  std::cout << "  -h, --help           show this help message for the selected command and exit" << std::endl;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int command_stat(const std::string &program_name, const std::vector<std::string> &args, bool verbose)
 | 
			
		||||
{
 | 
			
		||||
@ -602,6 +615,156 @@ int command_settings(const std::string &program_name, const std::vector<std::str
 | 
			
		||||
  return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void mkdir_path(const std::filesystem::path &path) {
 | 
			
		||||
  if (!path.is_absolute()) {
 | 
			
		||||
    // for absolute paths parent path converges at '/', then parent_path == path
 | 
			
		||||
    mkdir_path(std::filesystem::path{"/"} / path);
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
  std::filesystem::path parent = path.parent_path();
 | 
			
		||||
  if (path == parent) {
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
  lfs_info info;
 | 
			
		||||
  int ret = fs.Stat(path.generic_string().c_str(), &info);
 | 
			
		||||
  if (ret == 0) {
 | 
			
		||||
    // directory exists, nothing to do
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
  // try to create parent dir first
 | 
			
		||||
  mkdir_path(parent);
 | 
			
		||||
  // then create current dir
 | 
			
		||||
  ret = fs.DirCreate(path.generic_string().c_str());
 | 
			
		||||
  if (ret < 0) {
 | 
			
		||||
    std::cout << "mkdir_path: fs.DirCreate returned error code: " << ret  << " " << lfs_error_to_string(ret) << std::endl;
 | 
			
		||||
    assert(false);
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
int command_res(const std::string &program_name, const std::vector<std::string> &args, bool verbose)
 | 
			
		||||
{
 | 
			
		||||
  if (verbose) {
 | 
			
		||||
    std::cout << "running 'res'" << std::endl;
 | 
			
		||||
  }
 | 
			
		||||
  for (const std::string &arg : args)
 | 
			
		||||
  {
 | 
			
		||||
    if (arg == "-h" || arg == "--help")
 | 
			
		||||
    {
 | 
			
		||||
      print_help_res(program_name);
 | 
			
		||||
      return 0;
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
  if (args.size() < 1) {
 | 
			
		||||
    std::cout << "error: no action specified" << std::endl;
 | 
			
		||||
    print_help_res(program_name);
 | 
			
		||||
    return 1;
 | 
			
		||||
  }
 | 
			
		||||
  if (args.at(0) == "load") {
 | 
			
		||||
    if (verbose) {
 | 
			
		||||
      std::cout << "running 'res load'" << std::endl;
 | 
			
		||||
    }
 | 
			
		||||
    if (args.size() < 2) {
 | 
			
		||||
      std::cout << "error: res load needs at least one path to a ressource bundle to load" << std::endl;
 | 
			
		||||
      print_help_res(program_name);
 | 
			
		||||
      return 1;
 | 
			
		||||
    }
 | 
			
		||||
    for (size_t i=1; i<args.size(); i++) {
 | 
			
		||||
      const std::filesystem::path path = args.at(i);
 | 
			
		||||
      if (verbose) {
 | 
			
		||||
        std::cout << "loading resource file: " << path << std::endl;
 | 
			
		||||
      }
 | 
			
		||||
      if (path.extension() == ".zip") {
 | 
			
		||||
        const std::string &zip_filename = args.at(i);
 | 
			
		||||
        const size_t f_size = std::filesystem::file_size(path);
 | 
			
		||||
        std::vector<uint8_t> buffer_compressed(f_size);
 | 
			
		||||
        std::ifstream ifs(path, std::ios::binary);
 | 
			
		||||
        ifs.read((char*)(buffer_compressed.data()), f_size);
 | 
			
		||||
 | 
			
		||||
        mz_zip_archive zip_archive {};
 | 
			
		||||
        int mz_status = mz_zip_reader_init_file(&zip_archive, zip_filename.c_str(), 0);
 | 
			
		||||
        if (!mz_status) {
 | 
			
		||||
          std::cout << "error: mz_zip_reader_init_file() failed!" << std::endl;
 | 
			
		||||
          return 1;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        mz_uint zip_num_files = mz_zip_reader_get_num_files(&zip_archive);
 | 
			
		||||
        if (verbose) {
 | 
			
		||||
          std::cout << "zip: num of files in zip: " << zip_num_files << std::endl;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        size_t uncomp_size = 0;
 | 
			
		||||
        void *p = nullptr;
 | 
			
		||||
        // extract resources.json file to heap, create a string and parse it
 | 
			
		||||
        p = mz_zip_reader_extract_file_to_heap(&zip_archive, "resources.json", &uncomp_size, 0);
 | 
			
		||||
        if (!p)
 | 
			
		||||
        {
 | 
			
		||||
          std::cout << "mz_zip_reader_extract_file_to_heap() failed to extract resources.json file" << std::endl;
 | 
			
		||||
          mz_zip_reader_end(&zip_archive);
 | 
			
		||||
          return 1;
 | 
			
		||||
        }
 | 
			
		||||
        std::string_view json_data(static_cast<const char *>(p), uncomp_size);
 | 
			
		||||
        nlohmann::json doc = nlohmann::json::parse(json_data);
 | 
			
		||||
        mz_free(p); // free json data, already converted into json document
 | 
			
		||||
        if (!doc.contains("resources")) {
 | 
			
		||||
          std::cout << "resources.json is missing 'resources' entry" << std::endl;
 | 
			
		||||
          mz_zip_reader_end(&zip_archive);
 | 
			
		||||
          return 1;
 | 
			
		||||
        }
 | 
			
		||||
        // copy all listed resources to SPI raw file
 | 
			
		||||
        for (const auto &res : doc["resources"]) {
 | 
			
		||||
          const auto filename = res["filename"].get<std::string>();
 | 
			
		||||
          const auto dest_path = res["path"].get<std::string>();
 | 
			
		||||
          if (verbose) {
 | 
			
		||||
            std::cout << "copy file " << std::left << std::setw(25) << filename
 | 
			
		||||
              << " from zip to SPI path '" << dest_path << "'" << std::endl;
 | 
			
		||||
          }
 | 
			
		||||
          // make sure destination directory exists before copy
 | 
			
		||||
          const std::filesystem::path dest_dir = std::filesystem::path{dest_path}.parent_path();
 | 
			
		||||
          mkdir_path(dest_dir);
 | 
			
		||||
          // extract from zip to heap to then copy to SPI raw file
 | 
			
		||||
          void *p = mz_zip_reader_extract_file_to_heap(&zip_archive, filename.c_str(), &uncomp_size, 0);
 | 
			
		||||
          if (!p) {
 | 
			
		||||
            std::cout << "mz_zip_reader_extract_file_to_heap() failed to extract file: " << filename << std::endl;
 | 
			
		||||
            mz_zip_reader_end(&zip_archive);
 | 
			
		||||
            return 1;
 | 
			
		||||
          }
 | 
			
		||||
          lfs_file_t file_p;
 | 
			
		||||
          int ret = fs.FileOpen(&file_p, dest_path.c_str(), LFS_O_WRONLY | LFS_O_CREAT);
 | 
			
		||||
          if (ret) {
 | 
			
		||||
            std::cout << "fs.FileOpen returned error code: " << ret  << " " << lfs_error_to_string(ret) << std::endl;
 | 
			
		||||
            return ret;
 | 
			
		||||
          }
 | 
			
		||||
          ret = fs.FileWrite(&file_p, static_cast<uint8_t *>(p), uncomp_size);
 | 
			
		||||
          if (ret < 0) {
 | 
			
		||||
            std::cout << "fs.FileWrite returned error code: " << ret  << " " << lfs_error_to_string(ret) << std::endl;
 | 
			
		||||
            fs.FileClose(&file_p);
 | 
			
		||||
            return ret;
 | 
			
		||||
          }
 | 
			
		||||
          // file copy complete, close destination file and free data
 | 
			
		||||
          fs.FileClose(&file_p);
 | 
			
		||||
          mz_free(p);
 | 
			
		||||
        }
 | 
			
		||||
        // all done, close archive
 | 
			
		||||
        mz_zip_reader_end(&zip_archive);
 | 
			
		||||
        if (verbose) {
 | 
			
		||||
          std::cout << "finished: zip file fully loaded into SPI memory: " << zip_filename << std::endl;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
      } else {
 | 
			
		||||
        std::cout << "error: resource has unknown extension: " << args.at(i) << std::endl;
 | 
			
		||||
        print_help_res(program_name);
 | 
			
		||||
        return 1;
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  } else {
 | 
			
		||||
    std::cout << "error: unknown res action '" << args.at(0) << "'" << std::endl;
 | 
			
		||||
    print_help_res(program_name);
 | 
			
		||||
    return 1;
 | 
			
		||||
  }
 | 
			
		||||
  return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
int main(int argc, char **argv)
 | 
			
		||||
{
 | 
			
		||||
  // parse arguments
 | 
			
		||||
@ -649,6 +812,8 @@ int main(int argc, char **argv)
 | 
			
		||||
    return command_cp(argv[0], args, verbose);
 | 
			
		||||
  } else if (command == "settings") {
 | 
			
		||||
    return command_settings(argv[0], args, verbose);
 | 
			
		||||
  } else if (command == "res") {
 | 
			
		||||
    return command_res(argv[0], args, verbose);
 | 
			
		||||
  } else
 | 
			
		||||
  {
 | 
			
		||||
    std::cout << "unknown argument '" << command << "'" << std::endl;
 | 
			
		||||
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user