diff --git a/CMakeLists.txt b/CMakeLists.txt index 771452e..20b7fd2 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -35,6 +35,7 @@ add_executable(ezremote_client source/smbclient.cpp source/windows.cpp source/webdavclient.cpp + source/zip_util.cpp source/imgui_draw.cpp source/imgui_impl_sdl.cpp source/imgui_impl_sdlrenderer.cpp diff --git a/data/assets/langs/English.ini b/data/assets/langs/English.ini index 5b7a307..4795cfd 100644 --- a/data/assets/langs/English.ini +++ b/data/assets/langs/English.ini @@ -90,3 +90,7 @@ STR_EXTRACT=Extract STR_EXTRACTING=Extracting STR_FAILED_TO_EXTRACT=Failed to extract STR_EXTRACT_LOCATION=Extract Location +STR_COMPRESS=Compress +STR_ZIP_FILE_PATH=Zip Filename +STR_COMPRESSING=Compressing +STR_ERROR_CREATE_ZIP=Error occured while creating zip diff --git a/source/actions.cpp b/source/actions.cpp index 342fe93..d5a7145 100644 --- a/source/actions.cpp +++ b/source/actions.cpp @@ -18,6 +18,7 @@ #include "ftpclient.h" #include "smbclient.h" #include "webdavclient.h" +#include "zip_util.h" namespace Actions { @@ -735,75 +736,15 @@ namespace Actions } } - int Extract(const DirEntry &file, const std::string &dir) - { - unz_global_info global_info; - unz_file_info file_info; - unzFile zipfile = unzOpen(file.path); - std::string dest_dir = std::string(dir); - if (dest_dir[dest_dir.length()-1] != '/') - { - dest_dir = dest_dir + "/"; - } - if (zipfile == NULL) - { - return 0; - } - unzGetGlobalInfo(zipfile, &global_info); - unzGoToFirstFile(zipfile); - uint64_t curr_extracted_bytes = 0; - uint64_t curr_file_bytes = 0; - int num_files = global_info.number_entry; - char fname[512]; - char ext_fname[512]; - char read_buffer[32768]; - - for (int zip_idx = 0; zip_idx < num_files; ++zip_idx) - { - if (stop_activity) - break; - unzGetCurrentFileInfo(zipfile, &file_info, fname, 512, NULL, 0, NULL, 0); - sprintf(ext_fname, "%s%s", dest_dir.c_str(), fname); - const size_t filename_length = strlen(ext_fname); - if (ext_fname[filename_length - 1] != '/') - { - snprintf(activity_message, 255, "%s %s: %s", lang_strings[STR_EXTRACTING], file.name, fname); - curr_file_bytes = 0; - unzOpenCurrentFile(zipfile); - FS::MkDirs(ext_fname, true); - FILE *f = fopen(ext_fname, "wb"); - while (curr_file_bytes < file_info.uncompressed_size) - { - int rbytes = unzReadCurrentFile(zipfile, read_buffer, 32768); - if (rbytes > 0) - { - fwrite(read_buffer, 1, rbytes, f); - curr_extracted_bytes += rbytes; - curr_file_bytes += rbytes; - } - } - fclose(f); - unzCloseCurrentFile(zipfile); - } - if ((zip_idx + 1) < num_files) - { - unzGoToNextFile(zipfile); - } - } - unzClose(zipfile); - return 1; - } - void *ExtractZipThread(void *argp) { - file_transfering = true; for (std::set::iterator it = multi_selected_local_files.begin(); it != multi_selected_local_files.end(); ++it) { if (stop_activity) break; if (!it->isDir) { - int ret = Extract(*it, extract_zip_folder); + int ret = ZipUtil::Extract(*it, extract_zip_folder); if (ret == 0) { sprintf(status_message, "%s %s", lang_strings[STR_FAILED_TO_EXTRACT], it->name); @@ -831,6 +772,44 @@ namespace Actions } } + void *MakeZipThread(void *argp) + { + zipFile zf = zipOpen64(zip_file_path, APPEND_STATUS_CREATE); + if (zf != NULL) + { + for (std::set::iterator it = multi_selected_local_files.begin(); it != multi_selected_local_files.end(); ++it) + { + if (stop_activity) + break; + int res = ZipUtil::ZipAddPath(zf, it->path, strlen(it->directory)+1, Z_DEFAULT_COMPRESSION); + if (res <= 0) + { + sprintf(status_message, "%s", lang_strings[STR_ERROR_CREATE_ZIP]); + sceKernelUsleep(2000000); + } + } + zipClose(zf, NULL); + } + activity_inprogess = false; + multi_selected_local_files.clear(); + Windows::SetModalMode(false); + selected_action = ACTION_REFRESH_LOCAL_FILES; + return NULL; + } + + void MakeLocalZip() + { + sprintf(status_message, "%s", ""); + int res = pthread_create(&bk_activity_thid, NULL, MakeZipThread, NULL); + if (res != 0) + { + file_transfering = false; + activity_inprogess = false; + multi_selected_local_files.clear(); + Windows::SetModalMode(false); + } + } + std::string GetGoogleDownloadUrl(std::string &url) { size_t scheme_pos = url.find_first_of("://"); diff --git a/source/actions.h b/source/actions.h index 92f48fb..74349cd 100644 --- a/source/actions.h +++ b/source/actions.h @@ -86,6 +86,8 @@ namespace Actions void *KeepAliveThread(void *argp); void *ExtractZipThread(void *argp); void ExtractLocalZips(); + void *MakeZipThread(void *argp); + void MakeLocalZip(); } #endif \ No newline at end of file diff --git a/source/lang.cpp b/source/lang.cpp index a153c2d..6679886 100644 --- a/source/lang.cpp +++ b/source/lang.cpp @@ -101,7 +101,11 @@ char lang_strings[LANG_STRINGS_NUM][LANG_STR_SIZE] = { "Extract", // STR_EXTRACT "Extracting", // STR_EXTRACTING "Failed to extract", // STR_FAILED_TO_EXTRACT - "Extract Location" // STR_EXTRACT_LOCATION + "Extract Location", // STR_EXTRACT_LOCATION + "Compress", // STR_COMPRESS + "Zip Filename", // STR_ZIP_FILE_PATH + "Compressing", // STR_COMPRESSING + "Error occured while creating zip" // STR_ERROR_CREATE_ZIP }; bool needs_extended_font = false; diff --git a/source/lang.h b/source/lang.h index 52328a0..8807eea 100644 --- a/source/lang.h +++ b/source/lang.h @@ -95,7 +95,11 @@ FUNC(STR_EXTRACT) \ FUNC(STR_EXTRACTING) \ FUNC(STR_FAILED_TO_EXTRACT) \ - FUNC(STR_EXTRACT_LOCATION) + FUNC(STR_EXTRACT_LOCATION) \ + FUNC(STR_COMPRESS) \ + FUNC(STR_ZIP_FILE_PATH) \ + FUNC(STR_COMPRESSING) \ + FUNC(STR_ERROR_CREATE_ZIP) #define GET_VALUE(x) x, #define GET_STRING(x) #x, @@ -105,7 +109,7 @@ enum FOREACH_STR(GET_VALUE) }; -#define LANG_STRINGS_NUM 92 +#define LANG_STRINGS_NUM 96 #define LANG_ID_SIZE LANG_STRINGS_NUM #define LANG_STR_SIZE 256 extern char lang_identifiers[LANG_STRINGS_NUM][LANG_ID_SIZE]; diff --git a/source/windows.cpp b/source/windows.cpp index d9d9190..64aabc7 100644 --- a/source/windows.cpp +++ b/source/windows.cpp @@ -59,6 +59,7 @@ bool set_focus_to_remote = false; bool select_url_inprogress = false; int favorite_url_idx = 0; char extract_zip_folder[256]; +char zip_file_path[384]; bool dont_prompt_overwrite = false; bool dont_prompt_overwrite_cb = false; @@ -157,6 +158,46 @@ namespace Windows paused = modal; } + std::string getUniqueZipFilename() + { + std::string zipfolder; + std::string zipname; + if (strcmp(multi_selected_local_files.begin()->directory, "/") == 0) + { + zipfolder = "/data"; + } + else + { + zipfolder = multi_selected_local_files.begin()->directory; + } + if (multi_selected_local_files.size() == 1) + { + zipname = multi_selected_local_files.begin()->name; + } + + else if (strcmp(multi_selected_local_files.begin()->directory, "/") == 0) + { + zipname = "new_zip"; + } + else + { + zipname = std::string(multi_selected_local_files.begin()->directory); + zipname = zipname.substr(zipname.find_last_of("/")+1); + } + + std::string zip_path; + zip_path = zipfolder + "/" + zipname; + int i = 0; + while (true) + { + std::string temp_path; + i > 0 ? temp_path = zip_path + "(" + std::to_string(i) + ").zip" : temp_path = zip_path + ".zip"; + if (!FS::FileExists(temp_path)) + return temp_path; + i++; + } + } + void ConnectionPanel() { ImGuiStyle *style = &ImGui::GetStyle(); @@ -738,6 +779,28 @@ namespace Windows ime_after_update = AfterExtractFolderCallback; Dialog::initImeDialog(lang_strings[STR_EXTRACT_LOCATION], extract_zip_folder, 255, ORBIS_TYPE_BASIC_LATIN, 600, 350); gui_mode = GUI_MODE_IME; + file_transfering = true; + SetModalMode(false); + ImGui::CloseCurrentPopup(); + } + ImGui::PopID(); + ImGui::Separator(); + + ImGui::PushID("Compress##settings"); + if (ImGui::Selectable(lang_strings[STR_COMPRESS], false, flags | ImGuiSelectableFlags_DontClosePopups, ImVec2(220, 0))) + { + std::string zipname; + std::string zipfolder; + + ResetImeCallbacks(); + sprintf(zip_file_path, "%s", getUniqueZipFilename().c_str()); + ime_single_field = zip_file_path; + ime_field_size = 383; + ime_callback = SingleValueImeCallback; + ime_after_update = AfterZipFileCallback; + Dialog::initImeDialog(lang_strings[STR_ZIP_FILE_PATH], zip_file_path, 383, ORBIS_TYPE_BASIC_LATIN, 600, 350); + gui_mode = GUI_MODE_IME; + file_transfering = true; SetModalMode(false); ImGui::CloseCurrentPopup(); } @@ -1219,10 +1282,17 @@ namespace Windows case ACTION_EXTRACT_LOCAL_ZIP: activity_inprogess = true; stop_activity = false; - file_transfering = false; + file_transfering = true; selected_action = ACTION_NONE; Actions::ExtractLocalZips(); break; + case ACTION_CREATE_LOCAL_ZIP: + activity_inprogess = true; + stop_activity = false; + file_transfering = true; + selected_action = ACTION_NONE; + Actions::MakeLocalZip(); + break; case ACTION_RENAME_LOCAL: if (gui_mode != GUI_MODE_IME) { @@ -1437,4 +1507,9 @@ namespace Windows { selected_action = ACTION_EXTRACT_LOCAL_ZIP; } + + void AfterZipFileCallback(int ime_result) + { + selected_action = ACTION_CREATE_LOCAL_ZIP; + } } diff --git a/source/windows.h b/source/windows.h index 142ded8..f011cb1 100644 --- a/source/windows.h +++ b/source/windows.h @@ -37,6 +37,7 @@ extern int overwrite_type; extern ACTIONS action_to_take; extern bool file_transfering; extern char extract_zip_folder[]; +extern char zip_file_path[]; static ImVector s_GroupPanelLabelStack; @@ -201,6 +202,7 @@ namespace Windows void AfterPackageUrlCallback(int ime_result); void AfterFavoriteUrlCallback(int ime_result); void AfterExtractFolderCallback(int ime_result); + void AfterZipFileCallback(int ime_result); } #endif diff --git a/source/zip_util.cpp b/source/zip_util.cpp new file mode 100644 index 0000000..d6c77fb --- /dev/null +++ b/source/zip_util.cpp @@ -0,0 +1,270 @@ + +#include +#include +#include +#include +#include +#include +#include +#include +#include "common.h" +#include "fs.h" +#include "lang.h" +#include "rtc.h" +#include "windows.h" + +#define TRANSFER_SIZE (128 * 1024) + +namespace ZipUtil +{ + void convertToZipTime(time_t time, tm_zip *tmzip) + { + OrbisDateTime gmt; + OrbisDateTime lt; + + struct tm tm = *localtime(&time); + gmt.day = tm.tm_mday; + gmt.month = tm.tm_mon + 1; + gmt.year = tm.tm_year + 1900; + gmt.hour = tm.tm_hour; + gmt.minute = tm.tm_min; + gmt.second = tm.tm_sec; + + convertUtcToLocalTime(&gmt, <); + + tmzip->tm_sec = lt.second; + tmzip->tm_min = lt.minute; + tmzip->tm_hour = lt.hour; + tmzip->tm_mday = lt.day; + tmzip->tm_mon = lt.month; + tmzip->tm_year = lt.year; + } + + int ZipAddFile(zipFile zf, const std::string &path, int filename_start, int level) + { + int res; + // Get file stat + struct stat file_stat; + memset(&file_stat, 0, sizeof(file_stat)); + res = stat(path.c_str(), &file_stat); + if (res < 0) + return res; + + // Get file local time + zip_fileinfo zi; + memset(&zi, 0, sizeof(zip_fileinfo)); + convertToZipTime(file_stat.st_mtim.tv_sec, &zi.tmz_date); + + bytes_transfered = 0; + bytes_to_download = file_stat.st_size; + + // Large file? + int use_zip64 = (file_stat.st_size >= 0xFFFFFFFF); + + // Open new file in zip + res = zipOpenNewFileInZip3_64(zf, path.substr(filename_start).c_str(), &zi, + NULL, 0, NULL, 0, NULL, + (level != 0) ? Z_DEFLATED : 0, + level, 0, + -MAX_WBITS, DEF_MEM_LEVEL, Z_DEFAULT_STRATEGY, + NULL, 0, use_zip64); + if (res < 0) + return res; + + // Open file to add + FILE *fd = FS::OpenRead(path); + if (fd == NULL) + { + zipCloseFileInZip(zf); + return 0; + } + + // Add file to zip + void *buf = memalign(4096, TRANSFER_SIZE); + uint64_t seek = 0; + + while (1) + { + int read = FS::Read(fd, buf, TRANSFER_SIZE); + if (read < 0) + { + free(buf); + FS::Close(fd); + zipCloseFileInZip(zf); + return read; + } + + if (read == 0) + break; + + int written = zipWriteInFileInZip(zf, buf, read); + if (written < 0) + { + free(buf); + FS::Close(fd); + zipCloseFileInZip(zf); + return written; + } + + seek += written; + bytes_transfered += read; + } + + free(buf); + FS::Close(fd); + zipCloseFileInZip(zf); + + return 1; + } + + int ZipAddFolder(zipFile zf, const std::string &path, int filename_start, int level) + { + int res; + + // Get file stat + struct stat file_stat; + memset(&file_stat, 0, sizeof(file_stat)); + res = stat(path.c_str(), &file_stat); + if (res < 0) + return res; + + // Get file local time + zip_fileinfo zi; + memset(&zi, 0, sizeof(zip_fileinfo)); + convertToZipTime(file_stat.st_mtim.tv_sec, &zi.tmz_date); + + // Open new file in zip + std::string folder = path.substr(filename_start); + if (folder[folder.length()-1] != '/') + folder = folder + "/"; + + res = zipOpenNewFileInZip3_64(zf, folder.c_str(), &zi, + NULL, 0, NULL, 0, NULL, + (level != 0) ? Z_DEFLATED : 0, + level, 0, + -MAX_WBITS, DEF_MEM_LEVEL, Z_DEFAULT_STRATEGY, + NULL, 0, 0); + + if (res < 0) + return res; + + zipCloseFileInZip(zf); + return 1; + } + + int ZipAddPath(zipFile zf, const std::string &path, int filename_start, int level) + { + DIR *dfd = opendir(path.c_str()); + if (dfd != NULL) + { + int ret = ZipAddFolder(zf, path, filename_start, level); + if (ret <= 0) + return ret; + + struct dirent *dirent; + do + { + dirent = readdir(dfd); + if (stop_activity) + return 1; + if (dirent != NULL && strcmp(dirent->d_name, ".") != 0 && strcmp(dirent->d_name, "..") != 0) + { + int new_path_length = path.length() + strlen(dirent->d_name) + 2; + char *new_path = (char*)malloc(new_path_length); + snprintf(new_path, new_path_length, "%s%s%s", path.c_str(), FS::hasEndSlash(path.c_str()) ? "" : "/", dirent->d_name); + + int ret = 0; + + if (dirent->d_type & DT_DIR) + { + ret = ZipAddPath(zf, new_path, filename_start, level); + } + else + { + sprintf(activity_message, "%s %s", lang_strings[STR_COMPRESSING], new_path); + ret = ZipAddFile(zf, new_path, filename_start, level); + } + + free(new_path); + + // Some folders are protected and return 0x80010001. Bypass them + if (ret <= 0) + { + closedir(dfd); + return ret; + } + } + } while (dirent != NULL); + + closedir(dfd); + } + else + { + return ZipAddFile(zf, path, filename_start, level); + } + + return 1; + } + + int Extract(const DirEntry &file, const std::string &dir) + { + unz_global_info global_info; + unz_file_info file_info; + unzFile zipfile = unzOpen(file.path); + std::string dest_dir = std::string(dir); + if (dest_dir[dest_dir.length() - 1] != '/') + { + dest_dir = dest_dir + "/"; + } + if (zipfile == NULL) + { + return 0; + } + unzGetGlobalInfo(zipfile, &global_info); + unzGoToFirstFile(zipfile); + uint64_t curr_extracted_bytes = 0; + uint64_t curr_file_bytes = 0; + int num_files = global_info.number_entry; + char fname[512]; + char ext_fname[512]; + char read_buffer[TRANSFER_SIZE]; + + for (int zip_idx = 0; zip_idx < num_files; ++zip_idx) + { + if (stop_activity) + break; + unzGetCurrentFileInfo(zipfile, &file_info, fname, 512, NULL, 0, NULL, 0); + sprintf(ext_fname, "%s%s", dest_dir.c_str(), fname); + const size_t filename_length = strlen(ext_fname); + bytes_transfered = 0; + bytes_to_download = file_info.uncompressed_size; + if (ext_fname[filename_length - 1] != '/') + { + snprintf(activity_message, 255, "%s %s: %s", lang_strings[STR_EXTRACTING], file.name, fname); + curr_file_bytes = 0; + unzOpenCurrentFile(zipfile); + FS::MkDirs(ext_fname, true); + FILE *f = fopen(ext_fname, "wb"); + while (curr_file_bytes < file_info.uncompressed_size) + { + int rbytes = unzReadCurrentFile(zipfile, read_buffer, TRANSFER_SIZE); + if (rbytes > 0) + { + fwrite(read_buffer, 1, rbytes, f); + curr_extracted_bytes += rbytes; + curr_file_bytes += rbytes; + bytes_transfered = curr_file_bytes; + } + } + fclose(f); + unzCloseCurrentFile(zipfile); + } + if ((zip_idx + 1) < num_files) + { + unzGoToNextFile(zipfile); + } + } + unzClose(zipfile); + return 1; + } +} \ No newline at end of file diff --git a/source/zip_util.h b/source/zip_util.h new file mode 100644 index 0000000..94227de --- /dev/null +++ b/source/zip_util.h @@ -0,0 +1,16 @@ +#ifndef ZIP_UTIL_H +#define ZIP_UTIL_H + +#include +#include +#include +#include +#include "common.h" +#include "fs.h" + +namespace ZipUtil +{ + int ZipAddPath(zipFile zf, const std::string &path, int filename_start, int level); + int Extract(const DirEntry &file, const std::string &dir); +} +#endif \ No newline at end of file