Merge branch 'master' into master

This commit is contained in:
Markus Birth 2024-05-27 02:53:26 +01:00 committed by GitHub
commit 76dd3b8566
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
17 changed files with 4195 additions and 1084 deletions

View File

@ -20,18 +20,18 @@ Garmin wearables SKU numbers
| fenix 5X/tactix Charlie | 006-B2604-00 | 16,000 MB | X | 006-B2663-00 | | 006-B2957-00 | 006-B2196-01 | | 006-B2605-00 |
| Forerunner 935 | 006-B2691-00 | 64 MB | X | 006-B2665-00 | | 006-B2957-00 | 006-B2196-01 | | |
| fenix 5/quatix 5 | 006-B2697-00 | 64 MB | X | 006-B2661-00 | | 006-B2957-00 | 006-B2196-01 | | |
| D2 Charlie | 006-B2819-00 | 16,000 MB | X | 006-B2663-00 | | 006-B2957-00 | 006-B2196-01 | | 006-B2820-00 |
| D2 Charlie | 006-B2819-00 | 16,000 MB | X | 006-B2663-00 | | 006-B3750-00 | 006-B2196-01 | | 006-B2820-00 |
| Descent Mk1 | 006-B2859-00 | 16,000 MB | X | 006-B2664-00 | | 006-B1621-00 | 006-B2196-01 | | 006-B2869-00 |
| Forerunner 645 Music | 006-B2888-00 | 4,000 MB | X | 006-B2897-00 | 006-B2887-00 | 006-B1621-00 | 006-B2196-02 | 006-B2822-00 | |
| Forerunner 645 Music | 006-B2886-00 | 4,000 MB | X | 006-B2897-00 | 006-B2887-00 | 006-B1621-00 | 006-B2196-02 | 006-B2822-01 | |
| fenix 5S Plus | 006-B2900-00 | 16,000 MB | X | 006-B3013-00 | 006-B3153-00 | 006-B3750-00 | 006-B2196-02 | 006-B2822-01 | |
| Forerunner 645M APAC | 006-B3004-00 | 4,000 MB | X | 006-B3009-00 | 006-B3199-00 | 006-B1621-00 | 006-B2196-02 | 006-B2822-00 | |
| fenix 5S Plus | 006-B2900-00 | 16,000 MB | X | 006-B3013-00 | 006-B3153-00 | 006-B2957-00 | 006-B2196-02 | 006-B2822-01 | |
| Forerunner 245 Music | 006-B3077-00 | 4,000 MB | G3 | 006-B3079-00 | 006-B3205-00 | 006-B1621-00 | 006-B2196-03 | | |
| fenix 5 Plus | 006-B3110-00 | 16,000 MB | X | 006-B3014-00 | 006-B3153-00 | 006-B2957-00 | 006-B2196-02 | 006-B2822-01 | |
| fenix 5X Plus | 006-B3111-00 | 16,000 MB | POx | 006-B3015-00 | 006-B3153-00 | 006-B2957-00 | 006-B2196-02 | 006-B2822-01 | |
| fenix 5 Plus | 006-B3110-00 | 16,000 MB | X | 006-B3014-00 | 006-B3153-00 | 006-B3750-00 | 006-B2196-02 | 006-B2822-01 | |
| fenix 5X Plus | 006-B3111-00 | 16,000 MB | POx | 006-B3015-00 | 006-B3153-00 | 006-B3750-00 | 006-B2196-02 | 006-B2822-01 | |
| Forerunner 945 | 006-B3113-00 | 16,000 MB | G3 | 006-B3114-00 | 006-B3303-00 | 006-B3107-00 | | | |
| D2 Delta S | 006-B3196-00 | 16,000 MB | X | 006-B3014-00 | 006-B3260-00 | 006-B2957-00 | 006-B2196-02 | 006-B2822-01 | |
| D2 Delta | 006-B3197-00 | 16,000 MB | X | 006-B3014-00 | 006-B3260-00 | 006-B2957-00 | 006-B2196-02 | 006-B2822-01 | |
| D2 Delta PX | 006-B3198-00 | 16,000 MB | POx | 006-B3014-00 | 006-B3260-00 | 006-B2957-00 | 006-B2196-02 | 006-B2822-01 | |
| D2 Delta S | 006-B3196-00 | 16,000 MB | X | 006-B3014-00 | 006-B3260-00 | 006-B3750-00 | 006-B2196-02 | 006-B2822-01 | |
| D2 Delta | 006-B3197-00 | 16,000 MB | X | 006-B3014-00 | 006-B3260-00 | 006-B3750-00 | 006-B2196-02 | 006-B2822-01 | |
| D2 Delta PX | 006-B3198-00 | 16,000 MB | POx | 006-B3014-00 | 006-B3260-00 | 006-B3750-00 | 006-B2196-02 | 006-B2822-01 | |
| MARQ Driver | 006-B3246-00 | 32,000 MB | G3 | 006-B3252-00 | 006-B3253-00 | 006-B3107-00 | | | |
| MARQ Aviator | 006-B3247-00 | 32,000 MB | G3 | 006-B3252-00 | 006-B3253-00 | 006-B3107-00 | | | |
| MARQ Captain | 006-B3248-00 | 32,000 MB | G3 | 006-B3252-00 | 006-B3253-00 | 006-B3107-00 | | | |
@ -45,6 +45,7 @@ Garmin wearables SKU numbers
| Fenix 6 Pro | 006-B3290-00 | 32,000 MB | G3 | 006-B3295-00 | 006-B3293-00 | | | | |
| Fenix 6X Pro | 006-B3291-00 | 32,000 MB | G3 | 006-B3296-00 | 006-B3293-00 | | | | |
| MARQ Adventurer | 006-B3624-00 | 32,000 MB | G3 | | | | | | |
| Venu Sq - Music | 006-B3596-00 | 4,000 MB | | 006-B3602-00 | 006-B3597-00 | 006-B3799-12 | 006-B2196-03 | | 006-B3598-00 |
@ -55,3 +56,4 @@ Notes
* ANT/BLE/BT firmwares of the D2 Delta and fenix 5 Plus series are identical par the SKU number
* GPS firmwares 1621 and 2957 are for the same chip, but 2957 has Galileo support, 1621 does not
* GPS firmwares 3107 and 3506 are the new Sony chipset (introduced with the MARQ series)
* fenix 5 Plus and D2 Delta series switched GPS from 006-B2957-00 to 006-B3750-00 in recent firmwares

