From 14c02404111f41bafa0f68e35de94698a4993435 Mon Sep 17 00:00:00 2001 From: SG Date: Wed, 18 Oct 2023 16:03:34 +0300 Subject: [PATCH] Storage: unzip --- .gitmodules | 3 + applications/services/storage/storage_cli.c | 145 ++++++++++++++++++++ firmware/targets/f7/target.json | 1 + lib/SConscript | 1 + lib/uzlib | 1 + lib/uzlib.scons | 34 +++++ 6 files changed, 185 insertions(+) create mode 160000 lib/uzlib create mode 100644 lib/uzlib.scons diff --git a/.gitmodules b/.gitmodules index 52cf4a207..d3c712142 100644 --- a/.gitmodules +++ b/.gitmodules @@ -38,3 +38,6 @@ [submodule "lib/stm32wb_copro"] path = lib/stm32wb_copro url = https://github.com/flipperdevices/stm32wb_copro.git +[submodule "lib/uzlib"] + path = lib/uzlib + url = https://github.com/pfalcon/uzlib.git diff --git a/applications/services/storage/storage_cli.c b/applications/services/storage/storage_cli.c index 74bcf2d92..3f35db67d 100644 --- a/applications/services/storage/storage_cli.c +++ b/applications/services/storage/storage_cli.c @@ -445,6 +445,146 @@ static void storage_cli_remove(Cli* cli, FuriString* path) { furi_record_close(RECORD_STORAGE); } +#include + +typedef struct { + File* file; + uint8_t* buffer; + size_t buffer_size; + uint8_t* dict; + size_t dict_size; + struct uzlib_uncomp uzlib; + + bool eof; +} UnZipData; + +typedef int (*UnizpDataReadCb)(struct uzlib_uncomp* uncomp); + +UnZipData* unzip_data_alloc(size_t dict_size, size_t buffer_size, UnizpDataReadCb cb, File* file) { + UnZipData* data = malloc(sizeof(UnZipData)); + data->file = file; + data->buffer_size = buffer_size; + data->buffer = malloc(buffer_size); + data->dict_size = dict_size; + data->dict = malloc(dict_size); + + uzlib_uncompress_init(&data->uzlib, data->dict, data->dict_size); + + data->uzlib.source = 0; + data->uzlib.source_limit = 0; + data->uzlib.source_read_cb = cb; + data->eof = false; + + return data; +} + +void unzip_data_free(UnZipData* uzlib) { + free(uzlib->buffer); + free(uzlib->dict); + free(uzlib); +} + +int unzip_read_cb(struct uzlib_uncomp* uncomp) { + void* data_p = uncomp; + data_p -= offsetof(UnZipData, uzlib); + UnZipData* data = data_p; + + uint16_t read_size = storage_file_read(data->file, data->buffer, data->buffer_size); + data->uzlib.source = &data->buffer[1]; // we will return buffer[0] at exit + data->uzlib.source_limit = data->buffer + read_size; + + if(read_size == 0) { + return -1; + } + + return data->buffer[0]; +} + +int32_t unzip_get_data(UnZipData* data, void* out, size_t out_len) { + if(data->eof) { + return 0; + } + + data->uzlib.dest = out; + data->uzlib.dest_limit = (uint8_t*)out + out_len; + + int status = uzlib_uncompress_chksum(&data->uzlib); + + if(status == TINF_DONE) { + data->eof = true; + } + if(status < 0) { + return status; + } + return data->uzlib.dest - (uint8_t*)out; +} + +#include + +static void storage_cli_unzip(Cli* cli, FuriString* path) { + UNUSED(cli); + UNUSED(path); + Storage* api = furi_record_open(RECORD_STORAGE); + File* in_file = storage_file_alloc(api); + File* out_file = storage_file_alloc(api); + UnZipData* data = unzip_data_alloc(32 * 1024, 10 * 1024, unzip_read_cb, in_file); + Profiler* profiler = profiler_alloc(); + + int res; + + const size_t out_size = 10 * 1024; + uint8_t* out = malloc(out_size); + + profiler_start(profiler, "unzip"); + + do { + if(!storage_file_open(in_file, furi_string_get_cstr(path), FSAM_READ, FSOM_OPEN_EXISTING)) { + storage_cli_print_error(storage_file_get_error(in_file)); + break; + } + + if(!storage_file_open(out_file, "/ext/out.txt", FSAM_WRITE, FSOM_CREATE_ALWAYS)) { + storage_cli_print_error(storage_file_get_error(out_file)); + break; + } + + res = uzlib_gzip_parse_header(&data->uzlib); + if(res != TINF_OK) { + printf("Error parsing header: %d\n", res); + break; + } + + while(true) { + res = unzip_get_data(data, out, out_size); + if(res < 0) { + printf("Error during decompression: %d\n", res); + break; + } + if(res == 0) { + printf("End\n"); + break; + } + + uint16_t written_size = storage_file_write(out_file, out, res); + if(written_size != res) { + storage_cli_print_error(storage_file_get_error(out_file)); + break; + } + } + } while(false); + + profiler_stop(profiler, "unzip"); + + profiler_dump(profiler); + + profiler_free(profiler); + storage_file_free(in_file); + storage_file_free(out_file); + unzip_data_free(data); + free(out); + furi_record_close(RECORD_STORAGE); +} + static void storage_cli_rename(Cli* cli, FuriString* old_path, FuriString* args) { UNUSED(cli); Storage* api = furi_record_open(RECORD_STORAGE); @@ -566,6 +706,11 @@ void storage_cli(Cli* cli, FuriString* args, void* context) { break; } + if(furi_string_cmp_str(cmd, "unzip") == 0) { + storage_cli_unzip(cli, path); + break; + } + if(furi_string_cmp_str(cmd, "rename") == 0) { storage_cli_rename(cli, path, args); break; diff --git a/firmware/targets/f7/target.json b/firmware/targets/f7/target.json index 9bb87000c..04f40d4dd 100644 --- a/firmware/targets/f7/target.json +++ b/firmware/targets/f7/target.json @@ -29,6 +29,7 @@ "toolbox", "nfc", "digital_signal", + "uzlib", "pulse_reader", "microtar", "usb_stm32", diff --git a/lib/SConscript b/lib/SConscript index 907a5a41d..65ab0b0fb 100644 --- a/lib/SConscript +++ b/lib/SConscript @@ -102,6 +102,7 @@ libs = env.BuildModules( "lfrfid", "flipper_application", "music_worker", + "uzlib", ], ) diff --git a/lib/uzlib b/lib/uzlib new file mode 160000 index 000000000..6d60d651a --- /dev/null +++ b/lib/uzlib @@ -0,0 +1 @@ +Subproject commit 6d60d651a4499a64f2e5b21b4cc08d98cb84b5c1 diff --git a/lib/uzlib.scons b/lib/uzlib.scons new file mode 100644 index 000000000..7f56af8d8 --- /dev/null +++ b/lib/uzlib.scons @@ -0,0 +1,34 @@ +Import("env") + +env.Append( + # CPPPATH=[ + # "#/lib/subghz", + # ], + # SDK_HEADERS=[ + # File("environment.h"), + # ], +) + +libenv = env.Clone(FW_LIB_NAME="uzlib") +libenv.ApplyLibFlags() + +libenv.AppendUnique( + CCFLAGS=[ + "-Wno-redundant-decls", + "-Wno-sign-compare", + ], +) + +sources = [ + "uzlib/src/adler32.c", + "uzlib/src/crc32.c", + "uzlib/src/defl_static.c", + "uzlib/src/genlz77.c", + "uzlib/src/tinfgzip.c", + "uzlib/src/tinflate.c", + "uzlib/src/tinfzlib.c", +] + +lib = libenv.StaticLibrary("${FW_LIB_NAME}", sources) +libenv.Install("${LIB_DIST_DIR}", lib) +Return("lib")