~saiko/mcrestool

59b49b5524fad34efcd72d1e3a9f1e46334f9d82 — 2xsaiko 5 months ago 03bf98f
some cleanup
M .gitignore => .gitignore +3 -0
@@ 2,3 2,6 @@ build/

.idea/
cmake-build-debug/

# testing! ;)
*.jar
\ No newline at end of file

M CMakeLists.txt => CMakeLists.txt +32 -8
@@ 3,12 3,21 @@ project(mcrestool)

set(CMAKE_CXX_STANDARD_REQUIRED ON)

set(CMAKE_INCLUDE_CURRENT_DIR ON)
set(CMAKE_AUTOMOC ON)
set(CMAKE_AUTOUIC ON)

find_package(Qt5Core CONFIG REQUIRED)
find_package(Qt5Widgets CONFIG REQUIRED)
find_package(ECM REQUIRED NO_MODULE)
set(CMAKE_MODULE_PATH ${ECM_MODULE_PATH})

include(KDEInstallDirs NO_POLICY_SCOPE)
include(KDECompilerSettings NO_POLICY_SCOPE)
include(KDECMakeSettings NO_POLICY_SCOPE)

find_package(Qt5 CONFIG REQUIRED
        Core
        Widgets)

find_package(KF5 REQUIRED
        Archive)

set(mcrestool_SRC
        src/main.cpp


@@ 18,14 27,29 @@ set(mcrestool_SRC
        src/model/restree.cpp src/model/restree.h
        src/ui/mainwin.cpp src/ui/mainwin.h
        src/ui/langtblw.cpp src/ui/langtblw.h
        src/ui/itembtn.cpp src/ui/itembtn.h src/ui/recpedtw.cpp src/ui/recpedtw.h src/ui/recpshcr.cpp src/ui/recpshcr.h src/ui/rcpedext.cpp src/ui/rcpedext.h src/util.h src/fs/langfile.cpp src/fs/langfile.h src/result.h src/except.h)
        src/ui/itembtn.cpp src/ui/itembtn.h
        src/ui/recpedtw.cpp src/ui/recpedtw.h
        src/ui/recpshcr.cpp src/ui/recpshcr.h
        src/ui/rcpedext.cpp src/ui/rcpedext.h
        src/util.h
        src/fs/langfile.cpp src/fs/langfile.h
        src/result.h
        src/except.h
        src/ui/recpsmel.cpp
        src/ui/recpsmel.h
        src/model/restritm.cpp src/model/restritm.h
        src/project/projsrc.cpp src/project/projsrc.h
        src/fs/datasrc.cpp src/fs/datasrc.h
        src/fs/dirdtsrc.cpp src/fs/dirdtsrc.h
        src/fs/arcdtsrc.cpp src/fs/arcdtsrc.h src/project/datapack.cpp src/project/datapack.h src/project/respack.cpp src/project/respack.h src/project/resdom.cpp src/project/resdom.h)

add_executable(mcrestool ${mcrestool_SRC})

set_property(TARGET mcrestool PROPERTY CXX_STANDARD 20)

target_link_libraries(mcrestool Qt5::Core)
target_link_libraries(mcrestool Qt5::Widgets)
#target_link_libraries(mcrestool quazip)
target_link_libraries(mcrestool
        Qt5::Core
        Qt5::Widgets
        KF5::Archive)

install(TARGETS mcrestool DESTINATION bin)

A src/fs/arcdtsrc.cpp => src/fs/arcdtsrc.cpp +63 -0
@@ 0,0 1,63 @@
#include <QDir>
#include "src/util.h"
#include "arcdtsrc.h"

ArchiveDataSource::ArchiveDataSource(KArchive* archive, QObject* parent) :
    DataSource(parent),
    archive(archive) {}

ArchiveDataSource::~ArchiveDataSource() {
    delete archive;
}

const KArchiveEntry* ArchiveDataSource::find_entry(const QString& path) {
    QString clean_path = QDir::cleanPath("/" + path);
    QStringList parts = clean_path.split('/');
    const KArchiveDirectory* cd = archive->directory();
    const KArchiveEntry* entry = nullptr;

    auto iterator = parts.begin() + 1; // skip empty first path (to the left of the initial /)
    while (iterator != parts.end()) {
        entry = cd->entry(*iterator);
        if (!entry) return nullptr;

        if (entry->isDirectory()) {
            if (!(cd = dynamic_cast<const KArchiveDirectory*>(entry))) {
                unreachable();
            }
        }
        iterator += 1;
    }

    return entry;
}

