Extracting + Graphing Wii Fit data
- 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("<9xcxcxcxcxcxcxcxcxcxcxc",line) #Condense our unpacked characters into a string wf_name = string.join(data,'') #open a new CSV file for this person. #If the name is shorter than 4 characters or has whitespace, the script #will exit. This should probably be fixed. FW = open('WiiFit_'+wf_name[0:4]+'.csv', 'w') recordWriter = csv.writer(FW, delimiter=",", quotechar="'", quoting=csv.QUOTE_MINIMAL) #Weigh-in data starts 0x38a1 bytes into the record FH.seek(record_start + 0x38a1) #we'll loop through the record data until it starts coming up blank while(1): #4 byte date line = FH.read(4) data = struct.unpack(">i",line) #bit shift to get the month, day, and year. Could also get time if you wanted. year = data >> 20 & 0x7ff month = data >> 16 & 0xf day = data >> 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] + [data] + [data]) #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.