From 9e15182af2318a752fbb53f0385c16cc29f4a4b2 Mon Sep 17 00:00:00 2001 From: NeroBurner Date: Mon, 29 Aug 2022 12:05:21 +0200 Subject: [PATCH] littlefs-do binary to work with spi raw file (#52) Add helper to modify spi raw file, to make experimenting with it easier. ```sh $ ./littlefs-do --help Usage: ./littlefs-do [options] Commands: -h, --help show this help message for the selected command and exit -v, --verbose print status messages to the console stat show information of specified file or directory ls list available files in 'spiNorFlash.raw' file mkdir create directory rmdir remove directory rm remove directory or file cp copy files into or out of flash file settings list settings from 'settings.h' ``` In the process restructure the CMake file for less duplicate includes/defines for both executables (`infinisim` and `littlefs-do`). Upload the `littlefs-do` binary built by the CI additionally to the `infinisim` binary. Use the updated upload-artifact@v3 template to do that. --- .github/workflows/lv_sim.yml | 8 +- CMakeLists.txt | 141 +++++--- README.md | 20 ++ littlefs-do-main.cpp | 661 +++++++++++++++++++++++++++++++++++ 4 files changed, 774 insertions(+), 56 deletions(-) create mode 100644 littlefs-do-main.cpp diff --git a/.github/workflows/lv_sim.yml b/.github/workflows/lv_sim.yml index fc5527e..87350a8 100644 --- a/.github/workflows/lv_sim.yml +++ b/.github/workflows/lv_sim.yml @@ -64,7 +64,13 @@ jobs: cmake --build build_lv_sim - name: Upload simulator executable - uses: actions/upload-artifact@v2 + uses: actions/upload-artifact@v3 with: name: infinisim path: build_lv_sim/infinisim + + - name: Upload littlefs-do executable + uses: actions/upload-artifact@v3 + with: + name: littlefs-do + path: build_lv_sim/littlefs-do diff --git a/CMakeLists.txt b/CMakeLists.txt index c178618..651ca3e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -50,22 +50,64 @@ configure_file( "${CMAKE_CURRENT_BINARY_DIR}/lv_conf.h" COPYONLY) -file(GLOB_RECURSE INCLUDES "lv_drivers/*.h" "${InfiniTime_DIR}/src/libs/lvgl/src/*.h" "./*.h" ) -file(GLOB_RECURSE SOURCES "lv_drivers/*.c" "${InfiniTime_DIR}/src/libs/lvgl/src/*.c" ) +file(GLOB_RECURSE LVGL_INCLUDES "lv_drivers/*.h" "${InfiniTime_DIR}/src/libs/lvgl/src/*.h" "./*.h" ) +file(GLOB_RECURSE LVGL_SOURCES "lv_drivers/*.c" "${InfiniTime_DIR}/src/libs/lvgl/src/*.c" ) + +add_library(sim-base STATIC + # LVGL sources + ${LVGL_SOURCES} ${LVGL_INCLUDES} + # FreeRTOS + sim/FreeRTOS.h + sim/FreeRTOS.cpp + sim/task.h + sim/task.cpp + sim/timers.h + sim/timers.cpp + sim/queue.h + sim/queue.cpp + # src/FreeRTOS + sim/portmacro_cmsis.h + sim/portmacro_cmsis.cpp + # nrf + sim/libraries/log/nrf_log.h + sim/libraries/delay/nrf_delay.h + sim/libraries/delay/nrf_delay.cpp + sim/nrfx/nrfx_log.h + sim/nrfx/drivers/include/nrfx_twi.h + sim/nrfx/hal/nrf_gpio.h + sim/nrfx/hal/nrf_gpio.cpp + sim/nrfx/hal/nrfx_gpiote.h # includes hal/nrf_gpio.h + sim/nrfx/hal/nrf_rtc.h + sim/nrfx/hal/nrf_rtc.cpp + # nrf/components/libraries/timer + sim/libraries/timer/app_timer.h + sim/libraries/timer/app_timer.cpp + sim/libraries/gpiote/app_gpiote.h # includes hal/nrf_gpio.h +) +# include the generated lv_conf.h file before anything else +target_include_directories(sim-base PUBLIC "${CMAKE_CURRENT_BINARY_DIR}") # lv_conf.h +target_include_directories(sim-base PUBLIC "sim") +target_include_directories(sim-base PUBLIC "sim/libraries/log") # for nrf_log.h +target_include_directories(sim-base PUBLIC "sim/libraries/timer") # for app_timer.h +target_include_directories(sim-base PUBLIC "sim/nrfx") # for nrfx_log.h and others +target_include_directories(sim-base PUBLIC "sim/nrfx/hal") # for nrfx_log.h + +target_include_directories(sim-base PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}") # lv_drv_conf.h +target_include_directories(sim-base PUBLIC "${InfiniTime_DIR}/src/libs") +target_include_directories(sim-base PUBLIC "lv_drivers") + +target_include_directories(sim-base PUBLIC "${InfiniTime_DIR}/src") # InfiniTime drivers, components and all -add_executable(infinisim main.cpp ${SOURCES} ${INCLUDES}) set(MONITOR_ZOOM 1 CACHE STRING "Scale simulator window by this factor") if(MONITOR_ZOOM MATCHES "^[0-9]\.?[0-9]*") message(STATUS "Using MONITOR_ZOOM=${MONITOR_ZOOM}") - target_compile_definitions(infinisim PRIVATE MONITOR_ZOOM=${MONITOR_ZOOM}) + target_compile_definitions(sim-base PUBLIC MONITOR_ZOOM=${MONITOR_ZOOM}) else() message(FATAL_ERROR "variable MONITOR_ZOOM=${MONITOR_ZOOM} must be a positive number") endif() -# include the generated lv_conf.h file before anything else -target_include_directories(infinisim PRIVATE "${CMAKE_CURRENT_BINARY_DIR}") - +add_executable(infinisim main.cpp) # add simulator files target_sources(infinisim PUBLIC sim/displayapp/LittleVgl.h @@ -108,45 +150,8 @@ target_sources(infinisim PUBLIC sim/drivers/SpiNorFlash.cpp sim/heartratetask/HeartRateTask.h sim/heartratetask/HeartRateTask.cpp - # FreeRTOS - sim/FreeRTOS.h - sim/FreeRTOS.cpp - sim/task.h - sim/task.cpp - sim/timers.h - sim/timers.cpp - sim/queue.h - sim/queue.cpp - # src/FreeRTOS - sim/portmacro_cmsis.h - sim/portmacro_cmsis.cpp - # nrf - sim/libraries/log/nrf_log.h - sim/libraries/delay/nrf_delay.h - sim/libraries/delay/nrf_delay.cpp - sim/nrfx/nrfx_log.h - sim/nrfx/drivers/include/nrfx_twi.h - sim/nrfx/hal/nrf_gpio.h - sim/nrfx/hal/nrf_gpio.cpp - sim/nrfx/hal/nrfx_gpiote.h # includes hal/nrf_gpio.h - sim/nrfx/hal/nrf_rtc.h - sim/nrfx/hal/nrf_rtc.cpp - # nrf/components/libraries/timer - sim/libraries/timer/app_timer.h - sim/libraries/timer/app_timer.cpp - sim/libraries/gpiote/app_gpiote.h # includes hal/nrf_gpio.h - ) -target_include_directories(infinisim PRIVATE "sim") -target_include_directories(infinisim PRIVATE "sim/libraries/log") # for nrf_log.h -target_include_directories(infinisim PRIVATE "sim/libraries/timer") # for app_timer.h -target_include_directories(infinisim PRIVATE "sim/nrfx") # for nrfx_log.h and others -target_include_directories(infinisim PRIVATE "sim/nrfx/hal") # for nrfx_log.h - -target_compile_definitions(infinisim PRIVATE LV_CONF_INCLUDE_SIMPLE) - -target_include_directories(infinisim PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}") -target_include_directories(infinisim PRIVATE "${InfiniTime_DIR}/src/libs") -target_include_directories(infinisim PRIVATE "lv_drivers") +) +target_link_libraries(infinisim PUBLIC sim-base) # add dates library if(EXISTS "${InfiniTime_DIR}/src/libs/date/includes") @@ -175,21 +180,12 @@ file(GLOB InfiniTime_WIDGETS "${InfiniTime_DIR}/src/displayapp/widgets/*.cpp" "${InfiniTime_DIR}/src/displayapp/widgets/*.h" ) -set(LITTLEFS_SRC - ${InfiniTime_DIR}/src/libs/littlefs/lfs_util.h - ${InfiniTime_DIR}/src/libs/littlefs/lfs.h - ${InfiniTime_DIR}/src/libs/littlefs/lfs_util.c - ${InfiniTime_DIR}/src/libs/littlefs/lfs.c -) target_sources(infinisim PUBLIC ${InfiniTime_SCREENS}) target_sources(infinisim PUBLIC ${InfiniTime_FONTS}) target_sources(infinisim PUBLIC ${InfiniTime_ICONS}) target_sources(infinisim PUBLIC ${InfiniTime_WIDGETS}) -target_sources(infinisim PUBLIC ${LITTLEFS_SRC}) # add files directly from InfiniTime sources -target_include_directories(infinisim PRIVATE "${InfiniTime_DIR}/src") -target_include_directories(infinisim PRIVATE "${InfiniTime_DIR}/src/libs/littlefs") target_sources(infinisim PUBLIC ${InfiniTime_DIR}/src/BootloaderVersion.h ${InfiniTime_DIR}/src/BootloaderVersion.cpp @@ -242,6 +238,16 @@ else() ) endif() +# littlefs +add_library(littlefs STATIC + ${InfiniTime_DIR}/src/libs/littlefs/lfs_util.h + ${InfiniTime_DIR}/src/libs/littlefs/lfs.h + ${InfiniTime_DIR}/src/libs/littlefs/lfs_util.c + ${InfiniTime_DIR}/src/libs/littlefs/lfs.c +) +target_include_directories(littlefs PUBLIC "${InfiniTime_DIR}/src/libs/littlefs") +target_link_libraries(infinisim PRIVATE littlefs) + # QCBOR add_library(QCBOR STATIC ${InfiniTime_DIR}/src/libs/QCBOR/src/ieee754.c @@ -305,3 +311,28 @@ endif() target_include_directories(infinisim PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}/gif-h") install(TARGETS infinisim DESTINATION bin) + +# helper library to manipulate littlefs raw image +add_executable(littlefs-do + littlefs-do-main.cpp + # want to use FS.h + ${InfiniTime_DIR}/src/components/fs/FS.h + ${InfiniTime_DIR}/src/components/fs/FS.cpp + # dependencies for FS.h + sim/drivers/SpiNorFlash.h + sim/drivers/SpiNorFlash.cpp + # dependencies for SpiNorFlash.h + ${InfiniTime_DIR}/src/drivers/Spi.h + ${InfiniTime_DIR}/src/drivers/Spi.cpp + sim/drivers/SpiMaster.h + sim/drivers/SpiMaster.cpp + + ${InfiniTime_DIR}/src/components/settings/Settings.h + ${InfiniTime_DIR}/src/components/settings/Settings.cpp +) + +target_link_libraries(littlefs-do PUBLIC sim-base) +target_link_libraries(littlefs-do PUBLIC littlefs) + +target_link_libraries(littlefs-do PRIVATE SDL2::SDL2) +target_link_libraries(littlefs-do PRIVATE infinitime_fonts) diff --git a/README.md b/README.md index 2dfc037..bba73fe 100644 --- a/README.md +++ b/README.md @@ -115,6 +115,26 @@ Using the keyboard the following events can be triggered: - `i` ... take screenshot - `I` ... start/stop Gif scren capture +## Littlefs-do helper + +To help working with the SPI-raw file the tool `littlefs-do` is provided. +The SPI-raw file emulates the persistent 4MB storage available over the SPI bus on the PineTime. + +```sh +$ ./littlefs-do --help +Usage: ./littlefs-do [options] +Commands: + -h, --help show this help message for the selected command and exit + -v, --verbose print status messages to the console + stat show information of specified file or directory + ls list available files in 'spiNorFlash.raw' file + mkdir create directory + rmdir remove directory + rm remove directory or file + cp copy files into or out of flash file + settings list settings from 'settings.h' +``` + ## Licenses This project is released under the GNU General Public License version 3 or, at your option, any later version. diff --git a/littlefs-do-main.cpp b/littlefs-do-main.cpp new file mode 100644 index 0000000..728de83 --- /dev/null +++ b/littlefs-do-main.cpp @@ -0,0 +1,661 @@ + +/** + * @file littlefs-do-main.cpp + * + */ + +/********************* + * INCLUDES + *********************/ +#define _DEFAULT_SOURCE /* needed for usleep() */ +#include +#include + +#include +#include +#include +#include +#include +#include +#include // std::pow + +#include "components/fs/FS.h" +#include "components/settings/Settings.h" +#include "drivers/SpiNorFlash.h" + +/********************* + * DEFINES + *********************/ + +/********************** + * TYPEDEFS + **********************/ + +/********************** + * STATIC PROTOTYPES + **********************/ + +/********************** + * STATIC VARIABLES + **********************/ + +/********************** + * MACROS + **********************/ + +/********************** + * GLOBAL FUNCTIONS + **********************/ + +/********************* + * DEFINES + *********************/ + +/********************** + * TYPEDEFS + **********************/ + +/********************** + * VARIABLES + **********************/ + +/********************** + * STATIC PROTOTYPES + **********************/ + +/********************** + * GLOBAL FUNCTIONS + **********************/ + +Pinetime::Drivers::SpiNorFlash spiNorFlash {"spiNorFlash.raw"}; + +Pinetime::Controllers::FS fs {spiNorFlash}; +Pinetime::Controllers::Settings settingsController {fs}; + +const char* lfs_error_to_string(int err) { + if (err == LFS_ERR_OK) return "LFS_ERR_OK"; // No error + if (err == LFS_ERR_IO) return "LFS_ERR_IO"; // Error during device operation + if (err == LFS_ERR_CORRUPT) return "LFS_ERR_CORRUPT"; // Corrupted + if (err == LFS_ERR_NOENT) return "LFS_ERR_NOENT"; // No directory entry + if (err == LFS_ERR_EXIST) return "LFS_ERR_EXIST"; // Entry already exists + if (err == LFS_ERR_NOTDIR) return "LFS_ERR_NOTDIR"; // Entry is not a dir + if (err == LFS_ERR_ISDIR) return "LFS_ERR_ISDIR"; // Entry is a dir + if (err == LFS_ERR_NOTEMPTY) return "LFS_ERR_NOTEMPTY"; // Dir is not empty + if (err == LFS_ERR_BADF) return "LFS_ERR_BADF"; // Bad file number + if (err == LFS_ERR_FBIG) return "LFS_ERR_FBIG"; // File too large + if (err == LFS_ERR_INVAL) return "LFS_ERR_INVAL"; // Invalid parameter + if (err == LFS_ERR_NOSPC) return "LFS_ERR_NOSPC"; // No space left on device + if (err == LFS_ERR_NOMEM) return "LFS_ERR_NOMEM"; // No more memory available + if (err == LFS_ERR_NOATTR) return "LFS_ERR_NOATTR"; // No data/attr available + if (err == LFS_ERR_NAMETOOLONG) return "LFS_ERR_NAMETOOLONG"; // File name too long + return "unknown"; +} + +void print_help_generic(const std::string &program_name) +{ + std::cout << "Usage: " << program_name << " [options]" << std::endl; + std::cout << "Commands:" << std::endl; + std::cout << " -h, --help show this help message for the selected command and exit" << std::endl; + std::cout << " -v, --verbose print status messages to the console" << std::endl; + std::cout << " stat show information of specified file or directory" << std::endl; + std::cout << " ls list available files in 'spiNorFlash.raw' file" << std::endl; + std::cout << " mkdir create directory" << std::endl; + std::cout << " rmdir remove directory" << std::endl; + 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; +} +void print_help_stat(const std::string &program_name) +{ + std::cout << "Usage: " << program_name << " stat [options] [path]" << std::endl; + std::cout << "Options:" << std::endl; + std::cout << " -h, --help show this help message for the selected command and exit" << std::endl; + std::cout << " path path to directory or file to work on, defaults to '/'" << std::endl; +} +void print_help_ls(const std::string &program_name) +{ + std::cout << "Usage: " << program_name << " ls [options] [path]" << std::endl; + std::cout << "Options:" << std::endl; + std::cout << " -h, --help show this help message for the selected command and exit" << std::endl; + std::cout << " path path to directory or file to work on, defaults to '/'" << std::endl; +} +void print_help_mkdir(const std::string &program_name) +{ + std::cout << "Usage: " << program_name << " mkdir [options] path" << std::endl; + std::cout << "Options:" << std::endl; + std::cout << " -h, --help show this help message for the selected command and exit" << std::endl; + std::cout << " path path to directory to create" << std::endl; +} +void print_help_rmdir(const std::string &program_name) +{ + std::cout << "Usage: " << program_name << " rmdir [options] path" << std::endl; + std::cout << "Options:" << std::endl; + std::cout << " -h, --help show this help message for the selected command and exit" << std::endl; + std::cout << " path path to directory to remove" << std::endl; +} +void print_help_rm(const std::string &program_name) +{ + std::cout << "Usage: " << program_name << " rm [options] path" << std::endl; + std::cout << "Options:" << std::endl; + std::cout << " -h, --help show this help message for the selected command and exit" << std::endl; + std::cout << " path path to file or directory directory to remove" << std::endl; +} +void print_help_cp(const std::string &program_name) +{ + std::cout << "Usage: " << program_name << " cp [options] source [source2 ...] destination" << std::endl; + std::cout << "Options:" << std::endl; + std::cout << " -h, --help show this help message for the selected command and exit" << std::endl; + std::cout << std::endl; + std::cout << "if the destination starts with '/' it is assumed to copy sources from" << std::endl; + std::cout << "the host system into the raw image, otherwise the files are copied" << std::endl; + std::cout << "from the raw image to the host system provided directory." << std::endl; +} +void print_help_settings(const std::string &program_name) +{ + std::cout << "Usage: " << program_name << " settings [options]" << 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 &args, bool verbose) +{ + if (verbose) + std::cout << "running command 'stat'" << std::endl; + // argv: littlefs-do stat [args] + std::string path = "/"; + // check for help flag first + for (const std::string &arg : args) + { + if (arg == "-h" || arg == "--help") + { + print_help_stat(program_name); + return 0; + } + } + if (args.empty()) { + if (verbose) { + std::cout << "no path given, showing '/'" << std::endl; + } + } if (args.size() == 1) { + path = args.at(0); + } + + lfs_info info; + int ret = fs.Stat(path.c_str(), &info); + if (ret) { + std::cout << "fs.Stat returned error code: " << ret << " " << lfs_error_to_string(ret) << std::endl; + return ret; + } + if (info.type == LFS_TYPE_REG) { + std::cout << "type: REG" << std::endl; + std::cout << "size: " << info.size << std::endl; + std::cout << "name: " << std::string(info.name) << std::endl; + } else if (info.type == LFS_TYPE_DIR) { + std::cout << "type: DIR" << std::endl; + std::cout << "name: " << std::string(info.name) << std::endl; + } + else { + std::cout << "unknown type: " << info.type << std::endl; + return 1; + } + return 0; +} + +int command_ls(const std::string &program_name, const std::vector &args, bool verbose) +{ + if (verbose) + std::cout << "running command 'ls'" << std::endl; + // argv: littlefs-do ls [args] + std::string path = "/"; + // check for help flag first + for (const std::string &arg : args) + { + if (arg == "-h" || arg == "--help") + { + print_help_ls(program_name); + return 0; + } + } + if (args.empty()) { + if (verbose) { + std::cout << "no path given, showing '/'" << std::endl; + } + } if (args.size() == 1) { + path = args.at(0); + } + + lfs_info info; + int ret = fs.Stat(path.c_str(), &info); + if (ret) { + std::cout << "fs.Stat returned error code: " << ret << " " << lfs_error_to_string(ret) << std::endl; + return ret; + } + if (info.type == LFS_TYPE_REG) { + std::cout << "type: REG" << std::endl; + std::cout << "size: " << info.size << std::endl; + std::cout << "name: " << std::string(info.name) << std::endl; + } else if (info.type == LFS_TYPE_DIR) { + std::cout << "type: DIR" << std::endl; + std::cout << "name: " << std::string(info.name) << std::endl; + lfs_dir_t lfs_dir; + ret = fs.DirOpen(path.c_str(), &lfs_dir); + if (ret) { + std::cout << "fs.DirOpen returned error code: " << ret << " " << lfs_error_to_string(ret) << std::endl; + return ret; + } + ret = fs.DirRead(&lfs_dir, &info); + while (ret > 0) { + if (ret < 0) { + std::cout << "fs.DirRead returned error code: " << ret << " " << lfs_error_to_string(ret) << std::endl; + return ret; + } + if (info.type == LFS_TYPE_REG) { + std::cout << "type: REG"; + std::cout << " size: " << info.size; + std::cout << " name: " << std::string(info.name) << std::endl; + } else if (info.type == LFS_TYPE_DIR) { + std::cout << "type: DIR"; + std::cout << " name: " << std::string(info.name) << std::endl; + } else { + std::cout << "unknown type: " << info.type << std::endl; + return 1; + } + // fill for next iteration + ret = fs.DirRead(&lfs_dir, &info); + } // end of while loop + if (ret < 0) { + std::cout << "fs.DirRead returned error code: " << ret << " " << lfs_error_to_string(ret) << std::endl; + return ret; + } + } else { // endif provided path + std::cout << "unknown type: " << info.type << std::endl; + return 1; + } + return 0; +} +int command_mkdir(const std::string &program_name, const std::vector &args, bool verbose) +{ + if (verbose) { + std::cout << "running command 'mkdir'" << std::endl; + } + // argv: littlefs-do mkdir path + // check for help flag first + for (const std::string &arg : args) + { + if (arg == "-h" || arg == "--help") + { + print_help_mkdir(program_name); + return 0; + } + } + if (args.empty()) { + std::cout << "error: no path given" << std::endl; + print_help_mkdir(program_name); + return 1; + } + for (const std::string &path : args) + { + if (verbose) { + std::cout << "mkdir: " << path << std::endl; + } + int ret = fs.DirCreate(path.c_str()); + if (ret < 0) { + std::cout << "fs.DirCreate returned error code: " << ret << " " << lfs_error_to_string(ret) << std::endl; + return ret; + } + } + return 0; +} + +int command_rmdir(const std::string &program_name, const std::vector &args, bool verbose) +{ + if (verbose) { + std::cout << "running command 'rmdir'" << std::endl; + } + // argv: littlefs-do rmdir path + // check for help flag first + for (const std::string &arg : args) + { + if (arg == "-h" || arg == "--help") + { + print_help_rmdir(program_name); + return 0; + } + } + if (args.empty()) { + std::cout << "error: no path given" << std::endl; + print_help_rmdir(program_name); + return 1; + } + for (const std::string &path : args) + { + if (verbose) { + std::cout << "rmdir: " << path << std::endl; + } + lfs_info info; + int ret = fs.Stat(path.c_str(), &info); + if (ret) { + std::cout << "fs.Stat returned error code: " << ret << " " << lfs_error_to_string(ret) << std::endl; + return ret; + } + if (info.type == LFS_TYPE_REG) { + std::cout << "error: provided path '" << path << "is a file" << std::endl; + return 1; + } + // assume non-files are directories + ret = fs.FileDelete(path.c_str()); + if (ret < 0) { + std::cout << "fs.FileDelete returned error code: " << ret << " " << lfs_error_to_string(ret) << std::endl; + return ret; + } + } + return 0; +} + +int command_rm(const std::string &program_name, const std::vector &args, bool verbose) +{ + if (verbose) { + std::cout << "running command 'rm'" << std::endl; + } + // argv: littlefs-do rm path + // check for help flag first + for (const std::string &arg : args) + { + if (arg == "-h" || arg == "--help") + { + print_help_rm(program_name); + return 0; + } + } + if (args.empty()) { + std::cout << "error: no path given" << std::endl; + print_help_rm(program_name); + return 1; + } + for (const std::string &path : args) + { + if (verbose) { + std::cout << "rm: " << path << std::endl; + } + // assume non-files are directories + int ret = fs.FileDelete(path.c_str()); + if (ret < 0) { + std::cout << "fs.FileDelete returned error code: " << ret << " " << lfs_error_to_string(ret) << std::endl; + return ret; + } + } + return 0; +} + +int command_cp(const std::string &program_name, const std::vector &args, bool verbose) +{ + if (verbose) { + std::cout << "running 'cp'" << std::endl; + } + for (const std::string &arg : args) + { + if (arg == "-h" || arg == "--help") + { + print_help_cp(program_name); + return 0; + } + } + if (args.size() < 2) { + std::cout << "error: no destination given, need source and destination" << std::endl; + print_help_cp(program_name); + return 1; + } + const std::string &destination = args.back(); + static constexpr size_t memorySize {0x400000}; + std::array buffer; + if (destination[0] == '/') { + if (verbose) { + std::cout << "destination starts with '/', copying files into image" << std::endl; + } + { + lfs_info info; + int ret = fs.Stat(destination.c_str(), &info); + if (ret) { + std::cout << "fs.Stat for destination path returned error code: " << ret << " " << lfs_error_to_string(ret) << std::endl; + return ret; + } + if (info.type == LFS_TYPE_REG) { + std::cout << "destination is file, assumed directory" << std::endl; + return 1; + } + } + for (size_t i=0; i &args, bool verbose) +{ + if (verbose) { + std::cout << "running 'settings'" << std::endl; + } + for (const std::string &arg : args) + { + if (arg == "-h" || arg == "--help") + { + print_help_settings(program_name); + return 0; + } + } + + if (verbose) { + std::cout << "calling Settings::Init()" << std::endl; + } + settingsController.Init(); + using namespace Pinetime::Controllers; + { + auto clockface = settingsController.GetClockFace(); + auto clockface_str = [](auto val) { + if (val == 0) return "Digital"; + if (val == 1) return "Analog"; + if (val == 2) return "PineTimeStyle"; + if (val == 3) return "Terminal"; + return "unknown"; + }(clockface); + std::cout << "ClockFace: " << static_cast(clockface) << " " << clockface_str << std::endl; + } + { + auto chimes = settingsController.GetChimeOption(); + auto chimes_str = [](auto val) { + if (val == Settings::ChimesOption::None) return "None"; + if (val == Settings::ChimesOption::Hours) return "Hours"; + if (val == Settings::ChimesOption::HalfHours) return "HalfHours"; + return "unknown"; + }(chimes); + std::cout << "Chimes: " << static_cast(chimes) << " " << chimes_str << std::endl; + } + auto color_str = [](auto c) { + if (c == Settings::Colors::White) return "White"; + if (c == Settings::Colors::Silver) return "Silver"; + if (c == Settings::Colors::Gray) return "Gray"; + if (c == Settings::Colors::Black) return "Black"; + if (c == Settings::Colors::Red) return "Red"; + if (c == Settings::Colors::Maroon) return "Maroon"; + if (c == Settings::Colors::Yellow) return "Yellow"; + if (c == Settings::Colors::Olive) return "Olive"; + if (c == Settings::Colors::Lime) return "Lime"; + if (c == Settings::Colors::Green) return "Cyan"; + if (c == Settings::Colors::Teal) return "Teal"; + if (c == Settings::Colors::Blue) return "Blue"; + if (c == Settings::Colors::Navy) return "Navy"; + if (c == Settings::Colors::Magenta) return "Magenta"; + if (c == Settings::Colors::Purple) return "Purple"; + if (c == Settings::Colors::Orange) return "Orange"; + return "unknown"; + }; + std::cout << "PTSColorTime: " << color_str(settingsController.GetPTSColorTime()) << std::endl; + std::cout << "PTSColorBar: " << color_str(settingsController.GetPTSColorBar()) << std::endl; + std::cout << "PTSColorBG: " << color_str(settingsController.GetPTSColorBG()) << std::endl; + std::cout << "AppMenu: " << static_cast(settingsController.GetAppMenu()) << std::endl; + std::cout << "SettingsMenu: " << static_cast(settingsController.GetSettingsMenu()) << std::endl; + std::cout << "ClockType: " << (settingsController.GetClockType() == Settings::ClockType::H24 ? "H24" : "H12") << std::endl; + { + auto notif = settingsController.GetNotificationStatus(); + auto notif_str = [](auto val) { + if (val == Settings::Notification::ON) return "ON"; + if (val == Settings::Notification::OFF) return "OFF"; + return "unknown"; + }(notif); + std::cout << "NotificationStatus: " << static_cast(notif) << " " << notif_str << std::endl; + } + std::cout << "ScreenTimeOut: " << settingsController.GetScreenTimeOut() << " ms" << std::endl; + std::cout << "ShakeThreshold: " << settingsController.GetShakeThreshold() << std::endl; + { + std::cout << "WakeUpModes: " << std::endl; + std::cout << "- SingleTap: " << (settingsController.isWakeUpModeOn(Settings::WakeUpMode::SingleTap) ? "ON" : "OFF") << std::endl; + std::cout << "- DoubleTap: " << (settingsController.isWakeUpModeOn(Settings::WakeUpMode::DoubleTap) ? "ON" : "OFF") << std::endl; + std::cout << "- RaiseWrist: " << (settingsController.isWakeUpModeOn(Settings::WakeUpMode::RaiseWrist) ? "ON" : "OFF") << std::endl; + std::cout << "- Shake: " << (settingsController.isWakeUpModeOn(Settings::WakeUpMode::Shake) ? "ON" : "OFF") << std::endl; + } + { + auto brightness = settingsController.GetBrightness(); + auto brightness_str = [](auto val) { + if (val == BrightnessController::Levels::Off) return "Off"; + if (val == BrightnessController::Levels::Low) return "Low"; + if (val == BrightnessController::Levels::Medium) return "Medium"; + if (val == BrightnessController::Levels::High) return "High"; + return "unknown"; + }(brightness); + std::cout << "Brightness: " << static_cast(brightness) << " " << brightness_str << std::endl; + } + std::cout << "StepsGoal: " << settingsController.GetStepsGoal() << std::endl; + std::cout << "BleRadioEnabled: " << (settingsController.GetBleRadioEnabled() ? "true" : "false") << std::endl; + return 0; +} + +int main(int argc, char **argv) +{ + // parse arguments + if (argc <= 1) { + print_help_generic(argv[0]); + return 1; + } + bool verbose = false; + std::vector args; + for (int i=1; i