diff --git a/Components/EGMManager.cpp b/Components/EGMManager.cpp new file mode 100644 index 000000000..d586393e7 --- /dev/null +++ b/Components/EGMManager.cpp @@ -0,0 +1,214 @@ +#include "EGMManager.h" +#include "gmk.h" +#include "gmx.h" +#include "yyp.h" + +#include +#include +#include +#include +#include + +EGMManager::EGMManager() : _egm(nullptr), _repo(nullptr) { + qDebug() << "Intializing git library"; + git_libgit2_init(); +} + +EGMManager::~EGMManager() { + git_tree_free(_git_tree); + git_signature_free(_git_sig); + git_repository_free(_repo); + git_libgit2_shutdown(); +} + +buffers::Project* EGMManager::NewProject() { + _project = std::make_unique(); + QTemporaryDir dir; + dir.setAutoRemove(false); + _rootPath = dir.path(); + InitRepo(); + return _project.get(); +} + +buffers::Project* EGMManager::LoadProject(const QString& fPath) { + QFileInfo fileInfo(fPath); + const QString suffix = fileInfo.suffix(); + + if (suffix == "egm") { + _project = _egm.LoadEGM(fPath.toStdString()); + } else if (suffix == "gm81" || suffix == "gmk" || suffix == "gm6" || suffix == "gmd") { + _project = gmk::LoadGMK(fPath.toStdString(), GetEventData()); + } else if (suffix == "gmx") { + _project = gmx::LoadGMX(fPath.toStdString(), GetEventData()); + } else if (suffix == "yyp") { + _project = yyp::LoadYYP(fPath.toStdString(), GetEventData()); + } + + return _project.get(); +} + +buffers::Game* EGMManager::GetGame() { return _project->mutable_game(); } + +bool EGMManager::LoadEventData(const QString& fPath) { + QFile f(fPath); + if (f.exists()) { + _event_data = std::make_unique(ParseEventFile(fPath.toStdString())); + } else { + qDebug() << "Error: Failed to load events file. Loading internal events.ey."; + QFile internal_events(":/events.ey"); + internal_events.open(QIODevice::ReadOnly | QFile::Text); + std::stringstream ss; + ss << internal_events.readAll().toStdString(); + _event_data = std::make_unique(ParseEventFile(ss)); + } + + _egm = egm::EGM(_event_data.get()); + + return _event_data->events().size() > 0; +} + +EventData* EGMManager::GetEventData() { return _event_data.get(); } + +git_commit* EGMManager::GitLookUp(const git_oid* id) { + git_commit* commit = nullptr; + int error = git_commit_lookup(&commit, _repo, id); + if (error != 0) GitError(); + return commit; +} + +bool EGMManager::InitRepo() { + qDebug() << "Intializing git repository at: " << _rootPath; + git_repository_init_options opts = GIT_REPOSITORY_INIT_OPTIONS_INIT; + opts.description = "ENIGMA Game"; + int error = git_repository_init_ext(&_repo, _rootPath.toStdString().c_str(), &opts); + if (error != 0) { + GitError(); + return false; + } + + error = git_signature_default(&_git_sig, _repo); + if (error != 0) { + GitError(); + return false; + } + + error = git_repository_index(&_git_index, _repo); + if (error != 0) { + GitError(); + return false; + } + error = git_index_write_tree(&_git_tree_id, _git_index); + if (error != 0) { + GitError(); + return false; + } + error = git_tree_lookup(&_git_tree, _repo, &_git_tree_id); + if (error != 0) { + GitError(); + return false; + } + error = git_commit_create_v(&_git_commit_id, _repo, "HEAD", _git_sig, _git_sig, NULL, "Initial commit", _git_tree, 0); + if (error != 0) { + GitError(); + return false; + } + + return CreateBranch("RadialGM") && Checkout("RadialGM"); +} + +bool EGMManager::CreateBranch(const QString& branchName) { + qDebug() << "Creating git branch: " << branchName; + git_commit* c = GitLookUp(&_git_commit_id); + git_reference* ref = nullptr; + int error = git_branch_create(&ref, _repo, branchName.toStdString().c_str(), c, 0); + git_reference_free(ref); + if (error != 0) { + GitError(); + return false; + } + return true; +} + +bool EGMManager::Checkout(const QString& ref) { + qDebug() << "Checking out git reference: " << ref; + int error = git_repository_set_head(_repo, ref.toStdString().c_str()); + if (error != 0) { + GitError(); + return false; + } + return true; +} + +/*bool EGMManager::CheckoutFile(const QString& ref, const QString& file) { +}*/ + +bool EGMManager::AddFile(const QString& file) { + qDebug() << "Adding file to EGM: " << file; + int error = git_index_add_bypath(_git_index, file.toStdString().c_str()); + if (error != 0) { + GitError(); + return false; + } + return true; +} + +bool EGMManager::MoveFile(const QString& file, const QString& newPath) { return false; } + +bool EGMManager::RemoveFile(const QString& file) { + qDebug() << "Removing file from EGM: " << file; + int error = git_index_remove_bypath(_git_index, file.toStdString().c_str()); + if (error != 0) { + GitError(); + return false; + } + return true; +} + +bool EGMManager::RemoveDir(const QString& dir) { + qDebug() << "Removing directory from EGM: " << dir; + int error = git_index_remove_directory(_git_index, dir.toStdString().c_str(), 0); + if (error != 0) { + GitError(); + return false; + } + return true; +} + +bool EGMManager::CommitChanges(const QString& message) { + qDebug() << "Commiting changes"; + int error = git_commit_create_v(&_git_commit_id, _repo, "HEAD", _git_sig, _git_sig, NULL, + message.toStdString().c_str(), _git_tree, 0); + if (error != 0) { + GitError(); + return false; + } + return true; +} + +/*bool EGMManager::AddRemote(const QString& url) { + +} + +bool EGMManager::ChangeRemote(unsigned index, const QString& url) { + +} + +bool EGMManager::RemoveRemote(unsigned index) { + +} + +const QStringList& EGMManager::GetRemotes() const { + +} + +bool EGMManager::PushChanges() { + +}*/ + +bool EGMManager::Save(const QString& fPath) {} + +void EGMManager::GitError() { + const git_error* e = git_error_last(); + QErrorMessage errorDialog; + errorDialog.showMessage(QString("Git Error: ") + e->klass + "\n" + e->message); +} diff --git a/Components/EGMManager.h b/Components/EGMManager.h new file mode 100644 index 000000000..ff84094da --- /dev/null +++ b/Components/EGMManager.h @@ -0,0 +1,60 @@ +#ifndef EGMMANAGER_H +#define EGMMANAGER_H + +#include "event_reader/event_parser.h" +#include "egm.h" + +#include +#include +#include + +#include + +class EGMManager : public QObject { + Q_OBJECT +public: + EGMManager(); + ~EGMManager(); + buffers::Project* NewProject(); + buffers::Project* LoadProject(const QString& fPath); + buffers::Game *GetGame(); + bool LoadEventData(const QString& fPath); + EventData* GetEventData(); + bool InitRepo(); + git_commit* GitLookUp(const git_oid* id); + bool CreateBranch(const QString& branchName); + bool Checkout(const QString& ref); + //bool CheckoutFile(const QString& ref, const QString& file); + bool AddFile(const QString& file); + bool MoveFile(const QString& file, const QString& newPath); + bool RemoveFile(const QString& file); + bool RemoveDir(const QString& dir); + bool CommitChanges(const QString& message); + /*bool AddRemote(const QString& url); + bool ChangeRemote(unsigned index, const QString& url); + bool RemoveRemote(unsigned index); + const QStringList& GetRemotes() const; + bool PushChanges();*/ + +signals: + QStringList FilesEditedExternally(); + QStringList ConflictedFilesDetected(); + +public slots: + void GitError(); + bool Save(const QString& fPath); + +protected: + std::unique_ptr _project; + egm::EGM _egm; + QStringList _remoteURLs; + std::unique_ptr _event_data; + QString _rootPath; + git_repository* _repo; + git_signature* _git_sig; + git_index* _git_index; + git_oid _git_tree_id, _git_commit_id; + git_tree* _git_tree; +}; + +#endif // EGMMANAGER_H diff --git a/MainWindow.cpp b/MainWindow.cpp index fd57d8749..e44f0c8b8 100644 --- a/MainWindow.cpp +++ b/MainWindow.cpp @@ -25,26 +25,22 @@ #include "Plugins/ServerPlugin.h" #endif -#include "gmk.h" -#include "gmx.h" -#include "yyp.h" - #include +#include #include #include -#include -#include #undef GetMessage -QList MainWindow::EnigmaSearchPaths = {QDir::currentPath(), "./enigma-dev", "../enigma-dev", "../RadialGM/Submodules/enigma-dev"}; +QList MainWindow::EnigmaSearchPaths = {QDir::currentPath(), "./enigma-dev", "../enigma-dev", + "../RadialGM/Submodules/enigma-dev"}; QFileInfo MainWindow::EnigmaRoot = MainWindow::getEnigmaRoot(); QList MainWindow::systemCache; MainWindow *MainWindow::_instance = nullptr; +EGMManager MainWindow::egmManager; QScopedPointer MainWindow::resourceMap; QScopedPointer MainWindow::treeModel; -std::unique_ptr MainWindow::_event_data; static QTextEdit *diagnosticTextEdit = nullptr; static QAction *toggleDiagnosticsAction = nullptr; @@ -94,27 +90,15 @@ QFileInfo MainWindow::getEnigmaRoot() { auto entryList = dir.entryInfoList(QStringList({"ENIGMAsystem"}), filters, QDir::SortFlag::NoSort); if (!entryList.empty()) { EnigmaRoot = entryList.first(); - break; + break; } } return EnigmaRoot; } -MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), _ui(new Ui::MainWindow), egm(nullptr) { - - if (!EnigmaRoot.filePath().isEmpty()) { - _event_data = std::make_unique(ParseEventFile((EnigmaRoot.absolutePath() + "/events.ey").toStdString())); - } else { - qDebug() << "Error: Failed to locate ENIGMA sources. Loading internal events.ey.\n" << "Search Paths:\n" << MainWindow::EnigmaSearchPaths; - QFile internal_events(":/events.ey"); - internal_events.open(QIODevice::ReadOnly | QFile::Text); - std::stringstream ss; - ss << internal_events.readAll().toStdString(); - _event_data = std::make_unique(ParseEventFile(ss)); - } - - egm = egm::EGM(_event_data.get()); +MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), _ui(new Ui::MainWindow) { + egmManager.LoadEventData(EnigmaRoot.absolutePath() + "/events.ey"); ArtManager::Init(); @@ -126,6 +110,7 @@ MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), _ui(new Ui::MainW setCorner(Qt::BottomRightCorner, Qt::RightDockWidgetArea); _ui->setupUi(this); + this->tabifyDockWidget(_ui->outputDockWidget, _ui->gitDockWidget); QToolBar *outputTB = new QToolBar(this); outputTB->setIconSize(QSize(24, 24)); @@ -188,7 +173,10 @@ MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), _ui(new Ui::MainW openNewProject(); } -MainWindow::~MainWindow() { diagnosticTextEdit = nullptr; delete _ui; } +MainWindow::~MainWindow() { + diagnosticTextEdit = nullptr; + delete _ui; +} void MainWindow::setCurrentConfig(const buffers::resources::Settings &settings) { emit _instance->CurrentConfigChanged(settings); @@ -293,9 +281,9 @@ void MainWindow::updateWindowMenu() { numberString = numberString.insert(numberString.length() - 1, '&'); QString text = tr("%1 %2").arg(numberString).arg(windowTitle); - QAction *action = _ui->menuWindow->addAction( - mdiSubWindow->windowIcon(), text, mdiSubWindow, - [this, mdiSubWindow]() { _ui->mdiArea->setActiveSubWindow(mdiSubWindow); }); + QAction *action = + _ui->menuWindow->addAction(mdiSubWindow->windowIcon(), text, mdiSubWindow, + [this, mdiSubWindow]() { _ui->mdiArea->setActiveSubWindow(mdiSubWindow); }); windowActions.append(action); action->setCheckable(true); action->setChecked(mdiSubWindow == _ui->mdiArea->activeSubWindow()); @@ -303,19 +291,7 @@ void MainWindow::updateWindowMenu() { } void MainWindow::openFile(QString fName) { - QFileInfo fileInfo(fName); - const QString suffix = fileInfo.suffix(); - - std::unique_ptr loadedProject = nullptr; - if (suffix == "egm") { - loadedProject = egm.LoadEGM(fName.toStdString()); - } else if (suffix == "gm81" || suffix == "gmk" || suffix == "gm6" || suffix == "gmd") { - loadedProject = gmk::LoadGMK(fName.toStdString(), _event_data.get()); - } else if (suffix == "gmx") { - loadedProject = gmx::LoadGMX(fName.toStdString(), _event_data.get()); - } else if (suffix == "yyp") { - loadedProject = yyp::LoadYYP(fName.toStdString(), _event_data.get()); - } + buffers::Project* loadedProject = egmManager.LoadProject(fName); if (!loadedProject) { QMessageBox::warning(this, tr("Failed To Open Project"), tr("There was a problem loading the project: ") + fName, @@ -323,14 +299,14 @@ void MainWindow::openFile(QString fName) { return; } - MainWindow::setWindowTitle(fileInfo.fileName() + " - ENIGMA"); + MainWindow::setWindowTitle(fName + " - ENIGMA"); _recentFiles->prependFile(fName); - openProject(std::move(loadedProject)); + openProject(loadedProject); } void MainWindow::openNewProject() { MainWindow::setWindowTitle(tr(" - ENIGMA")); - auto newProject = std::unique_ptr(new buffers::Project()); + auto newProject = egmManager.NewProject(); auto *root = newProject->mutable_game()->mutable_root(); QList defaultGroups = {tr("Sprites"), tr("Sounds"), tr("Backgrounds"), tr("Paths"), tr("Scripts"), tr("Shaders"), tr("Fonts"), tr("Timelines"), @@ -340,17 +316,15 @@ void MainWindow::openNewProject() { groupNode->set_folder(true); groupNode->set_name(groupName.toStdString()); } - openProject(std::move(newProject)); + openProject(newProject); } -void MainWindow::openProject(std::unique_ptr openedProject) { +void MainWindow::openProject(buffers::Project* openedProject) { this->_ui->mdiArea->closeAllSubWindows(); ArtManager::clearCache(); - _project = std::move(openedProject); - - resourceMap.reset(new ResourceModelMap(_project->mutable_game()->mutable_root(), nullptr)); - treeModel.reset(new TreeModel(_project->mutable_game()->mutable_root(), resourceMap.get(), nullptr)); + resourceMap.reset(new ResourceModelMap(openedProject->mutable_game()->mutable_root(), nullptr)); + treeModel.reset(new TreeModel(openedProject->mutable_game()->mutable_root(), resourceMap.get(), nullptr)); _ui->treeView->setModel(treeModel.get()); treeModel->connect(treeModel.get(), &TreeModel::ResourceRenamed, resourceMap.get(), @@ -404,7 +378,9 @@ void MainWindow::on_actionCloseOthers_triggered() { } } -void MainWindow::on_actionToggleTabbedView_triggered() { this->setTabbedMode(_ui->actionToggleTabbedView->isChecked()); } +void MainWindow::on_actionToggleTabbedView_triggered() { + this->setTabbedMode(_ui->actionToggleTabbedView->isChecked()); +} void MainWindow::on_actionNext_triggered() { _ui->mdiArea->activateNextSubWindow(); } @@ -434,8 +410,7 @@ void MainWindow::on_actionExploreENIGMA_triggered() { QDesktopServices::openUrl( void MainWindow::on_actionAbout_triggered() { QMessageBox aboutBox(QMessageBox::Information, tr("About"), - tr("ENIGMA is a free, open-source, and cross-platform game engine."), - QMessageBox::Ok, this); + tr("ENIGMA is a free, open-source, and cross-platform game engine."), QMessageBox::Ok, this); QAbstractButton *aboutQtButton = aboutBox.addButton(tr("About Qt"), QMessageBox::HelpRole); aboutBox.exec(); @@ -576,12 +551,9 @@ void MainWindow::on_actionDelete_triggered() { selectedNames += (node == *selectedNodes.begin() ? "" : ", ") + QString::fromStdString(node->name()); } - QMessageBox mb( - QMessageBox::Icon::Question, - tr("Delete Resources"), - tr("Do you want to delete the selected resources from the project?"), - QMessageBox::Yes | QMessageBox::No, this - ); + QMessageBox mb(QMessageBox::Icon::Question, tr("Delete Resources"), + tr("Do you want to delete the selected resources from the project?"), + QMessageBox::Yes | QMessageBox::No, this); mb.setDetailedText(selectedNames); int ret = mb.exec(); if (ret != QMessageBox::Yes) return; diff --git a/MainWindow.h b/MainWindow.h index 1c7353206..a372a4db8 100644 --- a/MainWindow.h +++ b/MainWindow.h @@ -6,19 +6,18 @@ #include "Models/TreeModel.h" class MainWindow; +#include "Components/EGMManager.h" #include "Components/RecentFiles.h" #include "project.pb.h" #include "server.pb.h" -#include "event_reader/event_parser.h" -#include "egm.h" +#include #include #include #include #include #include -#include namespace Ui { class MainWindow; @@ -27,6 +26,21 @@ class MainWindow; class MainWindow : public QMainWindow { Q_OBJECT + private: + void closeEvent(QCloseEvent *event) override; + + static MainWindow *_instance; + static EGMManager egmManager; + QHash _subWindows; + Ui::MainWindow *_ui; + QPointer _recentFiles; + + void openSubWindow(buffers::TreeNode *item); + void readSettings(); + void writeSettings(); + void setTabbedMode(bool enabled); + static QFileInfo getEnigmaRoot(); + public: static QScopedPointer resourceMap; static QScopedPointer treeModel; @@ -34,12 +48,12 @@ class MainWindow : public QMainWindow { explicit MainWindow(QWidget *parent); ~MainWindow(); - void openProject(std::unique_ptr openedProject); - buffers::Game *Game() const { return this->_project->mutable_game(); } + void openProject(buffers::Project* openedProject); + buffers::Game *Game() const { return egmManager.GetGame(); } + static EventData *GetEventData() { return egmManager.GetEventData(); } static QList EnigmaSearchPaths; static QFileInfo EnigmaRoot; - static EventData* GetEventData() { return _event_data.get(); } signals: void CurrentConfigChanged(const buffers::resources::Settings &settings); @@ -101,27 +115,6 @@ class MainWindow : public QMainWindow { void on_treeView_doubleClicked(const QModelIndex &index); void on_treeView_customContextMenuRequested(const QPoint &pos); - - private: - void closeEvent(QCloseEvent *event) override; - - static MainWindow *_instance; - - QHash _subWindows; - - Ui::MainWindow *_ui; - - std::unique_ptr _project; - QPointer _recentFiles; - - static std::unique_ptr _event_data; - egm::EGM egm; - - void openSubWindow(buffers::TreeNode *item); - void readSettings(); - void writeSettings(); - void setTabbedMode(bool enabled); - static QFileInfo getEnigmaRoot(); }; #endif // MAINWINDOW_H diff --git a/MainWindow.ui b/MainWindow.ui index b8926488a..e5f98ae48 100644 --- a/MainWindow.ui +++ b/MainWindow.ui @@ -100,7 +100,7 @@ 0 0 1200 - 31 + 21 @@ -497,6 +497,155 @@ + + + Git + + + 8 + + + + + 4 + + + 4 + + + 4 + + + 4 + + + 4 + + + + + + + + obj_playerchanged + + + + :/actions/edit.png:/actions/edit.png + + + + + spr_deleted + + + + :/actions/delete.png:/actions/delete.png + + + + + spr_added + + + + :/actions/add.png:/actions/add.png + + + + + + + + 24 + + + + + + + + + true + + + + 1 + + + + + [Working Copy] + + + + :/actions/tag.png:/actions/tag.png + + + + + Automatic Commit + + + + + Automatic Commit + + + + Adds the player + + + + + + First commit + + + + + + + + + + ... + + + + :/actions/add.png:/actions/add.png + + + + + + + ... + + + + :/actions/find.png:/actions/find.png + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + diff --git a/RadialGM.pro b/RadialGM.pro index c20f30c0c..4e08e70c2 100644 --- a/RadialGM.pro +++ b/RadialGM.pro @@ -65,9 +65,11 @@ LIBS += -L$$PWD/Submodules/enigma-dev/CommandLine/libEGM/ \ -L$$PWD/Submodules/enigma-dev/ \ -lProtocols \ -lpugixml \ - -lgrpc++ + -lgrpc++ \ + -lgit2 SOURCES += \ + Components/EGMManager.cpp \ Dialogs/EventArgumentsDialog.cpp \ Dialogs/TimelineChangeMoment.cpp \ Editors/IncludeEditor.cpp \ @@ -117,6 +119,7 @@ SOURCES += \ Models/TreeSortFilterProxyModel.cpp HEADERS += \ + Components/EGMManager.h \ Dialogs/EventArgumentsDialog.h \ Dialogs/TimelineChangeMoment.h \ Editors/IncludeEditor.h \