A modded EditSaber for making beat saber maps.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

481 lines
17 KiB

  1. #include "ZipUtilityPrivatePCH.h"
  2. #include "ZipFileFunctionLibrary.h"
  3. #include "ZipFileFunctionInternalCallback.h"
  4. #include "ListCallback.h"
  5. #include "ProgressCallback.h"
  6. #include "IPluginManager.h"
  7. #include "WFULambdaRunnable.h"
  8. #include "ZULambdaDelegate.h"
  9. #include "SevenZipCallbackHandler.h"
  10. #include "WindowsFileUtilityFunctionLibrary.h"
  11. #include "7zpp.h"
  12. using namespace SevenZip;
  13. //Private Namespace
  14. namespace{
  15. //Threaded Lambda convenience wrappers - Task graph is only suitable for short duration lambdas, but doesn't incur thread overhead
  16. FGraphEventRef RunLambdaOnAnyThread(TFunction< void()> InFunction)
  17. {
  18. return FFunctionGraphTask::CreateAndDispatchWhenReady(InFunction, TStatId(), nullptr, ENamedThreads::AnyThread);
  19. }
  20. //Uses proper threading, for any task that may run longer than about 2 seconds.
  21. void RunLongLambdaOnAnyThread(TFunction< void()> InFunction)
  22. {
  23. WFULambdaRunnable::RunLambdaOnBackGroundThread(InFunction);
  24. }
  25. // Run the lambda on the queued threadpool
  26. IQueuedWork* RunLambdaOnThreadPool(TFunction< void()> InFunction)
  27. {
  28. return WFULambdaRunnable::AddLambdaToQueue(InFunction);
  29. }
  30. //Private static vars
  31. SevenZipLibrary SZLib;
  32. //Utility functions
  33. FString PluginRootFolder()
  34. {
  35. return IPluginManager::Get().FindPlugin("ZipUtility")->GetBaseDir();
  36. //return FPaths::ConvertRelativePathToFull(FPaths::GameDir());
  37. }
  38. FString DLLPath()
  39. {
  40. #if _WIN64
  41. FString PlatformString = FString(TEXT("Win64"));
  42. #else
  43. FString PlatformString = FString(TEXT("Win32"));
  44. #endif
  45. //Swap these to change which license you wish to fall under for zip-utility
  46. FString DLLString = FString("7z.dll"); //Using 7z.dll: GNU LGPL + unRAR restriction
  47. //FString dllString = FString("7za.dll"); //Using 7za.dll: GNU LGPL license, crucially doesn't support .zip out of the box
  48. return FPaths::ConvertRelativePathToFull(FPaths::Combine(*PluginRootFolder(), TEXT("ThirdParty/7zpp/dll"), *PlatformString, *DLLString));
  49. }
  50. FString ReversePathSlashes(FString forwardPath)
  51. {
  52. return forwardPath.Replace(TEXT("/"), TEXT("\\"));
  53. }
  54. bool IsValidDirectory(FString& Directory, FString& FileName, const FString& Path)
  55. {
  56. bool Found = Path.Split(TEXT("/"), &Directory, &FileName, ESearchCase::IgnoreCase, ESearchDir::FromEnd);
  57. //try a back split
  58. if (!Found)
  59. {
  60. Found = Path.Split(TEXT("\\"), &Directory, &FileName, ESearchCase::IgnoreCase, ESearchDir::FromEnd);
  61. }
  62. //No valid Directory found
  63. if (!Found)
  64. return false;
  65. else
  66. return true;
  67. }
  68. SevenZip::CompressionLevelEnum libZipLevelFromUELevel(ZipUtilityCompressionLevel ueLevel) {
  69. switch (ueLevel)
  70. {
  71. case COMPRESSION_LEVEL_NONE:
  72. return SevenZip::CompressionLevel::None;
  73. case COMPRESSION_LEVEL_FAST:
  74. return SevenZip::CompressionLevel::Fast;
  75. case COMPRESSION_LEVEL_NORMAL:
  76. return SevenZip::CompressionLevel::Normal;
  77. default:
  78. return SevenZip::CompressionLevel::None;
  79. }
  80. }
  81. SevenZip::CompressionFormatEnum libZipFormatFromUEFormat(ZipUtilityCompressionFormat UeFormat) {
  82. switch (UeFormat)
  83. {
  84. case COMPRESSION_FORMAT_UNKNOWN:
  85. return CompressionFormat::Unknown;
  86. case COMPRESSION_FORMAT_SEVEN_ZIP:
  87. return CompressionFormat::SevenZip;
  88. case COMPRESSION_FORMAT_ZIP:
  89. return CompressionFormat::Zip;
  90. case COMPRESSION_FORMAT_GZIP:
  91. return CompressionFormat::GZip;
  92. case COMPRESSION_FORMAT_BZIP2:
  93. return CompressionFormat::BZip2;
  94. case COMPRESSION_FORMAT_RAR:
  95. return CompressionFormat::Rar;
  96. case COMPRESSION_FORMAT_TAR:
  97. return CompressionFormat::Tar;
  98. case COMPRESSION_FORMAT_ISO:
  99. return CompressionFormat::Iso;
  100. case COMPRESSION_FORMAT_CAB:
  101. return CompressionFormat::Cab;
  102. case COMPRESSION_FORMAT_LZMA:
  103. return CompressionFormat::Lzma;
  104. case COMPRESSION_FORMAT_LZMA86:
  105. return CompressionFormat::Lzma86;
  106. default:
  107. return CompressionFormat::Unknown;
  108. }
  109. }
  110. FString defaultExtensionFromUEFormat(ZipUtilityCompressionFormat ueFormat)
  111. {
  112. switch (ueFormat)
  113. {
  114. case COMPRESSION_FORMAT_UNKNOWN:
  115. return FString(TEXT(".dat"));
  116. case COMPRESSION_FORMAT_SEVEN_ZIP:
  117. return FString(TEXT(".7z"));
  118. case COMPRESSION_FORMAT_ZIP:
  119. return FString(TEXT(".zip"));
  120. case COMPRESSION_FORMAT_GZIP:
  121. return FString(TEXT(".gz"));
  122. case COMPRESSION_FORMAT_BZIP2:
  123. return FString(TEXT(".bz2"));
  124. case COMPRESSION_FORMAT_RAR:
  125. return FString(TEXT(".rar"));
  126. case COMPRESSION_FORMAT_TAR:
  127. return FString(TEXT(".tar"));
  128. case COMPRESSION_FORMAT_ISO:
  129. return FString(TEXT(".iso"));
  130. case COMPRESSION_FORMAT_CAB:
  131. return FString(TEXT(".cab"));
  132. case COMPRESSION_FORMAT_LZMA:
  133. return FString(TEXT(".lzma"));
  134. case COMPRESSION_FORMAT_LZMA86:
  135. return FString(TEXT(".lzma86"));
  136. default:
  137. return FString(TEXT(".dat"));
  138. }
  139. }
  140. using namespace std;
  141. //Background Thread convenience functions
  142. UZipOperation* UnzipFilesOnBGThreadWithFormat(const TArray<int32> FileIndices, const FString& ArchivePath, const FString& DestinationDirectory, const UObject* ProgressDelegate, ZipUtilityCompressionFormat Format)
  143. {
  144. UZipOperation* ZipOperation = NewObject<UZipOperation>();
  145. IQueuedWork* Work = RunLambdaOnThreadPool([ProgressDelegate, FileIndices, ArchivePath, DestinationDirectory, Format, ZipOperation]
  146. {
  147. SevenZipCallbackHandler PrivateCallback;
  148. PrivateCallback.ProgressDelegate = (UObject*)ProgressDelegate;
  149. ZipOperation->SetCallbackHandler(&PrivateCallback);
  150. //UE_LOG(LogClass, Log, TEXT("path is: %s"), *path);
  151. SevenZipExtractor Extractor(SZLib, *ArchivePath);
  152. if (Format == COMPRESSION_FORMAT_UNKNOWN)
  153. {
  154. if (!Extractor.DetectCompressionFormat())
  155. {
  156. UE_LOG(LogTemp, Log, TEXT("auto-compression detection did not succeed, passing in unknown format to 7zip library."));
  157. }
  158. }
  159. else
  160. {
  161. Extractor.SetCompressionFormat(libZipFormatFromUEFormat(Format));
  162. }
  163. // Extract indices
  164. const int32 NumberFiles = FileIndices.Num();
  165. unsigned int* Indices = new unsigned int[NumberFiles];
  166. for (int32 idx = 0; idx < NumberFiles; idx++)
  167. {
  168. Indices[idx] = FileIndices[idx];
  169. }
  170. // Perform the extraction
  171. Extractor.ExtractFilesFromArchive(Indices, NumberFiles, *DestinationDirectory, &PrivateCallback);
  172. // Clean up the indices
  173. delete Indices;
  174. // Null out the callback handler now that we're exiting
  175. ZipOperation->SetCallbackHandler(nullptr);
  176. });
  177. ZipOperation->SetThreadPoolWorker(Work);
  178. return ZipOperation;
  179. }
  180. //Background Thread convenience functions
  181. UZipOperation* UnzipOnBGThreadWithFormat(const FString& ArchivePath, const FString& DestinationDirectory, const UObject* ProgressDelegate, ZipUtilityCompressionFormat Format)
  182. {
  183. UZipOperation* ZipOperation = NewObject<UZipOperation>();
  184. IQueuedWork* Work = RunLambdaOnThreadPool([ProgressDelegate, ArchivePath, DestinationDirectory, Format, ZipOperation]
  185. {
  186. SevenZipCallbackHandler PrivateCallback;
  187. PrivateCallback.ProgressDelegate = (UObject*)ProgressDelegate;
  188. ZipOperation->SetCallbackHandler(&PrivateCallback);
  189. //UE_LOG(LogClass, Log, TEXT("path is: %s"), *path);
  190. SevenZipExtractor Extractor(SZLib, *ArchivePath);
  191. if (Format == COMPRESSION_FORMAT_UNKNOWN)
  192. {
  193. if (!Extractor.DetectCompressionFormat())
  194. {
  195. UE_LOG(LogTemp, Log, TEXT("auto-compression detection did not succeed, passing in unknown format to 7zip library."));
  196. }
  197. }
  198. else
  199. {
  200. Extractor.SetCompressionFormat(libZipFormatFromUEFormat(Format));
  201. }
  202. Extractor.ExtractArchive(*DestinationDirectory, &PrivateCallback);
  203. // Null out the callback handler now that we're exiting
  204. ZipOperation->SetCallbackHandler(nullptr);
  205. });
  206. ZipOperation->SetThreadPoolWorker(Work);
  207. return ZipOperation;
  208. }
  209. void ListOnBGThread(const FString& Path, const FString& Directory, const UObject* ListDelegate, ZipUtilityCompressionFormat Format)
  210. {
  211. //RunLongLambdaOnAnyThread - this shouldn't take long, but if it lags, swap the lambda methods
  212. RunLambdaOnAnyThread([ListDelegate, Path, Format, Directory] {
  213. SevenZipCallbackHandler PrivateCallback;
  214. PrivateCallback.ProgressDelegate = (UObject*)ListDelegate;
  215. SevenZipLister Lister(SZLib, *Path);
  216. if (Format == COMPRESSION_FORMAT_UNKNOWN)
  217. {
  218. if (!Lister.DetectCompressionFormat())
  219. {
  220. UE_LOG(LogTemp, Log, TEXT("auto-compression detection did not succeed, passing in unknown format to 7zip library."));
  221. }
  222. }
  223. else
  224. {
  225. Lister.SetCompressionFormat(libZipFormatFromUEFormat(Format));
  226. }
  227. if (!Lister.ListArchive(&PrivateCallback))
  228. {
  229. // If ListArchive returned false, it was most likely because the compression format was unsupported
  230. // Call OnDone with a failure message, make sure to call this on the game thread.
  231. if (IZipUtilityInterface* ZipInterface = Cast<IZipUtilityInterface>((UObject*)ListDelegate))
  232. {
  233. UE_LOG(LogClass, Warning, TEXT("ZipUtility: Unknown failure for list operation on %s"), *Path);
  234. UZipFileFunctionLibrary::RunLambdaOnGameThread([ZipInterface, ListDelegate, Path]
  235. {
  236. ZipInterface->Execute_OnDone((UObject*)ListDelegate, *Path, EZipUtilityCompletionState::FAILURE_UNKNOWN);
  237. });
  238. }
  239. }
  240. });
  241. }
  242. UZipOperation* ZipOnBGThread(const FString& Path, const FString& FileName, const FString& Directory, const UObject* ProgressDelegate, ZipUtilityCompressionFormat UeCompressionformat, ZipUtilityCompressionLevel UeCompressionlevel)
  243. {
  244. UZipOperation* ZipOperation = NewObject<UZipOperation>();
  245. IQueuedWork* Work = RunLambdaOnThreadPool([ProgressDelegate, FileName, Path, UeCompressionformat, UeCompressionlevel, Directory, ZipOperation]
  246. {
  247. SevenZipCallbackHandler PrivateCallback;
  248. PrivateCallback.ProgressDelegate = (UObject*)ProgressDelegate;
  249. ZipOperation->SetCallbackHandler(&PrivateCallback);
  250. //Set the zip format
  251. ZipUtilityCompressionFormat UeFormat = UeCompressionformat;
  252. if (UeFormat == COMPRESSION_FORMAT_UNKNOWN)
  253. {
  254. UeFormat = COMPRESSION_FORMAT_ZIP;
  255. }
  256. //Disallow creating .rar archives as per unrar restriction, this won't work anyway so redirect to 7z
  257. else if (UeFormat == COMPRESSION_FORMAT_RAR)
  258. {
  259. UE_LOG(LogClass, Warning, TEXT("ZipUtility: Rar compression not supported for creating archives, re-targeting as 7z."));
  260. UeFormat = COMPRESSION_FORMAT_SEVEN_ZIP;
  261. }
  262. //concatenate the output filename
  263. FString OutputFileName = FString::Printf(TEXT("%s/%s%s"), *Directory, *FileName, *defaultExtensionFromUEFormat(UeFormat));
  264. //UE_LOG(LogClass, Log, TEXT("\noutputfile is: <%s>\n path is: <%s>"), *outputFileName, *path);
  265. SevenZipCompressor compressor(SZLib, *ReversePathSlashes(OutputFileName));
  266. compressor.SetCompressionFormat(libZipFormatFromUEFormat(UeFormat));
  267. compressor.SetCompressionLevel(libZipLevelFromUELevel(UeCompressionlevel));
  268. if (PathIsDirectory(*Path))
  269. {
  270. //UE_LOG(LogClass, Log, TEXT("Compressing Folder"));
  271. compressor.CompressDirectory(*ReversePathSlashes(Path), &PrivateCallback);
  272. }
  273. else
  274. {
  275. //UE_LOG(LogClass, Log, TEXT("Compressing File"));
  276. compressor.CompressFile(*ReversePathSlashes(Path), &PrivateCallback);
  277. }
  278. // Null out the callback handler
  279. ZipOperation->SetCallbackHandler(nullptr);
  280. //Todo: expand to support zipping up contents of current folder
  281. //compressor.CompressFiles(*ReversePathSlashes(path), TEXT("*"), &PrivateCallback);
  282. });
  283. ZipOperation->SetThreadPoolWorker(Work);
  284. return ZipOperation;
  285. }
  286. }//End private namespace
  287. UZipFileFunctionLibrary::UZipFileFunctionLibrary(const class FObjectInitializer& PCIP)
  288. : Super(PCIP)
  289. {
  290. UE_LOG(LogTemp, Log, TEXT("DLLPath is: %s"), *DLLPath());
  291. SZLib.Load(*DLLPath());
  292. }
  293. UZipFileFunctionLibrary::~UZipFileFunctionLibrary()
  294. {
  295. SZLib.Free();
  296. }
  297. bool UZipFileFunctionLibrary::UnzipFileNamed(const FString& archivePath, const FString& Name, UObject* ZipUtilityInterfaceDelegate, ZipUtilityCompressionFormat format /*= COMPRESSION_FORMAT_UNKNOWN*/)
  298. {
  299. UZipFileFunctionInternalCallback* InternalCallback = NewObject<UZipFileFunctionInternalCallback>();
  300. InternalCallback->SetFlags(RF_MarkAsRootSet);
  301. InternalCallback->SetCallback(Name, ZipUtilityInterfaceDelegate, format);
  302. ListFilesInArchive(archivePath, InternalCallback, format);
  303. return true;
  304. }
  305. bool UZipFileFunctionLibrary::UnzipFileNamedTo(const FString& archivePath, const FString& Name, const FString& destinationPath, UObject* ZipUtilityInterfaceDelegate, ZipUtilityCompressionFormat format /*= COMPRESSION_FORMAT_UNKNOWN*/)
  306. {
  307. UZipFileFunctionInternalCallback* InternalCallback = NewObject<UZipFileFunctionInternalCallback>();
  308. InternalCallback->SetFlags(RF_MarkAsRootSet);
  309. InternalCallback->SetCallback(Name, destinationPath, ZipUtilityInterfaceDelegate, format);
  310. ListFilesInArchive(archivePath, InternalCallback, format);
  311. return true;
  312. }
  313. UZipOperation* UZipFileFunctionLibrary::UnzipFilesTo(const TArray<int32> fileIndices, const FString & archivePath, const FString & destinationPath, UObject * ZipUtilityInterfaceDelegate, ZipUtilityCompressionFormat format)
  314. {
  315. return UnzipFilesOnBGThreadWithFormat(fileIndices, archivePath, destinationPath, ZipUtilityInterfaceDelegate, format);
  316. }
  317. UZipOperation* UZipFileFunctionLibrary::UnzipFiles(const TArray<int32> fileIndices, const FString & ArchivePath, UObject * ZipUtilityInterfaceDelegate, ZipUtilityCompressionFormat format)
  318. {
  319. FString Directory;
  320. FString FileName;
  321. //Check Directory validity
  322. if (!IsValidDirectory(Directory, FileName, ArchivePath))
  323. {
  324. return nullptr;
  325. }
  326. if (fileIndices.Num() == 0)
  327. {
  328. return nullptr;
  329. }
  330. return UnzipFilesTo(fileIndices, ArchivePath, Directory, ZipUtilityInterfaceDelegate, format);
  331. }
  332. UZipOperation* UZipFileFunctionLibrary::Unzip(const FString& ArchivePath, UObject* ZipUtilityInterfaceDelegate, ZipUtilityCompressionFormat Format /*= COMPRESSION_FORMAT_UNKNOWN*/)
  333. {
  334. FString Directory;
  335. FString FileName;
  336. //Check Directory validity
  337. if (!IsValidDirectory(Directory, FileName, ArchivePath) || !UWindowsFileUtilityFunctionLibrary::DoesFileExist(ArchivePath))
  338. {
  339. ((IZipUtilityInterface*)ZipUtilityInterfaceDelegate)->Execute_OnDone((UObject*)ZipUtilityInterfaceDelegate, ArchivePath, EZipUtilityCompletionState::FAILURE_NOT_FOUND);
  340. return nullptr;
  341. }
  342. return UnzipTo(ArchivePath, Directory, ZipUtilityInterfaceDelegate, Format);
  343. }
  344. UZipOperation* UZipFileFunctionLibrary::UnzipWithLambda(const FString& ArchivePath, TFunction<void()> OnDoneCallback, TFunction<void(float)> OnProgressCallback, ZipUtilityCompressionFormat Format)
  345. {
  346. UZULambdaDelegate* LambdaDelegate = NewObject<UZULambdaDelegate>();
  347. LambdaDelegate->AddToRoot();
  348. LambdaDelegate->SetOnDoneCallback([LambdaDelegate, OnDoneCallback]()
  349. {
  350. OnDoneCallback();
  351. LambdaDelegate->RemoveFromRoot();
  352. });
  353. LambdaDelegate->SetOnProgessCallback(OnProgressCallback);
  354. return Unzip(ArchivePath, LambdaDelegate, Format);
  355. }
  356. UZipOperation* UZipFileFunctionLibrary::UnzipTo(const FString& ArchivePath, const FString& DestinationPath, UObject* ZipUtilityInterfaceDelegate, ZipUtilityCompressionFormat Format)
  357. {
  358. return UnzipOnBGThreadWithFormat(ArchivePath, DestinationPath, ZipUtilityInterfaceDelegate, Format);
  359. }
  360. UZipOperation* UZipFileFunctionLibrary::Zip(const FString& ArchivePath, UObject* ZipUtilityInterfaceDelegate, ZipUtilityCompressionFormat Format, TEnumAsByte<ZipUtilityCompressionLevel> Level)
  361. {
  362. FString Directory;
  363. FString FileName;
  364. //Check Directory and File validity
  365. if (!IsValidDirectory(Directory, FileName, ArchivePath) || !UWindowsFileUtilityFunctionLibrary::DoesFileExist(ArchivePath))
  366. {
  367. ((IZipUtilityInterface*)ZipUtilityInterfaceDelegate)->Execute_OnDone((UObject*)ZipUtilityInterfaceDelegate, ArchivePath, EZipUtilityCompletionState::FAILURE_NOT_FOUND);
  368. return nullptr;
  369. }
  370. return ZipOnBGThread(ArchivePath, FileName, Directory, ZipUtilityInterfaceDelegate, Format, Level);
  371. }
  372. 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*/)
  373. {
  374. UZULambdaDelegate* LambdaDelegate = NewObject<UZULambdaDelegate>();
  375. LambdaDelegate->AddToRoot();
  376. LambdaDelegate->SetOnDoneCallback([OnDoneCallback, LambdaDelegate]()
  377. {
  378. OnDoneCallback();
  379. LambdaDelegate->RemoveFromRoot();
  380. });
  381. LambdaDelegate->SetOnProgessCallback(OnProgressCallback);
  382. return Zip(ArchivePath, LambdaDelegate, Format);
  383. }
  384. bool UZipFileFunctionLibrary::ListFilesInArchive(const FString& path, UObject* ListDelegate, ZipUtilityCompressionFormat format)
  385. {
  386. FString Directory;
  387. FString FileName;
  388. //Check Directory validity
  389. if (!IsValidDirectory(Directory, FileName, path))
  390. {
  391. return false;
  392. }
  393. ListOnBGThread(path, Directory, ListDelegate, format);
  394. return true;
  395. }
  396. FGraphEventRef UZipFileFunctionLibrary::RunLambdaOnGameThread(TFunction< void()> InFunction)
  397. {
  398. return FFunctionGraphTask::CreateAndDispatchWhenReady(InFunction, TStatId(), nullptr, ENamedThreads::GameThread);
  399. }