@@ -83,21 +83,260 @@ bool UFileSDKBPLibrary::RenameFileOrDirectory(
83
83
84
84
bool UFileSDKBPLibrary::CopyFile (
85
85
FString Source,
86
- FString Destination
86
+ FString Destination,
87
+ const FFileSDKCopyDelegate & ProgressCallback,
88
+ FFileSDKDelegatePreInfo PreInfo,
89
+ int ChunkSizeInKilobytes
87
90
) {
88
91
IPlatformFile & PlatformFile = FPlatformFileManager::Get ().GetPlatformFile ();
89
92
90
- return PlatformFile.CopyFile (*Destination, *Source);
93
+ const int64 MaxBufferSize = ChunkSizeInKilobytes * 1024 ;
94
+
95
+ // Note, the below code is mostly copied from the
96
+ // engine's GenericPlatformFile.cpp source file
97
+
98
+ TUniquePtr<IFileHandle> FromFile (PlatformFile.OpenRead (*Source, false ));
99
+ if (!FromFile) {
100
+ return false ;
101
+ }
102
+
103
+ TUniquePtr<IFileHandle> ToFile (PlatformFile.OpenWrite (*Destination, false , false ));
104
+ if (!ToFile) {
105
+ return false ;
106
+ }
107
+
108
+ int64 Size = FromFile->Size ();
109
+ int totalSizeKb = FMath::DivideAndRoundUp (Size, int64 (1000 ));
110
+ if (Size < 1 ) {
111
+ check (Size == 0 );
112
+ return true ;
113
+ }
114
+
115
+ int64 AllocSize = FMath::Min<int64>(MaxBufferSize, Size);
116
+ check (AllocSize);
117
+
118
+ uint8* Buffer = (uint8*)FMemory::Malloc (int32 (AllocSize));
119
+ check (Buffer);
120
+
121
+ while (Size) {
122
+ int64 ThisSize = FMath::Min<int64>(AllocSize, Size);
123
+ FromFile->Read (Buffer, ThisSize);
124
+ ToFile->Write (Buffer, ThisSize);
125
+ Size -= ThisSize;
126
+ ProgressCallback.ExecuteIfBound (
127
+ PreInfo.PriorWritten + totalSizeKb - FMath::DivideAndRoundUp (Size, int64 (1000 )),
128
+ PreInfo.TotalSize > 0 ? PreInfo.TotalSize : totalSizeKb
129
+ );
130
+ check (Size >= 0 );
131
+ }
132
+
133
+ FMemory::Free (Buffer);
134
+
135
+ #if PLATFORM_MAC || PLATFORM_IOS
136
+ struct stat FileInfo;
137
+ auto applePlatformFile = static_cast <FApplePlatformFile>(PlatformFile);
138
+ if (applePlatformFile.Stat (*Source, &FileInfo) == 0 ) {
139
+ FileInfo.st_mode |= S_IWUSR;
140
+ chmod (TCHAR_TO_UTF8 (*applePlatformFile.NormalizeFilename (*Destination)), FileInfo.st_mode );
141
+ }
142
+ #endif
143
+
144
+ return true ;
145
+ }
146
+
147
+ void UFileSDKBPLibrary::CopyFileAsync (
148
+ FString Source,
149
+ FString Destination,
150
+ const FFileSDKCopyDelegate & ProgressCallback,
151
+ FFileSDKDelegatePreInfo PreInfo,
152
+ int ChunkSizeInKilobytes
153
+ ) {
154
+ FFunctionGraphTask::CreateAndDispatchWhenReady (
155
+ [=] {
156
+ UFileSDKBPLibrary::CopyFile (
157
+ Source,
158
+ Destination,
159
+ ProgressCallback,
160
+ PreInfo,
161
+ ChunkSizeInKilobytes
162
+ );
163
+ },
164
+ TStatId (),
165
+ nullptr ,
166
+ ENamedThreads::AnyThread
167
+ );
91
168
}
92
169
93
170
bool UFileSDKBPLibrary::CopyDirectory (
94
171
FString Source,
95
172
FString Destination,
96
- bool OverwriteDestination
173
+ const FFileSDKCopyDelegate & ProgressCallback,
174
+ bool OverwriteDestination,
175
+ int ChunkSizeInKilobytes
97
176
) {
98
177
IPlatformFile & PlatformFile = FPlatformFileManager::Get ().GetPlatformFile ();
99
178
100
- return PlatformFile.CopyDirectoryTree (*Destination, *Source, OverwriteDestination);
179
+ // Note, the below code is mostly copied from the
180
+ // engine's GenericPlatformFile.cpp source file
181
+
182
+ check (*Destination);
183
+ check (*Source);
184
+
185
+ FString DestDir (Destination);
186
+ FPaths::NormalizeDirectoryName (DestDir);
187
+
188
+ FString SourceDir (Source);
189
+ FPaths::NormalizeDirectoryName (SourceDir);
190
+
191
+ // Does Source dir exist?
192
+ if (!PlatformFile.DirectoryExists (*SourceDir)) {
193
+ return false ;
194
+ }
195
+
196
+ // Destination directory exists already or can be created ?
197
+ if (
198
+ !PlatformFile.DirectoryExists (*DestDir) &&
199
+ !PlatformFile.CreateDirectory (*DestDir)
200
+ ) {
201
+ return false ;
202
+ }
203
+
204
+ // Get total size
205
+ struct FStatFilesAndDirs : public IPlatformFile ::FDirectoryStatVisitor {
206
+ IPlatformFile & PlatformFile;
207
+ int64 TotalFileSize;
208
+
209
+ FStatFilesAndDirs (IPlatformFile& InPlatformFile)
210
+ : PlatformFile(InPlatformFile)
211
+ , TotalFileSize(0 ) {
212
+ }
213
+
214
+ virtual bool Visit (const TCHAR* FilenameOrDirectory, const FFileStatData& StatData) {
215
+ if (!StatData.bIsDirectory ) {
216
+ TotalFileSize += StatData.FileSize ;
217
+ }
218
+
219
+ return true ;
220
+ }
221
+ };
222
+
223
+ // copy files and directories visitor
224
+ FStatFilesAndDirs StatFilesAndDirs (PlatformFile);
225
+
226
+ // don't bother getting file size of dir
227
+ // if the user doesn't want it
228
+ if (ProgressCallback.IsBound ()) {
229
+ bool statResult = PlatformFile.IterateDirectoryStatRecursively (*SourceDir, StatFilesAndDirs);
230
+
231
+ if (!statResult) {
232
+ return false ;
233
+ }
234
+ }
235
+
236
+ // Copy all files and directories
237
+ struct FCopyFilesAndDirs : public IPlatformFile ::FDirectoryVisitor {
238
+ IPlatformFile & PlatformFile;
239
+ const TCHAR* SourceRoot;
240
+ const TCHAR* DestRoot;
241
+ bool bOverwrite;
242
+ int chunkSizeInKilobytes;
243
+ FFileSDKCopyDelegate progressCallback;
244
+ FFileSDKDelegatePreInfo preInfo;
245
+
246
+ FCopyFilesAndDirs (
247
+ IPlatformFile& InPlatformFile,
248
+ const TCHAR* InSourceRoot,
249
+ const TCHAR* InDestRoot,
250
+ bool bInOverwrite,
251
+ int inChunkSizeInKilobytes,
252
+ FFileSDKCopyDelegate inProgressCallback,
253
+ int inTotalSizeKB
254
+ ) :
255
+ PlatformFile (InPlatformFile),
256
+ SourceRoot (InSourceRoot),
257
+ DestRoot (InDestRoot),
258
+ bOverwrite (bInOverwrite),
259
+ chunkSizeInKilobytes (inChunkSizeInKilobytes),
260
+ progressCallback (inProgressCallback) {
261
+ preInfo.TotalSize = inTotalSizeKB;
262
+ preInfo.PriorWritten = 0 ;
263
+ }
264
+
265
+ virtual bool Visit (const TCHAR* FilenameOrDirectory, bool bIsDirectory) {
266
+ FString NewName (FilenameOrDirectory);
267
+ // change the root
268
+ NewName = NewName.Replace (SourceRoot, DestRoot);
269
+
270
+ if (bIsDirectory) {
271
+ // create new directory structure
272
+ if (!PlatformFile.CreateDirectoryTree (*NewName) && !PlatformFile.DirectoryExists (*NewName)) {
273
+ return false ;
274
+ }
275
+ } else {
276
+ // Delete destination file if it exists and we are overwriting
277
+ if (PlatformFile.FileExists (*NewName) && bOverwrite) {
278
+ PlatformFile.DeleteFile (*NewName);
279
+ }
280
+
281
+ // Copy file from source
282
+ if (
283
+ !UFileSDKBPLibrary::CopyFile (
284
+ FilenameOrDirectory,
285
+ NewName,
286
+ progressCallback,
287
+ preInfo,
288
+ chunkSizeInKilobytes
289
+ )
290
+ ) {
291
+ // Not all files could be copied
292
+ return false ;
293
+ }
294
+
295
+ if (progressCallback.IsBound ()) {
296
+ auto statData = PlatformFile.GetStatData (FilenameOrDirectory);
297
+ preInfo.PriorWritten += FMath::DivideAndRoundUp (statData.FileSize , int64 (1000 ));
298
+ }
299
+ }
300
+ return true ; // continue searching
301
+ }
302
+ };
303
+
304
+ // copy files and directories visitor
305
+ FCopyFilesAndDirs CopyFilesAndDirs (
306
+ PlatformFile,
307
+ *SourceDir,
308
+ *DestDir,
309
+ OverwriteDestination,
310
+ ChunkSizeInKilobytes,
311
+ ProgressCallback,
312
+ FMath::DivideAndRoundUp (StatFilesAndDirs.TotalFileSize , int64 (1000 ))
313
+ );
314
+
315
+ // create all files subdirectories and files in subdirectories!
316
+ return PlatformFile.IterateDirectoryRecursively (*SourceDir, CopyFilesAndDirs);
317
+ }
318
+
319
+ void UFileSDKBPLibrary::CopyDirectoryAsync (
320
+ FString Source,
321
+ FString Destination,
322
+ const FFileSDKCopyDelegate & ProgressCallback,
323
+ bool OverwriteDestination,
324
+ int ChunkSizeInKilobytes
325
+ ) {
326
+ FFunctionGraphTask::CreateAndDispatchWhenReady (
327
+ [=] {
328
+ UFileSDKBPLibrary::CopyDirectory (
329
+ Source,
330
+ Destination,
331
+ ProgressCallback,
332
+ OverwriteDestination,
333
+ ChunkSizeInKilobytes
334
+ );
335
+ },
336
+ TStatId (),
337
+ nullptr ,
338
+ ENamedThreads::AnyThread
339
+ );
101
340
}
102
341
103
342
bool UFileSDKBPLibrary::ReadStringFromFile (FString FileName, FString & Content) {
0 commit comments