Browse Source

Working Extractor, DefSplitter and pcx convertor

krs 2 years ago
parent
commit
93cf2682f3

+ 4 - 0
client/CMT.cpp

@@ -59,6 +59,7 @@
 #include "../lib/serializer/Connection.h"
 #include "CServerHandler.h"
 #include "gui/NotificationHandler.h"
+#include "resourceExtractor/ResourceConverter.h"
 
 #include <boost/asio.hpp>
 
@@ -253,6 +254,7 @@ int main(int argc, char * argv[])
 
 	// Init filesystem and settings
 	preinitDLL(::console);
+
 	settings.init();
 	Settings session = settings.write["session"];
 	auto setSettingBool = [](std::string key, std::string arg) {
@@ -464,6 +466,8 @@ int main(int argc, char * argv[])
 		CCS->curh = new CCursorHandler();
 		graphics = new Graphics(); // should be before curh->init()
 
+		ConvertOriginalResourceFiles();
+
 		CCS->curh->initCursor();
 		logGlobal->info("Screen handler: %d ms", pomtime.getDiff());
 		pomtime.getDiff();

+ 4 - 0
client/CMakeLists.txt

@@ -54,6 +54,8 @@ set(client_SRCS
 		lobby/OptionsTab.cpp
 		lobby/RandomMapTab.cpp
 		lobby/SelectionTab.cpp
+		
+		resourceExtractor/ResourceConverter.cpp
 
 		CBitmapHandler.cpp
 		CreatureCostBox.cpp
@@ -128,6 +130,8 @@ set(client_HEADERS
 		lobby/OptionsTab.h
 		lobby/RandomMapTab.h
 		lobby/SelectionTab.h
+		
+		resourceExtractor/ResourceConverter.h
 
 		CBitmapHandler.h
 		CreatureCostBox.h

+ 8 - 4
client/gui/CAnimation.cpp

@@ -951,7 +951,7 @@ void CAnimation::initFromJson(const JsonNode & config)
 	}
 }
 
-void CAnimation::exportBitmaps(const boost::filesystem::path& path) const
+void CAnimation::exportBitmaps(const boost::filesystem::path& path, bool prependResourceName) const
 {
 	if(images.empty())
 	{
@@ -959,7 +959,7 @@ void CAnimation::exportBitmaps(const boost::filesystem::path& path) const
 		return;
 	}
 
-	boost::filesystem::path actualPath = path / "SPRITES" / name;
+	boost::filesystem::path actualPath = path / "Sprites" / name;
 	boost::filesystem::create_directories(actualPath);
 
 	size_t counter = 0;
@@ -974,9 +974,13 @@ void CAnimation::exportBitmaps(const boost::filesystem::path& path) const
 			const auto img = imagePair.second;
 
 			boost::format fmt("%d_%d.bmp");
-			fmt % group % frame;
+			fmt% group% frame;
+			std::string fileName = fmt.str();
+			if (prependResourceName)
+				fileName = name + "_" + fileName;
+
+			img->exportBitmap(actualPath / fileName);
 
-			img->exportBitmap(actualPath / fmt.str());
 			counter++;
 		}
 	}

+ 1 - 1
client/gui/CAnimation.h

@@ -122,7 +122,7 @@ public:
 
 	std::shared_ptr<IImage> getImage(size_t frame, size_t group=0, bool verbose=true) const;
 
-	void exportBitmaps(const boost::filesystem::path & path) const;
+	void exportBitmaps(const boost::filesystem::path & path, bool prependResourceName = false) const;
 
 	//all available frames
 	void load  ();

+ 126 - 0
client/resourceExtractor/ResourceConverter.cpp

