diff --git a/scene/sceneterrain.cpp b/scene/sceneterrain.cpp index 39f0f02d..db0135d3 100644 --- a/scene/sceneterrain.cpp +++ b/scene/sceneterrain.cpp @@ -81,7 +81,7 @@ namespace { std::uint32_t const EU07_FILEHEADER{MAKE_ID4('E', 'U', '0', '7')}; -std::uint32_t const EU07_FILEVERSION_TERRAIN{MAKE_ID4('B', 'T', 'F', '1')}; +std::uint32_t const EU07_FILEVERSION_TERRAIN{MAKE_ID4('B', 'T', 'F', '2')}; std::uint32_t const EU07_TERRAINBLOCK{MAKE_ID4('B', 'T', 'F', 'S')}; std::size_t const EU07_TERRAINVERTEXSIZE{8 * sizeof(float)}; @@ -92,6 +92,8 @@ struct file_header std::uint32_t materialcount{0}; std::uint32_t sectioncount{0}; std::uint64_t vertexcount{0}; + std::uint64_t extraoffset{0}; + std::uint32_t extrasize{0}; }; // adds value to the data in the byte order of the file @@ -215,7 +217,7 @@ bool read_file(std::string const &Filename, std::string &Data) // loads the fixed part of binary terrain file header. returns: true if the file can be used, false otherwise bool read_header(std::istream &Input, file_header &Header) { - char bytes[48]; + char bytes[60]; Input.read(bytes, sizeof(bytes)); if (Input.gcount() != static_cast(sizeof(bytes))) { @@ -238,6 +240,8 @@ bool read_header(std::istream &Input, file_header &Header) Header.materialcount = data.get(); Header.sectioncount = data.get(); Header.vertexcount = data.get(); + Header.extraoffset = data.get(); + Header.extrasize = data.get(); return data.good(); } @@ -395,6 +399,24 @@ bool terrain_file::write(std::string const &Text, std::string const &Binaryfile, conversion_scope scope; auto region{std::make_unique()}; scratch_data scratchpad{}; + std::string extra; + { + std::size_t begin{0}; + while (begin < Text.size()) + { + auto end{Text.find('\n', begin)}; + end = (end == std::string::npos ? Text.size() : end + 1); + auto const first{Text.find_first_not_of(" \t", begin)}; + if (first < end && Text.compare(first, 3, "//$") == 0) + { + auto line{Text.substr(first, end - first)}; + while (false == line.empty() && (line.back() == '\n' || line.back() == '\r')) + line.pop_back(); + extra += line + "\n"; + } + begin = end; + } + } { cParser input{Text, cParser::buffer_TEXT}; @@ -405,9 +427,26 @@ bool terrain_file::write(std::string const &Text, std::string const &Binaryfile, { if (token == "node") { + auto const begin{static_cast(input.TokenBegin())}; node_data nodedata; input.getTokens(4); input >> nodedata.range_max >> nodedata.range_min >> nodedata.name >> nodedata.type; + if (nodedata.type == "sweep") + { + auto rest{input.getToken()}; + while (false == rest.empty() && rest != "endsweep") + rest = input.getToken(); + auto const end{static_cast(input.TokenEnd())}; + auto const offset{scratchpad.location.offset.empty() ? glm::dvec3{0.0} : scratchpad.location.offset.top()}; + auto const placed{offset != glm::dvec3{0.0}}; + if (placed) + extra += "origin " + std::to_string(offset.x) + ' ' + std::to_string(offset.y) + ' ' + std::to_string(offset.z) + "\n"; + extra += Text.substr(begin, end - begin) + "\n"; + if (placed) + extra += "endorigin\n"; + token = input.getToken(); + continue; + } if (nodedata.type != "triangles" && nodedata.type != "triangle_strip" && nodedata.type != "triangle_fan") { Message = "node of type \"" + nodedata.type + "\" (line " + std::to_string(input.Line()) + ") can't be a part of binary terrain"; @@ -485,6 +524,9 @@ bool terrain_file::write(std::string const &Text, std::string const &Binaryfile, put(data, static_cast(sections.size())); auto const vertexcountposition{data.size()}; put(data, vertexcount); // filled in when the count is known + auto const extraposition{data.size()}; + put(data, std::uint64_t{0}); + put(data, std::uint32_t{0}); for (auto const &material : scope.materials()) { data.append(material.c_str(), material.size() + 1); @@ -562,6 +604,12 @@ bool terrain_file::write(std::string const &Text, std::string const &Binaryfile, put(directory, section->m_area.radius); offset += data.size(); } + output.write(extra.data(), static_cast(extra.size())); + data.clear(); + put(data, static_cast(offset)); + put(data, static_cast(extra.size())); + output.seekp(static_cast(extraposition)); + output.write(data.data(), static_cast(data.size())); // fill in the parts left for later output.seekp(static_cast(directoryposition)); output.write(directory.data(), static_cast(directory.size())); @@ -672,6 +720,25 @@ terrain_file::state terrain_file::prepare(std::string const &Textfile, std::stri return state::text; } +std::string terrain_file::extra(std::string const &Binaryfile) +{ + std::ifstream input{Binaryfile, std::ios::binary}; + file_header header; + if (input.fail() || false == read_header(input, header) || header.extrasize == 0) + { + return {}; + } + std::string text(header.extrasize, '\0'); + input.seekg(static_cast(header.extraoffset)); + input.read(text.data(), static_cast(text.size())); + if (static_cast(std::max(0, input.gcount())) != text.size()) + { + ErrorLog("Bad file: \"" + Binaryfile + "\" is damaged, the editor data it holds can't be read"); + return {}; + } + return text; +} + // makes content of specified binary terrain file a part of provided region bool terrain_file::attach(std::string const &Binaryfile, basic_region &Region, bool Deferred) { diff --git a/scene/sceneterrain.h b/scene/sceneterrain.h index b719604b..07359fbd 100644 --- a/scene/sceneterrain.h +++ b/scene/sceneterrain.h @@ -46,6 +46,7 @@ class terrain_file static bool attach(std::string const &Binaryfile, basic_region &Region, bool Deferred); // loads specified piece of a binary terrain file into provided section static void load(basic_section &Section, terrain_block const &Block); + static std::string extra(std::string const &Binaryfile); private: // converts provided content of a text terrain file to binary format and stores it in specified file. returns: true on success, false otherwise diff --git a/simulation/simulationstateserializer.cpp b/simulation/simulationstateserializer.cpp index fe5486e8..f73f913c 100644 --- a/simulation/simulationstateserializer.cpp +++ b/simulation/simulationstateserializer.cpp @@ -1235,6 +1235,7 @@ include_terrain_file( std::string File, std::string &Includes, scene::scratch_da else { binary.terrain_binaryfiles.emplace_back( binaryfile ); } + Includes += scene::terrain_file::extra( binaryfile ) + ' '; break; } case scene::terrain_file::state::text: {