New Monitor
Two week ago I decided to finally get a new gaming monitor to replace the one I currently use.
Since I have the XBox 360 Elite, I decided to go with HD, and it eventually came down to two choices.
- 28" HannsG HG281DJ
- 26" Iiyama E2607WS-B1
The HannsG was £40 cheaper, but 2" bigger, which made me suspicious about the quality of the image, but had a good review that also told me about Misco
But in the end I decided to go with it since Scan was out of stock on the Iiyama. The first order was screwed up, so I had to wait a few days for it to be sorted.
It finally arrived today, and I use it as my monitor at work for the rest of the day :D. Its amazing.
XBox 360 Elite Repair
On the 11th of this month, my XBox360 finally stopped being able to work anymore. After checking the receipt I noticed it was still under warranty, so I went onto the XBox website and found the repair center, and initialised a repair.
A couple of hours later, they had already sent me a prepaid UPS label that I had to print out, and all I needed to do was arrange a pickup. So I arrange for a collection on the 15th (They don't allow you to return it in the same box you bought it in, otherwise they don't return it), and by the 16th it had already arrived in Frankfurt.
And then on the 17th, it was already on its way back to me. I reckon they've probably just replaced the console rather than replace the disc drive.
I'm glad I didn't spend the extra money for the Game insurance, I'd rather pay Microsoft for an extended warrenty for the fast job they've done. Too bad I'm in work on Monday morning so I won't be able to collect til Tuesday / Wednesday.
Athena: XBox 360 Pad and Mac OS X (cont further)
The past couple of week's I've really gotten into writing code that communicates with gaming devices (the Mac is limited in this part of itself).
One annoyance I had was with getting rumbling working on the XBox 360 pad, eventually after essentially pinging all messages, I found and worked out the format for the rumble message, which was annoyingly nothing like the others listed.
Well after that I got kinda bored with the 360 and decided to turn towards another console controller, the Wiimote. And I thought I'd write my own driver.
Long story short, when I was having trouble getting my driver working, I looked at the code for the driver for Tattiebogle's XBox 360 pad driver, only to find out that it has redefined the rumble message to it's own format, namely the 4 byte one, only to forward on the settings to the 360 pad.
Well I'm glad I figured out why the message was different, kinda annoying that wasn't in his FAQ. Hopefully others will find this post and go "ohhhhhhhh" like I did when seeing the code.
As for the Wiimote, writing a driver for a Bluetooth device I've decided doesn't make sense, the device is already designed to communicate with another bluetooth device, so I made a class instead, using a Bluetooth API and documentation I found on WiiBrew.
I'll hopefully post my Xcode project for testing the 360 pad in a few days. And I'll probably write up my Wiimote stuff once I get it working the way I want. I'm really tempted to buy lots of cheap USB and Bluetooth devices on eBay now just to see if I can write drivers for them.
Athena: XBox 360 Pad and Mac OS X (cont)
In my previous entry, Athena: XBox 360 Pad and Mac OS X, I began work on getting the XBox 360 pad working in my game engine. I also said that I'd post how to change the LEDs and get rumble working when I figured it out.
I had no trouble with getting the LEDs flashing, but for some reason the rumble actuators just would not respond to my message with success.
I had searched at all other documentation on the pad on the internet, but although I found two variations of rumbling, neither worked.
It has taken my a week or two, but then I got the inspiration to just fire all possible messages to the pad, nullified apart from the header, and see responds with success. So I wrote some code to send 255 different type ids, with 255 different lengths.
//
//
void HIDInput::TestDevice( IOHIDDeviceRef inDeviceRef )
{
unsigned char reportData[255];
// Nullify Report
memset( reportData, 0, sizeof( unsigned char ) * 255 );
// Loop through most possible messages
for( unsigned char reportType = 0; reportType < 255; ++reportType )
{
for( unsigned char reportLength = 0; reportLength < 255; ++reportLength )
{
// Setup Header
reportData[0] = reportType;
reportData[1] = reportLength;
// Send Report to Pad
if( DeviceSendReport( inDeviceRef, reportType, reportData, reportLength ) )
{
printf( "Success - Type(%u), Length(%u)\n", reportType, reportLength );
}
else
{
printf( "Failed - Type(%u), Length(%u)\n", reportType, reportLength );
}
}
}
//
printf( "Finished\n" );
}
I'll explain show the function body for DeviceSendReport in a minute. After running this, only two reports returned with success, one was the LED report, and the other was of Type 0 with length 4.
Since the header take two bytes, I guessed the last two must be Actuator A and Actuator B. And after setting one of them to 0xff, I successfully managed to get one of the actuators rumbling, and then the other actuator with the other byte.
Now for the code for sending reports to the pad.
//
//
bool HIDInput::DeviceSendReport( IOHIDDeviceRef inDeviceRef, const unsigned char inType, const unsigned char * inReportData, const unsigned char inReportSize )
{
return IOHIDDeviceSetReport( inDeviceRef, kIOHIDReportTypeOutput, inType, inReportData, inReportSize ) == kIOReturnSuccess;
}
As you can see it takes a parameter called inDeviceRef, this is the value that was passed to DeviceMatchingCallback in the previous entry. And now here is the code to get the LED flashing, there are a few status values for it, so I've created an enum with some comments explaining what each do.
//
//
enum LEDStatus
{
kLED_AllOff = 0x00, // All LEDs are turned off
kLED_AllBlink = 0x01, // All LEDs repeatedly blink on and off
kLED_P1_FlashOn = 0x02, // Player 1 LED flashes then stays on
kLED_P2_FlashOn = 0x03, // Player 2 LED flashes then stays on
kLED_P3_FlashOn = 0x04, // Player 3 LED flashes then stays on
kLED_P4_FlashOn = 0x05, // Player 4 LED flashes then stays on
kLED_P1_On = 0x06, // Player 1 LED turns on
kLED_P2_On = 0x07, // Player 2 LED turns on
kLED_P3_On = 0x08, // Player 3 LED turns on
kLED_P4_On = 0x09, // Player 4 LED turns on
kLED_Rotating = 0x0a, // LEDs flash on in sequence
kLED_CurrentBlink = 0x0b, // Currently active LED blinks
kLED_SlowBlink = 0x0c, // All LEDs blink slowly
kLED_Alternating = 0x0d, // Player 1 & 4 alternate blink with Player 2 & 3
};
//
//
void HIDInput::FlashLED( IOHIDDeviceRef inDeviceRef, const LEDStatus inStatus )
{
//
const unsigned char kReportType = 0x01;
const unsigned char kReportSize = 0x03;
const unsigned char kReportData[kReportSize] =
{
kReportType, kReportSize, static_cast<Core::byte>( inStatus )
};
//
DeviceSendReport( inDeviceRef, kReportType, kReportData, kReportSize );
}
As you can see, it is fairly simple to send a message, all you have to remember is that the first two bytes contain a header (one byte for type, and one type for the size of the report including the header size). And finally for rumbling the motors.
//
//
void HIDInput::SetRumble( IOHIDDeviceRef inDeviceRef, const unsigned char inBigSpeed, const unsigned char inLittleSpeed )
{
//
const unsigned char kReportType = 0x00;
const unsigned char kReportSize = 0x04;
const unsigned char kReportData[kReportSize] =
{
kReportType, kReportSize, inBigSpeed, inLittleSpeed
};
//
DeviceSendReport( inDeviceRef, kReportType, kReportData, kReportSize );
}
I had some minor problems with the rumble not changing after the first call to SetRumble originally, however it turned out that I had just accidently set the report data variable to be static, so the initialisation was only setup with the first values passed.
Other Rumble Reports
As I mentioned before, I've found other message formats for rumbling the motors, I don't know if its a version thing, or something, but here are the other two I've found if you have trouble with mine.
Version 1 (Tattiebogle)
//
//
void HIDInput::SetRumble( IOHIDDeviceRef inDeviceRef, const unsigned char inBigSpeed, const unsigned char inLittleSpeed )
{
//
const unsigned char kReportType = 0x00;
const unsigned char kReportSize = 0x08;
const unsigned char kReportData[kReportSize] =
{
kReportType, kReportSize, 0x00, inBigSpeed, inLittleSpeed, 0x00, 0x00, 0x00
};
//
DeviceSendReport( inDeviceRef, kReportType, kReportData, kReportSize );
}
Version 2 (http://euc.jp/periphs/xbox-controller.ja.html)
//
//
void HIDInput::SetRumble( IOHIDDeviceRef inDeviceRef, const unsigned char inBigSpeed, const unsigned char inLittleSpeed )
{
//
const unsigned char kReportType = 0x00;
const unsigned char kReportSize = 0x06;
const unsigned char kReportData[kReportSize] =
{
kReportType, kReportSize, 0x00, inBigSpeed, 0x00, inLittleSpeed
};
//
DeviceSendReport( inDeviceRef, kReportType, kReportData, kReportSize );
}
Athena: XBox 360 Pad and Mac OS X
Tonight I began the process of writing some using the HID APIs to be able to use an XBox 360 controller on my MacBook Pro for the Athena engine.
As usual, my Courage game played host to testing this new method of control.
First of all, the XBox 360 pad uses a 0xff device class (Vendor specific), so normal HID drivers (they use a device class of 0x03) will not attach to it, so I needed to download a driver for MacOSX that could handle this. Also I didn't have a Microsoft Wireless Gaming Receiver so I bought a wired pad instead.
I tried the Tattiebogle driver, and at first it worked, but after I logged into VMWare Fusion and used by pad in Vista it stopped working on MacOSX. Or it could have been that I got the Wacom Bamboo pad installed recently.
So after a bit of searching I found another driver, which although didn't work, sparked Tattiebogle's to life. This one is known as XBox HID Driver for Mac OS X.
Once it was all up and running, getting code to listen for the 360 pad was a breeze, and Apple has a nice section on your website for it Technical Note TN2187: New HID Manager APIs for Mac OS X version 10. I'll summarise the basics needed to connect to the pad.
None of this code cleans up properly, like I said I only just started with the HID manager, so I'll probably post an example App at some point that does clean up properly when shutting down.
Setting up HID Manager
The following code is quite straight forward, it just sets up two callbacks, one for when a device matches what we are looking for, and one for when a device is removed.
The IOKit.framework is required.
I also have two global variables, one for the HID manager, and another the device we going to be reading (this code can only do one device since we only have one buffer variable).
//
//
#include <IOKit/hid/IOHIDLib.h>
#include <IOKit/hid/IOHIDUsageTables.h>
//
//
namespace HIDInput
{
//
void DeviceMatchingCallback( void * inContext, IOReturn inResult, void * inSender, IOHIDDeviceRef inIOHIDDeviceRef );
void DeviceRemovalCallback( void * inContext, IOReturn inResult, void * inSender, IOHIDDeviceRef inIOHIDDeviceRef );
void DeviceIOHIDReportCallback( void * inContext, IOReturn inResult, void * inSender, IOHIDReportType inType, uint32_t inReportID, uint8_t * inReport, CFIndex inReportLength );
Boolean Device_GetLongProperty( IOHIDDeviceRef inDeviceRef, CFStringRef inKey, long * outValue );
long Device_GetVendorID( IOHIDDeviceRef inIOHIDDeviceRef );
long Device_GetProductID( IOHIDDeviceRef inIOHIDDeviceRef );
//
IOHIDManagerRef l_IOHIDManagerRef;
uint8_t * l_pReportBuf = 0;
}
//
//
int main( int argc, char * argv[] )
{
// Create a HID manager with a default allocator
l_IOHIDManagerRef = IOHIDManagerCreate( kCFAllocatorDefault, kIOHIDOptionsTypeNone );
assert( IOHIDManagerRef );
// Look for all device and open the manager
IOHIDManagerSetDeviceMatching( l_IOHIDManagerRef, NULL );
IOHIDManagerOpen( l_IOHIDManagerRef, kIOHIDOptionsTypeNone );
// Register Callbacks to be run with application loop
IOHIDManagerRegisterDeviceMatchingCallback( l_IOHIDManagerRef, HIDInput::DeviceMatchingCallback, NULL );
IOHIDManagerRegisterDeviceRemovalCallback( l_IOHIDManagerRef, HIDInput::DeviceRemovalCallback, NULL );
IOHIDManagerScheduleWithRunLoop( l_IOHIDManagerRef, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode );
//
while( true )
{
// loop application
}
return 0;
}
The call to IOHIDManagerSetDeviceMatching() can have a dictionary passed to it to look only for devices matching certain criteria, but for simplicity, I'll just be checking all devices in this example, so I passed NULL as a second argument.
Checking for XBox360 Pad
With the code setup for calling back when a device is found that matches (or all devices in our case), we now need to check the device vendor id and product id to be sure they are what we are looking for.
//
//
Boolean HIDInput::Device_GetLongProperty( IOHIDDeviceRef inDeviceRef, CFStringRef inKey, long * outValue )
{
CFTypeRef tCFTypeRef = IOHIDDeviceGetProperty( inDeviceRef, inKey );
Boolean result = FALSE;
if( tCFTypeRef )
{
// If this is a number get its value
if( CFNumberGetTypeID( ) == CFGetTypeID( tCFTypeRef ) )
{
result = CFNumberGetValue( ( CFNumberRef ) tCFTypeRef, kCFNumberSInt32Type, outValue );
}
}
return result;
}
//
//
long HIDInput::Device_GetVendorID( IOHIDDeviceRef inIOHIDDeviceRef )
{
long result = 0;
Device_GetLongProperty( inIOHIDDeviceRef, CFSTR( kIOHIDVendorIDKey ), &result );
return result;
}
//
//
long HIDInput::Device_GetProductID( IOHIDDeviceRef inIOHIDDeviceRef )
{
long result = 0;
Device_GetLongProperty( inIOHIDDeviceRef, CFSTR( kIOHIDProductIDKey ), &result );
return result;
}
//
//
void HIDInput::DeviceMatchingCallback( void * inContext, IOReturn inResult, void * inSender, IOHIDDeviceRef inIOHIDDeviceRef )
{
if( !inIOHIDDeviceRef )
{
return;
}
const long kVendor_Microsoft = 0x045e;
const long kProduct_XBox360Pad = 0x028e;
const long kVendorID = Device_GetVendorID( inIOHIDDeviceRef );
const long kProductID = Device_GetProductID( inIOHIDDeviceRef );
if( kVendorID == kVendor_Microsoft && kProductID == kProduct_XBox360Pad )
{
// Assert if we're trying to create a second XBox 360 pad device
assert( l_pReportBuf );
// Get the size of the report for the device and allocate a buffer. The
// report size property should be right, however the packet header
// seems to want 20 bytes, so I'm allocating 20 bytes to be safe.
CFIndex reportSize = 20;
//IOHIDDevice_GetLongProperty( inIOHIDDeviceRef, CFSTR( kIOHIDMaxInputReportSizeKey ), &reportSize );
l_pReportBuf = static_cast<uint8_t *>( malloc( reportSize ) );
// Register a report callback for this device
IOHIDDeviceRegisterInputReportCallback( inIOHIDDeviceRef, l_pReportBuf, reportSize, DeviceIOHIDReportCallback, 0 );
printf( "XBox Pad Connected.\n" );
}
}
//
//
void HIDInput::DeviceRemovalCallback( void * inContext, IOReturn inResult, void * inSender, IOHIDDeviceRef inIOHIDDeviceRef )
{
// Deallocate report buffer
free( l_pReportBuf );
l_pReportBuf = 0;
//
printf( "XBox Pad Disconnected.\n" );
}
We have two constants in the device matching callback, one that is the vendor id for Microsoft, and the other which is the product id for the XBox 360 pad.
Since this code only supports the one pad, I've added an assert which will be triggered if a second pad is attempted to be matched that is a 360 pad (it checks to see if the report buffer has already been allocated).
Once a device has been removed, I free the memory for the report buffer. I think I'm suppose to unregister the input report callback by passing null as the callback, but I'm not 100% sure yet.
All the other helper functions were found on the Apple website technical note.
Reading Pad Status
XBox 360 pad updates come through on a report type of 0x00 and are 14 bytes long (though I have read on other sites of people getting reports of 20 bytes, this just seems to be padding).
I have skipped the checking of the report type in this code, but it should probably be checked, as well as the report length.
//
//
void HIDInput::DeviceIOHIDReportCallback( void * inContext, IOReturn inResult, void * inSender, IOHIDReportType inType, uint32_t inReportID, uint8_t * inReport, CFIndex inReportLength )
{
// Output hex for each byte in the report (always 2 characters long, making up the rest with 0)
for( int i = 0; inReportLength; ++i )
{
printf( "%02x", inReport[i] );
}
//
printf( "\n" );
}
That sourcecode allows you to read just the actual reports being sent from the pad, however if you want to be able to use it as a 360 pad. You'll get a message something like this...
0014000000002efcc70792ff9a06
The 0014 at the beginning is a packet header, first byte being the command, second being the size of the packet. The other sections are split up as follows...
0014aaaabbccddddeeeeffffgggg
a is an unsigned short of button flags
b is an unsigned char representing the left trigger (LT)
c is an unsigned char representing the right trigger (RT)
d is a signed short representing the Left Axis X
e is a signed short representing the Left Axis Y
f is a signed short representing the Right Axis X
g is a signed short representing the Right Axis Y
This can be put into a function like so...
//
//
void HIDInput::DeviceIOHIDReportCallback( void * inContext, IOReturn inResult, void * inSender, IOHIDReportType inType, uint32_t inReportID, uint8_t * inReport, CFIndex inReportLength )
{
unsigned char leftTrigger, rightTrigger;
short rightAxisX, rightAxisY;
short leftAxisX, leftAxisY;
unsigned short buttonFlags;
// Copy bytes to their respective variables (incase they are not aligned correctly)
memcpy( &buttonFlags, &inReport[2], sizeof( unsigned short ) );
memcpy( &leftTrigger, &inReport[4], sizeof( unsigned char ) );
memcpy( &rightTrigger, &inReport[5], sizeof( unsigned char ) );
memcpy( &leftAxisX, &inReport[6], sizeof( short ) );
memcpy( &leftAxisY, &inReport[8], sizeof( short ) );
memcpy( &rightAxisX, &inReport[10], sizeof( short ) );
memcpy( &rightAxisY, &inReport[12], sizeof( short ) );
// Output pad stats
printf( "Buttons %u\n", buttonFlags );
printf( "LTrigger: %u\n", leftTrigger );
printf( "RTrigger: %u\n", rightTrigger );
printf( "LAxis: %d, %d\n", leftAxisX, leftAxisY );
printf( "RAxis: %d, %d\n", rightAxisX, rightAxisY );
printf("\n");
}
I don't know what the 2048 button flag is, it is possibly the synchronization button that is on the wireless controller. So here is a list of all the other button flags and their bit number.
//
//
enum XBox360_ButtonFlags
{
XBOX360PAD_DPAD_UP = 1,
XBOX360PAD_DPAD_DOWN = 2,
XBOX360PAD_DPAD_LEFT = 4,
XBOX360PAD_DPAD_RIGHT = 8,
XBOX360PAD_START = 16,
XBOX360PAD_BACK = 32,
XBOX360PAD_LAXIS = 64,
XBOX360PAD_RAXIS = 128,
XBOX360PAD_LB = 256,
XBOX360PAD_RB = 512,
XBOX360PAD_XBOX = 1024,
XBOX360PAD_A = 4096,
XBOX360PAD_B = 8192,
XBOX360PAD_X = 16384,
XBOX360PAD_Y = 32768,
};
There is still a lot more to cover, so I'll probably make a follow up entry tomorrow or the day after for sending messages to the pad to make it vibrate, and how to clean up after you're finished properly. As well as post an example application.
I also found a few new variables to add to my list of debugging preprocessor. I'll explain on the next page since this one is getting rather long.
Prince of Persia
After preordering this game the moment I heard about it, I went out on the Friday afternoon after getting back from lunch only to remember that this was being released today, and so quickly went to pick it up. Finally getting back home, I quickly made dinner and then began to play.
The game started quite quickly, with no back story, your the prince in search of Farah, at which point I thought it was possibly a sequel to the previous trilogy, only to find out to my amusement that she was apparently my donkey carrying loads of gold.
One thing that is noticeable straight away, is the amount of doing nothing actually gets you achievements, every time there is a cut scene, or I jump, talk to someone, defeat a generic enemy.. you get the idea. This is probably the only game on the 360 that I've completed and nearly got all the achievements bar 2 (the speed run and the "do not die more than 100 times"). There is even an achievement to do nothing for one minute at the end.
Still the game is beautifully done, the cel-shading is amazingly done, and quite possibly the best I've seen in games since Tales of Symphonia. But this is let down by the lack of RPG elements, the dialog for a start whilst giving more information on why the enemies were there was bloody annoying, talking to Elika five times just to get everything, this may not seem that bad, however each conversation lasts between 5 seconds to 3 minutes, but with no way to skip it. And it appears usually at the most unintuitive time, usually just as I'm about to jump off a ledge, by which point when I get to solid ground, she doesn't want to talk anymore.
As for combat, there is a lot of combos, most of which can be strung together to make insanely large attacks, but unless you do them at the beginning of the game, there is very little chance you'll be able to perform them at the end game enemies to get the combo achievement since every attack you do they decide to block. As with the Sands of Time, there is a mechanism to stop you from dying, only in this game, it is impossible to die. Normally this would annoy me, but considering the annoyance and stress caused by just trying to get to the fertile land, with many accidental jumps when wall running, I was glad for it, and eventually if you suck enough, the enemies stop regaining health to the point unless they absorb the corruption, they will not regenerate at all.
One annoying aspect of the game is the Simon Says moments, when an enemy pushes you or you push an enemy to the edge of the screen or take enough damage and fall to the ground, you are expected to push a button in the time it takes to blink, so if you actually do blink whilst this is happening, you can be sure that the boss you just spent 10 minutes forcing its health down to 10%, will be back up to 90% again after Elika helps you (making you regret her saving you).
Finally the platform elements, I don't know what Ubisoft have done, but they better undo, the game feels like a one button game, to run along a wall you need only jump into it, but somewhere deep down I want to press A (possibly Mirror's Edge), however when I accidently do now I'm jumping off the way into a pit, if I want to get the fertile grounds later in the game, I need to jump onto Glyphs which give powers to Elika and allow me to reach other places, but only after I press Y 50 odd times to activate it. Most of the time there seems no point to these long winded transportation sequences, my only involvement is the dodging of a wall when flying, or tapping Y furiously to prevent falling off a glyph when landing. And that is another thing, where the hell are all the enemies. Whenever I enter a corrupted zone there is one enemy protected it, and if I'm quick enough I can slash him before he spawns and proceed to the platform the fertile land is one to take out the regions boss (again).
The I usually associate the Prince of Persia series as a platformer with combat and RPG elements, and lots of puzzles that take me a few hours to figure out. This one, whilst giving me freedom in where I want to go, has as far as I remember, two puzzles, one with turning levers to get objects into position and ... no wait that is it. The platforms can be confusing but when I press Y Elika kindly gives a glowing orb not only showing me where to go, but the actual path I need to follow, jumping onto walls and running across them, playing the game felt more like watching a long movie since my involvement just seemed to be guiding the prince in the direction he is meant to go, he might as well have been moving forward constantly and me just turning him, and I could complete the game.
Still the concept is nice, hopefully they'll add more gameplay and story into the sequel as I've heard this is going to be a trilogy as well (and judging by the ending, it better be).
Finally there are 1001 light seeds in the game that about 500 are needed to be able to complete the game, however finding all 1001 of them is a long painful task, although there is meter telling you how many is a zone you have out of how many, the limits of the zone are exactly clear, and trying to find one down below a floating platform because you like to keep your camera facing forward makes you run through each level again only this time falling off the world so you can get a glimpse of any that are below thing. This is the reason I didn't get the speed run when I set off to the all 1001, I eventually did the painful annoying task, but I keep asking myself, was it worth it, short answer, no.
Still it was quite enjoying when it came to freeing a zone, albeit easy, and I did pay attention to everything Elika said (getting another two achievements in the process), which gave me lots of information about the land, so much so I could live there and pass as one of them. And unlike a colleague of mine, I enjoyed the princes humor.
And there is one other bonus I read about recently, apart from the sequels to this game, and that is the Prince of Persia: Sands of Time film that will be coming out in 2010. Although I'm sure they will destroy it in every way, it they seemed to have cast the prince quite well.