QIODevice* ArchiveDataSource::file(const QString& path) {
    if (const auto* file = dynamic_cast<const KArchiveFile*>(find_entry(path))) {
        QIODevice* dev = file->createDevice();
        dev->setParent(this);
        return dev;
    }

    return nullptr;
}

QStringList ArchiveDataSource::list_dir(const QString& path) {
    if (const auto* dir = dynamic_cast<const KArchiveDirectory*>(find_entry(path))) {
        return dir->entries();
    }

    return QStringList();
}

bool ArchiveDataSource::read_only() {
    return true;
}

bool ArchiveDataSource::open(QIODevice::OpenMode mode) {
    return archive->open(mode);
}

void ArchiveDataSource::close() {
    archive->close();
}

A src/fs/arcdtsrc.h => src/fs/arcdtsrc.h +31 -0
@@ 0,0 1,31 @@
#ifndef MCRESTOOL_ARCDTSRC_H
#define MCRESTOOL_ARCDTSRC_H

#include "datasrc.h"
#include <KArchive>

class ArchiveDataSource : public DataSource {
Q_OBJECT

public:
    explicit ArchiveDataSource(KArchive* archive, QObject* parent = nullptr);

    ~ArchiveDataSource() override;

    bool open(QIODevice::OpenMode mode) override;

    QIODevice* file(const QString& path) override;

    QStringList list_dir(const QString& path) override;

    bool read_only() override;

    void close() override;

private:
    KArchive* archive;

    const KArchiveEntry* find_entry(const QString& path);
};

#endif //MCRESTOOL_ARCDTSRC_H

A src/fs/datasrc.cpp => src/fs/datasrc.cpp +3 -0
@@ 0,0 1,3 @@
#include "datasrc.h"

DataSource::DataSource(QObject* parent) : QObject(parent) {}

A src/fs/datasrc.h => src/fs/datasrc.h +25 -0
@@ 0,0 1,25 @@
#ifndef MCRESTOOL_DATASRC_H
#define MCRESTOOL_DATASRC_H

#include <QIODevice>

class DataSource : public QObject {
Q_OBJECT

protected:
    explicit DataSource(QObject* parent = nullptr);

public:
    virtual QIODevice* file(const QString& path) = 0;

    virtual QStringList list_dir(const QString& path) = 0;

    virtual bool open(QIODevice::OpenMode mode) = 0;

    virtual void close() = 0;

    virtual bool read_only() = 0;

};

#endif //MCRESTOOL_DATASRC_H

A src/fs/dirdtsrc.cpp => src/fs/dirdtsrc.cpp +23 -0
@@ 0,0 1,23 @@
#include <QFile>
#include <QDir>
#include "dirdtsrc.h"

DirDataSource::DirDataSource(const QString& path, QObject* parent) : DataSource(parent), path(QDir::cleanPath(path)) {}

QIODevice* DirDataSource::file(const QString& path) {
    return new QFile(this->path + QDir::cleanPath("/" + path), this);
}

QStringList DirDataSource::list_dir(const QString& path) {
    return QDir(QDir::cleanPath("/" + path)).entryList();
}

bool DirDataSource::read_only() {
    return false;
}

bool DirDataSource::open(QIODevice::OpenMode) {
    return QDir(path).exists();
}

void DirDataSource::close() {}

A src/fs/dirdtsrc.h => src/fs/dirdtsrc.h +27 -0
@@ 0,0 1,27 @@
#ifndef MCRESTOOL_DIRDTSRC_H
#define MCRESTOOL_DIRDTSRC_H

#include "datasrc.h"

class DirDataSource : public DataSource {
Q_OBJECT

public:
    explicit DirDataSource(const QString& path, QObject* parent = nullptr);

    QIODevice* file(const QString& path) override;

    bool open(QIODevice::OpenMode mode) override;

    void close() override;

    QStringList list_dir(const QString& path) override;

private:
    QString path;

    bool read_only() override;

};

#endif //MCRESTOOL_DIRDTSRC_H

M src/fs/langfile.cpp => src/fs/langfile.cpp +3 -3
@@ 1,6 1,6 @@
#include <QtCore/QJsonDocument>
#include <QtCore/QFile>
#include <QtCore/QJsonObject>
#include <QJsonDocument>
#include <QFile>
#include <QJsonObject>
#include "src/util.h"
#include "langfile.h"


