// Godot save decoder algorithm by Chris Souvey. #include "godot_variant.h" #include #include #include #include #include #include #include #include #include namespace { uint16_t ReadUint16(std::basic_istream& stream) { uint16_t result; stream.read(reinterpret_cast(&result), 2); return result; } uint32_t ReadUint32(std::basic_istream& stream) { uint32_t result; stream.read(reinterpret_cast(&result), 4); return result; } GodotVariant ParseVariant(std::basic_istream& stream) { uint16_t type = ReadUint16(stream); stream.ignore(2); switch (type) { case 1: { // bool bool boolval = (ReadUint32(stream) == 1); return {boolval}; } case 15: { // nodepath uint32_t name_length = ReadUint32(stream) & 0x7fffffff; uint32_t subname_length = ReadUint32(stream) & 0x7fffffff; uint32_t flags = ReadUint32(stream); std::vector result; for (size_t i = 0; i < name_length + subname_length; i++) { uint32_t char_length = ReadUint32(stream); uint32_t padded_length = (char_length % 4 == 0) ? char_length : (char_length + 4 - (char_length % 4)); std::vector next_bytes(padded_length); stream.read(next_bytes.data(), padded_length); std::string next_piece; std::copy(next_bytes.begin(), std::next(next_bytes.begin(), char_length), std::back_inserter(next_piece)); result.push_back(next_piece); } return {result}; } case 19: { // array uint32_t length = ReadUint32(stream) & 0x7fffffff; std::vector result; for (size_t i = 0; i < length; i++) { result.push_back(ParseVariant(stream)); } return {result}; } default: { // eh return {std::monostate{}}; } } } } // namespace GodotVariant ParseGodotFile(std::string filename) { std::ifstream file_stream(filename, std::ios_base::binary); file_stream.ignore(4); return ParseVariant(file_stream); }