mirror of
https://github.com/vcmi/vcmi.git
synced 2025-03-19 21:10:12 +02:00
Merge pull request #1119 from krs0/feature/H3_original_files_extractor
VCMI H3 original archives extractor #1118
This commit is contained in:
commit
c6d317e409
@ -667,7 +667,7 @@ void processCommand(const std::string &message)
|
||||
std::cout << "Command accepted.\t";
|
||||
|
||||
const bfs::path outPath =
|
||||
VCMIDirs::get().userCachePath() / "extracted";
|
||||
VCMIDirs::get().userExtractedPath();
|
||||
|
||||
bfs::create_directories(outPath);
|
||||
|
||||
@ -700,7 +700,7 @@ void processCommand(const std::string &message)
|
||||
std::cout << "Command accepted.\t";
|
||||
|
||||
const bfs::path outPath =
|
||||
VCMIDirs::get().userCachePath() / "extracted" / "configuration";
|
||||
VCMIDirs::get().userExtractedPath() / "configuration";
|
||||
|
||||
bfs::create_directories(outPath);
|
||||
|
||||
@ -741,7 +741,7 @@ void processCommand(const std::string &message)
|
||||
std::cout << "Command accepted.\t";
|
||||
|
||||
const bfs::path outPath =
|
||||
VCMIDirs::get().userCachePath() / "extracted" / "scripts";
|
||||
VCMIDirs::get().userExtractedPath() / "scripts";
|
||||
|
||||
bfs::create_directories(outPath);
|
||||
|
||||
@ -764,7 +764,7 @@ void processCommand(const std::string &message)
|
||||
std::cout << "Command accepted.\t";
|
||||
|
||||
const bfs::path outPath =
|
||||
VCMIDirs::get().userCachePath() / "extracted";
|
||||
VCMIDirs::get().userExtractedPath();
|
||||
|
||||
auto list = CResourceHandler::get()->getFilteredFiles([](const ResourceID & ident)
|
||||
{
|
||||
@ -880,7 +880,7 @@ void processCommand(const std::string &message)
|
||||
readed >> URI;
|
||||
std::unique_ptr<CAnimation> anim = make_unique<CAnimation>(URI);
|
||||
anim->preload();
|
||||
anim->exportBitmaps(VCMIDirs::get().userCachePath() / "extracted");
|
||||
anim->exportBitmaps(VCMIDirs::get().userExtractedPath());
|
||||
}
|
||||
else if(cn == "extract")
|
||||
{
|
||||
@ -889,7 +889,7 @@ void processCommand(const std::string &message)
|
||||
|
||||
if (CResourceHandler::get()->existsResource(ResourceID(URI)))
|
||||
{
|
||||
const bfs::path outPath = VCMIDirs::get().userCachePath() / "extracted" / URI;
|
||||
const bfs::path outPath = VCMIDirs::get().userExtractedPath() / URI;
|
||||
|
||||
auto data = CResourceHandler::get()->load(ResourceID(URI))->readAll();
|
||||
|
||||
|
@ -23,6 +23,8 @@ bfs::path IVCMIDirs::userLogsPath() const { return userCachePath(); }
|
||||
|
||||
bfs::path IVCMIDirs::userSavePath() const { return userDataPath() / "Saves"; }
|
||||
|
||||
bfs::path IVCMIDirs::userExtractedPath() const { return userCachePath() / "extracted"; }
|
||||
|
||||
bfs::path IVCMIDirs::fullLibraryPath(const std::string &desiredFolder, const std::string &baseLibName) const
|
||||
{
|
||||
return libraryPath() / desiredFolder / libraryName(baseLibName);
|
||||
@ -36,15 +38,16 @@ std::string IVCMIDirs::genHelpString() const
|
||||
const auto gdStringA = boost::algorithm::join(tempVec, ":");
|
||||
|
||||
return
|
||||
" game data: " + gdStringA + "\n"
|
||||
" libraries: " + libraryPath().string() + "\n"
|
||||
" server: " + serverPath().string() + "\n"
|
||||
" game data: " + gdStringA + "\n"
|
||||
" libraries: " + libraryPath().string() + "\n"
|
||||
" server: " + serverPath().string() + "\n"
|
||||
"\n"
|
||||
" user data: " + userDataPath().string() + "\n"
|
||||
" user cache: " + userCachePath().string() + "\n"
|
||||
" user config: " + userConfigPath().string() + "\n"
|
||||
" user logs: " + userLogsPath().string() + "\n"
|
||||
" user saves: " + userSavePath().string() + "\n"; // Should end without new-line?
|
||||
" user data: " + userDataPath().string() + "\n"
|
||||
" user cache: " + userCachePath().string() + "\n"
|
||||
" user config: " + userConfigPath().string() + "\n"
|
||||
" user logs: " + userLogsPath().string() + "\n"
|
||||
" user saves: " + userSavePath().string() + "\n";
|
||||
" user extracted: " + userExtractedPath().string() + "\n"; // Should end without new-line?
|
||||
}
|
||||
|
||||
void IVCMIDirs::init()
|
||||
@ -148,24 +151,24 @@ bool StartBatchCopyDataProgram(
|
||||
class VCMIDirsWIN32 final : public IVCMIDirs
|
||||
{
|
||||
public:
|
||||
boost::filesystem::path userDataPath() const override;
|
||||
boost::filesystem::path userCachePath() const override;
|
||||
boost::filesystem::path userConfigPath() const override;
|
||||
bfs::path userDataPath() const override;
|
||||
bfs::path userCachePath() const override;
|
||||
bfs::path userConfigPath() const override;
|
||||
|
||||
std::vector<boost::filesystem::path> dataPaths() const override;
|
||||
std::vector<bfs::path> dataPaths() const override;
|
||||
|
||||
boost::filesystem::path clientPath() const override;
|
||||
boost::filesystem::path serverPath() const override;
|
||||
bfs::path clientPath() const override;
|
||||
bfs::path serverPath() const override;
|
||||
|
||||
boost::filesystem::path libraryPath() const override;
|
||||
boost::filesystem::path binaryPath() const override;
|
||||
bfs::path libraryPath() const override;
|
||||
bfs::path binaryPath() const override;
|
||||
|
||||
std::string libraryName(const std::string& basename) const override;
|
||||
|
||||
void init() override;
|
||||
protected:
|
||||
boost::filesystem::path oldUserDataPath() const;
|
||||
boost::filesystem::path oldUserSavePath() const;
|
||||
bfs::path oldUserDataPath() const;
|
||||
bfs::path oldUserSavePath() const;
|
||||
};
|
||||
|
||||
void VCMIDirsWIN32::init()
|
||||
@ -355,8 +358,8 @@ std::string VCMIDirsWIN32::libraryName(const std::string& basename) const { retu
|
||||
class IVCMIDirsUNIX : public IVCMIDirs
|
||||
{
|
||||
public:
|
||||
boost::filesystem::path clientPath() const override;
|
||||
boost::filesystem::path serverPath() const override;
|
||||
bfs::path clientPath() const override;
|
||||
bfs::path serverPath() const override;
|
||||
|
||||
virtual bool developmentMode() const;
|
||||
};
|
||||
@ -427,14 +430,14 @@ bfs::path VCMIDirsIOS::binaryPath() const { return {iOS_utils::bundlePath()}; }
|
||||
class VCMIDirsOSX final : public VCMIDirsApple
|
||||
{
|
||||
public:
|
||||
boost::filesystem::path userDataPath() const override;
|
||||
boost::filesystem::path userCachePath() const override;
|
||||
boost::filesystem::path userLogsPath() const override;
|
||||
bfs::path userDataPath() const override;
|
||||
bfs::path userCachePath() const override;
|
||||
bfs::path userLogsPath() const override;
|
||||
|
||||
std::vector<boost::filesystem::path> dataPaths() const override;
|
||||
std::vector<bfs::path> dataPaths() const override;
|
||||
|
||||
boost::filesystem::path libraryPath() const override;
|
||||
boost::filesystem::path binaryPath() const override;
|
||||
bfs::path libraryPath() const override;
|
||||
bfs::path binaryPath() const override;
|
||||
|
||||
void init() override;
|
||||
};
|
||||
@ -464,12 +467,12 @@ void VCMIDirsOSX::init()
|
||||
|
||||
for (bfs::directory_iterator file(from); file != bfs::directory_iterator(); ++file)
|
||||
{
|
||||
const boost::filesystem::path& srcFilePath = file->path();
|
||||
const boost::filesystem::path dstFilePath = to / srcFilePath.filename();
|
||||
const bfs::path& srcFilePath = file->path();
|
||||
const bfs::path dstFilePath = to / srcFilePath.filename();
|
||||
|
||||
// TODO: Aplication should ask user what to do when file exists:
|
||||
// replace/ignore/stop process/replace all/ignore all
|
||||
if (!boost::filesystem::exists(dstFilePath))
|
||||
if (!bfs::exists(dstFilePath))
|
||||
bfs::rename(srcFilePath, dstFilePath);
|
||||
}
|
||||
|
||||
@ -526,14 +529,14 @@ bfs::path VCMIDirsOSX::binaryPath() const { return "."; }
|
||||
class VCMIDirsXDG : public IVCMIDirsUNIX
|
||||
{
|
||||
public:
|
||||
boost::filesystem::path userDataPath() const override;
|
||||
boost::filesystem::path userCachePath() const override;
|
||||
boost::filesystem::path userConfigPath() const override;
|
||||
bfs::path userDataPath() const override;
|
||||
bfs::path userCachePath() const override;
|
||||
bfs::path userConfigPath() const override;
|
||||
|
||||
std::vector<boost::filesystem::path> dataPaths() const override;
|
||||
std::vector<bfs::path> dataPaths() const override;
|
||||
|
||||
boost::filesystem::path libraryPath() const override;
|
||||
boost::filesystem::path binaryPath() const override;
|
||||
bfs::path libraryPath() const override;
|
||||
bfs::path binaryPath() const override;
|
||||
|
||||
std::string libraryName(const std::string& basename) const override;
|
||||
};
|
||||
|
@ -29,6 +29,9 @@ public:
|
||||
// Path to saved games
|
||||
virtual boost::filesystem::path userSavePath() const;
|
||||
|
||||
// Path to "extracted" directory, used to temporarily hold extracted Original H3 files
|
||||
virtual boost::filesystem::path userExtractedPath() const;
|
||||
|
||||
// Paths to global system-wide data directories. First items have higher priority
|
||||
virtual std::vector<boost::filesystem::path> dataPaths() const = 0;
|
||||
|
||||
|
@ -39,13 +39,13 @@ VCMI_LIB_NAMESPACE_BEGIN
|
||||
|
||||
LibClasses * VLC = nullptr;
|
||||
|
||||
DLL_LINKAGE void preinitDLL(CConsoleHandler * Console, bool onlyEssential)
|
||||
DLL_LINKAGE void preinitDLL(CConsoleHandler * Console, bool onlyEssential, bool extractArchives)
|
||||
{
|
||||
console = Console;
|
||||
VLC = new LibClasses();
|
||||
try
|
||||
{
|
||||
VLC->loadFilesystem(onlyEssential);
|
||||
VLC->loadFilesystem(onlyEssential, extractArchives);
|
||||
}
|
||||
catch(...)
|
||||
{
|
||||
@ -157,7 +157,7 @@ void LibClasses::updateEntity(Metatype metatype, int32_t index, const JsonNode &
|
||||
}
|
||||
}
|
||||
|
||||
void LibClasses::loadFilesystem(bool onlyEssential)
|
||||
void LibClasses::loadFilesystem(bool onlyEssential, bool extractArchives)
|
||||
{
|
||||
CStopWatch totalTime;
|
||||
CStopWatch loadTime;
|
||||
@ -165,7 +165,7 @@ void LibClasses::loadFilesystem(bool onlyEssential)
|
||||
CResourceHandler::initialize();
|
||||
logGlobal->info("\tInitialization: %d ms", loadTime.getDiff());
|
||||
|
||||
CResourceHandler::load("config/filesystem.json");
|
||||
CResourceHandler::load("config/filesystem.json", extractArchives);
|
||||
logGlobal->info("\tData loading: %d ms", loadTime.getDiff());
|
||||
|
||||
modh = new CModHandler();
|
||||
|
@ -100,8 +100,8 @@ public:
|
||||
void init(bool onlyEssential); //uses standard config file
|
||||
void clear(); //deletes all handlers and its data
|
||||
|
||||
|
||||
void loadFilesystem(bool onlyEssential);// basic initialization. should be called before init()
|
||||
// basic initialization. should be called before init(). Can also extract original H3 archives
|
||||
void loadFilesystem(bool onlyEssential, bool extractArchives = false);
|
||||
|
||||
#if SCRIPTING_ENABLED
|
||||
void scriptsLoaded();
|
||||
@ -151,7 +151,7 @@ public:
|
||||
|
||||
extern DLL_LINKAGE LibClasses * VLC;
|
||||
|
||||
DLL_LINKAGE void preinitDLL(CConsoleHandler * Console, bool onlyEssential = false);
|
||||
DLL_LINKAGE void preinitDLL(CConsoleHandler * Console, bool onlyEssential = false, bool extractArchives = false);
|
||||
DLL_LINKAGE void loadDLLClasses(bool onlyEssential = false);
|
||||
|
||||
|
||||
|
@ -10,6 +10,7 @@
|
||||
#include "StdInc.h"
|
||||
#include "CArchiveLoader.h"
|
||||
|
||||
#include "VCMIDirs.h"
|
||||
#include "CFileInputStream.h"
|
||||
#include "CCompressedStream.h"
|
||||
|
||||
@ -23,9 +24,10 @@ ArchiveEntry::ArchiveEntry()
|
||||
|
||||
}
|
||||
|
||||
CArchiveLoader::CArchiveLoader(std::string _mountPoint, boost::filesystem::path _archive) :
|
||||
CArchiveLoader::CArchiveLoader(std::string _mountPoint, bfs::path _archive, bool extractArchives) :
|
||||
archive(std::move(_archive)),
|
||||
mountPoint(std::move(_mountPoint))
|
||||
mountPoint(std::move(_mountPoint)),
|
||||
extractArchives(extractArchives)
|
||||
{
|
||||
// Open archive file(.snd, .vid, .lod)
|
||||
CFileInputStream fileStream(archive);
|
||||
@ -77,6 +79,25 @@ 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
|
||||
|
||||
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 +133,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 +163,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 +209,39 @@ std::unordered_set<ResourceID> CArchiveLoader::getFilteredFiles(std::function<bo
|
||||
return foundID;
|
||||
}
|
||||
|
||||
void CArchiveLoader::extractToFolder(const std::string & outputSubFolder, CInputStream & fileStream, ArchiveEntry entry)
|
||||
{
|
||||
si64 currentPosition = fileStream.tell(); // save filestream position
|
||||
|
||||
std::vector<ui8> data(entry.fullSize);
|
||||
fileStream.seek(entry.offset);
|
||||
fileStream.read(data.data(), 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((char*)data.data(), entry.fullSize);
|
||||
|
||||
fileStream.seek(currentPosition); // restore filestream position
|
||||
}
|
||||
|
||||
void CArchiveLoader::extractToFolder(const std::string & outputSubFolder, const std::string & mountPoint, ArchiveEntry entry)
|
||||
{
|
||||
std::unique_ptr<CInputStream> inputStream = load(ResourceID(mountPoint + entry.name));
|
||||
|
||||
extractToFolder(outputSubFolder, *inputStream, entry);
|
||||
}
|
||||
|
||||
bfs::path createExtractedFilePath(const std::string & outputSubFolder, const std::string & entryName)
|
||||
{
|
||||
bfs::path extractionFolderPath = VCMIDirs::get().userExtractedPath() / outputSubFolder;
|
||||
bfs::path extractedFilePath = extractionFolderPath / entryName;
|
||||
|
||||
bfs::create_directories(extractionFolderPath);
|
||||
|
||||
return extractedFilePath;
|
||||
}
|
||||
|
||||
VCMI_LIB_NAMESPACE_END
|
||||
|
@ -12,6 +12,8 @@
|
||||
#include "ISimpleResourceLoader.h"
|
||||
#include "ResourceID.h"
|
||||
|
||||
namespace bfs = boost::filesystem;
|
||||
|
||||
VCMI_LIB_NAMESPACE_BEGIN
|
||||
|
||||
class CFileInputStream;
|
||||
@ -52,10 +54,11 @@ public:
|
||||
* These are valid extensions: .LOD, .SND, .VID
|
||||
*
|
||||
* @param archive Specifies the file path to the archive which should be indexed and loaded.
|
||||
* @param extractArchives Specifies if the original H3 archives should be extracted to a separate folder.
|
||||
*
|
||||
* @throws std::runtime_error if the archive wasn't found or if the archive isn't supported
|
||||
*/
|
||||
CArchiveLoader(std::string mountPoint, boost::filesystem::path archive);
|
||||
CArchiveLoader(std::string mountPoint, bfs::path archive, bool extractArchives = false);
|
||||
|
||||
/// Interface implementation
|
||||
/// @see ISimpleResourceLoader
|
||||
@ -64,6 +67,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(const std::string & outputSubFolder, CInputStream & fileStream, ArchiveEntry entry);
|
||||
/** Extracts one archive entry to the specified subfolder. Used for Images, Sprites, etc */
|
||||
void extractToFolder(const std::string & outputSubFolder, const std::string & mountPoint, ArchiveEntry entry);
|
||||
|
||||
private:
|
||||
/**
|
||||
@ -88,12 +95,18 @@ private:
|
||||
void initSNDArchive(const std::string &mountPoint, CFileInputStream & fileStream);
|
||||
|
||||
/** The file path to the archive which is scanned and indexed. */
|
||||
boost::filesystem::path archive;
|
||||
bfs::path archive;
|
||||
|
||||
std::string mountPoint;
|
||||
|
||||
/** Holds all entries of the archive file. An entry can be accessed via the entry name. **/
|
||||
std::unordered_map<ResourceID, ArchiveEntry> entries;
|
||||
|
||||
/** Specifies if Original H3 archives should be extracted to a separate folder **/
|
||||
bool extractArchives;
|
||||
};
|
||||
|
||||
/** Constructs the file path for the extracted file. Creates the subfolder hierarchy aswell **/
|
||||
bfs::path createExtractedFilePath(const std::string & outputSubFolder, const std::string & entryName);
|
||||
|
||||
VCMI_LIB_NAMESPACE_END
|
||||
|
@ -26,9 +26,10 @@ VCMI_LIB_NAMESPACE_BEGIN
|
||||
std::map<std::string, ISimpleResourceLoader*> CResourceHandler::knownLoaders = std::map<std::string, ISimpleResourceLoader*>();
|
||||
CResourceHandler CResourceHandler::globalResourceHandler;
|
||||
|
||||
CFilesystemGenerator::CFilesystemGenerator(std::string prefix):
|
||||
CFilesystemGenerator::CFilesystemGenerator(std::string prefix, bool extractArchives):
|
||||
filesystem(new CFilesystemList()),
|
||||
prefix(prefix)
|
||||
prefix(prefix),
|
||||
extractArchives(extractArchives)
|
||||
{
|
||||
}
|
||||
|
||||
@ -105,7 +106,7 @@ void CFilesystemGenerator::loadArchive(const std::string &mountPoint, const Json
|
||||
std::string URI = prefix + config["path"].String();
|
||||
auto filename = CResourceHandler::get("initial")->getResourceName(ResourceID(URI, archiveType));
|
||||
if (filename)
|
||||
filesystem->addLoader(new CArchiveLoader(mountPoint, *filename), false);
|
||||
filesystem->addLoader(new CArchiveLoader(mountPoint, *filename, extractArchives), false);
|
||||
}
|
||||
|
||||
void CFilesystemGenerator::loadJsonMap(const std::string &mountPoint, const JsonNode & config)
|
||||
@ -200,13 +201,13 @@ ISimpleResourceLoader * CResourceHandler::get(std::string identifier)
|
||||
return knownLoaders.at(identifier);
|
||||
}
|
||||
|
||||
void CResourceHandler::load(const std::string &fsConfigURI)
|
||||
void CResourceHandler::load(const std::string &fsConfigURI, bool extractArchives)
|
||||
{
|
||||
auto fsConfigData = get("initial")->load(ResourceID(fsConfigURI, EResType::TEXT))->readAll();
|
||||
|
||||
const JsonNode fsConfig((char*)fsConfigData.first.get(), fsConfigData.second);
|
||||
|
||||
addFilesystem("data", "core", createFileSystem("", fsConfig["filesystem"]));
|
||||
addFilesystem("data", "core", createFileSystem("", fsConfig["filesystem"], extractArchives));
|
||||
}
|
||||
|
||||
void CResourceHandler::addFilesystem(const std::string & parent, const std::string & identifier, ISimpleResourceLoader * loader)
|
||||
@ -246,9 +247,9 @@ bool CResourceHandler::removeFilesystem(const std::string & parent, const std::s
|
||||
return true;
|
||||
}
|
||||
|
||||
ISimpleResourceLoader * CResourceHandler::createFileSystem(const std::string & prefix, const JsonNode &fsConfig)
|
||||
ISimpleResourceLoader * CResourceHandler::createFileSystem(const std::string & prefix, const JsonNode &fsConfig, bool extractArchives)
|
||||
{
|
||||
CFilesystemGenerator generator(prefix);
|
||||
CFilesystemGenerator generator(prefix, extractArchives);
|
||||
generator.loadConfig(fsConfig);
|
||||
return generator.getFilesystem();
|
||||
}
|
||||
|
@ -36,7 +36,8 @@ class DLL_LINKAGE CFilesystemGenerator
|
||||
TLoadFunctorMap genFunctorMap();
|
||||
public:
|
||||
/// prefix = prefix that will be given to file entries in all nodes of this filesystem
|
||||
CFilesystemGenerator(std::string prefix);
|
||||
/// extractArchives = Specifies if Original H3 archives should be extracted to a separate folder
|
||||
CFilesystemGenerator(std::string prefix, bool extractArchives = false);
|
||||
|
||||
/// loads configuration from json
|
||||
/// config - configuration to load, using format of "filesystem" entry in config/filesystem.json
|
||||
@ -44,6 +45,9 @@ public:
|
||||
|
||||
/// returns generated filesystem
|
||||
CFilesystemList * getFilesystem();
|
||||
|
||||
/** Specifies if Original H3 archives should be extracted to a separate folder **/
|
||||
bool extractArchives;
|
||||
};
|
||||
|
||||
/**
|
||||
@ -81,7 +85,7 @@ public:
|
||||
* Will load all filesystem data from Json data at this path (normally - config/filesystem.json)
|
||||
* @param fsConfigURI - URI from which data will be loaded
|
||||
*/
|
||||
static void load(const std::string & fsConfigURI);
|
||||
static void load(const std::string & fsConfigURI, bool extractArchives = false);
|
||||
|
||||
/**
|
||||
* @brief addFilesystem adds filesystem into global resource loader
|
||||
@ -104,7 +108,7 @@ public:
|
||||
* @param fsConfig - configuration to load
|
||||
* @return generated filesystem that contains all config entries
|
||||
*/
|
||||
static ISimpleResourceLoader * createFileSystem(const std::string &prefix, const JsonNode & fsConfig);
|
||||
static ISimpleResourceLoader * createFileSystem(const std::string &prefix, const JsonNode & fsConfig, bool extractArchives = false);
|
||||
|
||||
~CResourceHandler() = default;
|
||||
private:
|
||||
|
@ -19,7 +19,7 @@
|
||||
#include "../lib/filesystem/ISimpleResourceLoader.h"
|
||||
#include "../lib/JsonNode.h"
|
||||
#include "../lib/CRandomGenerator.h"
|
||||
|
||||
#include "../lib/VCMIDirs.h"
|
||||
|
||||
typedef std::map<size_t, std::vector<JsonNode>> source_map;
|
||||
|
||||
@ -686,6 +686,39 @@ std::shared_ptr<QImage> Animation::getImage(size_t frame, size_t group, bool ver
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void Animation::exportBitmaps(const QDir & path) const
|
||||
{
|
||||
if(images.empty())
|
||||
{
|
||||
logGlobal->error("Nothing to export, animation is empty");
|
||||
return;
|
||||
}
|
||||
|
||||
QString actualPath = path.absolutePath() + "/SPRITES/" + QString::fromStdString(name);
|
||||
QDir().mkdir(actualPath);
|
||||
|
||||
size_t counter = 0;
|
||||
|
||||
for(const auto& groupPair : images)
|
||||
{
|
||||
size_t group = groupPair.first;
|
||||
|
||||
for(const auto& imagePair : groupPair.second)
|
||||
{
|
||||
size_t frame = imagePair.first;
|
||||
const auto img = imagePair.second;
|
||||
|
||||
QString filename = QString("%1_%2_%3.png").arg(QString::fromStdString(name)).arg(group).arg(frame);
|
||||
QString filePath = actualPath + "/" + filename;
|
||||
img->save(filePath, "PNG");
|
||||
|
||||
counter++;
|
||||
}
|
||||
}
|
||||
|
||||
logGlobal->info("Exported %d frames to %s", counter, actualPath.toStdString());
|
||||
}
|
||||
|
||||
void Animation::load()
|
||||
{
|
||||
for (auto & elem : source)
|
||||
@ -774,4 +807,4 @@ void Animation::createFlippedGroup(const size_t sourceGroup, const size_t target
|
||||
auto image = getImage(frame, targetGroup);
|
||||
*image = image->transformed(QTransform::fromScale(1, -1));
|
||||
}
|
||||
}
|
||||
}
|
@ -83,6 +83,8 @@ public:
|
||||
void load (size_t frame, size_t group = 0);
|
||||
void unload(size_t frame, size_t group = 0);
|
||||
|
||||
void exportBitmaps(const QDir & path) const;
|
||||
|
||||
//total count of frames in group (including not loaded)
|
||||
size_t size(size_t group = 0) const;
|
||||
|
||||
|
@ -24,6 +24,7 @@ set(editor_SRCS
|
||||
inspector/messagewidget.cpp
|
||||
inspector/rewardswidget.cpp
|
||||
inspector/questwidget.cpp
|
||||
resourceExtractor/ResourceConverter.cpp
|
||||
)
|
||||
|
||||
set(editor_HEADERS
|
||||
@ -51,6 +52,7 @@ set(editor_HEADERS
|
||||
inspector/messagewidget.h
|
||||
inspector/rewardswidget.h
|
||||
inspector/questwidget.h
|
||||
resourceExtractor/ResourceConverter.h
|
||||
)
|
||||
|
||||
set(editor_FORMS
|
||||
|
@ -88,7 +88,34 @@ void MainWindow::saveUserSettings()
|
||||
s.setValue(mainWindowPositionSetting, pos());
|
||||
}
|
||||
|
||||
MainWindow::MainWindow(QWidget *parent) :
|
||||
void MainWindow::parseCommandLine(ExtractionOptions & extractionOptions)
|
||||
{
|
||||
QCommandLineParser parser;
|
||||
parser.addHelpOption();
|
||||
parser.addPositionalArgument("map", QCoreApplication::translate("main", "Filepath of the map to open."));
|
||||
|
||||
parser.addOptions({
|
||||
{"e", QCoreApplication::translate("main", "Extract original H3 archives into a separate folder.")},
|
||||
{"s", QCoreApplication::translate("main", "From an extracted archive, it Splits TwCrPort, CPRSMALL, FlagPort, ITPA, ITPt, Un32 and Un44 into individual PNG's.")},
|
||||
{"c", QCoreApplication::translate("main", "From an extracted archive, Converts single Images (found in Images folder) from .pcx to png.")},
|
||||
{"d", QCoreApplication::translate("main", "Delete original files, for the ones splitted / converted.")},
|
||||
});
|
||||
|
||||
parser.process(qApp->arguments());
|
||||
|
||||
const QStringList positionalArgs = parser.positionalArguments();
|
||||
|
||||
if(!positionalArgs.isEmpty())
|
||||
mapFilePath = positionalArgs.at(0);
|
||||
|
||||
extractionOptions = {
|
||||
parser.isSet("e"), {
|
||||
parser.isSet("s"),
|
||||
parser.isSet("c"),
|
||||
parser.isSet("d")}};
|
||||
}
|
||||
|
||||
MainWindow::MainWindow(QWidget* parent) :
|
||||
QMainWindow(parent),
|
||||
ui(new Ui::MainWindow),
|
||||
controller(this)
|
||||
@ -96,49 +123,55 @@ MainWindow::MainWindow(QWidget *parent) :
|
||||
ui->setupUi(this);
|
||||
loadUserSettings(); //For example window size
|
||||
setTitle();
|
||||
|
||||
|
||||
// Set current working dir to executable folder.
|
||||
// This is important on Mac for relative paths to work inside DMG.
|
||||
QDir::setCurrent(QApplication::applicationDirPath());
|
||||
|
||||
ExtractionOptions extractionOptions;
|
||||
parseCommandLine(extractionOptions);
|
||||
|
||||
//configure logging
|
||||
const boost::filesystem::path logPath = VCMIDirs::get().userLogsPath() / "VCMI_Editor_log.txt";
|
||||
console = new CConsoleHandler();
|
||||
logConfig = new CBasicLogConfigurator(logPath, console);
|
||||
logConfig->configureDefault();
|
||||
logGlobal->info("The log file will be saved to %s", logPath);
|
||||
|
||||
|
||||
//init
|
||||
preinitDLL(::console);
|
||||
preinitDLL(::console, false, extractionOptions.extractArchives);
|
||||
settings.init();
|
||||
|
||||
|
||||
// Initialize logging based on settings
|
||||
logConfig->configure();
|
||||
logGlobal->debug("settings = %s", settings.toJsonNode().toJson());
|
||||
|
||||
|
||||
// Some basic data validation to produce better error messages in cases of incorrect install
|
||||
auto testFile = [](std::string filename, std::string message) -> bool
|
||||
{
|
||||
if (CResourceHandler::get()->existsResource(ResourceID(filename)))
|
||||
return true;
|
||||
|
||||
|
||||
logGlobal->error("Error: %s was not found!", message);
|
||||
return false;
|
||||
};
|
||||
|
||||
if(!testFile("DATA/HELP.TXT", "Heroes III data") ||
|
||||
!testFile("MODS/VCMI/MOD.JSON", "VCMI data"))
|
||||
|
||||
if (!testFile("DATA/HELP.TXT", "Heroes III data") ||
|
||||
!testFile("MODS/VCMI/MOD.JSON", "VCMI data"))
|
||||
{
|
||||
QApplication::quit();
|
||||
}
|
||||
|
||||
|
||||
conf.init();
|
||||
logGlobal->info("Loading settings");
|
||||
|
||||
|
||||
init();
|
||||
|
||||
|
||||
graphics = new Graphics(); // should be before curh->init()
|
||||
graphics->load();//must be after Content loading but should be in main thread
|
||||
|
||||
if (extractionOptions.extractArchives)
|
||||
ResourceConverter::convertExtractedResourceFiles(extractionOptions.conversionOptions);
|
||||
|
||||
ui->mapView->setScene(controller.scene(0));
|
||||
ui->mapView->setController(&controller);
|
||||
@ -167,8 +200,8 @@ MainWindow::MainWindow(QWidget *parent) :
|
||||
show();
|
||||
|
||||
//Load map from command line
|
||||
if(qApp->arguments().size() >= 2)
|
||||
openMap(qApp->arguments().at(1));
|
||||
if(!mapFilePath.isEmpty())
|
||||
openMap(mapFilePath);
|
||||
}
|
||||
|
||||
MainWindow::~MainWindow()
|
||||
|
@ -5,7 +5,7 @@
|
||||
#include <QStandardItemModel>
|
||||
#include "mapcontroller.h"
|
||||
#include "../lib/Terrain.h"
|
||||
|
||||
#include "resourceExtractor/ResourceConverter.h"
|
||||
|
||||
class CMap;
|
||||
class ObjectBrowser;
|
||||
@ -129,6 +129,8 @@ private:
|
||||
void loadUserSettings();
|
||||
void saveUserSettings();
|
||||
|
||||
void parseCommandLine(ExtractionOptions & extractionOptions);
|
||||
|
||||
private:
|
||||
Ui::MainWindow * ui;
|
||||
ObjectBrowser * objectBrowser = nullptr;
|
||||
@ -142,4 +144,7 @@ private:
|
||||
int mapLevel = 0;
|
||||
|
||||
std::set<int> catalog;
|
||||
|
||||
// command line options
|
||||
QString mapFilePath; // FilePath to the H3 or VCMI map to open
|
||||
};
|
||||
|
91
mapeditor/resourceExtractor/ResourceConverter.cpp
Normal file
91
mapeditor/resourceExtractor/ResourceConverter.cpp
Normal file
@ -0,0 +1,91 @@
|
||||
/*
|
||||
* ResourceConverter.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
|
||||
*
|
||||
*/
|
||||
|
||||
#include "StdInc.h"
|
||||
|
||||
#include "ResourceConverter.h"
|
||||
|
||||
#include "../lib/JsonNode.h"
|
||||
#include "../lib/VCMIDirs.h"
|
||||
#include "../lib/filesystem/Filesystem.h"
|
||||
|
||||
#include "BitmapHandler.h"
|
||||
#include "Animation.h"
|
||||
|
||||
#include "boost/filesystem/path.hpp"
|
||||
#include "boost/locale.hpp"
|
||||
|
||||
void ResourceConverter::convertExtractedResourceFiles(ConversionOptions conversionOptions)
|
||||
{
|
||||
if (conversionOptions.splitDefs)
|
||||
splitDefFiles(conversionOptions.deleteOriginals);
|
||||
|
||||
if (conversionOptions.convertPcxToPng)
|
||||
doConvertPcxToPng(conversionOptions.deleteOriginals);
|
||||
}
|
||||
|
||||
void ResourceConverter::doConvertPcxToPng(bool deleteOriginals)
|
||||
{
|
||||
std::string filename;
|
||||
|
||||
bfs::path imagesPath = VCMIDirs::get().userExtractedPath() / "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()))
|
||||
return;
|
||||
|
||||
std::string filePath = dir_itr->path().string();
|
||||
std::string fileStem = dir_itr->path().stem().string();
|
||||
filename = dir_itr->path().filename().string();
|
||||
std::string filenameLowerCase = boost::locale::to_lower(filename);
|
||||
|
||||
if(bfs::extension(filenameLowerCase) == ".pcx")
|
||||
{
|
||||
auto img = BitmapHandler::loadBitmap(filenameLowerCase);
|
||||
bfs::path pngFilePath = imagesPath / (fileStem + ".png");
|
||||
img.save(pathToQString(pngFilePath), "PNG");
|
||||
|
||||
if(deleteOriginals)
|
||||
bfs::remove(filePath);
|
||||
}
|
||||
}
|
||||
catch(const std::exception & ex)
|
||||
{
|
||||
logGlobal->info(filename + " " + ex.what() + "\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ResourceConverter::splitDefFile(const std::string & fileName, const bfs::path & spritesPath, bool deleteOriginals)
|
||||
{
|
||||
if(CResourceHandler::get()->existsResource(ResourceID("SPRITES/" + fileName)))
|
||||
{
|
||||
std::unique_ptr<Animation> anim = make_unique<Animation>(fileName);
|
||||
anim->preload();
|
||||
anim->exportBitmaps(pathToQString(VCMIDirs::get().userExtractedPath()));
|
||||
|
||||
if(deleteOriginals)
|
||||
bfs::remove(spritesPath / fileName);
|
||||
}
|
||||
else
|
||||
logGlobal->error("Def File Split error! " + fileName);
|
||||
}
|
||||
|
||||
void ResourceConverter::splitDefFiles(bool deleteOriginals)
|
||||
{
|
||||
bfs::path spritesPath = VCMIDirs::get().userExtractedPath() / "SPRITES";
|
||||
|
||||
for(std::string defFilename : {"TwCrPort.def", "CPRSMALL.def", "FlagPort.def", "ITPA.def", "ITPt.def", "Un32.def", "Un44.def"})
|
||||
splitDefFile(defFilename, spritesPath, deleteOriginals);
|
||||
}
|
49
mapeditor/resourceExtractor/ResourceConverter.h
Normal file
49
mapeditor/resourceExtractor/ResourceConverter.h
Normal file
@ -0,0 +1,49 @@
|
||||
/*
|
||||
* ResourceConverter.h, 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
|
||||
*
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
namespace bfs = boost::filesystem;
|
||||
|
||||
// Struct for holding all Convertor Options
|
||||
struct ConversionOptions
|
||||
{
|
||||
bool splitDefs = false; // splits TwCrPort, CPRSMALL, FlagPort, ITPA, ITPt, Un32 and Un44 into individual PNG's
|
||||
bool convertPcxToPng = false; // converts single Images (found in Images folder) from .pcx to png.
|
||||
bool deleteOriginals = false; // delete original files, for the ones splitted / converted.
|
||||
};
|
||||
|
||||
// Struct for holding all Resource Extractor / Converter options
|
||||
struct ExtractionOptions
|
||||
{
|
||||
bool extractArchives = false; // if set, original H3 archives will be extracted into a separate folder
|
||||
ConversionOptions conversionOptions;
|
||||
};
|
||||
|
||||
class ResourceConverter
|
||||
{
|
||||
|
||||
public:
|
||||
|
||||
// Splits def files that are shared between factions and converts pcx to bmp depending on Extraction Options
|
||||
static void convertExtractedResourceFiles(ConversionOptions conversionOptions);
|
||||
|
||||
private:
|
||||
|
||||
// converts all pcx files from /Images into PNG
|
||||
static void doConvertPcxToPng(bool deleteOriginals);
|
||||
|
||||
// splits a def file into individual parts and converts the output to PNG format
|
||||
static void splitDefFile(const std::string& fileName, const bfs::path& spritesPath, bool deleteOriginals);
|
||||
|
||||
// splits def files (TwCrPort, CPRSMALL, FlagPort, ITPA, ITPt, Un32 and Un44) so that faction resources are independent
|
||||
// (town creature portraits, hero army creature portraits, adventure map dwellings, small town icons, big town icons,
|
||||
// hero speciality small icons, hero speciality large icons)
|
||||
static void splitDefFiles(bool deleteOriginals);
|
||||
};
|
Loading…
x
Reference in New Issue
Block a user