From f0d4210d19f3637c79aa0a010b098d7aa390202a Mon Sep 17 00:00:00 2001 From: Zinong Li <131403964+zinongli@users.noreply.github.com> Date: Wed, 24 Sep 2025 19:14:43 +0400 Subject: [PATCH 1/3] NFC: Amusement IC Card Parser (FeliCa Lite & Lite-S) (#4259) * type/brand parse * debug * decryptions * corrections * comments * AIC access code decryption and CRC check * easier than we thought! only acc_code[0] == 5 needs parsing * finishing touches * can't support serial number decode * Revert "can't support serial number decode" This reverts commit 7761b31bcf7effb97e608578ab1e090625078784. * serial number CAN be decrypted * format * print fix * fix CRC check * delete unnecessary line * linter fixes --------- Co-authored-by: hedger Co-authored-by: hedger --- applications/main/nfc/application.fam | 9 + .../main/nfc/plugins/supported_cards/aic.c | 678 ++++++++++++++++++ 2 files changed, 687 insertions(+) create mode 100644 applications/main/nfc/plugins/supported_cards/aic.c diff --git a/applications/main/nfc/application.fam b/applications/main/nfc/application.fam index 6342d18bf..a9c8808a8 100644 --- a/applications/main/nfc/application.fam +++ b/applications/main/nfc/application.fam @@ -274,3 +274,12 @@ App( requires=["nfc"], sources=["plugins/supported_cards/banapass.c"], ) + +App( + appid="aic_parser", + apptype=FlipperAppType.PLUGIN, + entry_point="aic_plugin_ep", + targets=["f7"], + requires=["nfc"], + sources=["plugins/supported_cards/aic.c"], +) diff --git a/applications/main/nfc/plugins/supported_cards/aic.c b/applications/main/nfc/plugins/supported_cards/aic.c new file mode 100644 index 000000000..c8f303eb9 --- /dev/null +++ b/applications/main/nfc/plugins/supported_cards/aic.c @@ -0,0 +1,678 @@ +// https://sega.bsnk.me/allnet/amusement_ic/ + +#include "nfc_supported_card_plugin.h" +#include +#include +#include +#include +#include +#include + +#define TAG "Amusement IC" + +#define N_TABLES 8 +#define ITERATION_ADD 5 + +static const uint8_t s_box[9][256] = { + {0x24, 0x3c, 0xba, 0x36, 0xe3, 0x85, 0xa4, 0xd0, 0x93, 0x43, 0x73, 0xb9, 0x70, 0x6e, 0xc9, + 0xf1, 0x10, 0x0e, 0x9b, 0x2c, 0x97, 0xe7, 0x0b, 0x63, 0x6c, 0x29, 0x20, 0xfe, 0x86, 0xf3, + 0xe1, 0xf5, 0xf6, 0x9f, 0xb6, 0x16, 0x04, 0x7b, 0x8f, 0xab, 0xb1, 0x39, 0x2a, 0x1b, 0xeb, + 0x5c, 0xa8, 0xac, 0x38, 0x11, 0x12, 0x5f, 0x89, 0x3e, 0x7d, 0xca, 0xec, 0x53, 0xdb, 0x6d, + 0x1e, 0xd2, 0x81, 0x78, 0x96, 0x46, 0xff, 0xf9, 0x54, 0x1c, 0x28, 0x7a, 0x4f, 0xd3, 0xc0, + 0xdc, 0xc1, 0x6a, 0xf2, 0xbc, 0xcb, 0x57, 0xfd, 0x4a, 0xe4, 0xf0, 0xb2, 0xc7, 0x95, 0x40, + 0x62, 0x52, 0x41, 0xe2, 0xad, 0x49, 0xa6, 0xb5, 0x1f, 0x02, 0xc8, 0xda, 0x92, 0xe5, 0xb0, + 0xc5, 0x64, 0x76, 0x48, 0x21, 0xde, 0x0f, 0x45, 0x58, 0x4c, 0xdf, 0xa7, 0x84, 0xcf, 0xd5, + 0x15, 0x4e, 0x27, 0x80, 0x6b, 0x7f, 0xfc, 0x44, 0x71, 0x47, 0x22, 0xdd, 0x30, 0x0c, 0xa1, + 0x3b, 0xe0, 0x37, 0xa0, 0x35, 0x23, 0x90, 0x32, 0x74, 0xbf, 0x8d, 0xc2, 0xea, 0xd6, 0x50, + 0xbb, 0xd9, 0xc4, 0x83, 0xb4, 0x31, 0x68, 0x55, 0x8b, 0x5d, 0xf4, 0x72, 0x18, 0xb7, 0xef, + 0xf7, 0x98, 0x2d, 0x01, 0x03, 0x61, 0xcc, 0x0a, 0x8e, 0x13, 0x00, 0xe8, 0x14, 0xaf, 0x09, + 0x51, 0x75, 0xa5, 0x2f, 0x1d, 0x0d, 0x65, 0x8a, 0xcd, 0x66, 0x07, 0x3d, 0x05, 0xd8, 0x4b, + 0xe9, 0x9d, 0x99, 0x7c, 0x91, 0xd1, 0xb8, 0x19, 0xc3, 0x2b, 0x42, 0x69, 0x88, 0xc6, 0x79, + 0x17, 0x3a, 0x4d, 0x5b, 0xa2, 0x5a, 0xfb, 0x25, 0x3f, 0xbd, 0xf8, 0xed, 0xce, 0xe6, 0x87, + 0xa3, 0x26, 0xa9, 0x2e, 0xbe, 0x94, 0x08, 0x7e, 0x67, 0x60, 0x8c, 0x9c, 0x5e, 0x6f, 0xb3, + 0x9e, 0x06, 0xfa, 0x82, 0xae, 0xee, 0x59, 0x77, 0xd7, 0x1a, 0x9a, 0x34, 0xd4, 0x56, 0xaa, + 0x33}, + {0xf9, 0x81, 0x7c, 0x00, 0xb9, 0x30, 0x37, 0xd5, 0x90, 0x51, 0x6e, 0xf0, 0xb2, 0x06, 0xfb, + 0xcd, 0x39, 0x14, 0x5f, 0xf8, 0x1b, 0xc7, 0x4a, 0x82, 0x70, 0x8c, 0x92, 0x1d, 0xea, 0xc3, + 0x4e, 0xc8, 0x12, 0xe6, 0xb6, 0x10, 0xd3, 0x2f, 0x95, 0x84, 0x25, 0x42, 0xa5, 0x72, 0xe5, + 0x8f, 0x55, 0xef, 0x86, 0xa2, 0x53, 0xae, 0xed, 0x26, 0x20, 0x47, 0xde, 0x78, 0x68, 0x28, + 0xe4, 0x45, 0xcc, 0x35, 0xbc, 0x3e, 0xbb, 0x8e, 0xc0, 0xbe, 0x34, 0xaf, 0xa6, 0x09, 0x64, + 0x01, 0x7b, 0x44, 0xf5, 0xf3, 0xb1, 0x3f, 0xa4, 0xb3, 0x32, 0x80, 0xc9, 0x4f, 0x6f, 0x40, + 0xbf, 0x21, 0x4c, 0x74, 0xe1, 0x11, 0x5e, 0xeb, 0x3d, 0x71, 0x2e, 0x9d, 0x62, 0x75, 0xd4, + 0x16, 0x48, 0x77, 0x13, 0x67, 0xad, 0x6b, 0x5d, 0x07, 0x87, 0xf7, 0xa8, 0x9a, 0x59, 0x76, + 0x8b, 0x9b, 0x1e, 0xd8, 0xee, 0xaa, 0xe9, 0x99, 0x2d, 0xc5, 0x97, 0xf1, 0xa7, 0x83, 0xfc, + 0xca, 0xdc, 0xba, 0xb8, 0x4b, 0xe8, 0x89, 0x17, 0x05, 0x0b, 0xa0, 0x65, 0x23, 0xd1, 0xce, + 0x36, 0x03, 0xd7, 0xe0, 0xf2, 0x91, 0xdf, 0x0e, 0x9c, 0x2c, 0x0d, 0x66, 0xd6, 0x73, 0x58, + 0x5c, 0xb4, 0x0a, 0x4d, 0xc2, 0x3b, 0xd2, 0xb7, 0xe2, 0xac, 0x33, 0xdb, 0x60, 0x27, 0xbd, + 0x56, 0x43, 0x24, 0x04, 0x02, 0x8d, 0x7d, 0x7f, 0xf6, 0xb5, 0xf4, 0xfd, 0xdd, 0x5b, 0xec, + 0x79, 0xc4, 0x22, 0x1f, 0xa1, 0x88, 0x54, 0xe7, 0x19, 0x98, 0x94, 0x7e, 0x31, 0xa3, 0x29, + 0xe3, 0x5a, 0xcb, 0x6a, 0xb0, 0xcf, 0x6d, 0x93, 0x6c, 0x7a, 0x08, 0xa9, 0x3c, 0x1c, 0xd0, + 0x63, 0x50, 0xc6, 0x85, 0x38, 0xc1, 0x41, 0x49, 0xab, 0x61, 0x52, 0x2a, 0x9f, 0xd9, 0x18, + 0x1a, 0x57, 0x46, 0x2b, 0x69, 0xda, 0xfa, 0xff, 0x0f, 0x15, 0x96, 0x3a, 0x8a, 0xfe, 0x9e, + 0x0c}, + {0x11, 0xa2, 0x9a, 0xc3, 0x94, 0xa0, 0x51, 0x01, 0x5b, 0xf7, 0xd1, 0x30, 0xee, 0x1f, 0x06, + 0xd5, 0x77, 0x67, 0xd3, 0xc1, 0x6e, 0xe7, 0x6a, 0x1a, 0xa4, 0x8e, 0x9f, 0x65, 0x66, 0xd6, + 0x0c, 0x2d, 0xc6, 0xe4, 0x69, 0xb7, 0x56, 0x5a, 0x57, 0x60, 0xfc, 0x79, 0x1e, 0xe1, 0x16, + 0x52, 0xf0, 0x07, 0xd0, 0xcd, 0xca, 0x78, 0xc9, 0x8b, 0xb3, 0x88, 0x27, 0xf6, 0xe0, 0xc0, + 0x84, 0xcf, 0x2f, 0xa3, 0x04, 0x00, 0x80, 0x40, 0xa7, 0xfa, 0x75, 0x9d, 0x4b, 0x89, 0x46, + 0x22, 0x28, 0x37, 0x34, 0x13, 0xff, 0x20, 0xb4, 0xda, 0x08, 0x26, 0x6c, 0x8f, 0xf2, 0x5d, + 0x7b, 0x73, 0x5c, 0x4c, 0x90, 0x71, 0x41, 0x8a, 0x9e, 0xbb, 0x12, 0xe8, 0x55, 0x6d, 0x2a, + 0x25, 0x4a, 0xaf, 0xbd, 0x95, 0x19, 0xa5, 0x31, 0xba, 0xad, 0xd9, 0xc5, 0xa6, 0x6b, 0x83, + 0xc4, 0xb6, 0xa9, 0xcc, 0xf9, 0xa1, 0xb1, 0x7a, 0xdc, 0xc7, 0xdb, 0x2e, 0x7e, 0x7f, 0x8d, + 0x4e, 0x62, 0x0f, 0x03, 0x39, 0xfd, 0xb8, 0xd4, 0x18, 0xc2, 0xec, 0x61, 0x02, 0x53, 0x1c, + 0x3b, 0x7d, 0xbc, 0x1d, 0x59, 0xf5, 0x8c, 0x98, 0xf1, 0x6f, 0x93, 0x85, 0xf8, 0x2c, 0x74, + 0xd8, 0x5e, 0x4d, 0x97, 0xab, 0x87, 0x82, 0x0a, 0x21, 0x42, 0x5f, 0x0d, 0x58, 0x33, 0x44, + 0x3a, 0xdd, 0xe9, 0xfb, 0x50, 0x47, 0xc8, 0xd7, 0x81, 0x9b, 0xe5, 0x1b, 0x17, 0x54, 0x86, + 0x2b, 0xef, 0xf4, 0x29, 0x32, 0x0e, 0x7c, 0x23, 0x15, 0xeb, 0xe6, 0x3c, 0x35, 0xe2, 0x96, + 0x68, 0x3f, 0x0b, 0x43, 0xce, 0xde, 0x9c, 0xbe, 0x4f, 0x45, 0x72, 0x76, 0xb5, 0x10, 0xe3, + 0xdf, 0x92, 0xd2, 0xa8, 0x48, 0x91, 0xb9, 0xac, 0xbf, 0x49, 0xb2, 0x99, 0x64, 0x70, 0xea, + 0xf3, 0x3e, 0x63, 0x14, 0xae, 0xed, 0xaa, 0x24, 0xfe, 0x3d, 0xcb, 0xb0, 0x09, 0x38, 0x36, + 0x05}, + {0xc4, 0xf0, 0x28, 0x49, 0x55, 0x4a, 0xf5, 0xfd, 0x75, 0xaf, 0x20, 0x69, 0xc8, 0x43, 0x86, + 0x6b, 0xc9, 0xa8, 0xc6, 0x54, 0x4c, 0xdd, 0x02, 0x5b, 0xe8, 0x9b, 0x59, 0x77, 0x34, 0xd7, + 0xc0, 0x51, 0x5f, 0xf3, 0x7e, 0xd4, 0xf1, 0x90, 0x81, 0xce, 0x19, 0x8a, 0x78, 0x33, 0xcd, + 0x97, 0x8c, 0xd6, 0x6a, 0x4b, 0x8f, 0xa4, 0x80, 0xdc, 0x1a, 0x17, 0x14, 0xc5, 0x07, 0x87, + 0x66, 0xad, 0x9d, 0x85, 0xb2, 0xf8, 0x8d, 0x98, 0xdf, 0x3e, 0x1f, 0x3f, 0x64, 0x58, 0x40, + 0xac, 0x6c, 0x5c, 0x08, 0x5d, 0x53, 0xba, 0xff, 0xd2, 0xa9, 0x2c, 0x25, 0xab, 0xe4, 0x32, + 0x38, 0x9a, 0xf9, 0xcb, 0xc2, 0x91, 0x67, 0xd0, 0xe3, 0x22, 0x29, 0x2d, 0x92, 0x48, 0xb4, + 0x0e, 0x99, 0x05, 0xa3, 0x6f, 0x0a, 0x15, 0xef, 0xd5, 0x10, 0x4f, 0xd8, 0xf6, 0x00, 0xe6, + 0x46, 0x2b, 0x31, 0x57, 0x83, 0x94, 0xae, 0x03, 0xeb, 0x8e, 0x13, 0x24, 0xa1, 0x1e, 0x5e, + 0xd1, 0x26, 0xd9, 0xa7, 0xca, 0x36, 0x6e, 0x71, 0x37, 0xee, 0xb1, 0xfa, 0x7c, 0x76, 0x06, + 0xb8, 0x23, 0xbe, 0x21, 0xbc, 0x9e, 0xc1, 0xda, 0x7a, 0x3b, 0x62, 0x27, 0x7b, 0xb0, 0xe5, + 0x63, 0x18, 0x82, 0x7f, 0x0f, 0xed, 0xa0, 0x2a, 0x9c, 0xbf, 0x11, 0xbd, 0xe0, 0x88, 0x0d, + 0x3a, 0x79, 0x52, 0x56, 0xf7, 0xb6, 0xd3, 0x09, 0x16, 0x1b, 0x70, 0xb3, 0x42, 0x60, 0x2f, + 0x1c, 0xa2, 0x9f, 0x72, 0x12, 0x45, 0x47, 0xbb, 0xe1, 0x0b, 0x01, 0x8b, 0x1d, 0xde, 0xec, + 0x0c, 0x4e, 0xe9, 0xcf, 0xcc, 0x95, 0x74, 0xf4, 0xa5, 0x93, 0xc7, 0xdb, 0x4d, 0xfb, 0x35, + 0x65, 0xfe, 0xe7, 0x39, 0xb5, 0xea, 0x96, 0xc3, 0x04, 0x41, 0x44, 0x84, 0xfc, 0x6d, 0x30, + 0xe2, 0xf2, 0x68, 0xb7, 0x89, 0x7d, 0x73, 0x3d, 0xb9, 0x5a, 0x50, 0xa6, 0x3c, 0x61, 0xaa, + 0x2e}, + {0x1a, 0x59, 0x9c, 0xad, 0xc8, 0xe4, 0x11, 0x54, 0xed, 0x37, 0x0f, 0x3a, 0xe6, 0x5f, 0x3c, + 0x4b, 0xb8, 0x15, 0x89, 0xb1, 0xe8, 0xda, 0x69, 0x77, 0x91, 0x56, 0x8b, 0xdb, 0x06, 0x24, + 0xcf, 0x18, 0xf8, 0xb0, 0x87, 0xdf, 0x8c, 0x35, 0xcb, 0x86, 0x53, 0x9d, 0xa4, 0x66, 0x4a, + 0x7a, 0x71, 0x0a, 0x48, 0x38, 0xff, 0xdc, 0x83, 0x20, 0xce, 0x98, 0x32, 0xf6, 0xd7, 0xaf, + 0x70, 0x5e, 0x73, 0x8a, 0x14, 0x72, 0x1e, 0xc1, 0x29, 0x79, 0x07, 0x08, 0xe9, 0x43, 0x46, + 0xd0, 0x2f, 0xde, 0x2a, 0x4f, 0x3d, 0x2c, 0x50, 0xb3, 0x75, 0xfc, 0x0b, 0x64, 0xae, 0x31, + 0x7b, 0x61, 0xa5, 0x30, 0xa0, 0x93, 0xd2, 0xbe, 0xc2, 0x55, 0xba, 0x6c, 0xf3, 0x62, 0x68, + 0xfd, 0xac, 0x3b, 0x95, 0x49, 0x1f, 0x6b, 0xb4, 0x85, 0xf7, 0xa6, 0x03, 0xec, 0x6e, 0x9a, + 0x81, 0x09, 0x0c, 0x6a, 0xee, 0x9e, 0x4e, 0xf0, 0xab, 0x2d, 0x7e, 0xa1, 0xe1, 0xbb, 0xc9, + 0xbc, 0x41, 0xf2, 0xfa, 0x2e, 0x1b, 0xdd, 0x27, 0x34, 0xd3, 0x60, 0x04, 0xb5, 0x01, 0xd6, + 0x40, 0xf9, 0xd5, 0x02, 0x47, 0xd9, 0xd8, 0xe0, 0x8f, 0x2b, 0xfb, 0xb7, 0xc5, 0xeb, 0x57, + 0x16, 0xe5, 0x78, 0x8d, 0xf4, 0x00, 0xcd, 0x82, 0x39, 0xc6, 0x96, 0xbd, 0xbf, 0xe3, 0x36, + 0x45, 0x0e, 0x26, 0x1d, 0x63, 0xe7, 0x84, 0x3e, 0xb6, 0x9b, 0x22, 0xa3, 0xf5, 0x8e, 0x23, + 0x6d, 0xc3, 0x99, 0x7d, 0x90, 0x97, 0x10, 0x25, 0x80, 0xd4, 0x4c, 0xe2, 0x74, 0xcc, 0xb2, + 0x5c, 0x33, 0xc7, 0xea, 0x05, 0x12, 0x3f, 0x51, 0xa8, 0xfe, 0xf1, 0x1c, 0x42, 0xca, 0xd1, + 0x76, 0x28, 0x6f, 0x92, 0xa7, 0x67, 0xa9, 0x19, 0xa2, 0x44, 0x5b, 0xaa, 0xef, 0x58, 0x7f, + 0x21, 0xc0, 0x88, 0x65, 0xb9, 0x5d, 0x4d, 0x0d, 0x5a, 0xc4, 0x13, 0x52, 0x7c, 0x9f, 0x94, + 0x17}, + {0xc7, 0x2b, 0x82, 0x61, 0x5b, 0xd0, 0x96, 0x84, 0xd3, 0x4a, 0x70, 0xa1, 0x9b, 0x59, 0x33, + 0x9f, 0xc0, 0x20, 0x14, 0x53, 0x29, 0x17, 0xc5, 0x0b, 0xc9, 0x7b, 0x97, 0x02, 0x0d, 0x3a, + 0x1e, 0x7c, 0x3f, 0x6b, 0x52, 0xe8, 0x75, 0x3d, 0xf6, 0xe4, 0x0c, 0x8a, 0x4f, 0xc3, 0x5f, + 0x26, 0x65, 0x73, 0x31, 0x23, 0x28, 0x48, 0x74, 0xaa, 0xa7, 0x36, 0x09, 0xb1, 0xe2, 0x91, + 0x04, 0x51, 0x22, 0xfc, 0x08, 0xa6, 0x05, 0xa4, 0xf1, 0x12, 0x1c, 0x19, 0xeb, 0x40, 0x37, + 0xc2, 0xa0, 0x41, 0x1d, 0xd4, 0xdc, 0x07, 0x43, 0x8f, 0x47, 0xaf, 0xd1, 0x2e, 0x98, 0xab, + 0x01, 0xba, 0xf0, 0x66, 0x68, 0xac, 0xf9, 0xe7, 0x69, 0xb6, 0xcb, 0x8d, 0x78, 0x87, 0x15, + 0x03, 0xd5, 0xdf, 0xa3, 0x1a, 0x9d, 0x6a, 0xea, 0x2f, 0x94, 0x4e, 0x9e, 0x42, 0xd7, 0xb8, + 0x38, 0x92, 0xd8, 0xbb, 0xde, 0xdd, 0x9a, 0xbc, 0xb0, 0x4c, 0x79, 0xf4, 0x58, 0x3e, 0xe9, + 0x83, 0x81, 0xff, 0xe3, 0x55, 0xfd, 0x5d, 0xb2, 0xef, 0x9c, 0x6d, 0x54, 0x99, 0x60, 0xda, + 0x3b, 0xec, 0xfa, 0x11, 0xd6, 0xc4, 0x2a, 0xed, 0x4b, 0xae, 0x13, 0xbf, 0xb9, 0x06, 0x8b, + 0xe0, 0x1f, 0x7f, 0x5a, 0xad, 0x90, 0x39, 0x0f, 0xf3, 0xbd, 0x46, 0x6c, 0x2c, 0xf2, 0xf8, + 0xfe, 0xd9, 0xe6, 0x72, 0x0e, 0x89, 0xbe, 0x5e, 0xc6, 0xa8, 0xcf, 0xf5, 0x57, 0x7e, 0x8c, + 0xb7, 0xe1, 0x88, 0x7a, 0xc8, 0x1b, 0x18, 0xdb, 0x6f, 0x35, 0xd2, 0x16, 0x32, 0x64, 0x63, + 0xa5, 0x8e, 0xf7, 0xb4, 0x76, 0x95, 0x86, 0x7d, 0xe5, 0xa9, 0x5c, 0x85, 0x00, 0xc1, 0x93, + 0x4d, 0xfb, 0x30, 0xa2, 0xb5, 0x25, 0x34, 0x10, 0x77, 0x3c, 0x67, 0x71, 0x2d, 0x44, 0x45, + 0x56, 0x0a, 0x50, 0xb3, 0xcd, 0x62, 0x80, 0xce, 0x21, 0x49, 0x24, 0xca, 0xee, 0x27, 0x6e, + 0xcc}, + {0xa5, 0xb0, 0x6d, 0xb2, 0x51, 0x55, 0x1f, 0xbf, 0x3e, 0x8d, 0xdd, 0x19, 0x92, 0xea, 0x1b, + 0xbd, 0x32, 0x65, 0x29, 0xa4, 0x89, 0x67, 0xb1, 0x90, 0x68, 0x00, 0xda, 0x0d, 0xa6, 0x59, + 0x54, 0xf2, 0x10, 0x14, 0x2f, 0x45, 0xe0, 0x3b, 0x23, 0xfa, 0xd7, 0x50, 0xac, 0x93, 0xe6, + 0x43, 0x01, 0x0a, 0x5c, 0x78, 0x70, 0x98, 0x46, 0xa9, 0x7b, 0xf3, 0x95, 0x07, 0x2a, 0xd3, + 0x74, 0xb3, 0x3f, 0xa3, 0x60, 0x82, 0x39, 0x4e, 0x34, 0x48, 0xc0, 0x1c, 0xf6, 0xc2, 0x91, + 0x64, 0x4d, 0x3c, 0xf8, 0x9d, 0x35, 0x9a, 0x94, 0xe3, 0x7a, 0xf0, 0xf9, 0x6e, 0xb9, 0x12, + 0xb8, 0x04, 0x2d, 0x02, 0x28, 0x13, 0x85, 0x72, 0x80, 0x87, 0xdb, 0x2b, 0xf1, 0x4f, 0x26, + 0xa2, 0xe1, 0x49, 0x7e, 0x9c, 0xcc, 0xa7, 0xb6, 0xa0, 0xd0, 0x9b, 0x36, 0x77, 0xad, 0x8b, + 0x6b, 0x4a, 0x03, 0x1d, 0x05, 0x8a, 0x06, 0x4b, 0xaf, 0xe5, 0x31, 0xb5, 0xd4, 0xc6, 0x0c, + 0x66, 0xba, 0x83, 0xfd, 0x09, 0x0f, 0xae, 0x71, 0xb7, 0x6f, 0xdc, 0x41, 0xe8, 0x17, 0x8e, + 0x40, 0x7f, 0x62, 0x30, 0xff, 0xa8, 0x84, 0x25, 0xfb, 0x16, 0xce, 0x37, 0x44, 0xab, 0x99, + 0x1e, 0xeb, 0x18, 0x3a, 0x47, 0xf7, 0x5f, 0x81, 0xcf, 0xed, 0x58, 0x2c, 0x6c, 0xfe, 0x9e, + 0x57, 0x53, 0x97, 0x20, 0xca, 0x79, 0x1a, 0x5a, 0x88, 0xf5, 0x69, 0x9f, 0xe7, 0xd9, 0x0e, + 0xbe, 0x42, 0xdf, 0x56, 0xe4, 0x4c, 0x22, 0xaa, 0x73, 0x0b, 0x15, 0xc5, 0xee, 0xfc, 0xc7, + 0xd6, 0xcb, 0xcd, 0x8c, 0xe2, 0x76, 0x21, 0xe9, 0xd1, 0xec, 0xc8, 0x7d, 0xd8, 0x8f, 0x61, + 0x7c, 0x2e, 0xbc, 0xde, 0xb4, 0x75, 0xd2, 0xc4, 0x63, 0x3d, 0xa1, 0x5e, 0x5d, 0x6a, 0x08, + 0x24, 0xc9, 0x27, 0xbb, 0xef, 0x33, 0x86, 0x5b, 0xd5, 0x38, 0x52, 0x11, 0xf4, 0xc3, 0x96, + 0xc1}, + {0x34, 0x5a, 0xdb, 0x2c, 0x59, 0x57, 0x12, 0x2b, 0x30, 0xc2, 0xa0, 0x92, 0xbf, 0xed, 0xbc, + 0x45, 0xde, 0x27, 0x9b, 0x96, 0xd3, 0xe6, 0xc5, 0xeb, 0xd8, 0x24, 0x4b, 0xa4, 0x21, 0xcc, + 0xa8, 0xd6, 0xce, 0x3c, 0xba, 0xb1, 0x09, 0xe0, 0xd7, 0x32, 0x66, 0x4a, 0x83, 0x1d, 0x19, + 0xca, 0x89, 0x67, 0x0f, 0x42, 0x07, 0x71, 0xe9, 0xbb, 0x44, 0x5e, 0x85, 0x17, 0xc4, 0xae, + 0x9c, 0x3f, 0x6b, 0x78, 0xe7, 0x6d, 0x02, 0xa7, 0xfe, 0x33, 0xe3, 0xd9, 0x0a, 0x7d, 0xea, + 0xf1, 0x18, 0x87, 0x7b, 0x62, 0x03, 0x79, 0x52, 0x23, 0x00, 0x3e, 0x25, 0xb9, 0xdd, 0x16, + 0x68, 0x3b, 0x1f, 0x90, 0xa6, 0x2a, 0xc6, 0x29, 0x91, 0x8a, 0x9d, 0xef, 0x1e, 0x84, 0x76, + 0xee, 0x4f, 0x39, 0xfb, 0x11, 0x1b, 0x0b, 0x93, 0xad, 0x49, 0xb7, 0x05, 0xe1, 0x4d, 0xcb, + 0xe8, 0x38, 0xd0, 0x55, 0xf2, 0xf7, 0x0d, 0x8b, 0x65, 0xcd, 0xfa, 0xd4, 0x9e, 0xc9, 0x81, + 0x4e, 0x14, 0xc8, 0x26, 0xb6, 0xf9, 0xaa, 0xf5, 0x80, 0xf8, 0x6a, 0x98, 0xab, 0x58, 0xf3, + 0xb8, 0x8e, 0xb5, 0x97, 0x43, 0x72, 0xa2, 0xda, 0x64, 0xc1, 0x40, 0x5c, 0x13, 0xb0, 0xe5, + 0xbd, 0x08, 0x9f, 0x1c, 0x7e, 0x8f, 0x06, 0xbe, 0x6e, 0x50, 0x1a, 0x2f, 0x37, 0xa1, 0xfc, + 0x10, 0x2e, 0x70, 0x53, 0x04, 0x20, 0x63, 0x48, 0xe2, 0xfd, 0x6f, 0x7a, 0x75, 0x61, 0xb3, + 0x35, 0x3a, 0xcf, 0x5b, 0x88, 0x82, 0x51, 0xd2, 0x47, 0xa5, 0xa9, 0x74, 0x6c, 0x31, 0x94, + 0x7c, 0x9a, 0xc0, 0xec, 0x15, 0x0e, 0x28, 0x01, 0x2d, 0xc3, 0xf0, 0xb4, 0xe4, 0x8d, 0xdc, + 0xac, 0x3d, 0x5f, 0x86, 0xc7, 0x95, 0x22, 0xf4, 0xb2, 0xa3, 0x54, 0x46, 0xd1, 0x77, 0x8c, + 0x0c, 0xff, 0xaf, 0x56, 0x5d, 0x36, 0x69, 0x7f, 0x99, 0x4c, 0xd5, 0x60, 0x73, 0xdf, 0x41, + 0xf6}, + {0x04, 0xda, 0x79, 0x63, 0x1e, 0xbd, 0xea, 0xe3, 0x0b, 0x65, 0x25, 0x01, 0xcd, 0xa9, 0x92, + 0xed, 0x18, 0x75, 0x57, 0x30, 0x89, 0x03, 0x09, 0x6a, 0xdc, 0xc7, 0x98, 0xa4, 0x50, 0x91, + 0x1f, 0xb1, 0x0c, 0x77, 0x85, 0x66, 0xca, 0x12, 0x1c, 0x67, 0xc2, 0xe0, 0x17, 0x83, 0x59, + 0x9d, 0xd5, 0x22, 0x82, 0x56, 0xa8, 0x9f, 0xc6, 0x60, 0x48, 0xb3, 0x11, 0xfc, 0xa3, 0x28, + 0xfb, 0x06, 0xdd, 0x5d, 0xba, 0x29, 0x7a, 0x4f, 0xe8, 0xfe, 0xe9, 0x10, 0xcb, 0xf3, 0x93, + 0x7b, 0x6c, 0x69, 0x54, 0xe7, 0x44, 0xa2, 0x84, 0x1d, 0x8d, 0xce, 0xff, 0xfa, 0x1a, 0x87, + 0x90, 0x74, 0xa1, 0xf8, 0x14, 0xaa, 0xbc, 0xc0, 0xcf, 0x31, 0xb4, 0xd9, 0xbf, 0xd8, 0x5e, + 0x26, 0x2d, 0xd0, 0xe4, 0x3f, 0x19, 0xd6, 0x8c, 0x2f, 0xab, 0x39, 0x58, 0x72, 0x6e, 0xdf, + 0x3b, 0xe6, 0x3c, 0xb6, 0x62, 0x88, 0xde, 0x40, 0xb2, 0x8f, 0x9b, 0x7c, 0x95, 0x43, 0xd1, + 0x9e, 0xac, 0x9c, 0xd4, 0x23, 0xe1, 0x0e, 0x4b, 0x53, 0x33, 0x46, 0x20, 0x13, 0x34, 0x1b, + 0x97, 0xf7, 0xf1, 0xc3, 0x61, 0x4a, 0x6f, 0x5a, 0x21, 0x7f, 0x70, 0x2e, 0x55, 0x41, 0x05, + 0xc5, 0xd7, 0x76, 0xe5, 0x27, 0x15, 0xec, 0x42, 0x5c, 0x4d, 0x78, 0x35, 0x8b, 0xef, 0xd2, + 0xee, 0xb5, 0xbe, 0xae, 0x02, 0x3a, 0xd3, 0x5f, 0xc4, 0x24, 0xf0, 0xf9, 0x51, 0x4e, 0xeb, + 0x00, 0x0f, 0xfd, 0xaf, 0x3e, 0xc1, 0x9a, 0x52, 0x86, 0x81, 0x80, 0x7e, 0xf6, 0x2b, 0xcc, + 0xb9, 0x7d, 0x68, 0xf2, 0xad, 0x99, 0xa7, 0x07, 0x2c, 0x73, 0x38, 0xb0, 0x6b, 0xb7, 0x8e, + 0x71, 0xa0, 0xf4, 0x3d, 0xa6, 0x0d, 0x37, 0xdb, 0x0a, 0x47, 0x36, 0x16, 0x96, 0x6d, 0x32, + 0x2a, 0x5b, 0xe2, 0x45, 0x94, 0xf5, 0xa5, 0x4c, 0xc8, 0x8a, 0x49, 0x64, 0xbb, 0x08, 0xc9, + 0xb8}, +}; + +static const uint8_t access_code_table_1[5][10][100] = { + { + {11, 38, 3, 63, 36, 39, 79, 19, 87, 68, 76, 51, 20, 56, 77, 61, 52, 73, 74, 94, + 45, 31, 99, 28, 29, 90, 81, 96, 65, 75, 13, 32, 70, 21, 55, 33, 9, 15, 92, 14, + 82, 97, 78, 37, 49, 0, 80, 57, 58, 7, 1, 89, 98, 53, 64, 5, 6, 66, 43, 83, + 10, 50, 47, 84, 62, 71, 4, 67, 86, 42, 35, 34, 91, 12, 17, 69, 44, 48, 85, 30, + 16, 95, 54, 23, 46, 18, 88, 59, 40, 60, 41, 25, 27, 22, 2, 24, 72, 93, 26, 8}, + {28, 58, 74, 34, 6, 14, 16, 46, 87, 24, 59, 57, 23, 88, 75, 65, 79, 38, 82, 13, + 49, 99, 8, 94, 15, 19, 0, 96, 41, 95, 45, 35, 1, 91, 2, 52, 42, 76, 90, 84, + 20, 54, 4, 63, 25, 5, 47, 85, 93, 12, 51, 81, 71, 69, 22, 17, 36, 64, 77, 80, + 98, 53, 44, 18, 10, 68, 97, 61, 56, 70, 92, 27, 40, 9, 60, 7, 72, 21, 43, 3, + 33, 32, 50, 26, 67, 62, 39, 48, 73, 86, 29, 89, 37, 78, 55, 66, 31, 30, 11, 83}, + {4, 16, 22, 66, 37, 14, 47, 89, 59, 60, 51, 92, 71, 70, 96, 13, 17, 85, 58, 93, + 81, 69, 28, 90, 78, 39, 63, 88, 0, 75, 2, 53, 23, 99, 94, 68, 54, 74, 41, 29, + 8, 26, 50, 98, 82, 95, 42, 7, 24, 77, 30, 9, 21, 1, 87, 62, 46, 15, 43, 38, + 36, 55, 49, 57, 52, 56, 91, 65, 11, 84, 6, 73, 97, 33, 83, 27, 35, 25, 40, 32, + 18, 31, 86, 48, 45, 3, 79, 76, 72, 64, 44, 20, 80, 19, 10, 5, 61, 34, 67, 12}, + {80, 91, 71, 26, 75, 8, 22, 92, 7, 70, 65, 51, 89, 31, 81, 30, 83, 47, 44, 85, + 55, 19, 57, 87, 74, 6, 73, 40, 49, 32, 52, 78, 0, 12, 54, 15, 64, 28, 4, 24, + 68, 17, 72, 53, 1, 94, 58, 88, 11, 82, 43, 86, 60, 63, 99, 96, 37, 77, 67, 33, + 18, 5, 41, 59, 16, 36, 79, 97, 61, 23, 56, 48, 98, 38, 66, 21, 34, 29, 10, 3, + 93, 46, 50, 39, 14, 9, 2, 13, 69, 42, 62, 35, 27, 90, 25, 76, 20, 95, 45, 84}, + {48, 16, 54, 47, 9, 39, 81, 91, 33, 53, 63, 21, 50, 85, 97, 90, 49, 36, 84, 74, + 51, 76, 37, 67, 59, 58, 23, 20, 65, 82, 7, 77, 12, 31, 46, 1, 5, 14, 3, 30, + 94, 95, 2, 34, 70, 89, 40, 87, 43, 79, 64, 57, 68, 26, 56, 44, 88, 61, 0, 24, + 71, 18, 60, 4, 80, 96, 27, 15, 6, 13, 98, 29, 28, 25, 72, 93, 55, 75, 52, 66, + 11, 8, 86, 45, 22, 83, 17, 10, 35, 38, 73, 99, 62, 41, 19, 69, 78, 92, 42, 32}, + {23, 19, 88, 44, 5, 39, 75, 81, 30, 14, 49, 54, 62, 55, 18, 83, 77, 76, 74, 92, + 6, 95, 52, 36, 15, 27, 53, 38, 28, 50, 48, 99, 78, 67, 90, 87, 16, 41, 58, 65, + 79, 11, 68, 84, 24, 97, 64, 47, 9, 22, 43, 86, 37, 8, 33, 17, 93, 61, 25, 72, + 57, 98, 42, 80, 13, 89, 2, 12, 31, 56, 26, 85, 3, 29, 66, 63, 82, 10, 20, 94, + 51, 0, 1, 4, 21, 60, 34, 35, 32, 45, 59, 71, 46, 69, 7, 96, 91, 40, 70, 73}, + {89, 62, 33, 19, 21, 97, 15, 2, 83, 22, 65, 30, 55, 7, 63, 73, 39, 61, 44, 37, + 11, 26, 31, 79, 91, 0, 72, 68, 23, 87, 28, 24, 45, 74, 98, 14, 70, 54, 88, 42, + 35, 59, 46, 16, 27, 92, 69, 66, 94, 32, 75, 77, 64, 57, 50, 71, 10, 3, 67, 81, + 90, 58, 99, 5, 82, 85, 25, 52, 12, 8, 48, 49, 76, 96, 80, 20, 29, 34, 53, 51, + 86, 93, 38, 84, 9, 36, 6, 60, 1, 78, 13, 95, 41, 17, 47, 18, 43, 40, 4, 56}, + {40, 68, 88, 47, 43, 28, 67, 23, 42, 99, 48, 3, 12, 27, 79, 25, 6, 55, 19, 80, + 81, 62, 83, 69, 39, 73, 36, 30, 44, 82, 70, 52, 31, 34, 58, 74, 54, 2, 13, 45, + 93, 49, 75, 72, 29, 87, 5, 7, 17, 76, 90, 63, 84, 51, 59, 35, 20, 97, 22, 65, + 57, 66, 78, 86, 11, 9, 71, 98, 53, 56, 15, 46, 92, 0, 26, 21, 32, 38, 77, 91, + 4, 24, 61, 85, 94, 1, 96, 64, 8, 41, 33, 50, 18, 60, 10, 16, 37, 95, 14, 89}, + {96, 23, 99, 42, 94, 91, 19, 53, 41, 25, 39, 73, 70, 72, 33, 35, 88, 2, 63, 49, + 95, 60, 48, 97, 5, 54, 47, 40, 98, 18, 57, 28, 67, 92, 10, 79, 13, 76, 8, 55, + 90, 62, 50, 87, 65, 82, 84, 21, 0, 14, 45, 44, 20, 38, 17, 3, 81, 16, 58, 4, + 31, 29, 86, 15, 43, 64, 26, 52, 46, 69, 37, 68, 6, 22, 61, 7, 83, 71, 30, 75, + 85, 78, 24, 27, 89, 9, 80, 34, 66, 77, 36, 93, 56, 59, 1, 12, 51, 32, 11, 74}, + {75, 99, 97, 8, 15, 48, 6, 25, 60, 90, 91, 95, 23, 3, 89, 77, 22, 78, 94, 55, + 98, 35, 53, 96, 18, 52, 12, 16, 50, 20, 49, 9, 17, 13, 57, 44, 14, 47, 86, 56, + 88, 41, 29, 32, 70, 1, 62, 37, 85, 38, 59, 36, 5, 68, 71, 69, 67, 27, 39, 4, + 54, 0, 84, 45, 61, 73, 79, 93, 58, 66, 10, 76, 51, 92, 19, 87, 24, 7, 28, 2, + 11, 80, 81, 33, 64, 74, 46, 65, 26, 72, 40, 82, 34, 42, 21, 83, 31, 30, 43, 63}, + }, + { + {98, 58, 34, 65, 83, 74, 88, 5, 89, 46, 80, 43, 18, 87, 9, 28, 71, 90, 4, 36, + 40, 11, 39, 31, 76, 92, 82, 48, 66, 41, 49, 99, 78, 79, 97, 86, 75, 68, 3, 7, + 62, 64, 26, 22, 13, 77, 54, 50, 10, 96, 15, 35, 94, 73, 57, 93, 16, 61, 37, 38, + 53, 2, 63, 23, 67, 72, 95, 70, 29, 33, 45, 47, 27, 24, 69, 44, 55, 85, 14, 8, + 91, 12, 42, 6, 84, 81, 19, 25, 32, 51, 1, 17, 52, 56, 60, 0, 59, 30, 21, 20}, + {26, 63, 99, 57, 77, 53, 9, 75, 76, 80, 46, 97, 37, 14, 1, 12, 35, 20, 0, 15, + 28, 19, 48, 41, 67, 30, 60, 69, 29, 34, 73, 10, 51, 47, 66, 82, 7, 98, 39, 96, + 8, 62, 42, 3, 95, 5, 11, 13, 90, 94, 32, 31, 83, 64, 36, 88, 72, 52, 38, 54, + 45, 17, 61, 81, 78, 44, 79, 55, 56, 86, 2, 40, 16, 24, 4, 71, 49, 33, 23, 92, + 84, 74, 65, 43, 87, 59, 89, 68, 22, 70, 25, 58, 21, 85, 6, 50, 27, 93, 91, 18}, + {22, 31, 11, 72, 37, 70, 90, 60, 24, 56, 28, 69, 54, 85, 5, 98, 1, 76, 59, 20, + 86, 32, 61, 49, 63, 42, 74, 53, 84, 3, 95, 23, 15, 36, 4, 81, 6, 21, 19, 27, + 17, 83, 30, 45, 25, 65, 10, 50, 80, 66, 46, 75, 93, 82, 78, 99, 97, 68, 8, 13, + 58, 94, 41, 14, 48, 52, 29, 79, 91, 89, 9, 55, 57, 2, 39, 92, 0, 44, 18, 62, + 73, 96, 34, 88, 16, 77, 71, 43, 33, 40, 7, 12, 87, 51, 26, 47, 64, 35, 67, 38}, + {0, 80, 63, 52, 49, 69, 15, 87, 55, 60, 22, 91, 31, 89, 78, 28, 48, 12, 10, 42, + 82, 23, 29, 18, 21, 2, 19, 7, 68, 37, 54, 90, 81, 43, 57, 94, 26, 95, 96, 88, + 14, 51, 79, 71, 66, 76, 34, 24, 30, 9, 62, 44, 77, 65, 6, 27, 4, 97, 38, 36, + 59, 3, 1, 50, 72, 46, 64, 35, 20, 11, 53, 13, 58, 39, 74, 8, 83, 75, 40, 98, + 17, 99, 85, 32, 92, 67, 84, 5, 25, 56, 16, 47, 70, 93, 86, 61, 73, 41, 45, 33}, + {64, 57, 56, 7, 86, 9, 65, 16, 1, 63, 71, 26, 53, 98, 69, 5, 84, 28, 36, 58, + 54, 99, 42, 74, 68, 3, 88, 22, 82, 97, 85, 45, 21, 66, 43, 59, 83, 51, 89, 87, + 31, 12, 95, 52, 60, 17, 80, 27, 72, 93, 10, 39, 91, 30, 75, 61, 24, 8, 23, 2, + 18, 73, 94, 6, 38, 44, 13, 40, 48, 4, 81, 37, 96, 46, 19, 49, 34, 50, 0, 20, + 14, 15, 25, 92, 33, 29, 77, 90, 78, 67, 62, 41, 35, 47, 79, 32, 11, 70, 76, 55}, + {24, 99, 28, 52, 64, 3, 63, 16, 15, 87, 47, 6, 59, 98, 13, 78, 57, 81, 56, 94, + 0, 37, 70, 73, 65, 83, 90, 92, 60, 82, 67, 40, 74, 34, 91, 2, 33, 85, 21, 54, + 12, 31, 84, 55, 53, 49, 36, 25, 39, 20, 29, 93, 51, 76, 42, 96, 71, 86, 95, 66, + 45, 19, 8, 58, 22, 69, 80, 79, 89, 17, 61, 18, 88, 38, 72, 75, 48, 30, 7, 10, + 11, 35, 23, 9, 26, 4, 62, 44, 1, 43, 46, 14, 41, 77, 97, 27, 68, 32, 5, 50}, + {90, 80, 22, 27, 16, 23, 11, 31, 8, 75, 54, 82, 68, 66, 10, 40, 95, 99, 14, 42, + 56, 88, 12, 55, 38, 6, 79, 84, 26, 92, 4, 67, 48, 5, 46, 77, 19, 53, 97, 34, + 64, 73, 58, 47, 18, 39, 81, 59, 74, 62, 87, 98, 50, 1, 25, 9, 72, 69, 61, 60, + 86, 3, 24, 28, 45, 30, 96, 36, 57, 83, 89, 63, 0, 33, 17, 7, 94, 15, 43, 2, + 44, 41, 20, 85, 32, 65, 93, 49, 91, 52, 71, 37, 35, 13, 21, 51, 70, 29, 78, 76}, + {95, 50, 72, 0, 68, 98, 49, 53, 56, 41, 54, 22, 57, 78, 66, 31, 52, 29, 86, 30, + 62, 26, 32, 46, 42, 48, 14, 21, 88, 94, 77, 92, 7, 63, 91, 28, 93, 38, 90, 71, + 34, 25, 11, 83, 55, 70, 97, 82, 89, 76, 10, 99, 9, 45, 60, 39, 44, 16, 17, 37, + 47, 79, 33, 36, 59, 8, 80, 23, 1, 18, 64, 51, 61, 81, 75, 40, 35, 73, 13, 65, + 12, 4, 19, 85, 96, 6, 2, 58, 27, 43, 5, 87, 20, 15, 3, 24, 84, 67, 74, 69}, + {38, 33, 29, 88, 49, 47, 60, 69, 75, 12, 44, 89, 35, 45, 23, 30, 21, 28, 65, 86, + 81, 40, 93, 97, 67, 2, 94, 9, 61, 92, 90, 32, 62, 36, 58, 51, 37, 73, 50, 63, + 39, 43, 16, 71, 46, 91, 13, 7, 20, 72, 41, 59, 70, 48, 10, 52, 5, 34, 76, 15, + 87, 6, 98, 18, 57, 77, 24, 79, 27, 19, 3, 0, 8, 95, 74, 64, 42, 4, 68, 31, + 84, 54, 99, 25, 1, 80, 85, 26, 83, 11, 56, 55, 78, 66, 82, 14, 53, 22, 17, 96}, + {26, 12, 62, 5, 78, 53, 31, 46, 51, 8, 69, 80, 32, 68, 76, 42, 29, 87, 28, 75, + 40, 91, 58, 7, 88, 92, 82, 96, 70, 50, 47, 65, 85, 3, 48, 73, 90, 89, 97, 84, + 81, 39, 43, 79, 64, 54, 2, 41, 38, 10, 24, 22, 25, 93, 44, 23, 49, 37, 14, 95, + 72, 74, 15, 11, 55, 98, 99, 0, 9, 16, 18, 20, 63, 30, 57, 19, 4, 17, 67, 35, + 60, 45, 59, 56, 36, 77, 66, 33, 27, 71, 86, 1, 13, 34, 21, 61, 6, 94, 52, 83}, + }, + { + {10, 87, 64, 11, 25, 97, 93, 18, 99, 38, 17, 72, 50, 23, 90, 57, 98, 12, 29, 46, + 19, 77, 42, 15, 73, 52, 53, 89, 35, 20, 9, 41, 79, 13, 95, 48, 85, 54, 61, 94, + 0, 74, 62, 40, 78, 68, 24, 28, 86, 21, 59, 14, 82, 84, 92, 58, 34, 31, 6, 16, + 75, 81, 43, 47, 7, 3, 2, 37, 67, 8, 66, 22, 30, 32, 71, 44, 70, 1, 36, 26, + 33, 63, 80, 83, 91, 76, 56, 39, 69, 55, 96, 60, 27, 49, 65, 88, 5, 4, 51, 45}, + {73, 87, 48, 43, 71, 40, 88, 23, 60, 35, 30, 5, 75, 25, 59, 95, 58, 79, 4, 89, + 96, 17, 38, 6, 39, 9, 68, 81, 3, 90, 93, 99, 16, 42, 21, 45, 74, 84, 77, 65, + 63, 98, 11, 51, 0, 33, 91, 22, 69, 34, 31, 20, 46, 2, 76, 54, 15, 62, 13, 70, + 92, 55, 82, 47, 78, 7, 24, 27, 86, 1, 56, 61, 12, 44, 85, 57, 49, 19, 18, 67, + 66, 52, 72, 53, 83, 41, 64, 50, 26, 94, 32, 80, 28, 10, 37, 14, 8, 97, 29, 36}, + {60, 50, 53, 78, 63, 90, 11, 33, 21, 24, 20, 55, 8, 25, 6, 58, 3, 98, 44, 82, + 96, 14, 1, 59, 92, 75, 51, 36, 30, 64, 72, 40, 16, 94, 2, 74, 93, 85, 12, 10, + 43, 32, 42, 39, 79, 97, 17, 38, 89, 29, 68, 52, 49, 0, 34, 19, 70, 84, 54, 9, + 23, 71, 65, 28, 83, 56, 67, 57, 13, 18, 77, 5, 4, 88, 41, 22, 46, 76, 48, 61, + 81, 45, 86, 35, 47, 87, 91, 99, 73, 66, 27, 37, 31, 15, 7, 80, 62, 95, 69, 26}, + {49, 73, 63, 66, 38, 35, 32, 96, 87, 26, 36, 92, 58, 6, 17, 47, 29, 84, 11, 18, + 67, 45, 78, 4, 52, 81, 59, 30, 91, 54, 56, 89, 48, 46, 20, 79, 1, 62, 9, 53, + 10, 95, 88, 34, 40, 80, 41, 64, 83, 55, 43, 7, 24, 61, 77, 5, 14, 93, 16, 21, + 85, 39, 76, 50, 57, 65, 37, 60, 28, 19, 2, 97, 42, 33, 13, 68, 90, 25, 69, 99, + 98, 74, 3, 8, 12, 15, 82, 71, 31, 23, 75, 27, 51, 94, 86, 0, 44, 22, 72, 70}, + {30, 32, 52, 21, 15, 79, 53, 40, 56, 44, 55, 6, 63, 39, 61, 4, 93, 43, 23, 99, + 38, 66, 88, 18, 1, 86, 33, 9, 82, 76, 70, 51, 48, 69, 85, 11, 5, 91, 59, 77, + 49, 75, 22, 87, 65, 41, 26, 50, 47, 98, 57, 10, 58, 62, 46, 28, 20, 71, 78, 95, + 0, 72, 80, 73, 92, 36, 96, 37, 68, 2, 35, 83, 90, 25, 13, 24, 34, 42, 45, 74, + 67, 16, 84, 97, 19, 64, 27, 14, 31, 12, 54, 60, 3, 8, 17, 89, 81, 94, 7, 29}, + {1, 18, 25, 13, 57, 53, 92, 95, 94, 88, 10, 82, 34, 83, 7, 28, 55, 11, 5, 58, + 8, 9, 74, 99, 52, 32, 62, 45, 65, 50, 41, 56, 96, 44, 38, 47, 3, 39, 81, 19, + 2, 22, 49, 12, 40, 67, 89, 76, 64, 70, 61, 21, 31, 54, 51, 17, 46, 30, 24, 60, + 66, 26, 77, 63, 37, 35, 23, 6, 4, 87, 79, 97, 20, 48, 36, 85, 73, 71, 27, 43, + 0, 15, 69, 78, 59, 86, 75, 33, 42, 84, 91, 93, 68, 80, 16, 98, 90, 72, 14, 29}, + {30, 16, 10, 67, 83, 81, 42, 94, 35, 59, 22, 5, 53, 61, 1, 96, 68, 45, 95, 66, + 36, 37, 97, 87, 77, 93, 86, 33, 49, 13, 43, 14, 0, 34, 7, 2, 41, 21, 31, 32, + 76, 55, 62, 72, 90, 71, 38, 23, 46, 12, 51, 19, 6, 44, 52, 48, 73, 24, 29, 74, + 85, 99, 80, 28, 47, 9, 39, 84, 64, 57, 75, 98, 88, 26, 17, 63, 27, 56, 69, 8, + 54, 25, 91, 78, 3, 50, 65, 70, 11, 4, 92, 60, 40, 58, 15, 89, 82, 18, 79, 20}, + {60, 88, 79, 35, 40, 43, 52, 4, 17, 31, 84, 44, 50, 94, 91, 38, 87, 25, 92, 62, + 14, 97, 37, 18, 86, 28, 65, 90, 19, 29, 30, 75, 68, 49, 69, 11, 59, 41, 89, 33, + 47, 54, 98, 42, 80, 56, 78, 99, 23, 82, 9, 48, 21, 1, 63, 22, 58, 95, 10, 12, + 26, 74, 85, 13, 8, 27, 24, 53, 61, 34, 64, 71, 66, 32, 83, 73, 15, 67, 0, 20, + 16, 6, 77, 45, 5, 93, 76, 39, 57, 2, 96, 46, 51, 3, 55, 7, 81, 70, 36, 72}, + {35, 56, 77, 58, 23, 83, 91, 81, 53, 70, 20, 74, 84, 50, 79, 51, 88, 44, 45, 71, + 37, 26, 54, 48, 27, 43, 0, 12, 95, 97, 1, 63, 67, 98, 59, 40, 13, 19, 31, 87, + 99, 16, 57, 30, 17, 92, 55, 68, 5, 46, 64, 8, 7, 42, 90, 33, 29, 9, 85, 24, + 2, 69, 32, 10, 21, 65, 39, 86, 52, 62, 76, 89, 82, 6, 78, 4, 47, 28, 3, 75, + 41, 15, 18, 14, 66, 73, 38, 80, 61, 49, 11, 22, 34, 93, 36, 25, 94, 96, 60, 72}, + {6, 90, 16, 43, 20, 55, 94, 8, 62, 59, 47, 32, 81, 67, 42, 35, 71, 19, 54, 49, + 2, 88, 38, 5, 53, 77, 85, 11, 73, 34, 3, 72, 95, 58, 82, 64, 44, 29, 93, 14, + 56, 98, 4, 37, 45, 75, 86, 60, 87, 7, 31, 17, 89, 97, 10, 70, 36, 99, 46, 63, + 76, 41, 48, 84, 9, 52, 1, 24, 83, 25, 68, 18, 50, 26, 30, 27, 61, 0, 91, 33, + 51, 40, 79, 39, 15, 74, 28, 78, 69, 96, 57, 65, 21, 80, 22, 23, 12, 13, 92, 66}, + }, + { + {93, 42, 18, 65, 28, 87, 39, 35, 71, 23, 96, 90, 15, 58, 78, 33, 92, 88, 49, 64, + 24, 84, 77, 8, 97, 50, 4, 53, 27, 83, 95, 31, 80, 47, 86, 55, 11, 40, 37, 14, + 34, 60, 54, 12, 94, 9, 41, 2, 26, 10, 6, 57, 75, 63, 32, 43, 13, 52, 82, 73, + 20, 30, 17, 5, 3, 68, 51, 89, 62, 79, 69, 70, 66, 45, 25, 22, 85, 0, 91, 76, + 38, 7, 99, 61, 29, 98, 16, 36, 44, 67, 59, 56, 48, 81, 21, 46, 74, 72, 19, 1}, + {78, 91, 97, 77, 75, 85, 32, 2, 40, 86, 38, 18, 56, 47, 50, 87, 27, 92, 64, 39, + 98, 23, 33, 72, 84, 58, 16, 99, 19, 65, 55, 73, 74, 42, 4, 48, 95, 8, 51, 3, + 37, 82, 96, 69, 14, 44, 12, 21, 76, 70, 36, 15, 71, 41, 20, 31, 54, 13, 0, 9, + 94, 67, 1, 60, 81, 61, 11, 6, 90, 5, 49, 10, 34, 7, 35, 30, 25, 88, 79, 93, + 66, 62, 22, 24, 52, 17, 89, 45, 83, 57, 28, 46, 63, 68, 26, 29, 80, 59, 43, 53}, + {78, 91, 97, 77, 75, 85, 32, 2, 40, 86, 38, 18, 56, 47, 50, 87, 27, 92, 64, 39, + 98, 23, 33, 72, 84, 58, 16, 99, 19, 65, 55, 73, 74, 42, 4, 48, 95, 8, 51, 3, + 37, 82, 96, 69, 14, 44, 12, 21, 76, 70, 36, 15, 71, 41, 20, 31, 54, 13, 0, 9, + 94, 67, 1, 60, 81, 61, 11, 6, 90, 5, 49, 10, 34, 7, 35, 30, 25, 88, 79, 93, + 66, 62, 22, 24, 52, 17, 89, 45, 83, 57, 28, 46, 63, 68, 26, 29, 80, 59, 43, 53}, + {41, 65, 16, 86, 77, 67, 51, 81, 50, 25, 90, 87, 98, 99, 23, 9, 4, 97, 39, 36, + 26, 42, 13, 66, 82, 22, 47, 88, 49, 46, 59, 78, 20, 19, 7, 93, 56, 84, 40, 61, + 89, 21, 74, 1, 54, 44, 34, 6, 28, 8, 43, 76, 48, 71, 85, 38, 83, 18, 24, 79, + 68, 80, 45, 33, 91, 27, 32, 70, 95, 3, 58, 60, 73, 10, 75, 52, 64, 2, 94, 30, + 17, 55, 62, 35, 11, 12, 53, 63, 14, 31, 57, 0, 37, 72, 92, 15, 5, 29, 69, 96}, + {74, 50, 44, 80, 11, 46, 39, 54, 24, 3, 7, 62, 41, 8, 75, 23, 57, 82, 51, 86, + 97, 79, 22, 60, 42, 14, 28, 67, 32, 73, 48, 56, 89, 26, 25, 77, 21, 68, 10, 2, + 58, 76, 92, 95, 38, 29, 43, 35, 12, 16, 55, 17, 63, 91, 45, 64, 59, 13, 47, 31, + 83, 5, 99, 49, 78, 71, 96, 53, 36, 90, 1, 66, 15, 0, 85, 65, 27, 4, 98, 94, + 6, 84, 72, 30, 70, 20, 61, 69, 37, 93, 34, 87, 9, 52, 88, 81, 33, 19, 40, 18}, + {35, 67, 27, 53, 26, 79, 20, 46, 38, 4, 72, 84, 70, 58, 65, 91, 92, 21, 32, 71, + 54, 24, 89, 0, 37, 8, 22, 81, 12, 87, 43, 18, 82, 17, 33, 76, 25, 13, 74, 51, + 99, 3, 55, 60, 19, 73, 86, 62, 93, 52, 31, 64, 96, 66, 14, 40, 42, 69, 45, 6, + 61, 34, 41, 59, 49, 10, 39, 2, 75, 80, 15, 94, 7, 23, 95, 78, 90, 5, 1, 28, + 56, 30, 9, 98, 44, 48, 68, 77, 47, 11, 63, 83, 88, 36, 97, 29, 57, 50, 16, 85}, + {8, 51, 32, 96, 34, 24, 88, 54, 87, 83, 45, 99, 49, 30, 55, 61, 95, 60, 59, 76, + 57, 5, 19, 56, 89, 66, 27, 94, 14, 6, 21, 58, 43, 74, 10, 2, 17, 62, 47, 16, + 69, 84, 63, 39, 4, 92, 35, 48, 53, 64, 71, 50, 72, 28, 42, 46, 91, 23, 15, 40, + 11, 78, 29, 86, 13, 90, 85, 20, 65, 93, 73, 80, 41, 37, 82, 79, 52, 9, 97, 38, + 75, 25, 44, 7, 26, 12, 22, 0, 33, 1, 31, 68, 70, 67, 98, 18, 77, 36, 3, 81}, + {34, 17, 91, 45, 63, 64, 1, 71, 98, 78, 94, 3, 99, 12, 15, 93, 69, 19, 61, 80, + 92, 81, 33, 51, 40, 10, 75, 24, 5, 87, 73, 46, 52, 86, 4, 70, 89, 56, 2, 67, + 72, 58, 49, 84, 29, 18, 22, 38, 95, 36, 43, 35, 37, 85, 57, 8, 76, 7, 27, 28, + 55, 74, 42, 13, 77, 31, 60, 39, 48, 68, 97, 79, 44, 54, 88, 82, 21, 66, 9, 23, + 59, 50, 47, 16, 83, 25, 6, 30, 41, 96, 90, 14, 62, 20, 65, 11, 26, 32, 53, 0}, + {7, 51, 77, 80, 0, 53, 56, 27, 2, 78, 28, 32, 26, 22, 73, 93, 36, 54, 64, 99, + 60, 50, 21, 11, 39, 42, 81, 8, 1, 79, 61, 5, 15, 31, 59, 33, 20, 94, 23, 88, + 69, 17, 35, 95, 40, 75, 85, 87, 43, 47, 89, 12, 71, 72, 9, 83, 67, 38, 90, 82, + 29, 10, 37, 91, 97, 74, 6, 86, 30, 63, 24, 49, 3, 13, 4, 57, 76, 70, 98, 34, + 58, 52, 96, 84, 25, 55, 44, 46, 18, 65, 62, 68, 48, 66, 41, 19, 14, 45, 16, 92}, + {96, 2, 76, 15, 31, 22, 71, 38, 88, 26, 67, 0, 21, 10, 58, 39, 63, 77, 23, 50, + 48, 19, 78, 30, 93, 91, 14, 12, 28, 11, 53, 73, 60, 61, 35, 79, 57, 70, 40, 20, + 56, 32, 65, 64, 68, 55, 82, 94, 45, 95, 66, 7, 43, 16, 37, 99, 72, 62, 13, 4, + 47, 8, 98, 89, 46, 29, 51, 85, 74, 17, 97, 84, 33, 18, 25, 83, 69, 44, 87, 49, + 92, 9, 90, 36, 41, 81, 59, 54, 34, 6, 42, 5, 80, 3, 1, 86, 27, 52, 24, 75}, + }, + { + {61, 85, 41, 37, 12, 25, 27, 17, 92, 52, 44, 66, 13, 15, 19, 78, 77, 9, 40, 20, + 58, 43, 76, 24, 59, 42, 28, 30, 96, 86, 62, 89, 67, 35, 45, 6, 36, 50, 74, 26, + 84, 95, 7, 68, 97, 79, 51, 16, 80, 3, 82, 2, 93, 21, 31, 98, 18, 60, 39, 72, + 54, 65, 73, 32, 5, 70, 99, 10, 14, 71, 34, 4, 64, 33, 55, 48, 75, 87, 57, 81, + 22, 46, 69, 63, 90, 23, 38, 56, 8, 29, 83, 88, 11, 0, 47, 53, 91, 94, 49, 1}, + {35, 22, 67, 24, 94, 82, 12, 68, 30, 43, 58, 96, 27, 69, 65, 18, 7, 79, 14, 2, + 44, 60, 54, 47, 51, 83, 89, 3, 20, 11, 40, 85, 37, 48, 49, 8, 73, 15, 90, 93, + 70, 84, 34, 4, 86, 98, 17, 46, 72, 63, 26, 56, 97, 74, 91, 99, 36, 80, 81, 41, + 53, 88, 77, 21, 0, 23, 64, 76, 95, 33, 62, 1, 75, 87, 10, 66, 57, 6, 28, 25, + 42, 9, 50, 39, 29, 78, 13, 19, 31, 45, 55, 61, 38, 71, 59, 5, 92, 52, 32, 16}, + {70, 29, 50, 57, 31, 9, 59, 45, 87, 71, 98, 89, 77, 97, 86, 5, 21, 61, 47, 41, + 76, 37, 65, 12, 58, 75, 78, 91, 27, 63, 64, 83, 55, 95, 4, 11, 82, 23, 39, 40, + 42, 68, 69, 48, 54, 13, 99, 33, 26, 0, 66, 79, 46, 72, 49, 44, 25, 74, 73, 28, + 38, 10, 2, 85, 15, 19, 52, 94, 7, 36, 17, 32, 96, 3, 20, 30, 92, 51, 14, 90, + 24, 67, 43, 34, 93, 84, 62, 81, 1, 56, 53, 88, 80, 35, 22, 6, 18, 16, 8, 60}, + {62, 75, 38, 50, 52, 86, 53, 73, 99, 59, 26, 46, 48, 90, 64, 1, 65, 13, 30, 36, + 57, 16, 97, 91, 51, 33, 80, 67, 5, 25, 8, 96, 94, 84, 6, 89, 88, 20, 10, 85, + 41, 56, 9, 34, 63, 0, 18, 12, 42, 15, 87, 4, 61, 43, 82, 17, 98, 60, 92, 70, + 49, 71, 47, 54, 79, 83, 27, 74, 58, 2, 76, 93, 22, 69, 78, 44, 28, 95, 72, 68, + 7, 55, 29, 77, 14, 23, 31, 32, 3, 37, 11, 66, 35, 39, 24, 81, 19, 21, 40, 45}, + {65, 78, 7, 83, 80, 77, 71, 72, 22, 85, 63, 73, 44, 87, 86, 88, 41, 30, 95, 67, + 58, 21, 4, 5, 26, 27, 75, 31, 99, 52, 92, 64, 28, 8, 19, 98, 84, 34, 9, 48, + 60, 0, 56, 1, 62, 16, 91, 32, 89, 79, 40, 11, 54, 51, 43, 12, 45, 66, 23, 13, + 96, 81, 47, 36, 46, 93, 2, 69, 33, 3, 20, 6, 35, 18, 74, 68, 53, 37, 29, 61, + 90, 57, 14, 42, 24, 76, 10, 38, 97, 39, 25, 50, 49, 70, 55, 82, 15, 17, 59, 94}, + {11, 17, 21, 48, 63, 0, 73, 14, 10, 88, 2, 28, 6, 9, 19, 69, 81, 13, 35, 95, + 60, 56, 30, 47, 91, 92, 86, 29, 76, 50, 16, 25, 82, 93, 8, 5, 58, 94, 98, 20, + 74, 77, 4, 52, 97, 79, 27, 42, 36, 12, 26, 72, 71, 45, 24, 87, 1, 39, 22, 89, + 53, 31, 61, 37, 33, 18, 59, 43, 80, 15, 54, 38, 78, 90, 32, 83, 23, 44, 55, 66, + 64, 3, 67, 49, 84, 62, 51, 41, 46, 85, 96, 34, 99, 75, 7, 40, 70, 68, 65, 57}, + {43, 38, 48, 47, 74, 88, 64, 54, 59, 35, 29, 18, 53, 91, 68, 66, 12, 79, 55, 41, + 34, 36, 33, 94, 56, 87, 25, 6, 1, 8, 63, 96, 93, 44, 40, 65, 13, 77, 61, 85, + 99, 80, 11, 51, 14, 46, 5, 39, 92, 27, 75, 52, 72, 83, 19, 62, 32, 24, 82, 95, + 17, 37, 0, 15, 97, 4, 42, 86, 84, 3, 26, 81, 98, 9, 73, 22, 28, 78, 60, 76, + 16, 23, 71, 89, 31, 57, 58, 20, 50, 10, 49, 45, 21, 67, 90, 30, 7, 2, 70, 69}, + {78, 35, 94, 59, 8, 23, 10, 76, 96, 80, 93, 9, 27, 95, 61, 84, 88, 91, 2, 33, + 1, 85, 3, 36, 79, 18, 92, 5, 34, 81, 98, 40, 38, 68, 90, 70, 89, 65, 48, 6, + 77, 30, 72, 62, 16, 83, 32, 54, 99, 82, 75, 41, 53, 17, 67, 43, 28, 21, 46, 7, + 25, 64, 13, 14, 87, 58, 63, 31, 39, 47, 24, 71, 74, 55, 20, 11, 22, 45, 44, 51, + 37, 15, 42, 56, 66, 29, 86, 49, 69, 57, 73, 50, 60, 0, 4, 19, 52, 97, 12, 26}, + {88, 86, 41, 1, 70, 10, 19, 2, 11, 44, 87, 26, 53, 59, 40, 18, 61, 13, 21, 56, + 85, 75, 25, 5, 97, 7, 36, 67, 76, 79, 90, 65, 91, 0, 98, 89, 20, 55, 93, 58, + 28, 63, 6, 83, 47, 71, 73, 49, 39, 31, 48, 69, 82, 50, 78, 62, 14, 74, 29, 54, + 57, 80, 32, 94, 3, 66, 30, 35, 8, 46, 16, 51, 24, 17, 4, 84, 45, 60, 34, 15, + 72, 77, 42, 12, 64, 9, 33, 95, 81, 96, 38, 43, 52, 99, 22, 92, 27, 23, 37, 68}, + {90, 93, 83, 21, 96, 71, 67, 28, 37, 75, 55, 58, 56, 39, 16, 46, 91, 41, 77, 92, + 99, 65, 27, 5, 24, 1, 69, 7, 31, 35, 33, 64, 12, 14, 45, 11, 98, 0, 57, 72, + 74, 13, 61, 40, 63, 89, 53, 85, 18, 76, 2, 70, 20, 60, 84, 52, 25, 43, 54, 17, + 36, 19, 97, 73, 94, 62, 3, 23, 34, 88, 29, 80, 50, 22, 81, 87, 6, 30, 78, 48, + 79, 44, 66, 8, 51, 86, 4, 15, 42, 10, 82, 32, 68, 26, 38, 47, 49, 9, 95, 59}, + }, +}; + +static const uint8_t access_code_table_2[10][10] = { + {4, 7, 8, 0, 9, 1, 3, 6, 2, 5}, + {5, 0, 4, 1, 3, 8, 9, 7, 6, 2}, + {0, 2, 7, 8, 9, 1, 6, 3, 5, 4}, + {5, 9, 8, 4, 1, 7, 0, 2, 3, 6}, + {7, 3, 0, 1, 8, 9, 6, 5, 2, 4}, + {7, 2, 4, 0, 1, 9, 6, 3, 5, 8}, + {4, 1, 6, 3, 5, 8, 0, 7, 2, 9}, + {6, 8, 4, 1, 5, 3, 7, 2, 9, 0}, + {4, 1, 8, 3, 7, 2, 0, 6, 5, 9}, + {7, 4, 5, 6, 2, 3, 9, 1, 8, 0}}; + +static void rotate_right(uint8_t* data, int n_bytes, int n_bits) { + uint8_t prior = data[n_bytes - 1]; + for(int i = 0; i < n_bytes; i++) { + uint8_t tmp = data[i]; + data[i] = (data[i] >> n_bits) | ((prior & ((1 << n_bits) - 1)) << (8 - n_bits)); + prior = tmp; + } +} + +static void decrypt_spad_0(const uint8_t* spad, uint8_t* decrypted) { + for(int i = 0; i < 16; i++) { + decrypted[i] = s_box[N_TABLES][spad[i]]; + } + + int count = (decrypted[15] >> 4) + 7; + int table = decrypted[15] + ITERATION_ADD * count; + + for(int iter = 0; iter < count; iter++) { + table -= ITERATION_ADD; + rotate_right(decrypted, 15, 5); // only the first 15 bytes + for(int i = 0; i < 15; i++) { + decrypted[i] = s_box[table % N_TABLES][decrypted[i]]; + } + } +} + +static uint16_t crc16(uint64_t data, int bits, uint16_t init, uint16_t poly) { + uint16_t v = init; + for(int i = 0; i < bits; ++i) { + v = (v >> 1) ^ (((v ^ data) & 1ULL) ? poly : 0); + data >>= 1; + } + return v; +} + +static bool + check_access_code_crc(const uint8_t ac[3], const uint8_t body[6], const uint8_t crc[5]) { + uint64_t msg = 0; + for(int i = 0; i < 3; ++i) { + if(ac[i] > 0xF) return false; + msg = (msg << 4) | ac[i]; + } + for(int i = 0; i < 6; ++i) { + uint8_t v = body[i]; + if(v > 99) return false; + msg = (msg << 4) | (v / 10); + msg = (msg << 4) | (v % 10); + } + uint16_t calculated_crc = crc16(msg, 60, 0xFFFF, 0x8408); + uint16_t expected_crc = crc[0] * 10000 + crc[1] * 1000 + crc[2] * 100 + crc[3] * 10 + crc[4]; + return (calculated_crc == expected_crc); +} + +static void parse_access_code(const uint8_t* access_code, FuriString* parsed_data) { + furi_assert(access_code); + furi_assert(parsed_data); + + uint8_t decrypted[6]; + // decrypted contains the decoded serial bytes (as 6 BCD bytes) + for(int i = 0, j = 3; i < 6; ++i, j += 2) { + decrypted[i] = (access_code[j]) * 10 + (access_code[j + 1]); + } + + uint8_t crc[5] = {0}; + memcpy(crc, access_code + 15, 5); + + int boxes1[6] = { + (crc[3] + crc[2]) % 10, crc[2], crc[3], crc[4], (crc[4] + crc[0]) % 10, crc[1]}; + + for(int n = 0; n < 6; ++n) { + decrypted[n] = access_code_table_1[4][boxes1[n]][decrypted[n]]; + } + + int rv = decrypted[0] / 10; + + int boxes2[6] = { + (crc[1] + crc[0]) % 10, crc[1], crc[2], crc[3], crc[4], (crc[4] + crc[1]) % 10}; + for(int n = 0; n < 6; ++n) { + if(n == 0) { + decrypted[n] = (decrypted[n] & 0xF0) | + access_code_table_2[boxes2[n]][decrypted[n] & 0x0F]; + } else { + decrypted[n] = access_code_table_1[rv][boxes2[n]][decrypted[n]]; + } + } + + furi_string_cat_printf( + parsed_data, + "Decrypted serial number:\n%02d%02d%02d%02d%02d%02d\n", + decrypted[0], + decrypted[1], + decrypted[2], + decrypted[3], + decrypted[4], + decrypted[5]); + + furi_string_cat_printf( + parsed_data, + "CRC check: %s\n", + check_access_code_crc(access_code, decrypted, crc) ? "Passed" : "Invalid"); +} + +bool aic_parse(const NfcDevice* device, FuriString* parsed_data) { + furi_assert(device); + furi_assert(parsed_data); + bool parsed = false; + + if(nfc_device_get_protocol(device) != NfcProtocolFelica) return false; + + const FelicaData* data = nfc_device_get_data(device, NfcProtocolFelica); + + const uint8_t ic_type = data->pmm.data[1]; + if(ic_type != 0xF0 && ic_type != 0xF1) { + // Must be Felica Lite (0xF0) or Lite-S (0xF1) to parse + return false; + } + + const uint8_t data_format_code_1 = data->data.fs.id.data[8]; + if(data_format_code_1 != 0) { + // We only know Data Format Code {0x00, 0xXX} + return false; + } + + parsed = true; + furi_string_printf(parsed_data, "\e#Amusement IC Card\n"); + furi_string_cat_str( + parsed_data, "::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::"); + furi_string_cat_str(parsed_data, "\nType:\n"); + + // Determine card brand and type + const uint8_t data_format_code_2 = data->data.fs.id.data[9]; + switch(data_format_code_2) { + case 0x2A: + furi_string_cat_str(parsed_data, "Bandai Namco Passport\n"); + break; + case 0x3A: + furi_string_cat_str(parsed_data, "Bandai Namco Passport (Old)\n"); + break; + case 0x68: + furi_string_cat_str(parsed_data, "Konami e-amusement pass\n"); + break; + case 0x78: + furi_string_cat_str(parsed_data, "SEGA Aime\n"); + break; + case 0x79: + furi_string_cat_str(parsed_data, "Taito NESiCA\n"); + break; + default: + parsed = false; + return parsed; // Unknown vendor + } + furi_string_cat_str( + parsed_data, "::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::"); + + // decrypt_spad_0 S-PAD 0 + uint8_t decrypted[16] = {0}; + decrypt_spad_0(data->data.fs.spad[0].data, decrypted); + + // Get Access Code + uint8_t access_code[20] = {0}; + for(int i = 0; i < 10; i++) { + access_code[i * 2] = (decrypted[i + 6] & 0xF0) >> 4; // Get upper nibble + access_code[i * 2 + 1] = decrypted[i + 6] & 0x0F; // Get lower nibble + } + furi_string_cat_str(parsed_data, "\nAccess Code:\n"); + bool access_code_is_bcd = true; + for(int i = 0; i < 20; i++) { + furi_string_cat_printf(parsed_data, "%d", access_code[i]); + if(i % 4 == 3) { + furi_string_cat_str(parsed_data, " "); + } + if(access_code[i] > 9) access_code_is_bcd = false; + } + furi_string_cat_str(parsed_data, "\n"); + + furi_string_cat_printf(parsed_data, "BCD valid: %s\n", access_code_is_bcd ? "Yes" : "No"); + furi_string_cat_str( + parsed_data, "::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::"); + + // Parse Access Code + if(access_code_is_bcd && access_code[0] == 5) { + furi_string_cat_str(parsed_data, "\n"); + parse_access_code(access_code, parsed_data); + } else { + furi_string_cat_printf( + parsed_data, "\nAccess code preamble wrong: expected 5, got %d\n", access_code[0]); + } + + furi_string_cat_str( + parsed_data, "::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::"); + furi_string_cat_str(parsed_data, "\nDecrypted S-PAD 0:\n"); + for(int i = 0; i < 16; i++) { + furi_string_cat_printf(parsed_data, "%02X ", decrypted[i]); + if(i == 7) { + furi_string_cat_str(parsed_data, "\n"); + } + } + furi_string_cat_str(parsed_data, "\n"); + furi_string_cat_str( + parsed_data, "::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::"); + + return parsed; +} + +/* Actual implementation of app<>plugin interface */ +static const NfcSupportedCardsPlugin aic_plugin = { + .protocol = NfcProtocolFelica, + .verify = NULL, + .read = NULL, + .parse = aic_parse, +}; + +/* Plugin descriptor to comply with basic plugin specification */ +static const FlipperAppPluginDescriptor aic_plugin_descriptor = { + .appid = NFC_SUPPORTED_CARD_PLUGIN_APP_ID, + .ep_api_version = NFC_SUPPORTED_CARD_PLUGIN_API_VERSION, + .entry_point = &aic_plugin, +}; + +/* Plugin entry point - must return a pointer to const descriptor */ +const FlipperAppPluginDescriptor* aic_plugin_ep(void) { + return &aic_plugin_descriptor; +} From 973fb26f575b70083f328bfc56e409d15a972fd0 Mon Sep 17 00:00:00 2001 From: minchogaydarov <134236905+minchogaydarov@users.noreply.github.com> Date: Wed, 24 Sep 2025 18:23:01 +0300 Subject: [PATCH 2/3] Add Daikin FTXN25LV1B9 and Toyotomi KTN22-12R32 (#4232) Co-authored-by: hedger --- .../infrared/resources/infrared/assets/ac.ir | 76 +++++++++++++++++++ 1 file changed, 76 insertions(+) diff --git a/applications/main/infrared/resources/infrared/assets/ac.ir b/applications/main/infrared/resources/infrared/assets/ac.ir index 96b4eb1c8..a7555e007 100644 --- a/applications/main/infrared/resources/infrared/assets/ac.ir +++ b/applications/main/infrared/resources/infrared/assets/ac.ir @@ -1935,3 +1935,79 @@ frequency: 38000 duty_cycle: 0.330000 data: 39670 99106 3227 1570 424 406 422 407 422 1183 424 405 424 1184 448 380 423 407 421 406 424 1183 424 1185 421 406 423 404 424 405 424 1184 423 1182 425 407 448 380 423 407 422 405 423 406 422 406 424 405 423 407 421 406 423 407 422 405 424 405 423 406 423 1184 422 408 421 408 422 405 424 406 421 407 422 406 423 405 423 1183 424 406 423 405 423 405 423 405 423 1186 421 1184 422 1184 422 1185 422 1184 447 1159 423 1184 422 1184 422 408 421 407 423 1184 421 407 448 381 422 405 423 409 421 406 422 406 422 407 422 406 423 1183 423 1185 422 406 423 405 424 1184 423 408 421 405 424 405 424 1184 422 1185 422 1184 422 407 423 408 420 409 420 1185 447 382 423 405 423 408 421 406 423 407 422 406 423 406 423 408 421 406 423 1183 424 407 422 406 424 405 424 406 423 407 423 406 423 408 422 407 422 405 424 408 421 407 422 407 422 406 423 406 423 407 422 406 423 406 422 408 421 407 422 408 421 407 422 406 423 408 422 406 423 405 423 409 422 406 422 406 423 406 423 407 422 407 423 405 424 1184 423 407 421 406 424 1184 423 1184 422 407 423 1183 423 405 424 1184 423 409 420 407 422 # +# Model: Daikin FTXN25LV1B9 +# +name: Off +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 9876 9703 9956 9681 4738 2366 489 310 423 822 488 822 488 311 422 823 487 311 423 310 424 310 423 310 424 823 487 311 422 310 424 823 487 310 424 311 422 310 424 311 422 823 487 311 422 310 423 310 424 823 488 311 423 310 423 310 423 823 488 310 423 310 424 310 424 823 487 310 424 311 423 824 487 823 487 823 487 310 423 823 487 311 423 311 423 310 424 311 422 824 487 310 423 311 422 824 487 311 422 310 424 310 423 824 486 824 487 310 424 310 423 311 423 824 487 311 422 310 423 310 424 310 423 824 486 824 486 310 424 824 486 824 486 824 486 20121 4685 +# +name: Dh +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 9907 9648 9958 9677 4742 2362 492 311 422 820 490 820 435 312 477 821 434 312 421 312 422 312 422 876 434 312 422 312 422 312 422 876 434 312 422 312 422 312 421 312 422 876 434 312 422 312 421 312 422 876 434 312 422 311 422 312 477 821 434 312 422 312 421 312 477 821 434 312 422 312 422 876 434 877 434 876 434 312 422 876 434 312 422 311 422 312 422 312 422 876 434 312 422 312 422 876 434 312 422 312 421 312 422 877 433 876 434 312 422 312 422 312 422 876 434 312 422 312 421 312 422 312 422 877 433 312 422 877 433 312 422 877 433 312 422 20174 4740 +# +name: Heat_lo +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 9827 9757 9876 9732 4687 2418 435 311 422 876 434 876 434 311 423 877 433 312 421 311 423 311 422 311 423 311 423 311 423 877 433 877 433 311 423 311 423 312 422 877 433 877 433 311 423 311 423 311 423 877 434 311 422 311 423 311 422 877 433 311 423 312 421 312 422 877 433 311 423 312 421 877 433 877 433 877 434 311 423 877 433 311 423 311 423 311 422 311 423 877 433 312 422 311 423 877 433 311 423 312 422 311 423 877 433 878 433 311 423 312 422 312 422 877 433 311 423 311 422 311 423 312 422 878 432 312 422 877 433 311 423 877 433 878 433 20174 4685 +# +name: Heat_hi +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 9898 9680 9901 9734 4685 2420 434 312 421 876 434 876 434 312 422 876 434 312 422 312 422 312 422 312 421 312 422 312 422 877 433 877 433 312 422 312 421 312 422 877 433 877 433 312 422 312 422 312 422 876 434 312 422 312 422 312 422 877 434 312 422 312 421 312 422 877 433 312 421 312 422 877 433 877 434 877 433 312 422 877 433 312 422 312 421 312 422 312 422 877 433 312 421 312 422 877 433 312 422 312 422 312 421 312 422 312 422 312 422 312 421 877 433 877 434 312 421 312 422 312 422 312 422 877 433 312 422 877 433 877 433 312 421 877 433 20174 4684 +# +name: Cool_hi +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 9932 9649 9959 9677 4742 2362 492 311 422 819 491 819 491 311 422 820 490 311 423 311 423 311 423 311 422 820 490 310 423 310 424 820 490 311 423 311 423 310 424 820 490 311 422 820 491 311 423 311 422 820 491 311 422 311 423 311 423 820 490 311 423 311 423 311 423 820 490 310 424 311 423 820 490 820 490 820 490 310 424 820 490 311 423 310 424 310 424 311 422 820 491 311 422 310 424 820 490 310 424 311 423 310 423 311 423 820 490 820 490 311 423 820 490 311 422 311 423 311 423 311 423 311 422 821 490 311 422 821 490 820 490 311 422 821 490 20118 4740 +# +name: Cool_lo +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 9878 9704 9903 9733 4687 2418 435 311 422 875 435 875 435 311 423 875 435 311 423 311 422 311 423 311 423 875 435 311 423 311 423 875 435 311 423 311 423 311 422 876 434 311 423 876 434 311 423 311 423 876 434 311 423 311 423 311 423 876 434 311 423 311 423 311 423 875 435 311 422 311 423 876 434 876 435 876 434 311 423 876 434 311 423 311 423 311 422 311 423 876 434 311 422 311 423 876 434 311 423 311 422 311 423 876 434 876 434 311 423 311 422 311 423 876 435 311 422 311 422 311 423 311 423 876 434 311 423 876 434 311 423 311 423 876 434 20174 4685 +# +# Model: Toyotomi KTN22-12R32 +# +name: Off +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 9067 4414 724 1584 753 452 754 453 753 478 728 478 727 479 726 480 726 481 725 1584 725 1585 724 1584 725 482 724 482 725 482 725 482 724 483 724 483 723 482 725 483 723 483 723 482 724 1585 724 482 724 482 724 483 724 482 725 483 723 483 723 1584 724 482 724 1585 723 482 724 483 723 1585 723 483 723 19925 724 483 724 1585 724 482 724 482 724 482 724 482 724 483 724 482 724 482 724 1585 724 483 724 483 724 482 725 483 724 1585 723 1585 724 483 723 483 724 483 724 483 724 482 723 483 724 482 724 482 724 483 723 483 723 483 724 483 723 483 724 1585 724 1585 723 1585 724 39949 9071 4415 725 482 725 481 725 482 725 483 723 481 725 482 724 483 724 482 724 482 724 482 724 482 724 482 725 483 723 483 723 482 725 483 722 483 724 483 723 482 724 483 724 483 723 482 724 483 723 483 724 483 723 483 723 483 723 483 724 482 724 1585 723 483 724 1585 725 482 724 1585 724 483 724 19926 724 483 723 482 725 482 724 482 724 482 725 482 724 482 725 483 724 482 723 482 725 483 723 482 725 482 725 483 723 483 724 483 723 483 724 483 723 482 724 483 723 483 724 482 725 482 724 483 724 483 723 482 724 483 723 483 723 482 725 1585 723 483 724 1585 724 +# +name: Dh +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 9042 4416 751 454 753 1555 753 453 754 1556 753 1558 750 479 726 480 726 482 724 1584 725 481 724 482 725 1585 725 482 725 482 724 482 724 482 725 482 725 483 723 483 724 482 724 482 724 1586 723 1585 723 482 724 483 724 483 724 483 724 482 725 1585 724 482 724 1585 724 484 723 482 724 1585 723 483 724 19928 723 482 725 482 724 482 725 483 724 483 724 482 725 482 724 483 724 483 723 1585 723 482 724 483 723 482 724 483 724 1585 724 1585 724 482 724 482 724 482 725 482 725 482 724 483 724 483 723 483 724 482 724 483 724 483 724 483 725 1585 724 483 723 482 724 1585 723 39946 9070 4414 725 481 725 481 725 482 724 482 724 482 724 481 725 482 724 482 724 482 725 483 723 482 725 483 724 482 725 482 724 482 724 482 724 483 723 483 724 483 724 482 725 483 724 482 724 482 725 483 723 482 725 483 723 482 724 483 724 482 724 1584 724 482 724 1585 724 483 724 1586 723 482 725 19926 724 482 724 482 724 482 725 483 723 482 725 482 725 482 724 483 724 482 724 482 725 483 724 482 725 483 723 483 723 483 723 482 724 483 724 483 723 482 724 483 724 482 724 483 723 483 723 483 724 483 723 483 723 482 724 483 723 483 724 1585 724 483 723 1586 723 +# +name: Heat_lo +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 9040 4431 702 503 702 503 702 1604 758 1548 756 448 729 475 730 475 729 477 728 1579 726 1579 727 1580 726 479 726 481 724 480 724 481 724 482 724 482 723 482 723 482 723 482 723 483 722 1583 724 1584 722 483 723 483 722 482 723 483 722 483 723 1584 722 482 724 1584 722 482 723 483 722 1585 721 483 723 19907 724 481 723 482 723 482 723 482 723 482 723 482 723 483 723 483 722 482 723 1584 722 482 722 482 723 483 723 482 723 1584 723 1584 721 483 722 483 722 483 722 484 721 482 723 483 723 483 722 483 722 483 723 483 722 484 721 484 721 1607 699 483 722 483 722 1608 698 39905 9065 4406 726 479 726 480 724 480 725 481 725 481 724 482 723 482 723 482 724 482 723 483 723 482 723 482 723 482 723 482 724 483 722 482 723 483 723 483 723 483 722 483 723 483 722 483 722 483 722 507 699 482 723 484 722 483 722 483 722 483 722 1607 699 483 722 1585 722 483 722 1584 723 506 698 19908 723 481 724 482 723 482 724 482 722 482 724 482 724 482 723 483 722 482 723 483 722 482 723 483 722 483 722 483 722 482 723 506 699 483 722 484 722 482 723 507 700 484 721 483 722 484 722 483 722 507 698 506 699 507 699 507 698 507 698 1608 698 507 698 1607 699 +# +name: Heat_hi +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 9074 4437 701 506 700 506 700 1607 702 1607 702 505 701 505 701 505 726 480 727 480 726 1582 726 1584 725 1583 724 481 726 481 725 482 724 483 724 482 724 482 725 482 724 482 725 483 724 1584 725 1584 725 482 724 482 726 482 724 482 725 483 724 1584 724 483 723 1585 723 483 724 482 725 1584 725 483 723 19925 724 482 724 482 724 482 725 482 725 482 725 483 724 482 724 482 724 482 724 1584 724 483 724 482 724 481 724 482 724 1585 724 1585 724 482 724 482 724 482 725 483 723 483 724 483 724 482 724 482 724 483 723 482 725 482 724 482 724 483 724 482 724 483 724 483 723 39945 9072 4414 725 481 726 481 725 482 724 482 725 481 725 482 724 482 724 482 725 482 724 483 724 483 724 483 723 482 725 482 724 482 724 483 723 482 724 483 724 483 723 483 724 483 724 482 725 482 725 483 724 483 724 482 725 483 724 482 724 482 725 1585 724 483 724 1584 724 482 724 1585 724 483 723 19929 723 482 724 482 725 482 724 483 724 483 723 483 723 482 724 482 724 482 724 482 724 482 725 482 724 482 724 483 724 483 723 482 724 482 724 483 724 483 723 482 725 483 724 482 724 482 725 482 724 483 724 483 724 483 724 483 724 482 724 1585 723 483 724 1585 724 +# +name: Cool_hi +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 9150 4356 783 1525 815 419 787 419 759 1549 760 447 758 447 758 449 756 451 755 451 755 451 754 452 754 453 754 452 754 453 753 453 753 454 753 454 752 453 753 454 752 454 753 455 751 1557 725 1585 725 481 725 481 725 482 725 482 725 482 724 1584 725 482 724 1584 725 482 724 481 726 1584 724 482 724 19927 724 481 726 482 724 482 725 481 725 481 725 482 725 482 724 481 726 481 725 1584 724 481 725 481 725 482 725 481 725 1584 725 1585 724 482 725 482 724 482 725 482 724 482 725 482 724 481 725 482 725 482 724 482 725 482 724 482 724 1584 724 1584 724 1584 724 1585 725 39945 9071 4412 726 480 726 480 726 481 725 481 725 481 725 481 725 481 725 482 725 481 724 481 726 482 725 481 725 482 724 482 724 482 725 482 724 481 725 482 725 482 725 482 724 482 724 482 725 481 725 482 725 482 725 481 726 482 724 482 724 481 726 1584 725 482 724 1584 724 482 724 1585 724 482 725 19924 725 481 725 481 726 481 725 481 725 481 726 481 724 482 725 482 724 481 725 481 725 481 725 481 726 482 725 481 725 481 725 482 724 481 752 455 752 454 753 455 751 454 752 454 753 454 751 453 753 454 752 454 753 454 753 454 752 455 752 1556 753 453 754 1557 752 +# +name: Cool_lo +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 9049 4436 702 1606 703 504 730 476 759 1548 758 448 730 476 730 476 730 477 729 1580 727 1581 727 1581 727 480 726 481 725 481 725 481 724 482 724 507 699 483 724 483 723 507 699 506 701 1609 699 1609 699 507 700 507 699 507 700 507 699 507 700 1610 699 507 700 1609 700 507 699 507 700 1610 699 507 700 19928 723 482 724 483 723 483 723 484 722 507 699 507 700 507 699 507 699 507 700 1609 700 508 699 507 700 507 699 507 699 1609 700 1609 699 507 699 508 699 507 699 507 700 507 700 507 700 507 699 507 699 507 700 507 699 507 700 507 699 507 700 1610 699 1610 699 507 700 39944 9074 4410 727 479 727 480 726 481 726 481 725 482 725 483 723 483 724 507 699 507 700 484 722 507 699 507 699 507 699 506 700 507 699 507 700 507 699 507 698 507 699 508 699 507 699 507 699 507 700 507 700 507 698 507 699 508 699 507 699 507 699 1610 699 507 699 1610 699 507 699 1609 699 507 699 19926 724 483 723 506 700 507 699 507 700 507 700 507 700 507 699 507 700 507 700 507 699 507 700 507 699 507 699 507 700 506 700 508 699 507 700 507 700 507 699 507 699 507 700 507 700 507 700 508 699 507 699 507 699 508 699 507 699 507 700 1610 698 507 699 1609 699 +# \ No newline at end of file From 5c54be4d9c241b1244da8487944f7b13c96bae95 Mon Sep 17 00:00:00 2001 From: Alexander Bays Date: Wed, 24 Sep 2025 10:45:40 -0500 Subject: [PATCH 3/3] Infrared: Add text scroll to remote buttons (#4210) * Infrared: Add text scroll to universal remote buttons Replaces center aligned text in the infrared universal remote with scrollable text if wider than the button and is cut off. Allows long descriptive button functions to be seen in some remotes. * linter fixes --------- Co-authored-by: hedger Co-authored-by: hedger --- .../services/gui/modules/button_menu.c | 116 +++++++++++++----- 1 file changed, 88 insertions(+), 28 deletions(-) diff --git a/applications/services/gui/modules/button_menu.c b/applications/services/gui/modules/button_menu.c index d9c178dd2..5204c6f22 100644 --- a/applications/services/gui/modules/button_menu.c +++ b/applications/services/gui/modules/button_menu.c @@ -10,6 +10,7 @@ #include #include +#define SCROLL_INTERVAL (333) #define ITEM_FIRST_OFFSET 17 #define ITEM_NEXT_OFFSET 4 #define ITEM_HEIGHT 14 @@ -35,13 +36,56 @@ typedef struct { ButtonMenuItemArray_t items; size_t position; const char* header; + size_t scroll_counter; + FuriTimer* scroll_timer; } ButtonMenuModel; +static void button_menu_draw_text( + Canvas* canvas, + uint8_t item_x, + uint8_t item_y, + const char* text, + bool selected, + ButtonMenuModel* model) { + FuriString* disp_str; + disp_str = furi_string_alloc_set(text); + bool draw_static = true; + + if(selected) { + size_t text_width = canvas_string_width(canvas, furi_string_get_cstr(disp_str)); + if(text_width >= ITEM_WIDTH - 8) { + elements_scrollable_text_line( + canvas, + item_x + 4, + item_y + ITEM_HEIGHT - 4, + ITEM_WIDTH - 8, + disp_str, + model->scroll_counter, + false); + draw_static = false; + } + } + + if(draw_static) { + elements_string_fit_width(canvas, disp_str, ITEM_WIDTH - 6); + canvas_draw_str_aligned( + canvas, + item_x + (ITEM_WIDTH / 2), + item_y + (ITEM_HEIGHT / 2), + AlignCenter, + AlignCenter, + furi_string_get_cstr(disp_str)); + } + + furi_string_free(disp_str); +} + static void button_menu_draw_control_button( Canvas* canvas, uint8_t item_position, const char* text, - bool selected) { + bool selected, + ButtonMenuModel* model) { furi_assert(canvas); furi_assert(text); @@ -54,20 +98,16 @@ static void button_menu_draw_control_button( elements_slightly_rounded_box(canvas, item_x, item_y, ITEM_WIDTH, ITEM_HEIGHT); canvas_set_color(canvas, ColorWhite); } - canvas_draw_str_aligned( - canvas, - item_x + (ITEM_WIDTH / 2), - item_y + (ITEM_HEIGHT / 2), - AlignCenter, - AlignCenter, - text); + + button_menu_draw_text(canvas, item_x, item_y, text, selected, model); } static void button_menu_draw_common_button( Canvas* canvas, uint8_t item_position, const char* text, - bool selected) { + bool selected, + ButtonMenuModel* model) { furi_assert(canvas); furi_assert(text); @@ -83,19 +123,7 @@ static void button_menu_draw_common_button( canvas_draw_rframe(canvas, item_x, item_y, ITEM_WIDTH, ITEM_HEIGHT, 5); } - FuriString* disp_str; - disp_str = furi_string_alloc_set(text); - elements_string_fit_width(canvas, disp_str, ITEM_WIDTH - 6); - - canvas_draw_str_aligned( - canvas, - item_x + (ITEM_WIDTH / 2), - item_y + (ITEM_HEIGHT / 2), - AlignCenter, - AlignCenter, - furi_string_get_cstr(disp_str)); - - furi_string_free(disp_str); + button_menu_draw_text(canvas, item_x, item_y, text, selected, model); } static void button_menu_view_draw_callback(Canvas* canvas, void* _model) { @@ -120,9 +148,17 @@ static void button_menu_view_draw_callback(Canvas* canvas, void* _model) { if(model->header) { FuriString* disp_str; disp_str = furi_string_alloc_set(model->header); - elements_string_fit_width(canvas, disp_str, ITEM_WIDTH - 6); - canvas_draw_str_aligned( - canvas, 32, 10, AlignCenter, AlignCenter, furi_string_get_cstr(disp_str)); + size_t header_width = canvas_string_width(canvas, furi_string_get_cstr(disp_str)); + + if(header_width >= ITEM_WIDTH - 8) { + elements_scrollable_text_line( + canvas, 3, 13, ITEM_WIDTH - 8, disp_str, model->scroll_counter, false); + } else { + elements_string_fit_width(canvas, disp_str, ITEM_WIDTH - 8); + canvas_draw_str_aligned( + canvas, 32, 10, AlignCenter, AlignCenter, furi_string_get_cstr(disp_str)); + } + furi_string_free(disp_str); } @@ -137,13 +173,15 @@ static void button_menu_view_draw_callback(Canvas* canvas, void* _model) { canvas, item_position % BUTTONS_PER_SCREEN, ButtonMenuItemArray_cref(it)->label, - (item_position == model->position)); + (item_position == model->position), + model); } else if(ButtonMenuItemArray_cref(it)->type == ButtonMenuItemTypeCommon) { button_menu_draw_common_button( canvas, item_position % BUTTONS_PER_SCREEN, ButtonMenuItemArray_cref(it)->label, - (item_position == model->position)); + (item_position == model->position), + model); } } } @@ -158,8 +196,10 @@ static void button_menu_process_up(ButtonMenu* button_menu) { { if(model->position > 0) { model->position--; + model->scroll_counter = 0; } else { model->position = ButtonMenuItemArray_size(model->items) - 1; + model->scroll_counter = 0; } }, true); @@ -174,8 +214,10 @@ static void button_menu_process_down(ButtonMenu* button_menu) { { if(model->position < (ButtonMenuItemArray_size(model->items) - 1)) { model->position++; + model->scroll_counter = 0; } else { model->position = 0; + model->scroll_counter = 0; } }, true); @@ -193,8 +235,10 @@ static void button_menu_process_right(ButtonMenu* button_menu) { position_candidate -= position_candidate % BUTTONS_PER_SCREEN; if(position_candidate < (ButtonMenuItemArray_size(model->items))) { model->position = position_candidate; + model->scroll_counter = 0; } else { model->position = 0; + model->scroll_counter = 0; } } }, @@ -217,6 +261,7 @@ static void button_menu_process_left(ButtonMenu* button_menu) { }; position_candidate -= position_candidate % BUTTONS_PER_SCREEN; model->position = position_candidate; + model->scroll_counter = 0; } }, true); @@ -314,6 +359,7 @@ void button_menu_reset(ButtonMenu* button_menu) { ButtonMenuItemArray_reset(model->items); model->position = 0; model->header = NULL; + model->scroll_counter = 0; }, true); } @@ -351,6 +397,12 @@ ButtonMenuItem* button_menu_add_item( return item; } +static void button_menu_process_timer_callback(void* context) { + ButtonMenu* button_menu = context; + with_view_model( + button_menu->view, ButtonMenuModel * model, { model->scroll_counter++; }, true); +} + ButtonMenu* button_menu_alloc(void) { ButtonMenu* button_menu = malloc(sizeof(ButtonMenu)); button_menu->view = view_alloc(); @@ -367,6 +419,10 @@ ButtonMenu* button_menu_alloc(void) { ButtonMenuItemArray_init(model->items); model->position = 0; model->header = NULL; + model->scroll_counter = 0; + model->scroll_timer = furi_timer_alloc( + button_menu_process_timer_callback, FuriTimerTypePeriodic, button_menu); + furi_timer_start(model->scroll_timer, SCROLL_INTERVAL); }, true); @@ -380,7 +436,11 @@ void button_menu_free(ButtonMenu* button_menu) { with_view_model( button_menu->view, ButtonMenuModel * model, - { ButtonMenuItemArray_clear(model->items); }, + { + ButtonMenuItemArray_clear(model->items); + furi_timer_stop(model->scroll_timer); + furi_timer_free(model->scroll_timer); + }, true); view_free(button_menu->view); free(button_menu);