@@ -0,0 +1,126 @@
+#include "StdInc.h"
+
+#include "ResourceConverter.h"
+
+#include "../lib/JsonNode.h"
+#include "../lib/VCMIDirs.h"
+#include "../lib/filesystem/Filesystem.h"
+
+#include "SDL.h"
+#include "./gui/CAnimation.h"
+#include "CBitmapHandler.h"
+
+#include "boost/filesystem/path.hpp"
+#include "boost/locale.hpp"
+
+namespace bfs = boost::filesystem;
+
+bool split_def_files = 1;
+bool convert_pcx_to_bmp = 1; // converts Images from .pcx to bmp. Can be used when you have .pcx converted already
+bool delete_source_files = 1; // delete source files or leave a copy in place.
+
+// converts all pcx files into bmp (H3 saves images as .pcx)
+void convertPcxToBmp()
+{
+	bfs::path extractedPath = VCMIDirs::get().userDataPath() / "extracted";
+	bfs::path imagesPath = extractedPath / "Images";
+
+	bfs::directory_iterator end_iter;
+
+	for ( bfs::directory_iterator dir_itr(imagesPath); dir_itr != end_iter; ++dir_itr )
+	{
+		try
+		{
+			if ( bfs::is_regular_file( dir_itr->status() ) )
+			{
+				std::string filename = dir_itr->path().filename().string();
+				filename = boost::locale::to_lower(filename);
+
+				if(filename.find(".pcx") != std::string::npos)
+				{
+					SDL_Surface *bitmap;
+					
+					bitmap = BitmapHandler::loadBitmap(filename);
+					
+					if(delete_source_files)
+						bfs::remove(imagesPath / filename);
+
+					bfs::path outFilePath = imagesPath / filename;
+					outFilePath.replace_extension(".bmp");
+	 				SDL_SaveBMP(bitmap, outFilePath.string().c_str());
+				}
+			}
+			else
+			{
+				logGlobal->info(dir_itr->path().filename().string() + " [other]\n");
+			}
+		}
+		catch ( const std::exception & ex )
+		{
+			logGlobal->info(dir_itr->path().filename().string() + " " + ex.what() + "\n");
+		}
+	}
+}
+
+// splits a def file into individual parts
+void splitDefFile(std::string fileName, bfs::path spritesPath)
+{
+	if (CResourceHandler::get()->existsResource(ResourceID("SPRITES/" + fileName)))
+	{
+		std::string URI = fileName;
+		std::unique_ptr<CAnimation> anim = make_unique<CAnimation>(URI);
+		anim->preload();
+		anim->exportBitmaps(VCMIDirs::get().userCachePath() / "extracted", true);
+
+		if(delete_source_files)
+			bfs::remove(spritesPath / fileName);
+	}
+	else
+		logGlobal->error("Def File Split error! " + fileName);
+}
+
+// split def files, this way faction resources are independent
+void splitDefFiles()
+{
+	bfs::path extractedPath = VCMIDirs::get().userDataPath() / "extracted";
+	bfs::path spritesPath = extractedPath / "Sprites";
+
+	splitDefFile("TwCrPort.def", spritesPath);	// split town creature portraits
+	splitDefFile("CPRSMALL.def", spritesPath);	// split hero army creature portraits 
+	splitDefFile("FlagPort.def", spritesPath);	// adventure map dwellings
+	splitDefFile("ITPA.def", spritesPath);		// small town icons
+	splitDefFile("ITPt.def", spritesPath);		// big town icons
+	splitDefFile("Un32.def", spritesPath);		// big town icons
+	splitDefFile("Un44.def", spritesPath);		// big town icons
+}
+
+// Splits def files that are shared between factions and converts pcx to bmp
+void ConvertOriginalResourceFiles()
+{
+	if (split_def_files)
+		splitDefFiles();
+
+	if (convert_pcx_to_bmp)
+		convertPcxToBmp();
+}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+

+ 13 - 0
client/resourceExtractor/ResourceConverter.h

@@ -0,0 +1,13 @@
+#pragma once
+
+/*
+ * VCMI_Lib.cpp, part of VCMI engine
+ *
+ * Authors: listed in file AUTHORS in main folder
+ *
+ * License: GNU General Public License v2.0 or later
+ * Full text of license available in license.txt file, in main folder
+ *
+ */
+ 
+void ConvertOriginalResourceFiles();

+ 78 - 1
lib/filesystem/CArchiveLoader.cpp

@@ -10,6 +10,7 @@
 #include "StdInc.h"
 #include "CArchiveLoader.h"
 
+#include "VCMIDirs.h"
 #include "CFileInputStream.h"
 #include "CCompressedStream.h"
 
@@ -17,13 +18,17 @@
 
 VCMI_LIB_NAMESPACE_BEGIN
 
+namespace bfs = boost::filesystem;
+
+const bool extractArchives = 1;
+
 ArchiveEntry::ArchiveEntry()
 	: offset(0), fullSize(0), compressedSize(0)
 {
 
 }
 
-CArchiveLoader::CArchiveLoader(std::string _mountPoint, boost::filesystem::path _archive) :
+CArchiveLoader::CArchiveLoader(std::string _mountPoint, bfs::path _archive) :
     archive(std::move(_archive)),
     mountPoint(std::move(_mountPoint))
 {
@@ -77,6 +82,28 @@ void CArchiveLoader::initLODArchive(const std::string &mountPoint, CFileInputStr
 
 		// Add lod entry to local entries map
 		entries[ResourceID(mountPoint + entry.name)] = entry;
+
+		if(extractArchives)
+		{
+			si64 currentPosition = fileStream.tell(); // save filestream position
+		
+			boost::locale::generator gen;
+			std::locale::global(gen(""));         // Create locale generator
+
+			std::string fName = filename;
+			boost::to_upper(fName);
+
+			if(fName.find(".PCX") != std::string::npos)
+				extractToFolder("Images", mountPoint, entry);
+			else if ((fName.find(".DEF") != std::string::npos ) || (fName.find(".MSK") != std::string::npos) || (fName.find(".FNT") != std::string::npos) || (fName.find(".PAL") != std::string::npos))
+				extractToFolder("Sprites", mountPoint, entry);
+			else if ((fName.find(".h3c") != std::string::npos))
+				extractToFolder("Sprites", mountPoint, entry);
+			else
+				extractToFolder("Misc", mountPoint, entry);
+
+			fileStream.seek(currentPosition); // restore filestream position
+		}
 	}
 }
 