M src/fs/langfile.h => src/fs/langfile.h +1 -1
@@ 1,7 1,7 @@
#ifndef MCRESTOOL_LANGFILE_H
#define MCRESTOOL_LANGFILE_H

#include <QtCore/QMap>
#include <QMap>

QMap<QString, QString> load_from_json(const QString& path);


M src/identifr.h => src/identifr.h +1 -1
@@ 1,7 1,7 @@
#ifndef MCRESTOOL_IDENTIFR_H
#define MCRESTOOL_IDENTIFR_H

#include <QtCore/QString>
#include <QString>

class Identifier {
public:

M src/main.cpp => src/main.cpp +7 -1
@@ 1,11 1,17 @@
#include "src/ui/mainwin.h"
#include "src/fs/datasrc.h"
#include "src/fs/arcdtsrc.h"
#include <QApplication>
#include <QDebug>

int main(int argc, char* argv[]) {
    QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
    QCoreApplication::setAttribute(Qt::AA_UseHighDpiPixmaps);

    QApplication app(argc, argv);
    MainWindow w;
    w.center();
    w.show();

    return QApplication::exec();
}
\ No newline at end of file
}

M src/model/langtbl.cpp => src/model/langtbl.cpp +4 -4
@@ 1,7 1,7 @@
#include <QtCore/QDir>
#include <QtCore/QUrl>
#include <QtCore/QSet>
#include <QtCore/QSharedPointer>
#include <QDir>
#include <QUrl>
#include <QSet>
#include <QSharedPointer>
#include "langtbl.h"

using std::optional;

M src/model/langtbl.h => src/model/langtbl.h +3 -3
@@ 1,9 1,9 @@
#ifndef MCRESTOOL_LANGTBL_H
#define MCRESTOOL_LANGTBL_H

#include <QtCore/QObject>
#include <QtCore/QMap>
#include <QtCore/QAbstractTableModel>
#include <QObject>
#include <QMap>
#include <QAbstractTableModel>
#include "src/table.h"

