mirror of
https://github.com/Theaninova/BeatLanguageMapper.git
synced 2026-01-20 17:03:49 +00:00
Mk2Rev1
various bugfixes and such. also bookmarks, temp song loaderm and undo overhaul with near complete multiplayer, but menu items disabled. i really wish i could split this into multiple commits, but i don't know how to work unreal.
This commit is contained in:
@@ -0,0 +1,11 @@
|
||||
// Fill out your copyright notice in the Description page of Project Settings.
|
||||
|
||||
#include "WindowsFileUtilityPrivatePCH.h"
|
||||
#include "WFUFileListInterface.h"
|
||||
|
||||
|
||||
UWFUFileListInterface::UWFUFileListInterface(const class FObjectInitializer& PCIP)
|
||||
: Super(PCIP)
|
||||
{
|
||||
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
|
||||
#include "WindowsFileUtilityPrivatePCH.h"
|
||||
#include "WindowsFileUtilityFunctionLibrary.h"
|
||||
#include "WFUFileListLambdaDelegate.h"
|
||||
|
||||
UWFUFileListLambdaDelegate::UWFUFileListLambdaDelegate()
|
||||
{
|
||||
OnDoneCallback = nullptr;
|
||||
}
|
||||
|
||||
void UWFUFileListLambdaDelegate::SetOnDoneCallback(TFunction<void(const TArray<FString>&, const TArray<FString>&)> InOnDoneCallback)
|
||||
{
|
||||
OnDoneCallback = InOnDoneCallback;
|
||||
}
|
||||
|
||||
void UWFUFileListLambdaDelegate::OnListFileFound_Implementation(const FString& FileName, int32 ByteCount, const FString& FilePath)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void UWFUFileListLambdaDelegate::OnListDirectoryFound_Implementation(const FString& DirectoryName, const FString& FilePath)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void UWFUFileListLambdaDelegate::OnListDone_Implementation(const FString& DirectoryPath, const TArray<FString>& Files, const TArray<FString>& Folders)
|
||||
{
|
||||
if (OnDoneCallback != nullptr)
|
||||
{
|
||||
OnDoneCallback(Files, Folders);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "Object.h"
|
||||
#include "WFUFileListLambdaDelegate.generated.h"
|
||||
|
||||
UCLASS()
|
||||
class WINDOWSFILEUTILITY_API UWFUFileListLambdaDelegate : public UObject, public IWFUFileListInterface
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
UWFUFileListLambdaDelegate();
|
||||
public:
|
||||
void SetOnDoneCallback(TFunction<void(const TArray<FString>&, const TArray<FString>&)> InOnDoneCallback);
|
||||
|
||||
protected:
|
||||
//File List Interface
|
||||
|
||||
virtual void OnListFileFound_Implementation(const FString& FileName, int32 ByteCount, const FString& FilePath) override;
|
||||
virtual void OnListDirectoryFound_Implementation(const FString& DirectoryName, const FString& FilePath) override;
|
||||
virtual void OnListDone_Implementation(const FString& DirectoryPath, const TArray<FString>& Files, const TArray<FString>& Folders) override;
|
||||
|
||||
TFunction<void(const TArray<FString>&, const TArray<FString>&)> OnDoneCallback;
|
||||
};
|
||||
@@ -0,0 +1,11 @@
|
||||
// Fill out your copyright notice in the Description page of Project Settings.
|
||||
|
||||
#include "WindowsFileUtilityPrivatePCH.h"
|
||||
#include "WFUFolderWatchInterface.h"
|
||||
|
||||
|
||||
UWFUFolderWatchInterface::UWFUFolderWatchInterface(const class FObjectInitializer& PCIP)
|
||||
: Super(PCIP)
|
||||
{
|
||||
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
|
||||
#include "WindowsFileUtilityPrivatePCH.h"
|
||||
#include "WindowsFileUtilityFunctionLibrary.h"
|
||||
#include "WFUFolderWatchLambdaDelegate.h"
|
||||
|
||||
UWFUFolderWatchLambdaDelegate::UWFUFolderWatchLambdaDelegate()
|
||||
{
|
||||
OnFileChangedCallback = nullptr;
|
||||
}
|
||||
|
||||
|
||||
void UWFUFolderWatchLambdaDelegate::SetOnFileChangedCallback(TFunction<void(FString, FString)> InOnFileChangedCallback)
|
||||
{
|
||||
OnFileChangedCallback = InOnFileChangedCallback;
|
||||
}
|
||||
|
||||
|
||||
void UWFUFolderWatchLambdaDelegate::OnFileChanged_Implementation(const FString& FileName, const FString& FilePath)
|
||||
{
|
||||
if (OnFileChangedCallback != nullptr)
|
||||
{
|
||||
OnFileChangedCallback(FileName, FilePath);
|
||||
}
|
||||
}
|
||||
|
||||
void UWFUFolderWatchLambdaDelegate::OnDirectoryChanged_Implementation(const FString& DirectoryName, const FString& DirectoryPath)
|
||||
{
|
||||
if (OnFileChangedCallback != nullptr)
|
||||
{
|
||||
OnFileChangedCallback(DirectoryName, DirectoryPath);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "Object.h"
|
||||
#include "WFUFolderWatchLambdaDelegate.generated.h"
|
||||
|
||||
UCLASS()
|
||||
class WINDOWSFILEUTILITY_API UWFUFolderWatchLambdaDelegate : public UObject, public IWFUFolderWatchInterface
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
UWFUFolderWatchLambdaDelegate();
|
||||
public:
|
||||
void SetOnFileChangedCallback(TFunction<void(FString, FString)> InOnFileChangedCallback);
|
||||
|
||||
protected:
|
||||
TFunction<void(FString, FString)> OnFileChangedCallback;
|
||||
|
||||
//IWFUFolderWatchInterface
|
||||
virtual void OnFileChanged_Implementation(const FString& FileName, const FString& FilePath) override;
|
||||
virtual void OnDirectoryChanged_Implementation(const FString& DirectoryName, const FString& DirectoryPath) override;
|
||||
};
|
||||
@@ -0,0 +1,100 @@
|
||||
#pragma once
|
||||
|
||||
#include "WindowsFileUtilityPrivatePCH.h"
|
||||
#include "WFULambdaRunnable.h"
|
||||
|
||||
uint64 WFULambdaRunnable::ThreadNumber = 0;
|
||||
|
||||
FQueuedThreadPool* WFULambdaRunnable::ThreadPool = nullptr;
|
||||
|
||||
WFULambdaRunnable::WFULambdaRunnable(TFunction< void()> InFunction)
|
||||
{
|
||||
FunctionPointer = InFunction;
|
||||
|
||||
FString threadStatGroup = FString::Printf(TEXT("FLambdaRunnable%d"), ThreadNumber++);
|
||||
Thread = NULL;
|
||||
Thread = FRunnableThread::Create(this, *threadStatGroup, 0, TPri_BelowNormal); //windows default = 8mb for thread, could specify more
|
||||
}
|
||||
|
||||
WFULambdaRunnable::~WFULambdaRunnable()
|
||||
{
|
||||
if (Thread == NULL)
|
||||
{
|
||||
delete Thread;
|
||||
Thread = NULL;
|
||||
}
|
||||
|
||||
ThreadPool->Destroy();
|
||||
}
|
||||
|
||||
//Run
|
||||
uint32 WFULambdaRunnable::Run()
|
||||
{
|
||||
if (FunctionPointer)
|
||||
FunctionPointer();
|
||||
|
||||
//UE_LOG(LogClass, Log, TEXT("FLambdaRunnable %d Run complete"), Number);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void WFULambdaRunnable::Exit()
|
||||
{
|
||||
//UE_LOG(LogClass, Log, TEXT("FLambdaRunnable %d Exit"), Number);
|
||||
|
||||
//delete ourselves when we're done
|
||||
delete this;
|
||||
}
|
||||
|
||||
void WFULambdaRunnable::InitThreadPool(int32 NumberOfThreads)
|
||||
{
|
||||
if (ThreadPool == nullptr)
|
||||
{
|
||||
ThreadPool = FQueuedThreadPool::Allocate();
|
||||
int32 NumThreadsInThreadPool = NumberOfThreads;
|
||||
ThreadPool->Create(NumThreadsInThreadPool, 32 * 1024);
|
||||
}
|
||||
}
|
||||
|
||||
void WFULambdaRunnable::EnsureCompletion()
|
||||
{
|
||||
Thread->WaitForCompletion();
|
||||
}
|
||||
|
||||
WFULambdaRunnable* WFULambdaRunnable::RunLambdaOnBackGroundThread(TFunction< void()> InFunction)
|
||||
{
|
||||
if (FPlatformProcess::SupportsMultithreading())
|
||||
{
|
||||
//UE_LOG(LogClass, Log, TEXT("FLambdaRunnable RunLambdaBackGroundThread"));
|
||||
return new WFULambdaRunnable(InFunction);
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
IQueuedWork* WFULambdaRunnable::AddLambdaToQueue(TFunction< void()> InFunction)
|
||||
{
|
||||
if (ThreadPool == nullptr)
|
||||
{
|
||||
WFULambdaRunnable::InitThreadPool(FPlatformMisc::NumberOfIOWorkerThreadsToSpawn());
|
||||
}
|
||||
|
||||
if (ThreadPool)
|
||||
{
|
||||
return AsyncLambdaPool(*ThreadPool, InFunction);
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
bool WFULambdaRunnable::RemoveLambdaFromQueue(IQueuedWork* Work)
|
||||
{
|
||||
if (ThreadPool)
|
||||
{
|
||||
return ThreadPool->RetractQueuedWork(Work);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
FGraphEventRef WFULambdaRunnable::RunShortLambdaOnGameThread(TFunction< void()> InFunction)
|
||||
{
|
||||
return FFunctionGraphTask::CreateAndDispatchWhenReady(InFunction, TStatId(), nullptr, ENamedThreads::GameThread);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
#include "WindowsFileUtilityPrivatePCH.h"
|
||||
|
||||
class FWindowsFileUtility : public IWindowsFileUtility
|
||||
{
|
||||
/** IModuleInterface implementation */
|
||||
virtual void StartupModule() override
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
virtual void ShutdownModule() override
|
||||
{
|
||||
|
||||
}
|
||||
};
|
||||
|
||||
IMPLEMENT_MODULE(IWindowsFileUtility, WindowsFileUtility)
|
||||
@@ -0,0 +1,414 @@
|
||||
#include "WindowsFileUtilityPrivatePCH.h"
|
||||
#include "WFUFolderWatchInterface.h"
|
||||
#include "WFUFileListInterface.h"
|
||||
#include "WindowsFileUtilityFunctionLibrary.h"
|
||||
|
||||
//static TMAP definition
|
||||
TMap<FString, TArray<FWatcher>> UWindowsFileUtilityFunctionLibrary::Watchers = TMap<FString, TArray<FWatcher>>();
|
||||
int TotalWatchers = 0;
|
||||
|
||||
UWindowsFileUtilityFunctionLibrary::UWindowsFileUtilityFunctionLibrary(const class FObjectInitializer& PCIP)
|
||||
: Super(PCIP)
|
||||
{
|
||||
}
|
||||
|
||||
#if PLATFORM_WINDOWS
|
||||
|
||||
#include "AllowWindowsPlatformTypes.h"
|
||||
#include <shellapi.h>
|
||||
#include <Shlwapi.h>
|
||||
|
||||
#pragma comment(lib, "Shlwapi.lib")
|
||||
|
||||
bool UWindowsFileUtilityFunctionLibrary::DoesFileExist(const FString& FullPath)
|
||||
{
|
||||
return 0 != PathFileExistsW(*FullPath);
|
||||
}
|
||||
|
||||
bool UWindowsFileUtilityFunctionLibrary::MoveFileTo(const FString& From, const FString& To)
|
||||
{
|
||||
//Using windows api
|
||||
return 0 != MoveFileW(*From, *To);
|
||||
}
|
||||
|
||||
|
||||
bool UWindowsFileUtilityFunctionLibrary::CreateDirectoryAt(const FString& FullPath)
|
||||
{
|
||||
//Using windows api
|
||||
return 0 != CreateDirectoryW(*FullPath, NULL);
|
||||
}
|
||||
|
||||
bool UWindowsFileUtilityFunctionLibrary::DeleteFileAt(const FString& FullPath)
|
||||
{
|
||||
//Using windows api
|
||||
return 0 != DeleteFileW(*FullPath);
|
||||
}
|
||||
|
||||
bool UWindowsFileUtilityFunctionLibrary::DeleteEmptyFolder(const FString& FullPath)
|
||||
{
|
||||
//Using windows api
|
||||
return 0 != RemoveDirectoryW(*FullPath);
|
||||
}
|
||||
|
||||
bool IsSubPathOf(const FString& path, const FString& basePath)
|
||||
{
|
||||
return path.Contains(basePath);
|
||||
}
|
||||
|
||||
//Dangerous function not recommended to be exposed to blueprint
|
||||
bool UWindowsFileUtilityFunctionLibrary::DeleteFolderRecursively(const FString& FullPath)
|
||||
{
|
||||
//Only allow user to delete folders sub-class to game folder
|
||||
if (!IsSubPathOf(FullPath, FPaths::ProjectDir()))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
int len = _tcslen(*FullPath);
|
||||
TCHAR *pszFrom = new TCHAR[len + 2];
|
||||
wcscpy_s(pszFrom, len + 2, *FullPath);
|
||||
pszFrom[len] = 0;
|
||||
pszFrom[len + 1] = 0;
|
||||
|
||||
SHFILEOPSTRUCT fileop;
|
||||
fileop.hwnd = NULL; // no status display
|
||||
fileop.wFunc = FO_DELETE; // delete operation
|
||||
fileop.pFrom = pszFrom; // source file name as double null terminated string
|
||||
fileop.pTo = NULL; // no destination needed
|
||||
fileop.fFlags = FOF_NOCONFIRMATION | FOF_SILENT; // do not prompt the user
|
||||
|
||||
fileop.fAnyOperationsAborted = FALSE;
|
||||
fileop.lpszProgressTitle = NULL;
|
||||
fileop.hNameMappings = NULL;
|
||||
|
||||
int ret = SHFileOperation(&fileop);
|
||||
delete[] pszFrom;
|
||||
return (ret == 0);
|
||||
}
|
||||
|
||||
void UWindowsFileUtilityFunctionLibrary::WatchFolder(const FString& FullPath, UObject* WatcherDelegate)
|
||||
{
|
||||
//Do we have an entry for this path?
|
||||
if (!Watchers.Contains(FullPath))
|
||||
{
|
||||
//Make an entry
|
||||
TArray<FWatcher> FreshList;
|
||||
Watchers.Add(FullPath, FreshList);
|
||||
Watchers[FullPath] = FreshList;
|
||||
}
|
||||
else
|
||||
{
|
||||
//if we do do we already watch from this object?
|
||||
TArray<FWatcher>& PathWatchers = Watchers[FullPath];
|
||||
|
||||
for (auto Watcher : PathWatchers)
|
||||
{
|
||||
if (Watcher.Delegate == WatcherDelegate)
|
||||
{
|
||||
//Already accounted for
|
||||
UE_LOG(LogTemp, Warning, TEXT("UWindowsFileUtilityFunctionLibrary::WatchFolder Duplicate watcher ignored!"));
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//Add to watchers
|
||||
FWatcher FreshWatcher;
|
||||
FreshWatcher.Delegate = WatcherDelegate;
|
||||
FreshWatcher.Path = FullPath;
|
||||
|
||||
const FWatcher* WatcherPtr = &FreshWatcher;
|
||||
|
||||
//fork this off to another process
|
||||
WFULambdaRunnable* Runnable = WFULambdaRunnable::RunLambdaOnBackGroundThread([FullPath, WatcherDelegate, WatcherPtr]()
|
||||
{
|
||||
UWindowsFileUtilityFunctionLibrary::WatchFolderOnBgThread(FullPath, WatcherPtr);
|
||||
});
|
||||
|
||||
FreshWatcher.Runnable = Runnable;
|
||||
|
||||
TArray<FWatcher>& PathWatchers = Watchers[FullPath];
|
||||
PathWatchers.Add(FreshWatcher);
|
||||
}
|
||||
|
||||
void UWindowsFileUtilityFunctionLibrary::StopWatchingFolder(const FString& FullPath, UObject* WatcherDelegate)
|
||||
{
|
||||
//Do we have an entry?
|
||||
if (!Watchers.Contains(FullPath))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
//We have an entry for this path, remove our watcher
|
||||
TArray<FWatcher> PathWatchers = Watchers[FullPath];
|
||||
for (int i = 0; i < PathWatchers.Num();i++)
|
||||
{
|
||||
FWatcher& PathWatcher = PathWatchers[i];
|
||||
if (PathWatcher.Delegate == WatcherDelegate)
|
||||
{
|
||||
//Stop the runnable
|
||||
PathWatcher.ShouldRun = false;
|
||||
PathWatcher.Runnable->Stop();
|
||||
|
||||
//Remove the watcher and we're done
|
||||
PathWatchers.RemoveAt(i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void UWindowsFileUtilityFunctionLibrary::ListContentsOfFolder(const FString& FullPath, UObject* Delegate)
|
||||
{
|
||||
//Longer than max path? throw error
|
||||
if (FullPath.Len() > MAX_PATH)
|
||||
{
|
||||
UE_LOG(LogTemp, Warning, TEXT("UWindowsFileUtilityFunctionLibrary::ListContentsOfFolder Error, path too long, listing aborted."));
|
||||
return;
|
||||
}
|
||||
|
||||
WFULambdaRunnable* Runnable = WFULambdaRunnable::RunLambdaOnBackGroundThread([&FullPath, Delegate]()
|
||||
{
|
||||
WIN32_FIND_DATA ffd;
|
||||
LARGE_INTEGER filesize;
|
||||
HANDLE hFind = INVALID_HANDLE_VALUE;
|
||||
DWORD dwError = 0;
|
||||
|
||||
FString SearchPath = FullPath + TEXT("\\*");
|
||||
|
||||
hFind = FindFirstFile(*SearchPath, &ffd);
|
||||
|
||||
if (INVALID_HANDLE_VALUE == hFind)
|
||||
{
|
||||
UE_LOG(LogTemp, Warning, TEXT("UWindowsFileUtilityFunctionLibrary::ListContentsOfFolder Error, invalid handle, listing aborted."));
|
||||
return;
|
||||
}
|
||||
|
||||
//Arrays to hold full information on Done
|
||||
TArray<FString> FileNames;
|
||||
TArray<FString> FolderNames;
|
||||
|
||||
//List loop, callback on game thread
|
||||
do
|
||||
{
|
||||
FString Name = FString(ffd.cFileName);
|
||||
FString ItemPath = FullPath + TEXT("\\") + Name;
|
||||
|
||||
//UE_LOG(LogTemp, Log, TEXT("Name: <%s>"), *Name);
|
||||
|
||||
if (Name.Equals(FString(TEXT("."))) ||
|
||||
Name.Equals(FString(TEXT(".."))) )
|
||||
{
|
||||
//ignore these first
|
||||
}
|
||||
//Folder
|
||||
else if (ffd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
|
||||
{
|
||||
FolderNames.Add(Name);
|
||||
WFULambdaRunnable::RunShortLambdaOnGameThread([Delegate, ItemPath, Name]
|
||||
{
|
||||
((IWFUFileListInterface*)Delegate)->Execute_OnListDirectoryFound((UObject*)Delegate, Name, ItemPath);
|
||||
});
|
||||
}
|
||||
//File
|
||||
else
|
||||
{
|
||||
FileNames.Add(Name);
|
||||
|
||||
filesize.LowPart = ffd.nFileSizeLow;
|
||||
filesize.HighPart = ffd.nFileSizeHigh;
|
||||
int32 TruncatedFileSize = filesize.QuadPart;
|
||||
|
||||
WFULambdaRunnable::RunShortLambdaOnGameThread([Delegate, ItemPath, Name, TruncatedFileSize]
|
||||
{
|
||||
((IWFUFileListInterface*)Delegate)->Execute_OnListFileFound((UObject*)Delegate, Name, TruncatedFileSize, ItemPath);
|
||||
});
|
||||
}
|
||||
|
||||
} while (FindNextFile(hFind, &ffd) != 0);
|
||||
|
||||
dwError = GetLastError();
|
||||
if (dwError != ERROR_NO_MORE_FILES)
|
||||
{
|
||||
UE_LOG(LogTemp, Warning, TEXT("UWindowsFileUtilityFunctionLibrary::ListContentsOfFolder Error while listing."));
|
||||
return;
|
||||
}
|
||||
|
||||
FindClose(hFind);
|
||||
|
||||
//Done callback with full list of names found
|
||||
WFULambdaRunnable::RunShortLambdaOnGameThread([Delegate, FullPath, FileNames, FolderNames]
|
||||
{
|
||||
((IWFUFileListInterface*)Delegate)->Execute_OnListDone((UObject*)Delegate, FullPath, FileNames, FolderNames);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
void UWindowsFileUtilityFunctionLibrary::ListContentsOfFolderToCallback(const FString& FullPath, TFunction<void(const TArray<FString>&, const TArray<FString>&)> OnListCompleteCallback)
|
||||
{
|
||||
UWFUFileListLambdaDelegate* LambdaDelegate = NewObject<UWFUFileListLambdaDelegate>();
|
||||
LambdaDelegate->SetOnDoneCallback(OnListCompleteCallback);
|
||||
|
||||
ListContentsOfFolder(FullPath, LambdaDelegate);
|
||||
}
|
||||
|
||||
void UWindowsFileUtilityFunctionLibrary::WatchFolderOnBgThread(const FString& FullPath, const FWatcher* WatcherPtr)
|
||||
{
|
||||
//mostly from https://msdn.microsoft.com/en-us/library/windows/desktop/aa365261(v=vs.85).aspx
|
||||
//call the delegate when the folder changes
|
||||
|
||||
//TODO: find out which file changed
|
||||
|
||||
DWORD dwWaitStatus;
|
||||
HANDLE dwChangeHandles[2];
|
||||
TCHAR lpDrive[4];
|
||||
TCHAR lpFile[_MAX_FNAME];
|
||||
TCHAR lpExt[_MAX_EXT];
|
||||
|
||||
//finding out about the notification
|
||||
FILE_NOTIFY_INFORMATION strFileNotifyInfo[1024];
|
||||
DWORD dwBytesReturned = 0;
|
||||
|
||||
_tsplitpath_s(*FullPath, lpDrive, 4, NULL, 0, lpFile, _MAX_FNAME, lpExt, _MAX_EXT);
|
||||
lpDrive[2] = (TCHAR)'\\';
|
||||
lpDrive[3] = (TCHAR)'\0';
|
||||
|
||||
// Watch the directory for file creation and deletion.
|
||||
|
||||
dwChangeHandles[0] = FindFirstChangeNotification(
|
||||
*FullPath, // directory to watch
|
||||
TRUE, // watch the subtree
|
||||
FILE_NOTIFY_CHANGE_FILE_NAME | FILE_NOTIFY_CHANGE_LAST_WRITE |
|
||||
FILE_NOTIFY_CHANGE_CREATION | FILE_NOTIFY_CHANGE_ATTRIBUTES | FILE_NOTIFY_CHANGE_SIZE// watch for generic file changes
|
||||
); // watch last write or file size change
|
||||
|
||||
if (dwChangeHandles[0] == INVALID_HANDLE_VALUE)
|
||||
{
|
||||
UE_LOG(LogTemp, Warning, TEXT("\n ERROR: FindFirstChangeNotification function failed.\n"));
|
||||
//ExitProcess(GetLastError());
|
||||
}
|
||||
|
||||
// Watch the subtree for directory creation and deletion.
|
||||
|
||||
dwChangeHandles[1] = FindFirstChangeNotification(
|
||||
lpDrive, // directory to watch
|
||||
TRUE, // watch the subtree
|
||||
FILE_NOTIFY_CHANGE_DIR_NAME); // watch dir name changes
|
||||
|
||||
if (dwChangeHandles[1] == INVALID_HANDLE_VALUE)
|
||||
{
|
||||
UE_LOG(LogTemp, Warning, TEXT("\n ERROR: FindFirstChangeNotification function failed.\n"));
|
||||
//ExitProcess(GetLastError());
|
||||
}
|
||||
|
||||
// Make a final validation check on our handles.
|
||||
if ((dwChangeHandles[0] == NULL) || (dwChangeHandles[1] == NULL))
|
||||
{
|
||||
UE_LOG(LogTemp, Warning, TEXT("\n ERROR: Unexpected NULL from FindFirstChangeNotification.\n"));
|
||||
//ExitProcess(GetLastError());
|
||||
}
|
||||
const FString DrivePath = FString(lpDrive);
|
||||
FString FileString;
|
||||
FString DirectoryString;
|
||||
const UObject* WatcherDelegate = WatcherPtr->Delegate;
|
||||
|
||||
//Wait while the runnable pointer hasn't been set
|
||||
|
||||
TotalWatchers++;
|
||||
UE_LOG(LogTemp, Log, TEXT("\nStarting Watcher loop %d...\n"), TotalWatchers);
|
||||
|
||||
while (WatcherPtr->ShouldRun) //Watcher.Runnable->Finished == false
|
||||
{
|
||||
// Wait for notification.
|
||||
//UE_LOG(LogTemp, Log, TEXT("\nWaiting for notification...\n"));
|
||||
|
||||
dwWaitStatus = WaitForMultipleObjects(2, dwChangeHandles,
|
||||
FALSE, INFINITE);
|
||||
|
||||
if (!WatcherPtr->ShouldRun)
|
||||
{
|
||||
UE_LOG(LogTemp, Log, TEXT("\nStop called while sleeping\n"));
|
||||
break;
|
||||
}
|
||||
if (!WatcherDelegate->IsValidLowLevel())
|
||||
{
|
||||
UE_LOG(LogTemp, Warning, TEXT("\nInvalid Watcher Delegate, exiting watch\n"));
|
||||
break;
|
||||
}
|
||||
|
||||
switch (dwWaitStatus)
|
||||
{
|
||||
case WAIT_OBJECT_0:
|
||||
|
||||
ReadDirectoryChangesW(dwChangeHandles[0], (LPVOID)&strFileNotifyInfo, sizeof(strFileNotifyInfo), FALSE, FILE_NOTIFY_CHANGE_LAST_WRITE, &dwBytesReturned, NULL, NULL);
|
||||
//UE_LOG(LogTemp, Warning, TEXT("Received info about: %s"), strFileNotifyInfo->FileName);
|
||||
|
||||
FileString = FString(strFileNotifyInfo[0].FileNameLength, strFileNotifyInfo[0].FileName);
|
||||
|
||||
// A file was created, renamed, or deleted in the directory.
|
||||
// Refresh this directory and restart the notification.
|
||||
|
||||
|
||||
|
||||
WFULambdaRunnable::RunShortLambdaOnGameThread([FullPath, FileString, WatcherDelegate]()
|
||||
{
|
||||
if (WatcherDelegate->GetClass()->ImplementsInterface(UWFUFolderWatchInterface::StaticClass()))
|
||||
{
|
||||
FString FilePath = FString::Printf(TEXT("%s\\%s"), *FullPath, *FileString);
|
||||
((IWFUFolderWatchInterface*)WatcherDelegate)->Execute_OnFileChanged((UObject*)WatcherDelegate, FileString, FilePath);
|
||||
}
|
||||
});
|
||||
if (FindNextChangeNotification(dwChangeHandles[0]) == FALSE)
|
||||
{
|
||||
UE_LOG(LogTemp, Warning, TEXT("\n ERROR: FindNextChangeNotification function failed.\n"));
|
||||
return;
|
||||
}
|
||||
break;
|
||||
|
||||
case WAIT_OBJECT_0 + 1:
|
||||
|
||||
// A directory was created, renamed, or deleted.
|
||||
// Refresh the tree and restart the notification.
|
||||
|
||||
ReadDirectoryChangesW(dwChangeHandles[1], (LPVOID)&strFileNotifyInfo, sizeof(strFileNotifyInfo), FALSE, FILE_NOTIFY_CHANGE_LAST_WRITE, &dwBytesReturned, NULL, NULL);
|
||||
DirectoryString = FString(strFileNotifyInfo[0].FileNameLength, strFileNotifyInfo[0].FileName);
|
||||
|
||||
WFULambdaRunnable::RunShortLambdaOnGameThread([FullPath, WatcherDelegate, DirectoryString]()
|
||||
{
|
||||
if (WatcherDelegate->GetClass()->ImplementsInterface(UWFUFolderWatchInterface::StaticClass()))
|
||||
{
|
||||
FString ChangedDirectoryPath = FString::Printf(TEXT("%s\\%s"), *FullPath, *DirectoryString);
|
||||
((IWFUFolderWatchInterface*)WatcherDelegate)->Execute_OnDirectoryChanged((UObject*)WatcherDelegate, DirectoryString, ChangedDirectoryPath);
|
||||
}
|
||||
});
|
||||
if (FindNextChangeNotification(dwChangeHandles[1]) == FALSE)
|
||||
{
|
||||
UE_LOG(LogTemp, Warning, TEXT("\n ERROR: FindNextChangeNotification function failed.\n"));
|
||||
return;
|
||||
}
|
||||
break;
|
||||
|
||||
case WAIT_TIMEOUT:
|
||||
|
||||
// A timeout occurred, this would happen if some value other
|
||||
// than INFINITE is used in the Wait call and no changes occur.
|
||||
// In a single-threaded environment you might not want an
|
||||
// INFINITE wait.
|
||||
|
||||
UE_LOG(LogTemp, Warning, TEXT("\nNo changes in the timeout period.\n"));
|
||||
break;
|
||||
|
||||
default:
|
||||
UE_LOG(LogTemp, Warning, TEXT("\n ERROR: Unhandled dwWaitStatus.\n"));
|
||||
return;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
TotalWatchers--;
|
||||
UE_LOG(LogTemp, Log, TEXT("\n Watcher loop stopped, total now: %d.\n"), TotalWatchers);
|
||||
}
|
||||
|
||||
#include "HideWindowsPlatformTypes.h"
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,10 @@
|
||||
// Copyright 1998-2015 Epic Games, Inc. All Rights Reserved.
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "CoreUObject.h"
|
||||
#include "EngineMinimal.h"
|
||||
|
||||
#include "IWindowsFileUtility.h"
|
||||
#include "WFULambdaRunnable.h"
|
||||
#include "WFUFileListInterface.h"
|
||||
#include "WFUFolderWatchInterface.h"
|
||||
@@ -0,0 +1,36 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "ModuleManager.h"
|
||||
|
||||
|
||||
/**
|
||||
* The public interface to this module. In most cases, this interface is only public to sibling modules
|
||||
* within this plugin.
|
||||
*/
|
||||
class IWindowsFileUtility : public IModuleInterface
|
||||
{
|
||||
|
||||
public:
|
||||
|
||||
/**
|
||||
* Singleton-like access to this module's interface. This is just for convenience!
|
||||
* Beware of calling this during the shutdown phase, though. Your module might have been unloaded already.
|
||||
*
|
||||
* @return Returns singleton instance, loading the module on demand if needed
|
||||
*/
|
||||
static inline IWindowsFileUtility& Get()
|
||||
{
|
||||
return FModuleManager::LoadModuleChecked< IWindowsFileUtility >("WindowsFileUtility");
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks to see if this module is loaded and ready. It is only valid to call Get() if IsAvailable() returns true.
|
||||
*
|
||||
* @return True if the module is loaded and ready to use
|
||||
*/
|
||||
static inline bool IsAvailable()
|
||||
{
|
||||
return FModuleManager::Get().IsModuleLoaded("WindowsFileUtility");
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,43 @@
|
||||
// Fill out your copyright notice in the Description page of Project Settings.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "WFUFileListInterface.generated.h"
|
||||
|
||||
UINTERFACE(MinimalAPI)
|
||||
class UWFUFileListInterface : public UInterface
|
||||
{
|
||||
GENERATED_UINTERFACE_BODY()
|
||||
};
|
||||
|
||||
class WINDOWSFILEUTILITY_API IWFUFileListInterface
|
||||
{
|
||||
GENERATED_IINTERFACE_BODY()
|
||||
|
||||
public:
|
||||
|
||||
/**
|
||||
* Called when a file has been found inside the folder of choice
|
||||
* @param FileName of the found file.
|
||||
* @param Size in bytes of the found file.
|
||||
* @param FilePath of the file that was found
|
||||
*/
|
||||
UFUNCTION(BlueprintNativeEvent, Category = FolderWatchEvent)
|
||||
void OnListFileFound(const FString& FileName, int32 ByteCount, const FString& FilePath);
|
||||
|
||||
/**
|
||||
* Called when a directory has been found inside the folder of choice
|
||||
* @param DirectoryName of the found directory.
|
||||
* @param FilePath of the file that was found
|
||||
*/
|
||||
UFUNCTION(BlueprintNativeEvent, Category = FolderWatchEvent)
|
||||
void OnListDirectoryFound(const FString& DirectoryName, const FString& FilePath);
|
||||
|
||||
/**
|
||||
* Called when the listing operation has completed.
|
||||
* @param DirectoryPath Path of the directory
|
||||
* @param Files array of files found
|
||||
*/
|
||||
UFUNCTION(BlueprintNativeEvent, Category = FolderWatchEvent)
|
||||
void OnListDone(const FString& DirectoryPath, const TArray<FString>& Files, const TArray<FString>& Folders);
|
||||
};
|
||||
@@ -0,0 +1,32 @@
|
||||
// Fill out your copyright notice in the Description page of Project Settings.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "WFUFolderWatchInterface.generated.h"
|
||||
|
||||
UINTERFACE(MinimalAPI)
|
||||
class UWFUFolderWatchInterface : public UInterface
|
||||
{
|
||||
GENERATED_UINTERFACE_BODY()
|
||||
};
|
||||
|
||||
class WINDOWSFILEUTILITY_API IWFUFolderWatchInterface
|
||||
{
|
||||
GENERATED_IINTERFACE_BODY()
|
||||
|
||||
public:
|
||||
|
||||
/**
|
||||
* Called when a file inside the folder has changed
|
||||
* @param FilePath Path of the file that has changed
|
||||
*/
|
||||
UFUNCTION(BlueprintNativeEvent, Category = FolderWatchEvent)
|
||||
void OnFileChanged(const FString& FileName, const FString& FilePath);
|
||||
|
||||
/**
|
||||
* Called when a directory inside the folder has changed
|
||||
* @param FilePath Path of the file that has changed
|
||||
*/
|
||||
UFUNCTION(BlueprintNativeEvent, Category = FolderWatchEvent)
|
||||
void OnDirectoryChanged(const FString& DirectoryName, const FString& DirectoryPath);
|
||||
};
|
||||
@@ -0,0 +1,71 @@
|
||||
// Copyright 1998-2018 Epic Games, Inc. All Rights Reserved.
|
||||
|
||||
#pragma once
|
||||
|
||||
/*
|
||||
Long duration lambda wrapper, which are generally not supported by the taskgraph system. New thread per lambda and they will auto-delete upon
|
||||
completion.
|
||||
*/
|
||||
class WINDOWSFILEUTILITY_API WFULambdaRunnable : public FRunnable
|
||||
{
|
||||
private:
|
||||
/** Thread to run the worker FRunnable on */
|
||||
FRunnableThread* Thread;
|
||||
|
||||
// Used to give each thread a unique stat group
|
||||
static uint64 ThreadNumber;
|
||||
|
||||
//Lambda function pointer
|
||||
TFunction< void()> FunctionPointer;
|
||||
|
||||
// A queued threadpool used to run lambda's in the background. This has lazy initialization and is meant to be used with
|
||||
// - AddLambdaToQueue
|
||||
// - RemoveLAmbdaFromQueue
|
||||
static FQueuedThreadPool* ThreadPool;
|
||||
public:
|
||||
//Constructor / Destructor
|
||||
WFULambdaRunnable(TFunction< void()> InFunction);
|
||||
virtual ~WFULambdaRunnable();
|
||||
|
||||
// Begin FRunnable interface.
|
||||
virtual uint32 Run() override;
|
||||
virtual void Exit() override;
|
||||
// End FRunnable interface
|
||||
|
||||
// Initializes the queued thread pool. This is called lazily when the first task is added to the queue
|
||||
// but can also be called by hand to initialize with a specific number of threads. The default number
|
||||
// of threads is FPlatformMisc::NumberOfIOWorkerThreadsToSpawn() which last I checked was hard-coded
|
||||
// at 4. <NOTE> that if you want to call this by hand, you need to do so before ever calling AddLambdaToQueue.
|
||||
static void InitThreadPool(int32 NumberOfThreads);
|
||||
|
||||
/** Makes sure this thread has stopped properly */
|
||||
void EnsureCompletion();
|
||||
|
||||
// Runs the passed lambda on the background thread, new thread per call
|
||||
static WFULambdaRunnable* RunLambdaOnBackGroundThread(TFunction< void()> InFunction);
|
||||
|
||||
// Adds a lambda to be ran on the queued thread pool. Returns a pointer to IQueuedWork which
|
||||
// can be used to later remove the queued job from the pool assuming it hasn't been processed.
|
||||
static IQueuedWork* AddLambdaToQueue(TFunction< void()> InFunction);
|
||||
|
||||
// Removes a lambda from the thread queue
|
||||
static bool RemoveLambdaFromQueue(IQueuedWork* Work);
|
||||
|
||||
// Runs a short lambda on the game thread via task graph system
|
||||
static FGraphEventRef RunShortLambdaOnGameThread(TFunction< void()> InFunction);
|
||||
|
||||
private:
|
||||
// This was yanked from Engine/Source/Runtime/Core/Public/Async/Async.h (originally called AsyncPool(..)). FQueuedThreadPool doesn't have
|
||||
// much documentation, so using the engine code as reference, pretty much everyone seems to use this templated function to queue up work.
|
||||
// It was modified to return an IQueuedWork instead of a TFuture to be more convenient for actually removing items from the queue.
|
||||
template<typename ResultType>
|
||||
static IQueuedWork* AsyncLambdaPool(FQueuedThreadPool& ThreadPool, TFunction<ResultType()> Function, TFunction<void()> CompletionCallback = TFunction<void()>())
|
||||
{
|
||||
TPromise<ResultType> Promise(MoveTemp(CompletionCallback));
|
||||
TFuture<ResultType> Future = Promise.GetFuture();
|
||||
IQueuedWork* Work = new TAsyncQueuedWork<ResultType>(MoveTemp(Function), MoveTemp(Promise));
|
||||
ThreadPool.AddQueuedWork(Work);
|
||||
return Work;
|
||||
}
|
||||
};
|
||||
|
||||
@@ -0,0 +1,74 @@
|
||||
#pragma once
|
||||
|
||||
#include "WFULambdaRunnable.h"
|
||||
#include "WindowsFileUtilityFunctionLibrary.generated.h"
|
||||
|
||||
//Struct to Track which delegate is watching files
|
||||
struct FWatcher
|
||||
{
|
||||
UObject* Delegate;
|
||||
|
||||
FString Path;
|
||||
|
||||
WFULambdaRunnable* Runnable = nullptr;
|
||||
|
||||
FThreadSafeBool ShouldRun = true;
|
||||
|
||||
};
|
||||
|
||||
inline bool operator==(const FWatcher& lhs, const FWatcher& rhs)
|
||||
{
|
||||
return lhs.Delegate == rhs.Delegate;
|
||||
}
|
||||
|
||||
UCLASS(ClassGroup = WindowsFileUtility, Blueprintable)
|
||||
class WINDOWSFILEUTILITY_API UWindowsFileUtilityFunctionLibrary : public UBlueprintFunctionLibrary
|
||||
{
|
||||
GENERATED_UCLASS_BODY()
|
||||
|
||||
|
||||
UFUNCTION(BlueprintCallable, Category = WindowsFileUtility)
|
||||
static bool DoesFileExist(const FString& FullPath);
|
||||
|
||||
/*Expects full path including name. you can use this function to rename files.*/
|
||||
UFUNCTION(BlueprintCallable, Category = WindowsFileUtility)
|
||||
static bool MoveFileTo(const FString& From, const FString& To);
|
||||
|
||||
/*Expects full path including folder name.*/
|
||||
UFUNCTION(BlueprintCallable, Category = WindowsFileUtility)
|
||||
static bool CreateDirectoryAt(const FString& FullPath);
|
||||
|
||||
/*Deletes file (not directory). Expects full path.*/
|
||||
UFUNCTION(BlueprintCallable, Category = WindowsFileUtility)
|
||||
static bool DeleteFileAt(const FString& FullPath);
|
||||
|
||||
/*Deletes empty folders only. Expects full path.*/
|
||||
UFUNCTION(BlueprintCallable, Category = WindowsFileUtility)
|
||||
static bool DeleteEmptyFolder(const FString& FullPath);
|
||||
|
||||
/*Dangerous function, not exposed to blueprint. */
|
||||
UFUNCTION(BlueprintCallable, Category = WindowsFileUtility)
|
||||
static bool DeleteFolderRecursively(const FString& FullPath);
|
||||
|
||||
/** Watch a folder for change. WatcherDelegate should respond to FolderWatchInterface*/
|
||||
UFUNCTION(BlueprintCallable, Category = WindowsFileUtility)
|
||||
static void WatchFolder(const FString& FullPath, UObject* WatcherDelegate);
|
||||
|
||||
/** Stop watching a folder for change. WatcherDelegate should respond to FolderWatchInterface*/
|
||||
UFUNCTION(BlueprintCallable, Category = WindowsFileUtility)
|
||||
static void StopWatchingFolder(const FString& FullPath, UObject* WatcherDelegate);
|
||||
|
||||
/** List the contents, expects UFileListInterface*/
|
||||
UFUNCTION(BlueprintCallable, Category = WindowsFileUtility)
|
||||
static void ListContentsOfFolder(const FString& FullPath, UObject* ListDelegate);
|
||||
|
||||
//Convenience C++ callback
|
||||
static void ListContentsOfFolderToCallback(const FString& FullPath, TFunction<void(const TArray<FString>&, const TArray<FString>&)> OnListCompleteCallback);
|
||||
|
||||
//Todo: add watch folder with threadsafe boolean passthrough
|
||||
//static void ListContentsOfFolderToCallback(const FString& FullPath, TFunction<void(const TArray<FString>&, const TArray<FString>&)> OnListCompleteCallback);
|
||||
|
||||
private:
|
||||
static void WatchFolderOnBgThread(const FString& FullPath, const FWatcher* Watcher);
|
||||
static TMap<FString, TArray<FWatcher>> Watchers;
|
||||
};
|
||||
@@ -0,0 +1,51 @@
|
||||
// Copyright 1998-2015 Epic Games, Inc. All Rights Reserved.
|
||||
|
||||
using UnrealBuildTool;
|
||||
using System.IO;
|
||||
|
||||
public class WindowsFileUtility : ModuleRules
|
||||
{
|
||||
public WindowsFileUtility(ReadOnlyTargetRules Target) : base(Target)
|
||||
{
|
||||
PublicIncludePaths.AddRange(
|
||||
new string[] {
|
||||
"WindowsFileUtility/Public"
|
||||
// ... add public include paths required here ...
|
||||
}
|
||||
);
|
||||
|
||||
PrivateIncludePaths.AddRange(
|
||||
new string[] {
|
||||
"WindowsFileUtility/Private",
|
||||
// ... add other private include paths required here ...
|
||||
}
|
||||
);
|
||||
|
||||
|
||||
PublicDependencyModuleNames.AddRange(
|
||||
new string[]
|
||||
{
|
||||
"Core",
|
||||
// ... add other public dependencies that you statically link with here ...
|
||||
}
|
||||
);
|
||||
|
||||
|
||||
PrivateDependencyModuleNames.AddRange(
|
||||
new string[]
|
||||
{
|
||||
"CoreUObject",
|
||||
"Engine",
|
||||
// ... add private dependencies that you statically link with here ...
|
||||
}
|
||||
);
|
||||
|
||||
DynamicallyLoadedModuleNames.AddRange(
|
||||
new string[]
|
||||
{
|
||||
// ... add any modules that your module loads dynamically here ...
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,100 @@
|
||||
#include "ZipUtilityPrivatePCH.h"
|
||||
#include "ZipFileFunctionLibrary.h"
|
||||
#include "SevenZipCallbackHandler.h"
|
||||
|
||||
void SevenZipCallbackHandler::OnProgress(const TString& archivePath, uint64 bytes)
|
||||
{
|
||||
const UObject* interfaceDelegate = ProgressDelegate;
|
||||
const uint64 bytesConst = bytes;
|
||||
const FString pathConst = FString(archivePath.c_str());
|
||||
|
||||
if (bytes > 0) {
|
||||
const float ProgressPercentage = ((double)((TotalBytes)-(BytesLeft - bytes)) / (double)TotalBytes) * 100;
|
||||
|
||||
UZipFileFunctionLibrary::RunLambdaOnGameThread([interfaceDelegate, pathConst, ProgressPercentage, bytesConst]
|
||||
{
|
||||
//UE_LOG(LogClass, Log, TEXT("Progress: %d bytes"), progress);
|
||||
((IZipUtilityInterface*)interfaceDelegate)->Execute_OnProgress((UObject*)interfaceDelegate, pathConst, ProgressPercentage, bytesConst);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
void SevenZipCallbackHandler::OnDone(const TString& archivePath)
|
||||
{
|
||||
const UObject* interfaceDelegate = ProgressDelegate;
|
||||
const FString pathConst = FString(archivePath.c_str());
|
||||
|
||||
UZipFileFunctionLibrary::RunLambdaOnGameThread([pathConst, interfaceDelegate]
|
||||
{
|
||||
//UE_LOG(LogClass, Log, TEXT("All Done!"));
|
||||
((IZipUtilityInterface*)interfaceDelegate)->Execute_OnDone((UObject*)interfaceDelegate, pathConst, EZipUtilityCompletionState::SUCCESS);
|
||||
});
|
||||
}
|
||||
|
||||
void SevenZipCallbackHandler::OnFileDone(const TString& archivePath, const TString& filePath, uint64 bytes)
|
||||
{
|
||||
const UObject* interfaceDelegate = ProgressDelegate;
|
||||
const FString pathConst = FString(archivePath.c_str());
|
||||
const FString filePathConst = FString(filePath.c_str());
|
||||
const uint64 bytesConst = bytes;
|
||||
|
||||
UZipFileFunctionLibrary::RunLambdaOnGameThread([interfaceDelegate, pathConst, filePathConst, bytesConst]
|
||||
{
|
||||
//UE_LOG(LogClass, Log, TEXT("File Done: %s, %d bytes"), filePathConst.c_str(), bytesConst);
|
||||
((IZipUtilityInterface*)interfaceDelegate)->Execute_OnFileDone((UObject*)interfaceDelegate, pathConst, filePathConst);
|
||||
});
|
||||
|
||||
//Handle byte decrementing
|
||||
if (bytes > 0) {
|
||||
BytesLeft -= bytes;
|
||||
const float ProgressPercentage = ((double)(TotalBytes - BytesLeft) / (double)TotalBytes) * 100;
|
||||
|
||||
UZipFileFunctionLibrary::RunLambdaOnGameThread([interfaceDelegate, pathConst, ProgressPercentage, bytes]
|
||||
{
|
||||
//UE_LOG(LogClass, Log, TEXT("Progress: %d bytes"), progress);
|
||||
((IZipUtilityInterface*)interfaceDelegate)->Execute_OnProgress((UObject*)interfaceDelegate, pathConst, ProgressPercentage, bytes);
|
||||
});
|
||||
}
|
||||
}
|
||||
void SevenZipCallbackHandler::OnStartWithTotal(const TString& archivePath, unsigned __int64 totalBytes)
|
||||
{
|
||||
TotalBytes = totalBytes;
|
||||
BytesLeft = TotalBytes;
|
||||
|
||||
const UObject* interfaceDelegate = ProgressDelegate;
|
||||
const uint64 bytesConst = TotalBytes;
|
||||
const FString pathConst = FString(archivePath.c_str());
|
||||
|
||||
UZipFileFunctionLibrary::RunLambdaOnGameThread([interfaceDelegate, pathConst, bytesConst]
|
||||
{
|
||||
//UE_LOG(LogClass, Log, TEXT("Starting with %d bytes"), bytesConst);
|
||||
((IZipUtilityInterface*)interfaceDelegate)->Execute_OnStartProcess((UObject*)interfaceDelegate, pathConst, bytesConst);
|
||||
});
|
||||
}
|
||||
void SevenZipCallbackHandler::OnFileFound(const TString& archivePath, const TString& filePath, int size)
|
||||
{
|
||||
const UObject* interfaceDelegate = ProgressDelegate;
|
||||
const uint64 bytesConst = TotalBytes;
|
||||
const FString pathString = FString(archivePath.c_str());
|
||||
const FString fileString = FString(filePath.c_str());
|
||||
|
||||
UZipFileFunctionLibrary::RunLambdaOnGameThread([interfaceDelegate, pathString, fileString, bytesConst]
|
||||
{
|
||||
((IZipUtilityInterface*)interfaceDelegate)->Execute_OnFileFound((UObject*)interfaceDelegate, pathString, fileString, bytesConst);
|
||||
});
|
||||
}
|
||||
void SevenZipCallbackHandler::OnListingDone(const TString& archivePath)
|
||||
{
|
||||
const UObject* interfaceDelegate = ProgressDelegate;
|
||||
const FString pathString = FString(archivePath.c_str());
|
||||
|
||||
UZipFileFunctionLibrary::RunLambdaOnGameThread([interfaceDelegate, pathString]
|
||||
{
|
||||
((IZipUtilityInterface*)interfaceDelegate)->Execute_OnDone((UObject*)interfaceDelegate, pathString, EZipUtilityCompletionState::SUCCESS);
|
||||
});
|
||||
}
|
||||
|
||||
bool SevenZipCallbackHandler::OnCheckBreak()
|
||||
{
|
||||
return bCancelOperation;
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
|
||||
#include "ZipUtilityPrivatePCH.h"
|
||||
#include "WindowsFileUtilityFunctionLibrary.h"
|
||||
#include "ZipFileFunctionLibrary.h"
|
||||
#include "ZULambdaDelegate.h"
|
||||
|
||||
UZULambdaDelegate::UZULambdaDelegate()
|
||||
{
|
||||
OnDoneCallback = nullptr;
|
||||
OnProgressCallback = nullptr;
|
||||
}
|
||||
|
||||
void UZULambdaDelegate::SetOnDoneCallback(TFunction<void()> InOnDoneCallback)
|
||||
{
|
||||
OnDoneCallback = InOnDoneCallback;
|
||||
}
|
||||
|
||||
void UZULambdaDelegate::SetOnProgessCallback(TFunction<void(float)> InOnProgressCallback)
|
||||
{
|
||||
OnProgressCallback = InOnProgressCallback;
|
||||
}
|
||||
|
||||
void UZULambdaDelegate::OnProgress_Implementation(const FString& archive, float percentage, int32 bytes)
|
||||
{
|
||||
if (OnProgressCallback != nullptr)
|
||||
{
|
||||
OnProgressCallback(percentage);
|
||||
}
|
||||
}
|
||||
|
||||
void UZULambdaDelegate::OnDone_Implementation(const FString& archive, EZipUtilityCompletionState CompletionState)
|
||||
{
|
||||
if (OnDoneCallback != nullptr)
|
||||
{
|
||||
OnDoneCallback();
|
||||
}
|
||||
}
|
||||
|
||||
void UZULambdaDelegate::OnStartProcess_Implementation(const FString& archive, int32 bytes)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void UZULambdaDelegate::OnFileDone_Implementation(const FString& archive, const FString& file)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void UZULambdaDelegate::OnFileFound_Implementation(const FString& archive, const FString& file, int32 size)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "Object.h"
|
||||
#include "ZULambdaDelegate.generated.h"
|
||||
|
||||
UCLASS()
|
||||
class ZIPUTILITY_API UZULambdaDelegate : public UObject, public IZipUtilityInterface
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
UZULambdaDelegate();
|
||||
public:
|
||||
void SetOnDoneCallback(TFunction<void()> InOnDoneCallback);
|
||||
void SetOnProgessCallback(TFunction<void(float)> InOnProgressCallback);
|
||||
|
||||
protected:
|
||||
//Zip utility interface
|
||||
virtual void OnProgress_Implementation(const FString& archive, float percentage, int32 bytes) override;
|
||||
virtual void OnDone_Implementation(const FString& archive, EZipUtilityCompletionState CompletionState) override;
|
||||
virtual void OnStartProcess_Implementation(const FString& archive, int32 bytes) override;
|
||||
virtual void OnFileDone_Implementation(const FString& archive, const FString& file) override;
|
||||
virtual void OnFileFound_Implementation(const FString& archive, const FString& file, int32 size) override;
|
||||
|
||||
TFunction<void()> OnDoneCallback;
|
||||
TFunction<void(float)> OnProgressCallback;
|
||||
};
|
||||
@@ -0,0 +1,51 @@
|
||||
#include "ZipUtilityPrivatePCH.h"
|
||||
#include "ZipFileFunctionInternalCallback.h"
|
||||
|
||||
UZipFileFunctionInternalCallback::UZipFileFunctionInternalCallback()
|
||||
{
|
||||
CompressionFormat = ZipUtilityCompressionFormat::COMPRESSION_FORMAT_UNKNOWN;
|
||||
DestinationFolder = FString();
|
||||
File = FString();
|
||||
FileIndex = 0;
|
||||
Callback = NULL;
|
||||
}
|
||||
|
||||
void UZipFileFunctionInternalCallback::OnFileFound_Implementation(const FString& archive, const FString& fileIn, int32 size)
|
||||
{
|
||||
if (!bFileFound && fileIn.ToLower().Contains(File.ToLower()))
|
||||
{
|
||||
TArray<int32> FileIndices = { FileIndex };
|
||||
|
||||
if (bUnzipto)
|
||||
{
|
||||
UZipFileFunctionLibrary::UnzipFilesTo(FileIndices, archive, DestinationFolder, Callback, CompressionFormat);
|
||||
}
|
||||
else
|
||||
{
|
||||
UZipFileFunctionLibrary::UnzipFiles(FileIndices, archive, Callback, CompressionFormat);
|
||||
}
|
||||
|
||||
if (bSingleFile)
|
||||
{
|
||||
bFileFound = true;
|
||||
}
|
||||
}
|
||||
|
||||
FileIndex++;
|
||||
}
|
||||
|
||||
void UZipFileFunctionInternalCallback::SetCallback(const FString& FileName, UObject* CallbackIn, TEnumAsByte<ZipUtilityCompressionFormat> CompressionFormatIn /*= ZipUtilityCompressionFormat::COMPRESSION_FORMAT_UNKNOWN*/)
|
||||
{
|
||||
File = FileName;
|
||||
Callback = CallbackIn;
|
||||
CompressionFormat = CompressionFormatIn;
|
||||
FileIndex = 0;
|
||||
}
|
||||
|
||||
void UZipFileFunctionInternalCallback::SetCallback(const FString& FileName, const FString& DestinationFolderIn, UObject* CallbackIn, TEnumAsByte<ZipUtilityCompressionFormat> CompressionFormatIn /*= ZipUtilityCompressionFormat::COMPRESSION_FORMAT_UNKNOWN*/)
|
||||
{
|
||||
SetCallback(FileName, CallbackIn, CompressionFormatIn);
|
||||
|
||||
bUnzipto = true;
|
||||
DestinationFolder = DestinationFolderIn;
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
#pragma once
|
||||
|
||||
#include "ZipUtilityInterface.h"
|
||||
#include "ZipFileFunctionInternalCallback.generated.h"
|
||||
|
||||
/**
|
||||
* This is used to provide a callback for unzipping single files, it ends up getting called from the ListFiles lambda.
|
||||
*/
|
||||
UCLASS(ClassGroup = ZipUtility)
|
||||
class ZIPUTILITY_API UZipFileFunctionInternalCallback : public UObject, public IZipUtilityInterface
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
private:
|
||||
|
||||
/** Compression format used to unzip */
|
||||
UPROPERTY(Transient)
|
||||
TEnumAsByte<ZipUtilityCompressionFormat> CompressionFormat;
|
||||
|
||||
/** Path of the file */
|
||||
UPROPERTY(Transient)
|
||||
FString File;
|
||||
|
||||
UPROPERTY(Transient)
|
||||
FString DestinationFolder;
|
||||
|
||||
/** Current File index parsed */
|
||||
UPROPERTY(Transient)
|
||||
int32 FileIndex = 0;
|
||||
|
||||
/** Callback object */
|
||||
UPROPERTY(Transient)
|
||||
UObject* Callback;
|
||||
|
||||
UPROPERTY(Transient)
|
||||
bool bSingleFile;
|
||||
|
||||
UPROPERTY(Transient)
|
||||
bool bFileFound;
|
||||
|
||||
UPROPERTY(Transient)
|
||||
bool bUnzipto;
|
||||
|
||||
public:
|
||||
UZipFileFunctionInternalCallback();
|
||||
|
||||
//IZipUtilityInterface overrides
|
||||
virtual void OnProgress_Implementation(const FString& archive, float percentage, int32 bytes) override {};
|
||||
|
||||
virtual void OnDone_Implementation(const FString& archive, EZipUtilityCompletionState CompletionState) override {};
|
||||
|
||||
virtual void OnStartProcess_Implementation(const FString& archive, int32 bytes) override {};
|
||||
|
||||
virtual void OnFileDone_Implementation(const FString& archive, const FString& file) override {
|
||||
UE_LOG(LogTemp, Log, TEXT("OnFileDone_Implementation"));
|
||||
};
|
||||
|
||||
virtual void OnFileFound_Implementation(const FString& archive, const FString& fileIn, int32 size) override;
|
||||
|
||||
void SetCallback(const FString& FileName, UObject* CallbackIn, TEnumAsByte<ZipUtilityCompressionFormat> CompressionFormatIn = ZipUtilityCompressionFormat::COMPRESSION_FORMAT_UNKNOWN);
|
||||
void SetCallback(const FString& FileName, const FString& DestinationFolder, UObject* CallbackIn, TEnumAsByte<ZipUtilityCompressionFormat> CompressionFormatIn = ZipUtilityCompressionFormat::COMPRESSION_FORMAT_UNKNOWN);
|
||||
|
||||
FORCEINLINE bool GetSingleFile() const { return bSingleFile; }
|
||||
FORCEINLINE void SetSingleFile(bool val) { bSingleFile = val; }
|
||||
};
|
||||
@@ -0,0 +1,481 @@
|
||||
#include "ZipUtilityPrivatePCH.h"
|
||||
|
||||
#include "ZipFileFunctionLibrary.h"
|
||||
#include "ZipFileFunctionInternalCallback.h"
|
||||
#include "ListCallback.h"
|
||||
#include "ProgressCallback.h"
|
||||
#include "IPluginManager.h"
|
||||
#include "WFULambdaRunnable.h"
|
||||
#include "ZULambdaDelegate.h"
|
||||
#include "SevenZipCallbackHandler.h"
|
||||
#include "WindowsFileUtilityFunctionLibrary.h"
|
||||
|
||||
#include "7zpp.h"
|
||||
|
||||
using namespace SevenZip;
|
||||
|
||||
//Private Namespace
|
||||
namespace{
|
||||
|
||||
//Threaded Lambda convenience wrappers - Task graph is only suitable for short duration lambdas, but doesn't incur thread overhead
|
||||
FGraphEventRef RunLambdaOnAnyThread(TFunction< void()> InFunction)
|
||||
{
|
||||
return FFunctionGraphTask::CreateAndDispatchWhenReady(InFunction, TStatId(), nullptr, ENamedThreads::AnyThread);
|
||||
}
|
||||
|
||||
//Uses proper threading, for any task that may run longer than about 2 seconds.
|
||||
void RunLongLambdaOnAnyThread(TFunction< void()> InFunction)
|
||||
{
|
||||
WFULambdaRunnable::RunLambdaOnBackGroundThread(InFunction);
|
||||
}
|
||||
|
||||
// Run the lambda on the queued threadpool
|
||||
IQueuedWork* RunLambdaOnThreadPool(TFunction< void()> InFunction)
|
||||
{
|
||||
return WFULambdaRunnable::AddLambdaToQueue(InFunction);
|
||||
}
|
||||
|
||||
//Private static vars
|
||||
SevenZipLibrary SZLib;
|
||||
|
||||
//Utility functions
|
||||
FString PluginRootFolder()
|
||||
{
|
||||
return IPluginManager::Get().FindPlugin("ZipUtility")->GetBaseDir();
|
||||
//return FPaths::ConvertRelativePathToFull(FPaths::GameDir());
|
||||
}
|
||||
|
||||
FString DLLPath()
|
||||
{
|
||||
#if _WIN64
|
||||
|
||||
FString PlatformString = FString(TEXT("Win64"));
|
||||
#else
|
||||
FString PlatformString = FString(TEXT("Win32"));
|
||||
#endif
|
||||
//Swap these to change which license you wish to fall under for zip-utility
|
||||
|
||||
FString DLLString = FString("7z.dll"); //Using 7z.dll: GNU LGPL + unRAR restriction
|
||||
//FString dllString = FString("7za.dll"); //Using 7za.dll: GNU LGPL license, crucially doesn't support .zip out of the box
|
||||
|
||||
return FPaths::ConvertRelativePathToFull(FPaths::Combine(*PluginRootFolder(), TEXT("ThirdParty/7zpp/dll"), *PlatformString, *DLLString));
|
||||
}
|
||||
|
||||
FString ReversePathSlashes(FString forwardPath)
|
||||
{
|
||||
return forwardPath.Replace(TEXT("/"), TEXT("\\"));
|
||||
}
|
||||
|
||||
bool IsValidDirectory(FString& Directory, FString& FileName, const FString& Path)
|
||||
{
|
||||
bool Found = Path.Split(TEXT("/"), &Directory, &FileName, ESearchCase::IgnoreCase, ESearchDir::FromEnd);
|
||||
//try a back split
|
||||
if (!Found)
|
||||
{
|
||||
Found = Path.Split(TEXT("\\"), &Directory, &FileName, ESearchCase::IgnoreCase, ESearchDir::FromEnd);
|
||||
}
|
||||
|
||||
//No valid Directory found
|
||||
if (!Found)
|
||||
return false;
|
||||
else
|
||||
return true;
|
||||
}
|
||||
|
||||
SevenZip::CompressionLevelEnum libZipLevelFromUELevel(ZipUtilityCompressionLevel ueLevel) {
|
||||
switch (ueLevel)
|
||||
{
|
||||
case COMPRESSION_LEVEL_NONE:
|
||||
return SevenZip::CompressionLevel::None;
|
||||
case COMPRESSION_LEVEL_FAST:
|
||||
return SevenZip::CompressionLevel::Fast;
|
||||
case COMPRESSION_LEVEL_NORMAL:
|
||||
return SevenZip::CompressionLevel::Normal;
|
||||
default:
|
||||
return SevenZip::CompressionLevel::None;
|
||||
}
|
||||
}
|
||||
|
||||
SevenZip::CompressionFormatEnum libZipFormatFromUEFormat(ZipUtilityCompressionFormat UeFormat) {
|
||||
switch (UeFormat)
|
||||
{
|
||||
case COMPRESSION_FORMAT_UNKNOWN:
|
||||
return CompressionFormat::Unknown;
|
||||
case COMPRESSION_FORMAT_SEVEN_ZIP:
|
||||
return CompressionFormat::SevenZip;
|
||||
case COMPRESSION_FORMAT_ZIP:
|
||||
return CompressionFormat::Zip;
|
||||
case COMPRESSION_FORMAT_GZIP:
|
||||
return CompressionFormat::GZip;
|
||||
case COMPRESSION_FORMAT_BZIP2:
|
||||
return CompressionFormat::BZip2;
|
||||
case COMPRESSION_FORMAT_RAR:
|
||||
return CompressionFormat::Rar;
|
||||
case COMPRESSION_FORMAT_TAR:
|
||||
return CompressionFormat::Tar;
|
||||
case COMPRESSION_FORMAT_ISO:
|
||||
return CompressionFormat::Iso;
|
||||
case COMPRESSION_FORMAT_CAB:
|
||||
return CompressionFormat::Cab;
|
||||
case COMPRESSION_FORMAT_LZMA:
|
||||
return CompressionFormat::Lzma;
|
||||
case COMPRESSION_FORMAT_LZMA86:
|
||||
return CompressionFormat::Lzma86;
|
||||
default:
|
||||
return CompressionFormat::Unknown;
|
||||
}
|
||||
}
|
||||
|
||||
FString defaultExtensionFromUEFormat(ZipUtilityCompressionFormat ueFormat)
|
||||
{
|
||||
switch (ueFormat)
|
||||
{
|
||||
case COMPRESSION_FORMAT_UNKNOWN:
|
||||
return FString(TEXT(".dat"));
|
||||
case COMPRESSION_FORMAT_SEVEN_ZIP:
|
||||
return FString(TEXT(".7z"));
|
||||
case COMPRESSION_FORMAT_ZIP:
|
||||
return FString(TEXT(".zip"));
|
||||
case COMPRESSION_FORMAT_GZIP:
|
||||
return FString(TEXT(".gz"));
|
||||
case COMPRESSION_FORMAT_BZIP2:
|
||||
return FString(TEXT(".bz2"));
|
||||
case COMPRESSION_FORMAT_RAR:
|
||||
return FString(TEXT(".rar"));
|
||||
case COMPRESSION_FORMAT_TAR:
|
||||
return FString(TEXT(".tar"));
|
||||
case COMPRESSION_FORMAT_ISO:
|
||||
return FString(TEXT(".iso"));
|
||||
case COMPRESSION_FORMAT_CAB:
|
||||
return FString(TEXT(".cab"));
|
||||
case COMPRESSION_FORMAT_LZMA:
|
||||
return FString(TEXT(".lzma"));
|
||||
case COMPRESSION_FORMAT_LZMA86:
|
||||
return FString(TEXT(".lzma86"));
|
||||
default:
|
||||
return FString(TEXT(".dat"));
|
||||
}
|
||||
}
|
||||
|
||||
using namespace std;
|
||||
|
||||
|
||||
|
||||
//Background Thread convenience functions
|
||||
UZipOperation* UnzipFilesOnBGThreadWithFormat(const TArray<int32> FileIndices, const FString& ArchivePath, const FString& DestinationDirectory, const UObject* ProgressDelegate, ZipUtilityCompressionFormat Format)
|
||||
{
|
||||
UZipOperation* ZipOperation = NewObject<UZipOperation>();
|
||||
|
||||
IQueuedWork* Work = RunLambdaOnThreadPool([ProgressDelegate, FileIndices, ArchivePath, DestinationDirectory, Format, ZipOperation]
|
||||
{
|
||||
SevenZipCallbackHandler PrivateCallback;
|
||||
PrivateCallback.ProgressDelegate = (UObject*)ProgressDelegate;
|
||||
ZipOperation->SetCallbackHandler(&PrivateCallback);
|
||||
|
||||
//UE_LOG(LogClass, Log, TEXT("path is: %s"), *path);
|
||||
SevenZipExtractor Extractor(SZLib, *ArchivePath);
|
||||
|
||||
|
||||
if (Format == COMPRESSION_FORMAT_UNKNOWN)
|
||||
{
|
||||
if (!Extractor.DetectCompressionFormat())
|
||||
{
|
||||
UE_LOG(LogTemp, Log, TEXT("auto-compression detection did not succeed, passing in unknown format to 7zip library."));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Extractor.SetCompressionFormat(libZipFormatFromUEFormat(Format));
|
||||
}
|
||||
|
||||
// Extract indices
|
||||
const int32 NumberFiles = FileIndices.Num();
|
||||
unsigned int* Indices = new unsigned int[NumberFiles];
|
||||
|
||||
for (int32 idx = 0; idx < NumberFiles; idx++)
|
||||
{
|
||||
Indices[idx] = FileIndices[idx];
|
||||
}
|
||||
|
||||
// Perform the extraction
|
||||
Extractor.ExtractFilesFromArchive(Indices, NumberFiles, *DestinationDirectory, &PrivateCallback);
|
||||
|
||||
// Clean up the indices
|
||||
delete Indices;
|
||||
|
||||
// Null out the callback handler now that we're exiting
|
||||
ZipOperation->SetCallbackHandler(nullptr);
|
||||
});
|
||||
|
||||
ZipOperation->SetThreadPoolWorker(Work);
|
||||
return ZipOperation;
|
||||
}
|
||||
|
||||
//Background Thread convenience functions
|
||||
UZipOperation* UnzipOnBGThreadWithFormat(const FString& ArchivePath, const FString& DestinationDirectory, const UObject* ProgressDelegate, ZipUtilityCompressionFormat Format)
|
||||
{
|
||||
UZipOperation* ZipOperation = NewObject<UZipOperation>();
|
||||
|
||||
IQueuedWork* Work = RunLambdaOnThreadPool([ProgressDelegate, ArchivePath, DestinationDirectory, Format, ZipOperation]
|
||||
{
|
||||
SevenZipCallbackHandler PrivateCallback;
|
||||
PrivateCallback.ProgressDelegate = (UObject*)ProgressDelegate;
|
||||
ZipOperation->SetCallbackHandler(&PrivateCallback);
|
||||
|
||||
//UE_LOG(LogClass, Log, TEXT("path is: %s"), *path);
|
||||
SevenZipExtractor Extractor(SZLib, *ArchivePath);
|
||||
|
||||
if (Format == COMPRESSION_FORMAT_UNKNOWN)
|
||||
{
|
||||
if (!Extractor.DetectCompressionFormat())
|
||||
{
|
||||
UE_LOG(LogTemp, Log, TEXT("auto-compression detection did not succeed, passing in unknown format to 7zip library."));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Extractor.SetCompressionFormat(libZipFormatFromUEFormat(Format));
|
||||
}
|
||||
|
||||
Extractor.ExtractArchive(*DestinationDirectory, &PrivateCallback);
|
||||
|
||||
// Null out the callback handler now that we're exiting
|
||||
ZipOperation->SetCallbackHandler(nullptr);
|
||||
});
|
||||
|
||||
ZipOperation->SetThreadPoolWorker(Work);
|
||||
return ZipOperation;
|
||||
}
|
||||
|
||||
void ListOnBGThread(const FString& Path, const FString& Directory, const UObject* ListDelegate, ZipUtilityCompressionFormat Format)
|
||||
{
|
||||
//RunLongLambdaOnAnyThread - this shouldn't take long, but if it lags, swap the lambda methods
|
||||
RunLambdaOnAnyThread([ListDelegate, Path, Format, Directory] {
|
||||
SevenZipCallbackHandler PrivateCallback;
|
||||
PrivateCallback.ProgressDelegate = (UObject*)ListDelegate;
|
||||
SevenZipLister Lister(SZLib, *Path);
|
||||
|
||||
if (Format == COMPRESSION_FORMAT_UNKNOWN)
|
||||
{
|
||||
if (!Lister.DetectCompressionFormat())
|
||||
{
|
||||
UE_LOG(LogTemp, Log, TEXT("auto-compression detection did not succeed, passing in unknown format to 7zip library."));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Lister.SetCompressionFormat(libZipFormatFromUEFormat(Format));
|
||||
}
|
||||
|
||||
if (!Lister.ListArchive(&PrivateCallback))
|
||||
{
|
||||
// If ListArchive returned false, it was most likely because the compression format was unsupported
|
||||
// Call OnDone with a failure message, make sure to call this on the game thread.
|
||||
if (IZipUtilityInterface* ZipInterface = Cast<IZipUtilityInterface>((UObject*)ListDelegate))
|
||||
{
|
||||
UE_LOG(LogClass, Warning, TEXT("ZipUtility: Unknown failure for list operation on %s"), *Path);
|
||||
UZipFileFunctionLibrary::RunLambdaOnGameThread([ZipInterface, ListDelegate, Path]
|
||||
{
|
||||
ZipInterface->Execute_OnDone((UObject*)ListDelegate, *Path, EZipUtilityCompletionState::FAILURE_UNKNOWN);
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
UZipOperation* ZipOnBGThread(const FString& Path, const FString& FileName, const FString& Directory, const UObject* ProgressDelegate, ZipUtilityCompressionFormat UeCompressionformat, ZipUtilityCompressionLevel UeCompressionlevel)
|
||||
{
|
||||
UZipOperation* ZipOperation = NewObject<UZipOperation>();
|
||||
|
||||
IQueuedWork* Work = RunLambdaOnThreadPool([ProgressDelegate, FileName, Path, UeCompressionformat, UeCompressionlevel, Directory, ZipOperation]
|
||||
{
|
||||
SevenZipCallbackHandler PrivateCallback;
|
||||
PrivateCallback.ProgressDelegate = (UObject*)ProgressDelegate;
|
||||
ZipOperation->SetCallbackHandler(&PrivateCallback);
|
||||
|
||||
//Set the zip format
|
||||
ZipUtilityCompressionFormat UeFormat = UeCompressionformat;
|
||||
|
||||
if (UeFormat == COMPRESSION_FORMAT_UNKNOWN)
|
||||
{
|
||||
UeFormat = COMPRESSION_FORMAT_ZIP;
|
||||
}
|
||||
//Disallow creating .rar archives as per unrar restriction, this won't work anyway so redirect to 7z
|
||||
else if (UeFormat == COMPRESSION_FORMAT_RAR)
|
||||
{
|
||||
UE_LOG(LogClass, Warning, TEXT("ZipUtility: Rar compression not supported for creating archives, re-targeting as 7z."));
|
||||
UeFormat = COMPRESSION_FORMAT_SEVEN_ZIP;
|
||||
}
|
||||
|
||||
//concatenate the output filename
|
||||
FString OutputFileName = FString::Printf(TEXT("%s/%s%s"), *Directory, *FileName, *defaultExtensionFromUEFormat(UeFormat));
|
||||
//UE_LOG(LogClass, Log, TEXT("\noutputfile is: <%s>\n path is: <%s>"), *outputFileName, *path);
|
||||
|
||||
SevenZipCompressor compressor(SZLib, *ReversePathSlashes(OutputFileName));
|
||||
compressor.SetCompressionFormat(libZipFormatFromUEFormat(UeFormat));
|
||||
compressor.SetCompressionLevel(libZipLevelFromUELevel(UeCompressionlevel));
|
||||
|
||||
if (PathIsDirectory(*Path))
|
||||
{
|
||||
//UE_LOG(LogClass, Log, TEXT("Compressing Folder"));
|
||||
compressor.CompressDirectory(*ReversePathSlashes(Path), &PrivateCallback);
|
||||
}
|
||||
else
|
||||
{
|
||||
//UE_LOG(LogClass, Log, TEXT("Compressing File"));
|
||||
compressor.CompressFile(*ReversePathSlashes(Path), &PrivateCallback);
|
||||
}
|
||||
|
||||
// Null out the callback handler
|
||||
ZipOperation->SetCallbackHandler(nullptr);
|
||||
//Todo: expand to support zipping up contents of current folder
|
||||
//compressor.CompressFiles(*ReversePathSlashes(path), TEXT("*"), &PrivateCallback);
|
||||
});
|
||||
ZipOperation->SetThreadPoolWorker(Work);
|
||||
return ZipOperation;
|
||||
}
|
||||
|
||||
}//End private namespace
|
||||
|
||||
UZipFileFunctionLibrary::UZipFileFunctionLibrary(const class FObjectInitializer& PCIP)
|
||||
: Super(PCIP)
|
||||
{
|
||||
UE_LOG(LogTemp, Log, TEXT("DLLPath is: %s"), *DLLPath());
|
||||
SZLib.Load(*DLLPath());
|
||||
}
|
||||
|
||||
UZipFileFunctionLibrary::~UZipFileFunctionLibrary()
|
||||
{
|
||||
SZLib.Free();
|
||||
}
|
||||
|
||||
bool UZipFileFunctionLibrary::UnzipFileNamed(const FString& archivePath, const FString& Name, UObject* ZipUtilityInterfaceDelegate, ZipUtilityCompressionFormat format /*= COMPRESSION_FORMAT_UNKNOWN*/)
|
||||
{
|
||||
UZipFileFunctionInternalCallback* InternalCallback = NewObject<UZipFileFunctionInternalCallback>();
|
||||
InternalCallback->SetFlags(RF_MarkAsRootSet);
|
||||
InternalCallback->SetCallback(Name, ZipUtilityInterfaceDelegate, format);
|
||||
|
||||
ListFilesInArchive(archivePath, InternalCallback, format);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool UZipFileFunctionLibrary::UnzipFileNamedTo(const FString& archivePath, const FString& Name, const FString& destinationPath, UObject* ZipUtilityInterfaceDelegate, ZipUtilityCompressionFormat format /*= COMPRESSION_FORMAT_UNKNOWN*/)
|
||||
{
|
||||
UZipFileFunctionInternalCallback* InternalCallback = NewObject<UZipFileFunctionInternalCallback>();
|
||||
InternalCallback->SetFlags(RF_MarkAsRootSet);
|
||||
InternalCallback->SetCallback(Name, destinationPath, ZipUtilityInterfaceDelegate, format);
|
||||
|
||||
ListFilesInArchive(archivePath, InternalCallback, format);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
UZipOperation* UZipFileFunctionLibrary::UnzipFilesTo(const TArray<int32> fileIndices, const FString & archivePath, const FString & destinationPath, UObject * ZipUtilityInterfaceDelegate, ZipUtilityCompressionFormat format)
|
||||
{
|
||||
return UnzipFilesOnBGThreadWithFormat(fileIndices, archivePath, destinationPath, ZipUtilityInterfaceDelegate, format);
|
||||
}
|
||||
|
||||
UZipOperation* UZipFileFunctionLibrary::UnzipFiles(const TArray<int32> fileIndices, const FString & ArchivePath, UObject * ZipUtilityInterfaceDelegate, ZipUtilityCompressionFormat format)
|
||||
{
|
||||
FString Directory;
|
||||
FString FileName;
|
||||
|
||||
//Check Directory validity
|
||||
if (!IsValidDirectory(Directory, FileName, ArchivePath))
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
|
||||
if (fileIndices.Num() == 0)
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return UnzipFilesTo(fileIndices, ArchivePath, Directory, ZipUtilityInterfaceDelegate, format);
|
||||
}
|
||||
|
||||
UZipOperation* UZipFileFunctionLibrary::Unzip(const FString& ArchivePath, UObject* ZipUtilityInterfaceDelegate, ZipUtilityCompressionFormat Format /*= COMPRESSION_FORMAT_UNKNOWN*/)
|
||||
{
|
||||
FString Directory;
|
||||
FString FileName;
|
||||
|
||||
//Check Directory validity
|
||||
if (!IsValidDirectory(Directory, FileName, ArchivePath) || !UWindowsFileUtilityFunctionLibrary::DoesFileExist(ArchivePath))
|
||||
{
|
||||
((IZipUtilityInterface*)ZipUtilityInterfaceDelegate)->Execute_OnDone((UObject*)ZipUtilityInterfaceDelegate, ArchivePath, EZipUtilityCompletionState::FAILURE_NOT_FOUND);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return UnzipTo(ArchivePath, Directory, ZipUtilityInterfaceDelegate, Format);
|
||||
}
|
||||
|
||||
UZipOperation* UZipFileFunctionLibrary::UnzipWithLambda(const FString& ArchivePath, TFunction<void()> OnDoneCallback, TFunction<void(float)> OnProgressCallback, ZipUtilityCompressionFormat Format)
|
||||
{
|
||||
UZULambdaDelegate* LambdaDelegate = NewObject<UZULambdaDelegate>();
|
||||
LambdaDelegate->AddToRoot();
|
||||
LambdaDelegate->SetOnDoneCallback([LambdaDelegate, OnDoneCallback]()
|
||||
{
|
||||
OnDoneCallback();
|
||||
LambdaDelegate->RemoveFromRoot();
|
||||
});
|
||||
LambdaDelegate->SetOnProgessCallback(OnProgressCallback);
|
||||
|
||||
return Unzip(ArchivePath, LambdaDelegate, Format);
|
||||
}
|
||||
|
||||
|
||||
UZipOperation* UZipFileFunctionLibrary::UnzipTo(const FString& ArchivePath, const FString& DestinationPath, UObject* ZipUtilityInterfaceDelegate, ZipUtilityCompressionFormat Format)
|
||||
{
|
||||
return UnzipOnBGThreadWithFormat(ArchivePath, DestinationPath, ZipUtilityInterfaceDelegate, Format);
|
||||
}
|
||||
|
||||
UZipOperation* UZipFileFunctionLibrary::Zip(const FString& ArchivePath, UObject* ZipUtilityInterfaceDelegate, ZipUtilityCompressionFormat Format, TEnumAsByte<ZipUtilityCompressionLevel> Level)
|
||||
{
|
||||
FString Directory;
|
||||
FString FileName;
|
||||
|
||||
//Check Directory and File validity
|
||||
if (!IsValidDirectory(Directory, FileName, ArchivePath) || !UWindowsFileUtilityFunctionLibrary::DoesFileExist(ArchivePath))
|
||||
{
|
||||
((IZipUtilityInterface*)ZipUtilityInterfaceDelegate)->Execute_OnDone((UObject*)ZipUtilityInterfaceDelegate, ArchivePath, EZipUtilityCompletionState::FAILURE_NOT_FOUND);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return ZipOnBGThread(ArchivePath, FileName, Directory, ZipUtilityInterfaceDelegate, Format, Level);
|
||||
}
|
||||
|
||||
UZipOperation* UZipFileFunctionLibrary::ZipWithLambda(const FString& ArchivePath, TFunction<void()> OnDoneCallback, TFunction<void(float)> OnProgressCallback /*= nullptr*/, ZipUtilityCompressionFormat Format /*= COMPRESSION_FORMAT_UNKNOWN*/, TEnumAsByte<ZipUtilityCompressionLevel> Level /*=COMPRESSION_LEVEL_NORMAL*/)
|
||||
{
|
||||
UZULambdaDelegate* LambdaDelegate = NewObject<UZULambdaDelegate>();
|
||||
LambdaDelegate->AddToRoot();
|
||||
LambdaDelegate->SetOnDoneCallback([OnDoneCallback, LambdaDelegate]()
|
||||
{
|
||||
OnDoneCallback();
|
||||
LambdaDelegate->RemoveFromRoot();
|
||||
});
|
||||
LambdaDelegate->SetOnProgessCallback(OnProgressCallback);
|
||||
|
||||
return Zip(ArchivePath, LambdaDelegate, Format);
|
||||
}
|
||||
|
||||
bool UZipFileFunctionLibrary::ListFilesInArchive(const FString& path, UObject* ListDelegate, ZipUtilityCompressionFormat format)
|
||||
{
|
||||
FString Directory;
|
||||
FString FileName;
|
||||
|
||||
//Check Directory validity
|
||||
if (!IsValidDirectory(Directory, FileName, path))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
ListOnBGThread(path, Directory, ListDelegate, format);
|
||||
return true;
|
||||
}
|
||||
|
||||
FGraphEventRef UZipFileFunctionLibrary::RunLambdaOnGameThread(TFunction< void()> InFunction)
|
||||
{
|
||||
return FFunctionGraphTask::CreateAndDispatchWhenReady(InFunction, TStatId(), nullptr, ENamedThreads::GameThread);
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
#include "ZipUtilityPrivatePCH.h"
|
||||
#include "SevenZipCallbackHandler.h"
|
||||
#include "WFULambdaRunnable.h"
|
||||
#include "ZipOperation.h"
|
||||
|
||||
UZipOperation::UZipOperation()
|
||||
{
|
||||
CallbackHandler = nullptr;
|
||||
}
|
||||
|
||||
void UZipOperation::StopOperation()
|
||||
{
|
||||
if (ThreadPoolWork != nullptr)
|
||||
{
|
||||
WFULambdaRunnable::RemoveLambdaFromQueue(ThreadPoolWork);
|
||||
}
|
||||
|
||||
if (CallbackHandler != nullptr)
|
||||
{
|
||||
CallbackHandler->bCancelOperation = true;
|
||||
CallbackHandler = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void UZipOperation::SetCallbackHandler(SevenZipCallbackHandler* Handler)
|
||||
{
|
||||
CallbackHandler = Handler;
|
||||
}
|
||||
|
||||
void UZipOperation::SetThreadPoolWorker(IQueuedWork* Work)
|
||||
{
|
||||
ThreadPoolWork = Work;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
#pragma once
|
||||
|
||||
#include "ZipUtilityPrivatePCH.h"
|
||||
#include "ZipUtilityInterface.h"
|
||||
|
||||
UZipUtilityInterface::UZipUtilityInterface(const class FObjectInitializer& PCIP)
|
||||
: Super(PCIP)
|
||||
{
|
||||
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
// Copyright 1998-2015 Epic Games, Inc. All Rights Reserved.
|
||||
|
||||
#include "ZipUtilityPrivatePCH.h"
|
||||
|
||||
#define LOCTEXT_NAMESPACE "FZipUtilityModule"
|
||||
|
||||
void FZipUtilityModule::StartupModule()
|
||||
{
|
||||
// This code will execute after your module is loaded into memory; the exact timing is specified in the .uplugin file per-module
|
||||
}
|
||||
|
||||
void FZipUtilityModule::ShutdownModule()
|
||||
{
|
||||
// This function may be called during shutdown to clean up your module. For modules that support dynamic reloading,
|
||||
// we call this function before unloading the module.
|
||||
}
|
||||
|
||||
#undef LOCTEXT_NAMESPACE
|
||||
|
||||
IMPLEMENT_MODULE(FZipUtilityModule, ZipUtility)
|
||||
@@ -0,0 +1,12 @@
|
||||
// Copyright 1998-2015 Epic Games, Inc. All Rights Reserved.
|
||||
|
||||
#include "ZipUtilityPlugin.h"
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "CoreUObject.h"
|
||||
#include "EngineMinimal.h"
|
||||
|
||||
// You should place include statements to your module's private header files here. You only need to
|
||||
// add includes for headers that are used in most of your module's source files though.
|
||||
|
||||
#include "ZipFileFunctionLibrary.h"
|
||||
@@ -0,0 +1,28 @@
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "7zpp.h"
|
||||
#include "ListCallback.h"
|
||||
#include "ProgressCallback.h"
|
||||
|
||||
using namespace SevenZip;
|
||||
/**
|
||||
* Forwards events from the 7zpp library to the UE4 listener.
|
||||
*/
|
||||
class ZIPUTILITY_API SevenZipCallbackHandler : public ListCallback, public ProgressCallback
|
||||
{
|
||||
public:
|
||||
|
||||
virtual void OnProgress(const TString& archivePath, uint64 bytes) override;
|
||||
virtual void OnDone(const TString& archivePath) override;
|
||||
virtual void OnFileDone(const TString& archivePath, const TString& filePath, uint64 bytes) override;
|
||||
virtual void OnStartWithTotal(const TString& archivePath, unsigned __int64 totalBytes) override;
|
||||
virtual void OnFileFound(const TString& archivePath, const TString& filePath, int size) override;
|
||||
virtual void OnListingDone(const TString& archivePath) override;
|
||||
virtual bool OnCheckBreak() override;
|
||||
|
||||
uint64 BytesLeft = 0;
|
||||
uint64 TotalBytes = 0;
|
||||
UObject* ProgressDelegate = nullptr;
|
||||
FThreadSafeBool bCancelOperation = false;
|
||||
};
|
||||
@@ -0,0 +1,102 @@
|
||||
#pragma once
|
||||
|
||||
#include "ZipUtilityInterface.h"
|
||||
#include "ZipOperation.h"
|
||||
#include "ZipFileFunctionLibrary.generated.h"
|
||||
|
||||
UENUM(BlueprintType)
|
||||
enum ZipUtilityCompressionFormat
|
||||
{
|
||||
COMPRESSION_FORMAT_UNKNOWN,
|
||||
COMPRESSION_FORMAT_SEVEN_ZIP,
|
||||
COMPRESSION_FORMAT_ZIP,
|
||||
COMPRESSION_FORMAT_GZIP,
|
||||
COMPRESSION_FORMAT_BZIP2,
|
||||
COMPRESSION_FORMAT_RAR,
|
||||
COMPRESSION_FORMAT_TAR,
|
||||
COMPRESSION_FORMAT_ISO,
|
||||
COMPRESSION_FORMAT_CAB,
|
||||
COMPRESSION_FORMAT_LZMA,
|
||||
COMPRESSION_FORMAT_LZMA86
|
||||
};
|
||||
|
||||
|
||||
UENUM(BlueprintType)
|
||||
enum ZipUtilityCompressionLevel
|
||||
{
|
||||
COMPRESSION_LEVEL_NONE,
|
||||
COMPRESSION_LEVEL_FAST,
|
||||
COMPRESSION_LEVEL_NORMAL
|
||||
};
|
||||
|
||||
class SevenZipCallbackHandler;
|
||||
class UZipFileFunctionInternalCallback;
|
||||
|
||||
/**
|
||||
A blueprint function library encapsulating all zip operations for both C++ and blueprint use.
|
||||
For some operations a UZipOperation object may be returned, if you're interested in it, ensure
|
||||
you guard it from garbage collection by e.g. storing it as a UProperty, otherwise you may safely
|
||||
ignore it.
|
||||
*/
|
||||
|
||||
UCLASS(ClassGroup = ZipUtility, Blueprintable)
|
||||
class ZIPUTILITY_API UZipFileFunctionLibrary : public UBlueprintFunctionLibrary
|
||||
{
|
||||
GENERATED_UCLASS_BODY()
|
||||
|
||||
public:
|
||||
~UZipFileFunctionLibrary();
|
||||
|
||||
/* Unzips file in archive containing Name via ListFilesInArchive/UnzipFiles. Automatically determines compression if unknown. Calls ZipUtilityInterface progress events. */
|
||||
UFUNCTION(BlueprintCallable, Category = ZipUtility)
|
||||
static bool UnzipFileNamed(const FString& archivePath, const FString& Name, UObject* ZipUtilityInterfaceDelegate, ZipUtilityCompressionFormat format = COMPRESSION_FORMAT_UNKNOWN);
|
||||
|
||||
/* Unzips file in archive containing Name at destination path via ListFilesInArchive/UnzipFilesTo. Automatically determines compression if unknown. Calls ZipUtilityInterface progress events. */
|
||||
UFUNCTION(BlueprintCallable, Category = ZipUtility)
|
||||
static bool UnzipFileNamedTo(const FString& archivePath, const FString& Name, const FString& destinationPath, UObject* ZipUtilityInterfaceDelegate, ZipUtilityCompressionFormat format = COMPRESSION_FORMAT_UNKNOWN);
|
||||
|
||||
/* Unzips the given file indexes in archive at destination path. Automatically determines compression if unknown. Calls ZipUtilityInterface progress events. */
|
||||
UFUNCTION(BlueprintCallable, Category = ZipUtility)
|
||||
static UZipOperation* UnzipFilesTo(const TArray<int32> fileIndices, const FString& archivePath, const FString& destinationPath, UObject* ZipUtilityInterfaceDelegate, ZipUtilityCompressionFormat format = COMPRESSION_FORMAT_UNKNOWN);
|
||||
|
||||
/* Unzips the given file indexes in archive at current path. Automatically determines compression if unknown. Calls ZipUtilityInterface progress events. */
|
||||
UFUNCTION(BlueprintCallable, Category = ZipUtility)
|
||||
static UZipOperation* UnzipFiles(const TArray<int32> fileIndices, const FString& ArchivePath, UObject* ZipUtilityInterfaceDelegate, ZipUtilityCompressionFormat format = COMPRESSION_FORMAT_UNKNOWN);
|
||||
|
||||
/* Unzips archive at current path. Automatically determines compression if unknown. Calls ZipUtilityInterface progress events. */
|
||||
UFUNCTION(BlueprintCallable, Category = ZipUtility)
|
||||
static UZipOperation* Unzip(const FString& ArchivePath, UObject* ZipUtilityInterfaceDelegate, ZipUtilityCompressionFormat Format = COMPRESSION_FORMAT_UNKNOWN);
|
||||
|
||||
/* Lambda C++ simple variant*/
|
||||
static UZipOperation* UnzipWithLambda( const FString& ArchivePath,
|
||||
TFunction<void()> OnDoneCallback,
|
||||
TFunction<void(float)> OnProgressCallback = nullptr,
|
||||
ZipUtilityCompressionFormat format = COMPRESSION_FORMAT_UNKNOWN);
|
||||
|
||||
/* Unzips archive at destination path. Automatically determines compression if unknown. Calls ZipUtilityInterface progress events. */
|
||||
UFUNCTION(BlueprintCallable, Category = ZipUtility)
|
||||
static UZipOperation* UnzipTo(const FString& ArchivePath, const FString& DestinationPath, UObject* ZipUtilityInterfaceDelegate, ZipUtilityCompressionFormat format = COMPRESSION_FORMAT_UNKNOWN);
|
||||
|
||||
/* Compresses the file or folder given at path and places the file in the same root folder. Calls ZipUtilityInterface progress events. Not all formats are supported for compression.*/
|
||||
UFUNCTION(BlueprintCallable, Category = ZipUtility)
|
||||
static UZipOperation* Zip( const FString& FileOrFolderPath,
|
||||
UObject* ZipUtilityInterfaceDelegate,
|
||||
ZipUtilityCompressionFormat Format = COMPRESSION_FORMAT_SEVEN_ZIP,
|
||||
TEnumAsByte<ZipUtilityCompressionLevel> Level = COMPRESSION_LEVEL_NORMAL);
|
||||
|
||||
/* Lambda C++ simple variant*/
|
||||
static UZipOperation* ZipWithLambda( const FString& ArchivePath,
|
||||
TFunction<void()> OnDoneCallback,
|
||||
TFunction<void(float)> OnProgressCallback = nullptr,
|
||||
ZipUtilityCompressionFormat Format = COMPRESSION_FORMAT_UNKNOWN,
|
||||
TEnumAsByte<ZipUtilityCompressionLevel> Level = COMPRESSION_LEVEL_NORMAL);
|
||||
|
||||
|
||||
/*Queries Archive content list, calls ZipUtilityInterface list events (OnFileFound)*/
|
||||
UFUNCTION(BlueprintCallable, Category = ZipUtility)
|
||||
static bool ListFilesInArchive(const FString& ArchivePath, UObject* ZipUtilityInterfaceDelegate, ZipUtilityCompressionFormat format = COMPRESSION_FORMAT_UNKNOWN);
|
||||
|
||||
static FGraphEventRef RunLambdaOnGameThread(TFunction< void()> InFunction);
|
||||
};
|
||||
|
||||
|
||||
@@ -0,0 +1,42 @@
|
||||
#pragma once
|
||||
#include "Object.h"
|
||||
#include "ZipOperation.generated.h"
|
||||
|
||||
class SevenZipCallbackHandler;
|
||||
/**
|
||||
* Used to track a zip/unzip operation on the WFULambdaRunnable ThreadPool and allows the ability to terminate the
|
||||
* operation early.
|
||||
*/
|
||||
UCLASS(BlueprintType)
|
||||
class ZIPUTILITY_API UZipOperation : public UObject
|
||||
{
|
||||
GENERATED_BODY()
|
||||
public:
|
||||
UZipOperation();
|
||||
|
||||
// Stops this zip/unzip if it is still valid. This stop event can occur in two places:
|
||||
// 1. The ThreadPool queue, if it can be stopped here then the operation has not yet started.
|
||||
// 2. The SevenZip layer, once the operation has started, it can be canceled while still running.
|
||||
// Note that calling this carries no guarantees of a successful stop, the end result might be one of:
|
||||
// * The file still got extracted (you were too late)
|
||||
// * The file was never extracted (caught it on time)
|
||||
// * The file was created but is of zero size (oops)
|
||||
// * The file was created, is of non-zero size but is not all there (cut off in the middle)
|
||||
// So, it could be a good idea to do some file housekeeping afterward.
|
||||
UFUNCTION(BlueprintCallable, Category = "Zip Operation")
|
||||
void StopOperation();
|
||||
|
||||
// Set the callback handler
|
||||
void SetCallbackHandler(SevenZipCallbackHandler* Handler);
|
||||
|
||||
// Set the queued work
|
||||
void SetThreadPoolWorker(IQueuedWork* Work);
|
||||
|
||||
private:
|
||||
// A pointer to the callback for this operation. Once the operation completes, this
|
||||
// pointer will become invalid.
|
||||
SevenZipCallbackHandler* CallbackHandler;
|
||||
|
||||
// The work that was queued on the async threadpool in WFULambdaRunnable
|
||||
IQueuedWork* ThreadPoolWork;
|
||||
};
|
||||
@@ -0,0 +1,62 @@
|
||||
// Copyright 1998-2015 Epic Games, Inc. All Rights Reserved.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "ZipUtilityInterface.generated.h"
|
||||
|
||||
UENUM(BlueprintType)
|
||||
enum EZipUtilityCompletionState
|
||||
{
|
||||
SUCCESS,
|
||||
FAILURE_NOT_FOUND,
|
||||
FAILURE_UNKNOWN
|
||||
};
|
||||
|
||||
|
||||
UINTERFACE(MinimalAPI)
|
||||
class UZipUtilityInterface : public UInterface
|
||||
{
|
||||
GENERATED_UINTERFACE_BODY()
|
||||
};
|
||||
|
||||
class ZIPUTILITY_API IZipUtilityInterface
|
||||
{
|
||||
GENERATED_IINTERFACE_BODY()
|
||||
|
||||
public:
|
||||
|
||||
/**
|
||||
* Called during process as it completes. Currently updates on per file progress.
|
||||
* @param percentage - percentage done
|
||||
*/
|
||||
UFUNCTION(BlueprintNativeEvent, Category = ZipUtilityProgressEvents)
|
||||
void OnProgress(const FString& archive, float percentage, int32 bytes);
|
||||
|
||||
/**
|
||||
* Called when whole process is complete (e.g. unzipping completed on archive)
|
||||
*/
|
||||
UFUNCTION(BlueprintNativeEvent, Category = ZipUtilityProgressEvents)
|
||||
void OnDone(const FString& archive, EZipUtilityCompletionState CompletionState);
|
||||
|
||||
/**
|
||||
* Called at beginning of process (NB this only supports providing size information for up to 2gb) TODO: fix 32bit BP size issue
|
||||
*/
|
||||
UFUNCTION(BlueprintNativeEvent, Category = ZipUtilityProgressEvents)
|
||||
void OnStartProcess(const FString& archive, int32 bytes);
|
||||
|
||||
/**
|
||||
* Called when file process is complete
|
||||
* @param path - path of the file that finished
|
||||
*/
|
||||
UFUNCTION(BlueprintNativeEvent, Category = ZipUtilityProgressEvents)
|
||||
void OnFileDone(const FString& archive, const FString& file);
|
||||
|
||||
|
||||
/**
|
||||
* Called when a file is found in the archive (e.g. listing the entries in the archive)
|
||||
* @param path - path of file
|
||||
* @param size - compressed size
|
||||
*/
|
||||
UFUNCTION(BlueprintNativeEvent, Category = ZipUtilityListEvents)
|
||||
void OnFileFound(const FString& archive, const FString& file, int32 size);
|
||||
};
|
||||
@@ -0,0 +1,38 @@
|
||||
// Copyright 1998-2015 Epic Games, Inc. All Rights Reserved.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "ModuleManager.h"
|
||||
|
||||
class ZIPUTILITY_API FZipUtilityModule : public IModuleInterface
|
||||
{
|
||||
public:
|
||||
|
||||
//CHN:
|
||||
//Maybe we need to use a public interface to fetch the module methods for C++? get a reference to zipfile etc
|
||||
|
||||
/**
|
||||
* Singleton-like access to this module's interface. This is just for convenience!
|
||||
* Beware of calling this during the shutdown phase, though. Your module might have been unloaded already.
|
||||
*
|
||||
* @return Returns singleton instance, loading the module on demand if needed
|
||||
*/
|
||||
static inline FZipUtilityModule& Get()
|
||||
{
|
||||
return FModuleManager::LoadModuleChecked< FZipUtilityModule >("ZipUtility");
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks to see if this module is loaded and ready. It is only valid to call Get() if IsAvailable() returns true.
|
||||
*
|
||||
* @return True if the module is loaded and ready to use
|
||||
*/
|
||||
static inline bool IsAvailable()
|
||||
{
|
||||
return FModuleManager::Get().IsModuleLoaded("ZipUtility");
|
||||
}
|
||||
|
||||
/** IModuleInterface implementation */
|
||||
virtual void StartupModule() override;
|
||||
virtual void ShutdownModule() override;
|
||||
};
|
||||
102
Plugins/ZipUtility-ue4/Source/ZipUtility/ZipUtility.Build.cs
Normal file
102
Plugins/ZipUtility-ue4/Source/ZipUtility/ZipUtility.Build.cs
Normal file
@@ -0,0 +1,102 @@
|
||||
// Copyright 1998-2015 Epic Games, Inc. All Rights Reserved.
|
||||
|
||||
using UnrealBuildTool;
|
||||
using System.IO;
|
||||
|
||||
public class ZipUtility : ModuleRules
|
||||
{
|
||||
private string ThirdPartyPath
|
||||
{
|
||||
get { return Path.GetFullPath(Path.Combine(ModuleDirectory, "../../ThirdParty/")); }
|
||||
}
|
||||
private string SevenZppPath
|
||||
{
|
||||
get { return Path.GetFullPath(Path.Combine(ThirdPartyPath, "7zpp")); }
|
||||
}
|
||||
|
||||
private string ATLPath
|
||||
{
|
||||
get { return "C:/Program Files (x86)/Microsoft Visual Studio/2017/Community/VC/Tools/MSVC/14.13.26128/atlmfc"; }
|
||||
}
|
||||
|
||||
public ZipUtility(ReadOnlyTargetRules Target) : base(Target)
|
||||
{
|
||||
bEnableExceptions = true;
|
||||
PublicIncludePaths.AddRange(
|
||||
new string[] {
|
||||
"ZipUtility/Public"
|
||||
// ... add public include paths required here ...
|
||||
}
|
||||
);
|
||||
|
||||
PrivateIncludePaths.AddRange(
|
||||
new string[] {
|
||||
"ZipUtility/Private",
|
||||
Path.Combine(SevenZppPath, "Include"),
|
||||
Path.Combine(ATLPath, "include"),
|
||||
// ... add other private include paths required here ...
|
||||
}
|
||||
);
|
||||
|
||||
|
||||
PublicDependencyModuleNames.AddRange(
|
||||
new string[]
|
||||
{
|
||||
"Core",
|
||||
"WindowsFileUtility"
|
||||
// ... add other public dependencies that you statically link with here ...
|
||||
}
|
||||
);
|
||||
|
||||
|
||||
PrivateDependencyModuleNames.AddRange(
|
||||
new string[]
|
||||
{
|
||||
"CoreUObject",
|
||||
"Engine",
|
||||
"Slate",
|
||||
"SlateCore",
|
||||
"Projects"
|
||||
// ... add private dependencies that you statically link with here ...
|
||||
}
|
||||
);
|
||||
|
||||
|
||||
DynamicallyLoadedModuleNames.AddRange(
|
||||
new string[]
|
||||
{
|
||||
// ... add any modules that your module loads dynamically here ...
|
||||
}
|
||||
);
|
||||
|
||||
LoadLib(Target);
|
||||
}
|
||||
public bool LoadLib(ReadOnlyTargetRules Target)
|
||||
{
|
||||
bool isLibrarySupported = false;
|
||||
|
||||
if ((Target.Platform == UnrealTargetPlatform.Win64) || (Target.Platform == UnrealTargetPlatform.Win32))
|
||||
{
|
||||
isLibrarySupported = true;
|
||||
|
||||
string PlatformSubPath = (Target.Platform == UnrealTargetPlatform.Win64) ? "Win64" : "Win32";
|
||||
string LibrariesPath = Path.Combine(SevenZppPath, "Lib");
|
||||
string DLLPath = Path.Combine(SevenZppPath, "dll");
|
||||
|
||||
PublicAdditionalLibraries.Add(Path.Combine(LibrariesPath, PlatformSubPath, "atls.lib"));
|
||||
PublicAdditionalLibraries.Add(Path.Combine(LibrariesPath, PlatformSubPath, "7zpp_u.lib"));
|
||||
PublicLibraryPaths.Add(Path.Combine(LibrariesPath, PlatformSubPath));
|
||||
|
||||
PublicDelayLoadDLLs.Add("7z.dll");
|
||||
RuntimeDependencies.Add(new RuntimeDependency(Path.Combine(DLLPath, PlatformSubPath, "7z.dll")));
|
||||
}
|
||||
|
||||
if (isLibrarySupported)
|
||||
{
|
||||
// Include path
|
||||
//PublicIncludePaths.Add(Path.Combine(SevenZppPath, "Include"));
|
||||
}
|
||||
|
||||
return isLibrarySupported;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user