View File

@ -1,11 +1,11 @@
Garmin Firmware Tools
=====================
This is a parser and some tools for working with GCD files (firmware updates).
This is a parser and some tools for working with Garmin firmware updates (GCD/RGN files).
It's in Python, so feel free to add cool new features and submit pull requests.
Thanks to TurboCCC, kunix and Alex W. for your work.
Thanks to TurboCCC, kunix and AlexWhiter for your work.
Most info from:
@ -14,12 +14,22 @@ Most info from:
* hours of looking at hex numbers
How YOU can help
----------------
If you'd like to help, feel free to get all the SKU numbers from your `GarminDevice.xml`
(numbers starting with 006-B...) and post them under "Issues" (create a new issue with the
name of your device).
Of course, pull requests are much appreciated, too.
Tools
-----
### gcdstruct.py [gcdfile]
### gcdstruct.py [gcdfile] / rgnstruct.py [rgnfile]
Will show the general structure of the GCD file and also validate the contained checksums, e.g.:
Will show the general structure of the GCD/RGN file and also validate the contained checksums, e.g.:
```
$ ./gcdstruct.py fenix5Plus_510.gcd
@ -167,3 +177,13 @@ Checks Express and WebUpdater for updates for the given hw_ids (1-4 digits) or f
The first one is used as the main device in the query. Shouldn't make a big difference in the results, though.
Special thanks to Alex W. for [his update check](https://github.com/AlexWhiter/GarminRelatedStuff).
### list_missing_hwids.py
Shows a list of hw_ids not yet listed in the `devices.py`. It prepends a call to `get_updates.py` for an
easy way to check the update servers for new devices.
To find future devices, you can supply a parameter (can be anything) and it will output 300 more hw_ids
after the last known.

14
TODO.md Normal file
View File

@ -0,0 +1,14 @@
TODO
====
* RGN file support
* convert between RGN and GCD
* Express-like updater
* should be text interface or ncurses
* detect Garmin MTP devices or /GARMIN directory in mounted roots
* MTP support: https://github.com/wangjiezhe/pymtp
* fetch GarminDevice.xml
* upload to Express, list returned files
* ask user, then download files and push to device
* maybe: support for additional downloads (languages, icons, etc.)
* maybe: support for running under Windows (although there you have the real Express)

113
binbase_find.py Normal file
View File

@ -0,0 +1,113 @@
#!/usr/bin/env python3
# https://github.com/mncoppola/ws30/blob/master/basefind.py
import os
import re
import signal
import struct
import sys
from operator import itemgetter
chars = "A-Za-z0-9/\\-:.,_$%'\"()[\]<> "
min_length = 10
scores = []
top_score = 0
regexp = bytes("[{}]{{{:d},}}".format(chars, min_length), "us-ascii")
pattern = re.compile(regexp)
regexpc = bytes("[{}]{{1,}}".format(chars), "us-ascii")
patternc = re.compile(regexpc)
def get_strings(filename, size):
table = set()
offset = 0
with open(filename, "rb") as f:
while True:
if offset >= size:
break
f.seek(offset)
try:
data = f.read(10)
except:
break
match = pattern.match(data)
if match:
f.seek(offset - 1)
try:
char = f.read(1)
except:
continue
if not patternc.match(char):
table.add(offset)
offset += len(match.group(0))
offset += 1
return table
def get_pointers(filename):
table = {}
with open(filename, "rb") as f:
while True:
try:
value = struct.unpack("<L", f.read(4))[0]
try:
table[value] += 1
except KeyError:
table[value] = 1
except:
break
return table
def high_scores(signal, frame):
print("\nTop 20 base address candidates:")
for score in sorted(scores, key=itemgetter(1), reverse=True)[:20]:
print("0x{:x}\t{:d}".format(*score))
sys.exit(0)
if __name__ == "__main__":
import argparse
parser = argparse.ArgumentParser()
def auto_int(x):
return int(x, 0)
parser.add_argument("--min_addr", type=auto_int, help="start searching at this address", default=0)
parser.add_argument("--max_addr", type=auto_int, help="stop searching at this address", default=0xfe000000)
parser.add_argument("--page_size", type=auto_int, help="search every this many byte", default=0x1000)
parser.add_argument("infile", help="file to scan")
args = parser.parse_args()
size = os.path.getsize(args.infile)
scores = []
print("Scanning binary for strings...")
str_table = get_strings(args.infile, size)
print("Total strings found: {:d}".format(len(str_table)))
print("Scanning binary for pointers...")
ptr_table = get_pointers(args.infile)
print("Total pointers found: {:d}".format(len(ptr_table)))
signal.signal(signal.SIGINT, high_scores)
for base in range(args.min_addr, args.max_addr, args.page_size):
if base % ( args.page_size * 1000 ) == 0:
print("Trying base address 0x{:x}".format(base))
print("\u001b[F\u001b[K", end="")
score = 0
ptrs = list(ptr_table.keys())
for ptr in ptrs:
if ptr < base:
#print("Removing pointer 0x{:x} from table".format(ptr))
del ptr_table[ptr]
continue
if ptr >= (base + size):
continue
offset = ptr - base
if offset in str_table:
score += ptr_table[ptr]
if score:
scores.append((base, score))
if score > top_score:
top_score = score
print("New highest score, 0x{:x}: {:d}".format(base, score))
high_scores(0, 0)

73
binbase_kunix.py Normal file
View File

@ -0,0 +1,73 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
# Many thanks to kunix!
"""
Calculates possible base address.
"""
from struct import unpack
import os.path
import sys
FILE = sys.argv[1]
OFFSET = 0
if len(sys.argv) > 2:
OFFSET = int(sys.argv[2])
BLOCKSIZE = 4096
END_MARKER = b"\xff\xff\x5a\xa5"
first_block = True
end_marker_pos = 0xffffffff
print("Reading {} ...".format(FILE))
with open(FILE, "rb") as f:
f.read(OFFSET)
while True:
block = f.read(BLOCKSIZE)
if first_block:
dw = unpack("<LLLLL", block[0:20])
first_block = False
if END_MARKER in block:
end_pos = block.find(END_MARKER)
found_pos = f.tell() - len(block) + end_pos + 2
if found_pos > end_marker_pos:
print("Found a second endmarker! Using that one.")
end_marker_pos = found_pos
#break
if len(block) < BLOCKSIZE:
break
f.close()
size = os.path.getsize(FILE)
print("File is {} (0x{:x}) Bytes.".format(size, size))
print("First double-words: 0x{:x} / 0x{:x} / 0x{:x} / 0x{:x} / 0x{:x}".format(dw[0], dw[1], dw[2], dw[3], dw[4]))
print("Assuming this is end marker location in memory: 0x{:x}".format(dw[1]))
print("Found end marker in file at: 0x{:x}".format(end_marker_pos))
base_addr = dw[1] - (end_marker_pos - OFFSET)
if base_addr < 0:
base_addr += 0xffffffff
print("This would make Base address probably 0x{:x}".format(base_addr))
if base_addr % 4 != 0:
print("However, bad alignment. Calculated base address not aligned to doublewords.")
if base_addr + size > 0xffffffff:
print("However, base address can't fit whole file.")
# Assumes second dword points to hwid
#if dw[2] % 2 != 0 or dw[2] - base_addr >= end_marker_pos - 3:
# print("Align & Bounds dw2 wrong.")
# sys.exit(1)
# Assumes third dword points to fwid
#if dw[3] % 2 != 0 or dw[3] - base_addr >= end_marker_pos - 3:
# print("Align & Bounds dw3 wrong.")
# sys.exit(1)
# hwid = dw[2] - base_addr
# fwid = dw[3] - base_addr

View File

@ -35,7 +35,7 @@ with open(FILE, "rb") as f:
start += 4
hwid = unpack("<H", block[start+24:start+24+2])[0]
fver = unpack("<H", block[start+28:start+28+2])[0]
print("- Hardware ID: 0x{:04x} / {:d} ({})".format(hwid, hwid, devices.DEVICES.get(hwid, "Unknown device")))
print("- Hardware ID: 0x{:04x} / {:d} ({})".format(hwid, hwid, devices.get_name(hwid, 0, "Unknown device")))
print("- Firmware Version: 0x{:04x} / {:04d}".format(fver, fver))
first_block = False
if END_MARKER in block:

240
components.txt Normal file
View File

@ -0,0 +1,240 @@
GUP1630:forerunner/Forerunner620.rgn
GUP1630:forerunner/Forerunner920XT.rgn
GUP1752:forerunner/Forerunner220.rgn
GUP1752:forerunner/Forerunner620.rgn
GUP1850:forerunner/Forerunner220.rgn
GUP1850:forerunner/Forerunner620.rgn
GUP1851:forerunner/Forerunner220.rgn
GUP1851:forerunner/Forerunner620.rgn
GUP1908:vivo/vivoactive.rgn
GUP1909:fenix_D2_tactix/D2Bravo.rgn
GUP1909:fenix_D2_tactix/D2BravoTitanium.rgn
GUP1909:fenix_D2_tactix/fenix3HRBeta.zip
GUP1909:fenix_D2_tactix/fenix3HR.rgn
GUP1909:fenix_D2_tactix/fenix3.rgn
GUP1909:fenix_D2_tactix/fenix3_tactixBravo_quatix3Beta.zip
GUP1909:fenix_D2_tactix/fenix3_tactixBravo_quatix3.rgn
GUP1909:vivo/vivoactive.rgn
GUP1909:vivo/vivosmartHRplus.rgn
GUP1909:vivo/vivosmart_HR.rgn
GUP1909:vivo/vivosmartHR.rgn
GUP1942:forerunner/Forerunner920XT.rgn
GUP1955:vivo/vivosmartAPAC.rgn
GUP1955:vivo/vivosmart.rgn
GUP1969:forerunner/Forerunner15.rgn
GUP2005:vivo/vivosmartAPAC.rgn
GUP2005:vivo/vivosmart.rgn
GUP2047:astro_alpha/T5_TT15Mini.rgn
GUP2047:edge/Edge25.rgn
GUP2047:edge/HMD_regionfileonly_.rgn
GUP2047:edge/VariaVision.rgn
GUP2047:forerunner/Forerunner225.rgn
GUP2047:forerunner/Forerunner25.rgn
GUP2047:forerunner/Forerunner30.rgn
GUP2047:forerunner/Forerunner35.rgn
GUP2047:forerunner/Forerunner920XTBeta.zip
GUP2047:forerunner/Forerunner920XT.rgn
GUP2047:misc_marine/GarminNautix_regionfileonly_.rgn
GUP2051:fenix_D2_tactix/D2Bravo.rgn
GUP2051:fenix_D2_tactix/fenix3.rgn
GUP2051:fenix_D2_tactix/fenix3_tactixBravo_quatix3.rgn
GUP2059:vivo/vivoactive.rgn
GUP2108:fenix_D2_tactix/D2Bravo.rgn
GUP2108:fenix_D2_tactix/fenix3.rgn
GUP2108:fenix_D2_tactix/fenix3_tactixBravo_quatix3.rgn
GUP2151:forerunner/Forerunner225.rgn
GUP2159:forerunner/Forerunner230.gcd
GUP2159:forerunner/Forerunner235.gcd
GUP2159:forerunner/Forerunner630.gcd
GUP2196:fenix_D2_tactix/D2BravoTitanium.rgn
GUP2196:fenix_D2_tactix/D2Charlie.gcd
GUP2196:fenix_D2_tactix/DescentAPAC_GCDfileonly_.gcd
GUP2196:fenix_D2_tactix/DescentMk1.gcd
GUP2196:fenix_D2_tactix/fenix3HRBeta.zip
GUP2196:fenix_D2_tactix/fenix3HR.rgn
GUP2196:fenix_D2_tactix/fenix5Beta.zip
GUP2196:fenix_D2_tactix/fenix5.gcd
GUP2196:fenix_D2_tactix/fenix5PlusBeta.zip
GUP2196:fenix_D2_tactix/fenix5SBeta.zip
GUP2196:fenix_D2_tactix/fenix5S.gcd
GUP2196:fenix_D2_tactix/fenix5SPlusBeta.zip
GUP2196:fenix_D2_tactix/fenix5XBeta.zip
GUP2196:fenix_D2_tactix/fenix5X.gcd
GUP2196:fenix_D2_tactix/fenix5XPlusBeta.zip
GUP2196:fenix_D2_tactix/fenix6ProBeta.zip
GUP2196:fenix_D2_tactix/fenix6SProBeta.zip
GUP2196:fenix_D2_tactix/fenix6XProBeta.zip
GUP2196:forerunner/Forerunner630.gcd
GUP2196:forerunner/Forerunner935.gcd
GUP2196:MARQ/MARQAdventurerBeta.zip
GUP2196:MARQ/MARQAthleteBeta.zip
GUP2196:MARQ/MARQAviatorBeta.zip
GUP2196:MARQ/MARQBeta.zip
GUP2196:MARQ/MARQCaptainBeta.zip
GUP2196:MARQ/MARQCommanderBeta.zip
GUP2196:MARQ/MARQDriverBeta.zip
GUP2196:MARQ/MARQExpeditionBeta.zip
GUP2197:forerunner/Forerunner630.gcd
GUP2202:baseball_golf/TruSwing.rgn
GUP2202:golf_baseball/TruSwing.rgn
GUP2228:forerunner/Forerunner225.rgn
GUP2338:vivo/vivoactiveHR.gcd
GUP2339:vivo/vivoactiveHR.gcd
GUP2340:vivo/vivoactiveHR.gcd
GUP2358:vivo/vivosmart_HR.rgn
GUP2358:vivo/vivosmartHR.rgn
GUP2369:forerunner/Forerunner235Beta.zip
GUP2369:forerunner/Forerunner235.gcd
GUP2392:fenix_D2_tactix/fenixChronosBeta_USB.zip
GUP2392:fenix_D2_tactix/fenixChronosBeta.zip
GUP2392:fenix_D2_tactix/fenixChronos.gcd
GUP2393:fenix_D2_tactix/fenixChronosBeta_USB.zip
GUP2393:fenix_D2_tactix/fenixChronosBeta.zip
GUP2393:fenix_D2_tactix/fenixChronos.gcd
GUP2403:baseball_golf/ApproachS20.rgn
GUP2403:golf_baseball/ApproachS20.rgn
GUP2405:baseball_golf/ApproachX40.rgn
GUP2405:golf_baseball/ApproachX40.rgn
GUP2414:fenix_D2_tactix/D2BravoTitanium.rgn
GUP2414:fenix_D2_tactix/fenix3HR.rgn
GUP2415:fenix_D2_tactix/fenix3HRBeta.zip
GUP2415:fenix_D2_tactix/fenix3HR.rgn
GUP2423:fenix_D2_tactix/fenixChronos.gcd
GUP2423:forerunner/Forerunner230Beta.zip
GUP2423:forerunner/Forerunner230.gcd
GUP2423:forerunner/Forerunner235Beta.zip
GUP2423:forerunner/Forerunner235.gcd
GUP2423:forerunner/Forerunner630Beta.zip
GUP2423:forerunner/Forerunner630.gcd
GUP2423:forerunner/Forerunner735XTBeta.zip
GUP2423:forerunner/Forerunner735XT.gcd
GUP2423:vivo/vivoactiveHR.gcd
GUP2447:edge/HMD_regionfileonly_.rgn
GUP2447:edge/VariaVision.rgn
GUP2447:misc_marine/GarminNautix_regionfileonly_.rgn
GUP2510:forerunner/Forerunner735XT.gcd
GUP2511:forerunner/Forerunner735XT.gcd
GUP2527:baseball_golf/ApproachX40.rgn
GUP2527:golf_baseball/ApproachX40.rgn
GUP2527:vivo/vivosmartHRplus.rgn
GUP2560:fenix_D2_tactix/D2BravoTitanium.rgn
GUP2583:astro_alpha/DeltaSmart.rgn
GUP2583:delta/DeltaSmart.rgn
GUP2605:fenix_D2_tactix/fenix5XBeta.zip
GUP2605:fenix_D2_tactix/fenix5X.gcd
GUP2657:baseball_golf/ApproachS60Beta.zip
GUP2657:golf_baseball/ApproachS60Beta.zip
GUP2661:fenix_D2_tactix/fenix5Beta.zip
GUP2661:fenix_D2_tactix/fenix5.gcd
GUP2662:fenix_D2_tactix/fenix5SBeta.zip
GUP2662:fenix_D2_tactix/fenix5S.gcd
GUP2663:fenix_D2_tactix/D2Charlie.gcd
GUP2663:fenix_D2_tactix/fenix5XBeta.zip
GUP2663:fenix_D2_tactix/fenix5X.gcd
GUP2664:fenix_D2_tactix/DescentMk1.gcd
GUP2665:forerunner/Forerunner935_1157_SNS_623_Beta.zip
GUP2665:forerunner/Forerunner935_1157_SNS_625_Beta.zip
GUP2665:forerunner/Forerunner935Beta.zip
GUP2665:forerunner/Forerunner935.gcd
GUP2666:vivo/vivoactive3.gcd
GUP2699:vivo/vivoactive3.gcd
GUP2699:vivo/vivoactive3t.gcd
GUP2752:baseball_golf/ApproachS60Beta.zip
GUP2752:baseball_golf/ApproachS60.gcd
GUP2752:golf_baseball/ApproachS60Beta.zip
GUP2752:golf_baseball/ApproachS60.gcd
GUP2792:foretrex/Foretrex601_701_WebUpdater_.gcd
GUP2818:xero/XeroA1_i_.gcd
GUP2820:fenix_D2_tactix/D2Charlie.gcd
GUP2822:vivo/vivoactive3.gcd
GUP2827:baseball_golf/Impact.rgn
GUP2827:golf_baseball/Impact.rgn
GUP2869:fenix_D2_tactix/DescentAPAC_GCDfileonly_.gcd
GUP2869:fenix_D2_tactix/DescentMk1.gcd
GUP2887:forerunner/Forerunner645MusicBeta.zip
GUP2896:forerunner/Forerunner645BetaBeta.zip
GUP2897:forerunner/Forerunner645MusicBeta.zip
GUP2957:chipset_firmware/Type_M/GPSChipsetTypeM5_2957_Beta.zip
GUP2957:fenix_D2_tactix/fenix5Beta.zip
GUP2957:fenix_D2_tactix/fenix5PlusBeta.zip
GUP2957:fenix_D2_tactix/fenix5SBeta.zip
GUP2957:fenix_D2_tactix/fenix5SPlusBeta.zip
GUP2957:fenix_D2_tactix/fenix5XBeta.zip
GUP2957:fenix_D2_tactix/fenix5XPlusBeta.zip
GUP2957:fenix_D2_tactix/fenixChronosBeta_USB.zip
GUP2957:fenix_D2_tactix/fenixChronosBeta.zip
GUP2957:forerunner/Forerunner935Beta.zip
GUP2983:vivo/vivoactive3t.gcd
GUP2993:fenix_D2_tactix/DescentAPAC_GCDfileonly_.gcd
GUP3013:fenix_D2_tactix/fenix5PlusBeta.zip
GUP3013:fenix_D2_tactix/fenix5SPlusBeta.zip
GUP3013:fenix_D2_tactix/fenix5XPlusBeta.zip
GUP3014:fenix_D2_tactix/fenix5PlusBeta.zip
GUP3014:fenix_D2_tactix/fenix5SPlusBeta.zip
GUP3014:fenix_D2_tactix/fenix5XPlusBeta.zip
GUP3015:fenix_D2_tactix/fenix5PlusBeta.zip
GUP3015:fenix_D2_tactix/fenix5SPlusBeta.zip
GUP3015:fenix_D2_tactix/fenix5XPlusBeta.zip
GUP3044:baseball_golf/ApproachS20.rgn
GUP3044:golf_baseball/ApproachS20.rgn
GUP3078:forerunner/Forerunner245Beta.zip
GUP3079:forerunner/Forerunner245MBeta.zip
GUP3107:chipset_firmware/Type_S/GPSChipsetTypeS1_3107_Beta.zip
GUP3114:forerunner/Forerunner945Beta.zip
GUP3127:fenix_D2_tactix/InstinctBeta.gcd
GUP3127:fenix_D2_tactix/InstinctBeta.zip
GUP3127:fenix_D2_tactix/Instinct.gcd
GUP3153:fenix_D2_tactix/fenix5PlusBeta.zip
GUP3153:fenix_D2_tactix/fenix5SPlusBeta.zip
GUP3153:fenix_D2_tactix/fenix5XPlusBeta.zip
GUP3204:forerunner/Forerunner245Beta.zip
GUP3205:forerunner/Forerunner245MBeta.zip
GUP3252:MARQ/MARQAdventurerBeta.zip
GUP3252:MARQ/MARQAthleteBeta.zip
GUP3252:MARQ/MARQAviatorBeta.zip
GUP3252:MARQ/MARQBeta.zip
GUP3252:MARQ/MARQCaptainBeta.zip
GUP3252:MARQ/MARQCommanderBeta.zip
GUP3252:MARQ/MARQDriverBeta.zip
GUP3252:MARQ/MARQExpeditionBeta.zip
GUP3252:MARQ/MARQSensorHubBeta.zip
GUP3253:MARQ/MARQAdventurerBeta.zip
GUP3253:MARQ/MARQANT_BLE_BTBeta.zip
GUP3253:MARQ/MARQAthleteBeta.zip
GUP3253:MARQ/MARQAviatorBeta.zip
GUP3253:MARQ/MARQBeta.zip
GUP3253:MARQ/MARQCaptainBeta.zip
GUP3253:MARQ/MARQCommanderBeta.zip
GUP3253:MARQ/MARQDriverBeta.zip
GUP3253:MARQ/MARQExpeditionBeta.zip
GUP3292:fenix_D2_tactix/fenix6Beta.zip
GUP3292:fenix_D2_tactix/fenix6SBeta.zip
GUP3293:fenix_D2_tactix/fenix6ProBeta.zip
GUP3293:fenix_D2_tactix/fenix6S_6_6XPro_ANT_BLE_BTBeta.zip
GUP3293:fenix_D2_tactix/fenix6SProBeta.zip
GUP3293:fenix_D2_tactix/fenix6XProBeta.zip
GUP3294:fenix_D2_tactix/fenix6SBeta.zip
GUP3294:fenix_D2_tactix/fenix6SProBeta.zip
GUP3294:fenix_D2_tactix/fenix6SProSensorHubBeta.zip
GUP3294:fenix_D2_tactix/fenix6SSensorHubBeta.zip
GUP3295:fenix_D2_tactix/fenix6Beta.zip
GUP3295:fenix_D2_tactix/fenix6ProBeta.zip
GUP3295:fenix_D2_tactix/fenix6ProSensorHubBeta.zip
GUP3295:fenix_D2_tactix/fenix6SensorHubBeta.zip
GUP3296:fenix_D2_tactix/fenix6XProBeta.zip
GUP3296:fenix_D2_tactix/fenix6XProSensorHubBetaBeta.zip
GUP3296:fenix_D2_tactix/fenix6XProSensorHubBeta.zip
GUP3296:fenix_D2_tactix/fenix6XSensorHubBeta.zip
GUP3303:forerunner/Forerunner945Beta.zip
GUP3315:baseball_golf/ApproachS40.gcd
GUP3315:golf_baseball/ApproachS40.gcd
GUP3316:baseball_golf/ApproachS40.gcd
GUP3316:golf_baseball/ApproachS40.gcd
GUP3406:swim/Swim2BetaBeta.zip
GUP3506:chipset_firmware/Type_S/GPSChipsetTypeS1_3506_Beta.zip
GUP5423:vivo/vivosmartAPAC.rgn
GUP5424:vivo/vivosmartAPAC.rgn
GUP6182:edge/HMD_regionfileonly_.rgn
GUP6182:edge/VariaVision.rgn
GUP6182:misc_marine/GarminNautix_regionfileonly_.rgn
GUP7124:vivo/vivoactive3t.gcd

1728
components2.txt Normal file

File diff suppressed because it is too large Load Diff

40
find_hwids.py Executable file
View File

@ -0,0 +1,40 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Parses a binary file for 006-Bxxxx-xx or 006Bxxxxxx occurrances.
"""
import os.path
import re
import sys
from grmn import devices
FILE = sys.argv[1]
pattern = re.compile(rb"006-?B\d\d\d\d-?[0-9A-F]{2}")
print("Reading {} ...".format(FILE))
with open(FILE, "rb") as f:
data = f.read()
f.close()
matches = pattern.findall(data)
results = []
for i in matches:
i = i.decode("utf-8")
if len(i) == 10:
i = "{}-{}-{}".format(i[0:3], i[3:8], i[8:])
results.append(i)
results = sorted(set(results))
for r in results:
print(r, end="")
hw_id = r[5:9]
sub_id = r[10:]
device_name = devices.get_name(hw_id, sub_id)
if device_name:
print(" - {}".format(device_name), end="")
print()

View File

@ -18,6 +18,7 @@ optp.add_option("-c", "--changelog", action="store_true", dest="changelog", help
optp.add_option("-l", "--license", action="store_true", dest="license", help="also show license")
optp.add_option("-E", "--express", action="store_false", dest="webupdater", default=True, help="Only query Garmin Express")
optp.add_option("-W", "--webupdater", action="store_false", dest="express", default=True, help="Only query WebUpdater")
optp.add_option("-q", "--quiet", action="store_true", dest="quiet", default=False, help="Only output results (if any)")
optp.add_option("--id", dest="unit_id", help="Specify custom Unit ID")
optp.add_option("--code", action="append", dest="unlock_codes", metavar="UNLOCK_CODE", default=[], help="Specify map unlock codes")
optp.add_option("--devicexml", dest="devicexml", metavar="FILE", help="Use specified GarminDevice.xml (also implies -E)")
@ -83,10 +84,11 @@ for i, sku in enumerate(device_skus):
if len(sku) <= 4:
device_skus[i] = "006-B{:>04}-00".format(sku)
if device_skus[0][0:5] == "006-B":
if device_skus[0][0:5] == "006-B" and not opts.quiet:
primary_hwid = int(device_skus[0][5:9])
device_name = devices.DEVICES.get(primary_hwid, "Unknown device")
print("Device (guessed): {}".format(device_name))
primary_subid = device_skus[0][10:]
device_name = devices.get_name(primary_hwid, primary_subid, "Unknown device")
print("Device {:04d}-{:02} (guessed): {}".format(primary_hwid, primary_subid, device_name))
if opts.unit_id:
print("Custom Unit ID: {}".format(opts.unit_id))
@ -99,14 +101,18 @@ for uc in opts.unlock_codes:
results = []
if opts.express:
print("Querying Garmin Express ...", end="", flush=True)
if not opts.quiet:
print("Querying Garmin Express ...", end="", flush=True)
results += us.query_express(device_skus)
print(" done.")
if not opts.quiet:
print(" done.")
if opts.webupdater:
print("Querying Garmin WebUpdater ...", end="", flush=True)
if not opts.quiet:
print("Querying Garmin WebUpdater ...", end="", flush=True)
results += us.query_webupdater(device_skus)
print(" done.")
if not opts.quiet:
print(" done.")
for r in results:
print(r)

View File

@ -15,7 +15,8 @@ class ChkSum:
with open(filename, "rb") as f:
while True:
block = f.read(blocksize)
self.add(block)
if len(block) != 0:
self.add(block)
if print_progress:
print(".", end="", flush=True)
if len(block) < blocksize:

File diff suppressed because it is too large Load Diff

View File

@ -129,7 +129,7 @@ class RgnBin:
def __str__(self):
txt = "Binary payload, {} Bytes".format(len(self.payload))
if self.hwid:
txt += "\n - hw_id: 0x{:04x} / {:d} ({})".format(self.hwid, self.hwid, devices.DEVICES.get(self.hwid, RED + "Unknown device" + RESET))
txt += "\n - hw_id: 0x{:04x} / {:d} ({})".format(self.hwid, self.hwid, devices.get_name(self.hwid, 0, RED + "Unknown device" + RESET))
if self.version:
txt += "\n - Version: 0x{:04x} / {:d}".format(self.version, self.version)
cksum = ChkSum()

View File

@ -21,6 +21,13 @@ TLV_TYPES = {
0x051b: "Binary Region 1b",
0x052b: "Binary Region 2b",
0x0533: "Binary Region 33 (dskimg)",
0x0534: "Binary Region 34",
0x0535: "Binary Region 35",
0x0536: "Binary Region 36",
0x0537: "Binary Region 37",
0x0538: "Binary Region 38",
0x0539: "Binary Region 39",
0x053a: "Binary Region 3a",
0x0549: "Binary Region 49",
0x0555: "Binary Region 55 (fw)",
0x0556: "Binary Region 56",
@ -33,6 +40,8 @@ TLV_TYPES = {
0x0599: "Binary Region 99",
0x059e: "Binary Region 9e (resources)",
0x05a2: "Binary Region a2",
0x05a4: "Binary Region a4",
0x05a5: "Binary Region a5",
0x05ab: "Binary Region ab",
0x05f5: "Binary Region f5",
0x05f9: "Binary Region f9",
@ -47,9 +56,10 @@ TLV_TYPES = {
0xffff: "EOF marker",
}
BINARY_TLVS = [ 0x0008, 0x02bd, 0x0505, 0x0510, 0x051b, 0x052b, 0x0533, 0x0549, 0x0555, 0x0556,
0x0557, 0x0566, 0x057f, 0x0588, 0x0590, 0x0595, 0x0599, 0x059e, 0x05a2, 0x05ab,
0x05f5, 0x05f9, 0x05fa, 0x05fb, 0x05fc, 0x05fd, 0x05fe, 0x07d1, 0x07d2, 0x07d3 ]
BINARY_TLVS = [ 0x0008, 0x02bd, 0x0505, 0x0510, 0x051b, 0x052b, 0x0533, 0x0534, 0x0535, 0x0536,
0x0537, 0x0538, 0x0539, 0x053a, 0x0549, 0x0555, 0x0556, 0x0557, 0x0566, 0x057f,
0x0588, 0x0590, 0x0595, 0x0599, 0x059e, 0x05a2, 0x05a4, 0x05a5, 0x05ab, 0x05f5,
0x05f9, 0x05fa, 0x05fb, 0x05fc, 0x05fd, 0x05fe, 0x07d1, 0x07d2, 0x07d3 ]
class TLV:
def __init__(self, type_id: int, expected_length: int, value=None, offset: int=None):
@ -232,13 +242,13 @@ class TLV6(TLV):
def __init__(self, type_id: int, expected_length: int, value=None, offset: int=None):
super().__init__(type_id, expected_length, value, offset)
self.fids = []
self.format = ""
self.format = []
self.fields = []
def add_fid(self, fid: int):
fdef = self.FIELD_TYPES[fid]
self.fids.append(fid)
self.format += fdef[0]
self.format.append(fdef[0])
self.fields.append(fdef[1])
def parse(self):
@ -249,7 +259,7 @@ class TLV6(TLV):
raise Exception(RED + "Invalid TLV6 payload length!" + RESET)
self.fids = []
self.format = ""
self.format = []
self.fields = []
for i in range(0, len(self.value), 2):
fid = unpack("<H", self.value[i:i+2])[0]
@ -304,7 +314,7 @@ class TLV7(TLV):
#print("Got {} Bytes.".format(len(self.value)))
#print("Format: {}".format(self.tlv6.format))
#print(repr(self.value))
values = unpack("<" + self.tlv6.format, self.value)
values = unpack("<" + "".join(self.tlv6.format), self.value)
for i, v in enumerate(values):
fid = self.tlv6.fids[i]
self.attr.append((fid, v))
@ -320,7 +330,7 @@ class TLV7(TLV):
fdesc = self.tlv6.fields[i]
(fid, v) = pair
if fid == 0x1009:
txt += "\n - Field {:d} ({:04x}): {:>20}: 0x{:04x} / {:d} ({})".format(i+1, fid, fdesc, v, v, devices.DEVICES.get(v, RED + "Unknown device" + RESET))
txt += "\n - Field {:d} ({:04x}): {:>20}: 0x{:04x} / {:d} ({})".format(i+1, fid, fdesc, v, v, devices.get_name(v, 0, RED + "Unknown device" + RESET))
elif fid == 0x2015:
txt += "\n - Field {:d} ({:04x}): {:>20}: {} Bytes".format(i+1, fid, fdesc, v)
elif fid == 0x4007:
@ -331,7 +341,7 @@ class TLV7(TLV):
def set_binary_length(self, new_length):
self.tlv6.parse()
values = unpack("<" + self.tlv6.format, self.value)
values = unpack("<" + "".join(self.tlv6.format), self.value)
new_values = []
for i, v in enumerate(values):
fid = self.tlv6.fids[i]
@ -339,7 +349,7 @@ class TLV7(TLV):
new_values.append(new_length)
else:
new_values.append(v)
self.value = pack("<" + self.tlv6.format, *new_values)
self.value = pack("<" + "".join(self.tlv6.format), *new_values)
self.is_parsed = False
def dump(self):
@ -354,12 +364,15 @@ class TLV7(TLV):
continue
elif k[0:2] != "0x":
continue
numval = int(v, 0)
if v[0:2] == "0x":
numval = int(v, 0)
else:
numval = unhexlify(v)
new_values.append(numval)
if not self.tlv6.is_parsed:
# Make sure we have the structure analysed (need format attr)
self.tlv6.parse()
self.value = pack("<" + self.tlv6.format, *new_values)
self.value = pack("<" + "".join(self.tlv6.format), *new_values)
self.length = len(self.value)
class TLVbinary(TLV):
@ -386,7 +399,7 @@ class TLVbinary(TLV):
elif valtype == "Q":
valstr = "0x{:08x}".format(v)
elif valtype == "31s":
valstr = repr(v)
valstr = hexlify(v).decode("utf-8")
else:
valstr = "0x{:08x}".format(v)
data.append(("0x{:04x}".format(fid), valstr, fdesc))
@ -401,10 +414,18 @@ class TLVbinary0401(TLVbinary):
sku = self.value[10:20].decode("utf-8")
hwid = int(sku[4:8])
txt += "\n - SKU: {}-{}-{}".format(sku[0:3], sku[3:8], sku[8:10])
txt += "\n - hw_id: 0x{:04x} / {:d} ({})".format(hwid, hwid, devices.DEVICES.get(hwid, RED + "Unknown device" + RESET))
txt += "\n - hw_id: 0x{:04x} / {:d} ({})".format(hwid, hwid, devices.get_name(hwid, 0, RED + "Unknown device" + RESET))
txt += "\n - Version: 0x{:04x} / {:d}".format(version, version)
elif skuprobe == b"SW_I":
swistring = self.value[10:20].decode("utf-8")
payloadprobe = self.value[0x40:0x42]
txt += "\n - Type: Software Inventory ({}) - actual payload starts at 0x40".format(swistring)
if payloadprobe == b"PK":
txt += "\n Probably ZIP archive"
elif payloadprobe == b"Fi":
txt += "\n Probably CSV file"
else:
txt += "\n - Unknown header format (0x{:04x} / 0x{:04x})".format(hdr1, hdr2)
txt += "\n - Unknown header format (0x{})".format(hexlify(skuprobe).decode("utf-8"))
#if not self.is_parsed:
# self.parse()
return txt

View File

@ -13,7 +13,8 @@ import requests
PROTO_API_GETALLUNITSOFTWAREUPDATES_URL = "http://omt.garmin.com/Rce/ProtobufApi/SoftwareUpdateService/GetAllUnitSoftwareUpdates"
WEBUPDATER_SOFTWAREUPDATE_URL = "https://www.garmin.com/support/WUSoftwareUpdate.jsp"
GRMN_CLIENT_VERSION = "6.17.0.0"
GRMN_CLIENT_VERSION = "6.19.4.0"
class UpdateInfo:
def __init__(self):
@ -106,6 +107,7 @@ class UpdateInfo:
def __repr__(self):
return "[{}] {} {} {}".format(self.source, self.sku, self.device_name, self.fw_version)
class UpdateServer:
def __init__(self):
@ -235,7 +237,7 @@ class UpdateServer:
def get_unit_updates(self, device_xml):
query = GetAllUnitSoftwareUpdates_pb2.GetAllUnitSoftwareUpdates()
query.client_data.client = "express"
query.client_data.language ="en_GB"
query.client_data.language = "en_GB"
query.client_data.client_platform = "Windows"
query.client_data.client_platform_version = "1000 "
query.device_xml = device_xml

70
list_kenwood_updates.py Normal file
View File

@ -0,0 +1,70 @@
#!/usr/bin/env python3
import requests
import re
BASE_URL = "https://kenwood.garmin.com"
MAIN_URL = "/kenwood/site/filterHeadUnitList"
reqs = requests.Session()
def get_url(url, params = {}, outfile = None):
global reqs
print("Now fetching {} ...".format(url))
#print(repr(params))
req = reqs.get(url, params=params)
page = req.content
#if outfile:
# with open(outfile, "wb") as f:
# f.write(page)
page = str(page).replace("\\n", "").replace("\\r", "").replace("\\t", "")
page = re.sub(r"\s+", " ", page)
return page
devlist = get_url(BASE_URL + MAIN_URL, {"regionKey": 0, "seriesKey": 0}, "kenmain.html")
devices = re.findall(r"<div class=\"item\"> <a href=\"(.*?)\">(.*?)</a> </div>", devlist)
print("Found {} devices.".format(len(devices)))
#print(repr(devices))
f = open("kenfiles.txt", "wt")
f.write("# Download with: wget -x -nc -i kenfiles.txt\n\n")
for dev in devices:
(url, devname) = dev
print("Checking updates for {} ...".format(devname.strip()))
url = url.strip()
(url, paramstr) = url.split("?", 1)
parampairs = paramstr.split("&")
params = {}
for p in parampairs:
(key, val) = p.split("=", 1)
params[key] = val
params["origin"] = "productUpdate" # gets added via JavaScript?
devpage = get_url(BASE_URL + url, params, "kendev.html")
updateLink = re.search(r"(/kenwood/site/softwareUpdates.*?)\\", devpage)
if not updateLink:
print("### No updates for {} found.".format(devname))
continue
updateLink = updateLink.group(1)
#print(repr(updateLink))
updpage = get_url(BASE_URL + updateLink, {}, "kenupd.html")
#print(repr(updpage))
links = re.findall(r"(https?://.*?)[\\\"']", updpage)
f.write("# {}\n".format(devname))
for l in links:
if l.endswith(("favicon.ico", "garmin.png", "termsOfUse.htm", "privacy-statement", "/us/")):
continue
#print(repr(l))
f.write(l)
f.write("\n")
f.write("\n")
f.close()

View File

@ -6,13 +6,19 @@
from grmn import devices
import sys
largest_gap = -1
gap_counter = 0
last_id = 0
missing = []
for i in range(0, 9999):
if i in devices.DEVICES:
last_id = i
if gap_counter > largest_gap:
largest_gap = gap_counter
gap_counter = 0
continue
missing.append(i)
gap_counter += 1
missing_count = 0
cur_line = []
@ -20,36 +26,37 @@ queue = []
for i in range(0, last_id+1):
if i % 10 == 0:
if len(cur_line) + len(queue) > 15:
print("./get_updates.py {}".format(" ".join(cur_line)))
print("./get_updates.py -q {}".format(" ".join(cur_line)))
cur_line = queue
else:
cur_line += queue
queue = []
if not i in missing:
if i not in missing:
continue
queue.append("{:04}".format(i))
missing_count += 1
cur_line += queue
if len(cur_line) > 0:
print("./get_updates.py {}".format(" ".join(cur_line)))
print("./get_updates.py -q {}".format(" ".join(cur_line)))
known_count = len(devices.DEVICES)
print()
print("{} unknown ids.".format(missing_count))
print("Last known id is: {:04}".format(last_id))
print("{} known, {} unknown ids. Last known id is: {:04d}".format(known_count, missing_count, last_id))
print("Largest gap is: {}".format(largest_gap))
if len(sys.argv) > 1:
print("-" * 100)
print("Here are some possible future ids:")
print("./get_updates.py", end="")
print("./get_updates.py -q", end="")
cur_line = 0
for i in range(last_id + 1, last_id + 200):
for i in range(last_id + 1, last_id + 300):
if i % 10 == 0 and cur_line > 5:
print()
print("./get_updates.py", end="")
print("./get_updates.py -q", end="")
cur_line = 0
print(" {:04}".format(i), end="")
cur_line += 1