class LanguageTable : public QAbstractTableModel {

M src/model/restree.cpp => src/model/restree.cpp +93 -27
@@ 1,51 1,117 @@
#include "restree.h"

ResourceTree::ResourceTree(QObject* parent) : QAbstractItemModel(parent) {
ResourceTree::ResourceTree(QObject* parent) :
    QAbstractItemModel(parent) {
    root_item = new TreeItem(QString());

    {
        auto mcjar = new TreeItem("minecraft.jar", root_item);
        auto mcnamespace = new TreeItem("minecraft", mcjar);

        auto mcassets = new TreeItem("Assets", mcnamespace);

        mcassets->append_child(new TreeItem("Localization", mcassets));
        mcnamespace->append_child(mcassets);

        auto mcdata = new TreeItem("Data", mcnamespace);

        auto recipes = new TreeItem("Recipes", mcdata);
        recipes->append_child(new TreeItem("recipe_a", recipes));
        recipes->append_child(new TreeItem("recipe_b", recipes));
        recipes->append_child(new TreeItem("recipe_c", recipes));
        recipes->append_child(new TreeItem("recipe_d", recipes));
        mcdata->append_child(recipes);
        mcnamespace->append_child(mcdata);

        mcjar->append_child(mcnamespace);
        root_item->append_child(mcjar);
    }

    {
        auto mcjar = new TreeItem("resources", root_item);
        auto mcnamespace = new TreeItem("rswires", mcjar);

        auto mcassets = new TreeItem("Assets", mcnamespace);

        mcassets->append_child(new TreeItem("Localization", mcassets));
        mcnamespace->append_child(mcassets);

        auto mcdata = new TreeItem("Data", mcnamespace);

        auto recipes = new TreeItem("Recipes", mcdata);
        recipes->append_child(new TreeItem("bundled_cable", recipes));
        recipes->append_child(new TreeItem("red_alloy_wire", recipes));
        recipes->append_child(new TreeItem("white_insulated_wire", recipes));
        mcdata->append_child(recipes);
        mcnamespace->append_child(mcdata);

        mcjar->append_child(mcnamespace);
        root_item->append_child(mcjar);
    }

}

ResourceTree::~ResourceTree() {
    delete root_item;
}

QModelIndex ResourceTree::index(int row, int column, const QModelIndex& parent) const {
    return parent.model()->index(row, column);
    if (!hasIndex(row, column, parent)) return QModelIndex();

    TreeItem* parent_item;

    if (!parent.isValid()) {
        parent_item = root_item;
    } else {
        parent_item = static_cast<TreeItem*>(parent.internalPointer());
    }

    TreeItem* child_item = parent_item->child(row);

    if (!child_item) return QModelIndex();

    return createIndex(row, column, child_item);
}

QModelIndex ResourceTree::parent(const QModelIndex& child) const {
    return QModelIndex();
}
    if (!child.isValid()) return QModelIndex();

int ResourceTree::rowCount(const QModelIndex& parent) const {
    if (parent == QModelIndex())
        return 5;
    else return 0;
}
    auto* item = static_cast<TreeItem*>(child.internalPointer());
    TreeItem* parent = item->parent_item();

int ResourceTree::columnCount(const QModelIndex& parent) const {
    return 1;
    if (parent == root_item) return QModelIndex();

    return createIndex(parent->row(), 0, parent);
}

QVariant ResourceTree::data(const QModelIndex& index, int role) const {
    if (role == Qt::DisplayRole) {
        return "Data!";
    }
    return QVariant();
}
    if (!index.isValid()) return QVariant();
    if (role != Qt::DisplayRole) return QVariant();

    auto* item = static_cast<TreeItem*>(index.internalPointer());

void ResourceTree::add_item(const ResRoot& root) {
    items.append(root);
    return item->text();
}

ResRoot ResRoot::demo() {
    ResRoot root;
Qt::ItemFlags ResourceTree::flags(const QModelIndex& index) const {
    if (!index.isValid())return Qt::NoItemFlags;

    ResDomain& domain = root.add_domain("minecraft");
    domain.create_lang();
    return QAbstractItemModel::flags(index);
}

    return root;
QVariant ResourceTree::headerData(int, Qt::Orientation, int) const {
    return QVariant();
}

ResDomain& ResRoot::add_domain(const QString& domain) {
    return domains[domain];
int ResourceTree::rowCount(const QModelIndex& parent) const {
    if (parent.column() > 0) return 0;

    if (!parent.isValid()) return root_item->child_count();

    auto* parent_item = static_cast<TreeItem*>(parent.internalPointer());
    return parent_item->child_count();
}

void ResDomain::create_lang() {
    lang = true;
int ResourceTree::columnCount(const QModelIndex& parent) const {
    return 1;
}

M src/model/restree.h => src/model/restree.h +12 -30
@@ 1,30 1,9 @@
#ifndef MCRESTOOL_RESTREE_H
#define MCRESTOOL_RESTREE_H

#include <QtCore/QString>
#include <QtCore/QAbstractItemModel>

class ResDomain {
    friend class ResourceTree;

public:
    void create_lang();

private:
    bool lang;
};

class ResRoot {
    friend class ResourceTree;

public:
    static ResRoot demo();

    ResDomain& add_domain(const QString& domain);

private:
    QMap<QString, ResDomain> domains;
};
#include <QString>
#include <QAbstractItemModel>
#include "restritm.h"

class ResourceTree : public QAbstractItemModel {
Q_OBJECT


@@ 32,21 11,24 @@ Q_OBJECT
public:
    explicit ResourceTree(QObject* parent = nullptr);

    void add_item(const ResRoot& root);

private:
    QList<ResRoot> items;
    ~ResourceTree() override;

    QModelIndex index(int row, int column, const QModelIndex& parent) const override;

    QModelIndex parent(const QModelIndex& child) const override;

    QVariant data(const QModelIndex& index, int role) const override;

    Qt::ItemFlags flags(const QModelIndex& index) const override;

    QVariant headerData(int section, Qt::Orientation orientation, int role) const override;

    int rowCount(const QModelIndex& parent) const override;

    int columnCount(const QModelIndex& parent) const override;

    QVariant data(const QModelIndex& index, int role) const override;

private:
    TreeItem* root_item;

};


A src/model/restritm.cpp => src/model/restritm.cpp +34 -0
@@ 0,0 1,34 @@
#include "restritm.h"

TreeItem::TreeItem(const QString& data, TreeItem* parentItem) :
    _item_data(data), _parent_item(parentItem) {}

TreeItem::~TreeItem() {
    qDeleteAll(_child_items);
}

void TreeItem::append_child(TreeItem* child) {
    _child_items.append(child);
}

TreeItem* TreeItem::child(int row) {
    if (row < 0 || row >= _child_items.size()) return nullptr;
    return _child_items.at(row);
}

int TreeItem::child_count() const {
    return _child_items.size();
}

QString TreeItem::text() const {
    return _item_data;
}

int TreeItem::row() const {
    if (!_parent_item) return 0;
    return _parent_item->_child_items.indexOf(const_cast<TreeItem*>(this));
}

TreeItem* TreeItem::parent_item() {
    return _parent_item;
}

A src/model/restritm.h => src/model/restritm.h +30 -0
@@ 0,0 1,30 @@
#ifndef MCRESTOOL_RESTRITM_H
#define MCRESTOOL_RESTRITM_H

#include <QVariant>

class TreeItem {
public:
    explicit TreeItem(const QString& text, TreeItem* parentItem = nullptr);

    ~TreeItem();

    void append_child(TreeItem* child);

    TreeItem* child(int row);

    int child_count() const;

    QString text() const;

    int row() const;

    TreeItem* parent_item();

private:
    QVector<TreeItem*> _child_items;
    QString _item_data;
    TreeItem* _parent_item;
};

#endif //MCRESTOOL_RESTRITM_H

A src/project/datapack.cpp => src/project/datapack.cpp +1 -0
@@ 0,0 1,1 @@
#include "datapack.h"

A src/project/datapack.h => src/project/datapack.h +8 -0
@@ 0,0 1,8 @@
#ifndef MCRESTOOL_DATAPACK_H
#define MCRESTOOL_DATAPACK_H

class DataPack {

};

#endif //MCRESTOOL_DATAPACK_H

A src/project/projsrc.cpp => src/project/projsrc.cpp +9 -0
@@ 0,0 1,9 @@
#include "projsrc.h"

ProjectSource::ProjectSource(DataSource* src, const QString& name, QObject* parent) {

}

bool ProjectSource::read_only() {
    return src->read_only();
}

A src/project/projsrc.h => src/project/projsrc.h +29 -0
@@ 0,0 1,29 @@
#ifndef MCRESTOOL_PROJSRC_H
#define MCRESTOOL_PROJSRC_H

#include "src/fs/datasrc.h"
#include "datapack.h"
#include "respack.h"
#include <QObject>
#include <QMap>

class ProjectSource : public QObject {
Q_OBJECT

public:
    explicit ProjectSource(DataSource* src, const QString& name, QObject* parent = nullptr);


    bool read_only();

private:
    QString name;

    QMap<QString, DataPack> data_packs;
    QMap<QString, ResourcePack> resource_packs;

    DataSource* src;

};

#endif //MCRESTOOL_PROJSRC_H

A src/project/resdom.cpp => src/project/resdom.cpp +1 -0
@@ 0,0 1,1 @@
#include "resdom.h"

A src/project/resdom.h => src/project/resdom.h +20 -0
@@ 0,0 1,20 @@
#ifndef MCRESTOOL_RESDOM_H
#define MCRESTOOL_RESDOM_H

#include <optional>
#include <QString>
#include "datapack.h"
#include "respack.h"

using std::optional;

class ResourceDomain {

private:
    QString domain;
    optional<DataPack> data;
    optional<ResourcePack> resources;

};

#endif //MCRESTOOL_RESDOM_H

A src/project/respack.cpp => src/project/respack.cpp +1 -0
@@ 0,0 1,1 @@
#include "respack.h"

A src/project/respack.h => src/project/respack.h +8 -0
@@ 0,0 1,8 @@
#ifndef MCRESTOOL_RESPACK_H
#define MCRESTOOL_RESPACK_H

class ResourcePack {

};

#endif //MCRESTOOL_RESPACK_H

M src/table.h => src/table.h +3 -3
@@ 1,9 1,9 @@
#ifndef MCRESTOOL_TABLE_H
#define MCRESTOOL_TABLE_H

#include <QtCore/QMap>
#include <QtCore/QSet>
#include <QtCore/QList>
#include <QMap>
#include <QSet>
#include <QList>
#include <optional>

using std::optional;

M src/ui/itembtn.h => src/ui/itembtn.h +2 -2
@@ 1,8 1,8 @@
#ifndef MCRESTOOL_ITEMBTN_H
#define MCRESTOOL_ITEMBTN_H

#include <QtWidgets/QPushButton>
#include <src/identifr.h>
#include <QPushButton>
#include "src/identifr.h"

class ItemButton : public QPushButton {
Q_OBJECT

M src/ui/langtblw.cpp => src/ui/langtblw.cpp +1 -1
@@ 1,4 1,4 @@
#include <QtWidgets/QInputDialog>
#include <QInputDialog>
#include "langtblw.h"
#include "ui_langtblw.h"


M src/ui/langtblw.h => src/ui/langtblw.h +1 -1
@@ 2,7 2,7 @@
#define MCRESTOOL_LANGTBLW_H

#include <QScopedPointer>
#include <QtWidgets/QWidget>
#include <QWidget>
#include "src/model/langtbl.h"

namespace Ui {

M src/ui/mainwin.cpp => src/ui/mainwin.cpp +5 -6
@@ 4,10 4,10 @@
#include "recpedtw.h"
#include <QApplication>
#include <QDesktopWidget>
#include <QtGui/QScreen>
#include <QtWidgets/QFileDialog>
#include <QScreen>
#include <QFileDialog>
#include <QInputDialog>
#include <iostream>
#include <QtWidgets/QInputDialog>

MainWindow::MainWindow(QWidget* parent) : QMainWindow(parent), ui(new Ui::MainWindow) {
    ui->setupUi(this);


@@ 20,6 20,7 @@ MainWindow::MainWindow(QWidget* parent) : QMainWindow(parent), ui(new Ui::MainWi
    connect(ui->res_tree, SIGNAL(visibilityChanged(bool)), this, SLOT(show_resource_tree(bool)));
    connect(ui->action_game_objects, SIGNAL(triggered(bool)), this, SLOT(show_game_objects(bool)));
    connect(ui->game_objects, SIGNAL(visibilityChanged(bool)), this, SLOT(show_game_objects(bool)));
    connect(ui->action_about_qt, &QAction::triggered, &QApplication::aboutQt);

    auto* ltw = new LanguageTableWindow(this);
    ui->mdi_area->addSubWindow(ltw);


@@ 30,9 31,7 @@ MainWindow::MainWindow(QWidget* parent) : QMainWindow(parent), ui(new Ui::MainWi
    connect(ui->action_insert_language, &QAction::triggered, ltw, &LanguageTableWindow::add_language);
    connect(ui->action_insert_translation_key, &QAction::triggered, ltw, &LanguageTableWindow::add_locale_key);

//    rt.add_item(ResRoot::demo());

//    ui->res_tree_view->setModel(&rt);
    ui->res_tree_view->setModel(new ResourceTree(this));
}

void MainWindow::center() {

M src/ui/mainwin.h => src/ui/mainwin.h +1 -1
@@ 3,7 3,7 @@

#include <QMainWindow>
#include <QScopedPointer>
#include <src/model/restree.h>
#include "src/model/restree.h"
#include "src/model/langtbl.h"

namespace Ui {

M src/ui/mainwin.ui => src/ui/mainwin.ui +5 -1
@@ 113,7 113,11 @@
   <widget class="QWidget" name="dock_widget_contents">
    <layout class="QVBoxLayout" name="verticalLayout">
     <item>
      <widget class="QTreeView" name="res_tree_view"/>
      <widget class="QTreeView" name="res_tree_view">
       <attribute name="headerVisible">
        <bool>false</bool>
       </attribute>
      </widget>
     </item>
    </layout>
   </widget>

M src/ui/rcpedext.h => src/ui/rcpedext.h +1 -1
@@ 1,7 1,7 @@
#ifndef MCRESTOOL_RCPEDEXT_H
#define MCRESTOOL_RCPEDEXT_H

#include <QtWidgets/QWidget>
#include <QWidget>

class RecipeEditExtensionWidget : public QWidget {
Q_OBJECT

M src/ui/recpedtw.cpp => src/ui/recpedtw.cpp +7 -2
@@ 2,8 2,11 @@
#include "recpedtw.h"
#include "ui_recpedtw.h"
#include "recpshcr.h"
#include "recpsmel.h"

RecipeEditWindow::RecipeEditWindow(QWidget* parent) : QWidget(parent), ui(new Ui::RecipeEditWindow) {
RecipeEditWindow::RecipeEditWindow(QWidget* parent) : QWidget(parent),
                                                      ui(new Ui::RecipeEditWindow),
                                                      extension_widget(nullptr) {
    ui->setupUi(this);

    connect(ui->type_box, SIGNAL(currentIndexChanged(int)), this, SLOT(change_recipe_type(int)));


@@ 14,8 17,10 @@ RecipeEditWindow::RecipeEditWindow(QWidget* parent) : QWidget(parent), ui(new Ui
void RecipeEditWindow::change_recipe_type(RecipeType type) {
    delete extension_widget;

    if (type == SHAPED_CRAFTING) {
    if (type == SHAPED_CRAFTING || type == SHAPELESS_CRAFTING) {
        extension_widget = new ShapedCraftingWidget(this);
    } else if (type == SMELTING || type == CAMPFIRE || type == BLASTING || type == SMOKING) {
        extension_widget = new SmeltingWidget(this);
    } else {
        unimplemented();
    }

M src/ui/recpedtw.h => src/ui/recpedtw.h +1 -1
@@ 1,7 1,7 @@
#ifndef MCRESTOOL_RECPEDTW_H
#define MCRESTOOL_RECPEDTW_H

#include <QtWidgets/QWidget>
#include <QWidget>
#include "rcpedext.h"

namespace Ui {

M src/ui/recpshcr.h => src/ui/recpshcr.h +1 -1
@@ 1,7 1,7 @@
#ifndef MCRESTOOL_RECPSHCR_H
#define MCRESTOOL_RECPSHCR_H

#include <QtWidgets/QWidget>
#include <QWidget>
#include "rcpedext.h"

namespace Ui {

M src/ui/recpshcr.ui => src/ui/recpshcr.ui +54 -16
@@ 6,8 6,8 @@
   <rect>
    <x>0</x>
    <y>0</y>
    <width>400</width>
    <height>293</height>
    <width>301</width>
    <height>173</height>
   </rect>
  </property>
  <property name="contextMenuPolicy">


@@ 166,28 166,66 @@
    </widget>
   </item>
   <item>
    <widget class="ItemButton" name="output_item">
     <property name="sizePolicy">
      <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
       <horstretch>0</horstretch>
       <verstretch>0</verstretch>
      </sizepolicy>
     </property>
    <widget class="QWidget" name="widget" native="true">
     <property name="minimumSize">
      <size>
       <width>40</width>
       <height>40</height>
       <width>56</width>
       <height>124</height>
      </size>
     </property>
     <property name="maximumSize">
      <size>
       <width>40</width>
       <height>40</height>
       <width>56</width>
       <height>124</height>
      </size>
     </property>
     <property name="text">
      <string/>
     </property>
     <widget class="ItemButton" name="output_item">
      <property name="geometry">
       <rect>
        <x>2</x>
        <y>36</y>
        <width>52</width>
        <height>52</height>
       </rect>
      </property>
      <property name="sizePolicy">
       <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
        <horstretch>0</horstretch>
        <verstretch>0</verstretch>
       </sizepolicy>
      </property>
      <property name="minimumSize">
       <size>
        <width>52</width>
        <height>52</height>
       </size>
      </property>
      <property name="maximumSize">
       <size>
        <width>40</width>
        <height>40</height>
       </size>
      </property>
      <property name="text">
       <string/>
      </property>
     </widget>
     <widget class="QSpinBox" name="spinBox">
      <property name="geometry">
       <rect>
        <x>2</x>
        <y>92</y>
        <width>52</width>
        <height>32</height>
       </rect>
      </property>
      <property name="minimum">
       <number>1</number>
      </property>
      <property name="maximum">
       <number>64</number>
      </property>
     </widget>
    </widget>
   </item>
  </layout>

A src/ui/recpsmel.cpp => src/ui/recpsmel.cpp +8 -0
@@ 0,0 1,8 @@
#include "recpsmel.h"
#include "ui_recpsmel.h"

SmeltingWidget::SmeltingWidget(QWidget* parent) : RecipeEditExtensionWidget(parent), ui(new Ui::SmeltingWidget) {
    ui->setupUi(this);
}

SmeltingWidget::~SmeltingWidget() = default;

A src/ui/recpsmel.h => src/ui/recpsmel.h +24 -0
@@ 0,0 1,24 @@
#ifndef MCRESTOOL_RECPSMEL_H
#define MCRESTOOL_RECPSMEL_H

#include <QWidget>
#include "rcpedext.h"

namespace Ui {
    class SmeltingWidget;
}

class SmeltingWidget : public RecipeEditExtensionWidget {
Q_OBJECT

public:
    explicit SmeltingWidget(QWidget* parent = nullptr);

    ~SmeltingWidget() override;

private:
    QScopedPointer<Ui::SmeltingWidget> ui;

};

#endif //MCRESTOOL_RECPSMEL_H

M src/ui/recpsmel.ui => src/ui/recpsmel.ui +161 -4
@@ 1,19 1,176 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
 <class>Form</class>
 <widget class="QWidget" name="Form">
 <class>SmeltingWidget</class>
 <widget class="QWidget" name="SmeltingWidget">
  <property name="geometry">
   <rect>
    <x>0</x>
    <y>0</y>
    <width>400</width>
    <height>308</height>
    <width>315</width>
    <height>229</height>
   </rect>
  </property>
  <property name="windowTitle">
   <string>Smelting</string>
  </property>
  <layout class="QVBoxLayout" name="verticalLayout_2">
   <item>
    <layout class="QHBoxLayout" name="horizontalLayout_2">
     <item>
      <widget class="ItemButton" name="input_item">
       <property name="minimumSize">
        <size>
         <width>40</width>
         <height>40</height>
        </size>
       </property>
       <property name="maximumSize">
        <size>
         <width>40</width>
         <height>40</height>
        </size>
       </property>
       <property name="text">
        <string/>
       </property>
      </widget>
     </item>
     <item>
      <widget class="QWidget" name="widget" native="true">
       <property name="minimumSize">
        <size>
         <width>56</width>
         <height>124</height>
        </size>
       </property>
       <property name="maximumSize">
        <size>
         <width>56</width>
         <height>124</height>
        </size>
       </property>
       <widget class="ItemButton" name="output_item">
        <property name="geometry">
         <rect>
          <x>2</x>
          <y>36</y>
          <width>52</width>
          <height>52</height>
         </rect>
        </property>
        <property name="sizePolicy">
         <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
          <horstretch>0</horstretch>
          <verstretch>0</verstretch>
         </sizepolicy>
        </property>
        <property name="minimumSize">
         <size>
          <width>52</width>
          <height>52</height>
         </size>
        </property>
        <property name="maximumSize">
         <size>
          <width>40</width>
          <height>40</height>
         </size>
        </property>
        <property name="text">
         <string/>
        </property>
       </widget>
       <widget class="QSpinBox" name="spinBox">
        <property name="geometry">
         <rect>
          <x>2</x>
          <y>92</y>
          <width>52</width>
          <height>32</height>
         </rect>
        </property>
        <property name="minimum">
         <number>1</number>
        </property>
        <property name="maximum">
         <number>64</number>
        </property>
       </widget>
      </widget>
     </item>
    </layout>
   </item>
   <item>
    <layout class="QFormLayout" name="formLayout">
     <item row="0" column="1">
      <widget class="QDoubleSpinBox" name="doubleSpinBox">
       <property name="sizePolicy">
        <sizepolicy hsizetype="MinimumExpanding" vsizetype="Fixed">
         <horstretch>0</horstretch>
         <verstretch>0</verstretch>
        </sizepolicy>
       </property>
       <property name="specialValueText">
        <string/>
       </property>
       <property name="suffix">
        <string> XP</string>
       </property>
       <property name="maximum">
        <double>100000.000000000000000</double>
       </property>
      </widget>
     </item>
     <item row="1" column="1">
      <widget class="QSpinBox" name="spinBox_3">
       <property name="sizePolicy">
        <sizepolicy hsizetype="MinimumExpanding" vsizetype="Fixed">
         <horstretch>0</horstretch>
         <verstretch>0</verstretch>
        </sizepolicy>
       </property>
       <property name="specialValueText">
        <string>(unchanged)</string>
       </property>
       <property name="suffix">
        <string> ticks</string>
       </property>
       <property name="minimum">
        <number>-1</number>
       </property>
       <property name="maximum">
        <number>2147483647</number>
       </property>
       <property name="value">
        <number>-1</number>
       </property>
      </widget>
     </item>
     <item row="0" column="0">
      <widget class="QLabel" name="label">
       <property name="text">
        <string>Experience</string>
       </property>
      </widget>
     </item>
     <item row="1" column="0">
      <widget class="QLabel" name="label_2">
       <property name="text">
        <string>Cook Time</string>
       </property>
      </widget>
     </item>
    </layout>
   </item>
  </layout>
 </widget>
 <customwidgets>
  <customwidget>
   <class>ItemButton</class>
   <extends>QPushButton</extends>
   <header>src/ui/itembtn.h</header>
  </customwidget>
 </customwidgets>
 <resources/>
 <connections/>
</ui>

M src/util.h => src/util.h +3 -1
@@ 1,8 1,10 @@
#ifndef MCRESTOOL_UTIL_H
#define MCRESTOOL_UTIL_H

#include <qglobal.h>
#include <QtGlobal>

#define unimplemented() (qt_assert("unimplemented", __FILE__, __LINE__))

#define unreachable() (qt_assert("unreachable", __FILE__, __LINE__))

#endif //MCRESTOOL_UTIL_H