From 5c32172076689226055d1e82d6ad6997d04aab73 Mon Sep 17 00:00:00 2001 From: Jeffrey Pfau Date: Sun, 2 Aug 2015 15:28:53 -0700 Subject: [PATCH 01/19] Qt: Make keymapper present more information, and space things out a bit more --- CHANGES | 1 + res/keymap.qpic | Bin 22895 -> 25382 bytes src/platform/qt/GBAKeyEditor.cpp | 10 +++++----- 3 files changed, 6 insertions(+), 5 deletions(-) diff --git a/CHANGES b/CHANGES index bb108edd9..10d60be18 100644 --- a/CHANGES +++ b/CHANGES @@ -110,6 +110,7 @@ Misc: - GBA Input: Allow axes and buttons to be mapped to the same key - GBA BIOS: Stub out SoundBias - Qt: Gamepads can now have both buttons and analog axes mapped to the same key + - Qt: Increase usability of key mapper 0.2.1: (2015-05-13) Bugfixes: diff --git a/res/keymap.qpic b/res/keymap.qpic index ffb7f8b7d34b14fd240edf7ce42fac58ce594c68..ccdc82a08c7aac6bc404d55805e8323a4947d24b 100644 GIT binary patch literal 25382 zcmd^I33OCN*6xJ?#sw8nQBgn^QDhMu7Z7p**%Ss4>~57M8wu$S5Ry(v3c@0yIIe&S zCt({d0!@oio$v@7-JLty}kg zRgL}o_3GL`KX7~?a7ML&_XKwZ0>Pd5HPI>==od)aD{q3qg9nX_!OZtVKM(gk9IA-l zgRPew7qC?wA`!&zA%kIylo~edn{NWPl?M;DY16c6ad993BSmi2rJWA`+@jM|^Bu+u zez=V^wH^9t^wVhUYMg?D6HLdyP9Ql`2aIb-U+D>XSozr}-OwU*3}Ru>f17uVX&na( zLMMEKkAsEaocCLwI(JoxgM~#3<(xV^%(jI_ksnhoe(^?CmpcZru=BSl5T@H=0Q?L( z{WNgMX@kMSrmar77PI9BJvrNEDkFGgSmf&Ry`k-aO;30hIsLR>7GI!_Hx^}v>(I}` zeSe588rvzq-4qyt^ZE}jtZ4WmSS$?nt z<{e|EExInmJlk>PLZ%H4hHFLIZ`#h~8O*jN=*d~zL0i~yY?)G zqW|-@IQY)k@tJ+zgu?cZ)I6`*r(nMKIQY(^i|0lT*Vbq#QawhWY%x?9B~B$NtflXc z4%t`35cxzX1pPmx7JTMlVSp`Ztv=C;MiWq)xoPU)Yq zys%eZ`X`fXkDPfcW2Rn8^tOxC`WGgXxypHCfNFx%1Vb}M0dj_H&SF{D(N-}Bbwi$<#lnz|Z^(~lHK8@w7XJ?Gbw0?!LU7GF7_F>Z z;b3973P?8qZh4ilpeJYh5rlr*yWPKj^<<~bp%hpc`kjF;q{k9)$Hido_!%s5BKs(8 z7V7Fc@{h7(S{bwLvvL+IIDO4+gPMPf<%PU{SS*a=B9>D?Q~Kj4T(9vP77M}9gEXzR zz+yP|Ja2D;TV7?%%;lRP?Q4Z~1U=iZSV&{jw4AmfZENnh7+bUBhecM|njT2W$)G`z zNO`$ktkj@Xk6BTv-VtWrAF|@S8EHwg^bZt7i+&d;_M0_O;9+67Ivro3MHkdDa%knN zHCFu_G=;PA3tET966=j+1)=}N6qT(7i{b8Ay;{8)$!8&O&1%0A-S%g}zfl`P!tvnw-x*49wH%2wR3 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 L`M>KBY7qDr+y&?8 literal 22895 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 Date: Mon, 3 Aug 2015 20:02:50 -0700 Subject: [PATCH 02/19] Qt: Redo key shortcut editing --- src/platform/qt/KeyEditor.cpp | 69 +++++++--- src/platform/qt/ShortcutController.cpp | 180 ++++++++++++++++--------- src/platform/qt/ShortcutController.h | 24 ++-- src/platform/qt/ShortcutView.cpp | 53 +++----- src/platform/qt/ShortcutView.h | 2 - src/platform/qt/ShortcutView.ui | 50 +------ 6 files changed, 204 insertions(+), 174 deletions(-) diff --git a/src/platform/qt/KeyEditor.cpp b/src/platform/qt/KeyEditor.cpp index ef06d6e41..e97de168e 100644 --- a/src/platform/qt/KeyEditor.cpp +++ b/src/platform/qt/KeyEditor.cpp @@ -7,6 +7,7 @@ #include "GamepadAxisEvent.h" #include "GamepadButtonEvent.h" +#include "ShortcutController.h" #include @@ -20,6 +21,7 @@ KeyEditor::KeyEditor(QWidget* parent) , m_button(false) { setAlignment(Qt::AlignCenter); + setFocusPolicy(Qt::ClickFocus); } void KeyEditor::setValue(int key) { @@ -27,7 +29,11 @@ void KeyEditor::setValue(int key) { if (m_button) { updateButtonText(); } else { - setText(QKeySequence(key).toString(QKeySequence::NativeText)); + if (key < 0) { + setText(tr("---")); + } else { + setText(QKeySequence(key).toString(QKeySequence::NativeText)); + } } emit valueChanged(key); } @@ -71,27 +77,60 @@ QSize KeyEditor::sizeHint() const { void KeyEditor::keyPressEvent(QKeyEvent* event) { if (!m_button) { - setValue(event->key()); + if (m_key < 0) { + m_key = 0; + } + if (ShortcutController::isModifierKey(event->key())) { + switch (event->key()) { + case Qt::Key_Shift: + setValue(m_key | Qt::ShiftModifier); + break; + case Qt::Key_Control: + setValue(m_key | Qt::ControlModifier); + break; + case Qt::Key_Alt: + setValue(m_key | Qt::AltModifier); + break; + case Qt::Key_Meta: + setValue(m_key | Qt::MetaModifier); + break; + default: + setValue(m_key); + } + } else { + setValue(event->key() | (m_key & (Qt::ShiftModifier | Qt::ControlModifier | Qt::AltModifier | Qt::MetaModifier))); + } } event->accept(); } bool KeyEditor::event(QEvent* event) { if (!m_button) { - return QWidget::event(event); - } - if (event->type() == GamepadButtonEvent::Down()) { - setValueButton(static_cast(event)->value()); - event->accept(); - return true; - } - if (event->type() == GamepadAxisEvent::Type()) { - GamepadAxisEvent* gae = static_cast(event); - if (gae->isNew()) { - setValueAxis(gae->axis(), gae->direction()); + if (event->type() == QEvent::KeyPress) { + QKeyEvent* keyEvent = static_cast(event); + if (keyEvent->key() != Qt::Key_Tab && keyEvent->key() != Qt::Key_Backtab) { + return QWidget::event(event); + } + if (!(keyEvent->modifiers() & ~Qt::ShiftModifier)) { + keyPressEvent(keyEvent); + keyEvent->accept(); + return true; + } + } + } else { + if (event->type() == GamepadButtonEvent::Down()) { + setValueButton(static_cast(event)->value()); + event->accept(); + return true; + } + if (event->type() == GamepadAxisEvent::Type()) { + GamepadAxisEvent* gae = static_cast(event); + if (gae->isNew()) { + setValueAxis(gae->axis(), gae->direction()); + } + event->accept(); + return true; } - event->accept(); - return true; } return QWidget::event(event); } diff --git a/src/platform/qt/ShortcutController.cpp b/src/platform/qt/ShortcutController.cpp index 2c0276956..7d1189dcd 100644 --- a/src/platform/qt/ShortcutController.cpp +++ b/src/platform/qt/ShortcutController.cpp @@ -37,7 +37,7 @@ QVariant ShortcutController::data(const QModelIndex& index, int role) const { case 0: return item->visibleName(); case 1: - return item->shortcut().toString(QKeySequence::NativeText); + return QKeySequence(item->shortcut()).toString(QKeySequence::NativeText); case 2: if (item->button() >= 0) { return item->button(); @@ -125,7 +125,7 @@ void ShortcutController::addAction(QMenu* menu, QAction* action, const QString& } void ShortcutController::addFunctions(QMenu* menu, std::function press, std::function release, - const QKeySequence& shortcut, const QString& visibleName, const QString& name) { + int shortcut, const QString& visibleName, const QString& name) { ShortcutItem* smenu = m_menuMap[menu]; if (!smenu) { return; @@ -145,6 +145,11 @@ void ShortcutController::addFunctions(QMenu* menu, std::function press, createIndex(smenu->items().count() - 1, 2, item)); } +void ShortcutController::addFunctions(QMenu* menu, std::function press, std::function release, + const QKeySequence& shortcut, const QString& visibleName, const QString& name) { + addFunctions(menu, press, release, shortcut[0], visibleName, name); +} + void ShortcutController::addMenu(QMenu* menu, QMenu* parentMenu) { ShortcutItem* smenu = m_menuMap[parentMenu]; if (!smenu) { @@ -179,10 +184,10 @@ const ShortcutController::ShortcutItem* ShortcutController::itemAt(const QModelI return static_cast(index.internalPointer()); } -QKeySequence ShortcutController::shortcutAt(const QModelIndex& index) const { +int ShortcutController::shortcutAt(const QModelIndex& index) const { const ShortcutItem* item = itemAt(index); if (!item) { - return QKeySequence(); + return 0; } return item->shortcut(); } @@ -195,7 +200,7 @@ bool ShortcutController::isMenuAt(const QModelIndex& index) const { return item->menu(); } -void ShortcutController::updateKey(const QModelIndex& index, const QKeySequence& keySequence) { +void ShortcutController::updateKey(const QModelIndex& index, int keySequence) { if (!index.isValid()) { return; } @@ -204,23 +209,28 @@ void ShortcutController::updateKey(const QModelIndex& index, const QKeySequence& return; } ShortcutItem* item = itemAt(index); - if (item->functions().first) { - QKeySequence oldShortcut = item->shortcut(); - if (!oldShortcut.isEmpty()) { - m_heldKeys.take(oldShortcut); - } - if (!keySequence.isEmpty()) { - m_heldKeys[keySequence] = item; - } - } - item->setShortcut(keySequence); + updateKey(item, keySequence); if (m_config) { - m_config->setQtOption(item->name(), keySequence.toString(), KEY_SECTION); + m_config->setQtOption(item->name(), QKeySequence(keySequence).toString(), KEY_SECTION); } emit dataChanged(createIndex(index.row(), 0, index.internalPointer()), createIndex(index.row(), 2, index.internalPointer())); } +void ShortcutController::updateKey(ShortcutItem* item, int keySequence) { + int oldShortcut = item->shortcut(); + if (item->functions().first) { + if (oldShortcut > 0) { + m_heldKeys.take(oldShortcut); + } + if (keySequence > 0) { + m_heldKeys[keySequence] = item; + } + } + + item->setShortcut(keySequence); +} + void ShortcutController::updateButton(const QModelIndex& index, int button) { if (!index.isValid()) { return; @@ -286,7 +296,7 @@ void ShortcutController::updateAxis(const QModelIndex& index, int axis, GamepadA } void ShortcutController::clearKey(const QModelIndex& index) { - updateKey(index, QKeySequence()); + updateKey(index, 0); } void ShortcutController::clearButton(const QModelIndex& index) { @@ -299,22 +309,27 @@ bool ShortcutController::eventFilter(QObject*, QEvent* event) { if (keyEvent->isAutoRepeat()) { return false; } - auto item = m_heldKeys.find(keyEventToSequence(keyEvent)); - if (item == m_heldKeys.end()) { - return false; - } - ShortcutItem::Functions pair = item.value()->functions(); - if (event->type() == QEvent::KeyPress) { - if (pair.first) { - pair.first(); - } + int key = keyEvent->key(); + if (!isModifierKey(key)) { + key |= keyEvent->modifiers(); } else { - if (pair.second) { - pair.second(); - } + key = toModifierKey(key | keyEvent->modifiers()); + } + auto item = m_heldKeys.find(key); + if (item != m_heldKeys.end()) { + ShortcutItem::Functions pair = item.value()->functions(); + if (event->type() == QEvent::KeyPress) { + if (pair.first) { + pair.first(); + } + } else { + if (pair.second) { + pair.second(); + } + } + event->accept(); + return true; } - event->accept(); - return true; } if (event->type() == GamepadButtonEvent::Down()) { auto item = m_buttons.find(static_cast(event)->value()); @@ -378,15 +393,11 @@ void ShortcutController::loadShortcuts(ShortcutItem* item) { } QVariant shortcut = m_config->getQtOption(item->name(), KEY_SECTION); if (!shortcut.isNull()) { - QKeySequence keySequence(shortcut.toString()); - if (item->functions().first) { - QKeySequence oldShortcut = item->shortcut(); - if (!oldShortcut.isEmpty()) { - m_heldKeys.take(oldShortcut); - } - m_heldKeys[keySequence] = item; + if (shortcut.toString().endsWith("+")) { + updateKey(item, toModifierShortcut(shortcut.toString())); + } else { + updateKey(item, QKeySequence(shortcut.toString())[0]); } - item->setShortcut(keySequence); } loadGamepadShortcuts(item); } @@ -446,26 +457,6 @@ void ShortcutController::loadGamepadShortcuts(ShortcutItem* item) { } } -QKeySequence ShortcutController::keyEventToSequence(const QKeyEvent* event) { - QString modifier = QString::null; - - if (event->modifiers() & Qt::ShiftModifier) { - modifier += "Shift+"; - } - if (event->modifiers() & Qt::ControlModifier) { - modifier += "Ctrl+"; - } - if (event->modifiers() & Qt::AltModifier) { - modifier += "Alt+"; - } - if (event->modifiers() & Qt::MetaModifier) { - modifier += "Meta+"; - } - - QString key = QKeySequence(event->key()).toString(); - return QKeySequence(modifier + key); -} - void ShortcutController::loadProfile(const QString& profile) { m_profileName = profile; m_profile = InputProfile::findProfile(profile); @@ -481,9 +472,69 @@ void ShortcutController::onSubitems(ShortcutItem* item, std::functionshortcut()) + , m_shortcut(action->shortcut().isEmpty() ? 0 : action->shortcut()[0]) , m_menu(nullptr) , m_name(name) , m_button(-1) @@ -496,7 +547,7 @@ ShortcutController::ShortcutItem::ShortcutItem(QAction* action, const QString& n .remove("..."); } -ShortcutController::ShortcutItem::ShortcutItem(ShortcutController::ShortcutItem::Functions functions, const QKeySequence& shortcut, const QString& visibleName, const QString& name, ShortcutItem* parent) +ShortcutController::ShortcutItem::ShortcutItem(ShortcutController::ShortcutItem::Functions functions, int shortcut, const QString& visibleName, const QString& name, ShortcutItem* parent) : m_action(nullptr) , m_shortcut(shortcut) , m_functions(functions) @@ -512,6 +563,7 @@ ShortcutController::ShortcutItem::ShortcutItem(ShortcutController::ShortcutItem: ShortcutController::ShortcutItem::ShortcutItem(QMenu* menu, ShortcutItem* parent) : m_action(nullptr) + , m_shortcut(0) , m_menu(menu) , m_button(-1) , m_axis(-1) @@ -530,7 +582,7 @@ void ShortcutController::ShortcutItem::addAction(QAction* action, const QString& } void ShortcutController::ShortcutItem::addFunctions(ShortcutController::ShortcutItem::Functions functions, - const QKeySequence& shortcut, const QString& visibleName, + int shortcut, const QString& visibleName, const QString& name) { m_items.append(ShortcutItem(functions, shortcut, visibleName, name, this)); } @@ -539,10 +591,10 @@ void ShortcutController::ShortcutItem::addSubmenu(QMenu* menu) { m_items.append(ShortcutItem(menu, this)); } -void ShortcutController::ShortcutItem::setShortcut(const QKeySequence& shortcut) { +void ShortcutController::ShortcutItem::setShortcut(int shortcut) { m_shortcut = shortcut; if (m_action) { - m_action->setShortcut(shortcut); + m_action->setShortcut(QKeySequence(shortcut)); } } diff --git a/src/platform/qt/ShortcutController.h b/src/platform/qt/ShortcutController.h index 5cc6f06a4..61e6cd3e1 100644 --- a/src/platform/qt/ShortcutController.h +++ b/src/platform/qt/ShortcutController.h @@ -9,7 +9,6 @@ #include "GamepadAxisEvent.h" #include -#include #include @@ -38,13 +37,13 @@ private: typedef QPair, std::function> Functions; ShortcutItem(QAction* action, const QString& name, ShortcutItem* parent = nullptr); - ShortcutItem(Functions functions, const QKeySequence& shortcut, const QString& visibleName, const QString& name, + ShortcutItem(Functions functions, int shortcut, const QString& visibleName, const QString& name, ShortcutItem* parent = nullptr); ShortcutItem(QMenu* action, ShortcutItem* parent = nullptr); QAction* action() { return m_action; } const QAction* action() const { return m_action; } - const QKeySequence& shortcut() const { return m_shortcut; } + const int shortcut() const { return m_shortcut; } Functions functions() const { return m_functions; } QMenu* menu() { return m_menu; } const QMenu* menu() const { return m_menu; } @@ -55,11 +54,11 @@ private: ShortcutItem* parent() { return m_parent; } const ShortcutItem* parent() const { return m_parent; } void addAction(QAction* action, const QString& name); - void addFunctions(Functions functions, const QKeySequence& shortcut, const QString& visibleName, + void addFunctions(Functions functions, int shortcut, const QString& visibleName, const QString& name); void addSubmenu(QMenu* menu); int button() const { return m_button; } - void setShortcut(const QKeySequence& sequence); + void setShortcut(int sequence); void setButton(int button) { m_button = button; } int axis() const { return m_axis; } GamepadAxisEvent::Direction direction() const { return m_direction; } @@ -71,7 +70,7 @@ private: private: QAction* m_action; - QKeySequence m_shortcut; + int m_shortcut; QMenu* m_menu; Functions m_functions; QString m_name; @@ -99,21 +98,25 @@ public: virtual int rowCount(const QModelIndex& parent = QModelIndex()) const override; void addAction(QMenu* menu, QAction* action, const QString& name); + void addFunctions(QMenu* menu, std::function press, std::function release, + int shortcut, const QString& visibleName, const QString& name); void addFunctions(QMenu* menu, std::function press, std::function release, const QKeySequence& shortcut, const QString& visibleName, const QString& name); void addMenu(QMenu* menu, QMenu* parent = nullptr); - QKeySequence shortcutAt(const QModelIndex& index) const; + int shortcutAt(const QModelIndex& index) const; bool isMenuAt(const QModelIndex& index) const; - void updateKey(const QModelIndex& index, const QKeySequence& keySequence); + void updateKey(const QModelIndex& index, int keySequence); void updateButton(const QModelIndex& index, int button); void updateAxis(const QModelIndex& index, int axis, GamepadAxisEvent::Direction direction); void clearKey(const QModelIndex& index); void clearButton(const QModelIndex& index); - static QKeySequence keyEventToSequence(const QKeyEvent*); + static int toModifierShortcut(const QString& shortcut); + static bool isModifierKey(int key); + static int toModifierKey(int key); public slots: void loadProfile(const QString& profile); @@ -127,12 +130,13 @@ private: void loadShortcuts(ShortcutItem*); void loadGamepadShortcuts(ShortcutItem*); void onSubitems(ShortcutItem*, std::function func); + void updateKey(ShortcutItem* item, int keySequence); ShortcutItem m_rootMenu; QMap m_menuMap; QMap m_buttons; QMap, ShortcutItem*> m_axes; - QMap m_heldKeys; + QMap m_heldKeys; ConfigController* m_config; QString m_profileName; const InputProfile* m_profile; diff --git a/src/platform/qt/ShortcutView.cpp b/src/platform/qt/ShortcutView.cpp index 036d9cf76..d1f694da5 100644 --- a/src/platform/qt/ShortcutView.cpp +++ b/src/platform/qt/ShortcutView.cpp @@ -19,10 +19,14 @@ ShortcutView::ShortcutView(QWidget* parent) , m_input(nullptr) { m_ui.setupUi(this); - m_ui.keyEdit->setValueButton(-1); - m_ui.keySequenceEdit->installEventFilter(this); + m_ui.keyEdit->setValueKey(0); - connect(m_ui.keySequenceEdit, SIGNAL(keySequenceChanged(const QKeySequence&)), this, SLOT(updateKey(const QKeySequence&))); + connect(m_ui.gamepadButton, &QAbstractButton::pressed, [this]() { + m_ui.keyEdit->setValueButton(-1); + }); + connect(m_ui.keyboardButton, &QAbstractButton::pressed, [this]() { + m_ui.keyEdit->setValueKey(0); + }); connect(m_ui.keyEdit, SIGNAL(valueChanged(int)), this, SLOT(updateButton(int))); connect(m_ui.keyEdit, SIGNAL(axisChanged(int, int)), this, SLOT(updateAxis(int, int))); connect(m_ui.shortcutTable, SIGNAL(doubleClicked(const QModelIndex&)), this, SLOT(load(const QModelIndex&))); @@ -42,21 +46,6 @@ void ShortcutView::setInputController(InputController* controller) { m_input->stealFocus(this); } -bool ShortcutView::eventFilter(QObject*, QEvent* event) { - if (event->type() == QEvent::KeyPress) { - QKeyEvent* keyEvent = static_cast(event); - if (keyEvent->key() != Qt::Key_Tab && keyEvent->key() != Qt::Key_Backtab) { - return false; - } - if (!(keyEvent->modifiers() & ~Qt::ShiftModifier)) { - m_ui.keySequenceEdit->setKeySequence(ShortcutController::keyEventToSequence(keyEvent)); - keyEvent->accept(); - return true; - } - } - return false; -} - void ShortcutView::load(const QModelIndex& index) { if (!m_controller) { return; @@ -64,23 +53,20 @@ void ShortcutView::load(const QModelIndex& index) { if (m_controller->isMenuAt(index)) { return; } - QKeySequence sequence = m_controller->shortcutAt(index); + int shortcut = m_controller->shortcutAt(index); if (index.column() == 1) { m_ui.keyboardButton->click(); } else if (index.column() == 2) { m_ui.gamepadButton->click(); } + bool blockSignals = m_ui.keyEdit->blockSignals(true); + m_ui.keyEdit->setFocus(Qt::MouseFocusReason); if (m_ui.gamepadButton->isChecked()) { - bool blockSignals = m_ui.keyEdit->blockSignals(true); - m_ui.keyEdit->setFocus(); m_ui.keyEdit->setValueButton(-1); // There are no default bindings - m_ui.keyEdit->blockSignals(blockSignals); } else { - bool blockSignals = m_ui.keySequenceEdit->blockSignals(true); - m_ui.keySequenceEdit->setFocus(); - m_ui.keySequenceEdit->setKeySequence(sequence); - m_ui.keySequenceEdit->blockSignals(blockSignals); + m_ui.keyEdit->setValueKey(shortcut); } + m_ui.keyEdit->blockSignals(blockSignals); } void ShortcutView::clear() { @@ -96,22 +82,19 @@ void ShortcutView::clear() { m_ui.keyEdit->setValueButton(-1); } else { m_controller->clearKey(index); - m_ui.keySequenceEdit->setKeySequence(QKeySequence()); + m_ui.keyEdit->setValueKey(-1); } } -void ShortcutView::updateKey(const QKeySequence& shortcut) { - if (!m_controller || m_controller->isMenuAt(m_ui.shortcutTable->selectionModel()->currentIndex())) { - return; - } - m_controller->updateKey(m_ui.shortcutTable->selectionModel()->currentIndex(), shortcut); -} - void ShortcutView::updateButton(int button) { if (!m_controller || m_controller->isMenuAt(m_ui.shortcutTable->selectionModel()->currentIndex())) { return; } - m_controller->updateButton(m_ui.shortcutTable->selectionModel()->currentIndex(), button); + if (m_ui.gamepadButton->isChecked()) { + m_controller->updateButton(m_ui.shortcutTable->selectionModel()->currentIndex(), button); + } else { + m_controller->updateKey(m_ui.shortcutTable->selectionModel()->currentIndex(), button); + } } void ShortcutView::updateAxis(int axis, int direction) { diff --git a/src/platform/qt/ShortcutView.h b/src/platform/qt/ShortcutView.h index 59a997347..37c95c887 100644 --- a/src/platform/qt/ShortcutView.h +++ b/src/platform/qt/ShortcutView.h @@ -27,14 +27,12 @@ public: void setInputController(InputController* input); protected: - virtual bool eventFilter(QObject* obj, QEvent* event) override; virtual bool event(QEvent*) override; virtual void closeEvent(QCloseEvent*) override; private slots: void load(const QModelIndex&); void clear(); - void updateKey(const QKeySequence&); void updateButton(int button); void updateAxis(int axis, int direction); diff --git a/src/platform/qt/ShortcutView.ui b/src/platform/qt/ShortcutView.ui index 05d114c02..8b28e4d52 100644 --- a/src/platform/qt/ShortcutView.ui +++ b/src/platform/qt/ShortcutView.ui @@ -6,7 +6,7 @@ 0 0 - 425 + 432 443 @@ -60,16 +60,6 @@ - - - - - 0 - 0 - - - - @@ -78,9 +68,6 @@ 0 - - false - @@ -95,38 +82,5 @@ - - - keyboardButton - toggled(bool) - keySequenceEdit - setVisible(bool) - - - 86 - 374 - - - 66 - 20 - - - - - gamepadButton - toggled(bool) - keyEdit - setVisible(bool) - - - 213 - 374 - - - 206 - 340 - - - - + From 4d79fd7324a58f9202e8a7a302def5d9a801e40a Mon Sep 17 00:00:00 2001 From: Jeffrey Pfau Date: Mon, 17 Aug 2015 21:24:55 -0700 Subject: [PATCH 03/19] Test: Add fuzzing harness and move perf-main into test folder --- CMakeLists.txt | 10 +- src/platform/test/fuzz-main.c | 210 ++++++++++++++++++++++++++++ src/platform/{ => test}/perf-main.c | 0 3 files changed, 219 insertions(+), 1 deletion(-) create mode 100644 src/platform/test/fuzz-main.c rename src/platform/{ => test}/perf-main.c (100%) diff --git a/CMakeLists.txt b/CMakeLists.txt index 9fdacfcc2..4f97de096 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -18,6 +18,7 @@ set(BUILD_QT ON CACHE BOOL "Build Qt frontend") set(BUILD_SDL ON CACHE BOOL "Build SDL frontend") set(BUILD_LIBRETRO OFF CACHE BOOL "Build libretro core") set(BUILD_PERF OFF CACHE BOOL "Build performance profiling tool") +set(BUILD_TEST OFF CACHE BOOL "Build testing harness") set(BUILD_STATIC OFF CACHE BOOL "Build a static library") set(BUILD_SHARED ON CACHE BOOL "Build a shared library") set(BUILD_GL ON CACHE STRING "Build with OpenGL") @@ -435,7 +436,7 @@ if(BUILD_QT) endif() if(BUILD_PERF) - set(PERF_SRC ${CMAKE_SOURCE_DIR}/src/platform/perf-main.c) + set(PERF_SRC ${CMAKE_SOURCE_DIR}/src/platform/test/perf-main.c) if(UNIX AND NOT APPLE) list(APPEND PERF_LIB rt) endif() @@ -446,6 +447,12 @@ if(BUILD_PERF) install(FILES ${CMAKE_SOURCE_DIR}/tools/perf.py DESTINATION "${CMAKE_INSTALL_LIBDIR}/${BINARY_NAME}" COMPONENT ${BINARY_NAME}-perf) endif() +if(BUILD_TEST) + add_executable(${BINARY_NAME}-fuzz ${CMAKE_SOURCE_DIR}/src/platform/test/fuzz-main.c) + target_link_libraries(${BINARY_NAME}-fuzz ${BINARY_NAME}) + install(TARGETS ${BINARY_NAME}-fuzz DESTINATION bin COMPONENT ${BINARY_NAME}-test) +endif() + # Packaging set(CPACK_PACKAGE_VERSION ${VERSION_STRING}) set(CPACK_PACKAGE_VERSION_MAJOR ${LIB_VERSION_MAJOR}) @@ -483,6 +490,7 @@ message(STATUS " Qt: ${BUILD_QT}") message(STATUS " SDL (${SDL_VERSION}): ${BUILD_SDL}") message(STATUS " Libretro core: ${BUILD_LIBRETRO}") message(STATUS " Profiling: ${BUILD_PERF}") +message(STATUS " Test harness: ${BUILD_TEST}") message(STATUS "Library summary:") message(STATUS " Static: ${BUILD_STATIC}") message(STATUS " Shared: ${BUILD_SHARED}") diff --git a/src/platform/test/fuzz-main.c b/src/platform/test/fuzz-main.c new file mode 100644 index 000000000..c244b09d4 --- /dev/null +++ b/src/platform/test/fuzz-main.c @@ -0,0 +1,210 @@ +/* Copyright (c) 2013-2015 Jeffrey Pfau + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +#include "gba/supervisor/thread.h" +#include "gba/supervisor/config.h" +#include "gba/gba.h" +#include "gba/renderers/video-software.h" +#include "gba/serialize.h" + +#include "platform/commandline.h" +#include "util/string.h" +#include "util/vfs.h" + +#include +#include + +#define FUZZ_OPTIONS "F:NO:S:V:" +#define FUZZ_USAGE \ + "\nAdditional options:\n" \ + " -F FRAMES Run for the specified number of FRAMES before exiting\n" \ + " -N Disable video rendering entirely\n" \ + " -O OFFSET Offset to apply savestate overlay\n" \ + " -S FILE Load a savestate when starting the test\n" \ + " -V FILE Overlay a second savestate over the loaded savestate\n" \ + +struct FuzzOpts { + bool noVideo; + unsigned frames; + size_t overlayOffset; + char* savestate; + char* ssOverlay; +}; + +static void _GBAFuzzRunloop(struct GBAThread* context, unsigned frames); +static void _GBAFuzzShutdown(int signal); +static bool _parseFuzzOpts(struct SubParser* parser, struct GBAConfig* config, int option, const char* arg); +static void _loadSavestate(struct GBAThread* context); + +static struct GBAThread* _thread; +static bool _dispatchExiting = false; +static struct VFile* _savestate = 0; +static struct VFile* _savestateOverlay = 0; +static size_t _overlayOffset; + +int main(int argc, char** argv) { + signal(SIGINT, _GBAFuzzShutdown); + + + struct FuzzOpts fuzzOpts = { false, 0, 0, 0, 0 }; + struct SubParser subparser = { + .usage = FUZZ_USAGE, + .parse = _parseFuzzOpts, + .extraOptions = FUZZ_OPTIONS, + .opts = &fuzzOpts + }; + + struct GBAConfig config; + GBAConfigInit(&config, "fuzz"); + GBAConfigLoad(&config); + + struct GBAOptions opts = { + .idleOptimization = IDLE_LOOP_DETECT + }; + GBAConfigLoadDefaults(&config, &opts); + + struct GBAArguments args; + bool parsed = parseArguments(&args, &config, argc, argv, &subparser); + if (!parsed || args.showHelp) { + usage(argv[0], FUZZ_USAGE); + freeArguments(&args); + GBAConfigFreeOpts(&opts); + GBAConfigDeinit(&config); + return !parsed; + } + + struct GBAVideoSoftwareRenderer renderer; + renderer.outputBuffer = 0; + + struct GBAThread context = {}; + _thread = &context; + + if (!fuzzOpts.noVideo) { + GBAVideoSoftwareRendererCreate(&renderer); + renderer.outputBuffer = malloc(256 * 256 * 4); + renderer.outputBufferStride = 256; + context.renderer = &renderer.d; + } + if (fuzzOpts.savestate) { + _savestate = VFileOpen(fuzzOpts.savestate, O_RDONLY); + free(fuzzOpts.savestate); + } + if (fuzzOpts.ssOverlay) { + _overlayOffset = fuzzOpts.overlayOffset; + if (_overlayOffset < sizeof(struct GBASerializedState)) { + _savestateOverlay = VFileOpen(fuzzOpts.ssOverlay, O_RDONLY); + } + free(fuzzOpts.ssOverlay); + } + if (_savestate) { + context.startCallback = _loadSavestate; + } + + context.debugger = createDebugger(&args, &context); + context.overrides = GBAConfigGetOverrides(&config); + + GBAConfigMap(&config, &opts); + opts.audioSync = false; + opts.videoSync = false; + GBAMapArgumentsToContext(&args, &context); + GBAMapOptionsToContext(&opts, &context); + + int didStart = GBAThreadStart(&context); + + if (!didStart) { + goto cleanup; + } + GBAThreadInterrupt(&context); + if (GBAThreadHasCrashed(&context)) { + GBAThreadJoin(&context); + goto cleanup; + } + + GBAThreadContinue(&context); + + _GBAFuzzRunloop(&context, fuzzOpts.frames); + GBAThreadJoin(&context); + +cleanup: + if (_savestate) { + _savestate->close(_savestate); + } + if (_savestateOverlay) { + _savestateOverlay->close(_savestateOverlay); + } + GBAConfigFreeOpts(&opts); + freeArguments(&args); + GBAConfigDeinit(&config); + free(context.debugger); + if (renderer.outputBuffer) { + free(renderer.outputBuffer); + } + + return !didStart || GBAThreadHasCrashed(&context); +} + +static void _GBAFuzzRunloop(struct GBAThread* context, unsigned duration) { + unsigned frames = 0; + while (context->state < THREAD_EXITING) { + if (GBASyncWaitFrameStart(&context->sync, 0)) { + ++frames; + } + GBASyncWaitFrameEnd(&context->sync); + if (frames >= duration) { + _GBAFuzzShutdown(0); + } + if (_dispatchExiting) { + GBAThreadEnd(context); + } + } +} + +static void _GBAFuzzShutdown(int signal) { + UNUSED(signal); + // This will come in ON the GBA thread, so we have to handle it carefully + _dispatchExiting = true; + ConditionWake(&_thread->sync.videoFrameAvailableCond); +} + +static bool _parseFuzzOpts(struct SubParser* parser, struct GBAConfig* config, int option, const char* arg) { + UNUSED(config); + struct FuzzOpts* opts = parser->opts; + errno = 0; + switch (option) { + case 'F': + opts->frames = strtoul(arg, 0, 10); + return !errno; + case 'N': + opts->noVideo = true; + return true; + case 'O': + opts->overlayOffset = strtoul(arg, 0, 10); + return !errno; + case 'S': + opts->savestate = strdup(arg); + return true; + case 'V': + opts->ssOverlay = strdup(arg); + return true; + default: + return false; + } +} + +static void _loadSavestate(struct GBAThread* context) { + if (!_savestateOverlay) { + GBALoadStateNamed(context->gba, _savestate); + } else { + struct GBASerializedState* state = GBAAllocateState(); + _savestate->read(_savestate, state, sizeof(*state)); + _savestateOverlay->read(_savestateOverlay, (uint8_t*) state + _overlayOffset, sizeof(*state) - _overlayOffset); + GBADeserialize(context->gba, state); + GBADeallocateState(state); + _savestateOverlay->close(_savestateOverlay); + _savestateOverlay = 0; + } + _savestate->close(_savestate); + _savestate = 0; +} diff --git a/src/platform/perf-main.c b/src/platform/test/perf-main.c similarity index 100% rename from src/platform/perf-main.c rename to src/platform/test/perf-main.c From 3271c1a0fd1b38329fd8b3ee56f2f4ba63afaba5 Mon Sep 17 00:00:00 2001 From: Jeffrey Pfau Date: Mon, 17 Aug 2015 21:27:39 -0700 Subject: [PATCH 04/19] Misc: Fix linkage on _parseGraphicsArg --- src/platform/commandline.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/platform/commandline.c b/src/platform/commandline.c index 3502df37c..ff63f7c7a 100644 --- a/src/platform/commandline.c +++ b/src/platform/commandline.c @@ -50,7 +50,7 @@ static const struct option _options[] = { { 0, 0, 0, 0 } }; -bool _parseGraphicsArg(struct SubParser* parser, struct GBAConfig* config, int option, const char* arg); +static bool _parseGraphicsArg(struct SubParser* parser, struct GBAConfig* config, int option, const char* arg); bool parseArguments(struct GBAArguments* opts, struct GBAConfig* config, int argc, char* const* argv, struct SubParser* subparser) { int ch; From 861928d12a25ade175917c18879340472dbc2895 Mon Sep 17 00:00:00 2001 From: Jeffrey Pfau Date: Mon, 17 Aug 2015 22:02:34 -0700 Subject: [PATCH 05/19] Qt: Window size command line options are now supported --- CHANGES | 4 ++++ doc/mgba-qt.6 | 15 +++++++++++++++ src/platform/commandline.c | 2 ++ src/platform/commandline.h | 1 + src/platform/qt/ConfigController.cpp | 4 ++-- src/platform/qt/ConfigController.h | 3 ++- src/platform/qt/GBAApp.cpp | 15 +++++++++++++-- 7 files changed, 39 insertions(+), 5 deletions(-) diff --git a/CHANGES b/CHANGES index d45a92a76..85669fc3e 100644 --- a/CHANGES +++ b/CHANGES @@ -1,3 +1,7 @@ +0.4.0: (Future) +Misc: + - Qt: Window size command line options are now supported + 0.3.0: (2015-08-16) Features: - Ability to hide individual background layers, or OBJs diff --git a/doc/mgba-qt.6 b/doc/mgba-qt.6 index 256384721..4ab76aafc 100644 --- a/doc/mgba-qt.6 +++ b/doc/mgba-qt.6 @@ -11,6 +11,7 @@ .Nd Game Boy Advance emulator .Sh SYNOPSIS .Nm mgba-qt +.Op Fl 123456f .Op Fl b Ar biosfile .Op Fl l Ar loglevel .Op Fl p Ar patchfile @@ -21,12 +22,26 @@ is a Game Boy Advance emulator. The options are as follows: .Bl -tag -width Ds +.It Fl 1 +Scale the window 1\(mu. +.It Fl 2 +Scale the window 2\(mu. +.It Fl 3 +Scale the window 3\(mu. +.It Fl 4 +Scale the window 4\(mu. +.It Fl 5 +Scale the window 5\(mu. +.It Fl 6 +Scale the window 6\(mu. .It Fl b Ar biosfile , Fl -bios Ar biosfile Specify a BIOS file to use during boot. If this flag is omitted, .Nm will use the BIOS specified in the configuration file, or a high\(hylevel emulated BIOS if none is specified. +.It Fl f +Start the emulator full\(hyscreen. .It Fl l Ar loglevel Log messages during emulation. .Ar loglevel diff --git a/src/platform/commandline.c b/src/platform/commandline.c index ff63f7c7a..e4142fffd 100644 --- a/src/platform/commandline.c +++ b/src/platform/commandline.c @@ -145,6 +145,7 @@ void initParserForGraphics(struct SubParser* parser, struct GraphicsOpts* opts) parser->parse = _parseGraphicsArg; parser->extraOptions = GRAPHICS_OPTIONS; opts->multiplier = 0; + opts->fullscreen = false; } bool _parseGraphicsArg(struct SubParser* parser, struct GBAConfig* config, int option, const char* arg) { @@ -152,6 +153,7 @@ bool _parseGraphicsArg(struct SubParser* parser, struct GBAConfig* config, int o struct GraphicsOpts* graphicsOpts = parser->opts; switch (option) { case 'f': + graphicsOpts->fullscreen = true; GBAConfigSetDefaultIntValue(config, "fullscreen", 1); return true; case '1': diff --git a/src/platform/commandline.h b/src/platform/commandline.h index 32275633b..203895088 100644 --- a/src/platform/commandline.h +++ b/src/platform/commandline.h @@ -42,6 +42,7 @@ struct SubParser { struct GraphicsOpts { int multiplier; + bool fullscreen; }; struct GBAThread; diff --git a/src/platform/qt/ConfigController.cpp b/src/platform/qt/ConfigController.cpp index 2a0ae0184..4fd374c1b 100644 --- a/src/platform/qt/ConfigController.cpp +++ b/src/platform/qt/ConfigController.cpp @@ -126,8 +126,8 @@ ConfigController::~ConfigController() { GBAConfigFreeOpts(&m_opts); } -bool ConfigController::parseArguments(GBAArguments* args, int argc, char* argv[]) { - if (::parseArguments(args, &m_config, argc, argv, 0)) { +bool ConfigController::parseArguments(GBAArguments* args, int argc, char* argv[], SubParser* subparser) { + if (::parseArguments(args, &m_config, argc, argv, subparser)) { GBAConfigMap(&m_config, &m_opts); return true; } diff --git a/src/platform/qt/ConfigController.h b/src/platform/qt/ConfigController.h index 3b0de11b2..439c14653 100644 --- a/src/platform/qt/ConfigController.h +++ b/src/platform/qt/ConfigController.h @@ -16,6 +16,7 @@ extern "C" { #include "gba/supervisor/config.h" #include "util/configuration.h" +#include "platform/commandline.h" } class QAction; @@ -64,7 +65,7 @@ public: ~ConfigController(); const GBAOptions* options() const { return &m_opts; } - bool parseArguments(GBAArguments* args, int argc, char* argv[]); + bool parseArguments(GBAArguments* args, int argc, char* argv[], SubParser* subparser = nullptr); ConfigOption* addOption(const char* key); void updateOption(const char* key); diff --git a/src/platform/qt/GBAApp.cpp b/src/platform/qt/GBAApp.cpp index ed9645ec9..72c72cda8 100644 --- a/src/platform/qt/GBAApp.cpp +++ b/src/platform/qt/GBAApp.cpp @@ -50,9 +50,12 @@ GBAApp::GBAApp(int& argc, char* argv[]) } GBAArguments args; - bool loaded = m_configController.parseArguments(&args, argc, argv); + GraphicsOpts graphicsOpts; + SubParser subparser; + initParserForGraphics(&subparser, &graphicsOpts); + bool loaded = m_configController.parseArguments(&args, argc, argv, &subparser); if (loaded && args.showHelp) { - usage(argv[0], 0); + usage(argv[0], subparser.usage); ::exit(0); return; } @@ -72,6 +75,14 @@ GBAApp::GBAApp(int& argc, char* argv[]) w->loadConfig(); } freeArguments(&args); + + if (graphicsOpts.multiplier) { + w->resizeFrame(VIDEO_HORIZONTAL_PIXELS * graphicsOpts.multiplier, VIDEO_VERTICAL_PIXELS * graphicsOpts.multiplier); + } + if (graphicsOpts.fullscreen) { + w->enterFullScreen(); + } + w->show(); w->controller()->setMultiplayerController(&m_multiplayer); From 1f899737d9825f60e6713fed4ff6d368dda32e56 Mon Sep 17 00:00:00 2001 From: Jeffrey Pfau Date: Mon, 17 Aug 2015 23:03:20 -0700 Subject: [PATCH 06/19] Qt: Windows no longer spawn in the top left on first launch --- CHANGES | 2 ++ src/platform/qt/Window.cpp | 14 +++++++++----- 2 files changed, 11 insertions(+), 5 deletions(-) diff --git a/CHANGES b/CHANGES index 5096fba14..d314caf1c 100644 --- a/CHANGES +++ b/CHANGES @@ -1,4 +1,6 @@ 0.4.0: (Future) +Bugfixes: + - Qt: Windows no longer spawn in the top left on first launch Misc: - Qt: Window size command line options are now supported - Qt: Increase usability of key mapper diff --git a/src/platform/qt/Window.cpp b/src/platform/qt/Window.cpp index 6307fdf12..721cec6a8 100644 --- a/src/platform/qt/Window.cpp +++ b/src/platform/qt/Window.cpp @@ -5,6 +5,7 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include "Window.h" +#include #include #include #include @@ -88,11 +89,6 @@ Window::Window(ConfigController* config, int playerId, QWidget* parent) m_screenWidget->setLockAspectRatio(m_logo.width(), m_logo.height()); setCentralWidget(m_screenWidget); - QVariant windowPos = m_config->getQtOption("windowPos"); - if (!windowPos.isNull()) { - move(windowPos.toPoint()); - } - connect(m_controller, SIGNAL(gameStarted(GBAThread*)), this, SLOT(gameStarted(GBAThread*))); connect(m_controller, SIGNAL(gameStarted(GBAThread*)), &m_inputController, SLOT(suspendScreensaver())); connect(m_controller, SIGNAL(gameStopped(GBAThread*)), m_display, SLOT(stopDrawing())); @@ -465,6 +461,14 @@ void Window::resizeEvent(QResizeEvent* event) { void Window::showEvent(QShowEvent* event) { resizeFrame(m_screenWidget->sizeHint().width(), m_screenWidget->sizeHint().height()); + QVariant windowPos = m_config->getQtOption("windowPos"); + if (!windowPos.isNull()) { + move(windowPos.toPoint()); + } else { + QRect rect = frameGeometry(); + rect.moveCenter(QApplication::desktop()->availableGeometry().center()); + move(rect.topLeft()); + } } void Window::closeEvent(QCloseEvent* event) { From 1f10613d1b7e96d362660c81c37ccf78c2276f7d Mon Sep 17 00:00:00 2001 From: Jeffrey Pfau Date: Tue, 18 Aug 2015 22:32:59 -0700 Subject: [PATCH 07/19] Qt: Fix install path of XDG desktop file with DESTDIR --- CHANGES | 1 + src/platform/qt/CMakeLists.txt | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGES b/CHANGES index d314caf1c..4c29f1696 100644 --- a/CHANGES +++ b/CHANGES @@ -1,6 +1,7 @@ 0.4.0: (Future) Bugfixes: - Qt: Windows no longer spawn in the top left on first launch + - Qt: Fix install path of XDG desktop file with DESTDIR Misc: - Qt: Window size command line options are now supported - Qt: Increase usability of key mapper diff --git a/src/platform/qt/CMakeLists.txt b/src/platform/qt/CMakeLists.txt index 3c1de1151..dc142a687 100644 --- a/src/platform/qt/CMakeLists.txt +++ b/src/platform/qt/CMakeLists.txt @@ -167,7 +167,7 @@ install(TARGETS ${BINARY_NAME}-qt if(UNIX AND NOT APPLE) find_program(DESKTOP_FILE_INSTALL desktop-file-install) if(DESKTOP_FILE_INSTALL) - install(CODE "execute_process(COMMAND ${DESKTOP_FILE_INSTALL} \"${CMAKE_SOURCE_DIR}/res/mgba-qt.desktop\" --dir \"$ENV{DESTDIR}${CMAKE_INSTALL_PREFIX}/share/applications/\")") + install(CODE "execute_process(COMMAND ${DESKTOP_FILE_INSTALL} \"${CMAKE_SOURCE_DIR}/res/mgba-qt.desktop\" --dir \"\$ENV{DESTDIR}\${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_DATADIR}/applications/\")") endif() endif() if(UNIX) From 0cdc9ff32887e57767fac74ea12c8616025583bd Mon Sep 17 00:00:00 2001 From: Jeffrey Pfau Date: Tue, 18 Aug 2015 22:33:45 -0700 Subject: [PATCH 08/19] Test: De-thread fuzzing harness --- src/platform/test/fuzz-main.c | 137 +++++++++++++++------------------- 1 file changed, 59 insertions(+), 78 deletions(-) diff --git a/src/platform/test/fuzz-main.c b/src/platform/test/fuzz-main.c index c244b09d4..5d75996db 100644 --- a/src/platform/test/fuzz-main.c +++ b/src/platform/test/fuzz-main.c @@ -10,6 +10,7 @@ #include "gba/serialize.h" #include "platform/commandline.h" +#include "util/memory.h" #include "util/string.h" #include "util/vfs.h" @@ -27,27 +28,21 @@ struct FuzzOpts { bool noVideo; - unsigned frames; + int frames; size_t overlayOffset; char* savestate; char* ssOverlay; }; -static void _GBAFuzzRunloop(struct GBAThread* context, unsigned frames); +static void _GBAFuzzRunloop(struct GBA* gba, int frames); static void _GBAFuzzShutdown(int signal); static bool _parseFuzzOpts(struct SubParser* parser, struct GBAConfig* config, int option, const char* arg); -static void _loadSavestate(struct GBAThread* context); -static struct GBAThread* _thread; static bool _dispatchExiting = false; -static struct VFile* _savestate = 0; -static struct VFile* _savestateOverlay = 0; -static size_t _overlayOffset; int main(int argc, char** argv) { signal(SIGINT, _GBAFuzzShutdown); - struct FuzzOpts fuzzOpts = { false, 0, 0, 0, 0 }; struct SubParser subparser = { .usage = FUZZ_USAGE, @@ -75,97 +70,99 @@ int main(int argc, char** argv) { return !parsed; } + struct GBA* gba = anonymousMemoryMap(sizeof(struct GBA)); + struct ARMCore* cpu = anonymousMemoryMap(sizeof(struct ARMCore)); + struct VFile* rom = VFileOpen(args.fname, O_RDONLY); + + GBACreate(gba); + ARMSetComponents(cpu, &gba->d, 0, 0); + ARMInit(cpu); + gba->sync = 0; + gba->hardCrash = false; + + GBALoadROM(gba, rom, 0, 0); + ARMReset(cpu); + + struct GBACartridgeOverride override; + const struct GBACartridge* cart = (const struct GBACartridge*) gba->memory.rom; + memcpy(override.id, &cart->id, sizeof(override.id)); + if (GBAOverrideFind(GBAConfigGetOverrides(&config), &override)) { + GBAOverrideApply(gba, &override); + } + struct GBAVideoSoftwareRenderer renderer; renderer.outputBuffer = 0; - struct GBAThread context = {}; - _thread = &context; + struct VFile* savestate = 0; + struct VFile* savestateOverlay = 0; + size_t overlayOffset; if (!fuzzOpts.noVideo) { GBAVideoSoftwareRendererCreate(&renderer); renderer.outputBuffer = malloc(256 * 256 * 4); renderer.outputBufferStride = 256; - context.renderer = &renderer.d; + GBAVideoAssociateRenderer(&gba->video, &renderer.d); } + if (fuzzOpts.savestate) { - _savestate = VFileOpen(fuzzOpts.savestate, O_RDONLY); + savestate = VFileOpen(fuzzOpts.savestate, O_RDONLY); free(fuzzOpts.savestate); } if (fuzzOpts.ssOverlay) { - _overlayOffset = fuzzOpts.overlayOffset; - if (_overlayOffset < sizeof(struct GBASerializedState)) { - _savestateOverlay = VFileOpen(fuzzOpts.ssOverlay, O_RDONLY); + overlayOffset = fuzzOpts.overlayOffset; + if (overlayOffset < sizeof(struct GBASerializedState)) { + savestateOverlay = VFileOpen(fuzzOpts.ssOverlay, O_RDONLY); } free(fuzzOpts.ssOverlay); } - if (_savestate) { - context.startCallback = _loadSavestate; + if (savestate) { + if (!savestateOverlay) { + GBALoadStateNamed(gba, savestate); + } else { + struct GBASerializedState* state = GBAAllocateState(); + savestate->read(savestate, state, sizeof(*state)); + savestateOverlay->read(savestateOverlay, (uint8_t*) state + overlayOffset, sizeof(*state) - overlayOffset); + GBADeserialize(gba, state); + GBADeallocateState(state); + savestateOverlay->close(savestateOverlay); + savestateOverlay = 0; + } + savestate->close(savestate); + savestate = 0; } - context.debugger = createDebugger(&args, &context); - context.overrides = GBAConfigGetOverrides(&config); - GBAConfigMap(&config, &opts); - opts.audioSync = false; - opts.videoSync = false; - GBAMapArgumentsToContext(&args, &context); - GBAMapOptionsToContext(&opts, &context); - int didStart = GBAThreadStart(&context); + blip_set_rates(gba->audio.left, GBA_ARM7TDMI_FREQUENCY, 0x8000); + blip_set_rates(gba->audio.right, GBA_ARM7TDMI_FREQUENCY, 0x8000); - if (!didStart) { - goto cleanup; + _GBAFuzzRunloop(gba, fuzzOpts.frames); + + if (savestate) { + savestate->close(savestate); } - GBAThreadInterrupt(&context); - if (GBAThreadHasCrashed(&context)) { - GBAThreadJoin(&context); - goto cleanup; - } - - GBAThreadContinue(&context); - - _GBAFuzzRunloop(&context, fuzzOpts.frames); - GBAThreadJoin(&context); - -cleanup: - if (_savestate) { - _savestate->close(_savestate); - } - if (_savestateOverlay) { - _savestateOverlay->close(_savestateOverlay); + if (savestateOverlay) { + savestateOverlay->close(savestateOverlay); } GBAConfigFreeOpts(&opts); freeArguments(&args); GBAConfigDeinit(&config); - free(context.debugger); if (renderer.outputBuffer) { free(renderer.outputBuffer); } - return !didStart || GBAThreadHasCrashed(&context); + return 0; } -static void _GBAFuzzRunloop(struct GBAThread* context, unsigned duration) { - unsigned frames = 0; - while (context->state < THREAD_EXITING) { - if (GBASyncWaitFrameStart(&context->sync, 0)) { - ++frames; - } - GBASyncWaitFrameEnd(&context->sync); - if (frames >= duration) { - _GBAFuzzShutdown(0); - } - if (_dispatchExiting) { - GBAThreadEnd(context); - } - } +static void _GBAFuzzRunloop(struct GBA* gba, int frames) { + do { + ARMRunLoop(gba->cpu); + } while (gba->video.frameCounter < frames && !_dispatchExiting); } static void _GBAFuzzShutdown(int signal) { UNUSED(signal); - // This will come in ON the GBA thread, so we have to handle it carefully _dispatchExiting = true; - ConditionWake(&_thread->sync.videoFrameAvailableCond); } static bool _parseFuzzOpts(struct SubParser* parser, struct GBAConfig* config, int option, const char* arg) { @@ -192,19 +189,3 @@ static bool _parseFuzzOpts(struct SubParser* parser, struct GBAConfig* config, i return false; } } - -static void _loadSavestate(struct GBAThread* context) { - if (!_savestateOverlay) { - GBALoadStateNamed(context->gba, _savestate); - } else { - struct GBASerializedState* state = GBAAllocateState(); - _savestate->read(_savestate, state, sizeof(*state)); - _savestateOverlay->read(_savestateOverlay, (uint8_t*) state + _overlayOffset, sizeof(*state) - _overlayOffset); - GBADeserialize(context->gba, state); - GBADeallocateState(state); - _savestateOverlay->close(_savestateOverlay); - _savestateOverlay = 0; - } - _savestate->close(_savestate); - _savestate = 0; -} From 76663c41cdceab9fd4277c0d0e2a0c462167e3d7 Mon Sep 17 00:00:00 2001 From: Jeffrey Pfau Date: Tue, 18 Aug 2015 22:42:21 -0700 Subject: [PATCH 09/19] Util: Migrate popcount32 to a header --- src/debugger/memory-debugger.c | 10 +++------- src/gba/memory.c | 12 +++--------- src/util/math.h | 17 +++++++++++++++++ 3 files changed, 23 insertions(+), 16 deletions(-) create mode 100644 src/util/math.h diff --git a/src/debugger/memory-debugger.c b/src/debugger/memory-debugger.c index e4d880a5b..69d402d64 100644 --- a/src/debugger/memory-debugger.c +++ b/src/debugger/memory-debugger.c @@ -7,16 +7,12 @@ #include "debugger.h" +#include "util/math.h" + #include static bool _checkWatchpoints(struct ARMDebugger* debugger, uint32_t address, struct DebuggerEntryInfo* info, int width); -static uint32_t _popcount32(unsigned bits) { - bits = bits - ((bits >> 1) & 0x55555555); - bits = (bits & 0x33333333) + ((bits >> 2) & 0x33333333); - return (((bits + (bits >> 4)) & 0xF0F0F0F) * 0x1010101) >> 24; -} - #define FIND_DEBUGGER(DEBUGGER, CPU) \ { \ DEBUGGER = 0; \ @@ -51,7 +47,7 @@ static uint32_t _popcount32(unsigned bits) { static uint32_t ARMDebuggerShim_ ## NAME (struct ARMCore* cpu, uint32_t address, int mask, enum LSMDirection direction, int* cycleCounter) { \ struct ARMDebugger* debugger; \ FIND_DEBUGGER(debugger, cpu); \ - uint32_t popcount = _popcount32(mask); \ + uint32_t popcount = popcount32(mask); \ int offset = 4; \ int base = address; \ if (direction & LSM_D) { \ diff --git a/src/gba/memory.c b/src/gba/memory.c index b97424130..d736141a8 100644 --- a/src/gba/memory.c +++ b/src/gba/memory.c @@ -12,11 +12,11 @@ #include "gba/io.h" #include "gba/serialize.h" #include "gba/hle-bios.h" +#include "util/math.h" #include "util/memory.h" #define IDLE_LOOP_THRESHOLD 10000 -static uint32_t _popcount32(unsigned bits); static void _pristineCow(struct GBA* gba); static uint32_t _deadbeef[1] = { 0xE710B710 }; // Illegal instruction on both ARM and Thumb @@ -1083,7 +1083,7 @@ uint32_t GBALoadMultiple(struct ARMCore* cpu, uint32_t address, int mask, enum L int popcount = 0; if (direction & LSM_D) { offset = -4; - popcount = _popcount32(mask); + popcount = popcount32(mask); address -= (popcount << 2) - 4; } @@ -1196,7 +1196,7 @@ uint32_t GBAStoreMultiple(struct ARMCore* cpu, uint32_t address, int mask, enum int popcount = 0; if (direction & LSM_D) { offset = -4; - popcount = _popcount32(mask); + popcount = popcount32(mask); address -= (popcount << 2) - 4; } @@ -1588,12 +1588,6 @@ void GBAMemoryDeserialize(struct GBAMemory* memory, const struct GBASerializedSt memcpy(memory->iwram, state->iwram, SIZE_WORKING_IRAM); } -uint32_t _popcount32(unsigned bits) { - bits = bits - ((bits >> 1) & 0x55555555); - bits = (bits & 0x33333333) + ((bits >> 2) & 0x33333333); - return (((bits + (bits >> 4)) & 0xF0F0F0F) * 0x1010101) >> 24; -} - void _pristineCow(struct GBA* gba) { if (gba->memory.rom != gba->pristineRom) { return; diff --git a/src/util/math.h b/src/util/math.h new file mode 100644 index 000000000..06dca22e5 --- /dev/null +++ b/src/util/math.h @@ -0,0 +1,17 @@ +/* Copyright (c) 2013-2015 Jeffrey Pfau + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +#ifndef UTIL_MATH_H +#define UTIL_MATH_H + +#include "util/common.h" + +static inline uint32_t popcount32(unsigned bits) { + bits = bits - ((bits >> 1) & 0x55555555); + bits = (bits & 0x33333333) + ((bits >> 2) & 0x33333333); + return (((bits + (bits >> 4)) & 0xF0F0F0F) * 0x1010101) >> 24; +} + +#endif From 003a21b13d8d563509de5d20966b7c03b14c626f Mon Sep 17 00:00:00 2001 From: Jeffrey Pfau Date: Tue, 18 Aug 2015 23:23:45 -0700 Subject: [PATCH 10/19] GBA Memory: Use a dynamically sized mask for ROM memory --- CHANGES | 1 + src/gba/gba.c | 5 +++++ src/gba/memory.c | 6 +++++- src/gba/memory.h | 1 + src/util/math.h | 42 ++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 54 insertions(+), 1 deletion(-) diff --git a/CHANGES b/CHANGES index 4c29f1696..5371cb06a 100644 --- a/CHANGES +++ b/CHANGES @@ -5,6 +5,7 @@ Bugfixes: Misc: - Qt: Window size command line options are now supported - Qt: Increase usability of key mapper + - GBA Memory: Use a dynamically sized mask for ROM memory 0.3.0: (2015-08-16) Features: diff --git a/src/gba/gba.c b/src/gba/gba.c index 2ae273760..f10ec8c6c 100644 --- a/src/gba/gba.c +++ b/src/gba/gba.c @@ -17,6 +17,7 @@ #include "util/crc32.h" #include "util/memory.h" +#include "util/math.h" #include "util/patch.h" #include "util/vfs.h" @@ -156,6 +157,7 @@ void GBAReset(struct ARMCore* cpu) { if (gba->yankedRomSize) { gba->memory.romSize = gba->yankedRomSize; + gba->memory.romMask = toPow2(gba->memory.romSize) - 1; gba->yankedRomSize = 0; } GBAMemoryReset(gba); @@ -403,6 +405,7 @@ void GBALoadROM(struct GBA* gba, struct VFile* vf, struct VFile* sav, const char gba->memory.rom = gba->pristineRom; gba->activeFile = fname; gba->memory.romSize = gba->pristineRomSize; + gba->memory.romMask = toPow2(gba->memory.romSize) - 1; gba->romCrc32 = doCrc32(gba->memory.rom, gba->memory.romSize); GBASavedataInit(&gba->memory.savedata, sav); GBAHardwareInit(&gba->memory.hw, &((uint16_t*) gba->memory.rom)[GPIO_REG_DATA >> 1]); @@ -412,6 +415,7 @@ void GBALoadROM(struct GBA* gba, struct VFile* vf, struct VFile* sav, const char void GBAYankROM(struct GBA* gba) { gba->yankedRomSize = gba->memory.romSize; gba->memory.romSize = 0; + gba->memory.romMask = 0; GBARaiseIRQ(gba, IRQ_GAMEPAK); } @@ -452,6 +456,7 @@ void GBAApplyPatch(struct GBA* gba, struct Patch* patch) { return; } gba->memory.romSize = patchedSize; + gba->memory.romMask = SIZE_CART0 - 1; gba->romCrc32 = doCrc32(gba->memory.rom, gba->memory.romSize); } diff --git a/src/gba/memory.c b/src/gba/memory.c index d736141a8..056058af2 100644 --- a/src/gba/memory.c +++ b/src/gba/memory.c @@ -50,6 +50,7 @@ void GBAMemoryInit(struct GBA* gba) { gba->memory.iwram = 0; gba->memory.rom = 0; gba->memory.romSize = 0; + gba->memory.romMask = 0; gba->memory.hw.p = gba; int i; @@ -269,7 +270,7 @@ static void GBASetActiveRegion(struct ARMCore* cpu, uint32_t address) { case REGION_CART2: case REGION_CART2_EX: cpu->memory.activeRegion = memory->rom; - cpu->memory.activeMask = SIZE_CART0 - 1; + cpu->memory.activeMask = memory->romMask; if ((address & (SIZE_CART0 - 1)) < memory->romSize) { break; } @@ -893,6 +894,7 @@ void GBAPatch32(struct ARMCore* cpu, uint32_t address, int32_t value, int32_t* o _pristineCow(gba); if ((address & (SIZE_CART0 - 4)) >= gba->memory.romSize) { gba->memory.romSize = (address & (SIZE_CART0 - 4)) + 4; + gba->memory.romMask = toPow2(gba->memory.romSize) - 1; } LOAD_32(oldValue, address & (SIZE_CART0 - 4), gba->memory.rom); STORE_32(value, address & (SIZE_CART0 - 4), gba->memory.rom); @@ -960,6 +962,7 @@ void GBAPatch16(struct ARMCore* cpu, uint32_t address, int16_t value, int16_t* o _pristineCow(gba); if ((address & (SIZE_CART0 - 1)) >= gba->memory.romSize) { gba->memory.romSize = (address & (SIZE_CART0 - 2)) + 2; + gba->memory.romMask = toPow2(gba->memory.romSize) - 1; } LOAD_16(oldValue, address & (SIZE_CART0 - 2), gba->memory.rom); STORE_16(value, address & (SIZE_CART0 - 2), gba->memory.rom); @@ -1017,6 +1020,7 @@ void GBAPatch8(struct ARMCore* cpu, uint32_t address, int8_t value, int8_t* old) _pristineCow(gba); if ((address & (SIZE_CART0 - 1)) >= gba->memory.romSize) { gba->memory.romSize = (address & (SIZE_CART0 - 2)) + 2; + gba->memory.romMask = toPow2(gba->memory.romSize) - 1; } oldValue = ((int8_t*) memory->rom)[address & (SIZE_CART0 - 1)]; ((int8_t*) memory->rom)[address & (SIZE_CART0 - 1)] = value; diff --git a/src/gba/memory.h b/src/gba/memory.h index dd9d4066b..e0f059730 100644 --- a/src/gba/memory.h +++ b/src/gba/memory.h @@ -118,6 +118,7 @@ struct GBAMemory { struct GBACartridgeHardware hw; struct GBASavedata savedata; size_t romSize; + uint32_t romMask; uint16_t romID; int fullBios; diff --git a/src/util/math.h b/src/util/math.h index 06dca22e5..31709e20a 100644 --- a/src/util/math.h +++ b/src/util/math.h @@ -14,4 +14,46 @@ static inline uint32_t popcount32(unsigned bits) { return (((bits + (bits >> 4)) & 0xF0F0F0F) * 0x1010101) >> 24; } +static inline unsigned clz32(uint32_t bits) { +#if defined(__GNUC__) || __clang__ + return __builtin_clz(bits); +#else + static const int table[256] = { + 8, 7, 6, 6, 5, 5, 5, 5, 4, 4, 4, 4, 4, 4, 4, 4, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 + }; + + if (bits & 0xFF000000) { + return table[bits >> 24]; + } else if (bits & 0x00FF0000) { + return table[bits >> 16] + 8; + } else if (bits & 0x0000FF00) { + return table[bits >> 8] + 16; + } + return table[bits] + 24; +#endif +} + +static inline uint32_t toPow2(uint32_t bits) { + if (!bits) { + return 0; + } + unsigned lz = clz32(bits - 1); + return 1 << (32 - lz); +} + #endif From 91b53fc0c124d9011e700d35a33e6c2e63a09ef8 Mon Sep 17 00:00:00 2001 From: Jeffrey Pfau Date: Wed, 19 Aug 2015 22:17:09 -0700 Subject: [PATCH 11/19] Qt: Fix drag and drop on Windows --- CHANGES | 1 + src/platform/qt/Window.cpp | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGES b/CHANGES index 5371cb06a..7f994466b 100644 --- a/CHANGES +++ b/CHANGES @@ -2,6 +2,7 @@ Bugfixes: - Qt: Windows no longer spawn in the top left on first launch - Qt: Fix install path of XDG desktop file with DESTDIR + - Qt: Fix drag and drop on Windows Misc: - Qt: Window size command line options are now supported - Qt: Increase usability of key mapper diff --git a/src/platform/qt/Window.cpp b/src/platform/qt/Window.cpp index 721cec6a8..f7e45de32 100644 --- a/src/platform/qt/Window.cpp +++ b/src/platform/qt/Window.cpp @@ -507,7 +507,7 @@ void Window::dropEvent(QDropEvent* event) { return; } event->accept(); - m_controller->loadGame(url.path()); + m_controller->loadGame(url.toLocalFile()); } void Window::mouseDoubleClickEvent(QMouseEvent* event) { From c6efb396d4080e2e1fcb3107fe203339ccab2acb Mon Sep 17 00:00:00 2001 From: Jeffrey Pfau Date: Wed, 19 Aug 2015 23:09:46 -0700 Subject: [PATCH 12/19] Qt: Remove useless help icons in dialogs --- CHANGES | 1 + src/platform/qt/AboutScreen.cpp | 2 +- src/platform/qt/GDBWindow.cpp | 2 +- src/platform/qt/OverrideView.cpp | 2 +- src/platform/qt/SensorView.cpp | 2 +- src/platform/qt/SettingsView.cpp | 2 +- 6 files changed, 6 insertions(+), 5 deletions(-) diff --git a/CHANGES b/CHANGES index 7f994466b..c7711204d 100644 --- a/CHANGES +++ b/CHANGES @@ -7,6 +7,7 @@ Misc: - Qt: Window size command line options are now supported - Qt: Increase usability of key mapper - GBA Memory: Use a dynamically sized mask for ROM memory + - Qt: Remove useless help icons in dialogs 0.3.0: (2015-08-16) Features: diff --git a/src/platform/qt/AboutScreen.cpp b/src/platform/qt/AboutScreen.cpp index c898f2c16..a52f02e8c 100644 --- a/src/platform/qt/AboutScreen.cpp +++ b/src/platform/qt/AboutScreen.cpp @@ -12,7 +12,7 @@ using namespace QGBA; AboutScreen::AboutScreen(QWidget* parent) - : QDialog(parent) + : QDialog(parent, Qt::WindowTitleHint | Qt::WindowSystemMenuHint | Qt::WindowCloseButtonHint) { m_ui.setupUi(this); diff --git a/src/platform/qt/GDBWindow.cpp b/src/platform/qt/GDBWindow.cpp index 10e4b55d8..eeaa76ca9 100644 --- a/src/platform/qt/GDBWindow.cpp +++ b/src/platform/qt/GDBWindow.cpp @@ -18,7 +18,7 @@ using namespace QGBA; GDBWindow::GDBWindow(GDBController* controller, QWidget* parent) - : QDialog(parent) + : QDialog(parent, Qt::WindowTitleHint | Qt::WindowSystemMenuHint | Qt::WindowCloseButtonHint) , m_gdbController(controller) { setWindowFlags(windowFlags() & ~Qt::WindowFullscreenButtonHint); diff --git a/src/platform/qt/OverrideView.cpp b/src/platform/qt/OverrideView.cpp index b836f7ce0..35c3ab074 100644 --- a/src/platform/qt/OverrideView.cpp +++ b/src/platform/qt/OverrideView.cpp @@ -15,7 +15,7 @@ extern "C" { using namespace QGBA; OverrideView::OverrideView(GameController* controller, ConfigController* config, QWidget* parent) - : QDialog(parent) + : QDialog(parent, Qt::WindowTitleHint | Qt::WindowSystemMenuHint | Qt::WindowCloseButtonHint) , m_controller(controller) , m_config(config) { diff --git a/src/platform/qt/SensorView.cpp b/src/platform/qt/SensorView.cpp index d3757d0c8..bf270e348 100644 --- a/src/platform/qt/SensorView.cpp +++ b/src/platform/qt/SensorView.cpp @@ -12,7 +12,7 @@ using namespace QGBA; SensorView::SensorView(GameController* controller, InputController* input, QWidget* parent) - : QDialog(parent) + : QDialog(parent, Qt::WindowTitleHint | Qt::WindowSystemMenuHint | Qt::WindowCloseButtonHint) , m_controller(controller) , m_input(input) , m_rotation(input->rotationSource()) diff --git a/src/platform/qt/SettingsView.cpp b/src/platform/qt/SettingsView.cpp index e3fd3d8f7..244d25b9a 100644 --- a/src/platform/qt/SettingsView.cpp +++ b/src/platform/qt/SettingsView.cpp @@ -13,7 +13,7 @@ using namespace QGBA; SettingsView::SettingsView(ConfigController* controller, QWidget* parent) - : QDialog(parent) + : QDialog(parent, Qt::WindowTitleHint | Qt::WindowSystemMenuHint | Qt::WindowCloseButtonHint) , m_controller(controller) { m_ui.setupUi(this); From 534c9ca8f87ebf948404c6ad514c99e2deb1bac1 Mon Sep 17 00:00:00 2001 From: Jeffrey Pfau Date: Wed, 19 Aug 2015 23:52:59 -0700 Subject: [PATCH 13/19] Qt: Reenable double buffering, as disabling it broke some Windows configs --- CHANGES | 1 + src/platform/qt/Display.cpp | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGES b/CHANGES index c7711204d..20b1f3885 100644 --- a/CHANGES +++ b/CHANGES @@ -3,6 +3,7 @@ Bugfixes: - Qt: Windows no longer spawn in the top left on first launch - Qt: Fix install path of XDG desktop file with DESTDIR - Qt: Fix drag and drop on Windows + - Qt: Reenable double buffering, as disabling it broke some Windows configs Misc: - Qt: Window size command line options are now supported - Qt: Increase usability of key mapper diff --git a/src/platform/qt/Display.cpp b/src/platform/qt/Display.cpp index 3766baf7b..014162562 100644 --- a/src/platform/qt/Display.cpp +++ b/src/platform/qt/Display.cpp @@ -22,7 +22,7 @@ Display::Driver Display::s_driver = Display::Driver::QT; Display* Display::create(QWidget* parent) { #ifdef BUILD_GL - QGLFormat format(QGLFormat(QGL::Rgba | QGL::SingleBuffer)); + QGLFormat format(QGLFormat(QGL::Rgba | QGL::DoubleBuffer)); format.setSwapInterval(1); #endif From cb7bc351138da46a41990ce52f08fa3b1cc4a3b1 Mon Sep 17 00:00:00 2001 From: Jeffrey Pfau Date: Fri, 21 Aug 2015 00:14:22 -0700 Subject: [PATCH 14/19] GBA: Add "GBAContext" for threadless operation and use with libretro --- src/gba/supervisor/context.c | 156 +++++++++++++++++++++++++++++++ src/gba/supervisor/context.h | 38 ++++++++ src/platform/libretro/libretro.c | 82 +++++++--------- 3 files changed, 227 insertions(+), 49 deletions(-) create mode 100644 src/gba/supervisor/context.c create mode 100644 src/gba/supervisor/context.h diff --git a/src/gba/supervisor/context.c b/src/gba/supervisor/context.c new file mode 100644 index 000000000..c4161c763 --- /dev/null +++ b/src/gba/supervisor/context.c @@ -0,0 +1,156 @@ +/* Copyright (c) 2013-2015 Jeffrey Pfau + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +#include "gba/supervisor/context.h" + +#include "gba/supervisor/overrides.h" + +#include "util/memory.h" +#include "util/vfs.h" + +bool GBAContextInit(struct GBAContext* context, const char* port) { + context->gba = anonymousMemoryMap(sizeof(struct GBA)); + context->cpu = anonymousMemoryMap(sizeof(struct ARMCore)); + context->rom = 0; + context->save = 0; + context->renderer = 0; + + if (!context->gba || !context->cpu) { + if (context->gba) { + mappedMemoryFree(context->gba, sizeof(struct GBA)); + } + if (context->cpu) { + mappedMemoryFree(context->cpu, sizeof(struct ARMCore)); + } + return false; + } + + GBAConfigInit(&context->config, port); + if (port) { + GBAConfigLoad(&context->config); + } + + GBACreate(context->gba); + ARMSetComponents(context->cpu, &context->gba->d, 0, 0); + ARMInit(context->cpu); + + context->gba->sync = 0; + return true; +} + +void GBAContextDeinit(struct GBAContext* context) { + if (context->bios) { + context->bios->close(context->bios); + context->bios = 0; + } + if (context->rom) { + context->rom->close(context->rom); + context->rom = 0; + } + if (context->save) { + context->save->close(context->save); + context->save = 0; + } + ARMDeinit(context->cpu); + GBADestroy(context->gba); + mappedMemoryFree(context->gba, 0); + mappedMemoryFree(context->cpu, 0); + GBAConfigDeinit(&context->config); +} + +bool GBAContextLoadROM(struct GBAContext* context, const char* path, bool autoloadSave) { + context->rom = VFileOpen(path, O_RDONLY); + if (!context->rom) { + return false; + } + + if (!GBAIsROM(context->rom)) { + context->rom->close(context->rom); + context->rom = 0; + return false; + } + + if (autoloadSave) { + context->save = VDirOptionalOpenFile(0, path, 0, ".sav", O_RDWR | O_CREAT); + } + return true; +} + +bool GBAContextLoadROMFromVFile(struct GBAContext* context, struct VFile* rom, struct VFile* save) { + context->rom = rom; + if (!GBAIsROM(context->rom)) { + context->rom = 0; + return false; + } + context->save = save; + return true; +} + +bool GBAContextLoadBIOS(struct GBAContext* context, const char* path) { + context->bios = VFileOpen(path, O_RDONLY); + if (!context->bios) { + return false; + } + + if (!GBAIsBIOS(context->bios)) { + context->bios->close(context->bios); + context->bios = 0; + return false; + } + return true; +} + +bool GBAContextLoadBIOSFromVFile(struct GBAContext* context, struct VFile* bios) { + context->bios = bios; + if (!GBAIsBIOS(context->bios)) { + context->bios = 0; + return false; + } + return true; +} + +bool GBAContextStart(struct GBAContext* context) { + struct GBAOptions opts = {}; + GBAConfigMap(&context->config, &opts); + + if (context->renderer) { + GBAVideoAssociateRenderer(&context->gba->video, context->renderer); + } + + GBALoadROM(context->gba, context->rom, context->save, 0); + if (opts.useBios && context->bios) { + GBALoadBIOS(context->gba, context->bios); + } + + ARMReset(context->cpu); + + if (opts.skipBios) { + GBASkipBIOS(context->cpu); + } + + struct GBACartridgeOverride override; + const struct GBACartridge* cart = (const struct GBACartridge*) context->gba->memory.rom; + memcpy(override.id, &cart->id, sizeof(override.id)); + if (GBAOverrideFind(GBAConfigGetOverrides(&context->config), &override)) { + GBAOverrideApply(context->gba, &override); + } + GBAConfigFreeOpts(&opts); + return true; +} + +void GBAContextStop(struct GBAContext* context) { + UNUSED(context); + // TODO? +} + +void GBAContextFrame(struct GBAContext* context, uint16_t keys) { + int activeKeys = keys; + context->gba->keySource = &activeKeys; + + int frameCounter = context->gba->video.frameCounter; + while (frameCounter == context->gba->video.frameCounter) { + ARMRunLoop(context->cpu); + } +} diff --git a/src/gba/supervisor/context.h b/src/gba/supervisor/context.h new file mode 100644 index 000000000..15b4d3949 --- /dev/null +++ b/src/gba/supervisor/context.h @@ -0,0 +1,38 @@ +/* Copyright (c) 2013-2015 Jeffrey Pfau + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +#ifndef CONTEXT_H +#define CONTEXT_H + +#include "util/common.h" + +#include "gba/supervisor/config.h" +#include "gba/input.h" + +struct GBAContext { + struct GBA* gba; + struct ARMCore* cpu; + struct GBAVideoRenderer* renderer; + struct VFile* rom; + struct VFile* save; + struct VFile* bios; + struct GBAConfig config; + struct GBAOptions opts; + struct GBAInputMap inputMap; +}; + +bool GBAContextInit(struct GBAContext* context, const char* port); +void GBAContextDeinit(struct GBAContext* context); + +bool GBAContextLoadROM(struct GBAContext* context, const char* path, bool autoloadSave); +bool GBAContextLoadROMFromVFile(struct GBAContext* context, struct VFile* rom, struct VFile* save); +bool GBAContextLoadBIOS(struct GBAContext* context, const char* path); +bool GBAContextLoadBIOSFromVFile(struct GBAContext* context, struct VFile* bios); + +bool GBAContextStart(struct GBAContext* context); +void GBAContextStop(struct GBAContext* context); +void GBAContextFrame(struct GBAContext* context, uint16_t keys); + +#endif diff --git a/src/platform/libretro/libretro.c b/src/platform/libretro/libretro.c index 9a986dd0e..caf2c9f74 100644 --- a/src/platform/libretro/libretro.c +++ b/src/platform/libretro/libretro.c @@ -7,12 +7,9 @@ #include "util/common.h" -#include "gba/gba.h" -#include "gba/interface.h" #include "gba/renderers/video-software.h" #include "gba/serialize.h" -#include "gba/supervisor/overrides.h" -#include "gba/video.h" +#include "gba/supervisor/context.h" #include "util/circle-buffer.h" #include "util/vfs.h" @@ -37,14 +34,10 @@ static void _setRumble(struct GBARumble* rumble, int enable); static uint8_t _readLux(struct GBALuminanceSource* lux); static void _updateLux(struct GBALuminanceSource* lux); -static struct GBA gba; -static struct ARMCore cpu; +static struct GBAContext context; static struct GBAVideoSoftwareRenderer renderer; -static struct VFile* rom; static void* data; -static struct VFile* save; static void* savedata; -static struct VFile* bios; static struct GBAAVStream stream; static int rumbleLevel; static struct CircleBuffer rumbleHistory; @@ -162,53 +155,49 @@ void retro_init(void) { stream.postAudioBuffer = _postAudioBuffer; stream.postVideoFrame = _postVideoFrame; - GBACreate(&gba); - ARMSetComponents(&cpu, &gba.d, 0, 0); - ARMInit(&cpu); - gba.logLevel = 0; // TODO: Settings - gba.logHandler = GBARetroLog; - gba.stream = &stream; - gba.idleOptimization = IDLE_LOOP_REMOVE; // TODO: Settings + GBAContextInit(&context, 0); + struct GBAOptions opts = { + .useBios = true, + .logLevel = 0, + .idleOptimization = IDLE_LOOP_REMOVE + }; + GBAConfigLoadDefaults(&context.config, &opts); + context.gba->logHandler = GBARetroLog; + context.gba->stream = &stream; if (rumbleCallback) { - gba.rumble = &rumble; + context.gba->rumble = &rumble; } - gba.luminanceSource = &lux; - rom = 0; + context.gba->luminanceSource = &lux; const char* sysDir = 0; if (environCallback(RETRO_ENVIRONMENT_GET_SYSTEM_DIRECTORY, &sysDir)) { char biosPath[PATH_MAX]; snprintf(biosPath, sizeof(biosPath), "%s%s%s", sysDir, PATH_SEP, "gba_bios.bin"); - bios = VFileOpen(biosPath, O_RDONLY); + struct VFile* bios = VFileOpen(biosPath, O_RDONLY); if (bios) { - GBALoadBIOS(&gba, bios); + GBAContextLoadBIOSFromVFile(&context, bios); } } GBAVideoSoftwareRendererCreate(&renderer); renderer.outputBuffer = malloc(256 * VIDEO_VERTICAL_PIXELS * BYTES_PER_PIXEL); renderer.outputBufferStride = 256; - GBAVideoAssociateRenderer(&gba.video, &renderer.d); + GBAVideoAssociateRenderer(&context.gba->video, &renderer.d); - GBAAudioResizeBuffer(&gba.audio, SAMPLES); + GBAAudioResizeBuffer(&context.gba->audio, SAMPLES); #if RESAMPLE_LIBRARY == RESAMPLE_BLIP_BUF - blip_set_rates(gba.audio.left, GBA_ARM7TDMI_FREQUENCY, 32768); - blip_set_rates(gba.audio.right, GBA_ARM7TDMI_FREQUENCY, 32768); + blip_set_rates(context.gba->audio.left, GBA_ARM7TDMI_FREQUENCY, 32768); + blip_set_rates(context.gba->audio.right, GBA_ARM7TDMI_FREQUENCY, 32768); #endif } void retro_deinit(void) { - if (bios) { - bios->close(bios); - bios = 0; - } - GBADestroy(&gba); + GBAContextDeinit(&context); } void retro_run(void) { - int keys; - gba.keySource = &keys; + uint16_t keys; inputPollCallback(); keys = 0; @@ -243,14 +232,11 @@ void retro_run(void) { } } - int frameCount = gba.video.frameCounter; - while (gba.video.frameCounter == frameCount) { - ARMRunLoop(&cpu); - } + GBAContextFrame(&context, keys); } void retro_reset(void) { - ARMReset(&cpu); + ARMReset(context.cpu); if (rumbleCallback) { CircleBufferClear(&rumbleHistory); @@ -258,6 +244,7 @@ void retro_reset(void) { } bool retro_load_game(const struct retro_game_info* game) { + struct VFile* rom; if (game->data) { data = malloc(game->size); memcpy(data, game->data, game->size); @@ -270,26 +257,23 @@ bool retro_load_game(const struct retro_game_info* game) { return false; } if (!GBAIsROM(rom)) { + rom->close(rom); + free(data); return false; } savedata = malloc(SIZE_CART_FLASH1M); - save = VFileFromMemory(savedata, SIZE_CART_FLASH1M); + struct VFile* save = VFileFromMemory(savedata, SIZE_CART_FLASH1M); - GBALoadROM(&gba, rom, save, game->path); - GBAOverrideApplyDefaults(&gba); - - ARMReset(&cpu); + GBAContextLoadROMFromVFile(&context, rom, save); + GBAContextStart(&context); return true; } void retro_unload_game(void) { - rom->close(rom); - rom = 0; + GBAContextStop(&context); free(data); data = 0; - save->close(save); - save = 0; free(savedata); savedata = 0; CircleBufferDeinit(&rumbleHistory); @@ -303,7 +287,7 @@ bool retro_serialize(void* data, size_t size) { if (size != retro_serialize_size()) { return false; } - GBASerialize(&gba, data); + GBASerialize(context.gba, data); return true; } @@ -311,7 +295,7 @@ bool retro_unserialize(const void* data, size_t size) { if (size != retro_serialize_size()) { return false; } - GBADeserialize(&gba, data); + GBADeserialize(context.gba, data); return true; } @@ -353,7 +337,7 @@ size_t retro_get_memory_size(unsigned id) { if (id != RETRO_MEMORY_SAVE_RAM) { return 0; } - switch (gba.memory.savedata.type) { + switch (context.gba->memory.savedata.type) { case SAVEDATA_AUTODETECT: case SAVEDATA_FLASH1M: return SIZE_CART_FLASH1M; From cad90d17688401e13de611d1c1fdc4ad0e97325f Mon Sep 17 00:00:00 2001 From: Jeffrey Pfau Date: Fri, 21 Aug 2015 00:28:47 -0700 Subject: [PATCH 15/19] Test: Use GBAContext for mgba-fuzz --- src/platform/test/fuzz-main.c | 63 +++++++++++++---------------------- 1 file changed, 23 insertions(+), 40 deletions(-) diff --git a/src/platform/test/fuzz-main.c b/src/platform/test/fuzz-main.c index 5d75996db..c7d6e91ad 100644 --- a/src/platform/test/fuzz-main.c +++ b/src/platform/test/fuzz-main.c @@ -3,8 +3,8 @@ * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -#include "gba/supervisor/thread.h" #include "gba/supervisor/config.h" +#include "gba/supervisor/context.h" #include "gba/gba.h" #include "gba/renderers/video-software.h" #include "gba/serialize.h" @@ -34,7 +34,7 @@ struct FuzzOpts { char* ssOverlay; }; -static void _GBAFuzzRunloop(struct GBA* gba, int frames); +static void _GBAFuzzRunloop(struct GBAContext* context, int frames); static void _GBAFuzzShutdown(int signal); static bool _parseFuzzOpts(struct SubParser* parser, struct GBAConfig* config, int option, const char* arg); @@ -51,44 +51,27 @@ int main(int argc, char** argv) { .opts = &fuzzOpts }; - struct GBAConfig config; - GBAConfigInit(&config, "fuzz"); - GBAConfigLoad(&config); - + struct GBAContext context; + GBAContextInit(&context, "fuzz"); struct GBAOptions opts = { .idleOptimization = IDLE_LOOP_DETECT }; - GBAConfigLoadDefaults(&config, &opts); + GBAConfigLoadDefaults(&context.config, &opts); + GBAConfigFreeOpts(&opts); struct GBAArguments args; - bool parsed = parseArguments(&args, &config, argc, argv, &subparser); + bool parsed = parseArguments(&args, &context.config, argc, argv, &subparser); if (!parsed || args.showHelp) { usage(argv[0], FUZZ_USAGE); freeArguments(&args); - GBAConfigFreeOpts(&opts); - GBAConfigDeinit(&config); + GBAContextDeinit(&context); return !parsed; } - struct GBA* gba = anonymousMemoryMap(sizeof(struct GBA)); - struct ARMCore* cpu = anonymousMemoryMap(sizeof(struct ARMCore)); struct VFile* rom = VFileOpen(args.fname, O_RDONLY); - GBACreate(gba); - ARMSetComponents(cpu, &gba->d, 0, 0); - ARMInit(cpu); - gba->sync = 0; - gba->hardCrash = false; - - GBALoadROM(gba, rom, 0, 0); - ARMReset(cpu); - - struct GBACartridgeOverride override; - const struct GBACartridge* cart = (const struct GBACartridge*) gba->memory.rom; - memcpy(override.id, &cart->id, sizeof(override.id)); - if (GBAOverrideFind(GBAConfigGetOverrides(&config), &override)) { - GBAOverrideApply(gba, &override); - } + context.gba->hardCrash = false; + GBAContextLoadROMFromVFile(&context, rom, 0); struct GBAVideoSoftwareRenderer renderer; renderer.outputBuffer = 0; @@ -101,9 +84,11 @@ int main(int argc, char** argv) { GBAVideoSoftwareRendererCreate(&renderer); renderer.outputBuffer = malloc(256 * 256 * 4); renderer.outputBufferStride = 256; - GBAVideoAssociateRenderer(&gba->video, &renderer.d); + GBAVideoAssociateRenderer(&context.gba->video, &renderer.d); } + GBAContextStart(&context); + if (fuzzOpts.savestate) { savestate = VFileOpen(fuzzOpts.savestate, O_RDONLY); free(fuzzOpts.savestate); @@ -117,12 +102,12 @@ int main(int argc, char** argv) { } if (savestate) { if (!savestateOverlay) { - GBALoadStateNamed(gba, savestate); + GBALoadStateNamed(context.gba, savestate); } else { struct GBASerializedState* state = GBAAllocateState(); savestate->read(savestate, state, sizeof(*state)); savestateOverlay->read(savestateOverlay, (uint8_t*) state + overlayOffset, sizeof(*state) - overlayOffset); - GBADeserialize(gba, state); + GBADeserialize(context.gba, state); GBADeallocateState(state); savestateOverlay->close(savestateOverlay); savestateOverlay = 0; @@ -131,12 +116,10 @@ int main(int argc, char** argv) { savestate = 0; } - GBAConfigMap(&config, &opts); + blip_set_rates(context.gba->audio.left, GBA_ARM7TDMI_FREQUENCY, 0x8000); + blip_set_rates(context.gba->audio.right, GBA_ARM7TDMI_FREQUENCY, 0x8000); - blip_set_rates(gba->audio.left, GBA_ARM7TDMI_FREQUENCY, 0x8000); - blip_set_rates(gba->audio.right, GBA_ARM7TDMI_FREQUENCY, 0x8000); - - _GBAFuzzRunloop(gba, fuzzOpts.frames); + _GBAFuzzRunloop(&context, fuzzOpts.frames); if (savestate) { savestate->close(savestate); @@ -144,9 +127,9 @@ int main(int argc, char** argv) { if (savestateOverlay) { savestateOverlay->close(savestateOverlay); } - GBAConfigFreeOpts(&opts); + GBAContextStop(&context); + GBAContextDeinit(&context); freeArguments(&args); - GBAConfigDeinit(&config); if (renderer.outputBuffer) { free(renderer.outputBuffer); } @@ -154,10 +137,10 @@ int main(int argc, char** argv) { return 0; } -static void _GBAFuzzRunloop(struct GBA* gba, int frames) { +static void _GBAFuzzRunloop(struct GBAContext* context, int frames) { do { - ARMRunLoop(gba->cpu); - } while (gba->video.frameCounter < frames && !_dispatchExiting); + GBAContextFrame(context, 0); + } while (context->gba->video.frameCounter < frames && !_dispatchExiting); } static void _GBAFuzzShutdown(int signal) { From 2dbf207f9b2257ad03b4349971c26c48aec4b564 Mon Sep 17 00:00:00 2001 From: Jeffrey Pfau Date: Fri, 21 Aug 2015 21:22:52 -0700 Subject: [PATCH 16/19] GUI: Merge file-select changes from Wii branch --- src/util/gui/file-select.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/util/gui/file-select.c b/src/util/gui/file-select.c index cc13a2b81..05a0fbb67 100644 --- a/src/util/gui/file-select.c +++ b/src/util/gui/file-select.c @@ -56,6 +56,7 @@ bool selectFile(const struct GUIParams* params, const char* basePath, char* outP strncpy(currentPath, basePath, sizeof(currentPath)); int oldInput = -1; size_t fileIndex = 0; + size_t start = 0; struct FileList currentFiles; FileListInit(¤tFiles, 0); @@ -72,6 +73,12 @@ bool selectFile(const struct GUIParams* params, const char* basePath, char* outP if (newInput & (1 << GUI_INPUT_DOWN) && fileIndex < FileListSize(¤tFiles) - 1) { ++fileIndex; } + if (fileIndex < start) { + start = fileIndex; + } + while ((fileIndex - start + 4) * GUIFontHeight(params->font) > params->height) { + ++start; + } if (newInput & (1 << GUI_INPUT_CANCEL)) { _cleanFiles(¤tFiles); FileListDeinit(¤tFiles); @@ -101,7 +108,7 @@ bool selectFile(const struct GUIParams* params, const char* basePath, char* outP GUIFontPrintf(params->font, 0, y, GUI_TEXT_LEFT, 0xFFFFFFFF, "Current directory: %s", currentPath); y += 2 * GUIFontHeight(params->font); size_t i; - for (i = 0; i < FileListSize(¤tFiles); ++i) { + for (i = start; i < FileListSize(¤tFiles); ++i) { int color = 0xE0A0A0A0; char bullet = ' '; if (i == fileIndex) { From b278bbb23d0f2ffcc93b598935532ed412babbdc Mon Sep 17 00:00:00 2001 From: Jeffrey Pfau Date: Sat, 22 Aug 2015 00:31:12 -0700 Subject: [PATCH 17/19] PSP2: Translate POSIX open modes to SCE open modes --- src/util/vfs.c | 24 +++++++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/src/util/vfs.c b/src/util/vfs.c index b1735136d..942b4c5b5 100644 --- a/src/util/vfs.c +++ b/src/util/vfs.c @@ -35,7 +35,29 @@ struct VFile* VFileOpen(const char* path, int flags) { } return VFileFOpen(path, chflags); #elif defined(PSP2) - return VFileOpenSce(path, flags, 0666); + int sceFlags = PSP2_O_RDONLY; + switch (flags & O_ACCMODE) { + case O_WRONLY: + sceFlags = PSP2_O_WRONLY; + break; + case O_RDWR: + sceFlags = PSP2_O_RDWR; + break; + case O_RDONLY: + sceFlags = PSP2_O_RDONLY; + break; + } + + if (flags & O_APPEND) { + sceFlags |= PSP2_O_APPEND; + } + if (flags & O_TRUNC) { + sceFlags |= PSP2_O_TRUNC; + } + if (flags & O_CREAT) { + sceFlags |= PSP2_O_CREAT; + } + return VFileOpenSce(path, sceFlags, 0666); #else return VFileOpenFD(path, flags); #endif From 540e1ff1e65e7616daf94eab19a0994fbbc5bb24 Mon Sep 17 00:00:00 2001 From: Jeffrey Pfau Date: Sat, 22 Aug 2015 00:39:40 -0700 Subject: [PATCH 18/19] PSP2: Make GBAConfig able to find PSP2 paths --- src/gba/supervisor/config.c | 56 ++++++++++++++++++++++--------------- 1 file changed, 33 insertions(+), 23 deletions(-) diff --git a/src/gba/supervisor/config.c b/src/gba/supervisor/config.c index ecf175dbb..0e04f65b5 100644 --- a/src/gba/supervisor/config.c +++ b/src/gba/supervisor/config.c @@ -18,6 +18,10 @@ #include #endif +#ifdef PSP2 +#include +#endif + #define SECTION_NAME_MAX 128 static const char* _lookupValue(const struct GBAConfig* config, const char* key) { @@ -135,13 +139,8 @@ bool GBAConfigSavePath(const struct GBAConfig* config, const char* path) { } void GBAConfigMakePortable(const struct GBAConfig* config) { - struct VFile* portable; -#ifndef _WIN32 - char out[PATH_MAX]; - getcwd(out, PATH_MAX); - strncat(out, PATH_SEP "portable.ini", PATH_MAX - strlen(out)); - portable = VFileOpen(out, O_WRONLY | O_CREAT); -#else + struct VFile* portable = 0; +#ifdef _WIN32 char out[MAX_PATH]; wchar_t wpath[MAX_PATH]; wchar_t wprojectName[MAX_PATH]; @@ -152,6 +151,13 @@ void GBAConfigMakePortable(const struct GBAConfig* config) { WideCharToMultiByte(CP_UTF8, 0, wpath, -1, out, MAX_PATH, 0, 0); StringCchCatA(out, MAX_PATH, "\\portable.ini"); portable = VFileOpen(out, O_WRONLY | O_CREAT); +#elif defined(PSP2) + // Already portable +#else + char out[PATH_MAX]; + getcwd(out, PATH_MAX); + strncat(out, PATH_SEP "portable.ini", PATH_MAX - strlen(out)); + portable = VFileOpen(out, O_WRONLY | O_CREAT); #endif if (portable) { portable->close(portable); @@ -161,22 +167,7 @@ void GBAConfigMakePortable(const struct GBAConfig* config) { void GBAConfigDirectory(char* out, size_t outLength) { struct VFile* portable; -#ifndef _WIN32 - getcwd(out, outLength); - strncat(out, PATH_SEP "portable.ini", outLength - strlen(out)); - portable = VFileOpen(out, O_RDONLY); - if (portable) { - getcwd(out, outLength); - portable->close(portable); - return; - } - - char* home = getenv("HOME"); - snprintf(out, outLength, "%s/.config", home); - mkdir(out, 0755); - snprintf(out, outLength, "%s/.config/%s", home, binaryName); - mkdir(out, 0755); -#else +#ifdef _WIN32 wchar_t wpath[MAX_PATH]; wchar_t wprojectName[MAX_PATH]; MultiByteToWideChar(CP_UTF8, 0, projectName, -1, wprojectName, MAX_PATH); @@ -196,6 +187,25 @@ void GBAConfigDirectory(char* out, size_t outLength) { CreateDirectoryW(wpath, NULL); } WideCharToMultiByte(CP_UTF8, 0, wpath, -1, out, outLength, 0, 0); +#elif defined(PSP2) + UNUSED(portable); + snprintf(out, outLength, "cache0:/%s", binaryName); + sceIoMkdir(out, 0777); +#else + getcwd(out, outLength); + strncat(out, PATH_SEP "portable.ini", outLength - strlen(out)); + portable = VFileOpen(out, O_RDONLY); + if (portable) { + getcwd(out, outLength); + portable->close(portable); + return; + } + + char* home = getenv("HOME"); + snprintf(out, outLength, "%s/.config", home); + mkdir(out, 0755); + snprintf(out, outLength, "%s/.config/%s", home, binaryName); + mkdir(out, 0755); #endif } From c9b01e0aff832ea0e2acc0ac56dda76b66117afd Mon Sep 17 00:00:00 2001 From: Jeffrey Pfau Date: Sat, 22 Aug 2015 00:41:24 -0700 Subject: [PATCH 19/19] PSP2: Use GBAContext --- src/platform/psp2/main.c | 4 +- src/platform/psp2/psp2-context.c | 228 ++++++++++++------------------- src/platform/psp2/psp2-context.h | 2 +- 3 files changed, 94 insertions(+), 140 deletions(-) diff --git a/src/platform/psp2/main.c b/src/platform/psp2/main.c index 02f1e6f6e..41ba491c5 100644 --- a/src/platform/psp2/main.c +++ b/src/platform/psp2/main.c @@ -72,7 +72,9 @@ int main() { if (!selectFile(¶ms, "cache0:", path, sizeof(path), "gba")) { break; } - GBAPSP2LoadROM(path); + if (!GBAPSP2LoadROM(path)) { + continue; + } GBAPSP2Runloop(); GBAPSP2UnloadROM(); } diff --git a/src/platform/psp2/psp2-context.c b/src/platform/psp2/psp2-context.c index efb49b671..7e7a1393a 100644 --- a/src/platform/psp2/psp2-context.c +++ b/src/platform/psp2/psp2-context.c @@ -8,8 +8,7 @@ #include "gba/gba.h" #include "gba/input.h" #include "gba/audio.h" -#include "gba/supervisor/overrides.h" -#include "gba/video.h" +#include "gba/supervisor/context.h" #include "gba/renderers/video-software.h" #include "util/circle-buffer.h" @@ -27,13 +26,8 @@ #include -static char gameName[13]; -static struct GBA* gba; -static struct ARMCore* cpu; -static struct VFile* rom; -static struct VFile* save; +static struct GBAContext context; static struct GBAVideoSoftwareRenderer renderer; -static struct GBAInputMap inputMap; static vita2d_texture* tex; static Thread audioThread; @@ -81,172 +75,130 @@ static THREAD_ENTRY _audioThread(void* context) { } void GBAPSP2Setup() { - GBAInputMapInit(&inputMap); - _mapVitaKey(&inputMap, PSP2_CTRL_CROSS, GBA_KEY_A); - _mapVitaKey(&inputMap, PSP2_CTRL_CIRCLE, GBA_KEY_B); - _mapVitaKey(&inputMap, PSP2_CTRL_START, GBA_KEY_START); - _mapVitaKey(&inputMap, PSP2_CTRL_SELECT, GBA_KEY_SELECT); - _mapVitaKey(&inputMap, PSP2_CTRL_UP, GBA_KEY_UP); - _mapVitaKey(&inputMap, PSP2_CTRL_DOWN, GBA_KEY_DOWN); - _mapVitaKey(&inputMap, PSP2_CTRL_LEFT, GBA_KEY_LEFT); - _mapVitaKey(&inputMap, PSP2_CTRL_RIGHT, GBA_KEY_RIGHT); - _mapVitaKey(&inputMap, PSP2_CTRL_LTRIGGER, GBA_KEY_L); - _mapVitaKey(&inputMap, PSP2_CTRL_RTRIGGER, GBA_KEY_R); + GBAContextInit(&context, 0); + struct GBAOptions opts = { + .useBios = true, + .logLevel = 0, + .idleOptimization = IDLE_LOOP_DETECT + }; + GBAConfigLoadDefaults(&context.config, &opts); + _mapVitaKey(&context.inputMap, PSP2_CTRL_CROSS, GBA_KEY_A); + _mapVitaKey(&context.inputMap, PSP2_CTRL_CIRCLE, GBA_KEY_B); + _mapVitaKey(&context.inputMap, PSP2_CTRL_START, GBA_KEY_START); + _mapVitaKey(&context.inputMap, PSP2_CTRL_SELECT, GBA_KEY_SELECT); + _mapVitaKey(&context.inputMap, PSP2_CTRL_UP, GBA_KEY_UP); + _mapVitaKey(&context.inputMap, PSP2_CTRL_DOWN, GBA_KEY_DOWN); + _mapVitaKey(&context.inputMap, PSP2_CTRL_LEFT, GBA_KEY_LEFT); + _mapVitaKey(&context.inputMap, PSP2_CTRL_RIGHT, GBA_KEY_RIGHT); + _mapVitaKey(&context.inputMap, PSP2_CTRL_LTRIGGER, GBA_KEY_L); + _mapVitaKey(&context.inputMap, PSP2_CTRL_RTRIGGER, GBA_KEY_R); struct GBAAxis desc = { GBA_KEY_DOWN, GBA_KEY_UP, 192, 64 }; - GBAInputBindAxis(&inputMap, PSP2_INPUT, 0, &desc); + GBAInputBindAxis(&context.inputMap, PSP2_INPUT, 0, &desc); desc = (struct GBAAxis) { GBA_KEY_RIGHT, GBA_KEY_LEFT, 192, 64 }; - GBAInputBindAxis(&inputMap, PSP2_INPUT, 1, &desc); + GBAInputBindAxis(&context.inputMap, PSP2_INPUT, 1, &desc); tex = vita2d_create_empty_texture_format(256, 256, SCE_GXM_TEXTURE_FORMAT_X8U8U8U8_1BGR); + GBAVideoSoftwareRendererCreate(&renderer); renderer.outputBuffer = vita2d_texture_get_datap(tex); renderer.outputBufferStride = 256; + GBAVideoAssociateRenderer(&context.gba->video, &renderer.d); + printf("%s starting", projectName); } -void GBAPSP2LoadROM(const char* path) { - GBAVideoSoftwareRendererCreate(&renderer); - - gba = anonymousMemoryMap(sizeof(struct GBA)); - cpu = anonymousMemoryMap(sizeof(struct ARMCore)); - - printf("GBA: %08X", gba); - printf("CPU: %08X", cpu); - - rom = VFileOpenSce(path, PSP2_O_RDONLY, 0666); - save = VDirOptionalOpenFile(0, path, 0, ".sav", PSP2_O_RDWR | PSP2_O_CREAT); - - printf("ROM: %08X", rom); - printf("Save: %08X", save); - - GBACreate(gba); - ARMSetComponents(cpu, &gba->d, 0, 0); - ARMInit(cpu); - printf("%s initialized.", "CPU"); - - gba->sync = 0; - - GBAVideoAssociateRenderer(&gba->video, &renderer.d); - - GBALoadROM(gba, rom, save, 0); - GBAOverrideApplyDefaults(gba); - GBAGetGameTitle(gba, gameName); - printf("ROM loaded: %s", gameName); - - ARMReset(cpu); +bool GBAPSP2LoadROM(const char* path) { + if (!GBAContextLoadROM(&context, path, true)) { + printf("%s failed to load!", path); + return false; + } + printf("%s loaded, starting...", path); + GBAContextStart(&context); + char gameTitle[13]; + GBAGetGameTitle(context.gba, gameTitle); + printf("%s started!", gameTitle); double ratio = GBAAudioCalculateRatio(1, 60, 1); - blip_set_rates(gba->audio.left, GBA_ARM7TDMI_FREQUENCY, 48000 * ratio); - blip_set_rates(gba->audio.right, GBA_ARM7TDMI_FREQUENCY, 48000 * ratio); + blip_set_rates(context.gba->audio.left, GBA_ARM7TDMI_FREQUENCY, 48000 * ratio); + blip_set_rates(context.gba->audio.right, GBA_ARM7TDMI_FREQUENCY, 48000 * ratio); CircleBufferInit(&audioContext.buffer, PSP2_AUDIO_BUFFER_SIZE * sizeof(struct GBAStereoSample)); MutexInit(&audioContext.mutex); ConditionInit(&audioContext.cond); audioContext.running = true; ThreadCreate(&audioThread, _audioThread, &audioContext); - - printf("%s all set and ready to roll.", projectName); + return true; } void GBAPSP2Runloop(void) { int activeKeys = 0; - gba->keySource = &activeKeys; bool fsToggle = false; - int frameCounter = 0; while (true) { - ARMRunLoop(cpu); + SceCtrlData pad; + sceCtrlPeekBufferPositive(0, &pad, 1); + if (pad.buttons & PSP2_CTRL_TRIANGLE) { + break; + } + if (pad.buttons & PSP2_CTRL_SQUARE) { + if (!fsToggle) { + fullscreen = !fullscreen; + } + fsToggle = true; + } else { + fsToggle = false; + } - if (frameCounter != gba->video.frameCounter) { - SceCtrlData pad; - sceCtrlPeekBufferPositive(0, &pad, 1); - if (pad.buttons & PSP2_CTRL_TRIANGLE) { + activeKeys = GBAInputMapKeyBits(&context.inputMap, PSP2_INPUT, pad.buttons, 0); + enum GBAKey angles = GBAInputMapAxis(&context.inputMap, PSP2_INPUT, 0, pad.ly); + if (angles != GBA_KEY_NONE) { + activeKeys |= 1 << angles; + } + angles = GBAInputMapAxis(&context.inputMap, PSP2_INPUT, 1, pad.lx); + if (angles != GBA_KEY_NONE) { + activeKeys |= 1 << angles; + } + angles = GBAInputMapAxis(&context.inputMap, PSP2_INPUT, 2, pad.ry); + if (angles != GBA_KEY_NONE) { + activeKeys |= 1 << angles; + } + angles = GBAInputMapAxis(&context.inputMap, PSP2_INPUT, 3, pad.rx); + if (angles != GBA_KEY_NONE) { + activeKeys |= 1 << angles; + } + + GBAContextFrame(&context, activeKeys); + + MutexLock(&audioContext.mutex); + while (blip_samples_avail(context.gba->audio.left) >= PSP2_SAMPLES) { + if (CircleBufferSize(&audioContext.buffer) + PSP2_SAMPLES * sizeof(struct GBAStereoSample) > CircleBufferCapacity(&audioContext.buffer)) { break; } - if (pad.buttons & PSP2_CTRL_SQUARE) { - if (!fsToggle) { - fullscreen = !fullscreen; - } - fsToggle = true; - } else { - fsToggle = false; + struct GBAStereoSample samples[PSP2_SAMPLES]; + blip_read_samples(context.gba->audio.left, &samples[0].left, PSP2_SAMPLES, true); + blip_read_samples(context.gba->audio.right, &samples[0].right, PSP2_SAMPLES, true); + int i; + for (i = 0; i < PSP2_SAMPLES; ++i) { + CircleBufferWrite16(&audioContext.buffer, samples[i].left); + CircleBufferWrite16(&audioContext.buffer, samples[i].right); } - - activeKeys = GBAInputMapKeyBits(&inputMap, PSP2_INPUT, pad.buttons, 0); - enum GBAKey angles = GBAInputMapAxis(&inputMap, PSP2_INPUT, 0, pad.ly); - if (angles != GBA_KEY_NONE) { - activeKeys |= 1 << angles; - } - angles = GBAInputMapAxis(&inputMap, PSP2_INPUT, 1, pad.lx); - if (angles != GBA_KEY_NONE) { - activeKeys |= 1 << angles; - } - angles = GBAInputMapAxis(&inputMap, PSP2_INPUT, 2, pad.ry); - if (angles != GBA_KEY_NONE) { - activeKeys |= 1 << angles; - } - angles = GBAInputMapAxis(&inputMap, PSP2_INPUT, 3, pad.rx); - if (angles != GBA_KEY_NONE) { - activeKeys |= 1 << angles; - } - - MutexLock(&audioContext.mutex); - while (blip_samples_avail(gba->audio.left) >= PSP2_SAMPLES) { - if (CircleBufferSize(&audioContext.buffer) + PSP2_SAMPLES * sizeof(struct GBAStereoSample) > CircleBufferCapacity(&audioContext.buffer)) { - break; - } - struct GBAStereoSample samples[PSP2_SAMPLES]; - blip_read_samples(gba->audio.left, &samples[0].left, PSP2_SAMPLES, true); - blip_read_samples(gba->audio.right, &samples[0].right, PSP2_SAMPLES, true); - int i; - for (i = 0; i < PSP2_SAMPLES; ++i) { - CircleBufferWrite16(&audioContext.buffer, samples[i].left); - CircleBufferWrite16(&audioContext.buffer, samples[i].right); - } - } - ConditionWake(&audioContext.cond); - MutexUnlock(&audioContext.mutex); - - vita2d_start_drawing(); - vita2d_clear_screen(); - GBAPSP2Draw(); - vita2d_end_drawing(); - vita2d_swap_buffers(); - - frameCounter = gba->video.frameCounter; } + ConditionWake(&audioContext.cond); + MutexUnlock(&audioContext.mutex); + + vita2d_start_drawing(); + vita2d_clear_screen(); + GBAPSP2Draw(); + vita2d_end_drawing(); + vita2d_swap_buffers(); } } void GBAPSP2UnloadROM(void) { - printf("%s shutting down...", projectName); - - ARMDeinit(cpu); - GBADestroy(gba); - - rom->close(rom); - save->close(save); - - MutexLock(&audioContext.mutex); - audioContext.running = false; - ConditionWake(&audioContext.cond); - MutexUnlock(&audioContext.mutex); - ThreadJoin(audioThread); - CircleBufferDeinit(&audioContext.buffer); - MutexDeinit(&audioContext.mutex); - ConditionDeinit(&audioContext.cond); - - mappedMemoryFree(gba, 0); - mappedMemoryFree(cpu, 0); - - gba = 0; - cpu = 0; - rom = 0; - save = 0; + GBAContextStop(&context); } void GBAPSP2Teardown(void) { - GBAInputMapDeinit(&inputMap); - + GBAContextDeinit(&context); vita2d_free_texture(tex); } diff --git a/src/platform/psp2/psp2-context.h b/src/platform/psp2/psp2-context.h index 45fca3af6..9680b20ff 100644 --- a/src/platform/psp2/psp2-context.h +++ b/src/platform/psp2/psp2-context.h @@ -11,7 +11,7 @@ void GBAPSP2Setup(void); void GBAPSP2Teardown(void); -void GBAPSP2LoadROM(const char* path); +bool GBAPSP2LoadROM(const char* path); void GBAPSP2Runloop(void); void GBAPSP2UnloadROM(void);