浏览代码

Start implementing actulal json serialization

AlexVinS 10 年之前
父节点
当前提交
27a29bd035

+ 11 - 4
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;
 }
 

+ 17 - 12
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<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)
-		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<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);
 }
 

+ 5 - 1
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;
 };

+ 1 - 1
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" <<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

+ 94 - 7
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<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());
 	}	

+ 18 - 9
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<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.
@@ -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<CMap> & map) override;	
 private:
 	void saveHeader();
-	
-	CInputOutputStream * output;
-	std::shared_ptr<CIOApi> ioApi;		
+		
 	CZipSaver saver;	
 };

+ 17 - 10
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<CMap> serialized = loader.loadMap();
-		
-		
-		MapComparer c;		
-		c(initialMap, serialized);
+		{
+			CMapSaverJson saver(&serializeBuffer);
+			saver.saveMap(initialMap);
+		}
+		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;
 	}