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