diff --git a/pcsx2/Recording/RecordingInputManager.h b/pcsx2/Recording/RecordingInputManager.h
deleted file mode 100644
index 42c7bf40ad..0000000000
--- a/pcsx2/Recording/RecordingInputManager.h
+++ /dev/null
@@ -1,40 +0,0 @@
-/* PCSX2 - PS2 Emulator for PCs
- * Copyright (C) 2002-2019 PCSX2 Dev Team
- *
- * PCSX2 is free software: you can redistribute it and/or modify it under the terms
- * of the GNU Lesser General Public License as published by the Free Software Found-
- * ation, either version 3 of the License, or (at your option) any later version.
- *
- * PCSX2 is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
- * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
- * PURPOSE. See the GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along with PCSX2.
- * If not, see .
- */
-
-#pragma once
-
-#include "PadData.h"
-
-
-#ifndef DISABLE_RECORDING
-class RecordingInputManager
-{
-public:
- RecordingInputManager();
-
- void ControllerInterrupt(u8 &data, u8 &port, u16 & BufCount, u8 buf[]);
- // Handles normal keys
- void SetButtonState(int port, PadData_NormalButton button, int pressure);
- // Handles analog sticks
- void UpdateAnalog(int port, PadData_AnalogVector vector, int value);
- void SetVirtualPadReading(int port, bool read);
-
-protected:
- PadData pad;
- bool virtualPad[2];
-};
-
-extern RecordingInputManager g_RecordingInput;
-#endif
diff --git a/pcsx2/Recording/VirtualPad/VirtualPad.cpp b/pcsx2/Recording/VirtualPad/VirtualPad.cpp
new file mode 100644
index 0000000000..e46460b3f3
--- /dev/null
+++ b/pcsx2/Recording/VirtualPad/VirtualPad.cpp
@@ -0,0 +1,386 @@
+/* PCSX2 - PS2 Emulator for PCs
+ * Copyright (C) 2002-2019 PCSX2 Dev Team
+ *
+ * PCSX2 is free software: you can redistribute it and/or modify it under the terms
+ * of the GNU Lesser General Public License as published by the Free Software Found-
+ * ation, either version 3 of the License, or (at your option) any later version.
+ *
+ * PCSX2 is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
+ * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
+ * PURPOSE. See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along with PCSX2.
+ * If not, see .
+ */
+
+#include "PrecompiledHeader.h"
+
+#include "math.h"
+
+#include "App.h"
+#include "Common.h"
+#include "MSWstuff.h"
+
+#include "Recording/VirtualPad/VirtualPad.h"
+#include "Recording/VirtualPad/VirtualPadResources.h"
+
+#include "Recording/VirtualPad/img/controller.h"
+
+#include "Recording/VirtualPad/img/circlePressed.h"
+#include "Recording/VirtualPad/img/crossPressed.h"
+#include "Recording/VirtualPad/img/squarePressed.h"
+#include "Recording/VirtualPad/img/trianglePressed.h"
+#include "Recording/VirtualPad/img/downPressed.h"
+#include "Recording/VirtualPad/img/leftPressed.h"
+#include "Recording/VirtualPad/img/rightPressed.h"
+#include "Recording/VirtualPad/img/upPressed.h"
+#include "Recording/VirtualPad/img/l1Pressed.h"
+#include "Recording/VirtualPad/img/l2Pressed.h"
+#include "Recording/VirtualPad/img/r1Pressed.h"
+#include "Recording/VirtualPad/img/r2Pressed.h"
+
+#include "Recording/VirtualPad/img/selectPressed.h"
+#include "Recording/VirtualPad/img/startPressed.h"
+#include "Recording/VirtualPad/img/l3Pressed.h"
+#include "Recording/VirtualPad/img/r3Pressed.h"
+
+// TODO - store position of frame in ini file?
+
+VirtualPad::VirtualPad(wxWindow* parent, wxWindowID id, const wxString& title, int controllerPort, const wxPoint& pos, const wxSize& size, long style) :
+ wxFrame(parent, id, title, pos, size, wxDEFAULT_FRAME_STYLE)
+{
+ // Images at 1.00 scale are designed to work well on HiDPI (4k) at 150% scaling (default recommended setting on windows)
+ // Therefore, on a 1080p monitor we halve the scaling, on 1440p we reduce it by 25%, which from some quick tests looks comparable
+ // Side-note - It would be possible to factor in monitor scaling, but considering that is platform specific (with some platforms only supporting
+ // integer scaling) this is likely not reliable.
+ // Slight multi-monitor support, will use whatever window pcsx2 is opened with, but won't currently re-init if
+ // windows are dragged between differing monitors!
+ wxDisplay display(wxDisplay::GetFromWindow(this));
+ wxRect screen = display.GetClientArea();
+ if (screen.height > 1080 && screen.height <= 1440) // 1440p display
+ {
+ scalingFactor = 0.75;
+ }
+ else if (screen.height <= 1080) // 1080p display
+ {
+ scalingFactor = 0.5;
+ } // otherwise use default 1.0 scaling
+
+ virtualPadData = VirtualPadData();
+ virtualPadData.background = NewBitmap(EmbeddedImage().Get(), wxPoint(0, 0));
+ // Use the background image's size to define the window size
+ SetClientSize(virtualPadData.background.width, virtualPadData.background.height);
+
+ // TODO - rename to Gui
+ InitPressureButtonGUIElements(virtualPadData.cross, NewBitmap(EmbeddedImage().Get(), wxPoint(968, 498)), this, wxPoint(1062, 660));
+ InitPressureButtonGUIElements(virtualPadData.circle, NewBitmap(EmbeddedImage().Get(), wxPoint(1057, 413)), this, wxPoint(1062, 700));
+ InitPressureButtonGUIElements(virtualPadData.triangle, NewBitmap(EmbeddedImage().Get(), wxPoint(968, 325)), this, wxPoint(1062, 740));
+ InitPressureButtonGUIElements(virtualPadData.square, NewBitmap(EmbeddedImage().Get(), wxPoint(879, 413)), this, wxPoint(1062, 780));
+ InitPressureButtonGUIElements(virtualPadData.down, NewBitmap(EmbeddedImage().Get(), wxPoint(191, 488)), this, wxPoint(199, 660), true);
+ InitPressureButtonGUIElements(virtualPadData.right, NewBitmap(EmbeddedImage().Get(), wxPoint(255, 429)), this, wxPoint(199, 700), true);
+ InitPressureButtonGUIElements(virtualPadData.up, NewBitmap(EmbeddedImage().Get(), wxPoint(191, 354)), this, wxPoint(199, 740), true);
+ InitPressureButtonGUIElements(virtualPadData.left, NewBitmap(EmbeddedImage().Get(), wxPoint(115, 429)), this, wxPoint(199, 780), true);
+ InitPressureButtonGUIElements(virtualPadData.l1, NewBitmap(EmbeddedImage().Get(), wxPoint(166, 8)), this, wxPoint(294, 20));
+ InitPressureButtonGUIElements(virtualPadData.l2, NewBitmap(EmbeddedImage().Get(), wxPoint(166, 81)), this, wxPoint(294, 100));
+ InitPressureButtonGUIElements(virtualPadData.r1, NewBitmap(EmbeddedImage().Get(), wxPoint(958, 7)), this, wxPoint(940, 20), true);
+ InitPressureButtonGUIElements(virtualPadData.r2, NewBitmap(EmbeddedImage().Get(), wxPoint(958, 81)), this, wxPoint(940, 100), true);
+
+ InitNormalButtonGUIElements(virtualPadData.select, NewBitmap(EmbeddedImage().Get(), wxPoint(473, 441)), this, wxPoint(545, 448));
+ InitNormalButtonGUIElements(virtualPadData.start, NewBitmap(EmbeddedImage().Get(), wxPoint(710, 440)), this, wxPoint(675, 448));
+ InitNormalButtonGUIElements(virtualPadData.l3, NewBitmap(EmbeddedImage().Get(), wxPoint(347, 585)), this, wxPoint(440, 835));
+ InitNormalButtonGUIElements(virtualPadData.r3, NewBitmap(EmbeddedImage().Get(), wxPoint(750, 585)), this, wxPoint(844, 835));
+
+ InitAnalogStickGuiElements(virtualPadData.leftAnalog, this, wxPoint(418, 656), 105, wxPoint(326, 782), wxPoint(545, 568), false, wxPoint(522, 800), wxPoint(522, 760));
+ InitAnalogStickGuiElements(virtualPadData.rightAnalog, this, wxPoint(821, 656), 105, wxPoint(730, 782), wxPoint(672, 568), true, wxPoint(720, 800), wxPoint(720, 760), true);
+
+ ignoreRealControllerBox = new wxCheckBox(this, wxID_ANY, wxEmptyString, NewScaledPoint(605, 256), wxDefaultSize);
+ Bind(wxEVT_CHECKBOX, &VirtualPad::OnIgnoreRealController, this, ignoreRealControllerBox->GetId());
+
+ // Bind Window Events
+ Bind(wxEVT_ERASE_BACKGROUND, &VirtualPad::OnEraseBackground, this);
+ Bind(wxEVT_MOTION, &VirtualPad::OnMouseEvent, this);
+ Bind(wxEVT_CLOSE_WINDOW, &VirtualPad::OnClose, this);
+ Bind(wxEVT_SET_FOCUS, &VirtualPad::OnFocusEvent, this);
+ Bind(wxEVT_KILL_FOCUS, &VirtualPad::OnFocusEvent, this);
+ // Temporary Paint event handler so the window displays properly before the controller-interrupt routine takes over with manual drawing.
+ // The reason for this is in order to minimize the performance impact, we need total control over when render is called
+ // Windows redraws the window _alot_ otherwise which leads to major performance problems
+ Bind(wxEVT_PAINT, &VirtualPad::OnPaint, this);
+
+ // Finalize layout
+ SetIcons(wxGetApp().GetIconBundle());
+ SetTitle(wxString::Format("Virtual Pad - Port %d", controllerPort + 1));
+ SetBackgroundColour(*wxWHITE);
+ SetBackgroundStyle(wxBG_STYLE_PAINT);
+ // This window does not allow for resizing for sake of simplicity: all images are scaled initially and stored, ready to be rendered
+ SetWindowStyle(style & ~wxRESIZE_BORDER);
+ SetDoubleBuffered(true);
+}
+
+// TODO - test open/close routine
+
+void VirtualPad::OnClose(wxCloseEvent & event)
+{
+ // Re-bind the Paint event in case this is due to a game being opened/closed
+ manualRedrawMode = false;
+ Bind(wxEVT_PAINT, &VirtualPad::OnPaint, this);
+ Hide(); // TODO - hide vs closed?
+}
+
+void VirtualPad::OnMouseEvent(wxMouseEvent &evt)
+{
+ evt.Skip();
+ return;
+}
+
+void VirtualPad::OnFocusEvent(wxFocusEvent &evt)
+{
+ evt.Skip();
+ return;
+}
+
+void VirtualPad::OnEraseBackground(wxEraseEvent& event)
+{
+ // Intentionally Empty
+ // See - https://wiki.wxwidgets.org/Flicker-Free_Drawing
+}
+
+void VirtualPad::OnPaint(wxPaintEvent &evt)
+{
+ wxPaintDC dc(this);
+ Render(dc);
+}
+
+void VirtualPad::Redraw()
+{
+ // This function is called once per frame once we start receiving data from the controller
+ // and there is actually an update that needs to be made.
+ // Once that occurs, we want total control over how often we re-render to minimize flickering
+ // and minimize the performance impact
+ if (!manualRedrawMode) {
+ Unbind(wxEVT_PAINT, &VirtualPad::OnPaint, this);
+ manualRedrawMode = true;
+ }
+ wxClientDC dc(this);
+ Render(dc);
+}
+
+void VirtualPad::Render(wxDC &dc)
+{
+ // Update GUI Elements and figure out what needs to be rendered
+ virtualPadData.circle.UpdateGuiElement(&renderQueue, clearScreenRequired);
+ virtualPadData.cross.UpdateGuiElement(&renderQueue, clearScreenRequired);
+ virtualPadData.square.UpdateGuiElement(&renderQueue, clearScreenRequired);
+ virtualPadData.triangle.UpdateGuiElement(&renderQueue, clearScreenRequired);
+ virtualPadData.down.UpdateGuiElement(&renderQueue, clearScreenRequired);
+ virtualPadData.left.UpdateGuiElement(&renderQueue, clearScreenRequired);
+ virtualPadData.right.UpdateGuiElement(&renderQueue, clearScreenRequired);
+ virtualPadData.up.UpdateGuiElement(&renderQueue, clearScreenRequired);
+ virtualPadData.l1.UpdateGuiElement(&renderQueue, clearScreenRequired);
+ virtualPadData.l2.UpdateGuiElement(&renderQueue, clearScreenRequired);
+ virtualPadData.r1.UpdateGuiElement(&renderQueue, clearScreenRequired);
+ virtualPadData.r2.UpdateGuiElement(&renderQueue, clearScreenRequired);
+
+ virtualPadData.select.UpdateGuiElement(&renderQueue, clearScreenRequired);
+ virtualPadData.start.UpdateGuiElement(&renderQueue, clearScreenRequired);
+ virtualPadData.l3.UpdateGuiElement(&renderQueue, clearScreenRequired);
+ virtualPadData.r3.UpdateGuiElement(&renderQueue, clearScreenRequired);
+
+ virtualPadData.leftAnalog.UpdateGuiElement(&renderQueue, clearScreenRequired);
+ virtualPadData.rightAnalog.UpdateGuiElement(&renderQueue, clearScreenRequired);
+
+ // Update Graphic Elements off render stack
+ // Before we start rendering (if we have to) clear and re-draw the background
+ if (!manualRedrawMode || clearScreenRequired || !renderQueue.empty())
+ {
+ wxBufferedDC bdc(&dc, dc.GetSize());
+ bdc.SetBrush(*wxRED);
+ bdc.DrawRectangle(wxPoint(0, 0), bdc.GetSize());
+ bdc.SetBrush(wxNullBrush);
+ bdc.DrawBitmap(virtualPadData.background.image, virtualPadData.background.coords, true);
+ clearScreenRequired = false;
+
+ while (!renderQueue.empty()) {
+ VirtualPadElement *element = renderQueue.front();
+ element->Render(bdc);
+ renderQueue.pop();
+ }
+ }
+}
+
+bool VirtualPad::UpdateControllerData(u16 const bufIndex, PadData *padData, bool readOnly)
+{
+ return virtualPadData.UpdateVirtualPadData(bufIndex, padData, ignoreRealController && !readOnly, readOnly);
+}
+
+void VirtualPad::OnIgnoreRealController(wxCommandEvent &event)
+{
+ wxCheckBox* ignoreButton = (wxCheckBox*) event.GetEventObject();
+ ignoreRealController = ignoreButton->GetValue();
+}
+
+void VirtualPad::OnNormalButtonPress(wxCommandEvent &event)
+{
+ wxCheckBox* pressedButton = (wxCheckBox*) event.GetEventObject();
+ ControllerNormalButton *eventBtn = buttonElements[pressedButton->GetId()];
+
+ eventBtn->pressed = pressedButton->GetValue();
+
+ // If the real controller is being bypassed, we move on, otherwise we begin bypassing the controller
+ if (!eventBtn->isControllerBypassed) {
+ eventBtn->isControllerBypassed = true;
+ }
+}
+
+void VirtualPad::OnPressureButtonPressureChange(wxCommandEvent &event)
+{
+ wxSpinCtrl* pressureSpinner = (wxSpinCtrl*) event.GetEventObject();
+ ControllerPressureButton *eventBtn = pressureElements[pressureSpinner->GetId()];
+
+ eventBtn->pressure = pressureSpinner->GetValue();
+ eventBtn->pressed = eventBtn->pressure > 0;
+
+ // If the real controller is being bypassed, we move on, otherwise we begin bypassing the controller
+ if (!eventBtn->isControllerPressureBypassed || !eventBtn->isControllerPressBypassed) {
+ eventBtn->isControllerPressureBypassed = true;
+ eventBtn->isControllerPressBypassed = true;
+ }
+}
+
+void VirtualPad::OnAnalogSpinnerChange(wxCommandEvent &event)
+{
+ wxSpinCtrl* analogSpinner = (wxSpinCtrl*) event.GetEventObject();
+ AnalogVector *eventVector = analogElements[analogSpinner->GetId()];
+
+ eventVector->val = analogSpinner->GetValue();
+ eventVector->slider->SetValue(eventVector->val);
+
+ // If the real controller is being bypassed, we move on, otherwise we begin bypassing the controller
+ if (!eventVector->isControllerBypassed) {
+ eventVector->isControllerBypassed = true;
+ }
+}
+
+void VirtualPad::OnAnalogSliderChange(wxCommandEvent &event)
+{
+ wxSlider* analogSlider = (wxSlider*) event.GetEventObject();
+ AnalogVector *eventVector = analogElements[analogSlider->GetId()];
+
+ eventVector->val = analogSlider->GetValue();
+ eventVector->spinner->SetValue(eventVector->val);
+
+ // If the real controller is being bypassed, we move on, otherwise we begin bypassing the controller
+ if (!eventVector->isControllerBypassed) {
+ eventVector->isControllerBypassed = true;
+ }
+}
+
+/// GUI Element Utility Functions
+
+wxPoint VirtualPad::NewScaledPoint(wxPoint point)
+{
+ return wxPoint(point.x * scalingFactor, point.y * scalingFactor);
+}
+
+wxPoint VirtualPad::NewScaledPoint(int x, int y)
+{
+ return wxPoint(x * scalingFactor, y * scalingFactor);
+}
+
+ImageFile VirtualPad::NewBitmap(wxImage resource, wxPoint point)
+{
+ return NewBitmap(scalingFactor, resource, point);
+}
+
+ImageFile VirtualPad::NewBitmap(float scalingFactor, wxImage resource, wxPoint point)
+{
+ wxBitmap bitmap = wxBitmap(resource.Rescale(resource.GetWidth() * scalingFactor, resource.GetHeight() * scalingFactor, wxIMAGE_QUALITY_HIGH));
+
+ ImageFile image = ImageFile();
+ image.image = bitmap;
+ image.width = bitmap.GetWidth();
+ image.height = bitmap.GetHeight();
+ image.coords = NewScaledPoint(point);
+
+ return image;
+}
+
+void VirtualPad::InitNormalButtonGUIElements(ControllerNormalButton &button, ImageFile image, wxWindow *parentWindow, wxPoint point)
+{
+ button.icon = image;
+ button.pressedBox = new wxCheckBox(parentWindow, wxID_ANY, wxEmptyString, NewScaledPoint(point), wxDefaultSize);
+ button.isControllerBypassed = false;
+ button.prevPressedVal = false;
+ Bind(wxEVT_CHECKBOX, &VirtualPad::OnNormalButtonPress, this, button.pressedBox->GetId());
+ buttonElements[button.pressedBox->GetId()] = &button;
+}
+
+void VirtualPad::InitPressureButtonGUIElements(ControllerPressureButton &button, ImageFile image, wxWindow *parentWindow, wxPoint point, bool rightAlignedPoint)
+{
+ wxPoint scaledPoint = wxPoint(point.x * scalingFactor, point.y * scalingFactor);
+ if (rightAlignedPoint) {
+ scaledPoint.x -= 100 * scalingFactor;
+ if (scaledPoint.x < 0) {
+ scaledPoint.x = 0;
+ }
+ }
+ wxSpinCtrl *spinner = new wxSpinCtrl(parentWindow, wxID_ANY, wxEmptyString, scaledPoint, wxSize(100 * scalingFactor, wxDefaultSize.GetHeight()), wxSP_ARROW_KEYS, 0, 255, 0);
+
+ // TODO - defaults on the classes' constructor would clean this up
+ button.icon = image;
+ button.prevPressedVal = 0;
+ button.pressureSpinner = spinner;
+ button.isControllerPressBypassed = false;
+ button.isControllerPressureBypassed = false;
+ button.prevPressedVal = false;
+ button.prevPressureVal = 0;
+ Bind(wxEVT_SPINCTRL, &VirtualPad::OnPressureButtonPressureChange, this, button.pressureSpinner->GetId());
+ pressureElements[button.pressureSpinner->GetId()] = &button;
+}
+
+void VirtualPad::InitAnalogStickGuiElements(AnalogStick &analog, wxWindow *parentWindow, wxPoint centerPoint, int radius, wxPoint xSliderPoint, wxPoint ySliderPoint, bool flipYSlider, wxPoint xSpinnerPoint, wxPoint ySpinnerPoint, bool rightAlignedSpinners)
+{
+ AnalogPosition analogPos = AnalogPosition();
+ analogPos.centerCoords = NewScaledPoint(centerPoint);
+ analogPos.endCoords = NewScaledPoint(centerPoint);
+ analogPos.radius = radius * scalingFactor;
+ analogPos.lineThickness = 6 * scalingFactor;
+
+ // TODO - make a function to scale wxSize values easier
+
+ wxSlider *xSlider = new wxSlider(parentWindow, wxID_ANY, 127, 0, 255, NewScaledPoint(xSliderPoint), wxSize(185 * scalingFactor, 30 * scalingFactor));
+ wxSlider *ySlider = new wxSlider(parentWindow, wxID_ANY, 127, 0, 255, NewScaledPoint(ySliderPoint), wxSize(30 * scalingFactor, 185 * scalingFactor), flipYSlider ? wxSL_LEFT : wxSL_RIGHT);
+
+ // TODO - function to right-align spinners easier
+
+ wxPoint xSpinnerScaledPoint = NewScaledPoint(xSpinnerPoint);
+ wxPoint ySpinnerScaledPoint = NewScaledPoint(ySpinnerPoint);
+ if (rightAlignedSpinners) {
+ xSpinnerScaledPoint.x -= 90 * scalingFactor;
+ if (xSpinnerScaledPoint.x < 0) {
+ xSpinnerScaledPoint.x = 0;
+ }
+
+ ySpinnerScaledPoint.x -= 90 * scalingFactor;
+ if (ySpinnerScaledPoint.x < 0) {
+ ySpinnerScaledPoint.x = 0;
+ }
+ }
+
+ wxSpinCtrl *xSpinner = new wxSpinCtrl(parentWindow, wxID_ANY, wxEmptyString, xSpinnerScaledPoint, wxSize(90 * scalingFactor, wxDefaultSize.GetHeight()), wxSP_ARROW_KEYS, 0, 255, 127);
+ wxSpinCtrl *ySpinner = new wxSpinCtrl(parentWindow, wxID_ANY, wxEmptyString, ySpinnerScaledPoint, wxSize(90 * scalingFactor, wxDefaultSize.GetHeight()), wxSP_ARROW_KEYS, 0, 255, 127);
+
+ analog.xVector.slider = xSlider;
+ analog.yVector.slider = ySlider;
+ analog.xVector.spinner = xSpinner;
+ analog.yVector.spinner = ySpinner;
+ analog.positionGraphic = analogPos;
+ Bind(wxEVT_SPINCTRL, &VirtualPad::OnAnalogSpinnerChange, this, analog.xVector.spinner->GetId());
+ Bind(wxEVT_SPINCTRL, &VirtualPad::OnAnalogSpinnerChange, this, analog.yVector.spinner->GetId());
+ Bind(wxEVT_SLIDER, &VirtualPad::OnAnalogSliderChange, this, analog.xVector.slider->GetId());
+ Bind(wxEVT_SLIDER, &VirtualPad::OnAnalogSliderChange, this, analog.yVector.slider->GetId());
+ analogElements[analog.xVector.spinner->GetId()] = &analog.xVector;
+ analogElements[analog.yVector.spinner->GetId()] = &analog.yVector;
+ analogElements[analog.xVector.slider->GetId()] = &analog.xVector;
+ analogElements[analog.yVector.slider->GetId()] = &analog.yVector;
+}
diff --git a/pcsx2/Recording/VirtualPad/VirtualPad.h b/pcsx2/Recording/VirtualPad/VirtualPad.h
new file mode 100644
index 0000000000..71c4e4e622
--- /dev/null
+++ b/pcsx2/Recording/VirtualPad/VirtualPad.h
@@ -0,0 +1,98 @@
+/* PCSX2 - PS2 Emulator for PCs
+ * Copyright (C) 2002-2019 PCSX2 Dev Team
+ *
+ * PCSX2 is free software: you can redistribute it and/or modify it under the terms
+ * of the GNU Lesser General Public License as published by the Free Software Found-
+ * ation, either version 3 of the License, or (at your option) any later version.
+ *
+ * PCSX2 is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
+ * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
+ * PURPOSE. See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along with PCSX2.
+ * If not, see .
+ */
+
+#pragma once
+
+#include