void FChunkWriter::FQueuedChunkWriter::GetChunkFilesizes(TMap<FGuid, int64>& OutChunkFileSizes) { FScopeLock ScopeLock(&ChunkFileSizesCS); OutChunkFileSizes.Empty(ChunkFileSizes.Num()); OutChunkFileSizes.Append(ChunkFileSizes); }
int32 SGraphPanel::OnPaint( const FPaintArgs& Args, const FGeometry& AllottedGeometry, const FSlateRect& MyClippingRect, FSlateWindowElementList& OutDrawElements, int32 LayerId, const FWidgetStyle& InWidgetStyle, bool bParentEnabled ) const { #if SLATE_HD_STATS SCOPE_CYCLE_COUNTER( STAT_SlateOnPaint_SGraphPanel ); #endif CachedAllottedGeometryScaledSize = AllottedGeometry.Size * AllottedGeometry.Scale; //Style used for objects that are the same between revisions FWidgetStyle FadedStyle = InWidgetStyle; FadedStyle.BlendColorAndOpacityTint(FLinearColor(0.45f,0.45f,0.45f,0.45f)); // First paint the background const UEditorExperimentalSettings& Options = *GetDefault<UEditorExperimentalSettings>(); const FSlateBrush* BackgroundImage = FEditorStyle::GetBrush(TEXT("Graph.Panel.SolidBackground")); PaintBackgroundAsLines(BackgroundImage, AllottedGeometry, MyClippingRect, OutDrawElements, LayerId); const float ZoomFactor = AllottedGeometry.Scale * GetZoomAmount(); FArrangedChildren ArrangedChildren(EVisibility::Visible); ArrangeChildNodes(AllottedGeometry, ArrangedChildren); // Determine some 'global' settings based on current LOD const bool bDrawShadowsThisFrame = GetCurrentLOD() > EGraphRenderingLOD::LowestDetail; // Because we paint multiple children, we must track the maximum layer id that they produced in case one of our parents // wants to an overlay for all of its contents. // Save LayerId for comment boxes to ensure they always appear below nodes & wires const int32 CommentNodeShadowLayerId = LayerId++; const int32 CommentNodeLayerId = LayerId++; // Save a LayerId for wires, which appear below nodes but above comments // We will draw them later, along with the arrows which appear above nodes. const int32 WireLayerId = LayerId++; const int32 NodeShadowsLayerId = LayerId; const int32 NodeLayerId = NodeShadowsLayerId + 1; int32 MaxLayerId = NodeLayerId; const FVector2D NodeShadowSize = GetDefault<UGraphEditorSettings>()->GetShadowDeltaSize(); const UEdGraphSchema* Schema = GraphObj->GetSchema(); // Draw the child nodes { // When drawing a marquee, need a preview of what the selection will be. const FGraphPanelSelectionSet* SelectionToVisualize = &(SelectionManager.SelectedNodes); FGraphPanelSelectionSet SelectionPreview; if ( Marquee.IsValid() ) { ApplyMarqueeSelection(Marquee, SelectionManager.SelectedNodes, SelectionPreview); SelectionToVisualize = &SelectionPreview; } // Context for rendering node infos FKismetNodeInfoContext Context(GraphObj); TArray<FGraphDiffControl::FNodeMatch> NodeMatches; for (int32 ChildIndex = 0; ChildIndex < ArrangedChildren.Num(); ++ChildIndex) { FArrangedWidget& CurWidget = ArrangedChildren[ChildIndex]; TSharedRef<SGraphNode> ChildNode = StaticCastSharedRef<SGraphNode>(CurWidget.Widget); // Examine node to see what layers we should be drawing in int32 ShadowLayerId = NodeShadowsLayerId; int32 ChildLayerId = NodeLayerId; // If a comment node, draw in the dedicated comment slots { UObject* NodeObj = ChildNode->GetObjectBeingDisplayed(); if (NodeObj && NodeObj->IsA(UEdGraphNode_Comment::StaticClass())) { ShadowLayerId = CommentNodeShadowLayerId; ChildLayerId = CommentNodeLayerId; } } const bool bNodeIsVisible = FSlateRect::DoRectanglesIntersect( CurWidget.Geometry.GetClippingRect(), MyClippingRect ); if (bNodeIsVisible) { const bool bSelected = SelectionToVisualize->Contains( StaticCastSharedRef<SNodePanel::SNode>(CurWidget.Widget)->GetObjectBeingDisplayed() ); // Handle Node renaming once the node is visible if( bSelected && ChildNode->IsRenamePending() ) { ChildNode->ApplyRename(); } // Draw the node's shadow. if (bDrawShadowsThisFrame || bSelected) { const FSlateBrush* ShadowBrush = ChildNode->GetShadowBrush(bSelected); FSlateDrawElement::MakeBox( OutDrawElements, ShadowLayerId, CurWidget.Geometry.ToInflatedPaintGeometry(NodeShadowSize), ShadowBrush, MyClippingRect ); } // Draw the comments and information popups for this node, if it has any. { const SNodePanel::SNode::FNodeSlot& CommentSlot = ChildNode->GetOrAddSlot( ENodeZone::TopCenter ); float CommentBubbleY = -CommentSlot.Offset.Get().Y; Context.bSelected = bSelected; TArray<FGraphInformationPopupInfo> Popups; { ChildNode->GetNodeInfoPopups(&Context, /*out*/ Popups); } for (int32 PopupIndex = 0; PopupIndex < Popups.Num(); ++PopupIndex) { FGraphInformationPopupInfo& Popup = Popups[PopupIndex]; PaintComment(Popup.Message, CurWidget.Geometry, MyClippingRect, OutDrawElements, ChildLayerId, Popup.BackgroundColor, /*inout*/ CommentBubbleY, InWidgetStyle); } } int32 CurWidgetsMaxLayerId; { UEdGraphNode* NodeObj = Cast<UEdGraphNode>(ChildNode->GetObjectBeingDisplayed()); /** When diffing nodes, nodes that are different between revisions are opaque, nodes that have not changed are faded */ FGraphDiffControl::FNodeMatch NodeMatch = FGraphDiffControl::FindNodeMatch(GraphObjToDiff, NodeObj, NodeMatches); if (NodeMatch.IsValid()) { NodeMatches.Add(NodeMatch); } const bool bNodeIsDifferent = (!GraphObjToDiff || NodeMatch.Diff()); /* When dragging off a pin, we want to duck the alpha of some nodes */ TSharedPtr< SGraphPin > OnlyStartPin = (1 == PreviewConnectorFromPins.Num()) ? PreviewConnectorFromPins[0].FindInGraphPanel(*this) : TSharedPtr< SGraphPin >(); const bool bNodeIsNotUsableInCurrentContext = Schema->FadeNodeWhenDraggingOffPin(NodeObj, OnlyStartPin.IsValid() ? OnlyStartPin.Get()->GetPinObj() : NULL); const FWidgetStyle& NodeStyleToUse = (bNodeIsDifferent && !bNodeIsNotUsableInCurrentContext)? InWidgetStyle : FadedStyle; // Draw the node.O CurWidgetsMaxLayerId = CurWidget.Widget->Paint( Args.WithNewParent(this), CurWidget.Geometry, MyClippingRect, OutDrawElements, ChildLayerId, NodeStyleToUse, ShouldBeEnabled( bParentEnabled ) ); } // Draw the node's overlay, if it has one. { // Get its size const FVector2D WidgetSize = CurWidget.Geometry.Size; { TArray<FOverlayBrushInfo> OverlayBrushes; ChildNode->GetOverlayBrushes(bSelected, WidgetSize, /*out*/ OverlayBrushes); for (int32 BrushIndex = 0; BrushIndex < OverlayBrushes.Num(); ++BrushIndex) { FOverlayBrushInfo& OverlayInfo = OverlayBrushes[BrushIndex]; const FSlateBrush* OverlayBrush = OverlayInfo.Brush; if(OverlayBrush != NULL) { FPaintGeometry BouncedGeometry = CurWidget.Geometry.ToPaintGeometry(OverlayInfo.OverlayOffset, OverlayBrush->ImageSize, 1.f); // Handle bouncing const float BounceValue = FMath::Sin(2.0f * PI * BounceCurve.GetLerpLooping()); BouncedGeometry.DrawPosition += (OverlayInfo.AnimationEnvelope * BounceValue * ZoomFactor); CurWidgetsMaxLayerId++; FSlateDrawElement::MakeBox( OutDrawElements, CurWidgetsMaxLayerId, BouncedGeometry, OverlayBrush, MyClippingRect ); } } } { TArray<FOverlayWidgetInfo> OverlayWidgets = ChildNode->GetOverlayWidgets(bSelected, WidgetSize); for (int32 WidgetIndex = 0; WidgetIndex < OverlayWidgets.Num(); ++WidgetIndex) { FOverlayWidgetInfo& OverlayInfo = OverlayWidgets[WidgetIndex]; if(OverlayInfo.Widget->GetVisibility() == EVisibility::Visible) { // call SlatePrepass as these widgets are not in the 'normal' child hierarchy OverlayInfo.Widget->SlatePrepass(); const FGeometry WidgetGeometry = CurWidget.Geometry.MakeChild(OverlayInfo.OverlayOffset, OverlayInfo.Widget->GetDesiredSize(), 1.f); OverlayInfo.Widget->Paint(Args.WithNewParent(this), WidgetGeometry, MyClippingRect, OutDrawElements, CurWidgetsMaxLayerId, InWidgetStyle, bParentEnabled); } } } } MaxLayerId = FMath::Max( MaxLayerId, CurWidgetsMaxLayerId + 1 ); } } } MaxLayerId += 1; // Draw connections between pins if (Children.Num() > 0 ) { //@TODO: Pull this into a factory like the pin and node ones FConnectionDrawingPolicy* ConnectionDrawingPolicy; { ConnectionDrawingPolicy = Schema->CreateConnectionDrawingPolicy(WireLayerId, MaxLayerId, ZoomFactor, MyClippingRect, OutDrawElements, GraphObj); if (!ConnectionDrawingPolicy) { if (Schema->IsA(UAnimationGraphSchema::StaticClass())) { ConnectionDrawingPolicy = new FAnimGraphConnectionDrawingPolicy(WireLayerId, MaxLayerId, ZoomFactor, MyClippingRect, OutDrawElements, GraphObj); } else if (Schema->IsA(UAnimationStateMachineSchema::StaticClass())) { ConnectionDrawingPolicy = new FStateMachineConnectionDrawingPolicy(WireLayerId, MaxLayerId, ZoomFactor, MyClippingRect, OutDrawElements, GraphObj); } else if (Schema->IsA(UEdGraphSchema_K2::StaticClass())) { ConnectionDrawingPolicy = new FKismetConnectionDrawingPolicy(WireLayerId, MaxLayerId, ZoomFactor, MyClippingRect, OutDrawElements, GraphObj); } else if (Schema->IsA(USoundCueGraphSchema::StaticClass())) { ConnectionDrawingPolicy = new FSoundCueGraphConnectionDrawingPolicy(WireLayerId, MaxLayerId, ZoomFactor, MyClippingRect, OutDrawElements, GraphObj); } else if (Schema->IsA(UMaterialGraphSchema::StaticClass())) { ConnectionDrawingPolicy = new FMaterialGraphConnectionDrawingPolicy(WireLayerId, MaxLayerId, ZoomFactor, MyClippingRect, OutDrawElements, GraphObj); } else { ConnectionDrawingPolicy = new FConnectionDrawingPolicy(WireLayerId, MaxLayerId, ZoomFactor, MyClippingRect, OutDrawElements); } } } TArray<TSharedPtr<SGraphPin>> OverridePins; for (const FGraphPinHandle& Handle : PreviewConnectorFromPins) { TSharedPtr<SGraphPin> Pin = Handle.FindInGraphPanel(*this); if (Pin.IsValid()) { OverridePins.Add(Pin); } } ConnectionDrawingPolicy->SetHoveredPins(CurrentHoveredPins, OverridePins, TimeSinceMouseEnteredPin); ConnectionDrawingPolicy->SetMarkedPin(MarkedPin); // Get the set of pins for all children and synthesize geometry for culled out pins so lines can be drawn to them. TMap<TSharedRef<SWidget>, FArrangedWidget> PinGeometries; TSet< TSharedRef<SWidget> > VisiblePins; for (int32 ChildIndex = 0; ChildIndex < Children.Num(); ++ChildIndex) { TSharedRef<SGraphNode> ChildNode = StaticCastSharedRef<SGraphNode>(Children[ChildIndex]); // If this is a culled node, approximate the pin geometry to the corner of the node it is within if (IsNodeCulled(ChildNode, AllottedGeometry)) { TArray< TSharedRef<SWidget> > NodePins; ChildNode->GetPins(NodePins); const FVector2D NodeLoc = ChildNode->GetPosition(); const FGeometry SynthesizedNodeGeometry(GraphCoordToPanelCoord(NodeLoc), AllottedGeometry.AbsolutePosition, FVector2D::ZeroVector, 1.f); for (TArray< TSharedRef<SWidget> >::TConstIterator NodePinIterator(NodePins); NodePinIterator; ++NodePinIterator) { const SGraphPin& PinWidget = static_cast<const SGraphPin&>((*NodePinIterator).Get()); FVector2D PinLoc = NodeLoc + PinWidget.GetNodeOffset(); const FGeometry SynthesizedPinGeometry(GraphCoordToPanelCoord(PinLoc), AllottedGeometry.AbsolutePosition, FVector2D::ZeroVector, 1.f); PinGeometries.Add(*NodePinIterator, FArrangedWidget(*NodePinIterator, SynthesizedPinGeometry)); } // Also add synthesized geometries for culled nodes ArrangedChildren.AddWidget( FArrangedWidget(ChildNode, SynthesizedNodeGeometry) ); } else { ChildNode->GetPins(VisiblePins); } } // Now get the pin geometry for all visible children and append it to the PinGeometries map TMap<TSharedRef<SWidget>, FArrangedWidget> VisiblePinGeometries; { this->FindChildGeometries(AllottedGeometry, VisiblePins, VisiblePinGeometries); PinGeometries.Append(VisiblePinGeometries); } // Draw preview connections (only connected on one end) if (PreviewConnectorFromPins.Num() > 0) { for (const FGraphPinHandle& Handle : PreviewConnectorFromPins) { TSharedPtr< SGraphPin > CurrentStartPin = Handle.FindInGraphPanel(*this); if (!CurrentStartPin.IsValid()) { continue; } const FArrangedWidget* PinGeometry = PinGeometries.Find( CurrentStartPin.ToSharedRef() ); if (PinGeometry != NULL) { FVector2D StartPoint; FVector2D EndPoint; if (CurrentStartPin->GetDirection() == EGPD_Input) { StartPoint = AllottedGeometry.AbsolutePosition + PreviewConnectorEndpoint; EndPoint = FGeometryHelper::VerticalMiddleLeftOf( PinGeometry->Geometry ) - FVector2D(ConnectionDrawingPolicy->ArrowRadius.X, 0); } else { StartPoint = FGeometryHelper::VerticalMiddleRightOf( PinGeometry->Geometry ); EndPoint = AllottedGeometry.AbsolutePosition + PreviewConnectorEndpoint; } ConnectionDrawingPolicy->DrawPreviewConnector(PinGeometry->Geometry, StartPoint, EndPoint, CurrentStartPin.Get()->GetPinObj()); } //@TODO: Re-evaluate this incompatible mojo; it's mutating every pin state every frame to accomplish a visual effect ConnectionDrawingPolicy->SetIncompatiblePinDrawState(CurrentStartPin, VisiblePins); } } else { //@TODO: Re-evaluate this incompatible mojo; it's mutating every pin state every frame to accomplish a visual effect ConnectionDrawingPolicy->ResetIncompatiblePinDrawState(VisiblePins); } // Draw all regular connections ConnectionDrawingPolicy->Draw(PinGeometries, ArrangedChildren); delete ConnectionDrawingPolicy; } // Draw a shadow overlay around the edges of the graph ++MaxLayerId; PaintSurroundSunkenShadow(FEditorStyle::GetBrush(TEXT("Graph.Shadow")), AllottedGeometry, MyClippingRect, OutDrawElements, MaxLayerId); if(ShowGraphStateOverlay.Get()) { const FSlateBrush* BorderBrush = nullptr; if((GEditor->bIsSimulatingInEditor || GEditor->PlayWorld != NULL)) { // Draw a surrounding indicator when PIE is active, to make it clear that the graph is read-only, etc... BorderBrush = FEditorStyle::GetBrush(TEXT("Graph.PlayInEditor")); } else if(!IsEditable.Get()) { // Draw a different border when we're not simulating but the graph is read-only BorderBrush = FEditorStyle::GetBrush(TEXT("Graph.ReadOnlyBorder")); } if(BorderBrush) { // Actually draw the border FSlateDrawElement::MakeBox( OutDrawElements, MaxLayerId, AllottedGeometry.ToPaintGeometry(), BorderBrush, MyClippingRect ); } } // Draw the marquee selection rectangle PaintMarquee(AllottedGeometry, MyClippingRect, OutDrawElements, MaxLayerId); // Draw the software cursor ++MaxLayerId; PaintSoftwareCursor(AllottedGeometry, MyClippingRect, OutDrawElements, MaxLayerId); return MaxLayerId; }
void FManifestBuilderImpl::BuildManifest() { TMap<FGuid, FChunkInfo> ChunkInfoLookup; bool Running = true; while (Running) { FDataScannerPtr NextScanner = GetNextScanner(); if (NextScanner.IsValid()) { FDataScanResult ScanResult = NextScanner->GetResultWhenComplete(); ChunkInfoLookup.Append(ScanResult.ChunkInfo); // Always reverse for now if (ScanResult.DataStructure.Num() > 0) { FChunkPart& ChunkPart = ScanResult.DataStructure[0]; if (ChunkPart.DataOffset != FileBuilder.CurrentDataPos) { check(ChunkPart.DataOffset < FileBuilder.CurrentDataPos); // Missing data! bool FoundPosition = false; uint64 DataCount = 0; for (int32 FileIdx = 0; FileIdx < Manifest->Data->FileManifestList.Num() && !FoundPosition; ++FileIdx) { FFileManifestData& FileManifest = Manifest->Data->FileManifestList[FileIdx]; FileManifest.Init(); uint64 FileStartIdx = DataCount; uint64 FileEndIdx = FileStartIdx + FileManifest.GetFileSize(); if (FileEndIdx > ChunkPart.DataOffset) { for (int32 ChunkIdx = 0; ChunkIdx < FileManifest.FileChunkParts.Num() && !FoundPosition; ++ChunkIdx) { FChunkPartData& ChunkPartData = FileManifest.FileChunkParts[ChunkIdx]; uint64 ChunkPartEndIdx = DataCount + ChunkPartData.Size; if (ChunkPartEndIdx < ChunkPart.DataOffset) { DataCount += ChunkPartData.Size; } else if (ChunkPartEndIdx > ChunkPart.DataOffset) { ChunkPartData.Size = ChunkPart.DataOffset - DataCount; FileBuilder.CurrentDataPos = DataCount + ChunkPartData.Size; FileManifest.FileChunkParts.SetNum(ChunkIdx + 1, false); FileManifest.FileChunkParts.Emplace(); Manifest->Data->FileManifestList.SetNum(FileIdx + 1, false); FileBuilder.FileManifest = &Manifest->Data->FileManifestList.Last(); bool FoundFile = BuildStreamer->GetFileSpan(FileStartIdx, FileBuilder.FileSpan); check(FoundFile); // Incorrect positional tracking FoundPosition = true; } else { FileBuilder.CurrentDataPos = DataCount + ChunkPartData.Size; FileManifest.FileChunkParts.SetNum(ChunkIdx + 1, false); FileManifest.FileChunkParts.Emplace(); Manifest->Data->FileManifestList.SetNum(FileIdx + 1, false); FileBuilder.FileManifest = &Manifest->Data->FileManifestList.Last(); bool FoundFile = BuildStreamer->GetFileSpan(FileStartIdx, FileBuilder.FileSpan); check(FoundFile); // Incorrect positional tracking FoundPosition = true; } } } else if (FileEndIdx < ChunkPart.DataOffset) { DataCount += FileManifest.GetFileSize(); } else { FileBuilder.FileManifest = nullptr; FileBuilder.CurrentDataPos = DataCount + FileManifest.GetFileSize(); Manifest->Data->FileManifestList.SetNum(FileIdx + 1, false); FoundPosition = true; } } check(ChunkPart.DataOffset == FileBuilder.CurrentDataPos); check(FileBuilder.FileManifest == nullptr || FileBuilder.FileSpan.Filename == Manifest->Data->FileManifestList.Last().Filename); } } for (int32 idx = 0; idx < ScanResult.DataStructure.Num(); ++idx) { FChunkPart& ChunkPart = ScanResult.DataStructure[idx]; // Starting new file? if (FileBuilder.FileManifest == nullptr) { Manifest->Data->FileManifestList.Emplace(); FileBuilder.FileManifest = &Manifest->Data->FileManifestList.Last(); bool FoundFile = BuildStreamer->GetFileSpan(FileBuilder.CurrentDataPos, FileBuilder.FileSpan); check(FoundFile); // Incorrect positional tracking FileBuilder.FileManifest->Filename = FileBuilder.FileSpan.Filename; FileBuilder.FileManifest->FileChunkParts.Emplace(); } FChunkPartData& FileChunkPartData = FileBuilder.FileManifest->FileChunkParts.Last(); FileChunkPartData.Guid = ChunkPart.ChunkGuid; FileChunkPartData.Offset = (FileBuilder.CurrentDataPos - ChunkPart.DataOffset) + ChunkPart.ChunkOffset; // Process data into file manifests int64 FileDataLeft = (FileBuilder.FileSpan.StartIdx + FileBuilder.FileSpan.Size) - FileBuilder.CurrentDataPos; int64 ChunkDataLeft = (ChunkPart.DataOffset + ChunkPart.PartSize) - FileBuilder.CurrentDataPos; check(FileDataLeft > 0); check(ChunkDataLeft > 0); if (ChunkDataLeft >= FileDataLeft) { FileBuilder.CurrentDataPos += FileDataLeft; FileChunkPartData.Size = FileDataLeft; } else { FileBuilder.CurrentDataPos += ChunkDataLeft; FileChunkPartData.Size = ChunkDataLeft; } FileDataLeft = (FileBuilder.FileSpan.StartIdx + FileBuilder.FileSpan.Size) - FileBuilder.CurrentDataPos; ChunkDataLeft = (ChunkPart.DataOffset + ChunkPart.PartSize) - FileBuilder.CurrentDataPos; check(FileDataLeft == 0 || ChunkDataLeft == 0); // End of file? if (FileDataLeft == 0) { // Fill out rest of data?? FFileSpan FileSpan; bool FoundFile = BuildStreamer->GetFileSpan(FileBuilder.FileSpan.StartIdx, FileSpan); check(FoundFile); // Incorrect positional tracking check(FileSpan.Filename == FileBuilder.FileManifest->Filename); FMemory::Memcpy(FileBuilder.FileManifest->FileHash.Hash, FileSpan.SHAHash.Hash, FSHA1::DigestSize); FFileAttributes Attributes = FileAttributesMap.FindRef(FileSpan.Filename); FileBuilder.FileManifest->bIsUnixExecutable = Attributes.bUnixExecutable || FileSpan.IsUnixExecutable; FileBuilder.FileManifest->SymlinkTarget = FileSpan.SymlinkTarget; FileBuilder.FileManifest->bIsReadOnly = Attributes.bReadOnly; FileBuilder.FileManifest->bIsCompressed = Attributes.bCompressed; FileBuilder.FileManifest->InstallTags = Attributes.InstallTags.Array(); FileBuilder.FileManifest->Init(); check(FileBuilder.FileManifest->GetFileSize() == FileBuilder.FileSpan.Size); FileBuilder.FileManifest = nullptr; } else if (ChunkDataLeft == 0) { FileBuilder.FileManifest->FileChunkParts.Emplace(); } // Continue with this chunk? if (ChunkDataLeft > 0) { --idx; } } } else { if (EndOfData) { Running = false; } else { CheckForWork->Wait(); CheckForWork->Reset(); } } } // Fill out chunk list from only chunks that remain referenced TSet<FGuid> ReferencedChunks; for (const auto& FileManifest : Manifest->Data->FileManifestList) { for (const auto& ChunkPart : FileManifest.FileChunkParts) { if (ReferencedChunks.Contains(ChunkPart.Guid) == false) { auto& ChunkInfo = ChunkInfoLookup[ChunkPart.Guid]; ReferencedChunks.Add(ChunkPart.Guid); Manifest->Data->ChunkList.Emplace(); auto& ChunkInfoData = Manifest->Data->ChunkList.Last(); ChunkInfoData.Guid = ChunkPart.Guid; ChunkInfoData.Hash = ChunkInfo.Hash; FMemory::Memcpy(ChunkInfoData.ShaHash.Hash, ChunkInfo.ShaHash.Hash, FSHA1::DigestSize); ChunkInfoData.FileSize = ChunkInfo.ChunkFileSize; ChunkInfoData.GroupNumber = FCrc::MemCrc32(&ChunkPart.Guid, sizeof(FGuid)) % 100; } } } // Get empty files FSHA1 EmptyHasher; EmptyHasher.Final(); const TArray< FString >& EmptyFileList = BuildStreamer->GetEmptyFiles(); for (const auto& EmptyFile : EmptyFileList) { Manifest->Data->FileManifestList.Emplace(); FFileManifestData& EmptyFileManifest = Manifest->Data->FileManifestList.Last(); EmptyFileManifest.Filename = EmptyFile; EmptyHasher.GetHash(EmptyFileManifest.FileHash.Hash); } // Fill out lookups Manifest->InitLookups(); }