Files
UnrealEngineUWP/Engine/Source/Runtime/PakFile/Private/IPlatformFilePak.cpp
Andrew Grant a0ef617fd2 Copying //UE4/Orion-Staging to //UE4/Main (Source: //Orion/Dev-General @ 2949393)
#lockdown Nick.Penwarden

==========================
MAJOR FEATURES + CHANGES
==========================

Change 2949393 on 2016/04/20 by Graeme.Thornton

	Orion non-pak file security.
	 - Removed security bypass code from platform pak file
	 - Added a delegate to pak file code which allows the game to decide whether a file should be allowed or not
	 - Added an orion delegate which whitelists appropriate files

	#rb robert.manuszewski
	#tests win64 client + dedicated server. golden path.

Change 2949232 on 2016/04/19 by david.nikdel

	#ROBOMERGE-AUTHOR: michael.noland
	Paragon: Added a distinct menu frame rate limit, currently set to 60 fps and not visible in settings (if the user sets a game frame rate limit of below 60, we also clamp the menu limit to that threshold, so they can go down but not up for menus)
	#jira OR-18017
	#rb marcus.wassmer
	#tests Ran paragon and switched between gameplay, menus, and replays, observing t.MaxFPS at different points

	#ROBOMERGE-SOURCE: CL 2949231 in //Orion/Main/...
	#ROBOMERGE-BOT: ORION (Main -> Dev-General)

Change 2949032 on 2016/04/19 by Zak.Middleton

	#orion - Lower default NetUpdateFrequency for minions (10->6). Avoid excessive latency for some knockback/knockup abilities that would have noticeable lag by forcing an update sooner when they are triggered.

	This should have the following effects:
	1. Reduce server CPU cost (we tick minions at the net frequency).
	2. Reduce server bandwidth
	3. Reduce client CPU cost (we move character capsules and perform overlaps when new positions are received).

	#rb Bart.Bressler, John.Pollard
	#codereview Dmitry.Rekman
	#tests MultiPIE AI lane, Replays

Change 2948966 on 2016/04/19 by Lina.Halper

	Added log (check) of the asset info for Anim Per Track contains invalid format key

	#rb: Michael.Noland
	#code review: Martin.Wilson, Laurent.Delayen, Michael.Noland
	#tests: editor/ cooked and test with AI_Tests with 10 bots.

Change 2948876 on 2016/04/19 by Michael.Noland

	PS4: Validate that the texture pool size is not set to automatic (-1, which will crash later on as an attempt to allocate too much memory)
	#rb none
	#codereview marcus.wassmer
	#tests Ran Paragon on PS4

Change 2948765 on 2016/04/19 by Daniel.Lamb

	Removed AssetImportData tag from cooked asset registry builds.
	#rb Andrew.Grant
	#test Cook orion

Change 2948691 on 2016/04/19 by Marcus.Wassmer

	Fix copytoresolvetarget ensure
	#rb none
	#test pc agora

Change 2948633 on 2016/04/19 by david.nikdel

	#ROBOMERGE-AUTHOR: jason.bestimt
	[AUTOMERGE]

	Fix copytoresolve crash and change validation to ensure.
	#test PC editor / PC golden path
	#rb none

	--------
	Integrated using branch //Orion/Main_to_//Orion/Release-Next (reversed) of change#2948169 by Marcus.Wassmer on 2016/04/19 10:50:32.

	#ROBOMERGE-SOURCE: CL 2948632 in //Orion/Main/...
	#ROBOMERGE-BOT: ORION (Main -> Dev-General)

Change 2948507 on 2016/04/19 by david.nikdel

	#ROBOMERGE-AUTHOR: andrew.grant
	Merging 2937781 (Pak signing) using //Orion/Dev-General_to_Release
	#rb none
	#tests cooked client, checked game runs

	#ROBOMERGE-SOURCE: CL 2948497 in //Orion/Release-0.24.1/... via CL 2948506
	#ROBOMERGE-BOT: ORION (Main -> Dev-General)

Change 2948431 on 2016/04/19 by Steve.Robb

	CL#s 2919775 and 2942793 integrated to prevent annotation map performance problems on shutdown and asserts in PIE.

	#codereview robert.manuszewski,bob.tellez
	#rb bob.tellez
	#tests Ran editor

Change 2948408 on 2016/04/19 by Leslie.Nivison

	Adding .tps
	#rb none
	#test none

Change 2948185 on 2016/04/19 by david.nikdel

	#ROBOMERGE-AUTHOR: chris.bunner
	Fix for HLOD visibility freeze.
	#tests Golden Path, Editor
	#rb rolando.caloca, michael.noland
	#lockdown andrew.grant
	#jira OR-19863

	#ROBOMERGE-SOURCE: CL 2948182 in //Orion/Release-0.24.1/... via CL 2948183
	#ROBOMERGE-BOT: ORION (Main -> Dev-General)

Change 2948149 on 2016/04/19 by Simon.Tovey

	Fixed crash. Collision rendering path was not dealing with mesh batch with 0 triangles where other paths do.

	#rb none
	#tests No more crash
	#codereview Marcus.Wassmer

Change 2948129 on 2016/04/19 by Lukasz.Furman

	fixed gameplay debugger getting stuck with outdated data pack on client,
	changed names of AI related debug cvars
	#rb none
	#tests game, PIE
	#codereview Mieszko.Zielinski

Change 2948027 on 2016/04/19 by david.nikdel

	#ROBOMERGE-AUTHOR: graeme.thornton
	Fix for OR-20033 - CRASH:  Client will crash with FRCPassPostProcessCircleDOFSetup

	#rb none
	#tests checked game runs without crashing

	#ROBOMERGE-SOURCE: CL 2948017 in //Orion/Main/...
	#ROBOMERGE-BOT: ORION (Main -> Dev-General)

Change 2947558 on 2016/04/18 by Matt.Kuhlenschmidt

	Fix compile error

	#rb none, #tests none

Change 2947509 on 2016/04/18 by Matt.Kuhlenschmidt

	Added more logging to track down

	https://jira.ol.epicgames.net/browse/OR-19841

	#rb none, #tests none

Change 2947412 on 2016/04/18 by Ryan.Gerleve

	Fix shadowed variable.

	#rb none
	#tests none

Change 2947377 on 2016/04/18 by Jamie.Dale

	Gather paths are now sorted by fuzzy-ness, so that more specific includes beat less specific excludes

	#rb Matt.Kuhlenschmidt
	#tests Built for Windows. Ran a gather, and confirmed that explicitly included heroes were now gathered, and that generically excluded heroes were absent from the gather.

