void UVaRestJsonObject::SetArrayField(const FString& FieldName, const TArray<UVaRestJsonValue*>& InArray) { if (!JsonObj.IsValid()) { return; } TArray< TSharedPtr<FJsonValue> > ValArray; // Process input array and COPY original values for (auto InVal : InArray) { TSharedPtr<FJsonValue> JsonVal = InVal->GetRootValue(); switch (InVal->GetType()) { case EVaJson::None: break; case EVaJson::Null: ValArray.Add(MakeShareable(new FJsonValueNull())); break; case EVaJson::String: ValArray.Add(MakeShareable(new FJsonValueString(JsonVal->AsString()))); break; case EVaJson::Number: ValArray.Add(MakeShareable(new FJsonValueNumber(JsonVal->AsNumber()))); break; case EVaJson::Boolean: ValArray.Add(MakeShareable(new FJsonValueBoolean(JsonVal->AsBool()))); break; case EVaJson::Array: ValArray.Add(MakeShareable(new FJsonValueArray(JsonVal->AsArray()))); break; case EVaJson::Object: ValArray.Add(MakeShareable(new FJsonValueObject(JsonVal->AsObject()))); break; default: break; } } JsonObj->SetArrayField(FieldName, ValArray); }
// @TODO LSwift: Perhaps replace FromBlob and ToBlob usage with hexadecimal notation instead bool FBuildPatchAppManifest::DeserializeFromJSON( const FString& JSONInput ) { bool bSuccess = true; TSharedPtr<FJsonObject> JSONManifestObject; TSharedRef<TJsonReader<TCHAR>> Reader = TJsonReaderFactory<TCHAR>::Create(JSONInput); // Clear current data DestroyData(); // Attempt to deserialize JSON if (!FJsonSerializer::Deserialize(Reader, JSONManifestObject) || !JSONManifestObject.IsValid()) { return false; } // Store a list of all data GUID for later use TSet<FGuid> AllDataGuids; // Get the values map TMap<FString, TSharedPtr<FJsonValue>>& JsonValueMap = JSONManifestObject->Values; // Manifest version did not always exist int32 ManifestFileVersionInt = 0; TSharedPtr<FJsonValue> JsonManifestFileVersion = JsonValueMap.FindRef(TEXT("ManifestFileVersion")); if (JsonManifestFileVersion.IsValid() && FromStringBlob(JsonManifestFileVersion->AsString(), ManifestFileVersionInt)) { Data->ManifestFileVersion = static_cast<EBuildPatchAppManifestVersion::Type>(ManifestFileVersionInt); } else { // Then we presume version just before we started outputting the version Data->ManifestFileVersion = static_cast<EBuildPatchAppManifestVersion::Type>(EBuildPatchAppManifestVersion::StartStoringVersion - 1); } // Get the app and version strings TSharedPtr< FJsonValue > JsonAppID = JsonValueMap.FindRef( TEXT("AppID") ); TSharedPtr< FJsonValue > JsonAppNameString = JsonValueMap.FindRef( TEXT("AppNameString") ); TSharedPtr< FJsonValue > JsonBuildVersionString = JsonValueMap.FindRef( TEXT("BuildVersionString") ); TSharedPtr< FJsonValue > JsonLaunchExe = JsonValueMap.FindRef( TEXT("LaunchExeString") ); TSharedPtr< FJsonValue > JsonLaunchCommand = JsonValueMap.FindRef( TEXT("LaunchCommand") ); TSharedPtr< FJsonValue > JsonPrereqName = JsonValueMap.FindRef( TEXT("PrereqName") ); TSharedPtr< FJsonValue > JsonPrereqPath = JsonValueMap.FindRef( TEXT("PrereqPath") ); TSharedPtr< FJsonValue > JsonPrereqArgs = JsonValueMap.FindRef( TEXT("PrereqArgs") ); bSuccess = bSuccess && JsonAppID.IsValid(); if( bSuccess ) { bSuccess = bSuccess && FromStringBlob( JsonAppID->AsString(), Data->AppID ); } bSuccess = bSuccess && JsonAppNameString.IsValid(); if( bSuccess ) { Data->AppName = JsonAppNameString->AsString(); } bSuccess = bSuccess && JsonBuildVersionString.IsValid(); if( bSuccess ) { Data->BuildVersion = JsonBuildVersionString->AsString(); } bSuccess = bSuccess && JsonLaunchExe.IsValid(); if( bSuccess ) { Data->LaunchExe = JsonLaunchExe->AsString(); } bSuccess = bSuccess && JsonLaunchCommand.IsValid(); if( bSuccess ) { Data->LaunchCommand = JsonLaunchCommand->AsString(); } // Get the prerequisites installer info. These are optional entries. Data->PrereqName = JsonPrereqName.IsValid() ? JsonPrereqName->AsString() : FString(); Data->PrereqPath = JsonPrereqPath.IsValid() ? JsonPrereqPath->AsString() : FString(); Data->PrereqArgs = JsonPrereqArgs.IsValid() ? JsonPrereqArgs->AsString() : FString(); // Get the FileManifestList TSharedPtr<FJsonValue> JsonFileManifestList = JsonValueMap.FindRef(TEXT("FileManifestList")); bSuccess = bSuccess && JsonFileManifestList.IsValid(); if( bSuccess ) { TArray<TSharedPtr<FJsonValue>> JsonFileManifestArray = JsonFileManifestList->AsArray(); for (auto JsonFileManifestIt = JsonFileManifestArray.CreateConstIterator(); JsonFileManifestIt && bSuccess; ++JsonFileManifestIt) { TSharedPtr<FJsonObject> JsonFileManifest = (*JsonFileManifestIt)->AsObject(); const int32 FileIndex = Data->FileManifestList.Add(FFileManifestData()); FFileManifestData& FileManifest = Data->FileManifestList[FileIndex]; FileManifest.Filename = JsonFileManifest->GetStringField(TEXT("Filename")); bSuccess = bSuccess && FString::ToBlob(JsonFileManifest->GetStringField(TEXT("FileHash")), FileManifest.FileHash.Hash, FSHA1::DigestSize); TArray<TSharedPtr<FJsonValue>> JsonChunkPartArray = JsonFileManifest->GetArrayField(TEXT("FileChunkParts")); for (auto JsonChunkPartIt = JsonChunkPartArray.CreateConstIterator(); JsonChunkPartIt && bSuccess; ++JsonChunkPartIt) { const int32 ChunkIndex = FileManifest.FileChunkParts.Add(FChunkPartData()); FChunkPartData& FileChunkPart = FileManifest.FileChunkParts[ChunkIndex]; TSharedPtr<FJsonObject> JsonChunkPart = (*JsonChunkPartIt)->AsObject(); bSuccess = bSuccess && FGuid::Parse(JsonChunkPart->GetStringField(TEXT("Guid")), FileChunkPart.Guid); bSuccess = bSuccess && FromStringBlob(JsonChunkPart->GetStringField(TEXT("Offset")), FileChunkPart.Offset); bSuccess = bSuccess && FromStringBlob(JsonChunkPart->GetStringField(TEXT("Size")), FileChunkPart.Size); AllDataGuids.Add(FileChunkPart.Guid); } FileManifest.bIsUnixExecutable = JsonFileManifest->HasField(TEXT("bIsUnixExecutable")) && JsonFileManifest->GetBoolField(TEXT("bIsUnixExecutable")); FileManifest.bIsReadOnly = JsonFileManifest->HasField(TEXT("bIsReadOnly")) && JsonFileManifest->GetBoolField(TEXT("bIsReadOnly")); FileManifest.bIsCompressed = JsonFileManifest->HasField(TEXT("bIsCompressed")) && JsonFileManifest->GetBoolField(TEXT("bIsCompressed")); FileManifest.SymlinkTarget = JsonFileManifest->HasField(TEXT("SymlinkTarget")) ? JsonFileManifest->GetStringField(TEXT("SymlinkTarget")) : TEXT(""); FileManifest.Init(); } } Data->FileManifestList.Sort(); for (auto& FileManifest : Data->FileManifestList) { FileManifestLookup.Add(FileManifest.Filename, &FileManifest); } // For each chunk setup it's info for (const auto& DataGuid : AllDataGuids) { int32 ChunkIndex = Data->ChunkList.Add(FChunkInfoData()); Data->ChunkList[ChunkIndex].Guid = DataGuid; } // Setup chunk info lookup for (auto& ChunkInfo : Data->ChunkList) { ChunkInfoLookup.Add(ChunkInfo.Guid, &ChunkInfo); } // Get the ChunkHashList bool bHasChunkHashList = false; TSharedPtr<FJsonValue> JsonChunkHashList = JsonValueMap.FindRef(TEXT("ChunkHashList")); bSuccess = bSuccess && JsonChunkHashList.IsValid(); if (bSuccess) { TSharedPtr<FJsonObject> JsonChunkHashListObj = JsonChunkHashList->AsObject(); for (auto ChunkHashIt = JsonChunkHashListObj->Values.CreateConstIterator(); ChunkHashIt && bSuccess; ++ChunkHashIt) { FGuid ChunkGuid; uint64 ChunkHash = 0; bSuccess = bSuccess && FGuid::Parse(ChunkHashIt.Key(), ChunkGuid); bSuccess = bSuccess && FromStringBlob(ChunkHashIt.Value()->AsString(), ChunkHash); if (bSuccess && ChunkInfoLookup.Contains(ChunkGuid)) { FChunkInfoData* ChunkInfoData = ChunkInfoLookup[ChunkGuid]; ChunkInfoData->Hash = ChunkHash; bHasChunkHashList = true; } } } // Get the DataGroupList TSharedPtr<FJsonValue> JsonDataGroupList = JsonValueMap.FindRef(TEXT("DataGroupList")); if (JsonDataGroupList.IsValid()) { TSharedPtr<FJsonObject> JsonDataGroupListObj = JsonDataGroupList->AsObject(); for (auto DataGroupIt = JsonDataGroupListObj->Values.CreateConstIterator(); DataGroupIt && bSuccess; ++DataGroupIt) { FGuid DataGuid; uint8 DataGroup = INDEX_NONE; // If the list exists, we must be able to parse it ok otherwise error bSuccess = bSuccess && FGuid::Parse(DataGroupIt.Key(), DataGuid); bSuccess = bSuccess && FromStringBlob(DataGroupIt.Value()->AsString(), DataGroup); if (bSuccess && ChunkInfoLookup.Contains(DataGuid)) { FChunkInfoData* ChunkInfoData = ChunkInfoLookup[DataGuid]; ChunkInfoData->GroupNumber = DataGroup; } } } else if (bSuccess) { // If the list did not exist in the manifest then the grouping is the deprecated crc functionality, as long // as there are no previous parsing errors we can build the group list from the Guids. for (auto& ChunkInfo : Data->ChunkList) { ChunkInfo.GroupNumber = FCrc::MemCrc_DEPRECATED(&ChunkInfo.Guid, sizeof(FGuid)) % 100; } } // Get the ChunkFilesizeList bool bHasChunkFilesizeList = false; TSharedPtr< FJsonValue > JsonChunkFilesizeList = JsonValueMap.FindRef(TEXT("ChunkFilesizeList")); if (JsonChunkFilesizeList.IsValid()) { TSharedPtr< FJsonObject > JsonChunkFilesizeListObj = JsonChunkFilesizeList->AsObject(); for (auto ChunkFilesizeIt = JsonChunkFilesizeListObj->Values.CreateConstIterator(); ChunkFilesizeIt; ++ChunkFilesizeIt) { FGuid ChunkGuid; int64 ChunkSize = 0; if (FGuid::Parse(ChunkFilesizeIt.Key(), ChunkGuid)) { FromStringBlob(ChunkFilesizeIt.Value()->AsString(), ChunkSize); if (ChunkInfoLookup.Contains(ChunkGuid)) { FChunkInfoData* ChunkInfoData = ChunkInfoLookup[ChunkGuid]; ChunkInfoData->FileSize = ChunkSize; bHasChunkFilesizeList = true; } } } } if (bHasChunkFilesizeList == false) { // Missing chunk list, version before we saved them compressed.. Assume chunk size for (FChunkInfoData& ChunkInfo : Data->ChunkList) { ChunkInfo.FileSize = FBuildPatchData::ChunkDataSize; } } // Get the bIsFileData value. The variable will exist in versions of StoresIfChunkOrFileData or later, otherwise the previous method is to check // if ChunkHashList is empty. TSharedPtr<FJsonValue> JsonIsFileData = JsonValueMap.FindRef(TEXT("bIsFileData")); if (JsonIsFileData.IsValid() && JsonIsFileData->Type == EJson::Boolean) { Data->bIsFileData = JsonIsFileData->AsBool(); } else { Data->bIsFileData = !bHasChunkHashList; } // Get the custom fields. This is optional, and should not fail if it does not exist TSharedPtr< FJsonValue > JsonCustomFields = JsonValueMap.FindRef( TEXT( "CustomFields" ) ); if( JsonCustomFields.IsValid() ) { TSharedPtr< FJsonObject > JsonCustomFieldsObj = JsonCustomFields->AsObject(); for( auto CustomFieldIt = JsonCustomFieldsObj->Values.CreateConstIterator(); CustomFieldIt && bSuccess; ++CustomFieldIt ) { Data->CustomFields.Add(FCustomFieldData(CustomFieldIt.Key(), CustomFieldIt.Value()->AsString())); } } CustomFieldLookup.Empty(Data->CustomFields.Num()); for (auto& CustomField : Data->CustomFields) { CustomFieldLookup.Add(CustomField.Key, &CustomField); } // If this is file data, fill out the guid to filename lookup, and chunk file size if (Data->bIsFileData) { for (auto& FileManifest : Data->FileManifestList) { if (FileManifest.FileChunkParts.Num() == 1) { FGuid& Guid = FileManifest.FileChunkParts[0].Guid; FileNameLookup.Add(Guid, &FileManifest.Filename); if (ChunkInfoLookup.Contains(Guid)) { FChunkInfoData* ChunkInfoData = ChunkInfoLookup[Guid]; ChunkInfoData->FileSize = FileManifest.GetFileSize(); } } else { bSuccess = false; } } } // Calculate build size TotalBuildSize = 0; TotalDownloadSize = 0; if (bSuccess) { for (auto& FileManifest : Data->FileManifestList) { TotalBuildSize += FileManifest.GetFileSize(); } for (auto& Chunk : Data->ChunkList) { TotalDownloadSize += Chunk.FileSize; } } // Mark as should be re-saved, client that stores manifests should start using binary bNeedsResaving = true; // Make sure we don't have any half loaded data if( !bSuccess ) { DestroyData(); } return bSuccess; }