Hi [mention removed],
I have not been able to find a build-in tool that takes you the size of the map and all of its dependencies, but you can create your own tool for it. The AssetRegistry is the class that you are looking into as it allows you to extract all the dependencies and all the assets references directly by the map. You can build out the full dependency graph of the opened map. Now that you already have the csv file of the final .ucas you can parse the file and gather the size of the packages that are related with the map and sum them all together.
With the following code you can get the dependencies of the package:
static void GatherDependenciesRecursive(const FName& RootPackage, TSet<FName>& OutPackages)
{
IAssetRegistry& AssetRegistry =
FModuleManager::LoadModuleChecked<FAssetRegistryModule>("AssetRegistry").Get();
TArray<FName> Stack;
Stack.Add(RootPackage);
while (!Stack.IsEmpty())
{
FName Current = Stack.Pop();
if (OutPackages.Contains(Current))
continue;
OutPackages.Add(Current);
TArray<FName> Dependencies;
AssetRegistry.GetDependencies(
Current,
Dependencies,
UE::AssetRegistry::EDependencyCategory::Package);
for (const FName& Dep : Dependencies)
{
if (!OutPackages.Contains(Dep))
Stack.Add(Dep);
}
}
}
The main idea would be that you tell this function to use the UPackage of the level you want to inspect. It will go through the csv file and group all the packages that are referenced by the map and gather all the sizes. With the following function, by passing the full path of the csv file you should get the amount of size references by the map and the assets, and you can keep expanding this function for any needed functionality as you have all the references of the map. Of course the functions need to be run in an editor environment.
static FString TrimCsvField(const FString& In)
{
FString Out = In;
Out = Out.TrimStartAndEnd();
if (Out.StartsWith(TEXT("\"")) && Out.EndsWith(TEXT("\"")) && Out.Len() >= 2)
{
Out = Out.Mid(1, Out.Len() - 2);
}
return Out;
}
bool UMyOwnBlueprintFunctionLibrary::EstimateCookedMapSizeFromCsv(const FString& CsvFilePath)
{
if (!GEditor)
return false;
UWorld* EditorWorld = GEditor->GetEditorWorldContext().World();
if (!EditorWorld)
return false;
UPackage* MapPackage = EditorWorld->GetOutermost();
if (!MapPackage)
return false;
const FName MapPackageName = MapPackage->GetFName();
TSet<FName> AllPackages;
GatherDependenciesRecursive(MapPackageName, AllPackages);
UE_LOG(LogTemp, Log, TEXT("Dependent packages: %d"), AllPackages.Num());
if (AllPackages.Num() == 0)
{
UE_LOG(LogTemp, Warning, TEXT("No dependencies found for map."));
return false;
}
TSet<FString> RelevantRelPaths;
for (const FName& PkgName : AllPackages)
{
const FString LongName = PkgName.ToString();
FString RelPath;
if (PackageNameToContentRelative(LongName, RelPath))
{
RelevantRelPaths.Add(RelPath);
}
}
if (RelevantRelPaths.Num() == 0)
return false;
TArray<FString> Lines;
if (!FFileHelper::LoadFileToStringArray(Lines, *CsvFilePath) || Lines.Num() < 2)
UE_LOG(LogTemp, Error, TEXT("Failed to read CSV: %s"), *CsvFilePath);
TArray<FString> HeaderFields;
Lines[0].ParseIntoArray(HeaderFields, TEXT(","), true);
int32 FilenameCol = -1;
int32 SizeCol = -1;
for (int32 i = 0; i < HeaderFields.Num(); ++i)
{
const FString Header = HeaderFields[i].TrimStartAndEnd();
if (Header.Equals(TEXT("Filename"), ESearchCase::IgnoreCase))
{
FilenameCol = i;
}
else if (Header.Equals(TEXT("CompressedSize"), ESearchCase::IgnoreCase) ||
Header.Equals(TEXT("Size"), ESearchCase::IgnoreCase) ||
Header.Equals(TEXT("FileSize"), ESearchCase::IgnoreCase))
{
if (SizeCol == -1)
{
SizeCol = i;
}
}
}
if (FilenameCol == -1 || SizeCol == -1)
{
UE_LOG(LogTemp, Error,
TEXT("Could not find 'Filename' and size column in CSV header. (%s)"),
*CsvFilePath);
return false;
}
int64 TotalBytes = 0;
for (int32 LineId = 1; LineId < Lines.Num(); ++LineId)
{
const FString& Line = Lines[LineId];
if (Line.IsEmpty())
{
continue;
}
TArray<FString> Fields;
Line.ParseIntoArray(Fields, TEXT(","), true);
if (Fields.Num() <= FMath::Max(FilenameCol, SizeCol))
{
continue;
}
FString FilenameField = TrimCsvField(Fields[FilenameCol]);
FString SizeField = TrimCsvField(Fields[SizeCol]);
if (FilenameField.IsEmpty() || SizeField.IsEmpty())
{
continue;
}
int64 ThisSize = 0;
{
TCHAR* End = nullptr;
const int64 Parsed = FCString::Strtoi64(*SizeField, &End, 10);
if (End == *SizeField)
{
continue;
}
ThisSize = Parsed;
}
int32 ContentIdx = FilenameField.Find(TEXT("Content/"), ESearchCase::IgnoreCase, ESearchDir::FromStart);
if (ContentIdx == INDEX_NONE)
{
continue;
}
FString RelWithExt = FilenameField.Mid(ContentIdx);
FString RelBase = FPaths::GetPath(RelWithExt) + TEXT("/") + FPaths::GetBaseFilename(RelWithExt);
if (RelevantRelPaths.Contains(RelBase))
{
TotalBytes += ThisSize;
}
}
UE_LOG(LogTemp, Warning,
TEXT("Map '%s' cooked size (from CSV) = %lld bytes (%.2f MB)"),
*MapPackageName.ToString(),
TotalBytes,
(float)TotalBytes / (1024.f * 1024.f));
return true;
}
Let me know if it did the job.
Best,
Joan
[Attachment Removed]