diff --git a/BackgroundExporter.cpp b/BackgroundExporter.cpp new file mode 100644 index 0000000..ea51568 --- /dev/null +++ b/BackgroundExporter.cpp @@ -0,0 +1,67 @@ +/**************************************************************************** + ** Deling Final Fantasy VIII Field Editor + ** Copyright (C) 2009-2020 Arzel Jérôme + ** + ** This program is free software: you can redistribute it and/or modify + ** it under the terms of the GNU General Public License as published by + ** the Free Software Foundation, either version 3 of the License, or + ** (at your option) any later version. + ** + ** This program is distributed in the hope that it will be useful, + ** but WITHOUT ANY WARRANTY; without even the implied warranty of + ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + ** GNU General Public License for more details. + ** + ** You should have received a copy of the GNU General Public License + ** along with this program. If not, see . + ****************************************************************************/ +#include "BackgroundExporter.h" +#include "FieldPC.h" +#include "FieldArchivePC.h" + +BackgroundExporter::BackgroundExporter(FieldArchive *archive) : + _archive(archive) +{ + +} + +bool BackgroundExporter::toDir(const QDir &dir, ArchiveObserver *observer) +{ + if (!_archive) { + return false; + } + + FieldArchiveIterator it = _archive->iterator(); + + if (observer) { + observer->setObserverMaximum(quint32(_archive->nbFields())); + } + + int i = 0; + + while (it.hasNext()) { + Field *f = it.next(); + + if (observer) { + if (observer->observerWasCanceled()) { + return false; + } + observer->setObserverValue(i++); + } + + if (f && f->isOpen() && (!f->isPc() || ((FieldPC *)f)->open2(((FieldArchivePC *)_archive)->getFsArchive())) && f->hasBackgroundFile()) { + BackgroundFile *background = f->getBackgroundFile(); + QString fieldName = f->name(); + + if (fieldName.isEmpty()) { + fieldName = QObject::tr("sans-nom"); + } + + if (!background->background().save(dir.filePath(fieldName + ".png"))) { + _lastErrorString = QObject::tr("Impossible d'exporter '%1' en image").arg(fieldName); + } + } + } + + return true; +} diff --git a/BackgroundExporter.h b/BackgroundExporter.h new file mode 100644 index 0000000..64c971b --- /dev/null +++ b/BackgroundExporter.h @@ -0,0 +1,36 @@ +/**************************************************************************** + ** Deling Final Fantasy VIII Field Editor + ** Copyright (C) 2009-2020 Arzel Jérôme + ** + ** This program is free software: you can redistribute it and/or modify + ** it under the terms of the GNU General Public License as published by + ** the Free Software Foundation, either version 3 of the License, or + ** (at your option) any later version. + ** + ** This program is distributed in the hope that it will be useful, + ** but WITHOUT ANY WARRANTY; without even the implied warranty of + ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + ** GNU General Public License for more details. + ** + ** You should have received a copy of the GNU General Public License + ** along with this program. If not, see . + ****************************************************************************/ +#ifndef BACKGROUNDEXPORTER_H +#define BACKGROUNDEXPORTER_H + +#include "FieldArchive.h" + +class BackgroundExporter +{ +public: + explicit BackgroundExporter(FieldArchive *archive); + bool toDir(const QDir &dir, ArchiveObserver *observer = nullptr); + inline const QString &errorString() const { + return _lastErrorString; + } +private: + FieldArchive *_archive; + QString _lastErrorString; +}; + +#endif // BACKGROUNDEXPORTER_H diff --git a/CharaModel.cpp b/CharaModel.cpp index 5dc5c85..451309c 100644 --- a/CharaModel.cpp +++ b/CharaModel.cpp @@ -17,39 +17,13 @@ ****************************************************************************/ #include "CharaModel.h" -CharaModel::CharaModel(const QString &name, const QList &toc, const QByteArray &data) : - _name(name) -{ - open(toc, data); -} - -CharaModel::CharaModel(const QString &name) : - _name(name) -{ -} - CharaModel::CharaModel() { } -bool CharaModel::open(const QList &toc, const QByteArray &data) +CharaModel::CharaModel(const QString &name, const QList &textures) : + _name(name), _textures(textures) { - _textures.clear(); - - // Toc = tim offsets + data offset + data size - - for(int i=0 ; i &toc, const QByteArray &data); - CharaModel(const QString &name); CharaModel(); + explicit CharaModel(const QString &name, const QList &textures = QList()); bool open(const QList &toc, const QByteArray &data); bool isEmpty() const; QString name() const; diff --git a/Deling.pro b/Deling.pro index b46028a..ff68d1f 100644 --- a/Deling.pro +++ b/Deling.pro @@ -20,10 +20,12 @@ lessThan(QT_MAJOR_VERSION, 5) { # Input HEADERS += MainWindow.h \ + BackgroundExporter.h \ EncounterExporter.h \ PreviewWidget.h \ QLZ4.h \ ScriptExporter.h \ + SearchAll.h \ parameters.h \ Data.h \ Config.h \ @@ -56,6 +58,7 @@ HEADERS += MainWindow.h \ FieldArchivePC.h \ JsmOpcode.h \ PlainTextEdit.h \ + widgets/AboutDialog.h \ widgets/PageWidget.h \ widgets/BackgroundWidget.h \ widgets/JsmWidget.h \ @@ -111,10 +114,12 @@ HEADERS += MainWindow.h \ JsmExpression.h SOURCES += MainWindow.cpp \ + BackgroundExporter.cpp \ EncounterExporter.cpp \ PreviewWidget.cpp \ QLZ4.cpp \ ScriptExporter.cpp \ + SearchAll.cpp \ main.cpp \ Data.cpp \ Config.cpp \ @@ -147,6 +152,7 @@ SOURCES += MainWindow.cpp \ FieldArchivePC.cpp \ JsmOpcode.cpp \ PlainTextEdit.cpp \ + widgets/AboutDialog.cpp \ widgets/PageWidget.cpp \ widgets/BackgroundWidget.cpp \ widgets/JsmWidget.cpp \ diff --git a/FF8DiscArchive.cpp b/FF8DiscArchive.cpp index ed91846..b07d79b 100644 --- a/FF8DiscArchive.cpp +++ b/FF8DiscArchive.cpp @@ -125,7 +125,7 @@ QByteArray FF8DiscArchive::fileLZS(const FF8DiscFile &file, bool strict) if((strict && file.getSize() != lzsSize+4) || (!strict && (lzsSize + 4)/SECTOR_SIZE_DATA + (int)(lzsSize%SECTOR_SIZE_DATA != 0) != file.getSize()/SECTOR_SIZE_DATA + (int)(file.getSize()%SECTOR_SIZE_DATA != 0))) return QByteArray(); - return LZS::decompressAll(readIso(lzsSize).constData(), lzsSize); + return LZS::decompress(readIso(lzsSize).constData(), lzsSize); } QByteArray FF8DiscArchive::fileGZ(const FF8DiscFile &file) @@ -174,7 +174,7 @@ bool FF8DiscArchive::extractGZ(const FF8DiscFile &file, const QString &destinati const QList &FF8DiscArchive::rootDirectory() { if(!rootFiles.isEmpty() || !IMGFound()) return rootFiles; - searchFiles(); + //searchFiles(); quint32 position, size, numSectors = sizeIMG / SECTOR_SIZE_DATA; qint64 maxPos; @@ -232,6 +232,14 @@ const FF8DiscFile &FF8DiscArchive::rootFile(int id) return rootDirectory().at(id); } +const FF8DiscFile &FF8DiscArchive::sysFntTdwFile() +{ + if (isDemo()) { + return rootFile(isJp() ? 9 : 8); + } + return rootFile(129); +} + const FF8DiscFile &FF8DiscArchive::fieldBinFile() { if (isDemo()) { diff --git a/FF8DiscArchive.h b/FF8DiscArchive.h index 5a10030..85a0b13 100644 --- a/FF8DiscArchive.h +++ b/FF8DiscArchive.h @@ -54,6 +54,7 @@ class FF8DiscArchive : public IsoArchive const QList &rootDirectory(); int rootCount(); const FF8DiscFile &rootFile(int id); + const FF8DiscFile &sysFntTdwFile(); const FF8DiscFile &fieldBinFile(); // QList worldmapDirectory(); // FF8DiscFile worldmapFile(int id); diff --git a/FF8Font.cpp b/FF8Font.cpp index 2fae845..f15acce 100644 --- a/FF8Font.cpp +++ b/FF8Font.cpp @@ -275,6 +275,18 @@ FF8Font *FF8Font::openFont(const QString &tdwPath, const QString &txtPath) } } +void FF8Font::registerFont(const QString &name, FF8Font *font) +{ + fonts.insert(name, font); +} + +void FF8Font::deregisterFont(const QString &name) +{ + if(fonts.contains(name)) { + delete fonts.take(name); + } +} + FF8Font *FF8Font::font(QString name) { if(name.isEmpty()) { diff --git a/FF8Font.h b/FF8Font.h index 6db4ed1..120258c 100644 --- a/FF8Font.h +++ b/FF8Font.h @@ -42,6 +42,8 @@ class FF8Font static bool listFonts(); static QStringList fontList(); + static void registerFont(const QString &name, FF8Font *font); + static void deregisterFont(const QString &name); static FF8Font *font(QString name); static FF8Font *getCurrentConfigFont(); static bool saveFonts(); diff --git a/FF8Image.cpp b/FF8Image.cpp index 7f82516..50d04f8 100644 --- a/FF8Image.cpp +++ b/FF8Image.cpp @@ -17,16 +17,6 @@ ****************************************************************************/ #include "FF8Image.h" -quint16 FF8Image::toPsColor(const QRgb &color) -{ - return (qRound(qRed(color)/COEFF_COLOR) & 31) | ((qRound(qGreen(color)/COEFF_COLOR) & 31) << 5) | ((qRound(qBlue(color)/COEFF_COLOR) & 31) << 10) | ((qAlpha(color)==255) << 15); -} - -QRgb FF8Image::fromPsColor(quint16 color, bool useAlpha) -{ - return qRgba(qRound((color & 31)*COEFF_COLOR), qRound((color>>5 & 31)*COEFF_COLOR), qRound((color>>10 & 31)*COEFF_COLOR), color == 0 && useAlpha ? 0 : 255); -} - QPixmap FF8Image::lzs(const QByteArray &data) { //QTime t;t.start(); @@ -36,7 +26,7 @@ QPixmap FF8Image::lzs(const QByteArray &data) memcpy(&lzs_size, lzs_constData, 4); if(lzs_size != real_size - 4) return errorPixmap(); - const QByteArray &decdata = LZS::decompressAll(lzs_constData + 4, lzs_size); + const QByteArray &decdata = LZS::decompress(lzs_constData + 4, lzs_size); quint16 l, h; int size = decdata.size(), i=8; @@ -109,90 +99,59 @@ QByteArray FF8Image::toLzs(const QImage &image, quint16 u1, quint16 u2) return data.prepend((char *)&lzs_size, 4); } -int FF8Image::findFirstTim(const QByteArray &data) +QList FF8Image::findTims(const QByteArray &data) { - int index = -1, dataSize = data.size(); - quint32 palSize, imgSize; + int indexInData = -1, dataSize = data.size(); + qint32 palSize, imgSize; quint16 w, h; quint8 bpp; const char *constData = data.constData(); - QByteArray search("\x10\x00\x00\x00",4); + QByteArray search("\x10\x00\x00\x00", 4); + QList ret; - while( (index = data.indexOf(search, index+4)) != -1 ) - { - palSize=0; - if(index+8 > dataSize) continue; - bpp = (quint8)data.at(index+4); + while((indexInData = data.indexOf(search, indexInData+4)) != -1) { + palSize = 0; + if(indexInData + 8 > dataSize) { + continue; + } + bpp = quint8(data.at(indexInData + 4)); // qDebug() << "bpp" << bpp; - if(bpp==8 || bpp==9) - { - memcpy(&palSize, &constData[index+8], 4); + if(bpp == 8 || bpp == 9) { + memcpy(&palSize, &constData[indexInData + 8], 4); // qDebug() << "palSize" << palSize; - if(index+20 > dataSize) continue; - memcpy(&w, &constData[index+16], 2); - memcpy(&h, &constData[index+18], 2); + if(indexInData + 20 > dataSize) { + continue; + } + memcpy(&w, &constData[indexInData + 16], 2); + memcpy(&h, &constData[indexInData + 18], 2); // qDebug() << "w" << w << "h" << h; - if(palSize != (quint32)w*h*2+12) continue; + if(palSize != w * h * 2 + 12) { + continue; + } + } + else if(bpp != 2 && bpp != 3) { + continue; } - else if(bpp!=2 && bpp!=3) continue; - memcpy(&imgSize, &constData[index+8+palSize], 4); + memcpy(&imgSize, &constData[indexInData + 8 + palSize], 4); // qDebug() << "imgSize" << imgSize; - if((quint32)(index+20+palSize) > (quint32)dataSize) continue; - memcpy(&w, &constData[index+16+palSize], 2); - memcpy(&h, &constData[index+18+palSize], 2); + if(indexInData + 20 + palSize > dataSize) { + continue; + } + memcpy(&w, &constData[indexInData+16+palSize], 2); + memcpy(&h, &constData[indexInData+18+palSize], 2); // qDebug() << "w" << w << "h" << h; - if(((bpp==8 || bpp==9 || bpp==2) && imgSize != (quint32)w*2*h+12) || (bpp==3 && imgSize != (quint32)w*3*h+12)) continue; - - return index; - } - return -1; -} - -int FF8Image::findTims(const QByteArray &data) -{ - int index = -1, oldIndex = 0, dataSize = data.size(); - quint32 palSize, imgSize; - quint16 w, h, i=0; - quint8 bpp; - const char *constData = data.constData(); - - while( (index = data.indexOf(QByteArray("\x10\x00\x00\x00",4), index+1)) != -1 ) - { - palSize=0; - if(index+4 >= dataSize) continue; - bpp = (quint8)data.at(index+4); - - if(bpp==8 || bpp==9) - { - memcpy(&palSize, &constData[index+8], 4); - if(index+20 >= dataSize) continue; - memcpy(&w, &constData[index+16], 2); - memcpy(&h, &constData[index+18], 2); - if(palSize != (quint32)w*h*2+12) continue; + if(((bpp == 8 || bpp == 9 || bpp == 2) && imgSize != w * 2 * h + 12) + || (bpp == 3 && imgSize != w * 3 * h + 12)) { + continue; } - else if(bpp!=2 && bpp!=3) continue; - - memcpy(&imgSize, &constData[index+8+palSize], 4); - if((quint32)index+20+palSize >= (quint32)dataSize) continue; - memcpy(&w, &constData[index+16+palSize], 2); - memcpy(&h, &constData[index+18+palSize], 2); - if(((bpp==8 || bpp==9 || bpp==2) && imgSize != (quint32)w*2*h+12) || (bpp==3 && imgSize != (quint32)w*3*h+12)) continue; - -// QFile fic(QString("C:/wamp/www/ff8/fils/fre/world/dat/wmsetfr%1.tim").arg(i++)); -// fic.open(QIODevice::WriteOnly); -// fic.write(data.mid(index, 8+palSize+imgSize)); -// fic.close(); - qDebug() << "espace" << (index-oldIndex) << "position" << QString().setNum(oldIndex, 16); - qDebug() << QString().setNum(index, 16) << "(taille=" << (8+palSize+imgSize) << ")"; - oldIndex = index+8+palSize+imgSize; -// Tim::image(data.mid(index, 8+palSize+imgSize), QString("C:/wamp/www/ff8/fils/fre/world/esk/texl%1.png").arg(i++)); + + ret.append(indexInData); } - qDebug() << "espace" << (dataSize-oldIndex) << "position" << QString().setNum(oldIndex, 16); - return i+1; + return ret; } void FF8Image::error(QPaintDevice *pd) diff --git a/FF8Image.h b/FF8Image.h index 7ab94f6..97f1b31 100644 --- a/FF8Image.h +++ b/FF8Image.h @@ -26,13 +26,20 @@ class FF8Image { public: - static quint16 toPsColor(const QRgb &color); - static QRgb fromPsColor(quint16 color, bool useAlpha=false); + static inline quint16 toPsColor(const QRgb &color) { + return (qRound(qRed(color)/COEFF_COLOR) & 31) | ((qRound(qGreen(color)/COEFF_COLOR) & 31) << 5) | ((qRound(qBlue(color)/COEFF_COLOR) & 31) << 10) | ((qAlpha(color)==255) << 15); + } + static inline QRgb fromPsColor(quint16 color, bool useAlpha=false) { + quint8 r = color & 31, + g = (color >> 5) & 31, + b = (color >> 10) & 31; + + return qRgba((r << 3) + (r >> 2), (g << 3) + (g >> 2), (b << 3) + (b >> 2), color == 0 && useAlpha ? 0 : 255); + } static QPixmap lzs(const QByteArray &data); static QByteArray toLzs(const QImage &image, quint16 u1, quint16 u2); - static int findFirstTim(const QByteArray &data); - static int findTims(const QByteArray &data); + static QList findTims(const QByteArray &data); static QImage errorImage(); static QPixmap errorPixmap(); diff --git a/FieldArchive.cpp b/FieldArchive.cpp index 3aff5ad..ed74175 100644 --- a/FieldArchive.cpp +++ b/FieldArchive.cpp @@ -181,17 +181,16 @@ bool FieldArchive::searchText(const QRegExp &text, int &fieldID, int &textID, in return false; } -bool FieldArchive::searchTextReverse(const QRegExp &text, int &fieldID, int &textID, int &from, int &index, int &size, Sorting sorting) const +bool FieldArchive::searchTextReverse(const QRegExp &text, int &fieldID, int &textID, int &from, int &size, Sorting sorting) const { QMap::const_iterator i, begin; if(!searchIteratorsP(i, begin, fieldID, sorting)) return false; for( ; i != begin-1 ; --i) { Field *field = getField(fieldID = i.value()); - if(field && field->hasMsdFile() && field->getMsdFile()->searchTextReverse(text, textID, from, index, size)) + if(field && field->hasMsdFile() && field->getMsdFile()->searchTextReverse(text, textID, from, size)) return true; - textID = 2147483647; - from = -1; + textID = from = 2147483647; } return false; @@ -221,11 +220,15 @@ bool FieldArchive::searchScriptText(const QRegExp &text, int &fieldID, int &grou Field *field = getField(fieldID = i.value()); if(field && field->hasMsdFile() && field->hasJsmFile()) { MsdFile *msd = field->getMsdFile(); - int textID = -1; - while((textID = msd->indexOfText(text, textID + 1)) != -1) { - if(field->getJsmFile()->search(JsmFile::SearchText, textID, groupID, methodID, opcodeID)) - return true; - ++textID; + JsmFile *jsm = field->getJsmFile(); + QList textIDs; + for (int textID = 0; textID < msd->nbText(); ++textID) { + if (text.indexIn(msd->text(textID)) >= 0) { + textIDs.append(quint64(textID)); + } + } + if (jsm->search(JsmFile::SearchText, textIDs, groupID, methodID, opcodeID)) { + return true; } } groupID = methodID = opcodeID = 0; @@ -243,7 +246,7 @@ bool FieldArchive::searchScriptReverse(JsmFile::SearchType type, quint64 value, Field *field = getField(fieldID = i.value()); if(field && field->hasJsmFile() && field->getJsmFile()->searchReverse(type, value, groupID, methodID, opcodeID)) return true; - groupID = methodID = opcodeID = -1; + groupID = methodID = opcodeID = 2147483647; } return false; @@ -258,14 +261,18 @@ bool FieldArchive::searchScriptTextReverse(const QRegExp &text, int &fieldID, in Field *field = getField(fieldID = i.value()); if(field && field->hasMsdFile() && field->hasJsmFile()) { MsdFile *msd = field->getMsdFile(); - int textID = -1; - while((textID = msd->indexOfText(text, textID + 1)) != -1) { - if(field->getJsmFile()->searchReverse(JsmFile::SearchText, textID, groupID, methodID, opcodeID)) - return true; - ++textID; + JsmFile *jsm = field->getJsmFile(); + QList textIDs; + for (int textID = 0; textID < msd->nbText(); ++textID) { + if (text.indexIn(msd->text(textID)) >= 0) { + textIDs.append(quint64(textID)); + } + } + if (jsm->searchReverse(JsmFile::SearchText, textIDs, groupID, methodID, opcodeID)) { + return true; } } - groupID = methodID = opcodeID = -1; + groupID = methodID = opcodeID = 2147483647; } return false; diff --git a/FieldArchive.h b/FieldArchive.h index 7e41ea7..b80c280 100644 --- a/FieldArchive.h +++ b/FieldArchive.h @@ -49,7 +49,7 @@ class FieldArchive virtual bool openBG(Field *field) const=0; bool compileScripts(int &errorFieldID, int &errorGroupID, int &errorMethodID, int &errorLine, QString &errorStr); bool searchText(const QRegExp &text, int &fieldID, int &textID, int &from, int &size, Sorting=SortByMapId) const; - bool searchTextReverse(const QRegExp &text, int &fieldID, int &textID, int &from, int &index, int &size, Sorting=SortByMapId) const; + bool searchTextReverse(const QRegExp &text, int &fieldID, int &textID, int &from, int &size, Sorting=SortByMapId) const; bool searchScript(JsmFile::SearchType type, quint64 value, int &fieldID, int &groupID, int &methodID, int &opcodeID, Sorting=SortByMapId) const; bool searchScriptText(const QRegExp &text, int &fieldID, int &groupID, int &methodID, int &opcodeID, Sorting=SortByMapId) const; bool searchScriptReverse(JsmFile::SearchType type, quint64 value, int &fieldID, int &groupID, int &methodID, int &opcodeID, Sorting=SortByMapId) const; diff --git a/FieldArchivePS.cpp b/FieldArchivePS.cpp index f126006..fc0d957 100644 --- a/FieldArchivePS.cpp +++ b/FieldArchivePS.cpp @@ -25,7 +25,14 @@ FieldArchivePS::FieldArchivePS() FieldArchivePS::~FieldArchivePS() { - if(iso) delete iso; + if(iso) { + if(iso->isDemo()) { + FF8Font::deregisterFont("demo"); + Config::setValue("encoding", "00"); + } + + delete iso; + } } QString FieldArchivePS::archivePath() const @@ -107,7 +114,7 @@ int FieldArchivePS::open(const QString &path, ArchiveObserver *progress) else desc = QString(); - indexOf = mapList().indexOf(field->name()); + indexOf = iso->isDemo() ? i : mapList().indexOf(field->name()); QString mapId = indexOf==-1 ? "~" : QString("%1").arg(indexOf, 3, 10, QChar('0')); fields.append(field); @@ -129,10 +136,18 @@ int FieldArchivePS::open(const QString &path, ArchiveObserver *progress) openModels(); - if(iso->isJp() && Config::value("encoding", "00").toString() == "00") { - Config::setValue("encoding", "01"); - } else if(!iso->isJp() && Config::value("encoding", "00").toString() == "01") { - Config::setValue("encoding", "00"); + if(iso->isDemo()) { + QByteArray sysFntTdw = iso->file(iso->sysFntTdwFile()); + TdwFile *tdw = new TdwFile(); + tdw->open(sysFntTdw); + FF8Font::registerFont("demo", new FF8Font(tdw, QByteArray())); + Config::setValue("encoding", "demo"); + } else { + if(iso->isJp() && Config::value("encoding", "00").toString() == "00") { + Config::setValue("encoding", "01"); + } else if(!iso->isJp() && Config::value("encoding", "00").toString() == "01") { + Config::setValue("encoding", "00"); + } } return 0; diff --git a/FieldPC.cpp b/FieldPC.cpp index 584ae84..859dcbb 100644 --- a/FieldPC.cpp +++ b/FieldPC.cpp @@ -380,6 +380,12 @@ void FieldPC::save(QByteArray &fs_data, QByteArray &fl_data, QByteArray &fi_data header->setFileData(filePath(Sfx), fs_data, sfx); } } + if(hasBackgroundFile() && getBackgroundFile()->isModified()) { + QByteArray map; + if(getBackgroundFile()->save(map)) { + header->setFileData(filePath(Map), fs_data, map); + } + } header->save(fl_data, fi_data); } diff --git a/FieldPS.cpp b/FieldPS.cpp index 5cfe80a..b8b9d1c 100644 --- a/FieldPS.cpp +++ b/FieldPS.cpp @@ -322,6 +322,13 @@ bool FieldPS::save(QByteArray &dat, QByteArray &mim) header.setSize(FieldDatHeader::AKAO, akao.size()); } } + if(hasBackgroundFile() && getBackgroundFile()->isModified()) { + QByteArray map; + if(getBackgroundFile()->save(map)) { + dat.replace(header.position(FieldDatHeader::Map), header.size(FieldDatHeader::Map), map); + header.setSize(FieldDatHeader::Map, map.size()); + } + } dat.replace(0, 48, header.save()); diff --git a/FsArchive.cpp b/FsArchive.cpp index 1b37106..4bc466b 100644 --- a/FsArchive.cpp +++ b/FsArchive.cpp @@ -1300,7 +1300,7 @@ bool FsArchive::repair(FsArchive *other) QByteArray d; if(size <= 5000000) { QByteArray data = fs.read(size); - d = LZS::decompressAll(data); + d = LZS::decompress(data); decSize = d.size(); qDebug() << "repair entry compressed" << pos << size << d.size(); compressed = true; diff --git a/FsDialog.cpp b/FsDialog.cpp index 50116a3..1e48239 100644 --- a/FsDialog.cpp +++ b/FsDialog.cpp @@ -19,7 +19,7 @@ #include "ProgressWidget.h" FsDialog::FsDialog(FsArchive *fsArchive, QWidget *parent) : - QWidget(parent), fsArchive(fsArchive), currentPal(0) + QWidget(parent), fsArchive(fsArchive), currentImage(0), currentPal(0) { setMinimumSize(800, 528); @@ -59,10 +59,10 @@ FsDialog::FsDialog(FsArchive *fsArchive, QWidget *parent) : connect(list, SIGNAL(itemDoubleClicked(QTreeWidgetItem*,int)), SLOT(doubleClicked(QTreeWidgetItem*))); connect(list, SIGNAL(itemChanged(QTreeWidgetItem*,int)), SLOT(renameOK(QTreeWidgetItem*,int))); connect(list, SIGNAL(fileDropped(QStringList)), SLOT(addFile(QStringList))); - connect(list, SIGNAL(itemSelectionChanged()), SLOT(setButtonsEnabled())); - connect(list, SIGNAL(itemSelectionChanged()), SLOT(generatePreview())); + connect(list, SIGNAL(itemSelectionChanged()), SLOT(changePreview())); connect(up, SIGNAL(released()), SLOT(parentDir())); connect(pathWidget, SIGNAL(returnPressed()), SLOT(openDir())); + connect(preview, SIGNAL(currentImageChanged(int)), SLOT(changeImageInPreview(int))); connect(preview, SIGNAL(currentPaletteChanged(int)), SLOT(changeImagePaletteInPreview(int))); if(fsArchive!=nullptr && fsArchive->dirExists("c:\\ff8\\data\\")) @@ -126,6 +126,14 @@ void FsDialog::setButtonsEnabled() } } +void FsDialog::changePreview() +{ + currentImage = 0; + currentPal = 0; + setButtonsEnabled(); + generatePreview(); +} + void FsDialog::generatePreview() { QList items = list->selectedItems(); @@ -180,18 +188,24 @@ void FsDialog::generatePreview() else if(fileType == "h" || fileType == "c" || fileType == "sym" || fileType.isEmpty() || fileType == "bak" || fileType == "dir" - || fileType == "fl") + || fileType == "fl" || fileType == "txt") { preview->textPreview(QString(data)); } + else if(fileType == "png" || fileType == "jpg" || fileType == "jpeg") + { + preview->imagePreview(QPixmap::fromImage(QImage::fromData(data)), fileName); + } else { - int index; - if((index = FF8Image::findFirstTim(data)) != -1) + QList indexes = FF8Image::findTims(data); + if(!indexes.isEmpty()) { - TimFile timFile(data.mid(index)); + TimFile timFile(data.mid(indexes.value(currentImage, indexes.first()))); timFile.setCurrentColorTable(currentPal); - preview->imagePreview(QPixmap::fromImage(timFile.image()), fileName, currentPal, timFile.colorTableCount()); + preview->imagePreview(QPixmap::fromImage(timFile.image()), fileName, + currentPal, timFile.colorTableCount(), + currentImage, indexes.size()); } else { @@ -200,9 +214,21 @@ void FsDialog::generatePreview() } } +void FsDialog::changeImageInPreview(int imageID) +{ + if(imageID < 0) { + return; + } + + currentImage = imageID; + generatePreview(); +} + void FsDialog::changeImagePaletteInPreview(int palID) { - if(palID < 0) return; + if(palID < 0) { + return; + } currentPal = palID; generatePreview(); diff --git a/FsDialog.h b/FsDialog.h index 594e487..6eac965 100644 --- a/FsDialog.h +++ b/FsDialog.h @@ -39,7 +39,8 @@ class FsDialog : public QWidget signals: private slots: void setButtonsEnabled(); - void generatePreview(); + void changePreview(); + void changeImageInPreview(int imageID); void changeImagePaletteInPreview(int palID); void doubleClicked(QTreeWidgetItem *); void openDir(); @@ -52,6 +53,7 @@ private slots: void rename(); void renameOK(QTreeWidgetItem *, int); private: + void generatePreview(); void add(QStringList sources, bool fromDir = false); void openDir(const QString &); QStringList listFilesInDir(QString dirPath); @@ -65,7 +67,7 @@ private slots: QLineEdit *pathWidget; QString currentPath; FsArchive *fsArchive; - int currentPal; + int currentImage, currentPal; }; #endif // FSDIALOG_H diff --git a/FsPreviewWidget.cpp b/FsPreviewWidget.cpp index 2750f15..a6030df 100644 --- a/FsPreviewWidget.cpp +++ b/FsPreviewWidget.cpp @@ -16,6 +16,7 @@ ** along with this program. If not, see . ****************************************************************************/ #include "FsPreviewWidget.h" +#include "BGPreview2.h" FsPreviewWidget::FsPreviewWidget(QWidget *parent) : QStackedWidget(parent) @@ -30,6 +31,7 @@ QWidget *FsPreviewWidget::imageWidget() { QWidget *ret = new QWidget(this); + imageSelect = new QComboBox(ret); palSelect = new QComboBox(ret); scrollArea = new QScrollArea(ret); @@ -38,9 +40,11 @@ QWidget *FsPreviewWidget::imageWidget() QVBoxLayout *layout = new QVBoxLayout(ret); layout->setContentsMargins(QMargins()); + layout->addWidget(imageSelect, 0, Qt::AlignCenter); layout->addWidget(palSelect, 0, Qt::AlignCenter); layout->addWidget(scrollArea); + connect(imageSelect, SIGNAL(currentIndexChanged(int)), SIGNAL(currentImageChanged(int))); connect(palSelect, SIGNAL(currentIndexChanged(int)), SIGNAL(currentPaletteChanged(int))); return ret; @@ -59,7 +63,9 @@ void FsPreviewWidget::clearPreview() setCurrentIndex(EmptyPage); } -void FsPreviewWidget::imagePreview(const QPixmap &image, const QString &name, int palID, int palCount) +void FsPreviewWidget::imagePreview(const QPixmap &image, const QString &name, + int palID, int palCount, int imageID, + int imageCount) { setCurrentIndex(ImagePage); BGPreview2 *lbl = new BGPreview2(); @@ -67,6 +73,19 @@ void FsPreviewWidget::imagePreview(const QPixmap &image, const QString &name, in lbl->setName(name); scrollArea->setWidget(lbl); + imageSelect->blockSignals(true); + imageSelect->clear(); + if(imageCount > 1) { + imageSelect->setVisible(true); + for(int i=0 ; iaddItem(tr("Image %1").arg(i)); + } + imageSelect->setCurrentIndex(imageID); + } else { + imageSelect->setVisible(false); + } + imageSelect->blockSignals(false); + palSelect->blockSignals(true); palSelect->clear(); if(palCount > 1) { diff --git a/FsPreviewWidget.h b/FsPreviewWidget.h index 293cc9e..3dff7b4 100644 --- a/FsPreviewWidget.h +++ b/FsPreviewWidget.h @@ -19,24 +19,26 @@ #define FSPREVIEWWIDGET_H #include -#include "BGPreview2.h" class FsPreviewWidget : public QStackedWidget { Q_OBJECT public: enum Pages { EmptyPage, ImagePage, TextPage }; - explicit FsPreviewWidget(QWidget *parent = 0); + explicit FsPreviewWidget(QWidget *parent = nullptr); void clearPreview(); - void imagePreview(const QPixmap &image, const QString &name=QString(), int palID=0, int palCount=0); + void imagePreview(const QPixmap &image, const QString &name = QString(), + int palID = 0, int palCount = 0, int imageID = 0, + int imageCount = 0); void textPreview(const QString &text); signals: + void currentImageChanged(int); void currentPaletteChanged(int); private: QWidget *imageWidget(); QWidget *textWidget(); QScrollArea *scrollArea; - QComboBox *palSelect; + QComboBox *imageSelect, *palSelect; }; #endif // FSPREVIEWWIDGET_H diff --git a/JsmData.cpp b/JsmData.cpp index 585d060..39dcd55 100644 --- a/JsmData.cpp +++ b/JsmData.cpp @@ -17,12 +17,13 @@ ****************************************************************************/ #include "JsmData.h" -JsmData::JsmData() +JsmData::JsmData() : + _demo(false) { } -JsmData::JsmData(const QByteArray &scriptData) : - scriptData(scriptData) +JsmData::JsmData(const QByteArray &scriptData, bool demo) : + scriptData(scriptData), _demo(demo) { if(this->scriptData.size() % 4 != 0) { qWarning() << "JsmData::JsmData : Incorrect size" << this->scriptData.size(); @@ -55,7 +56,14 @@ JsmOpcode JsmData::opcode(int opcodeID) const memcpy(&op, constData + opcodeID * 4, 4); - return JsmOpcode(op); + JsmOpcode ret(op); + + // CANIME and CANIMEKEEP does not exist in early demo version + if(_demo && ret.key() >= JsmOpcode::CANIME) { + ret.setKey(ret.key() + 2); + } + + return ret; } JsmOpcode *JsmData::opcodep(int opcodeID) const @@ -134,7 +142,7 @@ JsmData &JsmData::replace(int opcodeID, int nbOpcode, const JsmData &after) JsmData JsmData::mid(int opcodeID, int nbOpcode) const { - return JsmData(scriptData.mid(opcodeID*4, nbOpcode==-1 ? nbOpcode : nbOpcode*4)); + return JsmData(scriptData.mid(opcodeID*4, nbOpcode==-1 ? nbOpcode : nbOpcode*4), _demo); } const QByteArray &JsmData::constData() const diff --git a/JsmData.h b/JsmData.h index bb5b256..3d8a880 100644 --- a/JsmData.h +++ b/JsmData.h @@ -25,7 +25,7 @@ class JsmData { public: JsmData(); - JsmData(const QByteArray &scriptData); + JsmData(const QByteArray &scriptData, bool demo); int nbOpcode() const; JsmData &append(const JsmOpcode &opcode); JsmData &append(const JsmData &jsmData); @@ -46,6 +46,7 @@ class JsmData JsmOpcode operator[](int opcodeID) const; private: QByteArray scriptData; + bool _demo; }; #endif // JSMDATA_H diff --git a/JsmOpcode.h b/JsmOpcode.h index 10512d7..d2388dc 100644 --- a/JsmOpcode.h +++ b/JsmOpcode.h @@ -418,7 +418,7 @@ class JsmOpcode return false; } virtual inline JsmOpcode *controlStructJump() const { - return 0; + return nullptr; } virtual inline bool isVirtual() const { return false; diff --git a/JsmScripts.cpp b/JsmScripts.cpp index 12502d2..24f8772 100644 --- a/JsmScripts.cpp +++ b/JsmScripts.cpp @@ -691,6 +691,7 @@ JsmProgram JsmScripts::program(int groupID, int methodID, return JsmProgram(); } JsmOpcode *firstOp = opcodes.first(); + QList labels = searchJumps(opcodes); QList::const_iterator begin = opcodes.constBegin(); // Ignore label if correct at the beginning if(firstOp->key() == JsmOpcode::LBL @@ -698,7 +699,6 @@ JsmProgram JsmScripts::program(int groupID, int methodID, begin += 1; delete firstOp; } - QList labels = searchJumps(opcodes); QSet usedLabels; // Filled by program() JsmProgram p = program(opcodes.constBegin(), begin, opcodes.constEnd(), labels, collectPointers, usedLabels); diff --git a/LZS.cpp b/LZS.cpp index 4aa0f64..369f56f 100644 --- a/LZS.cpp +++ b/LZS.cpp @@ -40,21 +40,17 @@ const QByteArray &LZS::decompress(const QByteArray &data, int max) const QByteArray &LZS::decompress(const char *data, int fileSize, int max) { - int curResult=0, sizeAlloc=max+10; - quint16 curBuff=4078, adresse, premOctet=0, i, length; + int sizeAlloc=qMin(max, fileSize * 5); + quint16 curBuff=4078, offset, premOctet=0, i, length; const quint8 *fileData = (const quint8 *)data; const quint8 *endFileData = fileData + fileSize; - // Impossible case - if(quint64(sizeAlloc) > 2000 * quint64(fileSize)) { - qWarning() << "LZS::decompress impossible ratio case" << sizeAlloc << 2000 * quint64(fileSize); - result.clear(); - return result; - } + // Internal buffer is still allocated using this method instead of clear + result.resize(0); - if(result.size() < sizeAlloc) { + if(result.capacity() < sizeAlloc) { try { - result.resize(sizeAlloc); + result.reserve(sizeAlloc); } catch(std::bad_alloc) { result.clear(); return result; @@ -69,96 +65,26 @@ const QByteArray &LZS::decompress(const char *data, int fileSize, int max) premOctet = *fileData++ | 0xff00;//On récupère le premier octet puis on avance d'un octet } - if(fileData >= endFileData || curResult >= max) { - result.truncate(curResult); - return result;//Fini ! + if(fileData >= endFileData || result.size() >= max) { + return result; // Done } if(premOctet & 1) { - result[curResult] = text_buf[curBuff] = *fileData++;//On récupère l'octet (qui n'est pas compressé) et on le sauvegarde dans la chaine finale (result) et dans le buffer. Et bien sûr on fait avancer les curseurs d'un octet. + result.append(text_buf[curBuff] = *fileData++);//On récupère l'octet (qui n'est pas compressé) et on le sauvegarde dans la chaine finale (result) et dans le buffer. Et bien sûr on fait avancer les curseurs d'un octet. curBuff = (curBuff + 1) & 4095;//Le curseur du buffer doit toujours être entre 0 et 4095 - ++curResult; } else { -// quint16 twoBytes = *((const quint16 *)fileData); -// adresse = (twoBytes & 0x00FF) | ((twoBytes & 0xF000) >> 4); -// length = ((twoBytes & 0x0F00) >> 8) + 2 + adresse; -// fileData += 2; - - adresse = *fileData++; - length = *fileData++; - adresse |= (length & 0xF0) << 4;//on récupère l'adresse dans les deux octets (qui sont "compressés") - length = (length & 0xF) + 2 + adresse; - - for(i=adresse ; i<=length ; ++i) - { - result[curResult] = text_buf[curBuff] = text_buf[i & 4095];//On va chercher l'octet (qui est décompressé) dans le buffer à l'adresse indiquée puis on le sauvegarde dans la chaine finale et le buffer. - curBuff = (curBuff + 1) & 4095; - ++curResult; - } - } - } -} - -const QByteArray &LZS::decompressAll(const QByteArray &data) -{ - return decompressAll(data.constData(), data.size()); -} - -const QByteArray &LZS::decompressAll(const char *data, int fileSize) -{ - int curResult=0, sizeAlloc=fileSize*5; - quint16 curBuff=4078, adresse, premOctet=0, i, length; - const quint8 *fileData = (const quint8 *)data; - const quint8 *endFileData = fileData + fileSize; - - if(result.size() < sizeAlloc) { - try { - result.resize(sizeAlloc); - } catch(std::bad_alloc) { - result.clear(); - return result; - } - } - - memset(text_buf, 0, 4078);//Le buffer de 4096 octets est initialisé à 0 - - forever - { - if(((premOctet >>= 1) & 256) == 0) { - premOctet = *fileData++ | 0xff00;//On récupère le premier octet puis on avance d'un octet - } - - if(fileData >= endFileData) { - result.truncate(curResult); - return result;//Fini ! - } - - if(premOctet & 1) - { - result[curResult] = text_buf[curBuff] = *fileData++;//On récupère l'octet (qui n'est pas compressé) et on le sauvegarde dans la chaine finale (result) et dans le buffer. Et bien sûr on fait avancer les curseurs d'un octet. - curBuff = (curBuff + 1) & 4095;//Le curseur du buffer doit toujours être entre 0 et 4095 - ++curResult; - } - else - { -// quint16 twoBytes = *((const quint16 *)fileData); -// adresse = (twoBytes & 0x00FF) | ((twoBytes & 0xF000) >> 4); -// length = ((twoBytes & 0x0F00) >> 8) + 2 + adresse; -// fileData += 2; - - adresse = *fileData++; + offset = *fileData++; length = *fileData++; - adresse |= (length & 0xF0) << 4;//on récupère l'adresse dans les deux octets (qui sont "compressés") - length = (length & 0xF) + 2 + adresse; + offset |= (length & 0xF0) << 4;//on récupère l'offset dans les deux octets (qui sont "compressés") + length = (length & 0xF) + 2 + offset; - for(i=adresse ; i<=length ; ++i) + for(i=offset ; i<=length ; ++i) { - result[curResult] = text_buf[curBuff] = text_buf[i & 4095];//On va chercher l'octet (qui est décompressé) dans le buffer à l'adresse indiquée puis on le sauvegarde dans la chaine finale et le buffer. + result.append(text_buf[curBuff] = text_buf[i & 4095]);//On va chercher l'octet (qui est décompressé) dans le buffer à l'adresse indiquée puis on le sauvegarde dans la chaine finale et le buffer. curBuff = (curBuff + 1) & 4095; - ++curResult; } } } diff --git a/LZS.h b/LZS.h index 8e4f29f..96272d5 100644 --- a/LZS.h +++ b/LZS.h @@ -31,10 +31,8 @@ class LZS { public: - static const QByteArray &decompress(const QByteArray &data, int max); - static const QByteArray &decompress(const char *data, int fileSize, int max); - static const QByteArray &decompressAll(const QByteArray &data); - static const QByteArray &decompressAll(const char *data, int fileSize); + static const QByteArray &decompress(const QByteArray &data, int max = std::numeric_limits::max()); + static const QByteArray &decompress(const char *data, int fileSize, int max = std::numeric_limits::max()); static const QByteArray &compress(const QByteArray &fileData); static const QByteArray &compress(const char *data, int sizeData); static void clear(); diff --git a/Listwidget.cpp b/Listwidget.cpp index c0a8f3e..e271018 100644 --- a/Listwidget.cpp +++ b/Listwidget.cpp @@ -26,12 +26,8 @@ ListWidget::ListWidget(QWidget *parent) : _toolBar = new QToolBar(this); _toolBar->setIconSize(QSize(16, 16)); - QFont font; - font.setPointSize(8); - _listWidget = new QListWidget(this); _listWidget->setUniformItemSizes(true); - _listWidget->setFont(font); QVBoxLayout *layout = new QVBoxLayout(this); layout->addWidget(_toolBar); diff --git a/MainWindow.cpp b/MainWindow.cpp index f1cfd65..b864301 100644 --- a/MainWindow.cpp +++ b/MainWindow.cpp @@ -22,15 +22,23 @@ #include "ProgressWidget.h" #include "ScriptExporter.h" #include "EncounterExporter.h" +#include "BackgroundExporter.h" +#include "widgets/MsdWidget.h" +#include "widgets/JsmWidget.h" +#include "widgets/CharaWidget.h" +#include "widgets/WalkmeshWidget.h" +#include "widgets/BackgroundWidget.h" +#include "widgets/EncounterWidget.h" +#include "widgets/TdwWidget.h" +#include "widgets/SoundWidget.h" +#include "widgets/MiscWidget.h" +#include "widgets/AboutDialog.h" MainWindow::MainWindow() : fieldArchive(nullptr), field(nullptr), currentField(nullptr), fieldThread(new FieldThread), file(nullptr), menuGameLang(nullptr), fsDialog(nullptr), _varManager(nullptr), firstShow(true) { - QFont font; - font.setPointSize(8); - setMinimumSize(700, 600); resize(Config::value("mainWindowSize", QSize(768, 502)).toSize()); if(Config::value("mainWindowMaximized", true).toBool()) @@ -54,12 +62,13 @@ MainWindow::MainWindow() menuExportAll = menu->addMenu(tr("Exporter tout")); menuExportAll->addAction(tr("Scripts..."), this, SLOT(exportAllScripts())); menuExportAll->addAction(tr("Rencontres aléatoires..."), this, SLOT(exportAllEncounters())); + menuExportAll->addAction(tr("Décors..."), this, SLOT(exportAllBackground())); actionImport = menu->addAction(tr("Importer..."), this, SLOT(importCurrent())); actionOpti = menu->addAction(tr("Optimiser l'archive..."), this, SLOT(optimizeArchive())); menu->addSeparator(); menu->addAction(tr("Plein écran"), this, SLOT(fullScreen()), Qt::Key_F11); actionClose = menu->addAction(QApplication::style()->standardIcon(QStyle::SP_DialogCloseButton), tr("Fe&rmer"), this, SLOT(closeFiles())); - menu->addAction(tr("&Quitter"), this, SLOT(close())); + menu->addAction(tr("&Quitter"), this, SLOT(close()))->setMenuRole(QAction::QuitRole); menu = menuBar->addMenu(tr("&Outils")); actionFind = menu->addAction(QIcon(":/images/find.png"), tr("Rec&hercher..."), this, SLOT(search()), QKeySequence::Find); @@ -69,9 +78,14 @@ MainWindow::MainWindow() actionRun->setShortcutContext(Qt::ApplicationShortcut); actionRun->setEnabled(Data::ff8Found()); addAction(actionRun); - menuBar->addAction(tr("Op&tions"), this, SLOT(configDialog())); - menuBar->addAction(tr("?"), this, SLOT(about())); +#ifndef Q_OS_MAC + menuBar->addAction(tr("Op&tions"), this, SLOT(configDialog()))->setMenuRole(QAction::PreferencesRole); + menuBar->addAction(tr("&?"), this, SLOT(about()))->setMenuRole(QAction::AboutRole); +#else + menu->addAction(tr("Op&tions"), this, SLOT(configDialog()))->setMenuRole(QAction::PreferencesRole); + menu->addAction(tr("&?"), this, SLOT(about()))->setMenuRole(QAction::AboutRole); +#endif setMenuBar(menuBar); @@ -90,7 +104,6 @@ MainWindow::MainWindow() list1->setSortingEnabled(true); list1->setAutoScroll(false); list1->setColumnWidth(2, 28); - list1->setFont(font); list1->sortByColumn(Config::value("list1ColumnSort",2).toInt(), Qt::AscendingOrder); list1->setUniformRowHeights(true); list1->header()->setStretchLastSection(false); @@ -153,13 +166,16 @@ MainWindow::MainWindow() setCentralWidget(mainStackedWidget); - searchDialog = new Search(list1, this); + searchAllDialog = new SearchAll(this); + searchDialog = new Search(list1, searchAllDialog, this); closeFiles(); setCurrentPage(Config::value("currentPage", TextPage).toInt()); connect(searchDialog, SIGNAL(foundText(int,int,int,int)), SLOT(gotoText(int,int,int,int))); connect(searchDialog, SIGNAL(foundOpcode(int,int,int,int)), SLOT(gotoScript(int,int,int,int))); + connect(searchAllDialog, SIGNAL(foundText(int,int,int,int)), SLOT(gotoText(int,int,int,int))); + connect(searchAllDialog, SIGNAL(foundOpcode(int,int,int,int)), SLOT(gotoScript(int,int,int,int))); connect(lineSearch, SIGNAL(textEdited(QString)), SLOT(filterMap())); connect(lineSearch, SIGNAL(returnPressed()), SLOT(filterMap())); connect(this, SIGNAL(fieldIdChanged(int)), searchDialog, SLOT(setFieldId(int))); @@ -235,6 +251,7 @@ void MainWindow::filterMap() bool MainWindow::openArchive(const QString &path) { searchDialog->setFieldArchive(fieldArchive); + searchAllDialog->setFieldArchive(fieldArchive); if(_varManager != nullptr) _varManager->setFieldArchive(fieldArchive); @@ -531,6 +548,7 @@ int MainWindow::closeFiles(bool quit) setReadOnly(false); searchDialog->setFieldArchive(nullptr); + searchAllDialog->setFieldArchive(nullptr); if(_varManager != nullptr) _varManager->setFieldArchive(nullptr); if(fsDialog) { @@ -695,12 +713,6 @@ void MainWindow::exportCurrent() } } } - else if(i == Field::Background) { - if(currentField->hasBackgroundFile()) { -// filter.append(currentField->getJsmFile()->filterText()); -// typeList.append(i); - } - } else { if(currentField->hasFile(Field::FileType(i))) { filter.append(currentField->getFile(Field::FileType(i))->filterText()); @@ -777,6 +789,28 @@ void MainWindow::exportAllEncounters() } } +void MainWindow::exportAllBackground() +{ + if(!fieldArchive) return; + + QString oldPath = Config::value("export_path").toString(); + + QString dirPath = QFileDialog::getExistingDirectory(this, tr("Exporter"), oldPath); + if (dirPath.isNull()) { + return; + } + + Config::setValue("export_path", dirPath); + + ProgressWidget progress(tr("Export..."), ProgressWidget::Cancel, this); + + BackgroundExporter exporter(fieldArchive); + + if (!exporter.toDir(dirPath, &progress) && !progress.observerWasCanceled()) { + QMessageBox::warning(this, tr("Erreur"), exporter.errorString()); + } +} + void MainWindow::importCurrent() { if(!currentField) return; @@ -958,40 +992,6 @@ void MainWindow::gotoScript(int fieldID, int groupID, int methodID, int opcodeID void MainWindow::about() { - QDialog about(this, Qt::Dialog | Qt::CustomizeWindowHint); - about.setFixedSize(200,300); - - QFont font; - font.setPointSize(12); - - QLabel image(&about); - image.setPixmap(QPixmap(":/images/deling_city.png")); - image.move(-5, about.height() - 128); - - QLabel desc1(PROG_FULLNAME, &about); - desc1.setFont(font); - desc1.setFixedWidth(about.width()); - desc1.setAlignment(Qt::AlignHCenter); - - font.setPointSize(8); - - QLabel desc2(tr("Par myst6re
github.com/myst6re/deling