Change 2947351 on 2016/04/18 by Ryan.Gerleve

	Allow overriding the demo.AsyncLoadWorld setting with a URL option when playing a replay.
	Store the entire URL in the demo net driver instead of just the map name, so that the options can be accessed later.

	#tests golden path, replays
	#rb john.pollard

Change 2947103 on 2016/04/18 by david.nikdel

	#ROBOMERGE-AUTHOR: jason.bestimt
	#ORION_MAIN - Merge 24.1 @ CL 2947071

	#RB:none
	#Tests:none

	#ROBOMERGE-SOURCE: CL 2947102 in //Orion/Main/...
	#ROBOMERGE-BOT: ORION (Main -> Dev-General)

Change 2947007 on 2016/04/18 by Zak.Middleton

	#ue4 - Improve linear smoothing in the presence of low net frequency updates.

	#rb Bart.Bressler
	#tests MultiPIE AI with lanes

Change 2946994 on 2016/04/18 by Mieszko.Zielinski

	Improvements to NavigationSystem's "abstract navigation data" support #UE4

	#rb Lukasz.Furman
	#test golden path

Change 2946760 on 2016/04/18 by Chris.Bunner

	Fixing up bad merge, recommit of CL 2819472 - ForceLOD now clamps to available LODs on primitive, i.e. use MinLOD rather than not drawing at all.
	#tests Editor
	#rb None

Change 2946745 on 2016/04/18 by david.nikdel

	#ROBOMERGE-AUTHOR: jason.bestimt
	#ORION_MAIN - Merge 24.1 @ CL 2946637

	#RB:none
	#Tests:none

	#ROBOMERGE-SOURCE: CL 2946656 in //Orion/Main/...
	#ROBOMERGE-BOT: ORION (Main -> Dev-General)

Change 2946645 on 2016/04/18 by Richard.Fawcett

	When promoting a buidl to staged, prevent enumeration of files already in S3

	Enumerating files in S3 is a slow process, and it turns out that simply uploading all chunks blindly is more efficient than enumerating existing chunks and selectively uploading only the new ones.

	#rb Leigh.Swift
	#tests This technique has already been used in launcher promotions for several months

Change 2946622 on 2016/04/18 by Richard.Fawcett

	By default, when enumerating chunks from a manifest file, skip checking they exist on disk at enumeration time.

	This will fail anyway further down the line if the files don't exist, but will improve speed of stage promotions by around five minutes. In practice, we have NEVER seen a job fail at this point because of the existence check.

	#rb Leigh.Swift
	#tests Ensure that output of ExtractDataFilenamesFromManifest method is identical both with and without bSkipExistsCheck specified.

Change 2945812 on 2016/04/15 by Daniel.Lamb

	Fixed error in diff cooked build commandlet.
	#rb ben.marsh
	#test Compile.

Change 2945110 on 2016/04/15 by Matt.Kuhlenschmidt

	Fix crash exporting actors with non-scene components to fbx

	#rb none, #tests full scene exporting on maps that crashed
	#codereview alexis.matte

Change 2945078 on 2016/04/15 by Simon.Tovey

	Fix for OR-19778

	When some pooled systems are reused, on init they have a non zero lod level but the emitter instances are created at LOD 0 initially.
	So the component did not think it had to update it's LOD but the emitters were not at the correct LOD.
	Have forced a LOD set on init when the component LOD is non-zero.

	#rb none
	#tests Works in editor and game.

	#codereview Olaf.Piesche

Change 2944664 on 2016/04/14 by Uriel.Doyon

	Fix to SM4 compilation issue
	#jira OR-19706
	#rb marcus.wassmer
	#tests tested editor in SM4 and SM5

Change 2944642 on 2016/04/14 by Lukasz.Furman

	changed waypoint switch conditions in meta nav paths
	#rb none
	#tests PIE
	#codereview Mieszko.Zielinski

Change 2944599 on 2016/04/14 by david.nikdel

	#ROBOMERGE-AUTHOR: andrew.grant
	Added sha1 to UnrealPak list output
	#rb none
	#tests listed content of pakfile

	#ROBOMERGE-SOURCE: CL 2944595 in //Orion/Release-0.24/... via CL 2944597 via CL 2944598
	#ROBOMERGE-BOT: ORION (Main -> Dev-General)

Change 2944441 on 2016/04/14 by Marcus.Wassmer

	Duplicate change to output shader compiler errors.
	#rb none
	#test run PC and see errors.

Change 2944437 on 2016/04/14 by John.Pollard

	Possible fix for https://jira.ol.epicgames.net/browse/OR-19614

	#rb JoshM
	#codereview Josh.Markiewicz
	#tests Golden path matchmaking

Change 2944430 on 2016/04/14 by david.nikdel

	#ROBOMERGE-AUTHOR: michael.noland
	Engine: Added support for more/fewer settings in individual categories to the editor scalability control widget
	#rb david.ratti
	#tests Tested in the editor

	#ROBOMERGE-SOURCE: CL 2944428 in //Orion/Main/...
	#ROBOMERGE-BOT: ORION (Main -> Dev-General)

Change 2944198 on 2016/04/14 by David.Ratti

	Paragon - register for slow/stun/root/silence callbacks on any tag count  change, not just add/remove. This is so the UI will update if you get another stack of a stackable slow GE.

	Ability system - unify client stack count change code path with server. Client now properly update owner ASC's tag map and broadcasts all delegates there.

	#rb dayY
	#tests pie

Change 2944124 on 2016/04/14 by Wes.Hunt

	Change the TPS redirects for DX modules to point to the proper DX redist TPS which is what packaged games will need.
	#codereview:leslie.nivison
	#rb none
	#tests ran UAT ListThirdPartySoftware <for Orion>

Change 2944107 on 2016/04/14 by Wes.Hunt

	MeshUtilities now depends on new module nvTessLib to better track the third party dependency.
	#codereview:daniel.wright
	#rb none
	#tests build OrionClient/Editor for Win64

Change 2944102 on 2016/04/14 by Wes.Hunt

	Tweak to UBT -ListBuildFolders to do a distinct in a better place to cut down on duplicate module searches.
	#tests ran the UBT command
	#rb none

Change 2943851 on 2016/04/14 by Ryan.Gerleve

	Fix the ForEachNetDriver helper function to get the world context directly off the world instead of going through the game instance. Ensures the correct net drivers will be used when there are multiple worlds but only one game instance.

	#rb john.pollard
	#tests golden path, replays, PIE

