Skip to content

Instantly share code, notes, and snippets.

@yoshi314
Forked from tewalds/wiifit.py
Last active January 3, 2022 10:52
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save yoshi314/c63664debc140593c7fccdadc5cea632 to your computer and use it in GitHub Desktop.
Save yoshi314/c63664debc140593c7fccdadc5cea632 to your computer and use it in GitHub Desktop.
Extract weights from Wii Fit
#!/usr/bin/python
# Based on http://www.kellbot.com/2010/05/extracting-graphing-wii-fit-data/
#
# updated for python3
#
# Good references:
# http://jansenprice.com/blog?id=9-Extracting-Data-from-Wii-Fit-Plus-Savegame-Files
# http://stackoverflow.com/questions/616249/wii-fit-data-format
#
# Instructions to use this:
#
# 1. Download the data from the Wii to an SD card.
# 2. Build tachtig, needed to decrypt the data file.
# $ sudo apt-get install openssl libssl-dev
# $ git clone git://git.infradead.org/users/segher/wii.git
# $ cd wii
# $ make
# Ignore the tons of warnings, just as long as it created tachtig. Double
# check with:
# $ ls -l tachtig
# $ cd ..
# 3. Install the keys needed to decrypt the file
# $ mkdir ~/.wii
# $ echo ab01b9d8e1622b08afbad84dbfc2a55d | xxd -r -p - ~/.wii/sd-key
# $ echo 216712e6aa1f689f95c5a22324dc6a98 | xxd -r -p - ~/.wii/sd-iv
# $ echo 0e65378199be4517ab06ec22451a5793 | xxd -r -p - ~/.wii/md5-blanker
# 4. Decrypt it:
# $ wii/tachtig [SD_CARD/private/wii/title/RFPE/]data.bin
# That should create a directory with one or more .dat files, likely either
# called RPHealth.dat or FitPlus0.dat
# 5. Extract the contents by running this script:
# $ chmod +x wiifit.py
# $ ./wiifit.py <location of .dat file>
# 6. Do whatever you want with the resulting WiiFit_<name>.csv file.
from __future__ import division
import csv
import struct
import string
import sys
# Path to WiiFit data file
file_name = sys.argv[1] if len(sys.argv) > 1 else 'RPHealth.dat'
# Adjust based on what the wii took off for each recording, in Kg.
weight_adjustment = float(sys.argv[2]) if len(sys.argv) > 2 else 0.0
# Each record is 0x9271 bytes long
record_length = 0x9281
with open(file_name, 'rb') as in_file:
# Iterate through each Mii.
for record_start in range(0, record_length * 100, record_length):
# Go to the start of the current record.
in_file.seek(record_start)
# Read the first 30 bytes (header + name).
line = in_file.read(30)
# For some reason names are stored as N a m e instead of Name.
# Throw away the header and any extranous spaces.
data = struct.unpack('<9xcxcxcxcxcxcxcxcxcxcxc', line)
#print(data)
# Condense our unpacked characters into a string.
wf_name = b"".join(data).decode().strip('\0')
# my file has an empty record before last one,
# so i used continue to skip over
# this unfortunately fails once it gets past the last record
if not wf_name:
print("Skipping this entry, seems to be blank record")
continue
#break
print( 'Found', wf_name)
# Open a new CSV file for this person.
with open('WiiFit_%s.csv' % wf_name, 'w') as out_file:
writer = csv.writer(out_file, delimiter=',', quotechar="'", quoting=csv.QUOTE_MINIMAL)
writer.writerow(['Date', 'Weight', 'BMI', 'Balance'])
# Weight data starts 0x38a1 bytes into the record.
in_file.seek(record_start + 0x38a1)
# Loop through the record data until it starts coming up blank.
while True:
# Each line is 21 bytes. We only care about the first bit.
line = in_file.read(21)
# print("parsing" , line)
# 4 bytes of hex-packed date followed by three sets of 2 bytes
# representing weight, BMI, and balance.
# print(struct.unpack('>i3H',line[0:10]))
packed_date, weight, bmi, balance = struct.unpack('>i3H', line[0:10])
# Unpack the date: http://stackoverflow.com/questions/719129/datetime-hex-format
year = packed_date >> 20 & 0x7ff
month = (packed_date >> 16 & 0xf) + 1
day = packed_date >> 11 & 0x1f
hour = packed_date >> 6 & 0x1f
minute = packed_date & 0x3f
second = 0
if year == 0:
break
date = '%s-%02d-%02d %02d:%02d:%02d' % (year, month, day, hour, minute, second)
writer.writerow([date, weight_adjustment + (weight / 10), bmi / 100, balance / 10])
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment