371 lines
12 KiB
C++
371 lines
12 KiB
C++
|
|
|
|
// TnzCore includes
|
|
#include "tsystem.h"
|
|
#include "tiio.h"
|
|
#include "tcontenthistory.h"
|
|
#include "tconvert.h"
|
|
#include "tmsgcore.h"
|
|
|
|
// STD includes
|
|
#include <map>
|
|
|
|
// Qt includes
|
|
#include <QDir>
|
|
|
|
#include "tlevel_io.h"
|
|
|
|
using namespace std;
|
|
|
|
DEFINE_CLASS_CODE(TLevelReader, 8)
|
|
DEFINE_CLASS_CODE(TLevelWriter, 9)
|
|
// DEFINE_CLASS_CODE(TLevelReaderWriter, 25) //brutto
|
|
|
|
//-----------------------------------------------------------
|
|
|
|
typedef std::pair<QString, int> LevelReaderKey;
|
|
std::map<LevelReaderKey, TLevelReaderCreateProc *> LevelReaderTable;
|
|
std::map<QString, std::pair<TLevelWriterCreateProc *, bool>> LevelWriterTable;
|
|
// std::map<std::string, TLevelReaderWriterCreateProc*> LevelReaderWriterTable;
|
|
|
|
//-----------------------------------------------------------
|
|
|
|
TLevelReader::TLevelReader(const TFilePath &path)
|
|
: TSmartObject(m_classCode)
|
|
, m_info(0)
|
|
, m_path(path)
|
|
, m_contentHistory(0)
|
|
, m_frameFormat(TFrameId::FOUR_ZEROS) {}
|
|
|
|
//-----------------------------------------------------------
|
|
|
|
TLevelReader::~TLevelReader() {
|
|
delete m_contentHistory;
|
|
delete m_info;
|
|
}
|
|
|
|
//-----------------------------------------------------------
|
|
|
|
TLevelReaderP::TLevelReaderP(const TFilePath &path, int reader) {
|
|
QString extension = QString::fromStdString(toLower(path.getType()));
|
|
LevelReaderKey key(extension, reader);
|
|
std::map<LevelReaderKey, TLevelReaderCreateProc *>::iterator it;
|
|
it = LevelReaderTable.find(key);
|
|
if (it != LevelReaderTable.end()) {
|
|
m_pointer = it->second(path);
|
|
assert(m_pointer);
|
|
} else {
|
|
m_pointer = new TLevelReader(path);
|
|
}
|
|
m_pointer->addRef();
|
|
}
|
|
|
|
//-----------------------------------------------------------
|
|
|
|
namespace {
|
|
bool myLess(const TFilePath &l, const TFilePath &r) {
|
|
return l.getFrame() < r.getFrame();
|
|
}
|
|
} // namespace
|
|
|
|
//-----------------------------------------------------------
|
|
|
|
const TImageInfo *TLevelReader::getImageInfo(TFrameId fid) {
|
|
if (m_info)
|
|
return m_info;
|
|
else {
|
|
TImageReaderP frameReader = getFrameReader(fid);
|
|
if (!frameReader) return 0;
|
|
|
|
const TImageInfo *fInfo = frameReader->getImageInfo();
|
|
if (!fInfo) return 0;
|
|
|
|
m_info = new TImageInfo(*fInfo);
|
|
if (m_info->m_properties)
|
|
m_info->m_properties = m_info->m_properties->clone();
|
|
|
|
return m_info;
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------
|
|
|
|
const TImageInfo *TLevelReader::getImageInfo() {
|
|
if (m_info) return m_info;
|
|
TLevelP level = loadInfo();
|
|
if (level->getFrameCount() == 0) return 0;
|
|
return getImageInfo(level->begin()->first);
|
|
}
|
|
|
|
//-----------------------------------------------------------
|
|
|
|
TLevelP TLevelReader::loadInfo() {
|
|
TFilePath parentDir = m_path.getParentDir();
|
|
TFilePath levelName(m_path.getLevelName());
|
|
// cout << "Parent dir = '" << parentDir << "'" << endl;
|
|
// cout << "Level name = '" << levelName << "'" << endl;
|
|
TFilePathSet files;
|
|
try {
|
|
files = TSystem::readDirectory(parentDir, false, true, true);
|
|
} catch (...) {
|
|
throw TImageException(m_path, "unable to read directory content");
|
|
}
|
|
TLevelP level;
|
|
vector<TFilePath> data;
|
|
for (TFilePathSet::iterator it = files.begin(); it != files.end(); it++) {
|
|
TFilePath ln(it->getLevelName());
|
|
// cout << "try " << *it << " " << it->getLevelName() << endl;
|
|
if (levelName == TFilePath(it->getLevelName())) {
|
|
try {
|
|
level->setFrame(it->getFrame(), TImageP());
|
|
data.push_back(*it);
|
|
} catch (TMalformedFrameException tmfe) {
|
|
// skip frame named incorrectly warning to the user in the message
|
|
// center.
|
|
DVGui::warning(QString::fromStdWString(
|
|
tmfe.getMessage() + L": " +
|
|
QObject::tr("Skipping frame.").toStdWString()));
|
|
continue;
|
|
}
|
|
}
|
|
}
|
|
if (!data.empty()) {
|
|
std::vector<TFilePath>::iterator it =
|
|
std::min_element(data.begin(), data.end(), myLess);
|
|
|
|
m_frameFormat = (*it).getFrame().getCurrentFormat();
|
|
/*
|
|
TFilePath fr = (*it).withoutParentDir().withName("").withType("");
|
|
wstring ws = fr.getWideString();
|
|
if (ws.length() == 5) {
|
|
if (ws.rfind(L'_') == (int)wstring::npos)
|
|
m_frameFormat = TFrameId::FOUR_ZEROS;
|
|
else
|
|
m_frameFormat = TFrameId::UNDERSCORE_FOUR_ZEROS;
|
|
} else if (ws.rfind(L'0') == 1) { // leads with any number of zeros
|
|
if (ws.rfind(L'_') == (int)wstring::npos)
|
|
m_frameFormat = TFrameId::CUSTOM_PAD;
|
|
else
|
|
m_frameFormat = TFrameId::UNDERSCORE_CUSTOM_PAD;
|
|
} else {
|
|
if (ws.rfind(L'_') == (int)wstring::npos)
|
|
m_frameFormat = TFrameId::NO_PAD;
|
|
else
|
|
m_frameFormat = TFrameId::UNDERSCORE_NO_PAD;
|
|
}
|
|
*/
|
|
} else
|
|
m_frameFormat = TFrameId::FOUR_ZEROS;
|
|
|
|
return level;
|
|
}
|
|
|
|
//-----------------------------------------------------------
|
|
|
|
TImageReaderP TLevelReader::getFrameReader(TFrameId fid) {
|
|
return TImageReaderP(m_path.withFrame(fid, m_frameFormat));
|
|
}
|
|
|
|
//-----------------------------------------------------------
|
|
|
|
void TLevelReader::getSupportedFormats(QStringList &names) {
|
|
for (std::map<LevelReaderKey, TLevelReaderCreateProc *>::iterator it =
|
|
LevelReaderTable.begin();
|
|
it != LevelReaderTable.end(); ++it) {
|
|
names.push_back(it->first.first);
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------
|
|
|
|
TSoundTrack *TLevelReader::loadSoundTrack() { return 0; }
|
|
|
|
//===========================================================
|
|
|
|
TLevelWriter::TLevelWriter(const TFilePath &path, TPropertyGroup *prop)
|
|
: TSmartObject(m_classCode)
|
|
, m_path(path)
|
|
, m_properties(prop)
|
|
, m_contentHistory(0)
|
|
, m_frameFormatTemplateFId(TFrameId::NO_FRAME) {
|
|
string ext = path.getType();
|
|
if (!prop) m_properties = Tiio::makeWriterProperties(ext);
|
|
}
|
|
|
|
//-----------------------------------------------------------
|
|
|
|
TLevelWriter::~TLevelWriter() {
|
|
delete m_properties;
|
|
delete m_contentHistory;
|
|
}
|
|
|
|
//-----------------------------------------------------------
|
|
|
|
TLevelWriterP::TLevelWriterP(const TFilePath &path, TPropertyGroup *winfo) {
|
|
QString type = QString::fromStdString(toLower(path.getType()));
|
|
std::map<QString, std::pair<TLevelWriterCreateProc *, bool>>::iterator it;
|
|
it = LevelWriterTable.find(type);
|
|
if (it != LevelWriterTable.end())
|
|
m_pointer = it->second.first(
|
|
path,
|
|
winfo ? winfo->clone() : Tiio::makeWriterProperties(path.getType()));
|
|
else
|
|
m_pointer = new TLevelWriter(
|
|
path,
|
|
winfo ? winfo->clone() : Tiio::makeWriterProperties(path.getType()));
|
|
|
|
assert(m_pointer);
|
|
m_pointer->addRef();
|
|
}
|
|
|
|
//-----------------------------------------------------------
|
|
|
|
void TLevelWriter::save(const TLevelP &level) {
|
|
for (TLevel::Iterator it = level->begin(); it != level->end(); it++) {
|
|
if (it->second) getFrameWriter(it->first)->save(it->second);
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------
|
|
|
|
void TLevelWriter::saveSoundTrack(TSoundTrack *) {
|
|
return;
|
|
throw TException("The level format doesn't support soundtracks");
|
|
}
|
|
|
|
//-----------------------------------------------------------
|
|
|
|
void TLevelWriter::setFrameRate(double fps) { m_frameRate = fps; }
|
|
|
|
//-----------------------------------------------------------
|
|
|
|
void TLevelWriter::getSupportedFormats(QStringList &names,
|
|
bool onlyRenderFormats) {
|
|
for (std::map<QString, std::pair<TLevelWriterCreateProc *, bool>>::iterator
|
|
it = LevelWriterTable.begin();
|
|
it != LevelWriterTable.end(); ++it) {
|
|
if (!onlyRenderFormats || it->second.second) names.push_back(it->first);
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------
|
|
|
|
TImageWriterP TLevelWriter::getFrameWriter(TFrameId fid) {
|
|
// change the frame format with the template
|
|
if (!m_frameFormatTemplateFId.isNoFrame()) {
|
|
fid.setZeroPadding(m_frameFormatTemplateFId.getZeroPadding());
|
|
fid.setStartSeqInd(m_frameFormatTemplateFId.getStartSeqInd());
|
|
}
|
|
|
|
TImageWriterP iw(m_path.withFrame(fid));
|
|
iw->setProperties(m_properties);
|
|
return iw;
|
|
}
|
|
|
|
//-----------------------------------------------------------
|
|
|
|
void TLevelWriter::setContentHistory(TContentHistory *contentHistory) {
|
|
if (contentHistory != m_contentHistory) {
|
|
delete m_contentHistory;
|
|
m_contentHistory = contentHistory;
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------
|
|
|
|
void TLevelWriter::renumberFids(const std::map<TFrameId, TFrameId> &table) {
|
|
typedef std::map<TFrameId, TFrameId> Table;
|
|
|
|
struct locals {
|
|
static inline QString qstring(const TFilePath &fp) {
|
|
return QString::fromStdWString(fp.getWideString());
|
|
}
|
|
static inline QString temp(const QString &str) {
|
|
return str + QString("_");
|
|
}
|
|
};
|
|
|
|
if (m_path.getDots() == "..") {
|
|
try {
|
|
// Extract all image file paths of the level
|
|
QDir parentDir(
|
|
QString::fromStdWString(m_path.getParentDir().getWideString()));
|
|
parentDir.setFilter(QDir::Files);
|
|
|
|
QStringList nameFilters;
|
|
// check for both period and underscore
|
|
nameFilters << QString::fromStdWString(m_path.getWideName()) + ".*." +
|
|
QString::fromStdString(m_path.getType())
|
|
<< QString::fromStdWString(m_path.getWideName()) + "_*." +
|
|
QString::fromStdString(m_path.getType());
|
|
parentDir.setNameFilters(nameFilters);
|
|
|
|
TFilePathSet fpset;
|
|
TSystem::readDirectory(fpset, parentDir, false); // Could throw
|
|
|
|
// Traverse each file, trying to match it with a table entry
|
|
std::vector<QString> storedDstPaths;
|
|
|
|
TFilePathSet::iterator st, sEnd(fpset.end());
|
|
for (st = fpset.begin(); st != sEnd; ++st) {
|
|
const QString &src = locals::qstring(*st);
|
|
const TFrameId &fid = st->getFrame(); // Could throw ! (and I'm quite
|
|
// appalled of that o.o')
|
|
|
|
Table::const_iterator dt(table.find(fid));
|
|
if (dt == table.end()) {
|
|
// The frame must be removed
|
|
QFile::remove(src);
|
|
} else {
|
|
if (fid == dt->second) continue;
|
|
|
|
// The frame must be renumbered
|
|
const QString &dst = locals::qstring(st->withFrame(dt->second));
|
|
|
|
if (!QFile::rename(src, dst)) {
|
|
// Use a temporary file rename to ensure that other frames to be
|
|
// renumbered
|
|
// are not overwritten.
|
|
if (QFile::rename(locals::qstring(*st), locals::temp(dst)))
|
|
storedDstPaths.push_back(dst);
|
|
|
|
// If the second rename did not happen, the problem was not on dst,
|
|
// but on src.
|
|
// Alas, it means that rename on source is not possible - skip.
|
|
}
|
|
}
|
|
}
|
|
|
|
// At this point, temporaries should be restored to originals. In case the
|
|
// rename of one of those files cannot be finalized, leave the temporary -
|
|
// as
|
|
// it may be impossible to roll back (another frame could have been
|
|
// renumbered
|
|
// to the would-roll-back frame) !
|
|
|
|
std::vector<QString>::iterator dt, dEnd(storedDstPaths.end());
|
|
for (dt = storedDstPaths.begin(); dt != dEnd; ++dt)
|
|
QFile::rename(locals::temp(*dt), *dt);
|
|
} catch (...) {
|
|
// Could not read the directory - skip silently
|
|
}
|
|
}
|
|
}
|
|
|
|
//============================================================
|
|
|
|
void TLevelReader::define(QString extension, int reader,
|
|
TLevelReaderCreateProc *proc) {
|
|
LevelReaderKey key(extension, reader);
|
|
LevelReaderTable[key] = proc;
|
|
// cout << "LevelReader " << extension << " registered" << endl;
|
|
}
|
|
|
|
//-----------------------------------------------------------
|
|
|
|
void TLevelWriter::define(QString extension, TLevelWriterCreateProc *proc,
|
|
bool isRenderFormat) {
|
|
LevelWriterTable[extension] =
|
|
std::pair<TLevelWriterCreateProc *, bool>(proc, isRenderFormat);
|
|
// cout << "LevelWriter " << extension << " registered" << endl;
|
|
}
|