// Copyright Pandores Marketplace 2024. All Rights Reserved. #pragma once #include "CoreMinimal.h" #include "Zipper.generated.h" UENUM(BlueprintType) enum class EZipCompressLevel : uint8 { // Compression Level 0. No compression at all. Faster but bigger .zip file. Level0 = 0 UMETA(DisplayName = "Level 0"), Level1 = 1 UMETA(DisplayName = "Level 1", Tooltip = "Compression Level 1"), Level2 = 2 UMETA(DisplayName = "Level 2", Tooltip = "Compression Level 2"), Level3 = 3 UMETA(DisplayName = "Level 3", Tooltip = "Compression Level 3"), Level4 = 4 UMETA(DisplayName = "Level 4", Tooltip = "Compression Level 4"), Level5 = 5 UMETA(DisplayName = "Level 5", Tooltip = "Compression Level 5"), Level6 = 6 UMETA(DisplayName = "Level 6", Tooltip = "Compression Level 6"), Level7 = 7 UMETA(DisplayName = "Level 7", Tooltip = "Compression Level 7"), Level8 = 8 UMETA(DisplayName = "Level 8", Tooltip = "Compression Level 8"), // Compression Level 9. Highest compression level. Slower but smaller .zip file. Level9 = 9 UMETA(DisplayName = "Level 9") }; UENUM(BlueprintType) enum class EZipCreationFlag : uint8 { // Cancel the extraction if a file causes conflict. CancelIfExists, // Replace existing files. Overwrite, // Append the files to the existing archive. Append }; DECLARE_DYNAMIC_MULTICAST_DELEGATE_FourParams(FOnFileZippedDynamic, const FString&, ArchivePath, const FString&, FilePath, const int64, FilesZippedCount, const int64, TotalFilesCount); DECLARE_MULTICAST_DELEGATE_FourParams(FOnFileZipped, const FString&, const FString&, const int64, const int64); DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams(FOnFilesZippedDynamic, const bool, bSuccess, const int64, TotalFilesZipped); DECLARE_MULTICAST_DELEGATE_TwoParams(FOnFilesZipped, const bool, const int64); /** * Helper to zip files. */ UCLASS(BlueprintType) class ZIPIT_API UZipper : public UObject { GENERATED_BODY() private: typedef void(*FOnFileZippedPtr)(void* Data, const int64, const int64, const FString&, const FString&); public: UZipper(); ~UZipper(); /** * Creates a new Zipper * @return A new Zipper */ UFUNCTION(BlueprintCallable, BlueprintPure, Category = "ZipIt|Zip") static UPARAM(DisplayName = "Zipper") UZipper* CreateZipper(); /** * Adds a file to the archive. * @param FilePath The path of the file we want to add. * @param ArchivePath The full path (name included) of the file in the archive. */ UFUNCTION(BlueprintCallable, Category = "ZipIt|Zip") void AddFile(const FString& FilePath, const FString& ArchivePath = TEXT("")); /** * Adds a full directory to the archive. * @param Path The path of the directory to add. */ UFUNCTION(BlueprintCallable, Category = "ZipIt|Zip") void AddDirectory(const FString& Path); /** * Adds all the files in the directory matching the extension to the zip archive. * @param Path The path of the directory. * @param MatchingExtension The extension of the files to be added. */ UFUNCTION(BlueprintCallable, Category = "ZipIt|Zip") void AddFilesInDirectoryWithExtension(const FString& Path, const FString& MatchingExtension); /** * Remove a file that was added previously. * @param ArchivePath The path of the file that was added previously we want to delete. * @return If the file was there and was removed. */ UFUNCTION(BlueprintCallable, Category = "ZipIt|Zip") UPARAM(DisplayName = "Success") bool RemoveFile(const FString& ArchivePath); /** * Zip the files added previously into a single file. * This function is asynchronous, it runs on another thread. * @param TargetLocation The target location of the zip archive. * @param Password The password of the zip archive. No password if blank. * @param CompressLevel The compression level of the archive. 0 means no compression. * @param CreationFlag What we want to do with this archive. * @return If the file has been zipped. */ UFUNCTION(BlueprintCallable, Category = "ZipIt|Zip") UPARAM(DisplayName = "Success") bool Zip(const FString& TargetLocation, const FString& Password = TEXT(""), const EZipCompressLevel CompressLevel = EZipCompressLevel::Level9, const EZipCreationFlag CreationFlag = EZipCreationFlag::CancelIfExists); /** * Zip the files added previously into a single file. * This function is asynchronous, it runs on another thread. * @param TargetLocation The target location of the zip archive. * @param Password The password of the zip archive. No password if blank. * @param CompressLevel The compression level of the archive. 0 means no compression. * @param CreationFlag What we want to do with this archive. */ UFUNCTION(BlueprintCallable, Category = "ZipIt|Zip", DisplayName = "Zip Asynchronously") void ZipAsync(FString TargetLocation, FString Password = TEXT(""), const EZipCompressLevel CompressLevel = EZipCompressLevel::Level9, const EZipCreationFlag CreationFlag = EZipCreationFlag::CancelIfExists); /** * @return The number of files previously added in the zipper. The directories * and their content are not included. */ UFUNCTION(BlueprintCallable, Category = "ZipIt|Zip", BlueprintPure) UPARAM(DisplayName = "Count") int32 GetFilesCount() const; // Delegate called just after a file has been successfully zipped. UPROPERTY(BlueprintAssignable, Category = "ZipIt|Zip", DisplayName = "OnFileZippedEvent") FOnFileZippedDynamic OnFileZippedEventDynamic; // Called when the zipping task is over. UPROPERTY(BlueprintAssignable, Category = "ZipIt|Zip", DisplayName = "OnFilesZippedEvent") FOnFilesZippedDynamic OnFilesZippedEventDynamic; // Delegate called just after a file has been successfully zipped. FOnFileZipped& OnFileZipped(); // Called when the zipping task is over. FOnFilesZipped& OnFilesZipped(); private: FOnFileZipped OnFileZippedEvent; FOnFilesZipped OnFilesZippedEvent; private: /** * Check if this file is large. * @param Path The path of the file to check. * @return true if the file is bigger than 0xffffffff. */ static bool IsLargeFile(const FString& Path); /** * Get the Cyclic Redundancy Checksum of the file. * @param Data The data of the file to get CRC. * @return The Cyclic Redundancy Checksum. */ static unsigned long GetFileCrc(const TArray64& Data); /** * Add the content of the directories as files. */ static void AddDirectoriesToFiles(const TArray& Directories, TMap& Files); /** * Zip the files. */ static int64 ZipInternal ( const FString& TargetLocation, const FString& Password, const EZipCompressLevel CompressLevel, const EZipCreationFlag CreationFlag, TMap& Files, TArray& Directories, FOnFileZippedPtr Callback, void* CallbackData ); private: // Call the delegate and take care of being on the good thread. void CallOnFileZippedEvent(const FString& ArchivePath, const FString& FilePath, const int64 FilesZipped, const int64 TotalFiles); // Call the delegate and take care of being on the good thread. void CallOnFilesZippedEvent(const bool bSuccess, const int64 TotalFilesCount); private: // The files we have to include in the archive. // Stored as { TargetFile, InputFile }. // Using a map directly removes duplicates. TMap Files; // The list of directories to add into the archive. TArray Directories; // Number of concurrent sends. // Used to know if we can remove the zipper from the root. int64 ConcurrentialAsyncZips; };