Merci à :
  • Aali
  • Aladore384
  • Asa
  • Maki
  • kruci
"), &about); - desc2.setTextInteractionFlags(Qt::LinksAccessibleByMouse | Qt::LinksAccessibleByKeyboard); - desc2.setTextFormat(Qt::RichText); - desc2.setOpenExternalLinks(true); - desc2.move(9,40); - desc2.setFont(font); - - QLabel desc3(QString("Qt %1").arg(QT_VERSION_STR), &about); - QPalette pal = desc3.palette(); - pal.setColor(QPalette::WindowText, QColor(0xAA,0xAA,0xAA)); - desc3.setPalette(pal); - desc3.move(about.width()-8-desc3.sizeHint().width(), about.height()-8-desc3.sizeHint().height()); - desc3.setFont(font); - - QPushButton button(tr("Fermer"), &about); - button.move(8, about.height()-8-button.sizeHint().height()); - connect(&button, SIGNAL(released()), &about, SLOT(close())); - + AboutDialog about(this); about.exec(); } diff --git a/MainWindow.h b/MainWindow.h index 85323fe..692981c 100644 --- a/MainWindow.h +++ b/MainWindow.h @@ -24,17 +24,10 @@ #include "FieldArchivePS.h" #include "FieldThread.h" #include "Search.h" +#include "SearchAll.h" #include "BGPreview.h" #include "TextPreview.h" -#include "widgets/MsdWidget.h" -#include "widgets/JsmWidget.h" -#include "widgets/CharaWidget.h" -#include "widgets/WalkmeshWidget.h" -#include "widgets/BackgroundWidget.h" -#include "widgets/EncounterWidget.h" -#include "widgets/TdwWidget.h" -#include "widgets/SoundWidget.h" -#include "widgets/MiscWidget.h" +#include "widgets/PageWidget.h" #include "VarManager.h" #include "MiscSearch.h" #include "FsDialog.h" @@ -67,6 +60,7 @@ private slots: void exportCurrent(); void exportAllScripts(); void exportAllEncounters(); + void exportAllBackground(); void importCurrent(); void optimizeArchive(); void manageArchive(); @@ -102,6 +96,7 @@ private slots: MiscSearch *miscSearchD; QLabel *currentPath; Search *searchDialog; + SearchAll *searchAllDialog; QAction *actionSave; QAction *actionSaveAs; QMenu *menuExportAll; diff --git a/MiscSearch.cpp b/MiscSearch.cpp index d47b1be..8c83126 100644 --- a/MiscSearch.cpp +++ b/MiscSearch.cpp @@ -23,11 +23,8 @@ MiscSearch::MiscSearch(FieldArchive *fieldArchive, QWidget *parent) setWindowTitle(tr("Rechercher tout")); setWindowModality(Qt::NonModal); setSizeGripEnabled(true); - QFont font; - font.setPointSize(8); list = new QListWidget(this); - list->setFont(font); list->setUniformItemSizes(true); textEdit = new QPlainTextEdit(this); diff --git a/Search.cpp b/Search.cpp index 30d0018..4937e93 100644 --- a/Search.cpp +++ b/Search.cpp @@ -16,9 +16,11 @@ ** along with this program. If not, see . ****************************************************************************/ #include "Search.h" +#include "SearchAll.h" -Search::Search(QTreeWidget *fieldList, QWidget *parent) - : QDialog(parent, Qt::Tool), fieldArchive(NULL), fieldList(fieldList) +Search::Search(QTreeWidget *fieldList, SearchAll *searchAllDialog, QWidget *parent) + : QDialog(parent, Qt::Tool), fieldArchive(nullptr), fieldList(fieldList), + searchAllDialog(searchAllDialog) { fieldID = -1; textID = from = groupID = methodID = opcodeID = 0; @@ -32,25 +34,26 @@ Search::Search(QTreeWidget *fieldList, QWidget *parent) buttonNext = new QPushButton(tr("Chercher le suivant"), this); buttonPrev = new QPushButton(tr("Chercher le précédent"), this); + buttonSearchAll = new QPushButton(tr("Chercher tout"), this); buttonPrev->setAutoDefault(false); buttonNext->setAutoDefault(false); buttonNext->setEnabled(false); buttonPrev->setEnabled(false); buttonNext->setDefault(true); - new QShortcut(QKeySequence::FindNext, this, SLOT(findNext()), 0, Qt::ApplicationShortcut); - new QShortcut(QKeySequence::FindPrevious, this, SLOT(findPrev()), 0, Qt::ApplicationShortcut); + new QShortcut(QKeySequence::FindNext, this, SLOT(findNext()), nullptr, Qt::ApplicationShortcut); + new QShortcut(QKeySequence::FindPrevious, this, SLOT(findPrev()), nullptr, Qt::ApplicationShortcut); + int maxWidth = qMax(buttonPrev->sizeHint().width(), buttonNext->sizeHint().width()); // buttonNext.width == buttonPrev.width - if(buttonPrev->sizeHint().width() > buttonNext->sizeHint().width()) - buttonNext->setFixedSize(buttonPrev->sizeHint()); - else - buttonPrev->setFixedSize(buttonNext->sizeHint()); + buttonNext->setFixedWidth(maxWidth); + buttonPrev->setFixedWidth(maxWidth); QGridLayout *layout = new QGridLayout(this); layout->addWidget(tabWidget, 0, 0, 1, 2); layout->addWidget(buttonPrev, 1, 0, Qt::AlignRight); layout->addWidget(buttonNext, 1, 1, Qt::AlignLeft); + layout->addWidget(buttonSearchAll, 2, 0, 1, 2, Qt::AlignCenter); QMargins margins = layout->contentsMargins(); margins.setTop(0); margins.setLeft(0); @@ -59,6 +62,7 @@ Search::Search(QTreeWidget *fieldList, QWidget *parent) connect(buttonNext, SIGNAL(released()), SLOT(findNext())); connect(buttonPrev, SIGNAL(released()), SLOT(findPrev())); + connect(buttonSearchAll, SIGNAL(released()), SLOT(findAll())); connect(tabWidget, SIGNAL(currentChanged(int)), SLOT(setFocus())); connect(searchTextField, SIGNAL(textEdited(QString)), searchScriptTextField, SLOT(setText(QString))); connect(searchScriptTextField, SIGNAL(textEdited(QString)), searchTextField, SLOT(setText(QString))); @@ -147,7 +151,6 @@ QWidget *Search::scriptPageWidget() layout->addWidget(typeScriptChoice); layout->addWidget(scriptStacked); layout->addStretch(1); - layout->setContentsMargins(QMargins()); connect(typeScriptChoice, SIGNAL(currentIndexChanged(int)), scriptStacked, SLOT(setCurrentIndex(int))); connect(typeScriptChoice, SIGNAL(currentIndexChanged(int)), SLOT(setFocus())); @@ -168,6 +171,7 @@ QWidget *Search::scriptPageWidget1() layout->addWidget(searchScriptTextField); layout->addWidget(scriptCheckBox); layout->addWidget(scriptCheckBox2); + layout->setContentsMargins(QMargins()); layout->addStretch(1); return ret; @@ -181,6 +185,10 @@ QWidget *Search::scriptPageWidget2() for(int i=0 ; iaddItem(QString("%1 - %2").arg(i, 3, 16, QChar('0')).arg(JsmOpcode::opcodes[i])); } + searchOpcode->setEditable(true); + searchOpcode->setInsertPolicy(QComboBox::NoInsert); + searchOpcode->completer()->setCompletionMode(QCompleter::PopupCompletion); + searchOpcode->completer()->setFilterMode(Qt::MatchContains); searchOpcodeValue = new QSpinBox(ret); searchOpcodeValue->setRange(-1, 162777216);// 0 -> 2^24 @@ -192,6 +200,7 @@ QWidget *Search::scriptPageWidget2() layout->addWidget(searchOpcodeValue, 1, 1); layout->setRowStretch(2, 1); layout->setColumnStretch(1, 1); + layout->setContentsMargins(QMargins()); return ret; } @@ -203,6 +212,10 @@ QWidget *Search::scriptPageWidget3() selectScriptVar = new QComboBox(ret); for(int i=0 ; i<1536 ; ++i) selectScriptVar->addItem(QString("%1 - %2").arg(i, 4, 10, QChar('0')).arg(Config::value(QString("var%1").arg(i)).toString())); + selectScriptVar->setEditable(true); + selectScriptVar->setInsertPolicy(QComboBox::NoInsert); + selectScriptVar->completer()->setCompletionMode(QCompleter::PopupCompletion); + selectScriptVar->completer()->setFilterMode(Qt::MatchContains); QGroupBox *group = new QGroupBox(ret); QRadioButton *allScriptVar = new QRadioButton(tr("Tout"), ret); popScriptVar = new QRadioButton(tr("Pop uniquement"), ret); @@ -218,8 +231,10 @@ QWidget *Search::scriptPageWidget3() groupLayout->addStretch(1); QVBoxLayout *layout = new QVBoxLayout(ret); - layout->addWidget(selectScriptVar, 0, Qt::AlignTop); - layout->addWidget(group, 0, Qt::AlignTop); + layout->addWidget(selectScriptVar, 0); + layout->addWidget(group, 0); + layout->addStretch(1); + layout->setContentsMargins(QMargins()); return ret; } @@ -238,6 +253,8 @@ QWidget *Search::scriptPageWidget4() layout->addWidget(new QLabel(tr("Label"),ret), 0, 1); layout->addWidget(selectScriptGroup, 1, 0); layout->addWidget(selectScriptLabel, 1, 1); + layout->setRowStretch(2, 1); + layout->setContentsMargins(QMargins()); return ret; } @@ -249,9 +266,11 @@ QWidget *Search::scriptPageWidget5() selectMap = new QSpinBox(ret); selectMap->setRange(0, 65535); - QHBoxLayout *layout = new QHBoxLayout(ret); - layout->addWidget(new QLabel(tr("Écran id"),ret)); - layout->addWidget(selectMap); + QGridLayout *layout = new QGridLayout(ret); + layout->addWidget(new QLabel(tr("Écran id"),ret), 0, 0); + layout->addWidget(selectMap, 0, 1); + layout->setRowStretch(1, 1); + layout->setContentsMargins(QMargins()); return ret; } @@ -282,30 +301,22 @@ void Search::focusInEvent(QFocusEvent *) void Search::setFieldArchive(FieldArchive *fieldArchive) { this->fieldArchive = fieldArchive; - buttonNext->setEnabled(fieldArchive!=NULL); - buttonPrev->setEnabled(fieldArchive!=NULL); + buttonNext->setEnabled(fieldArchive != nullptr); + buttonPrev->setEnabled(fieldArchive != nullptr); + buttonSearchAll->setEnabled(fieldArchive != nullptr); } void Search::setFieldId(int fieldID) { -// if(fieldID != this->fieldID) -// { - this->fieldID = fieldID; -// qDebug() << "FieldID=" << fieldID << "(Search::setFieldId)"; - this->textID = -1; -// qDebug() << "textID=" << textID << "(Search::setFieldId::textID=0)"; - this->from = -1; -// } + this->fieldID = fieldID; + this->textID = -1; + this->from = -1; } void Search::setTextId(int textID) { -// if(textID != this->textID) -// { - this->textID = textID; -// qDebug() << "textID=" << textID << "(Search::setTextId)"; - this->from = -1; -// } + this->textID = textID; + this->from = -1; } void Search::setFrom(int from) @@ -377,6 +388,30 @@ void Search::findPrev() buttonPrev->setEnabled(true); } +void Search::findAll() +{ + if (fieldArchive == nullptr) { + return; + } + + searchAllDialog->show(); + searchAllDialog->activateWindow(); + searchAllDialog->raise(); + + parentWidget()->setEnabled(false); + setEnabled(false); + + if (currentIndex() == Text) { + findAllText(); + } + else if (currentIndex() == Script) { + findAllScript(); + } + + parentWidget()->setEnabled(true); + setEnabled(true); +} + bool Search::findNextText() { int size; @@ -384,49 +419,53 @@ bool Search::findNextText() ++from; - if(fieldArchive->searchText(regexp(), fieldID, textID, from, size, sort)) - { -// qDebug() << "from=" << from << "(MsdFile::findNextText::from=index)"; + if (fieldArchive->searchText(regexp(), fieldID, textID, from, size, sort)) { emit foundText(fieldID, textID, from, size); - // "found" signal may change 'from' value -// qDebug() << "from=" << from << "(MsdFile::findNextText::from++)"; return true; } - textID = from = fieldID = -1; -// qDebug() << "FieldID=" << fieldID << "(Search::findNextText::0)"; -// qDebug() << "textID=" << textID << "(Search::findNextText::0)"; -// qDebug() << "from=" << from << "(MsdFile::findNextText::0)"; + fieldID = textID = from = -1; return false; } bool Search::findPrevText() { - int index, size; + int size; FieldArchive::Sorting sort = sorting(); --from; - if(fieldArchive->searchTextReverse(regexp(), fieldID, textID, from, index, size, sort)) - { -// qDebug() << "from=" << from << "(Search::findPrevText::from=index)"; - emit foundText(fieldID, textID, index, size); + if (fieldArchive->searchTextReverse(regexp(), fieldID, textID, from, size, sort)) { + emit foundText(fieldID, textID, from, size); return true; } - fieldID = textID = 2147483647; - from = 0; -// qDebug() << "FieldID=" << fieldID << "(Search::findPrevText::0)"; -// qDebug() << "textID=" << textID << "(Search::findPrevText::0)"; -// qDebug() << "from=" << from << "(Search::findPrevText::0)"; + fieldID = textID = from = 2147483647; return false; + } bool Search::findNextScript() { int sav; + + ++opcodeID; + + if (findNextScript(fieldID, groupID, methodID, opcodeID)) { + sav = opcodeID; + emit foundOpcode(fieldID, groupID, methodID, opcodeID); + // "found" signal may change 'opcodeID' value + opcodeID = sav; + return true; + } + + return false; +} + +bool Search::findNextScript(int &fieldID, int &groupID, int &methodID, int &opcodeID) +{ bool found = false; FieldArchive::Sorting sort = sorting(); @@ -441,36 +480,31 @@ bool Search::findNextScript() } else if(typeScriptChoice->currentIndex() == 3) { Field *field = fieldArchive->getField(fieldID); - if(field != NULL && field->hasJsmFile()) { + if(field != nullptr && field->hasJsmFile()) { found = field->getJsmFile()->search(JsmFile::SearchExec, (selectScriptGroup->value() & 0xFFFF) | ((selectScriptLabel->value() & 0xFFFF) << 16), groupID, methodID, opcodeID); } if(!found) { - groupID = methodID = opcodeID = 0; + groupID = methodID = opcodeID = 2147483647; return false; } } else if(typeScriptChoice->currentIndex() == 4) { Field *field = fieldArchive->getField(fieldID); - if(field != NULL && field->hasJsmFile()) { + if(field != nullptr && field->hasJsmFile()) { found = field->getJsmFile()->search(JsmFile::SearchMapJump, (selectScriptGroup->value() & 0xFFFF) | ((selectScriptLabel->value() & 0xFFFF) << 16), groupID, methodID, opcodeID); } if(!found) { - groupID = methodID = opcodeID = 0; + groupID = methodID = opcodeID = 2147483647; return false; } } if(found) { - sav = opcodeID; - emit foundOpcode(fieldID, groupID, methodID, opcodeID); - // "found" signal may change 'opcodeID' value - opcodeID = sav + 1; return true; } - groupID = methodID = opcodeID = 0; - fieldID = -1; + fieldID = groupID = methodID = opcodeID = 2147483647; return false; } @@ -481,6 +515,8 @@ bool Search::findPrevScript() bool found = false; FieldArchive::Sorting sort = sorting(); + --opcodeID; + if(typeScriptChoice->currentIndex() == 0) { found = fieldArchive->searchScriptTextReverse(regexp(), fieldID, groupID, methodID, opcodeID, sort); } @@ -502,11 +538,53 @@ bool Search::findPrevScript() sav = opcodeID; emit foundOpcode(fieldID, groupID, methodID, opcodeID); // "found" signal may change 'opcodeID' value - opcodeID = sav - 1; + opcodeID = sav; return true; } - fieldID = groupID = methodID = opcodeID = -1; + fieldID = groupID = methodID = opcodeID = 2147483647; return false; } + +void Search::findAllScript() +{ + int fieldID = -1, groupID = 0, methodID = 0, opcodeID = 0; + + QDeadlineTimer t(50); + + searchAllDialog->setScriptSearch(); + while (findNextScript(fieldID, groupID, methodID, ++opcodeID)) { + if (t.hasExpired()) { + QCoreApplication::processEvents(); + t.setRemainingTime(50); + } + // Cancelled + if (searchAllDialog->isHidden()) { + break; + } + searchAllDialog->addResultOpcode(fieldID, groupID, methodID, opcodeID); + } +} + +void Search::findAllText() +{ + FieldArchive::Sorting sort = sorting(); + int fieldID = -1, textID = -1, from = -1; + + QDeadlineTimer t(50); + + searchAllDialog->setTextSearch(); + int size; + while (fieldArchive->searchText(regexp(), fieldID, textID, ++from, size, sort)) { + if (t.hasExpired()) { + QCoreApplication::processEvents(); + t.setRemainingTime(50); + } + // Cancelled + if (searchAllDialog->isHidden()) { + break; + } + searchAllDialog->addResultText(fieldID, textID, from, size); + } +} diff --git a/Search.h b/Search.h index 8922134..f3b1897 100644 --- a/Search.h +++ b/Search.h @@ -21,6 +21,8 @@ #include #include "FieldArchive.h" +class SearchAll; + class Search : public QDialog { Q_OBJECT @@ -29,7 +31,7 @@ class Search : public QDialog Text=0, Script }; - explicit Search(QTreeWidget *fieldList, QWidget *parent=0); + explicit Search(QTreeWidget *fieldList, SearchAll *searchAllDialog, QWidget *parent = nullptr); void setFieldArchive(FieldArchive *); void setSearchText(const QString &text); void setSearchOpcode(int opcode); @@ -45,6 +47,7 @@ public slots: private slots: void findNext(); void findPrev(); + void findAll(); protected: void focusInEvent(QFocusEvent *); private: @@ -55,7 +58,10 @@ private slots: bool findNextText(); bool findPrevText(); bool findNextScript(); + bool findNextScript(int &fieldID, int &groupID, int &methodID, int &opcodeID); bool findPrevScript(); + void findAllScript(); + void findAllText(); QWidget *textPageWidget(); QWidget *scriptPageWidget(); QWidget *scriptPageWidget1(); @@ -73,9 +79,10 @@ private slots: QRadioButton *popScriptVar, *pushScriptVar; QSpinBox *selectScriptGroup, *selectScriptLabel; QStackedWidget *scriptStacked; - QPushButton *buttonNext, *buttonPrev; + QPushButton *buttonNext, *buttonPrev, *buttonSearchAll; FieldArchive *fieldArchive; QTreeWidget *fieldList; + SearchAll *searchAllDialog; int fieldID; int textID, from; int groupID, methodID, opcodeID; diff --git a/SearchAll.cpp b/SearchAll.cpp new file mode 100644 index 0000000..f5b69d0 --- /dev/null +++ b/SearchAll.cpp @@ -0,0 +1,204 @@ +#include "SearchAll.h" +#include "MainWindow.h" +#include "FieldArchive.h" +#include "Config.h" + +SearchAll::SearchAll(QWidget *parent) : + QDialog(parent, Qt::Tool), _fieldArchive(nullptr) +{ + setWindowTitle(tr("Chercher tout")); + + resize(800, 600); + _resultList = new QTreeWidget(this); + _resultList->setAlternatingRowColors(true); + _resultList->setItemsExpandable(true); + _resultList->setSortingEnabled(true); + _resultList->setAutoScroll(false); + _resultList->setFrameShape(QFrame::NoFrame); + _resultList->setExpandsOnDoubleClick(false); + _resultList->setContextMenuPolicy(Qt::ActionsContextMenu); + _resultList->setSelectionMode(QAbstractItemView::ExtendedSelection); + + QGridLayout *layout = new QGridLayout(this); + layout->addWidget(_resultList, 0, 0); + layout->setContentsMargins(QMargins()); + + connect(_resultList, SIGNAL(itemActivated(QTreeWidgetItem*,int)), SLOT(gotoResult(QTreeWidgetItem*))); + + QAction *copy = new QAction(QIcon(":/images/copy.png"), tr("Copy"), this); + copy->setShortcut(QKeySequence("Ctrl+C")); + copy->setShortcutContext(Qt::WidgetWithChildrenShortcut); + connect(copy, SIGNAL(triggered()), SLOT(copySelected())); + + _resultList->addAction(copy); + + setScriptSearch(); +} + +void SearchAll::clear() +{ + _resultList->clear(); + itemByMapID.clear(); +} + +void SearchAll::setScriptSearch() +{ + clear(); + _resultList->setColumnCount(4); + _resultList->setHeaderLabels(QStringList() << tr("Group") << tr("Script") << tr("Lines") << tr("Instruction")); + _searchMode = ScriptSearch; +} + +void SearchAll::setTextSearch() +{ + clear(); + _resultList->setColumnCount(2); + _resultList->setHeaderLabels(QStringList() << tr("Text #") << tr("Text")); + _searchMode = TextSearch; +} + +void SearchAll::setFieldArchive(FieldArchive *fieldArchive) +{ + _fieldArchive = fieldArchive; + setEnabled(fieldArchive); + if (!fieldArchive) { + clear(); + } +} + +void SearchAll::addResultOpcode(int mapID, int groupID, int methodID, int opcodeID) +{ + addResult(mapID, createItemOpcode(mapID, groupID, methodID, opcodeID)); +} + +void SearchAll::addResultText(int mapID, int textID, int index, int size) +{ + addResult(mapID, createItemText(mapID, textID, index, size)); +} + +void SearchAll::addResult(int mapID, QTreeWidgetItem *item) +{ + QTreeWidgetItem *itemToAdd = itemByMapID.value(mapID); + if (!itemToAdd) { + itemToAdd = createItemField(mapID); + itemByMapID.insert(mapID, itemToAdd); + _resultList->addTopLevelItem(itemToAdd); + itemToAdd->setExpanded(true); + } + itemToAdd->addChild(item); +} + +QTreeWidgetItem *SearchAll::createItemField(int mapID) const +{ + QTreeWidgetItem *item = new QTreeWidgetItem( + QStringList() + << QString("%1").arg(mapID, 3)); + + item->setData(0, Qt::UserRole, mapID); + if (_fieldArchive != nullptr) { + Field *f = _fieldArchive->getField(mapID); + if (f) { + item->setText(0, QString("%1 : %2") + .arg(mapID, 3) + .arg(f->name())); + } + } + + for (int col = 0; col < _resultList->columnCount(); ++col) { + item->setBackground(col, Qt::gray); + item->setForeground(col, Qt::white); + } + + return item; +} + +QTreeWidgetItem *SearchAll::createItemOpcode(int mapID, int groupID, int methodID, int opcodeID) const +{ + QTreeWidgetItem *item = new QTreeWidgetItem( + QStringList() + << QString("%1").arg(groupID, 3) + << QString("%1").arg(methodID, 2) + << QString("%1").arg(opcodeID + 1, 5) + ); + + item->setData(0, Qt::UserRole, mapID); + item->setData(1, Qt::UserRole, groupID); + item->setData(2, Qt::UserRole, methodID); + item->setData(3, Qt::UserRole, opcodeID); + + if(_fieldArchive) { + Field *f = _fieldArchive->getField(mapID); + if (f) { + const JsmScripts &s = f->getJsmFile()->getScripts(); + const QString &groupName = s.group(groupID).name(); + const QString &scriptName = s.script(groupID, methodID).name(); + item->setText(0, groupName.isEmpty() ? QString("%1").arg(groupID, 3) : QString("%1 : %2").arg(groupID, 3).arg(groupName)); + item->setText(1, scriptName.isEmpty() ? QString("%1").arg(methodID, 3) : QString("%1 : %2").arg(methodID, 3).arg(scriptName)); + item->setText(3, s.opcode(groupID, methodID, opcodeID).toString()); + } + } + + return item; +} + +QTreeWidgetItem *SearchAll::createItemText(int mapID, int textID, int index, int size) const +{ + Q_UNUSED(size) + QTreeWidgetItem *item = new QTreeWidgetItem( + QStringList() + << QString("%1").arg(textID, 3) + << QString("%1").arg(index, 3)); + + item->setData(0, Qt::UserRole, mapID); + item->setData(1, Qt::UserRole, textID); + item->setData(2, Qt::UserRole, index); + + if (_fieldArchive != nullptr) { + Field *f = _fieldArchive->getField(mapID); + if (f) { + item->setText(1, f->getMsdFile()->text(textID)); + } + } + + return item; +} + +void SearchAll::gotoResult(QTreeWidgetItem *item) +{ + int mapID = item->data(0, Qt::UserRole).toInt(); + + if (_searchMode == ScriptSearch) { + int groupID = item->data(1, Qt::UserRole).toInt(), + methodID = item->data(2, Qt::UserRole).toInt(), + opcodeID = item->data(3, Qt::UserRole).toInt(); + emit foundOpcode(mapID, groupID, methodID, opcodeID); + } else { + int textID = item->data(1, Qt::UserRole).toInt(), + index = item->data(2, Qt::UserRole).toInt(), + size = item->data(3, Qt::UserRole).toInt(); + emit foundText(mapID, textID, index, size); + } +} + +void SearchAll::copySelected() const +{ + QList selectedItems = _resultList->selectedItems(); + + QString str; + QTreeWidgetItemIterator it(_resultList); + while (*it) { + if(selectedItems.contains(*it)) { + QStringList cols; + for (int col = 0; col < (*it)->columnCount(); ++col) { + cols.append((*it)->text(col).trimmed()); + } + str.append(cols.join("\t").trimmed() + "\n"); + if (cols.value(1).isEmpty()) { // Field row + str.append(QString(cols.value(0).size(), '=') + "\n"); + } + } + ++it; + } + + QApplication::clipboard()->setText(str); +} diff --git a/SearchAll.h b/SearchAll.h new file mode 100644 index 0000000..8523a99 --- /dev/null +++ b/SearchAll.h @@ -0,0 +1,40 @@ +#ifndef SEARCHALL_H +#define SEARCHALL_H + +#include + +class FieldArchive; + +class SearchAll : public QDialog +{ + Q_OBJECT +public: + explicit SearchAll(QWidget *parent); + void setScriptSearch(); + void setTextSearch(); + void setFieldArchive(FieldArchive *fieldArchive); + void clear(); +public slots: + void addResultOpcode(int mapID, int groupID, int methodID, int opcodeID); + void addResultText(int mapID, int textID, int index, int size); +private slots: + void gotoResult(QTreeWidgetItem *item); + void copySelected() const; +signals: + void foundText(int mapID, int textID, int index, int size); + void foundOpcode(int mapID, int groupID, int methodID, int opcodeID); +private: + enum Mode { + ScriptSearch, TextSearch + }; + void addResult(int fieldID, QTreeWidgetItem *item); + QTreeWidgetItem *createItemField(int mapID) const; + QTreeWidgetItem *createItemOpcode(int mapID, int grpScriptID, int scriptID, int opcodeID) const; + QTreeWidgetItem *createItemText(int mapID, int textID, int index, int size) const; + QTreeWidget *_resultList; + FieldArchive *_fieldArchive; + QMap itemByMapID; + Mode _searchMode; +}; + +#endif // SEARCHALL_H diff --git a/VarManager.cpp b/VarManager.cpp index 4efdfc6..547f82d 100644 --- a/VarManager.cpp +++ b/VarManager.cpp @@ -21,15 +21,12 @@ VarManager::VarManager(FieldArchive *fieldArchive, QWidget *parent) : QWidget(parent, Qt::Tool) { setWindowTitle(tr("Gestionnaire de variables")); - QFont font; - font.setPointSize(8); var = new QSpinBox(this); var->setRange(0, 1536);// Valid vars : 4 -> 1536 name = new QLineEdit(this); list = new QTreeWidget(this); - list->setFont(font); list->setHeaderLabels(QStringList() << tr("Var") << tr("Type") << tr("Nom") << tr("Écran") << tr("MEM fr") << tr("MEM us")); list->setAutoScroll(false); list->setIndentation(0); diff --git a/appveyor.yml b/appveyor.yml index 59f03ed..fff1408 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -1,16 +1,21 @@ +image: Visual Studio 2019 + environment: matrix: - USE_MSVC: 1 - QTDIR: C:\Qt\5.6\msvc2013 + QTDIR: C:\Qt\5.14\msvc2017 + QTTOOLS: C:\Qt\Tools\QtCreator - USE_MINGW: 1 - QTDIR: C:\Qt\5.6\mingw49_32 + QTDIR: C:\Qt\5.14\mingw73_32 + QTTOOLS: C:\Qt\Tools\mingw730_32 install: - - if "%USE_MINGW%"=="1" set PATH=%QTDIR%\bin;C:\Qt\Tools\mingw492_32\bin;%PATH% - - if "%USE_MSVC%"=="1" set PATH=%QTDIR%\bin;C:\Qt\Tools\QtCreator\bin;%PATH% - - '"C:\Program Files (x86)\Microsoft Visual Studio 12.0\VC\vcvarsall.bat"' - - curl -L -o upx.zip http://upx.sourceforge.net/download/upx391w.zip && - 7z e upx.zip *.exe -r + - DIR C:\Qt + - DIR C:\Qt\Tools + - DIR %QTDIR%\.. + - DIR %QTTOOLS% + - set PATH=%QTDIR%\bin;%QTTOOLS%\bin;%PATH% + - '"C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\VC\Auxiliary\Build\vcvars32.bat"' before_build: - qmake -config release Deling.pro diff --git a/deploy.bat b/deploy.bat index 606fca2..bf4f44c 100644 --- a/deploy.bat +++ b/deploy.bat @@ -13,7 +13,7 @@ rem Deploy DLLs %LIB_DIR%\windeployqt.exe --force --release --dir %OUTPUT_DIR% --no-quick-import --no-translations --no-webkit2 --no-angle --no-svg --no-webkit %EXE_PATH% rem Removing unused DLLs -del /q %OUTPUT_DIR%\opengl32sw.dll +if exist %OUTPUT_DIR%\opengl32sw.dll del /q %OUTPUT_DIR%\opengl32sw.dll rem Deploy Translations for %%l in (%LANGUAGES%) do ( @@ -25,6 +25,3 @@ xcopy /y %LIB_DIR%\liblz4.dll %OUTPUT_DIR% rem Deploy Exe xcopy /y %EXE_PATH% %OUTPUT_DIR% - -rem Compress Exe and DLLs. Note: DLLs in platforms/ directory should not be compressed. -rem upx %OUTPUT_DIR%\*.dll %OUTPUT_DIR%\Deling.exe diff --git a/files/BackgroundFile.cpp b/files/BackgroundFile.cpp index 5626a63..bc60b54 100644 --- a/files/BackgroundFile.cpp +++ b/files/BackgroundFile.cpp @@ -18,25 +18,29 @@ #include "files/BackgroundFile.h" QByteArray BackgroundFile::mim = QByteArray(); -QByteArray BackgroundFile::map = QByteArray(); -Tile Tile::fromTile1(const Tile1 &tileType1) +Tile Tile::fromTile1(const Tile1 &tileType1, int sizeOfTile) { Tile tile; tile.X = tileType1.X; tile.Y = tileType1.Y; tile.Z = tileType1.Z; tile.texID = tileType1.texID & 0xF; - tile.blend1 = (tileType1.texID >> 4) & 1; - tile.blend2 = tileType1.texID >> 5; - tile.ZZ1 = tileType1.ZZ1; + tile.draw = (tileType1.texID >> 4) & 1; + tile.blend = (tileType1.texID >> 5) & 3; + tile.depth = (tileType1.texID >> 7) & 3; tile.palID = (tileType1.palID >> 6) & 0xF; tile.srcX = tileType1.srcX; tile.srcY = tileType1.srcY; tile.layerID = 0; - tile.blendType = 4; - tile.parameter = tileType1.parameter; - tile.state = tileType1.state; + tile.blendType = tile.blend & 1 ? 1 : 4; + if (sizeOfTile < 16) { + tile.parameter = 255; + tile.state = 0; + } else { + tile.parameter = tileType1.parameter; + tile.state = tileType1.state; + } return tile; } @@ -47,98 +51,195 @@ Tile Tile::fromTile2(const Tile2 &tileType2) tile.Y = tileType2.Y; tile.Z = tileType2.Z; tile.texID = tileType2.texID & 0xF; - tile.blend1 = (tileType2.texID >> 4) & 1; - tile.blend2 = tileType2.texID >> 5; - tile.ZZ1 = tileType2.ZZ1; + tile.draw = (tileType2.texID >> 4) & 1; + tile.blend = (tileType2.texID >> 5) & 3; + tile.depth = (tileType2.texID >> 7) & 3; tile.palID = (tileType2.palID >> 6) & 0xF; + + if (tile.X != 0x7fff && (tileType2.palID & 0x3F || tileType2.palID >> 10 != 0xF)) { + qDebug() << "Tile::fromTile2 strange palID" << tileType2.palID; + } + if (tile.texID >> 9) { + qDebug() << "Tile::fromTile2 strange texID" << tileType2.texID; + } + tile.srcX = tileType2.srcX; tile.srcY = tileType2.srcY; - tile.layerID = tileType2.layerID & 0x7F; + tile.layerID = tileType2.layerID; tile.blendType = tileType2.blendType; tile.parameter = tileType2.parameter; tile.state = tileType2.state; return tile; } +Tile1 Tile::toTile1(const Tile &tile) +{ + Tile1 tileType1; + tileType1.X = tile.X; + tileType1.Y = tile.Y; + tileType1.Z = tile.Z; + quint8 blend = tile.blend & 3; + if (tile.blendType != 4) { + blend |= 1; + } + tileType1.texID = quint16(tile.depth << 7) + | quint16(tile.blend << 5) + | quint16(tile.draw << 4) + | quint16(tile.texID); + tileType1.palID = 0x3C00 | quint16((tile.palID & 0xF) << 6); + tileType1.srcX = tile.srcX; + tileType1.srcY = tile.srcY; + tileType1.parameter = tile.parameter; + tileType1.state = tile.state; + return tileType1; +} + +Tile2 Tile::toTile2(const Tile &tile) +{ + Tile2 tileType2; + tileType2.X = tile.X; + tileType2.Y = tile.Y; + tileType2.Z = tile.Z; + tileType2.texID = quint16(tile.depth << 7) + | quint16(tile.blend << 5) + | quint16(tile.draw << 4) + | quint16(tile.texID); + tileType2.palID = 0x3C00 | quint16((tile.palID & 0xF) << 6); + tileType2.srcX = quint8(tile.srcX); + tileType2.srcY = quint8(tile.srcY); + tileType2.layerID = tile.layerID; + tileType2.blendType = tile.blendType; + tileType2.parameter = tile.parameter; + tileType2.state = tile.state; + return tileType2; +} + BackgroundFile::BackgroundFile() : - File(), opened(false) + File(), opened(false), _mapType(TypeNew), _visibleTile(-1) { } bool BackgroundFile::open(const QByteArray &map, const QByteArray &mim, const QMultiMap *defaultParams) { - int mimSize = mim.size(), mapSize = map.size(), tilePos=0; - const char *constMapData = map.constData(); + this->mim = mim; if(!opened) { allparams.clear(); params.clear(); layers.clear(); + int mimSize = mim.size(); + MapType mapType; if(mimSize == 401408) { -// Tile1 tile1; -// int sizeOfTile = mapSize-map.lastIndexOf("\xff\x7f"); - -// if(mapSize%sizeOfTile != 0) return; - -// while(tilePos+15 < mapSize) -// { -// memcpy(&tile1, &constMapData[tilePos], sizeOfTile); -// if(tile1.X == 0x7fff) break; -// tilePos += sizeOfTile; - -// if(tile1.parameter!=255 && !allparams.contains(tile1.parameter, tile1.state)) -// { -// allparams.insert(tile1.parameter, tile1.state); -// if(tile1.state==0) -// params.insert(tile1.parameter, tile1.state); -// } -// } + mapType = TypeOld; } else if(mimSize == 438272) { - Tile2 tile2; - - while(tilePos+15 < mapSize) { - memcpy(&tile2, constMapData + tilePos, 16); - if(tile2.X == 0x7fff) { - break; - } - - if(tile2.parameter != 255 && - !allparams.contains(tile2.parameter, tile2.state)) { - allparams.insert(tile2.parameter, tile2.state); - // enable parameter only when state = 0 - if(!defaultParams && tile2.state == 0) { - params.insert(tile2.parameter, tile2.state); - } - } - layers.insert(tile2.layerID, true); - tilePos += 16; - } - if(defaultParams) { - params = *defaultParams; + mapType = TypeNew; + } + + _tiles = parseTiles(map, mapType); + _mapType = mapType; + + openParameters(); + + if(defaultParams) { + params = *defaultParams; + } else { + // Enable parameter only when state = 0 + foreach(quint8 param, allparams.keys(0)) { + params.insert(param, 0); } } } - this->map = map; - this->mim = mim; - opened = true; return true; } -QImage BackgroundFile::background(bool hideBG) const +bool BackgroundFile::save(QByteArray &map) { - int mimSize = mim.size(); + Tile1 tile1; + Tile2 tile2; + const size_t size = _mapType == TypeOldShort ? 14 : 16; + + map = QByteArray(int(size) * (_tiles.size() + 1), '\0'); + char *data = map.data(); + + foreach(const Tile &tile, _tiles) { + if (_mapType == TypeNew) { + tile2 = Tile::toTile2(tile); + memcpy(data, &tile2, 16); + } else { + tile1 = Tile::toTile1(tile); + memcpy(data, &tile1, size); + } + data += size; + } + + tile2 = Tile2(); + tile2.X = 0x7fff; + memcpy(data, &tile2, size); + + return true; +} - if(mimSize == 401408) - return type1(); - else if(mimSize == 438272) - return type2(hideBG); - else +bool BackgroundFile::openParameters() +{ + int i = 0; + + allparams.clear(); + layers.clear(); + _tilesZOrder.clear(); + _bounds = BackgroundBounds(); + + foreach(const Tile &tile, _tiles) { + if(tile.parameter != 255 && + !allparams.contains(tile.parameter, tile.state)) { + allparams.insert(tile.parameter, tile.state); + } + + if(qAbs(tile.X) < 1000 && qAbs(tile.Y) < 1000) { + if(tile.X >= 0 && tile.X > _bounds.right) + _bounds.right = tile.X; + else if(tile.X < 0 && -tile.X > _bounds.left) + _bounds.left = -tile.X; + if(tile.Y >= 0 && tile.Y > _bounds.bottom) + _bounds.bottom = tile.Y; + else if(tile.Y < 0 && -tile.Y > _bounds.top) + _bounds.top = -tile.Y; + } else { + qDebug() << "BackgroundFile::background out of bounds"; + } + + layers.insert(tile.layerID, true); + _tilesZOrder.insert(4096 - tile.Z, i); + + i += 1; + } + + return true; +} + +QImage BackgroundFile::background(bool hideBG) const +{ + int mimSize = mim.size(), palOffset = 4096, srcYWidth = 1664; + MapType mapType; + + if(mimSize == 401408) { + mapType = TypeOld; + palOffset = 0; + srcYWidth = 1536; + } else if(mimSize == 438272) { + mapType = TypeNew; + } else { + if (mimSize > 0) { + qDebug() << "Error BackgroundFile::background" << mimSize; + } return FF8Image::errorImage(); + } + + return toImage(palOffset, srcYWidth, _tilesZOrder, _bounds, hideBG); } QImage BackgroundFile::background(const QList &activeParams, bool hideBG) @@ -172,331 +273,257 @@ QImage BackgroundFile::background(const QList &activeParams, bool hideBG return image; } -QImage BackgroundFile::type1() const +QList BackgroundFile::parseTiles(const QByteArray &map, + MapType &type) const { - int mapSize = map.size(), tilePos=0; + int mapSize = map.size(), tilePos = 0, + sizeOfTile = mapSize - map.lastIndexOf("\xff\x7f"); Tile1 tileType1; + Tile2 tileType2; Tile tile; - QMultiMap tiles; - const char *constMimData = mim.constData(), *constMapData = map.constData(); + QList tiles; + const char *constMapData = map.constData(); + + type = sizeOfTile == 14 ? TypeOldShort : type; - int largeurMin=0, largeurMax=0, hauteurMin=0, hauteurMax=0, sizeOfTile = mapSize-map.lastIndexOf("\xff\x7f"); - if(mapSize%sizeOfTile != 0) return QImage(); + if(sizeOfTile != 14 && sizeOfTile != 16) { + qDebug() << "BackgroundFile::parseTiles: invalid map size" << mapSize << sizeOfTile; + return tiles; + } -// qDebug() << "Type 1"; + while(tilePos + sizeOfTile <= mapSize) { + if(type != TypeNew) { + memcpy(&tileType1, constMapData + tilePos, size_t(sizeOfTile)); + tile = Tile::fromTile1(tileType1, sizeOfTile); + } else { + memcpy(&tileType2, constMapData + tilePos, size_t(sizeOfTile)); + // HACK + if(tileType2.blendType >= 60) { + type = TypeOld; + return parseTiles(map, type); + } + tile = Tile::fromTile2(tileType2); + } - while(tilePos + 15 < mapSize) { - memcpy(&tileType1, constMapData + tilePos, sizeOfTile); - tile = Tile::fromTile1(tileType1); if(tile.X == 0x7fff) { -// qDebug() << "Fin des tiles" << tilePos << mapSize; break; } + + tiles.append(tile); tilePos += sizeOfTile; - if(qAbs(tile.X)<1000 && qAbs(tile.Y)<1000) { - if(tile.X >= 0 && tile.X > largeurMax) - largeurMax = tile.X; - else if(tile.X < 0 && -tile.X > largeurMin) - largeurMin = -tile.X; - if(tile.Y >= 0 && tile.Y > hauteurMax) - hauteurMax = tile.Y; - else if(tile.Y < 0 && -tile.Y > hauteurMin) - hauteurMin = -tile.Y; - -// if(tile.parameter==255 || params.isEmpty() || params.contains(tile.parameter, tile.state)) -// { - tiles.insert(4096-tile.Z, tile); -// } - } } - int width = largeurMin+largeurMax+16; - QImage image(width, hauteurMin+hauteurMax+16, QImage::Format_RGB32); + return tiles; +} + +void BackgroundFile::setTiles(const QList &tiles) +{ + _tiles = tiles; + setModified(true); + openParameters(); +} + +const Tile &BackgroundFile::tile(quint16 index) const +{ + return _tiles.at(index); +} + +void BackgroundFile::setTile(quint16 index, const Tile &tile) +{ + _tiles.replace(index, tile); + setModified(true); + openParameters(); +} + +void BackgroundFile::setVisibleTile(int index) +{ + _visibleTile = index; +} + +QImage BackgroundFile::toImage(int palOffset, int srcYWidth, + const QMultiMap &tiles, + const BackgroundBounds &bounds, bool hideBG) const +{ + int width = bounds.left + bounds.right + 16, + height = bounds.top + bounds.bottom + 16; + const char *constMimData = mim.constData(); + QImage image(width, height, QImage::Format_RGB32); QRgb *pixels = (QRgb *)image.bits(); image.fill(0xFF000000); - int baseX, pos, x, y, palStart; - qint16 color; - - foreach(const Tile &tile, tiles) { - pos = 8192 + tile.texID*128 + tile.srcX + 1536*tile.srcY; - x = 0; - y = (hauteurMin + tile.Y) * width; - baseX = largeurMin + tile.X; - palStart = tile.palID*512; - - for(int i=0 ; i<24576 ; ++i) { - memcpy(&color, &constMimData[palStart+((quint8)mim.at(pos+i))*2], 2); - if(color!=0) - pixels[baseX+x + y] = FF8Image::fromPsColor(color); - - if(x==15) { - x=0; - i += 1520; - y += width; - } - else - ++x; + foreach(int tileIndex, tiles) { + const Tile &tile = _tiles.at(tileIndex); + + if(((hideBG || tile.parameter != 255) && + !params.contains(tile.parameter, tile.state)) || + (!layers.isEmpty() && !layers.value(tile.layerID))) { + continue; + } + + if (_visibleTile < 0 || _visibleTile != tileIndex) { + drawTile(tile, palOffset, srcYWidth, width, bounds, + constMimData, pixels); + } + } + + if (_visibleTile >= 0) { + const Tile &tile = _tiles.at(_visibleTile); + drawTile(tile, palOffset, srcYWidth, width, bounds, + constMimData, pixels); + + for(int i = 0 ; i < 16 ; ++i) { + pixels[bounds.left + tile.X + (bounds.top + tile.Y) * width + i] = qRgb(255, 0, 0); + pixels[bounds.left + tile.X + (bounds.top + tile.Y + 15) * width + i] = qRgb(255, 0, 0); + pixels[bounds.left + tile.X + (bounds.top + tile.Y + i) * width] = qRgb(255, 0, 0); + pixels[bounds.left + tile.X + 15 + (bounds.top + tile.Y + i) * width] = qRgb(255, 0, 0); } } return image; } -QImage BackgroundFile::type2(bool hideBG) const +void BackgroundFile::drawTile(const Tile &tile, int palOffset, int srcYWidth, + int imageWidth, const BackgroundBounds &bounds, + const char *constMimData, QRgb *pixels) { - int mapSize = map.size(), tilePos=0, - baseX, pos, x, y, palStart, largeurMin=0, - largeurMax=0, hauteurMin=0, hauteurMax=0; - Tile1 tileType1; - Tile2 tileType2; - Tile tile; - QMultiMap tiles; - qint16 color; - quint8 index, blendType; - const char *constMimData = mim.constData(), - *constMapData = map.constData(); -// QFile debug("C:/Users/vista/Documents/Deling/data/debug.txt"); -// debug.open(QIODevice::WriteOnly); - -// QImage imageDebugPal(QSize(64, 1024), QImage::Format_RGB32); -// QRgb couleur; -// x=y=0; -// for(int i=2048 ; i<6144 ; ++i) { -// memcpy(&color, &constMimData[i*2], 2); -// couleur = FF8Image::fromPsColor(color); - -// imageDebugPal.setPixel(QPoint(x*4,y*4), couleur); -// imageDebugPal.setPixel(QPoint(x*4,y*4+1), couleur); -// imageDebugPal.setPixel(QPoint(x*4,y*4+2), couleur); -// imageDebugPal.setPixel(QPoint(x*4,y*4+3), couleur); - -// imageDebugPal.setPixel(QPoint(x*4+1,y*4), couleur); -// imageDebugPal.setPixel(QPoint(x*4+1,y*4+1), couleur); -// imageDebugPal.setPixel(QPoint(x*4+1,y*4+2), couleur); -// imageDebugPal.setPixel(QPoint(x*4+1,y*4+3), couleur); - -// imageDebugPal.setPixel(QPoint(x*4+2,y*4), couleur); -// imageDebugPal.setPixel(QPoint(x*4+2,y*4+1), couleur); -// imageDebugPal.setPixel(QPoint(x*4+2,y*4+2), couleur); -// imageDebugPal.setPixel(QPoint(x*4+2,y*4+3), couleur); - -// imageDebugPal.setPixel(QPoint(x*4+3,y*4), couleur); -// imageDebugPal.setPixel(QPoint(x*4+3,y*4+1), couleur); -// imageDebugPal.setPixel(QPoint(x*4+3,y*4+2), couleur); -// imageDebugPal.setPixel(QPoint(x*4+3,y*4+3), couleur); - -// ++x; -// if(x==16){ -// x=0; -// ++y; -// } -// } -// imageDebugPal.save("C:/Users/vista/Documents/Deling/data/palette.png"); - -// QImage imageDebugTex(QSize(1664/**2*/, 256), QImage::Format_RGB32); -// for(int pal=0 ; pal<16 ; ++pal) { -// int palStart2 = 4096+pal*512; -// x=y=0; -// for(int i=12288 ; i<438272 ; ++i) { -// memcpy(&color, &constMimData[palStart2+((quint8)mim.at(i)/*&0xF*/)*2], 2); -// imageDebugTex.setPixel(QPoint(x,y), FF8Image::fromPsColor(color)); -// ++x; -//// memcpy(&color, &constMimData[palStart2+((quint8)mim.at(i)>>4)*2], 2); -//// imageDebugTex.setPixel(QPoint(x,y), FF8Image::fromPsColor(color)); -//// ++x; -// if(x==1664/**2*/){ -// x=0; -// ++y; -// } -// } -// imageDebugTex.save(QString("C:/Users/vista/Documents/Deling/data/texture%1.png").arg(pal)); -// } - -// qDebug() << "Type 2" << mapSize; - - while(tilePos + 15 < mapSize) { - memcpy(&tileType2, constMapData + tilePos, 16); - tile = Tile::fromTile2(tileType2); - if(tile.X == 0x7fff) { -// qDebug() << "Fin des tiles" << tilePos << mapSize; - break; - } - - if(qAbs(tile.X)<1000 && qAbs(tile.Y)<1000) { - if(tile.X >= 0 && tile.X > largeurMax) - largeurMax = tile.X; - else if(tile.X < 0 && -tile.X > largeurMin) - largeurMin = -tile.X; - if(tile.Y >= 0 && tile.Y > hauteurMax) - hauteurMax = tile.Y; - else if(tile.Y < 0 && -tile.Y > hauteurMin) - hauteurMin = -tile.Y; - - if(((!hideBG && tile.parameter == 255) || - params.contains(tile.parameter, tile.state)) - && layers.value(tile.layerID)) { - // HACK - if(tile.blendType >= 60) { - memcpy(&tileType1, constMapData + tilePos, 16); - tile = Tile::fromTile1(tileType1); - } - -// debug.write( -// QString(QString("========== TILE %1 ==========\n").arg(tiles.size(),3) -// +QString("X=%1 | Y=%2 | Z=%3\n").arg(tile.X,4).arg(tile.Y,4).arg(tile.Z,4) -// +QString("texID=%1 | deph=%2 | palID=%3\n").arg(tile.texID,2).arg(tile.blend2,2).arg(tile.palID,2) -// +QString("srcX=%1 | srcY=%2 | layerID=%3\n").arg(tile.srcX,3).arg(tile.srcY,3).arg(tile.layerID,3) -// +QString("blendType=%1 | parameter=%2 | state=%3\n").arg(tile.blendType,3).arg(tile.parameter,3).arg(tile.state,3) -// ).toLatin1()); - - tiles.insert(4096 - tile.Z, tile); + quint16 color; + const int baseX = bounds.left + tile.X, + palStart = palOffset + tile.palID * 512; + int pos = palOffset + 8192 + tile.texID * 128 + tile.srcY * srcYWidth, + x = 0, y = (bounds.top + tile.Y) * imageWidth; + + if(tile.depth == 2) { + pos += tile.srcX * 2; + srcYWidth /= 2; + + for(int i=0 ; i= 4) { - pos = 12288 + tile.texID*128 + tile.srcX + 1664*tile.srcY; - for(int i=0 ; i<26624 ; ++i) { - memcpy(&color, &constMimData[palStart+((quint8)mim.at(pos+i))*2], 2); - if(color!=0) { - if(blendType<4) - pixels[baseX + x + y] = BGcolor(color, blendType, pixels[baseX + x + y]); - else - pixels[baseX + x + y] = BGcolor(color); - -// if(tile.blend2 == 4) { -// pixels[baseX + x + y] = qRgb(255,0,0); -// } -// if(tile.blend2 == 5) { -// pixels[baseX + x + y] = qRgb(0,255,0); -// } -// if(tile.blend2 == 6) { -// pixels[baseX + x + y] = qRgb(0,0,255); -// } -// if(tile.blend2 == 7) { -// pixels[baseX + x + y] = qRgb(255,0,255); -// } - } - - if(x==15) { - x=0; - i += 1648;//1664 - 16 - y += width; - } - else - ++x; + } else if(tile.depth == 1) { + pos += tile.srcX; + + for(int i=0 ; i> 4) * 2, 2); + BGcolor(color, tile.blendType, pixels, baseX + x + y, !tile.draw); + + if(x == 15) { + x = 0; + i += srcYWidth - 8; + y += imageWidth; + } else { ++x; - - memcpy(&color, &constMimData[palStart+(index>>4)*2], 2); - if(color!=0) { - if(blendType<4) - pixels[baseX + x + y] = BGcolor(color, blendType, pixels[baseX + x + y]); - else - pixels[baseX + x + y] = BGcolor(color); - -// if(tile.blend2 == 0) { -// pixels[baseX + x + y] = qRgb(255,255,0); -// } -// if(tile.blend2 == 1) { -// pixels[baseX + x + y] = qRgb(0,255,255); -// } -// if(tile.blend2 == 2) { -// pixels[baseX + x + y] = qRgb(255,255,255); -// } -// if(tile.blend2 == 3) { -// pixels[baseX + x + y] = qRgb(127,127,127); -// } - } - - if(x==15) { - x=0; - i += 1656;//1664 - 16/2 - y += width; - } - else - ++x; } } } +} - return image; +void BackgroundFile::BGcolor(quint16 value, quint8 blendType, QRgb *pixels, + int index, bool forceBlack) +{ + if(!forceBlack && value == 0) { + return; + } + + QRgb color; + + if(forceBlack) { + color = 0; + } else { + color = FF8Image::fromPsColor(value); + } + + if(blendType == 4) { + pixels[index] = color; + } else { + QRgb color0 = pixels[index]; + int r = qRed(color), g = 0, b = 0; + + switch(blendType) { + case 0: + r = (qRed(color0) + r) / 2; + g = (qGreen(color0) + r) / 2; + b = (qBlue(color0) + r) / 2; + break; + case 1: + r = qRed(color0) + r; + if(r > 255) r = 255; + g = qGreen(color0) + qGreen(color); + if(g > 255) g = 255; + b = qBlue(color0) + qBlue(color); + if(b > 255) b = 255; + break; + case 2: + r = qRed(color0) - r; + if(r < 0) r = 0; + g = qGreen(color0) - qGreen(color); + if(g < 0) g = 0; + b = qBlue(color0) - qBlue(color); + if(b < 0) b = 0; + break; + case 3: + r = qRed(color0) + int(.25 * r); + if(r > 255) r = 255; + g = qGreen(color0) + int(.25 * qGreen(color)); + if(g > 255) g = 255; + b = qBlue(color0) + int(.25 * qBlue(color)); + if(b > 255) b = 255; + break; + } + + pixels[index] = qRgb(r, g, b); + } } -QRgb BackgroundFile::BGcolor(int value, quint8 blendType, QRgb color0) +QImage BackgroundFile::mimToImage(MapDepth depth) { - QRgb color = FF8Image::fromPsColor(value); - int r = qRed(color), g = qGreen(color), b = qBlue(color); - - switch(blendType) { - case 0: - r = (qRed(color0) + r)/2; - g = (qGreen(color0) + r)/2; - b = (qBlue(color0) + r)/2; - break; - case 1: - r = qRed(color0) + r; - if(r>255) r = 255; - g = qGreen(color0) + g; - if(g>255) g = 255; - b = qBlue(color0) + b; - if(b>255) b = 255; - break; - case 2: - r = qRed(color0) - r; - if(r<0) r = 0; - g = qGreen(color0) - g; - if(g<0) g = 0; - b = qBlue(color0) - b; - if(b<0) b = 0; - break; - case 3: - r = qRed(color0) + 0.25*r; - if(r>255) r = 255; - g = qGreen(color0) + 0.25*g; - if(g>255) g = 255; - b = qBlue(color0) + 0.25*b; - if(b>255) b = 255; - break; + if(depth == DepthColor) { + int width = 832; + int height = (mim.size() - 0x3000) / (2 * width); + const char *data = mim.constData(); + + if(mim.size() < 0x3000 + width * height * 2) { + return QImage(); + } + + QImage img(width, height, QImage::Format_RGB32); + QRgb *px = (QRgb *)img.bits(); + + for(int j=0 ; j *defaultParams = 0); + bool open(const QByteArray &map, const QByteArray &mim, const QMultiMap *defaultParams = nullptr); inline QString filterText() const { - return QString(); + return QObject::tr("Fichier map tiles écran PC (*.map)"); } + bool save(QByteArray &map); QImage background(bool hideBG=false) const; - QImage background(const QList &activeParams, bool hideBG=false); + QImage background(const QList &activeParams, bool hideBG = false); + static QImage mimToImage(MapDepth depth); + + inline const QList &tiles() const { + return _tiles; + } + void setTiles(const QList &tiles); + const Tile &tile(quint16 index) const; + void setTile(quint16 index, const Tile &tile); + + void setVisibleTile(int index); QMultiMap params; QMultiMap allparams; QMap layers; private: + enum MapType { + TypeOld, + TypeOldShort, + TypeNew + }; + using File::open; - QImage type1() const; - QImage type2(bool hideBG=false) const; - static QRgb BGcolor(int value, quint8 blendType=4, QRgb color0=0); - static QByteArray map, mim; + bool openParameters(); + QList parseTiles(const QByteArray &map, MapType &type) const; + QImage toImage(int palOffset, int srcYWidth, + const QMultiMap &tiles, + const BackgroundBounds &bounds, bool hideBG) const; + static void drawTile(const Tile &tile, int palOffset, int srcYWidth, + int imageWidth, const BackgroundBounds &bounds, + const char *constMimData, QRgb *pixels); + static void BGcolor(quint16 value, quint8 blendType, QRgb *pixels, + int index, bool forceBlack); + static QByteArray mim; bool opened; + MapType _mapType; + QList _tiles; + QMultiMap _tilesZOrder; + BackgroundBounds _bounds; + int _visibleTile; }; #endif // BACKGROUNDFILE_H diff --git a/files/CharaFile.cpp b/files/CharaFile.cpp index a262901..53c8690 100644 --- a/files/CharaFile.cpp +++ b/files/CharaFile.cpp @@ -153,9 +153,25 @@ bool CharaFile::open(const QByteArray &one, bool ps) qWarning() << "CharaFile::open Compression error" << i << lzsSize << data.size(); return false; } - data = LZS::decompressAll(data.constData() + 4, lzsSize); + data = LZS::decompress(data.constData() + 4, lzsSize); } - models.append(CharaModel(name, toc, data)); + + QList textures; + + // Toc = tim offsets + data offset + data size + + for(int i=0 ; i= scripts.nbGroup()) { + groupID = scripts.nbGroup() - 1; + } + + while (groupID >= 0) { + if (methodID >= scripts.nbScript(groupID)) { + methodID = scripts.nbScript(groupID) - 1; + } + + while (methodID >= 0) { + int nbOpcode; + const int pos = scripts.posScript(groupID, methodID, &nbOpcode); + + if (opcodeID >= nbOpcode) { + opcodeID = nbOpcode - 1; + } + + while (opcodeID >= 0) { + if (search(type, value, quint16(pos), opcodeID)) { + return true; + } + + --opcodeID; + } + --methodID; + opcodeID = 2147483647; + } + --groupID; + methodID = 2147483647; + } + + return false; +} + +bool JsmFile::search(SearchType type, const QList &values, int &groupID, int &methodID, int &opcodeID) const +{ + int groupListSize = scripts.nbGroup(), nbOpcode, methodCount; + + if(groupID < 0) groupID = 0; + if(methodID < 0) methodID = 0; + if(opcodeID < 0) opcodeID = 0; - if(groupID < 0 || groupID >= scripts.nbGroup()) + while(groupID < groupListSize) { + methodCount = scripts.nbScript(groupID); + + while(methodID < methodCount) { + const int pos = scripts.posScript(groupID, methodID, &nbOpcode); + + while(opcodeID < nbOpcode) { + for (quint64 value: values) { + if(search(type, value, quint16(pos), opcodeID)) { + return true; + } + } + + ++opcodeID; + } + ++methodID; + opcodeID = 0; + } + ++groupID; + methodID = 0; + } + + return false; +} + +bool JsmFile::searchReverse(SearchType type, const QList &values, int &groupID, int &methodID, int &opcodeID) const +{ + if (groupID >= scripts.nbGroup()) { groupID = scripts.nbGroup() - 1; + } - while(groupID >= 0) { - if(methodID < 0 || methodID >= scripts.nbScript(groupID)) + while (groupID >= 0) { + if (methodID >= scripts.nbScript(groupID)) { methodID = scripts.nbScript(groupID) - 1; + } - while(methodID >= 0) { - pos = scripts.posScript(groupID, methodID, &nbOpcode); - if(opcodeID < 0 || opcodeID >= nbOpcode) + while (methodID >= 0) { + int nbOpcode; + const int pos = scripts.posScript(groupID, methodID, &nbOpcode); + + if (opcodeID >= nbOpcode) { opcodeID = nbOpcode - 1; + } - while(opcodeID >= 0) { - if(search(type, value, pos, opcodeID)) return true; + while (opcodeID >= 0) { + for (quint64 value: values) { + if (search(type, value, quint16(pos), opcodeID)) { + return true; + } + } --opcodeID; } --methodID; + opcodeID = 2147483647; } --groupID; + methodID = 2147483647; } return false; diff --git a/files/JsmFile.h b/files/JsmFile.h index fe15a76..c962a31 100644 --- a/files/JsmFile.h +++ b/files/JsmFile.h @@ -86,7 +86,9 @@ class JsmFile : public File const JsmScripts &getScripts() const; bool search(SearchType type, quint64 value, int &groupID, int &methodID, int &opcodeID) const; + bool search(SearchType type, const QList &values, int &groupID, int &methodID, int &opcodeID) const; bool searchReverse(SearchType type, quint64 value, int &groupID, int &methodID, int &opcodeID) const; + bool searchReverse(SearchType type, const QList &values, int &groupID, int &methodID, int &opcodeID) const; QList searchAllVars() const; QList searchAllSpells(const QString &fieldName) const; QList searchAllCards(const QString &fieldName) const; diff --git a/files/MchFile.cpp b/files/MchFile.cpp index 4be23c1..770ffb5 100644 --- a/files/MchFile.cpp +++ b/files/MchFile.cpp @@ -58,7 +58,20 @@ bool MchFile::open(const QByteArray &mch, const QString &name) toc.append(modelOffset); toc.append(mch.size()); - _model = new CharaModel(name, toc, mch); + QList textures; + + // Toc = tim offsets + data offset + data size + + for(int i=0 ; i= nbText()) - return false; - if((from = txt.indexIn(text(textID), from)) != -1) { - size = txt.matchedLength(); - return true; + if (textID >= nbText()) { + textID = nbText() - 1; + from = 2147483647; } - return searchText(txt, ++textID, from = 0, size); -} - -bool MsdFile::searchTextReverse(const QRegExp &txt, int &textID, int &from, int &index, int &size) const -{ - if(textID >= nbText()) { - textID = nbText()-1; - from = -1; - } - if(textID < 0) - return false; - FF8Text t = text(textID); - if((index = txt.lastIndexIn(t, from)) != -1) { - from = index - t.size(); - size = txt.matchedLength(); - return true; + for (; textID >= 0; --textID) { + const FF8Text t = text(textID); + int offset = from - t.size(); + if (offset >= 0) { + offset = -1; + } + from = txt.lastIndexIn(t, offset); + if (from != -1) { + size = txt.matchedLength(); + return true; + } + from = 2147483647; } - return searchTextReverse(txt, --textID, from = -1, index, size); + return false; } bool MsdFile::isJp() const diff --git a/files/MsdFile.h b/files/MsdFile.h index 0cd7e47..b7a48f9 100644 --- a/files/MsdFile.h +++ b/files/MsdFile.h @@ -36,11 +36,11 @@ class MsdFile : public File void setText(int, const FF8Text &); void insertText(int); void removeText(int); - int nbText() const; - bool hasText(const QRegExp &txt) const; - int indexOfText(const QRegExp &txt, int from = 0) const; + inline int nbText() const { + return texts.size(); + } bool searchText(const QRegExp &txt, int &textID, int &from, int &size) const; - bool searchTextReverse(const QRegExp &txt, int &textID, int &from, int &index, int &size) const; + bool searchTextReverse(const QRegExp &txt, int &textID, int &from, int &size) const; bool isJp() const; private: QList texts; diff --git a/files/PmpFile.cpp b/files/PmpFile.cpp index 15118c9..cb092d4 100644 --- a/files/PmpFile.cpp +++ b/files/PmpFile.cpp @@ -35,7 +35,7 @@ bool PmpFile::open(const QByteArray &pmp) // qDebug() << lzsSize << pmp.left(24).toHex() << pmp.size(); // int dec = 0; -// this->pmp = LZS::decompressAll(&(pmp.constData()[dec]), pmp.size()-dec); +// this->pmp = LZS::decompress(&(pmp.constData()[dec]), pmp.size()-dec); // if(pmp.size() != 4) { // palette().save(QString("pmp/%1-palette.png").arg(currentFieldName)); // for(int palID=0 ; palID<16 ; ++palID) { diff --git a/files/TexFile.cpp b/files/TexFile.cpp index 11a5378..7a45385 100644 --- a/files/TexFile.cpp +++ b/files/TexFile.cpp @@ -18,35 +18,35 @@ #include "TexFile.h" TexFile::TexFile(const QByteArray &data) : - TextureFile() + TextureFile() { - open(data); + open(data); } TexFile::TexFile(const TextureFile &textureFile, const TexStruct &header, - const QVector &colorKeyArray) : - TextureFile(textureFile), header(header), colorKeyArray(colorKeyArray) + const QVector &colorKeyArray) : + TextureFile(textureFile), header(header), colorKeyArray(colorKeyArray) { } bool TexFile::open(const QByteArray &data) { - const char *constData = data.constData(); + const char *constData = data.constData(); quint32 w, h, headerSize, paletteSectionSize, imageSectionSize, colorKeySectionSize; - if((quint32)data.size() < sizeof(TexStruct)) { - qWarning() << "tex size too short!"; + if (quint32(data.size()) < sizeof(TexStruct)) { + qWarning() << "tex size too short!" << data.size(); return false; } memcpy(&header, constData, sizeof(TexStruct)); - if(header.version == 1) { + if (header.version == 1) { headerSize = sizeof(TexStruct) - 4; - } else if(header.version == 2) { + } else if (header.version == 2) { headerSize = sizeof(TexStruct); } else { - qWarning() << "unknown tex version!"; + qWarning() << "unknown tex version!" << header.version; return false; } @@ -56,28 +56,24 @@ bool TexFile::open(const QByteArray &data) imageSectionSize = w * h * header.bytesPerPixel; colorKeySectionSize = header.hasColorKeyArray ? header.nbPalettes : 0; - if((quint32)data.size() != headerSize + paletteSectionSize + imageSectionSize + colorKeySectionSize) { - qWarning() << "tex invalid size!"; + if ((quint32)data.size() != headerSize + paletteSectionSize + imageSectionSize + colorKeySectionSize) { + qWarning() << "tex invalid size!" << data.size() << headerSize << paletteSectionSize << imageSectionSize + << colorKeySectionSize; return false; } - //debug(); - quint32 i; - if(header.nbPalettes > 0) - { + if (header.nbPalettes > 0) { quint32 index, imageStart = headerSize + paletteSectionSize; - _colorTables.clear(); - for(quint32 palID=0 ; palID < header.nbPalettes ; ++palID) { + for (quint32 palID=0; palID < header.nbPalettes; ++palID) { quint32 paletteStart = headerSize+header.nbColorsPerPalette1*4*palID; _image = QImage(w, h, QImage::Format_Indexed8); QVector colors; - for(i=0 ; i 4) { + qWarning() << "tex invalid bytesPerPixel!" << header.bytesPerPixel; + return false; + } + + for (i=0; i. ****************************************************************************/ #define PROG_NAME "Deling" -#define PROG_VERSION "0.10.1b" +#define PROG_VERSION "0.11.0b" #define PROG_FULLNAME QString("%1 %2").arg(PROG_NAME, PROG_VERSION) -#define RC_PRODUCT_VERSION 0,10,1,0 +#define RC_PRODUCT_VERSION 0,11,0,0 #define RC_FILE_DESCRIPTION_STR "Deling FF8 Field Editor" #define RC_COMPANY_NAME_STR "myst6re" diff --git a/widgets/AboutDialog.cpp b/widgets/AboutDialog.cpp new file mode 100644 index 0000000..dc2ebf1 --- /dev/null +++ b/widgets/AboutDialog.cpp @@ -0,0 +1,41 @@ +#include "AboutDialog.h" +#include "../parameters.h" +#include "../Data.h" + +AboutDialog::AboutDialog(QWidget *parent) + : QDialog(parent, Qt::Dialog | Qt::CustomizeWindowHint) +{ + QLabel *image = new QLabel(this); + image->setPixmap(QPixmap(":/images/deling_city.png")); + + QLabel *desc1 = new QLabel(QString("