@@ -112,6 +139,9 @@ void CArchiveLoader::initVIDArchive(const std::string &mountPoint, CFileInputStr
 		auto it = offsets.find(entry.second.offset);
 		it++;
 		entry.second.fullSize = *it - entry.second.offset;
+
+		if(extractArchives)
+			extractToFolder("Video", fileStream, entry.second);
 	}
 }
 
@@ -139,6 +169,9 @@ void CArchiveLoader::initSNDArchive(const std::string &mountPoint, CFileInputStr
 		entry.fullSize = reader.readInt32();
 		entry.compressedSize = 0;
 		entries[ResourceID(mountPoint + entry.name)] = entry;
+
+		if(extractArchives)
+			extractToFolder("Sound", fileStream, entry);
 	}
 }
 
@@ -182,4 +215,48 @@ std::unordered_set<ResourceID> CArchiveLoader::getFilteredFiles(std::function<bo
 	return foundID;
 }
 
+void CArchiveLoader::extractToFolder( std::string outputSubFolder, CFileInputStream& fileStream, ArchiveEntry entry)
+{
+	si64 currentPosition = fileStream.tell(); // save filestream position
+
+	std::unique_ptr<char[]> data = std::unique_ptr<char[]>(new char[entry.fullSize]);
+	fileStream.seek(entry.offset);
+	fileStream.read((ui8*)data.get(), entry.fullSize);
+
+	bfs::path extractedFilePath = createExtractedFilePath(outputSubFolder, entry.name);
+
+	// writeToOutputFile
+	std::ofstream out(extractedFilePath.string(), std::ofstream::binary);
+	out.exceptions(std::ifstream::failbit | std::ifstream::badbit);
+	out.write(data.get(), entry.fullSize);
+
+	fileStream.seek(currentPosition); // restore filestream position
+}
+
+void CArchiveLoader::extractToFolder( std::string outputSubFolder, const std::string& mountPoint, ArchiveEntry entry)
+{
+
+	std::unique_ptr<CInputStream> & inputStream = load(ResourceID(mountPoint + entry.name));
+
+	std::unique_ptr<char[]> data = std::unique_ptr<char[]>(new char[entry.fullSize]);
+	inputStream->read((ui8*)data.get(), entry.fullSize);
+
+	bfs::path extractedFilePath = createExtractedFilePath(outputSubFolder, entry.name);
+
+	// writeToOutputFile
+	std::ofstream out(extractedFilePath.string(), std::ofstream::binary);
+	out.exceptions(std::ifstream::failbit | std::ifstream::badbit);
+	out.write(data.get(), entry.fullSize);
+}
+
+bfs::path createExtractedFilePath(std::string outputSubFolder, std::string entryName)
+{
+	bfs::path extractionFolderPath = VCMIDirs::get().userCachePath() / "extracted" / outputSubFolder;
+	bfs::path extractedFilePath = extractionFolderPath / entryName;
+
+	bfs::create_directories(extractionFolderPath);
+
+	return extractedFilePath;
+}
+
 VCMI_LIB_NAMESPACE_END

+ 7 - 0
lib/filesystem/CArchiveLoader.h

@@ -64,6 +64,10 @@ public:
 	std::string getMountPoint() const override;
 	void updateFilteredFiles(std::function<bool(const std::string &)> filter) const override {}
 	std::unordered_set<ResourceID> getFilteredFiles(std::function<bool(const ResourceID &)> filter) const override;
+	/** Extracts one archive entry to the specified subfolder. Used for Video and Sound */
+	void extractToFolder(std::string outputSubFolder, CFileInputStream & fileStream, ArchiveEntry entry);
+	/** Extracts one archive entry to the specified subfolder. Used for Images, Sprites, etc */
+	void extractToFolder(std::string outputSubFolder, const std::string &mountPoint, ArchiveEntry entry);
 
 private:
 	/**
@@ -96,4 +100,7 @@ private:
 	std::unordered_map<ResourceID, ArchiveEntry> entries;
 };
 
+/** Constructs the file path for the extracted file. Creates the subfolder hierarchy aswell **/
+boost::filesystem::path createExtractedFilePath(std::string outputSubFolder, std::string entryName);
+
 VCMI_LIB_NAMESPACE_END