diff --git a/src/definition/DefinitionNode.cpp b/src/definition/DefinitionNode.cpp index d196e38..8b825ff 100644 --- a/src/definition/DefinitionNode.cpp +++ b/src/definition/DefinitionNode.cpp @@ -1,5 +1,6 @@ #include "DefinitionNode.h" +#include "Logs.h" #include "PackStream.h" DefinitionNode::DefinitionNode(DefinitionNode* parent, const std::string &name): @@ -72,19 +73,56 @@ std::string DefinitionNode::toString(int indent) const void DefinitionNode::save(PackStream* stream) const { - stream->write(name); + int children_count = (int)children.size(); + stream->write(&children_count); + for (auto child: children) { - child->save(stream); + stream->write(child->name); + + int child_size = child->getStreamSize(); + if (child_size >= 0) + { + stream->write(&child_size); + child->save(stream); + } + else + { + // Child size not known, write it to a temporary stream to know it + Logs::debug() << "Unknown size for child " << child->name << ", unefficient writing to temporary stream" << std::endl; + PackStream substream; + child->save(&substream); + stream->writeFromBuffer(substream, true); + } } } void DefinitionNode::load(PackStream* stream) { - name = stream->readString(); - for (auto child: children) + int children_count; + + stream->read(&children_count); + + for (int i = 0; i < children_count; i++) { - child->load(stream); + std::string child_name = stream->readString(); + + int child_size; + stream->read(&child_size); + + DefinitionNode *child = findChildByName(child_name); + if (child) + { + // TODO type check + child->load(stream); + } + else + { + // TODO Ask subclass if it can instanciate a child + // Else skip length of unknown child + stream->skipBytes(child_size); + Logs::warning() << "Skipped unknown child '" << child_name << "'" << std::endl; + } } } @@ -122,6 +160,23 @@ void DefinitionNode::removeChild(DefinitionNode* child) } else { - qWarning("Trying to remove not found child '%s' from '%s'", child->name.c_str(), name.c_str()); + Logs::warning() << "Trying to remove not found child '" << child->name << "' from '" << name << "'" << std::endl; } } + +DefinitionNode *DefinitionNode::findChildByName(const std::string name) +{ + for (auto child: children) + { + if (child->name == name) + { + return child; + } + } + return NULL; +} + +int DefinitionNode::getStreamSize() const +{ + return -1; +} diff --git a/src/definition/DefinitionNode.h b/src/definition/DefinitionNode.h index bcf6ba5..8e99175 100644 --- a/src/definition/DefinitionNode.h +++ b/src/definition/DefinitionNode.h @@ -38,6 +38,15 @@ public: protected: void addChild(DefinitionNode* child); void removeChild(DefinitionNode* child); + virtual DefinitionNode *findChildByName(const std::string name); + + /** + * Get the size in bytes this child will consume when serialized to a stream. + * + * Return -1 if it can't be known. In this case, the saving will be done in a temporary + * stream to know the exact size, which will not be very efficient. + */ + int getStreamSize() const; private: DefinitionNode* parent; diff --git a/src/definition/FloatNode.cpp b/src/definition/FloatNode.cpp new file mode 100644 index 0000000..d466b6a --- /dev/null +++ b/src/definition/FloatNode.cpp @@ -0,0 +1,35 @@ +#include "FloatNode.h" + +#include "PackStream.h" +#include + +FloatNode::FloatNode(DefinitionNode* parent, const std::string &name, double value): + DefinitionNode(parent, name), value(value) +{ +} + +std::string FloatNode::toString(int indent) const +{ + std::ostringstream stream; + + stream << DefinitionNode::toString(indent) << " " << value; + + return stream.str(); +} + +void FloatNode::save(PackStream *stream) const +{ + stream->write(&value); +} + +void FloatNode::load(PackStream *stream) +{ + stream->read(&value); +} + +void FloatNode::copy(DefinitionNode *destination) const +{ + // TODO type check + + ((FloatNode *)destination)->value = value; +} diff --git a/src/definition/FloatNode.h b/src/definition/FloatNode.h new file mode 100644 index 0000000..7e866bb --- /dev/null +++ b/src/definition/FloatNode.h @@ -0,0 +1,32 @@ +#ifndef FLOATNODE_H +#define FLOATNODE_H + +#include "definition_global.h" + +#include "DefinitionNode.h" + +namespace paysages { +namespace definition { + +/** + * Node with a single floating point numeric value, for the definition tree. + */ +class DEFINITIONSHARED_EXPORT FloatNode: public DefinitionNode +{ +public: + FloatNode(DefinitionNode* parent, const std::string &name, double value = 0.0); + + inline double getValue() const {return value;} + + virtual std::string toString(int indent) const override; + virtual void save(PackStream* stream) const override; + virtual void load(PackStream* stream) override; + virtual void copy(DefinitionNode* destination) const override; +private: + double value; +}; + +} +} + +#endif // FLOATNODE_H diff --git a/src/definition/Layers.cpp b/src/definition/Layers.cpp index fe46bfe..cf4736f 100644 --- a/src/definition/Layers.cpp +++ b/src/definition/Layers.cpp @@ -21,7 +21,11 @@ void Layers::save(PackStream *stream) const int layer_count = (int)layers.size(); stream->write(&layer_count); - DefinitionNode::save(stream); + for (int i = 0; i < layer_count; i++) + { + stream->write(layers[i]->getName()); + layers[i]->save(stream); + } } void Layers::load(PackStream *stream) @@ -36,10 +40,13 @@ void Layers::load(PackStream *stream) clear(); for (int i = 0; i < layer_count; i++) { - addLayer(); + int position = addLayer(); + if (position >= 0) + { + layers[position]->setName(stream->readString()); + layers[position]->load(stream); + } } - - DefinitionNode::load(stream); } void Layers::copy(DefinitionNode* destination_) const @@ -171,3 +178,26 @@ void Layers::clear() removeLayer(0); } } + +DefinitionNode *Layers::findChildByName(const std::string name) +{ + DefinitionNode *result = DefinitionNode::findChildByName(name); + if (result) + { + return result; + } + else + { + int position = addLayer(); + if (position >= 0) + { + result = getLayer(position); + result->setName(name); + return result; + } + else + { + return NULL; + } + } +} diff --git a/src/definition/Layers.h b/src/definition/Layers.h index 7e514d0..fa83762 100644 --- a/src/definition/Layers.h +++ b/src/definition/Layers.h @@ -45,6 +45,9 @@ public: void moveLayer(DefinitionNode* layer, int new_position); void clear(); +protected: + virtual DefinitionNode *findChildByName(const std::string name) override; + public: LayerConstructor layer_constructor; int max_layer_count; diff --git a/src/definition/TerrainDefinition.cpp b/src/definition/TerrainDefinition.cpp index a7cc29e..df49085 100644 --- a/src/definition/TerrainDefinition.cpp +++ b/src/definition/TerrainDefinition.cpp @@ -3,6 +3,7 @@ #include "TerrainHeightMap.h" #include "NoiseGenerator.h" #include "PackStream.h" +#include "FloatNode.h" TerrainDefinition::TerrainDefinition(DefinitionNode* parent): DefinitionNode(parent, "terrain") @@ -15,6 +16,7 @@ TerrainDefinition::TerrainDefinition(DefinitionNode* parent): addChild(height_map); water_height = -0.3; + _water_height = new FloatNode(this, "water_height", -0.3); _height_noise = new NoiseGenerator; } diff --git a/src/definition/TerrainDefinition.h b/src/definition/TerrainDefinition.h index 5ceddc2..626d8ea 100644 --- a/src/definition/TerrainDefinition.h +++ b/src/definition/TerrainDefinition.h @@ -52,6 +52,9 @@ public: NoiseGenerator* _height_noise; double _min_height; double _max_height; + +private: + FloatNode *_water_height; }; } diff --git a/src/definition/definition.pro b/src/definition/definition.pro index 3156846..710ea35 100644 --- a/src/definition/definition.pro +++ b/src/definition/definition.pro @@ -30,7 +30,8 @@ SOURCES += \ PaintedGrid.cpp \ PaintedGridBrush.cpp \ PaintedGridData.cpp \ - DefinitionNode.cpp + DefinitionNode.cpp \ + FloatNode.cpp HEADERS +=\ definition_global.h \ @@ -50,7 +51,8 @@ HEADERS +=\ PaintedGrid.h \ PaintedGridBrush.h \ PaintedGridData.h \ - DefinitionNode.h + DefinitionNode.h \ + FloatNode.h unix:!symbian { maemo5 { diff --git a/src/definition/definition_global.h b/src/definition/definition_global.h index 3f40a44..25767ea 100644 --- a/src/definition/definition_global.h +++ b/src/definition/definition_global.h @@ -14,6 +14,7 @@ namespace paysages { namespace definition { class DefinitionNode; + class FloatNode; class Scenery; class CameraDefinition; class SurfaceMaterial; diff --git a/src/system/PackStream.cpp b/src/system/PackStream.cpp index 49010b1..88c40d7 100644 --- a/src/system/PackStream.cpp +++ b/src/system/PackStream.cpp @@ -1,5 +1,6 @@ #include "PackStream.h" +#include "Logs.h" #include #include #include @@ -7,15 +8,15 @@ PackStream::PackStream() { file = NULL; - stream = NULL; + buffer = new QByteArray(); + stream = new QDataStream(buffer, QIODevice::WriteOnly); + stream->setVersion(QDataStream::Qt_5_2); } PackStream::~PackStream() { - if (stream) - { - delete stream; - } + delete buffer; + delete stream; if (file) { delete file; @@ -24,7 +25,7 @@ PackStream::~PackStream() bool PackStream::bindToFile(const std::string &filepath, bool write) { - if (not file and not stream) + if (not file) { file = new QFile(QString::fromStdString(filepath)); if (not file->open(write ? QIODevice::WriteOnly : QIODevice::ReadOnly)) @@ -32,14 +33,28 @@ bool PackStream::bindToFile(const std::string &filepath, bool write) return false; } - stream = new QDataStream(file); + QDataStream *new_stream = new QDataStream(file); + if (new_stream) + { + delete stream; + stream = new_stream; + stream->setVersion(QDataStream::Qt_5_2); + return true; + } + else + { + return false; + } + } + else + { + return false; } - return stream != NULL; } void PackStream::write(const int *value) { - if (stream and value) + if (value) { *stream << *value; } @@ -47,7 +62,7 @@ void PackStream::write(const int *value) void PackStream::write(const double *value) { - if (stream and value) + if (value) { *stream << *value; } @@ -55,7 +70,7 @@ void PackStream::write(const double *value) void PackStream::write(const char *value, int max_length) { - if (stream and value) + if (value) { int length = qstrlen(value); *stream << QString::fromUtf8(value, length > max_length ? max_length : length); @@ -64,15 +79,29 @@ void PackStream::write(const char *value, int max_length) void PackStream::write(const std::string &value) { - if (stream) + *stream << QString::fromStdString(value); +} + +void PackStream::writeFromBuffer(const PackStream &other, bool prepend_size) +{ + if (other.file) { - *stream << QString::fromStdString(value); + Logs::error() << "Try to write from a substream bound to a file: " << other.file->fileName().toStdString() << std::endl; + } + else + { + if (prepend_size) + { + int buffer_size = (int)other.buffer->size(); + write(&buffer_size); + } + stream->writeRawData(other.buffer->data(), other.buffer->size()); } } void PackStream::read(int* value) { - if (stream and value and not stream->atEnd()) + if (value and not stream->atEnd()) { int output; *stream >> output; @@ -82,7 +111,7 @@ void PackStream::read(int* value) void PackStream::read(double* value) { - if (stream and value and not stream->atEnd()) + if (value and not stream->atEnd()) { double output; *stream >> output; @@ -92,7 +121,7 @@ void PackStream::read(double* value) void PackStream::read(char* value, int max_length) { - if (stream and value and not stream->atEnd()) + if (value and not stream->atEnd()) { QString output; *stream >> output; @@ -103,7 +132,7 @@ void PackStream::read(char* value, int max_length) std::string PackStream::readString() { - if (stream and not stream->atEnd()) + if (not stream->atEnd()) { QString output; *stream >> output; @@ -124,3 +153,8 @@ void PackStream::skip(const double &value, int count) { stream->skipRawData(sizeof(value) * count); } + +void paysages::system::PackStream::skipBytes(int bytes) +{ + stream->skipRawData(bytes); +} diff --git a/src/system/PackStream.h b/src/system/PackStream.h index 2de2d03..231b852 100644 --- a/src/system/PackStream.h +++ b/src/system/PackStream.h @@ -27,17 +27,28 @@ public: void write(const char *value, const int max_length); void write(const std::string &value); - void read(int* value); - void read(double* value); - void read(char* value, int max_length); + /** + * Write the contents of another stream into this one. + * + * The other stream must not have been bound to a file (still a memory buffer). + * + * If *prepend_size* is true, an integer will be written on front with the number of bytes written. + */ + void writeFromBuffer(const PackStream &other, bool prepend_size = false); + + void read(int *value); + void read(double *value); + void read(char *value, int max_length); std::string readString(); void skip(const int &value, int count=1); void skip(const double &value, int count=1); + void skipBytes(int bytes); private: - QFile* file; - QDataStream* stream; + QFile *file; + QByteArray *buffer; + QDataStream *stream; }; } diff --git a/src/tests/DefinitionNode_Test.cpp b/src/tests/DefinitionNode_Test.cpp index ea8eb26..a652ce3 100644 --- a/src/tests/DefinitionNode_Test.cpp +++ b/src/tests/DefinitionNode_Test.cpp @@ -1,6 +1,8 @@ #include "BaseTestCase.h" #include "DefinitionNode.h" +#include "FloatNode.h" +#include "PackStream.h" TEST(DefinitionNode, toString) { @@ -33,3 +35,40 @@ TEST(DefinitionNode, attachDetach) delete root; } + +TEST(DefinitionNode, saveLoad) +{ + PackStream *stream; + + DefinitionNode* before = new DefinitionNode(NULL, "root"); + DefinitionNode* before1 = new DefinitionNode(before, "before1"); + FloatNode* before11 = new FloatNode(before1, "before11", 2.1); + FloatNode* before12 = new FloatNode(before1, "before12", -4.3); + DefinitionNode* before2 = new DefinitionNode(before, "before2"); + DefinitionNode* before21 = new DefinitionNode(before2, "before21"); + FloatNode* before22 = new FloatNode(before2, "before22"); + FloatNode* before3 = new FloatNode(before, "before3", 6.7); + + stream = new PackStream(); + stream->bindToFile("/tmp/test_paysages_pack", true); + before->save(stream); + delete stream; + + // Same definition tree, but with missing nodes, and added ones + DefinitionNode* after = new DefinitionNode(NULL, "root"); + DefinitionNode* after1 = new DefinitionNode(after, "before1"); + FloatNode* after12 = new FloatNode(after1, "before12"); + FloatNode* after13 = new FloatNode(after1, "before13"); + FloatNode* after3 = new FloatNode(after, "before3"); + FloatNode* after4 = new FloatNode(after, "before4"); + + stream = new PackStream(); + stream->bindToFile("/tmp/test_paysages_pack"); + after->load(stream); + delete stream; + + EXPECT_DOUBLE_EQ(-4.3, after12->getValue()); + EXPECT_DOUBLE_EQ(0.0, after13->getValue()); + EXPECT_DOUBLE_EQ(6.7, after3->getValue()); + EXPECT_DOUBLE_EQ(0.0, after4->getValue()); +} diff --git a/src/tests/Layers_Test.cpp b/src/tests/Layers_Test.cpp index e191344..4e9d605 100644 --- a/src/tests/Layers_Test.cpp +++ b/src/tests/Layers_Test.cpp @@ -77,3 +77,31 @@ TEST(Layers, maxLayerCount) layers1.addLayer(); EXPECT_EQ(2, layers1.count()); } + +TEST(Layers, saveLoad) +{ + PackStream *stream; + + Layers layers1(NULL, "test", _construc1); + layers1.addLayer(); + layers1.addLayer(); + ASSERT_EQ(2, layers1.count()); + layers1.getLayer(0)->setName("first"); + layers1.getLayer(1)->setName("second"); + + stream = new PackStream(); + stream->bindToFile("/tmp/test_paysages_pack", true); + layers1.save(stream); + delete stream; + + Layers layers2(NULL, "test", _construc1); + + stream = new PackStream(); + stream->bindToFile("/tmp/test_paysages_pack"); + layers2.load(stream); + delete stream; + + ASSERT_EQ(2, layers2.count()); + EXPECT_EQ("first", layers2.getLayer(0)->getName()); + EXPECT_EQ("second", layers2.getLayer(1)->getName()); +} diff --git a/src/tests/Scenery_Test.cpp b/src/tests/Scenery_Test.cpp index 5dbc439..551d976 100644 --- a/src/tests/Scenery_Test.cpp +++ b/src/tests/Scenery_Test.cpp @@ -1,6 +1,7 @@ #include "BaseTestCase.h" #include "Scenery.h" +#include "Logs.h" TEST(Scenery, saveGlobal) {