Initial re-upload of spice2x-24-08-24
This commit is contained in:
171
games/ddr/ddr.cpp
Normal file
171
games/ddr/ddr.cpp
Normal file
@@ -0,0 +1,171 @@
|
||||
#include "ddr.h"
|
||||
|
||||
#include "acioemu/handle.h"
|
||||
#include "avs/game.h"
|
||||
#include "hooks/devicehook.h"
|
||||
#include "hooks/setupapihook.h"
|
||||
#include "hooks/sleephook.h"
|
||||
#include "hooks/input/dinput8/hook.h"
|
||||
#include "util/utils.h"
|
||||
#include "util/libutils.h"
|
||||
#include "util/fileutils.h"
|
||||
#include "util/detour.h"
|
||||
#include "cfg/configurator.h"
|
||||
|
||||
#include "io.h"
|
||||
|
||||
#include "p3io/foot.h"
|
||||
#include "p3io/p3io.h"
|
||||
#include "p3io/sate.h"
|
||||
#include "p3io/usbmem.h"
|
||||
|
||||
#include "p4io/p4io.h"
|
||||
|
||||
using namespace acioemu;
|
||||
|
||||
namespace games::ddr {
|
||||
|
||||
// settings
|
||||
bool SDMODE = false;
|
||||
|
||||
static decltype(SendMessage) *SendMessage_real = nullptr;
|
||||
|
||||
static SHORT WINAPI GetAsyncKeyState_hook(int vKey) {
|
||||
|
||||
// disable debug keys
|
||||
return 0;
|
||||
}
|
||||
|
||||
static LRESULT WINAPI SendMessage_hook(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam) {
|
||||
|
||||
// ignore broadcasts
|
||||
if (hWnd == HWND_BROADCAST) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
// fallback
|
||||
return SendMessage_real(hWnd, Msg, wParam, lParam);
|
||||
}
|
||||
|
||||
DDRGame::DDRGame() : Game("Dance Dance Revolution") {
|
||||
}
|
||||
|
||||
void DDRGame::pre_attach() {
|
||||
if (!cfg::CONFIGURATOR_STANDALONE && avs::game::is_model("TDX")) {
|
||||
log_fatal(
|
||||
"ddr",
|
||||
"BAD MODEL NAME ERROR\n\n\n"
|
||||
"!!! model name set to TDX, this is WRONG and will break your game !!!\n"
|
||||
"!!! !!!\n"
|
||||
"!!! !!!\n"
|
||||
"!!! spice2x does not yet support TDX, use MDX instead. !!!\n"
|
||||
"!!! !!!\n"
|
||||
"!!! !!!\n"
|
||||
"!!! model name set to TDX, this is WRONG and will break your game !!!\n\n\n"
|
||||
);
|
||||
}
|
||||
|
||||
if (!cfg::CONFIGURATOR_STANDALONE && avs::game::DEST[0] == 'U') {
|
||||
log_warning(
|
||||
"ddr",
|
||||
"U-REGION WARNING\n\n\n"
|
||||
"!!! !!!\n"
|
||||
"!!! !!!\n"
|
||||
"!!! !!!\n"
|
||||
"!!! !!!\n"
|
||||
"!!! <dest> is set to U region !!!\n"
|
||||
"!!! !!!\n"
|
||||
"!!! While this is legal, unless you have compatible data, this !!!\n"
|
||||
"!!! will most likely crash your game or fail to boot. !!!\n"
|
||||
"!!! !!!\n"
|
||||
"!!! It is recommended that you stick with J region. !!!\n"
|
||||
"!!! !!!\n"
|
||||
"!!! !!!\n"
|
||||
"!!! !!!\n"
|
||||
"!!! !!!\n\n\n"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
void DDRGame::attach() {
|
||||
Game::attach();
|
||||
|
||||
// dinput hook on this dll since the game dll doesn't load it
|
||||
auto game_mdx = libutils::try_library(MODULE_PATH / "gamemdx.dll");
|
||||
hooks::input::dinput8::init(game_mdx);
|
||||
|
||||
// init device hook
|
||||
devicehook_init();
|
||||
|
||||
// add fake devices
|
||||
if (avs::game::DLL_NAME == "arkmdxbio2.dll") {
|
||||
devicehook_add(new acioemu::ACIOHandle(L"COM1"));
|
||||
} else if(avs::game::DLL_NAME == "arkmdxp4.dll") {
|
||||
devicehook_add(new DDRP4IOHandle());
|
||||
} else {
|
||||
devicehook_add(new DDRFOOTHandle());
|
||||
devicehook_add(new DDRSATEHandle());
|
||||
devicehook_add(new DDRUSBMEMHandle());
|
||||
devicehook_add(new DDRP3IOHandle());
|
||||
}
|
||||
|
||||
// has nothing to do with P3IO, but is enough to trick the game into SD/HD mode
|
||||
const char *settings_property = ddr::SDMODE ? "Generic Television" : "Generic Monitor";
|
||||
const char settings_detail[] = R"(\\.\P3IO)";
|
||||
|
||||
// settings 1
|
||||
SETUPAPI_SETTINGS settings1 {};
|
||||
settings1.class_guid[0] = 0x1FA4A480;
|
||||
settings1.class_guid[1] = 0x40C7AC60;
|
||||
settings1.class_guid[2] = 0x7952ACA7;
|
||||
settings1.class_guid[3] = 0x5A57340F;
|
||||
memcpy(settings1.property_devicedesc, settings_property, strlen(settings_property) + 1);
|
||||
memcpy(settings1.interface_detail, settings_detail, sizeof(settings_detail));
|
||||
|
||||
// settings 2
|
||||
SETUPAPI_SETTINGS settings2 {};
|
||||
settings2.class_guid[0] = 0x4D36E96E;
|
||||
settings2.class_guid[1] = 0x11CEE325;
|
||||
settings2.class_guid[2] = 0x8C1BF;
|
||||
settings2.class_guid[3] = 0x1803E12B;
|
||||
memcpy(settings2.property_devicedesc, settings_property, strlen(settings_property) + 1);
|
||||
memcpy(settings2.interface_detail, settings_detail, sizeof(settings_detail));
|
||||
|
||||
const char settings_detail_p4io[] = R"(\\.\P4IO)";
|
||||
|
||||
// settings p4io
|
||||
SETUPAPI_SETTINGS settingsp4io {};
|
||||
settingsp4io.class_guid[0] = 0x8B7250A5;
|
||||
settingsp4io.class_guid[1] = 0x46C94F61;
|
||||
settingsp4io.class_guid[2] = 0x68E63A84;
|
||||
settingsp4io.class_guid[3] = 0x206A4706;
|
||||
memcpy(settingsp4io.property_devicedesc, settings_property, strlen(settings_property) + 1);
|
||||
memcpy(settingsp4io.interface_detail, settings_detail_p4io, sizeof(settings_detail_p4io));
|
||||
|
||||
// init SETUP API
|
||||
setupapihook_init(avs::game::DLL_INSTANCE);
|
||||
|
||||
// DDR ACE actually uses another DLL for things
|
||||
if (game_mdx != nullptr) {
|
||||
setupapihook_init(game_mdx);
|
||||
}
|
||||
|
||||
// add settings
|
||||
setupapihook_add(settings1);
|
||||
setupapihook_add(settings2);
|
||||
setupapihook_add(settingsp4io);
|
||||
|
||||
// misc hooks
|
||||
detour::iat_try("GetAsyncKeyState", GetAsyncKeyState_hook, avs::game::DLL_INSTANCE);
|
||||
detour::iat_try("GetKeyState", GetAsyncKeyState_hook, avs::game::DLL_INSTANCE);
|
||||
SendMessage_real = detour::iat_try("SendMessageW", SendMessage_hook, avs::game::DLL_INSTANCE);
|
||||
detour::iat_try("SendMessageA", SendMessage_hook, avs::game::DLL_INSTANCE);
|
||||
}
|
||||
|
||||
void DDRGame::detach() {
|
||||
Game::detach();
|
||||
|
||||
// dispose device hook
|
||||
devicehook_dispose();
|
||||
}
|
||||
}
|
||||
17
games/ddr/ddr.h
Normal file
17
games/ddr/ddr.h
Normal file
@@ -0,0 +1,17 @@
|
||||
#pragma once
|
||||
|
||||
#include "games/game.h"
|
||||
|
||||
namespace games::ddr {
|
||||
|
||||
// settings
|
||||
extern bool SDMODE;
|
||||
|
||||
class DDRGame : public games::Game {
|
||||
public:
|
||||
DDRGame();
|
||||
virtual void pre_attach() override;
|
||||
virtual void attach() override;
|
||||
virtual void detach() override;
|
||||
};
|
||||
}
|
||||
178
games/ddr/io.cpp
Normal file
178
games/ddr/io.cpp
Normal file
@@ -0,0 +1,178 @@
|
||||
#include "io.h"
|
||||
|
||||
std::vector<Button> &games::ddr::get_buttons() {
|
||||
static std::vector<Button> analogs;
|
||||
|
||||
if (analogs.empty()) {
|
||||
analogs = GameAPI::Buttons::getButtons("Dance Dance Revolution");
|
||||
|
||||
GameAPI::Buttons::sortButtons(
|
||||
&analogs,
|
||||
"Service",
|
||||
"Test",
|
||||
"Coin Mech",
|
||||
"P1 Start",
|
||||
"P1 Panel Up",
|
||||
"P1 Panel Down",
|
||||
"P1 Panel Left",
|
||||
"P1 Panel Right",
|
||||
"P1 Menu Up",
|
||||
"P1 Menu Down",
|
||||
"P1 Menu Left",
|
||||
"P1 Menu Right",
|
||||
"P2 Start",
|
||||
"P2 Panel Up",
|
||||
"P2 Panel Down",
|
||||
"P2 Panel Left",
|
||||
"P2 Panel Right",
|
||||
"P2 Menu Up",
|
||||
"P2 Menu Down",
|
||||
"P2 Menu Left",
|
||||
"P2 Menu Right"
|
||||
);
|
||||
}
|
||||
|
||||
return analogs;
|
||||
}
|
||||
|
||||
std::vector<Light> &games::ddr::get_lights() {
|
||||
static std::vector<Light> lights;
|
||||
|
||||
if (lights.empty()) {
|
||||
lights = GameAPI::Lights::getLights("Dance Dance Revolution");
|
||||
|
||||
GameAPI::Lights::sortLights(
|
||||
&lights,
|
||||
"P1 Foot Left",
|
||||
"P1 Foot Up",
|
||||
"P1 Foot Right",
|
||||
"P1 Foot Down",
|
||||
"P2 Foot Left",
|
||||
"P2 Foot Up",
|
||||
"P2 Foot Right",
|
||||
"P2 Foot Down",
|
||||
"Spot Red",
|
||||
"Spot Blue",
|
||||
"Top Spot Red",
|
||||
"Top Spot Blue",
|
||||
"P1 Halogen Upper",
|
||||
"P1 Halogen Lower",
|
||||
"P2 Halogen Upper",
|
||||
"P2 Halogen Lower",
|
||||
"P1 Button",
|
||||
"P2 Button",
|
||||
"Neon",
|
||||
"HD P1 Start",
|
||||
"HD P1 Menu Left-Right",
|
||||
"HD P1 Menu Up-Down",
|
||||
"HD P2 Start",
|
||||
"HD P2 Menu Left-Right",
|
||||
"HD P2 Menu Up-Down",
|
||||
"HD P1 Speaker F R",
|
||||
"HD P1 Speaker F G",
|
||||
"HD P1 Speaker F B",
|
||||
"HD P1 Speaker W R",
|
||||
"HD P1 Speaker W G",
|
||||
"HD P1 Speaker W B",
|
||||
"HD P2 Speaker F R",
|
||||
"HD P2 Speaker F G",
|
||||
"HD P2 Speaker F B",
|
||||
"HD P2 Speaker W R",
|
||||
"HD P2 Speaker W G",
|
||||
"HD P2 Speaker W B",
|
||||
|
||||
"WHITE Speaker Top R",
|
||||
"WHITE Speaker Top G",
|
||||
"WHITE Speaker Top B",
|
||||
"WHITE Speaker Bottom R",
|
||||
"WHITE Speaker Bottom G",
|
||||
"WHITE Speaker Bottom B",
|
||||
"WHITE Woofer R",
|
||||
"WHITE Woofer G",
|
||||
"WHITE Woofer B",
|
||||
|
||||
"GOLD P1 Menu Start",
|
||||
"GOLD P1 Menu Up",
|
||||
"GOLD P1 Menu Down",
|
||||
"GOLD P1 Menu Left",
|
||||
"GOLD P1 Menu Right",
|
||||
|
||||
"GOLD P2 Menu Start",
|
||||
"GOLD P2 Menu Up",
|
||||
"GOLD P2 Menu Down",
|
||||
"GOLD P2 Menu Left",
|
||||
"GOLD P2 Menu Right",
|
||||
|
||||
"GOLD Title Panel Left",
|
||||
"GOLD Title Panel Center",
|
||||
"GOLD Title Panel Right",
|
||||
|
||||
"GOLD P1 Woofer Corner",
|
||||
"GOLD P2 Woofer Corner",
|
||||
|
||||
"GOLD P1 Card Unit R",
|
||||
"GOLD P1 Card Unit G",
|
||||
"GOLD P1 Card Unit B",
|
||||
|
||||
"GOLD P2 Card Unit R",
|
||||
"GOLD P2 Card Unit G",
|
||||
"GOLD P2 Card Unit B",
|
||||
|
||||
"GOLD Top Panel Avg R",
|
||||
"GOLD Top Panel Avg G",
|
||||
"GOLD Top Panel Avg B",
|
||||
|
||||
"GOLD Monitor Side Left Avg R",
|
||||
"GOLD Monitor Side Left Avg G",
|
||||
"GOLD Monitor Side Left Avg B",
|
||||
|
||||
"GOLD Monitor Side Right Avg R",
|
||||
"GOLD Monitor Side Right Avg G",
|
||||
"GOLD Monitor Side Right Avg B",
|
||||
|
||||
"GOLD P1 Foot Up Avg R",
|
||||
"GOLD P1 Foot Up Avg G",
|
||||
"GOLD P1 Foot Up Avg B",
|
||||
|
||||
"GOLD P1 Foot Down Avg R",
|
||||
"GOLD P1 Foot Down Avg G",
|
||||
"GOLD P1 Foot Down Avg B",
|
||||
|
||||
"GOLD P1 Foot Left Avg R",
|
||||
"GOLD P1 Foot Left Avg G",
|
||||
"GOLD P1 Foot Left Avg B",
|
||||
|
||||
"GOLD P1 Foot Right Avg R",
|
||||
"GOLD P1 Foot Right Avg G",
|
||||
"GOLD P1 Foot Right Avg B",
|
||||
|
||||
"GOLD P2 Foot Up Avg R",
|
||||
"GOLD P2 Foot Up Avg G",
|
||||
"GOLD P2 Foot Up Avg B",
|
||||
|
||||
"GOLD P2 Foot Down Avg R",
|
||||
"GOLD P2 Foot Down Avg G",
|
||||
"GOLD P2 Foot Down Avg B",
|
||||
|
||||
"GOLD P2 Foot Left Avg R",
|
||||
"GOLD P2 Foot Left Avg G",
|
||||
"GOLD P2 Foot Left Avg B",
|
||||
|
||||
"GOLD P2 Foot Right Avg R",
|
||||
"GOLD P2 Foot Right Avg G",
|
||||
"GOLD P2 Foot Right Avg B",
|
||||
|
||||
"GOLD P1 Stage Corner Up-Left",
|
||||
"GOLD P1 Stage Corner Up-Right",
|
||||
"GOLD P1 Stage Corner Down-Left",
|
||||
"GOLD P1 Stage Corner Down-Right",
|
||||
|
||||
"GOLD P2 Stage Corner Up-Left",
|
||||
"GOLD P2 Stage Corner Up-Right",
|
||||
"GOLD P2 Stage Corner Down-Left",
|
||||
"GOLD P2 Stage Corner Down-Right"
|
||||
);
|
||||
}
|
||||
|
||||
return lights;
|
||||
}
|
||||
181
games/ddr/io.h
Normal file
181
games/ddr/io.h
Normal file
@@ -0,0 +1,181 @@
|
||||
#pragma once
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include "cfg/api.h"
|
||||
|
||||
namespace games::ddr {
|
||||
|
||||
// all buttons in correct order
|
||||
namespace Buttons {
|
||||
enum {
|
||||
SERVICE,
|
||||
TEST,
|
||||
COIN_MECH,
|
||||
P1_START,
|
||||
P1_PANEL_UP,
|
||||
P1_PANEL_DOWN,
|
||||
P1_PANEL_LEFT,
|
||||
P1_PANEL_RIGHT,
|
||||
P1_MENU_UP,
|
||||
P1_MENU_DOWN,
|
||||
P1_MENU_LEFT,
|
||||
P1_MENU_RIGHT,
|
||||
P2_START,
|
||||
P2_PANEL_UP,
|
||||
P2_PANEL_DOWN,
|
||||
P2_PANEL_LEFT,
|
||||
P2_PANEL_RIGHT,
|
||||
P2_MENU_UP,
|
||||
P2_MENU_DOWN,
|
||||
P2_MENU_LEFT,
|
||||
P2_MENU_RIGHT,
|
||||
};
|
||||
}
|
||||
|
||||
// all lights in correct order
|
||||
namespace Lights {
|
||||
enum {
|
||||
P1_FOOT_LEFT,
|
||||
P1_FOOT_UP,
|
||||
P1_FOOT_RIGHT,
|
||||
P1_FOOT_DOWN,
|
||||
|
||||
P2_FOOT_LEFT,
|
||||
P2_FOOT_UP,
|
||||
P2_FOOT_RIGHT,
|
||||
P2_FOOT_DOWN,
|
||||
|
||||
SPOT_RED,
|
||||
SPOT_BLUE,
|
||||
TOP_SPOT_RED,
|
||||
TOP_SPOT_BLUE,
|
||||
|
||||
P1_HALOGEN_UPPER,
|
||||
P1_HALOGEN_LOWER,
|
||||
P2_HALOGEN_UPPER,
|
||||
P2_HALOGEN_LOWER,
|
||||
|
||||
P1_BUTTON,
|
||||
P2_BUTTON,
|
||||
NEON,
|
||||
|
||||
HD_P1_START,
|
||||
HD_P1_LEFT_RIGHT,
|
||||
HD_P1_UP_DOWN,
|
||||
|
||||
HD_P2_START,
|
||||
HD_P2_LEFT_RIGHT,
|
||||
HD_P2_UP_DOWN,
|
||||
|
||||
HD_P1_SPEAKER_F_R,
|
||||
HD_P1_SPEAKER_F_G,
|
||||
HD_P1_SPEAKER_F_B,
|
||||
HD_P1_SPEAKER_W_R,
|
||||
HD_P1_SPEAKER_W_G,
|
||||
HD_P1_SPEAKER_W_B,
|
||||
|
||||
HD_P2_SPEAKER_F_R,
|
||||
HD_P2_SPEAKER_F_G,
|
||||
HD_P2_SPEAKER_F_B,
|
||||
HD_P2_SPEAKER_W_R,
|
||||
HD_P2_SPEAKER_W_G,
|
||||
HD_P2_SPEAKER_W_B,
|
||||
|
||||
WHITE_SPEAKER_TOP_R,
|
||||
WHITE_SPEAKER_TOP_G,
|
||||
WHITE_SPEAKER_TOP_B,
|
||||
WHITE_SPEAKER_BOTTOM_R,
|
||||
WHITE_SPEAKER_BOTTOM_G,
|
||||
WHITE_SPEAKER_BOTTOM_B,
|
||||
WHITE_WOOFER_R,
|
||||
WHITE_WOOFER_G,
|
||||
WHITE_WOOFER_B,
|
||||
|
||||
GOLD_P1_MENU_START,
|
||||
GOLD_P1_MENU_UP,
|
||||
GOLD_P1_MENU_DOWN,
|
||||
GOLD_P1_MENU_LEFT,
|
||||
GOLD_P1_MENU_RIGHT,
|
||||
|
||||
GOLD_P2_MENU_START,
|
||||
GOLD_P2_MENU_UP,
|
||||
GOLD_P2_MENU_DOWN,
|
||||
GOLD_P2_MENU_LEFT,
|
||||
GOLD_P2_MENU_RIGHT,
|
||||
|
||||
GOLD_TITLE_PANEL_LEFT,
|
||||
GOLD_TITLE_PANEL_CENTER,
|
||||
GOLD_TITLE_PANEL_RIGHT,
|
||||
|
||||
GOLD_P1_WOOFER_CORNER,
|
||||
GOLD_P2_WOOFER_CORNER,
|
||||
|
||||
GOLD_P1_CARD_UNIT_R,
|
||||
GOLD_P1_CARD_UNIT_G,
|
||||
GOLD_P1_CARD_UNIT_B,
|
||||
|
||||
GOLD_P2_CARD_UNIT_R,
|
||||
GOLD_P2_CARD_UNIT_G,
|
||||
GOLD_P2_CARD_UNIT_B,
|
||||
|
||||
GOLD_TOP_PANEL_AVG_R,
|
||||
GOLD_TOP_PANEL_AVG_G,
|
||||
GOLD_TOP_PANEL_AVG_B,
|
||||
|
||||
GOLD_MONITOR_SIDE_LEFT_AVG_R,
|
||||
GOLD_MONITOR_SIDE_LEFT_AVG_G,
|
||||
GOLD_MONITOR_SIDE_LEFT_AVG_B,
|
||||
|
||||
GOLD_MONITOR_SIDE_RIGHT_AVG_R,
|
||||
GOLD_MONITOR_SIDE_RIGHT_AVG_G,
|
||||
GOLD_MONITOR_SIDE_RIGHT_AVG_B,
|
||||
|
||||
GOLD_P1_FOOT_UP_AVG_R,
|
||||
GOLD_P1_FOOT_UP_AVG_G,
|
||||
GOLD_P1_FOOT_UP_AVG_B,
|
||||
|
||||
GOLD_P1_FOOT_DOWN_AVG_R,
|
||||
GOLD_P1_FOOT_DOWN_AVG_G,
|
||||
GOLD_P1_FOOT_DOWN_AVG_B,
|
||||
|
||||
GOLD_P1_FOOT_LEFT_AVG_R,
|
||||
GOLD_P1_FOOT_LEFT_AVG_G,
|
||||
GOLD_P1_FOOT_LEFT_AVG_B,
|
||||
|
||||
GOLD_P1_FOOT_RIGHT_AVG_R,
|
||||
GOLD_P1_FOOT_RIGHT_AVG_G,
|
||||
GOLD_P1_FOOT_RIGHT_AVG_B,
|
||||
|
||||
GOLD_P2_FOOT_UP_AVG_R,
|
||||
GOLD_P2_FOOT_UP_AVG_G,
|
||||
GOLD_P2_FOOT_UP_AVG_B,
|
||||
|
||||
GOLD_P2_FOOT_DOWN_AVG_R,
|
||||
GOLD_P2_FOOT_DOWN_AVG_G,
|
||||
GOLD_P2_FOOT_DOWN_AVG_B,
|
||||
|
||||
GOLD_P2_FOOT_LEFT_AVG_R,
|
||||
GOLD_P2_FOOT_LEFT_AVG_G,
|
||||
GOLD_P2_FOOT_LEFT_AVG_B,
|
||||
|
||||
GOLD_P2_FOOT_RIGHT_AVG_R,
|
||||
GOLD_P2_FOOT_RIGHT_AVG_G,
|
||||
GOLD_P2_FOOT_RIGHT_AVG_B,
|
||||
|
||||
GOLD_P1_STAGE_UP_LEFT,
|
||||
GOLD_P1_STAGE_UP_RIGHT,
|
||||
GOLD_P1_STAGE_DOWN_LEFT,
|
||||
GOLD_P1_STAGE_DOWN_RIGHT,
|
||||
|
||||
GOLD_P2_STAGE_UP_LEFT,
|
||||
GOLD_P2_STAGE_UP_RIGHT,
|
||||
GOLD_P2_STAGE_DOWN_LEFT,
|
||||
GOLD_P2_STAGE_DOWN_RIGHT,
|
||||
};
|
||||
}
|
||||
|
||||
// getters
|
||||
std::vector<Button> &get_buttons();
|
||||
std::vector<Light> &get_lights();
|
||||
}
|
||||
92
games/ddr/p3io/foot.cpp
Normal file
92
games/ddr/p3io/foot.cpp
Normal file
@@ -0,0 +1,92 @@
|
||||
#include "foot.h"
|
||||
|
||||
#include "rawinput/rawinput.h"
|
||||
#include "util/logging.h"
|
||||
|
||||
#include "../ddr.h"
|
||||
#include "../io.h"
|
||||
|
||||
bool games::ddr::DDRFOOTHandle::open(LPCWSTR lpFileName) {
|
||||
if (wcscmp(lpFileName, L"COM1") != 0) {
|
||||
return false;
|
||||
}
|
||||
log_info("ddr", "Opened COM1 (FOOT)");
|
||||
return true;
|
||||
}
|
||||
|
||||
int games::ddr::DDRFOOTHandle::read(LPVOID lpBuffer, DWORD nNumberOfBytesToRead) {
|
||||
|
||||
// check data trigger and buffer size
|
||||
if (data_trigger && nNumberOfBytesToRead >= 1) {
|
||||
data_trigger = false;
|
||||
memset(lpBuffer, 0x11, 1);
|
||||
return 1;
|
||||
}
|
||||
|
||||
// no data
|
||||
return 0;
|
||||
}
|
||||
|
||||
int games::ddr::DDRFOOTHandle::write(LPCVOID lpBuffer, DWORD nNumberOfBytesToWrite) {
|
||||
|
||||
// check buffer size for lights
|
||||
if (nNumberOfBytesToWrite == 4) {
|
||||
|
||||
// mappings
|
||||
static const size_t mapping_bits[] = {
|
||||
4, 6, 3, 5, // P1 L, U, R, D,
|
||||
12, 14, 11, 13, // P2 L, U, R, D
|
||||
22 // NEON
|
||||
};
|
||||
|
||||
static const size_t mapping[] = {
|
||||
Lights::P1_FOOT_LEFT,
|
||||
Lights::P1_FOOT_UP,
|
||||
Lights::P1_FOOT_RIGHT,
|
||||
Lights::P1_FOOT_DOWN,
|
||||
Lights::P2_FOOT_LEFT,
|
||||
Lights::P2_FOOT_UP,
|
||||
Lights::P2_FOOT_RIGHT,
|
||||
Lights::P2_FOOT_DOWN,
|
||||
Lights::NEON
|
||||
};
|
||||
|
||||
// get light bits
|
||||
uint32_t light_bits = *((uint32_t*) lpBuffer);
|
||||
|
||||
// get lights
|
||||
auto &lights = get_lights();
|
||||
|
||||
// bit scan
|
||||
for (size_t i = 0; i < 8; i++) {
|
||||
float value = (light_bits & (1 << mapping_bits[i])) ? 1.f : 0.f;
|
||||
GameAPI::Lights::writeLight(RI_MGR, lights.at(mapping[i]), value);
|
||||
}
|
||||
|
||||
// only set the neon if in SD mode
|
||||
// p3io sets it for HD mode.
|
||||
if (games::ddr::SDMODE) {
|
||||
float value = (light_bits & (1 << mapping_bits[8])) ? 1.f : 0.f;
|
||||
GameAPI::Lights::writeLight(RI_MGR, lights.at(mapping[8]), value);
|
||||
}
|
||||
|
||||
// flush
|
||||
RI_MGR->devices_flush_output();
|
||||
}
|
||||
|
||||
// trigger out data
|
||||
data_trigger = true;
|
||||
|
||||
// return all data written
|
||||
return (int) nNumberOfBytesToWrite;
|
||||
}
|
||||
|
||||
int games::ddr::DDRFOOTHandle::device_io(DWORD dwIoControlCode, LPVOID lpInBuffer, DWORD nInBufferSize, LPVOID lpOutBuffer,
|
||||
DWORD nOutBufferSize) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
bool games::ddr::DDRFOOTHandle::close() {
|
||||
log_info("ddr", "Closed COM1 (FOOT).");
|
||||
return true;
|
||||
}
|
||||
23
games/ddr/p3io/foot.h
Normal file
23
games/ddr/p3io/foot.h
Normal file
@@ -0,0 +1,23 @@
|
||||
#pragma once
|
||||
|
||||
#include "hooks/devicehook.h"
|
||||
|
||||
namespace games::ddr {
|
||||
|
||||
class DDRFOOTHandle : public CustomHandle {
|
||||
private:
|
||||
bool data_trigger = false;
|
||||
|
||||
public:
|
||||
bool open(LPCWSTR lpFileName) override;
|
||||
|
||||
int read(LPVOID lpBuffer, DWORD nNumberOfBytesToRead) override;
|
||||
|
||||
int write(LPCVOID lpBuffer, DWORD nNumberOfBytesToWrite) override;
|
||||
|
||||
int device_io(DWORD dwIoControlCode, LPVOID lpInBuffer, DWORD nInBufferSize, LPVOID lpOutBuffer,
|
||||
DWORD nOutBufferSize) override;
|
||||
|
||||
bool close() override;
|
||||
};
|
||||
}
|
||||
550
games/ddr/p3io/p3io.cpp
Normal file
550
games/ddr/p3io/p3io.cpp
Normal file
@@ -0,0 +1,550 @@
|
||||
#include "p3io.h"
|
||||
|
||||
#include "cfg/api.h"
|
||||
#include "rawinput/rawinput.h"
|
||||
#include "util/logging.h"
|
||||
#include "util/utils.h"
|
||||
|
||||
#include "../ddr.h"
|
||||
#include "../io.h"
|
||||
|
||||
using namespace acioemu;
|
||||
|
||||
games::ddr::DDRP3IOHandle::HDXSDevice::HDXSDevice() {
|
||||
this->node_count = 1;
|
||||
}
|
||||
|
||||
bool games::ddr::DDRP3IOHandle::HDXSDevice::parse_msg(
|
||||
MessageData* msg_in,
|
||||
circular_buffer<uint8_t> *response_buffer
|
||||
) {
|
||||
|
||||
#ifdef ACIOEMU_LOG
|
||||
log_info("ddr", "HDXS ADDR: {}, CMD: 0x{:x}", msg_in->addr, msg_in->cmd.code);
|
||||
#endif
|
||||
|
||||
// handle command
|
||||
switch (msg_in->cmd.code) {
|
||||
case ACIO_CMD_GET_VERSION: {
|
||||
|
||||
// send version data
|
||||
auto msg = this->create_msg(msg_in, MSG_VERSION_SIZE);
|
||||
this->set_version(msg, 0x204, 0, 1, 6, 0, "HDXS");
|
||||
write_msg(msg, response_buffer);
|
||||
delete msg;
|
||||
break;
|
||||
}
|
||||
case ACIO_CMD_KEEPALIVE: {
|
||||
|
||||
// send empty message
|
||||
auto msg = this->create_msg(msg_in, 0);
|
||||
write_msg(msg, response_buffer);
|
||||
delete msg;
|
||||
break;
|
||||
}
|
||||
case 0x0112: { // LED
|
||||
static const size_t hd_button_static_mapping[] {
|
||||
Lights::HD_P1_START,
|
||||
Lights::HD_P1_UP_DOWN,
|
||||
Lights::HD_P1_LEFT_RIGHT,
|
||||
Lights::HD_P2_START,
|
||||
Lights::HD_P2_UP_DOWN,
|
||||
Lights::HD_P2_LEFT_RIGHT,
|
||||
};
|
||||
static const size_t hd_panel_static_mapping[] {
|
||||
Lights::HD_P1_SPEAKER_F_G,
|
||||
Lights::HD_P1_SPEAKER_F_R,
|
||||
Lights::HD_P1_SPEAKER_F_B,
|
||||
Lights::HD_P2_SPEAKER_F_G,
|
||||
Lights::HD_P2_SPEAKER_F_R,
|
||||
Lights::HD_P2_SPEAKER_F_B,
|
||||
Lights::HD_P1_SPEAKER_W_G,
|
||||
Lights::HD_P1_SPEAKER_W_R,
|
||||
Lights::HD_P1_SPEAKER_W_B,
|
||||
Lights::HD_P2_SPEAKER_W_G,
|
||||
Lights::HD_P2_SPEAKER_W_R,
|
||||
Lights::HD_P2_SPEAKER_W_B,
|
||||
};
|
||||
|
||||
// get lights
|
||||
auto &lights = games::ddr::get_lights();
|
||||
|
||||
// check to see mode at runtime.
|
||||
if (!games::ddr::SDMODE) {
|
||||
const auto &data = &msg_in->cmd.raw[1];
|
||||
|
||||
// button LEDs
|
||||
for (size_t i = 0; i < std::size(hd_button_static_mapping); i++) {
|
||||
const float value = (data[i] & 0x80) ? 1.f : 0.f;
|
||||
GameAPI::Lights::writeLight(RI_MGR, lights[hd_button_static_mapping[i]], value);
|
||||
}
|
||||
|
||||
// speaker LEDs
|
||||
for (size_t i = 0; i < std::size(hd_panel_static_mapping) / 3; i++) {
|
||||
const size_t light_index = i * 3;
|
||||
const float g = static_cast<float>(data[light_index + 0] & 0x7f) / 127.f;
|
||||
const float r = static_cast<float>(data[light_index + 1] & 0x7f) / 127.f;
|
||||
const float b = static_cast<float>(data[light_index + 2] & 0x7f) / 127.f;
|
||||
GameAPI::Lights::writeLight(RI_MGR, lights[hd_panel_static_mapping[light_index + 0]], g);
|
||||
GameAPI::Lights::writeLight(RI_MGR, lights[hd_panel_static_mapping[light_index + 1]], r);
|
||||
GameAPI::Lights::writeLight(RI_MGR, lights[hd_panel_static_mapping[light_index + 2]], b);
|
||||
}
|
||||
}
|
||||
|
||||
// flush
|
||||
RI_MGR->devices_flush_output();
|
||||
|
||||
// send status 0
|
||||
auto msg = this->create_msg_status(msg_in, 0x00);
|
||||
write_msg(msg, response_buffer);
|
||||
delete msg;
|
||||
|
||||
break;
|
||||
}
|
||||
case ACIO_CMD_CLEAR:
|
||||
case ACIO_CMD_STARTUP:
|
||||
case 0x0110: // ???
|
||||
case 0x0128: // ???
|
||||
case 0xFF: // BROADCAST
|
||||
{
|
||||
// send status 0
|
||||
auto msg = this->create_msg_status(msg_in, 0x00);
|
||||
write_msg(msg, response_buffer);
|
||||
delete msg;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
|
||||
// mark as handled
|
||||
return true;
|
||||
}
|
||||
|
||||
void games::ddr::DDRP3IOHandle::write_msg(const uint8_t *data, size_t len) {
|
||||
read_buf.put(0xAA);
|
||||
read_buf.put((uint8_t) len);
|
||||
for (size_t i = 0; i < len; i++) {
|
||||
uint8_t b = data[i];
|
||||
if (b == 0xAA || b == 0xFF) {
|
||||
read_buf.put(0xFF);
|
||||
b = ~b;
|
||||
}
|
||||
read_buf.put(b);
|
||||
}
|
||||
}
|
||||
|
||||
bool games::ddr::DDRP3IOHandle::open(LPCWSTR lpFileName) {
|
||||
if (wcscmp(lpFileName, L"\\\\.\\P3IO\\p3io") != 0)
|
||||
return false;
|
||||
|
||||
if (!acio_emu) {
|
||||
acio_emu = new acioemu::ACIOEmu();
|
||||
// DO NOT CHANGE THE ORDER OF THESE
|
||||
// (until ICCA is split into separate devices with individual unit ids)
|
||||
acio_emu->add_device(new acioemu::ICCADevice(false, true, 2));
|
||||
acio_emu->add_device(new HDXSDevice());
|
||||
}
|
||||
|
||||
log_info("ddr", "Opened P3IO");
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
int games::ddr::DDRP3IOHandle::read(LPVOID lpBuffer, DWORD nNumberOfBytesToRead) {
|
||||
auto buffer = reinterpret_cast<uint8_t *>(lpBuffer);
|
||||
|
||||
// read from emu
|
||||
DWORD bytes_read = 0;
|
||||
while (!read_buf.empty() && bytes_read < nNumberOfBytesToRead) {
|
||||
buffer[bytes_read++] = read_buf.get();
|
||||
}
|
||||
|
||||
// this mustn't happen ever
|
||||
if (bytes_read == 0) {
|
||||
log_fatal("ddr", "p3io received no data");
|
||||
}
|
||||
|
||||
// return amount of bytes read
|
||||
return (int) bytes_read;
|
||||
}
|
||||
|
||||
int games::ddr::DDRP3IOHandle::write(LPCVOID lpBuffer, DWORD nNumberOfBytesToWrite) {
|
||||
auto buffer = reinterpret_cast<const uint8_t *>(lpBuffer);
|
||||
|
||||
// check header
|
||||
if (nNumberOfBytesToWrite < 2 || buffer[0] != 0xAA || buffer[1] > 0x7F) {
|
||||
log_warning("ddr", "p3io has the wrong header: {}", bin2hex(buffer, (int) nNumberOfBytesToWrite));
|
||||
|
||||
return (int) nNumberOfBytesToWrite;
|
||||
}
|
||||
|
||||
// parse data
|
||||
std::vector<uint8_t> parsed;
|
||||
for (DWORD i = 2; i < nNumberOfBytesToWrite; i++) {
|
||||
uint8_t b = buffer[i];
|
||||
|
||||
if (b == 0xFFu && i + 1 < nNumberOfBytesToWrite) {
|
||||
b = ~buffer[++i];
|
||||
}
|
||||
|
||||
parsed.push_back(b);
|
||||
}
|
||||
|
||||
// check message size
|
||||
if (parsed.size() != buffer[1]) {
|
||||
log_warning("ddr", "p3io has wrong message size: {}/{}", parsed.size(), (int) buffer[1]);
|
||||
return (int) nNumberOfBytesToWrite;
|
||||
}
|
||||
|
||||
// check command
|
||||
switch (parsed[1]) {
|
||||
case 0x01: { // VERSION
|
||||
uint8_t data[8];
|
||||
memset(data, 0, 8);
|
||||
|
||||
// device code
|
||||
strncpy((char*) &data[2], "JDX", 4);
|
||||
|
||||
// write message
|
||||
write_msg(data, sizeof(data));
|
||||
break;
|
||||
}
|
||||
case 0x05: { // WATCHDOG
|
||||
uint8_t data[] = {0x00, 0x00};
|
||||
write_msg(data, sizeof(data));
|
||||
break;
|
||||
}
|
||||
case 0x24: { // SET LIGHTS
|
||||
|
||||
// hd mappings
|
||||
static const size_t hd_mapping_bits[] {
|
||||
0x1000000, // HD SPOT RED
|
||||
0x2000000, // HD SPOT BLUE
|
||||
0x4000000, // HD TOP SPOT RED
|
||||
0x8000000, // HD TOP SPOT BLUE
|
||||
};
|
||||
static const size_t hd_mapping[] {
|
||||
Lights::SPOT_RED,
|
||||
Lights::SPOT_BLUE,
|
||||
Lights::TOP_SPOT_RED,
|
||||
Lights::TOP_SPOT_BLUE
|
||||
};
|
||||
|
||||
// sd mappings, bits are reused according to gamemode
|
||||
static const size_t sd_mapping_bits[] {
|
||||
0x01000000, // SD P1 BUTTON
|
||||
0x02000000, // SD P2 BUTTON
|
||||
0x40000000, // SD P1 HALOGEN LOWER
|
||||
0x80000000, // SD P1 HALOGEN UPPER
|
||||
0x10000000, // SD P2 HALOGEN LOWER
|
||||
0x20000000, // SD P2 HALOGEN UPPER
|
||||
};
|
||||
static const size_t sd_mapping[] {
|
||||
Lights::P1_BUTTON,
|
||||
Lights::P2_BUTTON,
|
||||
Lights::P1_HALOGEN_LOWER,
|
||||
Lights::P1_HALOGEN_UPPER,
|
||||
Lights::P2_HALOGEN_LOWER,
|
||||
Lights::P2_HALOGEN_UPPER
|
||||
};
|
||||
|
||||
static const size_t hd_to_sd_mapping[] {
|
||||
Lights::P1_HALOGEN_LOWER,
|
||||
Lights::P1_HALOGEN_UPPER,
|
||||
Lights::P2_HALOGEN_LOWER,
|
||||
Lights::P2_HALOGEN_UPPER
|
||||
};
|
||||
|
||||
// get light bits
|
||||
uint32_t light_bits = *reinterpret_cast<uint32_t *>(&parsed[3]);
|
||||
|
||||
// get lights
|
||||
auto &lights = get_lights();
|
||||
|
||||
// check to see mode at runtime.
|
||||
if (games::ddr::SDMODE) {
|
||||
|
||||
// bit scan
|
||||
for (size_t i = 0; i < 6; i++) {
|
||||
float value = (light_bits & sd_mapping_bits[i]) ? 1.f : 0.f;
|
||||
GameAPI::Lights::writeLight(RI_MGR, lights[sd_mapping[i]], value);
|
||||
}
|
||||
} else {
|
||||
|
||||
// bit scan
|
||||
for (size_t i = 0; i < 4; i++) {
|
||||
float value = (light_bits & hd_mapping_bits[i]) ? 1.f : 0.f;
|
||||
GameAPI::Lights::writeLight(RI_MGR, lights[hd_mapping[i]], value);
|
||||
|
||||
// perform HD->SD light mappings for older cabinet styles.
|
||||
GameAPI::Lights::writeLight(RI_MGR, lights[hd_to_sd_mapping[i]], value);
|
||||
}
|
||||
|
||||
// use both sat spots for a neon pulse
|
||||
float value_neon = (light_bits & hd_mapping_bits[0] && hd_mapping_bits[1]) ? 1.f : 0.f;
|
||||
GameAPI::Lights::writeLight(RI_MGR, lights[Lights::NEON], value_neon);
|
||||
}
|
||||
|
||||
// flush
|
||||
RI_MGR->devices_flush_output();
|
||||
|
||||
uint8_t data[] = {0x00, 0x00};
|
||||
write_msg(data, sizeof(data));
|
||||
break;
|
||||
}
|
||||
case 0x25: { // SEC PLUG
|
||||
uint8_t data[43] {};
|
||||
|
||||
// plug present
|
||||
data[2] = 1;
|
||||
|
||||
// copy data
|
||||
if (parsed[2] & 0x10) {
|
||||
static const uint8_t black_data[] {
|
||||
0xF0, 0xF1, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6, 0xF7,
|
||||
0xBE, 0xB5, 0xB2, 0xAC, 0x16, 0x8C, 0xE7, 0xA8,
|
||||
0x92, 0xB8, 0x1A, 0x86, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF7
|
||||
};
|
||||
memcpy(&data[3], black_data, sizeof(black_data));
|
||||
} else {
|
||||
static const uint8_t white_data[] {
|
||||
0xF0, 0xF1, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6, 0xF7,
|
||||
0xC3, 0xD4, 0x45, 0xE8, 0x7C, 0x17, 0x20, 0x08,
|
||||
0x82, 0x20, 0x08, 0x82, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1B
|
||||
};
|
||||
memcpy(&data[3], white_data, sizeof(white_data));
|
||||
}
|
||||
|
||||
// write data
|
||||
write_msg(data, sizeof(data));
|
||||
break;
|
||||
}
|
||||
case 0x27: { // CABINET TYPE
|
||||
if (games::ddr::SDMODE) {
|
||||
uint8_t data[] = {0x00, 0x00};
|
||||
write_msg(data, sizeof(data));
|
||||
} else {
|
||||
uint8_t data[] = {0x00, 0x01};
|
||||
write_msg(data, sizeof(data));
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 0x29: { // VIDEO FREQUENCY
|
||||
uint8_t data[] = {0x00, 0x00, 0x00};
|
||||
write_msg(data, sizeof(data));
|
||||
break;
|
||||
}
|
||||
case 0x2B: { // ???
|
||||
uint8_t data[] = {0x00, 0x00};
|
||||
write_msg(data, sizeof(data));
|
||||
break;
|
||||
}
|
||||
case 0x2F: { // MODE
|
||||
uint8_t data[] = {0x00, 0x00};
|
||||
write_msg(data, sizeof(data));
|
||||
break;
|
||||
}
|
||||
case 0x31: { // COIN STOCK
|
||||
uint8_t data[] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
|
||||
// TODO
|
||||
write_msg(data, sizeof(data));
|
||||
break;
|
||||
}
|
||||
case 0x32: { // ???
|
||||
uint8_t data[] = {0x00, 0x00};
|
||||
write_msg(data, sizeof(data));
|
||||
break;
|
||||
}
|
||||
case 0x38: { // PORT OPERATION
|
||||
uint8_t op = parsed[3];
|
||||
uint8_t port = parsed[2];
|
||||
//uint8_t baud = parsed[4];
|
||||
|
||||
// open port
|
||||
if (op == 0x00) {
|
||||
log_info("ddr", "opened p3io remote port #{}", (int) port);
|
||||
|
||||
uint8_t data[] = {0x00, 0x00, 0x00};
|
||||
write_msg(data, sizeof(data));
|
||||
break;
|
||||
}
|
||||
|
||||
// close port
|
||||
if (op == 0xFF) {
|
||||
log_info("ddr", "closed p3io remote port #{}", (int) port);
|
||||
|
||||
uint8_t data[] = {0x00, 0x00, 0x00};
|
||||
write_msg(data, sizeof(data));
|
||||
break;
|
||||
}
|
||||
|
||||
// error
|
||||
uint8_t data[] = {0x00, 0x00, 0xFF};
|
||||
write_msg(data, sizeof(data));
|
||||
break;
|
||||
}
|
||||
case 0x3A: { // REMOTE PORT WRITE
|
||||
uint8_t len = parsed[3];
|
||||
|
||||
// check length
|
||||
if (len > parsed.size() - 4) {
|
||||
log_fatal("ddr", "p3io remote port data too small");
|
||||
}
|
||||
|
||||
// pass data to ACIO
|
||||
for (int i = 4; i < len + 4; i++) {
|
||||
acio_emu->write(parsed[i]);
|
||||
}
|
||||
|
||||
// no error
|
||||
uint8_t data[] = {0x00, 0x00, len};
|
||||
write_msg(data, sizeof(data));
|
||||
break;
|
||||
}
|
||||
case 0x3B: { // REMOTE PORT READ
|
||||
uint8_t len = parsed[3];
|
||||
|
||||
// build msg data
|
||||
std::vector<uint8_t> msg_data;
|
||||
msg_data.reserve(static_cast<size_t>(len) + 3);
|
||||
msg_data.push_back(0x00);
|
||||
msg_data.push_back(0x00);
|
||||
|
||||
// placeholder for ACIO message size
|
||||
msg_data.push_back(len);
|
||||
|
||||
// read data from ACIO
|
||||
uint8_t acio_len = 0;
|
||||
while (acio_len < len && acio_len < 0xFF) {
|
||||
auto cur_byte = acio_emu->read();
|
||||
|
||||
if (cur_byte.has_value()) {
|
||||
msg_data.push_back(cur_byte.value());
|
||||
acio_len++;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// update placeholder with actual length
|
||||
msg_data[2] = acio_len;
|
||||
|
||||
// write message
|
||||
write_msg(msg_data.data(), msg_data.size());
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
log_fatal("ddr", "p3io unknown command: 0x{:x}", parsed[1]);
|
||||
}
|
||||
}
|
||||
|
||||
// return all data written
|
||||
return (int) nNumberOfBytesToWrite;
|
||||
}
|
||||
|
||||
int games::ddr::DDRP3IOHandle::device_io(
|
||||
DWORD dwIoControlCode,
|
||||
LPVOID lpInBuffer,
|
||||
DWORD nInBufferSize,
|
||||
LPVOID lpOutBuffer,
|
||||
DWORD nOutBufferSize
|
||||
) {
|
||||
|
||||
// check buffer size
|
||||
if (nOutBufferSize >= 4) {
|
||||
|
||||
// cool down
|
||||
Sleep(1);
|
||||
|
||||
// get controls as single variable (4 bytes)
|
||||
auto &controls = *(uint32_t*) lpOutBuffer;
|
||||
|
||||
// reset
|
||||
controls = 0;
|
||||
|
||||
/*
|
||||
* P3IO DDR Bit Mappings
|
||||
* 4 bytes, from low to high order (0-31)
|
||||
* all bits represent the inverted state
|
||||
*
|
||||
*
|
||||
* 30 - SERVICE
|
||||
* 28 - TEST
|
||||
* 29 - COIN MECH
|
||||
* 8 - P1 START
|
||||
* 9 - P1 PANEL UP
|
||||
* 10 - P1 PANEL DOWN
|
||||
* 11 - P1 PANEL LEFT
|
||||
* 12 - P1 PANEL RIGHT
|
||||
* 24 - P1 MENU UP
|
||||
* 25 - P1 MENU DOWN
|
||||
* 14 - P1 MENU LEFT
|
||||
* 15 - P1 MENU RIGHT
|
||||
* 16 - P2 START
|
||||
* 17 - P2 PANEL UP
|
||||
* 18 - P2 PANEL DOWN
|
||||
* 19 - P2 PANEL LEFT
|
||||
* 20 - P2 PANEL RIGHT
|
||||
* 26 - P2 MENU UP
|
||||
* 27 - P2 MENU DOWN
|
||||
* 22 - P2 MENU LEFT
|
||||
* 23 - P2 MENU RIGHT
|
||||
*/
|
||||
|
||||
// shift table
|
||||
static size_t shift_table[] = {
|
||||
30, 28, 29, 8, 9, 10, 11, 12, 24, 25, 14, 15, 16, 17, 18, 19, 20, 26, 27, 22, 23
|
||||
};
|
||||
static size_t button_table[] = {
|
||||
Buttons::SERVICE,
|
||||
Buttons::TEST,
|
||||
Buttons::COIN_MECH,
|
||||
Buttons::P1_START,
|
||||
Buttons::P1_PANEL_UP,
|
||||
Buttons::P1_PANEL_DOWN,
|
||||
Buttons::P1_PANEL_LEFT,
|
||||
Buttons::P1_PANEL_RIGHT,
|
||||
Buttons::P1_MENU_UP,
|
||||
Buttons::P1_MENU_DOWN,
|
||||
Buttons::P1_MENU_LEFT,
|
||||
Buttons::P1_MENU_RIGHT,
|
||||
Buttons::P2_START,
|
||||
Buttons::P2_PANEL_UP,
|
||||
Buttons::P2_PANEL_DOWN,
|
||||
Buttons::P2_PANEL_LEFT,
|
||||
Buttons::P2_PANEL_RIGHT,
|
||||
Buttons::P2_MENU_UP,
|
||||
Buttons::P2_MENU_DOWN,
|
||||
Buttons::P2_MENU_LEFT,
|
||||
Buttons::P2_MENU_RIGHT,
|
||||
};
|
||||
|
||||
// update states
|
||||
auto &buttons = get_buttons();
|
||||
size_t count = 0;
|
||||
for (auto shift : shift_table) {
|
||||
if (GameAPI::Buttons::getState(RI_MGR, buttons.at(button_table[count++]))) {
|
||||
controls |= 1 << shift;
|
||||
}
|
||||
}
|
||||
|
||||
// invert controls
|
||||
controls = ~controls;
|
||||
|
||||
// return data size
|
||||
return 4;
|
||||
}
|
||||
|
||||
// fail
|
||||
return -1;
|
||||
}
|
||||
|
||||
bool games::ddr::DDRP3IOHandle::close() {
|
||||
log_info("ddr", "Closed P3IO");
|
||||
return true;
|
||||
}
|
||||
37
games/ddr/p3io/p3io.h
Normal file
37
games/ddr/p3io/p3io.h
Normal file
@@ -0,0 +1,37 @@
|
||||
#pragma once
|
||||
|
||||
#include "acioemu/acioemu.h"
|
||||
#include "hooks/devicehook.h"
|
||||
|
||||
namespace games::ddr {
|
||||
|
||||
class DDRP3IOHandle : public CustomHandle {
|
||||
private:
|
||||
acioemu::ACIOEmu *acio_emu;
|
||||
circular_buffer<uint8_t> read_buf = circular_buffer<uint8_t>(1024);
|
||||
|
||||
class HDXSDevice : public acioemu::ACIODeviceEmu {
|
||||
public:
|
||||
HDXSDevice();
|
||||
|
||||
bool parse_msg(
|
||||
acioemu::MessageData* msg_in,
|
||||
circular_buffer<uint8_t> *response_buffer
|
||||
) override;
|
||||
};
|
||||
|
||||
void write_msg(const uint8_t *data, size_t len);
|
||||
|
||||
public:
|
||||
bool open(LPCWSTR lpFileName) override;
|
||||
|
||||
int read(LPVOID lpBuffer, DWORD nNumberOfBytesToRead) override;
|
||||
|
||||
int write(LPCVOID lpBuffer, DWORD nNumberOfBytesToWrite) override;
|
||||
|
||||
int device_io(DWORD dwIoControlCode, LPVOID lpInBuffer, DWORD nInBufferSize, LPVOID lpOutBuffer,
|
||||
DWORD nOutBufferSize) override;
|
||||
|
||||
bool close() override;
|
||||
};
|
||||
}
|
||||
102
games/ddr/p3io/sate.cpp
Normal file
102
games/ddr/p3io/sate.cpp
Normal file
@@ -0,0 +1,102 @@
|
||||
#include "sate.h"
|
||||
#include "util/logging.h"
|
||||
|
||||
using namespace acioemu;
|
||||
|
||||
games::ddr::DDRSATEHandle::SATEDevice::SATEDevice() {
|
||||
this->node_count = 7;
|
||||
}
|
||||
|
||||
bool games::ddr::DDRSATEHandle::SATEDevice::parse_msg(
|
||||
acioemu::MessageData *msg_in,
|
||||
circular_buffer<uint8_t> *response_buffer
|
||||
) {
|
||||
|
||||
// check command
|
||||
switch (msg_in->cmd.code) {
|
||||
case ACIO_CMD_GET_VERSION: {
|
||||
|
||||
// send version data
|
||||
auto msg = this->create_msg(msg_in, MSG_VERSION_SIZE);
|
||||
this->set_version(msg, 0x105, 0, 1, 1, 0, "DDRS");
|
||||
write_msg(msg, response_buffer);
|
||||
delete msg;
|
||||
break;
|
||||
}
|
||||
case ACIO_CMD_CLEAR:
|
||||
case ACIO_CMD_STARTUP:
|
||||
case 0xFF: // BROADCAST
|
||||
{
|
||||
// send status 0
|
||||
auto msg = this->create_msg_status(msg_in, 0x00);
|
||||
write_msg(msg, response_buffer);
|
||||
delete msg;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
|
||||
// mark as handled
|
||||
return true;
|
||||
}
|
||||
|
||||
bool games::ddr::DDRSATEHandle::open(LPCWSTR lpFileName) {
|
||||
if (wcscmp(lpFileName, L"COM2") != 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
log_info("ddr", "Opened COM2 (SATE)");
|
||||
|
||||
// ACIO device
|
||||
acio_emu.add_device(new SATEDevice());
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
int games::ddr::DDRSATEHandle::read(LPVOID lpBuffer, DWORD nNumberOfBytesToRead) {
|
||||
auto buffer = reinterpret_cast<uint8_t *>(lpBuffer);
|
||||
|
||||
// read from emu
|
||||
DWORD bytes_read = 0;
|
||||
while (bytes_read < nNumberOfBytesToRead) {
|
||||
auto cur_byte = acio_emu.read();
|
||||
|
||||
if (cur_byte.has_value()) {
|
||||
buffer[bytes_read++] = cur_byte.value();
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// return amount of bytes read
|
||||
return (int) bytes_read;
|
||||
}
|
||||
|
||||
int games::ddr::DDRSATEHandle::write(LPCVOID lpBuffer, DWORD nNumberOfBytesToWrite) {
|
||||
auto buffer = reinterpret_cast<const uint8_t *>(lpBuffer);
|
||||
|
||||
// write to emu
|
||||
for (DWORD i = 0; i < nNumberOfBytesToWrite; i++) {
|
||||
acio_emu.write(buffer[i]);
|
||||
}
|
||||
|
||||
// return all data written
|
||||
return (int) nNumberOfBytesToWrite;
|
||||
}
|
||||
|
||||
int games::ddr::DDRSATEHandle::device_io(
|
||||
DWORD dwIoControlCode,
|
||||
LPVOID lpInBuffer,
|
||||
DWORD nInBufferSize,
|
||||
LPVOID lpOutBuffer,
|
||||
DWORD nOutBufferSize
|
||||
) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
bool games::ddr::DDRSATEHandle::close() {
|
||||
log_info("ddr", "Closed COM2 (SATE).");
|
||||
|
||||
return true;
|
||||
}
|
||||
31
games/ddr/p3io/sate.h
Normal file
31
games/ddr/p3io/sate.h
Normal file
@@ -0,0 +1,31 @@
|
||||
#pragma once
|
||||
|
||||
#include "hooks/devicehook.h"
|
||||
#include "acioemu/acioemu.h"
|
||||
|
||||
namespace games::ddr {
|
||||
|
||||
class DDRSATEHandle : public CustomHandle {
|
||||
private:
|
||||
acioemu::ACIOEmu acio_emu;
|
||||
|
||||
class SATEDevice : public acioemu::ACIODeviceEmu {
|
||||
public:
|
||||
SATEDevice();
|
||||
|
||||
bool parse_msg(acioemu::MessageData *msg_in, circular_buffer<uint8_t> *response_buffer) override;
|
||||
};
|
||||
|
||||
public:
|
||||
bool open(LPCWSTR lpFileName) override;
|
||||
|
||||
int read(LPVOID lpBuffer, DWORD nNumberOfBytesToRead) override;
|
||||
|
||||
int write(LPCVOID lpBuffer, DWORD nNumberOfBytesToWrite) override;
|
||||
|
||||
int device_io(DWORD dwIoControlCode, LPVOID lpInBuffer, DWORD nInBufferSize, LPVOID lpOutBuffer,
|
||||
DWORD nOutBufferSize) override;
|
||||
|
||||
bool close() override;
|
||||
};
|
||||
}
|
||||
60
games/ddr/p3io/usbmem.cpp
Normal file
60
games/ddr/p3io/usbmem.cpp
Normal file
@@ -0,0 +1,60 @@
|
||||
#include "usbmem.h"
|
||||
#include "util/logging.h"
|
||||
|
||||
void games::ddr::DDRUSBMEMHandle::respond(const char *data) {
|
||||
size_t len = strlen(data) + 1;
|
||||
memcpy(response_data, data, len);
|
||||
memcpy(response_data + len, "\r>", 2);
|
||||
response_data_size = len + 2;
|
||||
}
|
||||
|
||||
bool games::ddr::DDRUSBMEMHandle::open(LPCWSTR lpFileName) {
|
||||
if (wcscmp(lpFileName, L"COM3") != 0) {
|
||||
return false;
|
||||
}
|
||||
log_info("ddr", "Opened COM3 (USBMEM)");
|
||||
return true;
|
||||
}
|
||||
|
||||
int games::ddr::DDRUSBMEMHandle::read(LPVOID lpBuffer, DWORD nNumberOfBytesToRead) {
|
||||
|
||||
// check for response
|
||||
if (response_data_size && nNumberOfBytesToRead >= response_data_size) {
|
||||
size_t bytes_read = response_data_size;
|
||||
memcpy(lpBuffer, response_data, response_data_size);
|
||||
response_data_size = 0;
|
||||
return (int) bytes_read;
|
||||
}
|
||||
|
||||
// no data
|
||||
return 0;
|
||||
}
|
||||
|
||||
int games::ddr::DDRUSBMEMHandle::write(LPCVOID lpBuffer, DWORD nNumberOfBytesToWrite) {
|
||||
|
||||
// check CMD
|
||||
if (!memcmp(lpBuffer, "sver", 4))
|
||||
respond("done GQHDXJAA SPICE");
|
||||
else if (!memcmp(lpBuffer, "on_a", 4) ||
|
||||
!memcmp(lpBuffer, "on_b", 4) ||
|
||||
!memcmp(lpBuffer, "offa", 4) ||
|
||||
!memcmp(lpBuffer, "offb", 4) ||
|
||||
!memcmp(lpBuffer, "lma ", 4) ||
|
||||
!memcmp(lpBuffer, "lmb ", 4))
|
||||
respond("done");
|
||||
else
|
||||
respond("not connected");
|
||||
|
||||
// return all data written
|
||||
return (int) nNumberOfBytesToWrite;
|
||||
}
|
||||
|
||||
int games::ddr::DDRUSBMEMHandle::device_io(DWORD dwIoControlCode, LPVOID lpInBuffer, DWORD nInBufferSize,
|
||||
LPVOID lpOutBuffer, DWORD nOutBufferSize) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
bool games::ddr::DDRUSBMEMHandle::close() {
|
||||
log_info("ddr", "Closed COM3 (USBMEM).");
|
||||
return true;
|
||||
}
|
||||
26
games/ddr/p3io/usbmem.h
Normal file
26
games/ddr/p3io/usbmem.h
Normal file
@@ -0,0 +1,26 @@
|
||||
#pragma once
|
||||
|
||||
#include "hooks/devicehook.h"
|
||||
|
||||
namespace games::ddr {
|
||||
|
||||
class DDRUSBMEMHandle : public CustomHandle {
|
||||
private:
|
||||
char response_data[256]{};
|
||||
size_t response_data_size = 0;
|
||||
|
||||
void respond(const char *data);
|
||||
|
||||
public:
|
||||
bool open(LPCWSTR lpFileName) override;
|
||||
|
||||
int read(LPVOID lpBuffer, DWORD nNumberOfBytesToRead) override;
|
||||
|
||||
int write(LPCVOID lpBuffer, DWORD nNumberOfBytesToWrite) override;
|
||||
|
||||
int device_io(DWORD dwIoControlCode, LPVOID lpInBuffer, DWORD nInBufferSize, LPVOID lpOutBuffer,
|
||||
DWORD nOutBufferSize) override;
|
||||
|
||||
bool close() override;
|
||||
};
|
||||
}
|
||||
352
games/ddr/p4io/p4io.cpp
Normal file
352
games/ddr/p4io/p4io.cpp
Normal file
@@ -0,0 +1,352 @@
|
||||
#include "p4io.h"
|
||||
|
||||
#include "rawinput/rawinput.h"
|
||||
#include "util/logging.h"
|
||||
#include "util/utils.h"
|
||||
|
||||
#include "../io.h"
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
#ifndef FILE_DEVICE_UNKNOWN
|
||||
#define FILE_DEVICE_UNKNOWN 0x00000022
|
||||
#endif
|
||||
|
||||
// Packet header seems to be
|
||||
// AA cmdId Seq maybe_length?
|
||||
|
||||
namespace {
|
||||
#pragma pack(push, 1)
|
||||
enum P4IOIoCtl : uint16_t {
|
||||
P4IO_IOCTL_GET_INPUTS = 0x801,
|
||||
P4IO_IOCTL_GET_VERSION_FUNC = 0x803,
|
||||
};
|
||||
|
||||
enum class P4IOCmd : uint8_t {
|
||||
Nop = 0x00,
|
||||
GetTypeVerProd = 0x01,
|
||||
UpdateLights = 0x12,
|
||||
CoinHandler = 0x18,
|
||||
Unk_1C = 0x1C,
|
||||
IoSciOpen = 0x20,
|
||||
SciTransfer = 0x21,
|
||||
IoSciClose = 0x24,
|
||||
ReadSecPlug_01 = 0x40,
|
||||
ReadSecPlug_02 = 0x41,
|
||||
};
|
||||
|
||||
struct P4IOHeader {
|
||||
uint8_t magic;
|
||||
P4IOCmd cmd;
|
||||
uint8_t seq;
|
||||
uint8_t length;
|
||||
};
|
||||
#pragma pack(pop)
|
||||
|
||||
const uint8_t blackPlugData[0x20] = {
|
||||
0xC9, 0xFE, 0xC1, 0x5B, 0x8D, 0x8A, 0xE7, 0xA8,
|
||||
0x92, 0xB8, 0x1A, 0x86, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77
|
||||
};
|
||||
|
||||
const uint8_t whitePlugData[0x20] = {
|
||||
0x18, 0x98, 0x12, 0x71, 0xB3, 0xA2, 0x20, 0x08,
|
||||
0x82, 0x20, 0x08, 0x82, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xDA
|
||||
};
|
||||
}
|
||||
|
||||
namespace games::ddr {
|
||||
bool DDRP4IOHandle::open(LPCWSTR lpFileName) {
|
||||
if (wcscmp(lpFileName, L"\\\\.\\P4IO\\p4io") != 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!m_acio_emu) {
|
||||
m_acio_emu = new acioemu::ACIOEmu();
|
||||
m_acio_emu->add_device(new acioemu::ICCADevice(false, true, 2));
|
||||
m_acio_emu->add_device(new HDXSDevice());
|
||||
}
|
||||
|
||||
log_info("ddr", "Opened P4IO");
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
int DDRP4IOHandle::read(LPVOID lpBuffer, DWORD nNumberOfBytesToRead) {
|
||||
DWORD transferSize = std::min(nNumberOfBytesToRead, (DWORD)m_read_buf.size());
|
||||
memcpy(lpBuffer, m_read_buf.data(), transferSize);
|
||||
m_read_buf.erase(m_read_buf.begin(), m_read_buf.begin()+transferSize);
|
||||
|
||||
return transferSize;
|
||||
}
|
||||
|
||||
int DDRP4IOHandle::write(LPCVOID lpBuffer, DWORD nNumberOfBytesToWrite) {
|
||||
const P4IOHeader *header = reinterpret_cast<const P4IOHeader*>(lpBuffer);
|
||||
const uint8_t *data = reinterpret_cast<const uint8_t*>(reinterpret_cast<const uint8_t*>(lpBuffer)+sizeof(P4IOHeader));
|
||||
|
||||
if(nNumberOfBytesToWrite < sizeof(P4IOHeader)
|
||||
|| header->magic != 0xAA
|
||||
|| nNumberOfBytesToWrite < sizeof(P4IOHeader)+header->length)
|
||||
{
|
||||
log_warning("ddr", "p4io has the wrong header: {}", bin2hex(reinterpret_cast<const uint8_t*>(lpBuffer),
|
||||
(int)nNumberOfBytesToWrite));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
std::vector<uint8_t> responseBuffer(64);
|
||||
P4IOHeader *respHeader = reinterpret_cast<P4IOHeader*>(responseBuffer.data());
|
||||
respHeader->magic = 0xAA;
|
||||
respHeader->cmd = header->cmd;
|
||||
respHeader->seq = header->seq;
|
||||
uint8_t *respData = responseBuffer.data()+sizeof(P4IOHeader);
|
||||
|
||||
switch(header->cmd)
|
||||
{
|
||||
case P4IOCmd::Nop:
|
||||
case P4IOCmd::Unk_1C:
|
||||
case P4IOCmd::IoSciOpen:
|
||||
case P4IOCmd::IoSciClose:
|
||||
{
|
||||
m_read_buf.insert(m_read_buf.end(), responseBuffer.begin(), responseBuffer.end());
|
||||
return nNumberOfBytesToWrite;
|
||||
}
|
||||
break;
|
||||
case P4IOCmd::GetTypeVerProd: {
|
||||
respHeader->length = 0x2C;
|
||||
|
||||
// type
|
||||
memcpy(&respData[0], "JDX", 4);
|
||||
|
||||
// version
|
||||
respData[5] = 69;
|
||||
respData[6] = 69;
|
||||
respData[7] = 69;
|
||||
|
||||
// prod
|
||||
memcpy(&respData[8], "prod", 4);
|
||||
|
||||
// date
|
||||
memcpy(&respData[0xC], "2024-03-12", 10);
|
||||
|
||||
// time
|
||||
memcpy(&respData[0x1C], "06:39:21", 8);
|
||||
|
||||
m_read_buf_mutex.lock();
|
||||
m_read_buf.insert(m_read_buf.end(), responseBuffer.begin(), responseBuffer.end());
|
||||
m_read_buf_mutex.unlock();
|
||||
return nNumberOfBytesToWrite;
|
||||
}
|
||||
break;
|
||||
case P4IOCmd::UpdateLights: {
|
||||
static const size_t lightMapping[] = {
|
||||
Lights::WHITE_SPEAKER_TOP_G,
|
||||
Lights::WHITE_SPEAKER_TOP_R,
|
||||
Lights::WHITE_SPEAKER_TOP_B,
|
||||
Lights::WHITE_SPEAKER_BOTTOM_G,
|
||||
Lights::WHITE_SPEAKER_BOTTOM_R,
|
||||
Lights::WHITE_SPEAKER_BOTTOM_B,
|
||||
Lights::WHITE_WOOFER_G,
|
||||
Lights::WHITE_WOOFER_R,
|
||||
Lights::WHITE_WOOFER_B,
|
||||
Lights::HD_P1_START,
|
||||
Lights::HD_P1_UP_DOWN,
|
||||
Lights::HD_P1_LEFT_RIGHT
|
||||
};
|
||||
|
||||
auto &lights = get_lights();
|
||||
|
||||
for(size_t i = 0; i < std::size(lightMapping); ++i) {
|
||||
GameAPI::Lights::writeLight(RI_MGR, lights[lightMapping[i]], data[i] == 0 ? 0.f : 1.f);
|
||||
}
|
||||
|
||||
RI_MGR->devices_flush_output();
|
||||
|
||||
m_read_buf_mutex.lock();
|
||||
m_read_buf.insert(m_read_buf.end(), responseBuffer.begin(), responseBuffer.end());
|
||||
m_read_buf_mutex.unlock();
|
||||
return nNumberOfBytesToWrite;
|
||||
}
|
||||
break;
|
||||
case P4IOCmd::CoinHandler: {
|
||||
respHeader->length = 0x04;
|
||||
|
||||
// TODO:
|
||||
m_read_buf_mutex.lock();
|
||||
m_read_buf.insert(m_read_buf.end(), responseBuffer.begin(), responseBuffer.end());
|
||||
m_read_buf_mutex.unlock();
|
||||
return nNumberOfBytesToWrite;
|
||||
}
|
||||
break;
|
||||
case P4IOCmd::SciTransfer: {
|
||||
// request is 1 byte port #, followed by data
|
||||
|
||||
if(header->length > 1 && data[0] == 0) {
|
||||
for(size_t i = 0; i < header->length-1u; ++i) {
|
||||
m_acio_emu->write(data[1+i]);
|
||||
}
|
||||
}
|
||||
|
||||
respHeader->length = 2;
|
||||
|
||||
//response is 1 byte port #, 1 byte error flag, followed by data
|
||||
if(header->length >= 1 && data[0] == 0) {
|
||||
uint8_t readBytes = 0;
|
||||
|
||||
while(readBytes < 58) {
|
||||
const auto b = m_acio_emu->read();
|
||||
|
||||
if(!b.has_value()) {
|
||||
break;
|
||||
}
|
||||
|
||||
respData[2+readBytes] = b.value();
|
||||
++readBytes;
|
||||
}
|
||||
|
||||
respHeader->length = 2+readBytes;
|
||||
}
|
||||
|
||||
m_read_buf_mutex.lock();
|
||||
m_read_buf.insert(m_read_buf.end(), responseBuffer.begin(), responseBuffer.end());
|
||||
m_read_buf_mutex.unlock();
|
||||
return nNumberOfBytesToWrite;
|
||||
}
|
||||
break;
|
||||
case P4IOCmd::ReadSecPlug_01: {
|
||||
// maybe just a presense check
|
||||
|
||||
respHeader->length = 0x09;
|
||||
memset(&respData[1], 0xAA, 8);
|
||||
|
||||
m_read_buf_mutex.lock();
|
||||
m_read_buf.insert(m_read_buf.end(), responseBuffer.begin(), responseBuffer.end());
|
||||
m_read_buf_mutex.unlock();
|
||||
return nNumberOfBytesToWrite;
|
||||
}
|
||||
break;
|
||||
case P4IOCmd::ReadSecPlug_02: {
|
||||
respHeader->length = 0x21;
|
||||
|
||||
if(data[0] == 0) {
|
||||
memcpy(&respData[1], blackPlugData, sizeof(blackPlugData));
|
||||
} else {
|
||||
memcpy(&respData[1], whitePlugData, sizeof(whitePlugData));
|
||||
}
|
||||
|
||||
m_read_buf_mutex.lock();
|
||||
m_read_buf.insert(m_read_buf.end(), responseBuffer.begin(), responseBuffer.end());
|
||||
m_read_buf_mutex.unlock();
|
||||
return nNumberOfBytesToWrite;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int DDRP4IOHandle::device_io(DWORD dwIoControlCode, LPVOID lpInBuffer, DWORD nInBufferSize, LPVOID lpOutBuffer,
|
||||
DWORD nOutBufferSize) {
|
||||
if(((dwIoControlCode>>16)&0xFFFF) == FILE_DEVICE_UNKNOWN) {
|
||||
switch((dwIoControlCode>>2)&0x3FFF) {
|
||||
case P4IO_IOCTL_GET_INPUTS: {
|
||||
memset(lpOutBuffer, 0, 16);
|
||||
auto controls = (uint32_t*) lpOutBuffer;
|
||||
|
||||
const std::pair<size_t, uint8_t> buttonMapping[] =
|
||||
{
|
||||
{Buttons::SERVICE, 25}, //Good
|
||||
{Buttons::TEST, 28}, //Good
|
||||
{Buttons::COIN_MECH, 24}, //Good
|
||||
{Buttons::P1_START, 0}, //Good
|
||||
{Buttons::P1_PANEL_UP, 5}, //Good
|
||||
{Buttons::P1_PANEL_DOWN, 6}, //Good
|
||||
{Buttons::P1_PANEL_LEFT, 7}, //Goood
|
||||
{Buttons::P1_PANEL_RIGHT, 16}, //Good
|
||||
{Buttons::P1_MENU_UP, 1}, //Good
|
||||
{Buttons::P1_MENU_DOWN, 2}, //Good
|
||||
{Buttons::P1_MENU_LEFT, 3}, //Good
|
||||
{Buttons::P1_MENU_RIGHT, 4}, //Good
|
||||
{Buttons::P2_START, 8}, //Good
|
||||
{Buttons::P2_PANEL_UP, 13}, //Good
|
||||
{Buttons::P2_PANEL_DOWN, 14}, //Good
|
||||
{Buttons::P2_PANEL_LEFT, 15}, //Good
|
||||
{Buttons::P2_PANEL_RIGHT, 20}, //Good
|
||||
{Buttons::P2_MENU_UP, 9}, //Good
|
||||
{Buttons::P2_MENU_DOWN, 10}, //Good
|
||||
{Buttons::P2_MENU_LEFT, 11}, //Good
|
||||
{Buttons::P2_MENU_RIGHT, 12}, //Good
|
||||
};
|
||||
|
||||
auto &buttons = get_buttons();
|
||||
for(const auto &mapping : buttonMapping)
|
||||
{
|
||||
if(GameAPI::Buttons::getState(RI_MGR, buttons[mapping.first]))
|
||||
{
|
||||
controls[mapping.second/32] |= 1<<(mapping.second%32);
|
||||
}
|
||||
}
|
||||
|
||||
return 16;
|
||||
}
|
||||
break;
|
||||
case P4IO_IOCTL_GET_VERSION_FUNC: {
|
||||
if(nOutBufferSize > strlen("spicy p4io") + 1) {
|
||||
strcpy((char*)lpOutBuffer, "spicy p4io");
|
||||
return 11;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool DDRP4IOHandle::close() {
|
||||
log_info("ddr", "Closed P4IO");
|
||||
return true;
|
||||
}
|
||||
|
||||
DDRP4IOHandle::HDXSDevice::HDXSDevice() {
|
||||
this->node_count = 1;
|
||||
}
|
||||
|
||||
bool DDRP4IOHandle::HDXSDevice::parse_msg(
|
||||
acioemu::MessageData* msg_in,
|
||||
circular_buffer<uint8_t> *response_buffer) {
|
||||
switch(msg_in->cmd.code)
|
||||
{
|
||||
case acioemu::ACIO_CMD_GET_VERSION: {
|
||||
auto msg = this->create_msg(msg_in, acioemu::MSG_VERSION_SIZE);
|
||||
this->set_version(msg, 0x204, 0, 1, 6, 0, "HDXS");
|
||||
write_msg(msg, response_buffer);
|
||||
delete msg;
|
||||
}
|
||||
break;
|
||||
case acioemu::ACIO_CMD_KEEPALIVE: {
|
||||
auto msg = this->create_msg(msg_in, 0);
|
||||
write_msg(msg, response_buffer);
|
||||
delete msg;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case acioemu::ACIO_CMD_STARTUP:
|
||||
case acioemu::ACIO_CMD_CLEAR:
|
||||
case 0x110:
|
||||
case 0x128:
|
||||
{
|
||||
auto msg = this->create_msg_status(msg_in, 0x00);
|
||||
write_msg(msg, response_buffer);
|
||||
delete msg;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
log_info("ddr", "HDXS unhandled {:03X}", msg_in->cmd.code);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
36
games/ddr/p4io/p4io.h
Normal file
36
games/ddr/p4io/p4io.h
Normal file
@@ -0,0 +1,36 @@
|
||||
#pragma once
|
||||
|
||||
#include "acioemu/acioemu.h"
|
||||
#include "hooks/devicehook.h"
|
||||
#include <mutex>
|
||||
|
||||
namespace games::ddr {
|
||||
class DDRP4IOHandle : public CustomHandle {
|
||||
private:
|
||||
acioemu::ACIOEmu *m_acio_emu;
|
||||
std::vector<uint8_t> m_read_buf;
|
||||
std::mutex m_read_buf_mutex;
|
||||
|
||||
class HDXSDevice : public acioemu::ACIODeviceEmu {
|
||||
public:
|
||||
HDXSDevice();
|
||||
|
||||
bool parse_msg(
|
||||
acioemu::MessageData* msg_in,
|
||||
circular_buffer<uint8_t> *response_buffer
|
||||
) override;
|
||||
};
|
||||
|
||||
public:
|
||||
bool open(LPCWSTR lpFileName) override;
|
||||
|
||||
int read(LPVOID lpBuffer, DWORD nNumberOfBytesToRead) override;
|
||||
|
||||
int write(LPCVOID lpBuffer, DWORD nNumberOfBytesToWrite) override;
|
||||
|
||||
int device_io(DWORD dwIoControlCode, LPVOID lpInBuffer, DWORD nInBufferSize, LPVOID lpOutBuffer,
|
||||
DWORD nOutBufferSize) override;
|
||||
|
||||
bool close() override;
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user