ezusb driver and optional devicepath.dat

This commit is contained in:
CrazyRedMachine
2020-08-10 00:15:58 +02:00
parent 0dad13c220
commit bc47b562ff
12 changed files with 405 additions and 25 deletions
Binary file not shown.
+1 -8
View File
@@ -7,11 +7,4 @@ set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -static-libstdc++ -static-libgcc")
include_directories(${popn_modeswitch_SOURCE_DIR})
link_directories(${PROJECT_SOURCE_DIR}/include)
# STATIC LINK HID
find_library(HIDDLL NAMES hid PATHS ${CMAKE_SOURCE_DIR}/lib)
message(STATUS "find_library for hid returned ${HIDDLL}")
add_library(hid MODULE IMPORTED)
set_property(TARGET hid PROPERTY IMPORTED_LOCATION "${HIDDLL}")
add_executable(modeswitch modeswitch.cpp)
target_link_libraries(modeswitch hid)
add_executable(modeswitch modeswitch.cpp)
+55 -17
View File
@@ -1,13 +1,61 @@
/* TO BE COMPILED WITH MINGW */
/* Visual C++ compilation didn't work for me on the BemaniPC (invalid win32 application) */
#define WINVER 0x0500
#include <windows.h>
#include <fstream>
HANDLE g_hidHandle;
/**
* Initialize the g_hidHandle global variable
* It will attempt to read paths from devicepath.dat file if it exists until it finds one which works.
* If nothing is found, as a last resort it will try the two default paths from the Due and Leonardo
* versions of the firmware.
*
* @return 0 on success, -1 on error
*/
static int controller_init(){
FILE *file;
char path[256];
file = fopen("devicepath.dat", "r");
if (file == NULL)
{
goto last_resort;
}
while ( fgets(path,256,file) != NULL )
{
path[strcspn(path, "\r\n")] = 0;
g_hidHandle = CreateFile(path, GENERIC_READ|GENERIC_WRITE, FILE_SHARE_READ|FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL);
if ( g_hidHandle != INVALID_HANDLE_VALUE )
break;
}
fclose(file);
if ( g_hidHandle == INVALID_HANDLE_VALUE )
{
goto last_resort;
}
return 0;
last_resort:
g_hidHandle = CreateFile("\\\\?\\HID#VID_2341&PID_003E&MI_02#7&3156e204&0&0000#{4d1e55b2-f16f-11cf-88cb-001111000030}",
GENERIC_READ|GENERIC_WRITE, FILE_SHARE_READ|FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL);
if ( g_hidHandle != INVALID_HANDLE_VALUE )
return 0;
g_hidHandle = CreateFile("\\\\?\\HID#VID_2341&PID_8036&MI_02#7&63200bf&0&0000#{4d1e55b2-f16f-11cf-88cb-001111000030}",
GENERIC_READ|GENERIC_WRITE, FILE_SHARE_READ|FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL);
if ( g_hidHandle != INVALID_HANDLE_VALUE )
return 0;
printf("Couldn't open device. Make sure devicepath.dat exists and contains the correct path.\r\n");
return -1;
}
int main(int argc, char* argv[])
{
FILE *file;
char path[256];
HANDLE hidHandle;
char OutputReport[5];
int res;
int mode;
@@ -25,18 +73,8 @@ int main(int argc, char* argv[])
printf("Invalid mode value %d\r\n", mode);
return 2;
}
file = fopen("devicepath.dat", "r");
while ( fgets(path,256,file) != NULL )
{
path[strcspn(path, "\r\n")] = 0;
hidHandle = CreateFile(path, GENERIC_READ|GENERIC_WRITE, FILE_SHARE_READ|FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL);
if ( hidHandle != INVALID_HANDLE_VALUE )
break;
}
fclose(file);
if ( hidHandle == INVALID_HANDLE_VALUE )
if ( controller_init() == -1 )
{
printf("Couldn't open device. Make sure devicepath.dat contains the correct path.\r\n");
return 3;
@@ -48,7 +86,7 @@ int main(int argc, char* argv[])
OutputReport[3] = 0x00;
OutputReport[4] = mode+16; //mode+magicbit
WriteFile(hidHandle, OutputReport, 5, &BytesWritten, NULL);
WriteFile(g_hidHandle, OutputReport, 5, &BytesWritten, NULL);
if ( BytesWritten == 5 )
{
res = 0;
@@ -74,6 +112,6 @@ int main(int argc, char* argv[])
res = 4;
}
CloseHandle(hidHandle);
CloseHandle(g_hidHandle);
return res;
}
+2
View File
@@ -38,6 +38,8 @@ The included devicepath.dat should work with both Due and Leonardo versions out
devicepath.dat file should contain the device path on its own on a single line. Multiple paths on multiple lines should work too in which case it will open the first valid path it finds.
If no valid path is found or if there is no `devicepath.dat` file, it will attempt to open the default paths from the Due and Leonardo versions (same as the ones found in this included devicepath file).
### spicetools
open ```%appdata%/spicetools.xml```, look for a "devid" value starting with ```\\?\HID#VID_2341&amp;PID_003E``` (Due) or ```\\?\HID#VID_2341&amp;PID_8036``` (Leonardo)
+6
View File
@@ -48,6 +48,12 @@ The 00 key is mapped to comma (for direct compatibility with spicetools) and the
It also has a cool light animation on boot which you can easily adapt to your liking :)
## ezusb driver
The I/O is HID so it can be mapped with the usual IO emulation tools, but it can also be used without emulation, just like an official IOBoard, provided you replace the original `ezusb.dll` file with the one from this repo.
This way the firmware is fully compatible with anything that works on an official cabinet (PopnForwarder (a bit silly but why not :D), DJMame...)
## Light modes
There are 4 different modes :
Binary file not shown.
BIN
View File
Binary file not shown.
+19
View File
@@ -0,0 +1,19 @@
# ezusb.dll for the Pop'n Ultimate Controller
This is a dll meant to seamlessly replace the original one from the different games (and compatible with both the PopnForwarder and DJMame, or really anything else meant to be used with ezusb.dll).
For ease of use I included pre-compiled dll working on WinXP Embedded (for official Pop'n Music cabinets).
## Usage
Replace the game ezusb.dll file with this one and the game will read input and send lights to your Arduino as if it were the original IOBoard.
Pop'n Music Adventure used a different dll format so you have to set a flag in `ezusb.h` for the dll to compile in a way that will work with this game.
The dll makes the lights turn on for 1 sec on load so you'll know if the game managed to load your device.
## How to retrieve devicepath
The dll will attempt to open the default paths from the Due and Leonardo versions of this firmware, in case it doesn't work you can put a `devicepath.dat` file in the same folder as the dll.
See instructions from ModeSwitch to know more about the path format and how to find the one corresponding to your device.
+12
View File
@@ -0,0 +1,12 @@
cmake_minimum_required(VERSION 3.14)
project(UltimatePopnController-ezusb)
set(CMAKE_CXX_STANDARD 14)
include_directories(${ezusb_SOURCE_DIR})
link_directories(${PROJECT_SOURCE_DIR}/include)
link_directories(${PROJECT_SOURCE_DIR}/lib)
add_library(ezusb SHARED ezusb.cpp ezusb.h)
target_link_libraries(ezusb hid)
SET_TARGET_PROPERTIES(ezusb PROPERTIES PREFIX "")
+273
View File
@@ -0,0 +1,273 @@
/* TO BE COMPILED WITH VISUAL STUDIO SO THAT EXPORTS ARE PROPERLY MANGLED */
#define _CRT_SECURE_NO_WARNINGS
#include <Windows.h>
extern "C" {
// This file is in the Windows DDK available from Microsoft.
#include <hidsdi.h>
}
#include <cstdint>
#include <fstream>
#include "ezusb.h"
HANDLE g_hid_handle;
/**
* Initialize the g_hid_handle global variable
* It will attempt to read paths from devicepath.dat file if it exists until it finds one which works.
* If nothing is found, as a last resort it will try the two default paths from the Due and Leonardo
* versions of the firmware.
*
* @return 0 on success, -1 on error
*/
static int controller_init(){
FILE *file;
char path[256];
file = fopen("devicepath.dat", "r");
if (file == NULL)
{
goto last_resort;
}
while ( fgets(path,256,file) != NULL )
{
path[strcspn(path, "\r\n")] = 0;
g_hid_handle = CreateFile(path, GENERIC_READ|GENERIC_WRITE, FILE_SHARE_READ|FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL);
if ( g_hid_handle != INVALID_HANDLE_VALUE )
break;
}
fclose(file);
if ( g_hid_handle == INVALID_HANDLE_VALUE )
{
goto last_resort;
}
return 0;
last_resort:
g_hid_handle = CreateFile("\\\\?\\HID#VID_2341&PID_003E&MI_02#7&3156e204&0&0000#{4d1e55b2-f16f-11cf-88cb-001111000030}",
GENERIC_READ|GENERIC_WRITE, FILE_SHARE_READ|FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL);
if ( g_hid_handle != INVALID_HANDLE_VALUE )
return 0;
g_hid_handle = CreateFile("\\\\?\\HID#VID_2341&PID_8036&MI_02#7&63200bf&0&0000#{4d1e55b2-f16f-11cf-88cb-001111000030}",
GENERIC_READ|GENERIC_WRITE, FILE_SHARE_READ|FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL);
if ( g_hid_handle != INVALID_HANDLE_VALUE )
return 0;
printf("Couldn't open device. Make sure devicepath.dat exists and contains the correct path.\r\n");
return -1;
}
/**
* Read buttons state from the Arduino and convert them into Pop'n Music format
*
* Pop'n Music processes button states as a uint32_t bitfield with the following format :
* bits 0 (LSB) to 5 : nothing
* bit 6 : service
* bit 7 : test
* bits 8 to 16 : buttons 1 to 9
* bit 22 : coin mech
*
* @param pad_bits[out] button state bitfield
* @return 0 on success, -1 on error
*/
static int controller_read_buttons(uint32_t *pad_bits){
DWORD BytesRead = 0;
byte res[4];
unsigned char buf[6]; // gamepad report length is 3 bytes in Arduino firmware, doubled because NumInputBuffer is set to 2
buf[0] = 0x04; // gamepad report ID is 4 in Arduino firmware
*pad_bits = 0;
ReadFile(g_hid_handle, buf, 6, &BytesRead, NULL);
// BytesRead should either be 6 (if it successfully read 2 reports) or 3 (only one)
if ( BytesRead != 6 && BytesRead != 3 )
{
return -1;
}
/* HID read ok, convert latest report bytes to pop'n bitfield */
res[3] = 0;
res[2] = (buf[BytesRead-1]<<3 | buf[BytesRead-1]) & 0x41;
res[1] = buf[BytesRead-2];
res[0] = 0;
buf[BytesRead-1] >>= 1;
if ( buf[BytesRead-1]&1 ) res[0] |= 0x80;
buf[BytesRead-1] >>= 1;
if ( buf[BytesRead-1]&1 ) res[0] |= 0x40;
*pad_bits = *(uint32_t *)res;
#ifdef DEBUG
for (int i = 0; i<32; i++){
printf("%c",( ((uint32_t )*pad_bits>>(31-i))&1)?'1':'0');
if (((i+1)%4)==0) printf(" ");
}
printf("\n");
#endif
return 0;
}
/**
* Read lights state and send the corresponding HID report to the Arduino
*
* Pop'n Music sends lights state as a int32_t bitfield with the following format :
* bits 0 (LSB) to 4 : neons from top to bottom
* bits 8 to 11 : side lamps (left blue, left red, right blue, right red)
* bits 23 to 31 : button lamps from left to right
*
* @param lamp_bits[in] lamp state bitfield
* @return 0 on success, -1 on error
*/
static int controller_write_leds(int32_t lamp_bits){
DWORD BytesWritten = 0;
byte *src = (byte*) &lamp_bits; //cast as byte array for fast conversion
byte buf[5]; // HID lights report length is 5 bytes in Arduino firmware
/* convert bitfield into HID Report format */
buf[4] = 0;
buf[3] = (src[1] >> 2);
buf[2] = (src[1] << 6) | (src[0] << 1) | (src[3] >> 7);
buf[1] = (src[3] << 1) | (src[2] >> 7);
buf[0] = 0x05; // HID lights report id is 5 in Arduino firmware
/* send HID Report */
WriteFile(g_hid_handle, buf, 5, &BytesWritten, NULL);
if ( BytesWritten == 5 )
return 0;
#ifdef DEBUG
printf("Error sending HID report (%u bytes written)\r\n",BytesWritten);
#endif
return -1;
}
/* EZUSB EXPORTS */
#if POPN15_FORMAT == 1
extern "C" {
#endif
__declspec(dllexport) int __cdecl usbCheckAlive() {
return 1;
}
__declspec(dllexport) int __cdecl usbCheckSecurityNew(int i) {
return 0;
}
__declspec(dllexport) int __cdecl usbCoinGet(int i) {
return 0;
}
__declspec(dllexport) int __cdecl usbCoinMode(int i) {
return 0;
}
__declspec(dllexport) int __cdecl usbEnd() {
CloseHandle(g_hid_handle);
return 0;
}
__declspec(dllexport) int __cdecl usbFirmResult() {
return 0;
}
__declspec(dllexport) int __cdecl usbGetKEYID(unsigned char *a, int i) {
return 0;
}
__declspec(dllexport) int __cdecl usbGetSecurity(int i, unsigned char *a) {
return 0;
}
__declspec(dllexport) int __cdecl usbLamp(int32_t lamp_bits) {
return controller_write_leds(lamp_bits);
}
__declspec(dllexport) int __cdecl usbPadRead(unsigned long *pad_bits) {
return controller_read_buttons((uint32_t *)pad_bits);
}
__declspec(dllexport) int __cdecl usbPadReadLast(uint8_t *a1) {
memset(a1, 0, 4);
return 0;
}
__declspec(dllexport) int __cdecl usbSecurityInit() {
return 0;
}
__declspec(dllexport) int __cdecl usbSecurityInitDone() {
return 0;
}
__declspec(dllexport) int __cdecl usbSecuritySearch() {
return 0;
}
__declspec(dllexport) int __cdecl usbSecuritySearchDone() {
return 0;
}
__declspec(dllexport) int __cdecl usbSecuritySelect(int i) {
return 0;
}
__declspec(dllexport) int __cdecl usbSecuritySelectDone() {
return 0;
}
__declspec(dllexport) int __cdecl usbSetExtIo(int i) {
return 0;
}
__declspec(dllexport) int __cdecl usbStart(int i) {
if (controller_init() == -1)
{
printf("Could not init device.\n");
return -1;
}
bool hidres = HidD_SetNumInputBuffers(
g_hid_handle,
2);
if (!hidres)
{
printf("error %d setnuminputbuff\n",GetLastError());
return -1;
}
/* light everything for 1 sec to get visual confirmation
* the device is working */
controller_write_leds((uint32_t) 0xFFFFFFFF);
Sleep(1000);
controller_write_leds((uint32_t) 0x00000000);
return 0;
}
__declspec(dllexport) int __cdecl usbWdtReset() {
return 0;
}
__declspec(dllexport) int __cdecl usbWdtStart(int i) {
return 0;
}
__declspec(dllexport) int __cdecl usbWdtStartDone() {
return 0;
}
#if POPN15_FORMAT == 1
}
#endif
int __stdcall DllMain(HINSTANCE hinstDLL, DWORD fdwReason, void *) {
if (fdwReason == 1) {
//https://blogs.msdn.microsoft.com/larryosterman/2004/06/03/little-known-win32-apis-disablethreadlibrarycalls/
DisableThreadLibraryCalls(hinstDLL);
}
return 1;
}
+37
View File
@@ -0,0 +1,37 @@
#ifndef EZUSB_UPC_EZUSB_H
#define EZUSB_UPC_EZUSB_H
/* Pop'n Music Adventure ezusb.dll was compiled in C and thus used unmangled function names
* set this POPN15_FORMAT to 1 to compile a compatible version */
#define POPN15_FORMAT 0
#if POPN15_FORMAT == 1
extern "C" {
#endif
__declspec(dllexport) int __cdecl usbCheckAlive();
__declspec(dllexport) int __cdecl usbCheckSecurityNew(int i);
__declspec(dllexport) int __cdecl usbCoinGet(int i);
__declspec(dllexport) int __cdecl usbCoinMode(int i);
__declspec(dllexport) int __cdecl usbEnd();
__declspec(dllexport) int __cdecl usbFirmResult();
__declspec(dllexport) int __cdecl usbGetKEYID(unsigned char *a, int i);
__declspec(dllexport) int __cdecl usbGetSecurity(int i, unsigned char *a);
__declspec(dllexport) int __cdecl usbLamp(int32_t lamp_bits);
__declspec(dllexport) int __cdecl usbPadRead(unsigned long *pad_bits);
__declspec(dllexport) int __cdecl usbPadReadLast(uint8_t *a1);
__declspec(dllexport) int __cdecl usbSecurityInit();
__declspec(dllexport) int __cdecl usbSecurityInitDone();
__declspec(dllexport) int __cdecl usbSecuritySearch();
__declspec(dllexport) int __cdecl usbSecuritySearchDone();
__declspec(dllexport) int __cdecl usbSecuritySelect(int i);
__declspec(dllexport) int __cdecl usbSecuritySelectDone();
__declspec(dllexport) int __cdecl usbSetExtIo(int i);
__declspec(dllexport) int __cdecl usbStart(int i);
__declspec(dllexport) int __cdecl usbWdtReset();
__declspec(dllexport) int __cdecl usbWdtStart(int i);
__declspec(dllexport) int __cdecl usbWdtStartDone();
#if POPN15_FORMAT == 1
}
#endif
#endif //EZUSB_UPC_EZUSB_H
Binary file not shown.