//------------------------------------------------------------------------------
bool FEditorCategoryUtils::IsCategoryHiddenFromClass(UClass const* Class, FString const& Category)
{
	bool bIsHidden = false;

	TArray<FString> ClassHideCategories;
	GetClassHideCategories(Class, ClassHideCategories);

	// run the category through sanitization so we can ensure compares will hit
	FString const DisplayCategory = GetCategoryDisplayString(Category);

	for (FString& HideCategory : ClassHideCategories)
	{
		bIsHidden = (HideCategory == DisplayCategory);
		if (bIsHidden)
		{
			TArray<FString> ClassShowCategories;
			GetClassShowCategories(Class, ClassShowCategories);
			// if they hid it, and showed it... favor showing (could be a shown in a sub-class, and hid in a super)
			bIsHidden = (ClassShowCategories.Find(DisplayCategory) == INDEX_NONE);
		}
		else // see if the category's root is hidden
		{
			TArray<FString> SubCategoryList;
			DisplayCategory.ParseIntoArray(&SubCategoryList, TEXT("|"), /*InCullEmpty =*/true);

			FString FullSubCategoryPath;
			for (FString const& SubCategory : SubCategoryList)
			{
				FullSubCategoryPath += SubCategory;
				if ((HideCategory == SubCategory) || (HideCategory == FullSubCategoryPath))
				{
					TArray<FString> ClassShowCategories;
					GetClassShowCategories(Class, ClassShowCategories);
					// if they hid it, and showed it... favor showing (could be a shown in a sub-class, and hid in a super)
					bIsHidden = (ClassShowCategories.Find(DisplayCategory) == INDEX_NONE);
				}
				FullSubCategoryPath += "|";
			}
		}

		if (bIsHidden)
		{
			break;
		}
	}

	return bIsHidden;
}
		/**
		 * Recursively look for `TargetItem` in `InsertInto` and any of the descendants.
		 * Insert `ItemToInsert` relative to the target.
		 * Relative positioning dictated by `RelativeLocation`.
		 *
		 * @return true when successful.
		 */
		static bool InsertRecursive(TArray< TSharedPtr< FTestData > >& InsertInto, const TSharedRef<FTestData>& ItemToInsert, const TSharedRef<FTestData>& TargetItem, EItemDropZone RelativeLocation)
		{
			const int32 TargetIndex = InsertInto.Find(TargetItem);
			if (TargetIndex != INDEX_NONE)
			{
				if (RelativeLocation == EItemDropZone::AboveItem)
				{
					InsertInto.Insert(ItemToInsert, TargetIndex);
				}
				else if (RelativeLocation == EItemDropZone::BelowItem)
				{
					InsertInto.Insert(ItemToInsert, TargetIndex + 1);
				}
				else
				{
					ensure(RelativeLocation == EItemDropZone::OntoItem);
					InsertInto[TargetIndex]->Children.Insert(ItemToInsert, 0);
				}				
				return true;
			}

			// Did not successfully remove an item. Try all the children.
			for (int32 ItemIndex = 0; ItemIndex < InsertInto.Num(); ++ItemIndex)
			{
				if (InsertRecursive(InsertInto[ItemIndex]->Children, ItemToInsert, TargetItem, RelativeLocation))
				{
					return true;
				}
			}

			return false;
		}
/**
 * Enacts the transaction.
 */
void FTransaction::Apply()
{
	checkSlow(Inc==1||Inc==-1);

	// Figure out direction.
	const int32 Start = Inc==1 ? 0             : Records.Num()-1;
	const int32 End   = Inc==1 ? Records.Num() :              -1;

	// Init objects.
	TArray<UObject*> ChangedObjects;
	for( int32 i=Start; i!=End; i+=Inc )
	{
		Records[i].bRestored = false;
		if(ChangedObjects.Find(Records[i].Object) == INDEX_NONE)
		{
			Records[i].Object->CheckDefaultSubobjects();
			Records[i].Object->PreEditUndo();
			ChangedObjects.Add(Records[i].Object);
		}
	}
	for( int32 i=Start; i!=End; i+=Inc )
	{
		Records[i].Restore( this );
	}

	NumModelsModified = 0;		// Count the number of UModels that were changed.
	for(int32 ObjectIndex = 0;ObjectIndex < ChangedObjects.Num();ObjectIndex++)
	{
		UObject* ChangedObject = ChangedObjects[ObjectIndex];
		UModel* Model = Cast<UModel>(ChangedObject);
		if( Model && Model->Nodes.Num() )
		{
			FBSPOps::bspBuildBounds( Model );
			++NumModelsModified;
		}
		ChangedObject->PostEditUndo();
	}
	
	// Rebuild BSP here instead of waiting for the next tick since
	// multiple transaction events can occur in a single tick
	if (ABrush::NeedsRebuild())
	{
		GEditor->RebuildAlteredBSP();
	}

	// Flip it.
	if( bFlip )
	{
		Inc *= -1;
	}
	for(int32 ObjectIndex = 0;ObjectIndex < ChangedObjects.Num();ObjectIndex++)
	{
		UObject* ChangedObject = ChangedObjects[ObjectIndex];
		ChangedObject->CheckDefaultSubobjects();
	}
}
Example #4
0
void CSystemCreateStats::AddStationTable (CSystem *pSystem, const CString &sStationCriteria, const CString &sLocationAttribs, TArray<CStationTableCache::SEntry> &Table)

//	AddStationTable
//
//	Adds the station table.

	{
	int i;

	//	See if we already have an entry for this table.
	//	If we don't we add it.

	SEncounterTable *pEntry;
	if (!FindEncounterTable(Table, &pEntry))
		{
		pEntry = m_EncounterTables.Insert();
		pEntry->iLevel = pSystem->GetLevel();
		pEntry->pSystemType = pSystem->GetType();
		pEntry->sStationCriteria = sStationCriteria;
		pEntry->iCount = 1;

		ParseAttributes(sLocationAttribs, &pEntry->LabelAttribs);

		pEntry->bHasStation = false;
		for (i = 0; i < Table.GetCount(); i++)
			{
			pEntry->Table.Insert(Table[i].pType, Table[i].iChance);
			if (Table[i].pType->GetScale() == scaleStructure
					|| Table[i].pType->GetScale() == scaleShip)
				pEntry->bHasStation = true;
			}

		return;
		}

	//	If we already have the table we need to aggregate the entry. We start
	//	by incrementing the count.

	pEntry->iCount++;

	//	Next we remove any location/label attributes that are not common to 
	//	both tables. [We assume that if we have two identical tables then only
	//	the attributes in common count to make the table unique.]

	TArray<CString> NewAttribs;
	ParseAttributes(sLocationAttribs, &NewAttribs);
	for (i = 0; i < pEntry->LabelAttribs.GetCount(); i++)
		{
		if (!NewAttribs.Find(pEntry->LabelAttribs[i]))
			{
			pEntry->LabelAttribs.Delete(i);
			i--;
			}
		}
	}
void UNiagaraGraph::GetAttributes(TArray< FNiagaraVariableInfo >& OutAttributes)const
{
	const UNiagaraNodeOutput* OutNode = FindOutputNode();
	check(OutNode);

	for (const FNiagaraVariableInfo& Attr : OutNode->Outputs)
	{
		check(!OutAttributes.Find(Attr));
		OutAttributes.Add(Attr);
	}
}
FLinearColor FLogVisualizer::GetColorForCategory(const FString& InFilterName) const
{
    static TArray<FString> Filters;
    int32 CategoryIndex = Filters.Find(InFilterName);
    if (CategoryIndex == INDEX_NONE)
    {
        CategoryIndex = Filters.Add(InFilterName);
    }

    return GetColorForCategory(CategoryIndex);
}
Example #7
0
bool CDeviceClass::AccumulateEnhancements (CItemCtx &Device, CInstalledDevice *pTarget, TArray<CString> &EnhancementIDs, CItemEnhancementStack *pEnhancements)

//	AccumulateEnhancements
//
//	If this device can enhance pTarget, then we add to the list of enhancements.

	{
	int i;
	bool bEnhanced = false;

	CInstalledDevice *pDevice = Device.GetDevice();
	CSpaceObject *pSource = Device.GetSource();

	//	See if we can enhance the target device

	if (pDevice == NULL 
			|| (pDevice->IsEnabled() && !pDevice->IsDamaged()))
		{
		for (i = 0; i < m_Enhancements.GetCount(); i++)
			{
			//	If this type of enhancement has already been applied, skip it

			if (!m_Enhancements[i].sType.IsBlank()
					&& EnhancementIDs.Find(m_Enhancements[i].sType))
				continue;

			//	If we don't match the criteria, skip it.

			if (pSource 
					&& pTarget
					&& !pSource->GetItemForDevice(pTarget).MatchesCriteria(m_Enhancements[i].Criteria))
				continue;

			//	Add the enhancement

			pEnhancements->Insert(m_Enhancements[i].Enhancement);
			bEnhanced = true;

			//	Remember that we added this enhancement class

			if (!m_Enhancements[i].sType.IsBlank())
				EnhancementIDs.Insert(m_Enhancements[i].sType);
			}
		}

	//	Let sub-classes add their own

	if (OnAccumulateEnhancements(Device, pTarget, EnhancementIDs, pEnhancements))
		bEnhanced = true;

	//	Done

	return bEnhanced;
	}
void SVisualLoggerTimelinesContainer::OnObjectSelectionChanged(const TArray<FName>& RowNames)
{
	CachedSelectedTimelines.Reset();
	for (TSharedPtr<SLogVisualizerTimeline>& Timeline : TimelineItems)
	{
		if (RowNames.Find(Timeline->GetName()) != INDEX_NONE)
		{
			CachedSelectedTimelines.Add(Timeline);
		}
	}

	if (CachedSelectedTimelines.Num() >= 1)
	{
		FSlateApplication::Get().SetKeyboardFocus(SharedThis(CachedSelectedTimelines[CachedSelectedTimelines.Num()-1].Get()), EFocusCause::Navigation);
	}
}
	/** Add items to the output object array according to the input object set */
	void Generate(EStaticMeshLightingInfoObjectSets InObjectSet, TArray< TWeakObjectPtr<UObject> >& OutObjects)
	{
		/** The levels we are gathering information for. */
		TArray<ULevel*> Levels;

		UWorld* World = GWorld;
		// Fill the light list
		for (TObjectIterator<ULightComponent> LightIt; LightIt; ++LightIt)
		{
			ULightComponent* const Light = *LightIt;

			const bool bLightIsInWorld = Light->GetOwner() && !Light->GetOwner()->HasAnyFlags(RF_ClassDefaultObject) && World->ContainsActor(Light->GetOwner());
			if (bLightIsInWorld)
			{
				if (Light->HasStaticLighting() || Light->HasStaticShadowing())
				{
					// Add the light to the system's list of lights in the world.
					AllLights.Add(Light);
				}

				//append to the list of levels in use
				AddRequiredLevels( InObjectSet, World, Levels );				
			}
		}

		if (Levels.Num() > 0)
		{
			// Iterate over static mesh components in the list of levels...
			for (TObjectIterator<UStaticMeshComponent> SMCIt; SMCIt; ++SMCIt)
			{
				AActor* Owner = Cast<AActor>( (*SMCIt)->GetOwner() );			
				if (Owner && !Owner->HasAnyFlags(RF_ClassDefaultObject) )
				{
					int32 Dummy;
					ULevel* CheckLevel = Owner->GetLevel();
					if ((CheckLevel != NULL) && (Levels.Find(CheckLevel, Dummy)))
					{
						AddItem(*SMCIt, Owner, OutObjects);
					}
				}
			}
		}
	}
Example #10
0
int32 DiffTreeView::CurrentDifference(TSharedRef< STreeView<TSharedPtr< FBlueprintDifferenceTreeEntry > > > TreeView, const TArray< TSharedPtr<class FBlueprintDifferenceTreeEntry> >& Differences)
{
	auto SelectedItems = TreeView->GetSelectedItems();
	if (SelectedItems.Num() == 0)
	{
		return INDEX_NONE;
	}

	for (int32 Iter = 0; Iter < SelectedItems.Num(); ++Iter)
	{
		int32 Index = Differences.Find(SelectedItems[Iter]);
		if (Index != INDEX_NONE)
		{
			return Index;
		}
	}

	return INDEX_NONE;
}
void FStreamingLevelModel::OnDrop(const TSharedPtr<FLevelDragDropOp>& Op)
{
	TArray<ULevelStreaming*> DropStreamingLevels;
	
	for (auto It = Op->StreamingLevelsToDrop.CreateConstIterator(); It; ++It)
	{
		if ((*It).IsValid())
		{
			DropStreamingLevels.AddUnique((*It).Get());
		}
	}	
	
	// Prevent dropping items on itself
	if (DropStreamingLevels.Num() && DropStreamingLevels.Find(LevelStreaming.Get()) == INDEX_NONE)
	{
		UWorld* CurrentWorld = LevelCollectionModel.GetWorld();
		auto& WorldStreamingLevels = CurrentWorld->StreamingLevels;
		// Remove streaming level objects from a world streaming levels list
		for (auto It : DropStreamingLevels)
		{
			WorldStreamingLevels.Remove(It);
		}
		
		// Find a new place where to insert the in a world streaming levels list
		// Right after the current level, or at start of the list in case if this is persistent level
		int32 InsertIndex = WorldStreamingLevels.Find(LevelStreaming.Get());
		if (InsertIndex == INDEX_NONE)
		{
			InsertIndex = 0;
		}
		else
		{
			InsertIndex++;
		}

		WorldStreamingLevels.Insert(DropStreamingLevels, InsertIndex);
		CurrentWorld->MarkPackageDirty();
			
		// Force levels list refresh
		LevelCollectionModel.PopulateLevelsList();
	}
}
		/**
		 * Recursively look for `ItemToRemove` in `RemoveFrom` and any of the descendants, and remove `ItemToRemove`.
		 * @return true when successful.
		 */
		static bool RemoveRecursive(TArray< TSharedPtr< FTestData > >& RemoveFrom, const TSharedPtr<FTestData>& ItemToRemove)
		{
			int32 ItemIndex = RemoveFrom.Find(ItemToRemove);
			if (ItemIndex != INDEX_NONE)
			{
				RemoveFrom.RemoveAt(ItemIndex);
				return true;
			}

			// Did not successfully remove an item. Try all the children.
			for (ItemIndex = 0; ItemIndex < RemoveFrom.Num(); ++ItemIndex)
			{
				if (RemoveRecursive(RemoveFrom[ItemIndex]->Children, ItemToRemove))
				{
					return true;
				}
			}

			return false;
		}
void UK2Node_CallArrayFunction::GetArrayTypeDependentPins(TArray<UEdGraphPin*>& OutPins) const
{
	OutPins.Empty();

	UFunction* TargetFunction = GetTargetFunction();
	check(TargetFunction);

	const FString DependentPinMetaData = TargetFunction->GetMetaData(FBlueprintMetadata::MD_ArrayDependentParam);
	TArray<FString> TypeDependentPinNames;
	DependentPinMetaData.ParseIntoArray(TypeDependentPinNames, TEXT(","), true);

	for(TArray<UEdGraphPin*>::TConstIterator it(Pins); it; ++it)
	{
		UEdGraphPin* CurrentPin = *it;
		int32 ItemIndex = 0;
		if( CurrentPin && TypeDependentPinNames.Find(CurrentPin->PinName, ItemIndex) )
		{
			OutPins.Add(CurrentPin);
		}
	}
}
void UK2Node_FormatText::PinDefaultValueChanged(UEdGraphPin* Pin)
{
	const auto FormatPin = GetFormatPin();
	if(Pin == FormatPin && FormatPin->LinkedTo.Num() == 0)
	{
		const UEdGraphSchema_K2* K2Schema = GetDefault<UEdGraphSchema_K2>();
		
		TArray< FString > ArgumentParams;
		FText::GetFormatPatternParameters(FormatPin->DefaultTextValue, ArgumentParams);

		PinNames.Empty();

		for(auto It = ArgumentParams.CreateConstIterator(); It; ++It)
		{
			if(!FindArgumentPin(FText::FromString(*It)))
			{
				CreatePin(EGPD_Input, K2Schema->PC_Text, TEXT(""), NULL, false, false, *It);
			}
			PinNames.Add(FText::AsCultureInvariant(*It));
		}

		for(auto It = Pins.CreateConstIterator(); It; ++It)
		{
			UEdGraphPin* CheckPin = *It;
			if(CheckPin != FormatPin && CheckPin->Direction == EGPD_Input)
			{
				int Index = 0;
				if(!ArgumentParams.Find(CheckPin->PinName, Index))
				{
					CheckPin->BreakAllPinLinks();
					Pins.Remove(CheckPin);
					--It;
				}
			}
		}

		GetGraph()->NotifyGraphChanged();
	}
}
void FBoneHierarchyBuilder::ProcessBone(FName BoneName, int32 ExpectedParentIndex, const FTransform& InitialTransform, const FString& AnimationNameForErrors, int32 TimeForErrors)
{
	const int32 BoneNameAlreadyInsertedIndex = AllBones.Find(BoneName);
	if (BoneNameAlreadyInsertedIndex == INDEX_NONE)
	{
		if (ExpectedParentIndex == INDEX_NONE)
		{
			RootBones.Add(BoneName);
		}
		AllBones.Add(BoneName);
		ParentIndices.Add(ExpectedParentIndex);
		Transforms.Add(InitialTransform);
	}
	else
	{
		// Verify that the hierarchy hasn't changed
		if (ParentIndices[BoneNameAlreadyInsertedIndex] != ExpectedParentIndex)
		{
			UE_LOG(LogInit, Warning, TEXT("Bone hierarchy (for bone '%s') in animation '%s' was changed at time %d ms.  This change will be ignored and the animation will not play properly."), *BoneName.ToString(), *AnimationNameForErrors, TimeForErrors);
		}
	}
}
EReplacementResult FBlueprintNativeCodeGenModule::IsTargetedForReplacement(const UObject* Object) const
{
	if (Object == nullptr)
	{
		return EReplacementResult::DontReplace;
	}

	const UStruct* Struct = Cast<UStruct>(Object);
	const UEnum* Enum = Cast<UEnum>(Object);

	if (Struct == nullptr && Enum == nullptr)
	{
		return EReplacementResult::DontReplace;
	}

	EReplacementResult Result = EReplacementResult::ReplaceCompletely;
	if (const UClass* BlueprintClass = Cast<UClass>(Struct))
	{
		if (UBlueprint* Blueprint = Cast<UBlueprint>(BlueprintClass->ClassGeneratedBy))
		{
			static const FBoolConfigValueHelper NativizeAnimBPOnlyWhenNonReducibleFuncitons(TEXT("BlueprintNativizationSettings"), TEXT("bNativizeAnimBPOnlyWhenNonReducibleFuncitons"));
			if (NativizeAnimBPOnlyWhenNonReducibleFuncitons)
			{
				if (UAnimBlueprint* AnimBlueprint = Cast<UAnimBlueprint>(Blueprint))
				{
					ensure(AnimBlueprint->bHasBeenRegenerated);
					if (AnimBlueprint->bHasAnyNonReducibleFunction == UBlueprint::EIsBPNonReducible::No)
					{
						UE_LOG(LogBlueprintCodeGen, Log, TEXT("AnimBP %s without non-reducible functions is excluded from nativization"), *GetPathNameSafe(Blueprint));
						Result = EReplacementResult::GenerateStub;
					}
				}
			}

			const EBlueprintType UnconvertableBlueprintTypes[] = {
				//BPTYPE_Const,		// WTF is a "const" Blueprint?
				BPTYPE_MacroLibrary,
				BPTYPE_LevelScript,
			};

			EBlueprintType BlueprintType = Blueprint->BlueprintType;
			for (int32 TypeIndex = 0; TypeIndex < ARRAY_COUNT(UnconvertableBlueprintTypes); ++TypeIndex)
			{
				if (BlueprintType == UnconvertableBlueprintTypes[TypeIndex])
				{
					Result = EReplacementResult::GenerateStub;
				}
			}

			static const FBoolConfigValueHelper DontNativizeDataOnlyBP(TEXT("BlueprintNativizationSettings"), TEXT("bDontNativizeDataOnlyBP"));
			if (DontNativizeDataOnlyBP)
			{
				if (FBlueprintEditorUtils::IsDataOnlyBlueprint(Blueprint))
				{
					return EReplacementResult::DontReplace;
				}
			}

			for (UBlueprintGeneratedClass* ParentClassIt = Cast<UBlueprintGeneratedClass>(BlueprintClass->GetSuperClass())
				; ParentClassIt; ParentClassIt = Cast<UBlueprintGeneratedClass>(ParentClassIt->GetSuperClass()))
			{
				EReplacementResult ParentResult = IsTargetedForReplacement(ParentClassIt);
				if (ParentResult != EReplacementResult::ReplaceCompletely)
				{
					Result = EReplacementResult::GenerateStub;
				}
			}

			for (TAssetSubclassOf<UBlueprint> ExcludedBlueprintTypeAsset : ExcludedBlueprintTypes)
			{
				UClass* ExcludedBPClass = ExcludedBlueprintTypeAsset.Get();
				if (!ExcludedBPClass)
				{
					ExcludedBPClass = ExcludedBlueprintTypeAsset.LoadSynchronous();
				}
				if (ExcludedBPClass && Blueprint->IsA(ExcludedBPClass))
				{
					Result = EReplacementResult::GenerateStub;
				}
			}
		}
	}

	auto IsObjectFromDeveloperPackage = [](const UObject* Obj) -> bool
	{
		return Obj && Obj->GetOutermost()->HasAllPackagesFlags(PKG_Developer);
	};

	auto IsDeveloperObject = [&](const UObject* Obj) -> bool
	{
		if (Obj)
		{
			if (IsObjectFromDeveloperPackage(Obj))
			{
				return true;
			}
			const UStruct* StructToTest = Obj->IsA<UStruct>() ? CastChecked<const UStruct>(Obj) : Obj->GetClass();
			for (; StructToTest; StructToTest = StructToTest->GetSuperStruct())
			{
				if (IsObjectFromDeveloperPackage(StructToTest))
				{
					return true;
				}
			}
		}
		return false;
	};

	if (Object && (IsEditorOnlyObject(Object) || IsDeveloperObject(Object)))
	{
		UE_LOG(LogBlueprintCodeGen, Warning, TEXT("Object %s depends on Editor or Development stuff. It shouldn't be cooked."), *GetPathNameSafe(Object));
		return EReplacementResult::DontReplace;
	}

	// check blacklists:
	// we can't use FindObject, because we may be converting a type while saving
	if (Enum && ExcludedAssetTypes.Find(Enum->GetPathName()) != INDEX_NONE)
	{
		Result = EReplacementResult::GenerateStub;
	}

	while (Struct)
	{
		if (ExcludedAssetTypes.Find(Struct->GetPathName()) != INDEX_NONE)
		{
			Result = EReplacementResult::GenerateStub;
		}
		Struct = Struct->GetSuperStruct();
	}

	if (ExcludedAssets.Contains(Object->GetOutermost()))
	{
		Result = EReplacementResult::GenerateStub;
	}

	return Result;
}
void FSavedCustomSortSectionInfo::Restore(USkeletalMesh* NewSkelMesh, int32 LODModelIndex, TArray<int32>& UnmatchedSections)
{
	FStaticLODModel& LODModel = NewSkelMesh->GetImportedResource()->LODModels[LODModelIndex];
	FSkeletalMeshLODInfo& LODInfo = NewSkelMesh->LODInfo[LODModelIndex];

	// Re-order the UnmatchedSections so the old section index from the previous model is tried first
	int32 PrevSectionIndex = UnmatchedSections.Find(SavedSectionIdx);
	if( PrevSectionIndex != 0 && PrevSectionIndex != INDEX_NONE )
	{
		Exchange( UnmatchedSections[0], UnmatchedSections[PrevSectionIndex] );
	}

	// Find the strips in the old triangle data.
	TArray< TArray<uint32> > OldStrips[2];
	for( int32 IndexCopy=0; IndexCopy < (SavedSortOption==TRISORT_CustomLeftRight ? 2 : 1); IndexCopy++ )
	{
		const uint32* OldIndices = &SavedIndices[(SavedIndices.Num()>>1)*IndexCopy];
		TArray<uint32> OldTriSet;
		GetConnectedTriangleSets( SavedNumTriangles, OldIndices, OldTriSet );

		// Convert to strips
		int32 PrevTriSet = MAX_int32;
		for( int32 TriIndex=0;TriIndex<SavedNumTriangles; TriIndex++ )
		{
			if( OldTriSet[TriIndex] != PrevTriSet )
			{
				OldStrips[IndexCopy].AddZeroed();
				PrevTriSet = OldTriSet[TriIndex];
			}
			OldStrips[IndexCopy][OldStrips[IndexCopy].Num()-1].Add(OldIndices[TriIndex*3+0]);
			OldStrips[IndexCopy][OldStrips[IndexCopy].Num()-1].Add(OldIndices[TriIndex*3+1]);
			OldStrips[IndexCopy][OldStrips[IndexCopy].Num()-1].Add(OldIndices[TriIndex*3+2]);
		}
	}


	bool bFoundMatchingSection = false; 

	// Try all remaining sections to find a match
	for( int32 UnmatchedSectionsIdx=0; !bFoundMatchingSection && UnmatchedSectionsIdx<UnmatchedSections.Num(); UnmatchedSectionsIdx++ )
	{
		// Section of the new mesh to try
		int32 SectionIndex = UnmatchedSections[UnmatchedSectionsIdx];
		FSkelMeshSection& Section = LODModel.Sections[SectionIndex];

		TArray<uint32> Indices;
		LODModel.MultiSizeIndexContainer.GetIndexBuffer( Indices );
		const uint32* NewSectionIndices = Indices.GetData() + Section.BaseIndex;

		// Build the list of triangle sets in the new mesh's section
		TArray<uint32> TriSet;
		GetConnectedTriangleSets( Section.NumTriangles, NewSectionIndices, TriSet );

		// Mapping from triangle set number to the array of indices that make up the contiguous strip.
		TMap<uint32, TArray<uint32> > NewStripsMap;
		// Go through each triangle and assign it to the appropriate contiguous strip.
		// This is necessary if the strips in the index buffer are not contiguous.
		int32 Index=0;
		for( int32 s=0;s<TriSet.Num();s++ )
		{
			// Store the indices for this triangle in the appropriate contiguous set.
			TArray<uint32>* ThisStrip = NewStripsMap.Find(TriSet[s]);
			if( !ThisStrip )
			{
				ThisStrip = &NewStripsMap.Add(TriSet[s],TArray<uint32>());
			}

			// Add the three indices for this triangle.
			ThisStrip->Add(NewSectionIndices[Index++]);
			ThisStrip->Add(NewSectionIndices[Index++]);
			ThisStrip->Add(NewSectionIndices[Index++]);
		}

		// Get the new vertices
		TArray<FSoftSkinVertex> NewVertices;
		LODModel.GetVertices(NewVertices);

		// Do the processing once for each copy if the index data
		for( int32 IndexCopy=0; IndexCopy < (SavedSortOption==TRISORT_CustomLeftRight ? 2 : 1); IndexCopy++ )
		{
			// Copy strips in the new mesh's section into an array. We'll remove items from
			// here as we match to the old strips, so we need to keep a new copy of it each time.
			TArray<TArray<uint32> > NewStrips;
			for( TMap<uint32, TArray<uint32> >::TIterator It(NewStripsMap); It; ++It )
			{
				NewStrips.Add(It.Value());
			}

			// Match up old strips to new
			int32 NumMismatchedStrips = 0;
			TArray<TArray<uint32> > NewSortedStrips; // output
			for( int32 OsIdx=0;OsIdx<OldStrips[IndexCopy].Num();OsIdx++ )
			{
				TArray<uint32>& OldStripIndices = OldStrips[IndexCopy][OsIdx];

				int32 MatchingNewStrip = INDEX_NONE;

				for( int32 NsIdx=0;NsIdx<NewStrips.Num() && MatchingNewStrip==INDEX_NONE;NsIdx++ )
				{
					// Check if we have the same number of triangles in the old and new strips.
					if( NewStrips[NsIdx].Num() != OldStripIndices.Num() )
					{
						continue;
					}

					// Make a copy of the indices, as we'll remove them as we try to match triangles.
					TArray<uint32> NewStripIndices = NewStrips[NsIdx];

					// Check if all the triangles in the new strip closely match those in the old.
					for( int32 OldTriIdx=0;OldTriIdx<OldStripIndices.Num();OldTriIdx+=3 )
					{
						// Try to find a match for this triangle in the new strip.
						bool FoundMatch = false;
						for( int32 NewTriIdx=0;NewTriIdx<NewStripIndices.Num();NewTriIdx+=3 )
						{
							if( (SavedVertices[OldStripIndices[OldTriIdx+0]] - NewVertices[NewStripIndices[NewTriIdx+0]].Position).SizeSquared() < KINDA_SMALL_NUMBER &&
								(SavedVertices[OldStripIndices[OldTriIdx+1]] - NewVertices[NewStripIndices[NewTriIdx+1]].Position).SizeSquared() < KINDA_SMALL_NUMBER &&
								(SavedVertices[OldStripIndices[OldTriIdx+2]] - NewVertices[NewStripIndices[NewTriIdx+2]].Position).SizeSquared() < KINDA_SMALL_NUMBER )
							{
								// Found a triangle match. Remove the triangle from the new list and try to match the next old triangle.
								NewStripIndices.RemoveAt(NewTriIdx,3);
								FoundMatch = true;
								break;
							}
						}

						// If we didn't find a match for this old triangle, the whole strip doesn't match.
						if( !FoundMatch )
						{
							break;
						}
					}

					if( NewStripIndices.Num() == 0 )
					{
						// strip completely matched
						MatchingNewStrip = NsIdx;
					}
				}

				if( MatchingNewStrip != INDEX_NONE )
				{
					NewSortedStrips.Add( NewStrips[MatchingNewStrip] );
					NewStrips.RemoveAt(MatchingNewStrip);
				}
				else
				{
					NumMismatchedStrips++;
				}
			}

			if( IndexCopy == 0 )
			{
				if( 100 * NumMismatchedStrips / OldStrips[0].Num() > 50 )
				{
					// If less than 50% of this section's strips match, we assume this is not the correct section.
					break;
				}

				// This section matches!
				bFoundMatchingSection = true;

				// Warn the user if we couldn't match things up.
				if( NumMismatchedStrips )
				{
					UnFbx::FFbxImporter* FFbxImporter = UnFbx::FFbxImporter::GetInstance();
					FFbxImporter->AddTokenizedErrorMessage(FTokenizedMessage::Create(EMessageSeverity::Warning, FText::Format(LOCTEXT("RestoreSortingMismatchedStripsForSection", "While restoring \"{0}\" sort order for section {1}, {2} of {3} strips could not be matched to the new data."),
						FText::FromString(TriangleSortOptionToString((ETriangleSortOption)SavedSortOption)),
						FText::AsNumber(SavedSectionIdx),
						FText::AsNumber(NumMismatchedStrips),
						FText::AsNumber(OldStrips[0].Num()))));
				}

				// Restore the settings saved in the LODInfo (used for the UI)
				FTriangleSortSettings& TriangleSortSettings = LODInfo.TriangleSortSettings[SectionIndex];
				TriangleSortSettings.TriangleSorting = SavedSortOption;
				TriangleSortSettings.CustomLeftRightAxis = SavedCustomLeftRightAxis;
				TriangleSortSettings.CustomLeftRightBoneName = SavedCustomLeftRightBoneName;

				// Restore the sorting mode. For TRISORT_CustomLeftRight, this will also make the second copy of the index data.
				FVector SortCenter;
				bool bHaveSortCenter = NewSkelMesh->GetSortCenterPoint(SortCenter);
				LODModel.SortTriangles(SortCenter, bHaveSortCenter, SectionIndex, (ETriangleSortOption)SavedSortOption);
			}

			// Append any strips we couldn't match to the end
			NewSortedStrips += NewStrips;

			// Export the strips out to the index buffer in order
			TArray<uint32> Indexes;
			LODModel.MultiSizeIndexContainer.GetIndexBuffer( Indexes );
			uint32* NewIndices = Indexes.GetData() + (Section.BaseIndex + Section.NumTriangles*3*IndexCopy);
			for( int32 StripIdx=0;StripIdx<NewSortedStrips.Num();StripIdx++ )
			{
				FMemory::Memcpy( NewIndices, &NewSortedStrips[StripIdx][0], NewSortedStrips[StripIdx].Num() * sizeof(uint32) );

				// Cache-optimize the triangle order inside the final strip
				CacheOptimizeSortStrip( NewIndices, NewSortedStrips[StripIdx].Num() );

				NewIndices += NewSortedStrips[StripIdx].Num();
			}
			LODModel.MultiSizeIndexContainer.CopyIndexBuffer( Indexes );
		}
	}

	if( !bFoundMatchingSection )
	{
		UnFbx::FFbxImporter* FFbxImporter = UnFbx::FFbxImporter::GetInstance();
		FFbxImporter->AddTokenizedErrorMessage(FTokenizedMessage::Create(EMessageSeverity::Warning, FText::Format(LOCTEXT("FailedRestoreSortingNoSectionMatch", "Unable to restore triangle sort setting \"{0}\" for section number {1} in the old mesh, as a matching section could not be found in the new mesh. The custom sorting information has been lost."),
			FText::FromString(TriangleSortOptionToString((ETriangleSortOption)SavedSortOption)), FText::AsNumber(SavedSectionIdx))));
	}
}
Example #18
0
bool CSimpleMesh::LoadFromObjFile(char *szFileName) {
	TArray<VECTOR3Df> m_oVertexArray;
	TArray<VECTOR3Df> m_oNormalArray;
	TArray<VECTOR2Df> m_oTextureCoordArray;

	m_oMaterials.RemoveAll();
	m_oMaterialNames.RemoveAll();

	m_oSubsetsStartIndex.RemoveAll();
	m_oSubsetsMaterialIndex.RemoveAll();

	FreeTexureArray();
	m_oMaterialsTextureIndex.RemoveAll();

	TArray<FACE_STRUCT> oFaceBuffer;

#ifdef USE_HASHTABLE_FOR_VERTEX_SEARCH
	THashTable<FACE_STRUCT> oFaceHash(NUM_KEYS, FACE_STRUCT::GetKey);
#endif //USE_HASHTABLE_FOR_VERTEX_SEARCH

	TArray<unsigned short> oIndexBuffer;

	DELETE_OBJECT(m_poVB);
	DELETE_OBJECT(m_poIB);

	FILE *file = fopen(szFileName, "rt");
	if (!file)
		return false;

	char szLine[MAX_LINE_LEN];
	CString *ppsTokens;
	int iNumTokens;
	int iNumIndexes = 0;

	while (fgets(szLine, MAX_LINE_LEN, file)) {
		CString sLine(szLine);
		sLine.ToUpper();

		sLine.Tokenize(&ppsTokens, &iNumTokens, sDelimiters);

		if (iNumTokens == 0)
			continue;

		if (ppsTokens[0].Equals("MTLLIB")) {
			LoadMaterials(ppsTokens[1]);
		} else if (ppsTokens[0].Equals("VN")) {
			m_oNormalArray.Append(VECTOR3Df(ppsTokens[1].ToFloat(), ppsTokens[2].ToFloat(), ppsTokens[3].ToFloat()));
		} else if (ppsTokens[0].Equals("VT")) {
			m_oTextureCoordArray.Append(VECTOR2Df(ppsTokens[1].ToFloat(), ppsTokens[2].ToFloat()));
		} else if (ppsTokens[0].Equals("V")) {
			m_oVertexArray.Append(VECTOR3Df(ppsTokens[1].ToFloat(), ppsTokens[2].ToFloat(), ppsTokens[3].ToFloat()));
		} else if (ppsTokens[0].Equals("USEMTL")) {
			int iMaterialIndex = m_oMaterialNames.Find(ppsTokens[1]);
			m_oSubsetsMaterialIndex.Append(iMaterialIndex);
			m_oSubsetsStartIndex.Append(oIndexBuffer.GetSize());
		} else if (ppsTokens[0].Equals("F")) {
			struct FACE_STRUCT face;

			for (int i = 0; i < 3; i++) {
				face.iVertexIndex = ppsTokens[i * 3 + 1].ToInt() - 1;
				face.iTextureIndex = ppsTokens[i * 3 + 2].ToInt() - 1;
				face.iNormalIndex = ppsTokens[i * 3 + 3].ToInt() - 1;

#ifndef USE_HASHTABLE_FOR_VERTEX_SEARCH
				int index = oFaceBuffer.Find(face);
				if (index == -1) {
					oIndexBuffer.Append(oFaceBuffer.GetSize());
					oFaceBuffer.Append(face);
				} else {
					oIndexBuffer.Append(index);
				} 
#else
				FACE_STRUCT *found = oFaceHash.Find(face);
				if (found) {
					int index = found->iIndex;

					oIndexBuffer.Append(index);
				} else {
					face.iIndex = iNumIndexes++;
					oFaceHash.Insert(face);

					oIndexBuffer.Append(oFaceBuffer.GetSize());
					oFaceBuffer.Append(face);
				} 
#endif //USE_HASHTABLE_FOR_VERTEX_SEARCH
			}
		}

		SAFE_DELETE_ARRAY(ppsTokens);
	}


	m_oSubsetsStartIndex.Append(oIndexBuffer.GetSize());

	fclose(file);

	m_poIB = new CIndexBuffer(oIndexBuffer.GetSize());
	memcpy(m_poIB->GetIndexBuffer(), &oIndexBuffer[0], oIndexBuffer.GetSize() * sizeof(unsigned short));

	m_bHasTextures = m_oTextures.GetSize() > 0;
	m_bHasMaterials = m_oMaterials.GetSize() > 0;
	int iFormat = VERTEXFORMAT_XYZ | VERTEXFORMAT_NORMAL | (m_bHasTextures ? VERTEXFORMAT_TEXTURE : 0);

	m_poVB = new CVertexBuffer(iFormat, oFaceBuffer.GetSize());
	int iIndex = 0, iNumVertex = oFaceBuffer.GetSize();
	for (int i = 0; i < iNumVertex; i++) {
		float *pVertex = (float *)m_poVB->GetVertexAtIndex(i);

		memcpy(pVertex, &m_oVertexArray[oFaceBuffer[i].iVertexIndex], 3 * sizeof(float));
		memcpy(pVertex + 3, &m_oNormalArray[oFaceBuffer[i].iNormalIndex], 3 * sizeof(float));

		if (m_bHasTextures) 
			memcpy(pVertex + 6, &m_oTextureCoordArray[oFaceBuffer[i].iTextureIndex], 2 * sizeof(float));
	}

	return true;
}
/** Tests to see if a pin is schema compatible with a property */
bool FKismetCompilerUtilities::IsTypeCompatibleWithProperty(UEdGraphPin* SourcePin, UProperty* Property, FCompilerResultsLog& MessageLog, const UEdGraphSchema_K2* Schema, UClass* SelfClass)
{
	check(SourcePin != NULL);
	const FEdGraphPinType& Type = SourcePin->PinType;
	const EEdGraphPinDirection Direction = SourcePin->Direction; 

	const FString& PinCategory = Type.PinCategory;
	const FString& PinSubCategory = Type.PinSubCategory;
	const UObject* PinSubCategoryObject = Type.PinSubCategoryObject.Get();

	UProperty* TestProperty = NULL;
	const UFunction* OwningFunction = Cast<UFunction>(Property->GetOuter());
	if( Type.bIsArray )
	{
		// For arrays, the property we want to test against is the inner property
		if( UArrayProperty* ArrayProp = Cast<UArrayProperty>(Property) )
		{
			if(OwningFunction)
			{
				// Check for the magic ArrayParm property, which always matches array types
				FString ArrayPointerMetaData = OwningFunction->GetMetaData(TEXT("ArrayParm"));
				TArray<FString> ArrayPinComboNames;
				ArrayPointerMetaData.ParseIntoArray(&ArrayPinComboNames, TEXT(","), true);

				for(auto Iter = ArrayPinComboNames.CreateConstIterator(); Iter; ++Iter)
				{
					TArray<FString> ArrayPinNames;
					Iter->ParseIntoArray(&ArrayPinNames, TEXT("|"), true);

					if( ArrayPinNames[0] == SourcePin->PinName )
					{
						return true;
					}
				}
			}

			TestProperty = ArrayProp->Inner;
		}
		else
		{
			MessageLog.Error(*LOCTEXT("PinSpecifiedAsArray_Error", "Pin @@ is specified as an array, but does not have a valid array property.").ToString(), SourcePin);
			return false;
		}
	}
	else
	{
		// For scalars, we just take the passed in property
		TestProperty = Property;
	}

	// Check for the early out...if this is a type dependent parameter in an array function
	if ( (OwningFunction != NULL) && OwningFunction->HasMetaData(TEXT("ArrayParm")) )
	{
		// Check to see if this param is type dependent on an array parameter
		const FString DependentParams = OwningFunction->GetMetaData(TEXT("ArrayTypeDependentParams"));
		TArray<FString>	DependentParamNames;
		DependentParams.ParseIntoArray(&DependentParamNames, TEXT(","), true);
		if (DependentParamNames.Find(SourcePin->PinName) != INDEX_NONE)
		{
			//@todo:  This assumes that the wildcard coersion has done its job...I'd feel better if there was some easier way of accessing the target array type
			return true;
		}
	}

	int32 NumErrorsAtStart = MessageLog.NumErrors;

	// First check the type
	bool bTypeMismatch = false;
	bool bSubtypeMismatch = false;
	FString DesiredSubType(TEXT(""));

	if (PinCategory == Schema->PC_Boolean)
	{
		UBoolProperty* SpecificProperty = Cast<UBoolProperty>(TestProperty);
		bTypeMismatch = (SpecificProperty == NULL);
	}
	else if (PinCategory == Schema->PC_Byte)
	{
		UByteProperty* SpecificProperty = Cast<UByteProperty>(TestProperty);
		bTypeMismatch = (SpecificProperty == NULL);
	}
	else if (PinCategory == Schema->PC_Class)
	{
		const UClass* ClassType = (PinSubCategory == Schema->PSC_Self) ? SelfClass : Cast<const UClass>(PinSubCategoryObject);

		if (ClassType == NULL)
		{
			MessageLog.Error(*FString::Printf(*LOCTEXT("FindClassForPin_Error", "Failed to find class for pin @@").ToString()), SourcePin);
		}
		else
		{
			const UClass* MetaClass = NULL;
			if (auto ClassProperty = Cast<UClassProperty>(TestProperty))
			{
				MetaClass = ClassProperty->MetaClass;
			}
			else if (auto AssetClassProperty = Cast<UAssetClassProperty>(TestProperty))
			{
				MetaClass = AssetClassProperty->MetaClass;
			}

			if (MetaClass != NULL)
			{
				DesiredSubType = MetaClass->GetName();

				const UClass* OutputClass = (Direction == EGPD_Output) ? ClassType :  MetaClass;
				const UClass* InputClass = (Direction == EGPD_Output) ? MetaClass : ClassType;

				// It matches if it's an exact match or if the output class is more derived than the input class
				bTypeMismatch = bSubtypeMismatch = !((OutputClass == InputClass) || (OutputClass->IsChildOf(InputClass)));
			}
			else
			{
				bTypeMismatch = true;
			}
		}
	}
	else if (PinCategory == Schema->PC_Float)
	{
		UFloatProperty* SpecificProperty = Cast<UFloatProperty>(TestProperty);
		bTypeMismatch = (SpecificProperty == NULL);
	}
	else if (PinCategory == Schema->PC_Int)
	{
		UIntProperty* SpecificProperty = Cast<UIntProperty>(TestProperty);
		bTypeMismatch = (SpecificProperty == NULL);
	}
	else if (PinCategory == Schema->PC_Name)
	{
		UNameProperty* SpecificProperty = Cast<UNameProperty>(TestProperty);
		bTypeMismatch = (SpecificProperty == NULL);
	}
	else if (PinCategory == Schema->PC_Delegate)
	{
		const UFunction* SignatureFunction = Cast<const UFunction>(PinSubCategoryObject);
		const UDelegateProperty* PropertyDelegate = Cast<const UDelegateProperty>(TestProperty);
		bTypeMismatch = !(SignatureFunction 
			&& PropertyDelegate 
			&& PropertyDelegate->SignatureFunction 
			&& PropertyDelegate->SignatureFunction->IsSignatureCompatibleWith(SignatureFunction));
	}
	else if (PinCategory == Schema->PC_Object)
	{
		const UClass* ObjectType = (PinSubCategory == Schema->PSC_Self) ? SelfClass : Cast<const UClass>(PinSubCategoryObject);

		if (ObjectType == NULL)
		{
			MessageLog.Error(*FString::Printf(*LOCTEXT("FindClassForPin_Error", "Failed to find class for pin @@").ToString()), SourcePin);
		}
		else
		{
			UObjectPropertyBase* ObjProperty = Cast<UObjectPropertyBase>(TestProperty);
			if (ObjProperty != NULL && ObjProperty->PropertyClass)
			{
				DesiredSubType = ObjProperty->PropertyClass->GetName();

				const UClass* OutputClass = (Direction == EGPD_Output) ? ObjectType : ObjProperty->PropertyClass;
				const UClass* InputClass = (Direction == EGPD_Output) ? ObjProperty->PropertyClass : ObjectType;

				// It matches if it's an exact match or if the output class is more derived than the input class
				bTypeMismatch = bSubtypeMismatch = !((OutputClass == InputClass) || (OutputClass->IsChildOf(InputClass)));
			}
			else if (UInterfaceProperty* IntefaceProperty = Cast<UInterfaceProperty>(TestProperty))
			{
				UClass const* InterfaceClass = IntefaceProperty->InterfaceClass;
				if (InterfaceClass == NULL)
				{
					bTypeMismatch = true;
				}
				else 
				{
					DesiredSubType = InterfaceClass->GetName();
					bTypeMismatch = ObjectType->ImplementsInterface(InterfaceClass);
				}
			}
			else
			{
				bTypeMismatch = true;
			}
		}
	}
	else if (PinCategory == Schema->PC_String)
	{
		UStrProperty* SpecificProperty = Cast<UStrProperty>(TestProperty);
		bTypeMismatch = (SpecificProperty == NULL);
	}
	else if (PinCategory == Schema->PC_Text)
	{
		UTextProperty* SpecificProperty = Cast<UTextProperty>(TestProperty);
		bTypeMismatch = (SpecificProperty == NULL);
	}
	else if (PinCategory == Schema->PC_Struct)
	{
		const UScriptStruct* StructType = Cast<const UScriptStruct>(PinSubCategoryObject);
		if (StructType == NULL)
		{
			MessageLog.Error(*FString::Printf(*LOCTEXT("FindStructForPin_Error", "Failed to find struct for pin @@").ToString()), SourcePin);
		}
		else
		{
			UStructProperty* StructProperty = Cast<UStructProperty>(TestProperty);
			if (StructProperty != NULL)
			{
				DesiredSubType = StructProperty->Struct->GetName();
				bSubtypeMismatch = bTypeMismatch = (StructType != StructProperty->Struct);
			}
			else
			{
				bTypeMismatch = true;
			}
		}
	}
	else
	{
		MessageLog.Error(*FString::Printf(*LOCTEXT("UnsupportedTypeForPin", "Unsupported type (%s) on @@").ToString(), *UEdGraphSchema_K2::TypeToString(Type)), SourcePin);
	}

	if (bTypeMismatch)
	{
		MessageLog.Error(*FString::Printf(*LOCTEXT("TypeDoesNotMatchPropertyOfType_Error", "@@ of type %s doesn't match the property %s of type %s").ToString(),
			*UEdGraphSchema_K2::TypeToString(Type),
			*Property->GetName(),
			*UEdGraphSchema_K2::TypeToString(Property)),
			SourcePin);
	}

	// Now check the direction
	if (Property->HasAnyPropertyFlags(CPF_Parm))
	{
		// Parameters are directional
		const bool bOutParam = (Property->HasAnyPropertyFlags(CPF_OutParm | CPF_ReturnParm) && !(Property->HasAnyPropertyFlags(CPF_ReferenceParm)));

		if ( ((SourcePin->Direction == EGPD_Input) && bOutParam) || ((SourcePin->Direction == EGPD_Output) && !bOutParam))
		{
			MessageLog.Error(*FString::Printf(*LOCTEXT("DirectionMismatchParameter_Error", "The direction of @@ doesn't match the direction of parameter %s").ToString(), *Property->GetName()), SourcePin);
		}

 		if (Property->HasAnyPropertyFlags(CPF_ReferenceParm))
 		{
			TArray<FString> AutoEmittedTerms;
			Schema->GetAutoEmitTermParameters(OwningFunction, AutoEmittedTerms);
			const bool bIsAutoEmittedTerm = AutoEmittedTerms.Contains(SourcePin->PinName);

			// Make sure reference parameters are linked, except for FTransforms, which have a special node handler that adds an internal constant term
 			if (!bIsAutoEmittedTerm
				&& (SourcePin->LinkedTo.Num() == 0)
				&& (!SourcePin->PinType.PinSubCategoryObject.IsValid() || SourcePin->PinType.PinSubCategoryObject.Get()->GetName() != TEXT("Transform")) )
 			{
 				MessageLog.Error(*LOCTEXT("PassLiteral_Error", "Cannot pass a literal to @@.  Connect a variable to it instead.").ToString(), SourcePin);
 			}
 		}
	}

	return NumErrorsAtStart == MessageLog.NumErrors;
}
Example #20
0
void ParseCompatibility()
{
	TArray<FMD5Holder> md5array;
	FMD5Holder md5;
	FCompatValues flags;
	int i, x;
	unsigned int j;

	BCompatMap.Clear();
	CompatParams.Clear();

	// The contents of this file are not cumulative, as it should not
	// be present in user-distributed maps.
	FScanner sc(Wads.GetNumForFullName("compatibility.txt"));

	while (sc.GetString())	// Get MD5 signature
	{
		do
		{
			if (strlen(sc.String) != 32)
			{
				sc.ScriptError("MD5 signature must be exactly 32 characters long");
			}
			for (i = 0; i < 32; ++i)
			{
				if (sc.String[i] >= '0' && sc.String[i] <= '9')
				{
					x = sc.String[i] - '0';
				}
				else
				{
					sc.String[i] |= 'a' ^ 'A';
					if (sc.String[i] >= 'a' && sc.String[i] <= 'f')
					{
						x = sc.String[i] - 'a' + 10;
					}
					else
					{
						x = 0;
						sc.ScriptError("MD5 signature must be a hexadecimal value");
					}
				}
				if (!(i & 1))
				{
					md5.Bytes[i / 2] = x << 4;
				}
				else
				{
					md5.Bytes[i / 2] |= x;
				}
			}
			md5array.Push(md5);
			sc.MustGetString();
		} while (!sc.Compare("{"));
		memset(flags.CompatFlags, 0, sizeof(flags.CompatFlags));
		flags.ExtCommandIndex = ~0u;
		while (sc.GetString())
		{
			if ((i = sc.MatchString(&Options[0].Name, sizeof(*Options))) >= 0)
			{
				flags.CompatFlags[Options[i].WhichSlot] |= Options[i].CompatFlags;
			}
			else if (sc.Compare("clearlineflags"))
			{
				if (flags.ExtCommandIndex == ~0u) flags.ExtCommandIndex = CompatParams.Size();
				CompatParams.Push(CP_CLEARFLAGS);
				sc.MustGetNumber();
				CompatParams.Push(sc.Number);
				sc.MustGetNumber();
				CompatParams.Push(sc.Number);
			}
			else if (sc.Compare("setlineflags"))
			{
				if (flags.ExtCommandIndex == ~0u) flags.ExtCommandIndex = CompatParams.Size();
				CompatParams.Push(CP_SETFLAGS);
				sc.MustGetNumber();
				CompatParams.Push(sc.Number);
				sc.MustGetNumber();
				CompatParams.Push(sc.Number);
			}
			else if (sc.Compare("setlinespecial"))
			{
				if (flags.ExtCommandIndex == ~0u) flags.ExtCommandIndex = CompatParams.Size();
				CompatParams.Push(CP_SETSPECIAL);
				sc.MustGetNumber();
				CompatParams.Push(sc.Number);

				sc.MustGetString();
				CompatParams.Push(P_FindLineSpecial(sc.String, NULL, NULL));
				for (int i = 0; i < 5; i++)
				{
					sc.MustGetNumber();
					CompatParams.Push(sc.Number);
				}
			}
			else if (sc.Compare("clearlinespecial"))
			{
				if (flags.ExtCommandIndex == ~0u) flags.ExtCommandIndex = CompatParams.Size();
				CompatParams.Push(CP_CLEARSPECIAL);
				sc.MustGetNumber();
				CompatParams.Push(sc.Number);
			}
			else if (sc.Compare("setactivation"))
			{
				if (flags.ExtCommandIndex == ~0u) flags.ExtCommandIndex = CompatParams.Size();
				CompatParams.Push(CP_SETACTIVATION);
				sc.MustGetNumber();
				CompatParams.Push(sc.Number);
				sc.MustGetNumber();
				CompatParams.Push(sc.Number);
			}
			else if (sc.Compare("setsectoroffset"))
			{
				if (flags.ExtCommandIndex == ~0u) flags.ExtCommandIndex = CompatParams.Size();
				CompatParams.Push(CP_SETSECTOROFFSET);
				sc.MustGetNumber();
				CompatParams.Push(sc.Number);
				sc.MustGetString();
				CompatParams.Push(sc.MustMatchString(SectorPlanes));
				sc.MustGetFloat();
				CompatParams.Push(int(sc.Float*65536.));
			}
			else if (sc.Compare("setsectorspecial"))
			{
				if (flags.ExtCommandIndex == ~0u) flags.ExtCommandIndex = CompatParams.Size();
				CompatParams.Push(CP_SETSECTORSPECIAL);
				sc.MustGetNumber();
				CompatParams.Push(sc.Number);
				sc.MustGetNumber();
				CompatParams.Push(sc.Number);
			}
			else if (sc.Compare("setwallyscale"))
			{
				if (flags.ExtCommandIndex == ~0u) flags.ExtCommandIndex = CompatParams.Size();
				CompatParams.Push(CP_SETWALLYSCALE);
				sc.MustGetNumber();
				CompatParams.Push(sc.Number);
				sc.MustGetString();
				CompatParams.Push(sc.MustMatchString(LineSides));
				sc.MustGetString();
				CompatParams.Push(sc.MustMatchString(WallTiers));
				sc.MustGetFloat();
				CompatParams.Push(int(sc.Float*65536.));
			}
			else if (sc.Compare("setwalltexture"))
			{
				if (flags.ExtCommandIndex == ~0u) flags.ExtCommandIndex = CompatParams.Size();
				CompatParams.Push(CP_SETWALLTEXTURE);
				sc.MustGetNumber();
				CompatParams.Push(sc.Number);
				sc.MustGetString();
				CompatParams.Push(sc.MustMatchString(LineSides));
				sc.MustGetString();
				CompatParams.Push(sc.MustMatchString(WallTiers));
				sc.MustGetString();
				const FString texName = sc.String;
				const unsigned int texIndex = TexNames.Find(texName);
				const unsigned int texCount = TexNames.Size();
				if (texIndex == texCount)
				{
					TexNames.Push(texName);
				}
				CompatParams.Push(texIndex);
			}
			else if (sc.Compare("setthingz"))
			{
				if (flags.ExtCommandIndex == ~0u) flags.ExtCommandIndex = CompatParams.Size();
				CompatParams.Push(CP_SETTHINGZ);
				sc.MustGetNumber();
				CompatParams.Push(sc.Number);
				sc.MustGetFloat();
				CompatParams.Push(int(sc.Float*256));	// do not use full fixed here so that it can eventually handle larger levels
			}
			else if (sc.Compare("setsectortag"))
			{
				if (flags.ExtCommandIndex == ~0u) flags.ExtCommandIndex = CompatParams.Size();
				CompatParams.Push(CP_SETTAG);
				sc.MustGetNumber();
				CompatParams.Push(sc.Number);
				sc.MustGetNumber();
				CompatParams.Push(sc.Number);
			}
			else if (sc.Compare("setthingflags"))
			{
				if (flags.ExtCommandIndex == ~0u) flags.ExtCommandIndex = CompatParams.Size();
				CompatParams.Push(CP_SETTHINGFLAGS);
				sc.MustGetNumber();
				CompatParams.Push(sc.Number);
				sc.MustGetNumber();
				CompatParams.Push(sc.Number);
			}
			else if (sc.Compare("setvertex"))
			{
				if (flags.ExtCommandIndex == ~0u) flags.ExtCommandIndex = CompatParams.Size();
				CompatParams.Push(CP_SETVERTEX);
				sc.MustGetNumber();
				CompatParams.Push(sc.Number);
				sc.MustGetFloat();
				CompatParams.Push(int(sc.Float * 256));	// do not use full fixed here so that it can eventually handle larger levels
				sc.MustGetFloat();
				CompatParams.Push(int(sc.Float * 256));	// do not use full fixed here so that it can eventually handle larger levels
				flags.CompatFlags[SLOT_BCOMPAT] |= BCOMPATF_REBUILDNODES;
			}
			else if (sc.Compare("setthingskills"))
			{
				if (flags.ExtCommandIndex == ~0u) flags.ExtCommandIndex = CompatParams.Size();
				CompatParams.Push(CP_SETTHINGSKILLS);
				sc.MustGetNumber();
				CompatParams.Push(sc.Number);
				sc.MustGetNumber();
				CompatParams.Push(sc.Number);
			}
			else if (sc.Compare("setsectortexture"))
			{
				if (flags.ExtCommandIndex == ~0u) flags.ExtCommandIndex = CompatParams.Size();
				CompatParams.Push(CP_SETSECTORTEXTURE);
				sc.MustGetNumber();
				CompatParams.Push(sc.Number);
				sc.MustGetString();
				CompatParams.Push(sc.MustMatchString(SectorPlanes));
				sc.MustGetString();
				const FString texName = sc.String;
				const unsigned int texIndex = TexNames.Find(texName);
				const unsigned int texCount = TexNames.Size();
				if (texIndex == texCount)
				{
					TexNames.Push(texName);
				}
				CompatParams.Push(texIndex);
			}
			else if (sc.Compare("setsectorlight"))
			{
				if (flags.ExtCommandIndex == ~0u) flags.ExtCommandIndex = CompatParams.Size();
				CompatParams.Push(CP_SETSECTORLIGHT);
				sc.MustGetNumber();
				CompatParams.Push(sc.Number);
				sc.MustGetNumber();
				CompatParams.Push(sc.Number);
			}
			else
			{
				sc.UnGet();
				break;
			}
		}
		if (flags.ExtCommandIndex != ~0u) 
		{
			CompatParams.Push(CP_END);
		}
		sc.MustGetStringName("}");
		for (j = 0; j < md5array.Size(); ++j)
		{
			BCompatMap[md5array[j]] = flags;
		}
		md5array.Clear();
	}
}
Example #21
0
void SSuperSearchBox::OnTextChanged(const FText& InText)
{
	if(bIgnoreUIUpdate)
	{
		return;
	}

	const FString& InputTextStr = InputText->GetText().ToString();
	if(!InputTextStr.IsEmpty())
	{
		//search through google for online content
		{
			TSharedRef<class IHttpRequest> HttpRequest = FHttpModule::Get().CreateRequest();

			// build the url
			FString UrlEncodedString = FPlatformHttp::UrlEncode(InText.ToString());	//we need to url encode for special characters (especially other languages)
			// use partial response to only look at items and things we care about for items right now (title,link and label name)
			const FText QueryURL = FText::Format(FText::FromString("https://www.googleapis.com/customsearch/v1?key=AIzaSyCMGfdDaSfjqv5zYoS0mTJnOT3e9MURWkU&cx=009868829633250020713:y7tfd8hlcgg&fields=items(title,link,labels/name)&q={0}+less:forums"), FText::FromString(UrlEncodedString));

			//save http request into map to ensure correct ordering
			FText & Query = RequestQueryMap.FindOrAdd(HttpRequest);
			Query = InText;

			

			// kick off http request to read friends
			HttpRequest->OnProcessRequestComplete().BindRaw(this, &SSuperSearchBox::Query_HttpRequestComplete);
			HttpRequest->SetURL(QueryURL.ToString());
			HttpRequest->SetHeader(TEXT("Content-Type"), TEXT("application/json"));
			HttpRequest->SetVerb(TEXT("GET"));
			HttpRequest->ProcessRequest();
		}

#if WITH_EDITOR
		//search local tutorials
		{
			FAssetRegistryModule& AssetRegistry = FModuleManager::LoadModuleChecked<FAssetRegistryModule>(TEXT("AssetRegistry"));

			FARFilter Filter;
			Filter.ClassNames.Add(UBlueprint::StaticClass()->GetFName());
			Filter.bRecursiveClasses = true;
			Filter.TagsAndValues.Add(TEXT("ParentClass"), FString::Printf(TEXT("%s'%s'"), *UClass::StaticClass()->GetName(), *UEditorTutorial::StaticClass()->GetPathName()));

			TArray<FAssetData> AssetData;
			AssetRegistry.Get().GetAssets(Filter, AssetData);

			//add search result into cache
			FSearchResults& SearchResults = SearchResultsCache.FindOrAdd(InText.ToString());
			TArray<FSearchEntry>& TutorialResults = SearchResults.TutorialResults;
			TutorialResults.Empty();

			for (const FAssetData& Asset : AssetData)
			{
				const FString* SearchTag = Asset.TagsAndValues.Find("SearchTags");
				const FString* ResultTitle = Asset.TagsAndValues.Find("Title");
				if( ResultTitle)
				{
					if (ResultTitle->Contains(InText.ToString()))
					{
						FSearchEntry SearchEntry;
						SearchEntry.Title = *ResultTitle;
						SearchEntry.URL = "";
						SearchEntry.bCategory = false;
						SearchEntry.AssetData = Asset;
						TutorialResults.Add(SearchEntry);
					}
				}

				// If the asset has search tags, search them
				if ((SearchTag) && (SearchTag->IsEmpty()== false))
				{
					TArray<FString> SearchTags;			
					SearchTag->ParseIntoArray(SearchTags,TEXT(","));
					//trim any xs spaces off the strings.
					for (int32 iTag = 0; iTag < SearchTags.Num() ; iTag++)
					{
						SearchTags[iTag] = SearchTags[iTag].Trim();
						SearchTags[iTag] = SearchTags[iTag].TrimTrailing();
					}
					if (SearchTags.Find(InText.ToString()) != INDEX_NONE)
					{
						FSearchEntry SearchEntry;
						SearchEntry.Title = *ResultTitle;
						SearchEntry.URL = "";
						SearchEntry.bCategory = false;
						SearchEntry.AssetData = Asset;
						TutorialResults.Add(SearchEntry);
					}
				}
			}
		}
#endif
	}
	else
	{
		ClearSuggestions();
	}
}
Example #22
0
void CollectFilesToAdd(TArray<FPakInputPair>& OutFilesToAdd, const TArray<FPakInputPair>& InEntries, const TMap<FString, uint64>& OrderMap)
{
	UE_LOG(LogPakFile, Display, TEXT("Collecting files to add to pak file..."));
	const double StartTime = FPlatformTime::Seconds();

	// Start collecting files
	TSet<FString> AddedFiles;	
	for (int32 Index = 0; Index < InEntries.Num(); Index++)
	{
		const FPakInputPair& Input = InEntries[Index];
		const FString& Source = Input.Source;
		bool bCompression = Input.bNeedsCompression;
		bool bEncryption = Input.bNeedEncryption;


		FString Filename = FPaths::GetCleanFilename(Source);
		FString Directory = FPaths::GetPath(Source);
		FPaths::MakeStandardFilename(Directory);
		FPakFile::MakeDirectoryFromPath(Directory);

		if (Filename.IsEmpty())
		{
			Filename = TEXT("*.*");
		}
		if ( Filename.Contains(TEXT("*")) )
		{
			// Add multiple files
			TArray<FString> FoundFiles;
			IFileManager::Get().FindFilesRecursive(FoundFiles, *Directory, *Filename, true, false);

			for (int32 FileIndex = 0; FileIndex < FoundFiles.Num(); FileIndex++)
			{
				FPakInputPair FileInput;
				FileInput.Source = FoundFiles[FileIndex];
				FPaths::MakeStandardFilename(FileInput.Source);
				FileInput.Dest = FileInput.Source.Replace(*Directory, *Input.Dest, ESearchCase::IgnoreCase);
				const uint64* FoundOrder = OrderMap.Find(FileInput.Dest);
				if(FoundOrder)
				{
					FileInput.SuggestedOrder = *FoundOrder;
				}
				FileInput.bNeedsCompression = bCompression;
				FileInput.bNeedEncryption = bEncryption;
				if (!AddedFiles.Contains(FileInput.Source))
				{
					OutFilesToAdd.Add(FileInput);
					AddedFiles.Add(FileInput.Source);
				}
				else
				{
					int32 FoundIndex;
					OutFilesToAdd.Find(FileInput,FoundIndex);
					OutFilesToAdd[FoundIndex].bNeedEncryption |= bEncryption;
					OutFilesToAdd[FoundIndex].bNeedsCompression |= bCompression;
				}
			}
		}
		else
		{
			// Add single file
			FPakInputPair FileInput;
			FileInput.Source = Input.Source;
			FPaths::MakeStandardFilename(FileInput.Source);
			FileInput.Dest = FileInput.Source.Replace(*Directory, *Input.Dest, ESearchCase::IgnoreCase);
			const uint64* FoundOrder = OrderMap.Find(FileInput.Dest);
			if (FoundOrder)
			{
				FileInput.SuggestedOrder = *FoundOrder;
			}
			FileInput.bNeedEncryption = bEncryption;
			FileInput.bNeedsCompression = bCompression;

			if (AddedFiles.Contains(FileInput.Source))
			{
				int32 FoundIndex;
				OutFilesToAdd.Find(FileInput, FoundIndex);
				OutFilesToAdd[FoundIndex].bNeedEncryption |= bEncryption;
				OutFilesToAdd[FoundIndex].bNeedsCompression |= bCompression;
			}
			else
			{
				OutFilesToAdd.Add(FileInput);
				AddedFiles.Add(FileInput.Source);
			}
		}
	}

	// Sort by suggested order then alphabetically
	struct FInputPairSort
	{
		FORCEINLINE bool operator()(const FPakInputPair& A, const FPakInputPair& B) const
		{
			return A.SuggestedOrder == B.SuggestedOrder ? A.Dest < B.Dest : A.SuggestedOrder < B.SuggestedOrder;
		}
	};
	OutFilesToAdd.Sort(FInputPairSort());
	UE_LOG(LogPakFile, Display, TEXT("Collected %d files in %.2lfs."), OutFilesToAdd.Num(), FPlatformTime::Seconds() - StartTime);
}
Example #23
0
void UMorphTarget::RemapVertexIndices( USkeletalMesh* InBaseMesh, const TArray< TArray<uint32> > & BasedWedgePointIndices )
{
	// make sure base wedge point indices have more than what this morph target has
	// any morph target import needs base mesh (correct LOD index if it belongs to LOD)
	check ( BasedWedgePointIndices.Num() >= MorphLODModels.Num() );
	check ( InBaseMesh );

	// for each LOD
	FSkeletalMeshResource* ImportedResource = InBaseMesh->GetImportedResource();
	for ( int32 LODIndex=0; LODIndex<MorphLODModels.Num(); ++LODIndex )
	{
		FStaticLODModel & BaseLODModel = ImportedResource->LODModels[LODIndex];
		FMorphTargetLODModel& MorphLODModel = MorphLODModels[LODIndex];
		const TArray<uint32> & LODWedgePointIndices = BasedWedgePointIndices[LODIndex];
		TArray<uint32> NewWedgePointIndices;

		// If the LOD has been simplified, don't remap vertex indices else the data will be useless if the mesh is unsimplified.
		check( LODIndex < InBaseMesh->LODInfo.Num() );
		if ( InBaseMesh->LODInfo[ LODIndex ].bHasBeenSimplified  )
		{
			continue;
		}

		// copy the wedge point indices - it makes easy to find
		if( BaseLODModel.RawPointIndices.GetBulkDataSize() )
		{
			NewWedgePointIndices.Empty( BaseLODModel.RawPointIndices.GetElementCount() );
			NewWedgePointIndices.AddUninitialized( BaseLODModel.RawPointIndices.GetElementCount() );
			FMemory::Memcpy( NewWedgePointIndices.GetData(), BaseLODModel.RawPointIndices.Lock(LOCK_READ_ONLY), BaseLODModel.RawPointIndices.GetBulkDataSize() );
			BaseLODModel.RawPointIndices.Unlock();
		
			// Source Indices used : Save it so that you don't use it twice
			TArray<uint32> SourceIndicesUsed;
			SourceIndicesUsed.Empty(MorphLODModel.Vertices.Num());

			// go through all vertices
			for ( int32 VertIdx=0; VertIdx<MorphLODModel.Vertices.Num(); ++VertIdx )
			{	
				// Get Old Base Vertex ID
				uint32 OldVertIdx = MorphLODModel.Vertices[VertIdx].SourceIdx;
				// find PointIndices from the old list
				uint32 BasePointIndex = LODWedgePointIndices[OldVertIdx];

				// Find the PointIndices from new list
				int32 NewVertIdx = NewWedgePointIndices.Find(BasePointIndex);
				// See if it's already used
				if ( SourceIndicesUsed.Find(NewVertIdx) != INDEX_NONE )
				{
					// if used look for next right vertex index
					for ( int32 Iter = NewVertIdx + 1; Iter<NewWedgePointIndices.Num(); ++Iter )
					{
						// found one
						if (NewWedgePointIndices[Iter] == BasePointIndex)
						{
							// see if used again
							if (SourceIndicesUsed.Find(Iter) == INDEX_NONE)
							{
								// if not, this slot is available 
								// update new value
								MorphLODModel.Vertices[VertIdx].SourceIdx = Iter;
								SourceIndicesUsed.Add(Iter);									
								break;
							}
						}
					}
				}
				else
				{
					// update new value
					MorphLODModel.Vertices[VertIdx].SourceIdx = NewVertIdx;
					SourceIndicesUsed.Add(NewVertIdx);
				}
			}

			MorphLODModel.Vertices.Sort(FCompareVertexAnimDeltas());
		}
	}
}
void FDestructibleMeshEditorViewportClient::ProcessClick( class FSceneView& View, class HHitProxy* HitProxy, FKey Key, EInputEvent Event, uint32 HitX, uint32 HitY )
{
#if WITH_APEX
	bool bKeepSelection = Viewport->KeyState(EKeys::LeftControl) || Viewport->KeyState(EKeys::RightControl);
	bool bSelectionChanged = false;

	if (Key == EKeys::LeftMouseButton && Event == EInputEvent::IE_Released)
	{
		UDestructibleComponent* Comp = PreviewDestructibleComp.Get();
		NxDestructibleAsset* Asset = Comp->DestructibleMesh->ApexDestructibleAsset;
		const NxRenderMeshAsset* RenderMesh = Asset->getRenderMeshAsset();

		FVector2D ScreenPos(HitX, HitY);
		FVector ClickOrigin, ViewDir;
		View.DeprojectFVector2D(ScreenPos, ClickOrigin, ViewDir);

		float NearestHitDistance = FLT_MAX;
		int32 ClickedChunk = -1;

		for (uint32 i=0; i < Asset->getChunkCount(); ++i)
		{
			int32 PartIdx = Asset->getPartIndex(i);
			int32 BoneIdx = i+1;
			
			if (!Comp->IsBoneHidden(BoneIdx))
			{
				PxBounds3 PBounds = RenderMesh->getBounds(PartIdx);

				FVector Center = P2UVector(PBounds.getCenter()) + Comp->GetBoneLocation(Comp->GetBoneName(BoneIdx));
				FVector Extent = P2UVector(PBounds.getExtents());

				FBox Bounds(Center - Extent, Center + Extent);

				FVector HitLoc, HitNorm;
				float HitTime;
				
				if (FMath::LineExtentBoxIntersection(Bounds, ClickOrigin, ClickOrigin + ViewDir * 1000.0f, FVector(0,0,0), HitLoc, HitNorm, HitTime))
				{
					float dist = (HitLoc - ClickOrigin).SizeSquared();

					if (dist < NearestHitDistance)
					{
						NearestHitDistance = dist;
						ClickedChunk = i;
					}
				}
			}
		}

		if (ClickedChunk >= 0)
		{
			int32 Idx = SelectedChunkIndices.Find(ClickedChunk);
		
			if (Idx < 0)
			{
				if (!bKeepSelection) { SelectedChunkIndices.Empty(); }

				SelectedChunkIndices.Add(ClickedChunk);
				bSelectionChanged = true;
			}
			else
			{
				SelectedChunkIndices.RemoveAt(Idx);
				bSelectionChanged = true;
			}
		}
		else if (!bKeepSelection)
		{
			SelectedChunkIndices.Empty();
 			bSelectionChanged = true;
		}
	}

	if (bSelectionChanged)
	{
		UpdateChunkSelection(SelectedChunkIndices);
	}
#endif // WITH_APEX
}
Example #25
0
/** Determine if any pins are connected, if so make all the other pins the same type, if not, make sure pins are switched back to wildcards */
void UK2Node_Select::NotifyPinConnectionListChanged(UEdGraphPin* Pin)
{
	Super::NotifyPinConnectionListChanged(Pin);

	const UEdGraphSchema_K2* Schema = GetDefault<UEdGraphSchema_K2>();

	// If this is the Enum pin we need to set the enum and reconstruct the node
	if (Pin == GetIndexPin())
	{
		// If the index pin was just linked to another pin
		if (Pin->LinkedTo.Num() > 0)
		{
			UEdGraphPin* LinkPin = Pin->LinkedTo[0];
			IndexPinType = LinkPin->PinType;
			Pin->PinType = IndexPinType;

			// See if it was an enum pin
			if (LinkPin->PinType.PinCategory == Schema->PC_Byte &&
				LinkPin->PinType.PinSubCategoryObject != NULL &&
				LinkPin->PinType.PinSubCategoryObject->IsA(UEnum::StaticClass()))
			{
				UEnum* EnumPtr = Cast<UEnum>(LinkPin->PinType.PinSubCategoryObject.Get());
				SetEnum(EnumPtr);
			}
			else
			{
				SetEnum(NULL);
			}

			Schema->SetPinDefaultValueBasedOnType(Pin);

			GetGraph()->NotifyGraphChanged();
			UBlueprint* Blueprint = GetBlueprint();
			if(!Blueprint->bBeingCompiled)
			{
				FBlueprintEditorUtils::MarkBlueprintAsModified(Blueprint);
				Blueprint->BroadcastChanged();
			}

			// If the index pin is a boolean, we need to remove all but 2 options
			if (IndexPinType.PinCategory == Schema->PC_Boolean &&
				NumOptionPins != 2)
			{
				NumOptionPins = 2;
				bReconstructNode = true;
			}
		}
	}
	else
	{
		// Grab references to all option pins and the return pin
		TArray<UEdGraphPin*> OptionPins;
		GetOptionPins(OptionPins);
		UEdGraphPin* ReturnPin = FindPin(Schema->PN_ReturnValue);

		// See if this pin is one of the wildcard pins
		bool bIsWildcardPin = (Pin == ReturnPin || OptionPins.Find(Pin) != INDEX_NONE) && Pin->PinType.PinCategory == Schema->PC_Wildcard;

		// If the pin was one of the wildcards we have to handle it specially
		if (bIsWildcardPin)
		{
			// If the pin is linked, make sure the other wildcard pins match
			if (Pin->LinkedTo.Num() > 0)
			{
				// Set pin type on the pin
				Pin->PinType = Pin->LinkedTo[0]->PinType;

				// Make sure the return pin is the same pin type
				if (ReturnPin != Pin)
				{
					ReturnPin->Modify();

					ReturnPin->PinType = Pin->PinType;
					UEdGraphSchema_K2::ValidateExistingConnections(ReturnPin);
				}

				// Make sure all options are of the same pin type
				for (auto It = OptionPins.CreateConstIterator(); It; It++)
				{
					UEdGraphPin* OptionPin = (*It);
					if (*It && *It != Pin)
					{
						(*It)->Modify();

						(*It)->PinType = Pin->PinType;
						UEdGraphSchema_K2::ValidateExistingConnections(*It);
					}
				}

				bReconstructNode = true;
			}
		}
	}
}
	/**
	 * Should the shader for this material with the given platform, shader type and vertex 
	 * factory type combination be compiled
	 *
	 * @param Platform		The platform currently being compiled for
	 * @param ShaderType	Which shader is being compiled
	 * @param VertexFactory	Which vertex factory is being compiled (can be NULL)
	 *
	 * @return true if the shader should be compiled
	 */
	virtual bool ShouldCache(EShaderPlatform Platform, const FShaderType* ShaderType, const FVertexFactoryType* VertexFactoryType) const
	{
		// only generate the needed shaders (which should be very restrictive for fast recompiling during editing)
		// @todo: Add a FindShaderType by fname or something

		if( Material->IsUIMaterial() )
		{
			if (FCString::Stristr(ShaderType->GetName(), TEXT("TSlateMaterialShaderPS")))
			{
				return true;
			}
	
		}


		{
			bool bEditorStatsMaterial = Material->bIsMaterialEditorStatsMaterial;

			// Always allow HitProxy shaders.
			if (FCString::Stristr(ShaderType->GetName(), TEXT("HitProxy")))
			{
				return true;
			}

			// we only need local vertex factory for the preview static mesh
			if (VertexFactoryType != FindVertexFactoryType(FName(TEXT("FLocalVertexFactory"), FNAME_Find)))
			{
				//cache for gpu skinned vertex factory if the material allows it
				//this way we can have a preview skeletal mesh
				if (bEditorStatsMaterial ||
					!IsUsedWithSkeletalMesh() ||
					(VertexFactoryType != FindVertexFactoryType(FName(TEXT("TGPUSkinVertexFactoryfalse"), FNAME_Find)) &&
					VertexFactoryType != FindVertexFactoryType(FName(TEXT("TGPUSkinVertexFactorytrue"), FNAME_Find))))
				{
					return false;
				}
			}

			if (bEditorStatsMaterial)
			{
				TArray<FString> ShaderTypeNames;
				TArray<FString> ShaderTypeDescriptions;
				GetRepresentativeShaderTypesAndDescriptions(ShaderTypeNames, ShaderTypeDescriptions);

				//Only allow shaders that are used in the stats.
				return ShaderTypeNames.Find(ShaderType->GetName()) != INDEX_NONE;
			}

			// look for any of the needed type
			bool bShaderTypeMatches = false;

			// For FMaterialResource::GetRepresentativeInstructionCounts
			if (FCString::Stristr(ShaderType->GetName(), TEXT("BasePassPSTDistanceFieldShadowsAndLightMapPolicyHQ")))
			{
				bShaderTypeMatches = true;
			}
			else if (FCString::Stristr(ShaderType->GetName(), TEXT("BasePassPSFNoLightMapPolicy")))
			{
				bShaderTypeMatches = true;
			}
			else if (FCString::Stristr(ShaderType->GetName(), TEXT("CachedPointIndirectLightingPolicy")))
			{
				bShaderTypeMatches = true;
			}
			else if (FCString::Stristr(ShaderType->GetName(), TEXT("BasePassPSFSelfShadowedTranslucencyPolicy")))
			{
				bShaderTypeMatches = true;
			}
			// Pick tessellation shader based on material settings
			else if(FCString::Stristr(ShaderType->GetName(), TEXT("BasePassVSFNoLightMapPolicy")) ||
				FCString::Stristr(ShaderType->GetName(), TEXT("BasePassHSFNoLightMapPolicy")) ||
				FCString::Stristr(ShaderType->GetName(), TEXT("BasePassDSFNoLightMapPolicy")))
			{
				bShaderTypeMatches = true;
			}
			else if (FCString::Stristr(ShaderType->GetName(), TEXT("DepthOnly")))
			{
				bShaderTypeMatches = true;
			}
			else if (FCString::Stristr(ShaderType->GetName(), TEXT("ShadowDepth")))
			{
				bShaderTypeMatches = true;
			}
			else if (FCString::Stristr(ShaderType->GetName(), TEXT("TDistortion")))
			{
				bShaderTypeMatches = true;
			}
			else if (FCString::Stristr(ShaderType->GetName(), TEXT("TBasePassForForwardShading")))
			{
				bShaderTypeMatches = true;
			}

			return bShaderTypeMatches;
		}
	
	}
//-------------------------------------------------------------------------
//
//-------------------------------------------------------------------------
bool UnFbx::FFbxImporter::CreateAndLinkExpressionForMaterialProperty(
							FbxSurfaceMaterial& FbxMaterial,
							UMaterial* UnrealMaterial,
							const char* MaterialProperty ,
							FExpressionInput& MaterialInput, 
							bool bSetupAsNormalMap,
							TArray<FString>& UVSet )
{
	bool bCreated = false;
	FbxProperty FbxProperty = FbxMaterial.FindProperty( MaterialProperty );
	if( FbxProperty.IsValid() )
	{
		int32 LayeredTextureCount = FbxProperty.GetSrcObjectCount(FbxLayeredTexture::ClassId);
		if (LayeredTextureCount>0)
		{
			UE_LOG(LogFbxMaterialImport, Warning,TEXT("Layered TEXTures are not supported (material %s)"),ANSI_TO_TCHAR(FbxMaterial.GetName()));
		}
		else
		{
			int32 TextureCount = FbxProperty.GetSrcObjectCount(FbxTexture::ClassId);
			if (TextureCount>0)
			{
				for(int32 TextureIndex =0; TextureIndex<TextureCount; ++TextureIndex)
				{
					FbxFileTexture* FbxTexture = FbxProperty.GetSrcObject(FBX_TYPE(FbxFileTexture), TextureIndex);

					// create an unreal texture asset
					UTexture* UnrealTexture = ImportTexture(FbxTexture, bSetupAsNormalMap);
				
					if (UnrealTexture)
					{
						// and link it to the material 
						UMaterialExpressionTextureSample* UnrealTextureExpression = ConstructObject<UMaterialExpressionTextureSample>( UMaterialExpressionTextureSample::StaticClass(), UnrealMaterial );
						UnrealMaterial->Expressions.Add( UnrealTextureExpression );
						MaterialInput.Expression = UnrealTextureExpression;
						UnrealTextureExpression->Texture = UnrealTexture;
						UnrealTextureExpression->SamplerType = bSetupAsNormalMap ? SAMPLERTYPE_Normal : SAMPLERTYPE_Color;
						
						// add/find UVSet and set it to the texture
						FbxString UVSetName = FbxTexture->UVSet.Get();
						FString LocalUVSetName = ANSI_TO_TCHAR(UVSetName.Buffer());
						int32 SetIndex = UVSet.Find(LocalUVSetName);
						UMaterialExpressionTextureCoordinate* MyCoordExpression = ConstructObject<UMaterialExpressionTextureCoordinate>( UMaterialExpressionTextureCoordinate::StaticClass(), UnrealMaterial );
						UnrealMaterial->Expressions.Add( MyCoordExpression );
						MyCoordExpression->CoordinateIndex = (SetIndex >= 0)? SetIndex: 0;
						UnrealTextureExpression->Coordinates.Expression = MyCoordExpression;

						if ( !bSetupAsNormalMap )
						{
							UnrealMaterial->BaseColor.Expression = UnrealTextureExpression;
						}
						else
						{
							UnrealMaterial->Normal.Expression = UnrealTextureExpression;
						}

						bCreated = true;
					}		
				}
			}

			if (MaterialInput.Expression)
			{
				TArray<FExpressionOutput> Outputs = MaterialInput.Expression->GetOutputs();
				FExpressionOutput* Output = Outputs.GetTypedData();
				MaterialInput.Mask = Output->Mask;
				MaterialInput.MaskR = Output->MaskR;
				MaterialInput.MaskG = Output->MaskG;
				MaterialInput.MaskB = Output->MaskB;
				MaterialInput.MaskA = Output->MaskA;
			}
		}
	}

	return bCreated;
}
int32 FProceduralTerrainWorker::PolygonizeToTriangles(
	UTerrainGrid *TerrainGrid,
	float p_fSurfaceCrossValue,
	FIntVector ChunkStartSize,
	FIntVector ChunkEndSize,
	FVector &ChunkLocation,
	FVector &ChunkScale,
	FTransform &TerrainTransform,

	TArray<FVector> &Vertices,
	TArray<int32> &Indices,
	TArray<FVector> &Normals,
	TArray<FVector2D> &UVs,
	TArray<FColor> &VertexColors,
	TArray<FProcMeshTangent> &Tangents
	)
{
	// TODO?
	float PosX = ChunkLocation.X;
	float PosY = ChunkLocation.Y;
	float PosZ = ChunkLocation.Z;
	TArray<FVector> Positions;

	int NumTriangles = 0;
	for (int32 x = ChunkStartSize.X; x < ChunkEndSize.X; ++x)
	{
		for (int32 y = ChunkStartSize.Y; y < ChunkEndSize.Y; ++y)
		{
			for (int32 z = ChunkStartSize.Z; z < ChunkEndSize.Z; ++z)
			{

				// Get each points of a cube.
				float p0 = TerrainGrid->GetVoxel(x, y, z);
				float p1 = TerrainGrid->GetVoxel(x + 1, y, z);
				float p2 = TerrainGrid->GetVoxel(x, y + 1, z);
				float p3 = TerrainGrid->GetVoxel(x + 1, y + 1, z);
				float p4 = TerrainGrid->GetVoxel(x, y, z + 1);
				float p5 = TerrainGrid->GetVoxel(x + 1, y, z + 1);
				float p6 = TerrainGrid->GetVoxel(x, y + 1, z + 1);
				float p7 = TerrainGrid->GetVoxel(x + 1, y + 1, z + 1);

				/*
				Determine the index into the edge table which
				tells us which vertices are inside of the surface
				*/
				int crossBitMap = 0;

				if (p0 < p_fSurfaceCrossValue) crossBitMap |= 1;
				if (p1 < p_fSurfaceCrossValue) crossBitMap |= 2;

				if (p2 < p_fSurfaceCrossValue) crossBitMap |= 8;
				if (p3 < p_fSurfaceCrossValue) crossBitMap |= 4;

				if (p4 < p_fSurfaceCrossValue) crossBitMap |= 16;
				if (p5 < p_fSurfaceCrossValue) crossBitMap |= 32;

				if (p6 < p_fSurfaceCrossValue) crossBitMap |= 128;
				if (p7 < p_fSurfaceCrossValue) crossBitMap |= 64;


				/* Cube is entirely in/out of the surface */
				int edgeBits = edgeTable[crossBitMap];
				if (edgeBits == 0)
					continue;

				float interpolatedCrossingPoint = 0.0f;
				FVector interpolatedValues[12];

				if ((edgeBits & 1) > 0)
				{

					interpolatedCrossingPoint = (p_fSurfaceCrossValue - p0) / (p1 - p0);
					interpolatedValues[0] = FMath::Lerp(FVector(PosX + x, PosY + y, PosZ + z), FVector(PosX + x + 1, PosY + y, PosZ + z), interpolatedCrossingPoint);
				}
				if ((edgeBits & 2) > 0)
				{
					interpolatedCrossingPoint = (p_fSurfaceCrossValue - p1) / (p3 - p1);
					interpolatedValues[1] = FMath::Lerp(FVector(PosX + x + 1, PosY + y, PosZ + z), FVector(PosX + x + 1, PosY + y + 1, PosZ + z), interpolatedCrossingPoint);
				}
				if ((edgeBits & 4) > 0)
				{
					interpolatedCrossingPoint = (p_fSurfaceCrossValue - p2) / (p3 - p2);
					interpolatedValues[2] = FMath::Lerp(FVector(PosX + x, PosY + y + 1, PosZ + z), FVector(PosX + x + 1, PosY + y + 1, PosZ + z), interpolatedCrossingPoint);
				}
				if ((edgeBits & 8) > 0)
				{
					interpolatedCrossingPoint = (p_fSurfaceCrossValue - p0) / (p2 - p0);
					interpolatedValues[3] = FMath::Lerp(FVector(PosX + x, PosY + y, PosZ + z), FVector(PosX + x, PosY + y + 1, PosZ + z), interpolatedCrossingPoint);
				}

				//Top four edges
				if ((edgeBits & 16) > 0)
				{
					interpolatedCrossingPoint = (p_fSurfaceCrossValue - p4) / (p5 - p4);
					interpolatedValues[4] = FMath::Lerp(FVector(PosX + x, PosY + y, PosZ + z + 1), FVector(PosX + x + 1, PosY + y, PosZ + z + 1), interpolatedCrossingPoint);
				}
				if ((edgeBits & 32) > 0)
				{
					interpolatedCrossingPoint = (p_fSurfaceCrossValue - p5) / (p7 - p5);
					interpolatedValues[5] = FMath::Lerp(FVector(PosX + x + 1, PosY + y, PosZ + z + 1), FVector(PosX + x + 1, PosY + y + 1, PosZ + z + 1), interpolatedCrossingPoint);
				}
				if ((edgeBits & 64) > 0)
				{
					interpolatedCrossingPoint = (p_fSurfaceCrossValue - p6) / (p7 - p6);
					interpolatedValues[6] = FMath::Lerp(FVector(PosX + x, PosY + y + 1, PosZ + z + 1), FVector(PosX + x + 1, PosY + y + 1, PosZ + z + 1), interpolatedCrossingPoint);
				}
				if ((edgeBits & 128) > 0)
				{
					interpolatedCrossingPoint = (p_fSurfaceCrossValue - p4) / (p6 - p4);
					interpolatedValues[7] = FMath::Lerp(FVector(PosX + x, PosY + y, PosZ + z + 1), FVector(PosX + x, PosY + y + 1, PosZ + z + 1), interpolatedCrossingPoint);
				}

				//Side four edges
				if ((edgeBits & 256) > 0)
				{
					interpolatedCrossingPoint = (p_fSurfaceCrossValue - p0) / (p4 - p0);
					interpolatedValues[8] = FMath::Lerp(FVector(PosX + x, PosY + y, PosZ + z), FVector(PosX + x, PosY + y, PosZ + z + 1), interpolatedCrossingPoint);
				}
				if ((edgeBits & 512) > 0)
				{
					interpolatedCrossingPoint = (p_fSurfaceCrossValue - p1) / (p5 - p1);
					interpolatedValues[9] = FMath::Lerp(FVector(PosX + x + 1, PosY + y, PosZ + z), FVector(PosX + x + 1, PosY + y, PosZ + z + 1), interpolatedCrossingPoint);
				}
				if ((edgeBits & 1024) > 0)
				{
					interpolatedCrossingPoint = (p_fSurfaceCrossValue - p3) / (p7 - p3);
					interpolatedValues[10] = FMath::Lerp(FVector(PosX + x + 1, PosY + y + 1, PosZ + z), FVector(PosX + x + 1, PosY + y + 1, PosZ + z + 1), interpolatedCrossingPoint);
				}
				if ((edgeBits & 2048) > 0)
				{
					interpolatedCrossingPoint = (p_fSurfaceCrossValue - p2) / (p6 - p2);
					interpolatedValues[11] = FMath::Lerp(FVector(PosX + x, PosY + y + 1, PosZ + z), FVector(PosX + x, PosY + y + 1, PosZ + z + 1), interpolatedCrossingPoint);
				}

				crossBitMap <<= 4;

				int triangleIndex = 0;
				while (triTable[crossBitMap + triangleIndex] != -1)
				{
					// For each triangle in the look up table, create a triangle and add it to the list.
					int index1 = triTable[crossBitMap + triangleIndex];
					int index2 = triTable[crossBitMap + triangleIndex + 1];
					int index3 = triTable[crossBitMap + triangleIndex + 2];

					FDynamicMeshVertex Vertex0;
					Vertex0.Position = TerrainTransform.TransformVector(ChunkScale * interpolatedValues[index1]);
					FDynamicMeshVertex Vertex1;
					Vertex1.Position = TerrainTransform.TransformVector(ChunkScale * interpolatedValues[index2]);
					FDynamicMeshVertex Vertex2;
					Vertex2.Position = TerrainTransform.TransformVector(ChunkScale * interpolatedValues[index3]);

					Vertex0.TextureCoordinate.X = Vertex0.Position.X / 100.0f;
					Vertex0.TextureCoordinate.Y = Vertex0.Position.Y / 100.0f;
					
					Vertex1.TextureCoordinate.X = Vertex1.Position.X / 100.0f;
					Vertex1.TextureCoordinate.Y = Vertex1.Position.Y / 100.0f;

					Vertex2.TextureCoordinate.X = Vertex2.Position.X / 100.0f;
					Vertex2.TextureCoordinate.Y = Vertex2.Position.Y / 100.0f;

					// Fill Index buffer And Vertex buffer with the generated vertices.
					int32 VIndex0 = Positions.Find(Vertex0.Position);
					if (VIndex0 < 0)
					{
						VIndex0 = Positions.Add(Vertex0.Position);
						Indices.Add(VIndex0);
						Vertices.Add(Vertex0.Position);
						UVs.Add(FVector2D(Vertex0.TextureCoordinate));
					}
					else {
						Indices.Add(VIndex0);
					}

					int32 VIndex1 = Positions.Find(Vertex1.Position);
					if (VIndex1 < 0)
					{
						VIndex1 = Positions.Add(Vertex1.Position);
						Indices.Add(VIndex1);
						Vertices.Add(Vertex1.Position);
						UVs.Add(FVector2D(Vertex1.TextureCoordinate));
					}
					else {
						Indices.Add(VIndex1);
					}

					int32 VIndex2 = Positions.Find(Vertex2.Position);
					if (VIndex2 < 0)
					{
						VIndex2 = Positions.Add(Vertex2.Position);
						Indices.Add(VIndex2);
						Vertices.Add(Vertex2.Position);
						UVs.Add(FVector2D(Vertex2.TextureCoordinate));
					}
					else {
						Indices.Add(VIndex2);
					}

					++NumTriangles;
					triangleIndex += 3;

				}



			}
		}
	}



	VertexColors.SetNum(Vertices.Num(), false);
	Normals.SetNum(Vertices.Num(), false);
	Tangents.SetNum(Vertices.Num(), false);

	for (int32 Index = 0; Index < Indices.Num(); Index += 3)
	{
		const FVector Edge21 = Vertices[Indices[Index + 1]] - Vertices[Indices[Index + 2]];
		const FVector Edge20 = Vertices[Indices[Index + 0]] - Vertices[Indices[Index + 2]];
		FVector TriNormal = (Edge21 ^ Edge20).GetSafeNormal();

		FVector FaceTangentX = Edge20.GetSafeNormal();
		FVector FaceTangentY = (FaceTangentX ^ TriNormal).GetSafeNormal();
		FVector FaceTangentZ = TriNormal;

		// Use Gram-Schmidt orthogonalization to make sure X is orth with Z
		FaceTangentX -= FaceTangentZ * (FaceTangentZ | FaceTangentX);
		FaceTangentX.Normalize();

		// See if we need to flip TangentY when generating from cross product
		const bool bFlipBitangent = ((FaceTangentZ ^ FaceTangentX) | FaceTangentY) < 0.f;
		TriNormal.Normalize();
		FaceTangentX.Normalize();
		FProcMeshTangent tangent = FProcMeshTangent(FaceTangentX, bFlipBitangent);
		for (int32 i = 0; i < 3; i++)
		{
			Normals[Indices[Index + i]] = TriNormal;
			Tangents[Indices[Index + i]] = tangent;
			VertexColors[Indices[Index + i]] = FColor(1, 1, 1, 1);
		}
	}
	//UE_LOG(LogTemp, Warning, TEXT("NumTriangles: %d => %d,%d -> %d,%d"), NumTriangles, ChunkStartSize.X, ChunkStartSize.Y, ChunkEndSize.X, ChunkEndSize.Y);
	return NumTriangles;
}
int32 UFixupRedirectsCommandlet::Main( const FString& Params )
{
	// Retrieve list of all packages in .ini paths.
	TArray<FString> PackageList;
	FEditorFileUtils::FindAllPackageFiles(PackageList);
	if( !PackageList.Num() )
	{
		return 0;
	}

	// process the commandline
	FString Token;
	const TCHAR* CommandLine	= *Params;
	bool bIsQuietMode			= false;
	bool bIsTestOnly			= false;
	bool bShouldRestoreProgress= false;
	bool bIsSCCDisabled		= false;
	bool bUpdateEnginePackages = false;
	bool bDeleteRedirects		= true;
	bool bDeleteOnlyReferencedRedirects = false;
	bool bAutoSubmit			= false;
	bool bSandboxed			= IFileManager::Get().IsSandboxEnabled();

	while (FParse::Token(CommandLine, Token, 1))
	{
		if (Token == TEXT("-nowarn"))
		{
			bIsQuietMode = true;
		}
		else if (Token == TEXT("-testonly"))
		{
			bIsTestOnly = true;
		}
		else if (Token == TEXT("-restore"))
		{
			bShouldRestoreProgress = true;
		}
		else if (Token == TEXT("-NoAutoCheckout"))
		{
			bIsSCCDisabled = true;
		}
		else if (Token == TEXT("-AutoSubmit"))
		{
			bAutoSubmit = true;
		}
		else if (Token == TEXT("-UpdateEnginePackages"))
		{
			bUpdateEnginePackages = true;
		}
		else if (Token == TEXT("-NoDelete"))
		{
			bDeleteRedirects = false;
		}
		else if (Token == TEXT("-NoDeleteUnreferenced"))
		{
			bDeleteOnlyReferencedRedirects = true;
		}
		else if ( Token.Left(1) != TEXT("-") )
		{
			FPackageName::SearchForPackageOnDisk(Token, &GRedirectCollector.FileToFixup);
		}
	}

	if (bSandboxed)
	{
		UE_LOG(LogFixupRedirectsCommandlet, Display, TEXT("Running in a sandbox."));
		bIsSCCDisabled = true;
		bAutoSubmit = false;
		bDeleteRedirects = false;
	}


	bool bShouldSkipErrors = false;

	// Setup file Output log in the Saved Directory
	const FString ProjectDir = FPaths::GetPath(FPaths::GetProjectFilePath());
	const FString LogFilename = FPaths::Combine(*ProjectDir, TEXT("Saved"), TEXT("FixupRedirect"), TEXT("FixupRedirect.log"));
	FArchive* LogOutputFile = IFileManager::Get().CreateFileWriter(*LogFilename);
	LogOutputFile->Logf(TEXT("[Redirects]"));

	/////////////////////////////////////////////////////////////////////
	// Display any script code referenced redirects
	/////////////////////////////////////////////////////////////////////

	TArray<FString> UpdatePackages;
	TArray<FString> RedirectorsThatCantBeCleaned;

	if (!bShouldRestoreProgress)
	{
		// load all string asset reference targets, and add fake redirectors for them
		GRedirectCollector.ResolveStringAssetReference();

		for (int32 RedirIndex = 0; RedirIndex < GRedirectCollector.Redirections.Num(); RedirIndex++)
		{
			FRedirection& Redir = GRedirectCollector.Redirections[RedirIndex];
			UE_LOG(LogFixupRedirectsCommandlet, Warning, TEXT("Boot redirector can't be cleaned: %s(%s) -> %s(%s)"), *Redir.RedirectorName, *Redir.RedirectorPackageFilename, *Redir.DestinationObjectName, *Redir.PackageFilename);
			RedirectorsThatCantBeCleaned.AddUnique(Redir.RedirectorName);
		}

		/////////////////////////////////////////////////////////////////////
		// Build a list of packages that need to be updated
		/////////////////////////////////////////////////////////////////////

		SET_WARN_COLOR(COLOR_WHITE);
		UE_LOG(LogFixupRedirectsCommandlet, Warning, TEXT(""));
		UE_LOG(LogFixupRedirectsCommandlet, Warning, TEXT("Generating list of tasks:"));
		UE_LOG(LogFixupRedirectsCommandlet, Warning, TEXT("-------------------------"));
		CLEAR_WARN_COLOR();

		int32 GCIndex = 0;

		// process script code first pass, then everything else
		for( int32 PackageIndex = 0; PackageIndex < PackageList.Num(); PackageIndex++ )
		{
			const FString& Filename = PackageList[PackageIndex];

			// already loaded code, skip them, and skip autosave packages
			if (Filename.StartsWith(AutoSaveUtils::GetAutoSaveDir(), ESearchCase::IgnoreCase))
			{
				continue;
			}

			UE_LOG(LogFixupRedirectsCommandlet, Display, TEXT("Looking for redirects in %s..."), *Filename);

			// Assert if package couldn't be opened so we have no chance of messing up saving later packages.
			UPackage* Package = Cast<UPackage>(LoadPackage(NULL, *Filename, 0));

			// load all string asset reference targets, and add fake redirectors for them
			GRedirectCollector.ResolveStringAssetReference();

			if (!Package)
			{
				SET_WARN_COLOR(COLOR_RED);
				UE_LOG(LogFixupRedirectsCommandlet, Warning, TEXT("   ... failed to open %s"), *Filename);
				CLEAR_WARN_COLOR();

				// if we are not in quiet mode, and the user wants to continue, go on
				const FText Message = FText::Format( NSLOCTEXT("Unreal", "PackageFailedToOpen", "The package '{0}' failed to open. Should this commandlet continue operation, ignoring further load errors? Continuing may have adverse effects (object references may be lost)."), FText::FromString( Filename ) );
				if (bShouldSkipErrors || (!bIsQuietMode && GWarn->YesNof( Message )))
				{
					bShouldSkipErrors = true;
					continue;
				}
				else
				{
					return 1;
				}
			}

			// all notices here about redirects get this color
			SET_WARN_COLOR(COLOR_DARK_GREEN);

			// look for any redirects that were followed that are referenced by this package
			// (may have been loaded earlier if this package was loaded by script code)
			// any redirectors referenced by script code can't be cleaned up
			for (int32 RedirIndex = 0; RedirIndex < GRedirectCollector.Redirections.Num(); RedirIndex++)
			{
				FRedirection& Redir = GRedirectCollector.Redirections[RedirIndex];

				if (Redir.PackageFilename == Filename)
				{
					UE_LOG(LogFixupRedirectsCommandlet, Warning, TEXT("   ... references %s [-> %s]"), *Redir.RedirectorName, *Redir.DestinationObjectName);

					// put the source and dest packages in the list to be updated
					UpdatePackages.AddUnique(Redir.PackageFilename);
					UpdatePackages.AddUnique(Redir.RedirectorPackageFilename);
				}
			}

			// clear the flag for needing to collect garbage
			bool bNeedToCollectGarbage = false;

			// if this package has any redirectors, make sure we update it
			if (!GRedirectCollector.FileToFixup.Len() || GRedirectCollector.FileToFixup == FPackageName::FilenameToLongPackageName(Filename))
			{
				TArray<UObject *> ObjectsInOuter;
				GetObjectsWithOuter(Package, ObjectsInOuter);
				for( int32 Index = 0; Index < ObjectsInOuter.Num(); Index++ )
				{
					UObject* Obj = ObjectsInOuter[Index];
					UObjectRedirector* Redir = Cast<UObjectRedirector>(Obj);
					if (Redir)
					{
						// make sure this package is in the list of packages to update
						UpdatePackages.AddUnique(Filename);

						UE_LOG(LogFixupRedirectsCommandlet, Warning, TEXT("   ... has redirect %s [-> %s]"), *Redir->GetPathName(), *Redir->DestinationObject->GetFullName());
						LogOutputFile->Logf(TEXT("%s = %s"), *Redir->GetPathName(), *Redir->DestinationObject->GetFullName());

						// make sure we GC if we found a redirector
						bNeedToCollectGarbage = true;
					}
				}
			}

			CLEAR_WARN_COLOR();

			// collect garbage every N packages, or if there was any redirectors in the package 
			// (to make sure that redirectors get reloaded properly and followed by the callback)
			// also, GC after loading a map package, to make sure it's unloaded so that we can track
			// other packages loading a map package as a dependency
			if (((++GCIndex) % 50) == 0 || bNeedToCollectGarbage || Package->ContainsMap())
			{
				// collect garbage to close the package
				CollectGarbage(RF_Native);
				UE_LOG(LogFixupRedirectsCommandlet, Display, TEXT("GC..."));
				// reset our counter
				GCIndex = 0;
			}
		}

		// save off restore point
		FArchive* Ar = IFileManager::Get().CreateFileWriter(*(FPaths::GameSavedDir() + TEXT("Fixup.bin")));
		*Ar << GRedirectCollector.Redirections;
		*Ar << UpdatePackages;
		*Ar << RedirectorsThatCantBeCleaned;
		delete Ar;
	}
	else
	{
		// load restore point
		FArchive* Ar = IFileManager::Get().CreateFileReader(*(FPaths::GameSavedDir() + TEXT("Fixup.bin")));
		if( Ar != NULL )
		{
			*Ar << GRedirectCollector.Redirections;
			*Ar << UpdatePackages;
			*Ar << RedirectorsThatCantBeCleaned;
			delete Ar;
		}
	}

	// unregister the callback so we stop getting redirections added
	FCoreUObjectDelegates::RedirectorFollowed.RemoveAll(&GRedirectCollector);
	
	/////////////////////////////////////////////////////////////////////
	// Explain to user what is happening
	/////////////////////////////////////////////////////////////////////
	SET_WARN_COLOR(COLOR_YELLOW);
	UE_LOG(LogFixupRedirectsCommandlet, Warning, TEXT(""));
	UE_LOG(LogFixupRedirectsCommandlet, Warning, TEXT("Files that need to fixed up:"));
	SET_WARN_COLOR(COLOR_DARK_YELLOW);

	// print out the working set of packages
	for (int32 PackageIndex = 0; PackageIndex < UpdatePackages.Num(); PackageIndex++)
	{
		UE_LOG(LogFixupRedirectsCommandlet, Warning, TEXT("   %s"), *UpdatePackages[PackageIndex]);
	}

	UE_LOG(LogFixupRedirectsCommandlet, Warning, TEXT(""));
	CLEAR_WARN_COLOR();

	// if we are only testing, just just quiet before actually doing anything
	if (bIsTestOnly)
	{
		LogOutputFile->Close();
		delete LogOutputFile;
		LogOutputFile = NULL;
		return 0;
	}

	/////////////////////////////////////////////////////////////////////
	// Find redirectors that are referenced by packages we cant check out
	/////////////////////////////////////////////////////////////////////

	bool bEngineRedirectorCantBeCleaned = false;

	ISourceControlProvider& SourceControlProvider = ISourceControlModule::Get().GetProvider();

	// initialize source control if it hasn't been initialized yet
	if(!bIsSCCDisabled)
	{
		SourceControlProvider.Init();

		//Make sure we can check out all packages - update their state first
		SourceControlProvider.Execute(ISourceControlOperation::Create<FUpdateStatus>(), UpdatePackages);
	}

	for( int32 PackageIndex = 0; PackageIndex < UpdatePackages.Num(); PackageIndex++ )
	{
		const FString& Filename = UpdatePackages[PackageIndex];

		bool bCanEdit = true;

		if(!bIsSCCDisabled)
		{
			FSourceControlStatePtr SourceControlState = SourceControlProvider.GetState(Filename, EStateCacheUsage::ForceUpdate);
			if(SourceControlState.IsValid() && !SourceControlState->CanEdit())
			{
				bCanEdit = false;
			}
		}
		else if(IFileManager::Get().IsReadOnly(*Filename))
		{
			bCanEdit = false;
		}

		if(!bCanEdit)
		{
			const bool bAllowCheckout = bUpdateEnginePackages || !Filename.StartsWith( FPaths::EngineDir() );

			if (!bIsSCCDisabled && bAllowCheckout)
			{
				FString PackageName(FPackageName::FilenameToLongPackageName(Filename));
				FSourceControlStatePtr SourceControlState = SourceControlProvider.GetState(Filename, EStateCacheUsage::ForceUpdate);
				if (SourceControlState.IsValid() && SourceControlState->CanCheckout())
				{
					SourceControlProvider.Execute(ISourceControlOperation::Create<FCheckOut>(), SourceControlHelpers::PackageFilename(PackageName));
					FSourceControlStatePtr NewSourceControlState = SourceControlProvider.GetState(Filename, EStateCacheUsage::ForceUpdate);
					bCanEdit = NewSourceControlState.IsValid() && NewSourceControlState->CanEdit();
				}
			}

			// if the checkout failed for any reason, we can't clean it up
			if (bAllowCheckout && !bCanEdit)
			{
				SET_WARN_COLOR(COLOR_RED);
				UE_LOG(LogFixupRedirectsCommandlet, Warning, TEXT("Couldn't check out/edit %s..."), *Filename);
				CLEAR_WARN_COLOR();

				// load all string asset reference targets, and add fake redirectors for them
				GRedirectCollector.ResolveStringAssetReference();

				// any redirectors that are pointed to by this read-only package can't be fixed up
				for (int32 RedirIndex = 0; RedirIndex < GRedirectCollector.Redirections.Num(); RedirIndex++)
				{
					FRedirection& Redir = GRedirectCollector.Redirections[RedirIndex];

					// any redirectors pointed to by this file we don't clean
					if (Redir.PackageFilename == Filename)
					{
						RedirectorsThatCantBeCleaned.AddUnique(Redir.RedirectorName);
						UE_LOG(LogFixupRedirectsCommandlet, Warning, TEXT("   ... can't fixup references to %s"), *Redir.RedirectorName);

						// if this redirector is in an Engine package, we need to alert the user, because
						// Engine packages are shared between games, so if one game still needs the redirector
						// to be valid, then we can't check in Engine packages after another game has
						// run that could 
						if (!bEngineRedirectorCantBeCleaned)
						{
							FString PackagePath;
							FPackageName::DoesPackageExist(Redir.RedirectorPackageFilename, NULL, &PackagePath);
							if (PackagePath.StartsWith(FPaths::EngineDir()))
							{
								bEngineRedirectorCantBeCleaned = true;
							}
						}
					}
					// any redirectors in this file can't be deleted
					else if (Redir.RedirectorPackageFilename == Filename)
					{
						RedirectorsThatCantBeCleaned.AddUnique(Redir.RedirectorName);
						UE_LOG(LogFixupRedirectsCommandlet, Warning, TEXT("   ... can't delete %s"), *Redir.RedirectorName);
					}
				}
			} 
			else
			{
				SET_WARN_COLOR(COLOR_DARK_GREEN);
				UE_LOG(LogFixupRedirectsCommandlet, Warning, TEXT("Checked out %s..."), *Filename);
				CLEAR_WARN_COLOR();
			}
		}
	}
	// warn about an engine package that can't be cleaned up (may want to revert engine packages)
	if (bEngineRedirectorCantBeCleaned)
	{
		UE_LOG(LogFixupRedirectsCommandlet, Warning, TEXT(""));
		UE_LOG(LogFixupRedirectsCommandlet, Warning, TEXT("Engine package redirectors are referenced by packages that can't be cleaned. If you have multiple games, it's recommended you revert any checked out Engine packages."));
		bUpdateEnginePackages=false;
	}

	UE_LOG(LogFixupRedirectsCommandlet, Warning, TEXT(""));

	/////////////////////////////////////////////////////////////////////
	// Load and save packages to save out the proper fixed up references
	/////////////////////////////////////////////////////////////////////

	SET_WARN_COLOR(COLOR_WHITE);
	UE_LOG(LogFixupRedirectsCommandlet, Warning, TEXT("Fixing references to point to proper objects:"));
	UE_LOG(LogFixupRedirectsCommandlet, Warning, TEXT("---------------------------------------------"));
	CLEAR_WARN_COLOR();

	// keep a list of all packages that have ObjectRedirects in them. This is not needed if we aren't deleting redirects
	TArray<bool> PackagesWithRedirects;

	// Delete these packages entirely because they are empty
	TArray<bool> EmptyPackagesToDelete;

	if ( bDeleteRedirects )
	{
		PackagesWithRedirects.AddZeroed(UpdatePackages.Num());
		EmptyPackagesToDelete.AddZeroed(PackageList.Num());
	}

	bool bSCCEnabled = ISourceControlModule::Get().IsEnabled();

	// Iterate over all packages, loading and saving to remove all references to ObjectRedirectors (can't delete the Redirects yet)
	for( int32 PackageIndex = 0; PackageIndex < UpdatePackages.Num(); PackageIndex++ )
	{
		const FString& Filename = UpdatePackages[PackageIndex];

		// we can't do anything with packages that are read-only or cant be edited due to source control (we don't want to fixup the redirects)
		if(bSCCEnabled)
		{
			FSourceControlStatePtr SourceControlState = SourceControlProvider.GetState(SourceControlHelpers::PackageFilename(Filename), EStateCacheUsage::ForceUpdate);
			if(SourceControlState.IsValid() && !SourceControlState->CanEdit())
			{
				continue;
			}
		}
		else if(IFileManager::Get().IsReadOnly(*Filename))
		{
			continue;
		}
		
		// Assert if package couldn't be opened so we have no chance of messing up saving later packages.
		UE_LOG(LogFixupRedirectsCommandlet, Warning, TEXT("Cleaning %s"), *Filename);
		UPackage* Package = LoadPackage( NULL, *Filename, LOAD_NoVerify );
		// load all string asset reference targets, and add fake redirectors for them
		GRedirectCollector.ResolveStringAssetReference();

		// if it failed to open, we have already dealt with quitting if we are going to, so just skip it
		if (!Package)
		{
			continue;
		}

		if ( bDeleteOnlyReferencedRedirects && bDeleteRedirects )
		{
			TArray<UObject *> ObjectsInOuter;
			GetObjectsWithOuter(Package, ObjectsInOuter);
			for( int32 Index = 0; Index < ObjectsInOuter.Num(); Index++ )
			{
				UObject* Obj = ObjectsInOuter[Index];
				UObjectRedirector* Redir = Cast<UObjectRedirector>(Obj);
				if (Redir)
				{
					PackagesWithRedirects[PackageIndex] = 1;
					break;
				}
			}
		}

		// Don't save packages in the engine directory if we did not opt to update engine packages
		if( bUpdateEnginePackages || !Filename.StartsWith( FPaths::EngineDir() ) )
		{
			// save out the package
			UWorld* World = UWorld::FindWorldInPackage(Package);
			GEditor->SavePackage( Package, World, World ? RF_NoFlags : RF_Standalone, *Filename, GWarn );
		}
		
		// collect garbage to close the package
		UE_LOG(LogFixupRedirectsCommandlet, Display, TEXT("Collecting Garbage..."));
		CollectGarbage(RF_Native);
	}

	UE_LOG(LogFixupRedirectsCommandlet, Warning, TEXT(""));

	if ( bDeleteRedirects )
	{
		/////////////////////////////////////////////////////////////////////
		// Delete all redirects that are no longer referenced
		/////////////////////////////////////////////////////////////////////

		SET_WARN_COLOR(COLOR_WHITE);
		UE_LOG(LogFixupRedirectsCommandlet, Warning, TEXT("Deleting redirector objects:"));
		UE_LOG(LogFixupRedirectsCommandlet, Warning, TEXT("----------------------------"));
		CLEAR_WARN_COLOR();

		const TArray<FString>& PackagesToCleaseOfRedirects = bDeleteOnlyReferencedRedirects ? UpdatePackages : PackageList;

		int32 GCIndex = 0;

		// Again, iterate over all packages, loading and saving to and this time deleting all 
		for( int32 PackageIndex = 0; PackageIndex < PackagesToCleaseOfRedirects.Num(); PackageIndex++ )
		{
			const FString& Filename = PackagesToCleaseOfRedirects[PackageIndex];
		
			if (bDeleteOnlyReferencedRedirects && PackagesWithRedirects[PackageIndex] == 0)
			{
				continue;
			}

			// The file should have already been checked out/be writable
			// If it is read only that means the checkout failed or we are not using source control and we can not write this file.
			if(bSCCEnabled)
			{
				FSourceControlStatePtr SourceControlState = SourceControlProvider.GetState(SourceControlHelpers::PackageFilename(Filename), EStateCacheUsage::ForceUpdate);
				if(SourceControlState.IsValid() && !SourceControlState->CanEdit())
				{
					UE_LOG(LogFixupRedirectsCommandlet, Display, TEXT("Skipping file: source control says we cant edit the file %s..."), *Filename);
					continue;
				}
			}
			else if( IFileManager::Get().IsReadOnly( *Filename ))
			{
				UE_LOG(LogFixupRedirectsCommandlet, Display, TEXT("Skipping read-only file %s..."), *Filename);
				continue;
			}

			UE_LOG(LogFixupRedirectsCommandlet, Warning, TEXT("Wiping %s..."), *Filename);
		
			UPackage* Package = LoadPackage( NULL, *Filename, 0 );
			// load all string asset reference targets, and add fake redirectors for them
			GRedirectCollector.ResolveStringAssetReference();
			// if it failed to open, we have already dealt with quitting if we are going to, so just skip it
			if (!Package)
			{
				continue;
			}

			// assume that all redirs in this file are still-referenced
			bool bIsDirty = false;

			// see if this package is completely empty other than purged redirectors and metadata
			bool bIsEmpty = true;

			// delete all ObjectRedirects now
			TArray<UObjectRedirector*> Redirs;
			// find all redirectors, put into an array so delete doesn't mess up the iterator
			TArray<UObject *> ObjectsInOuter;
			GetObjectsWithOuter(Package, ObjectsInOuter);
			for( int32 Index = 0; Index < ObjectsInOuter.Num(); Index++ )
			{
				UObject* Obj = ObjectsInOuter[Index];
				UObjectRedirector *Redir = Cast<UObjectRedirector>(Obj);

				if (Redir)
				{
					int32 Dummy;
					// if the redirector was marked as uncleanable, skip it
					if (RedirectorsThatCantBeCleaned.Find(Redir->GetFullName(), Dummy))
					{
						SET_WARN_COLOR(COLOR_DARK_RED);
						UE_LOG(LogFixupRedirectsCommandlet, Warning, TEXT("   ... skipping still-referenced %s"), *Redir->GetFullName());
						CLEAR_WARN_COLOR();
						bIsEmpty = false;
						continue;
					}

					bIsDirty = true;
					SET_WARN_COLOR(COLOR_DARK_GREEN);
					UE_LOG(LogFixupRedirectsCommandlet, Warning, TEXT("   ... deleting %s"), *Redir->GetFullName());
					CLEAR_WARN_COLOR();
					Redirs.Add(Redir);
				}
				else if ( (!Obj->IsA(UMetaData::StaticClass()) && !Obj->IsA(UField::StaticClass()))
					|| Obj->IsA(UUserDefinedEnum::StaticClass())) // UserDefinedEnum should not be deleted
				{
					// This package has a real object
					bIsEmpty = false;
				}
			}

			if (bIsEmpty && !bDeleteOnlyReferencedRedirects)
			{
				EmptyPackagesToDelete[PackageIndex] = 1;
			}

			// mark them for deletion.
			for (int32 RedirIndex = 0; RedirIndex < Redirs.Num(); RedirIndex++)
			{
				UObjectRedirector* Redirector = Redirs[RedirIndex];
				// Remove standalone flag and mark as pending kill.
				Redirector->ClearFlags( RF_Standalone | RF_Public );
			
				// We don't need redirectors in the root set, the references should already be fixed up
				if ( Redirector->IsRooted() )
				{
					Redirector->RemoveFromRoot();
				}

				Redirector->MarkPendingKill();
			}
			Redirs.Empty();

			if (bIsDirty)
			{
				// Don't save packages in the engine directory if we did not opt to update engine packages
				if( bUpdateEnginePackages || !Filename.StartsWith( FPaths::EngineDir() ) )
				{
					// save the package
					UWorld* World = UWorld::FindWorldInPackage(Package);
					GEditor->SavePackage( Package, World, World ? RF_NoFlags : RF_Standalone, *Filename, GWarn );
				}
			}

			// collect garbage every N packages, or if there was any redirectors in the package 
			if (((++GCIndex) % 50) == 0 || bIsDirty || Package->ContainsMap())
			{
				// collect garbage to close the package
				CollectGarbage(RF_Native);
				UE_LOG(LogFixupRedirectsCommandlet, Display, TEXT("GC..."));
				// reset our counter
				GCIndex = 0;
			}
		}

		UE_LOG(LogFixupRedirectsCommandlet, Warning, TEXT(""));
	}
	else
	{
		SET_WARN_COLOR(COLOR_WHITE);
		UE_LOG(LogFixupRedirectsCommandlet, Warning, TEXT("----------------------------"));
		UE_LOG(LogFixupRedirectsCommandlet, Warning, TEXT("Not deleting any redirector objects:"));
		UE_LOG(LogFixupRedirectsCommandlet, Warning, TEXT("----------------------------"));
		UE_LOG(LogFixupRedirectsCommandlet, Warning, TEXT(""));
		CLEAR_WARN_COLOR();
	}

	/////////////////////////////////////////////////////////////////////
	// Clean up any unused trash
	/////////////////////////////////////////////////////////////////////

	SET_WARN_COLOR(COLOR_WHITE);
	UE_LOG(LogFixupRedirectsCommandlet, Warning, TEXT("Deleting empty and unused packages:"));
	UE_LOG(LogFixupRedirectsCommandlet, Warning, TEXT("---------------------------------------"));
	CLEAR_WARN_COLOR();

	LogOutputFile->Logf(TEXT(""));
	LogOutputFile->Logf(TEXT("[Deleted]"));

	// Iterate over packages, attempting to delete unreferenced packages
	for( int32 PackageIndex = 0; PackageIndex < PackageList.Num(); PackageIndex++ )
	{
		bool bDelete = false;
		const FString& Filename = PackageList[PackageIndex];
		
		FString PackageName(FPackageName::FilenameToLongPackageName(Filename));

		const bool bAllowDelete = bUpdateEnginePackages || !Filename.StartsWith( FPaths::EngineDir() );
		if (bDeleteRedirects && !bDeleteOnlyReferencedRedirects && EmptyPackagesToDelete[PackageIndex] && bAllowDelete)
		{
			// this package is completely empty, delete it
			bDelete = true;
		}

		if (bDelete == true)
		{
			struct Local
			{
				static bool DeleteFromDisk(const FString& InFilename, const FString& InMessage)
				{
					SET_WARN_COLOR(COLOR_GREEN);
					UE_LOG(LogFixupRedirectsCommandlet, Warning, TEXT("%s"), *InMessage);
					if (!IFileManager::Get().Delete(*InFilename, false, true))
					{
						SET_WARN_COLOR(COLOR_RED);
						UE_LOG(LogFixupRedirectsCommandlet, Warning, TEXT("  ... failed to delete from disk."), *InFilename);
						CLEAR_WARN_COLOR();
						return false;
					}
					CLEAR_WARN_COLOR();
					return true;
				}
			};

			if (!bIsSCCDisabled)
			{
				// get file SCC status
				FString FileName = SourceControlHelpers::PackageFilename(PackageName);
				FSourceControlStatePtr SourceControlState = SourceControlProvider.GetState(FileName, EStateCacheUsage::ForceUpdate);

				if(SourceControlState.IsValid() && (SourceControlState->IsCheckedOut() || SourceControlState->IsAdded()) )
				{
					UE_LOG(LogFixupRedirectsCommandlet, Warning, TEXT("Revert '%s' from source control..."), *Filename);
					SourceControlProvider.Execute(ISourceControlOperation::Create<FRevert>(), FileName);

					SET_WARN_COLOR(COLOR_GREEN);
					UE_LOG(LogFixupRedirectsCommandlet, Warning, TEXT("Deleting '%s' from source control..."), *Filename);
					CLEAR_WARN_COLOR();
					SourceControlProvider.Execute(ISourceControlOperation::Create<FDelete>(), FileName);
				}
				else if(SourceControlState.IsValid() && SourceControlState->CanCheckout())
				{
					SET_WARN_COLOR(COLOR_GREEN);
					UE_LOG(LogFixupRedirectsCommandlet, Warning, TEXT("Deleting '%s' from source control..."), *Filename);
					CLEAR_WARN_COLOR();
					SourceControlProvider.Execute(ISourceControlOperation::Create<FDelete>(), FileName);
				}
				else if(SourceControlState.IsValid() && SourceControlState->IsCheckedOutOther())
				{
					SET_WARN_COLOR(COLOR_RED);
					UE_LOG(LogFixupRedirectsCommandlet, Warning, TEXT("Couldn't delete '%s' from source control, someone has it checked out, skipping..."), *Filename);
					CLEAR_WARN_COLOR();
				}
				else if(SourceControlState.IsValid() && !SourceControlState->IsSourceControlled())
				{
					if(Local::DeleteFromDisk(Filename, FString::Printf(TEXT("'%s' is not in source control, attempting to delete from disk..."), *Filename)))
					{
						LogOutputFile->Logf(*Filename);
					}
				}
				else
				{
					if(Local::DeleteFromDisk(Filename, FString::Printf(TEXT("'%s' is in an unknown source control state, attempting to delete from disk..."), *Filename)))
					{
						LogOutputFile->Logf(*Filename);
					}
				}
			}
			else
			{
				if(Local::DeleteFromDisk(Filename, FString::Printf(TEXT("source control disabled while deleting '%s', attempting to delete from disk..."), *Filename)))
				{
					LogOutputFile->Logf(*Filename);
				}
			}
		}
	}
	LogOutputFile->Close();
	delete LogOutputFile;
	LogOutputFile = NULL;

	if (!bIsSCCDisabled && bAutoSubmit)
	{
		/////////////////////////////////////////////////////////////////////
		// Submit the results to source control
		/////////////////////////////////////////////////////////////////////
		UE_LOG(LogFixupRedirectsCommandlet, Display, TEXT("Submiting the results to source control"));

		// Work out the list of file to check in
		TArray <FString> FilesToSubmit;

		for( int32 PackageIndex = 0; PackageIndex < PackageList.Num(); PackageIndex++ )
		{
			const FString& Filename = PackageList[PackageIndex];

			FString PackageName(FPackageName::FilenameToLongPackageName(Filename));
			FSourceControlStatePtr SourceControlState = SourceControlProvider.GetState(SourceControlHelpers::PackageFilename(Filename), EStateCacheUsage::ForceUpdate);

			if( SourceControlState.IsValid() && (SourceControlState->IsCheckedOut() || SourceControlState->IsAdded() || SourceControlState->IsDeleted()) )
			{
				// Only submit engine packages if we're requested to
				if( bUpdateEnginePackages || !Filename.StartsWith( FPaths::EngineDir() ) )
				{
					FilesToSubmit.Add(*PackageName);
				}
			}
		}

		// Check in all changed files
		const FText Description = NSLOCTEXT("FixupRedirectsCmdlet", "ChangelistDescription", "Fixed up Redirects");
		TSharedRef<FCheckIn, ESPMode::ThreadSafe> CheckInOperation = ISourceControlOperation::Create<FCheckIn>();
		CheckInOperation->SetDescription( Description );
		SourceControlProvider.Execute(CheckInOperation, SourceControlHelpers::PackageFilenames(FilesToSubmit));

		// toss the SCC manager
		ISourceControlModule::Get().GetProvider().Close();
	}
	return 0;
}
Example #30
0
		int32 Find(const void* itemPtr) const override
		{
			return Array->Find(*static_cast<const T*>(itemPtr));
		}