// Copyright Epic Games, Inc. All Rights Reserved. #include "print_cmd.h" #include #include #include #include #include #include #include using namespace std::literals; namespace zen { static void PrintCbObject(CbObject Object, bool AddTypeComment) { ExtendableStringBuilder<1024> ObjStr; CompactBinaryToJson(Object, ObjStr, AddTypeComment); ForEachStrTok(ObjStr.ToView(), '\n', [](std::string_view Row) { ZEN_CONSOLE("{}", Row); return true; }); } static void PrintCompactBinary(IoBuffer Data, bool AddTypeComment) { ExtendableStringBuilder<1024> StreamString; CompactBinaryToJson(Data.GetView(), StreamString, AddTypeComment); ForEachStrTok(StreamString.ToView(), '\n', [](std::string_view Row) { ZEN_CONSOLE("{}", Row); return true; }); } static CbValidateError MakeFilteredResult(CbValidateError Result) { CbValidateError FilteredResult = Result; EnumRemoveFlags(FilteredResult, CbValidateError::InvalidString | CbValidateError::InvalidInteger | CbValidateError::InvalidFloat | CbValidateError::NonUniformObject | CbValidateError::NonUniformArray | CbValidateError::Padding); return FilteredResult; } PrintCommand::PrintCommand() { m_Options.add_options()("h,help", "Print help"); m_Options.add_option("", "s", "source", "Object payload file (use '-' to read from STDIN)", cxxopts::value(m_Filename), ""); m_Options.add_option("", "", "show-type-info", "Add type info annotation to compact binary objects", cxxopts::value(m_ShowCbObjectTypeInfo), ""); m_Options.parse_positional({"source"}); } PrintCommand::~PrintCommand() = default; void PrintCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** argv) { ZEN_UNUSED(GlobalOptions); if (!ParseOptions(argc, argv)) { return; } // Validate arguments if (m_Filename.empty()) throw OptionParseException("'--source' is required", m_Options.help()); FileContents Fc; if (m_Filename == "-") { Fc = ReadStdIn(); } else { MakeSafeAbsolutePathInPlace(m_Filename); Fc = ReadFile(m_Filename); } if (Fc.ErrorCode) { throw std::runtime_error(fmt::format("Failed to read file '{}': {}", m_Filename, Fc.ErrorCode.message())); } IoBuffer Data = Fc.Flatten(); IoHash RawHash; uint64_t RawSize; uint64_t CompressedSize; if (CompressedBuffer::ValidateCompressedHeader(Data, RawHash, RawSize, &CompressedSize)) { if (CompressedSize != Data.GetSize()) { ZEN_CONSOLE_WARN( "Compressed binary header total compressed size mismatch. Payload is {} bytes and header says compressed payload is {} " "bytes", Data.GetSize(), CompressedSize); } ZEN_CONSOLE("Compressed binary: size {}, raw size {}, hash: {}", Data.GetSize(), RawSize, RawHash); } else if (IsPackageMessage(Data)) { CbPackage Package = ParsePackageMessage(Data); CbObject Object = Package.GetObject(); std::span Attachments = Package.GetAttachments(); ZEN_CONSOLE("Package - {} attachments, object hash {}", Package.GetAttachments().size(), Package.GetObjectHash()); ZEN_CONSOLE(""); int AttachmentIndex = 1; for (const CbAttachment& Attachment : Attachments) { std::string AttachmentSize = "n/a"; const char* AttachmentType = "unknown"; if (Attachment.IsCompressedBinary()) { AttachmentType = "Compressed"; AttachmentSize = fmt::format("{} ({} uncompressed)", Attachment.AsCompressedBinary().GetCompressedSize(), Attachment.AsCompressedBinary().DecodeRawSize()); } else if (Attachment.IsBinary()) { AttachmentType = "Binary"; AttachmentSize = fmt::format("{}", Attachment.AsBinary().GetSize()); } else if (Attachment.IsObject()) { AttachmentType = "Object"; AttachmentSize = fmt::format("{}", Attachment.AsObject().GetSize()); } else if (Attachment.IsNull()) { AttachmentType = "null"; } ZEN_CONSOLE("Attachment #{} : {}, {}, size {}", AttachmentIndex, Attachment.GetHash(), AttachmentType, AttachmentSize); ++AttachmentIndex; } ZEN_CONSOLE("---8<---"); CbValidateError Result = ValidateCompactBinary(Object.GetView(), CbValidateMode::All); CbValidateError FilteredResult = MakeFilteredResult(Result); if (FilteredResult == CbValidateError::None && FilteredResult != Result) { ZEN_CONSOLE_WARN( "Object in package message file '{}' does not appear to be an optimal compact binary format (validation error {:#x}: '{}')", m_Filename, uint32_t(Result), ToString(Result)); } if (FilteredResult != CbValidateError::None) { throw std::runtime_error( fmt::format("Object in package message file '{}' does not appear to be compact binary (validation error {:#x}: '{}')", m_Filename, uint32_t(FilteredResult), ToString(FilteredResult))); } else { PrintCbObject(Object, m_ShowCbObjectTypeInfo); } } else { CbValidateError Result = ValidateCompactBinary(Data, CbValidateMode::All); CbValidateError FilteredResult = MakeFilteredResult(Result); if (FilteredResult == CbValidateError::None && FilteredResult != Result) { ZEN_CONSOLE_WARN("Data in file '{}' does not appear to be an optimal compact binary format (validation error {:#x}: '{}')", m_Filename, uint32_t(Result), ToString(Result)); } if (FilteredResult != CbValidateError::None) { throw std::runtime_error(fmt::format("Data in file '{}' does not appear to be compact binary (validation error {:#x}: '{}')", m_Filename, uint32_t(FilteredResult), ToString(FilteredResult))); } else { PrintCompactBinary(Data, m_ShowCbObjectTypeInfo); } } } ////////////////////////////////////////////////////////////////////////// PrintPackageCommand::PrintPackageCommand() { m_Options.add_options()("h,help", "Print help"); m_Options.add_option("", "s", "source", "Package payload file", cxxopts::value(m_Filename), ""); m_Options.add_option("", "", "show-type-info", "Add type info annotation to compact binary objects", cxxopts::value(m_ShowCbObjectTypeInfo), ""); m_Options.parse_positional({"source"}); } PrintPackageCommand::~PrintPackageCommand() { } void PrintPackageCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** argv) { ZEN_UNUSED(GlobalOptions); if (!ParseOptions(argc, argv)) { return; } // Validate arguments if (m_Filename.empty()) throw OptionParseException("'--source' is required", m_Options.help()); MakeSafeAbsolutePathInPlace(m_Filename); FileContents Fc = ReadFile(m_Filename); IoBuffer Data = Fc.Flatten(); CbPackage Package; bool Ok = Package.TryLoad(Data) || legacy::TryLoadCbPackage(Package, Data, &UniqueBuffer::Alloc); if (Ok) { ExtendableStringBuilder<1024> ObjStr; CompactBinaryToJson(Package.GetObject(), ObjStr, m_ShowCbObjectTypeInfo); ZEN_CONSOLE("{}", ObjStr); } else { ZEN_ERROR("Failed to load package, malformed package?"); } } } // namespace zen