33#include " CookedAssetWriter.h"
44#include " IoStorePackageMap.h"
55#include " ZenTools.h"
6+ #include " Dom/JsonObject.h"
67#include " HAL/FileManager.h"
78#include " Misc/Paths.h"
89#include " Serialization/LargeMemoryWriter.h"
910#include " Serialization/MemoryWriter.h"
1011#include " UObject/Class.h"
1112#include " UObject/Package.h"
1213#include " UObject/SoftObjectPath.h"
14+ #include " Misc/FileHelper.h"
15+ #include " Serialization/JsonSerializer.h"
1316
1417FAssetSerializationWriter::FAssetSerializationWriter ( FArchive& Ar, FAssetSerializationContext* Context ) : FArchiveProxy( Ar ), Context( Context )
1518{
@@ -59,25 +62,101 @@ FCookedAssetWriter::FCookedAssetWriter(const TSharedPtr<FIoStorePackageMap>& InP
5962{
6063}
6164
62- void FCookedAssetWriter::WritePackagesFromContainer ( const FIoContainerId& ContainerId )
65+ void FCookedAssetWriter::WritePackagesFromContainer ( const TSharedPtr<FIoStoreReader>& Reader )
6366{
67+ const FIoContainerId ContainerId = Reader->GetContainerId ();
6468 UE_LOG ( LogIoStoreTools, Display, TEXT (" Writing asset files for Container %lld" ), ContainerId.Value () );
6569
6670 FPackageContainerMetadata ContainerMetadata;
6771 if ( PackageMap->FindPackageContainerMetadata ( ContainerId, ContainerMetadata ) )
6872 {
6973 for ( const FPackageId& PackageId : ContainerMetadata.PackagesInContainer )
7074 {
71- WriteSinglePackage ( PackageId );
75+ WriteSinglePackage ( PackageId, false , Reader );
7276 }
7377 for ( const FPackageId& OptionalPackageId : ContainerMetadata.OptionalPackagesInContainer )
7478 {
75- WriteSinglePackage ( OptionalPackageId );
79+ WriteSinglePackage ( OptionalPackageId, true , Reader );
7680 }
7781 }
7882}
7983
80- void FCookedAssetWriter::WriteSinglePackage ( FPackageId PackageId )
84+ void FCookedAssetWriter::WriteGlobalScriptObjects (const TSharedPtr<FIoStoreReader>& Reader) const
85+ {
86+ TIoStatusOr<FIoBuffer> ScriptObjectsBuffer = Reader->Read (CreateIoChunkId (0 , 0 , EIoChunkType::ScriptObjects), FIoReadOptions ());
87+
88+ if ( ScriptObjectsBuffer.IsOk () )
89+ {
90+ const FString ScriptObjectsFilename = FPaths::Combine ( RootOutputDir, TEXT (" ScriptObjects.bin" ) );
91+ FFileHelper::SaveArrayToFile ( TArrayView<const uint8>( ScriptObjectsBuffer.ValueOrDie ().Data (), ScriptObjectsBuffer.ValueOrDie ().DataSize () ), *ScriptObjectsFilename );
92+
93+ UE_LOG ( LogIoStoreTools, Display, TEXT (" Written ScriptObjects chunk to '%s'" ), *ScriptObjectsFilename );
94+ }
95+ }
96+
97+ void FCookedAssetWriter::WritePackageStoreManifest () const
98+ {
99+ const FString PackageStoreFilename = RootOutputDir / TEXT (" PackageStoreManifest.json" );
100+ IFileManager::Get ().MakeDirectory ( *FPaths::GetPath ( PackageStoreFilename ), true );
101+
102+ const TSharedPtr<FJsonObject> RootObject = MakeShared<FJsonObject>();
103+
104+ TStringBuilder<64 > ChunkIdStringBuilder;
105+ auto ChunkIdToString = [&ChunkIdStringBuilder](const FIoChunkId& ChunkId)
106+ {
107+ ChunkIdStringBuilder.Reset ();
108+ ChunkIdStringBuilder << ChunkId;
109+ return *ChunkIdStringBuilder;
110+ };
111+
112+ TArray<TSharedPtr<FJsonValue>> FilesArray;
113+ for ( const TPair<FIoChunkId, FString>& FilePair : ChunkIdToSavedFileMap )
114+ {
115+ const TSharedPtr<FJsonObject> FileObject = MakeShared<FJsonObject>();
116+ FileObject->SetStringField ( TEXT (" Path" ), FilePair.Value );
117+ FileObject->SetStringField ( TEXT (" ChunkId" ), ChunkIdToString ( FilePair.Key ) );
118+
119+ FilesArray.Add ( MakeShared<FJsonValueObject>( FileObject ) );
120+ }
121+ RootObject->SetArrayField ( TEXT (" Files" ), FilesArray );
122+
123+ TArray<TSharedPtr<FJsonValue>> PackagesArray;
124+ for ( const TPair<FName, FSavedPackageInfo>& SavedPackageInfo : SavedPackageMap )
125+ {
126+ const TSharedPtr<FJsonObject> PackageObject = MakeShared<FJsonObject>();
127+ PackageObject->SetStringField ( TEXT (" Name" ), SavedPackageInfo.Key .ToString () );
128+
129+ if ( !SavedPackageInfo.Value .ExportBundleChunks .IsEmpty () )
130+ {
131+ TArray<TSharedPtr<FJsonValue>> ExportBundleChunkIdsArray;
132+ for ( const FIoChunkId& ExportBundleChunkId : SavedPackageInfo.Value .ExportBundleChunks )
133+ {
134+ ExportBundleChunkIdsArray.Add ( MakeShared<FJsonValueString>( ChunkIdToString ( ExportBundleChunkId ) ) );
135+ }
136+ PackageObject->SetArrayField ( TEXT (" ExportBundleChunkIds" ), ExportBundleChunkIdsArray );
137+ }
138+
139+ if ( !SavedPackageInfo.Value .BulkDataChunks .IsEmpty () )
140+ {
141+ TArray<TSharedPtr<FJsonValue>> BulkDataChunkIdsArray;
142+ for ( const FIoChunkId& BulkDataChunkId : SavedPackageInfo.Value .BulkDataChunks )
143+ {
144+ BulkDataChunkIdsArray.Add ( MakeShared<FJsonValueString>( ChunkIdToString ( BulkDataChunkId ) ) );
145+ }
146+ PackageObject->SetArrayField ( TEXT (" BulkDataChunkIds" ), BulkDataChunkIdsArray );
147+ }
148+ PackagesArray.Add ( MakeShared<FJsonValueObject>( PackageObject ) );
149+ }
150+ RootObject->SetArrayField ( TEXT (" Packages" ), PackagesArray );
151+
152+ FString ResultJsonString;
153+ FJsonSerializer::Serialize ( RootObject.ToSharedRef (), TJsonWriterFactory<>::Create ( &ResultJsonString ) );
154+
155+ check ( FFileHelper::SaveStringToFile ( ResultJsonString, *PackageStoreFilename ) );
156+ UE_LOG ( LogIoStoreTools, Display, TEXT (" Written PackageStore Manifest to '%s'" ), *PackageStoreFilename );
157+ }
158+
159+ void FCookedAssetWriter::WriteSinglePackage ( FPackageId PackageId, bool bIsOptionalSegmentPackage, const TSharedPtr<FIoStoreReader>& Reader )
81160{
82161 FPackageMapExportBundleEntry ExportBundleEntry;
83162 checkf ( PackageMap->FindExportBundleData ( PackageId, ExportBundleEntry ), TEXT (" Failed to find export bundle entry for PackageId %lld" ), PackageId.ValueForDebugging () );
@@ -93,13 +172,24 @@ void FCookedAssetWriter::WriteSinglePackage( FPackageId PackageId )
93172 SerializationContext.PackageId = PackageId;
94173 SerializationContext.PackageHeaderFilename = PackageFilename;
95174 SerializationContext.BundleData = &ExportBundleEntry;
175+ SerializationContext.IoStoreReader = Reader.Get ();
176+
177+ FSavedPackageInfo& SavedPackageInfo = SavedPackageMap.FindOrAdd ( SerializationContext.BundleData ->PackageName );
178+ SavedPackageInfo.ExportBundleChunks .Add ( SerializationContext.BundleData ->PackageChunkId );
96179
97180 // Populate package summary, and also process imports and exports
98181 ProcessPackageSummaryAndNamesAndExportsAndImports ( SerializationContext );
99182
100183 // Serialize exports into the separate file (event driven loader expects that)
101184 {
102- const FString ExportsFilename = FPaths::ChangeExtension ( SerializationContext.PackageHeaderFilename , LexToString ( EPackageExtension::Exports ) );
185+ FString ExtensionString = LexToString ( EPackageExtension::Exports );
186+
187+ // Optional segment packages have .o prefix before their extensions, e.g.
188+ if ( bIsOptionalSegmentPackage )
189+ {
190+ ExtensionString.InsertAt ( 0 , TEXT (" .o" ) );
191+ }
192+ const FString ExportsFilename = FPaths::ChangeExtension ( SerializationContext.PackageHeaderFilename , ExtensionString );
103193
104194 const TUniquePtr<FArchive> ExportsArchive ( IFileManager::Get ().CreateFileWriter ( *ExportsFilename, FILEWRITE_EvenIfReadOnly ) );
105195 checkf ( ExportsArchive.IsValid (), TEXT (" Failed to load exports file '%s'" ), *ExportsFilename );
@@ -112,7 +202,17 @@ void FCookedAssetWriter::WriteSinglePackage( FPackageId PackageId )
112202 // Serialize package summary and other necessary data into the main asset header file
113203 {
114204 const EPackageExtension HeaderExtension = ( SerializationContext.Summary .GetPackageFlags () & PKG_ContainsMap ) != 0 ? EPackageExtension::Map : EPackageExtension::Asset;
115- const FString HeaderFilename = FPaths::ChangeExtension ( SerializationContext.PackageHeaderFilename , LexToString ( HeaderExtension ) );
205+ FString ExtensionString = LexToString ( HeaderExtension );
206+
207+ // Optional segment packages have .o prefix before their extensions, e.g.
208+ if ( bIsOptionalSegmentPackage )
209+ {
210+ ExtensionString.InsertAt ( 0 , TEXT (" .o" ) );
211+ }
212+ const FString HeaderFilename = FPaths::ChangeExtension ( SerializationContext.PackageHeaderFilename , ExtensionString );
213+
214+ FString RelativeFilename = FPaths::SetExtension ( ExportBundleEntry.PackageFilename , ExtensionString );
215+ ChunkIdToSavedFileMap.Add ( SerializationContext.BundleData ->PackageChunkId , RelativeFilename );
116216
117217 const TUniquePtr<FArchive> HeaderArchive ( IFileManager::Get ().CreateFileWriter ( *HeaderFilename, FILEWRITE_EvenIfReadOnly ) );
118218 checkf ( HeaderArchive.IsValid (), TEXT (" Failed to open header file '%s'" ), *HeaderFilename );
@@ -122,6 +222,9 @@ void FCookedAssetWriter::WriteSinglePackage( FPackageId PackageId )
122222 HeaderArchive->Flush ();
123223 }
124224
225+ // Write bulk data
226+ WriteBulkData ( SerializationContext );
227+
125228 // Notify the user that we have finished writing the asset
126229 UE_LOG ( LogIoStoreTools, Display, TEXT (" Serialized Package '%s' to '%s'" ), *SerializationContext.BundleData ->PackageName .ToString (), *SerializationContext.PackageHeaderFilename );
127230 NumPackagesWritten++;
@@ -584,8 +687,8 @@ FPackageIndex FCookedAssetWriter::CreateObjectExport( const FPackageMapExportEnt
584687
585688 ObjectExport.ObjectFlags = ExportData.ObjectFlags ;
586689
587- ObjectExport.SerialSize = ExportData. CookedSerialData -> Num () ;
588- ObjectExport.SerialOffset = - 1 ; // Not resolved yet
690+ ObjectExport.SerialSize = INDEX_NONE ;
691+ ObjectExport.SerialOffset = INDEX_NONE;
589692
590693 ObjectExport.bForcedExport = false ; // not serialized
591694 ObjectExport.bNotForClient = EnumHasAnyFlags ( ExportData.FilterFlags , EExportFilterFlags::NotForClient );
@@ -868,20 +971,51 @@ void FCookedAssetWriter::WritePackageHeader(FArchive& Ar, FAssetSerializationCon
868971
869972void FCookedAssetWriter::WritePackageExports (FArchive& Ar, FAssetSerializationContext& Context)
870973{
974+ // Open the package bundle chunk to read exports
975+ TIoStatusOr<FIoBuffer> ChunkBuffer = Context.IoStoreReader ->Read ( Context.BundleData ->PackageChunkId , FIoReadOptions () );
976+ check ( ChunkBuffer.IsOk () );
977+ const uint8* ChunkDataStart = ChunkBuffer.ValueOrDie ().Data ();
978+ const uint8* ChunkDataEnd = ChunkDataStart + ChunkBuffer.ValueOrDie ().DataSize ();
979+
871980 // Write export blobs
872981 for ( int32 i = 0 ; i < Context.ExportMap .Num (); i++ )
873982 {
874- TArray<uint8>& SerialData = * Context.BundleData ->ExportMap [ i ]. CookedSerialData ;
983+ const FPackageMapExportEntry& OriginalExport = Context.BundleData ->ExportMap [ i ];
875984 FObjectExport& Export = Context.ExportMap [ i ];
876985
877986 Export.SerialOffset = Ar.Tell ();
878- Export.SerialSize = SerialData. Num () ;
987+ Export.SerialSize = OriginalExport. SerialDataSize ;
879988
880- Ar.Serialize ( SerialData.GetData (), SerialData.Num () );
989+ const uint8* SerialDataStart = ChunkDataStart + OriginalExport.SerialDataOffset ;
990+ check ( SerialDataStart <= ChunkDataEnd );
991+ Ar.Serialize ( const_cast <uint8*>( SerialDataStart ), OriginalExport.SerialDataSize );
881992 }
882993 Context.Summary .BulkDataStartOffset = Ar.Tell ();
883994
884995 // Exports end with the package file tag
885996 uint32 FooterData = PACKAGE_FILE_TAG;
886997 Ar << FooterData;
887998}
999+
1000+ void FCookedAssetWriter::WriteBulkData ( const FAssetSerializationContext& Context )
1001+ {
1002+ FSavedPackageInfo& SavedPackageInfo = SavedPackageMap.FindOrAdd ( Context.BundleData ->PackageName );
1003+
1004+ for ( const FIoChunkId& BulkDataChunkId : Context.BundleData ->BulkDataChunkIds )
1005+ {
1006+ TIoStatusOr<FIoBuffer> BulkDataBuffer = Context.IoStoreReader ->Read ( BulkDataChunkId, FIoReadOptions () );
1007+ check ( BulkDataBuffer.IsOk () );
1008+
1009+ TIoStatusOr<FIoStoreTocChunkInfo> ChunkInfo = Context.IoStoreReader ->GetChunkInfo ( BulkDataChunkId );
1010+ check ( ChunkInfo.IsOk () );
1011+
1012+ FString RelativeFilename = ChunkInfo.ValueOrDie ().FileName ;
1013+ RelativeFilename.RemoveFromStart ( TEXT (" ../../../" ) );
1014+
1015+ const FString ResultFilename = FPaths::Combine ( RootOutputDir, RelativeFilename );
1016+ FFileHelper::SaveArrayToFile ( TArrayView<const uint8>( BulkDataBuffer.ValueOrDie ().Data (), BulkDataBuffer.ValueOrDie ().DataSize () ), *ResultFilename );
1017+
1018+ ChunkIdToSavedFileMap.Add ( BulkDataChunkId, RelativeFilename );
1019+ SavedPackageInfo.BulkDataChunks .Add ( BulkDataChunkId );
1020+ }
1021+ }
0 commit comments