commit 2246ead5a827899242cb519b740fde86212096b9 Author: yenatch Date: Tue Sep 6 23:50:47 2016 -0400 Initial commit diff --git a/asm.cpp b/asm.cpp new file mode 100755 index 00000000..60855b76 --- /dev/null +++ b/asm.cpp @@ -0,0 +1,59 @@ +#include "asm.h" + +Asm::Asm() +{ +} + +void Asm::strip_comment(QString *line) { + bool in_string = false; + for (int i = 0; i < line->length(); i++) { + if (line->at(i) == '"') { + in_string = !in_string; + } else if (line->at(i) == '@') { + if (!in_string) { + line->truncate(i); + break; + } + } + } +} + +QList* Asm::parse(QString text) { + QList *parsed = new QList; + QStringList lines = text.split('\n'); + for (QString line : lines) { + QString label; + //QString macro; + //QStringList *params; + strip_comment(&line); + if (line.isEmpty()) { + } else if (line.contains(':')) { + label = line.left(line.indexOf(':')); + QStringList *list = new QStringList; + list->append(".label"); // This is not a real keyword. It's used only to make the output more regular. + list->append(label); + parsed->append(*list); + // There should not be anything else on the line. + // gas will raise a syntax error if there is. + } else { + line = line.trimmed(); + //parsed->append(line.split(QRegExp("\\s*,\\s*"))); + QString macro; + QStringList params; + int index = line.indexOf(QRegExp("\\s+")); + macro = line.left(index); + params = line.right(line.length() - index).trimmed().split(QRegExp("\\s*,\\s*")); + params.prepend(macro); + parsed->append(params); + } + //if (macro != NULL) { + // if (macros->contains(macro)) { + // void* function = macros->value(macro); + // if (function != NULL) { + // std::function function(params); + // } + // } + //} + } + return parsed; +} diff --git a/asm.h b/asm.h new file mode 100755 index 00000000..5aa55b55 --- /dev/null +++ b/asm.h @@ -0,0 +1,15 @@ +#ifndef ASM_H +#define ASM_H + +#include +#include + +class Asm +{ +public: + Asm(); + void strip_comment(QString*); + QList* parse(QString); +}; + +#endif // ASM_H diff --git a/block.cpp b/block.cpp new file mode 100755 index 00000000..144ba2de --- /dev/null +++ b/block.cpp @@ -0,0 +1,29 @@ +#include "block.h" + +Block::Block() { +} + +Block::Block(uint16_t word) +{ + tile = word & 0x3ff; + collision = (word >> 10) & 0x3; + elevation = (word >> 12) & 0xf; +} + +Block::Block(const Block &block) { + tile = block.tile; + collision = block.collision; + elevation = block.elevation; +} + +uint16_t Block::rawValue() { + return (tile & 0x3ff) + ((collision & 0x3) << 10) + ((elevation & 0xf) << 12); +} + +bool Block::operator ==(Block other) { + return (tile == other.tile) && (collision == other.collision) && (elevation == other.elevation); +} + +bool Block::operator !=(Block other) { + return !(operator ==(other)); +} diff --git a/block.h b/block.h new file mode 100755 index 00000000..8b3943d5 --- /dev/null +++ b/block.h @@ -0,0 +1,20 @@ +#ifndef BLOCK_H +#define BLOCK_H + +#include + +class Block +{ +public: + Block(); + Block(uint16_t); + Block(const Block&); + bool operator ==(Block); + bool operator !=(Block); + uint16_t tile:10; + uint16_t collision:2; + uint16_t elevation:4; + uint16_t rawValue(); +}; + +#endif // BLOCK_H diff --git a/blockdata.cpp b/blockdata.cpp new file mode 100755 index 00000000..b08b936d --- /dev/null +++ b/blockdata.cpp @@ -0,0 +1,39 @@ +#include "blockdata.h" + +Blockdata::Blockdata(QObject *parent) : QObject(parent) +{ + blocks = new QList; +} + +void Blockdata::addBlock(uint16_t word) { + Block block(word); + blocks->append(block); +} + +void Blockdata::addBlock(Block block) { + blocks->append(block); +} + +QByteArray Blockdata::serialize() { + QByteArray data; + for (int i = 0; i < blocks->length(); i++) { + Block block = blocks->value(i); + uint16_t word = block.rawValue(); + data.append(word & 0xff); + data.append((word >> 8) & 0xff); + } + return data; +} + +void Blockdata::copyFrom(Blockdata* other) { + blocks->clear(); + for (int i = 0; i < other->blocks->length(); i++) { + addBlock(other->blocks->value(i)); + } +} + +Blockdata* Blockdata::copy() { + Blockdata* blockdata = new Blockdata; + blockdata->copyFrom(this); + return blockdata; +} diff --git a/blockdata.h b/blockdata.h new file mode 100755 index 00000000..5ba4930b --- /dev/null +++ b/blockdata.h @@ -0,0 +1,28 @@ +#ifndef BLOCKDATA_H +#define BLOCKDATA_H + +#include "block.h" + +#include +#include + +class Blockdata : public QObject +{ + Q_OBJECT +public: + explicit Blockdata(QObject *parent = 0); + +public: + QList *blocks; + void addBlock(uint16_t); + void addBlock(Block); + QByteArray serialize(); + void copyFrom(Blockdata*); + Blockdata* copy(); + +signals: + +public slots: +}; + +#endif // BLOCKDATA_H diff --git a/editor.cpp b/editor.cpp new file mode 100755 index 00000000..9a56e5fc --- /dev/null +++ b/editor.cpp @@ -0,0 +1,324 @@ +#include "editor.h" + +Editor::Editor() +{ + +} + +void Editor::saveProject() { + if (project) { + project->saveAllMaps(); + } +} + +void Editor::save() { + if (project && map) { + project->saveMap(map); + } +} + +void Editor::undo() { + if (current_view) { + ((MapPixmapItem*)current_view)->undo(); + } +} + +void Editor::redo() { + if (current_view) { + ((MapPixmapItem*)current_view)->redo(); + } +} + +void Editor::setEditingMap() { + current_view = map_item; + map_item->draw(); + map_item->setVisible(true); + map_item->setEnabled(true); + collision_item->setVisible(false); + objects_group->setVisible(false); +} + +void Editor::setEditingCollision() { + current_view = collision_item; + collision_item->draw(); + collision_item->setVisible(true); + map_item->setVisible(false); + objects_group->setVisible(false); +} + +void Editor::setEditingObjects() { + objects_group->setVisible(true); + map_item->setVisible(true); + map_item->setEnabled(false); + collision_item->setVisible(false); +} + +void Editor::setMap(QString map_name) { + if (map_name.isNull()) { + return; + } + map = project->getMap(map_name); + displayMap(); +} + +void Editor::displayMap() { + scene = new QGraphicsScene; + + map_item = new MapPixmapItem(map); + map_item->draw(); + scene->addItem(map_item); + + collision_item = new CollisionPixmapItem(map); + collision_item->draw(); + scene->addItem(collision_item); + + objects_group = new QGraphicsItemGroup; + scene->addItem(objects_group); + + map_item->setVisible(false); + collision_item->setVisible(false); + objects_group->setVisible(false); + + int tw = 16; + int th = 16; + scene->setSceneRect( + -6 * tw, + -6 * th, + map_item->pixmap().width() + 12 * tw, + map_item->pixmap().height() + 12 * th + ); + + displayMetatiles(); + displayCollisionMetatiles(); + displayElevationMetatiles(); + displayMapObjects(); + displayMapConnections(); + displayMapBorder(); +} + +void Editor::displayMetatiles() { + scene_metatiles = new QGraphicsScene; + metatiles_item = new MetatilesPixmapItem(map); + metatiles_item->draw(); + scene_metatiles->addItem(metatiles_item); +} + +void Editor::displayCollisionMetatiles() { + scene_collision_metatiles = new QGraphicsScene; + collision_metatiles_item = new CollisionMetatilesPixmapItem(map); + collision_metatiles_item->draw(); + scene_collision_metatiles->addItem(collision_metatiles_item); +} + +void Editor::displayElevationMetatiles() { + scene_elevation_metatiles = new QGraphicsScene; + elevation_metatiles_item = new ElevationMetatilesPixmapItem(map); + elevation_metatiles_item->draw(); + scene_elevation_metatiles->addItem(elevation_metatiles_item); +} + +void Editor::displayMapObjects() { + for (QGraphicsItem *child : objects_group->childItems()) { + objects_group->removeFromGroup(child); + } + + project->loadObjectPixmaps(map->object_events); + for (int i = 0; i < map->object_events.length(); i++) { + ObjectEvent *object_event = map->object_events.value(i); + DraggablePixmapItem *object = new DraggablePixmapItem(object_event); + objects_group->addToGroup(object); + } + objects_group->setFiltersChildEvents(false); +} + +void Editor::displayMapConnections() { + for (Connection *connection : map->connections) { + if (connection->direction == "dive" || connection->direction == "emerge") { + continue; + } + Map *connected_map = project->getMap(connection->map_name); + QPixmap pixmap = connected_map->renderConnection(*connection); + int offset = connection->offset.toInt(nullptr, 0); + int x, y; + if (connection->direction == "up") { + x = offset * 16; + y = -pixmap.height(); + } else if (connection->direction == "down") { + x = offset * 16; + y = map->getHeight() * 16; + } else if (connection->direction == "left") { + x = -pixmap.width(); + y = offset * 16; + } else if (connection->direction == "right") { + x = map->getWidth() * 16; + y = offset * 16; + } + QGraphicsPixmapItem *item = new QGraphicsPixmapItem(pixmap); + item->setX(x); + item->setY(y); + scene->addItem(item); + } +} + +void Editor::displayMapBorder() { + QPixmap pixmap = map->renderBorder(); + for (int y = -6; y < map->getHeight() + 6; y += 2) + for (int x = -6; x < map->getWidth() + 6; x += 2) { + QGraphicsPixmapItem *item = new QGraphicsPixmapItem(pixmap); + item->setX(x * 16); + item->setY(y * 16); + item->setZValue(-1); + scene->addItem(item); + } +} + + +void MetatilesPixmapItem::draw() { + setPixmap(map->renderMetatiles()); +} + +void MetatilesPixmapItem::pick(uint tile) { + map->paint_tile = tile; + draw(); +} + +void MetatilesPixmapItem::mousePressEvent(QGraphicsSceneMouseEvent *event) { + QPointF pos = event->pos(); + int x = ((int)pos.x()) / 16; + int y = ((int)pos.y()) / 16; + //qDebug() << QString("(%1, %2)").arg(x).arg(y); + int width = pixmap().width() / 16; + int height = pixmap().height() / 16; + if ((x >= 0 && x < width) && (y >=0 && y < height)) { + pick(y * width + x); + } +} +void MetatilesPixmapItem::mouseReleaseEvent(QGraphicsSceneMouseEvent *event) { + mousePressEvent(event); +} + + +void MapPixmapItem::paint(QGraphicsSceneMouseEvent *event) { + if (map) { + QPointF pos = event->pos(); + int x = (int)(pos.x()) / 16; + int y = (int)(pos.y()) / 16; + Block *block = map->getBlock(x, y); + if (block) { + block->tile = map->paint_tile; + map->setBlock(x, y, *block); + } + draw(); + } +} + +void MapPixmapItem::floodFill(QGraphicsSceneMouseEvent *event) { + if (map) { + QPointF pos = event->pos(); + int x = (int)(pos.x()) / 16; + int y = (int)(pos.y()) / 16; + map->floodFill(x, y, map->paint_tile); + draw(); + } +} + +void MapPixmapItem::draw() { + setPixmap(map->render()); +} + +void MapPixmapItem::undo() { + map->undo(); + draw(); +} + +void MapPixmapItem::redo() { + map->redo(); + draw(); +} + +void MapPixmapItem::mousePressEvent(QGraphicsSceneMouseEvent *event) { + active = true; + if (event->button() == Qt::RightButton) { + right_click = true; + floodFill(event); + } else { + right_click = false; + paint(event); + } +} +void MapPixmapItem::mouseMoveEvent(QGraphicsSceneMouseEvent *event) { + if (active) { + if (right_click) { + floodFill(event); + } else { + paint(event); + } + } +} +void MapPixmapItem::mouseReleaseEvent(QGraphicsSceneMouseEvent *event) { + active = false; +} + +void CollisionPixmapItem::draw() { + setPixmap(map->renderCollision()); +} + +void CollisionPixmapItem::paint(QGraphicsSceneMouseEvent *event) { + if (map) { + QPointF pos = event->pos(); + int x = (int)(pos.x()) / 16; + int y = (int)(pos.y()) / 16; + Block *block = map->getBlock(x, y); + if (block) { + if (map->paint_collision >= 0) { + block->collision = map->paint_collision; + } + if (map->paint_elevation >= 0) { + block->elevation = map->paint_elevation; + } + map->setBlock(x, y, *block); + } + draw(); + } +} + +void CollisionPixmapItem::floodFill(QGraphicsSceneMouseEvent *event) { + if (map) { + QPointF pos = event->pos(); + int x = (int)(pos.x()) / 16; + int y = (int)(pos.y()) / 16; + bool collision = map->paint_collision >= 0; + bool elevation = map->paint_elevation >= 0; + if (collision && elevation) { + map->floodFillCollisionElevation(x, y, map->paint_collision, map->paint_elevation); + } else if (collision) { + map->floodFillCollision(x, y, map->paint_collision); + } else if (elevation) { + map->floodFillElevation(x, y, map->paint_elevation); + } + draw(); + } +} + +void DraggablePixmapItem::mousePressEvent(QGraphicsSceneMouseEvent *mouse) { + active = true; + last_x = mouse->pos().x() / 16; + last_y = mouse->pos().y() / 16; + qDebug() << event->x_ + ", " + event->y_; +} + +void DraggablePixmapItem::mouseMoveEvent(QGraphicsSceneMouseEvent *mouse) { + if (active) { + int x = mouse->pos().x() / 16; + int y = mouse->pos().y() / 16; + if (x != last_x || y != last_y) { + event->setX(event->x() + x - last_x); + event->setY(event->y() + y - last_y); + update(); + } + } +} + +void DraggablePixmapItem::mouseReleaseEvent(QGraphicsSceneMouseEvent *mouse) { + active = false; +} diff --git a/editor.h b/editor.h new file mode 100755 index 00000000..54564247 --- /dev/null +++ b/editor.h @@ -0,0 +1,155 @@ +#ifndef EDITOR_H +#define EDITOR_H + +#include +#include +#include + +#include "project.h" + + +class DraggablePixmapItem : public QGraphicsPixmapItem { +public: + DraggablePixmapItem(QPixmap pixmap): QGraphicsPixmapItem(pixmap) { + } + Event *event; + DraggablePixmapItem(Event *event_) : QGraphicsPixmapItem(event_->pixmap) { + event = event_; + update(); + } + bool active; + bool right_click; + int last_x; + int last_y; + void update() { + int x = event->x() * 16; + int y = event->y() * 16; + x -= pixmap().width() / 32 * 16; + y -= pixmap().height() - 16; + setX(x); + setY(y); + setZValue(event->y()); + } + +protected: + void mousePressEvent(QGraphicsSceneMouseEvent*); + void mouseMoveEvent(QGraphicsSceneMouseEvent*); + void mouseReleaseEvent(QGraphicsSceneMouseEvent*); +}; + +class MapPixmapItem : public QGraphicsPixmapItem { +public: + MapPixmapItem(QPixmap pixmap): QGraphicsPixmapItem(pixmap) { + } + Map *map; + MapPixmapItem(Map *map_) { + map = map_; + } + bool active; + bool right_click; + virtual void paint(QGraphicsSceneMouseEvent*); + virtual void floodFill(QGraphicsSceneMouseEvent*); + virtual void undo(); + virtual void redo(); + virtual void draw(); + +protected: + void mousePressEvent(QGraphicsSceneMouseEvent*); + void mouseMoveEvent(QGraphicsSceneMouseEvent*); + void mouseReleaseEvent(QGraphicsSceneMouseEvent*); +}; + +class CollisionPixmapItem : public MapPixmapItem { +public: + CollisionPixmapItem(QPixmap pixmap): MapPixmapItem(pixmap) { + } + CollisionPixmapItem(Map *map_): MapPixmapItem(map_) { + } + +public: + virtual void paint(QGraphicsSceneMouseEvent*); + virtual void floodFill(QGraphicsSceneMouseEvent*); + virtual void draw(); +}; + +class MetatilesPixmapItem : public QGraphicsPixmapItem { +public: + MetatilesPixmapItem(QPixmap pixmap): QGraphicsPixmapItem(pixmap) { + } + MetatilesPixmapItem(Map *map_) { + map = map_; + } + Map* map; + virtual void pick(uint); + virtual void draw(); +protected: + void mousePressEvent(QGraphicsSceneMouseEvent*); + void mouseReleaseEvent(QGraphicsSceneMouseEvent*); +}; + +class CollisionMetatilesPixmapItem : public MetatilesPixmapItem { +public: + CollisionMetatilesPixmapItem(Map *map_): MetatilesPixmapItem(map_) { + } + virtual void pick(uint collision) { + map->paint_collision = collision; + draw(); + } + virtual void draw() { + setPixmap(map->renderCollisionMetatiles()); + } +}; + +class ElevationMetatilesPixmapItem : public MetatilesPixmapItem { +public: + ElevationMetatilesPixmapItem(Map *map_): MetatilesPixmapItem(map_) { + } + virtual void pick(uint elevation) { + map->paint_elevation = elevation; + draw(); + } + virtual void draw() { + setPixmap(map->renderElevationMetatiles()); + } +}; + + +class Editor +{ +public: + Editor(); +public: + Project *project; + Map *map; + void saveProject(); + void save(); + void undo(); + void redo(); + void setMap(QString map_name); + void displayMap(); + void displayMetatiles(); + void displayCollisionMetatiles(); + void displayElevationMetatiles(); + void displayMapObjects(); + void displayMapConnections(); + void displayMapBorder(); + + void setEditingMap(); + void setEditingCollision(); + void setEditingObjects(); + + QGraphicsScene *scene; + QGraphicsPixmapItem *current_view; + MapPixmapItem *map_item; + CollisionPixmapItem *collision_item; + QGraphicsItemGroup *objects_group; + + QGraphicsScene *scene_metatiles; + QGraphicsScene *scene_collision_metatiles; + QGraphicsScene *scene_elevation_metatiles; + MetatilesPixmapItem *metatiles_item; + CollisionMetatilesPixmapItem *collision_metatiles_item; + ElevationMetatilesPixmapItem *elevation_metatiles_item; +}; + +#endif // EDITOR_H diff --git a/event.cpp b/event.cpp new file mode 100755 index 00000000..11ca06cd --- /dev/null +++ b/event.cpp @@ -0,0 +1,52 @@ +#include "event.h" + +Event::Event() +{ + +} + +ObjectEvent::ObjectEvent() +{ + +} + +Warp::Warp() +{ + +} + +CoordEvent::CoordEvent() +{ + +} + +BGEvent::BGEvent() +{ + +} + +Sign::Sign() +{ + +} + +Sign::Sign(const BGEvent &bg) +{ + x_ = bg.x_; + y_ = bg.y_; + elevation_ = bg.elevation_; + type = bg.type; +} + +HiddenItem::HiddenItem() +{ + +} + +HiddenItem::HiddenItem(const BGEvent &bg) +{ + x_ = bg.x_; + y_ = bg.y_; + elevation_ = bg.elevation_; + type = bg.type; +} diff --git a/event.h b/event.h new file mode 100755 index 00000000..3b1dc635 --- /dev/null +++ b/event.h @@ -0,0 +1,100 @@ +#ifndef EVENT_H +#define EVENT_H + +#include +#include + +class Event +{ +public: + Event(); + +public: + int x() { + return x_.toInt(nullptr, 0); + } + int y() { + return y_.toInt(nullptr, 0); + } + int elevation() { + return elevation_.toInt(nullptr, 0); + } + void setX(int x) { + x_ = QString("%1").arg(x); + } + void setY(int y) { + y_ = QString("%1").arg(y); + } + + QString x_; + QString y_; + QString elevation_; + QPixmap pixmap; +}; + +class ObjectEvent : public Event { +public: + ObjectEvent(); + +public: + QString sprite; + QString replacement; // ???? + QString behavior; + QString radius_x; + QString radius_y; + QString property; + QString sight_radius; + QString script_label; + QString event_flag; +}; + +class Warp : public Event { +public: + Warp(); + +public: + QString destination_warp; + QString destination_map; +}; + +class CoordEvent : public Event { +public: + CoordEvent(); + +public: + QString unknown1; + QString unknown2; + QString unknown3; + QString unknown4; + QString script_label; +}; + +class BGEvent : public Event { +public: + BGEvent(); +public: + bool is_item() { + return type.toInt(nullptr, 0) >= 5; + } + QString type; +}; + +class Sign : public BGEvent { +public: + Sign(); + Sign(const BGEvent&); +public: + QString script_label; +}; + +class HiddenItem : public BGEvent { +public: + HiddenItem(); + HiddenItem(const BGEvent&); +public: + QString item; + QString unknown5; + QString unknown6; +}; + +#endif // EVENT_H diff --git a/main.cpp b/main.cpp new file mode 100755 index 00000000..aab39bb8 --- /dev/null +++ b/main.cpp @@ -0,0 +1,11 @@ +#include "mainwindow.h" +#include + +int main(int argc, char *argv[]) +{ + QApplication a(argc, argv); + MainWindow w; + w.show(); + + return a.exec(); +} diff --git a/mainwindow.cpp b/mainwindow.cpp new file mode 100755 index 00000000..09695706 --- /dev/null +++ b/mainwindow.cpp @@ -0,0 +1,370 @@ +#include "mainwindow.h" +#include "ui_mainwindow.h" +#include "project.h" + +#include +#include +#include +#include +#include + +MainWindow::MainWindow(QWidget *parent) : + QMainWindow(parent), + ui(new Ui::MainWindow) +{ + QCoreApplication::setOrganizationName("pret"); + QCoreApplication::setApplicationName("pretmap"); + + editor = new Editor; + + new QShortcut(QKeySequence(Qt::CTRL + Qt::SHIFT + Qt::Key_Z), this, SLOT(redo())); + ui->setupUi(this); + + QSettings settings; + QString key = "recent_projects"; + if (settings.contains(key)) { + QString default_dir = settings.value(key).toStringList().last(); + openProject(default_dir); + } +} + +MainWindow::~MainWindow() +{ + delete ui; +} + +void MainWindow::openProject(QString dir) { + bool already_open = (editor->project != NULL) && (editor->project->root == dir); + if (!already_open) { + editor->project = new Project; + editor->project->root = dir; + populateMapList(); + setMap(getDefaultMap()); + } else { + populateMapList(); + } +} + +QString MainWindow::getDefaultMap() { + QSettings settings; + QString key = "project:" + editor->project->root; + if (settings.contains(key)) { + QMap qmap = settings.value(key).toMap(); + if (qmap.contains("recent_map")) { + QString map_name = qmap.value("recent_map").toString(); + return map_name; + } + } + // Failing that, just get the first map in the list. + for (int i = 0; i < editor->project->groupedMapNames->length(); i++) { + QStringList *list = editor->project->groupedMapNames->value(i); + if (list->length()) { + return list->value(0); + } + } + return NULL; +} + +QString MainWindow::getExistingDirectory(QString dir) { + return QFileDialog::getExistingDirectory(this, "Open Directory", dir, QFileDialog::ShowDirsOnly | QFileDialog::DontResolveSymlinks); +} + +void MainWindow::on_action_Open_Project_triggered() +{ + QSettings settings; + QString key = "recent_projects"; + QString recent = "."; + if (settings.contains(key)) { + recent = settings.value(key).toStringList().last(); + } + QString dir = getExistingDirectory(recent); + if (!dir.isEmpty()) { + QStringList recents; + if (settings.contains(key)) { + recents = settings.value(key).toStringList(); + } + recents.removeAll(dir); + recents.append(dir); + settings.setValue(key, recents); + + openProject(dir); + } +} + +void MainWindow::setMap(QString map_name) { + if (map_name.isNull()) { + return; + } + editor->setMap(map_name); + + if (ui->tabWidget->currentIndex() == 1) { + editor->setEditingObjects(); + } else { + if (ui->tabWidget_2->currentIndex() == 1) { + editor->setEditingCollision(); + } else { + editor->setEditingMap(); + } + } + + ui->graphicsView_Map->setScene(editor->scene); + ui->graphicsView_Map->setSceneRect(editor->scene->sceneRect()); + ui->graphicsView_Map->setFixedSize(editor->scene->width() + 2, editor->scene->height() + 2); + + ui->graphicsView_Objects_Map->setScene(editor->scene); + ui->graphicsView_Objects_Map->setSceneRect(editor->scene->sceneRect()); + ui->graphicsView_Objects_Map->setFixedSize(editor->scene->width() + 2, editor->scene->height() + 2); + + ui->graphicsView_Metatiles->setScene(editor->scene_metatiles); + //ui->graphicsView_Metatiles->setSceneRect(editor->scene_metatiles->sceneRect()); + ui->graphicsView_Metatiles->setFixedSize(editor->metatiles_item->pixmap().width() + 2, editor->metatiles_item->pixmap().height() + 2); + + ui->graphicsView_Collision->setScene(editor->scene_collision_metatiles); + //ui->graphicsView_Collision->setSceneRect(editor->scene_collision_metatiles->sceneRect()); + ui->graphicsView_Collision->setFixedSize(editor->collision_metatiles_item->pixmap().width() + 2, editor->collision_metatiles_item->pixmap().height() + 2); + + ui->graphicsView_Elevation->setScene(editor->scene_elevation_metatiles); + //ui->graphicsView_Elevation->setSceneRect(editor->scene_elevation_metatiles->sceneRect()); + ui->graphicsView_Elevation->setFixedSize(editor->elevation_metatiles_item->pixmap().width() + 2, editor->elevation_metatiles_item->pixmap().height() + 2); + + displayMapProperties(); + + setRecentMap(map_name); + updateMapList(); +} + +void MainWindow::setRecentMap(QString map_name) { + QSettings settings; + QString key = "project:" + editor->project->root; + QMap qmap; + if (settings.contains(key)) { + qmap = settings.value(key).toMap(); + } + qmap.insert("recent_map", map_name); + settings.setValue(key, qmap); +} + +void MainWindow::displayMapProperties() { + Map *map = editor->map; + Project *project = editor->project; + + QStringList songs = project->getSongNames(); + ui->comboBox_Song->clear(); + ui->comboBox_Song->addItems(songs); + QString song = map->song; + if (!songs.contains(song)) { + song = project->getSongName(song.toInt()); + } + ui->comboBox_Song->setCurrentText(song); + + ui->comboBox_Location->clear(); + ui->comboBox_Location->addItems(project->getLocations()); + ui->comboBox_Location->setCurrentText(map->location); + + ui->comboBox_Visibility->clear(); + ui->comboBox_Visibility->addItems(project->getVisibilities()); + ui->comboBox_Visibility->setCurrentText(map->visibility); + + ui->comboBox_Weather->clear(); + ui->comboBox_Weather->addItems(project->getWeathers()); + ui->comboBox_Weather->setCurrentText(map->weather); + + ui->comboBox_Type->clear(); + ui->comboBox_Type->addItems(project->getMapTypes()); + ui->comboBox_Type->setCurrentText(map->type); + + ui->comboBox_BattleScene->clear(); + ui->comboBox_BattleScene->addItems(project->getBattleScenes()); + ui->comboBox_BattleScene->setCurrentText(map->battle_scene); + + ui->checkBox_ShowLocation->setChecked(map->show_location.toInt() > 0 || map->show_location == "TRUE"); +} + +void MainWindow::on_comboBox_Song_activated(const QString &song) +{ + editor->map->song = song; +} + +void MainWindow::on_comboBox_Location_activated(const QString &location) +{ + editor->map->location = location; +} + +void MainWindow::on_comboBox_Visibility_activated(const QString &visibility) +{ + editor->map->visibility = visibility; +} + +void MainWindow::on_comboBox_Weather_activated(const QString &weather) +{ + editor->map->weather = weather; +} + +void MainWindow::on_comboBox_Type_activated(const QString &type) +{ + editor->map->type = type; +} + +void MainWindow::on_comboBox_BattleScene_activated(const QString &battle_scene) +{ + editor->map->battle_scene = battle_scene; +} + +void MainWindow::on_checkBox_ShowLocation_clicked(bool checked) +{ + if (checked) { + editor->map->show_location = "TRUE"; + } else { + editor->map->show_location = "FALSE"; + } +} + + +void MainWindow::populateMapList() { + Project *project = editor->project; + + QIcon mapFolderIcon; + mapFolderIcon.addFile(QStringLiteral(":/icons/folder_closed_map.ico"), QSize(), QIcon::Normal, QIcon::Off); + mapFolderIcon.addFile(QStringLiteral(":/icons/folder_map.ico"), QSize(), QIcon::Normal, QIcon::On); + + QIcon folderIcon; + folderIcon.addFile(QStringLiteral(":/icons/folder_closed.ico"), QSize(), QIcon::Normal, QIcon::Off); + + QIcon mapIcon; + mapIcon.addFile(QStringLiteral(":/icons/map.ico"), QSize(), QIcon::Normal, QIcon::Off); + mapIcon.addFile(QStringLiteral(":/icons/image.ico"), QSize(), QIcon::Normal, QIcon::On); + + QStandardItemModel *model = new QStandardItemModel; + + QStandardItem *entry = new QStandardItem; + entry->setText(project->getProjectTitle()); + entry->setIcon(folderIcon); + entry->setEditable(false); + model->appendRow(entry); + + QStandardItem *maps = new QStandardItem; + maps->setText("maps"); + maps->setIcon(folderIcon); + maps->setEditable(false); + entry->appendRow(maps); + + project->readMapGroups(); + for (int i = 0; i < project->groupNames->length(); i++) { + QString group_name = project->groupNames->value(i); + QStandardItem *group = new QStandardItem; + group->setText(group_name); + group->setIcon(mapFolderIcon); + group->setEditable(false); + maps->appendRow(group); + QStringList *names = project->groupedMapNames->value(i); + for (int j = 0; j < names->length(); j++) { + QString map_name = names->value(j); + QStandardItem *map = new QStandardItem; + map->setText(QString("[%1.%2] ").arg(i).arg(j, 2, 10, QLatin1Char('0')) + map_name); + map->setIcon(mapIcon); + map->setEditable(false); + map->setData(map_name, Qt::UserRole); + group->appendRow(map); + //ui->mapList->setExpanded(model->indexFromItem(map), false); // redundant + } + } + + ui->mapList->setModel(model); + ui->mapList->setUpdatesEnabled(true); + ui->mapList->expandToDepth(2); + ui->mapList->repaint(); +} + +void MainWindow::on_mapList_activated(const QModelIndex &index) +{ + QVariant data = index.data(Qt::UserRole); + if (!data.isNull()) { + setMap(data.toString()); + } + updateMapList(); +} + +void MainWindow::markAllEdited(QAbstractItemModel *model) { + QList list; + list.append(QModelIndex()); + while (list.length()) { + QModelIndex parent = list.takeFirst(); + for (int i = 0; i < model->rowCount(parent); i++) { + QModelIndex index = model->index(i, 0, parent); + if (model->hasChildren(index)) { + list.append(index); + } + markEdited(index); + } + } +} + +void MainWindow::markEdited(QModelIndex index) { + QVariant data = index.data(Qt::UserRole); + if (!data.isNull()) { + QString map_name = data.toString(); + if (editor->project) { + if (editor->project->map_cache->contains(map_name)) { + // Just mark anything that's been opened for now. + // TODO if (project->getMap()->saved) + ui->mapList->setExpanded(index, true); + } + } + } +} + +void MainWindow::updateMapList() { + QAbstractItemModel *model = ui->mapList->model(); + markAllEdited(model); +} + +void MainWindow::on_action_Save_Project_triggered() +{ + editor->saveProject(); + updateMapList(); +} + +void MainWindow::undo() { + editor->undo(); +} + +void MainWindow::redo() { + editor->redo(); +} + +void MainWindow::on_action_Save_triggered() { + editor->save(); +} + +void MainWindow::on_tabWidget_2_currentChanged(int index) +{ + if (index == 0) { + editor->setEditingMap(); + } else if (index == 1) { + editor->setEditingCollision(); + } +} + +void MainWindow::on_action_Exit_triggered() +{ + QApplication::quit(); +} + +void MainWindow::on_tabWidget_currentChanged(int index) +{ + if (index == 0) { + on_tabWidget_2_currentChanged(ui->tabWidget_2->currentIndex()); + } else if (index == 1) { + editor->setEditingObjects(); + } +} + +void MainWindow::on_actionUndo_triggered() +{ + undo(); +} + +void MainWindow::on_actionRedo_triggered() +{ + redo(); +} diff --git a/mainwindow.h b/mainwindow.h new file mode 100755 index 00000000..0d79638a --- /dev/null +++ b/mainwindow.h @@ -0,0 +1,69 @@ +#ifndef MAINWINDOW_H +#define MAINWINDOW_H + +#include +#include +#include +#include +#include +#include +#include +#include "project.h" +#include "map.h" +#include "editor.h" + +namespace Ui { +class MainWindow; +} + +class MainWindow : public QMainWindow +{ + Q_OBJECT + +public: + explicit MainWindow(QWidget *parent = 0); + ~MainWindow(); + +private slots: + void on_action_Open_Project_triggered(); + void on_mapList_activated(const QModelIndex &index); + void on_action_Save_Project_triggered(); + + void undo(); + void redo(); + + void on_action_Save_triggered(); + void on_tabWidget_2_currentChanged(int index); + void on_action_Exit_triggered(); + void on_comboBox_Song_activated(const QString &arg1); + void on_comboBox_Location_activated(const QString &arg1); + void on_comboBox_Visibility_activated(const QString &arg1); + void on_comboBox_Weather_activated(const QString &arg1); + void on_comboBox_Type_activated(const QString &arg1); + void on_comboBox_BattleScene_activated(const QString &arg1); + void on_checkBox_ShowLocation_clicked(bool checked); + + void on_tabWidget_currentChanged(int index); + + void on_actionUndo_triggered(); + + void on_actionRedo_triggered(); + +private: + Ui::MainWindow *ui; + Editor *editor; + void setMap(QString); + void populateMapList(); + QString getExistingDirectory(QString); + void openProject(QString dir); + QString getDefaultMap(); + void setRecentMap(QString map_name); + + void markAllEdited(QAbstractItemModel *model); + void markEdited(QModelIndex index); + void updateMapList(); + + void displayMapProperties(); +}; + +#endif // MAINWINDOW_H diff --git a/mainwindow.ui b/mainwindow.ui new file mode 100755 index 00000000..adb7c468 --- /dev/null +++ b/mainwindow.ui @@ -0,0 +1,938 @@ + + + MainWindow + + + + 0 + 0 + 1019 + 666 + + + + + 0 + 0 + + + + MainWindow + + + + + + + Qt::Horizontal + + + + + 0 + 0 + + + + + 100 + 0 + + + + QAbstractItemView::SingleSelection + + + QAbstractItemView::SelectItems + + + false + + + + + true + + + + 1 + 0 + + + + 1 + + + false + + + false + + + + + :/icons/map.ico:/icons/map.ico + + + Map + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + Qt::Horizontal + + + + + 1 + 0 + + + + true + + + + + 0 + 0 + 571 + 564 + + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + Qt::Horizontal + + + + 166 + 16 + + + + + + + + Qt::Vertical + + + + 16 + 166 + + + + + + + + Qt::Horizontal + + + + 166 + 16 + + + + + + + + Qt::Vertical + + + + 16 + 166 + + + + + + + + + 0 + 0 + + + + false + + + + + + + + + true + + + + 0 + 0 + + + + + 156 + 0 + + + + 1 + + + + + 0 + 0 + + + + Blocks + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + 0 + 0 + + + + Qt::ScrollBarAlwaysOn + + + Qt::ScrollBarAsNeeded + + + QAbstractScrollArea::AdjustIgnored + + + true + + + + true + + + + 0 + 0 + 81 + 28 + + + + + 0 + 0 + + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + true + + + + 0 + 0 + + + + Qt::ScrollBarAlwaysOff + + + Qt::ScrollBarAlwaysOff + + + QAbstractScrollArea::AdjustIgnored + + + + + + + + + + + + true + + + + 0 + 0 + + + + Collision + + + + + 20 + 60 + 111 + 20 + + + + Qt::Horizontal + + + + + + 0 + 80 + 151 + 101 + + + + QFrame::StyledPanel + + + QFrame::Raised + + + + + 10 + 10 + 47 + 13 + + + + Elevation + + + + + + 10 + 30 + 131 + 61 + + + + + + + + 0 + 0 + 151 + 61 + + + + QFrame::StyledPanel + + + QFrame::Raised + + + + + 10 + 10 + 47 + 13 + + + + Collision + + + + + + 40 + 30 + 66 + 18 + + + + + + + + + + + + + true + + + Objects + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + Qt::Horizontal + + + + + 1 + 0 + + + + true + + + + + 0 + 0 + 571 + 564 + + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + Qt::Horizontal + + + + 166 + 16 + + + + + + + + Qt::Horizontal + + + + 166 + 16 + + + + + + + + Qt::Vertical + + + + 16 + 166 + + + + + + + + Qt::Vertical + + + + 16 + 166 + + + + + + + + + 0 + 0 + + + + + + + + + + true + + + + 0 + 0 + + + + + 156 + 0 + + + + + + + Selected + + + + + + + + 16777215 + 32 + + + + QFrame::StyledPanel + + + QFrame::Raised + + + + + 0 + 0 + 31 + 31 + + + + New + + + + + + 100 + 0 + 31 + 31 + + + + Delete + + + + + + + + + + + + + Attributes + + + + + 10 + 10 + 301 + 251 + + + + QFrame::StyledPanel + + + QFrame::Raised + + + + + 20 + 30 + 47 + 21 + + + + Song + + + + + + 20 + 60 + 47 + 21 + + + + Location + + + + + + 20 + 90 + 47 + 21 + + + + Visibility + + + + + + 20 + 120 + 47 + 21 + + + + Weather + + + + + + 20 + 150 + 47 + 21 + + + + Type + + + + + + 20 + 180 + 101 + 21 + + + + Show location name + + + + + + 20 + 210 + 81 + 21 + + + + Battle scene + + + + + + 80 + 60 + 211 + 22 + + + + + + + 80 + 90 + 211 + 22 + + + + + + + 80 + 30 + 211 + 22 + + + + + + + 80 + 120 + 211 + 22 + + + + + + + 80 + 150 + 211 + 22 + + + + + + + 90 + 210 + 201 + 22 + + + + + + + 130 + 180 + 161 + 21 + + + + + + + + + + 0 + 0 + 47 + 13 + + + + Header + + + + + + + + + + + + + 0 + 0 + 1019 + 21 + + + + + File + + + + + + + + + + Edit + + + + + + + + + + TopToolBarArea + + + false + + + + + + Save All + + + Ctrl+Shift+S + + + + + Exit + + + Ctrl+Q + + + + + Open Project... + + + Ctrl+O + + + + + Save + + + Ctrl+S + + + + + Undo + + + Ctrl+Z + + + + + Redo + + + + 50 + false + + + + Ctrl+Y + + + + + + + + + diff --git a/map.cpp b/map.cpp new file mode 100755 index 00000000..941838ff --- /dev/null +++ b/map.cpp @@ -0,0 +1,600 @@ +#include "map.h" + +#include +#include +#include + +Map::Map(QObject *parent) : QObject(parent) +{ + cached_blockdata = new Blockdata; + cached_collision = new Blockdata; + cached_border = new Blockdata; + paint_tile = 1; + paint_collision = 0; + paint_elevation = 3; +} + +int Map::getWidth() { + return width.toInt(nullptr, 0); +} + +int Map::getHeight() { + return height.toInt(nullptr, 0); +} + +Tileset* Map::getBlockTileset(uint metatile_index) { + uint primary_size = 0x200;//tileset_primary->metatiles->length(); + if (metatile_index < primary_size) { + return tileset_primary; + } else { + return tileset_secondary; + } +} + +QImage Map::getMetatileTile(uint tile) { + uint primary_size = 0x200;//tileset_primary->metatiles->length(); + if (tile < primary_size) { + return tileset_primary->tiles->value(tile); + } else { + return tileset_secondary->tiles->value(tile - primary_size); + } +} + +Metatile* Map::getMetatile(uint index) { + uint primary_size = 0x200;//tileset_primary->metatiles->length(); + if (index < primary_size) { + return tileset_primary->metatiles->value(index); + } else { + //qDebug() << QString("secondary tileset: %1").arg(index - primary_size, 0, 16); + return tileset_secondary->metatiles->value(index - primary_size); + } +} + +QImage Map::getCollisionMetatileImage(Block block) { + return getCollisionMetatileImage(block.collision); +} + +QImage Map::getCollisionMetatileImage(int collision) { + QImage metatile_image(16, 16, QImage::Format_RGBA8888); + QColor color; + if (collision == 0) { + color.setGreen(0xff); + } else if (collision == 1) { + color.setRed(0xff); + } else if (collision == 2) { + color.setBlue(0xff); + } else if (collision == 3) { + // black + } + metatile_image.fill(color); + return metatile_image; +} + +QImage Map::getElevationMetatileImage(Block block) { + return getElevationMetatileImage(block.elevation); +} + +QImage Map::getElevationMetatileImage(int elevation) { + QImage metatile_image(16, 16, QImage::Format_RGBA8888); + QColor color; + if (elevation < 15) { + uint saturation = (elevation + 1) * 16 + 15; + color.setGreen(saturation); + color.setRed(saturation); + color.setBlue(saturation); + } else { + color.setGreen(0xd0); + color.setBlue(0xd0); + color.setRed(0); + } + metatile_image.fill(color); + //QPainter painter(&metatile_image); + //painter.end(); + return metatile_image; +} + +QImage Map::getMetatileImage(uint tile) { + + QImage metatile_image(16, 16, QImage::Format_RGBA8888); + + Metatile* metatile = getMetatile(tile); + if (metatile == NULL) { + metatile_image.fill(0xffffffff); + return metatile_image; + } + + Tileset* blockTileset = getBlockTileset(tile); + + for (int layer = 0; layer < 2; layer++) + for (int y = 0; y < 2; y++) + for (int x = 0; x < 2; x++) { + //qDebug() << QString("x=%1 y=%2 layer=%3").arg(x).arg(y).arg(layer); + Tile tile = metatile->tiles->value((y * 2) + x + (layer * 4)); + QImage tile_image = getMetatileTile(tile.tile); + QList palette = blockTileset->palettes->value(tile.palette); + for (int j = 0; j < palette.length(); j++) { + tile_image.setColor(j, palette.value(j)); + } + //QVector vector = palette.toVector(); + //tile_image.setColorTable(vector); + if (layer > 0) { + QColor color(tile_image.color(15)); + color.setAlpha(0); + tile_image.setColor(15, color.rgba()); + } + QPainter metatile_painter(&metatile_image); + QPoint origin = QPoint(x*8, y*8); + metatile_painter.drawImage(origin, tile_image.mirrored(tile.xflip == 1, tile.yflip == 1)); + metatile_painter.end(); + } + + return metatile_image; +} + +bool Map::blockChanged(int i, Blockdata *cache) { + if (cache->blocks->length() <= i) { + return true; + } + return blockdata->blocks->value(i) != cache->blocks->value(i); +} + +void Map::cacheBorder() { + cached_border = new Blockdata; + for (int i = 0; i < border->blocks->length(); i++) { + Block block = border->blocks->value(i); + cached_border->blocks->append(block); + } +} + +void Map::cacheBlockdata() { + cached_blockdata = new Blockdata; + for (int i = 0; i < blockdata->blocks->length(); i++) { + Block block = blockdata->blocks->value(i); + cached_blockdata->blocks->append(block); + } +} + +void Map::cacheCollision() { + cached_collision = new Blockdata; + for (int i = 0; i < blockdata->blocks->length(); i++) { + Block block = blockdata->blocks->value(i); + cached_collision->blocks->append(block); + } +} + +QPixmap Map::renderCollision() { + bool changed_any = false; + int width_ = getWidth(); + int height_ = getHeight(); + if ( + collision_image.isNull() + || collision_image.width() != width_ * 16 + || collision_image.height() != height_ * 16 + ) { + collision_image = QImage(width_ * 16, height_ * 16, QImage::Format_RGBA8888); + changed_any = true; + } + QPainter painter(&collision_image); + for (int i = 0; i < blockdata->blocks->length(); i++) { + if (!blockChanged(i, cached_collision)) { + continue; + } + changed_any = true; + Block block = blockdata->blocks->value(i); + QImage metatile_image = getMetatileImage(block.tile); + QImage collision_metatile_image = getCollisionMetatileImage(block); + QImage elevation_metatile_image = getElevationMetatileImage(block); + int map_y = i / width_; + int map_x = i % width_; + QPoint metatile_origin = QPoint(map_x * 16, map_y * 16); + painter.setOpacity(1); + painter.drawImage(metatile_origin, metatile_image); + + painter.save(); + if (block.elevation == 15) { + painter.setOpacity(0.5); + } else if (block.elevation == 0) { + painter.setOpacity(0); + } else { + painter.setOpacity(1);//(block.elevation / 16.0) * 0.8); + painter.setCompositionMode(QPainter::CompositionMode_Overlay); + } + painter.drawImage(metatile_origin, elevation_metatile_image); + painter.restore(); + + painter.save(); + if (block.collision == 0) { + painter.setOpacity(0.1); + } else { + painter.setOpacity(0.4); + } + painter.drawImage(metatile_origin, collision_metatile_image); + painter.restore(); + + painter.save(); + painter.setOpacity(0.6); + painter.setPen(QColor(255, 255, 255, 192)); + painter.setFont(QFont("Helvetica", 8)); + painter.drawText(QPoint(metatile_origin.x(), metatile_origin.y() + 8), QString("%1").arg(block.elevation)); + painter.restore(); + } + painter.end(); + cacheCollision(); + if (changed_any) { + collision_pixmap = collision_pixmap.fromImage(collision_image); + } + return collision_pixmap; +} + +QPixmap Map::render() { + bool changed_any = false; + int width_ = getWidth(); + int height_ = getHeight(); + if ( + image.isNull() + || image.width() != width_ * 16 + || image.height() != height_ * 16 + ) { + image = QImage(width_ * 16, height_ * 16, QImage::Format_RGBA8888); + changed_any = true; + } + QPainter painter(&image); + for (int i = 0; i < blockdata->blocks->length(); i++) { + if (!blockChanged(i, cached_blockdata)) { + continue; + } + changed_any = true; + Block block = blockdata->blocks->value(i); + QImage metatile_image = getMetatileImage(block.tile); + int map_y = i / width_; + int map_x = i % width_; + QPoint metatile_origin = QPoint(map_x * 16, map_y * 16); + painter.drawImage(metatile_origin, metatile_image); + } + painter.end(); + if (changed_any) { + cacheBlockdata(); + pixmap = pixmap.fromImage(image); + } + return pixmap; +} + +QPixmap Map::renderBorder() { + bool changed_any = false; + int width_ = 2; + int height_ = 2; + if (border_image.isNull()) { + border_image = QImage(width_ * 16, height_ * 16, QImage::Format_RGBA8888); + changed_any = true; + } + QPainter painter(&border_image); + for (int i = 0; i < border->blocks->length(); i++) { + if (!blockChanged(i, cached_border)) { + continue; + } + changed_any = true; + Block block = border->blocks->value(i); + QImage metatile_image = getMetatileImage(block.tile); + int map_y = i / width_; + int map_x = i % width_; + painter.drawImage(QPoint(map_x * 16, map_y * 16), metatile_image); + } + painter.end(); + if (changed_any) { + cacheBorder(); + border_pixmap = border_pixmap.fromImage(border_image); + } + return border_pixmap; +} + +QPixmap Map::renderConnection(Connection connection) { + render(); + int x, y, w, h; + if (connection.direction == "up") { + x = 0; + y = getHeight() - 6; + w = getWidth(); + h = 6; + } else if (connection.direction == "down") { + x = 0; + y = 0; + w = getWidth(); + h = 6; + } else if (connection.direction == "left") { + x = getWidth() - 6; + y = 0; + w = 6; + h = getHeight(); + } else if (connection.direction == "right") { + x = 0; + y = 0; + w = 6; + h = getHeight(); + } else { + // this should not happen + x = 0; + y = 0; + w = getWidth(); + h = getHeight(); + } + QImage connection_image = image.copy(x * 16, y * 16, w * 16, h * 16); + //connection_image = connection_image.convertToFormat(QImage::Format_Grayscale8); + return QPixmap::fromImage(connection_image); +} + +QPixmap Map::renderCollisionMetatiles() { + int length_ = 4; + int height_ = 1; + int width_ = length_ / height_; + QImage image(width_ * 16, height_ * 16, QImage::Format_RGBA8888); + QPainter painter(&image); + for (int i = 0; i < length_; i++) { + int y = i / width_; + int x = i % width_; + QPoint origin(x * 16, y * 16); + QImage metatile_image = getCollisionMetatileImage(i); + painter.drawImage(origin, metatile_image); + } + drawSelection(paint_collision, width_, &painter); + painter.end(); + return QPixmap::fromImage(image); +} + +QPixmap Map::renderElevationMetatiles() { + int length_ = 16; + int height_ = 2; + int width_ = length_ / height_; + QImage image(width_ * 16, height_ * 16, QImage::Format_RGBA8888); + QPainter painter(&image); + for (int i = 0; i < length_; i++) { + int y = i / width_; + int x = i % width_; + QPoint origin(x * 16, y * 16); + QImage metatile_image = getElevationMetatileImage(i); + painter.drawImage(origin, metatile_image); + } + drawSelection(paint_elevation, width_, &painter); + painter.end(); + return QPixmap::fromImage(image); +} + +void Map::drawSelection(int i, int w, QPainter *painter) { + int x = i % w; + int y = i / w; + painter->save(); + painter->setPen(QColor(0xff, 0xff, 0xff)); + painter->drawRect(x * 16, y * 16, 15, 15); + painter->setPen(QColor(0, 0, 0)); + painter->drawRect(x * 16 - 1, y * 16 - 1, 17, 17); + painter->drawRect(x * 16 + 1, y * 16 + 1, 13, 13); + painter->restore(); +} + +QPixmap Map::renderMetatiles() { + int primary_length = tileset_primary->metatiles->length(); + int length_ = primary_length + tileset_secondary->metatiles->length(); + int width_ = 8; + int height_ = length_ / width_; + QImage image(width_ * 16, height_ * 16, QImage::Format_RGBA8888); + QPainter painter(&image); + for (int i = 0; i < length_; i++) { + uint tile = i; + if (i >= primary_length) { + tile += 0x200 - primary_length; + } + QImage metatile_image = getMetatileImage(tile); + int map_y = i / width_; + int map_x = i % width_; + QPoint metatile_origin = QPoint(map_x * 16, map_y * 16); + painter.drawImage(metatile_origin, metatile_image); + } + + drawSelection(paint_tile, width_, &painter); + + painter.end(); + return QPixmap::fromImage(image); +} + +Block* Map::getBlock(int x, int y) { + if (x >= 0 && x < getWidth()) + if (y >= 0 && y < getHeight()) { + int i = y * getWidth() + x; + return new Block(blockdata->blocks->value(i)); + } + return NULL; +} + +void Map::_setBlock(int x, int y, Block block) { + int i = y * getWidth() + x; + blockdata->blocks->replace(i, block); +} + +void Map::_floodFill(int x, int y, uint tile) { + QList todo; + todo.append(QPoint(x, y)); + while (todo.length()) { + QPoint point = todo.takeAt(0); + x = point.x(); + y = point.y(); + Block *block = getBlock(x, y); + if (block == NULL) { + continue; + } + uint old_tile = block->tile; + if (old_tile == tile) { + continue; + } + block->tile = tile; + _setBlock(x, y, *block); + if ((block = getBlock(x + 1, y)) && block->tile == old_tile) { + todo.append(QPoint(x + 1, y)); + } + if ((block = getBlock(x - 1, y)) && block->tile == old_tile) { + todo.append(QPoint(x - 1, y)); + } + if ((block = getBlock(x, y + 1)) && block->tile == old_tile) { + todo.append(QPoint(x, y + 1)); + } + if ((block = getBlock(x, y - 1)) && block->tile == old_tile) { + todo.append(QPoint(x, y - 1)); + } + } +} + +void Map::_floodFillCollision(int x, int y, uint collision) { + QList todo; + todo.append(QPoint(x, y)); + while (todo.length()) { + QPoint point = todo.takeAt(0); + x = point.x(); + y = point.y(); + Block *block = getBlock(x, y); + if (block == NULL) { + continue; + } + uint old_coll = block->collision; + if (old_coll == collision) { + continue; + } + block->collision = collision; + _setBlock(x, y, *block); + if ((block = getBlock(x + 1, y)) && block->collision == old_coll) { + todo.append(QPoint(x + 1, y)); + } + if ((block = getBlock(x - 1, y)) && block->collision == old_coll) { + todo.append(QPoint(x - 1, y)); + } + if ((block = getBlock(x, y + 1)) && block->collision == old_coll) { + todo.append(QPoint(x, y + 1)); + } + if ((block = getBlock(x, y - 1)) && block->collision == old_coll) { + todo.append(QPoint(x, y - 1)); + } + } +} + +void Map::_floodFillElevation(int x, int y, uint elevation) { + QList todo; + todo.append(QPoint(x, y)); + while (todo.length()) { + QPoint point = todo.takeAt(0); + x = point.x(); + y = point.y(); + Block *block = getBlock(x, y); + if (block == NULL) { + continue; + } + uint old_z = block->elevation; + if (old_z == elevation) { + continue; + } + Block block_(*block); + block_.elevation = elevation; + _setBlock(x, y, block_); + if ((block = getBlock(x + 1, y)) && block->elevation == old_z) { + todo.append(QPoint(x + 1, y)); + } + if ((block = getBlock(x - 1, y)) && block->elevation == old_z) { + todo.append(QPoint(x - 1, y)); + } + if ((block = getBlock(x, y + 1)) && block->elevation == old_z) { + todo.append(QPoint(x, y + 1)); + } + if ((block = getBlock(x, y - 1)) && block->elevation == old_z) { + todo.append(QPoint(x, y - 1)); + } + } +} + +void Map::_floodFillCollisionElevation(int x, int y, uint collision, uint elevation) { + QList todo; + todo.append(QPoint(x, y)); + while (todo.length()) { + QPoint point = todo.takeAt(0); + x = point.x(); + y = point.y(); + Block *block = getBlock(x, y); + if (block == NULL) { + continue; + } + uint old_coll = block->collision; + uint old_elev = block->elevation; + if (old_coll == collision && old_elev == elevation) { + continue; + } + block->collision = collision; + block->elevation = elevation; + _setBlock(x, y, *block); + if ((block = getBlock(x + 1, y)) && block->collision == old_coll && block->elevation == old_elev) { + todo.append(QPoint(x + 1, y)); + } + if ((block = getBlock(x - 1, y)) && block->collision == old_coll && block->elevation == old_elev) { + todo.append(QPoint(x - 1, y)); + } + if ((block = getBlock(x, y + 1)) && block->collision == old_coll && block->elevation == old_elev) { + todo.append(QPoint(x, y + 1)); + } + if ((block = getBlock(x, y - 1)) && block->collision == old_coll && block->elevation == old_elev) { + todo.append(QPoint(x, y - 1)); + } + } +} + + +void Map::undo() { + Blockdata *commit = history.pop(); + if (commit != NULL) { + blockdata->copyFrom(commit); + } +} + +void Map::redo() { + Blockdata *commit = history.next(); + if (commit != NULL) { + blockdata->copyFrom(commit); + } +} + +void Map::commit() { + Blockdata* commit = blockdata->copy(); + history.push(commit); +} + +void Map::setBlock(int x, int y, Block block) { + Block *old_block = getBlock(x, y); + if (old_block && (*old_block) != block) { + _setBlock(x, y, block); + commit(); + } +} + +void Map::floodFill(int x, int y, uint tile) { + Block *block = getBlock(x, y); + if (block && block->tile != tile) { + _floodFill(x, y, tile); + commit(); + } +} + +void Map::floodFillCollision(int x, int y, uint collision) { + Block *block = getBlock(x, y); + if (block && block->collision != collision) { + _floodFillCollision(x, y, collision); + commit(); + } +} + +void Map::floodFillElevation(int x, int y, uint elevation) { + Block *block = getBlock(x, y); + if (block && block->elevation != elevation) { + _floodFillElevation(x, y, elevation); + commit(); + } +} +void Map::floodFillCollisionElevation(int x, int y, uint collision, uint elevation) { + Block *block = getBlock(x, y); + if (block && (block->collision != collision || block->elevation != elevation)) { + _floodFillCollisionElevation(x, y, collision, elevation); + commit(); + } +} diff --git a/map.h b/map.h new file mode 100755 index 00000000..aad182cb --- /dev/null +++ b/map.h @@ -0,0 +1,165 @@ +#ifndef MAP_H +#define MAP_H + +#include "tileset.h" +#include "blockdata.h" +#include "event.h" + +#include +#include +#include + + +template +class History { +public: + QList history; + int head; + + History() { + head = -1; + } + T pop() { + if (head > 0) { + return history.at(--head); + } + return NULL; + } + T next() { + if (head + 1 < history.length()) { + return history.at(++head); + } + return NULL; + } + void push(T commit) { + while (head + 1 < history.length()) { + history.removeLast(); + } + history.append(commit); + head++; + } +}; + +class Connection { +public: + Connection() { + } +public: + QString direction; + QString offset; + QString map_name; +}; + +class Map : public QObject +{ + Q_OBJECT +public: + explicit Map(QObject *parent = 0); + +public: + QString name; + QString attributes_label; + QString events_label; + QString scripts_label; + QString connections_label; + QString song; + QString index; + QString location; + QString visibility; + QString weather; + QString type; + QString unknown; + QString show_location; + QString battle_scene; + + QString width; + QString height; + QString border_label; + QString blockdata_label; + QString tileset_primary_label; + QString tileset_secondary_label; + + Tileset *tileset_primary; + Tileset *tileset_secondary; + + Blockdata* blockdata; + +public: + int getWidth(); + int getHeight(); + Tileset* getBlockTileset(uint); + Metatile* getMetatile(uint); + QImage getMetatileImage(uint); + QImage getMetatileTile(uint); + QPixmap render(); + QPixmap renderMetatiles(); + + QPixmap renderCollision(); + QImage collision_image; + QPixmap collision_pixmap; + QImage getCollisionMetatileImage(Block); + QImage getElevationMetatileImage(Block); + QImage getCollisionMetatileImage(int); + QImage getElevationMetatileImage(int); + + QPixmap renderCollisionMetatiles(); + QPixmap renderElevationMetatiles(); + void drawSelection(int i, int w, QPainter *painter); + + bool blockChanged(int, Blockdata*); + Blockdata* cached_blockdata; + void cacheBlockdata(); + Blockdata* cached_collision; + void cacheCollision(); + QImage image; + QPixmap pixmap; + QList metatile_images; + int paint_tile; + int paint_collision; + int paint_elevation; + + Block *getBlock(int x, int y); + void setBlock(int x, int y, Block block); + void _setBlock(int x, int y, Block block); + + void floodFill(int x, int y, uint tile); + void _floodFill(int x, int y, uint tile); + void floodFillCollision(int x, int y, uint collision); + void _floodFillCollision(int x, int y, uint collision); + void floodFillElevation(int x, int y, uint elevation); + void _floodFillElevation(int x, int y, uint elevation); + void floodFillCollisionElevation(int x, int y, uint collision, uint elevation); + void _floodFillCollisionElevation(int x, int y, uint collision, uint elevation); + + History history; + void undo(); + void redo(); + void commit(); + + QString object_events_label; + QString warps_label; + QString coord_events_label; + QString bg_events_label; + + QList object_events; + QList warps; + QList coord_events; + QList signs; + QList hidden_items; + + QList connections; + QPixmap renderConnection(Connection); + + QImage border_image; + QPixmap border_pixmap; + Blockdata *border; + Blockdata *cached_border; + QPixmap renderBorder(); + void cacheBorder(); + +signals: + +public slots: +}; + +#endif // MAP_H diff --git a/metatile.cpp b/metatile.cpp new file mode 100755 index 00000000..93d435fa --- /dev/null +++ b/metatile.cpp @@ -0,0 +1,6 @@ +#include "metatile.h" + +Metatile::Metatile() +{ + tiles = new QList; +} diff --git a/metatile.h b/metatile.h new file mode 100755 index 00000000..851d04b7 --- /dev/null +++ b/metatile.h @@ -0,0 +1,16 @@ +#ifndef METATILE_H +#define METATILE_H + +#include "tile.h" +#include + +class Metatile +{ +public: + Metatile(); +public: + QList *tiles; + int attr; +}; + +#endif // METATILE_H diff --git a/pretmap.pro b/pretmap.pro new file mode 100755 index 00000000..3b951a3c --- /dev/null +++ b/pretmap.pro @@ -0,0 +1,43 @@ +#------------------------------------------------- +# +# Project created by QtCreator 2016-08-31T15:19:13 +# +#------------------------------------------------- + +QT += core gui + +greaterThan(QT_MAJOR_VERSION, 4): QT += widgets + +TARGET = pretmap +TEMPLATE = app + + +SOURCES += main.cpp\ + mainwindow.cpp \ + project.cpp \ + asm.cpp \ + map.cpp \ + blockdata.cpp \ + block.cpp \ + tileset.cpp \ + metatile.cpp \ + tile.cpp \ + event.cpp \ + editor.cpp + +HEADERS += mainwindow.h \ + project.h \ + asm.h \ + map.h \ + blockdata.h \ + block.h \ + tileset.h \ + metatile.h \ + tile.h \ + event.h \ + editor.h + +FORMS += mainwindow.ui + +RESOURCES += \ + resources/images.qrc diff --git a/project.cpp b/project.cpp new file mode 100755 index 00000000..93602ca7 --- /dev/null +++ b/project.cpp @@ -0,0 +1,787 @@ +#include "asm.h" +#include "project.h" +#include "tile.h" +#include "tileset.h" +#include "metatile.h" +#include "event.h" + +#include +#include +#include +#include +#include + +Project::Project() +{ + groupNames = new QStringList; + groupedMapNames = new QList; + map_cache = new QMap; + tileset_cache = new QMap; +} + +QString Project::getProjectTitle() { + return root.section('/', -1); +} + +Map* Project::loadMap(QString map_name) { + Map *map = new Map; + + map->name = map_name; + readMapHeader(map); + readMapAttributes(map); + getTilesets(map); + loadBlockdata(map); + loadMapBorder(map); + readMapEvents(map); + loadMapConnections(map); + map->commit(); + + map_cache->insert(map_name, map); + return map; +} + +void Project::loadMapConnections(Map *map) { + map->connections.clear(); + if (!map->connections_label.isNull()) { + QString path = root + QString("/data/maps/%1/connections.s").arg(map->name); + QString text = readTextFile(path); + if (text != NULL) { + QList *commands = parse(text); + QStringList *list = getLabelValues(commands, map->connections_label); + + //// Avoid using this value. It ought to be generated instead. + //int num_connections = list->value(0).toInt(nullptr, 0); + + QString connections_list_label = list->value(1); + QList *connections = getLabelMacros(commands, connections_list_label); + for (QStringList command : *connections) { + QString macro = command.value(0); + if (macro == "connection") { + Connection *connection = new Connection; + connection->direction = command.value(1); + connection->offset = command.value(2); + connection->map_name = command.value(3); + map->connections.append(connection); + } + } + } + } +} + +QList* Project::getLabelMacros(QList *list, QString label) { + bool in_label = false; + QList *new_list = new QList; + for (int i = 0; i < list->length(); i++) { + QStringList params = list->value(i); + QString macro = params.value(0); + if (macro == ".label") { + if (params.value(1) == label) { + in_label = true; + } else if (in_label) { + // If nothing has been read yet, assume the label + // we're looking for is in a stack of labels. + if (new_list->length() > 0) { + break; + } + } + } else if (in_label) { + new_list->append(params); + } + } + return new_list; +} + +// For if you don't care about filtering by macro, +// and just want all values associated with some label. +QStringList* Project::getLabelValues(QList *list, QString label) { + list = getLabelMacros(list, label); + QStringList *values = new QStringList; + for (int i = 0; i < list->length(); i++) { + QStringList params = list->value(i); + QString macro = params.value(0); + // Ignore .align + if (macro == ".align") { + continue; + } + for (int j = 1; j < params.length(); j++) { + values->append(params.value(j)); + } + } + return values; +} + +void Project::readMapHeader(Map* map) { + QString label = map->name; + Asm *parser = new Asm; + + QString header_text = readTextFile(root + "/data/maps/" + label + "/header.s"); + QStringList *header = getLabelValues(parser->parse(header_text), label); + map->attributes_label = header->value(0); + map->events_label = header->value(1); + map->scripts_label = header->value(2); + map->connections_label = header->value(3); + map->song = header->value(4); + map->index = header->value(5); + map->location = header->value(6); + map->visibility = header->value(7); + map->weather = header->value(8); + map->type = header->value(9); + map->unknown = header->value(10); + map->show_location = header->value(11); + map->battle_scene = header->value(12); +} + +void Project::saveMapHeader(Map *map) { + QString label = map->name; + QString header_path = root + "/data/maps/" + label + "/header.s"; + QString text = ""; + text += QString("%1::\n").arg(label); + text += QString("\t.4byte %1\n").arg(map->attributes_label); + text += QString("\t.4byte %1\n").arg(map->events_label); + text += QString("\t.4byte %1\n").arg(map->scripts_label); + text += QString("\t.4byte %1\n").arg(map->connections_label); + text += QString("\t.2byte %1\n").arg(map->song); + text += QString("\t.2byte %1\n").arg(map->index); + text += QString("\t.byte %1\n").arg(map->location); + text += QString("\t.byte %1\n").arg(map->visibility); + text += QString("\t.byte %1\n").arg(map->weather); + text += QString("\t.byte %1\n").arg(map->type); + text += QString("\t.2byte %1\n").arg(map->unknown); + text += QString("\t.byte %1\n").arg(map->show_location); + text += QString("\t.byte %1\n").arg(map->battle_scene); + saveTextFile(header_path, text); +} + +void Project::readMapAttributes(Map* map) { + Asm *parser = new Asm; + + QString assets_text = readTextFile(root + "/data/maps/_assets.s"); + QStringList *attributes = getLabelValues(parser->parse(assets_text), map->attributes_label); + map->width = attributes->value(0); + map->height = attributes->value(1); + map->border_label = attributes->value(2); + map->blockdata_label = attributes->value(3); + map->tileset_primary_label = attributes->value(4); + map->tileset_secondary_label = attributes->value(5); +} + +void Project::getTilesets(Map* map) { + map->tileset_primary = getTileset(map->tileset_primary_label); + map->tileset_secondary = getTileset(map->tileset_secondary_label); +} + +Tileset* Project::loadTileset(QString label) { + Asm *parser = new Asm; + + QString headers_text = readTextFile(root + "/data/tilesets/headers.s"); + QStringList *values = getLabelValues(parser->parse(headers_text), label); + Tileset *tileset = new Tileset; + tileset->name = label; + tileset->is_compressed = values->value(0); + tileset->is_secondary = values->value(1); + tileset->padding = values->value(2); + tileset->tiles_label = values->value(3); + tileset->palettes_label = values->value(4); + tileset->metatiles_label = values->value(5); + tileset->metatile_attrs_label = values->value(6); + tileset->callback_label = values->value(7); + + loadTilesetAssets(tileset); + + tileset_cache->insert(label, tileset); + return tileset; +} + +QString Project::getBlockdataPath(Map* map) { + QString text = readTextFile(root + "/data/maps/_assets.s"); + QStringList *values = getLabelValues(parse(text), map->blockdata_label); + QString path; + if (!values->isEmpty()) { + path = root + "/" + values->value(0).section('"', 1, 1); + } else { + path = root + "/data/maps/" + map->name + "/map.bin"; + } + return path; +} + +QString Project::getMapBorderPath(Map *map) { + QString text = readTextFile(root + "/data/maps/_assets.s"); + QStringList *values = getLabelValues(parse(text), map->border_label); + QString path; + if (!values->isEmpty()) { + path = root + "/" + values->value(0).section('"', 1, 1); + } else { + path = root + "/data/maps/" + map->name + "/border.bin"; + } + return path; +} + +void Project::loadBlockdata(Map* map) { + QString path = getBlockdataPath(map); + map->blockdata = readBlockdata(path); +} + +void Project::loadMapBorder(Map *map) { + QString path = getMapBorderPath(map); + map->border = readBlockdata(path); +} + +void Project::saveBlockdata(Map* map) { + QString path = getBlockdataPath(map); + writeBlockdata(path, map->blockdata); +} + +void Project::writeBlockdata(QString path, Blockdata *blockdata) { + QFile file(path); + if (file.open(QIODevice::WriteOnly)) { + QByteArray data = blockdata->serialize(); + file.write(data); + } +} + +void Project::saveAllMaps() { + QList keys = map_cache->keys(); + for (int i = 0; i < keys.length(); i++) { + QString key = keys.value(i); + Map* map = map_cache->value(key); + saveMap(map); + } +} + +void Project::saveMap(Map *map) { + saveBlockdata(map); + saveMapHeader(map); +} + +void Project::loadTilesetAssets(Tileset* tileset) { + Asm* parser = new Asm; + QString category = (tileset->is_secondary == "TRUE") ? "secondary" : "primary"; + QString dir_path = root + "/data/tilesets/" + category + "/" + tileset->name.replace("gTileset_", "").toLower(); + + QString graphics_text = readTextFile(root + "/data/tilesets/graphics.s"); + QList *graphics = parser->parse(graphics_text); + QStringList *tiles_values = getLabelValues(graphics, tileset->tiles_label); + QStringList *palettes_values = getLabelValues(graphics, tileset->palettes_label); + + QString tiles_path; + if (!tiles_values->isEmpty()) { + tiles_path = root + "/" + tiles_values->value(0).section('"', 1, 1); + } else { + tiles_path = dir_path + "/tiles.4bpp"; + if (tileset->is_compressed == "TRUE") { + tiles_path += ".lz"; + } + } + + QStringList *palette_paths = new QStringList; + if (!palettes_values->isEmpty()) { + for (int i = 0; i < palettes_values->length(); i++) { + QString value = palettes_values->value(i); + palette_paths->append(root + "/" + value.section('"', 1, 1)); + } + } else { + QString palettes_dir_path = dir_path + "/palettes"; + for (int i = 0; i < 16; i++) { + palette_paths->append(palettes_dir_path + "/" + QString("%1").arg(i, 2, 10, QLatin1Char('0')) + ".gbapal"); + } + } + + QString metatiles_path; + QString metatile_attrs_path; + QString metatiles_text = readTextFile(root + "/data/tilesets/metatiles.s"); + QList *metatiles_macros = parser->parse(metatiles_text); + QStringList *metatiles_values = getLabelValues(metatiles_macros, tileset->metatiles_label); + if (!metatiles_values->isEmpty()) { + metatiles_path = root + "/" + metatiles_values->value(0).section('"', 1, 1); + } else { + metatiles_path = dir_path + "/metatiles.bin"; + } + QStringList *metatile_attrs_values = getLabelValues(metatiles_macros, tileset->metatile_attrs_label); + if (!metatile_attrs_values->isEmpty()) { + metatile_attrs_path = root + "/" + metatile_attrs_values->value(0).section('"', 1, 1); + } else { + metatile_attrs_path = dir_path + "/metatile_attributes.bin"; + } + + // tiles + tiles_path = fixGraphicPath(tiles_path); + QImage *image = new QImage(tiles_path); + //image->setColor(0, qRgb(0xff, 0, 0)); // debug + + QList *tiles = new QList; + int w = 8; + int h = 8; + for (int y = 0; y < image->height(); y += h) + for (int x = 0; x < image->width(); x += w) { + QImage tile = image->copy(x, y, w, h); + tiles->append(tile); + } + tileset->tiles = tiles; + + // metatiles + //qDebug() << metatiles_path; + QFile metatiles_file(metatiles_path); + if (metatiles_file.open(QIODevice::ReadOnly)) { + QByteArray data = metatiles_file.readAll(); + int num_metatiles = data.length() / 16; + int num_layers = 2; + QList *metatiles = new QList; + for (int i = 0; i < num_metatiles; i++) { + Metatile *metatile = new Metatile; + int index = i * 16; + for (int j = 0; j < 4 * num_layers; j++) { + uint16_t word = data[index++] & 0xff; + word += (data[index++] & 0xff) << 8; + Tile tile; + tile.tile = word & 0x3ff; + tile.xflip = (word >> 10) & 1; + tile.yflip = (word >> 11) & 1; + tile.palette = (word >> 12) & 0xf; + metatile->tiles->append(tile); + } + metatiles->append(metatile); + } + tileset->metatiles = metatiles; + } + + QFile attrs_file(metatile_attrs_path); + //qDebug() << metatile_attrs_path; + if (attrs_file.open(QIODevice::ReadOnly)) { + QByteArray data = attrs_file.readAll(); + int num_metatiles = data.length() / 2; + for (int i = 0; i < num_metatiles; i++) { + uint16_t word = data[i*2] & 0xff; + word += (data[i*2 + 1] & 0xff) << 8; + tileset->metatiles->value(i)->attr = word; + } + } + + // palettes + QList> *palettes = new QList>; + for (int i = 0; i < palette_paths->length(); i++) { + QString path = palette_paths->value(i); + // the palettes are not compressed. this should never happen. it's only a precaution. + path = path.replace(QRegExp("\\.lz$"), ""); + // TODO default to .pal (JASC-PAL) + // just use .gbapal for now + QFile file(path); + QList palette; + if (file.open(QIODevice::ReadOnly)) { + QByteArray data = file.readAll(); + for (int j = 0; j < 16; j++) { + uint16_t word = data[j*2] & 0xff; + word += (data[j*2 + 1] & 0xff) << 8; + int red = word & 0x1f; + int green = (word >> 5) & 0x1f; + int blue = (word >> 10) & 0x1f; + QRgb color = qRgb(red * 8, green * 8, blue * 8); + palette.prepend(color); + } + } + //qDebug() << path; + palettes->append(palette); + } + tileset->palettes = palettes; +} + +Blockdata* Project::readBlockdata(QString path) { + Blockdata *blockdata = new Blockdata; + //qDebug() << path; + QFile file(path); + if (file.open(QIODevice::ReadOnly)) { + QByteArray data = file.readAll(); + for (int i = 0; (i + 1) < data.length(); i += 2) { + uint16_t word = (data[i] & 0xff) + ((data[i + 1] & 0xff) << 8); + blockdata->addBlock(word); + } + } + return blockdata; +} + +Map* Project::getMap(QString map_name) { + if (map_cache->contains(map_name)) { + return map_cache->value(map_name); + } else { + Map *map = loadMap(map_name); + return map; + } +} + +Tileset* Project::getTileset(QString label) { + if (tileset_cache->contains(label)) { + return tileset_cache->value(label); + } else { + Tileset *tileset = loadTileset(label); + return tileset; + } +} + +QString Project::readTextFile(QString path) { + QFile file(path); + if (!file.open(QIODevice::ReadOnly)) { + //QMessageBox::information(0, "Error", QString("Could not open '%1': ").arg(path) + file.errorString()); + return NULL; + } + QTextStream in(&file); + QString text = ""; + while (!in.atEnd()) { + text += in.readLine() + "\n"; + } + return text; +} + +void Project::saveTextFile(QString path, QString text) { + QFile file(path); + if (file.open(QIODevice::WriteOnly)) { + file.write(text.toUtf8()); + } +} + +void Project::readMapGroups() { + QString text = readTextFile(root + "/data/maps/_groups.s"); + if (text == NULL) { + return; + } + Asm *parser = new Asm; + QList *commands = parser->parse(text); + + bool in_group_pointers = false; + QStringList *groups = new QStringList; + for (int i = 0; i < commands->length(); i++) { + QStringList params = commands->value(i); + QString macro = params.value(0); + if (macro == ".label") { + if (in_group_pointers) { + break; + } + if (params.value(1) == "gMapGroups") { + in_group_pointers = true; + } + } else if (macro == ".4byte") { + if (in_group_pointers) { + for (int j = 1; j < params.length(); j++) { + groups->append(params.value(j)); + } + } + } + } + + QList *maps = new QList; + for (int i = 0; i < groups->length(); i++) { + QStringList *list = new QStringList; + maps->append(list); + } + + int group = -1; + for (int i = 0; i < commands->length(); i++) { + QStringList params = commands->value(i); + QString macro = params.value(0); + if (macro == ".label") { + group = groups->indexOf(params.value(1)); + } else if (macro == ".4byte") { + if (group != -1) { + for (int j = 1; j < params.length(); j++) { + QStringList *list = maps->value(group); + list->append(params.value(j)); + } + } + } + } + + groupNames = groups; + groupedMapNames = maps; +} + +QList* Project::parse(QString text) { + Asm *parser = new Asm; + return parser->parse(text); +} + +QStringList Project::getLocations() { + // TODO + QStringList names; + for (int i = 0; i < 88; i++) { + names.append(QString("%1").arg(i)); + } + return names; +} + +QStringList Project::getVisibilities() { + // TODO + QStringList names; + for (int i = 0; i < 16; i++) { + names.append(QString("%1").arg(i)); + } + return names; +} + +QStringList Project::getWeathers() { + // TODO + QStringList names; + for (int i = 0; i < 16; i++) { + names.append(QString("%1").arg(i)); + } + return names; +} + +QStringList Project::getMapTypes() { + // TODO + QStringList names; + for (int i = 0; i < 16; i++) { + names.append(QString("%1").arg(i)); + } + return names; +} + +QStringList Project::getBattleScenes() { + // TODO + QStringList names; + for (int i = 0; i < 16; i++) { + names.append(QString("%1").arg(i)); + } + return names; +} + +QStringList Project::getSongNames() { + QStringList names; + QString text = readTextFile(root + "/constants/songs.s"); + if (text != NULL) { + QList *commands = parse(text); + for (int i = 0; i < commands->length(); i++) { + QStringList params = commands->value(i); + QString macro = params.value(0); + if (macro == ".equiv") { + names.append(params.value(1)); + } + } + } + return names; +} + +QString Project::getSongName(int value) { + QStringList names; + QString text = readTextFile(root + "/constants/songs.s"); + if (text != NULL) { + QList *commands = parse(text); + for (int i = 0; i < commands->length(); i++) { + QStringList params = commands->value(i); + QString macro = params.value(0); + if (macro == ".equiv") { + if (value == ((QString)(params.value(2))).toInt(nullptr, 0)) { + return params.value(1); + } + } + } + } + return ""; +} + +QMap Project::getMapObjGfxConstants() { + QMap constants; + QString text = readTextFile(root + "/constants/map_object_constants.s"); + if (text != NULL) { + QList *commands = parse(text); + for (int i = 0; i < commands->length(); i++) { + QStringList params = commands->value(i); + QString macro = params.value(0); + if (macro == ".set") { + QString constant = params.value(1); + if (constant.startsWith("MAP_OBJ_GFX_")) { + int value = params.value(2).toInt(nullptr, 0); + constants.insert(constant, value); + } + } + } + } + return constants; +} + +QString Project::fixGraphicPath(QString path) { + path = path.replace(QRegExp("\\.lz$"), ""); + path = path.replace(QRegExp("\\.[1248]bpp$"), ".png"); + return path; +} + +void Project::loadObjectPixmaps(QList objects) { + bool needs_update = false; + for (ObjectEvent *object : objects) { + if (object->pixmap.isNull()) { + needs_update = true; + break; + } + } + if (!needs_update) { + return; + } + + QMap constants = getMapObjGfxConstants(); + + QString pointers_path = root + "/data/graphics/field_objects/map_object_graphics_info_pointers.s"; + QString pointers_text = readTextFile(pointers_path); + if (pointers_text == NULL) { + return; + } + QString info_path = root + "/data/graphics/field_objects/map_object_graphics_info.s"; + QString info_text = readTextFile(info_path); + if (info_text == NULL) { + return; + } + QString pic_path = root + "/data/graphics/field_objects/map_object_pic_tables.s"; + QString pic_text = readTextFile(pic_path); + if (pic_text == NULL) { + return; + } + QString assets_path = root + "/data/graphics/field_objects/map_object_graphics.s"; + QString assets_text = readTextFile(assets_path); + if (assets_text == NULL) { + return; + } + + QStringList *pointers = getLabelValues(parse(pointers_text), "gMapObjectGraphicsInfoPointers"); + QList *info_commands = parse(info_text); + QList *asset_commands = parse(assets_text); + QList *pic_commands = parse(pic_text); + + for (ObjectEvent *object : objects) { + if (!object->pixmap.isNull()) { + continue; + } + int id = constants.value(object->sprite); + QString info_label = pointers->value(id); + QStringList *info = getLabelValues(info_commands, info_label); + QString pic_label = info->value(12); + + QList *pic = getLabelMacros(pic_commands, pic_label); + for (int i = 0; i < pic->length(); i++) { + QStringList command = pic->value(i); + QString macro = command.value(0); + if (macro == "obj_frame_tiles") { + QString label = command.value(1); + QStringList *incbins = getLabelValues(asset_commands, label); + QString path = incbins->value(0).section('"', 1, 1); + path = fixGraphicPath(path); + QPixmap pixmap(root + "/" + path); + object->pixmap = pixmap; + break; + } + } + } +} + +void Project::readMapEvents(Map *map) { + // lazy + QString path = root + QString("/data/maps/events/%1.s").arg(map->name); + QString text = readTextFile(path); + + QStringList *labels = getLabelValues(parse(text), map->events_label); + map->object_events_label = labels->value(0); + map->warps_label = labels->value(1); + map->coord_events_label = labels->value(2); + map->bg_events_label = labels->value(3); + + QList *object_events = getLabelMacros(parse(text), map->object_events_label); + map->object_events.clear(); + for (QStringList command : *object_events) { + if (command.value(0) == "object_event") { + ObjectEvent *object = new ObjectEvent; + // This macro is not fixed as of writing, but it should take fewer args. + bool old_macro = false; + if (command.length() >= 20) { + command.removeAt(19); + command.removeAt(18); + command.removeAt(15); + command.removeAt(13); + command.removeAt(11); + command.removeAt(7); + command.removeAt(5); + command.removeAt(1); // id. not 0, but is just the index in the list of objects + old_macro = true; + } + int i = 1; + object->sprite = command.value(i++); + object->replacement = command.value(i++); + object->x_ = command.value(i++); + object->y_ = command.value(i++); + object->elevation_ = command.value(i++); + object->behavior = command.value(i++); + if (old_macro) { + int radius = command.value(i++).toInt(nullptr, 0); + object->radius_x = QString("%1").arg(radius & 0xf); + object->radius_y = QString("%1").arg((radius >> 4) & 0xf); + } else { + object->radius_x = command.value(i++); + object->radius_y = command.value(i++); + } + object->property = command.value(i++); + object->sight_radius = command.value(i++); + object->script_label = command.value(i++); + object->event_flag = command.value(i++); + + map->object_events.append(object); + } + } + + QList *warps = getLabelMacros(parse(text), map->warps_label); + map->warps.clear(); + for (QStringList command : *warps) { + if (command.value(0) == "warp_def") { + Warp *warp = new Warp; + int i = 1; + warp->x_ = command.value(i++); + warp->y_ = command.value(i++); + warp->elevation_ = command.value(i++); + warp->destination_warp = command.value(i++); + warp->destination_map = command.value(i++); + map->warps.append(warp); + } + } + + QList *coords = getLabelMacros(parse(text), map->coord_events_label); + map->coord_events.clear(); + for (QStringList command : *coords) { + if (command.value(0) == "coord_event") { + CoordEvent *coord = new CoordEvent; + bool old_macro = false; + if (command.length() >= 9) { + command.removeAt(7); + command.removeAt(6); + command.removeAt(4); + old_macro = true; + } + int i = 1; + coord->x_ = command.value(i++); + coord->y_ = command.value(i++); + coord->elevation_ = command.value(i++); + coord->unknown1 = command.value(i++); + coord->script_label = command.value(i++); + map->coord_events.append(coord); + } + } + + QList *bgs = getLabelMacros(parse(text), map->warps_label); + map->hidden_items.clear(); + map->signs.clear(); + for (QStringList command : *bgs) { + if (command.value(0) == "bg_event") { + BGEvent *bg = new BGEvent; + int i = 1; + bg->x_ = command.value(i++); + bg->y_ = command.value(i++); + bg->elevation_ = command.value(i++); + bg->type = command.value(i++); + i++; + if (bg->is_item()) { + HiddenItem *item = new HiddenItem(*bg); + item->item = command.value(i++); + item->unknown5 = command.value(i++); + item->unknown6 = command.value(i++); + map->hidden_items.append(item); + } else { + Sign *sign = new Sign(*bg); + sign->script_label = command.value(i++); + map->signs.append(sign); + } + } + } + +} diff --git a/project.h b/project.h new file mode 100755 index 00000000..de3820f5 --- /dev/null +++ b/project.h @@ -0,0 +1,69 @@ +#ifndef PROJECT_H +#define PROJECT_H + +#include "map.h" +#include "blockdata.h" + +#include +#include + +class Project +{ +public: + Project(); + QString root; + QStringList *groupNames; + QList *groupedMapNames; + + QMap *map_cache; + Map* loadMap(QString); + Map* getMap(QString); + + QMap *tileset_cache; + Tileset* loadTileset(QString); + Tileset* getTileset(QString); + + Blockdata* readBlockdata(QString); + void loadBlockdata(Map*); + + QString readTextFile(QString path); + void saveTextFile(QString path, QString text); + + void readMapGroups(); + QString getProjectTitle(); + + QList* getLabelMacros(QList*, QString); + QStringList* getLabelValues(QList*, QString); + void readMapHeader(Map*); + void readMapAttributes(Map*); + void getTilesets(Map*); + void loadTilesetAssets(Tileset*); + + QString getBlockdataPath(Map*); + void saveBlockdata(Map*); + void writeBlockdata(QString, Blockdata*); + void saveAllMaps(); + void saveMap(Map*); + void saveMapHeader(Map*); + + QList* parse(QString text); + QStringList getSongNames(); + QString getSongName(int); + QStringList getLocations(); + QStringList getVisibilities(); + QStringList getWeathers(); + QStringList getMapTypes(); + QStringList getBattleScenes(); + + void loadObjectPixmaps(QList objects); + QMap getMapObjGfxConstants(); + QString fixGraphicPath(QString path); + + void readMapEvents(Map *map); + void loadMapConnections(Map *map); + + void loadMapBorder(Map *map); + QString getMapBorderPath(Map *map); +}; + +#endif // PROJECT_H diff --git a/resources/icons/folder.ico b/resources/icons/folder.ico new file mode 100755 index 00000000..12525f11 Binary files /dev/null and b/resources/icons/folder.ico differ diff --git a/resources/icons/folder_closed.ico b/resources/icons/folder_closed.ico new file mode 100755 index 00000000..f01179fe Binary files /dev/null and b/resources/icons/folder_closed.ico differ diff --git a/resources/icons/folder_closed_map.ico b/resources/icons/folder_closed_map.ico new file mode 100755 index 00000000..27f9810b Binary files /dev/null and b/resources/icons/folder_closed_map.ico differ diff --git a/resources/icons/folder_image.ico b/resources/icons/folder_image.ico new file mode 100755 index 00000000..6cbbc6db Binary files /dev/null and b/resources/icons/folder_image.ico differ diff --git a/resources/icons/folder_map.ico b/resources/icons/folder_map.ico new file mode 100755 index 00000000..e4e728f3 Binary files /dev/null and b/resources/icons/folder_map.ico differ diff --git a/resources/icons/image.ico b/resources/icons/image.ico new file mode 100755 index 00000000..9cee786f Binary files /dev/null and b/resources/icons/image.ico differ diff --git a/resources/icons/map.ico b/resources/icons/map.ico new file mode 100755 index 00000000..254102bf Binary files /dev/null and b/resources/icons/map.ico differ diff --git a/resources/images.qrc b/resources/images.qrc new file mode 100755 index 00000000..eccdadb9 --- /dev/null +++ b/resources/images.qrc @@ -0,0 +1,11 @@ + + + icons/folder.ico + icons/folder_closed.ico + icons/folder_closed_map.ico + icons/folder_image.ico + icons/folder_map.ico + icons/image.ico + icons/map.ico + + diff --git a/tile.cpp b/tile.cpp new file mode 100755 index 00000000..fd0f42f3 --- /dev/null +++ b/tile.cpp @@ -0,0 +1,6 @@ +#include "tile.h" + +Tile::Tile() +{ + +} diff --git a/tile.h b/tile.h new file mode 100755 index 00000000..f511a7cb --- /dev/null +++ b/tile.h @@ -0,0 +1,16 @@ +#ifndef TILE_H +#define TILE_H + + +class Tile +{ +public: + Tile(); +public: + int tile; + int xflip; + int yflip; + int palette; +}; + +#endif // TILE_H diff --git a/tileset.cpp b/tileset.cpp new file mode 100755 index 00000000..cb781cc6 --- /dev/null +++ b/tileset.cpp @@ -0,0 +1,6 @@ +#include "tileset.h" + +Tileset::Tileset() +{ + +} diff --git a/tileset.h b/tileset.h new file mode 100755 index 00000000..17006f18 --- /dev/null +++ b/tileset.h @@ -0,0 +1,27 @@ +#ifndef TILESET_H +#define TILESET_H + +#include "metatile.h" +#include + +class Tileset +{ +public: + Tileset(); +public: + QString name; + QString is_compressed; + QString is_secondary; + QString padding; + QString tiles_label; + QString palettes_label; + QString metatiles_label; + QString callback_label; + QString metatile_attrs_label; + + QList *tiles; + QList *metatiles; + QList> *palettes; +}; + +#endif // TILESET_H