diff --git a/lib/filesystem/CMemoryBuffer.cpp b/lib/filesystem/CMemoryBuffer.cpp index 8acbf586a..39b25dc9c 100644 --- a/lib/filesystem/CMemoryBuffer.cpp +++ b/lib/filesystem/CMemoryBuffer.cpp @@ -20,7 +20,10 @@ CMemoryBuffer::CMemoryBuffer(): si64 CMemoryBuffer::write(const ui8 * data, si64 size) { - buffer.reserve(tell()+size); + //do not shrink + const si64 newSize = tell()+size; + if(newSize>getSize()) + buffer.resize(newSize); std::copy(data, data + size, buffer.data() + position); position += size; @@ -32,8 +35,12 @@ si64 CMemoryBuffer::read(ui8 * data, si64 size) { si64 toRead = std::min(getSize() - tell(), size); - std::copy(buffer.data() + position, buffer.data() + position + toRead, data); - position += toRead; + if(toRead > 0) + { + std::copy(buffer.data() + position, buffer.data() + position + toRead, data); + position += toRead; + } + return toRead; } @@ -42,7 +49,7 @@ si64 CMemoryBuffer::seek(si64 position) { this->position = position; if (this->position >=getSize()) - this->position = getSize()-1; + this->position = getSize(); return this->position; } diff --git a/lib/filesystem/CZipSaver.cpp b/lib/filesystem/CZipSaver.cpp index f3c3c2076..937bcfa04 100644 --- a/lib/filesystem/CZipSaver.cpp +++ b/lib/filesystem/CZipSaver.cpp @@ -12,8 +12,9 @@ #include "CZipSaver.h" ///CZipOutputStream -CZipOutputStream::CZipOutputStream(zipFile archive, const std::string & archiveFilename): - handle(archive) +CZipOutputStream::CZipOutputStream(CZipSaver * owner_, zipFile archive, const std::string & archiveFilename): + handle(archive), + owner(owner_) { //zip_fileinfo fileInfo; @@ -27,14 +28,15 @@ CZipOutputStream::CZipOutputStream(zipFile archive, const std::string & archiveF "", Z_DEFLATED, Z_DEFAULT_COMPRESSION); + owner->activeStream = this; } CZipOutputStream::~CZipOutputStream() { zipCloseFileInZip(handle); + owner->activeStream = nullptr; } - si64 CZipOutputStream::write(const ui8 * data, si64 size) { int ret = zipWriteInFileInZip(handle, (const void*)data, (unsigned)size); @@ -49,18 +51,24 @@ si64 CZipOutputStream::write(const ui8 * data, si64 size) CZipSaver::CZipSaver(std::shared_ptr api, const std::string & path): ioApi(api), zipApi(ioApi->getApiStructure()), - handle(nullptr) + handle(nullptr), + activeStream(nullptr) { - - handle = zipOpen2_64(path.c_str(), APPEND_STATUS_CREATE, nullptr, &zipApi); if (handle == nullptr) - throw new std::runtime_error("Failed to create archive"); + throw new std::runtime_error("Failed to create archive"); } CZipSaver::~CZipSaver() { + if(activeStream != nullptr) + { + logGlobal->error("CZipSaver::~CZipSaver: active stream found"); + zipCloseFileInZip(handle); + } + + if(handle != nullptr) zipClose(handle, nullptr); } @@ -70,10 +78,7 @@ std::unique_ptr CZipSaver::addFile(const std::string & archiveFil if(activeStream != nullptr) throw new std::runtime_error("CZipSaver::addFile: stream already opened"); - std::unique_ptr stream(new CZipOutputStream(handle, archiveFilename)); - - activeStream = stream.get(); - - return stream; + std::unique_ptr stream(new CZipOutputStream(this, handle, archiveFilename)); + return std::move(stream); } diff --git a/lib/filesystem/CZipSaver.h b/lib/filesystem/CZipSaver.h index 3a0cb9752..06a4c1717 100644 --- a/lib/filesystem/CZipSaver.h +++ b/lib/filesystem/CZipSaver.h @@ -14,6 +14,8 @@ #include "MinizipExtensions.h" +class CZipSaver; + class DLL_LINKAGE CZipOutputStream: public COutputStream { public: @@ -22,7 +24,7 @@ public: * @param archive archive handle, must be opened * @param archiveFilename name of file to write */ - explicit CZipOutputStream(zipFile archive, const std::string & archiveFilename); + explicit CZipOutputStream(CZipSaver * owner_, zipFile archive, const std::string & archiveFilename); ~CZipOutputStream(); si64 write(const ui8 * data, si64 size) override; @@ -33,6 +35,7 @@ public: si64 getSize() override {return 0;}; private: zipFile handle; + CZipSaver * owner; }; class DLL_LINKAGE CZipSaver @@ -50,4 +53,5 @@ private: ///due to minizip design only one file stream may opened at a time COutputStream * activeStream; + friend class CZipOutputStream; }; diff --git a/lib/filesystem/MinizipExtensions.cpp b/lib/filesystem/MinizipExtensions.cpp index d07098957..013e57560 100644 --- a/lib/filesystem/MinizipExtensions.cpp +++ b/lib/filesystem/MinizipExtensions.cpp @@ -154,7 +154,7 @@ CProxyIOApi::~CProxyIOApi() CInputOutputStream * CProxyIOApi::openFile(const std::string& filename, int mode) const { - logGlobal->traceStream() << "CProxyIOApi: stream opened for" <traceStream() << "CProxyIOApi: stream opened for " <seek(0); return data;//todo: check that only one "copy" is opened diff --git a/lib/mapping/MapFormatJson.cpp b/lib/mapping/MapFormatJson.cpp index 09c5ebf94..991a1b30b 100644 --- a/lib/mapping/MapFormatJson.cpp +++ b/lib/mapping/MapFormatJson.cpp @@ -51,6 +51,9 @@ static EventCondition JsonToCondition(const JsonNode & node) } ///CMapFormatJson +const int CMapFormatJson::VERSION_MAJOR = 1; +const int CMapFormatJson::VERSION_MINOR = 0; + const std::string CMapFormatJson::HEADER_FILE_NAME = "header.json"; void CMapFormatJson::readTriggeredEvents(const JsonNode & input) @@ -102,9 +105,19 @@ void CMapPatcher::readPatchData() readTriggeredEvents(input); } +///CMapFormatZip +CMapFormatZip::CMapFormatZip(CInputOutputStream * stream): + buffer(stream), + ioApi(new CProxyIOApi(buffer)) +{ + +} + + ///CMapLoaderJson -CMapLoaderJson::CMapLoaderJson(CInputStream * stream): - input(stream) +CMapLoaderJson::CMapLoaderJson(CInputOutputStream * stream): + CMapFormatZip(stream), + loader("", "_", ioApi) { } @@ -168,26 +181,66 @@ JsonNode eventToJson(const EventCondition & cond) void CMapLoaderJson::readMap() { readHeader(); + map->initTerrain(); //TODO:readMap } void CMapLoaderJson::readHeader() { + //do not use map field here, use only mapHeader + ResourceID headerID(HEADER_FILE_NAME, EResType::TEXT); + + if(!loader.existsResource(headerID)) + throw new std::runtime_error(HEADER_FILE_NAME+" not found"); + + auto headerData = loader.load(headerID)->readAll(); + + const JsonNode header(reinterpret_cast(headerData.first.get()), headerData.second); + //TODO: read such data like map name & size -// readTriggeredEvents(); + //mapHeader->version = ??? //todo: new version field + + //todo: multilevel map load support + const JsonNode levels = header["mapLevels"]; + mapHeader->height = levels["surface"]["height"].Float(); + mapHeader->width = levels["surface"]["width"].Float(); + mapHeader->twoLevel = !levels["underground"].isNull(); + + mapHeader->name = header["name"].String(); + mapHeader->description = header["description"].String(); + + //todo: support arbitrary percentage + + static const std::map difficultyMap = + { + {"EASY", 0}, + {"NORMAL", 1}, + {"HARD", 2}, + {"EXPERT", 3}, + {"IMPOSSIBLE", 4} + }; + + mapHeader->difficulty = difficultyMap.at(header["difficulty"].String()); + mapHeader->levelLimit = header["levelLimit"].Float(); + + +// std::vector allowedHeroes; +// std::vector placeholdedHeroes; + + readTriggeredEvents(header); readPlayerInfo(); //TODO: readHeader } void CMapLoaderJson::readPlayerInfo() { + //ui8 howManyTeams; //TODO: readPlayerInfo } ///CMapSaverJson CMapSaverJson::CMapSaverJson(CInputOutputStream * stream): - output(stream), - ioApi(new CProxyIOApi(output)), + CMapFormatZip(stream), saver(ioApi, "_") { @@ -202,14 +255,48 @@ void CMapSaverJson::saveMap(const std::unique_ptr& map) { //TODO: saveMap this->map = map.get(); + saveHeader(); + } void CMapSaverJson::saveHeader() { JsonNode header; - //TODO: save header + header["versionMajor"].Float() = VERSION_MAJOR; + header["versionMinor"].Float() = VERSION_MINOR; + + //todo: multilevel map save support + JsonNode levels = header["mapLevels"]; + levels["surface"]["height"].Float() = map->height; + levels["surface"]["width"].Float() = map->width; + levels["surface"]["index"].Float() = 0; + + if(map->twoLevel) + { + levels["underground"]["height"].Float() = map->height; + levels["underground"]["width"].Float() = map->width; + levels["underground"]["index"].Float() = 1; + } header["name"].String() = map->name; + header["description"].String() = map->description; + + + //todo: support arbitrary percentage + static const std::map difficultyMap = + { + {0, "EASY"}, + {1, "NORMAL"}, + {2, "HARD"}, + {3, "EXPERT"}, + {4, "IMPOSSIBLE"} + }; + + header["difficulty"].String() = difficultyMap.at(map->difficulty); + header["levelLimit"].Float() = map->levelLimit; + + //todo: allowedHeroes; + //todo: placeholdedHeroes; std::ostringstream out; out << header; @@ -217,7 +304,7 @@ void CMapSaverJson::saveHeader() { auto s = out.str(); - auto stream = saver.addFile(HEADER_FILE_NAME); + std::unique_ptr stream = saver.addFile(HEADER_FILE_NAME); stream->write((const ui8*)s.c_str(), s.size()); } diff --git a/lib/mapping/MapFormatJson.h b/lib/mapping/MapFormatJson.h index 114c6c51a..877e510a4 100644 --- a/lib/mapping/MapFormatJson.h +++ b/lib/mapping/MapFormatJson.h @@ -22,6 +22,9 @@ class TriggeredEvent; class DLL_LINKAGE CMapFormatJson { public: + static const int VERSION_MAJOR; + static const int VERSION_MINOR; + static const std::string HEADER_FILE_NAME; protected: @@ -73,7 +76,16 @@ private: const JsonNode input; }; -class DLL_LINKAGE CMapLoaderJson : public CMapFormatJson, public IMapLoader +class DLL_LINKAGE CMapFormatZip : public CMapFormatJson +{ +public: + CMapFormatZip(CInputOutputStream * stream); +protected: + CInputOutputStream * buffer; + std::shared_ptr ioApi; +}; + +class DLL_LINKAGE CMapLoaderJson : public CMapFormatZip, public IMapLoader { public: /** @@ -81,7 +93,7 @@ public: * * @param stream a stream containing the map data */ - CMapLoaderJson(CInputStream * stream); + CMapLoaderJson(CInputOutputStream * stream); /** * Loads the VCMI/Json map file. @@ -112,12 +124,11 @@ private: * Reads player information. */ void readPlayerInfo(); - - - CInputStream * input; + + CZipLoader loader; }; -class DLL_LINKAGE CMapSaverJson : public CMapFormatJson, public IMapSaver +class DLL_LINKAGE CMapSaverJson : public CMapFormatZip, public IMapSaver { public: /** @@ -136,8 +147,6 @@ public: void saveMap(const std::unique_ptr & map) override; private: void saveHeader(); - - CInputOutputStream * output; - std::shared_ptr ioApi; + CZipSaver saver; }; diff --git a/test/CMapFormatTest.cpp b/test/CMapFormatTest.cpp index 6f8e625b1..593fc3902 100644 --- a/test/CMapFormatTest.cpp +++ b/test/CMapFormatTest.cpp @@ -40,6 +40,7 @@ public: CMapGenerator gen; initialMap = gen.generate(&opt, TEST_RANDOM_SEED); + initialMap->name = "Test"; }; ~CMapTestFixture() { @@ -53,20 +54,26 @@ BOOST_AUTO_TEST_CASE(CMapFormatVCMI_Simple) { try { + logGlobal->info("CMapFormatVCMI_Simple start"); CMemoryBuffer serializeBuffer; - CMapSaverJson saver(&serializeBuffer); - saver.saveMap(initialMap); - - CMapLoaderJson loader(&serializeBuffer); - serializeBuffer.seek(0); - std::unique_ptr serialized = loader.loadMap(); - - - MapComparer c; - c(initialMap, serialized); + { + CMapSaverJson saver(&serializeBuffer); + saver.saveMap(initialMap); + } + serializeBuffer.seek(0); + { + CMapLoaderJson loader(&serializeBuffer); + std::unique_ptr serialized = loader.loadMap(); + + MapComparer c; + c(initialMap, serialized); + } + + logGlobal->info("CMapFormatVCMI_Simple finish"); } catch(const std::exception & e) { + logGlobal->info("CMapFormatVCMI_Simple crash"); logGlobal-> errorStream() << e.what(); throw; }