// Copyright 2022, FOSS-VG Developers and Contributers // // This program is free software: you can redistribute it and/or modify it // under the terms of the GNU Affero General Public License as published // by the Free Software Foundation, version 3. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. // See the GNU Affero General Public License for more details. // // You should have received a copy of the GNU Affero General Public License // version 3 along with this program. // If not, see https://www.gnu.org/licenses/agpl-3.0.en.html #include #include #include #include #include "../lib/nbt.hpp" #include "../lib/cli.hpp" #define EXIT_SUCCESS 0 #define EXIT_RUNTIME 1 #define EXIT_USAGE 2 #define EXIT_UNIMPLEMENTED 3 void printTagTypeName(NBT::Tag::Generic* tag, uint64_t offsetBytes) { std::cout << "[" << offsetBytes << ": "; switch (tag->getTagType()) { case NBT::TagType::END: std::cout << "End"; break; case NBT::TagType::INT8: std::cout << "8 Bit Integer"; break; case NBT::TagType::INT16: std::cout << "16 Bit Integer"; break; case NBT::TagType::INT32: std::cout << "32 Bit Integer"; break; case NBT::TagType::INT64: std::cout << "64 Bit Integer"; break; case NBT::TagType::FLOAT: std::cout << "Float"; break; case NBT::TagType::DOUBLE: std::cout << "Double"; break; case NBT::TagType::INT8_ARRAY: std::cout << "Array of 8 Bit Integers"; break; case NBT::TagType::STRING: std::cout << "String"; break; case NBT::TagType::LIST: std::cout << "List"; break; case NBT::TagType::COMPOUND: std::cout << "Compound"; break; case NBT::TagType::INT32_ARRAY: std::cout << "Array of 32 Bit Integers"; break; case NBT::TagType::INT64_ARRAY: std::cout << "Array of 64 Bit Integers"; break; default: // WTF? How'd you even get here? std::cout << "Unknown Type"; } std::cout << "]"; if (tag->name == "") { std::cout << ":"; } else { std::cout << " " << tag->name << ":"; } } void printNBytes(uint64_t bytes) { std::cout << bytes << (bytes==1? " byte":" bytes"); } void drawTree(NBT::Tag::Generic* tag, tiny_utf8::string prefix, uint64_t offsetBytes, bool listMode=false) { std::vector serialized; uint64_t headerSize = 0; if (listMode) { tag->serializeWithoutHeader(&serialized); std::cout << prefix << "|–Payload: "; printNBytes(serialized.size()); std::cout << std::endl; } else { tag->serialize(&serialized); if (tag->getTagType() == NBT::TagType::END) { headerSize = 1; } else { headerSize = (uint64_t) NBT::Helper::readInt16(serialized.data(), serialized.size(), 1).value+3; } std::cout << prefix << "|–Header: "; printNBytes(headerSize); std::cout << std::endl; std::cout << prefix << "|–Payload: "; printNBytes(serialized.size() - headerSize); std::cout << std::endl; } if (tag->getTagType() == NBT::TagType::END) { std::cout << prefix << "'–Total: "; } else { std::cout << prefix << "|–Total: "; } printNBytes(serialized.size()); std::cout << std::endl; switch (tag->getTagType()) { case NBT::TagType::END: break; case NBT::TagType::INT8: std::cout << prefix << "'–Value: " << (int32_t) reinterpret_cast(tag)->getValue() << std::endl; break; case NBT::TagType::INT16: std::cout << prefix << "'–Value: " << (int32_t) reinterpret_cast(tag)->getValue() << std::endl; break; case NBT::TagType::INT32: std::cout << prefix << "'–Value: " << reinterpret_cast(tag)->getValue() << std::endl; break; case NBT::TagType::INT64: std::cout << prefix << "'–Value: " << reinterpret_cast(tag)->getValue() << std::endl; break; case NBT::TagType::FLOAT: std::cout << prefix << "'–Value: " << reinterpret_cast(tag)->getValue() << std::endl; break; case NBT::TagType::DOUBLE: std::cout << prefix << "'–Value: " << reinterpret_cast(tag)->getValue() << std::endl; break; case NBT::TagType::INT8_ARRAY: { NBT::Tag::Int8Array* array = reinterpret_cast(tag); std::cout << prefix << "|–Length: " << array->length() << std::endl; std::cout << prefix << "'–Values: " << std::endl; for (uint64_t i=0; ilength(); i++) { if (i == array->length()-1) { std::cout << prefix << " '–" << (int64_t) array->getValue(i).value << std::endl; } else { std::cout << prefix << " |–" << (int64_t) array->getValue(i).value << std::endl; } } break; } case NBT::TagType::STRING: std::cout << prefix << "'–Value: " << reinterpret_cast(tag)->getValue() << std::endl; break; case NBT::TagType::LIST: { NBT::Tag::List* list = reinterpret_cast(tag); std::cout << prefix << "|–Contained Type: "; switch (list->getContainedType()) { case NBT::TagType::END: std::cout << "End"; break; case NBT::TagType::INT8: std::cout << "8 Bit Integer"; break; case NBT::TagType::INT16: std::cout << "16 Bit Integer"; break; case NBT::TagType::INT32: std::cout << "32 Bit Integer"; break; case NBT::TagType::INT64: std::cout << "64 Bit Integer"; break; case NBT::TagType::FLOAT: std::cout << "Float"; break; case NBT::TagType::DOUBLE: std::cout << "Double"; break; case NBT::TagType::INT8_ARRAY: std::cout << "Array of 8 Bit Integers"; break; case NBT::TagType::STRING: std::cout << "String"; break; case NBT::TagType::LIST: std::cout << "List"; break; case NBT::TagType::COMPOUND: std::cout << "Compound"; break; case NBT::TagType::INT32_ARRAY: std::cout << "Array of 32 Bit Integers"; break; case NBT::TagType::INT64_ARRAY: std::cout << "Array of 64 Bit Integers"; break; default: // WTF? How'd you even get here? std::cout << "Unknown Type"; } std::cout << std::endl; if (list->length() > 0) { std::cout << prefix << "|–Length: " << list->length() << std::endl; std::cout << prefix << "|" << std::endl; } else { std::cout << prefix << "'–Length: " << list->length() << std::endl; } offsetBytes = offsetBytes + headerSize + 5; for (uint64_t i=0; ilength(); i++) { if (i == list->length()-1) { std::cout << prefix << "'–"; printTagTypeName(list->getElementPointer(i).value, offsetBytes); std::cout << std::endl; drawTree(list->getElementPointer(i).value, prefix+" ", offsetBytes, true); } else { std::cout << prefix << "|–"; printTagTypeName(list->getElementPointer(i).value, offsetBytes); std::cout << std::endl; drawTree(list->getElementPointer(i).value, prefix+"| ", offsetBytes, true); } std::vector serializedElement; list->getElementPointer(i).value->serializeWithoutHeader(&serializedElement); offsetBytes += serializedElement.size(); } break; } case NBT::TagType::COMPOUND: { NBT::Tag::Compound* compound = reinterpret_cast(tag); std::cout << prefix << "|–Length: " << compound->length() << std::endl; std::cout << prefix << "|" << std::endl; offsetBytes = offsetBytes + headerSize; for (uint64_t i=0; ilength(); i++) { if (i == compound->length()-1) { std::cout << prefix << "'–"; printTagTypeName(compound->getElementPointer(i).value, offsetBytes); std::cout << std::endl; drawTree(compound->getElementPointer(i).value, prefix+" ", offsetBytes); } else { std::cout << prefix << "|–"; printTagTypeName(compound->getElementPointer(i).value, offsetBytes); std::cout << std::endl; drawTree(compound->getElementPointer(i).value, prefix+"| ", offsetBytes); } std::vector serializedElement; compound->getElementPointer(i).value->serialize(&serializedElement); offsetBytes += serializedElement.size(); } break; } case NBT::TagType::INT32_ARRAY: { NBT::Tag::Int32Array* array = reinterpret_cast(tag); std::cout << prefix << "|–Length: " << array->length() << std::endl; std::cout << prefix << "'–Values: " << std::endl; for (uint64_t i=0; ilength(); i++) { if (i == array->length()-1) { std::cout << prefix << " '–" << array->getValue(i).value << std::endl; } else { std::cout << prefix << " |–" << array->getValue(i).value << std::endl; } } break; } case NBT::TagType::INT64_ARRAY: { NBT::Tag::Int64Array* array = reinterpret_cast(tag); std::cout << prefix << "|–Length: " << array->length() << std::endl; std::cout << prefix << "'–Values: " << std::endl; for (uint64_t i=0; ilength(); i++) { if (i == array->length()-1) { std::cout << prefix << " '–" << array->getValue(i).value << std::endl; } else { std::cout << prefix << " |–" << array->getValue(i).value << std::endl; } } break; } default: // WTF? How'd you even get here? std::cout << prefix << "'–???" << std::endl; } std::cout << prefix << std::endl; } int main(int argc, char* argv[]) { std::vector flags; flags.push_back(CLI::Flag('h', "help", "print help and exit")); flags.push_back(CLI::Flag('l', "license", "print license information and exit")); std::vector options; std::vector arguments; arguments.push_back(CLI::Argument("FILE", "path of the file to dump")); CLI::ArgumentsParser cliParser = CLI::ArgumentsParser(argc, argv, flags, options, arguments, "Present NBT in human or machine readable formats"); if (cliParser.getFlag("help").value){ std::cout << cliParser.getUsage() << std::endl; return EXIT_SUCCESS; } if (cliParser.getFlag("license").value){ std::cout << "Copyright 2022, FOSS-VG Developers and Contributers\n" << "\n" << "DumpNBT is part of the FOSS-VG development tool suite.\n" << "\n" << "This program is free software: you can redistribute it and/or modify it\n" << "under the terms of the GNU Affero General Public License as published\n" << "by the Free Software Foundation, version 3.\n" << "\n" << "This program is distributed in the hope that it will be useful,\n" << "but WITHOUT ANY WARRANTY; without even the implied\n" << "warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n" << "See the GNU Affero General Public License for more details.\n" << "\n" << "You should have received a copy of the GNU Affero General Public License\n" << "version 3 along with this program.\n" << "If not, see https://www.gnu.org/licenses/agpl-3.0.en.html" << std::endl; return EXIT_SUCCESS; } if (cliParser.wrongUsage) { std::cerr << cliParser.getUsage() << std::endl; return EXIT_USAGE; } std::ifstream fileStream; fileStream.open(cliParser.getArgument(0).value, std::ios::in | std::ios::binary | std::ios::ate); if (!fileStream.is_open()) { std::cerr << argv[0] << ": Could not open file: " << cliParser.getArgument(0).value << std::endl; return EXIT_RUNTIME; } uint64_t fileSize = fileStream.tellg(); fileStream.seekg(0, std::ios::beg); uint8_t data[fileSize]; uint8_t* nextByte = new uint8_t; for (uint64_t i=0; i(nextByte), 1); data[i] = *nextByte; } fileStream.close(); ErrorOr> tags = NBT::deserialize(data, fileSize); if (tags.isError) { std::cerr << "Invalid data." << std::endl; return EXIT_RUNTIME; } uint64_t offsetBytes = 0; for (uint64_t i=0; i serialized; tags.value[i]->serialize(&serialized); offsetBytes += serialized.size(); } return EXIT_SUCCESS; }