VAG fix
This commit is contained in:
+104
-90
@@ -2,98 +2,112 @@
|
||||
//! Data is stored as binary to avoid plain-text keys in config.
|
||||
//! Format: "KATK" magic, n_entries (u16 LE), then per entry: type_id (u32 LE), key (u64 LE), then "VAG " + 64 bytes.
|
||||
//! Key bytes are little-endian (LSB first); the resulting u64 matches reference hex (MSB-first notation).
|
||||
//! All manufacture keys from the encrypted keystore list are included for current and future protocols.
|
||||
//! Type 20 (Star Line) is also included so KeyStore.star_line_mf_key is populated from the same key as SL_A2-A4.
|
||||
//! Generated from REFERENCES/mf_keys.txt by scripts/build_keystore_from_mf_keys.py
|
||||
|
||||
/// Blob: KATK + 41 entries (30 original + 11 Pandora new keys) + VAG 64 bytes.
|
||||
/// Blob: KATK + 83 entries + VAG 64 bytes.
|
||||
#[rustfmt::skip]
|
||||
pub const KEYSTORE_BLOB: &[u8] = &[
|
||||
b'K', b'A', b'T', b'K',
|
||||
0x29, 0x00, // 41 entries (30 original + 11 Pandora)
|
||||
// type 10 KIA
|
||||
0x0A, 0x00, 0x00, 0x00, 0xDB, 0x5C, 0xAA, 0x8D, 0xFC, 0xDF, 0xF5, 0xA8,
|
||||
// type 11 KIAV6A
|
||||
0x0B, 0x00, 0x00, 0x00, 0xFC, 0x23, 0xAA, 0x80, 0xA8, 0x64, 0x86, 0x63,
|
||||
// type 12 KIAV6B
|
||||
0x0C, 0x00, 0x00, 0x00, 0xF4, 0x2B, 0xA2, 0x88, 0xA0, 0x6C, 0x8E, 0x6B,
|
||||
// type 13 KIAV5
|
||||
0x0D, 0x00, 0x00, 0x00, 0x30, 0x30, 0x45, 0x4B, 0x52, 0x46, 0x54, 0x53,
|
||||
// type 1 Alligator
|
||||
0x01, 0x00, 0x00, 0x00, 0x78, 0x55, 0x6C, 0x62, 0xB2, 0xD4, 0xF3, 0xEE,
|
||||
// type 2 Mongoose
|
||||
0x02, 0x00, 0x00, 0x00, 0xBA, 0xA0, 0xC1, 0xA7, 0xB2, 0xE7, 0xA5, 0xD5,
|
||||
// type 1 SL_A6-A9/Tomahawk_9010
|
||||
0x01, 0x00, 0x00, 0x00, 0x03, 0x12, 0x30, 0x08, 0x57, 0x04, 0x04, 0x55,
|
||||
// type 1 Pantera
|
||||
0x01, 0x00, 0x00, 0x00, 0x65, 0x6E, 0x79, 0x64, 0x4E, 0x47, 0x41, 0x4D,
|
||||
// type 1 SL_A2-A4
|
||||
0x01, 0x00, 0x00, 0x00, 0xDA, 0x78, 0xFE, 0xF8, 0x9B, 0xF8, 0xF7, 0x9B,
|
||||
// type 1 Cenmax_St-5
|
||||
0x01, 0x00, 0x00, 0x00, 0xFC, 0xED, 0xCF, 0xF7, 0xA8, 0xFB, 0xFB, 0xAA,
|
||||
// type 1 SL_B6,B9_dop
|
||||
0x01, 0x00, 0x00, 0x00, 0xFE, 0xDC, 0xBA, 0x2A, 0xB3, 0xD4, 0xE5, 0xF6,
|
||||
// type 1 Harpoon
|
||||
0x01, 0x00, 0x00, 0x00, 0x64, 0x08, 0x07, 0x64, 0x07, 0x01, 0x87, 0x25,
|
||||
// type 1 Tomahawk_TZ-9030
|
||||
0x01, 0x00, 0x00, 0x00, 0x25, 0x87, 0x01, 0x07, 0x64, 0x07, 0x08, 0x64,
|
||||
// type 1 Tomahawk_Z,X_3-5
|
||||
0x01, 0x00, 0x00, 0x00, 0xEF, 0xCD, 0xAB, 0x2A, 0x3B, 0x4D, 0x5E, 0x6F,
|
||||
// type 1 Cenmax_St-7
|
||||
0x01, 0x00, 0x00, 0x00, 0x2B, 0x75, 0xAC, 0x80, 0xB9, 0x46, 0xB4, 0x66,
|
||||
// type 1 Sheriff
|
||||
0x01, 0x00, 0x00, 0x00, 0x78, 0x94, 0x18, 0xA9, 0x94, 0x25, 0x33, 0x12,
|
||||
// type 1 Pantera_CLK
|
||||
0x01, 0x00, 0x00, 0x00, 0x14, 0x0A, 0x61, 0x82, 0x0A, 0x85, 0x30, 0x41,
|
||||
// type 1 Cenmax
|
||||
0x01, 0x00, 0x00, 0x00, 0x56, 0x34, 0x12, 0x90, 0x78, 0x56, 0x34, 0x12,
|
||||
// type 1 Alligator_S-275
|
||||
0x01, 0x00, 0x00, 0x00, 0xF9, 0x84, 0x62, 0x67, 0xB7, 0x5C, 0x36, 0x54,
|
||||
// type 2 Guard_RF-311A
|
||||
0x02, 0x00, 0x00, 0x00, 0x47, 0x88, 0x19, 0xEB, 0xC2, 0xDA, 0x61, 0x19,
|
||||
// type 1 Partisan_RX
|
||||
0x01, 0x00, 0x00, 0x00, 0x1E, 0xF1, 0x28, 0x70, 0xA1, 0x12, 0x3C, 0xAD,
|
||||
// type 1 APS-1100_APS-2550
|
||||
0x01, 0x00, 0x00, 0x00, 0x28, 0x47, 0x33, 0x56, 0x50, 0x2C, 0x73, 0x68,
|
||||
// type 1 Pantera_XS/Jaguar
|
||||
0x01, 0x00, 0x00, 0x00, 0x27, 0x46, 0x33, 0x56, 0x50, 0x2C, 0x73, 0x67,
|
||||
// type 0 Teco
|
||||
0x00, 0x00, 0x00, 0x00, 0xD7, 0xD1, 0x26, 0xF1, 0xE8, 0x26, 0x15, 0xB5,
|
||||
// type 0 Leopard
|
||||
0x00, 0x00, 0x00, 0x00, 0x46, 0x8A, 0xD8, 0xE7, 0x33, 0xDC, 0x67, 0x10,
|
||||
// type 0 Faraon
|
||||
0x00, 0x00, 0x00, 0x00, 0xF5, 0x87, 0x22, 0xB6, 0x38, 0x64, 0x32, 0x8A,
|
||||
// type 0 Reff
|
||||
0x00, 0x00, 0x00, 0x00, 0x8A, 0x41, 0xC2, 0x7D, 0x55, 0xFA, 0x12, 0x69,
|
||||
// type 0 ZX-730-750-1055
|
||||
0x00, 0x00, 0x00, 0x00, 0x96, 0x15, 0x35, 0x27, 0xAB, 0x7A, 0x1A, 0xB5,
|
||||
// type 20 Star Line (same key as SL_A2-A4, for KeyStore.star_line_mf_key)
|
||||
0x14, 0x00, 0x00, 0x00, 0xDA, 0x78, 0xFE, 0xF8, 0x9B, 0xF8, 0xF7, 0x9B,
|
||||
// type 1 Pandora_M101
|
||||
0x01, 0x00, 0x00, 0x00, 0x99, 0xAE, 0x17, 0x2A, 0x9A, 0x7D, 0x1D, 0x38,
|
||||
// type 1 Pandora_PRO
|
||||
0x01, 0x00, 0x00, 0x00, 0xAC, 0x97, 0x15, 0x28, 0x7B, 0x97, 0x36, 0x1B,
|
||||
// type 1 Pandora_PRO2
|
||||
0x01, 0x00, 0x00, 0x00, 0xB9, 0x19, 0x2B, 0x39, 0x9A, 0x1E, 0xAF, 0x7E,
|
||||
// type 1 Pandora_SUBARU
|
||||
0x01, 0x00, 0x00, 0x00, 0x95, 0x79, 0x22, 0xA4, 0x34, 0xB9, 0x19, 0x35,
|
||||
// type 1 Pandora_SUZUKI
|
||||
0x01, 0x00, 0x00, 0x00, 0x73, 0xBD, 0xED, 0xDC, 0x06, 0xEF, 0x52, 0x25,
|
||||
// type 1 Pandora_DEA
|
||||
0x01, 0x00, 0x00, 0x00, 0x77, 0xB9, 0xE9, 0xD8, 0x02, 0xEB, 0x56, 0x21,
|
||||
// type 1 Pandora_GIBIDI
|
||||
0x01, 0x00, 0x00, 0x00, 0x76, 0xBA, 0xEA, 0xDB, 0x01, 0xEA, 0x55, 0x22,
|
||||
// type 1 Pandora_MCODE
|
||||
0x01, 0x00, 0x00, 0x00, 0x75, 0xBB, 0xEB, 0xDA, 0x00, 0xE9, 0x54, 0x23,
|
||||
// type 1 Pandora_Unknown_1
|
||||
0x01, 0x00, 0x00, 0x00, 0x72, 0xBC, 0xEC, 0xDD, 0x07, 0xEE, 0x53, 0x24,
|
||||
// type 1 Pandora_Unknown_2
|
||||
0x01, 0x00, 0x00, 0x00, 0x70, 0xBE, 0xEE, 0xDF, 0x05, 0xEC, 0x51, 0x26,
|
||||
// type 1 Pandora_Test_Debug_2
|
||||
// magic + n_entries
|
||||
0x4B, 0x41, 0x54, 0x4B, 0x53, 0x00,
|
||||
// entries 0..
|
||||
0x06, 0x00, 0x00, 0x00, 0x58, 0x47, 0x30, 0x23, 0x00, 0x00, 0x00, 0x00,
|
||||
0x07, 0x00, 0x00, 0x00, 0x48, 0x57, 0x20, 0x33, 0x00, 0x00, 0x00, 0x00,
|
||||
0x01, 0x00, 0x00, 0x00, 0x40, 0x30, 0x20, 0x10, 0x04, 0x03, 0x02, 0x01,
|
||||
0x02, 0x00, 0x00, 0x00, 0xEF, 0xCD, 0xAB, 0x89, 0x67, 0x45, 0x23, 0x01,
|
||||
0x01, 0x00, 0x00, 0x00, 0xEF, 0xCD, 0xAB, 0x89, 0x67, 0x45, 0x32, 0x01,
|
||||
b'V', b'A', b'G', b' ',
|
||||
// VAG raw 64 bytes
|
||||
0x01, 0x37, 0x6C, 0x86, 0xAD, 0xAB, 0xCC, 0x43, 0x07, 0x4D, 0xE8, 0x59, 0xC1, 0x2F, 0x36, 0xAB,
|
||||
0x02, 0x37, 0x7C, 0x65, 0xCE, 0xDC, 0x42, 0xEA, 0xA4, 0x53, 0xE8, 0x61, 0xD9, 0xB7, 0x20, 0xFC,
|
||||
0x03, 0x8A, 0xA3, 0x7B, 0x1E, 0x56, 0x1F, 0x83, 0x84, 0xB6, 0x19, 0xC5, 0x2E, 0x0A, 0x3F, 0xD7,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x01, 0x00, 0x00, 0x00, 0x03, 0x19, 0x98, 0xAA, 0xAB, 0xDA, 0xAE, 0x05,
|
||||
0x01, 0x00, 0x00, 0x00, 0x03, 0x19, 0x98, 0xAA, 0xAB, 0xDA, 0xAE, 0x08,
|
||||
0x00, 0x00, 0x00, 0x00, 0x46, 0x8A, 0xD8, 0xE7, 0x33, 0xDC, 0x67, 0x10,
|
||||
// entries 8..
|
||||
0x01, 0x00, 0x00, 0x00, 0x78, 0x94, 0x18, 0xA9, 0x94, 0x25, 0x33, 0x12,
|
||||
0x01, 0x00, 0x00, 0x00, 0x56, 0x34, 0x12, 0x90, 0x78, 0x56, 0x34, 0x12,
|
||||
0x02, 0x00, 0x00, 0x00, 0x14, 0xDF, 0xB1, 0xA9, 0x44, 0x05, 0x8B, 0x18,
|
||||
0x02, 0x00, 0x00, 0x00, 0x47, 0x88, 0x19, 0xEB, 0xC2, 0xDA, 0x61, 0x19,
|
||||
0x01, 0x00, 0x00, 0x00, 0xAC, 0x97, 0x15, 0x28, 0x7B, 0x97, 0x36, 0x1B,
|
||||
0x02, 0x00, 0x00, 0x00, 0x44, 0x6F, 0x38, 0x9D, 0xE5, 0xBB, 0x7D, 0x20,
|
||||
0x01, 0x00, 0x00, 0x00, 0x77, 0xB9, 0xE9, 0xD8, 0x02, 0xEB, 0x56, 0x21,
|
||||
0x01, 0x00, 0x00, 0x00, 0x76, 0xBA, 0xEA, 0xDB, 0x01, 0xEA, 0x55, 0x22,
|
||||
// entries 16..
|
||||
0x01, 0x00, 0x00, 0x00, 0x75, 0xBB, 0xEB, 0xDA, 0x00, 0xE9, 0x54, 0x23,
|
||||
0x01, 0x00, 0x00, 0x00, 0x72, 0xBC, 0xEC, 0xDD, 0x07, 0xEE, 0x53, 0x24,
|
||||
0x01, 0x00, 0x00, 0x00, 0x73, 0xBD, 0xED, 0xDC, 0x06, 0xEF, 0x52, 0x25,
|
||||
0x01, 0x00, 0x00, 0x00, 0x64, 0x08, 0x07, 0x64, 0x07, 0x01, 0x87, 0x25,
|
||||
0x01, 0x00, 0x00, 0x00, 0x02, 0x19, 0x99, 0x02, 0x19, 0x99, 0x26, 0x26,
|
||||
0x01, 0x00, 0x00, 0x00, 0x70, 0xBE, 0xEE, 0xDF, 0x05, 0xEC, 0x51, 0x26,
|
||||
0x0B, 0x00, 0x00, 0x00, 0x35, 0x08, 0x7C, 0x11, 0x9B, 0x3A, 0x19, 0x27,
|
||||
0x01, 0x00, 0x00, 0x00, 0x71, 0x44, 0x79, 0x7D, 0x30, 0x7B, 0x31, 0x30,
|
||||
// entries 24..
|
||||
0x01, 0x00, 0x00, 0x00, 0x61, 0x39, 0x4E, 0x30, 0x65, 0x38, 0x4C, 0x31,
|
||||
0x01, 0x00, 0x00, 0x00, 0x95, 0x79, 0x22, 0xA4, 0x34, 0xB9, 0x19, 0x35,
|
||||
0x01, 0x00, 0x00, 0x00, 0xE0, 0xDF, 0xF4, 0xA5, 0xAC, 0xAB, 0x2B, 0x35,
|
||||
0x02, 0x00, 0x00, 0x00, 0x9B, 0xA1, 0x6D, 0xF7, 0xB4, 0x1A, 0x46, 0x3E,
|
||||
0x01, 0x00, 0x00, 0x00, 0x99, 0xAE, 0x17, 0x2A, 0x9A, 0x7D, 0x1D, 0x38,
|
||||
0x01, 0x00, 0x00, 0x00, 0x01, 0x02, 0x03, 0x04, 0x10, 0x20, 0x30, 0x40,
|
||||
0x01, 0x00, 0x00, 0x00, 0x14, 0x0A, 0x61, 0x82, 0x0A, 0x85, 0x30, 0x41,
|
||||
0x01, 0x00, 0x00, 0x00, 0x30, 0x38, 0x39, 0x31, 0x58, 0x45, 0x4C, 0x41,
|
||||
// entries 32..
|
||||
0x02, 0x00, 0x00, 0x00, 0x83, 0x45, 0x13, 0x83, 0x30, 0x90, 0x92, 0x42,
|
||||
0x01, 0x00, 0x00, 0x00, 0x69, 0x47, 0x73, 0x65, 0x65, 0x73, 0x72, 0x47,
|
||||
0x01, 0x00, 0x00, 0x00, 0x53, 0x52, 0x54, 0x73, 0x6D, 0x6C, 0x4D, 0x48,
|
||||
0x03, 0x00, 0x00, 0x00, 0x76, 0x4F, 0x64, 0x55, 0x7A, 0x4D, 0x6D, 0x4C,
|
||||
0x01, 0x00, 0x00, 0x00, 0x65, 0x6E, 0x79, 0x64, 0x4E, 0x47, 0x41, 0x4D,
|
||||
0x0D, 0x00, 0x00, 0x00, 0x30, 0x30, 0x45, 0x4B, 0x52, 0x46, 0x54, 0x53,
|
||||
0x05, 0x00, 0x00, 0x00, 0x14, 0x8C, 0x61, 0x69, 0x76, 0x6C, 0x69, 0x53,
|
||||
0x01, 0x00, 0x00, 0x00, 0xF9, 0x84, 0x62, 0x67, 0xB7, 0x5C, 0x36, 0x54,
|
||||
// entries 40..
|
||||
0x01, 0x00, 0x00, 0x00, 0x4E, 0x49, 0x43, 0x45, 0x53, 0x4D, 0x52, 0x54,
|
||||
0x01, 0x00, 0x00, 0x00, 0x03, 0x12, 0x30, 0x08, 0x57, 0x04, 0x04, 0x55,
|
||||
0x02, 0x00, 0x00, 0x00, 0xFF, 0xCA, 0x76, 0x94, 0x22, 0x68, 0x27, 0x57,
|
||||
0x0B, 0x00, 0x00, 0x00, 0xFC, 0x23, 0xAA, 0x80, 0xA8, 0x64, 0x86, 0x63,
|
||||
0x01, 0x00, 0x00, 0x00, 0x25, 0x87, 0x01, 0x07, 0x64, 0x07, 0x08, 0x64,
|
||||
0x01, 0x00, 0x00, 0x00, 0x2B, 0x75, 0xAC, 0x80, 0xB9, 0x46, 0xB4, 0x66,
|
||||
0x01, 0x00, 0x00, 0x00, 0x27, 0x46, 0x33, 0x56, 0x50, 0x2C, 0x73, 0x67,
|
||||
0x01, 0x00, 0x00, 0x00, 0x28, 0x47, 0x33, 0x56, 0x50, 0x2C, 0x73, 0x68,
|
||||
// entries 48..
|
||||
0x00, 0x00, 0x00, 0x00, 0x8A, 0x41, 0xC2, 0x7D, 0x55, 0xFA, 0x12, 0x69,
|
||||
0x0C, 0x00, 0x00, 0x00, 0xF4, 0x2B, 0xA2, 0x88, 0xA0, 0x6C, 0x8E, 0x6B,
|
||||
0x09, 0x00, 0x00, 0x00, 0xD2, 0x3E, 0xC6, 0x15, 0x48, 0xAE, 0xB6, 0x6E,
|
||||
0x01, 0x00, 0x00, 0x00, 0xEF, 0xCD, 0xAB, 0x2A, 0x3B, 0x4D, 0x5E, 0x6F,
|
||||
0x02, 0x00, 0x00, 0x00, 0xBF, 0xDC, 0x6E, 0x37, 0xD4, 0xEE, 0xCB, 0x7B,
|
||||
0x01, 0x00, 0x00, 0x00, 0xB9, 0x19, 0x2B, 0x39, 0x9A, 0x1E, 0xAF, 0x7E,
|
||||
0x02, 0x00, 0x00, 0x00, 0x23, 0x12, 0x94, 0x84, 0x35, 0xF4, 0x55, 0x84,
|
||||
0x02, 0x00, 0x00, 0x00, 0x30, 0x4E, 0x39, 0x61, 0x78, 0x42, 0x07, 0x86,
|
||||
// entries 56..
|
||||
0x01, 0x00, 0x00, 0x00, 0xB7, 0x03, 0x79, 0x53, 0x59, 0x6E, 0x14, 0x89,
|
||||
0x00, 0x00, 0x00, 0x00, 0xF5, 0x87, 0x22, 0xB6, 0x38, 0x64, 0x32, 0x8A,
|
||||
0x01, 0x00, 0x00, 0x00, 0x9B, 0x2C, 0x9C, 0xC9, 0x36, 0x8C, 0x63, 0x96,
|
||||
0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xA5, 0x5A, 0x65, 0x04, 0x98,
|
||||
0x01, 0x00, 0x00, 0x00, 0xDA, 0x78, 0xFE, 0xF8, 0x9B, 0xF8, 0xF7, 0x9B,
|
||||
0x02, 0x00, 0x00, 0x00, 0x5C, 0xE2, 0xB8, 0x47, 0x3A, 0xFD, 0x61, 0x9C,
|
||||
0x01, 0x00, 0x00, 0x00, 0xA7, 0x2B, 0x31, 0xCF, 0x53, 0x81, 0xA0, 0x9D,
|
||||
0x0A, 0x00, 0x00, 0x00, 0xDB, 0x5C, 0xAA, 0x8D, 0xFC, 0xDF, 0xF5, 0xA8,
|
||||
// entries 64..
|
||||
0x02, 0x00, 0x00, 0x00, 0x97, 0x52, 0x65, 0xB7, 0x32, 0x85, 0x74, 0xA9,
|
||||
0x00, 0x00, 0x00, 0x00, 0xA1, 0xB5, 0x89, 0x21, 0xA3, 0xA7, 0x38, 0xAA,
|
||||
0x01, 0x00, 0x00, 0x00, 0xFC, 0xED, 0xCF, 0xF7, 0xA8, 0xFB, 0xFB, 0xAA,
|
||||
0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x59, 0x27, 0xBB, 0x35, 0xAC,
|
||||
0x01, 0x00, 0x00, 0x00, 0x1E, 0xF1, 0x28, 0x70, 0xA1, 0x12, 0x3C, 0xAD,
|
||||
0x02, 0x00, 0x00, 0x00, 0x39, 0x71, 0xCD, 0x8C, 0x5A, 0x62, 0xE5, 0xB3,
|
||||
0x00, 0x00, 0x00, 0x00, 0xD7, 0xD1, 0x26, 0xF1, 0xE8, 0x26, 0x15, 0xB5,
|
||||
0x00, 0x00, 0x00, 0x00, 0x96, 0x15, 0x35, 0x27, 0xAB, 0x7A, 0x1A, 0xB5,
|
||||
// entries 72..
|
||||
0x01, 0x00, 0x00, 0x00, 0xCC, 0x7E, 0xF9, 0xAA, 0xDD, 0x9E, 0xEE, 0xBB,
|
||||
0x04, 0x00, 0x00, 0x00, 0xD2, 0x3E, 0xC6, 0xB5, 0x48, 0xAE, 0xB6, 0xCE,
|
||||
0x02, 0x00, 0x00, 0x00, 0xBA, 0xA0, 0xC1, 0xA7, 0xB2, 0xE7, 0xA5, 0xD5,
|
||||
0x01, 0x00, 0x00, 0x00, 0x78, 0x55, 0x6C, 0x62, 0xB2, 0xD4, 0xF3, 0xEE,
|
||||
0x02, 0x00, 0x00, 0x00, 0x55, 0x7E, 0x64, 0xC8, 0x52, 0xE5, 0xA3, 0xF1,
|
||||
0x01, 0x00, 0x00, 0x00, 0xFE, 0xDC, 0xBA, 0x2A, 0xB3, 0xD4, 0xE5, 0xF6,
|
||||
0x02, 0x00, 0x00, 0x00, 0xAC, 0xFA, 0x01, 0x00, 0x60, 0x05, 0xAC, 0xFA,
|
||||
0x02, 0x00, 0x00, 0x00, 0xAC, 0xFA, 0x02, 0x00, 0x60, 0x05, 0xAC, 0xFA,
|
||||
// entries 80..
|
||||
0x01, 0x00, 0x00, 0x00, 0xDA, 0xF0, 0x34, 0xDA, 0xFA, 0x12, 0xDA, 0xFA,
|
||||
0x01, 0x00, 0x00, 0x00, 0x20, 0x03, 0x37, 0xD5, 0x2C, 0xE2, 0x4D, 0xFC,
|
||||
0x14, 0x00, 0x00, 0x00, 0xDA, 0x78, 0xFE, 0xF8, 0x9B, 0xF8, 0xF7, 0x9B,
|
||||
// VAG tag + 64 bytes
|
||||
0x56, 0x41, 0x47, 0x20, 0x01, 0x37, 0x6C, 0x86, 0xAD, 0xAB, 0xCC, 0x43,
|
||||
0x07, 0x4D, 0xE8, 0x59, 0xC1, 0x2F, 0x36, 0xAB, 0x02, 0x37, 0x7C, 0x65,
|
||||
0xCE, 0xDC, 0x42, 0xEA, 0xA4, 0x53, 0xE8, 0x61, 0xD9, 0xB7, 0x20, 0xFC,
|
||||
0x03, 0x8A, 0xA3, 0x7B, 0x1E, 0x56, 0x1F, 0x83, 0x84, 0xB6, 0x19, 0xC5,
|
||||
0x2E, 0x0A, 0x3F, 0xD7, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
];
|
||||
|
||||
+82
-8
@@ -10,15 +10,89 @@ const KEELOQ_MF_TYPES: &[u32] = &[0, 1, 2, 10, 20];
|
||||
|
||||
/// Names for each key entry in the blob (same order as entries before VAG). Used for fallback decode display.
|
||||
const KEY_ENTRY_NAMES: &[&str] = &[
|
||||
"KIA", "KIAV6A", "KIAV6B", "KIAV5", "Alligator", "Mongoose",
|
||||
"SL_A6-A9/Tomahawk_9010", "Pantera", "SL_A2-A4", "Cenmax_St-5", "SL_B6,B9_dop",
|
||||
"Harpoon", "Tomahawk_TZ-9030", "Tomahawk_Z,X_3-5", "Cenmax_St-7", "Sheriff",
|
||||
"Pantera_CLK", "Cenmax", "Alligator_S-275", "Guard_RF-311A", "Partisan_RX",
|
||||
"APS-1100_APS-2550", "Pantera_XS/Jaguar", "Teco", "Leopard", "Faraon", "Reff",
|
||||
"ZX-730-750-1055", "Star Line",
|
||||
"Pandora_M101", "Pandora_PRO", "Pandora_PRO2", "Pandora_SUBARU", "Pandora_SUZUKI",
|
||||
"Pandora_DEA", "Pandora_GIBIDI", "Pandora_MCODE", "Pandora_Unknown_1", "Pandora_Unknown_2",
|
||||
"KGB/Subaru",
|
||||
"Magic_2",
|
||||
"IronLogic",
|
||||
"Stilmatic",
|
||||
"Pandora_Test_Debug_2",
|
||||
"Rosh",
|
||||
"Dea_Mio",
|
||||
"Leopard",
|
||||
"Sheriff",
|
||||
"Cenmax",
|
||||
"Centurion",
|
||||
"Guard_RF-311A",
|
||||
"Pandora_PRO",
|
||||
"Cardin_S449",
|
||||
"Pandora_DEA",
|
||||
"Pandora_GIBIDI",
|
||||
"Pandora_MCODE",
|
||||
"Pandora_Unknown_1",
|
||||
"Pandora_SUZUKI",
|
||||
"Harpoon",
|
||||
"Gibidi",
|
||||
"Pandora_Unknown_2",
|
||||
"Jarolift",
|
||||
"KEY",
|
||||
"Novoferm",
|
||||
"Pandora_SUBARU",
|
||||
"Pecinin",
|
||||
"Merlin",
|
||||
"Pandora_M101",
|
||||
"IL-100(Smart)",
|
||||
"Pantera_CLK",
|
||||
"Kingates_Stylo4k",
|
||||
"Monarch",
|
||||
"Aprimatic",
|
||||
"NICE_MHOUSE",
|
||||
"BFT",
|
||||
"Pantera",
|
||||
"KIAV5",
|
||||
"FAAC_SLH",
|
||||
"Alligator_S-275",
|
||||
"NICE_Smilo",
|
||||
"SL_A6-A9/Tomahawk_9010",
|
||||
"Motorline",
|
||||
"KIAV6A",
|
||||
"Tomahawk_TZ-9030",
|
||||
"Cenmax_St-7",
|
||||
"Pantera_XS/Jaguar",
|
||||
"APS-1100_APS-2550",
|
||||
"Reff",
|
||||
"KIAV6B",
|
||||
"Beninca_ARC",
|
||||
"Tomahawk_Z,X_3-5",
|
||||
"Sommer",
|
||||
"Pandora_PRO2",
|
||||
"DoorHan",
|
||||
"EcoStar",
|
||||
"JCM_Tech",
|
||||
"Faraon",
|
||||
"Came_Space",
|
||||
"Magic_4",
|
||||
"SL_A2-A4",
|
||||
"Elmes_Poland",
|
||||
"Normstahl",
|
||||
"KIA",
|
||||
"GSN",
|
||||
"Mutanco_Mutancode",
|
||||
"Cenmax_St-5",
|
||||
"Magic_3",
|
||||
"Partisan_RX",
|
||||
"Steelmate",
|
||||
"Teco",
|
||||
"ZX-730-750-1055",
|
||||
"Jolly_Motors",
|
||||
"Beninca",
|
||||
"Mongoose",
|
||||
"Alligator",
|
||||
"Comunello",
|
||||
"SL_B6,B9_dop",
|
||||
"FAAC_RC,XT",
|
||||
"Genius_Bravo",
|
||||
"Rossi",
|
||||
"DTM_Neo",
|
||||
"Star Line",
|
||||
];
|
||||
|
||||
const MAGIC: &[u8; 4] = b"KATK";
|
||||
|
||||
+308
-308
@@ -1,308 +1,308 @@
|
||||
//! Protocol decoders and encoders for various keyfob systems.
|
||||
//!
|
||||
//! Protocols are aligned with the ProtoPirate reference (`REFERENCES/ProtoPirate/protocols/`).
|
||||
//! Each decoder processes level+duration pairs from the demodulator and optionally supports
|
||||
//! encoding (replay). Shared pieces: [common], [keeloq_common], [keys], [aut64].
|
||||
//!
|
||||
//! **Manchester decoding**: Ford, Fiat, and common each have separate Manchester state machines
|
||||
//! (FordV0ManchesterState, FiatV0ManchesterState, CommonManchesterState in common.rs). They are
|
||||
//! not reused across protocols. Event conventions match the reference per protocol (e.g. Kia V5
|
||||
//! opposite polarity; Fiat/Ford/common use Flipper-style: level ? ShortLow : ShortHigh).
|
||||
|
||||
mod common;
|
||||
pub mod keeloq_common;
|
||||
mod keeloq_generic;
|
||||
#[allow(dead_code)]
|
||||
pub mod aut64;
|
||||
#[allow(dead_code)]
|
||||
pub mod keys;
|
||||
mod kia_v0;
|
||||
mod kia_v1;
|
||||
mod kia_v2;
|
||||
mod kia_v3_v4;
|
||||
mod kia_v5;
|
||||
mod kia_v6;
|
||||
mod subaru;
|
||||
mod ford_v0;
|
||||
mod vag;
|
||||
mod fiat_v0;
|
||||
mod suzuki;
|
||||
mod scher_khan;
|
||||
mod star_line;
|
||||
mod psa;
|
||||
|
||||
pub use common::DecodedSignal;
|
||||
|
||||
use crate::capture::Capture;
|
||||
use crate::radio::demodulator::LevelDuration;
|
||||
|
||||
/// Protocol timing constants
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
#[allow(dead_code)]
|
||||
pub struct ProtocolTiming {
|
||||
/// Short pulse duration in µs
|
||||
pub te_short: u32,
|
||||
/// Long pulse duration in µs
|
||||
pub te_long: u32,
|
||||
/// Tolerance for timing matching in µs
|
||||
pub te_delta: u32,
|
||||
/// Minimum bit count for valid decode
|
||||
pub min_count_bit: usize,
|
||||
}
|
||||
|
||||
/// Trait for protocol decoders
|
||||
///
|
||||
/// Each protocol implements a state machine that processes level+duration pairs.
|
||||
pub trait ProtocolDecoder: Send + Sync {
|
||||
/// Get the protocol name
|
||||
fn name(&self) -> &'static str;
|
||||
|
||||
/// Get timing constants
|
||||
#[allow(dead_code)]
|
||||
fn timing(&self) -> ProtocolTiming;
|
||||
|
||||
/// Get supported frequencies in Hz
|
||||
fn supported_frequencies(&self) -> &[u32];
|
||||
|
||||
/// Reset the decoder state machine
|
||||
fn reset(&mut self);
|
||||
|
||||
/// Feed a level+duration pair to the decoder
|
||||
/// Returns Some(DecodedSignal) when a complete valid signal is decoded
|
||||
fn feed(&mut self, level: bool, duration_us: u32) -> Option<DecodedSignal>;
|
||||
|
||||
/// Check if this protocol supports encoding
|
||||
fn supports_encoding(&self) -> bool;
|
||||
|
||||
/// Encode a signal with the given button command
|
||||
fn encode(&self, decoded: &DecodedSignal, button: u8) -> Option<Vec<LevelDuration>>;
|
||||
}
|
||||
|
||||
/// Registry of all supported protocols
|
||||
pub struct ProtocolRegistry {
|
||||
decoders: Vec<Box<dyn ProtocolDecoder>>,
|
||||
}
|
||||
|
||||
impl ProtocolRegistry {
|
||||
/// Create a new protocol registry with all built-in protocols
|
||||
pub fn new() -> Self {
|
||||
let decoders: Vec<Box<dyn ProtocolDecoder>> = vec![
|
||||
// Kia protocols
|
||||
Box::new(kia_v0::KiaV0Decoder::new()),
|
||||
Box::new(kia_v1::KiaV1Decoder::new()),
|
||||
Box::new(kia_v2::KiaV2Decoder::new()),
|
||||
Box::new(kia_v3_v4::KiaV3V4Decoder::new()),
|
||||
Box::new(kia_v5::KiaV5Decoder::new()),
|
||||
Box::new(kia_v6::KiaV6Decoder::new()),
|
||||
// Other protocols (Ford before Subaru so 250/500µs Ford keyfobs decode as Ford)
|
||||
Box::new(ford_v0::FordV0Decoder::new()),
|
||||
Box::new(subaru::SubaruDecoder::new()),
|
||||
Box::new(vag::VagDecoder::new()),
|
||||
Box::new(fiat_v0::FiatV0Decoder::new()),
|
||||
Box::new(suzuki::SuzukiDecoder::new()),
|
||||
Box::new(scher_khan::ScherKhanDecoder::new()),
|
||||
Box::new(star_line::StarLineDecoder::new()),
|
||||
Box::new(psa::PsaDecoder::new()),
|
||||
];
|
||||
|
||||
Self { decoders }
|
||||
}
|
||||
|
||||
/// Process level+duration pairs from demodulator
|
||||
/// Returns decoded signal info if any protocol matches.
|
||||
/// Tries normal polarity first, then inverted polarity (so OOK captures where
|
||||
/// carrier-on is recorded as LOW can still decode as Fiat/Ford etc.).
|
||||
pub fn process_signal(&mut self, pairs: &[LevelDuration], frequency: u32) -> Option<(String, DecodedSignal)> {
|
||||
// Try normal polarity first
|
||||
if let Some(result) = self.process_signal_inner(pairs, frequency, false) {
|
||||
return Some(result);
|
||||
}
|
||||
// Try inverted polarity (capture LOW = RF HIGH)
|
||||
if let Some(result) = self.process_signal_inner(pairs, frequency, true) {
|
||||
return Some(result);
|
||||
}
|
||||
// No known protocol: try KeeLoq generic (uses keeloq_common with every keystore key)
|
||||
keeloq_generic::try_decode(pairs, frequency)
|
||||
}
|
||||
|
||||
/// ProtoPirate-style streaming decode: feed the whole stream, on each decode record and reset decoders, continue.
|
||||
/// Returns one entry per decode: (protocol name, decoded signal, pairs that produced it).
|
||||
/// Tries normal polarity first; if no decodes, runs again with inverted polarity.
|
||||
pub fn process_signal_stream(
|
||||
&mut self,
|
||||
pairs: &[LevelDuration],
|
||||
frequency: u32,
|
||||
) -> Vec<(String, DecodedSignal, Vec<LevelDuration>)> {
|
||||
let with_normal = self.process_signal_stream_inner(pairs, frequency, false);
|
||||
if !with_normal.is_empty() {
|
||||
return with_normal;
|
||||
}
|
||||
self.process_signal_stream_inner(pairs, frequency, true)
|
||||
}
|
||||
|
||||
/// Inner streaming decode with optional level inversion.
|
||||
fn process_signal_stream_inner(
|
||||
&mut self,
|
||||
pairs: &[LevelDuration],
|
||||
frequency: u32,
|
||||
invert_level: bool,
|
||||
) -> Vec<(String, DecodedSignal, Vec<LevelDuration>)> {
|
||||
let mut out = Vec::new();
|
||||
let mut segment_start = 0_usize;
|
||||
|
||||
for decoder in &mut self.decoders {
|
||||
decoder.reset();
|
||||
}
|
||||
|
||||
for (i, pair) in pairs.iter().enumerate() {
|
||||
let level = if invert_level { !pair.level } else { pair.level };
|
||||
let duration_us = pair.duration_us;
|
||||
|
||||
let mut hit = None;
|
||||
for decoder in &mut self.decoders {
|
||||
let freq_supported = decoder
|
||||
.supported_frequencies()
|
||||
.iter()
|
||||
.any(|&f| {
|
||||
let diff = if f > frequency { f - frequency } else { frequency - f };
|
||||
diff < (f / 50)
|
||||
});
|
||||
if !freq_supported {
|
||||
continue;
|
||||
}
|
||||
if let Some(decoded) = decoder.feed(level, duration_us) {
|
||||
hit = Some((decoder.name().to_string(), decoded));
|
||||
break;
|
||||
}
|
||||
}
|
||||
if let Some((name, decoded)) = hit {
|
||||
let segment: Vec<LevelDuration> = pairs[segment_start..=i]
|
||||
.iter()
|
||||
.map(|p| LevelDuration::new(p.level, p.duration_us))
|
||||
.collect();
|
||||
out.push((name, decoded, segment));
|
||||
for d in &mut self.decoders {
|
||||
d.reset();
|
||||
}
|
||||
segment_start = i + 1;
|
||||
}
|
||||
}
|
||||
|
||||
out
|
||||
}
|
||||
|
||||
/// Inner decode: feed pairs (with optional level flip) to decoders that support this frequency.
|
||||
fn process_signal_inner(
|
||||
&mut self,
|
||||
pairs: &[LevelDuration],
|
||||
frequency: u32,
|
||||
invert_level: bool,
|
||||
) -> Option<(String, DecodedSignal)> {
|
||||
for decoder in &mut self.decoders {
|
||||
decoder.reset();
|
||||
}
|
||||
|
||||
for pair in pairs {
|
||||
let level = if invert_level { !pair.level } else { pair.level };
|
||||
let duration_us = pair.duration_us;
|
||||
|
||||
for decoder in &mut self.decoders {
|
||||
let freq_supported = decoder
|
||||
.supported_frequencies()
|
||||
.iter()
|
||||
.any(|&f| {
|
||||
let diff = if f > frequency { f - frequency } else { frequency - f };
|
||||
diff < (f / 50) // 2% tolerance
|
||||
});
|
||||
|
||||
if !freq_supported {
|
||||
continue;
|
||||
}
|
||||
|
||||
if let Some(decoded) = decoder.feed(level, duration_us) {
|
||||
return Some((decoder.name().to_string(), decoded));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
|
||||
/// Try to decode a capture (for compatibility with old interface)
|
||||
#[allow(dead_code)]
|
||||
pub fn try_decode(&mut self, capture: &Capture) -> Option<(String, DecodedSignal)> {
|
||||
// Convert raw pairs to LevelDuration and process
|
||||
if capture.raw_pairs.is_empty() {
|
||||
return None;
|
||||
}
|
||||
|
||||
let pairs: Vec<LevelDuration> = capture.raw_pairs
|
||||
.iter()
|
||||
.map(|p| LevelDuration::new(p.level, p.duration_us))
|
||||
.collect();
|
||||
|
||||
self.process_signal(&pairs, capture.frequency)
|
||||
}
|
||||
|
||||
/// Get a decoder by name
|
||||
pub fn get(&self, name: &str) -> Option<&dyn ProtocolDecoder> {
|
||||
self.decoders
|
||||
.iter()
|
||||
.find(|d| d.name().eq_ignore_ascii_case(name))
|
||||
.map(|d| d.as_ref())
|
||||
}
|
||||
|
||||
/// List all protocol names
|
||||
#[allow(dead_code)]
|
||||
pub fn list_protocols(&self) -> Vec<&'static str> {
|
||||
self.decoders.iter().map(|d| d.name()).collect()
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for ProtocolRegistry {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
|
||||
/// Helper macro for duration comparison (matches protopirate's DURATION_DIFF)
|
||||
#[macro_export]
|
||||
macro_rules! duration_diff {
|
||||
($actual:expr, $expected:expr) => {
|
||||
if $actual > $expected {
|
||||
$actual - $expected
|
||||
} else {
|
||||
$expected - $actual
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::export::flipper::import_sub_raw;
|
||||
use crate::radio::LevelDuration;
|
||||
use std::path::Path;
|
||||
|
||||
#[test]
|
||||
fn ford_v0_decodes_imports_ford_unlock_sub() {
|
||||
let path = Path::new("IMPORTS/FORD/3_unlock_ford.sub");
|
||||
if !path.exists() {
|
||||
eprintln!("Skip: {:?} not found (run from crate root)", path);
|
||||
return;
|
||||
}
|
||||
let (freq, raw_pairs) = import_sub_raw(path).unwrap();
|
||||
let pairs: Vec<LevelDuration> = raw_pairs
|
||||
.iter()
|
||||
.map(|p| LevelDuration::new(p.level, p.duration_us))
|
||||
.collect();
|
||||
let mut reg = ProtocolRegistry::new();
|
||||
let results = reg.process_signal_stream(&pairs, freq);
|
||||
let ford_decodes: Vec<_> = results.iter().filter(|(name, _, _)| *name == "Ford V0").collect();
|
||||
assert!(
|
||||
!ford_decodes.is_empty(),
|
||||
"expected at least one Ford V0 decode from 3_unlock_ford.sub, got: {:?}",
|
||||
results.iter().map(|(n, _, _)| n.as_str()).collect::<Vec<_>>()
|
||||
);
|
||||
}
|
||||
}
|
||||
//! Protocol decoders and encoders for various keyfob systems.
|
||||
//!
|
||||
//! Protocols are aligned with the ProtoPirate reference (`REFERENCES/ProtoPirate/protocols/`).
|
||||
//! Each decoder processes level+duration pairs from the demodulator and optionally supports
|
||||
//! encoding (replay). Shared pieces: [common], [keeloq_common], [keys], [aut64].
|
||||
//!
|
||||
//! **Manchester decoding**: Ford, Fiat, and common each have separate Manchester state machines
|
||||
//! (FordV0ManchesterState, FiatV0ManchesterState, CommonManchesterState in common.rs). They are
|
||||
//! not reused across protocols. Event conventions match the reference per protocol (e.g. Kia V5
|
||||
//! opposite polarity; Fiat/Ford/common use Flipper-style: level ? ShortLow : ShortHigh).
|
||||
|
||||
mod common;
|
||||
pub mod keeloq_common;
|
||||
mod keeloq_generic;
|
||||
#[allow(dead_code)]
|
||||
pub mod aut64;
|
||||
#[allow(dead_code)]
|
||||
pub mod keys;
|
||||
mod kia_v0;
|
||||
mod kia_v1;
|
||||
mod kia_v2;
|
||||
mod kia_v3_v4;
|
||||
mod kia_v5;
|
||||
mod kia_v6;
|
||||
mod subaru;
|
||||
mod ford_v0;
|
||||
mod vag;
|
||||
mod fiat_v0;
|
||||
mod suzuki;
|
||||
mod scher_khan;
|
||||
mod star_line;
|
||||
mod psa;
|
||||
|
||||
pub use common::DecodedSignal;
|
||||
|
||||
use crate::capture::Capture;
|
||||
use crate::radio::demodulator::LevelDuration;
|
||||
|
||||
/// Protocol timing constants
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
#[allow(dead_code)]
|
||||
pub struct ProtocolTiming {
|
||||
/// Short pulse duration in µs
|
||||
pub te_short: u32,
|
||||
/// Long pulse duration in µs
|
||||
pub te_long: u32,
|
||||
/// Tolerance for timing matching in µs
|
||||
pub te_delta: u32,
|
||||
/// Minimum bit count for valid decode
|
||||
pub min_count_bit: usize,
|
||||
}
|
||||
|
||||
/// Trait for protocol decoders
|
||||
///
|
||||
/// Each protocol implements a state machine that processes level+duration pairs.
|
||||
pub trait ProtocolDecoder: Send + Sync {
|
||||
/// Get the protocol name
|
||||
fn name(&self) -> &'static str;
|
||||
|
||||
/// Get timing constants
|
||||
#[allow(dead_code)]
|
||||
fn timing(&self) -> ProtocolTiming;
|
||||
|
||||
/// Get supported frequencies in Hz
|
||||
fn supported_frequencies(&self) -> &[u32];
|
||||
|
||||
/// Reset the decoder state machine
|
||||
fn reset(&mut self);
|
||||
|
||||
/// Feed a level+duration pair to the decoder
|
||||
/// Returns Some(DecodedSignal) when a complete valid signal is decoded
|
||||
fn feed(&mut self, level: bool, duration_us: u32) -> Option<DecodedSignal>;
|
||||
|
||||
/// Check if this protocol supports encoding
|
||||
fn supports_encoding(&self) -> bool;
|
||||
|
||||
/// Encode a signal with the given button command
|
||||
fn encode(&self, decoded: &DecodedSignal, button: u8) -> Option<Vec<LevelDuration>>;
|
||||
}
|
||||
|
||||
/// Registry of all supported protocols
|
||||
pub struct ProtocolRegistry {
|
||||
decoders: Vec<Box<dyn ProtocolDecoder>>,
|
||||
}
|
||||
|
||||
impl ProtocolRegistry {
|
||||
/// Create a new protocol registry with all built-in protocols
|
||||
pub fn new() -> Self {
|
||||
let decoders: Vec<Box<dyn ProtocolDecoder>> = vec![
|
||||
// Kia protocols
|
||||
Box::new(kia_v0::KiaV0Decoder::new()),
|
||||
Box::new(kia_v1::KiaV1Decoder::new()),
|
||||
Box::new(kia_v2::KiaV2Decoder::new()),
|
||||
Box::new(kia_v3_v4::KiaV3V4Decoder::new()),
|
||||
Box::new(kia_v5::KiaV5Decoder::new()),
|
||||
Box::new(kia_v6::KiaV6Decoder::new()),
|
||||
// Other protocols (Ford before Subaru so 250/500µs Ford keyfobs decode as Ford)
|
||||
Box::new(ford_v0::FordV0Decoder::new()),
|
||||
Box::new(subaru::SubaruDecoder::new()),
|
||||
Box::new(vag::VagDecoder::new()),
|
||||
Box::new(fiat_v0::FiatV0Decoder::new()),
|
||||
Box::new(suzuki::SuzukiDecoder::new()),
|
||||
Box::new(scher_khan::ScherKhanDecoder::new()),
|
||||
Box::new(star_line::StarLineDecoder::new()),
|
||||
Box::new(psa::PsaDecoder::new()),
|
||||
];
|
||||
|
||||
Self { decoders }
|
||||
}
|
||||
|
||||
/// Process level+duration pairs from demodulator
|
||||
/// Returns decoded signal info if any protocol matches.
|
||||
/// Tries normal polarity first, then inverted polarity (so OOK captures where
|
||||
/// carrier-on is recorded as LOW can still decode as Fiat/Ford etc.).
|
||||
pub fn process_signal(&mut self, pairs: &[LevelDuration], frequency: u32) -> Option<(String, DecodedSignal)> {
|
||||
// Try normal polarity first
|
||||
if let Some(result) = self.process_signal_inner(pairs, frequency, false) {
|
||||
return Some(result);
|
||||
}
|
||||
// Try inverted polarity (capture LOW = RF HIGH)
|
||||
if let Some(result) = self.process_signal_inner(pairs, frequency, true) {
|
||||
return Some(result);
|
||||
}
|
||||
// No known protocol: try KeeLoq generic (uses keeloq_common with every keystore key)
|
||||
keeloq_generic::try_decode(pairs, frequency)
|
||||
}
|
||||
|
||||
/// ProtoPirate-style streaming decode: feed the whole stream, on each decode record and reset decoders, continue.
|
||||
/// Returns one entry per decode: (protocol name, decoded signal, pairs that produced it).
|
||||
/// Tries normal polarity first; if no decodes, runs again with inverted polarity.
|
||||
pub fn process_signal_stream(
|
||||
&mut self,
|
||||
pairs: &[LevelDuration],
|
||||
frequency: u32,
|
||||
) -> Vec<(String, DecodedSignal, Vec<LevelDuration>)> {
|
||||
let with_normal = self.process_signal_stream_inner(pairs, frequency, false);
|
||||
if !with_normal.is_empty() {
|
||||
return with_normal;
|
||||
}
|
||||
self.process_signal_stream_inner(pairs, frequency, true)
|
||||
}
|
||||
|
||||
/// Inner streaming decode with optional level inversion.
|
||||
fn process_signal_stream_inner(
|
||||
&mut self,
|
||||
pairs: &[LevelDuration],
|
||||
frequency: u32,
|
||||
invert_level: bool,
|
||||
) -> Vec<(String, DecodedSignal, Vec<LevelDuration>)> {
|
||||
let mut out = Vec::new();
|
||||
let mut segment_start = 0_usize;
|
||||
|
||||
for decoder in &mut self.decoders {
|
||||
decoder.reset();
|
||||
}
|
||||
|
||||
for (i, pair) in pairs.iter().enumerate() {
|
||||
let level = if invert_level { !pair.level } else { pair.level };
|
||||
let duration_us = pair.duration_us;
|
||||
|
||||
let mut hit = None;
|
||||
for decoder in &mut self.decoders {
|
||||
let freq_supported = decoder
|
||||
.supported_frequencies()
|
||||
.iter()
|
||||
.any(|&f| {
|
||||
let diff = if f > frequency { f - frequency } else { frequency - f };
|
||||
diff < (f / 50)
|
||||
});
|
||||
if !freq_supported {
|
||||
continue;
|
||||
}
|
||||
if let Some(decoded) = decoder.feed(level, duration_us) {
|
||||
hit = Some((decoder.name().to_string(), decoded));
|
||||
break;
|
||||
}
|
||||
}
|
||||
if let Some((name, decoded)) = hit {
|
||||
let segment: Vec<LevelDuration> = pairs[segment_start..=i]
|
||||
.iter()
|
||||
.map(|p| LevelDuration::new(p.level, p.duration_us))
|
||||
.collect();
|
||||
out.push((name, decoded, segment));
|
||||
for d in &mut self.decoders {
|
||||
d.reset();
|
||||
}
|
||||
segment_start = i + 1;
|
||||
}
|
||||
}
|
||||
|
||||
out
|
||||
}
|
||||
|
||||
/// Inner decode: feed pairs (with optional level flip) to decoders that support this frequency.
|
||||
fn process_signal_inner(
|
||||
&mut self,
|
||||
pairs: &[LevelDuration],
|
||||
frequency: u32,
|
||||
invert_level: bool,
|
||||
) -> Option<(String, DecodedSignal)> {
|
||||
for decoder in &mut self.decoders {
|
||||
decoder.reset();
|
||||
}
|
||||
|
||||
for pair in pairs {
|
||||
let level = if invert_level { !pair.level } else { pair.level };
|
||||
let duration_us = pair.duration_us;
|
||||
|
||||
for decoder in &mut self.decoders {
|
||||
let freq_supported = decoder
|
||||
.supported_frequencies()
|
||||
.iter()
|
||||
.any(|&f| {
|
||||
let diff = if f > frequency { f - frequency } else { frequency - f };
|
||||
diff < (f / 50) // 2% tolerance
|
||||
});
|
||||
|
||||
if !freq_supported {
|
||||
continue;
|
||||
}
|
||||
|
||||
if let Some(decoded) = decoder.feed(level, duration_us) {
|
||||
return Some((decoder.name().to_string(), decoded));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
|
||||
/// Try to decode a capture (for compatibility with old interface)
|
||||
#[allow(dead_code)]
|
||||
pub fn try_decode(&mut self, capture: &Capture) -> Option<(String, DecodedSignal)> {
|
||||
// Convert raw pairs to LevelDuration and process
|
||||
if capture.raw_pairs.is_empty() {
|
||||
return None;
|
||||
}
|
||||
|
||||
let pairs: Vec<LevelDuration> = capture.raw_pairs
|
||||
.iter()
|
||||
.map(|p| LevelDuration::new(p.level, p.duration_us))
|
||||
.collect();
|
||||
|
||||
self.process_signal(&pairs, capture.frequency)
|
||||
}
|
||||
|
||||
/// Get a decoder by name
|
||||
pub fn get(&self, name: &str) -> Option<&dyn ProtocolDecoder> {
|
||||
self.decoders
|
||||
.iter()
|
||||
.find(|d| d.name().eq_ignore_ascii_case(name))
|
||||
.map(|d| d.as_ref())
|
||||
}
|
||||
|
||||
/// List all protocol names
|
||||
#[allow(dead_code)]
|
||||
pub fn list_protocols(&self) -> Vec<&'static str> {
|
||||
self.decoders.iter().map(|d| d.name()).collect()
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for ProtocolRegistry {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
|
||||
/// Helper macro for duration comparison (matches protopirate's DURATION_DIFF)
|
||||
#[macro_export]
|
||||
macro_rules! duration_diff {
|
||||
($actual:expr, $expected:expr) => {
|
||||
if $actual > $expected {
|
||||
$actual - $expected
|
||||
} else {
|
||||
$expected - $actual
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::export::flipper::import_sub_raw;
|
||||
use crate::radio::LevelDuration;
|
||||
use std::path::Path;
|
||||
|
||||
#[test]
|
||||
fn ford_v0_decodes_imports_ford_unlock_sub() {
|
||||
let path = Path::new("IMPORTS/FORD/3_unlock_ford.sub");
|
||||
if !path.exists() {
|
||||
eprintln!("Skip: {:?} not found (run from crate root)", path);
|
||||
return;
|
||||
}
|
||||
let (freq, raw_pairs) = import_sub_raw(path).unwrap();
|
||||
let pairs: Vec<LevelDuration> = raw_pairs
|
||||
.iter()
|
||||
.map(|p| LevelDuration::new(p.level, p.duration_us))
|
||||
.collect();
|
||||
let mut reg = ProtocolRegistry::new();
|
||||
let results = reg.process_signal_stream(&pairs, freq);
|
||||
let ford_decodes: Vec<_> = results.iter().filter(|(name, _, _)| *name == "Ford V0").collect();
|
||||
assert!(
|
||||
!ford_decodes.is_empty(),
|
||||
"expected at least one Ford V0 decode from 3_unlock_ford.sub, got: {:?}",
|
||||
results.iter().map(|(n, _, _)| n.as_str()).collect::<Vec<_>>()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
+10
-8
@@ -899,14 +899,14 @@ impl ProtocolDecoder for VagDecoder {
|
||||
TE_SHORT_12 - duration
|
||||
};
|
||||
|
||||
// Reference: (300-duration)<=79 or (duration-300)<80 -> count pair (check_preamble1_prev)
|
||||
if te_diff < TE_DELTA_12 {
|
||||
// Check previous pulse
|
||||
let prev_diff = if self.te_last > TE_SHORT_12 {
|
||||
self.te_last - TE_SHORT_12
|
||||
} else {
|
||||
TE_SHORT_12 - self.te_last
|
||||
};
|
||||
if prev_diff <= TE_DELTA_12 {
|
||||
if prev_diff <= REF_RESET_DELTA {
|
||||
self.te_last = duration;
|
||||
self.header_count += 1;
|
||||
return None;
|
||||
@@ -915,12 +915,13 @@ impl ProtocolDecoder for VagDecoder {
|
||||
return None;
|
||||
}
|
||||
|
||||
// Check for gap (end of preamble): 600µs ±79, te_last 300±79 (ref check_gap1)
|
||||
// Duration not near 300: ref checks for 600µs gap (Preamble1->Data1), then reset
|
||||
// ref: set step=Reset; if header_count>=201 then duration=|duration-600|; if duration<=79 and te_last 300±79 -> Data1
|
||||
if self.header_count >= 201 {
|
||||
let gap_diff = if duration > TE_LONG_12 {
|
||||
duration - TE_LONG_12
|
||||
} else {
|
||||
let gap_diff = if duration < TE_LONG_12 {
|
||||
TE_LONG_12 - duration
|
||||
} else {
|
||||
duration - TE_LONG_12
|
||||
};
|
||||
if gap_diff <= REF_GAP1_DELTA {
|
||||
let prev_diff = if self.te_last > TE_SHORT_12 {
|
||||
@@ -952,9 +953,10 @@ impl ProtocolDecoder for VagDecoder {
|
||||
TE_LONG_12 - duration
|
||||
};
|
||||
|
||||
let event = if short_diff <= TE_DELTA_12 {
|
||||
// Reference Data1: short 300±79 (221..380), long 600±79 (521..680)
|
||||
let event = if short_diff <= REF_RESET_DELTA {
|
||||
Some(if level { ManchesterEvent::ShortLow } else { ManchesterEvent::ShortHigh })
|
||||
} else if long_diff <= TE_DELTA_12 {
|
||||
} else if long_diff <= REF_RESET_DELTA {
|
||||
Some(if level { ManchesterEvent::LongLow } else { ManchesterEvent::LongHigh })
|
||||
} else {
|
||||
None
|
||||
|
||||
Reference in New Issue
Block a user