%1

").arg(PROG_FULLNAME) % tr("Par Jérôme <myst6re> Arzel
" + "github.com/myst6re/deling"), this); + desc1->setTextInteractionFlags(Qt::LinksAccessibleByMouse | Qt::LinksAccessibleByKeyboard); + desc1->setTextFormat(Qt::RichText); + desc1->setOpenExternalLinks(true); + + QLabel *desc2 = new QLabel(tr("Merci à :
  • Aali
  • " + "
  • Aladore384
  • " + "
  • Asa
  • " + "
  • Maki
  • " + "
  • kruci
"), this); + desc2->setTextInteractionFlags(Qt::LinksAccessibleByMouse | Qt::LinksAccessibleByKeyboard); + desc2->setTextFormat(Qt::RichText); + desc2->setOpenExternalLinks(true); + + QDialogButtonBox *buttonBox = new QDialogButtonBox(Qt::Horizontal, this); + buttonBox->addButton(QDialogButtonBox::Close); + connect(buttonBox, SIGNAL(rejected()), this, SLOT(close())); + + QLabel *desc3 = new QLabel(QString("Qt %1").arg(QT_VERSION_STR), this); + QPalette pal = desc3->palette(); + pal.setColor(QPalette::WindowText, QColor(0xAA, 0xAA, 0xAA)); + desc3->setPalette(pal); + + QGridLayout *layout = new QGridLayout(this); + layout->addWidget(desc1, 0, 0, 1, 2); + layout->addWidget(desc2, 1, 0, 1, 2); + layout->addWidget(image, 2, 0, 1, 2); + layout->addWidget(desc3, 3, 0); + layout->addWidget(buttonBox, 3, 1); +} diff --git a/widgets/AboutDialog.h b/widgets/AboutDialog.h new file mode 100644 index 0000000..ec1150c --- /dev/null +++ b/widgets/AboutDialog.h @@ -0,0 +1,16 @@ +#ifndef ABOUTDIALOG_H +#define ABOUTDIALOG_H + +#include + +class AboutDialog : public QDialog +{ + Q_OBJECT +public: + explicit AboutDialog(QWidget *parent = nullptr); + +signals: + +}; + +#endif // ABOUTDIALOG_H diff --git a/widgets/BackgroundWidget.cpp b/widgets/BackgroundWidget.cpp index de65550..9d0f7cb 100644 --- a/widgets/BackgroundWidget.cpp +++ b/widgets/BackgroundWidget.cpp @@ -39,23 +39,89 @@ void BackgroundWidget::build() image->setAlignment(Qt::AlignCenter); scrollArea->setWidget(image); - parametersWidget = new QComboBox(this); + QWidget *page1 = new QWidget(this); + + parametersWidget = new QComboBox(page1); parametersWidget->setMinimumWidth(150); parametersWidget->setEnabled(false); - statesWidget = new QListWidget(this); + statesWidget = new QListWidget(page1); statesWidget->setFixedWidth(150); - layersWidget = new QListWidget(this); + layersWidget = new QListWidget(page1); layersWidget->setFixedWidth(150); - hideBack = new QCheckBox(tr("Cacher background"), this); + hideBack = new QCheckBox(tr("Cacher background"), page1); hideBack->setChecked(false); + QVBoxLayout *layout1 = new QVBoxLayout(page1); + layout1->addWidget(parametersWidget); + layout1->addWidget(statesWidget); + layout1->addWidget(layersWidget); + layout1->addWidget(hideBack); + layout1->setContentsMargins(QMargins()); + + QWidget *page2 = new QWidget(this); + currentTile = new QSpinBox(page2); + currentTile->setMinimum(0); + tileX = new QSpinBox(page2); + tileX->setRange(-32768, 32767); + tileY = new QSpinBox(page2); + tileY->setRange(-32768, 32767); + tileZ = new QSpinBox(page2); + tileZ->setRange(0, 65535); + tileTexID = new QSpinBox(page2); + tileTexID->setRange(0, 16); + tilePalID = new QSpinBox(page2); + tilePalID->setRange(0, 16); + tileBlend = new QSpinBox(page2); + tileBlend->setRange(0, 3); + tileDraw = new QCheckBox(page2); + tileDepth = new QSpinBox(page2); + tileDepth->setRange(0, 3); + tileSrcX = new QSpinBox(page2); + tileSrcX->setRange(0, 255); + tileSrcY = new QSpinBox(page2); + tileSrcY->setRange(0, 255); + tileLayerID = new QSpinBox(page2); + tileLayerID->setRange(0, 255); + tileBlendType = new QSpinBox(page2); + tileBlendType->setRange(0, 255); + tileParameter = new QSpinBox(page2); + tileParameter->setRange(0, 255); + tileState = new QSpinBox(page2); + tileState->setRange(0, 255); + + QFormLayout *layout2 = new QFormLayout(page2); + layout2->addRow(tr("Tile ID"), currentTile); + layout2->addRow(new QFrame(page2)); + layout2->addRow(tr("Destination X"), tileX); + layout2->addRow(tr("Destination Y"), tileY); + layout2->addRow(tr("Destination Z"), tileZ); + layout2->addRow(tr("Source X"), tileSrcX); + layout2->addRow(tr("Source Y"), tileSrcY); + layout2->addRow(tr("Texture"), tileTexID); + layout2->addRow(tr("Palette"), tilePalID); + layout2->addRow(tr("Transparence"), tileBlend); + layout2->addRow(tr("Dessiner"), tileDraw); + layout2->addRow(tr("Type transparence"), tileBlendType); + layout2->addRow(tr("Type couleur"), tileDepth); + layout2->addRow(tr("Couche"), tileLayerID); + layout2->addRow(tr("Paramètre"), tileParameter); + layout2->addRow(tr("État"), tileState); + layout2->setContentsMargins(QMargins()); + + stackedLayout = new QStackedLayout; + stackedLayout->addWidget(page1); + stackedLayout->addWidget(page2); + + QTabBar *tabBar = new QTabBar(this); + tabBar->addTab(tr("Paramètres")); + tabBar->addTab(tr("Tuiles")); + QGridLayout *layout = new QGridLayout(this); - layout->addWidget(scrollArea, 0, 0, 4, 1); - layout->addWidget(parametersWidget, 0, 1); - layout->addWidget(statesWidget, 1, 1); - layout->addWidget(layersWidget, 2, 1); - layout->addWidget(hideBack, 3, 1); + layout->addWidget(scrollArea, 0, 0, 2, 1); + layout->addWidget(tabBar, 0, 1); + layout->addLayout(stackedLayout, 1, 1); + layout->setColumnStretch(0, 1); layout->setContentsMargins(QMargins()); connect(parametersWidget, SIGNAL(currentIndexChanged(int)), SLOT(parameterChanged(int))); @@ -64,6 +130,23 @@ void BackgroundWidget::build() // connect(statesWidget, SIGNAL(itemClicked(QListWidgetItem*)), SLOT(switchItem(QListWidgetItem*))); // connect(layersWidget, SIGNAL(itemClicked(QListWidgetItem*)), SLOT(switchItem(QListWidgetItem*))); connect(hideBack, SIGNAL(toggled(bool)), SLOT(setHideBack(bool))); + connect(tabBar, SIGNAL(currentChanged(int)), stackedLayout, SLOT(setCurrentIndex(int))); + connect(tabBar, SIGNAL(currentChanged(int)), this, SLOT(setPage(int))); + connect(currentTile, SIGNAL(valueChanged(int)), this, SLOT(setCurrentTile(int))); + connect(tileX, SIGNAL(editingFinished()), this, SLOT(setTileX())); + connect(tileY, SIGNAL(editingFinished()), this, SLOT(setTileY())); + connect(tileZ, SIGNAL(editingFinished()), this, SLOT(setTileZ())); + connect(tileSrcX, SIGNAL(editingFinished()), this, SLOT(setTileSrcX())); + connect(tileSrcY, SIGNAL(editingFinished()), this, SLOT(setTileSrcY())); + connect(tileTexID, SIGNAL(editingFinished()), this, SLOT(setTileTexID())); + connect(tilePalID, SIGNAL(editingFinished()), this, SLOT(setTilePalID())); + connect(tileBlend, SIGNAL(editingFinished()), this, SLOT(setTileBlend())); + connect(tileDraw, SIGNAL(released()), this, SLOT(setTileDraw())); + connect(tileBlendType, SIGNAL(editingFinished()), this, SLOT(setTileBlendType())); + connect(tileDepth, SIGNAL(editingFinished()), this, SLOT(setTileDepth())); + connect(tileLayerID, SIGNAL(editingFinished()), this, SLOT(setTileLayerID())); + connect(tileParameter, SIGNAL(editingFinished()), this, SLOT(setTileParameter())); + connect(tileState, SIGNAL(editingFinished()), this, SLOT(setTileState())); PageWidget::build(); } @@ -76,10 +159,48 @@ void BackgroundWidget::clear() statesWidget->clear(); layersWidget->clear(); image->clear(); + currentTile->clear(); + tileX->clear(); + tileY->clear(); + tileZ->clear(); + tileTexID->clear(); + tilePalID->clear(); + tileBlend->clear(); + tileDraw->setChecked(false); + tileDepth->clear(); + tileSrcX->clear(); + tileSrcY->clear(); + tileLayerID->clear(); + tileBlendType->clear(); + tileParameter->clear(); + tileState->clear(); PageWidget::clear(); } + +void BackgroundWidget::setReadOnly(bool ro) +{ + PageWidget::setReadOnly(ro); + + if(!isBuilded()) return; + + tileX->setReadOnly(ro); + tileY->setReadOnly(ro); + tileZ->setReadOnly(ro); + tileTexID->setReadOnly(ro); + tilePalID->setReadOnly(ro); + tileBlend->setReadOnly(ro); + tileDraw->setDisabled(ro); + tileDepth->setReadOnly(ro); + tileSrcX->setReadOnly(ro); + tileSrcY->setReadOnly(ro); + tileLayerID->setReadOnly(ro); + tileBlendType->setReadOnly(ro); + tileParameter->setReadOnly(ro); + tileState->setReadOnly(ro); +} + void BackgroundWidget::parameterChanged(int index) { if(!hasData() || !data()->hasBackgroundFile()) return; @@ -144,6 +265,332 @@ void BackgroundWidget::updateBackground() image->setPixmap(QPixmap::fromImage(data()->getBackgroundFile()->background(hideBack->isChecked()))); } +void BackgroundWidget::setPage(int index) +{ + setCurrentTile(index != 1 ? -1 : 0); +} + +void BackgroundWidget::setCurrentTile(int index) +{ + if(!hasData() || !data()->hasBackgroundFile()) { + return; + } + + data()->getBackgroundFile()->setVisibleTile(index); + if (index >= 0) { + Tile t = data()->getBackgroundFile()->tile(quint16(index)); + tileX->setValue(t.X); + tileY->setValue(t.Y); + tileZ->setValue(t.Z); + tileTexID->setValue(t.texID); + tileDraw->setChecked(t.draw); + tileBlend->setValue(t.blend); + tileDepth->setValue(t.depth); + tilePalID->setValue(t.palID); + tileSrcX->setValue(t.srcX); + tileSrcY->setValue(t.srcY); + tileLayerID->setValue(t.layerID); + tileBlendType->setValue(t.blendType); + tileParameter->setValue(t.parameter); + tileState->setValue(t.state); + } + updateBackground(); +} + +void BackgroundWidget::setTileX() +{ + if(!hasData() || !data()->hasBackgroundFile()) { + return; + } + + int index = currentTile->value(); + + if (index < 0) { + return; + } + + Tile t = data()->getBackgroundFile()->tile(quint16(index)); + if (t.X != quint8(tileX->value())) { + t.X = qint16(tileX->value()); + data()->getBackgroundFile()->setTile(quint16(index), t); + + emit modified(); + } +} + +void BackgroundWidget::setTileY() +{ + if(!hasData() || !data()->hasBackgroundFile()) { + return; + } + + int index = currentTile->value(); + + if (index < 0) { + return; + } + + Tile t = data()->getBackgroundFile()->tile(quint16(index)); + if (t.Y != quint8(tileY->value())) { + t.Y = qint16(tileY->value()); + data()->getBackgroundFile()->setTile(quint16(index), t); + + emit modified(); + } +} + +void BackgroundWidget::setTileZ() +{ + if(!hasData() || !data()->hasBackgroundFile()) { + return; + } + + int index = currentTile->value(); + + if (index < 0) { + return; + } + + Tile t = data()->getBackgroundFile()->tile(quint16(index)); + if (t.Z != quint8(tileZ->value())) { + t.Z = quint16(tileZ->value()); + data()->getBackgroundFile()->setTile(quint16(index), t); + + emit modified(); + } +} + +void BackgroundWidget::setTileTexID() +{ + if(!hasData() || !data()->hasBackgroundFile()) { + return; + } + + int index = currentTile->value(); + + if (index < 0) { + return; + } + + Tile t = data()->getBackgroundFile()->tile(quint16(index)); + if (t.texID != quint8(tileTexID->value())) { + t.texID = quint8(tileTexID->value()); + data()->getBackgroundFile()->setTile(quint16(index), t); + + emit modified(); + } +} + +void BackgroundWidget::setTileBlend() +{ + if(!hasData() || !data()->hasBackgroundFile()) { + return; + } + + int index = currentTile->value(); + + if (index < 0) { + return; + } + + Tile t = data()->getBackgroundFile()->tile(quint16(index)); + if (t.blend != quint8(tileBlend->value())) { + t.blend = quint8(tileBlend->value()); + data()->getBackgroundFile()->setTile(quint16(index), t); + + emit modified(); + } +} + +void BackgroundWidget::setTilePalID() +{ + if(!hasData() || !data()->hasBackgroundFile()) { + return; + } + + int index = currentTile->value(); + + if (index < 0) { + return; + } + + Tile t = data()->getBackgroundFile()->tile(quint16(index)); + if (t.palID != quint8(tilePalID->value())) { + t.palID = quint8(tilePalID->value()); + data()->getBackgroundFile()->setTile(quint16(index), t); + + emit modified(); + } +} + +void BackgroundWidget::setTileDraw() +{ + if(!hasData() || !data()->hasBackgroundFile()) { + return; + } + + int index = currentTile->value(); + + if (index < 0) { + return; + } + + Tile t = data()->getBackgroundFile()->tile(quint16(index)); + if (t.draw != tileDraw->isChecked()) { + t.draw = tileDraw->isChecked(); + data()->getBackgroundFile()->setTile(quint16(index), t); + + emit modified(); + } +} + +void BackgroundWidget::setTileDepth() +{ + if(!hasData() || !data()->hasBackgroundFile()) { + return; + } + + int index = currentTile->value(); + + if (index < 0) { + return; + } + + Tile t = data()->getBackgroundFile()->tile(quint16(index)); + if (t.depth != tileDepth->value()) { + t.depth = quint8(tileDepth->value()); + data()->getBackgroundFile()->setTile(quint16(index), t); + + emit modified(); + } +} + +void BackgroundWidget::setTileSrcX() +{ + if(!hasData() || !data()->hasBackgroundFile()) { + return; + } + + int index = currentTile->value(); + + if (index < 0) { + return; + } + + Tile t = data()->getBackgroundFile()->tile(quint16(index)); + if (t.srcX != quint16(tileSrcX->value())) { + t.srcX = quint16(tileSrcX->value()); + data()->getBackgroundFile()->setTile(quint16(index), t); + + emit modified(); + } +} + +void BackgroundWidget::setTileSrcY() +{ + if(!hasData() || !data()->hasBackgroundFile()) { + return; + } + + int index = currentTile->value(); + + if (index < 0) { + return; + } + + Tile t = data()->getBackgroundFile()->tile(quint16(index)); + if (t.srcY != quint16(tileSrcY->value())) { + t.srcY = quint16(tileSrcY->value()); + data()->getBackgroundFile()->setTile(quint16(index), t); + + emit modified(); + } +} + +void BackgroundWidget::setTileLayerID() +{ + if(!hasData() || !data()->hasBackgroundFile()) { + return; + } + + int index = currentTile->value(); + + if (index < 0) { + return; + } + + Tile t = data()->getBackgroundFile()->tile(quint16(index)); + if (t.layerID != quint8(tileLayerID->value())) { + t.layerID = quint8(tileLayerID->value()); + data()->getBackgroundFile()->setTile(quint16(index), t); + + emit modified(); + } +} + +void BackgroundWidget::setTileBlendType() +{ + if(!hasData() || !data()->hasBackgroundFile()) { + return; + } + + int index = currentTile->value(); + + if (index < 0) { + return; + } + + Tile t = data()->getBackgroundFile()->tile(quint16(index)); + if (t.blendType != quint8(tileBlendType->value())) { + t.blendType = quint8(tileBlendType->value()); + data()->getBackgroundFile()->setTile(quint16(index), t); + + emit modified(); + } +} + +void BackgroundWidget::setTileParameter() +{ + if(!hasData() || !data()->hasBackgroundFile()) { + return; + } + + int index = currentTile->value(); + + if (index < 0) { + return; + } + + Tile t = data()->getBackgroundFile()->tile(quint16(index)); + if (t.parameter != quint8(tileParameter->value())) { + t.parameter = quint8(tileParameter->value()); + data()->getBackgroundFile()->setTile(quint16(index), t); + + emit modified(); + } +} + +void BackgroundWidget::setTileState() +{ + if(!hasData() || !data()->hasBackgroundFile()) { + return; + } + + int index = currentTile->value(); + + if (index < 0) { + return; + } + + Tile t = data()->getBackgroundFile()->tile(quint16(index)); + if (t.state != quint8(tileState->value())) { + t.state = quint8(tileState->value()); + data()->getBackgroundFile()->setTile(quint16(index), t); + + emit modified(); + } +} + void BackgroundWidget::fill() { if(!isBuilded()) build(); @@ -154,12 +601,14 @@ void BackgroundWidget::fill() image->setName(data()->name()); updateBackground(); + BackgroundFile *bgFile = data()->getBackgroundFile(); + parametersWidget->clear(); - QList parameters = data()->getBackgroundFile()->allparams.uniqueKeys(); + QList parameters = bgFile->allparams.uniqueKeys(); foreach(quint8 parameter, parameters) parametersWidget->addItem(tr("Paramètre %1").arg(parameter), parameter); - QList layerIDs = data()->getBackgroundFile()->layers.keys(); + QList layerIDs = bgFile->layers.keys(); QListWidgetItem *item; layersWidget->clear(); @@ -168,7 +617,7 @@ void BackgroundWidget::fill() item = new QListWidgetItem(tr("Couche %1").arg(layerID)); item->setData(Qt::UserRole, layerID); item->setFlags(Qt::ItemIsUserCheckable | Qt::ItemIsEnabled); - item->setCheckState(data()->getBackgroundFile()->layers.value(layerID, false) ? Qt::Checked : Qt::Unchecked); + item->setCheckState(bgFile->layers.value(layerID, false) ? Qt::Checked : Qt::Unchecked); layersWidget->addItem(item); } @@ -178,5 +627,13 @@ void BackgroundWidget::fill() hideBack->setEnabled(parametersWidget->count()>0); hideBack->setChecked(false); + if (stackedLayout->currentIndex() == 1) { + currentTile->setValue(0); + setCurrentTile(0); + } else { + currentTile->clear(); + } + currentTile->setMaximum(bgFile->tiles().size() - 1); + PageWidget::fill(); } diff --git a/widgets/BackgroundWidget.h b/widgets/BackgroundWidget.h index 4da34ea..0befc41 100644 --- a/widgets/BackgroundWidget.h +++ b/widgets/BackgroundWidget.h @@ -30,6 +30,7 @@ class BackgroundWidget : public PageWidget public: BackgroundWidget(QWidget *parent=0); void clear(); + void setReadOnly(bool ro); void fill(); inline QString tabName() const { return tr("Décors"); } private slots: @@ -38,6 +39,22 @@ private slots: void enableLayer(QListWidgetItem *item); // void switchItem(QListWidgetItem *item); void setHideBack(bool); + void setPage(int index); + void setCurrentTile(int index); + void setTileX(); + void setTileY(); + void setTileZ(); + void setTileTexID(); + void setTileBlend(); + void setTilePalID(); + void setTileDraw(); + void setTileDepth(); + void setTileSrcX(); + void setTileSrcY(); + void setTileLayerID(); + void setTileBlendType(); + void setTileParameter(); + void setTileState(); private: void updateBackground(); void build(); @@ -46,6 +63,13 @@ private slots: QComboBox *parametersWidget; QListWidget *statesWidget, *layersWidget; QCheckBox *hideBack; + QSpinBox *currentTile, *tileDepth; + QSpinBox *tileX, *tileY, *tileZ; + QSpinBox *tileTexID, *tileBlend, *tilePalID; + QCheckBox *tileDraw; + QSpinBox *tileSrcX, *tileSrcY, *tileLayerID; + QSpinBox *tileBlendType, *tileParameter, *tileState; + QStackedLayout *stackedLayout; }; #endif // BACKGROUNDWIDGET_H diff --git a/widgets/CharaPreview.h b/widgets/CharaPreview.h index 102485f..9754db2 100644 --- a/widgets/CharaPreview.h +++ b/widgets/CharaPreview.h @@ -26,10 +26,17 @@ class CharaPreview : public BGPreview2 { Q_OBJECT public: - explicit CharaPreview(QWidget *parent = 0); + explicit CharaPreview(QWidget *parent = nullptr); void fill(const QPixmap &background); void setMainModels(QHash *mainModels); void setModel(const CharaModel &model); +protected: + inline bool hasHeightForWidth() const override { + return true; + } + inline int heightForWidth(int w) const override { + return w; + } private: QHash *mainModels; }; diff --git a/widgets/JsmWidget.cpp b/widgets/JsmWidget.cpp index 17745ed..6cd4a8d 100644 --- a/widgets/JsmWidget.cpp +++ b/widgets/JsmWidget.cpp @@ -33,9 +33,6 @@ void JsmWidget::build() { if(isBuilded()) return; - QFont font; - font.setPointSize(8); - warningWidget = new QLabel(tr("Attention : Les scripts de cet écran sont dans un ancien format mal reconnu par Deling. Ce que vous pourrez lire ici n'aura peut-être aucun sens."), this); warningWidget->hide(); warningWidget->setWordWrap(true); @@ -43,14 +40,16 @@ void JsmWidget::build() list1 = new QTreeWidget(this); list1->setHeaderLabels(QStringList() << tr("Id") << tr("Groupe") << tr("Exec")); - list1->setFixedWidth(180); + list1->setMaximumWidth( + list1->fontMetrics().boundingRect(QString(16, 'M')).width()); + list1->setMinimumWidth( + list1->fontMetrics().boundingRect(QString(8, 'M')).width()); list1->setAutoScroll(false); list1->setIndentation(0); - list1->setFont(font); list1->setUniformRowHeights(true); + list1->setAlternatingRowColors(true); modelPreview = new CharaPreview(this); - modelPreview->setFixedSize(list1->width(), list1->width()); modelPreview->setMainModels(mainModels); QVBoxLayout *list1Layout = new QVBoxLayout(); @@ -60,11 +59,14 @@ void JsmWidget::build() list2 = new QTreeWidget(this); list2->setHeaderLabels(QStringList() << tr("Id") << tr("Script") << tr("Script label")); - list2->setFixedWidth(180); + list2->setMaximumWidth( + list2->fontMetrics().boundingRect(QString(16, 'M')).width()); + list2->setMinimumWidth( + list2->fontMetrics().boundingRect(QString(8, 'M')).width()); list2->setAutoScroll(false); list2->setIndentation(0); - list2->setFont(font); list2->setUniformRowHeights(true); + list2->setAlternatingRowColors(true); tabBar = new QTabBar(this); tabBar->setDrawBase(false); @@ -75,7 +77,7 @@ void JsmWidget::build() textEdit = te->textEdit(); QFont font2 = textEdit->document()->defaultFont(); font2.setStyleHint(QFont::TypeWriter); - font2.setFamily("Courrier"); + font2.setFamily("Courier"); textEdit->document()->setDefaultFont(font2); highlighter = new JsmHighlighter(textEdit->document()); // continue highlight when window is inactive @@ -467,7 +469,7 @@ QList JsmWidget::nameList() const int nbGroup = data()->getJsmFile()->getScripts().nbGroup(); int directorCount=1, squallCount=1, zellCount=1, irvineCount=1, quistisCount=1; int rinoaCount=1, selphieCount=1, seiferCount=1, edeaCount=1, lagunaCount=1, kirosCount=1; - int wardCount=1, drawPointCount=1, eventLineCount=1, doorCount=1; + int wardCount=1, modelCount=1, drawPointCount=1, eventLineCount=1, doorCount=1, bgCount=1; for(int groupID=0 ; groupIDgetJsmFile()->getScripts().group(groupID); @@ -538,6 +540,8 @@ QList JsmWidget::nameList() const item->setIcon(0, QIcon(":/images/icon-ward.png")); break; case -1: + if(name.isEmpty()) name = QString("Model%1").arg(modelCount); + modelCount++; item->setIcon(0, QIcon(":/images/3d_model.png")); break; case DRAWPOINT_CHARACTER: @@ -561,6 +565,8 @@ QList JsmWidget::nameList() const item->setIcon(0, QIcon(":/images/door.png")); break; case JsmGroup::Background: + if(name.isEmpty()) name = QString("Background%1").arg(bgCount); + bgCount++; item->setIcon(0, QIcon(":/images/background.png")); break; default: diff --git a/widgets/MiscWidget.cpp b/widgets/MiscWidget.cpp index fdb78ba..ff7d715 100644 --- a/widgets/MiscWidget.cpp +++ b/widgets/MiscWidget.cpp @@ -119,10 +119,10 @@ void MiscWidget::fill() if(!hasData()) return; - if(data()->hasInfFile()) { - nameEdit->setText(data()->getInfFile()->getMapName()); + if(data()->hasInfFile()) { + nameEdit->setText(data()->getInfFile()->getMapName()); pvpEdit2->setValue(data()->getInfFile()->pvp()); - } + } nameEdit->setEnabled(data()->hasInfFile()); pvpEdit2->setEnabled(data()->hasInfFile()); @@ -133,10 +133,9 @@ void MiscWidget::fill() pal = pal.scaledToWidth(256); } pmpPaletteView->setPixmap(pal); - updatePmpView(); - } + } pmpEdit->setEnabled(data()->hasPmpFile()); - pmpGroup->setEnabled(data()->hasPmpFile()); + updatePmpView(); if(data()->hasPmdFile()) { pmdEdit->setText(data()->getPmdFile()->getPmdData().toHex()); diff --git a/widgets/TdwLetter.cpp b/widgets/TdwLetter.cpp index 540f13b..f8c14a2 100644 --- a/widgets/TdwLetter.cpp +++ b/widgets/TdwLetter.cpp @@ -122,6 +122,7 @@ void TdwLetter::mouseMoveEvent(QMouseEvent *e) if(linePos / PIXEL_SIZE != newLinePos && newLinePos < 16) { _tdwFile->setCharWidth(_currentTable, _letter, newLinePos); update(); + emit widthEdited(newLinePos); } } else if(startDrag2) { setPixel(getPixel(mousePos)); diff --git a/widgets/TdwLetter.h b/widgets/TdwLetter.h index 9e77129..407a473 100644 --- a/widgets/TdwLetter.h +++ b/widgets/TdwLetter.h @@ -38,6 +38,7 @@ public slots: void reset(); signals: void imageChanged(const QRect &rect); + void widthEdited(int width); protected: virtual QSize sizeHint() const; virtual QSize minimumSizeHint() const; diff --git a/widgets/TdwWidget2.cpp b/widgets/TdwWidget2.cpp index 5358897..7fee409 100644 --- a/widgets/TdwWidget2.cpp +++ b/widgets/TdwWidget2.cpp @@ -31,13 +31,16 @@ TdwWidget2::TdwWidget2(bool isAdditionnalTable, QWidget *parent) : selectTable = new QComboBox(this); - fromImage1 = new QPushButton(tr("À partir d'une image..."), this); + /* fromImage1 = new QPushButton(tr("À partir d'une image..."), this); fromImage1->setVisible(false);//TODO fromImage2 = new QPushButton(tr("À partir d'une image..."), this); - fromImage2->setVisible(false);//TODO + fromImage2->setVisible(false);//TODO */ // QPushButton *resetButton1 = new QPushButton(tr("Annuler les modifications"), this);//TODO textLetter = new QLineEdit(this); textLetter->setReadOnly(isAdditionnalTable); + widthLetter = new QSpinBox(this); + widthLetter->setRange(0, 15); + widthLetter->setReadOnly(isAdditionnalTable); exportButton = new QPushButton(tr("Exporter..."), this); importButton = new QPushButton(tr("Importer..."), this); resetButton2 = new QPushButton(tr("Annuler les modifications"), this); @@ -45,19 +48,21 @@ TdwWidget2::TdwWidget2(bool isAdditionnalTable, QWidget *parent) : QGridLayout *layout = new QGridLayout(this); layout->addWidget(tdwGrid, 0, 0, 1, 2, Qt::AlignRight); - layout->addWidget(tdwLetter, 0, 2, 2, 2, Qt::AlignLeft); - layout->addWidget(fromImage1, 1, 0, 1, 2, Qt::AlignLeft); + layout->addWidget(tdwLetter, 0, 2, 2, 4, Qt::AlignLeft); + //layout->addWidget(fromImage1, 1, 0, 1, 2, Qt::AlignLeft); layout->addWidget(selectPal, 2, 0, Qt::AlignRight); layout->addWidget(selectTable, 2, 1, Qt::AlignLeft); - layout->addWidget(tdwPalette, 2, 2, 1, 2, Qt::AlignLeft); - layout->addWidget(textLetter, 3, 2); - layout->addWidget(fromImage2, 3, 3, Qt::AlignRight); + layout->addWidget(tdwPalette, 2, 2, 1, 4, Qt::AlignLeft); + layout->addWidget(new QLabel(tr("Texte :")), 3, 2); + layout->addWidget(textLetter, 3, 3); + layout->addWidget(new QLabel(tr("Largeur :")), 3, 4); + layout->addWidget(widthLetter, 3, 5); + //layout->addWidget(fromImage2, 3, 3, Qt::AlignRight); // layout->addWidget(resetButton1, 4, 0, 1, 2, Qt::AlignLeft); layout->addWidget(exportButton, 4, 0, Qt::AlignLeft); layout->addWidget(importButton, 4, 1, Qt::AlignLeft); - layout->addWidget(resetButton2, 4, 2, 1, 2, Qt::AlignRight); + layout->addWidget(resetButton2, 4, 2, 1, 4, Qt::AlignRight); layout->setRowStretch(5, 1); - layout->setColumnStretch(3, 1); layout->setContentsMargins(QMargins()); connect(selectPal, SIGNAL(currentIndexChanged(int)), SLOT(setColor(int))); @@ -71,6 +76,8 @@ TdwWidget2::TdwWidget2(bool isAdditionnalTable, QWidget *parent) : connect(resetButton2, SIGNAL(clicked()), SLOT(resetLetter())); connect(tdwPalette, SIGNAL(colorChanged(int)), tdwLetter, SLOT(setPixelIndex(int))); connect(textLetter, SIGNAL(textEdited(QString)), SLOT(editLetter(QString))); + connect(widthLetter, SIGNAL(valueChanged(int)), SLOT(editWidth(int))); + connect(tdwLetter, SIGNAL(widthEdited(int)), widthLetter, SLOT(setValue(int))); } void TdwWidget2::clear() @@ -107,15 +114,17 @@ void TdwWidget2::setIsAdditionnalTable(bool isAdditionnalTable) { this->isAdditionnalTable = isAdditionnalTable; textLetter->setReadOnly(isAdditionnalTable); + widthLetter->setReadOnly(isAdditionnalTable); } void TdwWidget2::setReadOnly(bool ro) { textLetter->setReadOnly(isAdditionnalTable || ro); + widthLetter->setReadOnly(isAdditionnalTable || ro); tdwLetter->setReadOnly(ro); tdwPalette->setReadOnly(ro); - fromImage1->setDisabled(ro); - fromImage2->setDisabled(ro); + /* fromImage1->setDisabled(ro); + fromImage2->setDisabled(ro); */ } void TdwWidget2::setColor(int i) @@ -151,6 +160,9 @@ void TdwWidget2::setLetter(int i) } else { textLetter->setText(FF8Text(ba.append((char)(0x20 + i)))); } + if(tdwLetter->tdwFile()) { + widthLetter->setValue(tdwLetter->tdwFile()->charWidth(tdwGrid->currentTable(), i)); + } } resetButton2->setEnabled(false); } @@ -162,6 +174,14 @@ void TdwWidget2::editLetter(const QString &letter) } } +void TdwWidget2::editWidth(int w) +{ + if(tdwLetter->tdwFile() && tdwLetter->tdwFile()->charWidth(tdwGrid->currentTable(), tdwGrid->currentLetter()) != w) { + tdwLetter->tdwFile()->setCharWidth(tdwGrid->currentTable(), tdwGrid->currentLetter(), w); + tdwLetter->update(); + } +} + void TdwWidget2::exportFont() { TdwFile *tdwFile = tdwGrid->tdwFile(); diff --git a/widgets/TdwWidget2.h b/widgets/TdwWidget2.h index 288b958..d59fb05 100644 --- a/widgets/TdwWidget2.h +++ b/widgets/TdwWidget2.h @@ -41,6 +41,7 @@ public slots: void setTable(int i); void setLetter(int i); void editLetter(const QString &letter); + void editWidth(int w); void exportFont(); void importFont(); void reset(); @@ -56,6 +57,7 @@ private slots: QComboBox *selectPal, *selectTable; QPushButton *fromImage1, *fromImage2; QLineEdit *textLetter; + QSpinBox *widthLetter; QPushButton *resetButton2; FF8Font *ff8Font; protected: diff --git a/widgets/WalkmeshWidget.cpp b/widgets/WalkmeshWidget.cpp index 0a4aad3..df79483 100644 --- a/widgets/WalkmeshWidget.cpp +++ b/widgets/WalkmeshWidget.cpp @@ -307,14 +307,12 @@ QWidget *WalkmeshWidget::buildCameraRangePage() QWidget *ret = new QWidget(this); rangeList1 = new QListWidget(ret); - rangeList1->setFixedWidth(125); for(int i=0 ; i<8 ; ++i) { rangeList1->addItem(tr("Limite caméra %1").arg(i+1)); } rangeList2 = new QListWidget(ret); - rangeList2->setFixedWidth(125); for(int i=0 ; i<2 ; ++i) { rangeList2->addItem(tr("Limite écran %1").arg(i+1));