Files
UnrealEngineUWP/Engine/Source/Programs/UnrealPackageTool/UnrealPackageTool.cpp
Devin Doucette 708f578522 UnrealPackageTool: Added a new tool to quickly scan package headers
The initial version only reports the presence of licensee versions as an error, to aid in tracking down invalid engine packages. The goal is to migrate functionality from the PkgInfo commandlet into this tool to provide low-overhead package scanning.

#preflight none
#rb Aurel.Cordonnier
#rnx

[CL 19303248 by Devin Doucette in ue5-main branch]
2022-03-08 10:58:17 -05:00

170 lines
4.2 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
#include "Async/ParallelFor.h"
#include "Containers/Array.h"
#include "Containers/DepletableMpscQueue.h"
#include "Containers/StringView.h"
#include "Containers/UnrealString.h"
#include "HAL/PlatformFile.h"
#include "Logging/LogMacros.h"
#include "Misc/CommandLine.h"
#include "Misc/PackagePath.h"
#include "Misc/Parse.h"
#include "Modules/ModuleManager.h"
#include "RequiredProgramMainCPPInclude.h"
#include "String/ParseTokens.h"
#include "UObject/PackageFileSummary.h"
DEFINE_LOG_CATEGORY_STATIC(LogUnrealPackageTool, Log, All);
IMPLEMENT_APPLICATION(UnrealPackageTool, "UnrealPackageTool");
namespace UE::PackageTool
{
struct FParams
{
TArray<FString> PackageRoots;
};
bool ParseParams(const TCHAR* CommandLine, FParams& OutParams)
{
if (!FParse::Param(CommandLine, TEXT("LicenseeVersionIsError")))
{
UE_LOG(LogUnrealPackageTool, Error,
TEXT("Expected -LicenseeVersionIsError which is the only supported mode right now."));
return false;
}
OutParams.PackageRoots.Reset();
for (FString Token; FParse::Token(CommandLine, Token, /*UseEscape*/ false);)
{
Token.ReplaceInline(TEXT("\""), TEXT(""));
const auto GetSwitchValues = [Token = FStringView(Token)](FStringView Match, TArray<FString>& OutValues)
{
if (Token.StartsWith(Match))
{
UE::String::ParseTokens(Token.RightChop(Match.Len()), TEXT('+'), [&OutValues](FStringView Value)
{
OutValues.Emplace(Value);
});
}
};
GetSwitchValues(TEXT("-AllPackagesIn="), OutParams.PackageRoots);
}
if (OutParams.PackageRoots.IsEmpty())
{
UE_LOG(LogUnrealPackageTool, Error,
TEXT("Expected at least one package root to be set by -AllPackagesIn=<Root1>+<Root2>."));
return false;
}
for (const FString& PackageRoot : OutParams.PackageRoots)
{
if (PackageRoot.IsEmpty() || !IFileManager::Get().DirectoryExists(*PackageRoot))
{
UE_LOG(LogUnrealPackageTool, Error,
TEXT("The package root '%s' must point to a directory."), *PackageRoot);
return false;
}
}
return true;
}
class FPackageRootVisitor final : public IPlatformFile::FDirectoryVisitor
{
public:
FPackageRootVisitor()
: IPlatformFile::FDirectoryVisitor(EDirectoryVisitorFlags::ThreadSafe)
{
}
bool Visit(const TCHAR* Path, bool bIsDirectory) final
{
if (!bIsDirectory)
{
EPackageExtension Extension = FPackagePath::ParseExtension(Path);
if (Extension == EPackageExtension::Asset || Extension == EPackageExtension::Map)
{
PackagePaths.Enqueue(Path);
}
}
return true;
}
TDepletableMpscQueue<FString> PackagePaths;
};
void ScanPackage(const FParams& Params, const TCHAR* Path)
{
if (TUniquePtr<FArchive> Ar{IFileManager::Get().CreateFileReader(Path, FILEREAD_Silent)})
{
FPackageFileSummary Summary;
*Ar << Summary;
if (Ar->Close())
{
if (Summary.CompatibleWithEngineVersion.IsLicenseeVersion())
{
UE_LOG(LogUnrealPackageTool, Error, TEXT("Package has a licensee version: %s"), Path);
}
}
else
{
UE_LOG(LogUnrealPackageTool, Warning, TEXT("Failed to read package file summary: %s"), Path);
}
}
else
{
UE_LOG(LogUnrealPackageTool, Warning, TEXT("Failed to open package: %s"), Path);
}
}
void Main(const FParams& Params)
{
FPackageRootVisitor Visitor;
ParallelFor(TEXT("ScanPackageRoots.PF"), Params.PackageRoots.Num(), 1, [&Params, &Visitor](int32 Index)
{
IFileManager::Get().IterateDirectoryRecursively(*Params.PackageRoots[Index], Visitor);
}, EParallelForFlags::Unbalanced);
TArray<FString> PackagePaths;
Visitor.PackagePaths.Deplete([&PackagePaths](FString PackagePath)
{
PackagePaths.Emplace(MoveTemp(PackagePath));
});
ParallelFor(TEXT("ScanPackage.PF"), PackagePaths.Num(), 1, [&Params, &PackagePaths](int32 Index)
{
ScanPackage(Params, *PackagePaths[Index]);
}, EParallelForFlags::Unbalanced);
}
} // UE::PackageTool
INT32_MAIN_INT32_ARGC_TCHAR_ARGV()
{
UE::PackageTool::FParams Params;
int32 Ret = GEngineLoop.PreInit(ArgC, ArgV);
if (Ret == 0)
{
Ret = !UE::PackageTool::ParseParams(FCommandLine::Get(), Params);
}
if (Ret == 0)
{
UE::PackageTool::Main(Params);
}
RequestEngineExit(TEXT("Exiting"));
FEngineLoop::AppPreExit();
FModuleManager::Get().UnloadModulesAtShutdown();
FEngineLoop::AppExit();
return Ret;
}