Change 2943847 on 2016/04/14 by Ryan.Gerleve

	Fixes to support client replay recording & playback in another world:
	When recording a replay, only swap actor roles if the remote role is ROLE_Authority
	When loading a replay checkpoint, call NetworkRemapPath to make sure paths have the correct name in the GuidCache

	#rb john.pollard
	#tests golden path, replays, PIE

Change 2943691 on 2016/04/14 by david.nikdel

	#ROBOMERGE-AUTHOR: jason.bestimt
	#ORION_24 - Fix for OR-19609, OR-19610, and OR-19611

	#RB:none
	#Tests:none

	#ROBOMERGE-SOURCE: CL 2943687 in //Orion/Release-0.24/... via CL 2943688
	#ROBOMERGE-BOT: ORION (Main -> Dev-General)

Change 2943508 on 2016/04/14 by Richard.Fawcett

	Automation: Add support for multipart file uploads to Amazon S3 to increase speed of large file uploads.

	#jira OPPBUILD-44
	#rb Leigh.Swift
	#tests Uploaded files to S3 using the new routines, downlaoded via AWS management console and ensured downloaded files identical to uploaded ones

Change 2943274 on 2016/04/13 by jason.bestimt

	#ORION_MAIN - Merge 24 @ CL 2943257

	#RB:none
	#Tests:none

	#ROBOMERGE-SOURCE: CL 2943271 in //Orion/Main/...
	#ROBOMERGE-BOT: ORION (Main -> Dev-General)

	#ROBOMERGE-SAYS: Beep boop! I couldn't merge this change. Please do it yourself, human.
	#CodeReview: david.nikdel, jason.bestimt

Change 2943178 on 2016/04/13 by Olaf.Piesche

	Bumping size of the particle curve texture to 512x512

	#rb martin.mittring

	#tests PC Editor, Game

Change 2943174 on 2016/04/13 by Aaron.McLeran

	OR-19392 Ensure condition failed: (*RequiresInitialization == 0) on loading into PVP match

	- Removing ensure since there is a rare edge case where it's possible for a sound looping node may get ResetChildren called twice.
	- Condition is when a child random node o fa looping node has a blank entry and results in no sound chosen in a given frame (which results in ResetChildren getting called). Later in the frame, if a sound had previously been playing with an active sound, it will have stop called on it, which will call NotifyWaveInstanceFinished and hit the ensure. Simply using the branch to check if the looping node has been initialized will work fine in this and other cases.

	#codereview Bob.Tellez
	#rb Bob.Tellez
	#tests ran orion with this change testing problematic sound cue

Change 2943042 on 2016/04/13 by Rob.Cannaday

	Fix crash in HTTP completion delegates on shutdown
	Stop ticking HTTP retry manager after FOnlineSubsystemImpl::Shutdown has been called
	#rb josh.markiewicz
	#tests shutting down multiple times

Change 2942913 on 2016/04/13 by Lukasz.Furman

	added meta navmesh paths
	#orion
	#rb Mieszko.Zielinski
	#tests PIE

Change 2942132 on 2016/04/13 by Wes.Hunt

	Enable UBT -ListBuildFolders to operate on Mac and iOS platforms without having to fully set up the remote environment.
	#codereview:leslie.nivison
	#rb peter.sauerbrei
	#tests running UBT with and without -listbuildfolders

Change 2941651 on 2016/04/12 by Jason.Bestimt

	#ORION_DG - Merge MAIN @ CL 2941645

	#RB:none
	#Tests:none

Change 2941539 on 2016/04/12 by Laurent.Delayen

	FABRIK: Normalize outgoing rotations.
	Fixes Chains Q ability crashing.

	#rb none
	#tests Chains not crashing

Change 2941469 on 2016/04/12 by Wes.Hunt

	Fix UBT -ListBuildFolders to not prep target for deployment.
	#codereview:leslie.nivison
	#rb none
	#tests tested -ListBuildFolders for Android

Change 2941434 on 2016/04/12 by Leslie.Nivison

	Adding/cleaning up .tps files
	#rb none
	#test none

Change 2941241 on 2016/04/12 by Daniel.Lamb

	Removed shadername from the shader code to fix deterministic material cooking issue.
	#jira UE-29320
	#codereview Marcus.Wassmer
	#rb Marcus.Wassmer
	#test Running editor, cooking orion.

Change 2941046 on 2016/04/12 by Laurent.Delayen

	Added safety net for non state AnimNotifies having a non-zero EndTriggerTimeOffset.
	Fixes Twinblast double shot for the left primary attack.

	#rb benn.gallagher
	#codereview lina.halper, ray.arnett, aaron.eady
	#tests twinblast's LMB

Change 2941032 on 2016/04/12 by Jason.Bestimt

	#ORION_24 - Merge MAIN @ CL 2940950

	#RB:none
	#Tests:none

[CL 2952833 by Andrew Grant in Main branch]
2016-04-22 11:21:10 -04:00

961 lines
27 KiB
C++

