In preparation to tinker with the miCoach data, I started with some better-travelled exercise bits: WiiFit body test data. Starting with Jansen Price’s excellent blog post on the subject, I slowly worked through the data and wrote a python script to interpret the binaries and save them to a CSV. By using the excellent flot javascript library, I was able to generate the nice graph above. There was a lot of trial and error, but here’s an overview of the process:
- Copy Wii save game data to the SD card. This is done from Wii Options > Data Management > Save Data > Wii
- Find the save game data on the card. It’s in something like ‘private/wii/title/RFPE’, although different regions may have slightly different codes. RFPE is the code for WiiFit Plus. Copy the WiiFit data.bin file from the SD card to your local machine.
- Decrypt data.bin. This is explained pretty well here. To create the keys I ended up creating text files with the hex string for each and then using “xxd -r -p sd_iv_hex sd_iv” et al to save a binary version. If you’re getting “MD5 mismatch” errors, you probably saved the keys incorrectly. If you aren’t sure, check the file size. They should be 16 bytes each.
- Run the decrypted RPHealth.dat through a parser (I wrote one in Python for this)
- Run the CSV through your favorite graph generation library. I use flot because Google Charts don’t handle dates very well.
Thanks to Jansen’s handy chart of which bits are where, writing the parser was pretty easy. This isn’t the most elegant code I’ve ever written, but it gets the job done:
import struct import string import csv mii = 0 #we know that each record is 0x9271 bytes long record_length = 0x9281 record_start = 0 #path to WiiFit data file infile = 'RPHealth.dat' FH = open(infile, 'rb'); ## It loops through 7 profiles, because I happen to know I have 7. ## A better approach would be to go to the end of the file, of course. while (mii < 7): #go to the start of the current record FH.seek(record_start) #read the first 30 bytes (header + name) line = FH.read(30) #for some reason names are stored as N a m e instead of Name. #Throw away the header any extranous spaces data = struct.unpack("i",line) #bit shift to get the month, day, and year. Could also get time if you wanted. year = data[0] >> 20 & 0x7ff month = data[0] >> 16 & 0xf day = data[0] >> 11 & 0x1f #break the loop if the date comes back 0 if(year == 0): break #format the date into something humans like to read date = str(int(year)) + '-' + str(int(month)+1) + '-' + str(int(day)) #the next three sets of 2 byte data represent weight, BMI, and balance line = FH.read(17) data = struct.unpack(">3H",line[0:6]) recordWriter.writerow([date] + [data[0]] + [data[1]] + [data[2]]) #now that we're done with the record, advance to the start of the next one record_start = record_start + record_length mii = mii+1
You can download a copy of it here.