mirror of
				https://github.com/vcmi/vcmi.git
				synced 2025-10-31 00:07:39 +02:00 
			
		
		
		
	Start implementing actulal json serialization
This commit is contained in:
		| @@ -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); | ||||
| 	 | ||||
| 	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; | ||||
| } | ||||
|  | ||||
|   | ||||
| @@ -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,10 +51,9 @@ si64 CZipOutputStream::write(const ui8 * data, si64 size) | ||||
| CZipSaver::CZipSaver(std::shared_ptr<CIOApi> 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) | ||||
| @@ -61,6 +62,13 @@ CZipSaver::CZipSaver(std::shared_ptr<CIOApi> api, const std::string & path): | ||||
|  | ||||
| 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<COutputStream> CZipSaver::addFile(const std::string & archiveFil | ||||
| 	if(activeStream != nullptr) | ||||
| 		throw new std::runtime_error("CZipSaver::addFile: stream already opened"); | ||||
| 	 | ||||
| 	std::unique_ptr<COutputStream> stream(new CZipOutputStream(handle, archiveFilename)); | ||||
| 	 | ||||
| 	activeStream = stream.get(); | ||||
| 	 | ||||
| 	return stream; | ||||
| 	std::unique_ptr<COutputStream> stream(new CZipOutputStream(this, handle, archiveFilename)); | ||||
| 	return std::move(stream); | ||||
| } | ||||
|  | ||||
|   | ||||
| @@ -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; | ||||
| }; | ||||
|   | ||||
| @@ -154,7 +154,7 @@ CProxyIOApi::~CProxyIOApi() | ||||
|  | ||||
| CInputOutputStream * CProxyIOApi::openFile(const std::string& filename, int mode) const | ||||
| { | ||||
| 	logGlobal->traceStream() << "CProxyIOApi: stream opened for" <<filename<<" with mode "<<mode;  | ||||
| 	logGlobal->traceStream() << "CProxyIOApi: stream opened for " <<filename<<" with mode "<<mode;  | ||||
| 	 | ||||
| 	data->seek(0); | ||||
| 	return data;//todo: check that only one "copy" is opened | ||||
|   | ||||
| @@ -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<char*>(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<std::string, ui8> 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<bool> allowedHeroes; | ||||
| //	std::vector<ui16> 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<CMap>& 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<ui8, std::string> 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<COutputStream> stream = saver.addFile(HEADER_FILE_NAME); | ||||
| 		 | ||||
| 		stream->write((const ui8*)s.c_str(), s.size()); | ||||
| 	}	 | ||||
|   | ||||
| @@ -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<CIOApi> 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. | ||||
| @@ -113,11 +125,10 @@ private: | ||||
| 	 */ | ||||
| 	void readPlayerInfo(); | ||||
| 	 | ||||
|  | ||||
| 	CInputStream * input; | ||||
| 	CZipLoader loader; | ||||
| }; | ||||
|  | ||||
| class DLL_LINKAGE CMapSaverJson : public CMapFormatJson, public IMapSaver | ||||
| class DLL_LINKAGE CMapSaverJson : public CMapFormatZip, public IMapSaver | ||||
| { | ||||
| public: | ||||
| 	/** | ||||
| @@ -137,7 +148,5 @@ public: | ||||
| private: | ||||
| 	void saveHeader(); | ||||
| 		 | ||||
| 	CInputOutputStream * output; | ||||
| 	std::shared_ptr<CIOApi> ioApi;		 | ||||
| 	CZipSaver saver;	 | ||||
| }; | ||||
|   | ||||
| @@ -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); | ||||
| 		{ | ||||
| 			CMapLoaderJson loader(&serializeBuffer); | ||||
| 			std::unique_ptr<CMap> 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; | ||||
| 	} | ||||
|   | ||||
		Reference in New Issue
	
	Block a user