// Copyright 1998-2016 Epic Games, Inc. All Rights Reserved.
#include "PakFilePrivatePCH.h"
#include "IPlatformFilePak.h"
#include "SecureHash.h"
#include "FileManagerGeneric.h"
#include "ModuleManager.h"
#include "IPlatformFileModule.h"
#include "IOBase.h"
#include "BigInt.h"
#include "SignedArchiveReader.h"
#include "PublicKey.inl"
#include "AES.h"
#include "GenericPlatformChunkInstall.h"
DEFINE_LOG_CATEGORY(LogPakFile);
DEFINE_STAT(STAT_PakFile_Read);
DEFINE_STAT(STAT_PakFile_NumOpenHandles);
FFilenameSecurityDelegate& FPakPlatformFile::GetFilenameSecurityDelegate()
{
static FFilenameSecurityDelegate Delegate;
return Delegate;
}
/**
* Class to handle correctly reading from a compressed file within a compressed package
*/
class FPakSimpleEncryption
{
public:
enum
{
Alignment = FAES::AESBlockSize,
};
static FORCEINLINE int64 AlignReadRequest(int64 Size)
{
return Align(Size, Alignment);
}
static FORCEINLINE void DecryptBlock(void* Data, int64 Size)
{
#ifdef AES_KEY
FAES::DecryptData((uint8*)Data, Size);
#endif
}
};
/**
* Thread local class to manage working buffers for file compression
*/
class FCompressionScratchBuffers : public TThreadSingleton<FCompressionScratchBuffers>
{
public:
FCompressionScratchBuffers()
: TempBufferSize(0)
, ScratchBufferSize(0)
{}
int64 TempBufferSize;
TAutoPtr<uint8> TempBuffer;
int64 ScratchBufferSize;
TAutoPtr<uint8> ScratchBuffer;
void EnsureBufferSpace(int64 CompressionBlockSize, int64 ScrachSize)
{
if(TempBufferSize < CompressionBlockSize)
{
TempBufferSize = CompressionBlockSize;
TempBuffer.Reset((uint8*)FMemory::Malloc(TempBufferSize));
}
if(ScratchBufferSize < ScrachSize)
{
ScratchBufferSize = ScrachSize;
ScratchBuffer.Reset((uint8*)FMemory::Malloc(ScratchBufferSize));
}
}
};
/**
* Class to handle correctly reading from a compressed file within a pak
*/
template< typename EncryptionPolicy = FPakNoEncryption >
class FPakCompressedReaderPolicy
{
public:
class FPakUncompressTask : public FNonAbandonableTask
{
public:
uint8* UncompressedBuffer;
int32 UncompressedSize;
uint8* CompressedBuffer;
int32 CompressedSize;
ECompressionFlags Flags;
void* CopyOut;
int64 CopyOffset;
int64 CopyLength;
void DoWork()
{
// Decrypt and Uncompress from memory to memory.
int64 EncryptionSize = EncryptionPolicy::AlignReadRequest(CompressedSize);
EncryptionPolicy::DecryptBlock(CompressedBuffer, EncryptionSize);
FCompression::UncompressMemory(Flags, UncompressedBuffer, UncompressedSize, CompressedBuffer, CompressedSize, false);
if (CopyOut)
{
FMemory::Memcpy(CopyOut, UncompressedBuffer+CopyOffset, CopyLength);
}
}
FORCEINLINE TStatId GetStatId() const
{
// TODO: This is called too early in engine startup.
return TStatId();
//RETURN_QUICK_DECLARE_CYCLE_STAT(FPakUncompressTask, STATGROUP_ThreadPoolAsyncTasks);
}
};
FPakCompressedReaderPolicy(const FPakFile& InPakFile, const FPakEntry& InPakEntry, FArchive* InPakReader)
: PakFile(InPakFile)
, PakEntry(InPakEntry)
, PakReader(InPakReader)
{
}
/** Pak file that own this file data */
const FPakFile& PakFile;
/** Pak file entry for this file. */
const FPakEntry& PakEntry;
/** Pak file archive to read the data from. */
FArchive* PakReader;
FORCEINLINE int64 FileSize() const
{
return PakEntry.UncompressedSize;
}
void Serialize(int64 DesiredPosition, void* V, int64 Length)
{
const int32 CompressionBlockSize = PakEntry.CompressionBlockSize;
uint32 CompressionBlockIndex = DesiredPosition / CompressionBlockSize;
uint8* WorkingBuffers[2];
int64 DirectCopyStart = DesiredPosition % PakEntry.CompressionBlockSize;
FAsyncTask<FPakUncompressTask> UncompressTask;
FCompressionScratchBuffers& ScratchSpace = FCompressionScratchBuffers::Get();
bool bStartedUncompress = false;
int64 WorkingBufferRequiredSize = FCompression::CompressMemoryBound((ECompressionFlags)PakEntry.CompressionMethod,CompressionBlockSize);
WorkingBufferRequiredSize = EncryptionPolicy::AlignReadRequest(WorkingBufferRequiredSize);
ScratchSpace.EnsureBufferSpace(CompressionBlockSize, WorkingBufferRequiredSize*2);
WorkingBuffers[0] = ScratchSpace.ScratchBuffer;
WorkingBuffers[1] = ScratchSpace.ScratchBuffer + WorkingBufferRequiredSize;
while (Length > 0)
{
const FPakCompressedBlock& Block = PakEntry.CompressionBlocks[CompressionBlockIndex];
int64 Pos = CompressionBlockIndex * CompressionBlockSize;
int64 CompressedBlockSize = Block.CompressedEnd-Block.CompressedStart;
int64 UncompressedBlockSize = FMath::Min<int64>(PakEntry.UncompressedSize-Pos, PakEntry.CompressionBlockSize);
int64 ReadSize = EncryptionPolicy::AlignReadRequest(CompressedBlockSize);
int64 WriteSize = FMath::Min<int64>(UncompressedBlockSize - DirectCopyStart, Length);
PakReader->Seek(Block.CompressedStart);
PakReader->Serialize(WorkingBuffers[CompressionBlockIndex & 1],ReadSize);
if (bStartedUncompress)
{
UncompressTask.EnsureCompletion();
bStartedUncompress = false;
}
FPakUncompressTask& TaskDetails = UncompressTask.GetTask();
if (DirectCopyStart == 0 && Length >= CompressionBlockSize)
{
// Block can be decompressed directly into output buffer
TaskDetails.Flags = (ECompressionFlags)PakEntry.CompressionMethod;
TaskDetails.UncompressedBuffer = (uint8*)V;
TaskDetails.UncompressedSize = UncompressedBlockSize;
TaskDetails.CompressedBuffer = WorkingBuffers[CompressionBlockIndex & 1];
TaskDetails.CompressedSize = CompressedBlockSize;
TaskDetails.CopyOut = nullptr;
}
else
{
// Block needs to be copied from a working buffer
TaskDetails.Flags = (ECompressionFlags)PakEntry.CompressionMethod;
TaskDetails.UncompressedBuffer = (uint8*)ScratchSpace.TempBuffer;
TaskDetails.UncompressedSize = UncompressedBlockSize;
TaskDetails.CompressedBuffer = WorkingBuffers[CompressionBlockIndex & 1];
TaskDetails.CompressedSize = CompressedBlockSize;
TaskDetails.CopyOut = V;
TaskDetails.CopyOffset = DirectCopyStart;
TaskDetails.CopyLength = WriteSize;
}
if (Length == WriteSize)
{
UncompressTask.StartSynchronousTask();
}
else
{
UncompressTask.StartBackgroundTask();
}
bStartedUncompress = true;
V = (void*)((uint8*)V + WriteSize);
Length -= WriteSize;
DirectCopyStart = 0;
++CompressionBlockIndex;
}
if(bStartedUncompress)
{
UncompressTask.EnsureCompletion();
}
}
};
bool FPakEntry::VerifyPakEntriesMatch(const FPakEntry& FileEntryA, const FPakEntry& FileEntryB)
{
bool bResult = true;
if (FileEntryA.Size != FileEntryB.Size)
{
UE_LOG(LogPakFile, Error, TEXT("Pak header file size mismatch, got: %lld, expected: %lld"), FileEntryB.Size, FileEntryA.Size);
bResult = false;
}
if (FileEntryA.UncompressedSize != FileEntryB.UncompressedSize)
{
UE_LOG(LogPakFile, Error, TEXT("Pak header uncompressed file size mismatch, got: %lld, expected: %lld"), FileEntryB.UncompressedSize, FileEntryA.UncompressedSize);
bResult = false;
}
if (FileEntryA.CompressionMethod != FileEntryB.CompressionMethod)
{
UE_LOG(LogPakFile, Error, TEXT("Pak header file compression method mismatch, got: %d, expected: %d"), FileEntryB.CompressionMethod, FileEntryA.CompressionMethod);
bResult = false;
}
if (FMemory::Memcmp(FileEntryA.Hash, FileEntryB.Hash, sizeof(FileEntryA.Hash)) != 0)
{
UE_LOG(LogPakFile, Error, TEXT("Pak file hash does not match its index entry"));
bResult = false;
}
return bResult;
}
bool FPakPlatformFile::IsNonPakFilenameAllowed(const FString& InFilename)
{
FFilenameSecurityDelegate& FilenameSecurityDelegate = GetFilenameSecurityDelegate();
if (!FilenameSecurityDelegate.IsBound())
{
return true;
}
if (GetLowerLevel()->FileExists(*InFilename))
{
return FilenameSecurityDelegate.Execute(*InFilename);
}
else
{
return false;
}
}
FPakFile::FPakFile(const TCHAR* Filename, bool bIsSigned)
: PakFilename(Filename)
, bSigned(bIsSigned)
, bIsValid(false)
{
FArchive* Reader = GetSharedReader(NULL);
if (Reader)
{
Timestamp = IFileManager::Get().GetTimeStamp(Filename);
Initialize(Reader);
}
}
FPakFile::FPakFile(IPlatformFile* LowerLevel, const TCHAR* Filename, bool bIsSigned)
: PakFilename(Filename)
, bSigned(bIsSigned)
, bIsValid(false)
{
FArchive* Reader = GetSharedReader(LowerLevel);
if (Reader)
{
Timestamp = LowerLevel->GetTimeStamp(Filename);
Initialize(Reader);
}
}
FPakFile::FPakFile(FArchive* Archive)
: bSigned(false)
, bIsValid(false)
{
Initialize(Archive);
}
FPakFile::~FPakFile()
{
}
FArchive* FPakFile::CreatePakReader(const TCHAR* Filename)
{
FArchive* ReaderArchive = IFileManager::Get().CreateFileReader(Filename);
return SetupSignedPakReader(ReaderArchive, Filename);
}
FArchive* FPakFile::CreatePakReader(IFileHandle& InHandle, const TCHAR* Filename)
{
FArchive* ReaderArchive = new FArchiveFileReaderGeneric(&InHandle, Filename, InHandle.Size());
return SetupSignedPakReader(ReaderArchive, Filename);
}
FArchive* FPakFile::SetupSignedPakReader(FArchive* ReaderArchive, const TCHAR* Filename)
{
if (FPlatformProperties::RequiresCookedData())
{
#if !USING_SIGNED_CONTENT
if (bSigned || FParse::Param(FCommandLine::Get(), TEXT("signedpak")) || FParse::Param(FCommandLine::Get(), TEXT("signed")))
#endif
{
if (!Decryptor.IsValid())
{
Decryptor = new FChunkCacheWorker(ReaderArchive, Filename);
}
ReaderArchive = new FSignedArchiveReader(ReaderArchive, Decryptor);
}
}
return ReaderArchive;
}
void FPakFile::Initialize(FArchive* Reader)
{
if (Reader->TotalSize() < Info.GetSerializedSize())
{
UE_LOG(LogPakFile, Fatal, TEXT("Corrupted pak file (too short). Verify your installation."));
}
else
{
// Serialize trailer and check if everything is as expected.
Reader->Seek(Reader->TotalSize() - Info.GetSerializedSize());
Info.Serialize(*Reader);
UE_CLOG(Info.Magic != FPakInfo::PakFile_Magic, LogPakFile, Fatal, TEXT("Trailing magic number (%ud) is different than the expected one. Verify your installation."), Info.Magic);
UE_CLOG(!(Info.Version >= FPakInfo::PakFile_Version_Initial && Info.Version <= FPakInfo::PakFile_Version_Latest), LogPakFile, Fatal, TEXT("Invalid pak file version (%d). Verify your installation."), Info.Version);
LoadIndex(Reader);
// LoadIndex should crash in case of an error, so just assume everything is ok if we got here.
bIsValid = true;
}
}
void FPakFile::LoadIndex(FArchive* Reader)
{
if (Reader->TotalSize() < (Info.IndexOffset + Info.IndexSize))
{
UE_LOG(LogPakFile, Fatal, TEXT("Corrupted index offset in pak file."));
}
else
{
// Load index into memory first.
Reader->Seek(Info.IndexOffset);
TArray<uint8> IndexData;
IndexData.AddUninitialized(Info.IndexSize);
Reader->Serialize(IndexData.GetData(), Info.IndexSize);
FMemoryReader IndexReader(IndexData);
// Check SHA1 value.
uint8 IndexHash[20];
FSHA1::HashBuffer(IndexData.GetData(), IndexData.Num(), IndexHash);
if (FMemory::Memcmp(IndexHash, Info.IndexHash, sizeof(IndexHash)) != 0)
{
UE_LOG(LogPakFile, Fatal, TEXT("Corrupted index in pak file (CRC mismatch)."));
}
// Read the default mount point and all entries.
int32 NumEntries = 0;
IndexReader << MountPoint;
IndexReader << NumEntries;
MakeDirectoryFromPath(MountPoint);
// Allocate enough memory to hold all entries (and not reallocate while they're being added to it).
Files.Empty(NumEntries);
for (int32 EntryIndex = 0; EntryIndex < NumEntries; EntryIndex++)
{
// Serialize from memory.
FPakEntry Entry;
FString Filename;
IndexReader << Filename;
Entry.Serialize(IndexReader, Info.Version);
// Add new file info.
Files.Add(Entry);
// Construct Index of all directories in pak file.
FString Path = FPaths::GetPath(Filename);
MakeDirectoryFromPath(Path);
FPakDirectory* Directory = Index.Find(Path);
if (Directory != NULL)
{
Directory->Add(Filename, &Files.Last());
}
else
{
FPakDirectory NewDirectory;
NewDirectory.Add(Filename, &Files.Last());
Index.Add(Path, NewDirectory);
// add the parent directories up to the mount point
while (MountPoint != Path)
{
Path = Path.Left(Path.Len()-1);
int32 Offset = 0;
if (Path.FindLastChar('/', Offset))
{
Path = Path.Left(Offset);
MakeDirectoryFromPath(Path);
if (Index.Find(Path) == NULL)
{
FPakDirectory ParentDirectory;
Index.Add(Path, ParentDirectory);
}
}
else
{
Path = MountPoint;
}
}
}
}
}
}
FArchive* FPakFile::GetSharedReader(IPlatformFile* LowerLevel)
{
uint32 Thread = FPlatformTLS::GetCurrentThreadId();
FArchive* PakReader = NULL;
{
FScopeLock ScopedLock(&CriticalSection);
TAutoPtr<FArchive>* ExistingReader = ReaderMap.Find(Thread);
if (ExistingReader)
{
PakReader = *ExistingReader;
}
}
if (!PakReader)
{
// Create a new FArchive reader and pass it to the new handle.
if (LowerLevel != NULL)
{
IFileHandle* PakHandle = LowerLevel->OpenRead(*GetFilename());
if (PakHandle)
{
PakReader = CreatePakReader(*PakHandle, *GetFilename());
}
}
else
{
PakReader = CreatePakReader(*GetFilename());
}
if (!PakReader)
{
UE_LOG(LogPakFile, Fatal, TEXT("Unable to create pak \"%s\" handle"), *GetFilename());
}
{
FScopeLock ScopedLock(&CriticalSection);
ReaderMap.Emplace(Thread, PakReader);
}
}
return PakReader;
}
#if !UE_BUILD_SHIPPING
class FPakExec : private FSelfRegisteringExec
{
FPakPlatformFile& PlatformFile;
public:
FPakExec(FPakPlatformFile& InPlatformFile)
: PlatformFile(InPlatformFile)
{}
/** Console commands **/
virtual bool Exec( UWorld* InWorld, const TCHAR* Cmd, FOutputDevice& Ar ) override
{
if (FParse::Command(&Cmd, TEXT("Mount")))
{
PlatformFile.HandleMountCommand(Cmd, Ar);
return true;
}
if (FParse::Command(&Cmd, TEXT("Unmount")))
{
PlatformFile.HandleUnmountCommand(Cmd, Ar);
return true;
}
else if (FParse::Command(&Cmd, TEXT("PakList")))
{
PlatformFile.HandlePakListCommand(Cmd, Ar);
return true;
}
return false;
}
};
static TAutoPtr<FPakExec> GPakExec;
void FPakPlatformFile::HandleMountCommand(const TCHAR* Cmd, FOutputDevice& Ar)
{
const FString PakFilename = FParse::Token(Cmd, false);
if (!PakFilename.IsEmpty())
{
const FString MountPoint = FParse::Token(Cmd, false);
Mount(*PakFilename, 0, MountPoint.IsEmpty() ? NULL : *MountPoint);
}
}
void FPakPlatformFile::HandleUnmountCommand(const TCHAR* Cmd, FOutputDevice& Ar)
{
const FString PakFilename = FParse::Token(Cmd, false);
if (!PakFilename.IsEmpty())
{
Unmount(*PakFilename);
}
}
void FPakPlatformFile::HandlePakListCommand(const TCHAR* Cmd, FOutputDevice& Ar)
{
TArray<FPakListEntry> Paks;
GetMountedPaks(Paks);
for (auto Pak : Paks)
{
Ar.Logf(TEXT("%s"), *Pak.PakFile->GetFilename());
}
}
#endif // !UE_BUILD_SHIPPING
FPakPlatformFile::FPakPlatformFile()
: LowerLevel(NULL)
, bSigned(false)
{
}
FPakPlatformFile::~FPakPlatformFile()
{
FCoreDelegates::OnMountPak.Unbind();
FCoreDelegates::OnUnmountPak.Unbind();
// We need to flush async IO... if it hasn't been shut down already.
if (FIOSystem::HasShutdown() == false)
{
FIOSystem& IOSystem = FIOSystem::Get();
IOSystem.BlockTillAllRequestsFinishedAndFlushHandles();
}
{
FScopeLock ScopedLock(&PakListCritical);
for (int32 PakFileIndex = 0; PakFileIndex < PakFiles.Num(); PakFileIndex++)
{
delete PakFiles[PakFileIndex].PakFile;
PakFiles[PakFileIndex].PakFile = nullptr;
}
}
}
void FPakPlatformFile::FindPakFilesInDirectory(IPlatformFile* LowLevelFile, const TCHAR* Directory, TArray<FString>& OutPakFiles)
{
// Helper class to find all pak files.
class FPakSearchVisitor : public IPlatformFile::FDirectoryVisitor
{
TArray<FString>& FoundPakFiles;
IPlatformChunkInstall* ChunkInstall;
public:
FPakSearchVisitor(TArray<FString>& InFoundPakFiles, IPlatformChunkInstall* InChunkInstall)
: FoundPakFiles(InFoundPakFiles)
, ChunkInstall(InChunkInstall)
{}
virtual bool Visit(const TCHAR* FilenameOrDirectory, bool bIsDirectory)
{
if (bIsDirectory == false)
{
FString Filename(FilenameOrDirectory);
if (FPaths::GetExtension(Filename) == TEXT("pak"))
{
// if a platform supports chunk style installs, make sure that the chunk a pak file resides in is actually fully installed before accepting pak files from it
if (ChunkInstall)
{
FString ChunkIdentifier(TEXT("pakchunk"));
FString BaseFilename = FPaths::GetBaseFilename(Filename);
if (BaseFilename.StartsWith(ChunkIdentifier))
{
int32 DelimiterIndex = 0;
int32 StartOfChunkIndex = ChunkIdentifier.Len();
BaseFilename.FindChar(TEXT('-'), DelimiterIndex);
FString ChunkNumberString = BaseFilename.Mid(StartOfChunkIndex, DelimiterIndex-StartOfChunkIndex);
int32 ChunkNumber = 0;
TTypeFromString<int32>::FromString(ChunkNumber, *ChunkNumberString);
if (ChunkInstall->GetChunkLocation(ChunkNumber) == EChunkLocation::NotAvailable)
{
return true;
}
}
}
FoundPakFiles.Add(Filename);
}
}
return true;
}
};
// Find all pak files.
FPakSearchVisitor Visitor(OutPakFiles, FPlatformMisc::GetPlatformChunkInstall());
LowLevelFile->IterateDirectoryRecursively(Directory, Visitor);
}
void FPakPlatformFile::FindAllPakFiles(IPlatformFile* LowLevelFile, const TArray<FString>& PakFolders, TArray<FString>& OutPakFiles)
{
// Find pak files from the specified directories.
for (int32 FolderIndex = 0; FolderIndex < PakFolders.Num(); ++FolderIndex)
{
FindPakFilesInDirectory(LowLevelFile, *PakFolders[FolderIndex], OutPakFiles);
}
}
void FPakPlatformFile::GetPakFolders(const TCHAR* CmdLine, TArray<FString>& OutPakFolders)
{
#if !UE_BUILD_SHIPPING
// Command line folders
FString PakDirs;
if (FParse::Value(CmdLine, TEXT("-pakdir="), PakDirs))
{
TArray<FString> CmdLineFolders;
PakDirs.ParseIntoArray(CmdLineFolders, TEXT("*"), true);
OutPakFolders.Append(CmdLineFolders);
}
#endif
// @todo plugin urgent: Needs to handle plugin Pak directories, too
// Hardcoded locations
OutPakFolders.Add(FString::Printf(TEXT("%sPaks/"), *FPaths::GameContentDir()));
OutPakFolders.Add(FString::Printf(TEXT("%sPaks/"), *FPaths::GameSavedDir()));
OutPakFolders.Add(FString::Printf(TEXT("%sPaks/"), *FPaths::EngineContentDir()));
}
bool FPakPlatformFile::CheckIfPakFilesExist(IPlatformFile* LowLevelFile, const TArray<FString>& PakFolders)
{
TArray<FString> FoundPakFiles;
FindAllPakFiles(LowLevelFile, PakFolders, FoundPakFiles);
return FoundPakFiles.Num() > 0;
}
bool FPakPlatformFile::ShouldBeUsed(IPlatformFile* Inner, const TCHAR* CmdLine) const
{
#if !USING_SIGNED_CONTENT
bool Result = FParse::Param(CmdLine, TEXT("Pak")) || FParse::Param(CmdLine, TEXT("Signedpak")) || FParse::Param(CmdLine, TEXT("Signed"));
if (FPlatformProperties::RequiresCookedData() && !Result && !FParse::Param(CmdLine, TEXT("NoPak")))
{
TArray<FString> PakFolders;
GetPakFolders(CmdLine, PakFolders);
Result = CheckIfPakFilesExist(Inner, PakFolders);
}
return Result;
#else
return true;
#endif
}
bool FPakPlatformFile::Initialize(IPlatformFile* Inner, const TCHAR* CmdLine)
{
// Inner is required.
check(Inner != NULL);
LowerLevel = Inner;
#if !USING_SIGNED_CONTENT
bSigned = FParse::Param(CmdLine, TEXT("Signedpak")) || FParse::Param(CmdLine, TEXT("Signed"));
if (!bSigned)
{
// Even if -signed is not provided in the command line, use signed reader if the hardcoded key is non-zero.
FEncryptionKey DecryptionKey;
DecryptionKey.Exponent.Parse(DECRYPTION_KEY_EXPONENT);
DecryptionKey.Modulus.Parse(DECRYPTION_KEY_MODULUS);
bSigned = !DecryptionKey.Exponent.IsZero() && !DecryptionKey.Modulus.IsZero();
}
#else
bSigned = true;
#endif
bool bMountPaks = true;
TArray<FString> PaksToLoad;
#if !UE_BUILD_SHIPPING
// Optionally get a list of pak filenames to load, only these paks will be mounted
FString CmdLinePaksToLoad;
if (FParse::Value(CmdLine, TEXT("-paklist="), CmdLinePaksToLoad))
{
CmdLinePaksToLoad.ParseIntoArray(PaksToLoad, TEXT("+"), true);
}
//if we are using a fileserver, then dont' mount paks automatically. We only want to read files from the server.
FString FileHostIP;
const bool bCookOnTheFly = FParse::Value(FCommandLine::Get(), TEXT("filehostip"), FileHostIP);
bMountPaks = !bCookOnTheFly;
#endif
if (bMountPaks)
{
// Find and mount pak files from the specified directories.
TArray<FString> PakFolders;
GetPakFolders(CmdLine, PakFolders);
TArray<FString> FoundPakFiles;
FindAllPakFiles(LowerLevel, PakFolders, FoundPakFiles);
// Sort in descending order.
FoundPakFiles.Sort(TGreater<FString>());
// Mount all found pak files
for (int32 PakFileIndex = 0; PakFileIndex < FoundPakFiles.Num(); PakFileIndex++)
{
const FString& PakFilename = FoundPakFiles[PakFileIndex];
bool bLoadPak = true;
if (PaksToLoad.Num() && !PaksToLoad.Contains(FPaths::GetBaseFilename(PakFilename)))
{
bLoadPak = false;
}
if (bLoadPak)
{
// hardcode default load ordering of game main pak -> game content -> engine content -> saved dir
// would be better to make this config but not even the config system is initialized here so we can't do that
uint32 PakOrder = 0;
if (PakFilename.StartsWith(FString::Printf(TEXT("%sPaks/%s-"), *FPaths::GameContentDir(), FApp::GetGameName())))
{
PakOrder = 4;
}
else if (PakFilename.StartsWith(FPaths::GameContentDir()))
{
PakOrder = 3;
}
else if (PakFilename.StartsWith(FPaths::EngineContentDir()))
{
PakOrder = 2;
}
else if (PakFilename.StartsWith(FPaths::GameSavedDir()))
{
PakOrder = 1;
}
Mount(*PakFilename, PakOrder);
}
}
}
#if !UE_BUILD_SHIPPING
GPakExec = new FPakExec(*this);
#endif // !UE_BUILD_SHIPPING
FCoreDelegates::OnMountPak.BindRaw(this, &FPakPlatformFile::HandleMountPakDelegate);
FCoreDelegates::OnUnmountPak.BindRaw(this, &FPakPlatformFile::HandleUnmountPakDelegate);
return !!LowerLevel;
}
bool FPakPlatformFile::Mount(const TCHAR* InPakFilename, uint32 PakOrder, const TCHAR* InPath /*= NULL*/)
{
bool bSuccess = false;
TSharedPtr<IFileHandle> PakHandle = MakeShareable(LowerLevel->OpenRead(InPakFilename));
if (PakHandle.IsValid())
{
FPakFile* Pak = new FPakFile(LowerLevel, InPakFilename, bSigned);
if (Pak->IsValid())
{
if (InPath != NULL)
{
Pak->SetMountPoint(InPath);
}
FString PakFilename = InPakFilename;
if ( PakFilename.EndsWith(TEXT("_P.pak")) )
{
PakOrder += 100;
}
{
// Add new pak file
FScopeLock ScopedLock(&PakListCritical);
FPakListEntry Entry;
Entry.ReadOrder = PakOrder;
Entry.PakFile = Pak;
PakFiles.Add(Entry);
PakFiles.StableSort();
}
bSuccess = true;
}
else
{
UE_LOG(LogPakFile, Warning, TEXT("Failed to mount pak \"%s\", pak is invalid."), InPakFilename);
}
}
else
{
UE_LOG(LogPakFile, Warning, TEXT("Pak \"%s\" does not exist!"), InPakFilename);
}
return bSuccess;
}
bool FPakPlatformFile::Unmount(const TCHAR* InPakFilename)
{
{
FScopeLock ScopedLock(&PakListCritical);
for (int32 PakIndex = 0; PakIndex < PakFiles.Num(); PakIndex++)
{
if (PakFiles[PakIndex].PakFile->GetFilename() == InPakFilename)
{
delete PakFiles[PakIndex].PakFile;
PakFiles.RemoveAt(PakIndex);
return true;
}
}
}
return false;
}
IFileHandle* FPakPlatformFile::CreatePakFileHandle(const TCHAR* Filename, FPakFile* PakFile, const FPakEntry* FileEntry)
{
IFileHandle* Result = NULL;
FArchive* PakReader = PakFile->GetSharedReader(LowerLevel);
// Create the handle.
if (FileEntry->CompressionMethod != COMPRESS_None && PakFile->GetInfo().Version >= FPakInfo::PakFile_Version_CompressionEncryption)
{
if (FileEntry->bEncrypted)
{
Result = new FPakFileHandle< FPakCompressedReaderPolicy<FPakSimpleEncryption> >(*PakFile, *FileEntry, PakReader, true);
}
else
{
Result = new FPakFileHandle< FPakCompressedReaderPolicy<> >(*PakFile, *FileEntry, PakReader, true);
}
}
else if (FileEntry->bEncrypted)
{
Result = new FPakFileHandle< FPakReaderPolicy<FPakSimpleEncryption> >(*PakFile, *FileEntry, PakReader, true);
}
else
{
Result = new FPakFileHandle<>(*PakFile, *FileEntry, PakReader, true);
}
return Result;
}
bool FPakPlatformFile::HandleMountPakDelegate(const FString& PakFilePath, uint32 PakOrder, IPlatformFile::FDirectoryVisitor* Visitor)
{
bool bReturn = Mount(*PakFilePath, PakOrder);
if (bReturn && Visitor != nullptr)
{
TArray<FPakListEntry> Paks;
GetMountedPaks(Paks);
// Find the single pak we just mounted
for (auto Pak : Paks)
{
if (PakFilePath == Pak.PakFile->GetFilename())
{
// Get a list of all of the files in the pak
for (FPakFile::FFileIterator It(*Pak.PakFile); It; ++It)
{
Visitor->Visit(*It.Filename(), false);
}
return true;
}
}
}
return bReturn;
}
bool FPakPlatformFile::HandleUnmountPakDelegate(const FString& PakFilePath)
{
return Unmount(*PakFilePath);
}
IFileHandle* FPakPlatformFile::OpenRead(const TCHAR* Filename, bool bAllowWrite)
{
IFileHandle* Result = NULL;
FPakFile* PakFile = NULL;
const FPakEntry* FileEntry = FindFileInPakFiles(Filename, &PakFile);
if (FileEntry != NULL)
{
Result = CreatePakFileHandle(Filename, PakFile, FileEntry);
}
#if !USING_SIGNED_CONTENT
else
{
if (IsNonPakFilenameAllowed(Filename))
{
// Default to wrapped file
Result = LowerLevel->OpenRead(Filename, bAllowWrite);
}
}
#endif
return Result;
}
bool FPakPlatformFile::BufferedCopyFile(IFileHandle& Dest, IFileHandle& Source, const int64 FileSize, uint8* Buffer, const int64 BufferSize) const
{
int64 RemainingSizeToCopy = FileSize;
// Continue copying chunks using the buffer
while (RemainingSizeToCopy > 0)
{
const int64 SizeToCopy = FMath::Min(BufferSize, RemainingSizeToCopy);
if (Source.Read(Buffer, SizeToCopy) == false)
{
return false;
}
if (Dest.Write(Buffer, SizeToCopy) == false)
{
return false;
}
RemainingSizeToCopy -= SizeToCopy;
}
return true;
}
bool FPakPlatformFile::CopyFile(const TCHAR* To, const TCHAR* From)
{
bool Result = false;
FPakFile* PakFile = NULL;
const FPakEntry* FileEntry = FindFileInPakFiles(From, &PakFile);
if (FileEntry != NULL)
{
// Copy from pak to LowerLevel->
// Create handles both files.
TAutoPtr<IFileHandle> DestHandle(LowerLevel->OpenWrite(To));
TAutoPtr<IFileHandle> SourceHandle(CreatePakFileHandle(From, PakFile, FileEntry));
if (DestHandle.IsValid() && SourceHandle.IsValid())
{
const int64 BufferSize = 64 * 1024; // Copy in 64K chunks.
uint8* Buffer = (uint8*)FMemory::Malloc(BufferSize);
Result = BufferedCopyFile(*DestHandle, *SourceHandle, SourceHandle->Size(), Buffer, BufferSize);
FMemory::Free(Buffer);
}
}
else
{
Result = LowerLevel->CopyFile(To, From);
}
return Result;
}
uint32 ComputePakChunkHash(const uint8* InData, const int64 InDataSize)
{
return FCrc::MemCrc32(InData, InDataSize);
}
/**
* Module for the pak file
*/
class FPakFileModule : public IPlatformFileModule
{
public:
virtual IPlatformFile* GetPlatformFile() override
{
static TScopedPointer<IPlatformFile> AutoDestroySingleton(new FPakPlatformFile());
return AutoDestroySingleton.GetOwnedPointer();
}
};
IMPLEMENT_MODULE(FPakFileModule, PakFile);