What's new

Rumblepak support for Blight's input plugin!

Jorix

New member
I made a patch that adds rumblepak support to Blight's input plugin!

Last sunday I found/created a driver for the rumble function of my controller, but the only Linux game I know that uses it is bzflag. So I decided to add support for it in mupen64 (many games at once ;) ). As I didn't find any documentation on how the plugins should work I decided to peek in the source code of the N-rage plugin to see how it should work and the fftest utility for an example of force feedback in Linux.

This patch is for version 0.0.10 of blight's input plugin as found in the mupen64 0.5 source code.

I added two extra configureable buttons to switch to either mempak or rumble pak, but it seems it doesn't work directly in game unless I make completely custom mempak handling, which is way too much work. So instead after you press the switch button, make a savestate and restart the rom to activate the changes.

The code is licensed under the GPL v2 of course just like the rest of blights input plugin.

Code:
diff blight_input_orig/configdialog_sdl.c blight_input/configdialog_sdl.c
55a56,57
> 	"Mempak switch",
> 	"Rumblepak switch",
105c107
< 	{ checkbutton_clicked, 1, 120, 45, 90, 25 },		// mempak
---
> 	{ checkbutton_clicked, 1, 120, 45, 100, 25 },		// plugin
122a125,126
> 	{ pad_button_clicked, MEMPAK,       110, 350, 150, 20 },
> 	{ pad_button_clicked, RUMBLEPAK,    110, 375, 150, 20 },
206c210
< 			memcpy( orig_cont[i].button, config[i].button, sizeof( SButtonMap ) * 14 );
---
> 			memcpy( orig_cont[i].button, config[i].button, sizeof( SButtonMap ) * 16 );
232c236
< 		if( config[cont].control.Plugin == PLUGIN_MEMPAK )
---
> 		if( config[cont].control.Plugin == PLUGIN_RAW )
234c238
< 		else
---
> 		else if( config[cont].control.Plugin == PLUGIN_NONE )
235a240,241
> 		else
> 			config[cont].control.Plugin = PLUGIN_RAW;
715a722,723
> 		write_text( screen, 110, 350, black, gray, button_names[MEMPAK] );
> 		write_text( screen, 110, 375, black, gray, button_names[RUMBLEPAK] );
727c735
< 		dstrect.x = 120; dstrect.y = 45; dstrect.w = 90; dstrect.h = 25;
---
> 		dstrect.x = 120; dstrect.y = 45; dstrect.w = 100; dstrect.h = 25;
730,731c738,744
< 		SDL_FillRect( screen, &dstrect, (config[cont].control.Plugin == PLUGIN_MEMPAK) ? u32gray : u32dark_gray );
< 		write_text( screen, dstrect.x + 12, dstrect.y, black, (config[cont].control.Plugin == PLUGIN_MEMPAK) ? gray : dark_gray, "Mem Pak" );
---
> 		SDL_FillRect( screen, &dstrect, (config[cont].control.Plugin != PLUGIN_NONE) ? u32gray : u32dark_gray );
> 		if (config[cont].control.Plugin == PLUGIN_NONE)
> 			write_text( screen, dstrect.x + 12, dstrect.y, black, dark_gray, "None" );
> 		if (config[cont].control.Plugin == PLUGIN_MEMPAK)
> 			write_text( screen, dstrect.x + 12, dstrect.y, black, gray, "Mem Pak" );
> 		if (config[cont].control.Plugin == PLUGIN_RAW)
> 			write_text( screen, dstrect.x + 12, dstrect.y, black, gray, "Rumble Pak" );
963c976
< 		memcpy( config[i].button, orig_cont[i].button, sizeof( SButtonMap ) * 14 );
---
> 		memcpy( config[i].button, orig_cont[i].button, sizeof( SButtonMap ) * 16 );
Common subdirectories: blight_input_orig/font and blight_input/font
diff blight_input_orig/plugin.c blight_input/plugin.c
17a18,24
> #include <sys/types.h>
> #include <sys/stat.h>
> #include <fcntl.h>
> #include <unistd.h>
> #include <dirent.h>
> #include <linux/input.h>
> 
52c59,61
< 	0x2000	 // L_TRIG
---
> 	0x2000,	 // L_TRIG
> 	0x4000,	 // Mempak switch
> 	0x8000	 // Rumblepak switch
72a82,83
> 	"Mempak switch",
> 	"Rumblepak switch",
159c170
< 	int cont, plugged, mempak, mouse, i, b, dev;
---
> 	int cont, plugged, plugin, mouse, i, b, dev;
172c183
< 		for( b = 0; b < 14; b++ )
---
> 		for( b = 0; b < 16; b++ )
212c223
< 		if( sscanf( line, "mempak=%d", &mempak ) == 1 )
---
> 		if( sscanf( line, "plugin=%d", &plugin ) == 1 )
214,215c225
< 			if( mempak )
< 				controller[cont].control.Plugin = PLUGIN_MEMPAK;
---
> 			controller[cont].control.Plugin = plugin;
262c272
< 				printf( "%s, %d: num = %d, key_a = %s, key_b = %s, button_a = %s, button_b = %s, axis = %s, hat = %s, hat_pos_a = %s, hat_pos_b = %s\n", __FILE__, __LINE__, num,
---
> 				printf( "%s, %d: num = %d, key_a = %s, key_b = %s, button_a = %s, button_b = %s, axis = %s, hat = %s, hat_pos_a = %s, hat_pos_b = %s\n", button, b, num,
292c302
< 				printf( "%s, %d: num = %d, key = %s, button = %s, axis = %s, hat = %s, hat_pos = %s, mbutton = %s\n", __FILE__, __LINE__, num, key_a, button_a, axis, hat, hat_pos_a, mbutton );
---
> 				printf( "%s, %d: num = %d, key = %s, button = %s, axis = %s, hat = %s, hat_pos = %s, mbutton = %s\n", button, b, num, key_a, button_a, axis, hat, hat_pos_a, mbutton );
354c364
< 		fprintf( f, "mempak=%d\n", (controller[i].control.Plugin == PLUGIN_MEMPAK) ? 1 : 0 ); // ???
---
> 		fprintf( f, "plugin=%d\n", controller[i].control.Plugin );
363c373
< 		for( b = 0; b < 14; b++ )
---
> 		for( b = 0; b < 16; b++ )
427c437
< 			fprintf( f, "%s=key( %s , %s ); button( %s , %s ); axis( %s ); hat( %s , %s , %s )\n", button_names[b+14],
---
> 			fprintf( f, "%s=key( %s , %s ); button( %s , %s ); axis( %s ); hat( %s , %s , %s )\n", button_names[b+16],
466a477,507
> 
> BYTE lastCommand[6];
> 
> struct ff_effect ffeffect[3];
> struct ff_effect ffstrong[3];
> struct ff_effect ffweak[3];
> 
> BYTE DataCRC( BYTE *Data, int iLenght )
> {
> 	register BYTE Remainder = Data[0];
> 
> 	int iByte = 1;
> 	BYTE bBit = 0;
> 
> 	while( iByte <= iLenght )
> 	{
> 		BOOL HighBit = ((Remainder & 0x80) != 0);
> 		Remainder = Remainder << 1;
> 
> 		Remainder += ( iByte < iLenght && Data[iByte] & (0x80 >> bBit )) ? 1 : 0;
> 
> 		Remainder ^= (HighBit) ? 0x85 : 0;
> 		
> 		bBit++;
> 		iByte += bBit/8;
> 		bBit %= 8;
> 	}
> 
> 	return Remainder;
> }
> 
470,474c511,591
< #if 0//def _DEBUG
< 	printf( "\nRaw Command (cont=%d):\n", Control );
< 	printf( "\t%02X %02X %02X %02X %02X %02X\n", Command[0], Command[1],
< 	        Command[2], Command[3], Command[4], Command[5]);//, Command[6], Command[7] );
< #endif
---
> 	BYTE *Data = &Command[5];
> 	struct input_event play;
> 
> 	if( Control == -1 )
> 		return;
> 
> 	switch (Command[2]){
> 	case RD_GETSTATUS:
> 	{
> 		/*printf( "Get status\n" );*/
> 		break;
> 	}
> 	case RD_READKEYS:
> 	{
> 		/*printf( "Read keys\n" );*/
> 		break;
> 	}
> 	case RD_READPAK:
> 	{
> 		/*printf( "Read pak\n" );*/
> 		if (controller[Control].control.Plugin == PLUGIN_RAW)
> 		{
> 			DWORD dwAddress = (Command[3] << 8) + (Command[4] & 0xE0);
> 
> 			if(( dwAddress >= 0x8000 ) && ( dwAddress < 0x9000 ) )
> 				memset( Data, 0x80, 32 );
> 			else
> 				memset( Data, 0x00, 32 );
> 
> 			Data[32] = DataCRC( Data, 32 );
> 			break;
> 			}
> 	}
> 	case RD_WRITEPAK:
> 	{
> 		/*printf( "Write pak\n" );*/
> 		if (controller[Control].control.Plugin == PLUGIN_RAW)
> 		{
> 			DWORD dwAddress = (Command[3] << 8) + (Command[4] & 0xE0);
> 			if( dwAddress == PAK_IO_RUMBLE && controller[Control].event_joystick != 0)
> 			{
> 				if( *Data )
> 				{
> 					play.type = EV_FF;
> 					play.code = ffeffect[Control].id;
> 					play.value = 1;
> 
> 					if (write(controller[Control].event_joystick, (const void*) &play, sizeof(play)) == -1)
> 						perror("Error starting rumble effect");
> 				}
> 				else
> 				{
> 					play.type = EV_FF;
> 					play.code = ffeffect[Control].id;
> 					play.value = 0;
> 
> 					if (write(controller[Control].event_joystick, (const void*) &play, sizeof(play)) == -1)
> 						perror("Error stopping rumble effect");
> 				}
> 			}
> 
> 			Data[32] = DataCRC( Data, 32 );
> 
> 			break;
> 		}
> 	}
> 	case RD_RESETCONTROLLER:
> 	{
> 		/*printf( "Reset controller\n" );*/
> 		break;
> 	}
> 	case RD_READEEPROM:
> 	{
> 		/*printf( "Read eeprom\n" );*/
> 		break;
> 	}
> 	case RD_WRITEEPROM:
> 	{
> 		/*printf( "Write eeprom\n" );*/
> 		break;
> 	}}
754a872
> 	struct input_event play;
767c885
< 		for( b = 0; b < 14; b++ )
---
> 		for( b = 0; b < 16; b++ )
830c948
< 		for( b = 0; b < 14; b++ )
---
> 		for( b = 0; b < 16; b++ )
864c982
< 		for( b = 0; b < 14; b++ )
---
> 		for( b = 0; b < 16; b++ )
918a1037,1058
> if (controller[Control].buttons.button & button_bits[14])
> 	{
> 	controller[Control].control.Plugin = PLUGIN_MEMPAK;
> 	play.type = EV_FF;
> 	play.code = ffweak[Control].id;
> 	play.value = 1;
> 
> 	if (write(controller[Control].event_joystick, (const void*) &play, sizeof(play)) == -1)
> 		perror("Error starting rumble effect");
> 	}
> 
> if (controller[Control].buttons.button & button_bits[15])
> 	{
> 	controller[Control].control.Plugin = PLUGIN_RAW;
> 	play.type = EV_FF;
> 	play.code = ffstrong[Control].id;
> 	play.value = 1;
> 
> 	if (write(controller[Control].event_joystick, (const void*) &play, sizeof(play)) == -1)
> 		perror("Error starting rumble effect");
> 	}
> 
937a1078,1081
> 	DIR *dp;
> 	struct dirent *ep;
> 	char temp[64];
> 	char temp2[64];
945a1090
> 	{
947a1093,1148
> 		sprintf(temp,"/sys/class/input/js%d/device",controller[i].device);
> 		dp = opendir ( temp );
> 
> 		if (dp != NULL)
> 		{
> 			while ((ep = readdir (dp)))
> 				if (!strncmp(ep->d_name,"input:event",11))
> 				{
> 					sscanf(ep->d_name,"input:%s",temp2);
> 					sprintf(temp,"/dev/input/%s",temp2);
> 				}
> 			(void) closedir (dp);
> 		}
> 
> 		controller[i].event_joystick = open(temp, O_RDWR);
>         	if (controller[i].event_joystick == -1)
> 		{
> 			controller[i].event_joystick = 0;
>         	}
> 		/*else if (ioctl(controller[i].event_joystick, EVIOCGBIT(EV_FF, sizeof(unsigned long) * 4), features) == -1)
> 		{
> 			perror("Ioctl query");
> 			controller[i].event_joystick = 0;
> 		}*/
> 		else
> 		{
> 			ffeffect[i].type = FF_RUMBLE;
> 			ffeffect[i].id = -1;
> 			ffeffect[i].u.rumble.strong_magnitude = 0xFFFF;
> 			ffeffect[i].u.rumble.weak_magnitude = 0xFFFF;
> 
> 			if (ioctl(controller[i].event_joystick, EVIOCSFF, &ffeffect[i]) == -1)
> 				perror("Upload effect");
> 
> 			ffstrong[i].type = FF_RUMBLE;
> 			ffstrong[i].id = -1;
> 			ffstrong[i].u.rumble.strong_magnitude = 0xFFFF;
> 			ffstrong[i].u.rumble.weak_magnitude = 0x0000;
> 			ffstrong[i].replay.length = 500;
> 			ffstrong[i].replay.delay = 0;
> 
> 			if (ioctl(controller[i].event_joystick, EVIOCSFF, &ffstrong[i]) == -1)
> 				perror("Upload effect");
> 
> 			ffweak[i].type = FF_RUMBLE;
> 			ffweak[i].id = -1;
> 			ffweak[i].u.rumble.strong_magnitude = 0x0000;
> 			ffweak[i].u.rumble.weak_magnitude = 0xFFFF;
> 			ffweak[i].replay.length = 500;
> 			ffweak[i].replay.delay = 0;
> 
> 			if (ioctl(controller[i].event_joystick, EVIOCSFF, &ffweak[i]) == -1)
> 				perror("Upload effect");
> 		}
> 	}
> 
diff blight_input_orig/plugin.h blight_input/plugin.h
32d31
< 
35a35,45
> // Some stuff from n-rage plugin
> #define RD_GETSTATUS		0x00 // get status
> #define RD_READKEYS			0x01 // read button values
> #define RD_READPAK			0x02 // read from controllerpack
> #define RD_WRITEPAK			0x03 // write to controllerpack
> #define RD_RESETCONTROLLER	0xff // reset controller
> #define RD_READEEPROM		0x04 // read eeprom
> #define RD_WRITEEPROM		0x05 // write eeprom
> 
> #define PAK_IO_RUMBLE		0xC000		// the address where rumble-commands are sent to
> 
51a62,63
> 	MEMPAK,
> 	RUMBLEPAK,
80c92
< 	SButtonMap    button[14];	// 14 buttons; in the order of EButton
---
> 	SButtonMap    button[16];	// 14 buttons; in the order of EButton
84a97,98
> 	int           event_joystick;	// the /dev/input/eventX device for force feeback
> 	
diff blight_input_orig/SDL_ttf.c blight_input/SDL_ttf.c
51c51
< #include <freetype/internal/ftobjs.h>
---
> /*#include <freetype/internal/ftobjs.h>*/
278c278
< 	stream->memory = library->memory;
---
> /*	stream->memory = library->memory;*/
 
OP
Jorix

Jorix

New member
And for those who don't know what a compiler is, a binary version.

Put this file in the plugins folder.

Make sure that the file /dev/input/eventX (Where X is the number of the device) is readable and writeable for non root users (by opening a konsole and typing "sudo chmod o+wr /dev/input/eventX" (without quotes and X is the device number))
To see which of these is for your joystick type "cat /dev/input/eventX" (again without quotes and X is the device number you want to try) and move your joystick. The one which will display strange characters when you move the joystick is the one.
 
Last edited:
OP
Jorix

Jorix

New member
Thanks for the thanks ;)
And great that it's going to be included! :bouncy: :mupen64:
 

Top