├── README.md ├── dolphin-files ├── dolphin-2.state ├── dolphin.state └── new-dolphin.state ├── dolphin-state.py ├── serial_binary.py └── serial_logger.py /README.md: -------------------------------------------------------------------------------- 1 | # dolphin_state.py 2 | 3 | Reads the `DolphinStoreData` struct from `dolphin.state` files. 4 | 5 | ## Usage 6 | 7 | ### Reading 8 | 9 | `python3 dolphin_state.py ` 10 | 11 | ### Writing 12 | 13 | **icounter**: Contains the amount of EXP flipper has 14 | 15 | **butthurt**: Level of happiness flipper has - `BUTTHURT_MAX = 14` 16 | 17 | With the `--icounter` and `--butthurt` the output `dolphin.state` can be modified. The script will automaticly update the checksum for the file. The `--out` parameter must be set. 18 | 19 | #### Setting EXP 20 | `python3 dolphin_state.py dolphin.state --icounter=1337 --out dolphin-new.state` 21 | 22 | #### Setting the Mood (Butthurt) 23 | `python3 dolphin_state.py dolphin.state --butthurt=14 --out dolphin-new.state` 24 | 25 | #### Setting EXP and Mood 26 | `python3 dolphin_state.py dolphin.state --icounter=1337 --butthurt=14 --out dolphin-new.state` 27 | 28 | ## Output 29 | 30 | ```python 31 | [+] Read 40 bytes from dolphin.state 32 | [+] Updating icounter to 1337 33 | [+] Saving dolphin state to new-dolphin.state 34 | [+] Calculated new checksum: 161 35 | [+] Saved output to: new-dolphin.state 36 | [+] SavedStructHeader 37 | magic: 208 38 | version: 1 39 | checksum: 161 40 | flags: 0 41 | timestamp: 0 42 | 43 | [+] DolphinStoreData 44 | DolphinAppSubGhz: 0 45 | DolphinAppRfid: 20 46 | DolphinAppNfc: 20 47 | DolphinAppIr: 0 48 | DolphinAppIbutton: 0 49 | DolphinAppBadusb: 0 50 | DolphinAppU2f: 2 51 | butthurt_daily_limit: 46 52 | 53 | flags: 0 54 | icounter: 1337 55 | butthurt: 14 56 | timestamp: 1678561214 (2023-03-11 14:00:14) 57 | 58 | [+] Passport 59 | level: 2 60 | mood: Angry enough to leave 61 | percent complete: 69.13% 62 | ``` 63 | 64 | 65 | ### Help 66 | 67 | ```python 68 | > python3 dolphin_state.py -h 69 | usage: dolphin_state.py [-h] file 70 | 71 | Read the contents of a flipper-zero's dolphin.state 72 | 73 | positional arguments: 74 | file Path of the dolphin.state file 75 | 76 | optional arguments: 77 | -h, --help show this help message and exit 78 | ``` 79 | 80 | #### Reading / Writing the dolphin.state 81 | 82 | 1. Use the file manager version of the qflipper software for PC. 83 | 2. Open the `internal flash storage` 84 | 3. Drag and drop the `dolphin.state` file to read/write it to the flipper! 85 | 86 | 87 | ## Credits 88 | Thanks to the FlipperZero team for developing this awsome product! 89 | 90 | - Lamp (Tarsad) : For the idea, and sharing his `dolphin.state` files. And explaining how one could read/write the `dolphin.state` file. 91 | 92 | -------------------------------------------------------------------------------- /dolphin-files/dolphin-2.state: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DroomOne/FlipperScripts/777912e4e707ff3129d803bdeb90976e096da7b3/dolphin-files/dolphin-2.state -------------------------------------------------------------------------------- /dolphin-files/dolphin.state: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DroomOne/FlipperScripts/777912e4e707ff3129d803bdeb90976e096da7b3/dolphin-files/dolphin.state -------------------------------------------------------------------------------- /dolphin-files/new-dolphin.state: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DroomOne/FlipperScripts/777912e4e707ff3129d803bdeb90976e096da7b3/dolphin-files/new-dolphin.state -------------------------------------------------------------------------------- /dolphin-state.py: -------------------------------------------------------------------------------- 1 | import argparse 2 | import struct 3 | import os 4 | from datetime import datetime 5 | 6 | parser = argparse.ArgumentParser(description="Read the contents of a flipper-zero's dolphin.state") 7 | parser.add_argument("file", type=str, help="Path of the dolphin.state file") 8 | parser.add_argument("--out", type=str, nargs='?', help="Output path of the dolphin.state file") 9 | parser.add_argument("--icounter", type=int, nargs='?', help="Overwrite the icounter value in dolphin.state") 10 | parser.add_argument("--butthurt", type=int, nargs='?', help="Overwrite the butthurt value in dolphin.state") 11 | args = parser.parse_args() 12 | 13 | HEADER_SIZE = 8 14 | DolphinAppMAX = 7 15 | BUTTHURT_MAX = 14 16 | 17 | # https://github.com/flipperdevices/flipperzero-firmware/blob/3c77ae2eb88db05e4bc8a51c7a0dbd64943c0f9f/applications/dolphin/helpers/dolphin_state.c 18 | LEVEL2_THRESHOLD = 300 19 | LEVEL3_THRESHOLD = 1800 20 | 21 | # https://github.com/flipperdevices/flipperzero-firmware/blob/3c77ae2eb88db05e4bc8a51c7a0dbd64943c0f9f/applications/dolphin/passport/passport.c 22 | moods = [{"name": "Happy", "max_butthurt": 4}, {"name": "Ok", "max_butthurt": 9}, {"name": "Angry", "max_butthurt": BUTTHURT_MAX}] 23 | 24 | 25 | # https://github.com/flipperdevices/flipperzero-firmware/blob/3c77ae2eb88db05e4bc8a51c7a0dbd64943c0f9f/lib/toolbox/saved_struct.c 26 | def unpack_header(buffer): 27 | (magic, version, checksum, flags, timestamp) = struct.unpack('BBBBI', buffer[0:HEADER_SIZE]) 28 | print("[+] SavedStructHeader") 29 | print(" magic:\t\t\t", magic) 30 | print(" version:\t\t\t" , version) 31 | print(" checksum:\t\t\t", checksum) 32 | print(" flags:\t\t\t", flags) 33 | print(" timestamp:\t\t\t", timestamp) 34 | print() 35 | 36 | 37 | # https://github.com/flipperdevices/flipperzero-firmware/blob/3c77ae2eb88db05e4bc8a51c7a0dbd64943c0f9f/applications/dolphin/helpers/dolphin_state.h 38 | def unpack_state(buffer): 39 | (DolphinAppSubGhz, DolphinAppRfid, DolphinAppNfc, DolphinAppIr, DolphinAppIbutton, DolphinAppBadusb, DolphinAppU2f, butthurt_daily_limit) = struct.unpack('BBBBBBBB', buffer[HEADER_SIZE:HEADER_SIZE+8]) 40 | print("[+] DolphinStoreData") 41 | print(" DolphinAppSubGhz:\t\t", DolphinAppSubGhz) 42 | print(" DolphinAppRfid:\t\t" , DolphinAppRfid) 43 | print(" DolphinAppNfc:\t\t", DolphinAppNfc) 44 | print(" DolphinAppIr:\t\t", DolphinAppIr) 45 | print(" DolphinAppIbutton:\t\t", DolphinAppIbutton) 46 | print(" DolphinAppBadusb:\t\t", DolphinAppBadusb) 47 | print(" DolphinAppU2f:\t\t", DolphinAppU2f) 48 | print(" butthurt_daily_limit:\t", butthurt_daily_limit) 49 | print() 50 | 51 | (flags, icounter, butthurt, timestamp) = struct.unpack("IIIQ", buffer[HEADER_SIZE+8:]) 52 | 53 | print(" flags:\t\t\t", flags) 54 | print(" icounter:\t\t\t", icounter) 55 | print(" butthurt:\t\t\t", butthurt) 56 | print(" timestamp:\t\t\t", timestamp, '(' + str(datetime.fromtimestamp(timestamp)) + ')') 57 | print() 58 | 59 | if icounter >= LEVEL3_THRESHOLD: 60 | # There's only 3 levels, so percentage is at 100% once we hit level 3 61 | passport_pct = 100 62 | passport_level = 3 63 | elif icounter >= LEVEL2_THRESHOLD: 64 | passport_pct = ((icounter - LEVEL2_THRESHOLD) / (LEVEL3_THRESHOLD - LEVEL2_THRESHOLD)) * 100 65 | passport_level = 2 66 | else: 67 | passport_pct = (icounter / LEVEL2_THRESHOLD) * 100 68 | passport_level = 1 69 | 70 | for mood in reversed(moods): 71 | if (butthurt <= mood["max_butthurt"]): passport_mood = mood["name"] 72 | 73 | # https://github.com/flipperdevices/flipperzero-firmware/tree/c97d9a633ebf94af2365c6e17760b44cd8c88c60/assets/dolphin/external/L1_Leaving_sad_128x64 74 | # and 75 | # https://github.com/flipperdevices/flipperzero-firmware/blob/c97d9a633ebf94af2365c6e17760b44cd8c88c60/assets/dolphin/external/manifest.txt 76 | # If butthurt == BUTTHURT_MAX, there's a chance the L1_Leaving_sad animation will play 77 | if (butthurt == BUTTHURT_MAX): passport_mood += " enough to leave" 78 | 79 | print("[+] Passport") 80 | print(" level:\t\t\t", passport_level) 81 | print(" mood:\t\t\t", passport_mood) 82 | print(" percent complete:\t\t %.2f%%" % passport_pct) 83 | 84 | def update_icounter(buffer, value): 85 | print("[+] Updating icounter to", value) 86 | return buffer[:HEADER_SIZE+12] + struct.pack("I", value) + buffer[HEADER_SIZE+16:] 87 | 88 | def update_butthurt(buffer, value): 89 | if value > BUTTHURT_MAX: 90 | print('[-]', value, 'is way too much butthurt for Flipper to handle (Max=14). Try decreasing it.') 91 | print('[-] Skipping update_butthurt') 92 | return buffer 93 | 94 | print("[+] Updating butthurt to", value) 95 | return buffer[:HEADER_SIZE+16] + struct.pack("I", value) + buffer[HEADER_SIZE+20:] 96 | 97 | # https://github.com/flipperdevices/flipperzero-firmware/blob/3c77ae2eb88db05e4bc8a51c7a0dbd64943c0f9f/lib/toolbox/saved_struct.c 98 | def dolphin_state_save(buffer, path): 99 | print('[+] Saving dolphin state to', path) 100 | checksum = 0 101 | for byte in buffer[HEADER_SIZE:]: 102 | checksum += byte 103 | 104 | print("[+] Calculated new checksum:", checksum % 256) 105 | buffer = buffer[:2] + struct.pack("B", checksum % 256) + buffer[3:] 106 | with open(path, 'wb') as writer: 107 | writer.write(buffer) 108 | 109 | print("[+] Saved output to:", path) 110 | return buffer 111 | 112 | 113 | if (args.icounter is not None or args.butthurt is not None) and args.out is None: 114 | print('--icounter and --butthurt requires a output path --out ') 115 | exit() 116 | 117 | if not os.path.exists(args.file): 118 | print('[-]', args.file, 'does not exists. Exiting.') 119 | exit() 120 | 121 | with open(args.file, 'rb') as reader: 122 | buffer = reader.read() 123 | 124 | print("[+] Read", len(buffer), "bytes from", args.file) 125 | 126 | if args.icounter is not None: 127 | buffer = update_icounter(buffer, args.icounter) 128 | 129 | if args.butthurt is not None: 130 | buffer = update_butthurt(buffer, args.butthurt) 131 | 132 | if args.out: 133 | buffer = dolphin_state_save(buffer, args.out) 134 | 135 | unpack_header(buffer) 136 | unpack_state(buffer) 137 | -------------------------------------------------------------------------------- /serial_binary.py: -------------------------------------------------------------------------------- 1 | import serial 2 | from hexdump import hexdump 3 | from colorama import init 4 | from termcolor import cprint 5 | 6 | init() 7 | flipper = serial.Serial("COM3", timeout=1) 8 | flipper.baudrate = 230400 9 | flipper.flushOutput() 10 | flipper.flushInput() 11 | 12 | flipper.timeout = None 13 | 14 | flipper.read_until(b'>: ') 15 | flipper.write(b"storage read /ext/tama_p1/save.bin\r") 16 | flipper.read_until(b'\n') 17 | flipper.readline() 18 | 19 | ''' 20 | >: storage read /ext/tama_p1/save.bin 21 | Size: 68 22 | Ķ ▒▒▒▒0▒▒▒▒▒ Q ▒▒▒▒▒ ▒W 23 | ▒' ▒▒▒▒▒▒▒▒ 24 | >: PuTTY 25 | ''' 26 | 27 | line = flipper.read_until(b'\x0D\x0A\x0D\x0A')[0:-4] 28 | hexdump(line) 29 | flipper.read_until(b'>: ') -------------------------------------------------------------------------------- /serial_logger.py: -------------------------------------------------------------------------------- 1 | import serial 2 | import os 3 | from colorama import init 4 | from termcolor import cprint 5 | 6 | init() 7 | flipper = serial.Serial("COM3", timeout=1) 8 | flipper.baudrate = 230400 9 | flipper.flushOutput() 10 | flipper.flushInput() 11 | 12 | flipper.timeout = None 13 | 14 | flipper.read_until(b'>: ') 15 | 16 | flipper.write(b"log\r") 17 | flipper.read_until(b'\n') 18 | 19 | while flipper.is_open: 20 | line = flipper.readline().rstrip() 21 | strline = line.decode('utf-8') 22 | 23 | cprint(strline) 24 | if 'Finished Writing' in strline: 25 | break 26 | --------------------------------------------------------------------------------