// Copyright 1998-2015 Epic Games, Inc. All Rights Reserved. #include "AddContentDialogPCH.h" #include "FeaturePackContentSource.h" #include "AssetToolsModule.h" #include "ContentBrowserModule.h" #include "IPlatformFilePak.h" #include "FileHelpers.h" #include "Editor/MainFrame/Public/Interfaces/IMainFrameModule.h" /*#include "SuperSearchModule.h"*/ #define LOCTEXT_NAMESPACE "ContentFeaturePacks" DEFINE_LOG_CATEGORY_STATIC(LogFeaturePack, Log, All); bool TryValidateTranslatedValue(TSharedPtr TranslatedValue, TSharedPtr& ErrorMessage) { if (TranslatedValue.IsValid() == false) { ErrorMessage = MakeShareable(new FString("Invalid translated value")); return false; } const TSharedPtr* TranslatedObject; if (TranslatedValue->TryGetObject(TranslatedObject) == false) { ErrorMessage = MakeShareable(new FString("Invalid translated value")); return false; } if ((*TranslatedObject)->HasTypedField("Language") == false) { ErrorMessage = MakeShareable(new FString("Translated value missing 'Language' field")); return false; } if ((*TranslatedObject)->HasTypedField("Text") == false) { ErrorMessage = MakeShareable(new FString("Translated value missing 'Text' field")); return false; } return true; } bool TryValidateManifestObject(TSharedPtr ManifestObject, TSharedPtr& ErrorMessage) { if (ManifestObject.IsValid() == false) { ErrorMessage = MakeShareable(new FString("Manifest object missing")); return false; } if (ManifestObject->HasTypedField("Name") == false) { ErrorMessage = MakeShareable(new FString("Manifest object missing 'Names' field")); return false; } for (TSharedPtr NameValue : ManifestObject->GetArrayField("Name")) { if (TryValidateTranslatedValue(NameValue, ErrorMessage) == false) { ErrorMessage = MakeShareable(new FString(FString::Printf(TEXT("Manifest object 'Names' field error: %s"), **ErrorMessage))); } } if (ManifestObject->HasTypedField("Description") == false) { ErrorMessage = MakeShareable(new FString("Manifest object missing 'Description' field")); return false; } for (TSharedPtr DescriptionValue : ManifestObject->GetArrayField("Description")) { if (TryValidateTranslatedValue(DescriptionValue, ErrorMessage) == false) { ErrorMessage = MakeShareable(new FString(FString::Printf(TEXT("Manifest object 'Description' field error: %s"), **ErrorMessage))); } } if (ManifestObject->HasTypedField("AssetTypes") == false) { ErrorMessage = MakeShareable(new FString("Manifest object missing 'AssetTypes' field")); return false; } for (TSharedPtr AssetTypesValue : ManifestObject->GetArrayField("AssetTypes")) { if (TryValidateTranslatedValue(AssetTypesValue, ErrorMessage) == false) { ErrorMessage = MakeShareable(new FString(FString::Printf(TEXT("Manifest object 'AssetTypes' field error: %s"), **ErrorMessage))); } } if (ManifestObject->HasTypedField("ClassTypes") == false) { ErrorMessage = MakeShareable(new FString("Manifest object missing 'ClassTypes' field")); return false; } if (ManifestObject->HasTypedField("Category") == false) { ErrorMessage = MakeShareable(new FString("Manifest object missing 'Category' field")); return false; } if (ManifestObject->HasTypedField("Thumbnail") == false) { ErrorMessage = MakeShareable(new FString("Manifest object missing 'Thumbnail' field")); return false; } if (ManifestObject->HasTypedField("Screenshots") == false) { ErrorMessage = MakeShareable(new FString("Manifest object missing 'Screenshots' field")); return false; } return true; } FFeaturePackContentSource::FFeaturePackContentSource(FString InFeaturePackPath) { FeaturePackPath = InFeaturePackPath; bPackValid = false; // Create a pak platform file and mount the feature pack file. FPakPlatformFile PakPlatformFile; FString CommandLine; PakPlatformFile.Initialize(&FPlatformFileManager::Get().GetPlatformFile(), TEXT("")); FString MountPoint = "root:/"; PakPlatformFile.Mount(*InFeaturePackPath, 0, *MountPoint); // Gets the manifest file as a JSon string TArray ManifestBuffer; if( LoadPakFileToBuffer(PakPlatformFile, FPaths::Combine(*MountPoint, TEXT("manifest.json")), ManifestBuffer) == false ) { UE_LOG(LogFeaturePack, Warning, TEXT("Error in Feature pack %s. Cannot find manifest."), *InFeaturePackPath); Category = EContentSourceCategory::Unknown; return; } FString ManifestString; FFileHelper::BufferToString(ManifestString, ManifestBuffer.GetData(), ManifestBuffer.Num()); // Populate text fields from the manifest. TSharedPtr ManifestObject; TSharedRef> ManifestReader = TJsonReaderFactory<>::Create(ManifestString); FJsonSerializer::Deserialize(ManifestReader, ManifestObject); if (ManifestReader->GetErrorMessage().IsEmpty() == false) { UE_LOG(LogFeaturePack, Warning, TEXT("Error in Feature pack %s. Failed to parse manifest: %s"), *InFeaturePackPath, *ManifestReader->GetErrorMessage()); Category = EContentSourceCategory::Unknown; return; } TSharedPtr ManifestObjectErrorMessage; if (TryValidateManifestObject(ManifestObject, ManifestObjectErrorMessage) == false) { UE_LOG(LogFeaturePack, Warning, TEXT("Error in Feature pack %s. Manifest object error: %s"), *InFeaturePackPath, **ManifestObjectErrorMessage); Category = EContentSourceCategory::Unknown; return; } for (TSharedPtr NameValue : ManifestObject->GetArrayField("Name")) { TSharedPtr LocalizedNameObject = NameValue->AsObject(); LocalizedNames.Add(FLocalizedText( LocalizedNameObject->GetStringField("Language"), FText::FromString(LocalizedNameObject->GetStringField("Text")))); } for (TSharedPtr DescriptionValue : ManifestObject->GetArrayField("Description")) { TSharedPtr LocalizedDescriptionObject = DescriptionValue->AsObject(); LocalizedDescriptions.Add(FLocalizedText( LocalizedDescriptionObject->GetStringField("Language"), FText::FromString(LocalizedDescriptionObject->GetStringField("Text")))); } // Parse asset types field for (TSharedPtr AssetTypesValue : ManifestObject->GetArrayField("AssetTypes")) { TSharedPtr LocalizedAssetTypesObject = AssetTypesValue->AsObject(); LocalizedAssetTypesList.Add(FLocalizedText( LocalizedAssetTypesObject->GetStringField("Language"), FText::FromString(LocalizedAssetTypesObject->GetStringField("Text")))); } // Parse asset types field for (TSharedPtr AssetTypesValue : ManifestObject->GetArrayField("SearchTags")) { TSharedPtr LocalizedAssetTypesObject = AssetTypesValue->AsObject(); LocalizedSearchTags.Add(FLocalizedTextArray( LocalizedAssetTypesObject->GetStringField("Language"), LocalizedAssetTypesObject->GetStringField("Text"))); } // Parse class types field ClassTypes = ManifestObject->GetStringField("ClassTypes"); // Parse initial focus asset if we have one - this is not required if (ManifestObject->HasTypedField("FocusAsset") == false) { FocusAssetIdent = ManifestObject->GetStringField("FocusAsset"); } // Use the path as the sort key - it will be alphabetical that way SortKey = FeaturePackPath; ManifestObject->TryGetStringField("SortKey", SortKey); FString CategoryString = ManifestObject->GetStringField("Category"); UEnum* Enum = FindObjectChecked(ANY_PACKAGE, TEXT("EContentSourceCategory")); int32 Index = Enum->FindEnumIndex(FName(*CategoryString)); Category = Index != INDEX_NONE ? (EContentSourceCategory)Index : EContentSourceCategory::Unknown; // Load image data FString IconFilename = ManifestObject->GetStringField("Thumbnail"); TSharedPtr> IconImageData = MakeShareable(new TArray()); FString ThumbnailFile = FPaths::Combine(*MountPoint, TEXT("Media"), *IconFilename); if( LoadPakFileToBuffer(PakPlatformFile, ThumbnailFile, *IconImageData) == true) { IconData = MakeShareable(new FImageData(IconFilename, IconImageData)); } else { UE_LOG(LogFeaturePack, Warning, TEXT("Error in Feature pack %s. Cannot find thumbnail %s."), *InFeaturePackPath, *ThumbnailFile ); } const TArray> ScreenshotFilenameArray = ManifestObject->GetArrayField("Screenshots"); for (const TSharedPtr ScreenshotFilename : ScreenshotFilenameArray) { TSharedPtr> SingleScreenshotData = MakeShareable(new TArray); if( LoadPakFileToBuffer(PakPlatformFile, FPaths::Combine(*MountPoint, TEXT("Media"), *ScreenshotFilename->AsString()), *SingleScreenshotData) ) { ScreenshotData.Add(MakeShareable(new FImageData(ScreenshotFilename->AsString(), SingleScreenshotData))); } else { UE_LOG(LogFeaturePack, Warning, TEXT("Error in Feature pack %s. Cannot find screenshot %s."), *InFeaturePackPath, *ScreenshotFilename->AsString() ); } } FSuperSearchModule& SuperSearchModule = FModuleManager::LoadModuleChecked< FSuperSearchModule >(TEXT("SuperSearch")); SuperSearchModule.GetActOnSearchTextClicked().AddRaw(this, &FFeaturePackContentSource::HandleActOnSearchText); SuperSearchModule.GetSearchTextChanged().AddRaw(this, &FFeaturePackContentSource::HandleSuperSearchTextChanged); bPackValid = true; } bool FFeaturePackContentSource::LoadPakFileToBuffer(FPakPlatformFile& PakPlatformFile, FString Path, TArray& Buffer) { bool bResult = false; TSharedPtr FileHandle(PakPlatformFile.OpenRead(*Path)); if( FileHandle.IsValid()) { Buffer.AddUninitialized(FileHandle->Size()); bResult = FileHandle->Read(Buffer.GetData(), FileHandle->Size()); } return bResult; } TArray FFeaturePackContentSource::GetLocalizedNames() const { return LocalizedNames; } TArray FFeaturePackContentSource::GetLocalizedDescriptions() const { return LocalizedDescriptions; } TArray FFeaturePackContentSource::GetLocalizedAssetTypes() const { return LocalizedAssetTypesList; } FString FFeaturePackContentSource::GetClassTypesUsed() const { return ClassTypes; } EContentSourceCategory FFeaturePackContentSource::GetCategory() const { return Category; } TSharedPtr FFeaturePackContentSource::GetIconData() const { return IconData; } TArray> FFeaturePackContentSource::GetScreenshotData() const { return ScreenshotData; } bool FFeaturePackContentSource::InstallToProject(FString InstallPath) { bool bResult = false; if( IsDataValid() == false ) { UE_LOG(LogFeaturePack, Warning, TEXT("Trying to install invalid pack %s"), *InstallPath); } else { FAssetToolsModule& AssetToolsModule = FModuleManager::Get().LoadModuleChecked("AssetTools"); TArray AssetPaths; AssetPaths.Add(FeaturePackPath); TArray ImportedObjects = AssetToolsModule.Get().ImportAssets(AssetPaths, InstallPath ); if( ImportedObjects.Num() == 0 ) { UE_LOG(LogFeaturePack, Warning, TEXT("No objects imported installing pack %s"), *InstallPath); } else { // Save any imported assets. TArray ToSave; for (auto ImportedObject : ImportedObjects) { ToSave.AddUnique(ImportedObject->GetOutermost()); } FEditorFileUtils::PromptForCheckoutAndSave( ToSave, /*bCheckDirty=*/ false, /*bPromptToSave=*/ false ); bResult = true; // Focus on a specific asset if we want to. if( GetFocusAssetName().IsEmpty() == false ) { UObject* FocusAsset = LoadObject(nullptr, *GetFocusAssetName()); if (FocusAsset) { FContentBrowserModule& ContentBrowserModule = FModuleManager::Get().LoadModuleChecked("ContentBrowser"); TArray SyncObjects; SyncObjects.Add(FocusAsset); ContentBrowserModule.Get().SyncBrowserToAssets(SyncObjects); } } } } return bResult; } FFeaturePackContentSource::~FFeaturePackContentSource() { } bool FFeaturePackContentSource::IsDataValid() const { if( bPackValid == false ) { return false; } // To Do maybe validate other data here return true; } FString FFeaturePackContentSource::GetFocusAssetName() const { return FocusAssetIdent; } FString FFeaturePackContentSource::GetSortKey() const { return SortKey; } void FFeaturePackContentSource::HandleActOnSearchText(TSharedPtr SearchEntry) { if (SearchEntry.IsValid()) { if (SearchEntry->bCategory == false) { UEnum* Enum = FindObjectChecked(ANY_PACKAGE, TEXT("EContentSourceCategory")); FString CurrentLanguage = FInternationalization::Get().GetCurrentCulture()->GetTwoLetterISOLanguageName(); FLocalizedText CurrentName = ChooseLocalizedText(LocalizedNames,CurrentLanguage); FString MyTitle = FText::Format( LOCTEXT("FeaturePackSearchResult", "{0} ({1})"), CurrentName.GetText(), Enum->GetDisplayNameText((int32)Category)).ToString(); if (SearchEntry->Title == MyTitle) { IMainFrameModule& MainFrameModule = FModuleManager::LoadModuleChecked(TEXT("MainFrame")); IAddContentDialogModule& AddContentDialogModule = FModuleManager::LoadModuleChecked("AddContentDialog"); AddContentDialogModule.ShowDialog(MainFrameModule.GetParentWindow().ToSharedRef()); } } } } void FFeaturePackContentSource::TryAddFeaturePackCategory(FString CategoryTitle, TArray< TSharedPtr >& OutSuggestions) { if (OutSuggestions.ContainsByPredicate([&CategoryTitle](TSharedPtr& InElement) { return ((InElement->Title == CategoryTitle) && (InElement->bCategory == true)); }) == false) { TSharedPtr FeaturePackCat = MakeShareable(new FSearchEntry()); FeaturePackCat->bCategory = true; FeaturePackCat->Title = CategoryTitle; OutSuggestions.Add(FeaturePackCat); } } void FFeaturePackContentSource::HandleSuperSearchTextChanged(const FString& InText, TArray< TSharedPtr >& OutSuggestions) { FString FeaturePackSearchCat = LOCTEXT("FeaturePackSearchCategory", "Feature Packs").ToString(); FString CurrentLanguage = FInternationalization::Get().GetCurrentCulture()->GetTwoLetterISOLanguageName(); FLocalizedText CurrentName = ChooseLocalizedText(LocalizedNames,CurrentLanguage); FLocalizedTextArray CurrentTagSet = ChooseLocalizedTextArray(LocalizedSearchTags,CurrentLanguage); FText AsText = FText::FromString(InText); TArray TagArray = CurrentTagSet.GetTags(); if (TagArray.Num() != 0) { UEnum* Enum = FindObjectChecked(ANY_PACKAGE, TEXT("EContentSourceCategory")); // Add a feature packs category for (int32 iTag = 0; iTag < TagArray.Num(); iTag++) { if (TagArray[iTag].EqualToCaseIgnored(AsText)) { // This will add the category if one doesnt exist TryAddFeaturePackCategory(FeaturePackSearchCat,OutSuggestions); TSharedPtr FeaturePackEntry = MakeShareable(new FSearchEntry()); FeaturePackEntry->Title = FText::Format( LOCTEXT("FeaturePackSearchResult", "{0} ({1})"), CurrentName.GetText(), Enum->GetDisplayNameText((int32)Category)).ToString(); FeaturePackEntry->bCategory = false; OutSuggestions.Add(FeaturePackEntry); return; } } } } FLocalizedText FFeaturePackContentSource::ChooseLocalizedText(TArray Choices, FString LanguageCode) { FLocalizedText Default; for (const FLocalizedText& Choice : Choices) { if (Choice.GetTwoLetterLanguage() == LanguageCode) { return Choice; break; } else if (Choice.GetTwoLetterLanguage() == TEXT("en")) { Default = Choice; } } return Default; } FLocalizedTextArray FFeaturePackContentSource::ChooseLocalizedTextArray(TArray Choices, FString LanguageCode) { FLocalizedTextArray Default; for (const FLocalizedTextArray& Choice : Choices) { if (Choice.GetTwoLetterLanguage() == LanguageCode) { return Choice; break; } else if (Choice.GetTwoLetterLanguage() == TEXT("en")) { Default = Choice; } } return Default; } #undef LOCTEXT_NAMESPACE