Skip to content

Instantly share code, notes, and snippets.

@carlansell94
Created October 18, 2023 15:55
Show Gist options
  • Save carlansell94/0b39b25806fa3563b6d92b0c9a88cc86 to your computer and use it in GitHub Desktop.
Save carlansell94/0b39b25806fa3563b6d92b0c9a88cc86 to your computer and use it in GitHub Desktop.
Wii Fit U Parser
#!/usr/bin/python
#
# Based on the Wii Fit parser found here: https://gist.github.com/tewalds/86170c18ecf6f211a9bc
#
# Extract data from a Wii Fit U .dat file to a CSV.
# Add the -p flag to include user name, date of birth and current height to the output.
# File paths can be passed to the script. If none are passed, 'Step0.dat' will be used.
#
#
# 0xD = Name start
# 0x23 = Height in cm (1)
# 0x24 = DOB (yyyymmdd) (4)
# 0x5AD3 = Start of measures (22 each):
# 0 = Date (4)
# 4 = Weight (kg) (2)
# 6 = BMI (2)
# 8 = Right balance percentage (2)
# 15 = Body test age (1)
#
import struct
import csv
import argparse
import sys
parser = argparse.ArgumentParser(description="Extract data from a Wii Fit U .dat file to CSV")
parser.add_argument(
"-p",
"--player-data",
action="store_true",
help="include player data (name, DOB, height) at the top of the CSV file",
)
parser.add_argument(
'files',
nargs='*',
metavar='files',
help='One or more .dat files to parse',
default=['Step0.dat']
)
args = parser.parse_args()
# Converts hex values to integers witout performing a value conversion
# E.g. 0x19 0x90 is converted to 1990
# Required for DOB
def hex_bytes_to_integer(hex_value):
num_bytes = len(hex_value)
nibbles = []
int_value = int.from_bytes(hex_value, byteorder='big')
for i in range(num_bytes * 2):
nibble = (int_value >> (4 * i)) & 0x0F
nibbles.insert(0, nibble)
return int(''.join(map(str, nibbles)))
for file in args.files:
with open(file, 'rb') as input_file:
input_file.seek(int('0xD', base=16))
player_name = ''
for x in range(10): # maximum name length of 10 characters
char = input_file.read(1)
if char == b'\x00':
break
player_name += str(char, 'UTF-8')
input_file.seek(1, 1)
if len(player_name) == 0:
print("Error reading player name")
sys.exit(1)
with open(f'WiiFitU_{player_name:s}.csv', encoding='utf-8', mode='w') as out_file:
writer = csv.writer(out_file, delimiter=',', quotechar="'", quoting=csv.QUOTE_MINIMAL)
if args.player_data:
input_file.seek(int('0x23', base=16), 0)
height = ord(input_file.read(1))
dob_year = hex_bytes_to_integer(input_file.read(2))
dob_month = hex_bytes_to_integer(input_file.read(1))
dob_day = hex_bytes_to_integer(input_file.read(1))
writer.writerow(['Name:', player_name])
writer.writerow(['DOB:', f'{dob_year}-{dob_month:02d}-{dob_day:02d}'])
writer.writerow(['Current Height:', str(height) + 'cm'])
writer.writerow([])
writer.writerow(['Date', 'Weight (kg)', 'BMI', 'Right Balance', 'Fit Age'])
input_file.seek(int('0x5AD3', base=16), 0)
while True:
record = input_file.read(21)
if record[0] == 0:
break
packed_date, weight, bmi, balance, fit_age = struct.unpack('>i3H4xB',record[0:15])
# Unpack the date: http://stackoverflow.com/questions/719129/datetime-hex-format
year = packed_date >> 20
month = (packed_date >> 16 & 0xf) + 1
day = packed_date >> 11 & 0x1f
hour = packed_date >> 6 & 0x1f
minute = packed_date & 0x3f
date = f'{year}-{month:02d}-{day:02d} {hour:02d}:{minute:02d}'
weight = weight / 10
bmi = bmi / 100
balance = f'{balance / 10}%'
if fit_age == 0:
fit_age = ''
writer.writerow([date, weight, bmi, balance, fit_age])
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment